',
'Voici CryptPad , l\'éditeur collaboratif en temps-réel Zero Knowledge. Tout est sauvegardé dés que vous le tapez.',
' ',
'Partagez le lien vers ce pad avec des amis ou utilisez le bouton Partager pour obtenir le lien de lecture-seule , qui permet la lecture mais non la modification.',
'
',
- '',
+ '',
'',
'Lancez-vous, commencez à taper...',
'
',
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index 66f771033..8ab0e2084 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -300,6 +300,12 @@ define(function () {
out.settings_anonymous = "You are not logged in. Settings here are specific to this browser.";
out.settings_publicSigningKey = "Public Signing Key";
+ out.settings_usage = "Usage";
+ out.settings_usageTitle = "See the total size of your pinned pads in MB";
+ out.settings_pinningNotAvailable = "Pinned pads are only available to registered users.";
+ out.settings_pinningError = "Something went wrong";
+ out.settings_usageAmount = "Your pinned pads occupy {0}MB";
+
// index.html
@@ -386,7 +392,7 @@ define(function () {
// Initial states
out.initialState = [
- '',
+ '',
'This is CryptPad , the Zero Knowledge realtime collaborative editor. Everything is saved as you type.',
' ',
'Share the link to this pad to edit with friends or use the Share button to share a read-only link which allows viewing but not editing.',
diff --git a/package.json b/package.json
index 882eeaa15..48aed5d0d 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
"scripts": {
"lint": "jshint --config .jshintrc --exclude-path .jshintignore .",
"test": "node TestSelenium.js",
- "style": "lessc ./customize.dist/src/less/cryptpad.less > ./customize.dist/main.css && lessc ./customize.dist/src/less/toolbar.less > ./customize.dist/toolbar.css && lessc ./www/drive/file.less > ./www/drive/file.css && lessc ./www/settings/main.less > ./www/settings/main.css && lessc ./www/slide/slide.less > ./www/slide/slide.css && lessc ./www/whiteboard/whiteboard.less > ./www/whiteboard/whiteboard.css",
+ "style": "lessc ./customize.dist/src/less/cryptpad.less > ./customize.dist/main.css && lessc ./customize.dist/src/less/toolbar.less > ./customize.dist/toolbar.css && lessc ./www/drive/file.less > ./www/drive/file.css && lessc ./www/settings/main.less > ./www/settings/main.css && lessc ./www/slide/slide.less > ./www/slide/slide.css && lessc ./www/whiteboard/whiteboard.less > ./www/whiteboard/whiteboard.css && lessc ./www/poll/poll.less > ./www/poll/poll.css",
"template": "cd customize.dist/src && node build.js"
}
}
diff --git a/www/assert/main.js b/www/assert/main.js
index b6a1fbd45..eb9bb9157 100644
--- a/www/assert/main.js
+++ b/www/assert/main.js
@@ -1,12 +1,10 @@
-require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([
- '/bower_components/jquery/dist/jquery.min.js',
+ 'jquery',
'/bower_components/hyperjson/hyperjson.js',
'/bower_components/textpatcher/TextPatcher.amd.js',
'json.sortify',
'/common/cryptpad-common.js',
-], function (jQuery, Hyperjson, TextPatcher, Sortify, Cryptpad) {
- var $ = window.jQuery;
+], function ($, Hyperjson, TextPatcher, Sortify, Cryptpad) {
window.Hyperjson = Hyperjson;
window.TextPatcher = TextPatcher;
window.Sortify = Sortify;
diff --git a/www/assert/media/main.js b/www/assert/media/main.js
index 31a496893..e60fa298b 100644
--- a/www/assert/media/main.js
+++ b/www/assert/media/main.js
@@ -1,8 +1,4 @@
-define([
- '/bower_components/jquery/dist/jquery.min.js',
-], function () {
- var $ = window.jQuery;
-
+define(['jquery'], function ($) {
$('media').each(function () {
window.alert("media tag selection works!");
});
diff --git a/www/assert/pretty/main.js b/www/assert/pretty/main.js
index 1b8272884..825ad03ac 100644
--- a/www/assert/pretty/main.js
+++ b/www/assert/pretty/main.js
@@ -1,8 +1,7 @@
define([
- '/bower_components/hyperjson/hyperjson.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Hyperjson) {
- var $ = window.jQuery;
+ 'jquery',
+ '/bower_components/hyperjson/hyperjson.js'
+], function ($, Hyperjson) {
var shjson = '["BODY",{"class":"cke_editable cke_editable_themed cke_contents_ltr cke_show_borders","spellcheck":"false"},[["P",{},["This is ",["STRONG",{},["CryptPad"]],", the zero knowledge realtime collaborative editor.",["BR",{},[]],"What you type here is encrypted so only people who have the link can access it.",["BR",{},[]],"Even the server cannot see what you type."]],["P",{},[["SMALL",{},[["I",{},["What you see here, what you hear here, when you leave here, let it stay here"]]]],["BR",{"type":"_moz"},[]]]]]]';
var hjson = JSON.parse(shjson);
diff --git a/www/assert/translations/main.js b/www/assert/translations/main.js
index e8825d422..9a5397c73 100644
--- a/www/assert/translations/main.js
+++ b/www/assert/translations/main.js
@@ -1,9 +1,8 @@
define([
- '/bower_components/jquery/dist/jquery.min.js',
+ 'jquery',
'/common/cryptpad-common.js',
'/customize/translations/messages.js',
-], function (jQuery, Cryptpad, English) {
- var $ = window.jQuery;
+], function ($, Cryptpad, English) {
var $body = $('body');
diff --git a/www/code/main.js b/www/code/main.js
index 227c1e1fa..50bf06617 100644
--- a/www/code/main.js
+++ b/www/code/main.js
@@ -1,5 +1,5 @@
-require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([
+ 'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
'/bower_components/textpatcher/TextPatcher.js',
@@ -12,10 +12,8 @@ define([
'/common/themes.js',
'/common/visible.js',
'/common/notify.js',
- '/bower_components/file-saver/FileSaver.min.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget, Modes, Themes, Visible, Notify) {
- var $ = window.jQuery;
+ '/bower_components/file-saver/FileSaver.min.js'
+], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget, Modes, Themes, Visible, Notify) {
var saveAs = window.saveAs;
var Messages = Cryptpad.Messages;
@@ -63,7 +61,7 @@ define([
styleActiveLine : true,
search: true,
highlightSelectionMatches: {showToken: /\w+/},
- extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
+ extraKeys: {"Shift-Ctrl-R": undefined},
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
mode: "javascript",
@@ -646,7 +644,7 @@ define([
return cursor;
};
- var onRemote = config.onRemote = function (info) {
+ var onRemote = config.onRemote = function () {
if (initializing) { return; }
var scroll = editor.getScrollInfo();
@@ -733,6 +731,7 @@ define([
var second = function (CM) {
Cryptpad.ready(function (err, env) {
andThen(CM);
+ Cryptpad.reportAppUsage();
});
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
diff --git a/www/common/boot2.js b/www/common/boot2.js
index 8a38fad2f..599875a93 100644
--- a/www/common/boot2.js
+++ b/www/common/boot2.js
@@ -1,7 +1,14 @@
// This is stage 1, it can be changed but you must bump the version of the project.
define([], function () {
// fix up locations so that relative urls work.
- require.config({ baseUrl: window.location.pathname });
+ require.config({
+ baseUrl: window.location.pathname,
+ paths: {
+ // jquery declares itself as literally "jquery" so it cannot be pulled by path :(
+ "jquery": "/bower_components/jquery/dist/jquery.min",
+ // json.sortify same
+ "json.sortify": "/bower_components/json.sortify/dist/JSON.sortify"
+ }
+ });
require([document.querySelector('script[data-bootload]').getAttribute('data-bootload')]);
});
-
diff --git a/www/common/clipboard.js b/www/common/clipboard.js
index 5e62f5a37..557c1a809 100644
--- a/www/common/clipboard.js
+++ b/www/common/clipboard.js
@@ -1,7 +1,4 @@
-define([
- '/bower_components/jquery/dist/jquery.min.js',
-], function () {
- var $ = window.jQuery;
+define(['jquery'], function ($) {
var Clipboard = {};
// copy arbitrary text to the clipboard
@@ -13,7 +10,7 @@ define([
$('body').append($ta);
- if (!($ta.length && $ta[0].select)) {
+ if (!($ta.length && $ta[0].select)) {
// console.log("oops");
return;
}
diff --git a/www/common/common-hash.js b/www/common/common-hash.js
new file mode 100644
index 000000000..034ee5bbb
--- /dev/null
+++ b/www/common/common-hash.js
@@ -0,0 +1,245 @@
+define([
+ '/common/common-util.js',
+ '/bower_components/chainpad-crypto/crypto.js',
+ '/bower_components/tweetnacl/nacl-fast.min.js'
+], function (Util, Crypto) {
+ var Nacl = window.nacl;
+
+ var Hash = {};
+
+ var uint8ArrayToHex = Util.uint8ArrayToHex;
+ var hexToBase64 = Util.hexToBase64;
+ var base64ToHex = Util.base64ToHex;
+
+ // This implementation must match that on the server
+ // it's used for a checksum
+ Hash.hashChannelList = function (list) {
+ return Nacl.util.encodeBase64(Nacl.hash(Nacl.util
+ .decodeUTF8(JSON.stringify(list))));
+ };
+
+ var getEditHashFromKeys = Hash.getEditHashFromKeys = function (chanKey, keys) {
+ if (typeof keys === 'string') {
+ return chanKey + keys;
+ }
+ if (!keys.editKeyStr) { return; }
+ return '/1/edit/' + hexToBase64(chanKey) + '/' + Crypto.b64RemoveSlashes(keys.editKeyStr);
+ };
+ var getViewHashFromKeys = Hash.getViewHashFromKeys = function (chanKey, keys) {
+ if (typeof keys === 'string') {
+ return;
+ }
+ return '/1/view/' + hexToBase64(chanKey) + '/' + Crypto.b64RemoveSlashes(keys.viewKeyStr);
+ };
+
+ var parsePadUrl = Hash.parsePadUrl = function (href) {
+ var patt = /^https*:\/\/([^\/]*)\/(.*?)\//i;
+
+ var ret = {};
+
+ if (!href) { return ret; }
+
+ if (!/^https*:\/\//.test(href)) {
+ var idx = href.indexOf('/#');
+ ret.type = href.slice(1, idx);
+ ret.hash = href.slice(idx + 2);
+ return ret;
+ }
+
+ var hash = href.replace(patt, function (a, domain, type, hash) {
+ ret.domain = domain;
+ ret.type = type;
+ return '';
+ });
+ ret.hash = hash.replace(/#/g, '');
+ return ret;
+ };
+
+ var getRelativeHref = Hash.getRelativeHref = function (href) {
+ if (!href) { return; }
+ if (href.indexOf('#') === -1) { return; }
+ var parsed = parsePadUrl(href);
+ return '/' + parsed.type + '/#' + parsed.hash;
+ };
+
+ /*
+ * Returns all needed keys for a realtime channel
+ * - no argument: use the URL hash or create one if it doesn't exist
+ * - secretHash provided: use secretHash to find the keys
+ */
+ var getSecrets = Hash.getSecrets = function (secretHash) {
+ var secret = {};
+ var generate = function () {
+ secret.keys = Crypto.createEditCryptor();
+ secret.key = Crypto.createEditCryptor().editKeyStr;
+ };
+ if (!secretHash && !/#/.test(window.location.href)) {
+ generate();
+ return secret;
+ } else {
+ var hash = secretHash || window.location.hash.slice(1);
+ if (hash.length === 0) {
+ generate();
+ return secret;
+ }
+ // old hash system : #{hexChanKey}{cryptKey}
+ // new hash system : #/{hashVersion}/{b64ChanKey}/{cryptKey}
+ if (hash.slice(0,1) !== '/' && hash.length >= 56) {
+ // Old hash
+ secret.channel = hash.slice(0, 32);
+ secret.key = hash.slice(32);
+ }
+ else {
+ // New hash
+ var hashArray = hash.split('/');
+ if (hashArray.length < 4) {
+ Hash.alert("Unable to parse the key");
+ throw new Error("Unable to parse the key");
+ }
+ var version = hashArray[1];
+ if (version === "1") {
+ var mode = hashArray[2];
+ if (mode === 'edit') {
+ secret.channel = base64ToHex(hashArray[3]);
+ var keys = Crypto.createEditCryptor(hashArray[4].replace(/-/g, '/'));
+ secret.keys = keys;
+ secret.key = keys.editKeyStr;
+ if (secret.channel.length !== 32 || secret.key.length !== 24) {
+ Hash.alert("The channel key and/or the encryption key is invalid");
+ throw new Error("The channel key and/or the encryption key is invalid");
+ }
+ }
+ else if (mode === 'view') {
+ secret.channel = base64ToHex(hashArray[3]);
+ secret.keys = Crypto.createViewCryptor(hashArray[4].replace(/-/g, '/'));
+ if (secret.channel.length !== 32) {
+ Hash.alert("The channel key is invalid");
+ throw new Error("The channel key is invalid");
+ }
+ }
+ }
+ }
+ }
+ return secret;
+ };
+
+ var getHashes = Hash.getHashes = function (channel, secret) {
+ var hashes = {};
+ if (secret.keys.editKeyStr) {
+ hashes.editHash = getEditHashFromKeys(channel, secret.keys);
+ }
+ if (secret.keys.viewKeyStr) {
+ hashes.viewHash = getViewHashFromKeys(channel, secret.keys);
+ }
+ return hashes;
+ };
+
+ var createChannelId = Hash.createChannelId = function () {
+ var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
+ if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
+ throw new Error('channel ids must consist of 32 hex characters');
+ }
+ return id;
+ };
+
+ var createRandomHash = Hash.createRandomHash = function () {
+ // 16 byte channel Id
+ var channelId = Util.hexToBase64(createChannelId());
+ // 18 byte encryption key
+ var key = Crypto.b64RemoveSlashes(Crypto.rand64(18));
+ return '/1/edit/' + [channelId, key].join('/');
+ };
+
+ var parseHash = Hash.parseHash = function (hash) {
+ var parsed = {};
+ if (hash.slice(0,1) !== '/' && hash.length >= 56) {
+ // Old hash
+ parsed.channel = hash.slice(0, 32);
+ parsed.key = hash.slice(32);
+ parsed.version = 0;
+ return parsed;
+ }
+ var hashArr = hash.split('/');
+ if (hashArr[1] && hashArr[1] === '1') {
+ parsed.version = 1;
+ parsed.mode = hashArr[2];
+ parsed.channel = hashArr[3];
+ parsed.key = hashArr[4];
+ parsed.present = hashArr[5] && hashArr[5] === 'present';
+ return parsed;
+ }
+ return;
+ };
+
+ // STORAGE
+ var findWeaker = Hash.findWeaker = function (href, recents) {
+ var rHref = href || getRelativeHref(window.location.href);
+ var parsed = parsePadUrl(rHref);
+ if (!parsed.hash) { return false; }
+ var weaker;
+ recents.some(function (pad) {
+ var p = parsePadUrl(pad.href);
+ if (p.type !== parsed.type) { return; } // Not the same type
+ if (p.hash === parsed.hash) { return; } // Same hash, not stronger
+ var pHash = parseHash(p.hash);
+ var parsedHash = parseHash(parsed.hash);
+ if (!parsedHash || !pHash) { return; }
+ if (pHash.version !== parsedHash.version) { return; }
+ if (pHash.channel !== parsedHash.channel) { return; }
+ if (pHash.mode === 'view' && parsedHash.mode === 'edit') {
+ weaker = pad.href;
+ return true;
+ }
+ return;
+ });
+ return weaker;
+ };
+ var findStronger = Hash.findStronger = function (href, recents) {
+ var rHref = href || getRelativeHref(window.location.href);
+ var parsed = parsePadUrl(rHref);
+ if (!parsed.hash) { return false; }
+ var stronger;
+ recents.some(function (pad) {
+ var p = parsePadUrl(pad.href);
+ if (p.type !== parsed.type) { return; } // Not the same type
+ if (p.hash === parsed.hash) { return; } // Same hash, not stronger
+ var pHash = parseHash(p.hash);
+ var parsedHash = parseHash(parsed.hash);
+ if (!parsedHash || !pHash) { return; }
+ if (pHash.version !== parsedHash.version) { return; }
+ if (pHash.channel !== parsedHash.channel) { return; }
+ if (pHash.mode === 'edit' && parsedHash.mode === 'view') {
+ stronger = pad.href;
+ return true;
+ }
+ return;
+ });
+ return stronger;
+ };
+ var isNotStrongestStored = Hash.isNotStrongestStored = function (href, recents) {
+ return findStronger(href, recents);
+ };
+
+ var hrefToHexChannelId = Hash.hrefToHexChannelId = function (href) {
+ var parsed = Hash.parsePadUrl(href);
+ if (!parsed || !parsed.hash) { return; }
+
+ parsed = Hash.parseHash(parsed.hash);
+
+ if (parsed.version === 0) {
+ return parsed.channel;
+ } else if (parsed.version !== 1) {
+ console.error("parsed href had no version");
+ console.error(parsed);
+ return;
+ }
+
+ var channel = parsed.channel;
+ if (!channel) { return; }
+
+ var hex = base64ToHex(channel);
+ return hex;
+ };
+
+ return Hash;
+});
diff --git a/www/common/common-interface.js b/www/common/common-interface.js
new file mode 100644
index 000000000..624bcfcf3
--- /dev/null
+++ b/www/common/common-interface.js
@@ -0,0 +1,220 @@
+define([
+ 'jquery',
+ '/customize/messages.js',
+ '/common/common-util.js',
+ '/customize/application_config.js',
+ '/bower_components/alertifyjs/dist/js/alertify.js'
+], function ($, Messages, Util, AppConfig, Alertify) {
+
+ var UI = {};
+
+ /*
+ * Alertifyjs
+ */
+ UI.Alertify = Alertify;
+
+ // set notification timeout
+ Alertify._$$alertify.delay = AppConfig.notificationTimeout || 5000;
+
+ var findCancelButton = UI.findCancelButton = function () {
+ return $('button.cancel');
+ };
+
+ var findOKButton = UI.findOKButton = function () {
+ return $('button.ok');
+ };
+
+ var listenForKeys = UI.listenForKeys = function (yes, no) {
+ var handler = function (e) {
+ switch (e.which) {
+ case 27: // cancel
+ if (typeof(no) === 'function') { no(e); }
+ no();
+ break;
+ case 13: // enter
+ if (typeof(yes) === 'function') { yes(e); }
+ break;
+ }
+ };
+
+ $(window).keyup(handler);
+ return handler;
+ };
+
+ var stopListening = UI.stopListening = function (handler) {
+ $(window).off('keyup', handler);
+ };
+
+ UI.alert = function (msg, cb, force) {
+ cb = cb || function () {};
+ if (force !== true) { msg = Util.fixHTML(msg); }
+ var close = function (e) {
+ findOKButton().click();
+ };
+ var keyHandler = listenForKeys(close, close);
+ Alertify.alert(msg, function (ev) {
+ cb(ev);
+ stopListening(keyHandler);
+ });
+ window.setTimeout(function () {
+ findOKButton().focus();
+ });
+ };
+
+ UI.prompt = function (msg, def, cb, opt, force) {
+ opt = opt || {};
+ cb = cb || function () {};
+ if (force !== true) { msg = Util.fixHTML(msg); }
+
+ var keyHandler = listenForKeys(function (e) { // yes
+ findOKButton().click();
+ }, function (e) { // no
+ findCancelButton().click();
+ });
+
+ Alertify
+ .defaultValue(def || '')
+ .okBtn(opt.ok || Messages.okButton || 'OK')
+ .cancelBtn(opt.cancel || Messages.cancelButton || 'Cancel')
+ .prompt(msg, function (val, ev) {
+ cb(val, ev);
+ stopListening(keyHandler);
+ }, function (ev) {
+ cb(null, ev);
+ stopListening(keyHandler);
+ });
+ };
+
+ UI.confirm = function (msg, cb, opt, force, styleCB) {
+ opt = opt || {};
+ cb = cb || function () {};
+ if (force !== true) { msg = Util.fixHTML(msg); }
+
+ var keyHandler = listenForKeys(function (e) {
+ findOKButton().click();
+ }, function (e) {
+ findCancelButton().click();
+ });
+
+ Alertify
+ .okBtn(opt.ok || Messages.okButton || 'OK')
+ .cancelBtn(opt.cancel || Messages.cancelButton || 'Cancel')
+ .confirm(msg, function () {
+ cb(true);
+ stopListening(keyHandler);
+ }, function () {
+ cb(false);
+ stopListening(keyHandler);
+ });
+
+ window.setTimeout(function () {
+ var $ok = findOKButton();
+ var $cancel = findCancelButton();
+ if (opt.okClass) { $ok.addClass(opt.okClass); }
+ if (opt.cancelClass) { $cancel.addClass(opt.cancelClass); }
+ if (opt.reverseOrder) {
+ $ok.insertBefore($ok.prev());
+ }
+ if (typeof(styleCB) === 'function') {
+ styleCB($ok.closest('.dialog'));
+ }
+ }, 0);
+ };
+
+ UI.log = function (msg) {
+ Alertify.success(Util.fixHTML(msg));
+ };
+
+ UI.warn = function (msg) {
+ Alertify.error(Util.fixHTML(msg));
+ };
+
+ /*
+ * spinner
+ */
+ UI.spinner = function (parent) {
+ var $target = $('', {
+ 'class': 'fa fa-spinner fa-pulse fa-4x fa-fw'
+ }).hide();
+
+ $(parent).append($target);
+
+ return {
+ show: function () {
+ $target.show();
+ return this;
+ },
+ hide: function () {
+ $target.hide();
+ return this;
+ },
+ get: function () {
+ return $target;
+ },
+ };
+ };
+
+ var LOADING = 'loading';
+
+ var getRandomTip = function () {
+ if (!Messages.tips || !Object.keys(Messages.tips).length) { return ''; }
+ var keys = Object.keys(Messages.tips);
+ var rdm = Math.floor(Math.random() * keys.length);
+ return Messages.tips[keys[rdm]];
+ };
+ UI.addLoadingScreen = function (loadingText, hideTips) {
+ var $loading, $container;
+ if ($('#' + LOADING).length) {
+ $loading = $('#' + LOADING).show();
+ if (loadingText) {
+ $('#' + LOADING).find('p').text(loadingText);
+ }
+ $container = $loading.find('.loadingContainer');
+ } else {
+ $loading = $('', {id: LOADING});
+ $container = $('
', {'class': 'loadingContainer'});
+ $container.append('
');
+ var $spinner = $('
', {'class': 'spinnerContainer'});
+ UI.spinner($spinner).show();
+ var $text = $('
').text(loadingText || Messages.loading);
+ $container.append($spinner).append($text);
+ $loading.append($container);
+ $('body').append($loading);
+ }
+ if (Messages.tips && !hideTips) {
+ var $loadingTip = $('
', {'id': 'loadingTip'});
+ var $tip = $('
', {'class': 'tips'}).text(getRandomTip()).appendTo($loadingTip);
+ $loadingTip.css({
+ 'top': $('body').height()/2 + $container.height()/2 + 20 + 'px'
+ });
+ $('body').append($loadingTip);
+ }
+ };
+ UI.removeLoadingScreen = function (cb) {
+ $('#' + LOADING).fadeOut(750, cb);
+ $('#loadingTip').css('top', '');
+ window.setTimeout(function () {
+ $('#loadingTip').fadeOut(750);
+ }, 3000);
+ };
+ UI.errorLoadingScreen = function (error, transparent) {
+ if (!$('#' + LOADING).is(':visible')) { UI.addLoadingScreen(undefined, true); }
+ $('.spinnerContainer').hide();
+ if (transparent) { $('#' + LOADING).css('opacity', 0.8); }
+ $('#' + LOADING).find('p').html(error || Messages.error);
+ };
+
+ var importContent = UI.importContent = function (type, f) {
+ return function () {
+ var $files = $(' ').click();
+ $files.on('change', function (e) {
+ var file = e.target.files[0];
+ var reader = new FileReader();
+ reader.onload = function (e) { f(e.target.result, file); };
+ reader.readAsText(file, type);
+ });
+ };
+ };
+
+ return UI;
+});
diff --git a/www/common/common-util.js b/www/common/common-util.js
new file mode 100644
index 000000000..29b25c4fb
--- /dev/null
+++ b/www/common/common-util.js
@@ -0,0 +1,85 @@
+define([], function () {
+ var Util = {};
+
+ var find = Util.find = function (map, path) {
+ return (map && path.reduce(function (p, n) {
+ return typeof(p[n]) !== 'undefined' && p[n];
+ }, map));
+ };
+
+ var fixHTML = Util.fixHTML = function (str) {
+ if (!str) { return ''; }
+ return str.replace(/[<>&"']/g, function (x) {
+ return ({ "<": "<", ">": ">", "&": "&", '"': """, "'": "'" })[x];
+ });
+ };
+
+ var hexToBase64 = Util.hexToBase64 = function (hex) {
+ var hexArray = hex
+ .replace(/\r|\n/g, "")
+ .replace(/([\da-fA-F]{2}) ?/g, "0x$1 ")
+ .replace(/ +$/, "")
+ .split(" ");
+ var byteString = String.fromCharCode.apply(null, hexArray);
+ return window.btoa(byteString).replace(/\//g, '-').slice(0,-2);
+ };
+
+ var base64ToHex = Util.base64ToHex = function (b64String) {
+ var hexArray = [];
+ atob(b64String.replace(/-/g, '/')).split("").forEach(function(e){
+ var h = e.charCodeAt(0).toString(16);
+ if (h.length === 1) { h = "0"+h; }
+ hexArray.push(h);
+ });
+ return hexArray.join("");
+ };
+
+ var uint8ArrayToHex = Util.uint8ArrayToHex = function (a) {
+ // call slice so Uint8Arrays work as expected
+ return Array.prototype.slice.call(a).map(function (e, i) {
+ var n = Number(e & 0xff).toString(16);
+ if (n === 'NaN') {
+ throw new Error('invalid input resulted in NaN');
+ }
+
+ switch (n.length) {
+ case 0: return '00'; // just being careful, shouldn't happen
+ case 1: return '0' + n;
+ case 2: return n;
+ default: throw new Error('unexpected value');
+ }
+ }).join('');
+ };
+
+ var deduplicateString = Util.deduplicateString = function (array) {
+ var a = array.slice();
+ for(var i=0; i&"']/g, function (x) {
- return ({ "<": "<", ">": ">", "&": "&", '"': """, "'": "'" })[x];
- });
- };
-
-
- var truncate = common.truncate = function (text, len) {
- if (typeof(text) === 'string' && text.length > len) {
- return text.slice(0, len) + '…';
- }
- return text;
- };
-
- var hexToBase64 = common.hexToBase64 = function (hex) {
- var hexArray = hex
- .replace(/\r|\n/g, "")
- .replace(/([\da-fA-F]{2}) ?/g, "0x$1 ")
- .replace(/ +$/, "")
- .split(" ");
- var byteString = String.fromCharCode.apply(null, hexArray);
- return window.btoa(byteString).replace(/\//g, '-').slice(0,-2);
- };
-
- var base64ToHex = common.base64ToHex = function (b64String) {
- var hexArray = [];
- atob(b64String.replace(/-/g, '/')).split("").forEach(function(e){
- var h = e.charCodeAt(0).toString(16);
- if (h.length === 1) { h = "0"+h; }
- hexArray.push(h);
- });
- return hexArray.join("");
- };
-
- var deduplicateString = common.deduplicateString = function (array) {
- var a = array.slice();
- for(var i=0; i= 56) {
- // Old hash
- parsed.channel = hash.slice(0, 32);
- parsed.key = hash.slice(32);
- parsed.version = 0;
- return parsed;
- }
- var hashArr = hash.split('/');
- if (hashArr[1] && hashArr[1] === '1') {
- parsed.version = 1;
- parsed.mode = hashArr[2];
- parsed.channel = hashArr[3];
- parsed.key = hashArr[4];
- parsed.present = hashArr[5] && hashArr[5] === 'present';
- return parsed;
- }
- return;
- };
- var getEditHashFromKeys = common.getEditHashFromKeys = function (chanKey, keys) {
- if (typeof keys === 'string') {
- return chanKey + keys;
- }
- if (!keys.editKeyStr) { return; }
- return '/1/edit/' + hexToBase64(chanKey) + '/' + Crypto.b64RemoveSlashes(keys.editKeyStr);
- };
- var getViewHashFromKeys = common.getViewHashFromKeys = function (chanKey, keys) {
- if (typeof keys === 'string') {
- return;
- }
- return '/1/view/' + hexToBase64(chanKey) + '/' + Crypto.b64RemoveSlashes(keys.viewKeyStr);
- };
- var getHashFromKeys = common.getHashFromKeys = getEditHashFromKeys;
-
- var specialHashes = common.specialHashes = ['iframe'];
-
- /*
- * Returns all needed keys for a realtime channel
- * - no argument: use the URL hash or create one if it doesn't exist
- * - secretHash provided: use secretHash to find the keys
- */
- var getSecrets = common.getSecrets = function (secretHash) {
- var secret = {};
- var generate = function () {
- secret.keys = Crypto.createEditCryptor();
- secret.key = Crypto.createEditCryptor().editKeyStr;
- };
- if (!secretHash && !/#/.test(window.location.href)) {
- generate();
- return secret;
- } else {
- var hash = secretHash || window.location.hash.slice(1);
- if (hash.length === 0 || specialHashes.indexOf(hash) !== -1) {
- generate();
- return secret;
- }
- // old hash system : #{hexChanKey}{cryptKey}
- // new hash system : #/{hashVersion}/{b64ChanKey}/{cryptKey}
- if (hash.slice(0,1) !== '/' && hash.length >= 56) {
- // Old hash
- secret.channel = hash.slice(0, 32);
- secret.key = hash.slice(32);
- }
- else {
- // New hash
- var hashArray = hash.split('/');
- if (hashArray.length < 4) {
- common.alert("Unable to parse the key");
- throw new Error("Unable to parse the key");
- }
- var version = hashArray[1];
- if (version === "1") {
- var mode = hashArray[2];
- if (mode === 'edit') {
- secret.channel = base64ToHex(hashArray[3]);
- var keys = Crypto.createEditCryptor(hashArray[4].replace(/-/g, '/'));
- secret.keys = keys;
- secret.key = keys.editKeyStr;
- if (secret.channel.length !== 32 || secret.key.length !== 24) {
- common.alert("The channel key and/or the encryption key is invalid");
- throw new Error("The channel key and/or the encryption key is invalid");
- }
- }
- else if (mode === 'view') {
- secret.channel = base64ToHex(hashArray[3]);
- secret.keys = Crypto.createViewCryptor(hashArray[4].replace(/-/g, '/'));
- if (secret.channel.length !== 32) {
- common.alert("The channel key is invalid");
- throw new Error("The channel key is invalid");
- }
- }
- }
- }
- }
- return secret;
- };
-
- var getHashes = common.getHashes = function (channel, secret) {
- var hashes = {};
- if (secret.keys.editKeyStr) {
- hashes.editHash = getEditHashFromKeys(channel, secret.keys);
- }
- if (secret.keys.viewKeyStr) {
- hashes.viewHash = getViewHashFromKeys(channel, secret.keys);
- }
- return hashes;
- };
-
-
- var uint8ArrayToHex = common.uint8ArrayToHex = function (a) {
- // call slice so Uint8Arrays work as expected
- return Array.prototype.slice.call(a).map(function (e, i) {
- var n = Number(e & 0xff).toString(16);
- if (n === 'NaN') {
- throw new Error('invalid input resulted in NaN');
- }
-
- switch (n.length) {
- case 0: return '00'; // just being careful, shouldn't happen
- case 1: return '0' + n;
- case 2: return n;
- default: throw new Error('unexpected value');
- }
- }).join('');
- };
-
- var createChannelId = common.createChannelId = function () {
- var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
- if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
- throw new Error('channel ids must consist of 32 hex characters');
- }
- return id;
- };
-
- var createRandomHash = common.createRandomHash = function () {
- // 16 byte channel Id
- var channelId = hexToBase64(createChannelId());
- // 18 byte encryption key
- var key = Crypto.b64RemoveSlashes(Crypto.rand64(18));
- return '/1/edit/' + [channelId, key].join('/');
- };
-
- var replaceHash = common.replaceHash = function (hash) {
- if (window.history && window.history.replaceState) {
- if (!/^#/.test(hash)) { hash = '#' + hash; }
- return void window.history.replaceState({}, window.document.title, hash);
- }
- window.location.hash = hash;
- };
-
- var storageKey = common.storageKey = 'CryptPad_RECENTPADS';
+ common.isArray = $.isArray;
/*
* localStorage formatting
@@ -389,10 +234,23 @@ load pinpad dynamically only after you know that it will be needed */
* title
* ??? // what else can we put in here?
*/
+ var checkObjectData = function (pad) {
+ if (!pad.ctime) { pad.ctime = pad.atime; }
+ if (/^https*:\/\//.test(pad.href)) {
+ pad.href = common.getRelativeHref(pad.href);
+ }
+ var parsed = common.parsePadUrl(pad.href);
+ if (!parsed || !parsed.hash) { return; }
+ if (!pad.title) {
+ pad.title = common.getDefaultname(parsed);
+ }
+ return parsed.hash;
+ };
+ // Migrate from legacy store (localStorage)
var migrateRecentPads = common.migrateRecentPads = function (pads) {
return pads.map(function (pad) {
var hash;
- if (isArray(pad)) {
+ if (Array.isArray(pad)) { // TODO DEPRECATE_F
var href = pad[0];
href.replace(/\#(.*)$/, function (a, h) {
hash = h;
@@ -405,16 +263,7 @@ load pinpad dynamically only after you know that it will be needed */
ctime: pad[1],
};
} else if (pad && typeof(pad) === 'object') {
- if (!pad.ctime) { pad.ctime = pad.atime; }
- if (!pad.title) {
- pad.href.replace(/#(.*)$/, function (x, hash) {
- pad.title = hash.slice(0,8);
- });
- }
- if (/^https*:\/\//.test(pad.href)) {
- pad.href = common.getRelativeHref(pad.href);
- }
- hash = pad.href.slice(pad.href.indexOf('#')+1);
+ hash = checkObjectData(pad);
if (!hash || !common.parseHash(hash)) { return; }
return pad;
} else {
@@ -424,15 +273,27 @@ load pinpad dynamically only after you know that it will be needed */
}
}).filter(function (x) { return x; });
};
+ // Remove everything from RecentPads that is not an object and check the objects
+ var checkRecentPads = common.checkRecentPads = function (pads) {
+ pads.forEach(function (pad, i) {
+ if (pad && typeof(pad) === 'object') {
+ var hash = checkObjectData(pad);
+ if (!hash || !common.parseHash(hash)) { return; }
+ return pad;
+ }
+ console.error("[Cryptpad.migrateRecentPads] pad had unexpected value");
+ getStore().removeData(i);
+ });
+ };
// Get the pads from localStorage to migrate them to the object store
var getLegacyPads = common.getLegacyPads = function (cb) {
- require(['/customize/store.js'], function(Legacy) {
+ require(['/customize/store.js'], function(Legacy) { // TODO DEPRECATE_F
Legacy.ready(function (err, legacy) {
if (err) { cb(err, null); return; }
legacy.get(storageKey, function (err2, recentPads) {
if (err2) { cb(err2, null); return; }
- if (isArray(recentPads)) {
+ if (Array.isArray(recentPads)) {
cb(void 0, migrateRecentPads(recentPads));
return;
}
@@ -442,58 +303,15 @@ load pinpad dynamically only after you know that it will be needed */
});
};
- var getHash = common.getHash = function () {
- return window.location.hash.slice(1);
- };
-
- var getRelativeHref = common.getRelativeHref = function (href) {
- if (!href) { return; }
- if (href.indexOf('#') === -1) { return; }
- var parsed = common.parsePadUrl(href);
- return '/' + parsed.type + '/#' + parsed.hash;
- };
-
- var parsePadUrl = common.parsePadUrl = function (href) {
- var patt = /^https*:\/\/([^\/]*)\/(.*?)\//i;
-
- var ret = {};
-
- if (!href) { return ret; }
-
- if (!/^https*:\/\//.test(href)) {
- var idx = href.indexOf('/#');
- ret.type = href.slice(1, idx);
- ret.hash = href.slice(idx + 2);
- return ret;
- }
-
- var hash = href.replace(patt, function (a, domain, type, hash) {
- ret.domain = domain;
- ret.type = type;
- return '';
- });
- ret.hash = hash.replace(/#/g, '');
- return ret;
- };
-
- var isNameAvailable = function (title, parsed, pads) {
- return !pads.some(function (pad) {
- // another pad is already using that title
- if (pad.title === title) {
- return true;
- }
- });
- };
-
// Create untitled documents when no name is given
- var getDefaultName = common.getDefaultName = function (parsed, recentPads) {
+ var getDefaultName = common.getDefaultName = function (parsed) {
var type = parsed.type;
var untitledIndex = 1;
var name = (Messages.type)[type] + ' - ' + new Date().toString().split(' ').slice(0,4).join(' ');
return name;
};
var isDefaultName = common.isDefaultName = function (parsed, title) {
- var name = getDefaultName(parsed, []);
+ var name = getDefaultName(parsed);
return title === name;
};
@@ -553,8 +371,9 @@ load pinpad dynamically only after you know that it will be needed */
});
return templates;
};
- var addTemplate = common.addTemplate = function (href) {
- getStore().addTemplate(href);
+ var addTemplate = common.addTemplate = function (data) {
+ getStore().pushData(data);
+ getStore().addPad(data.href, ['template']);
};
var isTemplate = common.isTemplate = function (href) {
@@ -594,29 +413,18 @@ load pinpad dynamically only after you know that it will be needed */
};
// STORAGE
- /* fetch and migrate your pad history from localStorage */
+ /* fetch and migrate your pad history from the store */
var getRecentPads = common.getRecentPads = function (cb) {
getStore().getDrive(storageKey, function (err, recentPads) {
- if (isArray(recentPads)) {
- cb(void 0, migrateRecentPads(recentPads));
+ if (Array.isArray(recentPads)) {
+ checkRecentPads(recentPads);
+ cb(void 0, recentPads);
return;
}
cb(void 0, []);
});
};
- // STORAGE
- /* commit a list of pads to localStorage */
- // TODO integrate pinning if enabled
- var setRecentPads = common.setRecentPads = function (pads, cb) {
- getStore().setDrive(storageKey, pads, function (err, data) {
- if (PINNING_ENABLED && isLoggedIn()) {
- console.log("TODO check pin hash");
- }
- cb(err, data);
- });
- };
-
// STORAGE: Display Name
var getLastName = common.getLastName = function (cb) {
common.getAttribute('username', function (err, userName) {
@@ -636,7 +444,6 @@ load pinpad dynamically only after you know that it will be needed */
};
// STORAGE
- // TODO integrate pinning if enabled
var forgetPad = common.forgetPad = function (href, cb) {
var parsed = parsePadUrl(href);
@@ -670,60 +477,12 @@ load pinpad dynamically only after you know that it will be needed */
}
};
- // STORAGE
- var findWeaker = common.findWeaker = function (href, recents) {
- var rHref = href || getRelativeHref(window.location.href);
- var parsed = parsePadUrl(rHref);
- if (!parsed.hash) { return false; }
- var weaker;
- recents.some(function (pad) {
- var p = parsePadUrl(pad.href);
- if (p.type !== parsed.type) { return; } // Not the same type
- if (p.hash === parsed.hash) { return; } // Same hash, not stronger
- var pHash = parseHash(p.hash);
- var parsedHash = parseHash(parsed.hash);
- if (!parsedHash || !pHash) { return; }
- if (pHash.version !== parsedHash.version) { return; }
- if (pHash.channel !== parsedHash.channel) { return; }
- if (pHash.mode === 'view' && parsedHash.mode === 'edit') {
- weaker = pad.href;
- return true;
- }
- return;
- });
- return weaker;
- };
- var findStronger = common.findStronger = function (href, recents) {
- var rHref = href || getRelativeHref(window.location.href);
- var parsed = parsePadUrl(rHref);
- if (!parsed.hash) { return false; }
- var stronger;
- recents.some(function (pad) {
- var p = parsePadUrl(pad.href);
- if (p.type !== parsed.type) { return; } // Not the same type
- if (p.hash === parsed.hash) { return; } // Same hash, not stronger
- var pHash = parseHash(p.hash);
- var parsedHash = parseHash(parsed.hash);
- if (!parsedHash || !pHash) { return; }
- if (pHash.version !== parsedHash.version) { return; }
- if (pHash.channel !== parsedHash.channel) { return; }
- if (pHash.mode === 'edit' && parsedHash.mode === 'view') {
- stronger = pad.href;
- return true;
- }
- return;
- });
- return stronger;
- };
- var isNotStrongestStored = common.isNotStrongestStored = function (href, recents) {
- return findStronger(href, recents);
- };
-
- // TODO integrate pinning
var setPadTitle = common.setPadTitle = function (name, cb) {
var href = window.location.href;
var parsed = parsePadUrl(href);
href = getRelativeHref(href);
+ // getRecentPads return the array from the drive, not a copy
+ // We don't have to call "set..." at the end, everything is stored with listmap
getRecentPads(function (err, recent) {
if (err) {
cb(err);
@@ -779,175 +538,18 @@ load pinpad dynamically only after you know that it will be needed */
if (!contains) {
var data = makePad(href, name);
- renamed.push(data);
- if (typeof(getStore().addPad) === "function") {
- getStore().addPad(href, common.initialPath, common.initialName || name);
- }
+ getStore().pushData(data);
+ getStore().addPad(href, common.initialPath, common.initialName || name);
}
-
- setRecentPads(renamed, function (err, data) {
- if (updateWeaker.length > 0) {
- updateWeaker.forEach(function (obj) {
- getStore().replaceHref(obj.o, obj.n);
- });
- }
- cb(err, data);
- });
+ if (updateWeaker.length > 0) {
+ updateWeaker.forEach(function (obj) {
+ getStore().replaceHref(obj.o, obj.n);
+ });
+ }
+ cb(err, recent);
});
};
- // STORAGE
- var getPadTitle = common.getPadTitle = function (cb) {
- var href = window.location.href;
- var parsed = parsePadUrl(window.location.href);
- var hashSlice = window.location.hash.slice(1,9);
- var title = '';
-
- getRecentPads(function (err, pads) {
- if (err) {
- cb(err);
- return;
- }
- pads.some(function (pad) {
- var p = parsePadUrl(pad.href);
- if (p.hash === parsed.hash && p.type === parsed.type) {
- title = pad.title || hashSlice;
- return true;
- }
- });
-
- if (title === '') { title = getDefaultName(parsed, pads); }
-
- cb(void 0, title);
- });
- };
-
- // STORAGE
- var causesNamingConflict = common.causesNamingConflict = function (title, cb) {
- var href = window.location.href;
-
- var parsed = parsePadUrl(href);
- getRecentPads(function (err, pads) {
- if (err) {
- cb(err);
- return;
- }
- var conflicts = pads.some(function (pad) {
- // another pad is already using that title
- if (pad.title === title) {
- var p = parsePadUrl(pad.href);
-
- if (p.type === parsed.type && p.hash === parsed.hash) {
- // the duplicate pad has the same type and hash
- // allow renames
- } else {
- // it's an entirely different pad... it conflicts
- return true;
- }
- }
- });
- cb(void 0, conflicts);
- });
- };
-
- var newPadNameKey = common.newPadNameKey = "newPadName";
- var newPadPathKey = common.newPadPathKey = "newPadPath";
-
- // local name?
- common.ready = function (f) {
- var block = 0;
- var env = {};
-
- var cb = function () {
- block--;
- if (!block) {
- f(void 0, env);
- }
- };
-
- if (sessionStorage[newPadNameKey]) {
- common.initialName = sessionStorage[newPadNameKey];
- delete sessionStorage[newPadNameKey];
- }
- if (sessionStorage[newPadPathKey]) {
- common.initialPath = sessionStorage[newPadPathKey];
- delete sessionStorage[newPadPathKey];
- }
-
- Store.ready(function (err, storeObj) {
- store = common.store = env.store = storeObj;
-
- var proxy = getProxy();
- var network = getNetwork();
-
- $(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"
- Alertify.reset();
-
- // Load the new pad when the hash has changed
- var oldHash = document.location.hash.slice(1);
- window.onhashchange = function () {
- var newHash = document.location.hash.slice(1);
- var parsedOld = parseHash(oldHash);
- var parsedNew = parseHash(newHash);
- if (parsedOld && parsedNew && (
- parsedOld.channel !== parsedNew.channel
- || parsedOld.mode !== parsedNew.mode
- || parsedOld.key !== parsedNew.key)) {
- document.location.reload();
- return;
- }
- if (parsedNew) {
- oldHash = newHash;
- }
- };
-
- if (PINNING_ENABLED && isLoggedIn()) {
- console.log("logged in. pads will be pinned");
- block++;
-
- // TODO setTimeout in case rpc doesn't
- // activate in reasonable time?
- 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;
-
- // TODO check if pin list is up to date
- // if not, reset
- cb();
- });
- } else if (PINNING_ENABLED) {
- console.log('not logged in. pads will not be pinned');
- } else {
- console.log('pinning disabled');
- }
-
- // 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);
- return;
- }
-
- block++;
- cb();
- });
- }, common);
- };
-
var errorHandlers = [];
common.onError = function (h) {
if (typeof h !== "function") { return; }
@@ -961,75 +563,6 @@ load pinpad dynamically only after you know that it will be needed */
});
};
- var LOADING = 'loading';
- var getRandomTip = function () {
- if (!Messages.tips || !Object.keys(Messages.tips).length) { return ''; }
- var keys = Object.keys(Messages.tips);
- var rdm = Math.floor(Math.random() * keys.length);
- return Messages.tips[keys[rdm]];
- };
- common.addLoadingScreen = function (loadingText, hideTips) {
- var $loading, $container;
- if ($('#' + LOADING).length) {
- $loading = $('#' + LOADING).show();
- if (loadingText) {
- $('#' + LOADING).find('p').text(loadingText);
- }
- $container = $loading.find('.loadingContainer');
- } else {
- $loading = $('', {id: LOADING});
- $container = $('
', {'class': 'loadingContainer'});
- $container.append('
');
- var $spinner = $('
', {'class': 'spinnerContainer'});
- common.spinner($spinner).show();
- var $text = $('
').text(loadingText || Messages.loading);
- $container.append($spinner).append($text);
- $loading.append($container);
- $('body').append($loading);
- }
- if (Messages.tips && !hideTips) {
- var $loadingTip = $('
', {'id': 'loadingTip'});
- var $tip = $('
', {'class': 'tips'}).text(getRandomTip()).appendTo($loadingTip);
- $loadingTip.css({
- 'top': $('body').height()/2 + $container.height()/2 + 20 + 'px'
- });
- $('body').append($loadingTip);
- }
- };
- common.removeLoadingScreen = function (cb) {
- $('#' + LOADING).fadeOut(750, cb);
- $('#loadingTip').css('top', '');
- window.setTimeout(function () {
- $('#loadingTip').fadeOut(750);
- }, 3000);
- };
- common.errorLoadingScreen = function (error, transparent) {
- if (!$('#' + LOADING).is(':visible')) { common.addLoadingScreen(undefined, true); }
- $('.spinnerContainer').hide();
- if (transparent) { $('#' + LOADING).css('opacity', 0.8); }
- $('#' + LOADING).find('p').html(error || Messages.error);
- };
-
- /*
- * Saving files
- */
- var fixFileName = common.fixFileName = function (filename) {
- return filename.replace(/ /g, '-').replace(/[\/\?]/g, '_')
- .replace(/_+/g, '_');
- };
-
- var importContent = common.importContent = function (type, f) {
- return function () {
- var $files = $(' ').click();
- $files.on('change', function (e) {
- var file = e.target.files[0];
- var reader = new FileReader();
- reader.onload = function (e) { f(e.target.result, file); };
- reader.readAsText(file, type);
- });
- };
- };
-
/*
* Buttons
*/
@@ -1051,27 +584,6 @@ load pinpad dynamically only after you know that it will be needed */
});
};
- var hrefToHexChannelId = common.hrefToHexChannelId = function (href) {
- var parsed = common.parsePadUrl(href);
- if (!parsed || !parsed.hash) { return; }
-
- parsed = common.parseHash(parsed.hash);
-
- if (parsed.version === 0) {
- return parsed.channel;
- } else if (parsed.version !== 1) {
- console.error("parsed href had no version");
- console.error(parsed);
- return;
- }
-
- var channel = parsed.channel;
- if (!channel) { return; }
-
- var hex = common.base64ToHex(channel);
- return hex;
- };
-
var getUserChannelList = common.getUserChannelList = function () {
var store = common.getStore();
var proxy = store.getProxy();
@@ -1084,7 +596,7 @@ load pinpad dynamically only after you know that it will be needed */
var userChannel = common.parseHash(userHash).channel;
if (!userChannel) { return null; }
- var list = fo.getFilesDataFiles().map(hrefToHexChannelId)
+ var list = fo.getFiles([fo.FILES_DATA]).map(hrefToHexChannelId)
.filter(function (x) { return x; });
list.push(common.base64ToHex(userChannel));
@@ -1098,6 +610,9 @@ load pinpad dynamically only after you know that it will be needed */
};
var pinsReady = common.pinsReady = function () {
+ if (!isLoggedIn()) {
+ return false;
+ }
if (!PINNING_ENABLED) {
console.error('[PINNING_DISABLED]');
return false;
@@ -1113,7 +628,7 @@ load pinpad dynamically only after you know that it will be needed */
if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
var list = getCanonicalChannelList();
- var local = rpc.hashChannelList(list);
+ var local = Hash.hashChannelList(list);
rpc.getServerHash(function (e, hash) {
if (e) { return void cb(e); }
cb(void 0, hash === local);
@@ -1121,8 +636,7 @@ load pinpad dynamically only after you know that it will be needed */
};
var resetPins = common.resetPins = function (cb) {
- if (!PINNING_ENABLED) { return void console.error('[PINNING_DISABLED]'); }
- if (!rpc) { return void console.error('[RPC_NOT_READY]'); }
+ if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
var list = getCanonicalChannelList();
rpc.reset(list, function (e, hash) {
@@ -1131,6 +645,29 @@ load pinpad dynamically only after you know that it will be needed */
});
};
+ var pinPads = common.pinPads = function (pads, cb) {
+ if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
+
+ rpc.pin(pads, function (e, hash) {
+ if (e) { return void cb(e); }
+ cb(void 0, hash);
+ });
+ };
+
+ var unpinPads = common.unpinPads = function (pads, cb) {
+ if (!pinsReady()) { return void cb ('[RPC_NOT_READY]'); }
+
+ rpc.unpin(pads, function (e, hash) {
+ if (e) { return void cb(e); }
+ cb(void 0, hash);
+ });
+ };
+
+ var getPinnedUsage = common.getPinnedUsage = function (cb) {
+ if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
+ rpc.getFileListSize(cb);
+ };
+
var createButton = common.createButton = function (type, rightside, data, callback) {
var button;
var size = "17px";
@@ -1148,7 +685,7 @@ load pinpad dynamically only after you know that it will be needed */
title: Messages.importButtonTitle,
}).append($('', {'class':'fa fa-upload', style: 'font:'+size+' FontAwesome'}));
if (callback) {
- button.click(common.importContent('text/plain', function (content, file) {
+ button.click(UI.importContent('text/plain', function (content, file) {
callback(content, file);
}));
}
@@ -1279,7 +816,7 @@ load pinpad dynamically only after you know that it will be needed */
//
// allowed options tags: ['a', 'hr', 'p']
var createDropdown = common.createDropdown = function (config) {
- if (typeof config !== "object" || !isArray(config.options)) { return; }
+ if (typeof config !== "object" || !Array.isArray(config.options)) { return; }
var allowedTags = ['a', 'p', 'hr'];
var isValidOption = function (o) {
@@ -1571,155 +1108,111 @@ load pinpad dynamically only after you know that it will be needed */
return $userAdmin;
};
- /*
- * Alertifyjs
- */
+ common.ready = function (f) {
+ var block = 0;
+ var env = {};
- var styleAlerts = common.styleAlerts = function () {};
-
- var findCancelButton = common.findCancelButton = function () {
- return $('button.cancel');
- };
-
- var findOKButton = common.findOKButton = function () {
- return $('button.ok');
- };
-
- var listenForKeys = common.listenForKeys = function (yes, no) {
- var handler = function (e) {
- switch (e.which) {
- case 27: // cancel
- if (typeof(no) === 'function') { no(e); }
- no();
- break;
- case 13: // enter
- if (typeof(yes) === 'function') { yes(e); }
- break;
+ var cb = function () {
+ block--;
+ if (!block) {
+ f(void 0, env);
}
};
- $(window).keyup(handler);
- return handler;
- };
+ if (sessionStorage[newPadNameKey]) {
+ common.initialName = sessionStorage[newPadNameKey];
+ delete sessionStorage[newPadNameKey];
+ }
+ if (sessionStorage[newPadPathKey]) {
+ common.initialPath = sessionStorage[newPadPathKey];
+ delete sessionStorage[newPadPathKey];
+ }
- var stopListening = common.stopListening = function (handler) {
- $(window).off('keyup', handler);
- };
+ Store.ready(function (err, storeObj) {
+ store = common.store = env.store = storeObj;
- common.alert = function (msg, cb, force) {
- cb = cb || function () {};
- if (force !== true) { msg = fixHTML(msg); }
- var close = function (e) {
- findOKButton().click();
- };
- var keyHandler = listenForKeys(close, close);
- Alertify.alert(msg, function (ev) {
- cb(ev);
- stopListening(keyHandler);
- });
- window.setTimeout(function () {
- findOKButton().focus();
- });
- };
+ var proxy = getProxy();
- common.prompt = function (msg, def, cb, opt, force) {
- opt = opt || {};
- cb = cb || function () {};
- if (force !== true) { msg = fixHTML(msg); }
+ /* TODO log users out if they are logged in, but don't have
+ signing keys stored in their object.
+ */
- var keyHandler = listenForKeys(function (e) { // yes
- findOKButton().click();
- }, function (e) { // no
- findCancelButton().click();
- });
+ var network = getNetwork();
- Alertify
- .defaultValue(def || '')
- .okBtn(opt.ok || Messages.okButton || 'OK')
- .cancelBtn(opt.cancel || Messages.cancelButton || 'Cancel')
- .prompt(msg, function (val, ev) {
- cb(val, ev);
- stopListening(keyHandler);
- }, function (ev) {
- cb(null, ev);
- stopListening(keyHandler);
+ $(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();
+
+ // Load the new pad when the hash has changed
+ var oldHash = document.location.hash.slice(1);
+ window.onhashchange = function () {
+ var newHash = document.location.hash.slice(1);
+ var parsedOld = parseHash(oldHash);
+ var parsedNew = parseHash(newHash);
+ if (parsedOld && parsedNew && (
+ parsedOld.channel !== parsedNew.channel
+ || parsedOld.mode !== parsedNew.mode
+ || parsedOld.key !== parsedNew.key)) {
+ document.location.reload();
+ return;
+ }
+ if (parsedNew) {
+ oldHash = newHash;
+ }
+ };
+
+ 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.arePinsSynced(function (err, yes) {
+ if (!yes) {
+ common.resetPins(function (err, hash) {
+ console.log('RESET DONE');
+ });
+ }
+ });
+ cb();
+ });
+ } else if (PINNING_ENABLED) {
+ console.log('not logged in. pads will not be pinned');
+ } else {
+ console.log('pinning disabled');
+ }
+
+ // 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);
+ return;
+ }
+
+ block++;
+ cb();
});
- };
-
- common.confirm = function (msg, cb, opt, force, styleCB) {
- opt = opt || {};
- cb = cb || function () {};
- if (force !== true) { msg = fixHTML(msg); }
-
- var keyHandler = listenForKeys(function (e) {
- findOKButton().click();
- }, function (e) {
- findCancelButton().click();
- });
-
- Alertify
- .okBtn(opt.ok || Messages.okButton || 'OK')
- .cancelBtn(opt.cancel || Messages.cancelButton || 'Cancel')
- .confirm(msg, function () {
- cb(true);
- stopListening(keyHandler);
- }, function () {
- cb(false);
- stopListening(keyHandler);
- });
-
- window.setTimeout(function () {
- var $ok = findOKButton();
- var $cancel = findCancelButton();
- if (opt.okClass) { $ok.addClass(opt.okClass); }
- if (opt.cancelClass) { $cancel.addClass(opt.cancelClass); }
- if (opt.reverseOrder) {
- $ok.insertBefore($ok.prev());
- }
- if (typeof(styleCB) === 'function') {
- styleCB($ok.closest('.dialog'));
- }
- }, 0);
- };
-
- common.log = function (msg) {
- Alertify.success(fixHTML(msg));
- };
-
- common.warn = function (msg) {
- Alertify.error(fixHTML(msg));
- };
-
- /*
- * spinner
- */
- common.spinner = function (parent) {
- var $target = $('', {
- 'class': 'fa fa-spinner fa-pulse fa-4x fa-fw'
- }).hide();
-
- $(parent).append($target);
-
- return {
- show: function () {
- $target.show();
- return this;
- },
- hide: function () {
- $target.hide();
- return this;
- },
- get: function () {
- return $target;
- },
- };
+ }, common);
};
$(function () {
Messages._applyTranslation();
});
- Alertify._$$alertify.delay = AppConfig.notificationTimeout || 5000;
-
return common;
});
diff --git a/www/common/cursor.js b/www/common/cursor.js
index 07e705244..6da99f1bd 100644
--- a/www/common/cursor.js
+++ b/www/common/cursor.js
@@ -2,10 +2,6 @@ define([
'/common/treesome.js',
'/bower_components/rangy/rangy-core.min.js'
], function (Tree, Rangy, saveRestore) {
- //window.Rangy = Rangy;
- //window.Tree = Tree;
- // do some function for the start and end of the cursor
-
var log = function (x) { console.log(x); };
var error = function (x) { console.log(x); };
var verbose = function (x) { if (window.verboseMode) { console.log(x); } };
@@ -27,108 +23,6 @@ define([
}
};
- // TODO deprecate
- // assumes a negative index
- var seekLeft /* = cursor.seekLeft*/ = function (el, delta, current) {
- var textLength;
- var previous;
-
- // normalize
-
- if (-delta >= current) {
- delta += current;
- current = 0;
- } else {
- current += delta;
- delta = 0;
- }
-
- while (delta) {
- previous = el;
- el = Tree.previousNode(el, inner);
- if (el) {
- textLength = el.textContent.length;
- if (-delta > textLength) {
- delta -= textLength;
- } else {
- current = textLength + delta;
- delta = 0;
- }
- } else {
- return {
- el: previous,
- offset: 0,
- error: "out of bounds"
- };
- }
- }
- return {
- el: el,
- offset: current
- };
- };
-
- // TODO deprecate
- // seekRight assumes a positive delta
- var seekRight = /* cursor.seekRight = */ function (el, delta, current) {
- var textLength;
- var previous;
-
- // normalize
- delta += current;
- current = 0;
-
- while (delta) {
- if (el) {
- textLength = el.textContent.length;
- if (delta >= textLength) {
- delta -= textLength;
- previous = el;
- el = Tree.nextNode(el, inner);
- } else {
- current = delta;
- delta = 0;
- }
- } else {
- // don't ever return a negative index
- if (previous.textContent.length) {
- textLength = previous.textContent.length - 1;
- } else {
- textLength = 0;
- }
- return {
- el: previous,
- offset: textLength,
- error: "out of bounds"
- };
- }
- }
- return {
- el: el,
- offset: current
- };
- };
-
- // TODO deprecate
- var seekToDelta = /* cursor.seekToDelta = */ function (el, delta, current) {
- var result = null;
- if (el) {
- if (delta < 0) {
- return seekLeft(el, delta, current);
- } else if (delta > 0) {
- return seekRight(el, delta, current);
- } else {
- result = {
- el: el,
- offset: current
- };
- }
- } else {
- error("[cursor.seekToDelta] el is undefined");
- }
- return result;
- };
-
/* cursor.update takes notes about wherever the cursor was last seen
in the event of a cursor loss, the information produced by side
effects of this function should be used to recover the cursor
@@ -262,109 +156,6 @@ define([
};
};
- /* getLength assumes that both nodes exist inside of the active editor. */
- // unused currently
- var getLength = cursor.getLength = function () {
- if (Range.start.el === Range.end.el) {
- if (Range.start.offset === Range.end.offset) { return 0; }
- if (Range.start.offset < Range.end.offset) {
- return Range.end.offset - Range.start.offset;
- } else {
- return Range.start.offset - Range.end.offset;
- }
- } else {
- var order = Tree.orderOfNodes(Range.start.el, Range.end.el, inner);
- var L;
- var cur;
-
- /* we know that the cursor elements are different, and that we
- must traverse to find the total length. We also know the
- order of the nodes (probably 1 or -1) */
- if (order === 1) {
- L = (Range.start.el.textContent.length - Range.start.offset);
- cur = Tree.nextNode(Range.start.el, inner);
- while (cur && cur !== Range.end.el) {
- L += cur.textContent.length;
- cur = Tree.nextNode(cur, inner);
- }
- L += Range.end.offset;
- return L;
- } else if (order === -1) {
- L = (Range.end.el.textContent - Range.end.offset);
- cur = Tree.nextNode(Range.end.el, inner);
- while (cur && cur !== Range.start.el) {
- L += cur.textContent.length;
- cur = Tree.nextNode(cur, inner);
- }
- L += Range.start.offset;
- return -L;
- } else {
- console.error("unexpected ordering of nodes...");
- return null;
- }
- }
- };
-
- // previously used for testing
- // TODO deprecate
- var delta = /* cursor.delta = */ function (delta1, delta2) {
- var sel = Rangy.getSelection(inner);
- delta2 = (typeof delta2 !== 'undefined') ? delta2 : delta1;
-
- // update returns errors if there are problems
- // and updates the persistent Range object
- var err = cursor.update(sel, inner);
- if (err) { return err; }
-
- // create a range to modify
- var range = Rangy.createRange();
-
- /*
- The assumption below is that Range.(start|end).el
- actually exists. This might not be the case.
- TODO check if start and end elements are defined
- */
-
- // using infromation about wherever you were last...
- // move both parts by some delta
- var start = seekToDelta(Range.start.el, delta1, Range.start.offset);
- var end = seekToDelta(Range.end.el, delta2, Range.end.offset);
-
- /* if range is backwards, cursor.delta fails
- so check if they're in the expected order
- before setting the new range */
-
- var order = Tree.orderOfNodes(start.el, end.el, inner);
- var backward;
-
- // this could all be one line but nobody would be able to read it
- if (order === -1) {
- // definitely backward
- backward = true;
- } else if (order === 0) {
- // might be backward, check offsets to know for sure
- backward = (start.offset > end.offset);
- } else {
- // definitely not backward
- backward = false;
- }
-
- if (backward) {
- range.setStart(end.el, end.offset);
- range.setEnd(start.el, start.offset);
- } else {
- range.setStart(start.el, start.offset);
- range.setEnd(end.el, end.offset);
- }
-
- // actually set the cursor to the new range
- sel.setSingleRange(range);
- return {
- startError: start.error,
- endError: end.error
- };
- };
-
cursor.brFix = function () {
cursor.update();
var start = Range.start;
diff --git a/www/common/fileObject.js b/www/common/fileObject.js
index 537ada142..6f947dbe3 100644
--- a/www/common/fileObject.js
+++ b/www/common/fileObject.js
@@ -1,7 +1,6 @@
define([
- '/bower_components/jquery/dist/jquery.min.js',
-], function () {
- var $ = window.jQuery;
+ 'jquery',
+], function ($) {
var module = {};
var Messages = {};
@@ -45,6 +44,23 @@ define([
return a;
};
+ var pushFileData = exp.pushData = function (data) {
+ Cryptpad.pinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e, hash) {
+ console.log(hash);
+ });
+ files[FILES_DATA].push(data);
+ };
+ var spliceFileData = exp.removeData = function (idx) {
+ var data = files[FILES_DATA][idx];
+ if (typeof data === "object") {
+ Cryptpad.unpinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e, hash) {
+ console.log(hash);
+ });
+ }
+ files[FILES_DATA].splice(idx, 1);
+ };
+
+
var comparePath = exp.comparePath = function (a, b) {
if (!a || !b || !$.isArray(a) || !$.isArray(b)) { return false; }
if (a.length !== b.length) { return false; }
@@ -72,12 +88,15 @@ define([
var isPathInTrash = exp.isPathInTrash = function (path) {
return path[0] && path[0] === TRASH;
};
+ var isInTrashRoot = exp.isInTrashRoot = function (path) {
+ return path[0] === TRASH && path.length === 4;
+ };
var isPathInFilesData = exp.isPathInFilesData = function (path) {
return path[0] && path[0] === FILES_DATA;
};
- var isFile = exp.isFile = function (element) {
+ var isFile = exp.isFile = function (element) {
return typeof(element) === "string";
};
@@ -422,10 +441,6 @@ define([
return paths;
};
- var isInTrashRoot = exp.isInTrashRoot = function (path) {
- return path[0] === TRASH && path.length === 4;
- };
-
var removePadAttribute = function (f) {
Object.keys(files).forEach(function (key) {
var hash = f.indexOf('#') !== -1 ? f.slice(f.indexOf('#') + 1) : null;
@@ -459,7 +474,7 @@ define([
var idx = files[FILES_DATA].indexOf(f);
if (idx !== -1) {
debug("Removing", f, "from filesData");
- files[FILES_DATA].splice(idx, 1);
+ spliceFileData(idx);
removePadAttribute(f.href);
}
});
@@ -469,9 +484,9 @@ define([
var parentPath = path.slice();
var key = parentPath.pop();
var parentEl = exp.findElement(files, parentPath);
- if (path.length === 4 && path[0] === TRASH) {
+ if (isInTrashRoot(path)) {
files[TRASH][path[1]].splice(path[2], 1);
- } else if (path[0] === UNSORTED || path[0] === TEMPLATE) {
+ } else if (isPathInHrefArray(path)) {
parentEl.splice(key, 1);
} else {
parentEl[key] = undefined;
@@ -745,7 +760,7 @@ define([
};
var pushNewFileData = function (href, title) {
- files[FILES_DATA].push({
+ pushFileData({
href: href,
title: title,
atime: +new Date(),
@@ -865,7 +880,7 @@ define([
var idx = files[FILES_DATA].indexOf(f);
if (idx !== -1) {
debug("Removing", f, "from filesData");
- files[FILES_DATA].splice(idx, 1);
+ spliceFileData(idx);
// Remove the "padAttributes" stored in the realtime object for that pad
removePadAttribute(f.href);
}
@@ -1009,7 +1024,7 @@ define([
return o.href === href;
});
if (!test) {
- files[FILES_DATA].push(fileData);
+ pushFileData(fileData);
}
if (files[TEMPLATE].indexOf(href) === -1) {
files[TEMPLATE].push(href);
@@ -1141,7 +1156,7 @@ define([
toClean.forEach(function (el) {
var idx = fd.indexOf(el);
if (idx !== -1) {
- fd.splice(idx, 1);
+ spliceFileData(idx);
}
});
};
diff --git a/www/common/fsStore.js b/www/common/fsStore.js
index 794a39ecd..b02cccf16 100644
--- a/www/common/fsStore.js
+++ b/www/common/fsStore.js
@@ -1,10 +1,10 @@
define([
+ 'jquery',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
'/bower_components/textpatcher/TextPatcher.amd.js',
- '/common/fileObject.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Listmap, Crypto, TextPatcher, FO) {
+ '/common/userObject.js',
+], function ($, Listmap, Crypto, TextPatcher, FO) {
/*
This module uses localStorage, which is synchronous, but exposes an
asyncronous API. This is so that we can substitute other storage
@@ -13,7 +13,6 @@ define([
To override these methods, create another file at:
/customize/storage.js
*/
- var $ = window.jQuery;
var Store = {};
var store;
@@ -86,21 +85,26 @@ define([
cb(void 0, Object.keys(storeObj));
};
+ ret.removeData = filesOp.removeData;
+ ret.pushData = filesOp.pushData;
+
ret.addPad = function (href, path, name) {
- filesOp.addPad(href, path, name);
+ filesOp.add(href, path, name);
};
ret.forgetPad = function (href, cb) {
- filesOp.forgetPad(href);
+ filesOp.forget(href);
cb();
};
- ret.addTemplate = function (href) {
- filesOp.addTemplate(href);
- };
-
ret.listTemplates = function () {
- return filesOp.listTemplates();
+ var templateFiles = filesOp.getFiles(['template']);
+ var res = [];
+ templateFiles.forEach(function (f) {
+ var data = filesOp.getFileData(f);
+ res.push(JSON.parse(JSON.stringify(data)));
+ });
+ return res;
};
ret.getProxy = function () {
@@ -120,7 +124,7 @@ define([
};
ret.replaceHref = function (o, n) {
- return filesOp.replaceHref(o, n);
+ return filesOp.replace(o, n);
};
var changeHandlers = ret.changeHandlers = [];
diff --git a/www/common/login.js b/www/common/login.js
index 8e9067bc7..dff4a3be3 100644
--- a/www/common/login.js
+++ b/www/common/login.js
@@ -1,12 +1,12 @@
define([
+ 'jquery',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js',
'/common/cryptpad-common.js',
'/common/credential.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
'/bower_components/scrypt-async/scrypt-async.min.js', // better load speed
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Listmap, Crypto, Cryptpad, Cred) {
+], function ($, Listmap, Crypto, Cryptpad, Cred) {
var Exports = {
Cred: Cred,
};
diff --git a/www/common/mergeDrive.js b/www/common/mergeDrive.js
index 688f1cedc..dfd891291 100644
--- a/www/common/mergeDrive.js
+++ b/www/common/mergeDrive.js
@@ -1,8 +1,7 @@
-require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([
'/common/cryptpad-common.js',
'/common/cryptget.js',
- '/common/fileObject.js',
+ '/common/userObject.js',
'json.sortify'
], function (Cryptpad, Crypt, FO, Sortify) {
var exp = {};
@@ -76,8 +75,8 @@ define([
console.error(msg || "Unable to find that path", path);
};
- if (path[0] === FO.TRASH && path.length === 4) {
- href = oldFo.getTrashElementData(path);
+ if (oldFo.isInTrashRoot(path)) {
+ href = oldFo.find(path.slice(0,3));
path.pop();
}
@@ -156,13 +155,13 @@ define([
var newData = Cryptpad.getStore().getProxy();
var newFo = newData.fo;
var newRecentPads = proxy.drive[Cryptpad.storageKey];
- var newFiles = newFo.getFilesDataFiles();
- var oldFiles = oldFo.getFilesDataFiles();
+ var newFiles = newFo.getFiles([newFo.FILES_DATA]);
+ var oldFiles = oldFo.getFiles([newFo.FILES_DATA]);
oldFiles.forEach(function (href) {
// Do not migrate a pad if we already have it, it would create a duplicate in the drive
if (newFiles.indexOf(href) !== -1) { return; }
// If we have a stronger version, do not add the current href
- if (Cryptpad.findStronger(href, newRecentPads)) { return; }
+ if (Cryptpad.findStronger(href, newRecentPads)) { console.log(href); return; }
// If we have a weaker version, replace the href by the new one
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted
var weaker = Cryptpad.findWeaker(href, newRecentPads);
@@ -176,7 +175,7 @@ define([
return;
});
// Update the file in the drive
- newFo.replaceHref(weaker, href);
+ newFo.replace(weaker, href);
return;
}
// Here it means we have a new href, so we should add it to the drive at its old location
diff --git a/www/common/otaml.js b/www/common/otaml.js
deleted file mode 100644
index 3c5482847..000000000
--- a/www/common/otaml.js
+++ /dev/null
@@ -1,1004 +0,0 @@
-(function(){
-var r=function(){var e="function"==typeof require&&require,r=function(i,o,u){o||(o=0);var n=r.resolve(i,o),t=r.m[o][n];if(!t&&e){if(t=e(n))return t}else if(t&&t.c&&(o=t.c,n=t.m,t=r.m[o][t.m],!t))throw new Error('failed to require "'+n+'" from '+o);if(!t)throw new Error('failed to require "'+i+'" from '+u);return t.exports||(t.exports={},t.call(t.exports,t,t.exports,r.relative(n,o))),t.exports};return r.resolve=function(e,n){var i=e,t=e+".js",o=e+"/index.js";return r.m[n][t]&&t?t:r.m[n][o]&&o?o:i},r.relative=function(e,t){return function(n){if("."!=n.charAt(0))return r(n,t,e);var o=e.split("/"),f=n.split("/");o.pop();for(var i=0;i .
- */
-
-var Common = require('./Common');
-var HtmlParse = require('./HtmlParse');
-var Operation = require('./Operation');
-var Sha = require('./SHA256');
-
-var makeTextOperation = module.exports.makeTextOperation = function(oldval, newval)
-{
- if (oldval === newval) { return; }
-
- var begin = 0;
- for (; oldval[begin] === newval[begin]; begin++) ;
-
- var end = 0;
- for (var oldI = oldval.length, newI = newval.length;
- oldval[--oldI] === newval[--newI];
- end++) ;
-
- if (end >= oldval.length - begin) { end = oldval.length - begin; }
- if (end >= newval.length - begin) { end = newval.length - begin; }
-
- return {
- offset: begin,
- toRemove: oldval.length - begin - end,
- toInsert: newval.slice(begin, newval.length - end),
- };
-};
-
-var VOID_TAG_REGEX = new RegExp('^(' + [
- 'area',
- 'base',
- 'br',
- 'col',
- 'hr',
- 'img',
- 'input',
- 'link',
- 'meta',
- 'param',
- 'command',
- 'keygen',
- 'source',
-].join('|') + ')$');
-
-// Get the offset of the previous open/close/void tag.
-// returns the offset of the opening angle bracket.
-var getPreviousTagIdx = function (data, idx)
-{
- if (idx === 0) { return -1; }
- idx = data.lastIndexOf('>', idx);
- // The html tag from hell:
- // < abc def="g" k='lm"nopw>"qrstu"
- for (;;) {
- var mch = data.substring(0,idx).match(/[<"'][^<'"]*$/);
- if (!mch) { return -1; }
- if (mch[0][0] === '<') { return mch.index; }
- idx = data.lastIndexOf(mch[0][0], mch.index-1);
- }
-};
-
-/**
- * Get the name of an HTML tag with leading / if the tag is an end tag.
- *
- * @param data the html text
- * @param offset the index of the < bracket.
- * @return the tag name with possible leading slash.
- */
-var getTagName = function (data, offset)
-{
- if (data[offset] !== '<') { throw new Error(); }
- // Match ugly tags like < / xxx>
- // or < xxx y="z" >
- var m = data.substring(offset).match(/^(<[\s\/]*)([a-zA-Z0-9_-]+)/);
- if (!m) { throw new Error("could not get tag name"); }
- if (m[1].indexOf('/') !== -1) { return '/'+m[2]; }
- return m[2];
-};
-
-/**
- * Get the previous non-void opening tag.
- *
- * @param data the document html
- * @param ctx an empty map for the first call, the same element thereafter.
- * @return an array containing the offset of the open bracket for the begin tag and the
- * the offset of the open bracket for the matching end tag.
- */
-var getPreviousNonVoidTag = function (data, ctx)
-{
- for (;;) {
- if (typeof(ctx.offsets) === 'undefined') {
- // ' ' is an invalid html element name so it will never match anything.
- ctx.offsets = [ { idx: data.length, name: ' ' } ];
- ctx.idx = data.length;
- }
-
- var prev = ctx.idx = getPreviousTagIdx(data, ctx.idx);
- if (prev === -1) {
- if (ctx.offsets.length > 1) { throw new Error(); }
- return [ 0, data.length ];
- }
- var prevTagName = getTagName(data, prev);
-
- if (prevTagName[0] === '/') {
- ctx.offsets.push({ idx: prev, name: prevTagName.substring(1) });
- } else if (prevTagName === ctx.offsets[ctx.offsets.length-1].name) {
- var os = ctx.offsets.pop();
- return [ prev, os.idx ];
- } else if (!VOID_TAG_REGEX.test(prevTagName)) {
- throw new Error();
- }
- }
-};
-
-var indexOfSkipQuoted = function (haystack, needle)
-{
- var os = 0;
- for (;;) {
- var dqi = haystack.indexOf('"');
- var sqi = haystack.indexOf("'");
- var needlei = haystack.indexOf(needle);
- if (needlei === -1) { return -1; }
- if (dqi > -1 && dqi < sqi && dqi < needlei) {
- dqi = haystack.indexOf('"', dqi+1);
- if (dqi === -1) { throw new Error(); }
- haystack = haystack.substring(dqi+1);
- os += dqi+1;
- } else if (sqi > -1 && sqi < needlei) {
- sqi = haystack.indexOf('"', sqi+1);
- if (sqi === -1) { throw new Error(); }
- haystack = haystack.substring(sqi+1);
- os += sqi+1;
- } else {
- return needlei + os;
- }
- }
-};
-
-var tagWidth = module.exports.tagWidth = function (nodeOuterHTML)
-{
- if (nodeOuterHTML.length < 2 || nodeOuterHTML[1] === '!' || nodeOuterHTML[0] !== '<') {
- return 0;
- }
- return indexOfSkipQuoted(nodeOuterHTML, '>') + 1;
-};
-
-var makeHTMLOperation = module.exports.makeHTMLOperation = function (oldval, newval)
-{
- var op = makeTextOperation(oldval, newval);
- if (!op) { return; }
-
- var end = op.offset + op.toRemove;
- var lastTag;
- var tag;
- var ctx = {};
- do {
- lastTag = tag;
- tag = getPreviousNonVoidTag(oldval, ctx);
- } while (tag[0] > op.offset || tag[1] < end);
-
- if (lastTag
- && end < lastTag[0]
- && op.offset > tag[0] + tagWidth(oldval.substring(tag[0])))
- {
- // plain old text operation.
- if (op.toRemove && oldval.substr(op.offset, op.toRemove).indexOf('<') !== -1) {
- throw new Error();
- }
- return op;
- }
-
- op.offset = tag[0];
- op.toRemove = tag[1] - tag[0];
- op.toInsert = newval.slice(tag[0], newval.length - (oldval.length - tag[1]));
-
- return op;
-};
-
-/**
- * Expand an operation to cover enough HTML that any naive transformation
- * will result in correct HTML.
- */
-var expandOp = module.exports.expandOp = function (html, op) {
-return op;
- if (Common.PARANOIA && typeof(html) !== 'string') { throw new Error(); }
- var ctx = {};
- for (;;) {
- var elem = HtmlParse.getPreviousElement(html, ctx);
- // reached the end, this should not happen...
- if (!elem) { throw new Error(JSON.stringify(op)); }
- if (elem.openTagIndex <= op.offset) {
- var endIndex = html.indexOf('>', elem.closeTagIndex) + 1;
- if (!endIndex) { throw new Error(); }
- if (endIndex >= op.offset + op.toRemove) {
- var newHtml = Operation.apply(op, html);
- var newEndIndex = endIndex - op.toRemove + op.toInsert.length;
- var out = Operation.create(elem.openTagIndex,
- endIndex - elem.openTagIndex,
- newHtml.substring(elem.openTagIndex, newEndIndex));
- if (Common.PARANOIA) {
- var test = Operation.apply(out, html);
- if (test !== newHtml) {
- throw new Error(test + '\n\n\n' + newHtml + '\n\n' + elem.openTagIndex + '\n\n' + newEndIndex);
- }
- if (out.toInsert[0] !== '<') { throw new Error(); }
- if (out.toInsert[out.toInsert.length - 1] !== '>') { throw new Error(); }
- }
- return out;
- }
- }
- //console.log(elem);
- }
-};
-
-var transformB = function (html, toTransform, transformBy) {
-
- var transformByEndOffset = transformBy.offset + transformBy.toRemove;
- if (toTransform.offset > transformByEndOffset) {
- // simple rebase
- toTransform.offset -= transformBy.toRemove;
- toTransform.offset += transformBy.toInsert.length;
- return toTransform;
- }
-
- var toTransformEndOffset = toTransform.offset + toTransform.toRemove;
-
- if (transformBy.offset > toTransformEndOffset) {
- // we're before them, no transformation needed.
- return toTransform;
- }
-
- // so we overlap, we're just going to revert one and apply the other.
- // The one which affects more content should probably be applied.
- var toRevert = toTransform;
- var toApply = transformBy;
- var swap = function () {
- var x = toRevert;
- toRevert = toApply;
- toApply = x;
- };
-
- if (toTransform.toInsert.length > transformBy.toInsert.length) {
- swap();
- } else if (toTransform.toInsert.length < transformBy.toInsert.length) {
- // fall through
- } else if (toTransform.toRemove > transformBy.toRemove) {
- swap();
- } else if (toTransform.toRemove < transformBy.toRemove) {
- // fall through
- } else {
- if (Operation.equals(toTransform, transformBy)) { return null; }
- // tie-breaker: we just strcmp the JSON.
- if (Common.strcmp(JSON.stringify(toTransform), JSON.stringify(transformBy)) < 0) { swap(); }
- }
-
- var inverse = Operation.invert(toRevert, html);
- if (Common.PARANOIA) {
- var afterToRevert = Operation.apply(toRevert, html);
-
- }
- if (Common.PARANOIA && !Operation.shouldMerge(inverse, toApply)) { throw new Error(); }
- var out = Operation.merge(inverse, toApply);
-};
-
-// FIXME looks like the old transform is deprecated? figure out why
-var transform = module.exports.transform = function (html, toTransform, transformBy) {
-
- return transformB(html, toTransform, transformBy);
-/*
- toTransform = Operation.clone(toTransform);
- toTransform = expandOp(html, toTransform);
-
- transformBy = Operation.clone(transformBy);
- transformBy = expandOp(html, transformBy);
-
- if (toTransform.offset >= transformBy.offset) {
- if (toTransform.offset >= transformBy.offset + transformBy.toRemove) {
- // simple rebase
- toTransform.offset -= transformBy.toRemove;
- toTransform.offset += transformBy.toInsert.length;
- return toTransform;
- }
-
- // They deleted our begin offset...
-
- var toTransformEndOffset = toTransform.offset + toTransform.toRemove;
- var transformByEndOffset = transformBy.offset + transformBy.toRemove;
- if (transformByEndOffset >= toTransformEndOffset) {
- // They also deleted our end offset, lets forget we wrote anything because
- // whatever it was, they deleted it's context.
- return null;
- }
-
- // goto the end, anything you deleted that they also deleted should be skipped.
- var newOffset = transformBy.offset + transformBy.toInsert.length;
- toTransform.toRemove = 0; //-= (newOffset - toTransform.offset);
- if (toTransform.toRemove < 0) { toTransform.toRemove = 0; }
- toTransform.offset = newOffset;
- if (toTransform.toInsert.length === 0 && toTransform.toRemove === 0) {
- return null;
- }
- return toTransform;
- }
- if (toTransform.offset + toTransform.toRemove < transformBy.offset) {
- return toTransform;
- }
- toTransform.toRemove = transformBy.offset - toTransform.offset;
- if (toTransform.toInsert.length === 0 && toTransform.toRemove === 0) {
- return null;
- }
- return toTransform;
-*/
-};
-
-},
-"SHA256.js": function(module, exports, require){
-/* A JavaScript implementation of the Secure Hash Algorithm, SHA-256
- * Version 0.3 Copyright Angel Marin 2003-2004 - http://anmar.eu.org/
- * Distributed under the BSD License
- * Some bits taken from Paul Johnston's SHA-1 implementation
- */
-(function () {
- var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
- function safe_add (x, y) {
- var lsw = (x & 0xFFFF) + (y & 0xFFFF);
- var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
- return (msw << 16) | (lsw & 0xFFFF);
- }
- function S (X, n) {return ( X >>> n ) | (X << (32 - n));}
- function R (X, n) {return ( X >>> n );}
- function Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
- function Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
- function Sigma0256(x) {return (S(x, 2) ^ S(x, 13) ^ S(x, 22));}
- function Sigma1256(x) {return (S(x, 6) ^ S(x, 11) ^ S(x, 25));}
- function Gamma0256(x) {return (S(x, 7) ^ S(x, 18) ^ R(x, 3));}
- function Gamma1256(x) {return (S(x, 17) ^ S(x, 19) ^ R(x, 10));}
- function newArray (n) {
- var a = [];
- for (;n>0;n--) {
- a.push(undefined);
- }
- return a;
- }
- function core_sha256 (m, l) {
- var K = [0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0xFC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x6CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2];
- var HASH = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19];
- var W = newArray(64);
- var a, b, c, d, e, f, g, h, i, j;
- var T1, T2;
- /* append padding */
- m[l >> 5] |= 0x80 << (24 - l % 32);
- m[((l + 64 >> 9) << 4) + 15] = l;
- for ( var i = 0; i>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
- return bin;
- }
- function binb2hex (binarray) {
- var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
- var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
- var str = "";
- for (var i = 0; i < binarray.length * 4; i++) {
- str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
- hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
- }
- return str;
- }
- function hex_sha256(s){
- return binb2hex(core_sha256(str2binb(s),s.length * chrsz));
- }
- module.exports.hex_sha256 = hex_sha256;
-}());
-
-},
-"Common.js": function(module, exports, require){
-/*
- * Copyright 2014 XWiki SAS
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-var PARANOIA = module.exports.PARANOIA = false;
-
-/* throw errors over non-compliant messages which would otherwise be treated as invalid */
-var TESTING = module.exports.TESTING = true;
-
-var assert = module.exports.assert = function (expr) {
- if (!expr) { throw new Error("Failed assertion"); }
-};
-
-var isUint = module.exports.isUint = function (integer) {
- return (typeof(integer) === 'number') &&
- (Math.floor(integer) === integer) &&
- (integer >= 0);
-};
-
-var randomASCII = module.exports.randomASCII = function (length) {
- var content = [];
- for (var i = 0; i < length; i++) {
- content[i] = String.fromCharCode( Math.floor(Math.random()*256) % 57 + 65 );
- }
- return content.join('');
-};
-
-var strcmp = module.exports.strcmp = function (a, b) {
- if (PARANOIA && typeof(a) !== 'string') { throw new Error(); }
- if (PARANOIA && typeof(b) !== 'string') { throw new Error(); }
- return ( (a === b) ? 0 : ( (a > b) ? 1 : -1 ) );
-}
-
-},
-"Operation.js": function(module, exports, require){
-/*
- * Copyright 2014 XWiki SAS
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-var Common = require('./Common');
-
-var Operation = module.exports;
-
-var check = Operation.check = function (op, docLength_opt) {
- Common.assert(op.type === 'Operation');
- Common.assert(Common.isUint(op.offset));
- Common.assert(Common.isUint(op.toRemove));
- Common.assert(typeof(op.toInsert) === 'string');
- Common.assert(op.toRemove > 0 || op.toInsert.length > 0);
- Common.assert(typeof(docLength_opt) !== 'number' || op.offset + op.toRemove <= docLength_opt);
-};
-
-var create = Operation.create = function (offset, toRemove, toInsert) {
- var out = {
- type: 'Operation',
- offset: offset || 0,
- toRemove: toRemove || 0,
- toInsert: toInsert || '',
- };
- if (Common.PARANOIA) { check(out); }
- return out;
-};
-
-var toObj = Operation.toObj = function (op) {
- if (Common.PARANOIA) { check(op); }
- return [op.offset,op.toRemove,op.toInsert];
-};
-
-var fromObj = Operation.fromObj = function (obj) {
- Common.assert(Array.isArray(obj) && obj.length === 3);
- return create(obj[0], obj[1], obj[2]);
-};
-
-var clone = Operation.clone = function (op) {
- return create(op.offset, op.toRemove, op.toInsert);
-};
-
-/**
- * @param op the operation to apply.
- * @param doc the content to apply the operation on
- */
-var apply = Operation.apply = function (op, doc)
-{
- if (Common.PARANOIA) {
- check(op);
- Common.assert(typeof(doc) === 'string');
- Common.assert(op.offset + op.toRemove <= doc.length);
- }
- return doc.substring(0,op.offset) + op.toInsert + doc.substring(op.offset + op.toRemove);
-};
-
-var invert = Operation.invert = function (op, doc) {
- if (Common.PARANOIA) {
- check(op);
- Common.assert(typeof(doc) === 'string');
- Common.assert(op.offset + op.toRemove <= doc.length);
- }
- var rop = clone(op);
- rop.toInsert = doc.substring(op.offset, op.offset + op.toRemove);
- rop.toRemove = op.toInsert.length;
- return rop;
-};
-
-var simplify = Operation.simplify = function (op, doc) {
- if (Common.PARANOIA) {
- check(op);
- Common.assert(typeof(doc) === 'string');
- Common.assert(op.offset + op.toRemove <= doc.length);
- }
- var rop = invert(op, doc);
- op = clone(op);
-
- var minLen = Math.min(op.toInsert.length, rop.toInsert.length);
- var i;
- for (i = 0; i < minLen && rop.toInsert[i] === op.toInsert[i]; i++) ;
- op.offset += i;
- op.toRemove -= i;
- op.toInsert = op.toInsert.substring(i);
- rop.toInsert = rop.toInsert.substring(i);
-
- if (rop.toInsert.length === op.toInsert.length) {
- for (i = rop.toInsert.length-1; i >= 0 && rop.toInsert[i] === op.toInsert[i]; i--) ;
- op.toInsert = op.toInsert.substring(0, i+1);
- op.toRemove = i+1;
- }
-
- if (op.toRemove === 0 && op.toInsert.length === 0) { return null; }
- return op;
-};
-
-var equals = Operation.equals = function (opA, opB) {
- return (opA.toRemove === opB.toRemove
- && opA.toInsert === opB.toInsert
- && opA.offset === opB.offset);
-};
-
-var lengthChange = Operation.lengthChange = function (op)
-{
- if (Common.PARANOIA) { check(op); }
- return op.toInsert.length - op.toRemove;
-};
-
-/*
- * @return the merged operation OR null if the result of the merger is a noop.
- */
-var merge = Operation.merge = function (oldOpOrig, newOpOrig) {
- if (Common.PARANOIA) {
- check(newOpOrig);
- check(oldOpOrig);
- }
-
- var newOp = clone(newOpOrig);
- var oldOp = clone(oldOpOrig);
- var offsetDiff = newOp.offset - oldOp.offset;
-
- if (newOp.toRemove > 0) {
- var origOldInsert = oldOp.toInsert;
- oldOp.toInsert = (
- oldOp.toInsert.substring(0,offsetDiff)
- + oldOp.toInsert.substring(offsetDiff + newOp.toRemove)
- );
- newOp.toRemove -= (origOldInsert.length - oldOp.toInsert.length);
- if (newOp.toRemove < 0) { newOp.toRemove = 0; }
-
- oldOp.toRemove += newOp.toRemove;
- newOp.toRemove = 0;
- }
-
- if (offsetDiff < 0) {
- oldOp.offset += offsetDiff;
- oldOp.toInsert = newOp.toInsert + oldOp.toInsert;
-
- } else if (oldOp.toInsert.length === offsetDiff) {
- oldOp.toInsert = oldOp.toInsert + newOp.toInsert;
-
- } else if (oldOp.toInsert.length > offsetDiff) {
- oldOp.toInsert = (
- oldOp.toInsert.substring(0,offsetDiff)
- + newOp.toInsert
- + oldOp.toInsert.substring(offsetDiff)
- );
- } else {
- throw new Error("should never happen\n" +
- JSON.stringify([oldOpOrig,newOpOrig], null, ' '));
- }
-
- if (oldOp.toInsert === '' && oldOp.toRemove === 0) {
- return null;
- }
- if (Common.PARANOIA) { check(oldOp); }
-
- return oldOp;
-};
-
-/**
- * If the new operation deletes what the old op inserted or inserts content in the middle of
- * the old op's content or if they abbut one another, they should be merged.
- */
-var shouldMerge = Operation.shouldMerge = function (oldOp, newOp) {
- if (Common.PARANOIA) {
- check(oldOp);
- check(newOp);
- }
- if (newOp.offset < oldOp.offset) {
- return (oldOp.offset <= (newOp.offset + newOp.toRemove));
- } else {
- return (newOp.offset <= (oldOp.offset + oldOp.toInsert.length));
- }
-};
-
-/**
- * Rebase newOp against oldOp.
- *
- * @param oldOp the eariler operation to have happened.
- * @param newOp the later operation to have happened (in time).
- * @return either the untouched newOp if it need not be rebased,
- * the rebased clone of newOp if it needs rebasing, or
- * null if newOp and oldOp must be merged.
- */
-var rebase = Operation.rebase = function (oldOp, newOp) {
- if (Common.PARANOIA) {
- check(oldOp);
- check(newOp);
- }
- if (newOp.offset < oldOp.offset) { return newOp; }
- newOp = clone(newOp);
- newOp.offset += oldOp.toRemove;
- newOp.offset -= oldOp.toInsert.length;
- return newOp;
-};
-
-/**
- * this is a lossy and dirty algorithm, everything else is nice but transformation
- * has to be lossy because both operations have the same base and they diverge.
- * This could be made nicer and/or tailored to a specific data type.
- *
- * @param toTransform the operation which is converted *MUTATED*.
- * @param transformBy an existing operation which also has the same base.
- * @return toTransform *or* null if the result is a no-op.
- */
-var transform0 = Operation.transform0 = function (text, toTransform, transformBy) {
- if (toTransform.offset > transformBy.offset) {
- if (toTransform.offset > transformBy.offset + transformBy.toRemove) {
- // simple rebase
- toTransform.offset -= transformBy.toRemove;
- toTransform.offset += transformBy.toInsert.length;
- return toTransform;
- }
- // goto the end, anything you deleted that they also deleted should be skipped.
- var newOffset = transformBy.offset + transformBy.toInsert.length;
- toTransform.toRemove = 0; //-= (newOffset - toTransform.offset);
- if (toTransform.toRemove < 0) { toTransform.toRemove = 0; }
- toTransform.offset = newOffset;
- if (toTransform.toInsert.length === 0 && toTransform.toRemove === 0) {
- return null;
- }
- return toTransform;
- }
- if (toTransform.offset + toTransform.toRemove < transformBy.offset) {
- return toTransform;
- }
- toTransform.toRemove = transformBy.offset - toTransform.offset;
- if (toTransform.toInsert.length === 0 && toTransform.toRemove === 0) {
- return null;
- }
- return toTransform;
-};
-
-/**
- * @param toTransform the operation which is converted
- * @param transformBy an existing operation which also has the same base.
- * @return a modified clone of toTransform *or* toTransform itself if no change was made.
- */
-var transform = Operation.transform = function (text, toTransform, transformBy, transformFunction) {
- if (Common.PARANOIA) {
- check(toTransform);
- check(transformBy);
- }
- transformFunction = transformFunction || transform0;
- toTransform = clone(toTransform);
- var result = transformFunction(text, toTransform, transformBy);
- if (Common.PARANOIA && result) { check(result); }
- return result;
-};
-
-/** Used for testing. */
-var random = Operation.random = function (docLength) {
- Common.assert(Common.isUint(docLength));
- var offset = Math.floor(Math.random() * 100000000 % docLength) || 0;
- var toRemove = Math.floor(Math.random() * 100000000 % (docLength - offset)) || 0;
- var toInsert = '';
- do {
- var toInsert = Common.randomASCII(Math.floor(Math.random() * 20));
- } while (toRemove === 0 && toInsert === '');
- return create(offset, toRemove, toInsert);
-};
-
-},
-"HtmlParse.js": function(module, exports, require){
-/*
- * Copyright 2014 XWiki SAS
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-var VOID_TAG_REGEX = module.exports.VOID_TAG_REGEX = new RegExp('^(' + [
- 'area',
- 'base',
- 'br',
- 'col',
- 'hr',
- 'img',
- 'input',
- 'link',
- 'meta',
- 'param',
- 'command',
- 'keygen',
- 'source',
-].join('|') + ')$');
-
-/**
- * Get the offset of the previous open/close/void tag.
- * returns the offset of the opening angle bracket.
- */
-var getPreviousTagIdx = module.exports.getPreviousTagIdx = function (data, idx) {
- if (idx === 0) { return -1; }
- idx = data.lastIndexOf('>', idx);
- // The html tag from hell:
- // < abc def="g" k='lm"nopw>"qrstu"
- for (;;) {
- var mch = data.substring(0,idx).match(/[<"'][^<'"]*$/);
- if (!mch) { return -1; }
- if (mch[0][0] === '<') { return mch.index; }
- idx = data.lastIndexOf(mch[0][0], mch.index-1);
- }
-};
-
-/**
- * Get the name of an HTML tag with leading / if the tag is an end tag.
- *
- * @param data the html text
- * @param offset the index of the < bracket.
- * @return the tag name with possible leading slash.
- */
-var getTagName = module.exports.getTagName = function (data, offset) {
- if (data[offset] !== '<') { throw new Error(); }
- // Match ugly tags like < / xxx>
- // or < xxx y="z" >
- var m = data.substring(offset).match(/^(<[\s\/]*)([a-zA-Z0-9_-]+)/);
- if (!m) { throw new Error("could not get tag name"); }
- if (m[1].indexOf('/') !== -1) { return '/'+m[2]; }
- return m[2];
-};
-
-/**
- * Get the previous void or opening tag.
- *
- * @param data the document html
- * @param ctx an empty map for the first call, the same element thereafter.
- * @return an object containing openTagIndex: the offset of the < bracket for the begin tag,
- * closeTagIndex: the the offset of the < bracket for the matching end tag, and
- * nodeName: the element name.
- * If the element is a void element, the second value in the array will be -1.
- */
-var getPreviousElement = module.exports.getPreviousElement = function (data, ctx) {
- for (;;) {
- if (typeof(ctx.offsets) === 'undefined') {
- // ' ' is an invalid html element name so it will never match anything.
- ctx.offsets = [ { idx: data.length, name: ' ' } ];
- ctx.idx = data.length;
- }
-
- var prev = ctx.idx = getPreviousTagIdx(data, ctx.idx);
- if (prev === -1) {
- if (ctx.offsets.length > 1) { throw new Error(); }
- return null;
- }
- var prevTagName = getTagName(data, prev);
-
- if (prevTagName[0] === '/') {
- ctx.offsets.push({ idx: prev, name: prevTagName.substring(1) });
- } else if (prevTagName === ctx.offsets[ctx.offsets.length-1].name) {
- var os = ctx.offsets.pop();
- return { openTagIndex: prev, closeTagIndex: os.idx, nodeName: prevTagName };
- } else if (!VOID_TAG_REGEX.test(prevTagName)) {
- throw new Error("unmatched tag [" + prevTagName + "] which is not a void tag");
- } else {
- return { openTagIndex: prev, closeTagIndex: -1, nodeName: prevTagName };
- }
- }
-};
-
-/**
- * Given a piece of HTML text which begins at the < of a non-close tag,
- * give the index within that content which contains the matching >
- * character skipping > characters contained within attributes.
- */
-var getEndOfTag = module.exports.getEndOfTag = function (html) {
- var arr = html.match(/['">][^"'>]*/g);
- var q = null;
- var idx = html.indexOf(arr[0]);
- for (var i = 0; i < arr.length; i++) {
- if (!q) {
- q = arr[i][0];
- if (q === '>') { return idx; }
- } else if (q === arr[i][0]) {
- q = null;
- }
- idx += arr[i].length;
- }
- throw new Error("Could not find end of tag");
-};
-
-
-var ParseTagState = {
- OUTSIDE: 0,
- NAME: 1,
- VALUE: 2,
- SQUOTE: 3,
- DQUOTE: 4,
-};
-
-var parseTag = module.exports.parseTag = function (html) {
- if (html[0] !== '<') { throw new Error("Must be the beginning of a tag"); }
-
- var out = {
- nodeName: null,
- attributes: [],
- endIndex: -1,
- trailingSlash: false
- };
-
- if (html.indexOf('>') < html.indexOf(' ') || html.indexOf(' ') === -1) {
- out.endIndex = html.indexOf('>');
- out.nodeName = html.substring(1, out.endIndex);
- return out;
- }
-
- out.nodeName = html.substring(1, html.indexOf(' '));
-
- if (html.indexOf('<' + out.nodeName + ' ') !== 0) {
- throw new Error("Nonstandard beginning of tag [" +
- html.substring(0, 30) + '] for nodeName [' + out.nodeName + ']');
- }
- var i = 1 + out.nodeName.length + 1;
-
- var state = ParseTagState.OUTSIDE;
- var name = [];
- var value = [];
- var pushAttribute = function () {
- out.attributes.push([name.join(''), value.join('')]);
- name = [];
- value = [];
- };
- for (; i < html.length; i++) {
- var chr = html[i];
- switch (state) {
- case ParseTagState.OUTSIDE: {
- if (chr === '/') {
- out.trailingSlash = true;
- } else if (chr.match(/[a-zA-Z0-9_-]/)) {
- state = ParseTagState.NAME;
- if (name.length > 0) { throw new Error(); }
- name.push(chr);
- } else if (chr === '>') {
- out.endIndex = i;
- return out;
- } else if (chr === ' ') {
- // fall through
- } else {
- throw new Error();
- }
- continue;
- }
- case ParseTagState.NAME: {
- if (chr.match(/[a-zA-Z0-9_-]/)) {
- name.push(chr);
- } else if (chr === '=') {
- state = ParseTagState.VALUE;
- } else if (chr === '/' || chr === ' ') {
- if (chr === '/') {
- out.trailingSlash = true;
- }
- out.attributes.push([name.join(''), null]);
- name = [];
- state = ParseTagState.OUTSIDE;
- } else if (chr === '>') {
- out.attributes.push([name.join(''), null]);
- name = [];
- out.endIndex = i;
- return out;
- } else {
- throw new Error("bad character [" + chr + "] in name [" + name.join('') + "]");
- }
- continue;
- }
- case ParseTagState.VALUE: {
- value.push(chr);
- if (chr === '"') {
- state = ParseTagState.DQUOTE;
- } else if (chr === "'") {
- state = ParseTagState.SQUOTE;
- } else {
- throw new Error();
- }
- continue;
- }
- case ParseTagState.SQUOTE: {
- value.push(chr);
- if (chr === "'") {
- pushAttribute();
- state = ParseTagState.OUTSIDE;
- }
- continue;
- }
- case ParseTagState.DQUOTE: {
- value.push(chr);
- if (chr === '"') {
- pushAttribute();
- state = ParseTagState.OUTSIDE;
- }
- continue;
- }
- }
- }
-
- throw new Error("reached end of file while parsing");
-};
-
-var serializeTag = module.exports.serializeTag = function (tag) {
- var out = ['<', tag.nodeName];
- for (var i = 0; i < tag.attributes.length; i++) {
- var att = tag.attributes[i];
- if (att[1] === null) {
- out.push(' ', att[0]);
- } else {
- out.push(' ', att[0], '=', att[1]);
- }
- }
- if (tag.trailingSlash) {
- out.push(' /');
- }
- out.push('>');
- return out.join('');
-};
-
-}
-};
-Otaml = r("Otaml.js");}());
diff --git a/www/common/pinpad.js b/www/common/pinpad.js
index 78ae607db..a9467cff9 100644
--- a/www/common/pinpad.js
+++ b/www/common/pinpad.js
@@ -1,17 +1,29 @@
define([
'/common/rpc.js',
- '/bower_components/tweetnacl/nacl-fast.min.js'
], function (Rpc) {
- var Nacl = window.nacl;
-
var create = function (network, proxy, cb) {
- if (!network) { return void cb('INVALID_NETWORK'); }
- if (!proxy) { return void cb('INVALID_PROXY'); }
+ if (!network) {
+ window.setTimeout(function () {
+ cb('INVALID_NETWORK');
+ });
+ return;
+ }
+ if (!proxy) {
+ window.setTimeout(function () {
+ cb('INVALID_PROXY');
+ });
+ return;
+ }
var edPrivate = proxy.edPrivate;
var edPublic = proxy.edPublic;
- if (!(edPrivate && edPublic)) { return void cb('INVALID_KEYS'); }
+ if (!(edPrivate && edPublic)) {
+ window.setTimeout(function () {
+ cb('INVALID_KEYS');
+ });
+ return;
+ }
Rpc.create(network, edPrivate, edPublic, function (e, rpc) {
if (e) { return void cb(e); }
@@ -26,21 +38,26 @@ define([
// you can ask the server to pin a particular channel for you
exp.pin = function (channels, cb) {
+ if (!Array.isArray(channels)) {
+ window.setTimeout(function () {
+ cb('[TypeError] pin expects an array');
+ });
+ return;
+ }
rpc.send('PIN', channels, cb);
};
// you can also ask to unpin a particular channel
exp.unpin = function (channels, cb) {
+ if (!Array.isArray(channels)) {
+ window.setTimeout(function () {
+ cb('[TypeError] pin expects an array');
+ });
+ return;
+ }
rpc.send('UNPIN', channels, cb);
};
- // This implementation must match that on the server
- // it's used for a checksum
- exp.hashChannelList = function (list) {
- return Nacl.util.encodeBase64(Nacl.hash(Nacl.util
- .decodeUTF8(JSON.stringify(list))));
- };
-
// ask the server what it thinks your hash is
exp.getServerHash = function (cb) {
rpc.send('GET_HASH', edPublic, function (e, hash) {
@@ -52,8 +69,14 @@ define([
};
// if local and remote hashes don't match, send a reset
- exp.reset = function (list, cb) {
- rpc.send('RESET', list, function (e, response) {
+ exp.reset = function (channels, cb) {
+ if (!Array.isArray(channels)) {
+ window.setTimeout(function () {
+ cb('[TypeError] pin expects an array');
+ });
+ return;
+ }
+ rpc.send('RESET', channels, function (e, response) {
cb(e, response[0]);
});
};
@@ -66,7 +89,12 @@ define([
// get the combined size of all channels (in bytes) for all the
// channels which the server has pinned for your publicKey
exp.getFileListSize = function (cb) {
- rpc.send('GET_TOTAL_SIZE', undefined, cb);
+ rpc.send('GET_TOTAL_SIZE', undefined, function (e, response) {
+ if (e) { return void cb(e); }
+ if (response && response.length) {
+ cb(void 0, response[0]);
+ }
+ });
};
cb(e, exp);
diff --git a/www/common/rainbow.js b/www/common/rainbow.js
deleted file mode 100644
index 6aa732c4e..000000000
--- a/www/common/rainbow.js
+++ /dev/null
@@ -1,29 +0,0 @@
-define([], function () {
- return function (n) {
- n = n || 24; // default is 24 colours
- var r = 0.6,
- i = 0,
- t = [],
- rgb = [0,2,4];
-
- while(i= 0) {
- // check from back to front
-
- // check the node's children (depth first)
- // if the predicate tests true, return true
- if (tree.some(root.children[last], predicate)) {
- return true;
- } // otherwise none of the nodes inside it matched.
-
- // check the node itself
- if (predicate(root.children[last], last)) {
- return true;
- }
- last--;
- }
- return false;
- };
-
- // FIXME this isn't being used
- var someText = tree.someIncludingText = function (root, predicate) {
- // take the index of the last element in the current root
- var last = root.childNodes.length - 1;
-
- // it might be a leaf node
- if (last < 0) { return false; }
-
- // otherwise it has children
- while (last >= 0) {
- // check from back to front
-
- // check the node's children (depth first)
- // if the predicate tests true, return true
- if (tree.someIncludingText(root.childNodes[last], predicate)) {
- return true;
- } // otherwise none of the nodes inside it matched.
-
- // check the node itself
- if (predicate(root.childNodes[last], last)) {
- return true;
- }
- last--;
- }
- return false;
- };
-
- // FIXME not being used
- tree.findSameHierarchy = function (list, ancestor) {
- var i = 0;
- var success = true;
- var last = list.length - 1;
- var el;
-
- tree.someIncludingText(ancestor, function (e) {
- // don't out of bounds
- if (i > last) {
- // unsuccessful
- success = false;
- return true;
- }
-
- if (list[i] === (e.tagName||e.nodeName)) {
-
- if (i === last) {
- el = e;
- return true;
- }
- i++;
- } else {
- // hierarchy has changed, what should we do?
- success = false;
- return true; // terminate
- }
- });
- return success? el: false;
- };
-
var indexOfNode = tree.indexOfNode = function (el) {
if (!(el && el.parentNode)) {
console.log("No parentNode found!");
@@ -107,13 +22,6 @@ define([], function () {
return el.childNodes.length;
};
- var parentsOf = tree.parentsOf = function (el, root) {
- var P = [];
- var p = el;
- while (p !== root) { P.push((p = p.parentNode)); }
- return P;
- };
-
/* rightmost and leftmost return the deepest right and left
leaf nodes of a tree
*/
diff --git a/www/common/userObject.js b/www/common/userObject.js
new file mode 100644
index 000000000..6a70a3622
--- /dev/null
+++ b/www/common/userObject.js
@@ -0,0 +1,898 @@
+define([
+ 'jquery',
+], function ($) {
+ var module = {};
+
+ var ROOT = module.ROOT = "root";
+ var UNSORTED = module.UNSORTED = "unsorted";
+ var TRASH = module.TRASH = "trash";
+ var TEMPLATE = module.TEMPLATE = "template";
+
+ var init = module.init = function (files, config) {
+ var exp = {};
+ var Cryptpad = config.Cryptpad;
+ var Messages = Cryptpad.Messages;
+
+ var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Cryptpad.storageKey;
+ var NEW_FOLDER_NAME = Messages.fm_newFolder;
+ var NEW_FILE_NAME = Messages.fm_newFile;
+
+ // Logging
+ var DEBUG = config.DEBUG || false;
+ var logging = function () {
+ console.log.apply(console, arguments);
+ };
+ var log = config.log || logging;
+ var logError = config.logError || logging;
+ var debug = config.debug || logging;
+ var error = exp.error = function() {
+ exp.fixFiles();
+ console.error.apply(console, arguments);
+ };
+
+ // TODO: workgroup
+ var workgroup = config.workgroup;
+
+
+ /*
+ * UTILS
+ */
+
+ var getStructure = exp.getStructure = function () {
+ var a = {};
+ a[ROOT] = {};
+ a[UNSORTED] = [];
+ a[TRASH] = {};
+ a[FILES_DATA] = [];
+ a[TEMPLATE] = [];
+ return a;
+ };
+ var getHrefArray = function () {
+ return [UNSORTED, TEMPLATE];
+ };
+
+
+ var compareFiles = function (fileA, fileB) { return fileA === fileB; };
+
+ var isFile = exp.isFile = function (element) {
+ return typeof(element) === "string";
+ };
+
+ var isReadOnlyFile = exp.isReadOnlyFile = function (element) {
+ if (!isFile(element)) { return false; }
+ var parsed = Cryptpad.parsePadUrl(element);
+ if (!parsed) { return false; }
+ var hash = parsed.hash;
+ var pHash = Cryptpad.parseHash(hash);
+ if (pHash && !pHash.mode) { return; }
+ return pHash && pHash.mode === 'view';
+ };
+
+ var isFolder = exp.isFolder = function (element) {
+ return typeof(element) !== "string";
+ };
+ var isFolderEmpty = exp.isFolderEmpty = function (element) {
+ if (typeof(element) !== "object") { return false; }
+ return Object.keys(element).length === 0;
+ };
+
+ var hasSubfolder = exp.hasSubfolder = function (element, trashRoot) {
+ if (typeof(element) !== "object") { return false; }
+ var subfolder = 0;
+ var addSubfolder = function (el, idx) {
+ subfolder += isFolder(el.element) ? 1 : 0;
+ };
+ for (var f in element) {
+ if (trashRoot) {
+ if ($.isArray(element[f])) {
+ element[f].forEach(addSubfolder);
+ }
+ } else {
+ subfolder += isFolder(element[f]) ? 1 : 0;
+ }
+ }
+ return subfolder;
+ };
+
+ var hasFile = exp.hasFile = function (element, trashRoot) {
+ if (typeof(element) !== "object") { return false; }
+ var file = 0;
+ var addFile = function (el, idx) {
+ file += isFile(el.element) ? 1 : 0;
+ };
+ for (var f in element) {
+ if (trashRoot) {
+ if ($.isArray(element[f])) {
+ element[f].forEach(addFile);
+ }
+ } else {
+ file += isFile(element[f]) ? 1 : 0;
+ }
+ }
+ return file;
+ };
+
+ // Get data from AllFiles (Cryptpad_RECENTPADS)
+ var getFileData = exp.getFileData = function (file) {
+ if (!file) { return; }
+ var res;
+ files[FILES_DATA].some(function(arr) {
+ var href = arr.href;
+ if (href === file) {
+ res = arr;
+ return true;
+ }
+ return false;
+ });
+ return res;
+ };
+
+ // Data from filesData
+ var getTitle = exp.getTitle = function (href) {
+ if (workgroup) { debug("No titles in workgroups"); return; }
+ var data = getFileData(href);
+ if (!href || !data) {
+ error("getTitle called with a non-existing href: ", href);
+ return;
+ }
+ return data.title;
+ };
+
+
+ // PATHS
+
+ var comparePath = exp.comparePath = function (a, b) {
+ if (!a || !b || !$.isArray(a) || !$.isArray(b)) { return false; }
+ if (a.length !== b.length) { return false; }
+ var result = true;
+ var i = a.length - 1;
+ while (result && i >= 0) {
+ result = a[i] === b[i];
+ i--;
+ }
+ return result;
+ };
+
+ var isSubpath = exp.isSubpath = function (path, parentPath) {
+ var pathA = parentPath.slice();
+ var pathB = path.slice(0, pathA.length);
+ return comparePath(pathA, pathB);
+ };
+
+ var isPathIn = exp.isPathIn = function (path, categories) {
+ if (!categories) { return; }
+ var idx = categories.indexOf('hrefArray');
+ if (idx !== -1) {
+ categories.splice(idx, 1);
+ categories = categories.concat(getHrefArray());
+ }
+ return categories.some(function (c) {
+ return Array.isArray(path) && path[0] === c;
+ });
+ };
+
+ var isInTrashRoot = exp.isInTrashRoot = function (path) {
+ return path[0] === TRASH && path.length === 4;
+ };
+
+
+ // FIND
+
+ var findElement = function (root, pathInput) {
+ if (!pathInput) {
+ error("Invalid path:\n", pathInput, "\nin root\n", root);
+ return;
+ }
+ if (pathInput.length === 0) { return root; }
+ var path = pathInput.slice();
+ var key = path.shift();
+ if (typeof root[key] === "undefined") {
+ debug("Unable to find the key '" + key + "' in the root object provided:", root);
+ return;
+ }
+ return findElement(root[key], path);
+ };
+
+ var find = exp.find = function (path) {
+ return findElement(files, path);
+ };
+
+
+ // GET FILES
+
+ var getFilesRecursively = function (root, arr) {
+ for (var e in root) {
+ if (isFile(root[e])) {
+ if(arr.indexOf(root[e]) === -1) { arr.push(root[e]); }
+ } else {
+ getFilesRecursively(root[e], arr);
+ }
+ }
+ };
+ var _getFiles = {};
+ _getFiles['array'] = function (cat) {
+ if (!files[cat]) { files[cat] = []; }
+ return files[cat].slice();
+ };
+ getHrefArray().forEach(function (c) {
+ _getFiles[c] = function () { return _getFiles['array'](c); };
+ });
+ _getFiles['hrefArray'] = function () {
+ var ret = [];
+ getHrefArray().forEach(function (c) {
+ ret = ret.concat(_getFiles[c]());
+ });
+ return Cryptpad.deduplicateString(ret);
+ };
+ _getFiles[ROOT] = function () {
+ var ret = [];
+ getFilesRecursively(files[ROOT], ret);
+ return ret;
+ };
+ _getFiles[TRASH] = function () {
+ var root = files[TRASH];
+ var ret = [];
+ var addFiles = function (el, idx) {
+ if (isFile(el.element)) {
+ if(ret.indexOf(el.element) === -1) { ret.push(el.element); }
+ } else {
+ getFilesRecursively(el.element, ret);
+ }
+ };
+ for (var e in root) {
+ if (!$.isArray(root[e])) {
+ error("Trash contains a non-array element");
+ return;
+ }
+ root[e].forEach(addFiles);
+ }
+ return ret;
+ };
+ _getFiles[FILES_DATA] = function () {
+ var ret = [];
+ files[FILES_DATA].forEach(function (el) {
+ if (el.href && ret.indexOf(el.href) === -1) {
+ ret.push(el.href);
+ }
+ });
+ return ret;
+ };
+ var getFiles = exp.getFiles = function (categories) {
+ var ret = [];
+ if (!categories || !categories.length) {
+ categories = [ROOT, 'hrefArray', TRASH, FILES_DATA];
+ }
+ categories.forEach(function (c) {
+ if (typeof _getFiles[c] === "function") {
+ ret = ret.concat(_getFiles[c]());
+ }
+ });
+ return Cryptpad.deduplicateString(ret);
+ };
+
+ // SEARCH
+ var _findFileInRoot = function (path, href) {
+ if (!isPathIn(path, [ROOT, TRASH])) { return []; }
+ var paths = [];
+ var root = find(path);
+ var addPaths = function (p) {
+ if (paths.indexOf(p) === -1) {
+ paths.push(p);
+ }
+ };
+
+ if (isFile(root)) {
+ if (compareFiles(href, root)) {
+ if (paths.indexOf(path) === -1) {
+ paths.push(path);
+ }
+ }
+ return paths;
+ }
+ for (var e in root) {
+ var nPath = path.slice();
+ nPath.push(e);
+ _findFileInRoot(nPath, href).forEach(addPaths);
+ }
+
+ return paths;
+ };
+ var _findFileInHrefArray = function (rootName, href) {
+ var unsorted = files[rootName].slice();
+ var ret = [];
+ var i = -1;
+ while ((i = unsorted.indexOf(href, i+1)) !== -1){
+ ret.push([rootName, i]);
+ }
+ return ret;
+ };
+ var _findFileInTrash = function (path, href) {
+ var root = find(path);
+ var paths = [];
+ var addPaths = function (p) {
+ if (paths.indexOf(p) === -1) {
+ paths.push(p);
+ }
+ };
+ if (path.length === 1 && typeof(root) === 'object') {
+ Object.keys(root).forEach(function (key) {
+ var arr = root[key];
+ if (!Array.isArray(arr)) { return; }
+ var nPath = path.slice();
+ nPath.push(key);
+ _findFileInTrash(nPath, href).forEach(addPaths);
+ });
+ }
+ if (path.length === 2) {
+ if (!Array.isArray(root)) { return []; }
+ root.forEach(function (el, i) {
+ var nPath = path.slice();
+ nPath.push(i);
+ nPath.push('element');
+ if (isFile(el.element)) {
+ if (compareFiles(href, el.element)) {
+ addPaths(nPath);
+ }
+ return;
+ }
+ _findFileInTrash(nPath, href).forEach(addPaths);
+ });
+ }
+ if (path.length >= 4) {
+ _findFileInRoot(path, href).forEach(addPaths);
+ }
+ return paths;
+ };
+ var findFile = exp.findFile = function (href) {
+ var rootpaths = _findFileInRoot([ROOT], href);
+ var unsortedpaths = _findFileInHrefArray(UNSORTED, href);
+ var templatepaths = _findFileInHrefArray(TEMPLATE, href);
+ var trashpaths = _findFileInTrash([TRASH], href);
+ return rootpaths.concat(unsortedpaths, templatepaths, trashpaths);
+ };
+ var search = exp.search = function (value) {
+ if (typeof(value) !== "string") { return []; }
+ var res = [];
+ // Search in ROOT
+ var findIn = function (root) {
+ Object.keys(root).forEach(function (k) {
+ if (isFile(root[k])) {
+ if (k.toLowerCase().indexOf(value.toLowerCase()) !== -1) {
+ res.push(root[k]);
+ }
+ return;
+ }
+ findIn(root[k]);
+ });
+ };
+ findIn(files[ROOT]);
+ // Search in TRASH
+ var trash = files[TRASH];
+ Object.keys(trash).forEach(function (k) {
+ if (k.toLowerCase().indexOf(value.toLowerCase()) !== -1) {
+ trash[k].forEach(function (el) {
+ if (isFile(el.element)) {
+ res.push(el.element);
+ }
+ });
+ }
+ trash[k].forEach(function (el) {
+ if (isFolder(el.element)) {
+ findIn(el.element);
+ }
+ });
+ });
+
+ // Search title
+ var allFilesList = files[FILES_DATA].slice();
+ allFilesList.forEach(function (t) {
+ if (t.title && t.title.toLowerCase().indexOf(value.toLowerCase()) !== -1) {
+ res.push(t.href);
+ }
+ });
+
+ // Search Href
+ var href = Cryptpad.getRelativeHref(value);
+ if (href) {
+ res.push(href);
+ }
+
+ res = Cryptpad.deduplicateString(res);
+
+ var ret = [];
+ res.forEach(function (l) {
+ var paths = findFile(l);
+ ret.push({
+ paths: findFile(l),
+ data: exp.getFileData(l)
+ });
+ });
+ return ret;
+ };
+
+ /**
+ * OPERATIONS
+ */
+
+ var getAvailableName = function (parentEl, name) {
+ if (typeof(parentEl[name]) === "undefined") { return name; }
+ var newName = name;
+ var i = 1;
+ while (typeof(parentEl[newName]) !== "undefined") {
+ newName = name + "_" + i;
+ i++;
+ }
+ return newName;
+ };
+
+ // FILES DATA
+ var pushFileData = exp.pushData = function (data) {
+ Cryptpad.pinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e, hash) {
+ if (e) { console.log(e); return; }
+ console.log(hash);
+ });
+ files[FILES_DATA].push(data);
+ };
+ var spliceFileData = exp.removeData = function (idx) {
+ var data = files[FILES_DATA][idx];
+ if (typeof data === "object") {
+ Cryptpad.unpinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e, hash) {
+ if (e) { console.log(e); return; }
+ console.log(hash);
+ });
+ }
+ files[FILES_DATA].splice(idx, 1);
+ };
+
+ // MOVE
+ var pushToTrash = function (name, element, path) {
+ var trash = files[TRASH];
+ if (typeof(trash[name]) === "undefined") { trash[name] = []; }
+ var trashArray = trash[name];
+ var trashElement = {
+ element: element,
+ path: path
+ };
+ trashArray.push(trashElement);
+ };
+ var copyElement = function (elementPath, newParentPath) {
+ if (comparePath(elementPath, newParentPath)) { return; } // Nothing to do...
+ var element = find(elementPath);
+ var newParent = find(newParentPath);
+
+ // Never move a folder in one of its children
+ if (isSubpath(newParentPath, elementPath)) {
+ log(Messages.fo_moveFolderToChildError);
+ return;
+ }
+
+ // Move to Trash
+ if (isPathIn(newParentPath, [TRASH])) {
+ if (!elementPath || elementPath.length < 2 || elementPath[0] === TRASH) {
+ debug("Can't move an element from the trash to the trash: ", elementPath);
+ return;
+ }
+ var key = elementPath[elementPath.length - 1];
+ var elName = isPathIn(elementPath, ['hrefArray']) ? getTitle(element) : key;
+ var parentPath = elementPath.slice();
+ parentPath.pop();
+ pushToTrash(elName, element, parentPath);
+ return true;
+ }
+ // Move to hrefArray
+ if (isPathIn(newParentPath, ['hrefArray'])) {
+ if (isFolder(element)) {
+ log(Messages.fo_moveUnsortedError);
+ return;
+ } else {
+ if (elementPath[0] === newParentPath[0]) { return; }
+ var fileRoot = newParentPath[0];
+ if (files[fileRoot].indexOf(element) === -1) {
+ files[fileRoot].push(element);
+ }
+ return true;
+ }
+ }
+ // Move to root
+ var name;
+ if (isPathIn(elementPath, ['hrefArray'])) {
+ name = getTitle(element);
+ } else if (isInTrashRoot(elementPath)) {
+ // Element from the trash root: elementPath = [TRASH, "{dirName}", 0, 'element']
+ name = elementPath[1];
+ } else {
+ name = elementPath[elementPath.length-1];
+ }
+ var newName = !isPathIn(elementPath, [ROOT]) ? getAvailableName(newParent, name) : name;
+
+ if (typeof(newParent[newName]) !== "undefined") {
+ log(Messages.fo_unavailableName);
+ return;
+ }
+ newParent[newName] = element;
+ return true;
+ };
+ var move = exp.move = function (paths, newPath, cb) {
+ // Copy the elements to their new location
+ var toRemove = [];
+ paths.forEach(function (p) {
+ var parentPath = p.slice();
+ parentPath.pop();
+ if (comparePath(parentPath, newPath)) { return; }
+ copyElement(p, newPath);
+ toRemove.push(p);
+ });
+ exp.delete(toRemove, cb);
+ };
+ var restore = exp.restore = function (path, cb) {
+ if (!isInTrashRoot(path)) { return; }
+ var parentPath = path.slice();
+ parentPath.pop();
+ var oldPath = find(parentPath).path;
+ move([path], oldPath, cb);
+ };
+
+
+ // ADD
+ var add = exp.add = function (href, path, name, cb) {
+ if (!href) { return; }
+ var newPath = path, parentEl;
+ if (path && !Array.isArray(path)) {
+ newPath = decodeURIComponent(path).split(',');
+ }
+ // Add to href array
+ if (path && isPathIn(newPath, ['hrefArray'])) {
+ parentEl = find(newPath);
+ parentEl.push(href);
+ return;
+ }
+ // Add to root
+ if (path && isPathIn(newPath, [ROOT]) && name) {
+ parentEl = find(newPath);
+ if (parentEl) {
+ var newName = getAvailableName(parentEl, name);
+ parentEl[newName] = href;
+ return;
+ }
+ }
+ // No path: push to unsorted
+ var filesList = getFiles([ROOT, TRASH, 'hrefArray']);
+ if (filesList.indexOf(href) === -1) { files[UNSORTED].push(href); }
+
+ if (typeof cb === "function") { cb(); }
+ };
+ var addFile = exp.addFile = function (filePath, name, type, cb) {
+ var parentEl = findElement(files, filePath);
+ var fileName = getAvailableName(parentEl, name || NEW_FILE_NAME);
+ var href = '/' + type + '/#' + Cryptpad.createRandomHash();
+ parentEl[fileName] = href;
+
+ pushFileData({
+ href: href,
+ title: fileName,
+ atime: +new Date(),
+ ctime: +new Date()
+ });
+
+ var newPath = filePath.slice();
+ newPath.push(fileName);
+ cb({
+ newPath: newPath
+ });
+ };
+ var addFolder = exp.addFolder = function (folderPath, name, cb) {
+ var parentEl = find(folderPath);
+ var folderName = getAvailableName(parentEl, name || NEW_FOLDER_NAME);
+ parentEl[folderName] = {};
+ var newPath = folderPath.slice();
+ newPath.push(folderName);
+ cb({
+ newPath: newPath
+ });
+ };
+
+ // FORGET (move with href not path)
+ var forget = exp.forget = function (href) {
+ var paths = findFile(href);
+ move(paths, [TRASH]);
+ };
+
+ // DELETE
+ // Permanently delete multiple files at once using a list of paths
+ // NOTE: We have to be careful when removing elements from arrays (trash root, unsorted or template)
+ var removePadAttribute = function (f) {
+ Object.keys(files).forEach(function (key) {
+ var hash = f.indexOf('#') !== -1 ? f.slice(f.indexOf('#') + 1) : null;
+ if (hash && key.indexOf(hash) === 0) {
+ debug("Deleting pad attribute in the realtime object");
+ files[key] = undefined;
+ delete files[key];
+ }
+ });
+ };
+ var checkDeletedFiles = function () {
+ // Nothing in FILES_DATA for workgroups
+ if (workgroup) { return; }
+
+ var filesList = getFiles([ROOT, 'hrefArray', TRASH]);
+ var toRemove = [];
+ files[FILES_DATA].forEach(function (arr) {
+ var f = arr.href;
+ if (filesList.indexOf(f) === -1) {
+ toRemove.push(arr);
+ }
+ });
+ toRemove.forEach(function (f) {
+ var idx = files[FILES_DATA].indexOf(f);
+ if (idx !== -1) {
+ debug("Removing", f, "from filesData");
+ spliceFileData(idx);
+ removePadAttribute(f.href);
+ }
+ });
+ };
+ var deleteHrefs = function (hrefs) {
+ hrefs.forEach(function (obj) {
+ var idx = files[obj.root].indexOf(obj.href);
+ files[obj.root].splice(idx, 1);
+ });
+ };
+ var deleteMultipleTrashRoot = function (roots) {
+ roots.forEach(function (obj) {
+ var idx = files[TRASH][obj.name].indexOf(obj.el);
+ files[TRASH][obj.name].splice(idx, 1);
+ });
+ };
+ var deleteMultiplePermanently = function (paths, nocheck) {
+ var hrefPaths = paths.filter(function(x) { return isPathIn(x, ['hrefArray']); });
+ var rootPaths = paths.filter(function(x) { return isPathIn(x, [ROOT]); });
+ var trashPaths = paths.filter(function(x) { return isPathIn(x, [TRASH]); });
+
+ var hrefs = [];
+ hrefPaths.forEach(function (path) {
+ var href = find(path);
+ hrefs.push({
+ root: path[0],
+ href: href
+ });
+ });
+ deleteHrefs(hrefs);
+
+ rootPaths.forEach(function (path) {
+ var parentPath = path.slice();
+ var key = parentPath.pop();
+ var parentEl = find(parentPath);
+ parentEl[key] = undefined;
+ delete parentEl[key];
+ });
+
+ var trashRoot = [];
+ trashPaths.forEach(function (path) {
+ var parentPath = path.slice();
+ var key = parentPath.pop();
+ var parentEl = find(parentPath);
+ // Trash root: we have array here, we can't just splice with the path otherwise we might break the path
+ // of another element in the loop
+ if (path.length === 4) {
+ trashRoot.push({
+ name: path[1],
+ el: parentEl
+ });
+ return;
+ }
+ // Trash but not root: it's just a tree so remove the key
+ parentEl[key] = undefined;
+ delete parentEl[key];
+ });
+ deleteMultipleTrashRoot(trashRoot);
+
+ // In some cases, we want to remove pads from a location without removing them from
+ // FILES_DATA (replaceHref)
+ if (!nocheck) { checkDeletedFiles(); }
+ };
+ var deletePath = exp.delete = function (paths, cb, nocheck) {
+ deleteMultiplePermanently(paths, nocheck);
+ if (typeof cb === "function") { cb(); }
+ };
+ var emptyTrash = exp.emptyTrash = function (cb) {
+ files[TRASH] = {};
+ checkDeletedFiles();
+ if(cb) { cb(); }
+ };
+
+ // RENAME
+ var rename = exp.rename = function (path, newName, cb) {
+ if (path.length <= 1) {
+ logError('Renaming `root` is forbidden');
+ return;
+ }
+ if (!newName || newName.trim() === "") { return; }
+ // Copy the element path and remove the last value to have the parent path and the old name
+ var element = find(path);
+ var parentPath = path.slice();
+ var oldName = parentPath.pop();
+ if (oldName === newName) {
+ return;
+ }
+ var parentEl = find(parentPath);
+ if (typeof(parentEl[newName]) !== "undefined") {
+ log(Messages.fo_existingNameError);
+ return;
+ }
+ parentEl[newName] = element;
+ parentEl[oldName] = undefined;
+ delete parentEl[oldName];
+ cb();
+ };
+
+ // REPLACE
+ var replaceFile = function (path, o, n) {
+ var root = find(path);
+
+ if (isFile(root)) { return; }
+ for (var e in root) {
+ if (isFile(root[e])) {
+ if (compareFiles(o, root[e])) {
+ root[e] = n;
+ }
+ } else {
+ var nPath = path.slice();
+ nPath.push(e);
+ replaceFile(nPath, o, n);
+ }
+ }
+ };
+ // Replace a href by a stronger one everywhere in the drive (except FILES_DATA)
+ var replaceHref = exp.replace = function (o, n) {
+ if (!isFile(o) || !isFile(n)) { return; }
+ var paths = findFile(o);
+
+ // Remove all the occurences in the trash
+ // Replace all the occurences not in the trash
+ // If all the occurences are in the trash or no occurence, add the pad to unsorted
+ var allInTrash = true;
+ paths.forEach(function (p) {
+ if (p[0] === TRASH) {
+ exp.delete(p, null, true); // 3rd parameter means skip "checkDeletedFiles"
+ return;
+ } else {
+ allInTrash = false;
+ var parentPath = p.slice();
+ var key = parentPath.pop();
+ var parentEl = find(parentPath);
+ parentEl[key] = n;
+ }
+ });
+ if (allInTrash) {
+ add(n);
+ }
+ };
+
+ /**
+ * INTEGRITY CHECK
+ */
+
+ var fixFiles = exp.fixFiles = function () {
+ // Explore the tree and check that everything is correct:
+ // * 'root', 'trash', 'unsorted' and 'filesData' exist and are objects
+ // * ROOT: Folders are objects, files are href
+ // * TRASH: Trash root contains only arrays, each element of the array is an object {element:.., path:..}
+ // * FILES_DATA: - Data (title, cdate, adte) are stored in filesData. filesData contains only href keys linking to object with title, cdate, adate.
+ // - Dates (adate, cdate) can be parsed/formatted
+ // - All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted'
+ // * UNSORTED: Contains only files (href), and does not contains files that are in ROOT
+ debug("Cleaning file system...");
+
+ var before = JSON.stringify(files);
+
+ var fixRoot = function (elem) {
+ if (typeof(files[ROOT]) !== "object") { debug("ROOT was not an object"); files[ROOT] = {}; }
+ var element = elem || files[ROOT];
+ for (var el in element) {
+ if (!isFile(element[el]) && !isFolder(element[el])) {
+ debug("An element in ROOT was not a folder nor a file. ", element[el]);
+ element[el] = undefined;
+ delete element[el];
+ } else if (isFolder(element[el])) {
+ fixRoot(element[el]);
+ }
+ }
+ };
+ var fixTrashRoot = function () {
+ if (typeof(files[TRASH]) !== "object") { debug("TRASH was not an object"); files[TRASH] = {}; }
+ var tr = files[TRASH];
+ var toClean;
+ var addToClean = function (obj, idx) {
+ if (typeof(obj) !== "object") { toClean.push(idx); return; }
+ if (!isFile(obj.element) && !isFolder(obj.element)) { toClean.push(idx); return; }
+ if (!$.isArray(obj.path)) { toClean.push(idx); return; }
+ };
+ for (var el in tr) {
+ if (!$.isArray(tr[el])) {
+ debug("An element in TRASH root is not an array. ", tr[el]);
+ tr[el] = undefined;
+ delete tr[el];
+ } else {
+ toClean = [];
+ tr[el].forEach(addToClean);
+ for (var i = toClean.length-1; i>=0; i--) {
+ tr[el].splice(toClean[i], 1);
+ }
+ }
+ }
+ };
+ var fixUnsorted = function () {
+ if (!Array.isArray(files[UNSORTED])) { debug("UNSORTED was not an array"); files[UNSORTED] = []; }
+ files[UNSORTED] = Cryptpad.deduplicateString(files[UNSORTED].slice());
+ var us = files[UNSORTED];
+ var rootFiles = getFiles([ROOT, TEMPLATE]).slice();
+ var toClean = [];
+ us.forEach(function (el, idx) {
+ if (!isFile(el) || rootFiles.indexOf(el) !== -1) {
+ toClean.push(idx);
+ }
+ });
+ toClean.forEach(function (idx) {
+ us.splice(idx, 1);
+ });
+ };
+ var fixTemplate = function () {
+ if (!Array.isArray(files[TEMPLATE])) { debug("TEMPLATE was not an array"); files[TEMPLATE] = []; }
+ files[TEMPLATE] = Cryptpad.deduplicateString(files[TEMPLATE].slice());
+ var us = files[TEMPLATE];
+ var rootFiles = getFiles([ROOT, UNSORTED]).slice();
+ var toClean = [];
+ us.forEach(function (el, idx) {
+ if (!isFile(el) || rootFiles.indexOf(el) !== -1) {
+ toClean.push(idx);
+ }
+ });
+ toClean.forEach(function (idx) {
+ us.splice(idx, 1);
+ });
+ };
+ var fixFilesData = function () {
+ if (!$.isArray(files[FILES_DATA])) { debug("FILES_DATA was not an array"); files[FILES_DATA] = []; }
+ var fd = files[FILES_DATA];
+ var rootFiles = getFiles([ROOT, TRASH, 'hrefArray']);
+ var toClean = [];
+ fd.forEach(function (el, idx) {
+ if (!el || typeof(el) !== "object") {
+ debug("An element in filesData was not an object.", el);
+ toClean.push(el);
+ return;
+ }
+ if (rootFiles.indexOf(el.href) === -1) {
+ debug("An element in filesData was not in ROOT, UNSORTED or TRASH.", el);
+ files[UNSORTED].push(el.href);
+ return;
+ }
+ });
+ toClean.forEach(function (el) {
+ var idx = fd.indexOf(el);
+ if (idx !== -1) {
+ spliceFileData(idx);
+ }
+ });
+ };
+
+ fixRoot();
+ fixTrashRoot();
+ if (!workgroup) {
+ fixUnsorted();
+ fixTemplate();
+ fixFilesData();
+ }
+
+ if (JSON.stringify(files) !== before) {
+ debug("Your file system was corrupted. It has been cleaned so that the pads you visit can be stored safely");
+ return;
+ }
+ debug("File system was clean");
+ };
+
+ return exp;
+ };
+
+ return module;
+});
diff --git a/www/drive/main.js b/www/drive/main.js
index 474ea4b06..f6e807031 100644
--- a/www/drive/main.js
+++ b/www/drive/main.js
@@ -1,20 +1,19 @@
-require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([
+ 'jquery',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.amd.js',
'json.sortify',
'/common/cryptpad-common.js',
- '/common/fileObject.js',
+ '/common/userObject.js',
'/common/toolbar.js',
'/customize/application_config.js',
'/common/cryptget.js',
'/common/mergeDrive.js'
-], function (Listmap, Crypto, TextPatcher, JSONSortify, Cryptpad, FO, Toolbar, AppConfig, Get, Merge) {
+], function ($, Listmap, Crypto, TextPatcher, JSONSortify, Cryptpad, FO, Toolbar, AppConfig, Get, Merge) {
var module = window.MODULE = {};
var Messages = Cryptpad.Messages;
- var $ = window.jQuery;
var saveAs = window.saveAs;
// Use `$(function () {});` to make sure the html is loaded before doing anything else
@@ -264,9 +263,7 @@ define([
var removeInput = function (cancel) {
if (!cancel && $iframe.find('.element-row > input').length === 1) {
var $input = $iframe.find('.element-row > input');
- filesOp.renameElement($input.data('path'), $input.val(), function () {
- APP.refresh();
- });
+ filesOp.rename($input.data('path'), $input.val(), APP.refresh);
}
$iframe.find('.element-row > input').remove();
$iframe.find('.element-row > span:hidden').removeAttr('style');
@@ -332,9 +329,7 @@ define([
$input.on('keyup', function (e) {
if (e.which === 13) {
removeInput(true);
- filesOp.renameElement(path, $input.val(), function () {
- refresh();
- });
+ filesOp.rename(path, $input.val(), refresh);
return;
}
if (e.which === 27) {
@@ -371,6 +366,7 @@ define([
var filterContextMenu = function ($menu, paths) {
//var path = $element.data('path');
+ if (!paths || paths.length === 0) { console.error('no paths'); }
var hide = [];
var hasFolder = false;
@@ -652,16 +648,12 @@ define([
var getElementName = function (path) {
// Trash root
- if (filesOp.isInTrashRoot(path)) {
- return path[0];
- }
+ if (filesOp.isInTrashRoot(path)) { return path[0]; }
// Root or trash
- if (filesOp.isPathInRoot(path) || filesOp.isPathInTrash(path)) {
- return path[path.length - 1];
- }
+ if (filesOp.isPathIn(path, [ROOT, TRASH])) { return path[path.length - 1]; }
// Unsorted or template
- if (filesOp.isPathInUnsorted(path) || filesOp.isPathInTemplate(path)) {
- var file = filesOp.findElement(files, path);
+ if (filesOp.isPathIn(path, ['hrefArray'])) {
+ var file = filesOp.find(path);
if (filesOp.isFile(file) && filesOp.getTitle(file)) {
return filesOp.getTitle(file);
}
@@ -674,10 +666,10 @@ define([
var moveElements = function (paths, newPath, force, cb) {
if (!APP.editable) { return; }
var andThen = function () {
- filesOp.moveElements(paths, newPath, cb);
+ filesOp.move(paths, newPath, cb);
};
// Cancel drag&drop from TRASH to TRASH
- if (filesOp.comparePath(newPath, [TRASH]) && paths.length >= 1 && paths[0][0] === TRASH) {
+ if (filesOp.isPathIn(newPath, [TRASH]) && paths.length && paths[0][0] === TRASH) {
return;
}
// "force" is currently unused but may be configurable by user
@@ -688,7 +680,7 @@ define([
var msg = Messages._getKey('fm_removeSeveralDialog', [paths.length]);
if (paths.length === 1) {
var path = paths[0];
- var name = path[0] === UNSORTED ? filesOp.getTitle(filesOp.findElement(files, path)) : path[path.length - 1];
+ var name = path[0] === UNSORTED ? filesOp.getTitle(filesOp.find(path)) : path[path.length - 1];
msg = Messages._getKey('fm_removeDialog', [name]);
}
Cryptpad.confirm(msg, function (res) {
@@ -707,7 +699,7 @@ define([
$selected.each(function (idx, elmt) {
var ePath = $(elmt).data('path');
if (ePath) {
- var val = filesOp.findElement(files, ePath);
+ var val = filesOp.find(ePath);
if (!val) { return; } // Error? A ".selected" element in not in the object
paths.push({
path: ePath,
@@ -721,7 +713,7 @@ define([
} else {
removeSelected();
$element.addClass('selected');
- var val = filesOp.findElement(files, path);
+ var val = filesOp.find(path);
if (!val) { return; } // The element in not in the object
paths = [{
path: path,
@@ -749,7 +741,7 @@ define([
var movedPaths = [];
var importedElements = [];
oldPaths.forEach(function (p) {
- var el = filesOp.findElement(files, p.path);
+ var el = filesOp.find(p.path);
if (el && (stringify(el) === stringify(p.value.el) || !p.value || !p.value.el)) {
movedPaths.push(p.path);
} else {
@@ -764,7 +756,8 @@ define([
moveElements(movedPaths, newPath, null, refresh);
}
if (importedElements && importedElements.length) {
- filesOp.importElements(importedElements, newPath, refresh);
+ // TODO workgroup
+ //filesOp.importElements(importedElements, newPath, refresh);
}
};
@@ -882,7 +875,7 @@ define([
newPath.push(key);
}
- var element = filesOp.findElement(files, newPath);
+ var element = filesOp.find(newPath);
var $icon = !isFolder ? getFileIcon(element) : undefined;
var ro = filesOp.isReadOnlyFile(element);
// ro undefined mens it's an old hash which doesn't support read-only
@@ -967,7 +960,7 @@ define([
// Create the title block with the "parent folder" button
var createTitle = function (path, noStyle) {
if (!path || path.length === 0) { return; }
- var isTrash = filesOp.isPathInTrash(path);
+ var isTrash = filesOp.isPathIn(path, [TRASH]);
var $title = $('', {'class': 'path unselectable'});
if (APP.mobile()) {
return $title;
@@ -1119,17 +1112,17 @@ define([
refresh();
};
$block.find('a.newFolder').click(function () {
- filesOp.createNewFolder(currentPath, null, onCreated);
+ filesOp.addFolder(currentPath, null, onCreated);
});
$block.find('a.newdoc').click(function (e) {
var type = $(this).attr('data-type') || 'pad';
var name = Cryptpad.getDefaultName({type: type});
- filesOp.createNewFile(currentPath, name, type, onCreated);
+ filesOp.addFile(currentPath, name, type, onCreated);
});
} else {
$block.find('a.newdoc').click(function (e) {
var type = $(this).attr('data-type') || 'pad';
- sessionStorage[Cryptpad.newPadPathKey] = filesOp.isPathInTrash(currentPath) ? '' : currentPath;
+ sessionStorage[Cryptpad.newPadPathKey] = filesOp.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
window.open('/' + type + '/');
});
}
@@ -1251,11 +1244,11 @@ define([
};
var allFilesSorted = function () {
- return filesOp.getUnsortedFiles().length === 0;
+ return filesOp.getFiles([UNSORTED]).length === 0;
};
var sortElements = function (folder, path, oldkeys, prop, asc, useHref, useData) {
- var root = filesOp.findElement(files, path);
+ var root = filesOp.find(path);
var test = folder ? filesOp.isFolder : filesOp.isFile;
var keys;
if (!useData) {
@@ -1478,7 +1471,7 @@ define([
var $atime = $(' ', {'class': 'col2'}).text(new Date(r.data.atime).toLocaleString());
var $ctimeName = $(' ', {'class': 'label2'}).text(Messages.fm_creation);
var $ctime = $(' ', {'class': 'col2'}).text(new Date(r.data.ctime).toLocaleString());
- if (filesOp.isPathInHrefArray(path)) {
+ if (filesOp.isPathIn(path, ['hrefArray'])) {
path.pop();
path.push(r.data.title);
}
@@ -1522,14 +1515,14 @@ define([
if (!path || path.length === 0) {
path = [ROOT];
}
- var isInRoot = filesOp.isPathInRoot(path);
+ var isInRoot = filesOp.isPathIn(path, [ROOT]);
var isTrashRoot = filesOp.comparePath(path, [TRASH]);
var isUnsorted = filesOp.comparePath(path, [UNSORTED]);
var isTemplate = filesOp.comparePath(path, [TEMPLATE]);
var isAllFiles = filesOp.comparePath(path, [FILES_DATA]);
var isSearch = path[0] === SEARCH;
- var root = isSearch ? undefined : filesOp.findElement(files, path);
+ var root = isSearch ? undefined : filesOp.find(path);
if (!isSearch && typeof(root) === "undefined") {
log(Messages.fm_unknownFolderError);
debug("Unable to locate the selected directory: ", path);
@@ -1642,11 +1635,11 @@ define([
var $el = $(e);
if ($el.data('path')) {
var path = $el.data('path');
- var element = filesOp.findElement(files, path);
+ var element = filesOp.find(path);
if (!filesOp.isFile(element)) { return; }
var data = filesOp.getFileData(element);
if (!data) { return; }
- if (filesOp.isPathInHrefArray(path)) { $el.find('.name').attr('title', data.title).text(data.title); }
+ if (filesOp.isPathIn(path, ['hrefArray'])) { $el.find('.name').attr('title', data.title).text(data.title); }
$el.find('.title').attr('title', data.title).text(data.title);
$el.find('.atime').attr('title', getDate(data.atime)).text(getDate(data.atime));
$el.find('.ctime').attr('title', getDate(data.ctime)).text(getDate(data.ctime));
@@ -1700,7 +1693,7 @@ define([
};
var createTree = function ($container, path) {
- var root = filesOp.findElement(files, path);
+ var root = filesOp.find(path);
// don't try to display what doesn't exist
if (!root) { return; }
@@ -1930,7 +1923,7 @@ define([
}
else if ($(this).hasClass('open_ro')) {
paths.forEach(function (p) {
- var el = filesOp.findElement(files, p.path);
+ var el = filesOp.find(p.path);
if (filesOp.isFolder(el)) { return; }
var roUrl = getReadOnlyUrl(el);
openFile(roUrl, false);
@@ -1942,11 +1935,11 @@ define([
module.newFolder = info.newPath;
module.displayDirectory(paths[0].path);
};
- filesOp.createNewFolder(paths[0].path, null, onCreated);
+ filesOp.addFolder(paths[0].path, null, onCreated);
}
else if ($(this).hasClass("properties")) {
if (paths.length !== 1) { return; }
- var el = filesOp.findElement(files, paths[0].path);
+ var el = filesOp.find(paths[0].path);
var prop = getProperties(el);
Cryptpad.alert('', undefined, true);
$('.alertify .msg').html(prop);
@@ -1972,8 +1965,8 @@ define([
}
else if ($(this).hasClass('open_ro')) {
paths.forEach(function (p) {
- var el = filesOp.findElement(files, p.path);
- if (filesOp.isPathInFilesData(p.path)) { el = el.href; }
+ var el = filesOp.find(p.path);
+ if (filesOp.isPathIn(p.path, [FILES_DATA])) { el = el.href; }
if (!el || filesOp.isFolder(el)) { return; }
var roUrl = getReadOnlyUrl(el);
openFile(roUrl, false);
@@ -1986,7 +1979,7 @@ define([
}
else if ($(this).hasClass("properties")) {
if (paths.length !== 1) { return; }
- var el = filesOp.findElement(files, paths[0].path);
+ var el = filesOp.find(paths[0].path);
var prop = getProperties(el);
Cryptpad.alert('', undefined, true);
$('.alertify .msg').html(prop);
@@ -2004,12 +1997,12 @@ define([
refresh();
};
if ($(this).hasClass("newfolder")) {
- filesOp.createNewFolder(path, null, onCreated);
+ filesOp.addFolder(path, null, onCreated);
}
else if ($(this).hasClass("newdoc")) {
var type = $(this).data('type') || 'pad';
var name = Cryptpad.getDefaultName({type: type});
- filesOp.createNewFile(path, name, type, onCreated);
+ filesOp.addFile(path, name, type, onCreated);
}
module.hideMenu();
});
@@ -2046,7 +2039,7 @@ define([
if (path.length === 4) { name = path[1]; }
Cryptpad.confirm(Messages._getKey("fm_removePermanentlyDialog", [name]), function(res) {
if (!res) { return; }
- filesOp.removeFromTrash(path, refresh);
+ filesOp.delete([path], refresh);
});
return;
}
@@ -2055,8 +2048,7 @@ define([
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]);
Cryptpad.confirm(msg, function(res) {
if (!res) { return; }
- filesOp.deletePathsPermanently(pathsList);
- refresh();
+ filesOp.delete(pathsList, refresh);
});
}
else if ($(this).hasClass("restore")) {
@@ -2064,13 +2056,12 @@ define([
if (path.length === 4) { name = path[1]; }
Cryptpad.confirm(Messages._getKey("fm_restoreDialog", [name]), function(res) {
if (!res) { return; }
- filesOp.restoreTrash(path, refresh);
+ filesOp.restore(path, refresh);
});
}
else if ($(this).hasClass("properties")) {
- if (paths.length !== 1) { return; }
- if (path.length !== 4) { return; }
- var element = filesOp.getTrashElementData(path);
+ if (paths.length !== 1 || path.length !== 4) { return; }
+ var element = filesOp.find(path.slice(0,3)); // element containing the oldpath
var sPath = stringifyPath(element.path);
Cryptpad.alert('' + Messages.fm_originalPath + " : " + sPath, undefined, true);
}
@@ -2094,20 +2085,17 @@ define([
$appContainer.on('keydown', function (e) {
// "Del"
if (e.which === 46) {
- if (filesOp.isPathInFilesData(currentPath)) { return; } // We can't remove elements directly from filesData
+ if (filesOp.isPathIn(currentPath, [FILES_DATA])) { return; } // We can't remove elements directly from filesData
var $selected = $iframe.find('.selected');
if (!$selected.length) { return; }
var paths = [];
- var isTrash = filesOp.isPathInTrash(currentPath);
+ var isTrash = filesOp.isPathIn(currentPath, [TRASH]);
$selected.each(function (idx, elmt) {
if (!$(elmt).data('path')) { return; }
paths.push($(elmt).data('path'));
});
// If we are in the trash or anon pad or if we are holding the "shift" key, delete permanently,
if (isTrash || e.shiftKey) {
- //var cb = filesOp.removeFromTrash; // We're in the trash
- //if (!isTrash) { cb = filesOp.deletePathPermanently; } // We're in root
-
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]);
if (paths.length === 1) {
msg = Messages.fm_removePermanentlyDialog;
@@ -2116,8 +2104,7 @@ define([
Cryptpad.confirm(msg, function(res) {
$(ifrw).focus();
if (!res) { return; }
- filesOp.deletePathsPermanently(paths);
- refresh();
+ filesOp.delete(paths, refresh);
});
return;
}
@@ -2143,10 +2130,8 @@ define([
if (path[0] !== 'drive') { return false; }
path = path.slice(1);
var cPath = currentPath.slice();
- if ((filesOp.isPathInUnsorted(cPath) && filesOp.isPathInUnsorted(path)) ||
- (filesOp.isPathInTemplate(cPath) && filesOp.isPathInTemplate(path)) ||
- (path.length >= cPath.length && filesOp.isSubpath(path, cPath)) ||
- (filesOp.isPathInTrash(cPath) && filesOp.isPathInTrash(path))) {
+ if ((filesOp.isPathIn(cPath, ['hrefArray', TRASH]) && cPath[0] === path[0]) ||
+ (path.length >= cPath.length && filesOp.isSubpath(path, cPath))) {
// Reload after a few ms to make sure all the change events have been received
onRefresh.refresh();
} else if (path.length && path[0] === FILES_DATA) {
@@ -2159,10 +2144,8 @@ define([
if (path[0] !== 'drive') { return false; }
path = path.slice(1);
var cPath = currentPath.slice();
- if ((filesOp.isPathInUnsorted(cPath) && filesOp.isPathInUnsorted(path)) ||
- (filesOp.isPathInTemplate(cPath) && filesOp.isPathInTemplate(path)) ||
- (path.length >= cPath.length && filesOp.isSubpath(path, cPath)) ||
- (filesOp.isPathInTrash(cPath) && filesOp.isPathInTrash(path))) {
+ if ((filesOp.isPathIn(cPath, ['hrefArray', TRASH]) && cPath[0] === path[0]) ||
+ (path.length >= cPath.length && filesOp.isSubpath(path, cPath))) {
// Reload after a few to make sure all the change events have been received
onRefresh.to = window.setTimeout(refresh, 500);
}
@@ -2243,6 +2226,7 @@ define([
// don't initialize until the store is ready.
Cryptpad.ready(function () {
+ Cryptpad.reportAppUsage();
APP.$bar = $iframe.find('#toolbar');
var storeObj = Cryptpad.getStore().getProxy && Cryptpad.getStore().getProxy().proxy ? Cryptpad.getStore().getProxy() : undefined;
diff --git a/www/examples/board/board.js b/www/examples/board/board.js
index 08f0b0dcd..26d695173 100644
--- a/www/examples/board/board.js
+++ b/www/examples/board/board.js
@@ -1,7 +1,6 @@
define([
- '/bower_components/jquery/dist/jquery.min.js',
-],function () {
- var $ = window.jQuery;
+ 'jquery'
+],function ($) {
var Board = {};
var proxy;
diff --git a/www/examples/board/main.js b/www/examples/board/main.js
index ca690bdc4..c5c664c49 100644
--- a/www/examples/board/main.js
+++ b/www/examples/board/main.js
@@ -1,5 +1,6 @@
define([
- '/api/config?cb=' + Math.random().toString(16).substring(2),
+ 'jquery',
+ '/api/config',
'/customize/messages.js',
'board.js',
'/bower_components/textpatcher/TextPatcher.js',
@@ -8,10 +9,9 @@ define([
'/common/cryptpad-common.js',
'/common/visible.js',
'/common/notify.js',
- '/bower_components/file-saver/FileSaver.min.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Config, Messages, Board, TextPatcher, Listmap, Crypto, Cryptpad, Visible, Notify) {
- var $ = window.jQuery;
+ '/bower_components/file-saver/FileSaver.min.js'
+], function ($, Config, Messages, Board, TextPatcher, Listmap, Crypto, Cryptpad, Visible, Notify) {
+
var saveAs = window.saveAs;
Cryptpad.styleAlerts();
diff --git a/www/examples/form/main.js b/www/examples/form/main.js
index bdb10065a..04a65e2c4 100644
--- a/www/examples/form/main.js
+++ b/www/examples/form/main.js
@@ -1,16 +1,14 @@
-require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([
- '/api/config?cb=' + Math.random().toString(16).substring(2),
+ 'jquery',
+ '/api/config',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.amd.js',
'json.sortify',
'ula.js',
'/bower_components/chainpad-json-validator/json-ot.js',
- '/common/cryptpad-common.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Config, Realtime, Crypto, TextPatcher, Sortify, Formula, JsonOT, Cryptpad) {
- var $ = window.jQuery;
+ '/common/cryptpad-common.js'
+], function ($, Config, Realtime, Crypto, TextPatcher, Sortify, Formula, JsonOT, Cryptpad) {
var secret = Cryptpad.getSecrets();
diff --git a/www/examples/hack/main.js b/www/examples/hack/main.js
index 82a2b0f6e..c59460a60 100644
--- a/www/examples/hack/main.js
+++ b/www/examples/hack/main.js
@@ -1,12 +1,11 @@
define([
- '/api/config?cb=' + Math.random().toString(16).substring(2),
+ 'jquery',
+ '/api/config',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.amd.js',
- '/common/cryptpad-common.js',
- '/bower_components/jquery/dist/jquery.min.js'
-], function (Config, Realtime, Crypto, TextPatcher, Cryptpad) {
- var $ = window.jQuery;
+ '/common/cryptpad-common.js'
+], function ($, Config, Realtime, Crypto, TextPatcher, Cryptpad) {
var secret = Cryptpad.getSecrets();
diff --git a/www/examples/json/main.js b/www/examples/json/main.js
index 8b018ebd7..90654afa6 100644
--- a/www/examples/json/main.js
+++ b/www/examples/json/main.js
@@ -1,11 +1,10 @@
define([
- '/api/config?cb=' + Math.random().toString(16).substring(2),
+ 'jquery',
+ '/api/config',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js',
- '/common/cryptpad-common.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Config, RtListMap, Crypto, Common) {
- var $ = window.jQuery;
+ '/common/cryptpad-common.js'
+], function ($, Config, RtListMap, Crypto, Common) {
var secret = Common.getSecrets();
diff --git a/www/examples/pin/main.js b/www/examples/pin/main.js
index 1853b9e4c..ad6905d7d 100644
--- a/www/examples/pin/main.js
+++ b/www/examples/pin/main.js
@@ -1,10 +1,8 @@
-require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([
+ 'jquery',
'/common/cryptpad-common.js',
- '/common/pinpad.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Cryptpad, Pinpad) {
- var $ = window.jQuery;
+ '/common/pinpad.js'
+], function ($, Cryptpad, Pinpad) {
var APP = window.APP = {
Cryptpad: Cryptpad,
};
diff --git a/www/examples/read/main.js b/www/examples/read/main.js
index 9bce1ebe7..5692f9b6b 100644
--- a/www/examples/read/main.js
+++ b/www/examples/read/main.js
@@ -1,8 +1,7 @@
define([
- '/common/cryptget.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Crypt) {
- var $ = window.jQuery;
+ 'jquery',
+ '/common/cryptget.js'
+], function ($, Crypt) {
var $target = $('#target');
var $dest = $('#dest');
diff --git a/www/examples/render/index.html b/www/examples/render/index.html
index 24e7e41b5..56112aab3 100644
--- a/www/examples/render/index.html
+++ b/www/examples/render/index.html
@@ -3,7 +3,7 @@
-
+
diff --git a/www/poll/main.js b/www/poll/main.js
index 7225bee7f..5aa3e4ee2 100644
--- a/www/poll/main.js
+++ b/www/poll/main.js
@@ -1,4 +1,5 @@
define([
+ 'jquery',
'/bower_components/textpatcher/TextPatcher.js',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad-crypto/crypto.js',
@@ -9,10 +10,8 @@ define([
'/common/toolbar.js',
'/common/visible.js',
'/common/notify.js',
- '/bower_components/file-saver/FileSaver.min.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (TextPatcher, Listmap, Crypto, Cryptpad, Cryptget, Hyperjson, Renderer, Toolbar, Visible, Notify) {
- var $ = window.jQuery;
+ '/bower_components/file-saver/FileSaver.min.js'
+], function ($, TextPatcher, Listmap, Crypto, Cryptpad, Cryptget, Hyperjson, Renderer, Toolbar, Visible, Notify) {
var Messages = Cryptpad.Messages;
@@ -25,6 +24,7 @@ define([
var secret = Cryptpad.getSecrets();
var readOnly = secret.keys && !secret.keys.editKeyStr;
+ // DEPRECATE_F
if (!secret.keys) {
secret.keys = secret.key;
}
@@ -210,7 +210,7 @@ define([
};
/* Any time the realtime object changes, call this function */
- var change = function (o, n, path, throttle) {
+ var change = function (o, n, path, throttle, cb) {
if (path && !Cryptpad.isArray(path)) {
return;
}
@@ -259,8 +259,14 @@ define([
var displayedObj2 = mergeUncommitted(APP.proxy, APP.uncommitted);
var f = getFocus();
Render.updateTable(table, displayedObj2, conf);
+ APP.proxy.table.rowsOrder.forEach(function (rowId) {
+ $('input[data-rt-id="' + rowId +'"]').val(APP.proxy.table.rows[rowId] || '');
+ });
updateDisplayedTable();
setFocus(f);
+ if (typeof(cb) === "function") {
+ cb();
+ }
};
if (throttle) {
@@ -280,7 +286,7 @@ define([
};
/* Called whenever an event is fired on an input element */
- var handleInput = function (input) {
+ var handleInput = function (input, isKeyup) {
var type = input.type.toLowerCase();
var id = getRealtimeId(input);
@@ -331,7 +337,9 @@ define([
});
} else if (isEdit) {
unlockRow(id, function () {
- change();
+ change(null, null, null, null, function() {
+ $('input[data-rt-id="' + id + '"]').focus();
+ });
});
}
} else if (type === 'col') {
@@ -344,7 +352,9 @@ define([
});
} else if (isEdit) {
unlockColumn(id, function () {
- change();
+ change(null, null, null, null, function() {
+ $('input[data-rt-id="' + id + '"]').focus();
+ });
});
}
} else if (type === 'cell') {
@@ -354,8 +364,8 @@ define([
}
};
- var hideInputs = function (e) {
- if ($(e.target).is('[type="text"]')) {
+ var hideInputs = function (e, isKeyup) {
+ if (!isKeyup && $(e.target).is('[type="text"]')) {
return;
}
$('.lock[data-rt-id!="' + APP.userid + '"]').html(lockHTML);
@@ -388,6 +398,10 @@ define([
switch (nodeName) {
case 'INPUT':
+ if (isKeyup && (e.keyCode === 13 || e.keyCode === 27)) {
+ hideInputs(e, isKeyup);
+ return;
+ }
handleInput(target);
break;
case 'SPAN':
@@ -546,20 +560,18 @@ define([
var $table = APP.$table = $(Render.asHTML(displayedObj, null, colsOrder, readOnly));
var $createRow = APP.$createRow = $('#create-option').click(function () {
//console.error("BUTTON CLICKED! LOL");
- Render.createRow(proxy, function () {
- change();
- var order = APP.proxy.table.rowsOrder;
-
- var last = order[order.length - 1];
- var $newest = $('[data-rt-id="' + last + '"]');
- $newest.val('');
- window.setTimeout(change);
+ Render.createRow(proxy, function (empty, id) {
+ change(null, null, null, null, function() {
+ $('.edit[data-rt-id="' + id + '"]').click();
+ });
});
});
var $createCol = APP.$createCol = $('#create-user').click(function () {
- Render.createColumn(proxy, function () {
- change();
+ Render.createColumn(proxy, function (empty, id) {
+ change(null, null, null, null, function() {
+ $('.edit[data-rt-id="' + id + '"]').click();
+ });
});
});
@@ -768,19 +780,11 @@ define([
}
Cryptpad.onDisplayNameChanged(setName);
-
- Cryptpad.getPadTitle(function (err, title) {
- if (err) {
- error(err);
- debug("Couldn't get pad title");
- return;
- }
- updateTitle(title || defaultName);
- });
};
// don't initialize until the store is ready.
Cryptpad.ready(function () {
+ Cryptpad.reportAppUsage();
var config = {
websocketURL: Cryptpad.getWebsocketURL(),
channel: secret.channel,
@@ -838,4 +842,3 @@ define([
});
});
-
diff --git a/www/poll/poll.css b/www/poll/poll.css
new file mode 100644
index 000000000..321121b59
--- /dev/null
+++ b/www/poll/poll.css
@@ -0,0 +1,357 @@
+html,
+body {
+ width: 100%;
+ height: 100%;
+ margin: 0px;
+ padding: 0px;
+ border: 0px;
+}
+.cryptpad-toolbar h2 {
+ font: normal normal normal 12px Arial, Helvetica, Tahoma, Verdana, Sans-Serif;
+ color: #000;
+ line-height: auto;
+}
+.cryptpad-toolbar {
+ display: inline-block;
+}
+.realtime {
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+}
+.realtime input[type="text"] {
+ height: 1em;
+ margin: 0px;
+}
+.text-cell input[type="text"] {
+ width: 400px;
+}
+input[type="text"][disabled],
+textarea[disabled] {
+ background-color: transparent;
+ font: white;
+ border: 0px;
+}
+table#table {
+ margin: 0px;
+}
+#tableContainer {
+ position: relative;
+ padding: 29px;
+ padding-right: 79px;
+}
+#tableContainer button {
+ height: 2rem;
+ display: none;
+}
+#publish {
+ display: none;
+}
+#publish,
+#admin {
+ margin-top: 15px;
+ margin-bottom: 15px;
+}
+#create-user {
+ position: absolute;
+ display: inline-block;
+ /*left: 0px;*/
+ top: 55px;
+ width: 50px;
+ overflow: hidden;
+}
+#create-option {
+ width: 50px;
+}
+#tableScroll {
+ overflow-y: hidden;
+ overflow-x: auto;
+ margin-left: calc(30% - 50px + 29px);
+ max-width: 70%;
+ width: auto;
+ display: inline-block;
+}
+#description {
+ padding: 15px;
+ margin: auto;
+ min-width: 80%;
+ width: 80%;
+ min-height: 5em;
+ font-size: 20px;
+ font-weight: bold;
+}
+#description[disabled] {
+ resize: none;
+ color: #000;
+ border: 1px solid #444;
+}
+#commit {
+ width: 100%;
+}
+#howItWorks {
+ width: 80%;
+ margin: auto;
+}
+div.upper {
+ width: 80%;
+ margin: auto;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ margin: 20px;
+}
+tbody {
+ border: 1px solid #555;
+}
+tbody tr {
+ text-align: center;
+}
+tbody tr:first-of-type th {
+ font-size: 20px;
+ border-top: 0px;
+ font-weight: bold;
+ padding: 10px;
+ text-decoration: underline;
+}
+tbody tr:first-of-type th.table-refresh {
+ color: #46E981;
+ text-decoration: none;
+ cursor: pointer;
+}
+tbody tr:nth-child(odd) {
+ background-color: #ffffff;
+}
+tbody tr th:first-of-type {
+ border-left: 0px;
+}
+tbody tr th {
+ box-sizing: border-box;
+ border: 1px solid #555;
+}
+tbody tr th,
+tbody tr td {
+ color: #555;
+}
+tbody tr th.remove,
+tbody tr td.remove {
+ cursor: pointer;
+}
+tbody tr th:last-child {
+ border-right: 0px;
+}
+tbody td {
+ border-right: 1px solid #555;
+ padding: 12px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+}
+tbody td:last-child {
+ border-right: none;
+}
+form.realtime,
+div.realtime {
+ padding: 0px;
+ margin: 0px;
+}
+form.realtime > textarea,
+div.realtime > textarea {
+ width: 50%;
+ height: 15vh;
+}
+form.realtime table,
+div.realtime table {
+ border-collapse: collapse;
+ width: calc(100% - 1px);
+}
+form.realtime table tr td:first-child,
+div.realtime table tr td:first-child {
+ position: absolute;
+ left: 29px;
+ top: auto;
+ width: calc(30% - 50px);
+}
+form.realtime table tr td,
+div.realtime table tr td {
+ padding: 0px;
+ margin: 0px;
+}
+form.realtime table tr td div.text-cell,
+div.realtime table tr td div.text-cell {
+ padding: 0px;
+ margin: 0px;
+ height: 100%;
+}
+form.realtime table tr td div.text-cell input,
+div.realtime table tr td div.text-cell input {
+ width: 80%;
+ width: 90%;
+ height: 100%;
+ border: 0px;
+}
+form.realtime table tr td div.text-cell input[disabled],
+div.realtime table tr td div.text-cell input[disabled] {
+ background-color: transparent;
+ color: #000;
+ font-weight: bold;
+}
+form.realtime table tr td.checkbox-cell,
+div.realtime table tr td.checkbox-cell {
+ margin: 0px;
+ padding: 0px;
+ height: 100%;
+ min-width: 150px;
+}
+form.realtime table tr td.checkbox-cell div.checkbox-contain,
+div.realtime table tr td.checkbox-cell div.checkbox-contain {
+ display: inline-block;
+ height: 100%;
+ width: 100%;
+ position: relative;
+}
+form.realtime table tr td.checkbox-cell div.checkbox-contain label,
+div.realtime table tr td.checkbox-cell div.checkbox-contain label {
+ background-color: transparent;
+ display: block;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ height: 100%;
+ width: 100%;
+}
+form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable),
+div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) {
+ display: none;
+}
+form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover,
+div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover {
+ font-weight: bold;
+ background-color: #FA5858;
+ color: #000;
+ display: block;
+}
+form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover:after,
+div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover:after {
+ height: 100%;
+}
+form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover:after,
+div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover:after {
+ content: "✖";
+}
+form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.yes,
+div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.yes {
+ background-color: #46E981;
+}
+form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.yes:after,
+div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.yes:after {
+ content: "✔";
+}
+form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.uncommitted,
+div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.uncommitted {
+ background: #ddd;
+}
+form.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.mine,
+div.realtime table tr td.checkbox-cell div.checkbox-contain input[type="checkbox"]:not(.editable) ~ .cover.mine {
+ display: none;
+}
+form.realtime table input[type="text"],
+div.realtime table input[type="text"] {
+ height: auto;
+ border: 1px solid #fff;
+ width: 80%;
+}
+form.realtime table thead td,
+div.realtime table thead td {
+ padding: 0px 5px;
+ background: #aaa;
+ border-radius: 20px 20px 0 0;
+ text-align: center;
+}
+form.realtime table thead td input[type="text"],
+div.realtime table thead td input[type="text"] {
+ width: 100%;
+ box-sizing: border-box;
+}
+form.realtime table thead td input[type="text"][disabled],
+div.realtime table thead td input[type="text"][disabled] {
+ color: #000;
+ padding: 1px 5px;
+ border: none;
+}
+form.realtime table tbody .text-cell,
+div.realtime table tbody .text-cell {
+ background: #aaa;
+}
+form.realtime table tbody .text-cell input[type="text"],
+div.realtime table tbody .text-cell input[type="text"] {
+ width: calc(100% - 50px);
+}
+form.realtime table tbody .text-cell .edit,
+div.realtime table tbody .text-cell .edit {
+ float: right;
+ margin: 0 10px 0 0;
+}
+form.realtime table tbody .text-cell .remove,
+div.realtime table tbody .text-cell .remove {
+ float: left;
+ margin: 0 0 0 10px;
+}
+form.realtime table tbody td label,
+div.realtime table tbody td label {
+ border: 0.5px solid #555;
+}
+form.realtime table .edit,
+div.realtime table .edit {
+ color: #000;
+ cursor: pointer;
+ float: left;
+ margin-left: 10px;
+}
+form.realtime table .remove,
+div.realtime table .remove {
+ float: right;
+ margin-right: 10px;
+}
+form.realtime table thead tr th input[type="text"][disabled],
+div.realtime table thead tr th input[type="text"][disabled] {
+ background-color: transparent;
+ color: #555;
+ font-weight: bold;
+}
+form.realtime table thead tr th .remove,
+div.realtime table thead tr th .remove {
+ cursor: pointer;
+ font-size: 20px;
+}
+form.realtime table tfoot tr,
+div.realtime table tfoot tr {
+ border: none;
+}
+form.realtime table tfoot tr td,
+div.realtime table tfoot tr td {
+ border: none;
+ text-align: center;
+}
+form.realtime table tfoot tr td .save,
+div.realtime table tfoot tr td .save {
+ padding: 15px;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+}
+form.realtime #adduser,
+div.realtime #adduser,
+form.realtime #addoption,
+div.realtime #addoption {
+ color: #46E981;
+ border: 1px solid #46E981;
+ padding: 15px;
+ cursor: pointer;
+}
+form.realtime #adduser,
+div.realtime #adduser {
+ border-top-left-radius: 5px;
+}
+form.realtime #addoption,
+div.realtime #addoption {
+ border-bottom-left-radius: 5px;
+}
diff --git a/www/poll/poll.less b/www/poll/poll.less
new file mode 100644
index 000000000..7292b0e15
--- /dev/null
+++ b/www/poll/poll.less
@@ -0,0 +1,387 @@
+@import "../../customize.dist/src/less/variables.less";
+@import "../../customize.dist/src/less/mixins.less";
+
+@poll-th-bg: #aaa;
+@poll-td-bg: #aaa;
+@poll-border-color: #555;
+@poll-cover-color: #000;
+@poll-fg: #000;
+
+html, body {
+ width: 100%;
+ height: 100%;
+ margin: 0px;
+ padding: 0px;
+ border: 0px;
+}
+
+.cryptpad-toolbar h2 {
+ font: normal normal normal 12px Arial, Helvetica, Tahoma, Verdana, Sans-Serif;
+ color: #000;
+ line-height: auto;
+}
+.cryptpad-toolbar {
+ display: inline-block;
+}
+.realtime {
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+}
+
+.realtime input[type="text"] {
+ height: 1em;
+ margin: 0px;
+}
+.text-cell input[type="text"] {
+ width: 400px;
+}
+
+input[type="text"][disabled], textarea[disabled] {
+ background-color: transparent;
+ font: white;
+ border: 0px;
+}
+table#table {
+ margin: 0px;
+}
+#tableContainer {
+ position: relative;
+ padding: 29px;
+ padding-right: 79px;
+}
+#tableContainer button {
+ height: 2rem;
+ display: none;
+}
+#publish {
+ display: none;
+}
+#publish, #admin {
+ margin-top: 15px;
+ margin-bottom: 15px;
+}
+#create-user {
+ position: absolute;
+ display: inline-block;
+ /*left: 0px;*/
+ top: 55px;
+ width: 50px;
+ overflow: hidden;
+}
+#create-option {
+ width: 50px;
+}
+#tableScroll {
+ overflow-y: hidden;
+ overflow-x: auto;
+ margin-left: calc(~"30% - 50px + 29px");
+ max-width: 70%;
+ width: auto;
+ display: inline-block;
+}
+#description {
+ padding: 15px;
+ margin: auto;
+
+ min-width: 80%;
+ width: 80%;
+ min-height: 5em;
+ font-size: 20px;
+ font-weight: bold;
+
+}
+#description[disabled] {
+ resize: none;
+ color: #000;
+ border: 1px solid #444;
+}
+
+#commit {
+ width: 100%;
+}
+#howItWorks {
+ width: 80%;
+ margin: auto;
+}
+div.upper {
+ width: 80%;
+ margin: auto;
+}
+
+// from cryptpad.less
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ margin: 20px;
+}
+tbody {
+ border: 1px solid @poll-border-color;
+ tr {
+ text-align: center;
+ &:first-of-type th{
+ font-size: 20px;
+ border-top: 0px;
+ font-weight: bold;
+ padding: 10px;
+ text-decoration: underline;
+ &.table-refresh {
+ color: @cp-green;
+ text-decoration: none;
+ cursor: pointer;
+ }
+
+ }
+ &:nth-child(odd) {
+ background-color: @light-base;
+ }
+ th:first-of-type {
+ border-left: 0px;
+ }
+ th {
+ box-sizing: border-box;
+ border: 1px solid @poll-border-color;
+ }
+ th, td {
+ color: @fore;
+
+ &.remove {
+ cursor: pointer;
+ }
+ }
+ th:last-child {
+ border-right: 0px;
+ }
+ }
+
+ td {
+ border-right: 1px solid @poll-border-color;
+ padding: 12px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ &:last-child {
+ border-right: none;
+ }
+ }
+}
+
+form.realtime, div.realtime {
+ > input {
+ &[type="text"] {
+
+ }
+ }
+ > textarea {
+ width: 50%;
+ height: 15vh;
+ }
+
+ padding: 0px;
+ margin: 0px;
+
+ table {
+ border-collapse: collapse;
+ width: ~"calc(100% - 1px)";
+ tr {
+ td:first-child {
+ position:absolute;
+ left: 29px;
+ top: auto;
+ width: ~"calc(30% - 50px)";
+ }
+ td {
+ padding: 0px;
+ margin: 0px;
+
+ div.text-cell {
+ padding: 0px;
+ margin: 0px;
+ height: 100%;
+
+ input {
+ width: 80%;
+ width: 90%;
+ height: 100%;
+ border: 0px;
+ &[disabled] {
+ background-color: transparent;
+ color: @poll-fg;
+ font-weight: bold;
+ }
+ }
+ }
+
+ &.checkbox-cell {
+ margin: 0px;
+ padding: 0px;
+ height: 100%;
+ min-width: 150px;
+
+ div.checkbox-contain {
+ display: inline-block;
+ height: 100%;
+ width: 100%;
+ position: relative;
+
+ label {
+ background-color: transparent;
+ display: block;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ height: 100%;
+ width: 100%;
+ }
+
+ input {
+ &[type="checkbox"] {
+ &:not(.editable) {
+ display: none;
+
+ ~ .cover {
+ display: block;
+ font-weight: bold;
+
+ background-color: @cp-red;
+ color: @poll-cover-color;
+
+ &:after {
+ height: 100%;
+ }
+
+ &:after { content: "✖"; }
+
+ display: block;
+ &.yes {
+ background-color: @cp-green;
+ &:after { content: "✔"; }
+ }
+
+ &.uncommitted {
+ background: #ddd;
+ }
+
+
+ &.mine {
+ display: none;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ input {
+ &[type="text"] {
+ height: auto;
+ border: 1px solid @base;
+ width: 80%;
+ }
+ }
+ thead {
+ td {
+ padding: 0px 5px;
+ background: @poll-th-bg;
+ border-radius: 20px 20px 0 0;
+ text-align: center;
+ input {
+ &[type="text"] {
+ width: 100%;
+ box-sizing: border-box;
+ &[disabled] {
+ color: @poll-fg;
+ padding: 1px 5px;
+ border: none;
+ }
+ }
+ }
+ }
+ }
+
+ tbody {
+ .text-cell {
+ background: @poll-td-bg;
+ //border-radius: 20px 0 0 20px;
+ input[type="text"] {
+ width: ~"calc(100% - 50px)";
+ }
+ .edit {
+ float:right;
+ margin: 0 10px 0 0;
+ }
+ .remove {
+ float: left;
+ margin: 0 0 0 10px;
+ }
+ }
+ td {
+ label {
+ border: .5px solid @poll-border-color;
+ }
+ }
+ }
+ .edit {
+ color: @poll-cover-color;
+ cursor: pointer;
+ float: left;
+ margin-left: 10px;
+ }
+
+ .remove {
+ float: right;
+ margin-right: 10px;
+ }
+
+ thead {
+ tr {
+ th {
+ input[type="text"][disabled] {
+ background-color: transparent;
+ color: @fore;
+ font-weight: bold;
+ }
+ .remove {
+ cursor: pointer;
+ font-size: 20px;
+ }
+ }
+ }
+ }
+ tbody {
+ tr {
+ td {
+
+ }
+ }
+ }
+ tfoot {
+ tr {
+ border: none;
+ td {
+ border: none;
+ text-align: center;
+ .save {
+ padding: 15px;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ }
+ }
+ }
+ }
+ }
+
+ #adduser,
+ #addoption {
+ color: @cp-green;
+ border: 1px solid @cp-green;
+ padding: 15px;
+ cursor: pointer;
+ }
+
+ #adduser { .top-left; }
+ #addoption { .bottom-left; }
+}
diff --git a/www/register/main.js b/www/register/main.js
index 251a69f24..374118919 100644
--- a/www/register/main.js
+++ b/www/register/main.js
@@ -1,11 +1,10 @@
define([
+ 'jquery',
'/common/login.js',
'/common/cryptpad-common.js',
'/common/cryptget.js',
- '/common/credential.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Login, Cryptpad, Crypt) {
- var $ = window.jQuery;
+ '/common/credential.js'
+], function ($, Login, Cryptpad, Crypt) {
var APP = window.APP = {
Login: Login,
@@ -68,6 +67,12 @@ define([
proxy.edPublic = result.edPublic;
proxy.edPrivate = result.edPrivate;
+ // feedback API won't work because proxy wasn't loaded
+ $.ajax({
+ type: 'HEAD',
+ url: '/common/feedback.html?REGISTRATION=' + (+new Date()),
+ });
+
Cryptpad.whenRealtimeSyncs(result.realtime, function () {
Cryptpad.login(result.userHash, result.userName, function () {
if (sessionStorage.redirectTo) {
diff --git a/www/settings/index.html b/www/settings/index.html
index 795ab825a..56d77905e 100644
--- a/www/settings/index.html
+++ b/www/settings/index.html
@@ -8,7 +8,6 @@
-
diff --git a/www/settings/main.js b/www/settings/main.js
index 64bf15f7b..8600dba4d 100644
--- a/www/settings/main.js
+++ b/www/settings/main.js
@@ -1,11 +1,10 @@
define([
+ 'jquery',
'/common/cryptpad-common.js',
'/common/cryptget.js',
'/common/mergeDrive.js',
- '/bower_components/file-saver/FileSaver.min.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Cryptpad, Crypt, Merge) {
- var $ = window.jQuery;
+ '/bower_components/file-saver/FileSaver.min.js'
+], function ($, Cryptpad, Crypt, Merge) {
var saveAs = window.saveAs;
var USERNAME_KEY = 'cryptpad.username';
@@ -227,6 +226,40 @@ define([
return $div;
};
+ var bytesToMegabytes = function (bytes) {
+ return Math.floor((bytes / (1024 * 1024) * 100)) / 100;
+ };
+
+ var createUsageButton = function (obj) {
+ var proxy = obj.proxy;
+
+ var $div = $('', { 'class': 'pinned-usage' })
+ .text(Messages.settings_usageTitle)
+ .append('
');
+
+ $('
', {
+ 'class': 'btn btn-primary', // fa fa-hdd-o ?
+ })
+ .text(Messages.settings_usage)
+ .click(function () {
+ if (!(proxy.edPublic && proxy.edPrivate)) {
+ // suggest that they login/register
+ Cryptpad.alert(Messages.settings_pinningNotAvailable);
+ return;
+ }
+ Cryptpad.getPinnedUsage(function (e, bytes) {
+ if (e) {
+ Cryptpad.alert(Messages.settings_pinningError);
+ return;
+ }
+ Cryptpad.alert(Messages._getKey('settings_usageAmount', [bytesToMegabytes(bytes)]));
+ });
+ })
+ .appendTo($div);
+
+ return $div;
+ };
+
var createImportLocalPads = function (obj) {
if (!Cryptpad.isLoggedIn()) { return; }
var $div = $('', {'class': 'importLocalPads'});
@@ -271,6 +304,7 @@ define([
APP.$container.append(createBackupDrive(obj));
APP.$container.append(createImportLocalPads(obj));
APP.$container.append(createResetDrive(obj));
+ APP.$container.append(createUsageButton(obj));
APP.$container.append(createUserFeedbackToggle(obj));
obj.proxy.on('change', [], refresh);
obj.proxy.on('remove', [], refresh);
@@ -308,6 +342,7 @@ define([
? Cryptpad.getStore().getProxy() : undefined;
andThen(storeObj);
+ Cryptpad.reportAppUsage();
});
});
@@ -322,4 +357,3 @@ define([
}
});
});
-
diff --git a/www/slide/main.js b/www/slide/main.js
index 25c367a85..cf1b0c040 100644
--- a/www/slide/main.js
+++ b/www/slide/main.js
@@ -1,5 +1,5 @@
-require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
define([
+ 'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
'/bower_components/textpatcher/TextPatcher.js',
@@ -13,10 +13,8 @@ define([
'/common/visible.js',
'/common/notify.js',
'/slide/slide.js',
- '/bower_components/file-saver/FileSaver.min.js',
- '/bower_components/jquery/dist/jquery.min.js',
-], function (Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget, Modes, Themes, Visible, Notify, Slide) {
- var $ = window.jQuery;
+ '/bower_components/file-saver/FileSaver.min.js'
+], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget, Modes, Themes, Visible, Notify, Slide) {
var saveAs = window.saveAs;
var Messages = Cryptpad.Messages;
@@ -83,7 +81,7 @@ define([
styleActiveLine : true,
search: true,
highlightSelectionMatches: {showToken: /\w+/},
- extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
+ extraKeys: {"Shift-Ctrl-R": undefined},
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
mode: "javascript",
@@ -840,7 +838,7 @@ define([
return cursor;
};
- var onRemote = config.onRemote = function (info) {
+ var onRemote = config.onRemote = function () {
if (initializing) { return; }
var scroll = editor.getScrollInfo();
@@ -929,6 +927,7 @@ define([
var second = function (CM) {
Cryptpad.ready(function (err, env) {
andThen(CM);
+ Cryptpad.reportAppUsage();
});
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
diff --git a/www/slide/slide.js b/www/slide/slide.js
index b4db44922..6c7b68fe8 100644
--- a/www/slide/slide.js
+++ b/www/slide/slide.js
@@ -1,9 +1,8 @@
define([
+ 'jquery',
'/bower_components/marked/marked.min.js',
- '/bower_components/diff-dom/diffDOM.js',
- '/bower_components/jquery/dist/jquery.min.js',
-],function (Marked) {
- var $ = window.jQuery;
+ '/bower_components/diff-dom/diffDOM.js'
+],function ($, Marked) {
var DiffDOM = window.diffDOM;
var renderer = new Marked.Renderer();
diff --git a/www/whiteboard/main.js b/www/whiteboard/main.js
index c28ad84c8..6cadfc487 100644
--- a/www/whiteboard/main.js
+++ b/www/whiteboard/main.js
@@ -1,9 +1,6 @@
-require.config({ paths: {
- 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify'
-}});
-
define([
- '/api/config?cb=' + Math.random().toString(16).substring(2),
+ 'jquery',
+ '/api/config',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
'/bower_components/chainpad-crypto/crypto.js',
'/common/toolbar.js',
@@ -17,14 +14,12 @@ define([
'/common/notify.js',
'/customize/application_config.js',
'/bower_components/secure-fabric.js/dist/fabric.min.js',
- '/bower_components/jquery/dist/jquery.min.js',
'/bower_components/file-saver/FileSaver.min.js',
-], function (Config, Realtime, Crypto, Toolbar, TextPatcher, JSONSortify, JsonOT, Cryptpad, Cryptget, Colors, Visible, Notify, AppConfig) {
+], function ($, Config, Realtime, Crypto, Toolbar, TextPatcher, JSONSortify, JsonOT, Cryptpad, Cryptget, Colors, Visible, Notify, AppConfig) {
var saveAs = window.saveAs;
var Messages = Cryptpad.Messages;
- var module = window.APP = { };
- var $ = module.$ = window.jQuery;
+ var module = window.APP = { $:$ };
var Fabric = module.Fabric = window.fabric;
$(function () {
@@ -618,6 +613,7 @@ window.canvas = canvas;
Cryptpad.ready(function (err, env) {
andThen();
+ Cryptpad.reportAppUsage();
});
Cryptpad.onError(function (info) {
if (info) {