From 75130150d5427c15368cba284c3c424abed8afdc Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Thu, 9 Nov 2017 15:36:49 +0100 Subject: [PATCH 01/11] Refactored out TextPatcher and JsonOT and replaced with new ChainPad --- bower.json | 6 +- www/code/inner.js | 2 - www/common/common-codemirror.js | 308 -------------------- www/common/common-history.js | 6 +- www/common/cryptget.js | 7 +- www/common/cryptpad-common.js | 7 +- www/common/fsStore.js | 3 +- www/common/sframe-app-framework.js | 20 +- www/common/sframe-chainpad-listmap.js | 18 +- www/common/sframe-chainpad-netflux-inner.js | 3 +- www/common/sframe-common-codemirror.js | 19 +- www/common/sframe-common-history.js | 5 +- www/common/text-cursor.js | 25 ++ www/contacts/inner.js | 2 - www/drive/inner.js | 8 +- www/examples/board/board.js | 242 --------------- www/examples/board/index.html | 97 ------ www/examples/board/main.js | 93 ------ www/examples/form/index.html | 78 ----- www/examples/form/main.js | 222 -------------- www/examples/form/types.md | 14 - www/examples/form/ula.js | 25 -- www/examples/style/index.html | 60 ---- www/examples/style/main.js | 75 ----- www/examples/text/index.html | 38 --- www/examples/text/main.js | 97 ------ www/filepicker/inner.js | 4 - www/pad/inner.js | 8 +- www/poll/inner.js | 11 +- www/poll/render.js | 11 +- www/whiteboard/inner.js | 15 +- 31 files changed, 82 insertions(+), 1447 deletions(-) delete mode 100644 www/common/common-codemirror.js create mode 100644 www/common/text-cursor.js delete mode 100644 www/examples/board/board.js delete mode 100644 www/examples/board/index.html delete mode 100644 www/examples/board/main.js delete mode 100644 www/examples/form/index.html delete mode 100644 www/examples/form/main.js delete mode 100644 www/examples/form/types.md delete mode 100644 www/examples/form/ula.js delete mode 100644 www/examples/style/index.html delete mode 100644 www/examples/style/main.js delete mode 100644 www/examples/text/index.html delete mode 100644 www/examples/text/main.js diff --git a/bower.json b/bower.json index 2b9c05687..0740b894d 100644 --- a/bower.json +++ b/bower.json @@ -29,10 +29,10 @@ "json.sortify": "~2.1.0", "secure-fabric.js": "secure-v1.7.9", "hyperjson": "~1.4.0", - "textpatcher": "^1.3.0", - "chainpad-json-validator": "^0.2.0", "chainpad-crypto": "^0.1.3", - "chainpad-listmap": "^0.3.0", + "chainpad-listmap": "git+https://git@github.com/xwiki-labs/chainpad-listmap.git#new-chainpad", + "chainpad": "git+https://git@github.com/xwiki-contrib/chainpad.git#transform-issues", + "chainpad-netflux": "^0.6.0", "file-saver": "1.3.1", "alertifyjs": "1.0.11", "scrypt-async": "1.2.0", diff --git a/www/code/inner.js b/www/code/inner.js index 5f2cf3df1..8e6f213f1 100644 --- a/www/code/inner.js +++ b/www/code/inner.js @@ -1,6 +1,5 @@ define([ 'jquery', - '/bower_components/textpatcher/TextPatcher.js', '/common/cryptpad-common.js', '/common/diffMarked.js', '/bower_components/nthen/index.js', @@ -38,7 +37,6 @@ define([ ], function ( $, - TextPatcher, Cryptpad, DiffMd, nThen, diff --git a/www/common/common-codemirror.js b/www/common/common-codemirror.js deleted file mode 100644 index a32d89af1..000000000 --- a/www/common/common-codemirror.js +++ /dev/null @@ -1,308 +0,0 @@ -define([ - 'jquery', - '/common/modes.js', - '/common/themes.js', - - '/bower_components/file-saver/FileSaver.min.js' -], function ($, Modes, Themes) { - var saveAs = window.saveAs; - var module = {}; - - module.create = function (ifrw, Cryptpad, defaultMode, CMeditor) { - var exp = {}; - var Messages = Cryptpad.Messages; - - var CodeMirror = exp.CodeMirror = CMeditor; - CodeMirror.modeURL = "cm/mode/%N/%N"; - - var $pad = $('#pad-iframe'); - var $textarea = exp.$textarea = $('#editor1'); - if (!$textarea.length) { $textarea = exp.$textarea = $pad.contents().find('#editor1'); } - - var Title; - var onLocal = function () {}; - var $rightside; - var $drawer; - exp.init = function (local, title, toolbar) { - if (typeof local === "function") { - onLocal = local; - } - Title = title; - $rightside = toolbar.$rightside; - $drawer = toolbar.$drawer; - }; - - var editor = exp.editor = CMeditor.fromTextArea($textarea[0], { - lineNumbers: true, - lineWrapping: true, - autoCloseBrackets: true, - matchBrackets : true, - showTrailingSpace : true, - styleActiveLine : true, - search: true, - highlightSelectionMatches: {showToken: /\w+/}, - extraKeys: {"Shift-Ctrl-R": undefined}, - foldGutter: true, - gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], - mode: defaultMode || "javascript", - readOnly: true - }); - editor.setValue(Messages.codeInitialState); - - var setMode = exp.setMode = function (mode, cb) { - exp.highlightMode = mode; - if (mode !== "text") { - CMeditor.autoLoadMode(editor, mode); - } - editor.setOption('mode', mode); - if (exp.$language) { - var name = exp.$language.find('a[data-value="' + mode + '"]').text() || undefined; - name = name ? Messages.languageButton + ' ('+name+')' : Messages.languageButton; - exp.$language.setValue(mode, name); - } - if(cb) { cb(mode); } - }; - - var setTheme = exp.setTheme = (function () { - var path = '/common/theme/'; - - var $head = $(ifrw.document.head); - - var themeLoaded = exp.themeLoaded = function (theme) { - return $head.find('link[href*="'+theme+'"]').length; - }; - - var loadTheme = exp.loadTheme = function (theme) { - $head.append($('', { - rel: 'stylesheet', - href: path + theme + '.css', - })); - }; - - return function (theme, $select) { - if (!theme) { - editor.setOption('theme', 'default'); - } else { - if (!themeLoaded(theme)) { - loadTheme(theme); - } - editor.setOption('theme', theme); - } - if ($select) { - var name = theme || undefined; - name = name ? Messages.themeButton + ' ('+theme+')' : Messages.themeButton; - $select.setValue(theme, name); - } - }; - }()); - - exp.getHeadingText = function () { - var lines = editor.getValue().split(/\n/); - - var text = ''; - lines.some(function (line) { - // lines including a c-style comment are also valuable - var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/; - if (clike.test(line)) { - line.replace(clike, function (a, one, two) { - if (!(two && two.replace)) { return; } - text = two.replace(/\*\/\s*$/, '').trim(); - }); - return true; - } - - // lisps? - var lispy = /^\s*(;|#\|)+(.*?)$/; - if (lispy.test(line)) { - line.replace(lispy, function (a, one, two) { - text = two; - }); - return true; - } - - // lines beginning with a hash are potentially valuable - // works for markdown, python, bash, etc. - var hash = /^#+(.*?)$/; - if (hash.test(line)) { - line.replace(hash, function (a, one) { - text = one; - }); - return true; - } - - // TODO make one more pass for multiline comments - }); - - return text.trim(); - }; - - exp.configureLanguage = function (cb, onModeChanged) { - var options = []; - Modes.list.forEach(function (l) { - options.push({ - tag: 'a', - attributes: { - 'data-value': l.mode, - 'href': '#', - }, - content: l.language // Pretty name of the language value - }); - }); - var dropdownConfig = { - text: 'Mode', // Button initial text - options: options, // Entries displayed in the menu - left: true, // Open to the left of the button - isSelect: true, - feedback: 'CODE_LANGUAGE', - }; - var $block = exp.$language = Cryptpad.createDropdown(dropdownConfig); - $block.find('button').attr('title', Messages.languageButtonTitle); - $block.find('a').click(function () { - setMode($(this).attr('data-value'), onModeChanged); - onLocal(); - }); - - if ($drawer) { $drawer.append($block); } - if (cb) { cb(); } - }; - - exp.configureTheme = function (cb) { - /* Remember the user's last choice of theme using localStorage */ - var themeKey = 'CRYPTPAD_CODE_THEME'; - var lastTheme = localStorage.getItem(themeKey) || 'default'; - - var options = []; - Themes.forEach(function (l) { - options.push({ - tag: 'a', - attributes: { - 'data-value': l.name, - 'href': '#', - }, - content: l.name // Pretty name of the language value - }); - }); - var dropdownConfig = { - text: 'Theme', // Button initial text - options: options, // Entries displayed in the menu - left: true, // Open to the left of the button - isSelect: true, - initialValue: lastTheme, - feedback: 'CODE_THEME', - }; - var $block = exp.$theme = Cryptpad.createDropdown(dropdownConfig); - $block.find('button').attr('title', Messages.themeButtonTitle); - - setTheme(lastTheme, $block); - - $block.find('a').click(function () { - var theme = $(this).attr('data-value'); - setTheme(theme, $block); - localStorage.setItem(themeKey, theme); - }); - - if ($drawer) { $drawer.append($block); } - if (cb) { cb(); } - }; - - exp.exportText = function () { - var text = editor.getValue(); - - var ext = Modes.extensionOf(exp.highlightMode); - - var title = Cryptpad.fixFileName(Title ? Title.suggestTitle('cryptpad') : "?") + (ext || '.txt'); - - Cryptpad.prompt(Messages.exportPrompt, title, function (filename) { - if (filename === null) { return; } - var blob = new Blob([text], { - type: 'text/plain;charset=utf-8' - }); - saveAs(blob, filename); - }); - }; - exp.importText = function (content, file) { - var $bar = ifrw.$('#cme_toolbox'); - var mode; - var mime = CodeMirror.findModeByMIME(file.type); - - if (!mime) { - var ext = /.+\.([^.]+)$/.exec(file.name); - if (ext[1]) { - mode = CMeditor.findModeByExtension(ext[1]); - mode = mode && mode.mode || null; - } - } else { - mode = mime && mime.mode || null; - } - - if (mode && Modes.list.some(function (o) { return o.mode === mode; })) { - setMode(mode); - $bar.find('#language-mode').val(mode); - } else { - console.log("Couldn't find a suitable highlighting mode: %s", mode); - setMode('text'); - $bar.find('#language-mode').val('text'); - } - - editor.setValue(content); - onLocal(); - }; - - var cursorToPos = function(cursor, oldText) { - var cLine = cursor.line; - var cCh = cursor.ch; - var pos = 0; - var textLines = oldText.split("\n"); - for (var line = 0; line <= cLine; line++) { - if(line < cLine) { - pos += textLines[line].length+1; - } - else if(line === cLine) { - pos += cCh; - } - } - return pos; - }; - - var posToCursor = function(position, newText) { - var cursor = { - line: 0, - ch: 0 - }; - var textLines = newText.substr(0, position).split("\n"); - cursor.line = textLines.length - 1; - cursor.ch = textLines[cursor.line].length; - return cursor; - }; - - exp.setValueAndCursor = function (oldDoc, remoteDoc, TextPatcher) { - var scroll = editor.getScrollInfo(); - //get old cursor here - var oldCursor = {}; - oldCursor.selectionStart = cursorToPos(editor.getCursor('from'), oldDoc); - oldCursor.selectionEnd = cursorToPos(editor.getCursor('to'), oldDoc); - - editor.setValue(remoteDoc); - editor.save(); - - var op = TextPatcher.diff(oldDoc, remoteDoc); - var selects = ['selectionStart', 'selectionEnd'].map(function (attr) { - return TextPatcher.transformCursor(oldCursor[attr], op); - }); - - if(selects[0] === selects[1]) { - editor.setCursor(posToCursor(selects[0], remoteDoc)); - } - else { - editor.setSelection(posToCursor(selects[0], remoteDoc), posToCursor(selects[1], remoteDoc)); - } - - editor.scrollTo(scroll.left, scroll.top); - }; - - return exp; - }; - - return module; -}); - diff --git a/www/common/common-history.js b/www/common/common-history.js index 6dea29ab7..23366f9a5 100644 --- a/www/common/common-history.js +++ b/www/common/common-history.js @@ -1,9 +1,9 @@ define([ 'jquery', - '/bower_components/chainpad-json-validator/json-ot.js', '/bower_components/chainpad-crypto/crypto.js', + '/bower_components/chainpad/chainpad.dist.js', -], function ($, JsonOT, Crypto) { +], function ($, Crypto) { var ChainPad = window.ChainPad; var History = {}; @@ -28,7 +28,7 @@ define([ return ChainPad.create({ userName: 'history', initialState: '', - transformFunction: JsonOT.validate, + patchTransformer: ChainPad.NaiveJSONStransformer, logLevel: 0, noPrune: true }); diff --git a/www/common/cryptget.js b/www/common/cryptget.js index cde78d298..a75e5c0f9 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -3,8 +3,7 @@ define([ '/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-netflux/chainpad-netflux.js', '/common/cryptpad-common.js', - '/bower_components/textpatcher/TextPatcher.js' -], function ($, Crypto, Realtime, Cryptpad, TextPatcher) { +], function ($, Crypto, Realtime, Cryptpad) { //var Messages = Cryptpad.Messages; //var noop = function () {}; var finish = function (S, err, doc) { @@ -72,9 +71,7 @@ define([ var realtime = Session.session = info.realtime; Session.network = info.network; - TextPatcher.create({ - realtime: realtime, - })(doc); + realtime.contentUpdate(doc); var to = window.setTimeout(function () { cb(new Error("Timeout")); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 8a5b38c66..2effdfef1 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -11,11 +11,9 @@ define([ '/common/common-title.js', '/common/common-metadata.js', '/common/common-messaging.js', - '/common/common-codemirror.js', '/common/common-file.js', '/file/file-crypto.js', '/common/common-realtime.js', - '/common/clipboard.js', '/common/pinpad.js', '/customize/application_config.js', @@ -23,7 +21,7 @@ define([ '/bower_components/nthen/index.js', '/bower_components/localforage/dist/localforage.min.js', ], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata, - Messaging, CodeMirror, Files, FileCrypto, Realtime, Clipboard, + Messaging, Files, FileCrypto, Realtime, Clipboard, Pinpad, AppConfig, MediaTag, Nthen, localForage) { // Configure MediaTags to use our local viewer @@ -154,9 +152,6 @@ define([ // Metadata common.createMetadata = Metadata.create; - // CodeMirror - common.createCodemirror = CodeMirror.create; - // Files common.createFileManager = function (config) { return Files.create(common, config); }; diff --git a/www/common/fsStore.js b/www/common/fsStore.js index f24409d8b..26d4e9f63 100644 --- a/www/common/fsStore.js +++ b/www/common/fsStore.js @@ -2,10 +2,9 @@ define([ 'jquery', '/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-crypto/crypto.js?v=0.1.5', - '/bower_components/textpatcher/TextPatcher.amd.js', '/common/userObject.js', '/common/migrate-user-object.js', -], function ($, Listmap, Crypto, TextPatcher, FO, Migrate) { +], function ($, Listmap, Crypto, FO, Migrate) { /* This module uses localStorage, which is synchronous, but exposes an asyncronous API. This is so that we can substitute other storage diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 342108678..7e3c6c537 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -2,9 +2,7 @@ define([ 'jquery', '/bower_components/hyperjson/hyperjson.js', '/common/toolbar3.js', - '/bower_components/chainpad-json-validator/json-ot.js', 'json.sortify', - '/bower_components/textpatcher/TextPatcher.js', '/common/cryptpad-common.js', '/bower_components/nthen/index.js', '/common/sframe-common.js', @@ -12,6 +10,7 @@ define([ '/common/common-util.js', '/common/common-thumbnail.js', '/customize/application_config.js', + '/bower_components/chainpad/chainpad.dist.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css', @@ -20,9 +19,7 @@ define([ $, Hyperjson, Toolbar, - JsonOT, JSONSortify, - TextPatcher, Cryptpad, nThen, SFCommon, @@ -31,6 +28,7 @@ define([ Thumb, AppConfig) { + var ChainPad = window.ChainPad; var SaveAs = window.saveAs; var UNINITIALIZED = 'UNINITIALIZED'; @@ -64,7 +62,6 @@ define([ var common; var cpNfInner; - var textPatcher; var readOnly; var title; var toolbar; @@ -182,10 +179,11 @@ define([ result in a feedback loop, which we call a browser fight */ // what changed? - var op = TextPatcher.diff(newContentStrNoMeta, newContent2StrNoMeta); + var ops = ChainPad.Diff.diff(newContentStrNoMeta, newContent2StrNoMeta); // log the changes - TextPatcher.log(newContentStrNoMeta, op); - var sop = JSON.stringify(TextPatcher.format(newContentStrNoMeta, op)); + console.log(newContentStrNoMeta); + console.log(ops); + var sop = JSON.stringify([ newContentStrNoMeta, ops ]); var fights = window.CryptPad_fights = window.CryptPad_fights || []; var index = fights.indexOf(sop); @@ -232,7 +230,7 @@ define([ } var contentStr = JSONSortify(content); - textPatcher(contentStr); + cpNfInner.chainpad.contentUpdate(contentStr); if (cpNfInner.chainpad.getUserDoc() !== contentStr) { console.error("realtime.getUserDoc() !== shjson"); } @@ -378,7 +376,7 @@ define([ }).nThen(function (waitFor) { cpNfInner = common.startRealtime({ // really basic operational transform - transformFunction: options.transformFunction || JsonOT.transform, + patchTransformer: options.patchTransformer || ChainPad.SmartJSONTransformer, // cryptpad debug logging (default is 1) // logLevel: 0, @@ -410,8 +408,6 @@ define([ cpNfInner.metadataMgr.onChange(checkReady); checkReady(); - textPatcher = TextPatcher.create({ realtime: cpNfInner.chainpad }); - var infiniteSpinnerModal = false; window.setInterval(function () { if (state === STATE.DISCONNECTED) { return; } diff --git a/www/common/sframe-chainpad-listmap.js b/www/common/sframe-chainpad-listmap.js index a4c63641d..3e5d9ab63 100644 --- a/www/common/sframe-chainpad-listmap.js +++ b/www/common/sframe-chainpad-listmap.js @@ -1,10 +1,10 @@ require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); define([ '/bower_components/chainpad-netflux/chainpad-netflux.js', - '/bower_components/chainpad-json-validator/json-ot.js', 'json.sortify', - '/bower_components/textpatcher/TextPatcher.js', -], function (Realtime, JsonOT, Sortify, TextPatcher) { + '/bower_components/chainpad/chainpad.dist.js' +], function (Realtime, Sortify) { + var ChainPad = window.ChainPad; var api = {}; // "Proxy" is undefined in Safari : we need to use an normal object and check if there are local // changes regurlarly. @@ -627,7 +627,7 @@ define([ userName: cfg.userName, initialState: Sortify(cfg.data), readOnly: cfg.readOnly, - transformFunction: JsonOT.transform || JsonOT.validate, + patchTransformer: ChainPad.SmartJSONTransformer, logLevel: typeof(cfg.logLevel) === 'undefined'? 0: cfg.logLevel, validateContent: function (content) { try { @@ -650,7 +650,6 @@ define([ var realtime; var proxy; - var patchText; realtimeOptions.onRemote = function () { if (initializing) { return; } @@ -667,10 +666,10 @@ define([ var onLocal = realtimeOptions.onLocal = function () { if (initializing) { return; } var strung = isFakeProxy? DeepProxy.stringifyFakeProxy(proxy): Sortify(proxy); - patchText(strung); + realtime.contentUpdate(strung); // try harder - if (realtime.getUserDoc() !== strung) { patchText(strung); } + if (realtime.getUserDoc() !== strung) { realtime.contentUpdate(strung); } // onLocal if (cfg.onLocal) { cfg.onLocal(); } @@ -688,13 +687,8 @@ define([ var ready = false; realtimeOptions.onReady = function (info) { if (ready) { return; } - // create your patcher if (realtime !== info.realtime) { realtime = rt.realtime = info.realtime; - patchText = TextPatcher.create({ - realtime: realtime, - logging: cfg.logging || false, - }); } else { console.error(realtime); } diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 0f150842c..4e2274db7 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -37,7 +37,7 @@ define([ var onReady = config.onReady || function () { }; var userName = config.userName; var initialState = config.initialState; - var transformFunction = config.transformFunction; + if (config.transformFunction) { throw new Error("transformFunction is nolonger allowed"); } var patchTransformer = config.patchTransformer; var validateContent = config.validateContent; var avgSyncMilliseconds = config.avgSyncMilliseconds; @@ -50,7 +50,6 @@ define([ var chainpad = ChainPad.create({ userName: userName, initialState: initialState, - transformFunction: transformFunction, patchTransformer: patchTransformer, validateContent: validateContent, avgSyncMilliseconds: avgSyncMilliseconds, diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js index b7037164f..e60314b7c 100644 --- a/www/common/sframe-common-codemirror.js +++ b/www/common/sframe-common-codemirror.js @@ -3,9 +3,12 @@ define([ '/common/modes.js', '/common/themes.js', '/common/cryptpad-common.js', - '/bower_components/textpatcher/TextPatcher.js', -], function ($, Modes, Themes, Cryptpad, TextPatcher) { + '/common/text-cursor.js', + + '/bower_components/chainpad/chainpad.dist.js' +], function ($, Modes, Themes, Cryptpad, TextCursor) { var module = {}; + var ChainPad = window.ChainPad; var cursorToPos = function(cursor, oldText) { var cLine = cursor.line; @@ -34,7 +37,7 @@ define([ return cursor; }; - module.setValueAndCursor = function (editor, oldDoc, remoteDoc, TextPatcher) { + module.setValueAndCursor = function (editor, oldDoc, remoteDoc) { var scroll = editor.getScrollInfo(); //get old cursor here var oldCursor = {}; @@ -44,9 +47,9 @@ define([ editor.setValue(remoteDoc); editor.save(); - var op = TextPatcher.diff(oldDoc, remoteDoc); + var ops = ChainPad.Diff.diff(oldDoc, remoteDoc); var selects = ['selectionStart', 'selectionEnd'].map(function (attr) { - return TextPatcher.transformCursor(oldCursor[attr], op); + return TextCursor.transformCursor(oldCursor[attr], ops); }); if(selects[0] === selects[1]) { @@ -295,8 +298,8 @@ define([ return { content: content }; }; - exp.setValueAndCursor = function (oldDoc, remoteDoc, TextPatcher) { - return module.setValueAndCursor(editor, oldDoc, remoteDoc, TextPatcher); + exp.setValueAndCursor = function (oldDoc, remoteDoc) { + return module.setValueAndCursor(editor, oldDoc, remoteDoc); }; ///// @@ -308,7 +311,7 @@ define([ exp.contentUpdate = function (newContent) { var oldDoc = canonicalize($textarea.val()); var remoteDoc = newContent.content; - exp.setValueAndCursor(oldDoc, remoteDoc, TextPatcher); + exp.setValueAndCursor(oldDoc, remoteDoc); }; exp.getContent = function () { diff --git a/www/common/sframe-common-history.js b/www/common/sframe-common-history.js index 9167afc5f..3c681b0e9 100644 --- a/www/common/sframe-common-history.js +++ b/www/common/sframe-common-history.js @@ -1,8 +1,7 @@ define([ 'jquery', - '/bower_components/chainpad-json-validator/json-ot.js', '/bower_components/chainpad/chainpad.dist.js', -], function ($, JsonOT) { +], function ($) { var ChainPad = window.ChainPad; var History = {}; @@ -31,7 +30,7 @@ define([ } }, initialState: '', - transformFunction: JsonOT.validate, + patchTransformer: ChainPad.NaiveJSONTransformer, logLevel: 0, noPrune: true }); diff --git a/www/common/text-cursor.js b/www/common/text-cursor.js new file mode 100644 index 000000000..e8c0f9936 --- /dev/null +++ b/www/common/text-cursor.js @@ -0,0 +1,25 @@ +define([ +], function () { + var module = { exports: {} }; + var transformCursor = function (cursor, op) { + if (!op) { return cursor; } + var pos = op.offset; + var remove = op.toRemove; + var insert = op.toInsert.length; + if (typeof cursor === 'undefined') { return; } + if (typeof remove === 'number' && pos < cursor) { + cursor -= Math.min(remove, cursor - pos); + } + if (typeof insert === 'number' && pos < cursor) { + cursor += insert; + } + return cursor; + }; + module.exports.transformCursor = function (cursor, ops) { + if (Array.isArray(ops)) { + for (var i = ops.length - 1; i >= 0; i--) { transformCursor(cursor, ops[i]); } + return; + } + transformCursor(ops); + }; +}); \ No newline at end of file diff --git a/www/contacts/inner.js b/www/contacts/inner.js index 5bd342a48..e17192b8b 100644 --- a/www/contacts/inner.js +++ b/www/contacts/inner.js @@ -2,7 +2,6 @@ define([ 'jquery', '/bower_components/chainpad-crypto/crypto.js', '/common/toolbar3.js', - '/bower_components/chainpad-json-validator/json-ot.js', '/common/cryptpad-common.js', '/bower_components/nthen/index.js', '/common/sframe-common.js', @@ -17,7 +16,6 @@ define([ $, Crypto, Toolbar, - JsonOT, Cryptpad, nThen, SFCommon, diff --git a/www/drive/inner.js b/www/drive/inner.js index 864b2038d..bec410c58 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -1,6 +1,5 @@ define([ 'jquery', - '/bower_components/textpatcher/TextPatcher.js', '/common/toolbar3.js', 'json.sortify', '/common/cryptpad-common.js', @@ -2940,12 +2939,7 @@ define([ var rt = APP.rt = Listmap.create(listmapConfig); proxy = rt.proxy; var onCreate = function (info) { - var realtime = APP.realtime = info.realtime; - - APP.patchText = TextPatcher.create({ - realtime: realtime, - logging: true, - }); + APP.realtime = info.realtime; metadataMgr = common.getMetadataMgr(); diff --git a/www/examples/board/board.js b/www/examples/board/board.js deleted file mode 100644 index eec92e681..000000000 --- a/www/examples/board/board.js +++ /dev/null @@ -1,242 +0,0 @@ -define([ - 'jquery' -],function ($) { - var Board = {}; - var proxy; - - var Uid = function (prefix) { - return function () { - return prefix + Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) - .toString(32).replace(/\./g, ''); - }; - }; - - var removeUid = function (A, e) { - var i = A.indexOf(e); - if (i === -1) { return -1; } - A.splice(i, 1); - return i; - }; - - var luid = Board.luid = Uid('l-'); // list-uid - var cuid = Board.cuid = Uid('c-'); // card uid - - var Input = Board.Input = function (opt) { - return $('', opt); - }; - - /* - populate the proxy with all the relevant fields - return boolean whether you are the first user - */ - Board.initialize = function (_proxy) { - proxy = _proxy; - var first = false; - - ['listOrder'].forEach(function (k) { - if (typeof(proxy[k]) === 'undefined') { - first = true; - proxy[k] = []; - } - }); - - ['lists', 'cards'].forEach(function (k) { - if (typeof(proxy[k]) === 'undefined') { - proxy[k] = {}; - } - }); - - return first; - }; - - /* - * a list is appended to the extant order - */ - var List = Board.List = function (id) { - if (!id) { - id = List.create(); - } - - var $input = Input({ - type: 'text', - placeholder: 'list title', - }) - .addClass('list-title') - .on('keyup change', function () { - var val = $input.val(); - proxy.lists[id].title = val; - }); - - var $cards = $('
', { - - }) - .addClass('card-holder'); - - var $new = $('', { - - }) - .addClass('add-card') - .text('add new card') - .click(function () { - // is this correct? - $cards.append(Board.Card(id)); - }); - - var $list = $('
', { - id: id, - }) - .addClass('list-column') - .append($input) - .append($cards) - .append($new); - - return $list; - }; - - /* - */ - List.create = function () { - var id = luid(); - proxy.listOrder.push(id); - proxy.lists[id] = { - title: "", - cards: [], - }; - - return id; - }; - - /* - */ - List.remove = function (id) { - var i = removeUid(proxy.listOrder, id); - if (i === -1) { - - } - }; - - /* - */ - List.move = function () { - - }; - - /* - */ - List.insert = function () { - - }; - - List.draw = function ($lists, lid) { - if (!lid) { - console.log("List Id not supplied"); - } - - - var $parent = $lists.find('#' + lid); - if (!$parent.length) { - console.log("Creating new list"); - // doesn't exist. draw it fresh - - var $list = Board.List(lid); - $lists.append($list); - - //console.log("Updating list"); - - //var $list = Board.List(lid); - var title = proxy.lists[lid].title; - - console.log(title); - - $list.find('input.list-title').val(title); - - - - return; - } - - - // else update - }; - - /* - * UI element - */ - var Card = Board.Card = function (pid) { - // pid => parent id - - var id = Card.create(pid); - - var $input = Input({ - placeholder: 'card description', - id: id, - }) - .addClass('card-title'); - - var $card = $('
', { - - }) - .addClass('card-container') - .append($input); - - return $card; - }; - - /* - * a card is instantiated within a parent list - * .create(parent) adds the relevant attributes to the data structure - * and returns the created id - */ - Card.create = function (pid) { - var id = cuid(); - - if (typeof(proxy.lists[pid]) === 'undefined') { - console.error("Trying to create card for list which does not exist"); - return id; - } - - proxy.lists[pid].cards.push(id); - proxy.cards[id] = { - // TODO what goes in a card - parent: pid, - title: "", - }; - - return id; - }; - - /* - */ - Card.move = function (/*uid, A, B*/) { - - }; - - /* - */ - Card.insert = function () { - - }; - - Card.draw = function ($lists, cid) { - if (!cid) { - console.error("card id not supplied"); - return; - } - - if (!proxy.cards[cid]) { - console.error("no such card: ", cid); - return; - } - - var card = proxy.cards[cid]; - card = card; // TODO actually draw - }; - - Board.Draw = function ($lists) { - proxy.listOrder.forEach(function (luid) { - List.draw($lists, luid); - }); - }; - - return Board; -}); diff --git a/www/examples/board/index.html b/www/examples/board/index.html deleted file mode 100644 index df0fb5312..000000000 --- a/www/examples/board/index.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - Zero Knowledge Date Picker - - - - - - - - - - - -
-
- Add List -
diff --git a/www/examples/board/main.js b/www/examples/board/main.js deleted file mode 100644 index 290f926b7..000000000 --- a/www/examples/board/main.js +++ /dev/null @@ -1,93 +0,0 @@ -define([ - 'jquery', - '/api/config', - '/customize/messages.js', - 'board.js', - '/bower_components/textpatcher/TextPatcher.js', - '/bower_components/chainpad-listmap/chainpad-listmap.js', - '/bower_components/chainpad-crypto/crypto.js', - '/common/cryptpad-common.js', - //'/common/visible.js', - //'/common/notify.js', - '/bower_components/file-saver/FileSaver.min.js' -], function ($, Config, Messages, Board, TextPatcher, Listmap, Crypto, Cryptpad /*, Visible, Notify*/) { - - // var saveAs = window.saveAs; - - Cryptpad.styleAlerts(); - console.log("Initializing your realtime session..."); - - var secret = Cryptpad.getSecrets(); - - var module = window.APP = { - Board: Board, - }; - -/* - var unnotify = function () { - if (!(module.tabNotification && - typeof(module.tabNotification.cancel) === 'function')) { return; } - module.tabNotification.cancel(); - }; - var notify = function () { - if (!(Visible.isSupported() && !Visible.currently())) { return; } - unnotify(); - module.tabNotification = Notify.tab(1000, 10); - }; -*/ - - var setEditable = function (bool) { - bool = bool; - }; - - setEditable(false); - - var $lists = $('#lists'); - - $('#create-list').click(function () { - Board.List.draw($lists); - }); - - var firstUser = function () { - Cryptpad.log("You are the first user to visit this board"); - }; - - var whenReady = function () { - var rt = module.rt; - var proxy = rt.proxy; - - var first = Board.initialize(proxy); - - //var board = module.board = Board.create(proxy); - - Board.Draw($lists); - - if (first) { firstUser(); } - }; - - var config = { - websocketURL: Config.websocketURL, - channel: secret.channel, - data: {}, - crypto: Crypto.createEncryptor(secret.key), - }; - - Cryptpad.ready(function () { - var rt = module.rt = Listmap.create(config); - var proxy = rt.proxy; - proxy - .on('create', function (info) { - module.realtime = info.realtime; - window.location.hash = info.channel + secret.key; - }) - .on('ready', function () { - Cryptpad.log("Ready!"); - whenReady({ - - }); - }) - .on('disconnect', function () { - Cryptpad.warn("Disconnected!"); - }); - }); -}); diff --git a/www/examples/form/index.html b/www/examples/form/index.html deleted file mode 100644 index b3ceb3a10..000000000 --- a/www/examples/form/index.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - -
- One - Two - Three
- - Checkbox One - Checkbox Two
- -
-
- - - Number
- - Ranges
- - Dropdowns
- - - -
- -
- - - diff --git a/www/examples/form/main.js b/www/examples/form/main.js deleted file mode 100644 index 7ffb1c0ec..000000000 --- a/www/examples/form/main.js +++ /dev/null @@ -1,222 +0,0 @@ -define([ - 'jquery', - '/api/config', - '/bower_components/chainpad-netflux/chainpad-netflux.js', - '/bower_components/chainpad-crypto/crypto.js', - '/bower_components/textpatcher/TextPatcher.amd.js', - 'json.sortify', - 'ula.js', - '/bower_components/chainpad-json-validator/json-ot.js', - '/common/cryptpad-common.js' -], function ($, Config, Realtime, Crypto, TextPatcher, Sortify, Formula, JsonOT, Cryptpad) { - - var secret = Cryptpad.getSecrets(); - - var module = window.APP = { - TextPatcher: TextPatcher, - Sortify: Sortify, - Formula: Formula, - }; - var initializing = true; - - var uid = module.uid = Formula.uid; - - var getInputType = Formula.getInputType; - var $elements = module.elements = $('input, select, textarea'); - - var eventsByType = Formula.eventsByType; - - var Map = module.Map = {}; - - var UI = module.UI = { - ids: [], - each: function (f) { - UI.ids.forEach(function (id, i, list) { - if (!UI[id]) { return; } - f(UI[id], i, list); - }); - }, - add: function (id, ui) { - if (UI.ids.indexOf(id) === -1) { - UI.ids.push(id); - - UI[id] = ui; - return true; - } else { - // it already exists - - return false; - } - }, - remove: function (id) { - delete UI[id]; - var idx = UI.ids.indexOf(id); - if (idx > -1) { - UI.ids.splice(idx, 1); - return true; - } - } - }; - - var cursorTypes = ['textarea', 'password', 'text']; - - var canonicalize = function (text) { return text.replace(/\r\n/g, '\n'); }; - $elements.each(function (index, element) { - var $this = $(this); - - var id = uid(); - var type = getInputType($this); - - // ignore hidden inputs, submit inputs, and buttons - if (['button', 'submit', 'hidden'].indexOf(type) !== -1) { - return; - } - - $this // give each element a uid - .data('rtform-uid', id) - // get its type - .data('rt-ui-type', type); - - var component = { - id: id, - $: $this, - element: element, - type: type, - preserveCursor: cursorTypes.indexOf(type) !== -1, - name: $this.prop('name'), - }; - - UI.add(id, component); - - component.value = (function () { - var checker = ['radio', 'checkbox'].indexOf(type) !== -1; - - if (checker) { - return function (content) { - return typeof content !== 'undefined'? - $this.prop('checked', !!content): - $this.prop('checked'); - }; - } else { - return function (content) { - return typeof content !== 'undefined' ? - $this.val(content): - typeof($this.val()) === 'string'? canonicalize($this.val()): $this.val(); - }; - } - }()); - - var update = component.update = function () { Map[id] = component.value(); }; - update(); - }); - - var config = module.config = { - initialState: Sortify(Map) || '{}', - websocketURL: Config.websocketURL, - userName: Crypto.rand64(8), - channel: secret.channel, - crypto: Crypto.createEncryptor(secret.key), - transformFunction: JsonOT.validate - }; - - var setEditable = module.setEditable = function (bool) { - /* (dis)allow editing */ - $elements.each(function () { - $(this).attr('disabled', !bool); - }); - }; - - setEditable(false); - - config.onInit = function (info) { - var realtime = module.realtime = info.realtime; - window.location.hash = info.channel + secret.key; - - // create your patcher - module.patchText = TextPatcher.create({ - realtime: realtime, - logging: true, - }); - }; - - var readValues = function () { - UI.each(function (ui) { - Map[ui.id] = ui.value(); - }); - }; - - var onLocal = config.onLocal = function () { - if (initializing) { return; } - /* serialize local changes */ - readValues(); - module.patchText(Sortify(Map)); - }; - - var updateValues = function () { - var userDoc = module.realtime.getUserDoc(); - var parsed = JSON.parse(userDoc); - - console.log(userDoc); - - // flush received values to the map - // but only if you don't have them locally - // this *shouldn't* break cursors - Object.keys(parsed).forEach(function (key) { - if (UI.ids.indexOf(key) === -1) { Map[key] = parsed[key]; } - }); - - UI.each(function (ui) { - var newval = parsed[ui.id]; - var oldval = ui.value(); - - if (typeof(newval) === 'undefined') { return; } - if (newval === oldval) { return; } - - var op; - var selects; - var element = ui.element; - if (ui.preserveCursor) { - op = TextPatcher.diff(oldval, newval); - selects = ['selectionStart', 'selectionEnd'].map(function (attr) { - return TextPatcher.transformCursor(element[attr], op); - }); - } - - ui.value(newval); - ui.update(); - - if (op && ui.preserveCursor) { - //console.log(selects); - element.selectionStart = selects[0]; - element.selectionEnd = selects[1]; - } - }); - }; - - config.onRemote = function () { - if (initializing) { return; } - /* integrate remote changes */ - updateValues(); - }; - - config.onReady = function () { - updateValues(); - - console.log("READY"); - setEditable(true); - initializing = false; - }; - - config.onAbort = function () { - window.alert("Network Connection Lost"); - }; - - Realtime.start(config); - - UI.each(function (ui) { - var type = ui.type; - var events = eventsByType[type]; - ui.$.on(events, onLocal); - }); - -}); diff --git a/www/examples/form/types.md b/www/examples/form/types.md deleted file mode 100644 index ab73d3bfa..000000000 --- a/www/examples/form/types.md +++ /dev/null @@ -1,14 +0,0 @@ - -```Javascript -/* elements that we need to listen to */ -/* - * text => $(text).val() - * password => $(password).val() - * radio => $(radio).prop('checked') - * checkbox => $(checkbox).prop('checked') - * number => $(number).val() // returns string, no default - * range => $(range).val() - * select => $(select).val() - * textarea => $(textarea).val() -*/ -``` diff --git a/www/examples/form/ula.js b/www/examples/form/ula.js deleted file mode 100644 index cc2d0763f..000000000 --- a/www/examples/form/ula.js +++ /dev/null @@ -1,25 +0,0 @@ -define([], function () { - var ula = {}; - - ula.uid = (function () { - var i = 0; - var prefix = 'rt_'; - return function () { return prefix + i++; }; - }()); - - ula.getInputType = function ($el) { return $el[0].type; }; - - ula.eventsByType = { - text: 'change keyup', - password: 'change keyup', - radio: 'change click', - checkbox: 'change click', - number: 'change', - range: 'keyup change', - 'select-one': 'change', - 'select-multiple': 'change', - textarea: 'change keyup', - }; - - return ula; -}); diff --git a/www/examples/style/index.html b/www/examples/style/index.html deleted file mode 100644 index 65f63ad88..000000000 --- a/www/examples/style/index.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - Edit this document's style - -

HTML Ipsum Presents

- -

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

- -

Header Level 2

- -
    -
  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. -
  3. Aliquam tincidunt mauris eu risus.
  4. -
- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

- -

Header Level 3

- -
    -
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • -
  • Aliquam tincidunt mauris eu risus.
  • -
- -

-#header h1 a {
-    display: block;
-    width: 300px;
-    height: 80px;
-}
-
- - - - - - - - - - - - - - - - - - -
th 1th 2
onetwo
threefour
- - - diff --git a/www/examples/style/main.js b/www/examples/style/main.js deleted file mode 100644 index 12b2e4840..000000000 --- a/www/examples/style/main.js +++ /dev/null @@ -1,75 +0,0 @@ -define([ - 'jquery', - '/api/config', - '/bower_components/chainpad-netflux/chainpad-netflux.js', - '/bower_components/chainpad-crypto/crypto.js', - '/bower_components/textpatcher/TextPatcher.amd.js', - '/common/cryptpad-common.js' -], function ($, Config, Realtime, Crypto, TextPatcher, Cryptpad) { - // TODO consider adding support for less.js - - var $style = $('style').first(), - $edit = $('#edit'); - - var module = window.APP = {}; - - var secret = Cryptpad.getSecrets(); - var config = { - websocketURL: Config.websocketURL, - channel: secret.channel, - crypto: Crypto.createEncryptor(secret.key), - }; - - var lazyDraw = (function () { - var to, - delay = 500; - return function (content) { - if (to) { clearTimeout(to); } - to = setTimeout(function () { - $style.text(content); - },delay); - }; - }()); - - var draw = function (content) { lazyDraw(content); }; - - var initializing = true; - - config.onInit = function (info) { - window.location.hash = info.channel + secret.key; - var realtime = module.realtime = info.realtime; - module.patchText = TextPatcher.create({ - realtime: realtime, - logging: true, - }); - - $(window).on('hashchange', function() { - window.location.reload(); - }); - }; - - config.onReady = function () { - var userDoc = module.realtime.getUserDoc(); - draw(userDoc); - console.log("Ready"); - initializing = false; - }; - - config.onRemote = function () { - draw(module.realtime.getUserDoc()); - }; - - config.onAbort = function () { - // notify the user of the abort - window.alert("Network Connection Lost"); - }; - - config.onLocal = function () { - // nope - }; - - - $edit.attr('href', '/examples/text/'+ window.location.hash); - - Realtime.start(config); -}); diff --git a/www/examples/text/index.html b/www/examples/text/index.html deleted file mode 100644 index b22cb7321..000000000 --- a/www/examples/text/index.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - diff --git a/www/examples/text/main.js b/www/examples/text/main.js deleted file mode 100644 index e4637b681..000000000 --- a/www/examples/text/main.js +++ /dev/null @@ -1,97 +0,0 @@ -define([ - 'jquery', - '/api/config', - '/bower_components/chainpad-netflux/chainpad-netflux.js', - '/bower_components/chainpad-crypto/crypto.js', - '/bower_components/textpatcher/TextPatcher.amd.js', - '/common/cryptpad-common.js' -], function ($, Config, Realtime, Crypto, TextPatcher, Cryptpad) { - - var secret = Cryptpad.getSecrets(); - if (!secret.keys) { - secret.keys = secret.key; - } - - var module = window.APP = { - TextPatcher: TextPatcher - }; - - var initializing = true; - var $textarea = $('textarea'); - - var config = module.config = { - initialState: '', - websocketURL: Config.websocketURL, - validateKey: secret.keys.validateKey || undefined, - channel: secret.channel, - crypto: Crypto.createEncryptor(secret.keys), - }; - - var setEditable = function (bool) { $textarea.attr('disabled', !bool); }; - var canonicalize = function (text) { return text.replace(/\r\n/g, '\n'); }; - - setEditable(false); - - config.onInit = function (info) { - var editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); - Cryptpad.replaceHash(editHash); - $(window).on('hashchange', function() { - window.location.reload(); - }); - }; - - config.onRemote = function () { - if (initializing) { return; } - var userDoc = module.realtime.getUserDoc(); - var content = canonicalize($textarea.val()); - - var op = TextPatcher.diff(content, userDoc); - var elem = $textarea[0]; - - var selects = ['selectionStart', 'selectionEnd'].map(function (attr) { - return TextPatcher.transformCursor(elem[attr], op); - }); - - $textarea.val(userDoc); - elem.selectionStart = selects[0]; - elem.selectionEnd = selects[1]; - }; - - var onLocal = config.onLocal = function () { - if (initializing) { return; } - module.patchText(canonicalize($textarea.val())); - }; - - config.onReady = function (info) { - var realtime = module.realtime = info.realtime; - module.patchText = TextPatcher.create({ - realtime: realtime - }); - - $textarea.val(realtime.getUserDoc()); - - setEditable(true); - initializing = false; - }; - - config.onAbort = function () { - setEditable(false); - window.alert("Server Connection Lost"); - }; - - config.onConnectionChange = function (info) { - if (info.state) { - initializing = true; - } else { - setEditable(false); - window.alert("Server Connection Lost. Trying to reconnect..."); - } - }; - - Realtime.start(config); - - ['cut', 'paste', 'change', 'keyup', 'keydown', 'select', 'textInput'] - .forEach(function (evt) { - $textarea.on(evt, onLocal); - }); -}); diff --git a/www/filepicker/inner.js b/www/filepicker/inner.js index 292ba32dc..f56ac559c 100644 --- a/www/filepicker/inner.js +++ b/www/filepicker/inner.js @@ -1,8 +1,6 @@ define([ 'jquery', '/bower_components/chainpad-crypto/crypto.js', - '/bower_components/textpatcher/TextPatcher.js', - '/bower_components/chainpad-json-validator/json-ot.js', '/common/cryptpad-common.js', '/bower_components/nthen/index.js', '/common/sframe-common.js', @@ -14,8 +12,6 @@ define([ ], function ( $, Crypto, - TextPatcher, - JsonOT, Cryptpad, nThen, SFCommon, diff --git a/www/pad/inner.js b/www/pad/inner.js index 2541f8ff0..fa605a44d 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -30,9 +30,9 @@ define([ '/api/config', '/common/common-hash.js', '/common/common-util.js', - '/bower_components/chainpad-json-validator/json-ot.js', '/bower_components/diff-dom/diffDOM.js', + '/bower_components/chainpad/chainpad.dist.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css', @@ -49,10 +49,10 @@ define([ MediaTag, ApiConfig, Hash, - Util, - JsonOT) + Util) { var DiffDom = window.diffDOM; + var ChainPad = window.ChainPad; var slice = function (coll) { return Array.prototype.slice.call(coll); @@ -555,7 +555,7 @@ define([ Framework.create({ toolbarContainer: '#cke_1_toolbox', contentContainer: '#cke_1_contents', - transformFunction: JsonOT.validate, + patchTransformer: ChainPad.NaiveJSONTransformer, thumbnail: { getContainer: function () { return $('iframe').contents().find('html')[0]; }, filter: function (el, before) { diff --git a/www/poll/inner.js b/www/poll/inner.js index ad7752a3d..be3e83ddd 100644 --- a/www/poll/inner.js +++ b/www/poll/inner.js @@ -1,6 +1,5 @@ define([ 'jquery', - '/bower_components/textpatcher/TextPatcher.js', '/common/toolbar3.js', '/common/cryptpad-common.js', '/common/common-util.js', @@ -16,21 +15,20 @@ define([ '/common/sframe-common-codemirror.js', '/common/sframe-common-interface.js', '/common/common-thumbnail.js', - 'cm/lib/codemirror', + 'cm/addon/display/placeholder', 'cm/mode/markdown/markdown', 'css!cm/lib/codemirror.css', - '/bower_components/file-saver/FileSaver.min.js', + '/bower_components/chainpad/chainpad.dist.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/customize/src/less2/main.less', ], function ( $, - TextPatcher, Toolbar, Cryptpad, Util, @@ -50,6 +48,7 @@ define([ { var Messages = Cryptpad.Messages; var saveAs = window.saveAs; + var ChainPad = window.ChainPad; var APP = window.APP = { unlocked: { @@ -1069,10 +1068,6 @@ define([ if (APP.realtime !== info.realtime) { APP.realtime = info.realtime; - APP.patchText = TextPatcher.create({ - realtime: info.realtime, - logging: true, - }); } metadataMgr = common.getMetadataMgr(); diff --git a/www/poll/render.js b/www/poll/render.js index afd119909..8eb3f1be5 100644 --- a/www/poll/render.js +++ b/www/poll/render.js @@ -2,10 +2,13 @@ define([ //'/common/cryptpad-common.js', 'jquery', '/bower_components/hyperjson/hyperjson.js', - '/bower_components/textpatcher/TextPatcher.js', + '/common/text-cursor.js', + '/bower_components/diff-dom/diffDOM.js', -], function ($, Hyperjson, TextPatcher) { + '/bower_components/chainpad/chainpad.dist.js' +], function ($, Hyperjson, TextCursor) { var DiffDOM = window.diffDOM; + var ChainPad = window.ChainPad; var Example = { metadata: { @@ -400,10 +403,10 @@ var Renderer = function (Cryptpad, APP) { var o = info.oldValue || ''; var n = info.newValue || ''; - var op = TextPatcher.diff(o, n); + var ops = ChainPad.Diff.diff(o, n); info.selection = ['selectionStart', 'selectionEnd'].map(function (attr) { - return TextPatcher.transformCursor(element[attr], op); + return TextCursor.transformCursor(element[attr], ops); }); } }; diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js index e52cb6828..a70f1eaa3 100644 --- a/www/whiteboard/inner.js +++ b/www/whiteboard/inner.js @@ -1,10 +1,8 @@ define([ 'jquery', '/bower_components/chainpad-crypto/crypto.js', - '/bower_components/textpatcher/TextPatcher.js', '/common/toolbar3.js', 'json.sortify', - '/bower_components/chainpad-json-validator/json-ot.js', '/common/cryptpad-common.js', '/common/common-util.js', '/common/cryptget.js', @@ -14,13 +12,13 @@ define([ '/api/config', '/common/common-realtime.js', '/customize/pages.js', - '/customize/application_config.js', '/common/common-thumbnail.js', '/whiteboard/colors.js', '/bower_components/secure-fabric.js/dist/fabric.min.js', '/bower_components/file-saver/FileSaver.min.js', + '/bower_components/chainpad/chainpad.dist.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css', @@ -28,10 +26,8 @@ define([ ], function ( $, Crypto, - TextPatcher, Toolbar, JSONSortify, - JsonOT, Cryptpad, Util, Cryptget, @@ -47,6 +43,7 @@ define([ { var saveAs = window.saveAs; var Messages = Cryptpad.Messages; + var ChainPad = window.ChainPad; var APP = window.APP = { Cryptpad: Cryptpad, @@ -254,7 +251,7 @@ define([ config = { readOnly: readOnly, - transformFunction: JsonOT.validate, + patchTransformer: ChainPad.NaiveJSONTransformer, // cryptpad debug logging (default is 1) // logLevel: 0, validateContent: function (content) { @@ -356,7 +353,7 @@ define([ var content = stringifyInner(canvas.toDatalessJSON()); - APP.patchText(content); + APP.realtime.contentUpdate(content); }; var addImageToCanvas = function (img) { @@ -516,10 +513,6 @@ define([ config.onReady = function (info) { if (APP.realtime !== info.realtime) { var realtime = APP.realtime = info.realtime; - APP.patchText = TextPatcher.create({ - realtime: realtime, - //logging: true - }); } var userDoc = APP.realtime.getUserDoc(); From 7b62a8042ef54757a89fb30fdfc6deb0e3a34a63 Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Thu, 9 Nov 2017 17:07:04 +0100 Subject: [PATCH 02/11] Remove TextPatcher and begin to include ChainPad directly using requirejs instead of sniffing off the window --- www/common/common-history.js | 4 +-- www/common/cryptpad-common.js | 28 ++++++++++++++++++--- www/common/sframe-app-framework.js | 4 +-- www/common/sframe-chainpad-listmap.js | 3 +-- www/common/sframe-chainpad-netflux-inner.js | 3 +-- www/common/sframe-common-codemirror.js | 4 +-- www/common/sframe-common-history.js | 3 +-- www/common/text-cursor.js | 1 + www/drive/inner.js | 1 - www/pad/inner.js | 6 ++--- www/poll/inner.js | 6 ++--- www/poll/render.js | 7 +++--- www/whiteboard/inner.js | 6 ++--- 13 files changed, 44 insertions(+), 32 deletions(-) diff --git a/www/common/common-history.js b/www/common/common-history.js index 23366f9a5..387f349fc 100644 --- a/www/common/common-history.js +++ b/www/common/common-history.js @@ -1,10 +1,8 @@ define([ 'jquery', '/bower_components/chainpad-crypto/crypto.js', - '/bower_components/chainpad/chainpad.dist.js', -], function ($, Crypto) { - var ChainPad = window.ChainPad; +], function ($, Crypto, ChainPad) { var History = {}; var getStates = function (rt) { diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 5b850723c..96043ecd2 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -15,16 +15,36 @@ define([ '/file/file-crypto.js', '/common/common-realtime.js', '/common/common-language.js', - '/common/clipboard.js', '/common/pinpad.js', '/customize/application_config.js', '/common/media-tag.js', '/bower_components/nthen/index.js', '/bower_components/localforage/dist/localforage.min.js', -], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata, - Messaging, Files, FileCrypto, Realtime, Language, Clipboard, - Pinpad, AppConfig, MediaTag, Nthen, localForage) { +], function ( + $, + Config, + Messages, + Store, + Util, + Hash, + UI, + History, + UserList, + Title, + Metadata, + Messaging, + Files, + FileCrypto, + Realtime, + Language, + Clipboard, + Pinpad, + AppConfig, + MediaTag, + Nthen, + localForage) +{ // Configure MediaTags to use our local viewer if (MediaTag && MediaTag.PdfPlugin) { diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 7e3c6c537..7f055cc32 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -26,9 +26,9 @@ define([ Messages, Util, Thumb, - AppConfig) + AppConfig, + ChainPad) { - var ChainPad = window.ChainPad; var SaveAs = window.saveAs; var UNINITIALIZED = 'UNINITIALIZED'; diff --git a/www/common/sframe-chainpad-listmap.js b/www/common/sframe-chainpad-listmap.js index 3e5d9ab63..ce6c04a83 100644 --- a/www/common/sframe-chainpad-listmap.js +++ b/www/common/sframe-chainpad-listmap.js @@ -3,8 +3,7 @@ define([ '/bower_components/chainpad-netflux/chainpad-netflux.js', 'json.sortify', '/bower_components/chainpad/chainpad.dist.js' -], function (Realtime, Sortify) { - var ChainPad = window.ChainPad; +], function (Realtime, Sortify, ChainPad) { var api = {}; // "Proxy" is undefined in Safari : we need to use an normal object and check if there are local // changes regurlarly. diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 4e2274db7..3e6ea43d2 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -18,8 +18,7 @@ define([ '/common/common-util.js', '/customize/application_config.js', '/bower_components/chainpad/chainpad.dist.js' -], function (Util, AppConfig) { - var ChainPad = window.ChainPad; +], function (Util, AppConfig, ChainPad) { var module = { exports: {} }; var badStateTimeout = typeof(AppConfig.badStateTimeout) === 'number' ? diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js index e60314b7c..1f7f73f5f 100644 --- a/www/common/sframe-common-codemirror.js +++ b/www/common/sframe-common-codemirror.js @@ -4,11 +4,9 @@ define([ '/common/themes.js', '/common/cryptpad-common.js', '/common/text-cursor.js', - '/bower_components/chainpad/chainpad.dist.js' -], function ($, Modes, Themes, Cryptpad, TextCursor) { +], function ($, Modes, Themes, Cryptpad, TextCursor, ChainPad) { var module = {}; - var ChainPad = window.ChainPad; var cursorToPos = function(cursor, oldText) { var cLine = cursor.line; diff --git a/www/common/sframe-common-history.js b/www/common/sframe-common-history.js index 3c681b0e9..204b856df 100644 --- a/www/common/sframe-common-history.js +++ b/www/common/sframe-common-history.js @@ -1,8 +1,7 @@ define([ 'jquery', '/bower_components/chainpad/chainpad.dist.js', -], function ($) { - var ChainPad = window.ChainPad; +], function ($, ChainPad) { var History = {}; var getStates = function (rt) { diff --git a/www/common/text-cursor.js b/www/common/text-cursor.js index e8c0f9936..eaf33d0b3 100644 --- a/www/common/text-cursor.js +++ b/www/common/text-cursor.js @@ -22,4 +22,5 @@ define([ } transformCursor(ops); }; + return module.exports; }); \ No newline at end of file diff --git a/www/drive/inner.js b/www/drive/inner.js index bec410c58..66bcc95b7 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -17,7 +17,6 @@ define([ 'less!/customize/src/less2/main.less', ], function ( $, - TextPatcher, Toolbar, JSONSortify, Cryptpad, diff --git a/www/pad/inner.js b/www/pad/inner.js index fa605a44d..f048ebdef 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -30,9 +30,9 @@ define([ '/api/config', '/common/common-hash.js', '/common/common-util.js', + '/bower_components/chainpad/chainpad.dist.js', '/bower_components/diff-dom/diffDOM.js', - '/bower_components/chainpad/chainpad.dist.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css', @@ -49,10 +49,10 @@ define([ MediaTag, ApiConfig, Hash, - Util) + Util, + ChainPad) { var DiffDom = window.diffDOM; - var ChainPad = window.ChainPad; var slice = function (coll) { return Array.prototype.slice.call(coll); diff --git a/www/poll/inner.js b/www/poll/inner.js index be3e83ddd..53bba7ee0 100644 --- a/www/poll/inner.js +++ b/www/poll/inner.js @@ -15,6 +15,7 @@ define([ '/common/sframe-common-codemirror.js', '/common/sframe-common-interface.js', '/common/common-thumbnail.js', + '/bower_components/chainpad/chainpad.dist.js', 'cm/lib/codemirror', 'cm/addon/display/placeholder', @@ -22,7 +23,6 @@ define([ 'css!cm/lib/codemirror.css', '/bower_components/file-saver/FileSaver.min.js', - '/bower_components/chainpad/chainpad.dist.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css', @@ -44,11 +44,11 @@ define([ SframeCM, SFUI, Thumb, + ChainPad, CMeditor) { var Messages = Cryptpad.Messages; var saveAs = window.saveAs; - var ChainPad = window.ChainPad; var APP = window.APP = { unlocked: { @@ -675,7 +675,7 @@ define([ }; var updateDescription = function (old, n) { var o = APP.editor.getValue(); - SframeCM.setValueAndCursor(APP.editor, o, n, TextPatcher); + SframeCM.setValueAndCursor(APP.editor, o, n); updatePublishedDescription(); common.notify(); }; diff --git a/www/poll/render.js b/www/poll/render.js index 8eb3f1be5..460f8628b 100644 --- a/www/poll/render.js +++ b/www/poll/render.js @@ -3,12 +3,11 @@ define([ 'jquery', '/bower_components/hyperjson/hyperjson.js', '/common/text-cursor.js', + '/bower_components/chainpad/chainpad.dist.js', - '/bower_components/diff-dom/diffDOM.js', - '/bower_components/chainpad/chainpad.dist.js' -], function ($, Hyperjson, TextCursor) { + '/bower_components/diff-dom/diffDOM.js' +], function ($, Hyperjson, TextCursor, ChainPad) { var DiffDOM = window.diffDOM; - var ChainPad = window.ChainPad; var Example = { metadata: { diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js index a70f1eaa3..f9fcfa1e8 100644 --- a/www/whiteboard/inner.js +++ b/www/whiteboard/inner.js @@ -15,10 +15,10 @@ define([ '/customize/application_config.js', '/common/common-thumbnail.js', '/whiteboard/colors.js', + '/bower_components/chainpad/chainpad.dist.js', '/bower_components/secure-fabric.js/dist/fabric.min.js', '/bower_components/file-saver/FileSaver.min.js', - '/bower_components/chainpad/chainpad.dist.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css', @@ -39,11 +39,11 @@ define([ Pages, AppConfig, Thumb, - Colors) + Colors, + ChainPad) { var saveAs = window.saveAs; var Messages = Cryptpad.Messages; - var ChainPad = window.ChainPad; var APP = window.APP = { Cryptpad: Cryptpad, From eaa7b6e8e5ed4e7a6abc229233d460d32dfb3330 Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Sat, 11 Nov 2017 01:26:54 +0100 Subject: [PATCH 03/11] Do content checks on the drive so if it gets corrupted, the patch will be rejected --- www/common/fsStore.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/www/common/fsStore.js b/www/common/fsStore.js index 1603581d2..6db9bd359 100644 --- a/www/common/fsStore.js +++ b/www/common/fsStore.js @@ -297,6 +297,15 @@ define([ crypto: Crypto.createEncryptor(secret.keys), userName: 'fs', logLevel: 1, + validateContent: function (content) { + try { + JSON.parse(content); + return true; + } catch (e) { + console.error("Failed to parse, rejecting patch"); + return false; + } + }, }; var exp = {}; From 50da6b3b2e8407e86c7e221af68337b65ead1e86 Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Sat, 11 Nov 2017 01:34:20 +0100 Subject: [PATCH 04/11] Needs to be handled in chainpad-listmap Revert "Do content checks on the drive so if it gets corrupted, the patch will be rejected" This reverts commit eaa7b6e8e5ed4e7a6abc229233d460d32dfb3330. --- www/common/fsStore.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/www/common/fsStore.js b/www/common/fsStore.js index 6db9bd359..1603581d2 100644 --- a/www/common/fsStore.js +++ b/www/common/fsStore.js @@ -297,15 +297,6 @@ define([ crypto: Crypto.createEncryptor(secret.keys), userName: 'fs', logLevel: 1, - validateContent: function (content) { - try { - JSON.parse(content); - return true; - } catch (e) { - console.error("Failed to parse, rejecting patch"); - return false; - } - }, }; var exp = {}; From 2e0021478333bb5a8a8ed76c42c2844fce1e98f0 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 23 Nov 2017 16:57:01 +0100 Subject: [PATCH 05/11] drop unreachable code --- www/pad/wysiwygarea-plugin.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/www/pad/wysiwygarea-plugin.js b/www/pad/wysiwygarea-plugin.js index 7a0d08788..0836428e9 100644 --- a/www/pad/wysiwygarea-plugin.js +++ b/www/pad/wysiwygarea-plugin.js @@ -487,16 +487,6 @@ define(['/api/config'], function (ApiConfig) { CKEDITOR.tools.callFunction(fw._.frameLoadedHandler, _iframe.contentWindow); }, 10); return; - - // Work around Firefox bug - error prune when called from XUL (http://dev.ckeditor.com/ticket/320), - // defer it thanks to the async nature of this method. - try { - doc.write( data ); - } catch ( e ) { - setTimeout( function() { - doc.write( data ); - }, 0 ); - } } }, @@ -737,4 +727,4 @@ CKEDITOR.config.disableNativeSpellChecker = true; * @member CKEDITOR.editor * @param {CKEDITOR.editor} editor This editor instance. * @param {CKEDITOR.dom.element} data The element being added. - */ \ No newline at end of file + */ From 80978377f254a7fad6722c6c57f74552643ca87c Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 24 Nov 2017 10:03:45 +0100 Subject: [PATCH 06/11] return transformed cursor value --- www/common/text-cursor.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/www/common/text-cursor.js b/www/common/text-cursor.js index eaf33d0b3..cd8f52213 100644 --- a/www/common/text-cursor.js +++ b/www/common/text-cursor.js @@ -3,6 +3,7 @@ define([ var module = { exports: {} }; var transformCursor = function (cursor, op) { if (!op) { return cursor; } + var pos = op.offset; var remove = op.toRemove; var insert = op.toInsert.length; @@ -17,10 +18,12 @@ define([ }; module.exports.transformCursor = function (cursor, ops) { if (Array.isArray(ops)) { - for (var i = ops.length - 1; i >= 0; i--) { transformCursor(cursor, ops[i]); } - return; + for (var i = ops.length - 1; i >= 0; i--) { + cursor = transformCursor(cursor, ops[i]); + } + return cursor; } - transformCursor(ops); + return transformCursor(ops); }; return module.exports; -}); \ No newline at end of file +}); From 661db129b282d2b9ea1de067957d71392893b97b Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 24 Nov 2017 10:32:58 +0100 Subject: [PATCH 07/11] lint compliance --- www/common/cryptget.js | 2 +- www/common/sframe-common-history.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/www/common/cryptget.js b/www/common/cryptget.js index 10a1ab1ee..ad59b89ae 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -5,7 +5,7 @@ define([ '/common/common-hash.js', '/common/common-realtime.js', //'/bower_components/textpatcher/TextPatcher.js' -], function (Crypto, CPNetflux, /* Cryptpad,*/ Util, Hash, Realtime) { +], function (Crypto, CPNetflux, Cryptpad, Util, Hash, Realtime) { var finish = function (S, err, doc) { if (S.done) { return; } S.cb(err, doc); diff --git a/www/common/sframe-common-history.js b/www/common/sframe-common-history.js index 02ccce93e..58a30782b 100644 --- a/www/common/sframe-common-history.js +++ b/www/common/sframe-common-history.js @@ -4,8 +4,8 @@ define([ //'/bower_components/chainpad-json-validator/json-ot.js', '/bower_components/chainpad/chainpad.dist.js', -], function ($, UI, JsonOT) { - var ChainPad = window.ChainPad; +], function ($, UI, ChainPad /* JsonOT */) { + //var ChainPad = window.ChainPad; var History = {}; var getStates = function (rt) { From 56102d3689feec03fb772697ca54f7fa5e5fac4b Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 24 Nov 2017 13:40:21 +0100 Subject: [PATCH 08/11] use unified listmap --- www/common/fsStore.js | 1 + www/common/sframe-chainpad-listmap.js | 744 -------------------------- www/drive/inner.js | 2 +- www/poll/inner.js | 2 +- www/todo/inner.js | 2 +- 5 files changed, 4 insertions(+), 747 deletions(-) delete mode 100644 www/common/sframe-chainpad-listmap.js diff --git a/www/common/fsStore.js b/www/common/fsStore.js index 6f35377d4..a635ecd28 100644 --- a/www/common/fsStore.js +++ b/www/common/fsStore.js @@ -302,6 +302,7 @@ define([ userName: 'fs', logLevel: 1, ChainPad: ChainPad, + classic: true, }; var exp = {}; diff --git a/www/common/sframe-chainpad-listmap.js b/www/common/sframe-chainpad-listmap.js deleted file mode 100644 index ce6c04a83..000000000 --- a/www/common/sframe-chainpad-listmap.js +++ /dev/null @@ -1,744 +0,0 @@ -require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); -define([ - '/bower_components/chainpad-netflux/chainpad-netflux.js', - 'json.sortify', - '/bower_components/chainpad/chainpad.dist.js' -], function (Realtime, Sortify, ChainPad) { - var api = {}; - // "Proxy" is undefined in Safari : we need to use an normal object and check if there are local - // changes regurlarly. - var isFakeProxy = typeof window.Proxy === "undefined"; - - var DeepProxy = api.DeepProxy = (function () { - var deepProxy = {}; - - var isArray = deepProxy.isArray = Array.isArray || function (obj) { - return Object.toString(obj) === '[object Array]'; - }; - - /* Arrays and nulls both register as 'object' when using native typeof - we need to distinguish them as their own types, so use this instead. */ - var type = deepProxy.type = function (dat) { - return dat === null? 'null': isArray(dat)?'array': typeof(dat); - }; - - /* Check if an (sub-)element in an object or an array and should be a proxy. - If the browser doesn't support Proxy, return false */ - var isProxyable = deepProxy.isProxyable = function (obj, forceCheck) { - if (typeof forceCheck === "undefined" && isFakeProxy) { return false; } - return ['object', 'array'].indexOf(type(obj)) !== -1; - }; - - /* Any time you set a value, check its type. - If that type is proxyable, make a new proxy. */ - var setter = deepProxy.set = function (cb) { - return function (obj, prop, value) { - if (prop === 'on') { - throw new Error("'on' is a reserved attribute name for realtime lists and maps"); - } - if (isProxyable(value)) { - obj[prop] = deepProxy.create(value, cb); - } else { - obj[prop] = value; - } - - cb(); - return obj[prop] || true; // always return truthey or you have problems - }; - }; - - var pathMatches = deepProxy.pathMatches = function (path, pattern) { - return !pattern.some(function (x, i) { - return x !== path[i]; - }); - }; - - var lengthDescending = function (a, b) { return b.pattern.length - a.pattern.length; }; - - /* TODO implement 'off' as well. - change 'setter' to warn users when they attempt to set 'off' - */ - var on = function(events) { - return function (evt, pattern, f) { - switch (evt) { - case 'change': - // pattern needs to be an array - pattern = type(pattern) === 'array'? pattern: [pattern]; - - events.change.push({ - cb: function (oldval, newval, path, root) { - if (pathMatches(path, pattern)) { - return f(oldval, newval, path, root); - } - }, - pattern: pattern, - }); - // sort into descending order so we evaluate in order of specificity - events.change.sort(lengthDescending); - - break; - case 'remove': - pattern = type(pattern) === 'array'? pattern: [pattern]; - - events.remove.push({ - cb: function (oldval, path, root) { - if (pathMatches(path, pattern)) { return f(oldval, path, root); } - }, - pattern: pattern, - }); - - events.remove.sort(lengthDescending); - - break; - case 'ready': - events.ready.push({ - // on('ready' has a different signature than - // change and delete, so use 'pattern', not 'f' - - cb: function (info) { - pattern(info); - } - }); - break; - case 'disconnect': - events.disconnect.push({ - cb: function (info) { - // as above - pattern(info); - } - }); - break; - case 'reconnect': - events.reconnect.push({ - cb: function (info) { - // as above - pattern(info); - } - }); - break; - case 'create': - events.create.push({ - cb: function (info) { - pattern(info); - } - }); - break; - default: - break; - } - return this; - }; - }; - - var getter = deepProxy.get = function (/* cb */) { - var events = { - disconnect: [], - reconnect: [], - change: [], - ready: [], - remove: [], - create: [], - }; - - return function (obj, prop) { - if (prop === 'on') { - return on(events); - } else if (prop === '_isProxy') { - return true; - } else if (prop === '_events') { - return events; - } - return obj[prop]; - }; - }; - - var deleter = deepProxy.delete = function (cb) { - return function (obj, prop) { - if (typeof(obj[prop]) === 'undefined') { return true; } - delete obj[prop]; - cb(); - return true; - }; - }; - - var handlers = deepProxy.handlers = function (cb, isRoot) { - if (!isRoot) { - return { - set: setter(cb), - get: function (obj, prop) { - if (prop === '_isProxy') { - return true; - } - return obj[prop]; - }, - deleteProperty: deleter(cb), - }; - } - return { - set: setter(cb), - get: getter(cb), - deleteProperty: deleter(cb), - }; - }; - - var remoteChangeFlag = deepProxy.remoteChangeFlag = false; - - var stringifyFakeProxy = deepProxy.stringifyFakeProxy = function (proxy) { - var copy = JSON.parse(Sortify(proxy)); - delete copy._events; - delete copy._isProxy; - return Sortify(copy); - }; - - deepProxy.checkLocalChange = function (obj, cb) { - if (!isFakeProxy) { return; } - var oldObj = stringifyFakeProxy(obj); - window.setInterval(function() { - var newObj = stringifyFakeProxy(obj); - if (newObj !== oldObj) { - oldObj = newObj; - if (remoteChangeFlag) { - remoteChangeFlag = false; - } else { - cb(); - } - } - },300); - }; - - var create = deepProxy.create = function (obj, opt, isRoot) { - /* recursively create proxies in case users do: - `x.a = {b: {c: 5}}; - - otherwise the inner object is not a proxy, which leads to incorrect - behaviour on the client that initiated the object (but not for - clients that receive the objects) */ - - // if the user supplied a callback, use it to create handlers - // this saves a bit of work in recursion - var methods = type(opt) === 'function'? handlers(opt, isRoot) : opt; - switch (type(obj)) { - case 'object': - var keys = Object.keys(obj); - keys.forEach(function (k) { - if (isProxyable(obj[k]) && !obj[k]._isProxy) { - obj[k] = create(obj[k], opt); - } - }); - break; - case 'array': - obj.forEach(function (o, i) { - if (isProxyable(o) && !o._isProxy) { - obj[i] = create(obj[i], opt); - } - }); - break; - default: - // if it's not an array or object, you don't need to proxy it - throw new Error('attempted to make a proxy of an unproxyable object'); - } - if (!isFakeProxy) { - if (obj._isProxy) { - return obj; - } - return new window.Proxy(obj, methods); - } - - var proxy = JSON.parse(JSON.stringify(obj)); - - if (isRoot) { - var events = { - disconnect: [], - reconnect: [], - change: [], - ready: [], - remove: [], - create: [], - }; - proxy.on = on(events); - proxy._events = events; - } - return proxy; - }; - - // onChange(path, key, root, oldval, newval) - var onChange = function (path, key, root, oldval, newval) { - var P = path.slice(0); - P.push(key); - - /* returning false in your callback terminates 'bubbling up' - we can accomplish this with Array.some because we've presorted - listeners by the specificity of their path - */ - root._events.change.some(function (handler) { - return handler.cb(oldval, newval, P, root) === false; - }); - }; - - var find = deepProxy.find = function (map, path) { - /* safely search for nested values in an object via a path */ - return (map && path.reduce(function (p, n) { - return typeof p[n] !== 'undefined' && p[n]; - }, map)) || undefined; - }; - - var onRemove = function (path, key, root, old, top) { - var newpath = path.concat(key); - var X = find(root, newpath); - - var t_X = type(X); - - /* TODO 'find' is correct but unnecessarily expensive. - optimize it. */ - - switch (t_X) { - case 'array': - - if (top) { - // the top of an onremove should emit an onchange instead - onChange(path, key, root, old, undefined);// no newval since it's a deletion - } else { - root._events.remove.forEach(function (handler) { - return handler.cb(X, newpath, root); - }); - } - // remove all of the array's children - X.forEach(function (x, i) { - onRemove(newpath, i, root); - }); - - break; - case 'object': - if (top) { - onChange(path, key, root, old, undefined);// no newval since it's a deletion - } else { - root._events.remove.forEach(function (handler) { - return handler.cb(X, newpath, root, old, false); - }); - } - // remove all of the object's children - Object.keys(X).forEach(function (key) { - onRemove(newpath, key, root, X[key], false); - }); - - break; - default: - root._events.remove.forEach(function (handler) { - return handler.cb(X, newpath, root); - }); - break; - } - }; - - /* compare a new object 'B' against an existing proxy object 'A' - provide a unary function 'f' for the purpose of constructing new - deep proxies from regular objects and arrays. - - Supply the path as you recurse, for the purpose of emitting events - attached to particular paths within the complete structure. - - Operates entirely via side effects on 'A' - */ - var objects = deepProxy.objects = function (A, B, f, path, root) { - var Akeys = Object.keys(A); - var Bkeys = Object.keys(B); - - /* iterating over the keys in B will tell you if a new key exists - it will not tell you if a key has been removed. - to accomplish that you will need to iterate over A's keys - */ - - /* TODO return a truthy or falsey value (in 'objects' and 'arrays') - so that we have some measure of whether an object or array changed - (from the higher level in the tree, rather than doing everything - at the leaf level). - - bonus points if you can defer events until the complete diff has - finished (collect them into an array or something, and simplify - the event if possible) - */ - - Bkeys.forEach(function (b) { - var t_b = type(B[b]); - var old = A[b]; - - if (Akeys.indexOf(b) === -1) { - // there was an insertion - - // mind the fallthrough behaviour - switch (t_b) { - case 'undefined': - // umm. this should never happen? - throw new Error("undefined type has key. this shouldn't happen?"); - case 'array': - case 'object': - A[b] = f(B[b]); - break; - default: - A[b] = B[b]; - } - - // insertions are a change - - // onChange(path, key, root, oldval, newval) - onChange(path, b, root, old, B[b]); - return; - } - - // else the key already existed - var t_a = type(A[b]); - if (t_a !== t_b) { - // its type changed! - console.log("type changed from [%s] to [%s]", t_a, t_b); - switch (t_b) { - case 'undefined': - throw new Error("first pass should never reveal undefined keys"); - case 'array': - A[b] = f(B[b]); - // make a new proxy - break; - case 'object': - A[b] = f(B[b]); - // make a new proxy - break; - default: - // all other datatypes just require assignment. - A[b] = B[b]; - break; - } - - // type changes always mean a change happened - onChange(path, b, root, old, B[b]); - return; - } - - // values might have changed, if not types - if (['array', 'object'].indexOf(t_a) === -1) { - // it's not an array or object, so we can do deep equality - if (A[b] !== B[b]) { - // not equal, so assign - A[b] = B[b]; - - onChange(path, b, root, old, B[b]); - } - return; - } - - // else it's an array or object - var nextPath = path.slice(0).concat(b); - if (t_a === 'object') { - // it's an object - objects.call(root, A[b], B[b], f, nextPath, root); - } else { - // it's an array - deepProxy.arrays.call(root, A[b], B[b], f, nextPath, root); - } - }); - Akeys.forEach(function (a) { - var old = A[a]; - - if (a === "on" || a === "_events") { return; } - - // the key was deleted - if (Bkeys.indexOf(a) === -1 || type(B[a]) === 'undefined') { - onRemove(path, a, root, old, true); - delete A[a]; - } - }); - - return; - }; - - var arrays = deepProxy.arrays = function (A, B, f, path, root) { - var l_A = A.length; - var l_B = B.length; - - if (l_A !== l_B) { - // B is longer than Aj - // there has been an insertion - - // OR - - // A is longer than B - // there has been a deletion - - B.forEach(function (b, i) { - var t_a = type(A[i]); - var t_b = type(b); - - var old = A[i]; - - if (t_a !== t_b) { - // type changes are always destructive - // that's good news because destructive is easy - switch (t_b) { - case 'undefined': - throw new Error('this should never happen'); - case 'object': - A[i] = f(b); - break; - case 'array': - A[i] = f(b); - break; - default: - A[i] = b; - break; - } - - // path, key, root object, oldvalue, newvalue - onChange(path, i, root, old, b); - } else { - // same type - var nextPath = path.slice(0).concat(i); - - switch (t_b) { - case 'object': - objects.call(root, A[i], b, f, nextPath, root); - break; - case 'array': - if (arrays.call(root, A[i], b, f, nextPath, root)) { - onChange(path, i, root, old, b); - } - break; - default: - if (b !== A[i]) { - A[i] = b; - onChange(path, i, root, old, b); - } - break; - } - } - }); - - if (l_A > l_B) { - // A was longer than B, so there have been deletions - var i = l_B; - //var t_a; - var old; - - for (; i <= l_B; i++) { - // recursively delete - old = A[i]; - - onRemove(path, i, root, old, true); - } - // cool - } - - A.length = l_B; - return; - } - - // else they are the same length, iterate over their values - A.forEach(function (a, i) { - var t_a = type(a); - var t_b = type(B[i]); - - var old = a; - - // they have different types - if (t_a !== t_b) { - switch (t_b) { - case 'undefined': - onRemove(path, i, root, old, true); - break; - - // watch out for fallthrough behaviour - // if it's an object or array, create a proxy - case 'object': - case 'array': - A[i] = f(B[i]); - break; - default: - A[i] = B[i]; - break; - } - - onChange(path, i, root, old, B[i]); - return; - } - - // they are the same type, clone the paths array and push to it - var nextPath = path.slice(0).concat(i); - - // same type - switch (t_b) { - case 'undefined': - throw new Error('existing key had type `undefined`. this should never happen'); - case 'object': - if (objects.call(root, A[i], B[i], f, nextPath, root)) { - onChange(path, i, root, old, B[i]); - } - break; - case 'array': - if (arrays.call(root, A[i], B[i], f, nextPath, root)) { - onChange(path, i, root, old, B[i]); - } - break; - default: - if (A[i] !== B[i]) { - A[i] = B[i]; - onChange(path, i, root, old, B[i]); - } - break; - } - }); - return; - }; - - deepProxy.update = function (A, B, cb) { - var t_A = type(A); - var t_B = type(B); - - if (t_A !== t_B) { - throw new Error("Proxy updates can't result in type changes"); - } - - switch (t_B) { - /* use .call so you can supply a different `this` value */ - case 'array': - arrays.call(A, A, B, function (obj) { - return create(obj, cb); - }, [], A); - break; - case 'object': - // arrays.call(this, A , B , f, path , root) - objects.call(A, A, B, function (obj) { - return create(obj, cb); - }, [], A); - break; - default: - throw new Error("unsupported realtime datatype:" + t_B); - } - }; - - return deepProxy; - }()); - - api.create = function (cfg) { - /* validate your inputs before proceeding */ - - if (!DeepProxy.isProxyable(cfg.data, true)) { - throw new Error('unsupported datatype: '+ DeepProxy.type(cfg.data)); - } - - var realtimeOptions = { - userName: cfg.userName, - initialState: Sortify(cfg.data), - readOnly: cfg.readOnly, - patchTransformer: ChainPad.SmartJSONTransformer, - logLevel: typeof(cfg.logLevel) === 'undefined'? 0: cfg.logLevel, - validateContent: function (content) { - try { - JSON.parse(content); - return true; - } catch (e) { - console.error("Failed to parse, rejecting patch"); - return false; - } - }, - }; - - var initializing = true; - - var setterCb = function () { - if (!DeepProxy.remoteChangeFlag) { realtimeOptions.onLocal(); } - }; - - var rt = {}; - var realtime; - - var proxy; - - realtimeOptions.onRemote = function () { - if (initializing) { return; } - // TODO maybe implement history support here - - var userDoc = realtime.getUserDoc(); - var parsed = JSON.parse(userDoc); - - DeepProxy.remoteChangeFlag = true; - DeepProxy.update(proxy, parsed, setterCb); - DeepProxy.remoteChangeFlag = false; - }; - - var onLocal = realtimeOptions.onLocal = function () { - if (initializing) { return; } - var strung = isFakeProxy? DeepProxy.stringifyFakeProxy(proxy): Sortify(proxy); - realtime.contentUpdate(strung); - - // try harder - if (realtime.getUserDoc() !== strung) { realtime.contentUpdate(strung); } - - // onLocal - if (cfg.onLocal) { cfg.onLocal(); } - }; - - proxy = DeepProxy.create(cfg.data, setterCb, true); - console.log(proxy); - - realtimeOptions.onInit = function (info) { - proxy._events.create.forEach(function (handler) { - handler.cb(info); - }); - }; - - var ready = false; - realtimeOptions.onReady = function (info) { - if (ready) { return; } - if (realtime !== info.realtime) { - realtime = rt.realtime = info.realtime; - } else { - console.error(realtime); - } - - var userDoc = realtime.getUserDoc(); - var parsed = JSON.parse(userDoc); - - DeepProxy.update(proxy, parsed, setterCb); - - proxy._events.ready.forEach(function (handler) { - handler.cb(info); - }); - - DeepProxy.checkLocalChange(proxy, onLocal); - - initializing = false; - ready = true; - }; - - realtimeOptions.onAbort = function (info) { - proxy._events.disconnect.forEach(function (handler) { - handler.cb(info); - }); - }; - - realtimeOptions.onConnectionChange = function (info) { - if (info.state) { // reconnect - initializing = true; - proxy._events.reconnect.forEach(function (handler) { - handler.cb(info); - }); - return; - } - // disconnected - proxy._events.disconnect.forEach(function (handler) { - handler.cb(info); - }); - }; - - realtimeOptions.onError = function (info) { - proxy._events.disconnect.forEach(function (handler) { - handler.cb(info); - }); - }; - - realtime = rt.cpCnInner = cfg.common.startRealtime(realtimeOptions); - rt.proxy = proxy; - rt.realtime = realtime; - - return rt; - }; - - return api; -}); diff --git a/www/drive/inner.js b/www/drive/inner.js index 271ae201f..fa503606a 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -13,7 +13,7 @@ define([ '/common/common-realtime.js', '/common/userObject.js', '/customize/application_config.js', - '/common/sframe-chainpad-listmap.js', + '/bower_components/chainpad-listmap/chainpad-listmap.js', '/customize/messages.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', diff --git a/www/poll/inner.js b/www/poll/inner.js index b30e86479..c1799e663 100644 --- a/www/poll/inner.js +++ b/www/poll/inner.js @@ -7,7 +7,7 @@ define([ '/common/sframe-common.js', '/common/common-realtime.js', '/customize/application_config.js', - '/common/sframe-chainpad-listmap.js', + '/bower_components/chainpad-listmap/chainpad-listmap.js', '/customize/pages.js', '/poll/render.js', '/common/diffMarked.js', diff --git a/www/todo/inner.js b/www/todo/inner.js index d9b0fca3e..8d5de7778 100644 --- a/www/todo/inner.js +++ b/www/todo/inner.js @@ -1,7 +1,7 @@ define([ 'jquery', '/bower_components/chainpad-crypto/crypto.js', - '/common/sframe-chainpad-listmap.js', + '/bower_components/chainpad-listmap/chainpad-listmap.js', '/common/toolbar3.js', '/bower_components/nthen/index.js', '/common/sframe-common.js', From 701af29192144c90e5c0131f35ea299272036039 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 24 Nov 2017 15:12:00 +0100 Subject: [PATCH 09/11] fix broken register/login process --- www/common/login.js | 8 ++++++-- www/common/outer/local-store.js | 5 +++++ www/register/main.js | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/www/common/login.js b/www/common/login.js index 20661adcf..847ca74a7 100644 --- a/www/common/login.js +++ b/www/common/login.js @@ -5,9 +5,11 @@ define([ '/common/common-util.js', '/common/outer/network-config.js', '/common/credential.js', + '/bower_components/chainpad/chainpad.dist.js', + '/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/scrypt-async/scrypt-async.min.js', // better load speed -], function ($, Listmap, Crypto, Util, NetConfig, Cred) { +], function ($, Listmap, Crypto, Util, NetConfig, Cred, ChainPad) { var Exports = { Cred: Cred, }; @@ -64,6 +66,8 @@ define([ validateKey: opt.keys.validateKey, // derived validation key crypto: Crypto.createEncryptor(opt.keys), logLevel: 1, + classic: true, + ChainPad: ChainPad, }; var rt = opt.rt = Listmap.create(config); @@ -133,7 +137,7 @@ define([ return void cb('ALREADY_REGISTERED', res); } - cb(void 0, res); + setTimeout(function () { cb(void 0, res); }); }); }); }; diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index 09d135d2e..43611aa6f 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -52,6 +52,11 @@ define([ return hash; }; + var setUserHash = LocalStore.setUserHash = function (hash) { + var sHash = Hash.serializeHash(hash); + localStorage[Constants.userHashKey] = sHash; + }; + LocalStore.getAccountName = function () { return localStorage[Constants.userNameKey]; }; diff --git a/www/register/main.js b/www/register/main.js index 295f6acf0..69e3a59a4 100644 --- a/www/register/main.js +++ b/www/register/main.js @@ -60,7 +60,7 @@ define([ window.alert("Test passed!"); return; } - localStorage.User_hash = result.userHash; + LocalStore.setUserHash(result.userHash); var proxy = result.proxy; proxy.edPublic = result.edPublic; From 4ff4ccd1d339677ea5f964b8a71d65572a1876f8 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 27 Nov 2017 11:30:50 +0100 Subject: [PATCH 10/11] use faster and more correct 'find' implementation --- www/common/common-util.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/www/common/common-util.js b/www/common/common-util.js index 421dd9c1e..4e1e15c9e 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -25,9 +25,12 @@ define([], function () { }; Util.find = function (map, path) { - return (map && path.reduce(function (p, n) { - return typeof(p[n]) !== 'undefined' && p[n]; - }, map)); + var l = path.length; + for (var i = 0; i < l; i++) { + if (typeof(map[path[i]]) === 'undefined') { return; } + map = map[path[i]]; + } + return map; }; Util.uid = function () { From be5c5831ddf4f6de7de28c568ea4f021c26dddb2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 27 Nov 2017 12:18:04 +0100 Subject: [PATCH 11/11] lint compliance --- www/common/outer/local-store.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index 43611aa6f..d5fd03623 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -52,7 +52,7 @@ define([ return hash; }; - var setUserHash = LocalStore.setUserHash = function (hash) { + LocalStore.setUserHash = function (hash) { var sHash = Hash.serializeHash(hash); localStorage[Constants.userHashKey] = sHash; };