manual merge, still wip
This commit is contained in:
@@ -120,6 +120,7 @@ define([
|
||||
}
|
||||
console.log('CACHE MISS ' + url);
|
||||
((/\.less([\?\#].*)?$/.test(url)) ? loadLess : loadCSS)(url, function (err, css) {
|
||||
if (err) { console.error(err); }
|
||||
var output = fixAllURLs(css, url);
|
||||
cachePut(url, output);
|
||||
inject(output, url);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -129,6 +129,10 @@ define([
|
||||
return messenger.range_requests[txid];
|
||||
};
|
||||
|
||||
var deleteRangeRequest = function (txid) {
|
||||
delete messenger.range_requests[txid];
|
||||
};
|
||||
|
||||
messenger.getMoreHistory = function (curvePublic, hash, count, cb) {
|
||||
if (typeof(cb) !== 'function') { return; }
|
||||
|
||||
@@ -393,7 +397,8 @@ define([
|
||||
});
|
||||
|
||||
orderMessages(curvePublic, decrypted, req.sig);
|
||||
return void req.cb(void 0, decrypted);
|
||||
req.cb(void 0, decrypted);
|
||||
return deleteRangeRequest(txid);
|
||||
} else {
|
||||
console.log(parsed);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ define([], function () {
|
||||
handlers.splice(handlers.indexOf(cb), 1);
|
||||
},
|
||||
fire: function () {
|
||||
if (fired) { return; }
|
||||
if (once && fired) { return; }
|
||||
fired = true;
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
handlers.forEach(function (h) { h.apply(null, args); });
|
||||
@@ -203,5 +203,9 @@ define([], function () {
|
||||
};
|
||||
};
|
||||
|
||||
Util.slice = function (A) {
|
||||
return Array.prototype.slice.call(A);
|
||||
};
|
||||
|
||||
return Util;
|
||||
});
|
||||
|
||||
@@ -20,9 +20,10 @@ define([
|
||||
'/common/pinpad.js',
|
||||
'/customize/application_config.js',
|
||||
'/common/media-tag.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata,
|
||||
Messaging, CodeMirror, Files, FileCrypto, Realtime, Clipboard,
|
||||
Pinpad, AppConfig, MediaTag) {
|
||||
Pinpad, AppConfig, MediaTag, Nthen) {
|
||||
|
||||
// Configure MediaTags to use our local viewer
|
||||
if (MediaTag && MediaTag.PdfPlugin) {
|
||||
@@ -102,6 +103,7 @@ define([
|
||||
common.getAppType = Util.getAppType;
|
||||
common.notAgainForAnother = Util.notAgainForAnother;
|
||||
common.uid = Util.uid;
|
||||
common.slice = Util.slice;
|
||||
|
||||
// import hash utilities for export
|
||||
var createRandomHash = common.createRandomHash = Hash.createRandomHash;
|
||||
@@ -182,6 +184,9 @@ define([
|
||||
}
|
||||
return;
|
||||
};
|
||||
common.getLanguage = function () {
|
||||
return Messages._languageUsed;
|
||||
};
|
||||
common.getUserlist = function () {
|
||||
if (store) {
|
||||
if (store.getProxy() && store.getProxy().info) {
|
||||
@@ -531,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');
|
||||
@@ -2071,23 +2087,8 @@ define([
|
||||
|
||||
return function (f) {
|
||||
if (initialized) {
|
||||
return void window.setTimeout(function () {
|
||||
f(void 0, env);
|
||||
});
|
||||
return void setTimeout(function () { f(void 0, env); });
|
||||
}
|
||||
var block = 0;
|
||||
|
||||
var cb = function () {
|
||||
block--;
|
||||
if (!block) {
|
||||
initialized = true;
|
||||
|
||||
updateLocalVersion();
|
||||
common.addTooltips();
|
||||
f(void 0, env);
|
||||
if (typeof(window.onhashchange) === 'function') { window.onhashchange(); }
|
||||
}
|
||||
};
|
||||
|
||||
if (sessionStorage[newPadNameKey]) {
|
||||
common.initialName = sessionStorage[newPadNameKey];
|
||||
@@ -2097,7 +2098,6 @@ define([
|
||||
common.initialPath = sessionStorage[newPadPathKey];
|
||||
delete sessionStorage[newPadPathKey];
|
||||
}
|
||||
|
||||
common.onFriendRequest = function (confirmText, cb) {
|
||||
common.confirm(confirmText, cb, null, true);
|
||||
};
|
||||
@@ -2105,20 +2105,9 @@ define([
|
||||
common.log(data.logText);
|
||||
};
|
||||
|
||||
Store.ready(function (err, storeObj) {
|
||||
store = common.store = env.store = storeObj;
|
||||
common.addDirectMessageHandler(common);
|
||||
|
||||
var proxy = getProxy();
|
||||
var network = getNetwork();
|
||||
|
||||
network.on('disconnect', function () {
|
||||
Realtime.setConnectionState(false);
|
||||
});
|
||||
network.on('reconnect', function () {
|
||||
Realtime.setConnectionState(true);
|
||||
});
|
||||
|
||||
var proxy;
|
||||
var network;
|
||||
var provideFeedback = function () {
|
||||
if (Object.keys(proxy).length === 1) {
|
||||
feedback("FIRST_APP_USE", true);
|
||||
}
|
||||
@@ -2141,110 +2130,123 @@ define([
|
||||
}
|
||||
common.reportScreenDimensions();
|
||||
common.reportLanguage();
|
||||
};
|
||||
|
||||
$(function() {
|
||||
// Race condition : if document.body is undefined when alertify.js is loaded, Alertify
|
||||
// won't work. We have to reset it now to make sure it uses a correct "body"
|
||||
UI.Alertify.reset();
|
||||
// clear any tooltips that might get hung
|
||||
setInterval(function () { common.clearTooltips(); }, 5000);
|
||||
|
||||
// Load the new pad when the hash has changed
|
||||
var oldHref = document.location.href;
|
||||
window.onhashchange = function () {
|
||||
var newHref = document.location.href;
|
||||
var parsedOld = parsePadUrl(oldHref).hashData;
|
||||
var parsedNew = parsePadUrl(newHref).hashData;
|
||||
if (parsedOld && parsedNew && (
|
||||
parsedOld.type !== parsedNew.type
|
||||
|| parsedOld.channel !== parsedNew.channel
|
||||
|| parsedOld.mode !== parsedNew.mode
|
||||
|| parsedOld.key !== parsedNew.key)) {
|
||||
if (!parsedOld.channel) { oldHref = newHref; return; }
|
||||
document.location.reload();
|
||||
return;
|
||||
}
|
||||
if (parsedNew) {
|
||||
oldHref = newHref;
|
||||
}
|
||||
};
|
||||
|
||||
if (PINNING_ENABLED && isLoggedIn()) {
|
||||
console.log("logged in. pads will be pinned");
|
||||
block++;
|
||||
|
||||
Pinpad.create(network, proxy, function (e, call) {
|
||||
if (e) {
|
||||
console.error(e);
|
||||
return cb();
|
||||
}
|
||||
|
||||
console.log('RPC handshake complete');
|
||||
rpc = common.rpc = env.rpc = call;
|
||||
|
||||
common.getPinLimit(function (e, limit, plan, note) {
|
||||
if (e) { return void console.error(e); }
|
||||
common.account.limit = limit;
|
||||
localStorage.plan = common.account.plan = plan;
|
||||
common.account.note = note;
|
||||
cb();
|
||||
});
|
||||
|
||||
common.arePinsSynced(function (err, yes) {
|
||||
if (!yes) {
|
||||
common.resetPins(function (err) {
|
||||
if (err) {
|
||||
console.error("Pin Reset Error");
|
||||
return console.error(err);
|
||||
}
|
||||
console.log('RESET DONE');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (PINNING_ENABLED) {
|
||||
console.log('not logged in. pads will not be pinned');
|
||||
} else {
|
||||
console.log('pinning disabled');
|
||||
}
|
||||
|
||||
block++;
|
||||
require([
|
||||
'/common/rpc.js',
|
||||
], function (Rpc) {
|
||||
Rpc.createAnonymous(network, function (e, call) {
|
||||
if (e) {
|
||||
console.error(e);
|
||||
return void cb();
|
||||
}
|
||||
anon_rpc = common.anon_rpc = env.anon_rpc = call;
|
||||
cb();
|
||||
});
|
||||
Nthen(function (waitFor) {
|
||||
Store.ready(waitFor(function (err, storeObj) {
|
||||
store = common.store = env.store = storeObj;
|
||||
common.addDirectMessageHandler(common);
|
||||
proxy = getProxy();
|
||||
network = getNetwork();
|
||||
network.on('disconnect', function () {
|
||||
Realtime.setConnectionState(false);
|
||||
});
|
||||
network.on('reconnect', function () {
|
||||
Realtime.setConnectionState(true);
|
||||
});
|
||||
provideFeedback();
|
||||
}), common);
|
||||
}).nThen(function (waitFor) {
|
||||
$(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
// Race condition : if document.body is undefined when alertify.js is loaded, Alertify
|
||||
// won't work. We have to reset it now to make sure it uses a correct "body"
|
||||
UI.Alertify.reset();
|
||||
// clear any tooltips that might get hung
|
||||
setInterval(function () { common.clearTooltips(); }, 5000);
|
||||
|
||||
|
||||
// Everything's ready, continue...
|
||||
if($('#pad-iframe').length) {
|
||||
block++;
|
||||
var $iframe = $('#pad-iframe');
|
||||
var iframe = $iframe[0];
|
||||
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
if (iframeDoc.readyState === 'complete') {
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
$iframe.load(cb);
|
||||
// Load the new pad when the hash has changed
|
||||
var oldHref = document.location.href;
|
||||
window.onhashchange = function () {
|
||||
var newHref = document.location.href;
|
||||
var parsedOld = parsePadUrl(oldHref).hashData;
|
||||
var parsedNew = parsePadUrl(newHref).hashData;
|
||||
if (parsedOld && parsedNew && (
|
||||
parsedOld.type !== parsedNew.type
|
||||
|| parsedOld.channel !== parsedNew.channel
|
||||
|| parsedOld.mode !== parsedNew.mode
|
||||
|| parsedOld.key !== parsedNew.key)) {
|
||||
if (!parsedOld.channel) { oldHref = newHref; return; }
|
||||
document.location.reload();
|
||||
return;
|
||||
}
|
||||
if (parsedNew) { oldHref = newHref; }
|
||||
};
|
||||
|
||||
block++;
|
||||
cb();
|
||||
if (PINNING_ENABLED && isLoggedIn()) {
|
||||
console.log("logged in. pads will be pinned");
|
||||
var w0 = waitFor();
|
||||
Pinpad.create(network, proxy, function (e, call) {
|
||||
if (e) {
|
||||
console.error(e);
|
||||
return w0();
|
||||
}
|
||||
|
||||
console.log('RPC handshake complete');
|
||||
rpc = common.rpc = env.rpc = call;
|
||||
|
||||
common.getPinLimit(function (e, limit, plan, note) {
|
||||
if (e) { return void console.error(e); }
|
||||
common.account.limit = limit;
|
||||
localStorage.plan = common.account.plan = plan;
|
||||
common.account.note = note;
|
||||
w0();
|
||||
});
|
||||
|
||||
common.arePinsSynced(function (err, yes) {
|
||||
if (!yes) {
|
||||
common.resetPins(function (err) {
|
||||
if (err) {
|
||||
console.error("Pin Reset Error");
|
||||
return console.error(err);
|
||||
}
|
||||
console.log('RESET DONE');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (PINNING_ENABLED) {
|
||||
console.log('not logged in. pads will not be pinned');
|
||||
} else {
|
||||
console.log('pinning disabled');
|
||||
}
|
||||
|
||||
var w1 = waitFor();
|
||||
require([
|
||||
'/common/rpc.js',
|
||||
], function (Rpc) {
|
||||
Rpc.createAnonymous(network, function (e, call) {
|
||||
if (e) {
|
||||
console.error(e);
|
||||
return void w1();
|
||||
}
|
||||
anon_rpc = common.anon_rpc = env.anon_rpc = call;
|
||||
w1();
|
||||
});
|
||||
});
|
||||
}, common);
|
||||
|
||||
// Everything's ready, continue...
|
||||
if($('#pad-iframe').length) {
|
||||
var w2 = waitFor();
|
||||
var $iframe = $('#pad-iframe');
|
||||
var iframe = $iframe[0];
|
||||
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
if (iframeDoc.readyState === 'complete') {
|
||||
return void w2();
|
||||
}
|
||||
$iframe.load(w2); //cb);
|
||||
}
|
||||
}).nThen(function () {
|
||||
updateLocalVersion();
|
||||
common.addTooltips();
|
||||
f(void 0, env);
|
||||
if (typeof(window.onhashchange) === 'function') { window.onhashchange(); }
|
||||
});
|
||||
};
|
||||
|
||||
}());
|
||||
|
||||
// MAGIC that happens implicitly
|
||||
$(function () {
|
||||
Messages._applyTranslation();
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -77,5 +77,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;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -3,15 +3,11 @@ define([
|
||||
'/bower_components/hyperjson/hyperjson.js',
|
||||
'/common/toolbar3.js',
|
||||
'/bower_components/chainpad-json-validator/json-ot.js',
|
||||
'/common/TypingTests.js',
|
||||
'json.sortify',
|
||||
'/bower_components/textpatcher/TextPatcher.js',
|
||||
'/common/cryptpad-common.js',
|
||||
'/common/cryptget.js',
|
||||
'/pad/links.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/common/sframe-common.js',
|
||||
'/api/config',
|
||||
'/customize/messages.js',
|
||||
'/common/common-util.js',
|
||||
|
||||
@@ -23,15 +19,11 @@ define([
|
||||
Hyperjson,
|
||||
Toolbar,
|
||||
JsonOT,
|
||||
TypingTest,
|
||||
JSONSortify,
|
||||
TextPatcher,
|
||||
Cryptpad,
|
||||
Cryptget,
|
||||
Links,
|
||||
nThen,
|
||||
SFCommon,
|
||||
ApiConfig,
|
||||
Messages,
|
||||
Util)
|
||||
{
|
||||
@@ -90,12 +82,8 @@ define([
|
||||
return meta;
|
||||
};
|
||||
|
||||
var isEditable = function () {
|
||||
return (state === STATE.READY && !readOnly);
|
||||
};
|
||||
|
||||
var stateChange = function (newState) {
|
||||
var wasEditable = isEditable();
|
||||
var wasEditable = (state === STATE.READY);
|
||||
if (state === STATE.INFINITE_SPINNER) { return; }
|
||||
if (newState === STATE.INFINITE_SPINNER) {
|
||||
state = newState;
|
||||
@@ -106,6 +94,7 @@ define([
|
||||
} else {
|
||||
state = newState;
|
||||
}
|
||||
console.log(state + ' ' + wasEditable);
|
||||
switch (state) {
|
||||
case STATE.DISCONNECTED:
|
||||
case STATE.INITIALIZING: {
|
||||
@@ -118,7 +107,10 @@ define([
|
||||
}
|
||||
default:
|
||||
}
|
||||
if (wasEditable !== isEditable()) { evEditableStateChange.fire(isEditable()); }
|
||||
if (wasEditable !== (state === STATE.READY)) {
|
||||
console.log("fire");
|
||||
evEditableStateChange.fire(state === STATE.READY);
|
||||
}
|
||||
};
|
||||
|
||||
var onRemote = function () {
|
||||
|
||||
@@ -42,6 +42,7 @@ var afterLoaded = function (req) {
|
||||
updated: updated,
|
||||
cache: data.cache
|
||||
};
|
||||
window.cryptpadLanguage = data.language;
|
||||
require(['/common/sframe-boot2.js'], function () { });
|
||||
};
|
||||
window.addEventListener('message', onReply);
|
||||
|
||||
748
www/common/sframe-chainpad-listmap.js
Normal file
748
www/common/sframe-chainpad-listmap.js
Normal 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;
|
||||
});
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -182,11 +182,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",
|
||||
@@ -294,7 +318,7 @@ define([
|
||||
$userAdminContent.append($userName);
|
||||
options.push({
|
||||
tag: 'p',
|
||||
attributes: {'class': 'accountData'},
|
||||
attributes: {'class': 'cp-toolbar-account'},
|
||||
content: $userAdminContent.html()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ define([
|
||||
|
||||
SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) {
|
||||
sframeChan = sfc;
|
||||
}), false, { cache: cache });
|
||||
}), false, { cache: cache, language: Cryptpad.getLanguage() });
|
||||
Cryptpad.ready(waitFor());
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
@@ -228,23 +228,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);
|
||||
@@ -370,6 +369,20 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
if (cfg.addRpc) {
|
||||
cfg.addRpc(sframeChan, Cryptpad);
|
||||
}
|
||||
|
||||
@@ -164,10 +164,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) {
|
||||
|
||||
@@ -130,4 +130,9 @@ define({
|
||||
|
||||
// Put one or more entries to the cache which will go in localStorage.
|
||||
'EV_CACHE_PUT': true,
|
||||
|
||||
// Set and get the tags using the tag prompt button
|
||||
'Q_TAGS_GET': true,
|
||||
'EV_TAGS_SET': true,
|
||||
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -415,6 +415,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;
|
||||
@@ -446,6 +447,7 @@ define([
|
||||
res.forEach(function (l) {
|
||||
//var paths = findFile(l);
|
||||
ret.push({
|
||||
id: l,
|
||||
paths: findFile(l),
|
||||
data: exp.getFileData(l)
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user