define([ 'jquery', '/bower_components/marked/marked.min.js', '/common/cryptpad-common.js', '/common/media-tag.js', '/bower_components/diff-dom/diffDOM.js', '/bower_components/tweetnacl/nacl-fast.min.js', ],function ($, Marked, Cryptpad, MediaTag) { var DiffMd = {}; var DiffDOM = window.diffDOM; var renderer = new Marked.Renderer(); Marked.setOptions({ renderer: renderer }); DiffMd.render = function (md) { return Marked(md); }; var mediaMap = {}; // Tasks list var checkedTaskItemPtn = /^\s*\[x\]\s*/; var uncheckedTaskItemPtn = /^\s*\[ \]\s*/; renderer.listitem = function (text) { var isCheckedTaskItem = checkedTaskItemPtn.test(text); var isUncheckedTaskItem = uncheckedTaskItemPtn.test(text); if (isCheckedTaskItem) { text = text.replace(checkedTaskItemPtn, ' ') + '\n'; } if (isUncheckedTaskItem) { text = text.replace(uncheckedTaskItemPtn, ' ') + '\n'; } var cls = (isCheckedTaskItem || isUncheckedTaskItem) ? ' class="todo-list-item"' : ''; return '' + text + '\n'; }; renderer.image = function (href, title, text) { if (href.slice(0,6) === '/file/') { var parsed = Cryptpad.parsePadUrl(href); var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel); var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var mt = ''; if (mediaMap[src]) { mediaMap[src].forEach(function (n) { mt += n.outerHTML; }); } mt += ''; return mt; } var out = '' + text + '' : '>'; return out; }; var MutationObserver = window.MutationObserver; var forbiddenTags = [ 'SCRIPT', 'IFRAME', 'OBJECT', 'APPLET', //'VIDEO', 'AUDIO', ]; var unsafeTag = function (info) { /*if (info.node && $(info.node).parents('media-tag').length) { // Do not remove elements inside a media-tag return true; }*/ if (['addAttribute', 'modifyAttribute'].indexOf(info.diff.action) !== -1) { if (/^on/.test(info.diff.name)) { console.log("Rejecting forbidden element attribute with name", info.diff.name); return true; } } if (['addElement', 'replaceElement'].indexOf(info.diff.action) !== -1) { var msg = "Rejecting forbidden tag of type (%s)"; if (info.diff.element && forbiddenTags.indexOf(info.diff.element.nodeName) !== -1) { console.log(msg, info.diff.element.nodeName); return true; } else if (info.diff.newValue && forbiddenTags.indexOf(info.diff.newValue.nodeName) !== -1) { console.log("Replacing restricted element type (%s) with PRE", info.diff.newValue.nodeName); info.diff.newValue.nodeName = 'PRE'; } } }; var slice = function (coll) { return Array.prototype.slice.call(coll); }; /* remove listeners from the DOM */ var removeListeners = function (root) { slice(root.attributes).map(function (attr) { if (/^on/.test(attr.name)) { root.attributes.removeNamedItem(attr.name); } }); // all the way down slice(root.children).forEach(removeListeners); }; var domFromHTML = function (html) { var Dom = new DOMParser().parseFromString(html, "text/html"); removeListeners(Dom.body); return Dom; }; var DD = new DiffDOM({ preDiffApply: function (info) { if (unsafeTag(info)) { return true; } }, }); var makeDiff = function (A, B, id) { var Err; var Els = [A, B].map(function (frag) { if (typeof(frag) === 'object') { if (!frag || (frag && !frag.body)) { Err = "No body"; return; } var els = frag.body.querySelectorAll('#'+id); if (els.length) { return els[0]; } } Err = 'No candidate found'; }); if (Err) { return Err; } var patch = DD.diff(Els[0], Els[1]); return patch; }; DiffMd.apply = function (newHtml, $content) { var id = $content.attr('id'); if (!id) { throw new Error("The element must have a valid id"); } var $div = $('
', {id: id}).append(newHtml); var Dom = domFromHTML($('
').append($div).html()); var oldDom = domFromHTML($content[0].outerHTML); var patch = makeDiff(oldDom, Dom, id); if (typeof(patch) === 'string') { throw new Error(patch); } else { DD.apply($content[0], patch); var $mts = $content.find('media-tag:not(:has(*))'); $mts.each(function (i, el) { MediaTag(el); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { console.log(mutation); if (mutation.type === 'childList') { var list_values = [].slice.call(el.children); mediaMap[el.getAttribute('src')] = list_values; } }); }); observer.observe(el, { attributes: false, childList: true, characterData: false }); }); } }; $(window.document).on('decryption', function (e) { var decrypted = e.originalEvent; if (decrypted.callback) { decrypted.callback(); } }); return DiffMd; });