Refactored out TextPatcher and JsonOT and replaced with new ChainPad

This commit is contained in:
Caleb James DeLisle
2017-11-09 15:36:49 +01:00
parent 7f8147b18b
commit 75130150d5
31 changed files with 82 additions and 1447 deletions

View File

@@ -29,10 +29,10 @@
"json.sortify": "~2.1.0", "json.sortify": "~2.1.0",
"secure-fabric.js": "secure-v1.7.9", "secure-fabric.js": "secure-v1.7.9",
"hyperjson": "~1.4.0", "hyperjson": "~1.4.0",
"textpatcher": "^1.3.0",
"chainpad-json-validator": "^0.2.0",
"chainpad-crypto": "^0.1.3", "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", "file-saver": "1.3.1",
"alertifyjs": "1.0.11", "alertifyjs": "1.0.11",
"scrypt-async": "1.2.0", "scrypt-async": "1.2.0",

View File

@@ -1,6 +1,5 @@
define([ define([
'jquery', 'jquery',
'/bower_components/textpatcher/TextPatcher.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/diffMarked.js', '/common/diffMarked.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
@@ -38,7 +37,6 @@ define([
], function ( ], function (
$, $,
TextPatcher,
Cryptpad, Cryptpad,
DiffMd, DiffMd,
nThen, nThen,

View File

@@ -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($('<link />', {
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;
});

View File

@@ -1,9 +1,9 @@
define([ define([
'jquery', 'jquery',
'/bower_components/chainpad-json-validator/json-ot.js',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/bower_components/chainpad/chainpad.dist.js', '/bower_components/chainpad/chainpad.dist.js',
], function ($, JsonOT, Crypto) { ], function ($, Crypto) {
var ChainPad = window.ChainPad; var ChainPad = window.ChainPad;
var History = {}; var History = {};
@@ -28,7 +28,7 @@ define([
return ChainPad.create({ return ChainPad.create({
userName: 'history', userName: 'history',
initialState: '', initialState: '',
transformFunction: JsonOT.validate, patchTransformer: ChainPad.NaiveJSONStransformer,
logLevel: 0, logLevel: 0,
noPrune: true noPrune: true
}); });

View File

@@ -3,8 +3,7 @@ define([
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js', '/bower_components/chainpad-netflux/chainpad-netflux.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/bower_components/textpatcher/TextPatcher.js' ], function ($, Crypto, Realtime, Cryptpad) {
], function ($, Crypto, Realtime, Cryptpad, TextPatcher) {
//var Messages = Cryptpad.Messages; //var Messages = Cryptpad.Messages;
//var noop = function () {}; //var noop = function () {};
var finish = function (S, err, doc) { var finish = function (S, err, doc) {
@@ -72,9 +71,7 @@ define([
var realtime = Session.session = info.realtime; var realtime = Session.session = info.realtime;
Session.network = info.network; Session.network = info.network;
TextPatcher.create({ realtime.contentUpdate(doc);
realtime: realtime,
})(doc);
var to = window.setTimeout(function () { var to = window.setTimeout(function () {
cb(new Error("Timeout")); cb(new Error("Timeout"));

View File

@@ -11,11 +11,9 @@ define([
'/common/common-title.js', '/common/common-title.js',
'/common/common-metadata.js', '/common/common-metadata.js',
'/common/common-messaging.js', '/common/common-messaging.js',
'/common/common-codemirror.js',
'/common/common-file.js', '/common/common-file.js',
'/file/file-crypto.js', '/file/file-crypto.js',
'/common/common-realtime.js', '/common/common-realtime.js',
'/common/clipboard.js', '/common/clipboard.js',
'/common/pinpad.js', '/common/pinpad.js',
'/customize/application_config.js', '/customize/application_config.js',
@@ -23,7 +21,7 @@ define([
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/bower_components/localforage/dist/localforage.min.js', '/bower_components/localforage/dist/localforage.min.js',
], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata, ], 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) { Pinpad, AppConfig, MediaTag, Nthen, localForage) {
// Configure MediaTags to use our local viewer // Configure MediaTags to use our local viewer
@@ -154,9 +152,6 @@ define([
// Metadata // Metadata
common.createMetadata = Metadata.create; common.createMetadata = Metadata.create;
// CodeMirror
common.createCodemirror = CodeMirror.create;
// Files // Files
common.createFileManager = function (config) { return Files.create(common, config); }; common.createFileManager = function (config) { return Files.create(common, config); };

View File

@@ -2,10 +2,9 @@ define([
'jquery', 'jquery',
'/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5', '/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
'/bower_components/textpatcher/TextPatcher.amd.js',
'/common/userObject.js', '/common/userObject.js',
'/common/migrate-user-object.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 This module uses localStorage, which is synchronous, but exposes an
asyncronous API. This is so that we can substitute other storage asyncronous API. This is so that we can substitute other storage

View File

@@ -2,9 +2,7 @@ define([
'jquery', 'jquery',
'/bower_components/hyperjson/hyperjson.js', '/bower_components/hyperjson/hyperjson.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'/bower_components/chainpad-json-validator/json-ot.js',
'json.sortify', 'json.sortify',
'/bower_components/textpatcher/TextPatcher.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
@@ -12,6 +10,7 @@ define([
'/common/common-util.js', '/common/common-util.js',
'/common/common-thumbnail.js', '/common/common-thumbnail.js',
'/customize/application_config.js', '/customize/application_config.js',
'/bower_components/chainpad/chainpad.dist.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
@@ -20,9 +19,7 @@ define([
$, $,
Hyperjson, Hyperjson,
Toolbar, Toolbar,
JsonOT,
JSONSortify, JSONSortify,
TextPatcher,
Cryptpad, Cryptpad,
nThen, nThen,
SFCommon, SFCommon,
@@ -31,6 +28,7 @@ define([
Thumb, Thumb,
AppConfig) AppConfig)
{ {
var ChainPad = window.ChainPad;
var SaveAs = window.saveAs; var SaveAs = window.saveAs;
var UNINITIALIZED = 'UNINITIALIZED'; var UNINITIALIZED = 'UNINITIALIZED';
@@ -64,7 +62,6 @@ define([
var common; var common;
var cpNfInner; var cpNfInner;
var textPatcher;
var readOnly; var readOnly;
var title; var title;
var toolbar; var toolbar;
@@ -182,10 +179,11 @@ define([
result in a feedback loop, which we call a browser result in a feedback loop, which we call a browser
fight */ fight */
// what changed? // what changed?
var op = TextPatcher.diff(newContentStrNoMeta, newContent2StrNoMeta); var ops = ChainPad.Diff.diff(newContentStrNoMeta, newContent2StrNoMeta);
// log the changes // log the changes
TextPatcher.log(newContentStrNoMeta, op); console.log(newContentStrNoMeta);
var sop = JSON.stringify(TextPatcher.format(newContentStrNoMeta, op)); console.log(ops);
var sop = JSON.stringify([ newContentStrNoMeta, ops ]);
var fights = window.CryptPad_fights = window.CryptPad_fights || []; var fights = window.CryptPad_fights = window.CryptPad_fights || [];
var index = fights.indexOf(sop); var index = fights.indexOf(sop);
@@ -232,7 +230,7 @@ define([
} }
var contentStr = JSONSortify(content); var contentStr = JSONSortify(content);
textPatcher(contentStr); cpNfInner.chainpad.contentUpdate(contentStr);
if (cpNfInner.chainpad.getUserDoc() !== contentStr) { if (cpNfInner.chainpad.getUserDoc() !== contentStr) {
console.error("realtime.getUserDoc() !== shjson"); console.error("realtime.getUserDoc() !== shjson");
} }
@@ -378,7 +376,7 @@ define([
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
cpNfInner = common.startRealtime({ cpNfInner = common.startRealtime({
// really basic operational transform // really basic operational transform
transformFunction: options.transformFunction || JsonOT.transform, patchTransformer: options.patchTransformer || ChainPad.SmartJSONTransformer,
// cryptpad debug logging (default is 1) // cryptpad debug logging (default is 1)
// logLevel: 0, // logLevel: 0,
@@ -410,8 +408,6 @@ define([
cpNfInner.metadataMgr.onChange(checkReady); cpNfInner.metadataMgr.onChange(checkReady);
checkReady(); checkReady();
textPatcher = TextPatcher.create({ realtime: cpNfInner.chainpad });
var infiniteSpinnerModal = false; var infiniteSpinnerModal = false;
window.setInterval(function () { window.setInterval(function () {
if (state === STATE.DISCONNECTED) { return; } if (state === STATE.DISCONNECTED) { return; }

View File

@@ -1,10 +1,10 @@
require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([ define([
'/bower_components/chainpad-netflux/chainpad-netflux.js', '/bower_components/chainpad-netflux/chainpad-netflux.js',
'/bower_components/chainpad-json-validator/json-ot.js',
'json.sortify', 'json.sortify',
'/bower_components/textpatcher/TextPatcher.js', '/bower_components/chainpad/chainpad.dist.js'
], function (Realtime, JsonOT, Sortify, TextPatcher) { ], function (Realtime, Sortify) {
var ChainPad = window.ChainPad;
var api = {}; var api = {};
// "Proxy" is undefined in Safari : we need to use an normal object and check if there are local // "Proxy" is undefined in Safari : we need to use an normal object and check if there are local
// changes regurlarly. // changes regurlarly.
@@ -627,7 +627,7 @@ define([
userName: cfg.userName, userName: cfg.userName,
initialState: Sortify(cfg.data), initialState: Sortify(cfg.data),
readOnly: cfg.readOnly, readOnly: cfg.readOnly,
transformFunction: JsonOT.transform || JsonOT.validate, patchTransformer: ChainPad.SmartJSONTransformer,
logLevel: typeof(cfg.logLevel) === 'undefined'? 0: cfg.logLevel, logLevel: typeof(cfg.logLevel) === 'undefined'? 0: cfg.logLevel,
validateContent: function (content) { validateContent: function (content) {
try { try {
@@ -650,7 +650,6 @@ define([
var realtime; var realtime;
var proxy; var proxy;
var patchText;
realtimeOptions.onRemote = function () { realtimeOptions.onRemote = function () {
if (initializing) { return; } if (initializing) { return; }
@@ -667,10 +666,10 @@ define([
var onLocal = realtimeOptions.onLocal = function () { var onLocal = realtimeOptions.onLocal = function () {
if (initializing) { return; } if (initializing) { return; }
var strung = isFakeProxy? DeepProxy.stringifyFakeProxy(proxy): Sortify(proxy); var strung = isFakeProxy? DeepProxy.stringifyFakeProxy(proxy): Sortify(proxy);
patchText(strung); realtime.contentUpdate(strung);
// try harder // try harder
if (realtime.getUserDoc() !== strung) { patchText(strung); } if (realtime.getUserDoc() !== strung) { realtime.contentUpdate(strung); }
// onLocal // onLocal
if (cfg.onLocal) { cfg.onLocal(); } if (cfg.onLocal) { cfg.onLocal(); }
@@ -688,13 +687,8 @@ define([
var ready = false; var ready = false;
realtimeOptions.onReady = function (info) { realtimeOptions.onReady = function (info) {
if (ready) { return; } if (ready) { return; }
// create your patcher
if (realtime !== info.realtime) { if (realtime !== info.realtime) {
realtime = rt.realtime = info.realtime; realtime = rt.realtime = info.realtime;
patchText = TextPatcher.create({
realtime: realtime,
logging: cfg.logging || false,
});
} else { } else {
console.error(realtime); console.error(realtime);
} }

View File

@@ -37,7 +37,7 @@ define([
var onReady = config.onReady || function () { }; var onReady = config.onReady || function () { };
var userName = config.userName; var userName = config.userName;
var initialState = config.initialState; var initialState = config.initialState;
var transformFunction = config.transformFunction; if (config.transformFunction) { throw new Error("transformFunction is nolonger allowed"); }
var patchTransformer = config.patchTransformer; var patchTransformer = config.patchTransformer;
var validateContent = config.validateContent; var validateContent = config.validateContent;
var avgSyncMilliseconds = config.avgSyncMilliseconds; var avgSyncMilliseconds = config.avgSyncMilliseconds;
@@ -50,7 +50,6 @@ define([
var chainpad = ChainPad.create({ var chainpad = ChainPad.create({
userName: userName, userName: userName,
initialState: initialState, initialState: initialState,
transformFunction: transformFunction,
patchTransformer: patchTransformer, patchTransformer: patchTransformer,
validateContent: validateContent, validateContent: validateContent,
avgSyncMilliseconds: avgSyncMilliseconds, avgSyncMilliseconds: avgSyncMilliseconds,

View File

@@ -3,9 +3,12 @@ define([
'/common/modes.js', '/common/modes.js',
'/common/themes.js', '/common/themes.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/bower_components/textpatcher/TextPatcher.js', '/common/text-cursor.js',
], function ($, Modes, Themes, Cryptpad, TextPatcher) {
'/bower_components/chainpad/chainpad.dist.js'
], function ($, Modes, Themes, Cryptpad, TextCursor) {
var module = {}; var module = {};
var ChainPad = window.ChainPad;
var cursorToPos = function(cursor, oldText) { var cursorToPos = function(cursor, oldText) {
var cLine = cursor.line; var cLine = cursor.line;
@@ -34,7 +37,7 @@ define([
return cursor; return cursor;
}; };
module.setValueAndCursor = function (editor, oldDoc, remoteDoc, TextPatcher) { module.setValueAndCursor = function (editor, oldDoc, remoteDoc) {
var scroll = editor.getScrollInfo(); var scroll = editor.getScrollInfo();
//get old cursor here //get old cursor here
var oldCursor = {}; var oldCursor = {};
@@ -44,9 +47,9 @@ define([
editor.setValue(remoteDoc); editor.setValue(remoteDoc);
editor.save(); editor.save();
var op = TextPatcher.diff(oldDoc, remoteDoc); var ops = ChainPad.Diff.diff(oldDoc, remoteDoc);
var selects = ['selectionStart', 'selectionEnd'].map(function (attr) { var selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
return TextPatcher.transformCursor(oldCursor[attr], op); return TextCursor.transformCursor(oldCursor[attr], ops);
}); });
if(selects[0] === selects[1]) { if(selects[0] === selects[1]) {
@@ -295,8 +298,8 @@ define([
return { content: content }; return { content: content };
}; };
exp.setValueAndCursor = function (oldDoc, remoteDoc, TextPatcher) { exp.setValueAndCursor = function (oldDoc, remoteDoc) {
return module.setValueAndCursor(editor, oldDoc, remoteDoc, TextPatcher); return module.setValueAndCursor(editor, oldDoc, remoteDoc);
}; };
///// /////
@@ -308,7 +311,7 @@ define([
exp.contentUpdate = function (newContent) { exp.contentUpdate = function (newContent) {
var oldDoc = canonicalize($textarea.val()); var oldDoc = canonicalize($textarea.val());
var remoteDoc = newContent.content; var remoteDoc = newContent.content;
exp.setValueAndCursor(oldDoc, remoteDoc, TextPatcher); exp.setValueAndCursor(oldDoc, remoteDoc);
}; };
exp.getContent = function () { exp.getContent = function () {

View File

@@ -1,8 +1,7 @@
define([ define([
'jquery', 'jquery',
'/bower_components/chainpad-json-validator/json-ot.js',
'/bower_components/chainpad/chainpad.dist.js', '/bower_components/chainpad/chainpad.dist.js',
], function ($, JsonOT) { ], function ($) {
var ChainPad = window.ChainPad; var ChainPad = window.ChainPad;
var History = {}; var History = {};
@@ -31,7 +30,7 @@ define([
} }
}, },
initialState: '', initialState: '',
transformFunction: JsonOT.validate, patchTransformer: ChainPad.NaiveJSONTransformer,
logLevel: 0, logLevel: 0,
noPrune: true noPrune: true
}); });

25
www/common/text-cursor.js Normal file
View File

@@ -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);
};
});

View File

@@ -2,7 +2,6 @@ define([
'jquery', 'jquery',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'/bower_components/chainpad-json-validator/json-ot.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
@@ -17,7 +16,6 @@ define([
$, $,
Crypto, Crypto,
Toolbar, Toolbar,
JsonOT,
Cryptpad, Cryptpad,
nThen, nThen,
SFCommon, SFCommon,

View File

@@ -1,6 +1,5 @@
define([ define([
'jquery', 'jquery',
'/bower_components/textpatcher/TextPatcher.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'json.sortify', 'json.sortify',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
@@ -2940,12 +2939,7 @@ define([
var rt = APP.rt = Listmap.create(listmapConfig); var rt = APP.rt = Listmap.create(listmapConfig);
proxy = rt.proxy; proxy = rt.proxy;
var onCreate = function (info) { var onCreate = function (info) {
var realtime = APP.realtime = info.realtime; APP.realtime = info.realtime;
APP.patchText = TextPatcher.create({
realtime: realtime,
logging: true,
});
metadataMgr = common.getMetadataMgr(); metadataMgr = common.getMetadataMgr();

View File

@@ -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 $('<input>', 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 = $('<div>', {
})
.addClass('card-holder');
var $new = $('<a>', {
})
.addClass('add-card')
.text('add new card')
.click(function () {
// is this correct?
$cards.append(Board.Card(id));
});
var $list = $('<div>', {
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 = $('<div>', {
})
.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;
});

View File

@@ -1,97 +0,0 @@
<!DOCTYPE html>
<html class="cp board">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Zero Knowledge Date Picker</title>
<link rel="icon" type="image/png"
href="/customize/main-favicon.png"
data-main-favicon="/customize/main-favicon.png"
data-alt-favicon="/customize/alt-favicon.png"
id="favicon" />
<link rel="stylesheet" href="/customize/main.css" />
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<style>
html, body {
width: 100;
}
.clickable {
cursor: pointer;
}
#adduser, #addoption {
font-weight: bold;
}
#lists {
display: inline-block;
border: 1px solid white;
height: 80vh;
}
#create-list {
display: inline-block;
vertical-align: top;
background-color: green;
}
.list-column {
vertical-align: top;
box-sizing: border-box;
display: inline-block;
width: 400px;
height: 100%;
border: 1px solid white;
}
/* input */
input.list-title {
margin: 15px;
width: 80%;
display: block;
}
.card-holder {
border: 1px solid #ddd;
width: 90%;
margin: auto;
}
.add-card {
background-color: green;
display: block;
height: 20px;
width: 80%;
cursor: pointer;
margin: auto;
}
.card-title {
border: 5px solid blue;
}
.card-container {
display: block;
height: 50px;
width: 95%;
margin: auto;
padding: 5px;
border: 1px solid #ccc;
}
#board {
margin-left: 10vw;
overflow-x: visible;
}
</style>
</head>
<body>
<!--<div id="main"> -->
<div id="toolbar" class="buttons">
<sub><a href="/"></a></sub>
</div>
<div id="board">
<div id="lists"></div>
<span id="create-list">Add List</span>
</div>

View File

@@ -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!");
});
});
});

View File

@@ -1,78 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<style>
html, body{
padding: 0px;
margin: 0px;
overflow: hidden;
box-sizing: border-box;
}
form {
border: 3px solid black;
border-radius: 5px;
padding: 15px;
font-weight: bold !important;
font-size: 18px !important;
}
input[type="text"],
input[type="password"],
input[type="number"],
input[type="range"],
select
{
margin-top: 5px;
margin-bottom: 5px;
width: 80%;
}
textarea {
width: 80%;
height: 40vh;
font-weight: bold;
font-size: 18px;
}
</style>
</head>
<body>
<form>
<input type="radio" name="radio" value="one" checked>One
<input type="radio" name="radio" value="two">Two
<input type="radio" name="radio" value="three">Three<br>
<input type="checkbox" name="checkbox1" value="1">Checkbox One
<input type="checkbox" name="checkbox2" value="2">Checkbox Two<br>
<input type="text" name="text" placeholder="Text Input"><br>
<input type="password" name="password" placeholder="Passwords"><br>
<input type="number" name="number" min="1" max="5" placeholder="Numbers">Number<br>
<input type="range" name="range" min="0" max="100">Ranges<br>
<select name="select">
<option value="one">One</option>
<option value="two">Two</option>
<option value="three">Three</option>
<option value="four">Four</option>
</select> Dropdowns<br>
<select name="select-multiple" multiple>
<option value="pew">Pew</option>
<option value="bang">Bang</option>
<option value="kapow">Kapow</option>
<option value="zing">Zing</option>
</select>
<textarea name="textarea"></textarea><br>
</form>
</body>
</html>

View File

@@ -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);
});
});

View File

@@ -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()
*/
```

View File

@@ -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;
});

View File

@@ -1,60 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<style></style>
</head>
<body>
<a id="edit" href="#" target="_blank">Edit this document's style</a>
<h1>HTML Ipsum Presents</h1>
<p><strong>Pellentesque habitant morbi tristique</strong> 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. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote><p>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.</p></blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
<pre><code>
#header h1 a {
display: block;
width: 300px;
height: 80px;
}
</code></pre>
<table>
<thead>
<tr>
<th>th 1</th>
<th>th 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>one</td>
<td>two</td>
</tr>
<tr>
<td>three</td>
<td>four</td>
</tr>
</tbody>
</table>
</body>
</html>

View File

@@ -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);
});

View File

@@ -1,38 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<style>
html, body{
padding: 0px;
margin: 0px;
overflow: hidden;
box-sizing: border-box;
}
textarea{
width: 100%;
height: 100vh;
max-width: 100%;
max-height: 100vh;
font-size: 18px;
background-color: #073642;
color: #839496;
overflow-x: hidden;
/* disallow textarea resizes */
resize: none;
}
textarea[disabled] {
background-color: #275662;
color: #637476;
}
</style>
</head>
<body>
<textarea></textarea>
</body>
</html>

View File

@@ -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);
});
});

View File

@@ -1,8 +1,6 @@
define([ define([
'jquery', 'jquery',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.js',
'/bower_components/chainpad-json-validator/json-ot.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
@@ -14,8 +12,6 @@ define([
], function ( ], function (
$, $,
Crypto, Crypto,
TextPatcher,
JsonOT,
Cryptpad, Cryptpad,
nThen, nThen,
SFCommon, SFCommon,

View File

@@ -30,9 +30,9 @@ define([
'/api/config', '/api/config',
'/common/common-hash.js', '/common/common-hash.js',
'/common/common-util.js', '/common/common-util.js',
'/bower_components/chainpad-json-validator/json-ot.js',
'/bower_components/diff-dom/diffDOM.js', '/bower_components/diff-dom/diffDOM.js',
'/bower_components/chainpad/chainpad.dist.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
@@ -49,10 +49,10 @@ define([
MediaTag, MediaTag,
ApiConfig, ApiConfig,
Hash, Hash,
Util, Util)
JsonOT)
{ {
var DiffDom = window.diffDOM; var DiffDom = window.diffDOM;
var ChainPad = window.ChainPad;
var slice = function (coll) { var slice = function (coll) {
return Array.prototype.slice.call(coll); return Array.prototype.slice.call(coll);
@@ -555,7 +555,7 @@ define([
Framework.create({ Framework.create({
toolbarContainer: '#cke_1_toolbox', toolbarContainer: '#cke_1_toolbox',
contentContainer: '#cke_1_contents', contentContainer: '#cke_1_contents',
transformFunction: JsonOT.validate, patchTransformer: ChainPad.NaiveJSONTransformer,
thumbnail: { thumbnail: {
getContainer: function () { return $('iframe').contents().find('html')[0]; }, getContainer: function () { return $('iframe').contents().find('html')[0]; },
filter: function (el, before) { filter: function (el, before) {

View File

@@ -1,6 +1,5 @@
define([ define([
'jquery', 'jquery',
'/bower_components/textpatcher/TextPatcher.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/common-util.js', '/common/common-util.js',
@@ -16,21 +15,20 @@ define([
'/common/sframe-common-codemirror.js', '/common/sframe-common-codemirror.js',
'/common/sframe-common-interface.js', '/common/sframe-common-interface.js',
'/common/common-thumbnail.js', '/common/common-thumbnail.js',
'cm/lib/codemirror', 'cm/lib/codemirror',
'cm/addon/display/placeholder', 'cm/addon/display/placeholder',
'cm/mode/markdown/markdown', 'cm/mode/markdown/markdown',
'css!cm/lib/codemirror.css', 'css!cm/lib/codemirror.css',
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'/bower_components/chainpad/chainpad.dist.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less', 'less!/customize/src/less2/main.less',
], function ( ], function (
$, $,
TextPatcher,
Toolbar, Toolbar,
Cryptpad, Cryptpad,
Util, Util,
@@ -50,6 +48,7 @@ define([
{ {
var Messages = Cryptpad.Messages; var Messages = Cryptpad.Messages;
var saveAs = window.saveAs; var saveAs = window.saveAs;
var ChainPad = window.ChainPad;
var APP = window.APP = { var APP = window.APP = {
unlocked: { unlocked: {
@@ -1069,10 +1068,6 @@ define([
if (APP.realtime !== info.realtime) { if (APP.realtime !== info.realtime) {
APP.realtime = info.realtime; APP.realtime = info.realtime;
APP.patchText = TextPatcher.create({
realtime: info.realtime,
logging: true,
});
} }
metadataMgr = common.getMetadataMgr(); metadataMgr = common.getMetadataMgr();

View File

@@ -2,10 +2,13 @@ define([
//'/common/cryptpad-common.js', //'/common/cryptpad-common.js',
'jquery', 'jquery',
'/bower_components/hyperjson/hyperjson.js', '/bower_components/hyperjson/hyperjson.js',
'/bower_components/textpatcher/TextPatcher.js', '/common/text-cursor.js',
'/bower_components/diff-dom/diffDOM.js', '/bower_components/diff-dom/diffDOM.js',
], function ($, Hyperjson, TextPatcher) { '/bower_components/chainpad/chainpad.dist.js'
], function ($, Hyperjson, TextCursor) {
var DiffDOM = window.diffDOM; var DiffDOM = window.diffDOM;
var ChainPad = window.ChainPad;
var Example = { var Example = {
metadata: { metadata: {
@@ -400,10 +403,10 @@ var Renderer = function (Cryptpad, APP) {
var o = info.oldValue || ''; var o = info.oldValue || '';
var n = info.newValue || ''; var n = info.newValue || '';
var op = TextPatcher.diff(o, n); var ops = ChainPad.Diff.diff(o, n);
info.selection = ['selectionStart', 'selectionEnd'].map(function (attr) { info.selection = ['selectionStart', 'selectionEnd'].map(function (attr) {
return TextPatcher.transformCursor(element[attr], op); return TextCursor.transformCursor(element[attr], ops);
}); });
} }
}; };

View File

@@ -1,10 +1,8 @@
define([ define([
'jquery', 'jquery',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'json.sortify', 'json.sortify',
'/bower_components/chainpad-json-validator/json-ot.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/common-util.js', '/common/common-util.js',
'/common/cryptget.js', '/common/cryptget.js',
@@ -14,13 +12,13 @@ define([
'/api/config', '/api/config',
'/common/common-realtime.js', '/common/common-realtime.js',
'/customize/pages.js', '/customize/pages.js',
'/customize/application_config.js', '/customize/application_config.js',
'/common/common-thumbnail.js', '/common/common-thumbnail.js',
'/whiteboard/colors.js', '/whiteboard/colors.js',
'/bower_components/secure-fabric.js/dist/fabric.min.js', '/bower_components/secure-fabric.js/dist/fabric.min.js',
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'/bower_components/chainpad/chainpad.dist.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
@@ -28,10 +26,8 @@ define([
], function ( ], function (
$, $,
Crypto, Crypto,
TextPatcher,
Toolbar, Toolbar,
JSONSortify, JSONSortify,
JsonOT,
Cryptpad, Cryptpad,
Util, Util,
Cryptget, Cryptget,
@@ -47,6 +43,7 @@ define([
{ {
var saveAs = window.saveAs; var saveAs = window.saveAs;
var Messages = Cryptpad.Messages; var Messages = Cryptpad.Messages;
var ChainPad = window.ChainPad;
var APP = window.APP = { var APP = window.APP = {
Cryptpad: Cryptpad, Cryptpad: Cryptpad,
@@ -254,7 +251,7 @@ define([
config = { config = {
readOnly: readOnly, readOnly: readOnly,
transformFunction: JsonOT.validate, patchTransformer: ChainPad.NaiveJSONTransformer,
// cryptpad debug logging (default is 1) // cryptpad debug logging (default is 1)
// logLevel: 0, // logLevel: 0,
validateContent: function (content) { validateContent: function (content) {
@@ -356,7 +353,7 @@ define([
var content = stringifyInner(canvas.toDatalessJSON()); var content = stringifyInner(canvas.toDatalessJSON());
APP.patchText(content); APP.realtime.contentUpdate(content);
}; };
var addImageToCanvas = function (img) { var addImageToCanvas = function (img) {
@@ -516,10 +513,6 @@ define([
config.onReady = function (info) { config.onReady = function (info) {
if (APP.realtime !== info.realtime) { if (APP.realtime !== info.realtime) {
var realtime = APP.realtime = info.realtime; var realtime = APP.realtime = info.realtime;
APP.patchText = TextPatcher.create({
realtime: realtime,
//logging: true
});
} }
var userDoc = APP.realtime.getUserDoc(); var userDoc = APP.realtime.getUserDoc();