diff --git a/www/p/index.html b/www/p/index.html deleted file mode 100644 index 830d56125..000000000 --- a/www/p/index.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - diff --git a/www/p/inner.html b/www/p/inner.html deleted file mode 100644 index bf79dcd0d..000000000 --- a/www/p/inner.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/www/p/main.js b/www/p/main.js deleted file mode 100644 index 64eda9f70..000000000 --- a/www/p/main.js +++ /dev/null @@ -1,359 +0,0 @@ -require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); -define([ - '/api/config?cb=' + Math.random().toString(16).substring(2), - '/common/messages.js', - '/common/crypto.js', - '/common/RealtimeTextSocket.js', - '/common/hyperjson.js', - '/common/hyperscript.js', - '/p/toolbar.js', - '/common/cursor.js', - '/common/json-ot.js', - '/common/TypingTests.js', - 'json.sortify', - '/common/TextPatcher.js', - '/bower_components/diff-dom/diffDOM.js', - '/bower_components/jquery/dist/jquery.min.js', - '/customize/pad.js' -], function (Config, Messages, Crypto, realtimeInput, Hyperjson, Hyperscript, Toolbar, Cursor, JsonOT, TypingTest, JSONSortify, TextPatcher) { - var $ = window.jQuery; - var ifrw = $('#pad-iframe')[0].contentWindow; - var Ckeditor; // to be initialized later... - var DiffDom = window.diffDOM; - - var stringify = function (obj) { - return JSONSortify(obj); - }; - - window.Hyperjson = Hyperjson; - - var hjsonToDom = function (H) { - return Hyperjson.callOn(H, Hyperscript); - }; - - var userName = Crypto.rand64(8), - toolbar; - - var module = window.REALTIME_MODULE = { - Hyperjson: Hyperjson, - Hyperscript: Hyperscript, - logFights: true, - fights: [], - }; - - var isNotMagicLine = function (el) { - // factor as: - // return !(el.tagName === 'SPAN' && el.contentEditable === 'false'); - var filter = (el.tagName === 'SPAN' && - el.getAttribute('contentEditable') === 'false'); - if (filter) { - console.log("[hyperjson.serializer] prevented an element" + - "from being serialized:", el); - return false; - } - return true; - }; - - /* catch `type="_moz"` before it goes over the wire */ - var brFilter = function (hj) { - if (hj[1].type === '_moz') { hj[1].type = undefined; } - return hj; - }; - - /* TODO integrate into flow to prevent browser fights over style */ - var setStyle = function (elem, newStyleAttr) { - elem.setAttribute("data-chainpad-origstyle", newStyleAttr); - elem.setAttribute("style", newStyleAttr); - elem.setAttribute("data-chainpad-styleclone", elem.getAttribute("style")); - }; - - /* TODO integrate into flow to prevent browser fights over style */ - var getStyle = function (elem) { - var st = elem.getAttribute("style"); - if (elem.getAttribute("data-chainpad-styleclone") !== st) { return st; } - return elem.getAttribute("data-chainpad-origstyle"); - }; - - var stringifyDOM = module.stringifyDOM = function (dom) { - return stringify(Hyperjson.fromDOM(dom, isNotMagicLine, brFilter)); - }; - - var andThen = function (Ckeditor) { - $(window).on('hashchange', function() { - window.location.reload(); - }); - if (window.location.href.indexOf('#') === -1) { - window.location.href = window.location.href + '#' + Crypto.genKey(); - return; - } - - var fixThings = false; - var key = Crypto.parseKey(window.location.hash.substring(1)); - var editor = window.editor = Ckeditor.replace('editor1', { - // https://dev.ckeditor.com/ticket/10907 - needsBrFiller: fixThings, - needsNbspFiller: fixThings, - removeButtons: 'Source,Maximize', - // magicline plugin inserts html crap into the document which is not part of the - // document itself and causes problems when it's sent across the wire and reflected back - // but we filter it now, so that's ok. - removePlugins: 'resize' - }); - - editor.on('instanceReady', function (Ckeditor) { - editor.execCommand('maximize'); - var documentBody = ifrw.$('iframe')[0].contentDocument.body; - - documentBody.innerHTML = Messages.initialState; - - var inner = window.inner = documentBody; - var cursor = window.cursor = Cursor(inner); - - var setEditable = function (bool) { - // careful about putting attributes onto the DOM - // they get put into the chain, and you can have trouble - // getting rid of them later - - //inner.style.backgroundColor = bool? 'white': 'grey'; - inner.setAttribute('contenteditable', bool); - }; - - // don't let the user edit until the pad is ready - setEditable(false); - - var diffOptions = { - preDiffApply: function (info) { - /* DiffDOM will filter out magicline plugin elements - in practice this will make it impossible to use it - while someone else is typing, which could be annoying. - - we should check when such an element is going to be - removed, and prevent that from happening. */ - if (info.node && info.node.tagName === 'SPAN' && - info.node.getAttribute('contentEditable') === "false") { - // it seems to be a magicline plugin element... - if (info.diff.action === 'removeElement') { - // and you're about to remove it... - // this probably isn't what you want - - /* - I have never seen this in the console, but the - magic line is still getting removed on remote - edits. This suggests that it's getting removed - by something other than diffDom. - */ - console.log("preventing removal of the magic line!"); - - // return true to prevent diff application - return true; - } - } - - // no use trying to recover the cursor if it doesn't exist - if (!cursor.exists()) { return; } - - /* frame is either 0, 1, 2, or 3, depending on which - cursor frames were affected: none, first, last, or both - */ - var frame = info.frame = cursor.inNode(info.node); - - if (!frame) { return; } - - if (typeof info.diff.oldValue === 'string' && typeof info.diff.newValue === 'string') { - var pushes = cursor.pushDelta(info.diff.oldValue, info.diff.newValue); - - if (frame & 1) { - // push cursor start if necessary - if (pushes.commonStart < cursor.Range.start.offset) { - cursor.Range.start.offset += pushes.delta; - } - } - if (frame & 2) { - // push cursor end if necessary - if (pushes.commonStart < cursor.Range.end.offset) { - cursor.Range.end.offset += pushes.delta; - } - } - } - }, - postDiffApply: function (info) { - if (info.frame) { - if (info.node) { - if (info.frame & 1) { cursor.fixStart(info.node); } - if (info.frame & 2) { cursor.fixEnd(info.node); } - } else { console.error("info.node did not exist"); } - - var sel = cursor.makeSelection(); - var range = cursor.makeRange(); - - cursor.fixSelection(sel, range); - } - } - }; - - var now = function () { return new Date().getTime(); }; - - var realtimeOptions = { - // provide initialstate... - initialState: stringifyDOM(inner) || '{}', - - // really basic operational transform - // reject patch if it results in invalid JSON - transformFunction : JsonOT.validate, - - websocketURL: Config.websocketURL+'_old', - - // username - userName: userName, - - // communication channel name - channel: key.channel, - - // encryption key - cryptKey: key.cryptKey, - - crypto: Crypto, - }; - - var DD = new DiffDom(diffOptions); - - // apply patches, and try not to lose the cursor in the process! - var applyHjson = function (shjson) { - var userDocStateDom = hjsonToDom(JSON.parse(shjson)); - - /* in the DOM contentEditable is "false" - while "contenteditable" is undefined. - - When it goes over the wire, it seems hyperjson transforms it. - of course, hyperjson simply gets attributes from the DOM. - - el.attributes returns 'contenteditable', so we have to correct for that - - There are quite possibly all sorts of other attributes which might lose - information, and we won't know what they are until after we've lost them. - - this comes from hyperscript line 101. FIXME maybe - */ - userDocStateDom.setAttribute("contenteditable", "true"); // lol wtf - var patch = (DD).diff(inner, userDocStateDom); - (DD).apply(inner, patch); - }; - - var initializing = true; - - var onRemote = realtimeOptions.onRemote = function (info) { - if (initializing) { return; } - - var shjson = info.realtime.getUserDoc(); - - // remember where the cursor is - cursor.update(); - - // build a dom from HJSON, diff, and patch the editor - applyHjson(shjson); - - var shjson2 = stringifyDOM(inner); - if (shjson2 !== shjson) { - /* the client's browser made changes when pushing content - into the dom */ - console.error("shjson2 !== shjson"); - // push those changes back over the wire - module.patchText(shjson2); - - /* pushing back over the wire is necessary, but it can - result in a feedback loop, which we call a browser - fight */ - if (module.logFights) { - // what changed? - var op = TextPatcher.diff(shjson, shjson2); - // log the changes - TextPatcher.log(shjson, op); - var sop = JSON.stringify(TextPatcher.format(shjson, op)); - - var index = module.fights.indexOf(sop); - if (index === -1) { - module.fights.push(sop); - console.log("Found a new type of browser disagreement"); - console.log("You can inspect the list in your " + - "console at `REALTIME_MODULE.fights`"); - console.log(module.fights); - } else { - console.log("Encountered a known browser disagreement: " + - "available at `REALTIME_MODULE.fights[%s]`", index); - } - } - } - }; - - var onInit = realtimeOptions.onInit = function (info) { - var $bar = $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox'); - toolbar = info.realtime.toolbar = Toolbar.create($bar, userName, info.realtime); - - /* TODO handle disconnects and such*/ - }; - - var onReady = realtimeOptions.onReady = function (info) { - var shjson = info.realtime.getUserDoc(); - - module.patchText = TextPatcher.create({ - realtime: info.realtime, - logging: true, - }); - - applyHjson(shjson); - console.log("Unlocking editor"); - setEditable(true); - initializing = false; - }; - - var onAbort = realtimeOptions.onAbort = function (info) { - console.log("Aborting the session!"); - // stop the user from continuing to edit - // by setting the editable to false - setEditable(false); - toolbar.failed(); - }; - - var onLocal = realtimeOptions.onLocal = function () { - if (initializing) { return; } - var shjson = stringifyDOM(inner); - module.patchText(shjson); - }; - - var rti = module.realtimeInput = realtimeInput.start(realtimeOptions); - - /* hitting enter makes a new line, but places the cursor inside - of the
instead of the

. This makes it such that you - cannot type until you click, which is rather unnacceptable. - If the cursor is ever inside such a
, you probably want - to push it out to the parent element, which ought to be a - paragraph tag. This needs to be done on keydown, otherwise - the first such keypress will not be inserted into the P. */ - inner.addEventListener('keydown', cursor.brFix); - - var easyTest = window.easyTest = function () { - cursor.update(); - var start = cursor.Range.start; - var test = TypingTest.testInput(inner, start.el, start.offset, onLocal); - // why twice? - onLocal(); - return test; - }; - - editor.on('change', onLocal); - }); - }; - - var interval = 100; - var first = function () { - Ckeditor = ifrw.CKEDITOR; - if (Ckeditor) { - andThen(Ckeditor); - } else { - console.log("Ckeditor was not defined. Trying again in %sms",interval); - setTimeout(first, interval); - } - }; - - $(first); -}); diff --git a/www/p/toolbar.js b/www/p/toolbar.js deleted file mode 100644 index 4159ea0ac..000000000 --- a/www/p/toolbar.js +++ /dev/null @@ -1,233 +0,0 @@ -define([ - '/common/messages.js' -], function (Messages) { - - /** Id of the element for getting debug info. */ - var DEBUG_LINK_CLS = 'rtwysiwyg-debug-link'; - - /** Id of the div containing the user list. */ - var USER_LIST_CLS = 'rtwysiwyg-user-list'; - - /** Id of the div containing the lag info. */ - var LAG_ELEM_CLS = 'rtwysiwyg-lag'; - - /** The toolbar class which contains the user list, debug link and lag. */ - var TOOLBAR_CLS = 'rtwysiwyg-toolbar'; - - /** Key in the localStore which indicates realtime activity should be disallowed. */ - var LOCALSTORAGE_DISALLOW = 'rtwysiwyg-disallow'; - - var SPINNER_DISAPPEAR_TIME = 3000; - var SPINNER = [ '-', '\\', '|', '/' ]; - - var uid = function () { - return 'rtwysiwyg-uid-' + String(Math.random()).substring(2); - }; - - var createRealtimeToolbar = function ($container) { - var id = uid(); - $container.prepend( - '

' + - '
' + - '
' + - '
' - ); - var toolbar = $container.find('#'+id); - toolbar.append([ - '' - ].join('\n')); - return toolbar; - }; - - var createEscape = function ($container) { - var id = uid(); - $container.append('
⇐ Back
'); - var $ret = $container.find('#'+id); - $ret.on('click', function () { - window.location.href = '/'; - }); - return $ret[0]; - }; - - var createSpinner = function ($container) { - var id = uid(); - $container.append('
'); - return $container.find('#'+id)[0]; - }; - - var kickSpinner = function (spinnerElement, reversed) { - var txt = spinnerElement.textContent || '-'; - var inc = (reversed) ? -1 : 1; - spinnerElement.textContent = SPINNER[(SPINNER.indexOf(txt) + inc) % SPINNER.length]; - if (spinnerElement.timeout) { clearTimeout(spinnerElement.timeout); } - spinnerElement.timeout = setTimeout(function () { - spinnerElement.textContent = ''; - }, SPINNER_DISAPPEAR_TIME); - }; - - var createUserList = function ($container) { - var id = uid(); - $container.append('
'); - return $container.find('#'+id)[0]; - }; - - var updateUserList = function (myUserName, listElement, userList) { - var meIdx = userList.indexOf(myUserName); - if (meIdx === -1) { - listElement.textContent = Messages.synchronizing; - return; - } - if (userList.length === 1) { - listElement.textContent = Messages.editingAlone; - } else if (userList.length === 2) { - listElement.textContent = Messages.editingWithOneOtherPerson; - } else { - listElement.textContent = Messages.editingWith + ' ' + (userList.length - 1) + ' ' + Messages.otherPeople; - } - }; - - var createLagElement = function ($container) { - var id = uid(); - $container.append('
'); - return $container.find('#'+id)[0]; - }; - - var checkLag = function (realtime, lagElement) { - var lag = realtime.getLag(); - var lagSec = lag.lag/1000; - var lagMsg = Messages.lag + ' '; - if (lag.waiting && lagSec > 1) { - lagMsg += "?? " + Math.floor(lagSec); - } else { - lagMsg += lagSec; - } - lagElement.textContent = lagMsg; - }; - - // this is a little hack, it should go in it's own file. - // FIXME ok, so let's put it in its own file then - // TODO there should also be a 'clear recent pads' button - var rememberPad = function () { - // FIXME, this is overly complicated, use array methods - var recentPadsStr = localStorage['CryptPad_RECENTPADS']; - var recentPads = []; - if (recentPadsStr) { recentPads = JSON.parse(recentPadsStr); } - // TODO use window.location.hash or something like that - if (window.location.href.indexOf('#') === -1) { return; } - var now = new Date(); - var out = []; - for (var i = recentPads.length; i >= 0; i--) { - if (recentPads[i] && - // TODO precompute this time value, maybe make it configurable? - // FIXME precompute the date too, why getTime every time? - now.getTime() - recentPads[i][1] < (1000*60*60*24*30) && - recentPads[i][0] !== window.location.href) - { - out.push(recentPads[i]); - } - } - out.push([window.location.href, now.getTime()]); - localStorage['CryptPad_RECENTPADS'] = JSON.stringify(out); - }; - - var create = function ($container, myUserName, realtime) { - var toolbar = createRealtimeToolbar($container); - createEscape(toolbar.find('.rtwysiwyg-toolbar-leftside')); - var userListElement = createUserList(toolbar.find('.rtwysiwyg-toolbar-leftside')); - var spinner = createSpinner(toolbar.find('.rtwysiwyg-toolbar-rightside')); - var lagElement = createLagElement(toolbar.find('.rtwysiwyg-toolbar-rightside')); - - rememberPad(); - - var connected = false; - - realtime.onUserListChange(function (userList) { - if (userList.indexOf(myUserName) !== -1) { connected = true; } - if (!connected) { return; } - updateUserList(myUserName, userListElement, userList); - }); - - var ks = function () { - if (connected) { kickSpinner(spinner, false); } - }; - - realtime.onPatch(ks); - // Try to filter out non-patch messages, doesn't have to be perfect this is just the spinner - realtime.onMessage(function (msg) { if (msg.indexOf(':[2,') > -1) { ks(); } }); - - setInterval(function () { - if (!connected) { return; } - checkLag(realtime, lagElement); - }, 3000); - - return { - failed: function () { - connected = false; - userListElement.textContent = ''; - lagElement.textContent = ''; - }, - reconnecting: function () { - connected = false; - userListElement.textContent = Messages.reconnecting; - lagElement.textContent = ''; - }, - connected: function () { - connected = true; - } - }; - }; - - return { create: create }; -});