resolve merge conflicts

This commit is contained in:
ansuz
2017-10-02 18:59:47 +02:00
83 changed files with 10263 additions and 3812 deletions

View File

@@ -9,18 +9,20 @@ define([
var module = { exports: {} };
var key = Config.requireConf.urlArgs;
var localStorage = {};
try {
localStorage = window.localStorage || {};
if (localStorage['LESS_CACHE'] !== key) {
Object.keys(localStorage).forEach(function (k) {
if (k.indexOf('LESS_CACHE|') !== 0) { return; }
delete localStorage[k];
});
localStorage['LESS_CACHE'] = key;
if (!window.cryptpadCache) {
try {
localStorage = window.localStorage || {};
if (localStorage['LESS_CACHE'] !== key) {
Object.keys(localStorage).forEach(function (k) {
if (k.indexOf('LESS_CACHE|') !== 0) { return; }
delete localStorage[k];
});
localStorage['LESS_CACHE'] = key;
}
} catch (e) {
console.error(e);
localStorage = {};
}
} catch (e) {
console.error(e);
localStorage = {};
}
var cacheGet = function (k, cb) {
@@ -118,7 +120,7 @@ define([
}
console.log('CACHE MISS ' + url);
((/\.less([\?\#].*)?$/.test(url)) ? loadLess : loadCSS)(url, function (err, css) {
if (err) { console.log(err); }
if (err) { console.error(err); }
var output = fixAllURLs(css, url);
cachePut(url, output);
inject(output, url);

View File

@@ -131,13 +131,19 @@ define([
var $t = t.tokenfield = $(t.element).tokenfield();
t.getTokens = function () {
return $t.tokenfield('getTokens').map(function (token) {
return token.value;
return token.value.toLowerCase();
});
};
var $root = $t.parent();
$t.on('tokenfield:removetoken', function () {
$root.find('.token-input').focus();
});
t.preventDuplicates = function (cb) {
$t.on('tokenfield:createtoken', function (ev) {
var val;
ev.attrs.value = ev.attrs.value.toLowerCase();
if (t.getTokens().some(function (t) {
if (t === ev.attrs.value) { return ((val = t)); }
})) {
@@ -152,8 +158,8 @@ define([
$t.tokenfield('setTokens',
tokens.map(function (token) {
return {
value: token,
label: token,
value: token.toLowerCase(),
label: token.toLowerCase(),
};
}));
};
@@ -171,35 +177,38 @@ define([
var input = dialog.textInput();
var tagger = dialog.frame([
dialog.message('make some tags'), // TODO translate
dialog.message(Messages.tags_add),
input,
dialog.nav(),
]);
var field = UI.tokenField(input).preventDuplicates(function (val) {
UI.warn('Duplicate tag: ' + val); // TODO translate
UI.warn(Messages._getKey('tags_duplicate', [val]));
});
var close = Util.once(function () {
var $t = $(tagger).fadeOut(150, function () { $t.remove(); });
var listener;
var close = Util.once(function (result, ev) {
var $frame = $(tagger).fadeOut(150, function () {
stopListening(listener);
$frame.remove();
cb(result, ev);
});
});
var listener = listenForKeys(function () {}, function () {
close();
stopListening(listener);
});
var CB = Util.once(cb);
findOKButton(tagger).click(function () {
var $ok = findOKButton(tagger).click(function () {
var tokens = field.getTokens();
close();
CB(tokens);
close(tokens);
});
findCancelButton(tagger).click(function () {
close();
CB(null);
var $cancel = findCancelButton(tagger).click(function () {
close(null);
});
listenForKeys(function () {
$ok.click();
}, function () {
$cancel.click();
});
document.body.appendChild(tagger);
// :(
setTimeout(function () {
field.setTokens(tags);
@@ -244,7 +253,7 @@ define([
stopListening(listener);
cb();
});
listener = listenForKeys(close, close);
listener = listenForKeys(close, close, ok);
var $ok = $(ok).click(close);
document.body.appendChild(frame);
@@ -293,7 +302,7 @@ define([
$ok.click();
}, function () { // no
$cancel.click();
});
}, input);
document.body.appendChild(frame);
setTimeout(function () {
@@ -341,7 +350,7 @@ define([
$ok.click();
}, function () {
$cancel.click();
});
}, ok);
document.body.appendChild(frame);
setTimeout(function () {

View File

@@ -16,9 +16,10 @@ define([], function () {
handlers.splice(handlers.indexOf(cb), 1);
},
fire: function () {
if (fired) { return; }
if (once && fired) { return; }
fired = true;
handlers.forEach(function (h) { h(); });
var args = Array.prototype.slice.call(arguments);
handlers.forEach(function (h) { h.apply(null, args); });
}
};
};

View File

@@ -184,6 +184,9 @@ define([
}
return;
};
common.getLanguage = function () {
return Messages._languageUsed;
};
common.getUserlist = function () {
if (store) {
if (store.getProxy() && store.getProxy().info) {
@@ -533,6 +536,17 @@ define([
cb(void 0, entry);
};
common.resetTags = function (href, tags, cb) {
cb = cb || $.noop;
if (!Array.isArray(tags)) { return void cb('INVALID_TAGS'); }
getFileEntry(href, function (e, entry) {
if (e) { return void cb(e); }
if (!entry) { cb('NO_ENTRY'); }
entry.tags = tags.slice();
cb();
});
};
common.tagPad = function (href, tag, cb) {
if (typeof(cb) !== 'function') {
return void console.error('EXPECTED_CALLBACK');
@@ -2222,6 +2236,46 @@ define([
}
$iframe.load(w2); //cb);
}
}).nThen(function (waitFor) {
if (sessionStorage.createReadme) {
var w = waitFor();
require(['/common/cryptget.js'], function (Crypt) {
var hash = common.createRandomHash();
Crypt.put(hash, Messages.driveReadme, function (e) {
if (e) {
console.error("Error while creating the default pad:", e);
return void w();
}
var href = '/pad/#' + hash;
var data = {
href: href,
title: Messages.driveReadmeTitle,
atime: new Date().toISOString(),
ctime: new Date().toISOString()
};
common.getFO().pushData(data, function (e, id) {
if (e) {
console.error("Error while creating the default pad:", e);
return void w();
}
common.getFO().add(id);
w();
});
});
delete sessionStorage.createReadme;
});
}
}).nThen(function (waitFor) {
if (sessionStorage.migrateAnonDrive) {
var w = waitFor();
require(['/common/mergeDrive.js'], function (Merge) {
var hash = localStorage.FS_hash;
Merge.anonDriveIntoUser(getStore().getProxy(), hash, function () {
delete sessionStorage.migrateAnonDrive;
w();
});
});
}
}).nThen(function () {
updateLocalVersion();
common.addTooltips();

View File

@@ -4,7 +4,7 @@ define([
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
'/bower_components/textpatcher/TextPatcher.amd.js',
'/common/userObject.js',
'/common/migrate-user-object.js'
'/common/migrate-user-object.js',
], function ($, Listmap, Crypto, TextPatcher, FO, Migrate) {
/*
This module uses localStorage, which is synchronous, but exposes an
@@ -196,11 +196,11 @@ define([
Migrate(proxy, Cryptpad);
//storeObj = proxy;
store = initStore(fo, proxy, exp);
if (typeof(f) === 'function') {
f(void 0, store);
}
//storeObj = proxy;
var requestLogin = function () {
// log out so that you don't go into an endless loop...

View File

@@ -99,7 +99,7 @@ function context () {
} else if (k.substr(0, 5) === "data-") {
e.setAttribute(k, l[k])
} else {
e[k] = l[k]
e.setAttribute(k, l[k]);
}
}
} else if ('function' === typeof l) {

View File

@@ -36,7 +36,7 @@ define([], function () {
}
#cp-loading-tip {
position: fixed;
z-index: 100000;
z-index: 10000000;
top: 80%;
left: 0;
right: 0;

View File

@@ -83,9 +83,9 @@ define([
});
};
exp.anonDriveIntoUser = function (proxy, cb) {
exp.anonDriveIntoUser = function (proxyData, fsHash, cb) {
// Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb
if (!localStorage.FS_hash || !Cryptpad.isLoggedIn()) {
if (!fsHash || !Cryptpad.isLoggedIn()) {
if (typeof(cb) === "function") { return void cb(); }
}
// Get the content of FS_hash and then merge the objects, remove the migration key and cb
@@ -102,21 +102,23 @@ define([
return;
}
if (parsed) {
var proxy = proxyData.proxy;
var oldFo = FO.init(parsed.drive, {
Cryptpad: Cryptpad
});
var onMigrated = function () {
oldFo.fixFiles();
var newData = Cryptpad.getStore().getProxy();
var newFo = newData.fo;
var newFo = proxyData.fo;
var oldRecentPads = parsed.drive[newFo.FILES_DATA];
var newRecentPads = proxy.drive[newFo.FILES_DATA];
var newFiles = newFo.getFiles([newFo.FILES_DATA]);
var oldFiles = oldFo.getFiles([newFo.FILES_DATA]);
var newHrefs = Object.keys(newRecentPads).map(function (id) {
return newRecentPads[id].href;
});
oldFiles.forEach(function (id) {
var href = oldRecentPads[id].href;
// Do not migrate a pad if we already have it, it would create a duplicate in the drive
if (newFiles.indexOf(id) !== -1) { return; }
if (newHrefs.indexOf(href) !== -1) { return; }
// If we have a stronger version, do not add the current href
if (Cryptpad.findStronger(href, newRecentPads)) { return; }
// If we have a weaker version, replace the href by the new one
@@ -150,7 +152,7 @@ define([
if (!proxy.FS_hashes || !Array.isArray(proxy.FS_hashes)) {
proxy.FS_hashes = [];
}
proxy.FS_hashes.push(localStorage.FS_hash);
proxy.FS_hashes.push(fsHash);
if (typeof(cb) === "function") { cb(); }
};
oldFo.migrate(onMigrated);
@@ -158,7 +160,7 @@ define([
}
if (typeof(cb) === "function") { cb(); }
};
Crypt.get(localStorage.FS_hash, todo);
Crypt.get(fsHash, todo);
};
return exp;

View File

@@ -73,7 +73,6 @@ define(['json.sortify'], function (Sortify) {
});
};
console.log('here register');
sframeChan.on('EV_METADATA_UPDATE', function (ev) {
meta = ev;
if (ev.priv) {

View File

@@ -7,41 +7,13 @@ define([], function () {
return function (userObject, Cryptpad) {
var version = userObject.version || 0;
// DEPRECATED
// Migration 1: pad attributes moved to filesData
var migratePadAttributesToData = function () {
var files = userObject && userObject.drive;
if (!files) { return; }
var migratePadAttributes = function (el, id, parsed) {
// Migrate old pad attributes
['userid', 'previewMode'].forEach(function (attr) {
var key = parsed.hash + '.' + attr;
var key2 = parsed.hash.slice(0,-1) + '.' + attr;// old pads not ending with /
if (typeof(files[key]) !== "undefined" || typeof(files[key2]) !== "undefined") {
console.log("Migrating pad attribute", attr, "for pad", id);
el[attr] = files[key] || files[key2];
delete files[key];
delete files[key2];
}
});
};
var filesData = files.filesData;
if (!filesData) { return; }
var el, parsed;
for (var id in filesData) {
id = Number(id);
el = filesData[id];
parsed = el.href && Cryptpad.parsePadUrl(el.href);
if (!parsed) { continue; }
migratePadAttributes(el, id, parsed);
}
// Migration done
return true;
};
if (version < 1) {
migratePadAttributesToData();
Cryptpad.feedback('Migrate-1', true);
userObject.version = version = 1;
}
// Migration 2: global attributes from root to 'settings' subobjects
@@ -77,5 +49,19 @@ define([], function () {
Cryptpad.feedback('Migrate-2', true);
userObject.version = version = 2;
}
// Migration 3: language from localStorage to settings
var migrateLanguage = function () {
if (!localStorage.CRYPTPAD_LANG) { return; }
var l = localStorage.CRYPTPAD_LANG;
userObject.settings.language = l;
};
if (version < 3) {
migrateLanguage();
Cryptpad.feedback('Migrate-3', true);
userObject.version = version = 3;
}
};
});

View File

@@ -42,6 +42,26 @@ var afterLoaded = function (req) {
updated: updated,
cache: data.cache
};
data.localStore = data.localStore || {};
var lsUpdated = {};
window.cryptpadStore = {
get: function (k, cb) {
setTimeout(function () { cb(data.localStore[k]); });
},
getAll: function (cb) {
setTimeout(function () {
cb(JSON.parse(JSON.stringify(data.localStore)));
});
},
put: function (k, v, cb) {
cb = cb || function () { };
lsUpdated[k] = v;
setTimeout(function () { data.localStore[k] = v; cb(); });
},
updated: lsUpdated,
store: data.localStore
};
window.cryptpadLanguage = data.language;
require(['/common/sframe-boot2.js'], function () { });
};
window.addEventListener('message', onReply);

View File

@@ -0,0 +1,748 @@
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);
});
};
realtimeOptions.onReady = function (info) {
// 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;
};
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;
});

View File

@@ -46,7 +46,21 @@ define([
var metadataMgr = config.metadataMgr;
config = undefined;
var chainpad;
var chainpad = ChainPad.create({
userName: userName,
initialState: initialState,
transformFunction: transformFunction,
validateContent: validateContent,
avgSyncMilliseconds: avgSyncMilliseconds,
logLevel: logLevel
});
chainpad.onMessage(function(message, cb) {
sframeChan.query('Q_RT_MESSAGE', message, cb);
});
chainpad.onPatch(function () {
onRemote({ realtime: chainpad });
});
var myID;
var isReady = false;
var evConnected = Util.mkEvent(true);
@@ -67,33 +81,20 @@ define([
sframeChan.on('EV_RT_DISCONNECT', function () {
isReady = false;
if (chainpad) { chainpad.abort(); }
chainpad.abort();
onConnectionChange({ state: false });
});
sframeChan.on('EV_RT_CONNECT', function (content) {
//content.members.forEach(userList.onJoin);
myID = content.myID;
isReady = false;
if (chainpad) {
if (myID) {
// it's a reconnect
if (chainpad) { chainpad.start(); }
myID = content.myID;
chainpad.start();
onConnectionChange({ state: true, myId: myID });
return;
}
chainpad = ChainPad.create({
userName: userName,
initialState: initialState,
transformFunction: transformFunction,
validateContent: validateContent,
avgSyncMilliseconds: avgSyncMilliseconds,
logLevel: logLevel
});
chainpad.onMessage(function(message, cb) {
sframeChan.query('Q_RT_MESSAGE', message, cb);
});
chainpad.onPatch(function () {
onRemote({ realtime: chainpad });
});
myID = content.myID;
onInit({
myID: myID,
realtime: chainpad,
@@ -130,7 +131,8 @@ define([
getMyID: function () { return myID; },
metadataMgr: metadataMgr,
whenRealtimeSyncs: whenRealtimeSyncs,
onInfiniteSpinner: evInfiniteSpinner.reg
onInfiniteSpinner: evInfiniteSpinner.reg,
chainpad: chainpad,
});
};
return Object.freeze(module.exports);

View File

@@ -154,7 +154,9 @@ define([], function () {
// Do not remove wcObject, it allows us to use a new 'wc' without changing the handler if we
// want to keep the same chainpad (realtime) object
try {
if (window.Cryptpad_SUPPRESS_MSG) { return; }
wcObject.wc.bcast(message).then(function() {
if (window.Cryptpad_SUPPRESS_ACK) { return; }
cb();
}, function(err) {
// The message has not been sent, display the error.

View File

@@ -51,9 +51,7 @@ define([
$('<td>').text(Messages.cancel).appendTo($thead);
var createTableContainer = function ($body) {
console.log($body);
File.$container = $('<div>', { id: 'cp-fileupload' }).append($table).appendTo($body);
console.log('done');
return File.$container;
};
@@ -114,10 +112,13 @@ define([
};
onComplete = function (href) {
var mdMgr = common.getMetadataMgr();
var origin = mdMgr.getPrivateData().origin;
$link.prepend($('<span>', {'class': 'fa fa-external-link'}));
$link.attr('href', href)
.click(function (e) {
e.preventDefault();
window.open($link.attr('href'), '_blank');
window.open(origin + $link.attr('href'), '_blank');
});
var title = metadata.name;
Cryptpad.log(Messages._getKey('upload_success', [title]));
@@ -290,10 +291,22 @@ define([
onFileDrop(dropped, e);
});
};
var createCkeditorDropHandler = function () {
var editor = config.ckeditor;
editor.document.on('drop', function (ev) {
var dropped = ev.data.$.dataTransfer.files;
onFileDrop(dropped, ev);
ev.data.preventDefault(true);
});
};
var createUploader = function ($area, $hover, $body) {
if (!config.noHandlers) {
createAreaHandlers($area, null);
if (config.ckeditor) {
createCkeditorDropHandler();
} else {
createAreaHandlers($area, null);
}
}
createTableContainer($body);
};

View File

@@ -21,6 +21,15 @@ define([
var createRealtime = function () {
return ChainPad.create({
userName: 'history',
validateContent: function (content) {
try {
JSON.parse(content);
return true;
} catch (e) {
console.log('Failed to parse, rejecting patch');
return false;
}
},
initialState: '',
transformFunction: JsonOT.validate,
logLevel: 0,
@@ -69,9 +78,9 @@ define([
config.onLocal();
config.onRemote();
};
var onReady = function () {
config.setHistory(true);
};
config.setHistory(true);
var onReady = function () { };
var Messages = common.Messages;
var Cryptpad = common.getCryptpadCommon();

View File

@@ -1,12 +1,14 @@
define([
'jquery',
'/api/config',
'/common/cryptpad-common.js',
'/common/common-util.js',
'/common/media-tag.js',
'/common/tippy.min.js',
'/customize/application_config.js',
'css!/common/tippy.css',
], function ($, Cryptpad, MediaTag, Tippy, AppConfig) {
], function ($, Config, Cryptpad, Util, MediaTag, Tippy, AppConfig) {
var UI = {};
var Messages = Cryptpad.Messages;
@@ -182,11 +184,35 @@ define([
break;
case 'more':
button = $('<button>', {
title: Messages.moreActions || 'TODO',
title: Messages.moreActions,
'class': "cp-toolbar-drawer-button fa fa-ellipsis-h",
style: 'font:'+size+' FontAwesome'
});
break;
case 'savetodrive':
button = $('<button>', {
'class': 'fa fa-cloud-upload',
title: Messages.canvas_saveToDrive,
})
.click(common.prepareFeedback(type));
break;
case 'hashtag':
button = $('<button>', {
'class': 'fa fa-hashtag',
title: Messages.tags_title,
})
.click(common.prepareFeedback(type))
.click(function () {
sframeChan.query('Q_TAGS_GET', null, function (err, res) {
if (err || res.error) { return void console.error(err || res.error); }
Cryptpad.dialog.tagPrompt(res.data, function (tags) {
if (!Array.isArray(tags)) { return; }
console.error(tags);
sframeChan.event('EV_TAGS_SET', tags);
});
});
});
break;
default:
button = $('<button>', {
'class': "fa fa-question",
@@ -267,6 +293,103 @@ define([
}
};
/* Create a usage bar which keeps track of how much storage space is used
by your CryptDrive. The getPinnedUsage RPC is one of the heavier calls,
so we throttle its usage. Clients will not update more than once per
LIMIT_REFRESH_RATE. It will be update at least once every three such intervals
If changes are made to your drive in the interim, they will trigger an
update.
*/
var LIMIT_REFRESH_RATE = 30000; // milliseconds
UI.createUsageBar = function (common, cb) {
if (!common.isLoggedIn()) { return cb("NOT_LOGGED_IN"); }
// getPinnedUsage updates common.account.usage, and other values
// so we can just use those and only check for errors
var $container = $('<span>', {'class':'cp-limit-container'});
var todo;
var updateUsage = Cryptpad.notAgainForAnother(function () {
common.getPinUsage(todo);
}, LIMIT_REFRESH_RATE);
todo = function (err, data) {
if (err) { return void console.error(err); }
var usage = data.usage;
var limit = data.limit;
var plan = data.plan;
$container.html('');
var unit = Util.magnitudeOfBytes(limit);
usage = unit === 'GB'? Util.bytesToGigabytes(usage):
Util.bytesToMegabytes(usage);
limit = unit === 'GB'? Util.bytesToGigabytes(limit):
Util.bytesToMegabytes(limit);
var $limit = $('<span>', {'class': 'cp-limit-bar'}).appendTo($container);
var quota = usage/limit;
var $usage = $('<span>', {'class': 'cp-limit-usage'}).css('width', quota*100+'%');
var makeDonateButton = function () {
$('<a>', {
'class': 'cp-limit-upgrade btn btn-success',
href: Cryptpad.donateURL,
rel: "noreferrer noopener",
target: "_blank",
}).text(Messages.supportCryptpad).appendTo($container);
};
var makeUpgradeButton = function () {
$('<a>', {
'class': 'cp-limit-upgrade btn btn-success',
href: Cryptpad.upgradeURL,
rel: "noreferrer noopener",
target: "_blank",
}).text(Messages.upgradeAccount).appendTo($container);
};
if (!Config.removeDonateButton) {
if (!common.isLoggedIn() || !Config.allowSubscriptions) {
// user is not logged in, or subscriptions are disallowed
makeDonateButton();
} else if (!plan) {
// user is logged in and subscriptions are allowed
// and they don't have one. show upgrades
makeUpgradeButton();
} else {
// they have a plan. show nothing
}
}
var prettyUsage;
var prettyLimit;
if (unit === 'GB') {
prettyUsage = Messages._getKey('formattedGB', [usage]);
prettyLimit = Messages._getKey('formattedGB', [limit]);
} else {
prettyUsage = Messages._getKey('formattedMB', [usage]);
prettyLimit = Messages._getKey('formattedMB', [limit]);
}
if (quota < 0.8) { $usage.addClass('cp-limit-usage-normal'); }
else if (quota < 1) { $usage.addClass('cp-limit-usage-warning'); }
else { $usage.addClass('cp-limit-usage-above'); }
var $text = $('<span>', {'class': 'cp-limit-usage-text'});
$text.text(usage + ' / ' + prettyLimit);
$limit.append($usage).append($text);
};
setInterval(function () {
updateUsage();
}, LIMIT_REFRESH_RATE * 3);
updateUsage();
/*getProxy().on('change', ['drive'], function () {
updateUsage();
}); TODO*/
cb(null, $container);
};
UI.createUserAdminMenu = function (Common, config) {
var metadataMgr = Common.getMetadataMgr();
@@ -294,7 +417,7 @@ define([
$userAdminContent.append($userName);
options.push({
tag: 'p',
attributes: {'class': 'accountData'},
attributes: {'class': 'cp-toolbar-account'},
content: $userAdminContent.html()
});
}

View File

@@ -47,14 +47,21 @@ define([
localStorage.CRYPTPAD_URLARGS = ApiConfig.requireConf.urlArgs;
}
var cache = {};
var localStore = {};
Object.keys(localStorage).forEach(function (k) {
if (k.indexOf('CRYPTPAD_CACHE|') !== 0) { return; }
cache[k.slice(('CRYPTPAD_CACHE|').length)] = localStorage[k];
if (k.indexOf('CRYPTPAD_CACHE|') === 0) {
cache[k.slice(('CRYPTPAD_CACHE|').length)] = localStorage[k];
return;
}
if (k.indexOf('CRYPTPAD_STORE|') === 0) {
localStore[k.slice(('CRYPTPAD_STORE|').length)] = localStorage[k];
return;
}
});
SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) {
sframeChan = sfc;
}), false, { cache: cache });
}), false, { cache: cache, localStore: localStore, language: Cryptpad.getLanguage() });
Cryptpad.ready(waitFor());
}));
}).nThen(function (waitFor) {
@@ -64,8 +71,17 @@ define([
localStorage['CRYPTPAD_CACHE|' + k] = x[k];
});
});
sframeChan.on('EV_LOCALSTORE_PUT', function (x) {
Object.keys(x).forEach(function (k) {
if (typeof(x[k]) === "undefined") {
delete localStorage['CRYPTPAD_STORE|' + k];
return;
}
localStorage['CRYPTPAD_STORE|' + k] = x[k];
});
});
secret = Cryptpad.getSecrets();
secret = cfg.getSecrets ? cfg.getSecrets(Cryptpad) : Cryptpad.getSecrets();
if (!secret.channel) {
// New pad: create a new random channel id
secret.channel = Cryptpad.createChannelId();
@@ -231,23 +247,22 @@ define([
return null;
}
};
var msgs = [];
var onMsg = function (msg) {
var parsed = parse(msg);
if (parsed[0] === 'FULL_HISTORY_END') {
console.log('END');
cb();
cb(msgs);
return;
}
if (parsed[0] !== 'FULL_HISTORY') { return; }
if (parsed[1] && parsed[1].validateKey) { // First message
secret.keys.validateKey = parsed[1].validateKey;
return;
}
msg = parsed[1][4];
if (msg) {
msg = msg.replace(/^cp\|/, '');
var decryptedMsg = crypto.decrypt(msg, secret.keys.validateKey);
sframeChan.event('EV_RT_HIST_MESSAGE', decryptedMsg);
msgs.push(decryptedMsg);
}
};
network.on('message', onMsg);
@@ -282,6 +297,12 @@ define([
});
});
sframeChan.on('Q_SESSIONSTORAGE_PUT', function (data, cb) {
sessionStorage[data.key] = data.value;
cb();
});
// Present mode URL
sframeChan.on('Q_PRESENT_URL_GET_VALUE', function (data, cb) {
var parsed = Cryptpad.parsePadUrl(window.location.href);
@@ -373,6 +394,35 @@ define([
}
});
sframeChan.on('EV_OPEN_URL', function (url) {
if (url) {
window.open(url);
}
});
sframeChan.on('Q_TAGS_GET', function (data, cb) {
Cryptpad.getPadTags(null, function (err, data) {
cb({
error: err,
data: data
});
});
});
sframeChan.on('EV_TAGS_SET', function (data) {
console.log(data);
Cryptpad.resetTags(null, data);
});
sframeChan.on('Q_PIN_GET_USAGE', function (data, cb) {
Cryptpad.isOverPinLimit(function (err, overLimit, data) {
cb({
error: err,
data: data
});
});
});
if (cfg.addRpc) {
cfg.addRpc(sframeChan, Cryptpad);
}
@@ -487,7 +537,7 @@ define([
CpNfOuter.start({
sframeChan: sframeChan,
channel: secret.channel,
network: Cryptpad.getNetwork(),
network: cfg.newNetwork || Cryptpad.getNetwork(),
validateKey: secret.keys.validateKey || undefined,
readOnly: readOnly,
crypto: Crypto.createEncryptor(secret.keys),
@@ -499,7 +549,7 @@ define([
});
return;
}
if (readOnly) { return; }
if (readOnly || cfg.noHash) { return; }
Cryptpad.replaceHash(Cryptpad.getEditHashFromKeys(wc.id, secret.keys));
}
});

View File

@@ -77,6 +77,7 @@ define([
funcs.openTemplatePicker = callWithCommon(UI.openTemplatePicker);
funcs.displayAvatar = callWithCommon(UI.displayAvatar);
funcs.createButton = callWithCommon(UI.createButton);
funcs.createUsageBar = callWithCommon(UI.createUsageBar);
// History
funcs.getHistory = callWithCommon(History.create);
@@ -156,6 +157,12 @@ define([
if (cb) { cb(data); }
});
};
funcs.getPinUsage = function (cb) {
cb = cb || $.noop;
ctx.sframeChan.query('Q_PIN_GET_USAGE', null, function (err, data) {
cb(err || data.error, data.data);
});
};
funcs.isOverPinLimit = function (cb) {
ctx.sframeChan.query('Q_GET_PIN_LIMIT_STATUS', null, function (err, data) {
@@ -164,10 +171,14 @@ define([
};
funcs.getFullHistory = function (realtime, cb) {
ctx.sframeChan.on('EV_RT_HIST_MESSAGE', function (content) {
realtime.message(content);
ctx.sframeChan.query('Q_GET_FULL_HISTORY', null, function (err, messages) {
if (err) { return void console.error(err); }
if (!Array.isArray(messages)) { return; }
messages.forEach(function (m) {
realtime.message(m);
});
cb();
});
ctx.sframeChan.query('Q_GET_FULL_HISTORY', null, cb);
};
funcs.getPadAttribute = function (key, cb) {
@@ -199,6 +210,15 @@ define([
}, cb);
};
funcs.sessionStorage = {
put: function (key, value, cb) {
ctx.sframeChan.query('Q_SESSIONSTORAGE_PUT', {
key: key,
value: value
}, cb);
}
};
funcs.isStrongestStored = function () {
var data = ctx.metadataMgr.getPrivateData();
if (data.availableHashes.fileHash) { return true; }
@@ -210,6 +230,9 @@ define([
ctx.sframeChan.query('Q_SETTINGS_SET_DISPLAY_NAME', name, cb);
};
funcs.mergeAnonDrive = function (cb) {
ctx.sframeChan.query('Q_MERGE_ANON_DRIVE', null, cb);
};
// Friends
var pendingFriends = [];
funcs.getPendingFriends = function () {
@@ -260,6 +283,7 @@ define([
}; */
funcs.gotoURL = function (url) { ctx.sframeChan.event('EV_GOTO_URL', url); };
funcs.openURL = function (url) { ctx.sframeChan.event('EV_OPEN_URL', url); };
funcs.whenRealtimeSyncs = evRealtimeSynced.reg;
@@ -283,6 +307,18 @@ define([
ctx.sframeChan.event('EV_CACHE_PUT', x);
};
});
ctx.sframeChan.whenReg('EV_LOCALSTORE_PUT', function () {
if (Object.keys(window.cryptpadStore.updated).length) {
ctx.sframeChan.event('EV_LOCALSTORE_PUT', window.cryptpadStore.updated);
}
window.cryptpadStore._put = window.cryptpadStore.put;
window.cryptpadStore.put = function (k, v, cb) {
window.cryptpadStore._put(k, v, cb);
var x = {};
x[k] = v;
ctx.sframeChan.event('EV_LOCALSTORE_PUT', x);
};
});
UI.addTooltips();

View File

@@ -67,6 +67,9 @@ define({
// Use anonymous rpc from inside the iframe (for avatars & pin usage).
'Q_ANON_RPC_MESSAGE': true,
// Get the user's pin limit, usage and plan
'Q_PIN_GET_USAGE': true,
// Check the pin limit to determine if we can store the pad in the drive or if we should.
// display a warning
'Q_GET_PIN_LIMIT_STATUS': true,
@@ -123,12 +126,16 @@ define({
// Make the browser window navigate to a given URL, if no URL is passed then it will reload.
'EV_GOTO_URL': true,
// Make the parent window open a given URL in a new tab. It allows us to keep sessionStorage
// form the parent window.
'EV_OPEN_URL': true,
// Present mode URL
'Q_PRESENT_URL_GET_VALUE': true,
'EV_PRESENT_URL_SET_VALUE': true,
// Put one or more entries to the cache which will go in localStorage.
// Cache is wiped after each new release
'EV_CACHE_PUT': true,
// Contacts
@@ -147,4 +154,17 @@ define({
'Q_CONTACTS_SEND_MESSAGE': true,
'Q_CONTACTS_SET_CHANNEL_HEAD': true,
// Put one or more entries to the localStore which will go in localStorage.
'EV_LOCALSTORE_PUT': true,
// Put one entry in the parent sessionStorage
'Q_SESSIONSTORAGE_PUT': true,
// Set and get the tags using the tag prompt button
'Q_TAGS_GET': true,
'EV_TAGS_SET': true,
// Merge the anonymous drive (FS_hash) into the current logged in user's drive, to keep the pads
// in the drive at registration.
'Q_MERGE_ANON_DRIVE': true,
});

View File

@@ -688,7 +688,7 @@ define([
};
var typing = -1;
var kickSpinner = function (toolbar, config, local) {
var kickSpinner = function (toolbar, config/*, local*/) {
if (!toolbar.spinner) { return; }
var $spin = toolbar.spinner;
@@ -708,7 +708,7 @@ define([
window.clearInterval($spin.interval);
typing = -1;
$spin.text(Messages.saved);
}, local ? 0 : SPINNER_DISAPPEAR_TIME);
}, /*local ? 0 :*/ SPINNER_DISAPPEAR_TIME);
};
config.sfCommon.whenRealtimeSyncs(onSynced);
};

View File

@@ -18,6 +18,7 @@ define([
var exp = {};
var Cryptpad = config.Cryptpad;
var Messages = Cryptpad.Messages;
var loggedIn = config.loggedIn || Cryptpad.isLoggedIn();
var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Cryptpad.storageKey;
var OLD_FILES_DATA = module.OLD_FILES_DATA = exp.OLD_FILES_DATA = Cryptpad.oldStorageKey;
@@ -415,6 +416,7 @@ define([
var containsSearchedTag = function (T) {
if (!tags) { return false; }
if (!T.length) { return false; }
T = T.map(function (t) { return t.toLowerCase(); });
return tags.some(function (tag) {
return T.some(function (t) {
return t.indexOf(tag) !== -1;
@@ -424,6 +426,7 @@ define([
getFiles([FILES_DATA]).forEach(function (id) {
var data = allFilesList[id];
if (!data) { return; }
if (Array.isArray(data.tags) && containsSearchedTag(data.tags)) {
res.push(id);
} else
@@ -446,6 +449,7 @@ define([
res.forEach(function (l) {
//var paths = findFile(l);
ret.push({
id: l,
paths: findFile(l),
data: exp.getFileData(l)
});
@@ -454,7 +458,7 @@ define([
};
exp.getRecentPads = function () {
var allFiles = files[FILES_DATA];
var sorted = Object.keys(allFiles)
var sorted = Object.keys(allFiles).filter(function (a) { return allFiles[a]; })
.sort(function (a,b) {
return allFiles[a].atime < allFiles[b].atime;
})
@@ -479,13 +483,14 @@ define([
// FILES DATA
exp.pushData = function (data, cb) {
// TODO: can only be called from outside atm
if (typeof cb !== "function") { cb = function () {}; }
var todo = function () {
var id = Cryptpad.createRandomInteger();
files[FILES_DATA][id] = data;
cb(null, id);
};
if (!Cryptpad.isLoggedIn() || !AppConfig.enablePinning || config.testMode) {
if (!loggedIn || !AppConfig.enablePinning || config.testMode) {
return void todo();
}
Cryptpad.pinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e) {
@@ -583,7 +588,7 @@ define([
// ADD
var add = exp.add = function (id, path) {
if (!Cryptpad.isLoggedIn() && !config.testMode) { return; }
if (!loggedIn && !config.testMode) { return; }
var data = files[FILES_DATA][id];
if (!data || typeof(data) !== "object") { return; }
var newPath = path, parentEl;
@@ -622,7 +627,7 @@ define([
exp.forget = function (href) {
var id = getIdFromHref(href);
if (!id) { return; }
if (!Cryptpad.isLoggedIn() && !config.testMode) {
if (!loggedIn && !config.testMode) {
// delete permanently
exp.removePadAttribute(href);
spliceFileData(id);
@@ -651,7 +656,7 @@ define([
};
var checkDeletedFiles = function () {
// Nothing in OLD_FILES_DATA for workgroups
if (workgroup || (!Cryptpad.isLoggedIn() && !config.testMode)) { return; }
if (workgroup || (!loggedIn && !config.testMode)) { return; }
var filesList = getFiles([ROOT, 'hrefArray', TRASH]);
getFiles([FILES_DATA]).forEach(function (id) {
@@ -678,7 +683,7 @@ define([
var trashPaths = paths.filter(function(x) { return isPathIn(x, [TRASH]); });
var allFilesPaths = paths.filter(function(x) { return isPathIn(x, [FILES_DATA]); });
if (!Cryptpad.isLoggedIn() && !config.testMode) {
if (!loggedIn && !config.testMode) {
allFilesPaths.forEach(function (path) {
var el = find(path);
if (!el) { return; }
@@ -853,6 +858,7 @@ define([
}
try {
debug("Migrating file system...");
// TODO
Cryptpad.feedback('Migrate-oldFilesData', true);
files.migrate = 1;
var next = function () {
@@ -902,6 +908,7 @@ define([
};
if (exp.rt) {
exp.rt.sync();
// TODO
Cryptpad.whenRealtimeSyncs(exp.rt, next);
} else {
window.setTimeout(next, 1000);
@@ -1035,20 +1042,6 @@ define([
}
});
};
var migrateAttributes = function (el, id, parsed) {
// Migrate old pad attributes
['userid', 'previewMode'].forEach(function (attr) {
var key = parsed.hash + '.' + attr;
var key2 = parsed.hash.slice(0,-1) + '.' + attr;// old pads not ending with /
if (typeof(files[key]) !== "undefined" || typeof(files[key2]) !== "undefined") {
debug("Migrating pad attribute", attr, "for pad", id);
el[attr] = files[key] || files[key2];
delete files[key];
delete files[key2];
}
});
// Migration done
};
var fixFilesData = function () {
if (typeof files[FILES_DATA] !== "object") { debug("OLD_FILES_DATA was not an object"); files[FILES_DATA] = {}; }
var fd = files[FILES_DATA];
@@ -1075,9 +1068,7 @@ define([
continue;
}
migrateAttributes(el, id, parsed);
if ((Cryptpad.isLoggedIn() || config.testMode) && rootFiles.indexOf(id) === -1) {
if ((loggedIn || config.testMode) && rootFiles.indexOf(id) === -1) {
debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el);
var newName = Cryptpad.createChannelId();
root[newName] = id;