Merge branch 'merge-staging' into staging

pull/1/head
ansuz 7 years ago
commit 812df7de6f

@ -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",

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

@ -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 () {

@ -1,15 +1,11 @@
define([
'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-realtime.js',
'/common/outer/network-config.js',
'/bower_components/textpatcher/TextPatcher.js'
], function ($, Crypto, CPNetflux, Util, Hash, Realtime, NetConfig, TextPatcher) {
//var Messages = Cryptpad.Messages;
//var noop = function () {};
], function (Crypto, CPNetflux, Util, Hash, Realtime, NetConfig) {
var finish = function (S, err, doc) {
if (S.done) { return; }
S.cb(err, doc);
@ -75,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"));

@ -2,16 +2,16 @@ 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/common-interface.js',
'/common/common-hash.js',
'/common/common-util.js',
'/common/common-constants.js',
'/common/migrate-user-object.js',
'/bower_components/chainpad/chainpad.dist.js',
'/common/outer/network-config.js',
'/common/outer/local-store.js',
], function ($, Listmap, Crypto, TextPatcher, FO, UI, Hash, Util, Constants, Migrate, NetConfig,
], function ($, Listmap, Crypto, FO, UI, Hash, Util, Constants, Migrate, ChainPad, NetConfig,
LocalStore) {
/*
This module uses localStorage, which is synchronous, but exposes an
@ -305,6 +305,8 @@ define([
crypto: Crypto.createEncryptor(secret.keys),
userName: 'fs',
logLevel: 1,
ChainPad: ChainPad,
classic: true,
};
var exp = {};

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

@ -52,6 +52,11 @@ define([
return hash;
};
LocalStore.setUserHash = function (hash) {
var sHash = Hash.serializeHash(hash);
localStorage[Constants.userHashKey] = sHash;
};
LocalStore.getAccountName = function () {
return localStorage[Constants.userNameKey];
};

@ -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',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
'/customize/messages.js',
@ -13,6 +11,7 @@ define([
'/common/common-thumbnail.js',
'/common/common-feedback.js',
'/customize/application_config.js',
'/bower_components/chainpad/chainpad.dist.js',
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
@ -22,9 +21,7 @@ define([
$,
Hyperjson,
Toolbar,
JsonOT,
JSONSortify,
TextPatcher,
nThen,
SFCommon,
Messages,
@ -32,7 +29,8 @@ define([
UI,
Thumb,
Feedback,
AppConfig)
AppConfig,
ChainPad)
{
var SaveAs = window.saveAs;
@ -63,7 +61,6 @@ define([
var common;
var cpNfInner;
var textPatcher;
var readOnly;
var title;
var toolbar;
@ -181,10 +178,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);
@ -231,7 +229,7 @@ define([
}
var contentStr = JSONSortify(content);
textPatcher(contentStr);
cpNfInner.chainpad.contentUpdate(contentStr);
if (cpNfInner.chainpad.getUserDoc() !== contentStr) {
console.error("realtime.getUserDoc() !== shjson");
}
@ -377,7 +375,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,
@ -409,8 +407,6 @@ define([
cpNfInner.metadataMgr.onChange(checkReady);
checkReady();
textPatcher = TextPatcher.create({ realtime: cpNfInner.chainpad });
var infiniteSpinnerModal = false;
window.setInterval(function () {
if (state === STATE.DISCONNECTED) { return; }

@ -1,751 +0,0 @@
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) {
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,
transformFunction: JsonOT.transform || JsonOT.validate,
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;
var patchText;
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);
patchText(strung);
// try harder
if (realtime.getUserDoc() !== strung) { patchText(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; }
// 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);
}
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;
});

@ -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' ?
@ -37,7 +36,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 +49,6 @@ define([
var chainpad = ChainPad.create({
userName: userName,
initialState: initialState,
transformFunction: transformFunction,
patchTransformer: patchTransformer,
validateContent: validateContent,
avgSyncMilliseconds: avgSyncMilliseconds,

@ -6,8 +6,9 @@ define([
'/common/common-ui-elements.js',
'/common/common-hash.js',
'/common/common-util.js',
'/bower_components/textpatcher/TextPatcher.js',
], function ($, Modes, Themes, Messages, UIElements, Hash, Util, TextPatcher) {
'/common/text-cursor.js',
'/bower_components/chainpad/chainpad.dist.js',
], function ($, Modes, Themes, Messages, UIElements, Hash, Util, TextCursor, ChainPad) {
var module = {};
var cursorToPos = function(cursor, oldText) {
@ -37,7 +38,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 = {};
@ -47,9 +48,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]) {
@ -299,8 +300,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);
};
/////
@ -312,7 +313,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 () {

@ -1,11 +1,11 @@
define([
'jquery',
'/common/common-interface.js',
'/bower_components/chainpad-json-validator/json-ot.js',
//'/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) {
@ -33,7 +33,9 @@ define([
}
},
initialState: '',
transformFunction: JsonOT.validate,
//patchTransformer: ChainPad.NaiveJSONTransformer,
//logLevel: 0,
//transformFunction: JsonOT.validate,
logLevel: config.debug ? 1 : 0,
noPrune: true
});

@ -0,0 +1,29 @@
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--) {
cursor = transformCursor(cursor, ops[i]);
}
return cursor;
}
return transformCursor(ops);
};
return module.exports;
});

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

@ -1,6 +1,5 @@
define([
'jquery',
'/bower_components/textpatcher/TextPatcher.js',
'/common/toolbar3.js',
'json.sortify',
'/common/common-util.js',
@ -14,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',
@ -22,7 +21,6 @@ define([
'less!/customize/src/less2/main.less',
], function (
$,
TextPatcher,
Toolbar,
JSONSortify,
Util,
@ -2967,12 +2965,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();

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

@ -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>

@ -1,94 +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/common-interface.js',
//'/common/visible.js',
//'/common/notify.js',
'/bower_components/file-saver/FileSaver.min.js'
], function ($, Config, Messages, Board, TextPatcher, Listmap, Crypto, Cryptpad, UI /*, 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 () {
UI.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 () {
UI.log("Ready!");
whenReady({
});
})
.on('disconnect', function () {
UI.warn("Disconnected!");
});
});
});

@ -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>

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

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

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

@ -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>

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

@ -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>

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

@ -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',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
'/common/common-interface.js',
@ -18,8 +16,6 @@ define([
], function (
$,
Crypto,
TextPatcher,
JsonOT,
nThen,
SFCommon,
UI,

@ -30,7 +30,7 @@ define([
'/api/config',
'/common/common-hash.js',
'/common/common-util.js',
'/bower_components/chainpad-json-validator/json-ot.js',
'/bower_components/chainpad/chainpad.dist.js',
'/bower_components/diff-dom/diffDOM.js',
@ -50,7 +50,7 @@ define([
ApiConfig,
Hash,
Util,
JsonOT)
ChainPad)
{
var DiffDom = window.diffDOM;
@ -537,7 +537,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) {

@ -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.
*/
*/

@ -1,6 +1,5 @@
define([
'jquery',
'/bower_components/textpatcher/TextPatcher.js',
'/common/toolbar3.js',
'/common/common-util.js',
'/common/cryptget.js',
@ -8,21 +7,21 @@ 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',
'/common/sframe-common-codemirror.js',
'/common/common-thumbnail.js',
'/bower_components/chainpad/chainpad.dist.js',
'/common/common-interface.js',
'/customize/messages.js',
'cm/lib/codemirror',
'cm/addon/display/placeholder',
'cm/mode/markdown/markdown',
'css!cm/lib/codemirror.css',
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
@ -30,7 +29,6 @@ define([
'less!/customize/src/less2/main.less',
], function (
$,
TextPatcher,
Toolbar,
Util,
Cryptget,
@ -44,6 +42,7 @@ define([
DiffMd,
SframeCM,
Thumb,
ChainPad,
UI,
Messages,
CMeditor)
@ -674,7 +673,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();
};
@ -1067,10 +1066,6 @@ define([
if (APP.realtime !== info.realtime) {
APP.realtime = info.realtime;
APP.patchText = TextPatcher.create({
realtime: info.realtime,
logging: true,
});
}
metadataMgr = common.getMetadataMgr();

@ -1,11 +1,13 @@
define([
'jquery',
'/bower_components/hyperjson/hyperjson.js',
'/bower_components/textpatcher/TextPatcher.js',
'/common/text-cursor.js',
'/bower_components/chainpad/chainpad.dist.js',
'/common/common-util.js',
'/customize/messages.js',
'/bower_components/diff-dom/diffDOM.js',
], function ($, Hyperjson, TextPatcher, Util, Messages) {
'/bower_components/diff-dom/diffDOM.js'
], function ($, Hyperjson, TextCursor, ChainPad, Util, Messages) {
var DiffDOM = window.diffDOM;
var Example = {
@ -401,10 +403,10 @@ var Renderer = function (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);
});
}
};

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

@ -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',

@ -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/common-util.js',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
@ -16,6 +14,7 @@ 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',
@ -26,10 +25,8 @@ define([
], function (
$,
Crypto,
TextPatcher,
Toolbar,
JSONSortify,
JsonOT,
Util,
nThen,
SFCommon,
@ -40,7 +37,8 @@ define([
Messages,
AppConfig,
Thumb,
Colors)
Colors,
ChainPad)
{
var saveAs = window.saveAs;
@ -245,7 +243,7 @@ define([
config = {
readOnly: readOnly,
transformFunction: JsonOT.validate,
patchTransformer: ChainPad.NaiveJSONTransformer,
// cryptpad debug logging (default is 1)
// logLevel: 0,
validateContent: function (content) {
@ -347,7 +345,7 @@ define([
var content = stringifyInner(canvas.toDatalessJSON());
APP.patchText(content);
APP.realtime.contentUpdate(content);
};
var addImageToCanvas = function (img) {
@ -505,11 +503,7 @@ define([
config.onReady = function (info) {
if (APP.realtime !== info.realtime) {
var realtime = APP.realtime = info.realtime;
APP.patchText = TextPatcher.create({
realtime: realtime,
//logging: true
});
APP.realtime = info.realtime;
}
var userDoc = APP.realtime.getUserDoc();

Loading…
Cancel
Save