From d16cc2472dfef812f95f399404903b1ba86b9545 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Nov 2020 10:49:13 +0100 Subject: [PATCH 001/201] First prototype of an offline cache --- lib/hk-util.js | 13 ++++++-- www/common/outer/async-store.js | 5 ++- www/common/outer/cache-store.js | 55 ++++++++++++++++++++++++++++++++ www/common/outer/sharedfolder.js | 4 ++- www/common/outer/team.js | 5 ++- 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 www/common/outer/cache-store.js diff --git a/lib/hk-util.js b/lib/hk-util.js index 14263e481..4fa2140f9 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -419,9 +419,11 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, _cb) => { // fall through to the next block if the offset of the hash in question is not in memory if (lastKnownHash && typeof(lkh) !== "number") { return; } + // If we have a lastKnownHash or we didn't ask for one, we don't need the next blocks + waitFor.abort(); + // Since last 2 checkpoints if (!lastKnownHash) { - waitFor.abort(); // Less than 2 checkpoints in the history: return everything if (index.cpIndex.length < 2) { return void cb(null, 0); } // Otherwise return the second last checkpoint's index @@ -436,7 +438,14 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, _cb) => { to reconcile their differences. */ } - offset = lkh; + // If our lastKnownHash is older than the 2nd to last checkpoint, + // only send the last 2 checkpoints and ignore "lkh" + if (lkh && index.cpIndex.length >= 2 && lkh < index.cpIndex[0].offset) { + return void cb(null, index.cpIndex[0].offset); + } + + // Otherwise use our lastKnownHash + cb(null, lkh); })); }).nThen((w) => { // skip past this block if the offset is anything other than -1 diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 62e078050..a39734dc4 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -10,6 +10,7 @@ define([ '/common/common-realtime.js', '/common/common-messaging.js', '/common/pinpad.js', + '/common/outer/cache-store.js', '/common/outer/sharedfolder.js', '/common/outer/cursor.js', '/common/outer/onlyoffice.js', @@ -28,7 +29,7 @@ define([ '/bower_components/nthen/index.js', '/bower_components/saferphore/index.js', ], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback, - Realtime, Messaging, Pinpad, + Realtime, Messaging, Pinpad, Cache, SF, Cursor, OnlyOffice, Mailbox, Profile, Team, Messenger, History, NetConfig, AppConfig, Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) { @@ -1590,6 +1591,7 @@ define([ Store.leavePad(null, data, function () {}); }; var conf = { + Cache: Cache, onReady: function (pad) { var padData = pad.metadata || {}; channel.data = padData; @@ -2640,6 +2642,7 @@ define([ readOnly: false, validateKey: secret.keys.validateKey || undefined, crypto: Crypto.createEncryptor(secret.keys), + Cache: Cache, userName: 'fs', logLevel: 1, ChainPad: ChainPad, diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js new file mode 100644 index 000000000..139501872 --- /dev/null +++ b/www/common/outer/cache-store.js @@ -0,0 +1,55 @@ +define([ + '/bower_components/localforage/dist/localforage.min.js', +], function (localForage) { + var S = {}; + + var cache = localForage.createInstance({ + name: "cp_cache" + }); + + // id: channel ID or blob ID + // returns array of messages + S.getChannelCache = function (id, cb) { + cache.getItem(id, function (err, obj) { + if (err || !obj || !Array.isArray(obj.c)) { + return void cb(err || 'EINVAL'); + } + cb(null, obj); + }); + }; + + var checkCheckpoints = function (array) { + if (!Array.isArray(array)) { return; } + var cp = 0; + for (var i = array.length - 1; i >= 0; i--) { + if (array[i].isCheckpoint) { cp++; } + if (cp === 2) { + array.splice(0, i); + break; + } + } + }; + + S.storeCache = function (id, validateKey, val, cb) { + cb = cb || function () {}; + if (!Array.isArray(val) || !validateKey) { return void cb('EINVAL'); } + checkCheckpoints(val); + cache.setItem(id, { + k: validateKey, + c: val + }, function (err) { + cb(err); + }); + }; + + S.clearChannel = function (id, cb) { + cb = cb || function () {}; + cache.removeItem(id, cb); + }; + + S.clear = function () { + cache.clear(); + }; + + return S; +}); diff --git a/www/common/outer/sharedfolder.js b/www/common/outer/sharedfolder.js index e34cd64df..3c1c8632d 100644 --- a/www/common/outer/sharedfolder.js +++ b/www/common/outer/sharedfolder.js @@ -2,12 +2,13 @@ define([ '/common/common-hash.js', '/common/common-util.js', '/common/userObject.js', + '/common/outer/cache-store.js', '/bower_components/nthen/index.js', '/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad/chainpad.dist.js', -], function (Hash, Util, UserObject, +], function (Hash, Util, UserObject, Cache, nThen, Crypto, Listmap, ChainPad) { var SF = {}; @@ -174,6 +175,7 @@ define([ ChainPad: ChainPad, classic: true, network: network, + Cache: Cache, metadata: { validateKey: secret.keys.validateKey || undefined, owners: owners diff --git a/www/common/outer/team.js b/www/common/outer/team.js index de9206511..dc078a2f4 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -12,6 +12,7 @@ define([ '/common/common-feedback.js', '/common/outer/invitation.js', '/common/cryptget.js', + '/common/outer/cache-store.js', '/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-crypto/crypto.js', @@ -21,7 +22,7 @@ define([ '/bower_components/saferphore/index.js', '/bower_components/tweetnacl/nacl-fast.min.js', ], function (Util, Hash, Constants, Realtime, - ProxyManager, UserObject, SF, Roster, Messaging, Feedback, Invite, Crypt, + ProxyManager, UserObject, SF, Roster, Messaging, Feedback, Invite, Crypt, Cache, Listmap, Crypto, CpNetflux, ChainPad, nThen, Saferphore) { var Team = {}; @@ -426,6 +427,7 @@ define([ channel: secret.channel, crypto: crypto, ChainPad: ChainPad, + Cache: Cache, metadata: { validateKey: secret.keys.validateKey || undefined, }, @@ -573,6 +575,7 @@ define([ logLevel: 1, classic: true, ChainPad: ChainPad, + Cache: Cache, owners: [ctx.store.proxy.edPublic] }; nThen(function (waitFor) { From 4126ae5d8b56b0bd846606fcfe8904b99d9e28dc Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Nov 2020 13:48:59 +0100 Subject: [PATCH 002/201] Display pad content from cache before joining the network/channel --- www/common/cryptpad-common.js | 4 +++ www/common/outer/async-store.js | 6 ++++ www/common/sframe-app-framework.js | 38 +++++++++++++++++++++ www/common/sframe-chainpad-netflux-inner.js | 10 ++++++ www/common/sframe-chainpad-netflux-outer.js | 13 ++++++- www/common/toolbar.js | 12 +++++++ 6 files changed, 82 insertions(+), 1 deletion(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 8874871d8..82ea2991f 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -992,6 +992,8 @@ define([ pad.onJoinEvent = Util.mkEvent(); pad.onLeaveEvent = Util.mkEvent(); pad.onDisconnectEvent = Util.mkEvent(); + pad.onCacheEvent = Util.mkEvent(); + pad.onCacheReadyEvent = Util.mkEvent(); pad.onConnectEvent = Util.mkEvent(); pad.onErrorEvent = Util.mkEvent(); pad.onMetadataEvent = Util.mkEvent(); @@ -1957,6 +1959,8 @@ define([ PAD_JOIN: common.padRpc.onJoinEvent.fire, PAD_LEAVE: common.padRpc.onLeaveEvent.fire, PAD_DISCONNECT: common.padRpc.onDisconnectEvent.fire, + PAD_CACHE: common.padRpc.onCacheEvent.fire, + PAD_CACHE_READY: common.padRpc.onCacheReadyEvent.fire, PAD_CONNECT: common.padRpc.onConnectEvent.fire, PAD_ERROR: common.padRpc.onErrorEvent.fire, PAD_METADATA: common.padRpc.onMetadataEvent.fire, diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index a39734dc4..599d1d3b3 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1592,6 +1592,12 @@ define([ }; var conf = { Cache: Cache, + onCacheStart: function () { + postMessage(clientId, "PAD_CACHE"); + }, + onCacheReady: function (info) { + postMessage(clientId, "PAD_CACHE_READY"); + }, onReady: function (pad) { var padData = pad.metadata || {}; channel.data = padData; diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 3097cf6d2..81ab18d45 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -467,7 +467,44 @@ define([ }); }; + var onCacheReady = function () { + stateChange(STATE.DISCONNECTED); + toolbar.offline(true); + var newContentStr = cpNfInner.chainpad.getUserDoc(); + if (toolbar) { + // Check if we have a new chainpad instance + toolbar.resetChainpad(cpNfInner.chainpad); + } +console.log(newContentStr); + + // Invalid cache? abort + // XXX tell outer/worker to invalidate cache + if (newContentStr === '') { return; } + + var privateDat = cpNfInner.metadataMgr.getPrivateData(); + var type = privateDat.app; + + var newContent = JSON.parse(newContentStr); + var metadata = extractMetadata(newContent); +console.log('OKOK'); + + // Make sure we're using the correct app for this cache + if (metadata && typeof(metadata.type) !== 'undefined' && metadata.type !== type) { + console.error('return'); + return; + } + + cpNfInner.metadataMgr.updateMetadata(metadata); + newContent = normalize(newContent); + if (!unsyncMode) { + contentUpdate(newContent, function () { return function () {}}); + } + + UI.removeLoadingScreen(emitResize); + }; var onReady = function () { + toolbar.offline(false); + console.error('READY'); var newContentStr = cpNfInner.chainpad.getUserDoc(); if (state === STATE.DELETED) { return; } @@ -732,6 +769,7 @@ define([ onRemote: onRemote, onLocal: onLocal, onInit: onInit, + onCacheReady: onCacheReady, onReady: function () { evStart.reg(onReady); }, onConnectionChange: onConnectionChange, onError: onError, diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index abb1cebdf..57689b7ea 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -34,6 +34,7 @@ define([ var onLocal = config.onLocal || function () { }; var setMyID = config.setMyID || function () { }; var onReady = config.onReady || function () { }; + var onCacheReady = config.onCacheReady || function () { }; var onError = config.onError || function () { }; var userName = config.userName; var initialState = config.initialState; @@ -93,6 +94,14 @@ define([ evInfiniteSpinner.fire(); }, 2000); + sframeChan.on('EV_RT_CACHE', function (isPermanent) { + // XXX + }); + sframeChan.on('EV_RT_CACHE_READY', function (isPermanent) { + // XXX + onCacheReady({realtime: chainpad}); + console.error('PEWPEWPEW'); + }); sframeChan.on('EV_RT_DISCONNECT', function (isPermanent) { isReady = false; chainpad.abort(); @@ -134,6 +143,7 @@ define([ evConnected.fire(); }); sframeChan.on('Q_RT_MESSAGE', function (content, cb) { + console.log(content); if (isReady) { onLocal(true); // should be onBeforeMessage } diff --git a/www/common/sframe-chainpad-netflux-outer.js b/www/common/sframe-chainpad-netflux-outer.js index 72cfdef9a..056b9cd28 100644 --- a/www/common/sframe-chainpad-netflux-outer.js +++ b/www/common/sframe-chainpad-netflux-outer.js @@ -89,6 +89,7 @@ define([], function () { validateKey = msgObj.validateKey; } var message = msgIn(msgObj.user, msgObj.msg); + console.log(message); if (!message) { return; } lastTime = msgObj.time; @@ -114,16 +115,26 @@ define([], function () { if (firstConnection) { firstConnection = false; // Add the handlers to the WebChannel - padRpc.onMessageEvent.reg(function (msg) { onMessage(msg); }); padRpc.onJoinEvent.reg(function (m) { sframeChan.event('EV_RT_JOIN', m); }); padRpc.onLeaveEvent.reg(function (m) { sframeChan.event('EV_RT_LEAVE', m); }); } }; + padRpc.onMessageEvent.reg(function (msg) { onMessage(msg); }); + padRpc.onDisconnectEvent.reg(function (permanent) { sframeChan.event('EV_RT_DISCONNECT', permanent); }); + padRpc.onCacheReadyEvent.reg(function () { + console.log('ONCACHEREADY'); + sframeChan.event('EV_RT_CACHE_READY'); + }); + + padRpc.onCacheEvent.reg(function () { + sframeChan.event('EV_RT_CACHE'); + }); + padRpc.onConnectEvent.reg(function (data) { onOpen(data); }); diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 78e4e91f3..0927cde21 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -1365,6 +1365,18 @@ MessengerUI, Messages) { } }; + toolbar.offline = function (bool) { + toolbar.connected = !bool; // Can't edit title + toolbar.history = bool; // Stop "Initializing" state + toolbar.isErrorState = bool; // Stop kickSpinner + toolbar.title.toggleClass('cp-toolbar-unsync', bool); // "read only" next to the title + if (bool && toolbar.spinner) { + toolbar.spinner.text("OFFLINE"); // XXX + } else { + kickSpinner(toolbar, config); + } + }; + // On log out, remove permanently the realtime elements of the toolbar Common.onLogout(function () { failed(); From 8d5d85c7f8d3ad0bdc7fc3fba094670bf7031644 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Nov 2020 15:35:14 +0100 Subject: [PATCH 003/201] Add XXX --- www/common/outer/cache-store.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index 139501872..aed480860 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -1,4 +1,5 @@ define([ + // XXX Load util and use mkAsync '/bower_components/localforage/dist/localforage.min.js', ], function (localForage) { var S = {}; @@ -21,6 +22,7 @@ define([ var checkCheckpoints = function (array) { if (!Array.isArray(array)) { return; } var cp = 0; + // XXX check sliceCpIndex from hk-util: use the same logic for forks for (var i = array.length - 1; i >= 0; i--) { if (array[i].isCheckpoint) { cp++; } if (cp === 2) { @@ -37,6 +39,8 @@ define([ cache.setItem(id, { k: validateKey, c: val + // XXX add "time" here +new Date() ==> we want to store the time when it was updated in our indexeddb in case we want to remove inactive entries from indexeddb later + // XXX we would also need to update the time when we "getChannelCache" }, function (err) { cb(err); }); From ebd007fdb95cfed508e52a52a2b42d7da422cffb Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Nov 2020 17:17:20 +0100 Subject: [PATCH 004/201] Use offline state per team and not global state --- www/common/outer/team.js | 23 +++++++++++------ www/teams/inner.js | 53 ++++++++++++++++++++++++++++++++-------- www/teams/main.js | 4 +-- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/www/common/outer/team.js b/www/common/outer/team.js index de9206511..bb2b8fbf5 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -57,11 +57,11 @@ define([ }); proxy.on('disconnect', function () { team.offline = true; - team.sendEvent('NETWORK_DISCONNECT'); + team.sendEvent('NETWORK_DISCONNECT', team.id); }); proxy.on('reconnect', function () { team.offline = false; - team.sendEvent('NETWORK_RECONNECT'); + team.sendEvent('NETWORK_RECONNECT', team.id); }); } proxy.on('change', [], function (o, n, p) { @@ -931,7 +931,9 @@ define([ if (!team) { return void cb ({error: 'ENOENT'}); } if (!team.roster) { return void cb({error: 'NO_ROSTER'}); } var state = team.roster.getState() || {}; - cb(state.metadata || {}); + var md = state.metadata || {}; + md.offline = team.offline; + cb(md); }; var setTeamMetadata = function (ctx, data, cId, cb) { @@ -1879,15 +1881,15 @@ define([ var t = Util.clone(teams); Object.keys(t).forEach(function (id) { // If failure to load the team, don't send it - if (ctx.teams[id]) { return; } + if (ctx.teams[id]) { + t[id].offline = ctx.teams[id].offline; + return; + } t[id].error = true; }); cb(t); }; team.execCommand = function (clientId, obj, cb) { - if (ctx.store.offline) { - return void cb({ error: 'OFFLINE' }); - } var cmd = obj.cmd; var data = obj.data; @@ -1911,30 +1913,36 @@ define([ return void setTeamMetadata(ctx, data, clientId, cb); } if (cmd === 'OFFER_OWNERSHIP') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void offerOwnership(ctx, data, clientId, cb); } if (cmd === 'ANSWER_OWNERSHIP') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void answerOwnership(ctx, data, clientId, cb); } if (cmd === 'DESCRIBE_USER') { return void describeUser(ctx, data, clientId, cb); } if (cmd === 'INVITE_TO_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void inviteToTeam(ctx, data, clientId, cb); } if (cmd === 'LEAVE_TEAM') { return void leaveTeam(ctx, data, clientId, cb); } if (cmd === 'JOIN_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void joinTeam(ctx, data, clientId, cb); } if (cmd === 'REMOVE_USER') { return void removeUser(ctx, data, clientId, cb); } if (cmd === 'DELETE_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void deleteTeam(ctx, data, clientId, cb); } if (cmd === 'CREATE_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void createTeam(ctx, data, clientId, cb); } if (cmd === 'GET_EDITABLE_FOLDERS') { @@ -1947,6 +1955,7 @@ define([ return void getPreviewContent(ctx, data, clientId, cb); } if (cmd === 'ACCEPT_LINK_INVITATION') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void acceptLinkInvitation(ctx, data, clientId, cb); } }; diff --git a/www/teams/inner.js b/www/teams/inner.js index a75ed94db..d49bf6209 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -46,7 +46,9 @@ define([ Backup, Messages) { - var APP = {}; + var APP = { + teams: {} + }; var driveAPP = {}; var saveAs = window.saveAs; //var SHARED_FOLDER_NAME = Messages.fm_sharedFolderName; @@ -211,6 +213,11 @@ define([ if (obj && obj.error) { return void UI.warn(Messages.error); } + + // Refresh offline state + APP.teams[APP.team] = APP.teams[APP.team] || {}; + APP.teams[APP.team].offline = obj.offline; + common.displayAvatar($avatar, obj.avatar, obj.name); $category.append($avatar); $avatar.append(h('span.cp-sidebarlayout-category-name', obj.name)); @@ -333,6 +340,11 @@ define([ }); APP.drive = drive; driveAPP.refresh = drive.refresh; + + if (APP.teams[id] && APP.teams[id].offline) { + setEditable(false); + drive.refresh(); + } }); }; @@ -406,7 +418,18 @@ define([ content.push(h('h3', Messages.team_listTitle + ' ' + slots)); + var metadataMgr = common.getMetadataMgr(); + var privateData = metadataMgr.getPrivateData(); + var teams = privateData.teams || {}; + APP.teams = {}; + keys.forEach(function (id) { + if (!obj[id].empty) { + APP.teams[id] = { + offline: obj[id] && obj[id].offline + }; + } + var team = obj[id]; if (team.empty) { list.push(h('div.cp-team-list-team.empty', [ @@ -1433,27 +1456,37 @@ define([ } }); - var onDisconnect = function (noAlert) { + var teams = privateData.teams || {}; + + var onDisconnect = function (teamId) { + if (APP.team && teamId && APP.team !== teamId) { return; } setEditable(false); if (APP.team && driveAPP.refresh) { driveAPP.refresh(); } toolbar.failed(); - if (!noAlert) { UIElements.disconnectAlert(); } + UIElements.disconnectAlert(); }; - var onReconnect = function () { + var onReconnect = function (teamId) { + if (APP.team && teamId && APP.team !== teamId) { return; } setEditable(true); if (APP.team && driveAPP.refresh) { driveAPP.refresh(); } toolbar.reconnecting(); UIElements.reconnectAlert(); }; - sframeChan.on('EV_DRIVE_LOG', function (msg) { - UI.log(msg); + sframeChan.on('EV_DRIVE_LOG', function (data) { + UI.log(data.msg); }); - sframeChan.on('EV_NETWORK_DISCONNECT', function () { - onDisconnect(); + sframeChan.on('EV_NETWORK_DISCONNECT', function (teamId) { + onDisconnect(teamId); + if (teamId && APP.teams[teamId]) { + APP.teams[teamId].offline = true; + } }); - sframeChan.on('EV_NETWORK_RECONNECT', function () { - onReconnect(); + sframeChan.on('EV_NETWORK_RECONNECT', function (teamId) { + onReconnect(teamId); + if (teamId && APP.teams[teamId]) { + APP.teams[teamId].offline = false; + } }); common.onLogout(function () { setEditable(false); }); }); diff --git a/www/teams/main.js b/www/teams/main.js index cab84d160..0344a902f 100644 --- a/www/teams/main.js +++ b/www/teams/main.js @@ -85,10 +85,10 @@ define([ sframeChan.event('EV_'+obj.data.ev, obj.data.data); } if (obj.data.ev === 'NETWORK_RECONNECT') { - sframeChan.event('EV_NETWORK_RECONNECT'); + sframeChan.event('EV_NETWORK_RECONNECT', obj.data.data); } if (obj.data.ev === 'NETWORK_DISCONNECT') { - sframeChan.event('EV_NETWORK_DISCONNECT'); + sframeChan.event('EV_NETWORK_DISCONNECT', obj.data.data); } }); From 446cca0725452110db24d269b45292348c7cf075 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Nov 2020 18:29:09 +0100 Subject: [PATCH 005/201] Use offline state per shared folder in the drive --- customize.dist/loading.js | 1 + www/common/drive-ui.js | 26 ++++++++++++++++++++++++-- www/common/outer/async-store.js | 5 ++++- www/common/proxy-manager.js | 8 ++++++++ www/drive/inner.js | 2 ++ www/teams/inner.js | 2 ++ 6 files changed, 41 insertions(+), 3 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 4f8b79125..733b3fff0 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -327,6 +327,7 @@ button.primary:hover{ if (!built) { return; } try { var node = document.querySelector('.cp-loading-progress'); + if (!node) { return; } if (node.parentNode) { node.parentNode.removeChild(node); } document.querySelector('.cp-loading-spinner-container').setAttribute('style', 'display:none;'); document.querySelector('#cp-loading-message').setAttribute('style', 'display:block;'); diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 53e496ef4..4886b213e 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -42,6 +42,7 @@ define([ var APP = window.APP = { editable: false, + online: true, mobile: function () { if (window.matchMedia) { return !window.matchMedia('(any-pointer:fine)').matches; } else { return $('body').width() <= 600; } @@ -267,13 +268,25 @@ define([ }; // Handle disconnect/reconnect - var setEditable = function (state, isHistory) { + // If isHistory and isSf are both false, update the "APP.online" flag + // If isHistory is true, update the "APP.history" flag + // isSf is used to detect offline shared folders: setEditable is called on displayDirectory + var setEditable = function (state, isHistory, isSf) { if (APP.closed || !APP.$content || !$.contains(document.documentElement, APP.$content[0])) { return; } + if (isHistory) { + APP.history = !state; + } else if (!isSf) { + APP.online = state; + } + state = APP.online && !APP.history && state; APP.editable = !APP.readOnly && state; + if (!state) { APP.$content.addClass('cp-app-drive-readonly'); - if (!isHistory) { + if (!APP.history || !APP.online) { $('#cp-app-drive-connection-state').show(); + } else { + $('#cp-app-drive-connection-state').hide(); } $('[draggable="true"]').attr('draggable', false); } @@ -3670,6 +3683,15 @@ define([ } var readOnlyFolder = false; + + // If the shared folder is offline, add the "DISCONNECTED" banner, otherwise + // use the normal "editable" behavior (based on drive offline or history mode) + if (sfId && manager.folders[sfId].offline) { + setEditable(false, false, true); + } else { + setEditable(true, false, true); + } + if (APP.readOnly) { // Read-only drive (team?) $content.prepend($readOnly.clone()); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 62e078050..76b92feab 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -120,10 +120,13 @@ define([ Store.getSharedFolder = function (clientId, data, cb) { var s = getStore(data.teamId); var id = data.id; + var proxy; if (!s || !s.manager) { return void cb({ error: 'ENOTFOUND' }); } if (s.manager.folders[id]) { + proxy = Util.clone(s.manager.folders[id].proxy); + proxy.offline = Boolean(s.manager.folders[id].offline); // If it is loaded, return the shared folder proxy - return void cb(s.manager.folders[id].proxy); + return void cb(proxy); } else { // Otherwise, check if we know this shared folder var shared = Util.find(s.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {}; diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 88f4a052b..74bfcd890 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -40,6 +40,14 @@ define([ userObject: userObject, leave: leave }; + if (proxy.on) { + proxy.on('disconnect', function () { + Env.folders[id].offline = true; + }); + proxy.on('reconnect', function () { + Env.folders[id].online = true; + }); + } return userObject; }; diff --git a/www/drive/inner.js b/www/drive/inner.js index f85819083..9aa080dc3 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -82,6 +82,8 @@ define([ var readOnly = !secret.keys.editKeyStr; if (!manager || !manager.folders[fId]) { return; } manager.folders[fId].userObject.setReadOnly(readOnly, secret.keys.secondaryKey); + + manager.folders[fId].offline = newObj.offline; })); }); // Remove from memory folders that have been deleted from the drive remotely diff --git a/www/teams/inner.js b/www/teams/inner.js index d49bf6209..c50dde5a1 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -93,6 +93,8 @@ define([ var readOnly = !secret.keys.editKeyStr; if (!manager || !manager.folders[fId]) { return; } manager.folders[fId].userObject.setReadOnly(readOnly, secret.keys.secondaryKey); + + manager.folders[fId].offline = newObj.offline; })); }); // Remove from memory folders that have been deleted from the drive remotely From 4edf74587ef964743c2545698ea5ef4f38818836 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Nov 2020 18:30:09 +0100 Subject: [PATCH 006/201] lint compliance --- www/teams/inner.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/www/teams/inner.js b/www/teams/inner.js index c50dde5a1..05f823e2d 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -420,9 +420,6 @@ define([ content.push(h('h3', Messages.team_listTitle + ' ' + slots)); - var metadataMgr = common.getMetadataMgr(); - var privateData = metadataMgr.getPrivateData(); - var teams = privateData.teams || {}; APP.teams = {}; keys.forEach(function (id) { @@ -1458,8 +1455,6 @@ define([ } }); - var teams = privateData.teams || {}; - var onDisconnect = function (teamId) { if (APP.team && teamId && APP.team !== teamId) { return; } setEditable(false); From b456fee9e9beb5285055b979d01deb2e9e6099c3 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 4 Nov 2020 12:24:57 +0100 Subject: [PATCH 007/201] Fix API error --- www/teams/inner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/teams/inner.js b/www/teams/inner.js index 05f823e2d..070d11825 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -1470,8 +1470,8 @@ define([ UIElements.reconnectAlert(); }; - sframeChan.on('EV_DRIVE_LOG', function (data) { - UI.log(data.msg); + sframeChan.on('EV_DRIVE_LOG', function (msg) { + UI.log(msg); }); sframeChan.on('EV_NETWORK_DISCONNECT', function (teamId) { onDisconnect(teamId); From 0e08abecb39573c10dbff2ac15fb7fb9e513cacf Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 5 Nov 2020 18:33:17 +0530 Subject: [PATCH 008/201] update changelog for 3.24.0 --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f649883d..3330eb35b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,40 @@ +# YunnanLakeNewt (3.24.0) + +## Goals + +We are once again working to develop some significant new features. This release is fairly small but includes some significant changes to detect and handle a variety of errors. + +## Update notes + +This release includes some minor corrections the recommended NGINX configuration supplied in `cryptpad/docs/example.nginx.conf`. + +To update from 3.23.2 to 3.24.0: + +1. Update your NGINX config to replicate the most recent changes and reload NGINX to apply them. +2. Stop the nodejs server. +3. Pull the latest code from the `3.24.0` tag or the `main` branch using `git`. +4. Ensure you have the latest clientside and serverside dependencies with `bower update` and `npm install`. +5. Restart the nodejs server. + +## Features + +* A variety of CryptPad's pages now feature a much-improved loading screen which provides a more informative account of what is being loaded. It also implements some generic error handling to detect and report when something has failed in a catastrophic way. This is intended to both inform users that the page is in a broken state as well as to improve the quality of the debugging information they can provide to us so that we can fix the underlying cause. +* It is now possible to create spreadsheets from templates. Template functionality has existed for a long time in our other editors, however, OnlyOffice's architecture differs significantly and required the implementation of a wholly different system. +* One user reported some confusion regarding the use of the Kanban app's _tag_ functionality. We've updated the UI to be a little more informative. +* The "table of contents" in rich text pads now includes "anchors" created via the editor's toolbar. + +## Bug fixes + +* Recent changes to CryptPad's recommended CSP headers enabled Firefox to export spreadsheets to XLSX format, but they also triggered some regressions due to a number of incompatible APIs. + * Our usage of the `sessionStorage` for the purpose of passing important information to editors opened in a new tab stopped working. This meant that when you created a document in a folder, the resulting new tab would not receive the argument describing where it should be stored, and would instead save it to the default location. We've addressed this by replacing our usage of sessionStorage with a new format for passing the same arguments via the hash in the new document's URL. + * The `window.print` API also failed in a variety of cases. We've updated the relevant CSP headers to only be applied on the sheet editor (to support XSLX export) but allow printing elsewhere. We've also updated some print styles to provide more appealing results. +* The table of contents available in rich text pads failed to scroll when there were a sufficient number of heading to flow beyond the length of the page. Now a scrollbar appears when necessary. +* We discovered a number of cases where the presence of an allow list prevented some valid behaviour due to the server incorrectly concluding that users were not authenticated. We've improved the client's ability to detect these cases and re-authenticate when necessary. +* We also found that when the server was under very heavy load some database queries were timing out because they were slow (but not stopped). We've addressed this to only terminate such queries if they have been entirely inactive for several minutes. +* It was possible for "safe links" to include a mode ("edit" or "view") which did not match the rights of the user opening them. For example, if a user loaded a safe link with edit rights though they only had read-only access via their "viewer" role in a team. CryptPad will now recover from such cases and open the document with the closest set of access rights that they possess. +* We found that the server query `"IS_NEW_PAD"` could return an error but that clients would incorrectly interpret such a response as a `false`. This has been corrected. +* Finally, we've modified the "trash" UI for user and team drives such that when users attempt to empty their trash of owned shared folders they are prompted to remove the items or delete them from the server entirely, as they would be with other owned assets. + # XerusDaamsi reloaded (3.23.2) A number of instance administrators reported issues following our 3.23.1 release. We suspect the issues were caused by applying the recommended update steps out of order which would result in the incorrect HTTP header values getting cached for the most recent version of a file. Since the most recently updated headers modified some security settings, this caused a catastrophic error on clients receiving the incorrect headers which caused them to fail to load under certain circumstances. From e998c21691aa302ce9d0523f638d54403c43168e Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 5 Nov 2020 16:19:05 +0100 Subject: [PATCH 009/201] Invalidate corrupted caches --- www/common/cryptpad-common.js | 4 +++ www/common/outer/async-store.js | 10 +++++- www/common/outer/cache-store.js | 37 ++++++++++++--------- www/common/outer/store-rpc.js | 1 + www/common/sframe-app-framework.js | 23 ++++++++----- www/common/sframe-chainpad-netflux-inner.js | 6 ---- www/common/sframe-chainpad-netflux-outer.js | 2 -- www/common/sframe-common-outer.js | 9 ++++- 8 files changed, 58 insertions(+), 34 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 82ea2991f..34272dcfa 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1006,6 +1006,10 @@ define([ postMessage("GIVE_PAD_ACCESS", data, cb); }; + common.onCorruptedCache = function (channel) { + postMessage("CORRUPTED_CACHE", channel); + }; + common.setPadMetadata = function (data, cb) { postMessage('SET_PAD_METADATA', data, cb); }; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 7066d3921..56e1cadfd 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1607,7 +1607,7 @@ define([ if (padData && padData.validateKey && store.messenger) { store.messenger.storeValidateKey(data.channel, padData.validateKey); } - postMessage(clientId, "PAD_READY"); + postMessage(clientId, "PAD_READY", pad.noCache); }, onMessage: function (m, user, validateKey, isCp, hash) { channel.lastHash = hash; @@ -1738,6 +1738,14 @@ define([ channel.sendMessage(msg, clientId, cb); }; + Store.corruptedCache = function (clientId, channel) { + var chan = channels[channel]; + if (!chan || !chan.cpNf) { return; } + Cache.clearChannel(channel); + if (!chan.cpNf.resetCache) { return; } + chan.cpNf.resetCache(); + }; + // Unpin and pin the new channel in all team when changing a pad password Store.changePadPasswordPin = function (clientId, data, cb) { var oldChannel = data.oldChannel; diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index aed480860..687259ddd 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -1,7 +1,7 @@ define([ - // XXX Load util and use mkAsync + '/common/common-util.js', '/bower_components/localforage/dist/localforage.min.js', -], function (localForage) { +], function (Util, localForage) { var S = {}; var cache = localForage.createInstance({ @@ -11,43 +11,50 @@ define([ // id: channel ID or blob ID // returns array of messages S.getChannelCache = function (id, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); cache.getItem(id, function (err, obj) { if (err || !obj || !Array.isArray(obj.c)) { return void cb(err || 'EINVAL'); } cb(null, obj); + obj.t = +new Date(); + cache.setItem(id, obj); }); }; + // Keep the last two checkpoint + any checkpoint that may exist in the last 100 messages + // FIXME: duplicate system with sliceCpIndex from lib/hk-util.js var checkCheckpoints = function (array) { if (!Array.isArray(array)) { return; } - var cp = 0; - // XXX check sliceCpIndex from hk-util: use the same logic for forks - for (var i = array.length - 1; i >= 0; i--) { - if (array[i].isCheckpoint) { cp++; } - if (cp === 2) { - array.splice(0, i); - break; - } + // Keep the last 100 messages + if (array.length > 100) { + array.splice(0, array.length - 100); } + // Remove every message before the first checkpoint + var firstCpIdx; + array.some(function (el, i) { + if (!el.isCheckpoint) { return; } + firstCpIdx = i; + return true; + }); + array.splice(0, firstCpIdx); }; S.storeCache = function (id, validateKey, val, cb) { - cb = cb || function () {}; + cb = Util.once(Util.mkAsync(cb || function () {})); if (!Array.isArray(val) || !validateKey) { return void cb('EINVAL'); } checkCheckpoints(val); cache.setItem(id, { k: validateKey, - c: val - // XXX add "time" here +new Date() ==> we want to store the time when it was updated in our indexeddb in case we want to remove inactive entries from indexeddb later - // XXX we would also need to update the time when we "getChannelCache" + c: val, + t: (+new Date()) // 't' represent the "lastAccess" of this cache (get or set) }, function (err) { cb(err); }); }; S.clearChannel = function (id, cb) { - cb = cb || function () {}; + cb = Util.once(Util.mkAsync(cb || function () {})); cache.removeItem(id, cb); }; diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 6ac3e52b0..767448284 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -88,6 +88,7 @@ define([ CHANGE_PAD_PASSWORD_PIN: Store.changePadPasswordPin, GET_LAST_HASH: Store.getLastHash, GET_SNAPSHOT: Store.getSnapshot, + CORRUPTED_CACHE: Store.corruptedCache, // Drive DRIVE_USEROBJECT: Store.userObjectCommand, // Settings, diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 81ab18d45..59dd51520 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -467,6 +467,10 @@ define([ }); }; + var onCorruptedCache = function (cb) { + var sframeChan = common.getSframeChannel(); + sframeChan.event("Q_CORRUPTED_CACHE", cb); + }; var onCacheReady = function () { stateChange(STATE.DISCONNECTED); toolbar.offline(true); @@ -475,23 +479,19 @@ define([ // Check if we have a new chainpad instance toolbar.resetChainpad(cpNfInner.chainpad); } -console.log(newContentStr); - // Invalid cache? abort - // XXX tell outer/worker to invalidate cache - if (newContentStr === '') { return; } + // Invalid cache + if (newContentStr === '') { return void onCorruptedCache(); } var privateDat = cpNfInner.metadataMgr.getPrivateData(); var type = privateDat.app; var newContent = JSON.parse(newContentStr); var metadata = extractMetadata(newContent); -console.log('OKOK'); // Make sure we're using the correct app for this cache if (metadata && typeof(metadata.type) !== 'undefined' && metadata.type !== type) { - console.error('return'); - return; + return void onCorruptedCache(); } cpNfInner.metadataMgr.updateMetadata(metadata); @@ -504,7 +504,7 @@ console.log('OKOK'); }; var onReady = function () { toolbar.offline(false); - console.error('READY'); + var newContentStr = cpNfInner.chainpad.getUserDoc(); if (state === STATE.DELETED) { return; } @@ -545,7 +545,12 @@ console.log('OKOK'); console.log("Either this is an empty document which has not been touched"); console.log("Or else something is terribly wrong, reloading."); Feedback.send("NON_EMPTY_NEWDOC"); - setTimeout(function () { common.gotoURL(); }, 1000); + // The cache may be wrong, empty it and reload after. + waitFor.abort(); + UI.errorLoadingScreen("MAYBE CORRUPTED CACHE... RELOADING"); // XXX + onCorruptedCache(function () { + setTimeout(function () { common.gotoURL(); }, 1000); + }); return; } console.log('updating title'); diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 57689b7ea..e70a5874e 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -94,13 +94,8 @@ define([ evInfiniteSpinner.fire(); }, 2000); - sframeChan.on('EV_RT_CACHE', function (isPermanent) { - // XXX - }); sframeChan.on('EV_RT_CACHE_READY', function (isPermanent) { - // XXX onCacheReady({realtime: chainpad}); - console.error('PEWPEWPEW'); }); sframeChan.on('EV_RT_DISCONNECT', function (isPermanent) { isReady = false; @@ -143,7 +138,6 @@ define([ evConnected.fire(); }); sframeChan.on('Q_RT_MESSAGE', function (content, cb) { - console.log(content); if (isReady) { onLocal(true); // should be onBeforeMessage } diff --git a/www/common/sframe-chainpad-netflux-outer.js b/www/common/sframe-chainpad-netflux-outer.js index 056b9cd28..c19299da2 100644 --- a/www/common/sframe-chainpad-netflux-outer.js +++ b/www/common/sframe-chainpad-netflux-outer.js @@ -89,7 +89,6 @@ define([], function () { validateKey = msgObj.validateKey; } var message = msgIn(msgObj.user, msgObj.msg); - console.log(message); if (!message) { return; } lastTime = msgObj.time; @@ -127,7 +126,6 @@ define([], function () { }); padRpc.onCacheReadyEvent.reg(function () { - console.log('ONCACHEREADY'); sframeChan.event('EV_RT_CACHE_READY'); }); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index b7ce64d4d..8fd37db4b 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -53,12 +53,13 @@ define([ '/common/common-constants.js', '/common/common-feedback.js', '/common/outer/local-store.js', + '/common/outer/cache-store.js', '/customize/application_config.js', '/common/test.js', '/common/userObject.js', ], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel, _SecureIframe, _Messaging, _Notifier, _Hash, _Util, _Realtime, - _Constants, _Feedback, _LocalStore, _AppConfig, _Test, _UserObject) { + _Constants, _Feedback, _LocalStore, _Cache, _AppConfig, _Test, _UserObject) { CpNfOuter = _CpNfOuter; Cryptpad = _Cryptpad; Crypto = Utils.Crypto = _Crypto; @@ -73,6 +74,7 @@ define([ Utils.Constants = _Constants; Utils.Feedback = _Feedback; Utils.LocalStore = _LocalStore; + Utils.Cache = _Cache; Utils.UserObject = _UserObject; AppConfig = _AppConfig; Test = _Test; @@ -1635,6 +1637,11 @@ define([ }); }; + sframeChan.on('Q_CORRUPTED_CACHE', function (cb) { + Utils.Cache.clearChannel(secret.channel, cb); + Cryptpad.onCorruptedCache(secret.channel); + }); + sframeChan.on('Q_CREATE_PAD', function (data, cb) { if (!isNewFile || rtStarted) { return; } // Create a new hash From e37cf4b59cbdefc3009f8e82062ae4d7defef3a1 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 5 Nov 2020 16:50:20 +0100 Subject: [PATCH 010/201] lint compliance --- www/common/outer/async-store.js | 2 +- www/common/outer/cache-store.js | 4 ++-- www/common/sframe-app-framework.js | 2 +- www/common/sframe-chainpad-netflux-inner.js | 2 +- www/common/sframe-common-outer.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 56e1cadfd..687326139 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1598,7 +1598,7 @@ define([ onCacheStart: function () { postMessage(clientId, "PAD_CACHE"); }, - onCacheReady: function (info) { + onCacheReady: function () { postMessage(clientId, "PAD_CACHE_READY"); }, onReady: function (pad) { diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index 687259ddd..56c78a5be 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -53,8 +53,8 @@ define([ }); }; - S.clearChannel = function (id, cb) { - cb = Util.once(Util.mkAsync(cb || function () {})); + S.clearChannel = function (id, _cb) { + cb = Util.once(Util.mkAsync(_cb || function () {})); cache.removeItem(id, cb); }; diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 59dd51520..6353fb2f8 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -497,7 +497,7 @@ define([ cpNfInner.metadataMgr.updateMetadata(metadata); newContent = normalize(newContent); if (!unsyncMode) { - contentUpdate(newContent, function () { return function () {}}); + contentUpdate(newContent, function () { return function () {}; }); } UI.removeLoadingScreen(emitResize); diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index e70a5874e..1bdcc260f 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -94,7 +94,7 @@ define([ evInfiniteSpinner.fire(); }, 2000); - sframeChan.on('EV_RT_CACHE_READY', function (isPermanent) { + sframeChan.on('EV_RT_CACHE_READY', function () { onCacheReady({realtime: chainpad}); }); sframeChan.on('EV_RT_DISCONNECT', function (isPermanent) { diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 8fd37db4b..4c448e822 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1637,7 +1637,7 @@ define([ }); }; - sframeChan.on('Q_CORRUPTED_CACHE', function (cb) { + sframeChan.on('Q_CORRUPTED_CACHE', function (data, cb) { Utils.Cache.clearChannel(secret.channel, cb); Cryptpad.onCorruptedCache(secret.channel); }); From 517492f7d36cf22d3fef158045f15dee87781e4e Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 5 Nov 2020 17:30:35 +0100 Subject: [PATCH 011/201] Fix loading errors --- customize.dist/loading.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 733b3fff0..344c61942 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -317,9 +317,12 @@ button.primary:hover{ var c = types.indexOf(data.type); if (c < current) { return console.error(data); } try { - document.querySelector('.cp-loading-spinner-container').style.display = 'none'; - document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); - document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); + var el1 = document.querySelector('.cp-loading-spinner-container'); + if (el1) { el1.style.display = 'none'; } + var el2 = document.querySelector('.cp-loading-progress-list'); + if (el2) { el2.innerHTML = makeList(data); } + var el3 = document.querySelector('.cp-loading-progress-container'); + if (el3) { el3.innerHTML = makeBar(data); } } catch (e) { console.error(e); } }; window.CryptPad_updateLoadingProgress = updateLoadingProgress; From bc2387256f61b63a4ac85104e34da42e56c78c1d Mon Sep 17 00:00:00 2001 From: Christian Pietsch Date: Thu, 5 Nov 2020 19:23:08 +0100 Subject: [PATCH 012/201] Set reasonable value for $PWD /home/cryptpad/cryptpad/cryptpad seems one cryptpad too many, and it does not match the sample value of WorkingDirectory above --- docs/cryptpad.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cryptpad.service b/docs/cryptpad.service index eee8b2af5..43d8652f6 100644 --- a/docs/cryptpad.service +++ b/docs/cryptpad.service @@ -17,7 +17,7 @@ SyslogIdentifier=cryptpad User=cryptpad Group=cryptpad # modify to match your working directory -Environment='PWD="/home/cryptpad/cryptpad/cryptpad"' +Environment='PWD="/home/cryptpad/cryptpad"' # systemd sets the open file limit to 4000 unless you override it # cryptpad stores its data with the filesystem, so you should increase this to match the value of `ulimit -n` From 7fe1ec14149b153e1d5a6b68f9ad21c4e84f6e62 Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 5 Nov 2020 19:52:00 +0100 Subject: [PATCH 013/201] Translated using Weblate (Finnish) Currently translated at 100.0% (1372 of 1372 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fi/ --- www/common/translations/messages.fi.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.fi.json b/www/common/translations/messages.fi.json index fe4ee1f64..ac5ace154 100644 --- a/www/common/translations/messages.fi.json +++ b/www/common/translations/messages.fi.json @@ -1459,5 +1459,14 @@ "history_cantRestore": "Palauttaminen epäonnistui. Yhteytesi on katkennut.", "history_close": "Sulje", "history_restore": "Palauta", - "share_bar": "Luo linkki" + "share_bar": "Luo linkki", + "error_unhelpfulScriptError": "Skriptivirhe: Lisätietoja selaimen kehittäjäkonsolissa", + "tag_edit": "Muokkaa", + "tag_add": "Lisää", + "loading_state_5": "Uudelleenrakenna asiakirja", + "loading_state_4": "Lataa Teams", + "loading_state_3": "Lataa jaetut kansiot", + "loading_state_2": "Päivitä sisältö", + "loading_state_1": "Lataa Drive", + "loading_state_0": "Rakenna käyttöliittymä" } From 681d2caca7cab9380fc0d4aadfcc46224db210f0 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 6 Nov 2020 19:19:18 +0530 Subject: [PATCH 014/201] minimal patch for iOS firefox --- www/common/boot2.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/www/common/boot2.js b/www/common/boot2.js index 30f776a25..87bba4a43 100644 --- a/www/common/boot2.js +++ b/www/common/boot2.js @@ -37,6 +37,42 @@ define([ window.alert("CryptPad needs localStorage to work. Try changing your cookie permissions, or using a different browser"); }; + + var getLogElement = function () { + var logger = document.querySelector('#cp-logger'); + if (logger) { return logger; } + logger = document.createElement('div'); + logger.setAttribute('id', 'cp-logger'); + document.body.appendChild(logger); + var css = function(){/* #cp-logger { display: none; } */}.toString().slice(14, -3); + var style = document.createElement('style'); + style.type = 'text/css'; + style.appendChild(document.createTextNode(css)); + document.head.appendChild(style); + return logger; + }; + + + var logToDom = function () { + var pre = document.createElement('pre'); + pre.innerText = 'x'; + getLogElement().appendChild(pre); + }; + + if (window.Proxy) { + var c = console; + window.console = new Proxy(c, { + get: function (o, k) { + if (k !== 'error') { return o[k]; } + return function () { + var args = Array.prototype.slice.call(arguments); + c.error.apply(null, args); + logToDom(); + }; + }, + }); + } + window.onerror = function (e) { if (/requirejs\.org/.test(e)) { console.log(); From 760356c4ffc56a3c622e4a4ac4b7a73f74132a83 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 6 Nov 2020 19:28:36 +0530 Subject: [PATCH 015/201] patch loading.js --- customize.dist/loading.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index edb66ef0a..1db126fa5 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -312,15 +312,24 @@ button.primary:hover{ return bar; }; + var hideSpinner = function () { + try { + document.querySelector('.cp-loading-spinner-container').style.display = 'none'; + document.querySelector('.cp-loading-spinner-container').setAttribute('style', 'display:none;'); + } catch (err) { return; } + }; + var hasErrored = false; var updateLoadingProgress = function (data) { if (!built || !data) { return; } var c = types.indexOf(data.type); if (c < current) { return console.error(data); } try { - document.querySelector('.cp-loading-spinner-container').style.display = 'none'; - document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); - document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); + hideSpinner(); + var list = document.querySelector('.cp-loading-progress-list'); + list && (list.innerHTML = makeList(data)); + var container = document.querySelector('.cp-loading-progress-container'); + container && (container.innerHTML = makeBar(data)); } catch (e) { if (!hasErrored) { console.error(e); } } @@ -329,6 +338,7 @@ button.primary:hover{ window.CryptPad_loadingError = function (err) { if (!built) { return; } + console.error(err); hasErrored = true; var err2; if (err === 'Script error.') { @@ -339,10 +349,12 @@ button.primary:hover{ var node = document.querySelector('.cp-loading-progress'); if (!node) { return; } if (node.parentNode) { node.parentNode.removeChild(node); } - document.querySelector('.cp-loading-spinner-container').setAttribute('style', 'display:none;'); + hideSpinner(); document.querySelector('#cp-loading-message').setAttribute('style', 'display:block;'); document.querySelector('#cp-loading-message').innerText = err2 || err; - } catch (e) { console.error(e); } + } catch (e) { + console.error(e); + } }; return function () { built = true; From 46703d627b36144972e201e0f27d857e61a10d94 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 6 Nov 2020 19:30:57 +0530 Subject: [PATCH 016/201] . --- www/common/cryptpad-common.js | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index ca65ce77b..c4be384b8 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2056,6 +2056,8 @@ define([ }; var userHash; + console.error("pewpew"); + //console.error('pewpew'); Nthen(function (waitFor) { if (AppConfig.beforeLogin) { @@ -2108,6 +2110,7 @@ define([ // FIXME Backward compatibility if (sessionStorage.newPadFileData) { + /* common.fromFileData = JSON.parse(sessionStorage.newPadFileData); var _parsed1 = Hash.parsePadUrl(common.fromFileData.href); var _parsed2 = Hash.parsePadUrl(window.location.href); @@ -2115,6 +2118,7 @@ define([ if (_parsed1.type !== _parsed2.type) { delete common.fromFileData; } } delete sessionStorage.newPadFileData; + */ } if (sessionStorage.newPadPath) { @@ -2134,11 +2138,12 @@ define([ var postMsg, worker; var noWorker = AppConfig.disableWorkers || false; var noSharedWorker = false; - if (localStorage.CryptPad_noWorkers) { + if (localStorage.CryptPad_noWorkers || true) { noWorker = localStorage.CryptPad_noWorkers === '1'; - console.error('WebWorker/SharedWorker state forced to ' + !noWorker); + //console.error('WebWorker/SharedWorker state forced to ' + !noWorker); } Nthen(function (waitFor2) { + return; if (Worker) { var w = waitFor2(); try { @@ -2161,6 +2166,7 @@ define([ w(); } } + return; if (typeof(SharedWorker) !== "undefined") { try { new SharedWorker(''); @@ -2170,6 +2176,18 @@ define([ } } }).nThen(function (waitFor2) { + // Use the async store in the main thread if workers are not available + require(['/common/outer/noworker.js'], waitFor2(function (NoWorker) { + NoWorker.onMessage(function (data) { + msgEv.fire({data: data}); + }); + postMsg = function (d) { setTimeout(function () { NoWorker.query(d); }); }; + NoWorker.create(); + })); + return; + + + if (!noWorker && !noSharedWorker && typeof(SharedWorker) !== "undefined") { worker = new SharedWorker('/common/outer/sharedworker.js?' + urlArgs); worker.onerror = function (e) { @@ -2337,6 +2355,8 @@ define([ } if (parsedNew.hashData) { oldHref = newHref; } }; + + /* // Listen for login/logout in other tabs window.addEventListener('storage', function (e) { if (e.key !== Constants.userHashKey) { return; } @@ -2347,7 +2367,7 @@ define([ } else if (o && !n) { LocalStore.logout(); } - }); + });*/ LocalStore.onLogout(function () { console.log('onLogout: disconnect'); postMessage("DISCONNECT"); @@ -2367,6 +2387,7 @@ define([ common.mergeAnonDrive(waitFor()); } }).nThen(function (waitFor) { + return; if (AppConfig.afterLogin) { AppConfig.afterLogin(common, waitFor()); } From 5946b10d0bd97488544ceb69c5b1782e2ed879a6 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 6 Nov 2020 15:00:58 +0100 Subject: [PATCH 017/201] No page reload when the cache is corrupted onReady --- lib/hk-util.js | 6 ++++-- www/common/outer/cache-store.js | 4 +++- www/common/sframe-app-framework.js | 21 +++++++++++++-------- www/common/sframe-common-outer.js | 3 +-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/hk-util.js b/lib/hk-util.js index 4fa2140f9..20e0ce5d4 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -440,9 +440,11 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, _cb) => { // If our lastKnownHash is older than the 2nd to last checkpoint, // only send the last 2 checkpoints and ignore "lkh" - if (lkh && index.cpIndex.length >= 2 && lkh < index.cpIndex[0].offset) { + // XXX XXX this is probably wrong! ChainPad may not accept checkpoints that are not connected to root + // XXX We probably need to send an EUNKNOWN here so that the client can recreate a new chainpad + /*if (lkh && index.cpIndex.length >= 2 && lkh < index.cpIndex[0].offset) { return void cb(null, index.cpIndex[0].offset); - } + }*/ // Otherwise use our lastKnownHash cb(null, lkh); diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index 56c78a5be..ff2a40e93 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -55,7 +55,9 @@ define([ S.clearChannel = function (id, _cb) { cb = Util.once(Util.mkAsync(_cb || function () {})); - cache.removeItem(id, cb); + cache.removeItem(id, function () { + cb(); + }); }; S.clear = function () { diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 6353fb2f8..50565cc0b 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -467,9 +467,14 @@ define([ }); }; - var onCorruptedCache = function (cb) { - var sframeChan = common.getSframeChannel(); - sframeChan.event("Q_CORRUPTED_CACHE", cb); + var noCache = false; // Prevent reload loops + var onCorruptedCache = function () { + if (noCache) { + // XXX translation key + return UI.errorLoadingScreen("Reload loop: empty chainpad for a non-empty channel"); + } + noCache = true; + sframeChan.event("Q_CORRUPTED_CACHE"); }; var onCacheReady = function () { stateChange(STATE.DISCONNECTED); @@ -547,17 +552,17 @@ define([ Feedback.send("NON_EMPTY_NEWDOC"); // The cache may be wrong, empty it and reload after. waitFor.abort(); - UI.errorLoadingScreen("MAYBE CORRUPTED CACHE... RELOADING"); // XXX - onCorruptedCache(function () { - setTimeout(function () { common.gotoURL(); }, 1000); - }); + onCorruptedCache(); return; } - console.log('updating title'); title.updateTitle(title.defaultTitle); evOnDefaultContentNeeded.fire(); } }).nThen(function () { + // We have a valid chainpad, reenable cache fix in case with reconnect with + // a corrupted cache + noCache = false; + stateChange(STATE.READY); firstConnection = false; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 4c448e822..a53171150 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1637,8 +1637,7 @@ define([ }); }; - sframeChan.on('Q_CORRUPTED_CACHE', function (data, cb) { - Utils.Cache.clearChannel(secret.channel, cb); + sframeChan.on('EV_CORRUPTED_CACHE', function () { Cryptpad.onCorruptedCache(secret.channel); }); From 11abd6170b7affc1b3217e27a1df31e1ccf17e24 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 6 Nov 2020 15:30:56 +0100 Subject: [PATCH 018/201] Use indexeddb cache for blobs --- www/common/media-tag.js | 70 +++++++++++++++++++++++++-------- www/common/outer/cache-store.js | 22 +++++++++++ 2 files changed, 75 insertions(+), 17 deletions(-) diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 27683c54e..1d7cd0c61 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -1,8 +1,5 @@ -(function(name, definition) { - if (typeof module !== 'undefined') { module.exports = definition(); } - else if (typeof define === 'function' && typeof define.amd === 'object') { define(definition); } - else { this[name] = definition(); } -}('MediaTag', function() { +(function (window) { +var factory = function (Cache) { var cache; var cypherChunkLength = 131088; @@ -133,20 +130,44 @@ cb = function () {}; }; - var xhr = new XMLHttpRequest(); - xhr.open('GET', src, true); - xhr.responseType = 'arraybuffer'; - - xhr.onerror = function () { return void cb("XHR_ERROR"); }; - xhr.onload = function () { - // Error? - if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); } + var _src = src.replace(/(\/)*$/, ''); // Remove trailing slashes + var idx = _src.lastIndexOf('/'); + var cacheKey = _src.slice(idx+1); + if (!/^[a-f0-9]{48}$/.test(cacheKey)) { cacheKey = undefined; } + + var fetch = function () { + var xhr = new XMLHttpRequest(); + xhr.open('GET', src, true); + xhr.responseType = 'arraybuffer'; + + xhr.onerror = function () { return void cb("XHR_ERROR"); }; + xhr.onload = function () { + // Error? + if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); } + + var arrayBuffer = xhr.response; + if (arrayBuffer) { + var u8 = new Uint8Array(arrayBuffer); + if (cacheKey) { + return void Cache.setBlobCache(cacheKey, u8, function (err) { + cb(null, u8); + }); + } + cb(null, u8); + } + }; - var arrayBuffer = xhr.response; - if (arrayBuffer) { cb(null, new Uint8Array(arrayBuffer)); } + xhr.send(null); }; - xhr.send(null); + if (!cacheKey) { return void fetch(); } + + Cache.getBlobCache(cacheKey, function (err, u8) { + if (err || !u8) { return void fetch(); } + console.error('using cache', cacheKey); + cb(null, u8); + }); + }; // Decryption tools @@ -469,4 +490,19 @@ }; return init; -})); +}; + + if (typeof(module) !== 'undefined' && module.exports) { + module.exports = factory( + require("./outer/cache-store.js") + ); + } else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) { + define([ + '/common/outer/cache-store.js', + ], function (Cache) { + return factory(Cache); + }); + } else { + // unsupported initialization + } +}(typeof(window) !== 'undefined'? window : {})); diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index ff2a40e93..fe3b541b2 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -8,6 +8,28 @@ define([ name: "cp_cache" }); + S.getBlobCache = function (id, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); + cache.getItem(id, function (err, obj) { + if (err || !obj || !obj.c) { + return void cb(err || 'EINVAL'); + } + cb(null, obj.c); + obj.t = +new Date(); + cache.setItem(id, obj); + }); + }; + S.setBlobCache = function (id, u8, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); + if (!u8) { return void cb('EINVAL'); } + cache.setItem(id, { + c: u8, + t: (+new Date()) // 't' represent the "lastAccess" of this cache (get or set) + }, function (err) { + cb(err); + }); + }; + // id: channel ID or blob ID // returns array of messages S.getChannelCache = function (id, cb) { From 3468bb33a2b7bfa0740072bdd640045f35846619 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 12 Nov 2020 11:22:46 +0100 Subject: [PATCH 019/201] Test notification prompt --- www/common/sframe-common-outer.js | 9 +++++++-- www/common/toolbar.js | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index ad128db33..d17924cf0 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -569,6 +569,7 @@ define([ isPresent: parsed.hashData && parsed.hashData.present, isEmbed: parsed.hashData && parsed.hashData.embed, isHistoryVersion: parsed.hashData && parsed.hashData.versionHash, + notifications: Notification && Notification.permission === "granted", accounts: { donateURL: Cryptpad.donateURL, upgradeURL: Cryptpad.upgradeURL @@ -1569,9 +1570,13 @@ define([ }); }); - if (cfg.messaging) { - Notifier.getPermission(); + sframeChan.on('Q_ASK_NOTIFICATION', function (data, cb) { + Notification.requestPermission(function (s) { + cb(s === "granted"); + }); + }); + if (cfg.messaging) { sframeChan.on('Q_CHAT_OPENPADCHAT', function (data, cb) { Cryptpad.universal.execCommand({ type: 'messenger', diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 78e4e91f3..709d659b5 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -990,6 +990,28 @@ MessengerUI, Messages) { h('div.cp-notifications-empty', Messages.notifications_empty) ]); var pads_options = [div]; + + var metadataMgr = config.metadataMgr; + var privateData = metadataMgr.getPrivateData(); + if (!privateData.notifications) { + Messages.allowNotifications = "Allow notifications"; // XXX + var allowNotif = h('div.cp-notifications-gotoapp', h('p', Messages.allowNotifications)); + pads_options.unshift(h("hr")); + pads_options.unshift(allowNotif); + var $allow = $(allowNotif).click(function () { + Common.getSframeChannel().event('Q_ASK_NOTIFICATION', null, function (granted) { + if (!granted) { return; } + $(allowNotif).remove(); + }); + }); + metadataMgr.onChange(function () { + var privateData = metadataMgr.getPrivateData(); + if (!privateData.notifications) { return; } + $allow.remove(); + }); + } + + if (Common.isLoggedIn()) { pads_options.unshift(h("hr")); pads_options.unshift(openNotifsApp); From ca065e557e04ae7efce30351dda1a410e9b55db3 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 12 Nov 2020 11:29:05 +0100 Subject: [PATCH 020/201] Improve notification prompt --- www/common/toolbar.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 709d659b5..5d453f061 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -999,16 +999,18 @@ MessengerUI, Messages) { pads_options.unshift(h("hr")); pads_options.unshift(allowNotif); var $allow = $(allowNotif).click(function () { - Common.getSframeChannel().event('Q_ASK_NOTIFICATION', null, function (granted) { - if (!granted) { return; } + Common.getSframeChannel().event('Q_ASK_NOTIFICATION', null, function (e, allow) { + if (!allow) { return; } $(allowNotif).remove(); }); }); - metadataMgr.onChange(function () { + var onChange = function () { var privateData = metadataMgr.getPrivateData(); if (!privateData.notifications) { return; } $allow.remove(); - }); + metadataMgr.off('change', onChange); + }; + metadataMgr.onChange(onChange); } From 59f427f1b530fbafe8fc2b43d2e3ca9c7b75fc78 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 12 Nov 2020 17:20:48 +0100 Subject: [PATCH 021/201] Don't notify for old mailbox messages --- www/common/outer/mailbox.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/common/outer/mailbox.js b/www/common/outer/mailbox.js index 11c912a44..803d8bc93 100644 --- a/www/common/outer/mailbox.js +++ b/www/common/outer/mailbox.js @@ -297,6 +297,7 @@ proxy.mailboxes = { msg: msg, hash: hash }; + var notify = box.ready; Handlers.add(ctx, box, message, function (dismissed, toDismiss) { if (toDismiss) { // List of other messages to remove dismiss(ctx, toDismiss, '', function () { @@ -314,8 +315,7 @@ proxy.mailboxes = { } box.content[hash] = msg; showMessage(ctx, type, message, null, function (obj) { - if (!box.ready) { return; } - if (!obj || !obj.msg) { return; } + if (!obj || !obj.msg || !notify) { return; } Notify.system(undefined, obj.msg); }); }); From b155f7b2911aefdddd327b81152978d42babdebc Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 17 Nov 2020 16:09:47 +0530 Subject: [PATCH 022/201] increase child-process timeout and improve error handling --- lib/workers/db-worker.js | 4 ++-- lib/workers/index.js | 4 +++- www/common/common-util.js | 13 +++++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/workers/db-worker.js b/lib/workers/db-worker.js index 42c75fdca..0b4c4d03d 100644 --- a/lib/workers/db-worker.js +++ b/lib/workers/db-worker.js @@ -568,7 +568,7 @@ process.on('message', function (data) { const cb = function (err, value) { process.send({ - error: err, + error: Util.serializeError(err), txid: data.txid, pid: data.pid, value: value, @@ -577,7 +577,7 @@ process.on('message', function (data) { if (!ready) { return void init(data.config, function (err) { - if (err) { return void cb(err); } + if (err) { return void cb(Util.serializeError(err)); } ready = true; cb(); }); diff --git a/lib/workers/index.js b/lib/workers/index.js index 6e9f57e88..522339812 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -9,6 +9,7 @@ const PID = process.pid; const DB_PATH = 'lib/workers/db-worker'; const MAX_JOBS = 16; +const DEFAULT_QUERY_TIMEOUT = 60000 * 15; // increased from three to fifteen minutes because queries for very large files were taking as long as seven minutes Workers.initialize = function (Env, config, _cb) { var cb = Util.once(Util.mkAsync(_cb)); @@ -113,6 +114,7 @@ Workers.initialize = function (Env, config, _cb) { const txid = guid(); var cb = Util.once(Util.mkAsync(Util.both(_cb, function (err /*, value */) { if (err !== 'TIMEOUT') { return; } + Log.debug("WORKER_TIMEOUT_CAUSE", msg); // in the event of a timeout the user will receive an error // but the state used to resend a query in the event of a worker crash // won't be cleared. This also leaks a slot that could be used to keep @@ -132,7 +134,7 @@ Workers.initialize = function (Env, config, _cb) { state.tasks[txid] = msg; // default to timing out affter 180s if no explicit timeout is passed - var timeout = typeof(opt.timeout) !== 'undefined'? opt.timeout: 180000; + var timeout = typeof(opt.timeout) !== 'undefined'? opt.timeout: DEFAULT_QUERY_TIMEOUT; response.expect(txid, cb, timeout); state.worker.send(msg); }; diff --git a/www/common/common-util.js b/www/common/common-util.js index 41797e7c8..dfc6e12d7 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -30,6 +30,15 @@ return JSON.parse(JSON.stringify(o)); }; + Util.serializeError = function (err) { + if (!(err instanceof Error)) { return err; } + var ser = {}; + Object.getOwnPropertyNames(err).forEach(function (key) { + ser[key] = err[key]; + }); + return ser; + }; + Util.tryParse = function (s) { try { return JSON.parse(s); } catch (e) { return;} }; @@ -113,13 +122,13 @@ var handle = function (id, args) { var fn = pending[id]; if (typeof(fn) !== 'function') { - errorHandler("MISSING_CALLBACK", { + return void errorHandler("MISSING_CALLBACK", { id: id, args: args, }); } try { - pending[id].apply(null, Array.isArray(args)? args : [args]); + fn.apply(null, Array.isArray(args)? args : [args]); } catch (err) { errorHandler('HANDLER_ERROR', { error: err, From 0e0c0fd6556d082d265456959e80ee9077fa790b Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 17 Nov 2020 17:45:02 +0530 Subject: [PATCH 023/201] guard against typeError for unsupported notification APIs --- www/common/notify.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/www/common/notify.js b/www/common/notify.js index a3c8cd2b8..81dee662d 100644 --- a/www/common/notify.js +++ b/www/common/notify.js @@ -16,6 +16,8 @@ define(['/api/config'], function (ApiConfig) { var getPermission = Module.getPermission = function (f) { f = f || function () {}; + // "Notification.requestPermission is not a function" on Firefox 68.11.0esr + if (!Notification || typeof(Notification.requestPermission) !== 'function') { return void f(false); } Notification.requestPermission(function (permission) { if (permission === "granted") { f(true); } else { f(false); } From ebc394c4b31f81866162f662e8487d4c14554c24 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 17 Nov 2020 18:09:34 +0530 Subject: [PATCH 024/201] expose defaultStorageLimit via /api/config --- server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server.js b/server.js index 60247f47a..eb2787475 100644 --- a/server.js +++ b/server.js @@ -202,6 +202,7 @@ var serveConfig = (function () { adminKeys: Env.admins, inactiveTime: Env.inactiveTime, supportMailbox: Env.supportMailbox, + defaultStorageLimit: Env.defaultStorageLimit, maxUploadSize: Env.maxUploadSize, premiumUploadSize: Env.premiumUploadSize, }, null, '\t'), From c9ce6da49628166a05518a076c0c459623aeeaec Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 17 Nov 2020 19:05:05 +0530 Subject: [PATCH 025/201] . --- www/common/cryptpad-common.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index c4be384b8..cb312892b 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2056,8 +2056,6 @@ define([ }; var userHash; - console.error("pewpew"); - //console.error('pewpew'); Nthen(function (waitFor) { if (AppConfig.beforeLogin) { @@ -2355,8 +2353,6 @@ define([ } if (parsedNew.hashData) { oldHref = newHref; } }; - - /* // Listen for login/logout in other tabs window.addEventListener('storage', function (e) { if (e.key !== Constants.userHashKey) { return; } @@ -2367,7 +2363,7 @@ define([ } else if (o && !n) { LocalStore.logout(); } - });*/ + }); LocalStore.onLogout(function () { console.log('onLogout: disconnect'); postMessage("DISCONNECT"); @@ -2387,7 +2383,6 @@ define([ common.mergeAnonDrive(waitFor()); } }).nThen(function (waitFor) { - return; if (AppConfig.afterLogin) { AppConfig.afterLogin(common, waitFor()); } From b37fd224b0381d4f5ab9f3a61b3302130c058d26 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 17 Nov 2020 19:19:55 +0530 Subject: [PATCH 026/201] continue bisecting --- www/common/cryptpad-common.js | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index cb312892b..2d7440087 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2056,6 +2056,7 @@ define([ }; var userHash; + console.error('without this error statement Firefox on iOS throws a script error...'); Nthen(function (waitFor) { if (AppConfig.beforeLogin) { @@ -2108,7 +2109,6 @@ define([ // FIXME Backward compatibility if (sessionStorage.newPadFileData) { - /* common.fromFileData = JSON.parse(sessionStorage.newPadFileData); var _parsed1 = Hash.parsePadUrl(common.fromFileData.href); var _parsed2 = Hash.parsePadUrl(window.location.href); @@ -2116,7 +2116,6 @@ define([ if (_parsed1.type !== _parsed2.type) { delete common.fromFileData; } } delete sessionStorage.newPadFileData; - */ } if (sessionStorage.newPadPath) { @@ -2136,12 +2135,11 @@ define([ var postMsg, worker; var noWorker = AppConfig.disableWorkers || false; var noSharedWorker = false; - if (localStorage.CryptPad_noWorkers || true) { + if (localStorage.CryptPad_noWorkers) { noWorker = localStorage.CryptPad_noWorkers === '1'; - //console.error('WebWorker/SharedWorker state forced to ' + !noWorker); + console.error('WebWorker/SharedWorker state forced to ' + !noWorker); } Nthen(function (waitFor2) { - return; if (Worker) { var w = waitFor2(); try { @@ -2164,7 +2162,6 @@ define([ w(); } } - return; if (typeof(SharedWorker) !== "undefined") { try { new SharedWorker(''); @@ -2174,18 +2171,6 @@ define([ } } }).nThen(function (waitFor2) { - // Use the async store in the main thread if workers are not available - require(['/common/outer/noworker.js'], waitFor2(function (NoWorker) { - NoWorker.onMessage(function (data) { - msgEv.fire({data: data}); - }); - postMsg = function (d) { setTimeout(function () { NoWorker.query(d); }); }; - NoWorker.create(); - })); - return; - - - if (!noWorker && !noSharedWorker && typeof(SharedWorker) !== "undefined") { worker = new SharedWorker('/common/outer/sharedworker.js?' + urlArgs); worker.onerror = function (e) { From 30e1c448d39a6c294f0911d26d5d5d59d5c388d9 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 17 Nov 2020 19:35:09 +0530 Subject: [PATCH 027/201] cleanup and lint compliance --- customize.dist/loading.js | 15 ++++++++++----- www/common/boot2.js | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 1db126fa5..e3f5c24f7 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -315,10 +315,17 @@ button.primary:hover{ var hideSpinner = function () { try { document.querySelector('.cp-loading-spinner-container').style.display = 'none'; - document.querySelector('.cp-loading-spinner-container').setAttribute('style', 'display:none;'); } catch (err) { return; } }; + var getList = function () { + return document.querySelector('.cp-loading-progress-list') || {}; + }; + + var getProgressBar = function () { + return document.querySelector('.cp-loading-progress-container') || {}; + }; + var hasErrored = false; var updateLoadingProgress = function (data) { if (!built || !data) { return; } @@ -326,10 +333,8 @@ button.primary:hover{ if (c < current) { return console.error(data); } try { hideSpinner(); - var list = document.querySelector('.cp-loading-progress-list'); - list && (list.innerHTML = makeList(data)); - var container = document.querySelector('.cp-loading-progress-container'); - container && (container.innerHTML = makeBar(data)); + getList().innerHTML = makeList(data); + getProgressBar().innerHTML = makeBar(data); } catch (e) { if (!hasErrored) { console.error(e); } } diff --git a/www/common/boot2.js b/www/common/boot2.js index 87bba4a43..0d879b32f 100644 --- a/www/common/boot2.js +++ b/www/common/boot2.js @@ -61,7 +61,7 @@ define([ if (window.Proxy) { var c = console; - window.console = new Proxy(c, { + window.console = new window.Proxy(c, { get: function (o, k) { if (k !== 'error') { return o[k]; } return function () { From 713516f7517ff135ebc53ee64166db78124eaf9b Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 17 Nov 2020 19:52:37 +0530 Subject: [PATCH 028/201] keep bisecting to narrow down on the Firefox iOS error --- customize.dist/loading.js | 27 +++++--------------------- www/common/boot2.js | 36 ----------------------------------- www/common/cryptpad-common.js | 22 +++++++++++++++++++-- 3 files changed, 25 insertions(+), 60 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index e3f5c24f7..edb66ef0a 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -312,29 +312,15 @@ button.primary:hover{ return bar; }; - var hideSpinner = function () { - try { - document.querySelector('.cp-loading-spinner-container').style.display = 'none'; - } catch (err) { return; } - }; - - var getList = function () { - return document.querySelector('.cp-loading-progress-list') || {}; - }; - - var getProgressBar = function () { - return document.querySelector('.cp-loading-progress-container') || {}; - }; - var hasErrored = false; var updateLoadingProgress = function (data) { if (!built || !data) { return; } var c = types.indexOf(data.type); if (c < current) { return console.error(data); } try { - hideSpinner(); - getList().innerHTML = makeList(data); - getProgressBar().innerHTML = makeBar(data); + document.querySelector('.cp-loading-spinner-container').style.display = 'none'; + document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); + document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); } catch (e) { if (!hasErrored) { console.error(e); } } @@ -343,7 +329,6 @@ button.primary:hover{ window.CryptPad_loadingError = function (err) { if (!built) { return; } - console.error(err); hasErrored = true; var err2; if (err === 'Script error.') { @@ -354,12 +339,10 @@ button.primary:hover{ var node = document.querySelector('.cp-loading-progress'); if (!node) { return; } if (node.parentNode) { node.parentNode.removeChild(node); } - hideSpinner(); + document.querySelector('.cp-loading-spinner-container').setAttribute('style', 'display:none;'); document.querySelector('#cp-loading-message').setAttribute('style', 'display:block;'); document.querySelector('#cp-loading-message').innerText = err2 || err; - } catch (e) { - console.error(e); - } + } catch (e) { console.error(e); } }; return function () { built = true; diff --git a/www/common/boot2.js b/www/common/boot2.js index 0d879b32f..30f776a25 100644 --- a/www/common/boot2.js +++ b/www/common/boot2.js @@ -37,42 +37,6 @@ define([ window.alert("CryptPad needs localStorage to work. Try changing your cookie permissions, or using a different browser"); }; - - var getLogElement = function () { - var logger = document.querySelector('#cp-logger'); - if (logger) { return logger; } - logger = document.createElement('div'); - logger.setAttribute('id', 'cp-logger'); - document.body.appendChild(logger); - var css = function(){/* #cp-logger { display: none; } */}.toString().slice(14, -3); - var style = document.createElement('style'); - style.type = 'text/css'; - style.appendChild(document.createTextNode(css)); - document.head.appendChild(style); - return logger; - }; - - - var logToDom = function () { - var pre = document.createElement('pre'); - pre.innerText = 'x'; - getLogElement().appendChild(pre); - }; - - if (window.Proxy) { - var c = console; - window.console = new window.Proxy(c, { - get: function (o, k) { - if (k !== 'error') { return o[k]; } - return function () { - var args = Array.prototype.slice.call(arguments); - c.error.apply(null, args); - logToDom(); - }; - }, - }); - } - window.onerror = function (e) { if (/requirejs\.org/.test(e)) { console.log(); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 2d7440087..2aaa50796 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2056,9 +2056,27 @@ define([ }; var userHash; - console.error('without this error statement Firefox on iOS throws a script error...'); - Nthen(function (waitFor) { + Nthen(function () { + var getLogElement = function () { + var logger = document.createElement('div'); + logger.setAttribute('id', 'cp-logger'); + document.body.appendChild(logger); + var css = function(){/* #cp-logger { display: none; } */}.toString().slice(14, -3); + var style = document.createElement('style'); + style.type = 'text/css'; + style.appendChild(document.createTextNode(css)); + document.head.appendChild(style); + return logger; + }; + var logToDom = function () { + var pre = document.createElement('pre'); + pre.innerText = 'x'; + getLogElement(); + getLogElement().appendChild(pre); + }; + logToDom(); + }).nThen(function (waitFor) { if (AppConfig.beforeLogin) { AppConfig.beforeLogin(LocalStore.isLoggedIn(), waitFor()); } From 700ea4c282fecd92781fcd5c659e53c054e9dc2d Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 17 Nov 2020 20:25:12 +0530 Subject: [PATCH 029/201] minimal patch and an ominous warning comment --- www/common/cryptpad-common.js | 42 +++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 2aaa50796..9a9807e7e 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2057,26 +2057,34 @@ define([ var userHash; - Nthen(function () { - var getLogElement = function () { - var logger = document.createElement('div'); - logger.setAttribute('id', 'cp-logger'); - document.body.appendChild(logger); - var css = function(){/* #cp-logger { display: none; } */}.toString().slice(14, -3); + (function iOSFirefoxFix () { +/* + For some bizarre reason Firefox on iOS throws an error during the + loading process unless we call this function. Drawing these elements + to the DOM presumably causes the JS engine to wait just a little bit longer + until some APIs we need are ready. This occurs despite all this code being + run after the usual dom-ready events. This fix was discovered while trying + to log the error messages to the DOM because it's extremely difficult + to debug Firefox iOS in the usual ways. In summary, computers are terrible. +*/ + try { var style = document.createElement('style'); - style.type = 'text/css'; - style.appendChild(document.createTextNode(css)); + style.type = 'text/css'; + style.appendChild(document.createTextNode('#cp-logger { display: none; }')); document.head.appendChild(style); - return logger; - }; - var logToDom = function () { + + var logger = document.createElement('div'); + logger.setAttribute('id', 'cp-logger'); + document.body.appendChild(logger); + var pre = document.createElement('pre'); - pre.innerText = 'x'; - getLogElement(); - getLogElement().appendChild(pre); - }; - logToDom(); - }).nThen(function (waitFor) { + pre.innerText = 'x'; + pre.style.display = 'none'; + logger.appendChild(pre); + } catch (err) { console.error(err); } + }()); + + Nthen(function (waitFor) { if (AppConfig.beforeLogin) { AppConfig.beforeLogin(LocalStore.isLoggedIn(), waitFor()); } From 7bbb46c2ef43f73d25e92ad117163111f48747ed Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 17 Nov 2020 16:02:06 +0100 Subject: [PATCH 030/201] Trim history prompt --- www/common/common-ui-elements.js | 39 +++++++++++++++++++ www/common/onlyoffice/inner.js | 10 +++++ www/common/sframe-app-framework.js | 2 + www/common/sframe-common.js | 60 ++++++++++++++++++++++++++++++ www/drive/inner.js | 4 ++ www/poll/inner.js | 2 + 6 files changed, 117 insertions(+) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index e977f1f49..3cb044231 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2684,6 +2684,45 @@ define([ }; + Messages.history_trimPrompt = "This document's history is very large ({0}) and it may impact the loading time. You can delete the unnecessary history."; + UIElements.displayTrimHistoryPrompt = function (common, data) { + var mb = Util.bytesToMegabytes(data.size); + var text = Messages._getKey('history_trimPrompt', [ + Messages._getKey('formattedMB', [mb]) + ]); + var yes = h('button.cp-corner-primary', [ + h('span.fa.fa-trash-o'), + Messages.trimHistory_button + ]); + var no = h('button.cp-corner-cancel', Messages.crowdfunding_popup_no); // Not now + var actions = h('div', [no, yes]); + + var dontShowAgain = function () { + var until = (+new Date()) + (7 * 24 * 3600 * 1000); // 7 days from now + until = (+new Date()) + 30000; // XXX 30s from now + if (data.drive) { + common.setAttribute(['drive', 'trim'], until); + return; + } + common.setPadAttribute('trim', until); + }; + + var modal = UI.cornerPopup(text, actions, '', {}); + + $(yes).click(function () { + modal.delete(); + if (data.drive) { + common.openURL('/settings/#drive'); + return; + } + common.getSframeChannel().event('EV_PROPERTIES_OPEN'); + }); + $(no).click(function () { + dontShowAgain(); + modal.delete(); + }); + }; + UIElements.displayFriendRequestModal = function (common, data) { var msg = data.content.msg; var userData = msg.content.user; diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 28d06dd32..c9efb1eda 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1310,6 +1310,16 @@ define([ if (APP.migrate && !readOnly) { onMigrateRdy.fire(); } + + // Check if history can/should be trimmed + var cp = getLastCp(); + if (cp && cp.file && cp.hash) { + var channels = [{ + channel: content.channel, + lastKnownHash: cp.hash + }]; + common.checkTrimHistory(channels); + } } } }; diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 3097cf6d2..5f28373c2 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -551,6 +551,8 @@ define([ Thumb.initPadThumbnails(common, options.thumbnail); } } + + common.checkTrimHistory(); }); }; var onConnectionChange = function (info) { diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 3eb86ff93..4198a649a 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -264,6 +264,66 @@ define([ return teamChatChannel; }; + // When opening a pad, if were an owner check the history size and prompt for trimming if + // necessary + funcs.checkTrimHistory = function (channels, isDrive) { + channels = channels || []; + var priv = ctx.metadataMgr.getPrivateData(); + + var limit = 100 * 1024 * 1024; // 100MB + limit = 100 * 1024; // XXX 100KB + + var owned; + nThen(function (w) { + if (isDrive) { + funcs.getAttribute(['drive', 'trim'], w(function (err, val) { + if (err || typeof(val) !== "number") { return; } + if (val < (+new Date())) { return; } + w.abort(); + })); + return; + } + funcs.getPadAttribute('trim', w(function (err, val) { + if (err || typeof(val) !== "number") { return; } + if (val < (+new Date())) { return; } + w.abort(); + })); + }).nThen(function (w) { + // Check ownership + // DRIVE + if (isDrive) { + if (!priv.isDriveOwned) { return void w.abort(); } + return; + } + // PAD + channels.push({ channel: priv.channel }); + funcs.getPadMetadata({ + channel: priv.channel + }, w(function (md) { + if (md && md.error) { return void w.abort(); } + var owners = md.owners; + owned = funcs.isOwned(owners); + if (!owned) { return void w.abort(); } + })); + }).nThen(function () { + // We're an owner: check the history size + var history = funcs.makeUniversal('history'); + history.execCommand('GET_HISTORY_SIZE', { + account: isDrive, + pad: !isDrive, + channels: channels, + teamId: typeof(owned) === "number" && owned + }, function (obj) { + if (obj && obj.error) { return; } // can't get history size: abort + var bytes = obj.size; + if (!bytes || typeof(bytes) !== "number") { return; } // no history: abort + if (bytes < limit) { return; } + obj.drive = isDrive; + UIElements.displayTrimHistoryPrompt(funcs, obj); + }); + }); + }; + var cursorChannel; // common-ui-elements needs to be able to get the cursor channel to put it in metadata when // importing a template diff --git a/www/drive/inner.js b/www/drive/inner.js index f85819083..2be9e06e6 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -310,6 +310,10 @@ define([ onReconnect(); }); common.onLogout(function () { setEditable(false); }); + + // Check if our drive history needs to be trimmed + common.checkTrimHistory(null, true); + }); }; main(); diff --git a/www/poll/inner.js b/www/poll/inner.js index b36466a13..bd40bc678 100644 --- a/www/poll/inner.js +++ b/www/poll/inner.js @@ -1087,6 +1087,8 @@ define([ common.openPadChat(function () {}); UI.removeLoadingScreen(); + + common.checkTrimHistory(); }; var onError = function (info) { From 62930f65b4910681d876285a03ebe98e263f86bb Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 18 Nov 2020 12:41:47 +0530 Subject: [PATCH 031/201] fix the iOS Firefox fix --- www/common/cryptpad-common.js | 1 - 1 file changed, 1 deletion(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 9a9807e7e..9f88e85a9 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2079,7 +2079,6 @@ define([ var pre = document.createElement('pre'); pre.innerText = 'x'; - pre.style.display = 'none'; logger.appendChild(pre); } catch (err) { console.error(err); } }()); From e1850088461490437b46d100d7d4baa035d632b8 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 18 Nov 2020 18:26:10 +0530 Subject: [PATCH 032/201] complete uploads in child processes also fix a nasty race condition for unowned file uploads --- lib/commands/upload.js | 22 ++++------------------ lib/storage/blob.js | 39 +++++++++++++++++++-------------------- lib/workers/db-worker.js | 33 +++++++++++++++++++++++++++++++++ lib/workers/index.js | 9 +++++++++ 4 files changed, 65 insertions(+), 38 deletions(-) diff --git a/lib/commands/upload.js b/lib/commands/upload.js index 7286caa93..6dc0aa911 100644 --- a/lib/commands/upload.js +++ b/lib/commands/upload.js @@ -75,21 +75,9 @@ Upload.upload = function (Env, safeKey, chunk, cb) { Env.blobStore.upload(safeKey, chunk, cb); }; -var reportStatus = function (Env, label, safeKey, err, id) { - var data = { - safeKey: safeKey, - err: err && err.message || err, - id: id, - }; - var method = err? 'error': 'info'; - Env.Log[method](label, data); -}; - Upload.complete = function (Env, safeKey, arg, cb) { - Env.blobStore.complete(safeKey, arg, function (err, id) { - reportStatus(Env, 'UPLOAD_COMPLETE', safeKey, err, id); - cb(err, id); - }); + Env.blobStore.closeBlobstage(safeKey); + Env.completeUpload(safeKey, arg, false, cb); }; Upload.cancel = function (Env, safeKey, arg, cb) { @@ -97,9 +85,7 @@ Upload.cancel = function (Env, safeKey, arg, cb) { }; Upload.complete_owned = function (Env, safeKey, arg, cb) { - Env.blobStore.completeOwned(safeKey, arg, function (err, id) { - reportStatus(Env, 'UPLOAD_COMPLETE_OWNED', safeKey, err, id); - cb(err, id); - }); + Env.blobStore.closeBlobstage(safeKey); + Env.completeUpload(safeKey, arg, true, cb); }; diff --git a/lib/storage/blob.js b/lib/storage/blob.js index dfbc802b4..044eeaeaa 100644 --- a/lib/storage/blob.js +++ b/lib/storage/blob.js @@ -139,6 +139,15 @@ var upload = function (Env, safeKey, content, cb) { } }; +var closeBlobstage = function (Env, safeKey) { + var session = Env.getSession(safeKey); + if (!(session && session.blobstage && typeof(session.blobstage.close) === 'function')) { + return; + } + session.blobstage.close(); + delete session.blobstage; +}; + // upload_cancel var upload_cancel = function (Env, safeKey, fileSize, cb) { var session = Env.getSession(safeKey); @@ -159,27 +168,22 @@ var upload_cancel = function (Env, safeKey, fileSize, cb) { // upload_complete var upload_complete = function (Env, safeKey, id, cb) { - var session = Env.getSession(safeKey); - - if (session.blobstage && session.blobstage.close) { - session.blobstage.close(); - delete session.blobstage; - } + closeBlobstage(Env, safeKey); var oldPath = makeStagePath(Env, safeKey); var newPath = makeBlobPath(Env, id); nThen(function (w) { // make sure the path to your final location exists - Fse.mkdirp(Path.dirname(newPath), function (e) { + Fse.mkdirp(Path.dirname(newPath), w(function (e) { if (e) { w.abort(); return void cb('RENAME_ERR'); } - }); + })); }).nThen(function (w) { // make sure there's not already something in that exact location - isFile(newPath, function (e, yes) { + isFile(newPath, w(function (e, yes) { if (e) { w.abort(); return void cb(e); @@ -188,8 +192,8 @@ var upload_complete = function (Env, safeKey, id, cb) { w.abort(); return void cb('RENAME_ERR'); } - cb(void 0, newPath, id); - }); + cb(void 0, id); + })); }).nThen(function () { // finally, move the old file to the new path // FIXME we could just move and handle the EEXISTS instead of the above block @@ -217,15 +221,7 @@ var tryId = function (path, cb) { // owned_upload_complete var owned_upload_complete = function (Env, safeKey, id, cb) { - var session = Env.getSession(safeKey); - - // the file has already been uploaded to the staging area - // close the pending writestream - if (session.blobstage && session.blobstage.close) { - session.blobstage.close(); - delete session.blobstage; - } - + closeBlobstage(Env, safeKey); if (!isValidId(id)) { return void cb('EINVAL_ID'); } @@ -582,6 +578,9 @@ BlobStore.create = function (config, _cb) { }, }, + closeBlobstage: function (safeKey) { + closeBlobstage(Env, safeKey); + }, complete: function (safeKey, id, _cb) { var cb = Util.once(Util.mkAsync(_cb)); if (!isValidSafeKey(safeKey)) { return void cb('INVALID_SAFEKEY'); } diff --git a/lib/workers/db-worker.js b/lib/workers/db-worker.js index 0b4c4d03d..9d5abf386 100644 --- a/lib/workers/db-worker.js +++ b/lib/workers/db-worker.js @@ -457,6 +457,38 @@ const evictInactive = function (data, cb) { Eviction(Env, cb); }; +var reportStatus = function (Env, label, safeKey, err, id) { + var data = { + safeKey: safeKey, + err: err && err.message || err, + id: id, + }; + var method = err? 'error': 'info'; + Env.Log[method](label, data); +}; + +const completeUpload = function (data, cb) { + if (!data) { return void cb('INVALID_ARGS'); } + var owned = data.owned; + var safeKey = data.safeKey; + var arg = data.arg; + + var method; + var label; + if (owned) { + method = 'completeOwned'; + label = 'UPLOAD_COMPLETE_OWNED'; + } else { + method = 'complete'; + label = 'UPLOAD_COMPLETE'; + } + + Env.blobStore[method](safeKey, arg, function (err, id) { + reportStatus(Env, label, safeKey, err, id); + cb(err, id); + }); +}; + const COMMANDS = { COMPUTE_INDEX: computeIndex, COMPUTE_METADATA: computeMetadata, @@ -471,6 +503,7 @@ const COMMANDS = { RUN_TASKS: runTasks, WRITE_TASK: writeTask, EVICT_INACTIVE: evictInactive, + COMPLETE_UPLOAD: completeUpload, }; COMMANDS.INLINE = function (data, cb) { diff --git a/lib/workers/index.js b/lib/workers/index.js index 522339812..b5704c68b 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -424,6 +424,15 @@ Workers.initialize = function (Env, config, _cb) { }, cb); }; + Env.completeUpload = function (safeKey, arg, owned, cb) { + sendCommand({ + command: "COMPLETE_UPLOAD", + owned: owned, + safeKey: safeKey, + arg: arg, + }, cb); + }; + cb(void 0); }); }; From d60bdbfba11c4f859be9287014b1cb41f4269600 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 18 Nov 2020 15:11:34 +0100 Subject: [PATCH 033/201] Don't show XDR encoding errors --- customize.dist/loading.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index edb66ef0a..b0d66981c 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -329,6 +329,12 @@ button.primary:hover{ window.CryptPad_loadingError = function (err) { if (!built) { return; } + + if (err === 'Error: XDR encoding failure') { + console.warn(err); + return; + } + hasErrored = true; var err2; if (err === 'Script error.') { From bd4b44476dbe387987fcb5bab052be79639a37a4 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 18 Nov 2020 16:49:40 +0100 Subject: [PATCH 034/201] Archive and restore UI in the admin panel --- lib/commands/admin-rpc.js | 17 +++-- lib/storage/file.js | 4 +- www/admin/app-admin.less | 10 +++ www/admin/inner.js | 137 ++++++++++++++++++++++++++++++++++++++ www/common/common-hash.js | 4 +- 5 files changed, 164 insertions(+), 8 deletions(-) diff --git a/lib/commands/admin-rpc.js b/lib/commands/admin-rpc.js index d7f22825d..26e574307 100644 --- a/lib/commands/admin-rpc.js +++ b/lib/commands/admin-rpc.js @@ -167,12 +167,19 @@ var archiveDocument = function (Env, Server, cb, data) { // Env.blobStore.archive.proof(userSafeKey, blobId, cb) }; -var restoreArchivedDocument = function (Env, Server, cb) { - // Env.msgStore.restoreArchivedChannel(channelName, cb) - // Env.blobStore.restore.blob(blobId, cb) - // Env.blobStore.restore.proof(userSafekey, blobId, cb) +var restoreArchivedDocument = function (Env, Server, cb, data) { + var id = Array.isArray(data) && data[1]; + if (typeof(id) !== 'string' || id.length < 32) { return void cb("EINVAL"); } - cb("NOT_IMPLEMENTED"); + switch (id.length) { + case 32: + return void Env.msgStore.restoreArchivedChannel(id, cb); + case 48: + // Env.blobStore.restore.proof(userSafekey, id, cb) // XXX .... + return void Env.blobStore.restore.blob(id, cb); + default: + return void cb("INVALID_ID_LENGTH"); + } }; // CryptPad_AsyncStore.rpc.send('ADMIN', ['CLEAR_CACHED_CHANNEL_INDEX', documentID], console.log) diff --git a/lib/storage/file.js b/lib/storage/file.js index b1ccfde0f..03bbaa8b4 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -681,9 +681,9 @@ var unarchiveChannel = function (env, channelName, cb) { // restore the metadata log Fse.move(archiveMetadataPath, metadataPath, w(function (err) { // if there's nothing to move, you're done. - if (err && err.code === 'ENOENT') { + /*if (err && err.code === 'ENOENT') { return CB(); - } + }*/ // XXX make sure removing this part won't break anything // call back with an error if something goes wrong if (err) { w.abort(); diff --git a/www/admin/app-admin.less b/www/admin/app-admin.less index 5112fd9e2..651e9dddd 100644 --- a/www/admin/app-admin.less +++ b/www/admin/app-admin.less @@ -97,5 +97,15 @@ color: @colortheme_logo-2; } } + + input.cp-admin-inval { + border-color: red !important; + } + .cp-admin-nopassword { + .cp-admin-pw { + display: none !important; + } + } + } diff --git a/www/admin/inner.js b/www/admin/inner.js index db5afca54..d9bebc097 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -43,6 +43,8 @@ define([ 'general': [ 'cp-admin-flush-cache', 'cp-admin-update-limit', + 'cp-admin-archive', + 'cp-admin-unarchive', // 'cp-admin-registration', ], 'quota': [ @@ -107,6 +109,141 @@ define([ }); return $div; }; + Messages.admin_archiveTitle = "Archive documents"; // XXX + Messages.admin_archiveHint = "Make a document unavailable without deleting it permanently. It will be placed in an 'archive' directory and deleted after a few days (configurable in the server configuration file)."; // XXX + Messages.admin_archiveButton = "Archive"; + + Messages.admin_unarchiveTitle = "Restore archived documents"; // XXX + Messages.admin_unarchiveHint = "Restore a document that has previously been archived"; + Messages.admin_unarchiveButton = "Restore"; + + Messages.admin_archiveInput = "Document URL"; + Messages.admin_archiveInput2 = "Document password"; + Messages.admin_archiveInval = "Invalid document"; + Messages.restoredFromServer = "Pad restored"; + + var archiveForm = function (archive, $div, $button) { + var label = h('label', { for: 'cp-admin-archive' }, Messages.admin_archiveInput); + var input = h('input#cp-admin-archive', { + type: 'text' + }); + + var label2 = h('label.cp-admin-pw', { + for: 'cp-admin-archive-pw' + }, Messages.admin_archiveInput2); + var input2 = UI.passwordInput({ + id: 'cp-admin-archive-pw', + placeholder: Messages.login_password + }); + var $pw = $(input2); + $pw.addClass('cp-admin-pw'); + var $pwInput = $pw.find('input'); + + + $button.before(h('div.cp-admin-setlimit-form', [ + label, + input, + label2, + input2 + ])); + + $div.addClass('cp-admin-nopassword'); + + var parsed; + var $input = $(input).on('keypress change paste', function () { + setTimeout(function () { + $input.removeClass('cp-admin-inval'); + var val = $input.val().trim(); + if (!val) { + $div.toggleClass('cp-admin-nopassword', true); + return; + } + + parsed = Hash.isValidHref(val); + $pwInput.val(''); + + if (!parsed || !parsed.hashData) { + $div.toggleClass('cp-admin-nopassword', true); + return void $input.addClass('cp-admin-inval'); + } + + var pw = parsed.hashData.version !== 3 && parsed.hashData.password; + $div.toggleClass('cp-admin-nopassword', !pw); + }); + }); + $pw.on('keypress change', function () { + setTimeout(function () { + $pw.toggleClass('cp-admin-inval', !$pwInput.val()); + }); + }); + + var clicked = false; + $button.click(function () { + if (!parsed || !parsed.hashData) { + UI.warn(Messages.admin_archiveInval); + return; + } + var pw = parsed.hashData.password ? $pwInput.val() : undefined; + var channel; + if (parsed.hashData.version === 3) { + channel = parsed.hashData.channel; + } else { + var secret = Hash.getSecrets(parsed.type, parsed.hash, pw); + channel = secret && secret.channel; + } + + if (!channel) { + UI.warn(Messages.admin_archiveInval); + return; + } + + if (clicked) { return; } + clicked = true; + + nThen(function (waitFor) { + if (!archive) { return; } + common.getFileSize(channel, waitFor(function (err, size) { + if (!err && size === 0) { + clicked = false; + waitFor.abort(); + return void UI.warn(Messages.admin_archiveInval); + } + })); + }).nThen(function () { + sFrameChan.query('Q_ADMIN_RPC', { + cmd: archive ? 'ARCHIVE_DOCUMENT' : 'RESTORE_ARCHIVED_DOCUMENT', + data: channel + }, function (err, obj) { + var e = err || (obj && obj.error); + clicked = false; + if (e) { + UI.warn(Messages.error); + console.error(e); + return; + } + UI.log(archive ? Messages.deletedFromServer : Messages.restoredFromServer); + $input.val(''); + $pwInput.val(''); + }); + }); + }); + }; + + create['archive'] = function () { + var key = 'archive'; + var $div = makeBlock(key, true); + var $button = $div.find('button'); + archiveForm(true, $div, $button); + return $div; + }; + create['unarchive'] = function () { + var key = 'unarchive'; + var $div = makeBlock(key, true); + var $button = $div.find('button'); + archiveForm(false, $div, $button); + return $div; + }; + create['registration'] = function () { var key = 'registration'; var $div = makeBlock(key, true); diff --git a/www/common/common-hash.js b/www/common/common-hash.js index 199c2cb54..4f7290b81 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -464,6 +464,8 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) }; if (!/^https*:\/\//.test(href)) { + // If it doesn't start with http(s), it should be a relative href + if (!/^\//.test(href)) { return ret; } idx = href.indexOf('/#'); ret.type = href.slice(1, idx); if (idx === -1) { return ret; } @@ -661,7 +663,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) if (parsed.hashData.key && !/^[a-zA-Z0-9+-/=]+$/.test(parsed.hashData.key)) { return; } } } - return true; + return parsed; }; Hash.decodeDataOptions = function (opts) { From faa84bcbd71a645c0e90b8cd36268edc61f850d3 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 19 Nov 2020 12:16:02 +0530 Subject: [PATCH 035/201] describe arguments for 'completeUpload' RPC --- lib/workers/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/workers/index.js b/lib/workers/index.js index b5704c68b..fe868e250 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -427,9 +427,9 @@ Workers.initialize = function (Env, config, _cb) { Env.completeUpload = function (safeKey, arg, owned, cb) { sendCommand({ command: "COMPLETE_UPLOAD", - owned: owned, - safeKey: safeKey, - arg: arg, + owned: owned, // Boolean + safeKey: safeKey, // String (public key) + arg: arg, // String (file id) }, cb); }; From 9bbc3acf3ab688617d9ac3f17fd4a146835e19ae Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 18 Nov 2020 15:11:34 +0100 Subject: [PATCH 036/201] Don't show XDR encoding errors --- customize.dist/loading.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index edb66ef0a..b0d66981c 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -329,6 +329,12 @@ button.primary:hover{ window.CryptPad_loadingError = function (err) { if (!built) { return; } + + if (err === 'Error: XDR encoding failure') { + console.warn(err); + return; + } + hasErrored = true; var err2; if (err === 'Script error.') { From 1aa790e02d7b730bb62c20318947054744973158 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 18 Nov 2020 15:11:34 +0100 Subject: [PATCH 037/201] Don't show XDR encoding errors --- customize.dist/loading.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index edb66ef0a..b0d66981c 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -329,6 +329,12 @@ button.primary:hover{ window.CryptPad_loadingError = function (err) { if (!built) { return; } + + if (err === 'Error: XDR encoding failure') { + console.warn(err); + return; + } + hasErrored = true; var err2; if (err === 'Script error.') { From 733d8071fc69fa7bcaf20d39470cae849e4f16d4 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 19 Nov 2020 13:29:48 +0530 Subject: [PATCH 038/201] display document ids in the properties modal --- www/common/inner/properties.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/www/common/inner/properties.js b/www/common/inner/properties.js index 58f10e738..143c32d81 100644 --- a/www/common/inner/properties.js +++ b/www/common/inner/properties.js @@ -12,6 +12,8 @@ define([ Messages, nThen) { var Properties = {}; + Messages.documentID = Messages.documentID || 'Document identifier'; // XXX + var getPadProperties = function (Env, data, opts, _cb) { var cb = Util.once(Util.mkAsync(_cb)); var common = Env.common; @@ -19,6 +21,13 @@ define([ var $d = $('
'); if (!data) { return void cb(void 0, $d); } + if (data.channel) { + $('
- + diff --git a/www/file/inner.js b/www/file/inner.js index b2aef53ed..8b3f12682 100644 --- a/www/file/inner.js +++ b/www/file/inner.js @@ -8,6 +8,7 @@ define([ '/common/common-util.js', '/common/common-hash.js', '/common/common-interface.js', + '/common/hyperscript.js', '/customize/messages.js', '/file/file-crypto.js', @@ -29,6 +30,7 @@ define([ Util, Hash, UI, + h, Messages, FileCrypto, MediaTag) @@ -47,8 +49,6 @@ define([ var $dlform = $('#cp-app-file-download-form'); var $dlview = $('#cp-app-file-download-view'); var $label = $form.find('label'); - var $dllabel = $dlform.find('label span'); - var $progress = $('#cp-app-file-dlprogress'); var $bar = $('.cp-toolbar-container'); var $body = $('body'); @@ -88,142 +88,172 @@ define([ var toolbar = APP.toolbar = Toolbar.create(configTb); if (!uploadMode) { - var hexFileName = secret.channel; - var src = fileHost + Hash.getBlobPathFromHex(hexFileName); - var key = secret.keys && secret.keys.cryptKey; - var cryptKey = Nacl.util.encodeBase64(key); - - FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) { - if (e) { - if (e === 'XHR_ERROR') { - return void UI.errorLoadingScreen(Messages.download_resourceNotAvailable, false, function () { - common.gotoURL('/file/'); - }); + (function () { + Messages.download = "Download"; // XXX + Messages.decrypt = "Decrypt"; // XXX + + var progress = h('div.cp-app-file-progress'); + var progressTxt = h('span.cp-app-file-progress-txt'); + var $progress = $(progress); + var $progressTxt = $(progressTxt); + var downloadEl = h('span.cp-app-file-progress-dl', Messages.download); + var decryptEl = h('span.cp-app-file-progress-dc', Messages.decrypt); + var progressContainer = h('div.cp-app-file-progress-container', [ + downloadEl, + decryptEl, + progress + ]); + + var hexFileName = secret.channel; + var src = fileHost + Hash.getBlobPathFromHex(hexFileName); + var key = secret.keys && secret.keys.cryptKey; + var cryptKey = Nacl.util.encodeBase64(key); + + FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) { + if (e) { + if (e === 'XHR_ERROR') { + return void UI.errorLoadingScreen(Messages.download_resourceNotAvailable, false, function () { + common.gotoURL('/file/'); + }); + } + return void console.error(e); } - return void console.error(e); - } - - // Add pad attributes when the file is saved in the drive - Title.onTitleChange(function () { - var owners = metadata.owners; - if (owners) { common.setPadAttribute('owners', owners); } - common.setPadAttribute('fileType', metadata.type); - }); - $(document).on('cpPadStored', function () { - var owners = metadata.owners; - if (owners) { common.setPadAttribute('owners', owners); } - common.setPadAttribute('fileType', metadata.type); - }); - - // Save to the drive or update the acces time - var title = document.title = metadata.name; - Title.updateTitle(title || Title.defaultTitle); - - var owners = metadata.owners; - if (owners) { - common.setPadAttribute('owners', owners); - } - if (metadata.type) { - common.setPadAttribute('fileType', metadata.type); - } - - toolbar.addElement(['pageTitle'], { - pageTitle: title, - title: Title.getTitleConfig(), - }); - toolbar.$drawer.append(common.createButton('forget', true)); - toolbar.$drawer.append(common.createButton('properties', true)); - if (common.isLoggedIn()) { - toolbar.$drawer.append(common.createButton('hashtag', true)); - } - toolbar.$file.show(); - - var displayFile = function (ev, sizeMb, CB) { - var called_back; - var cb = function (e) { - if (called_back) { return; } - called_back = true; - if (CB) { CB(e); } - }; - var $mt = $dlview.find('media-tag'); - $mt.attr('src', src); - $mt.attr('data-crypto-key', 'cryptpad:'+cryptKey); + // Add pad attributes when the file is saved in the drive + Title.onTitleChange(function () { + var owners = metadata.owners; + if (owners) { common.setPadAttribute('owners', owners); } + common.setPadAttribute('fileType', metadata.type); + }); + $(document).on('cpPadStored', function () { + var owners = metadata.owners; + if (owners) { common.setPadAttribute('owners', owners); } + common.setPadAttribute('fileType', metadata.type); + }); - var rightsideDisplayed = false; + // Save to the drive or update the acces time + var title = document.title = metadata.name; + Title.updateTitle(title || Title.defaultTitle); - MediaTag($mt[0]).on('complete', function (decrypted) { - $dlview.show(); - $dlform.hide(); - var $dlButton = $dlview.find('media-tag button'); - if (ev) { $dlButton.click(); } + var owners = metadata.owners; + if (owners) { + common.setPadAttribute('owners', owners); + } + if (metadata.type) { + common.setPadAttribute('fileType', metadata.type); + } - if (!rightsideDisplayed) { - toolbar.$drawer - .append(common.createButton('export', true, {}, function () { - saveAs(decrypted.content, decrypted.metadata.name); - })); - rightsideDisplayed = true; - } + toolbar.addElement(['pageTitle'], { + pageTitle: title, + title: Title.getTitleConfig(), + }); + toolbar.$drawer.append(common.createButton('forget', true)); + toolbar.$drawer.append(common.createButton('properties', true)); + if (common.isLoggedIn()) { + toolbar.$drawer.append(common.createButton('hashtag', true)); + } + toolbar.$file.show(); + + var displayFile = function (ev, sizeMb, CB) { + var called_back; + var cb = function (e) { + if (called_back) { return; } + called_back = true; + if (CB) { CB(e); } + }; + + var $mt = $dlview.find('media-tag'); + $mt.attr('src', src); + $mt.attr('data-crypto-key', 'cryptpad:'+cryptKey); + + var rightsideDisplayed = false; + + MediaTag($mt[0]).on('complete', function (decrypted) { + $dlview.show(); + $dlform.hide(); + var $dlButton = $dlview.find('media-tag button'); + if (ev) { $dlButton.click(); } + + if (!rightsideDisplayed) { + toolbar.$drawer + .append(common.createButton('export', true, {}, function () { + saveAs(decrypted.content, decrypted.metadata.name); + })); + rightsideDisplayed = true; + } + + // make pdfs big + var toolbarHeight = $('#cp-toolbar').height(); + var $another_iframe = $('media-tag iframe').css({ + 'height': 'calc(100vh - ' + toolbarHeight + 'px)', + 'width': '100vw', + 'position': 'absolute', + 'bottom': 0, + 'left': 0, + 'border': 0 + }); - // make pdfs big - var toolbarHeight = $('#cp-toolbar').height(); - var $another_iframe = $('media-tag iframe').css({ - 'height': 'calc(100vh - ' + toolbarHeight + 'px)', - 'width': '100vw', - 'position': 'absolute', - 'bottom': 0, - 'left': 0, - 'border': 0 + if ($another_iframe.length) { + $another_iframe.load(function () { + cb(); + }); + } else { + cb(); + } + }).on('progress', function (data) { + if (data.progress > 75) { return; } + var p = data.progress +'%'; + $progress.width(p); + $progressTxt.text(Math.floor(data.progress) + '%'); + }).on('error', function (err) { + console.error(err); }); + }; - if ($another_iframe.length) { - $another_iframe.load(function () { - cb(); + // XXX Update "download_button" key to use "download" first and "decrypt" second + var todoBigFile = function (sizeMb) { + $dlform.show(); + UI.removeLoadingScreen(); + var button = h('button.btn.btn-primary', { + title: Messages.download_button + }, Messages.download_button); + $dlform.append([ + h('h2', Util.fixHTML(metadata.name)), + h('div.cp-button-container', [ + button, + progressTxt + ]), + ]); + + // don't display the size if you don't know it. + if (typeof(sizeMb) === 'number') { + $dlform.find('h2').append(' - ' + + Messages._getKey('formattedMB', [sizeMb])); + } + var decrypting = false; + var onClick = function (ev) { + if (decrypting) { return; } + decrypting = true; + $(button).prop('disabled', 'disabled'); + $dlform.append(progressContainer); + displayFile(ev, sizeMb, function (err) { + $appContainer.css('background-color', + common.getAppConfig().appBackgroundColor); + if (err) { UI.alert(err); } }); - } else { - cb(); + }; + if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); } + $(button).click(onClick); + }; + common.getFileSize(hexFileName, function (e, data) { + if (e) { + return void UI.errorLoadingScreen(e); } - }).on('progress', function (data) { - var p = data.progress +'%'; - $progress.width(p); - }).on('error', function (err) { - console.error(err); + var size = Util.bytesToMegabytes(data); + return void todoBigFile(size); }); - }; - - var todoBigFile = function (sizeMb) { - $dlform.show(); - UI.removeLoadingScreen(); - $dllabel.append($('
')); - $dllabel.append(Util.fixHTML(metadata.name)); - - // don't display the size if you don't know it. - if (typeof(sizeM) === 'number') { - $dllabel.append($('
')); - $dllabel.append(Messages._getKey('formattedMB', [sizeMb])); - } - var decrypting = false; - var onClick = function (ev) { - if (decrypting) { return; } - decrypting = true; - displayFile(ev, sizeMb, function (err) { - $appContainer.css('background-color', - common.getAppConfig().appBackgroundColor); - if (err) { UI.alert(err); } - }); - }; - if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); } - $dlform.find('#cp-app-file-dlfile, #cp-app-file-dlprogress').click(onClick); - }; - common.getFileSize(hexFileName, function (e, data) { - if (e) { - return void UI.errorLoadingScreen(e); - } - var size = Util.bytesToMegabytes(data); - return void todoBigFile(size); }); - }); + })(); return; } From a29b98783aca432ed0aa7ac7ca0674b2edd3c43c Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 20 Nov 2020 16:38:18 +0100 Subject: [PATCH 048/201] Improve download table --- www/common/common-util.js | 6 ++++ www/common/drive-ui.js | 11 +++++++ www/common/make-backup.js | 45 +++++++++++++++++++++-------- www/common/sframe-common-file.js | 49 ++++++++++++++++++++------------ www/file/file-crypto.js | 10 +++++++ 5 files changed, 91 insertions(+), 30 deletions(-) diff --git a/www/common/common-util.js b/www/common/common-util.js index dfc6e12d7..435a72280 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -296,6 +296,12 @@ return void CB(void 0, new Uint8Array(xhr.response)); }; xhr.send(null); + + return { + cancel: function () { + if (xhr.abort) { xhr.abort(); } + } + }; }; Util.dataURIToBlob = function (dataURI) { diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 53e496ef4..c01bf0f54 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -4149,6 +4149,17 @@ define([ data.name = Util.fixFileName(folderName); data.folderName = Util.fixFileName(folderName) + '.zip'; + var uo = manager.user.userObject; + if (sfId && manager.folders[sfId]) { + uo = manager.folders[sfId].userObject; + } + if (uo.getFilesRecursively) { + data.list = uo.getFilesRecursively(folderElement).map(function (el) { + var d = uo.getFileData(el); + return d.channel; + }); + } + APP.FM.downloadFolder(data, function (err, obj) { console.log(err, obj); console.log('DONE'); diff --git a/www/common/make-backup.js b/www/common/make-backup.js index b718ae9c1..88602c8bb 100644 --- a/www/common/make-backup.js +++ b/www/common/make-backup.js @@ -53,9 +53,6 @@ define([ var _downloadFile = function (ctx, fData, cb, updateProgress) { var cancelled = false; - var cancel = function () { - cancelled = true; - }; var href = (fData.href && fData.href.indexOf('#') !== -1) ? fData.href : fData.roHref; var parsed = Hash.parsePadUrl(href); var hash = parsed.hash; @@ -63,10 +60,13 @@ define([ var secret = Hash.getSecrets('file', hash, fData.password); var src = (ctx.fileHost || '') + Hash.getBlobPathFromHex(secret.channel); var key = secret.keys && secret.keys.cryptKey; - Util.fetch(src, function (err, u8) { + + var fetchObj, decryptObj; + + fetchObj = Util.fetch(src, function (err, u8) { if (cancelled) { return; } if (err) { return void cb('E404'); } - FileCrypto.decrypt(u8, key, function (err, res) { + decryptObj = FileCrypto.decrypt(u8, key, function (err, res) { if (cancelled) { return; } if (err) { return void cb(err); } if (!res.content) { return void cb('EEMPTY'); } @@ -78,8 +78,25 @@ define([ content: res.content, download: dl }); - }, updateProgress && updateProgress.progress2); - }, updateProgress && updateProgress.progress); + }, function (data) { + if (cancelled) { return; } + if (updateProgress && updateProgress.progress2) { + updateProgress.progress2(data); + } + }); + }, function (data) { + if (cancelled) { return; } + if (updateProgress && updateProgress.progress) { + updateProgress.progress(data); + } + }); + + var cancel = function () { + cancelled = true; + if (fetchObj && fetchObj.cancel) { fetchObj.cancel(); } + if (decryptObj && decryptObj.cancel) { decryptObj.cancel(); } + }; + return { cancel: cancel }; @@ -162,10 +179,10 @@ define([ if (ctx.stop) { return; } if (to) { clearTimeout(to); } //setTimeout(g, 2000); - g(); - w(); ctx.done++; ctx.updateProgress('download', {max: ctx.max, current: ctx.done}); + g(); + w(); }; var error = function (err) { @@ -312,13 +329,14 @@ define([ delete ctx.zip; }; return { - stop: stop + stop: stop, + cancel: stop }; }; var _downloadFolder = function (ctx, data, cb, updateProgress) { - create(data, ctx.get, ctx.fileHost, function (blob, errors) { + return create(data, ctx.get, ctx.fileHost, function (blob, errors) { if (errors && errors.length) { console.error(errors); } // TODO show user errors var dl = function () { saveAs(blob, data.folderName); @@ -332,8 +350,11 @@ define([ if (typeof progress.current !== "number") { return; } updateProgress.folderProgress(progress.current / progress.max); } + else if (state === "compressing") { + updateProgress.folderProgress(2); + } else if (state === "done") { - updateProgress.folderProgress(1); + updateProgress.folderProgress(3); } }); }; diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index c9e2eeacd..8a5be3764 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -47,8 +47,9 @@ define([ return 'cp-fileupload-element-' + String(Math.random()).substring(2); }; + Messages.fileTableHeader = "Downloads and uploads"; // XXX var tableHeader = h('div.cp-fileupload-header', [ - h('div.cp-fileupload-header-title', h('span', Messages.fileuploadHeader || 'Uploaded files')), + h('div.cp-fileupload-header-title', h('span', Messages.fileTableHeader)), h('div.cp-fileupload-header-close', h('span.fa.fa-times')), ]); @@ -262,7 +263,8 @@ define([ // name $('').append($link).appendTo($tr); // size - $('').text(UIElements.prettySize(estimate)).appendTo($tr); + var size = estimate ? UIElements.prettySize(estimate) : ''; + $(h('td.cp-fileupload-size')).text(size).appendTo($tr); // progress $('', {'class': 'cp-fileupload-table-progress'}).append($progressContainer).appendTo($tr); // cancel @@ -590,12 +592,11 @@ define([ queue.next(); }; - /* var cancelled = function () { $row.find('.cp-fileupload-table-cancel').addClass('cancelled').html('').append(h('span.fa.fa-minus')); queue.inProgress = false; queue.next(); - };*/ + }; /** * Update progress in the download panel, for downloading a file @@ -627,8 +628,21 @@ define([ * As updateDLProgress but for folders * @param {number} progressValue Progression of download, between 0 and 1 */ + Messages.download_zip = "Building ZIP file..."; // XXX + Messages.download_zip_file = "File {0}/{1}"; // XXX var updateProgress = function (progressValue) { var text = Math.round(progressValue*100) + '%'; + if (Array.isArray(data.list)) { + text = Messages._getKey('download_zip_file', [Math.round(progressValue * data.list.length), data.list.length]); + } + if (progressValue === 2) { + text = Messages.download_zip; + progressValue = 1; + } + if (progressValue === 3) { + text = "100%"; + progressValue = 1; + } $pv.text(text); $pb.css({ width: (progressValue * 100) + '%' @@ -641,7 +655,8 @@ define([ get: common.getPad, sframeChan: sframeChan, }; - downloadFunction(ctx, data, function (err, obj) { + + var dl = downloadFunction(ctx, data, function (err, obj) { $link.prepend($('', {'class': 'fa fa-external-link'})) .attr('href', '#') .click(function (e) { @@ -657,19 +672,17 @@ define([ folderProgress: updateProgress, }); -/* - var $cancel = $('', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { - dl.cancel(); - $cancel.remove(); - $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); - cancelled(); - }); -*/ - - $row.find('.cp-fileupload-table-cancel') - .html('') - .append(h('span.fa.fa-minus')); - //.append($cancel); + var $cancel = $row.find('.cp-fileupload-table-cancel').html(''); + if (dl && dl.cancel) { + $('', { + 'class': 'cp-fileupload-table-cancel-button fa fa-times' + }).click(function () { + dl.cancel(); + $cancel.remove(); + $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); + cancelled(); + }).appendTo($cancel); + } }; File.downloadFile = function (fData, cb) { diff --git a/www/file/file-crypto.js b/www/file/file-crypto.js index a74439492..12453bb48 100644 --- a/www/file/file-crypto.js +++ b/www/file/file-crypto.js @@ -128,6 +128,11 @@ define([ metadata: undefined, }; + var cancelled = false; + var cancel = function () { + cancelled = true; + }; + var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength)); var metaChunk = Nacl.secretbox.open(metaBox, nonce, key); @@ -168,6 +173,7 @@ define([ var chunks = []; var again = function () { + if (cancelled) { return; } takeChunk(function (e, plaintext) { if (e) { return setTimeout(function () { @@ -188,6 +194,10 @@ define([ }; again(); + + return { + cancel: cancel + }; }; // metadata From 1fa8be7f28751349b0794bc2c5e1d86bf9ffe946 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 24 Nov 2020 10:29:38 +0100 Subject: [PATCH 049/201] LESS improvements --- customize.dist/src/less2/include/forms.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/customize.dist/src/less2/include/forms.less b/customize.dist/src/less2/include/forms.less index 0acbd49b4..737509130 100644 --- a/customize.dist/src/less2/include/forms.less +++ b/customize.dist/src/less2/include/forms.less @@ -2,6 +2,10 @@ @import (reference) "./variables.less"; .forms_main() { + --LessLoader_require: LessLoader_currentFile(); +} + +& { @alertify-fore: @colortheme_modal-fg; @alertify-btn-fg: @alertify-fore; @alertify-light-bg: fade(@alertify-fore, 25%); From 396eb4d263a3bf9cc69ed6f52df32a5cf19f9535 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 24 Nov 2020 16:38:31 +0100 Subject: [PATCH 050/201] Stop autodownloading big mediatags --- customize.dist/ckeditor-contents.css | 30 ++++ customize.dist/src/less2/include/forms.less | 8 + .../src/less2/include/markdown.less | 9 ++ server.js | 14 ++ www/common/diffMarked.js | 4 +- www/common/media-tag.js | 150 +++++++++++++++--- www/file/inner.js | 4 +- www/pad/inner.js | 5 +- 8 files changed, 197 insertions(+), 27 deletions(-) diff --git a/customize.dist/ckeditor-contents.css b/customize.dist/ckeditor-contents.css index 000162c00..a7939839d 100644 --- a/customize.dist/ckeditor-contents.css +++ b/customize.dist/ckeditor-contents.css @@ -213,3 +213,33 @@ media-tag * { width: 100%; height: 100%; } +media-tag button.btn { + background-color: #fff; + box-sizing: border-box; + outline: 0; + display: inline-flex; + align-items: center; + padding: 0 6px; + min-height: 36px; + line-height: 22px; + white-space: nowrap; + text-align: center; + text-transform: uppercase; + font-size: 14px; + text-decoration: none; + cursor: pointer; + border-radius: 0; + transition: none; + color: #3F4141; + border: 1px solid #3F4141; +} +media-tag button.btn:hover, media-tag button.btn:active, media-tag button.btn:focus { + background-color: #ccc; +} +media-tag button b { + margin-left: 5px; +} +media-tag button .fa { + display: inline; + margin-right: 5px; +} diff --git a/customize.dist/src/less2/include/forms.less b/customize.dist/src/less2/include/forms.less index 737509130..65eedf263 100644 --- a/customize.dist/src/less2/include/forms.less +++ b/customize.dist/src/less2/include/forms.less @@ -128,6 +128,14 @@ font-weight: bold; } + &.btn-default { + border-color: @cryptpad_text_col; + color: @cryptpad_text_col; + &:hover, &:active, &:focus { + background-color: #ccc; + } + } + &.danger, &.btn-danger { background-color: @colortheme_alertify-red; border-color: @colortheme_alertify-red-border; diff --git a/customize.dist/src/less2/include/markdown.less b/customize.dist/src/less2/include/markdown.less index 23eb056b0..d7fe13f43 100644 --- a/customize.dist/src/less2/include/markdown.less +++ b/customize.dist/src/less2/include/markdown.less @@ -94,6 +94,15 @@ height: 80vh; max-height: 90vh; } + button.btn-default { + display: inline-flex; + .fa { + margin-right: 5px; + } + b { + margin-left: 5px; + } + } } media-tag:empty { width: 100px; diff --git a/server.js b/server.js index eb2787475..3869af509 100644 --- a/server.js +++ b/server.js @@ -136,6 +136,20 @@ app.head(/^\/common\/feedback\.html/, function (req, res, next) { }); }()); +app.use('/blob', function (req, res, next) { + if (req.method === 'HEAD') { + Express.static(Path.join(__dirname, (config.blobPath || './blob')), { + setHeaders: function (res, path, stat) { + res.set('Access-Control-Allow-Origin', '*'); + res.set('Access-Control-Allow-Headers', 'Content-Length'); + res.set('Access-Control-Expose-Headers', 'Content-Length'); + } + })(req, res, next); + return; + } + next(); +}); + app.use(function (req, res, next) { if (req.method === 'OPTIONS' && /\/blob\//.test(req.url)) { res.setHeader('Access-Control-Allow-Origin', '*'); diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index 734551983..fd3fcafb3 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -668,7 +668,7 @@ define([ } return; } - MediaTag(el); + var mediaObject = MediaTag(el); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList') { @@ -676,7 +676,7 @@ define([ .map(function (el) { return el.outerHTML; }) .join(''); mediaMap[mutation.target.getAttribute('src')] = list_values; - observer.disconnect(); + if (mediaObject.complete) { observer.disconnect(); } } }); $mt.off('click dblclick preview'); diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 1ba9a1f53..4318ca8f6 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -63,7 +63,8 @@ ], pdf: {}, download: { - text: "Download" + text: "Save", + textDl: "Load attachment" }, Plugins: { /** @@ -114,8 +115,8 @@ }, download: function (metadata, url, content, cfg, cb) { var btn = document.createElement('button'); - btn.setAttribute('class', 'btn btn-success'); - btn.innerHTML = cfg.download.text + '
' + + btn.setAttribute('class', 'btn btn-default'); + btn.innerHTML = '' + cfg.download.text + '
' + (metadata.name ? '' + fixHTML(metadata.name) + '' : ''); btn.addEventListener('click', function () { saveFile(content, url, metadata.name); @@ -125,6 +126,92 @@ } }; + var makeProgressBar = function (cfg, mediaObject) { + // XXX CSP: we'll need to add style in cryptpad's less + var style = (function(){/* +.mediatag-progress-container { + position: relative; + border: 1px solid #0087FF; + background: white; + height: 25px; + display: inline-flex; + width: 200px; + align-items: center; + justify-content: center; + box-sizing: border-box; + vertical-align: top; +} +.mediatag-progress-bar { + position: absolute; + left: 0; + top: 0; + bottom: 0; + background: #0087FF; + width: 0%; +} +.mediatag-progress-text { + height: 25px; + margin-left: 5px; + line-height: 25px; + vertical-align: top; + width: auto; + display: inline-block; + color: #3F4141; + font-weight: bold; +} +*/}).toString().slice(14, -3); + var container = document.createElement('div'); + container.classList.add('mediatag-progress-container'); + var bar = document.createElement('div'); + bar.classList.add('mediatag-progress-bar'); + container.appendChild(bar); + + var text = document.createElement('span'); + text.classList.add('mediatag-progress-text'); + text.innerText = '0%'; + + mediaObject.on('progress', function (obj) { + var percent = obj.progress; + text.innerText = (Math.round(percent*10))/10+'%'; + bar.setAttribute('style', 'width:'+percent+'%;'); + }); + + mediaObject.tag.innerHTML = ''; + mediaObject.tag.appendChild(container); + mediaObject.tag.appendChild(text); + }; + var makeDownloadButton = function (cfg, mediaObject, size, cb) { + var btn = document.createElement('button'); + btn.setAttribute('class', 'btn btn-default'); + btn.innerHTML = '' + + cfg.download.textDl + ' (' + size + 'MB)'; + btn.addEventListener('click', function () { + makeProgressBar(cfg, mediaObject); + cb(); + }); + mediaObject.tag.innerHTML = ''; + mediaObject.tag.appendChild(btn); + }; + + var getFileSize = function (src, _cb) { + var cb = function (e, res) { + _cb(e, res); + cb = function () {}; + }; + // XXX Cache + var xhr = new XMLHttpRequest(); + xhr.open("HEAD", src); + xhr.onerror = function () { return void cb("XHR_ERROR"); }; + xhr.onreadystatechange = function() { + if (this.readyState === this.DONE) { + cb(null, Number(xhr.getResponseHeader("Content-Length"))); + } + }; + xhr.onload = function () { + if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); } + }; + xhr.send(); + }; // Download a blob from href var download = function (src, _cb, progressCb) { @@ -433,6 +520,7 @@ // End media-tag rendering: display the tag and emit the event var end = function (decrypted) { + mediaObject.complete = true; process(mediaObject, decrypted, cfg, function (err) { if (err) { return void emit('error', err); } mediaObject._blob = decrypted; @@ -441,36 +529,54 @@ }; // If we have the blob in our cache, don't download & decrypt it again, just display + // XXX Store in the cache the pending mediaobject: make sure we don't download and decrypt twice the same element at the same time if (cache[uid]) { end(cache[uid]); return mediaObject; } - // Download the encrypted blob - download(src, function (err, u8Encrypted) { + var dl = function () { + // Download the encrypted blob + download(src, function (err, u8Encrypted) { + if (err) { + if (err === "XHR_ERROR 404") { + mediaObject.tag.innerHTML = ''; + } + return void emit('error', err); + } + // Decrypt the blob + decrypt(u8Encrypted, strKey, function (errDecryption, u8Decrypted) { + if (errDecryption) { + return void emit('error', errDecryption); + } + // Cache and display the decrypted blob + cache[uid] = u8Decrypted; + end(u8Decrypted); + }, function (progress) { + emit('progress', { + progress: 50+0.5*progress + }); + }); + }, function (progress) { + emit('progress', { + progress: 0.5*progress + }); + }); + }; + + if (cfg.force) { dl(); return mediaObject; } + + var maxSize = 5 * 1024 * 1024; + getFileSize(src, function (err, size) { if (err) { if (err === "XHR_ERROR 404") { mediaObject.tag.innerHTML = ''; } return void emit('error', err); } - // Decrypt the blob - decrypt(u8Encrypted, strKey, function (errDecryption, u8Decrypted) { - if (errDecryption) { - return void emit('error', errDecryption); - } - // Cache and display the decrypted blob - cache[uid] = u8Decrypted; - end(u8Decrypted); - }, function (progress) { - emit('progress', { - progress: 50+0.5*progress - }); - }); - }, function (progress) { - emit('progress', { - progress: 0.5*progress - }); + if (!size || size < maxSize) { return void dl(); } + var sizeMb = Math.round(10 * size / 1024 / 1024) / 10; + makeDownloadButton(cfg, mediaObject, sizeMb, dl); }); return mediaObject; diff --git a/www/file/inner.js b/www/file/inner.js index 8b3f12682..c29657672 100644 --- a/www/file/inner.js +++ b/www/file/inner.js @@ -168,7 +168,9 @@ define([ var rightsideDisplayed = false; - MediaTag($mt[0]).on('complete', function (decrypted) { + MediaTag($mt[0], { + force: true // Download starts automatically + }).on('complete', function (decrypted) { $dlview.show(); $dlform.hide(); var $dlButton = $dlview.find('media-tag button'); diff --git a/www/pad/inner.js b/www/pad/inner.js index 235b4fafa..5139197e0 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -462,7 +462,7 @@ define([ setTimeout(function() { // Just in case var tags = dom.querySelectorAll('media-tag:empty'); Array.prototype.slice.call(tags).forEach(function(el) { - MediaTag(el); + var mediaObject = MediaTag(el); $(el).on('keydown', function(e) { if ([8, 46].indexOf(e.which) !== -1) { $(el).remove(); @@ -474,6 +474,7 @@ define([ if (mutation.type === 'childList') { var list_values = [].slice.call(el.children); mediaTagMap[el.getAttribute('src')] = list_values; + if (mediaObject.complete) { observer.disconnect(); } } }); }); @@ -492,7 +493,7 @@ define([ var src = tag.getAttribute('src'); if (mediaTagMap[src]) { mediaTagMap[src].forEach(function(n) { - tag.appendChild(n.cloneNode()); + tag.appendChild(n.cloneNode(true)); }); } }); From d199da95634fd5bc6496b4dd05662df66e5cbdd3 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 24 Nov 2020 16:49:12 +0100 Subject: [PATCH 051/201] lint compliance --- www/common/media-tag.js | 2 +- www/common/outer/cache-store.js | 4 ++-- www/common/sframe-app-framework.js | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 04e48d7f5..558ac9a19 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -246,7 +246,7 @@ var factory = function (Cache) { if (arrayBuffer) { var u8 = new Uint8Array(arrayBuffer); if (cacheKey) { - return void Cache.setBlobCache(cacheKey, u8, function (err) { + return void Cache.setBlobCache(cacheKey, u8, function () { cb(null, u8); }); } diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index fe3b541b2..4d5e30ed2 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -75,8 +75,8 @@ define([ }); }; - S.clearChannel = function (id, _cb) { - cb = Util.once(Util.mkAsync(_cb || function () {})); + S.clearChannel = function (id, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); cache.removeItem(id, function () { cb(); }); diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index f2a5996cf..d3a4bc174 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -474,7 +474,8 @@ define([ return UI.errorLoadingScreen("Reload loop: empty chainpad for a non-empty channel"); } noCache = true; - sframeChan.event("Q_CORRUPTED_CACHE"); + var sframeChan = common.getSframeChannel(); + sframeChan.event("EV_CORRUPTED_CACHE"); }; var onCacheReady = function () { stateChange(STATE.DISCONNECTED); From 9bb465bbcee1c49545881aeb26f634aeb6e5d600 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 24 Nov 2020 17:00:48 +0100 Subject: [PATCH 052/201] Fix style conflict --- customize.dist/loading.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index b0d66981c..5b916a0e5 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -241,7 +241,7 @@ p.cp-password-info{ animation-timing-function: cubic-bezier(.6,0.15,0.4,0.85); } -button.primary{ +button:not(.btn).primary{ border: 1px solid #4591c4; padding: 8px 12px; text-transform: uppercase; @@ -250,7 +250,7 @@ button.primary{ font-weight: bold; } -button.primary:hover{ +button:not(.btn).primary:hover{ background-color: rgb(52, 118, 162); } From d027005c878b95514f54c27352144c86d6765e52 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 24 Nov 2020 17:49:45 +0100 Subject: [PATCH 053/201] Automatically download cached media-tags and improve duplicates --- www/common/media-tag.js | 107 +++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 558ac9a19..bab444df3 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -1,5 +1,6 @@ (function (window) { var factory = function (Cache) { + var Promise = window.Promise; var cache; var cypherChunkLength = 131088; @@ -190,24 +191,43 @@ var factory = function (Cache) { mediaObject.tag.appendChild(btn); }; + var getCacheKey = function (src) { + var _src = src.replace(/(\/)*$/, ''); // Remove trailing slashes + var idx = _src.lastIndexOf('/'); + var cacheKey = _src.slice(idx+1); + if (!/^[a-f0-9]{48}$/.test(cacheKey)) { cacheKey = undefined; } + return cacheKey; + }; + var getFileSize = function (src, _cb) { var cb = function (e, res) { _cb(e, res); cb = function () {}; }; - // XXX Cache - var xhr = new XMLHttpRequest(); - xhr.open("HEAD", src); - xhr.onerror = function () { return void cb("XHR_ERROR"); }; - xhr.onreadystatechange = function() { - if (this.readyState === this.DONE) { - cb(null, Number(xhr.getResponseHeader("Content-Length"))); - } - }; - xhr.onload = function () { - if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); } + + var cacheKey = getCacheKey(src); + + var check = function () { + var xhr = new XMLHttpRequest(); + xhr.open("HEAD", src); + xhr.onerror = function () { return void cb("XHR_ERROR"); }; + xhr.onreadystatechange = function() { + if (this.readyState === this.DONE) { + cb(null, Number(xhr.getResponseHeader("Content-Length"))); + } + }; + xhr.onload = function () { + if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); } + }; + xhr.send(); }; - xhr.send(); + + if (!cacheKey) { return void check(); } + + Cache.getBlobCache(cacheKey, function (err, u8) { + if (err || !u8) { return void check(); } + cb(null, 0); + }); }; // Download a blob from href @@ -217,10 +237,7 @@ var factory = function (Cache) { cb = function () {}; }; - var _src = src.replace(/(\/)*$/, ''); // Remove trailing slashes - var idx = _src.lastIndexOf('/'); - var cacheKey = _src.slice(idx+1); - if (!/^[a-f0-9]{48}$/.test(cacheKey)) { cacheKey = undefined; } + var cacheKey = getCacheKey(src); var fetch = function () { var xhr = new XMLHttpRequest(); @@ -547,39 +564,41 @@ var factory = function (Cache) { }); }; - // If we have the blob in our cache, don't download & decrypt it again, just display - // XXX Store in the cache the pending mediaobject: make sure we don't download and decrypt twice the same element at the same time - if (cache[uid]) { - end(cache[uid]); - return mediaObject; - } + var error = function (err) { + mediaObject.tag.innerHTML = ''; + emit('error', err); + }; var dl = function () { // Download the encrypted blob - download(src, function (err, u8Encrypted) { - if (err) { - if (err === "XHR_ERROR 404") { - mediaObject.tag.innerHTML = ''; - } - return void emit('error', err); - } - // Decrypt the blob - decrypt(u8Encrypted, strKey, function (errDecryption, u8Decrypted) { - if (errDecryption) { - return void emit('error', errDecryption); + cache[uid] = cache[uid] || new Promise(function (resolve, reject) { + download(src, function (err, u8Encrypted) { + if (err) { + return void reject(err); } - // Cache and display the decrypted blob - cache[uid] = u8Decrypted; - end(u8Decrypted); + // Decrypt the blob + decrypt(u8Encrypted, strKey, function (errDecryption, u8Decrypted) { + if (errDecryption) { + return void reject(errDecryption); + } + // Cache and display the decrypted blob + cache[uid] = u8Decrypted; + resolve(u8Decrypted); + }, function (progress) { + emit('progress', { + progress: 50+0.5*progress + }); + }); }, function (progress) { emit('progress', { - progress: 50+0.5*progress + progress: 0.5*progress }); }); - }, function (progress) { - emit('progress', { - progress: 0.5*progress - }); + }); + cache[uid].then(function (u8) { + end(u8); + }, function (err) { + error(err); }); }; @@ -588,10 +607,7 @@ var factory = function (Cache) { var maxSize = 5 * 1024 * 1024; getFileSize(src, function (err, size) { if (err) { - if (err === "XHR_ERROR 404") { - mediaObject.tag.innerHTML = ''; - } - return void emit('error', err); + return void error(err); } if (!size || size < maxSize) { return void dl(); } var sizeMb = Math.round(10 * size / 1024 / 1024) / 10; @@ -618,6 +634,7 @@ var factory = function (Cache) { } else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) { define([ '/common/outer/cache-store.js', + '/bower_components/es6-promise/es6-promise.min.js' ], function (Cache) { return factory(Cache); }); From 418a170dc9e1cfdce0f237ee94e4aad6d41ca011 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Nov 2020 12:13:11 +0100 Subject: [PATCH 054/201] Fix type error --- www/common/media-tag.js | 1 - 1 file changed, 1 deletion(-) diff --git a/www/common/media-tag.js b/www/common/media-tag.js index bab444df3..e86fd6071 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -582,7 +582,6 @@ var factory = function (Cache) { return void reject(errDecryption); } // Cache and display the decrypted blob - cache[uid] = u8Decrypted; resolve(u8Decrypted); }, function (progress) { emit('progress', { From 3f88e29f30b7d168e0afea1ab7564d9a99987580 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Nov 2020 14:53:48 +0100 Subject: [PATCH 055/201] Add progress when creating a pad from a file --- customize.dist/loading.js | 11 ++++++++--- www/common/cryptpad-common.js | 10 +++++++--- www/common/sframe-common-outer.js | 7 ++++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 5b916a0e5..a335a8903 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -279,7 +279,7 @@ button:not(.btn).primary:hover{ var built = false; var types = ['less', 'drive', 'migrate', 'sf', 'team', 'pad', 'end']; - var current; + var current, progress; var makeList = function (data) { var c = types.indexOf(data.type); current = c; @@ -295,7 +295,7 @@ button:not(.btn).primary:hover{ }; var list = '
    '; types.forEach(function (el, i) { - if (i >= 6) { return; } + if (el === "end") { return; } list += getLi(i); }); list += '
'; @@ -303,7 +303,7 @@ button:not(.btn).primary:hover{ }; var makeBar = function (data) { var c = types.indexOf(data.type); - var l = types.length; + var l = types.length - 1; // don't count "end" as a type var progress = Math.min(data.progress, 100); var p = (progress / l) + (100 * c / l); var bar = '
'+ @@ -315,8 +315,13 @@ button:not(.btn).primary:hover{ var hasErrored = false; var updateLoadingProgress = function (data) { if (!built || !data) { return; } + + // Make sure progress doesn't go backward var c = types.indexOf(data.type); if (c < current) { return console.error(data); } + if (c === current && progress > data.progress) { return console.error(data); } + progress = data.progress; + try { document.querySelector('.cp-loading-spinner-container').style.display = 'none'; document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 9f88e85a9..714598ca4 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -701,7 +701,7 @@ define([ }); }; - common.useFile = function (Crypt, cb, optsPut) { + common.useFile = function (Crypt, cb, optsPut, onProgress) { var fileHost = Config.fileHost || window.location.origin; var data = common.fromFileData; var parsed = Hash.parsePadUrl(data.href); @@ -758,7 +758,9 @@ define([ return void cb(err); } u8 = _u8; - })); + }), function (progress) { + onProgress(progress * 50); + }); }).nThen(function (waitFor) { require(["/file/file-crypto.js"], waitFor(function (FileCrypto) { FileCrypto.decrypt(u8, key, waitFor(function (err, _res) { @@ -767,7 +769,9 @@ define([ return void cb(err); } res = _res; - })); + }), function (progress) { + onProgress(50 + progress * 50); + }); })); }).nThen(function (waitFor) { var ext = Util.parseFilename(data.title).ext; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index d17924cf0..768ca4db2 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1802,7 +1802,12 @@ define([ } startRealtime(); cb(); - }, cryptputCfg); + }, cryptputCfg, function (progress) { + sframeChan.event('EV_LOADING_INFO', { + type: 'pad', + progress: progress + }); + }); return; } // Start realtime outside the iframe and callback From cdda692492c1655af58ca40b7f7fd30671b8699a Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Nov 2020 15:14:33 +0100 Subject: [PATCH 056/201] Fix duplicate friend data --- www/common/common-messaging.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/common-messaging.js b/www/common/common-messaging.js index 15d0408f7..65f05961e 100644 --- a/www/common/common-messaging.js +++ b/www/common/common-messaging.js @@ -91,7 +91,7 @@ define([ Msg.updateMyData = function (store, curve) { var myData = createData(store.proxy, false); if (store.proxy.friends) { - store.proxy.friends.me = myData; + store.proxy.friends.me = Util.clone(myData); delete store.proxy.friends.me.channel; } if (store.modules['team']) { @@ -99,6 +99,7 @@ define([ } var todo = function (friend) { if (!friend || !friend.notifications) { return; } + delete friend.user; myData.channel = friend.channel; store.mailbox.sendTo('UPDATE_DATA', myData, { channel: friend.notifications, From 4027e48fd20ab09dd8520606e7f451a7a17b0b20 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Nov 2020 15:51:22 +0100 Subject: [PATCH 057/201] Open or share a mediatag from a pad --- www/common/inner/common-mediatag.js | 26 ++++++++++++++++++++++++++ www/common/sframe-common.js | 23 ++++++++++++++++++++--- www/secureiframe/inner.js | 12 ++++++------ 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/www/common/inner/common-mediatag.js b/www/common/inner/common-mediatag.js index 3d28d126a..e60f734d0 100644 --- a/www/common/inner/common-mediatag.js +++ b/www/common/inner/common-mediatag.js @@ -362,6 +362,9 @@ define([ }); }; + Messages.pad_mediatagShare = "Share file"; // XXX + Messages.pad_mediatagOpen = "Open file"; // XXX + var mediatagContextMenu; MT.importMediaTagMenu = function (common) { if (mediatagContextMenu) { return mediatagContextMenu; } @@ -377,6 +380,14 @@ define([ 'tabindex': '-1', 'data-icon': "fa-eye", }, Messages.pad_mediatagPreview)), + h('li.cp-svg', h('a.cp-app-code-context-openin.dropdown-item', { + 'tabindex': '-1', + 'data-icon': "fa-external-link", + }, Messages.pad_mediatagOpen)), + h('li.cp-svg', h('a.cp-app-code-context-share.dropdown-item', { + 'tabindex': '-1', + 'data-icon': "fa-shhare-alt", + }, Messages.pad_mediatagShare)), h('li', h('a.cp-app-code-context-saveindrive.dropdown-item', { 'tabindex': '-1', 'data-icon': "fa-cloud-upload", @@ -419,6 +430,21 @@ define([ else if ($this.hasClass("cp-app-code-context-open")) { $mt.trigger('preview'); } + else if ($this.hasClass("cp-app-code-context-openin")) { + var hash = common.getHashFromMediaTag($mt); + common.openURL(Hash.hashToHref(hash, 'file')); + } + else if ($this.hasClass("cp-app-code-context-share")) { + var data = { + file: true, + pathname: '/file/', + hashes: { + fileHash: common.getHashFromMediaTag($mt) + }, + title: Util.find($mt[0], ['_mediaObject', 'name']) || '' + }; + common.getSframeChannel().event('EV_SHARE_OPEN', data); + } }); return m; diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 4198a649a..7a00516a5 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -142,7 +142,7 @@ define([ } return; }; - funcs.importMediaTag = function ($mt) { + var getMtData = function ($mt) { if (!$mt || !$mt.is('media-tag')) { return; } var chanStr = $mt.attr('src'); var keyStr = $mt.attr('data-crypto-key'); @@ -154,10 +154,27 @@ define([ var channel = src.replace(/\/blob\/[0-9a-f]{2}\//i, ''); // Get key var key = keyStr.replace(/cryptpad:/i, ''); + return { + channel: channel, + key: key + }; + }; + funcs.getHashFromMediaTag = function ($mt) { + var data = getMtData($mt); + if (!data) { return; } + return Hash.getFileHashFromKeys({ + version: 1, + channel: data.channel, + keys: { fileKeyStr: data.key } + }); + }; + funcs.importMediaTag = function ($mt) { + var data = getMtData($mt); + if (!data) { return; } var metadata = $mt[0]._mediaObject._blob.metadata; ctx.sframeChan.query('Q_IMPORT_MEDIATAG', { - channel: channel, - key: key, + channel: data.channel, + key: data.key, name: metadata.name, type: metadata.type, owners: metadata.owners diff --git a/www/secureiframe/inner.js b/www/secureiframe/inner.js index c20d53a80..4d5b8ee4f 100644 --- a/www/secureiframe/inner.js +++ b/www/secureiframe/inner.js @@ -52,10 +52,10 @@ define([ : Share.getShareModal; f(common, { origin: priv.origin, - pathname: priv.pathname, - password: priv.password, - isTemplate: priv.isTemplate, - hashes: priv.hashes, + pathname: data.pathname || priv.pathname, + password: data.hashes ? '' : priv.password, + isTemplate: data.hashes ? false : priv.isTemplate, + hashes: data.hashes || priv.hashes, common: common, title: data.title, versionHash: data.versionHash, @@ -64,8 +64,8 @@ define([ hideIframe(); }, fileData: { - hash: priv.hashes.fileHash, - password: priv.password + hash: (data.hashes && data.hashes.fileHash) || priv.hashes.fileHash, + password: data.hashes ? '' : priv.password } }, function (e, modal) { if (e) { console.error(e); } From 3de5ca75a6164e34adc6f85e23a141e2ab5f1a7b Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Nov 2020 16:19:10 +0100 Subject: [PATCH 058/201] Add new mediatag options in rich text pads --- www/pad/inner.js | 24 ++++++++++++++++++++ www/pad/mediatag-plugin.js | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/www/pad/inner.js b/www/pad/inner.js index 5139197e0..2e5b2ad91 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -46,6 +46,7 @@ define([ '/common/test.js', '/bower_components/diff-dom/diffDOM.js', + '/bower_components/file-saver/FileSaver.min.js', 'css!/customize/src/print.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', @@ -1085,6 +1086,9 @@ define([ border: Messages.pad_mediatagBorder, preview: Messages.pad_mediatagPreview, 'import': Messages.pad_mediatagImport, + download: Messages.download_mt_button, + share: Messages.pad_mediatagShare, + open: Messages.pad_mediatagOpen, options: Messages.pad_mediatagOptions }; Ckeditor._commentsTranslations = { @@ -1165,6 +1169,26 @@ define([ editor.plugins.mediatag.import = function($mt) { framework._.sfCommon.importMediaTag($mt); }; + editor.plugins.mediatag.download = function($mt) { + var media = Util.find($mt, [0, '_mediaObject']); + if (!(media && media._blob)) { return void console.error($mt); } + window.saveAs(media._blob.content, media.name); + }; + editor.plugins.mediatag.open = function($mt) { + var hash = framework._.sfCommon.getHashFromMediaTag($mt); + framework._.sfCommon.openURL(Hash.hashToHref(hash, 'file')); + }; + editor.plugins.mediatag.share = function($mt) { + var data = { + file: true, + pathname: '/file/', + hashes: { + fileHash: framework._.sfCommon.getHashFromMediaTag($mt) + }, + title: Util.find($mt[0], ['_mediaObject', 'name']) || '' + }; + framework._.sfCommon.getSframeChannel().event('EV_SHARE_OPEN', data); + }; Links.init(Ckeditor, editor); }).nThen(function() { // Move ckeditor parts to have a structure like the other apps diff --git a/www/pad/mediatag-plugin.js b/www/pad/mediatag-plugin.js index 46747b18a..22a732748 100644 --- a/www/pad/mediatag-plugin.js +++ b/www/pad/mediatag-plugin.js @@ -53,15 +53,57 @@ editor.plugins.mediatag.import($mt); } }); + editor.addCommand('downloadMT', { + exec: function (editor) { + var w = targetWidget; + targetWidget = undefined; + var $mt = $(w.$).find('media-tag'); + editor.plugins.mediatag.download($mt); + } + }); + editor.addCommand('openMT', { + exec: function (editor) { + var w = targetWidget; + targetWidget = undefined; + var $mt = $(w.$).find('media-tag'); + editor.plugins.mediatag.open($mt); + } + }); + editor.addCommand('shareMT', { + exec: function (editor) { + var w = targetWidget; + targetWidget = undefined; + var $mt = $(w.$).find('media-tag'); + editor.plugins.mediatag.share($mt); + } + }); if (editor.addMenuItems) { editor.addMenuGroup('mediatag'); + editor.addMenuItem('open', { + label: Messages.open, + icon: 'iframe', + command: 'openMT', + group: 'mediatag' + }); + editor.addMenuItem('share', { + label: Messages.share, + icon: 'link', + command: 'shareMT', + group: 'mediatag' + }); editor.addMenuItem('importMediatag', { label: Messages.import, icon: 'save', command: 'importMediatag', group: 'mediatag' }); + editor.addMenuItem('download', { + label: Messages.download, + icon: 'save', + command: 'downloadMT', + group: 'mediatag' + }); editor.addMenuItem('mediatag', { label: Messages.options, icon: 'image', @@ -76,6 +118,9 @@ targetWidget = element; return { mediatag: CKEDITOR.TRISTATE_OFF, + open: CKEDITOR.TRISTATE_OFF, + share: CKEDITOR.TRISTATE_OFF, + download: CKEDITOR.TRISTATE_OFF, importMediatag: CKEDITOR.TRISTATE_OFF, }; } From ff0a5700349e57ec53c90110ff287893eedd9da7 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Nov 2020 16:36:48 +0100 Subject: [PATCH 059/201] Fix keybinding in upload modal --- www/common/common-interface.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 9fae121dc..3e9d848cd 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -65,12 +65,13 @@ define([ switch (e.which) { case 27: // cancel if (typeof(no) === 'function') { no(e); } + $(el || window).off('keydown', handler); break; case 13: // enter if (typeof(yes) === 'function') { yes(e); } + $(el || window).off('keydown', handler); break; } - $(el || window).off('keydown', handler); }; $(el || window).keydown(handler); From 76d527d0b23efc1044842d5da3e34ecb4cae8b0e Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Nov 2020 16:40:47 +0100 Subject: [PATCH 060/201] Don't store support attachments into the drive --- www/support/ui.js | 1 + 1 file changed, 1 insertion(+) diff --git a/www/support/ui.js b/www/support/ui.js index 9b86d6f20..a58fdf170 100644 --- a/www/support/ui.js +++ b/www/support/ui.js @@ -397,6 +397,7 @@ define([ var fmConfig = { body: $('body'), + noStore: true, // Don't store attachments into our drive onUploaded: function (ev, data) { if (ev.callback) { ev.callback(data); From 41bf79232da74ef6a2c87c5e56cd55c8112dc348 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Nov 2020 16:49:43 +0100 Subject: [PATCH 061/201] Add max image size in the upload error message in whiteboard #563 --- www/whiteboard/inner.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js index 44efd1421..bab1603dc 100644 --- a/www/whiteboard/inner.js +++ b/www/whiteboard/inner.js @@ -331,10 +331,11 @@ define([ APP.FM.handleFile(blob); }); }; + var MAX_IMAGE_SIZE = 1 * 1024 * 1024; // 1 MB + var maxSizeStr = Messages._getKey('formattedMB', [Util.bytesToMegabytes(MAX_IMAGE_SIZE)]); var addImageToCanvas = function (img) { - // 1 MB maximum - if (img.src && img.src.length > 1 * 1024 * 1024) { - UI.warn(Messages.upload_tooLargeBrief); + if (img.src && img.src.length > MAX_IMAGE_SIZE) { + UI.warn(Messages._getKey('upload_tooLargeBrief', [maxSizeStr])); // XXX update key return; } var w = img.width; @@ -356,8 +357,8 @@ define([ var file = e.target.files[0]; var reader = new FileReader(); // 1 MB maximum - if (file.size > 1 * 1024 * 1024) { - UI.warn(Messages.upload_tooLargeBrief); + if (file.size > MAX_IMAGE_SIZE) { + UI.warn(Messages._getKey('upload_tooLargeBrief', [maxSizeStr])); return; } reader.onload = function () { From 9fe36d5c931f7ce5616e5b350cfddcfb356fa081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Wed, 25 Nov 2020 15:53:59 +0000 Subject: [PATCH 062/201] Remove Document ID hard-coded key --- www/common/inner/properties.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/inner/properties.js b/www/common/inner/properties.js index 143c32d81..d3969b028 100644 --- a/www/common/inner/properties.js +++ b/www/common/inner/properties.js @@ -12,7 +12,7 @@ define([ Messages, nThen) { var Properties = {}; - Messages.documentID = Messages.documentID || 'Document identifier'; // XXX + Messages.documentID = Messages.documentID; var getPadProperties = function (Env, data, opts, _cb) { var cb = Util.once(Util.mkAsync(_cb)); From 2f062de290bec32026f4329e83ed994641b84388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Wed, 25 Nov 2020 17:02:39 +0000 Subject: [PATCH 063/201] Add archived alert message --- www/admin/inner.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/www/admin/inner.js b/www/admin/inner.js index d9bebc097..7b88fcbdf 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -120,7 +120,8 @@ define([ Messages.admin_archiveInput = "Document URL"; Messages.admin_archiveInput2 = "Document password"; Messages.admin_archiveInval = "Invalid document"; - Messages.restoredFromServer = "Pad restored"; + Messages.restoredFromServer = "Document restored"; + Messages.archivedFromServer = "Document archived"; var archiveForm = function (archive, $div, $button) { var label = h('label', { for: 'cp-admin-archive' }, Messages.admin_archiveInput); @@ -221,7 +222,7 @@ define([ console.error(e); return; } - UI.log(archive ? Messages.deletedFromServer : Messages.restoredFromServer); + UI.log(archive ? Messages.archivedFromServer : Messages.restoredFromServer); $input.val(''); $pwInput.val(''); }); From 466072d03b9dee88a1c1b642f9173248ed7b6ca6 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 26 Nov 2020 12:15:02 +0530 Subject: [PATCH 064/201] read files starting from the oldest known point of relevance when computing indices --- lib/storage/file.js | 64 +++++++++++++++++++++- lib/stream-file.js | 9 +-- lib/workers/db-worker.js | 115 ++++++++++++++++++++++++++++++++++----- 3 files changed, 167 insertions(+), 21 deletions(-) diff --git a/lib/storage/file.js b/lib/storage/file.js index b1ccfde0f..8cdfa747a 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -1,6 +1,6 @@ /*@flow*/ /* jshint esversion: 6 */ -/* global Buffer */ +/* globals Buffer */ var Fs = require("fs"); var Fse = require("fs-extra"); var Path = require("path"); @@ -66,6 +66,10 @@ var mkTempPath = function (env, channelId) { return mkPath(env, channelId) + '.temp'; }; +var mkOffsetPath = function (env, channelId) { + return mkPath(env, channelId) + '.offset'; +}; + // pass in the path so we can reuse the same function for archived files var channelExists = function (filepath, cb) { Fs.stat(filepath, function (err, stat) { @@ -131,7 +135,9 @@ const readMessagesBin = (env, id, start, msgHandler, cb) => { const collector = createIdleStreamCollector(stream); const handleMessageAndKeepStreamAlive = Util.both(msgHandler, collector.keepAlive); const done = Util.both(cb, collector); - return void readFileBin(stream, handleMessageAndKeepStreamAlive, done); + return void readFileBin(stream, handleMessageAndKeepStreamAlive, done, { + offset: start, + }); }; // reads classic metadata from a channel log and aborts @@ -190,6 +196,37 @@ var closeChannel = function (env, channelName, cb) { } }; +var clearOffset = function (env, channelId, cb) { + var path = mkOffsetPath(env, channelId); + // we should always be able to recover from invalid offsets, so failure to delete them + // is not catastrophic. Anything calling this function can optionally ignore errors it might report + Fs.unlink(path, cb); +}; + +var writeOffset = function (env, channelId, data, cb) { + var path = mkOffsetPath(env, channelId); + var s_data; + try { + s_data = JSON.stringify(data); + } catch (err) { + return void cb(err); + } + Fs.writeFile(path, s_data, cb); +}; + +var getOffset = function (env, channelId, cb) { + var path = mkOffsetPath(env, channelId); + Fs.readFile(path, function (err, content) { + if (err) { return void cb(err); } + try { + var json = JSON.parse(content); + cb(void 0, json); + } catch (err2) { + cb(err2); + } + }); +}; + // truncates a file to the end of its metadata line // TODO write the metadata in a dedicated file var clearChannel = function (env, channelId, _cb) { @@ -213,6 +250,7 @@ var clearChannel = function (env, channelId, _cb) { cb(); }); }); + clearOffset(env, channelId, function () {}); }); }; @@ -389,6 +427,7 @@ var removeChannel = function (env, channelName, cb) { CB(labelError("E_METADATA_REMOVAL", err)); } })); + clearOffset(env, channelName, w()); }).nThen(function () { if (errors === 2) { return void CB(labelError('E_REMOVE_CHANNEL', new Error("ENOENT"))); @@ -604,6 +643,8 @@ var archiveChannel = function (env, channelName, cb) { return void cb(err); } })); + }).nThen(function (w) { + clearOffset(env, channelName, w()); }).nThen(function (w) { // archive the dedicated metadata channel var metadataPath = mkMetadataPath(env, channelName); @@ -861,6 +902,7 @@ var trimChannel = function (env, channelName, hash, _cb) { } })); }).nThen(function (w) { + clearOffset(env, channelName, w()); cleanUp(w(function (err) { if (err) { w.abort(); @@ -1177,6 +1219,24 @@ module.exports.create = function (conf, _cb) { }); }, + // OFFSETS +// these exist strictly as an optimization +// you can always remove them without data loss + clearOffset: function (channelName, cb) { + if (!isValidChannelId(channelName)) { return void cb(new Error('EINVAL')); } + clearOffset(env, channelName, cb); + }, + writeOffset: function (channelName, data, _cb) { + var cb = Util.mkAsync(_cb); + if (!isValidChannelId(channelName)) { return void cb(new Error('EINVAL')); } + writeOffset(env, channelName, data, cb); + }, + getOffset: function (channelName, _cb) { + var cb = Util.mkAsync(_cb); + if (!isValidChannelId(channelName)) { return void cb(new Error('EINVAL')); } + getOffset(env, channelName, cb); + }, + // METADATA METHODS // fetch the metadata for a channel getChannelMetadata: function (channelName, cb) { diff --git a/lib/stream-file.js b/lib/stream-file.js index c3130365b..a164ceffc 100644 --- a/lib/stream-file.js +++ b/lib/stream-file.js @@ -44,8 +44,8 @@ const mkBufferSplit = () => { // return a streaming function which transforms buffers into objects // containing the buffer and the offset from the start of the stream -const mkOffsetCounter = () => { - let offset = 0; +const mkOffsetCounter = (offset) => { + offset = offset || 0; return Pull.map((buff) => { const out = { offset: offset, buff: buff }; // +1 for the eaten newline @@ -59,13 +59,14 @@ const mkOffsetCounter = () => { // that this function has a lower memory profile than our classic method // of reading logs line by line. // it also allows the handler to abort reading at any time -Stream.readFileBin = (stream, msgHandler, cb) => { +Stream.readFileBin = (stream, msgHandler, cb, opt) => { + opt = opt || {}; //const stream = Fs.createReadStream(path, { start: start }); let keepReading = true; Pull( ToPull.read(stream), mkBufferSplit(), - mkOffsetCounter(), + mkOffsetCounter(opt.offset), Pull.asyncMap((data, moreCb) => { msgHandler(data, moreCb, () => { try { diff --git a/lib/workers/db-worker.js b/lib/workers/db-worker.js index 9d5abf386..d871bcb97 100644 --- a/lib/workers/db-worker.js +++ b/lib/workers/db-worker.js @@ -1,5 +1,5 @@ /* jshint esversion: 6 */ -/* global process */ +/* globals process, Buffer */ const HK = require("../hk-util"); const Store = require("../storage/file"); @@ -114,14 +114,15 @@ const init = function (config, _cb) { * including the initial metadata line, if it exists */ -const computeIndex = function (data, cb) { - if (!data || !data.channel) { - return void cb('E_NO_CHANNEL'); - } - const channelName = data.channel; +const OPEN_CURLY_BRACE = Buffer.from('{'); +const CHECKPOINT_PREFIX = Buffer.from('cp|'); +const isValidOffsetNumber = function (n) { + return typeof(n) === 'number' && n >= 0; +}; - const cpIndex = []; +const computeIndexFromOffset = function (channelName, offset, cb) { + let cpIndex = []; let messageBuf = []; let i = 0; @@ -129,27 +130,42 @@ const computeIndex = function (data, cb) { const offsetByHash = {}; let offsetCount = 0; - let size = 0; + let size = offset || 0; + var start = offset || 0; + let unconventional = false; + nThen(function (w) { // iterate over all messages in the channel log // old channels can contain metadata as the first message of the log // skip over metadata as that is handled elsewhere // otherwise index important messages in the log - store.readMessagesBin(channelName, 0, (msgObj, readMore) => { + store.readMessagesBin(channelName, start, (msgObj, readMore, abort) => { let msg; // keep an eye out for the metadata line if you haven't already seen it // but only check for metadata on the first line - if (!i && msgObj.buff.indexOf('{') === 0) { - i++; // always increment the message counter + if (i) { + // fall through intentionally because the following blocks are invalid + // for all but the first message + } else if (msgObj.buff.includes(OPEN_CURLY_BRACE)) { msg = HK.tryParse(Env, msgObj.buff.toString('utf8')); - if (typeof msg === "undefined") { return readMore(); } + if (typeof msg === "undefined") { + i++; // always increment the message counter + return readMore(); + } // validate that the current line really is metadata before storing it as such // skip this, as you already have metadata... - if (HK.isMetadataMessage(msg)) { return readMore(); } + if (HK.isMetadataMessage(msg)) { + i++; // always increment the message counter + return readMore(); + } + } else if (!(msg = HK.tryParse(Env, msgObj.buff.toString('utf8')))) { + w.abort(); + abort(); + return CB("OFFSET_ERROR"); } i++; - if (msgObj.buff.indexOf('cp|') > -1) { + if (msgObj.buff.includes(CHECKPOINT_PREFIX)) { msg = msg || HK.tryParse(Env, msgObj.buff.toString('utf8')); if (typeof msg === "undefined") { return readMore(); } // cache the offsets of checkpoints if they can be parsed @@ -164,6 +180,7 @@ const computeIndex = function (data, cb) { } } else if (messageBuf.length > 100 && cpIndex.length === 0) { // take the last 50 messages + unconventional = true; messageBuf = messageBuf.slice(-50); } // if it's not metadata or a checkpoint then it should be a regular message @@ -192,11 +209,38 @@ const computeIndex = function (data, cb) { size = msgObj.offset + msgObj.buff.length + 1; }); })); + }).nThen(function (w) { + cpIndex = HK.sliceCpIndex(cpIndex, i); + + var new_start; + if (cpIndex.length) { + new_start = cpIndex[0].offset; + } else if (unconventional && messageBuf.length && isValidOffsetNumber(messageBuf[0].offset)) { + new_start = messageBuf[0].offset; + } + + if (new_start === start) { return; } + if (!isValidOffsetNumber(new_start)) { return; } + + // store the offset of the earliest relevant line so that you can start from there next time... + store.writeOffset(channelName, { + start: new_start, + created: +new Date(), + }, w(function () { + var diff = new_start - start; + Env.Log.info('WORKER_OFFSET_UPDATE', { + channel: channelName, + old_start: start, + new_start: new_start, + diff: diff, + diffMB: diff / 1024 / 1024, + }); + })); }).nThen(function () { // return the computed index CB(null, { // Only keep the checkpoints included in the last 100 messages - cpIndex: HK.sliceCpIndex(cpIndex, i), + cpIndex: cpIndex, offsetByHash: offsetByHash, offsets: offsetCount, size: size, @@ -206,6 +250,47 @@ const computeIndex = function (data, cb) { }); }; +const computeIndex = function (data, cb) { + if (!data || !data.channel) { + return void cb('E_NO_CHANNEL'); + } + + const channelName = data.channel; + const CB = Util.once(cb); + + var start = 0; + nThen(function (w) { + store.getOffset(channelName, w(function (err, obj) { + if (err) { return; } + if (obj && typeof(obj.start) === 'number' && obj.start > 0) { + start = obj.start; + Env.Log.verbose('WORKER_OFFSET_RECOVERY', { + channel: channelName, + start: start, + startMB: start / 1024 / 1024, + }); + } + })); + }).nThen(function (w) { + computeIndexFromOffset(channelName, start, w(function (err, index) { + if (err === 'OFFSET_ERROR') { + return Env.Log.error("WORKER_OFFSET_ERROR", { + channel: channelName, + }); + } + w.abort(); + CB(err, index); + })); + }).nThen(function (w) { + // if you're here there was an OFFSET_ERROR.. + // first remove the offset that caused the problem to begin with + store.clearOffset(channelName, w()); + }).nThen(function () { + // now get the history as though it were the first time + computeIndexFromOffset(channelName, 0, CB); + }); +}; + const computeMetadata = function (data, cb) { const ref = {}; const lineHandler = Meta.createLineHandler(ref, Env.Log.error); From 842ed464e270cd5ac3d4af1df6b072e2e8d009e3 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 26 Nov 2020 12:17:28 +0530 Subject: [PATCH 065/201] remove hardcoded translation --- www/common/inner/properties.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/www/common/inner/properties.js b/www/common/inner/properties.js index 143c32d81..ad8571367 100644 --- a/www/common/inner/properties.js +++ b/www/common/inner/properties.js @@ -12,8 +12,6 @@ define([ Messages, nThen) { var Properties = {}; - Messages.documentID = Messages.documentID || 'Document identifier'; // XXX - var getPadProperties = function (Env, data, opts, _cb) { var cb = Util.once(Util.mkAsync(_cb)); var common = Env.common; From 8ac6eed62a0a0070aa5e03ce2944cc22e8f736e5 Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 19 Nov 2020 19:18:18 +0100 Subject: [PATCH 066/201] Translated using Weblate (German) Currently translated at 100.0% (1373 of 1373 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/de/ --- www/common/translations/messages.de.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.de.json b/www/common/translations/messages.de.json index b8aa108ed..fe873ea34 100644 --- a/www/common/translations/messages.de.json +++ b/www/common/translations/messages.de.json @@ -1468,5 +1468,6 @@ "loading_state_1": "Drive laden", "loading_state_0": "Oberfläche vorbereiten", "loading_state_5": "Dokument rekonstruieren", - "error_unhelpfulScriptError": "Skriptfehler: Siehe Konsole im Browser für Details" + "error_unhelpfulScriptError": "Skriptfehler: Siehe Konsole im Browser für Details", + "documentID": "Kennung des Dokuments" } From 781261111bfa4278598ea8cc02dadb26ab5512f3 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 26 Nov 2020 12:49:40 +0100 Subject: [PATCH 067/201] Remove debugging code --- www/file/inner.js | 1 - 1 file changed, 1 deletion(-) diff --git a/www/file/inner.js b/www/file/inner.js index c29657672..137ca54b1 100644 --- a/www/file/inner.js +++ b/www/file/inner.js @@ -203,7 +203,6 @@ define([ cb(); } }).on('progress', function (data) { - if (data.progress > 75) { return; } var p = data.progress +'%'; $progress.width(p); $progressTxt.text(Math.floor(data.progress) + '%'); From 7448fa93df6c2ba9f4fffecb605cd675ddcabbf5 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 26 Nov 2020 16:27:12 +0100 Subject: [PATCH 068/201] Fix download from contextmenu when mediatag is not ready --- www/common/inner/common-mediatag.js | 3 +++ www/pad/inner.js | 2 ++ 2 files changed, 5 insertions(+) diff --git a/www/common/inner/common-mediatag.js b/www/common/inner/common-mediatag.js index e60f734d0..741529de0 100644 --- a/www/common/inner/common-mediatag.js +++ b/www/common/inner/common-mediatag.js @@ -364,6 +364,7 @@ define([ Messages.pad_mediatagShare = "Share file"; // XXX Messages.pad_mediatagOpen = "Open file"; // XXX + Messages.mediatag_notReady = "Not ready"; // XXX var mediatagContextMenu; MT.importMediaTagMenu = function (common) { @@ -424,6 +425,8 @@ define([ } else if ($this.hasClass("cp-app-code-context-download")) { var media = Util.find($mt, [0, '_mediaObject']); + if (!media) { return void console.error('no media'); } + if (!media.complete) { return void UI.warn(Messages.mediatag_notReady); } if (!(media && media._blob)) { return void console.error($mt); } window.saveAs(media._blob.content, media.name); } diff --git a/www/pad/inner.js b/www/pad/inner.js index 2e5b2ad91..8a99b6101 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -1171,6 +1171,8 @@ define([ }; editor.plugins.mediatag.download = function($mt) { var media = Util.find($mt, [0, '_mediaObject']); + if (!media) { return void console.error('no media'); } + if (!media.complete) { return void UI.warn(Messages.mediatag_notReady); } if (!(media && media._blob)) { return void console.error($mt); } window.saveAs(media._blob.content, media.name); }; From 5d6ebdfee6174b5f7670098205a3d6d427cd92af Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 26 Nov 2020 18:15:36 +0100 Subject: [PATCH 069/201] Make autodownload size for mediatags configurable --- .../src/less2/include/sidebar-layout.less | 2 +- www/common/inner/common-mediatag.js | 9 ++- www/common/media-tag.js | 3 +- www/common/sframe-common.js | 6 ++ www/file/inner.js | 3 - www/settings/app-settings.less | 4 ++ www/settings/inner.js | 56 ++++++++++++++++++- 7 files changed, 76 insertions(+), 7 deletions(-) diff --git a/customize.dist/src/less2/include/sidebar-layout.less b/customize.dist/src/less2/include/sidebar-layout.less index ace7350df..4273b0b9a 100644 --- a/customize.dist/src/less2/include/sidebar-layout.less +++ b/customize.dist/src/less2/include/sidebar-layout.less @@ -118,7 +118,7 @@ //border-radius: 0 0.25em 0.25em 0; //border: 1px solid #adadad; border-left: 0px; - height: @variables_input-height; + height: 40px; margin: 0 !important; } } diff --git a/www/common/inner/common-mediatag.js b/www/common/inner/common-mediatag.js index 741529de0..86900f5bb 100644 --- a/www/common/inner/common-mediatag.js +++ b/www/common/inner/common-mediatag.js @@ -17,11 +17,18 @@ define([ var Nacl = window.nacl; // Configure MediaTags to use our local viewer + // This file is loaded by sframe-common so the following config is used in all the inner apps if (MediaTag) { MediaTag.setDefaultConfig('pdf', { viewer: '/common/pdfjs/web/viewer.html' }); + Messages.mediatag_saveButton = "Save"; // XXX + MediaTag.setDefaultConfig('download', { + text: Messages.download_mt_button, + textDl: Messages.mediatag_saveButton + }); } + MT.MediaTag = MediaTag; // Cache of the avatars outer html (including ) var avatars = {}; @@ -68,7 +75,7 @@ define([ childList: true, characterData: false }); - MediaTag($tag[0]).on('error', function (data) { + MediaTag($tag[0], {force: true}).on('error', function (data) { console.error(data); }); }; diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 4318ca8f6..56e0f6bc6 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -566,7 +566,8 @@ if (cfg.force) { dl(); return mediaObject; } - var maxSize = 5 * 1024 * 1024; + var maxSize = typeof(config.maxDownloadSize) === "number" ? config.maxDownloadSize + : (5 * 1024 * 1024); getFileSize(src, function (err, size) { if (err) { if (err === "XHR_ERROR 404") { diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 7a00516a5..328bd70c8 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -809,6 +809,12 @@ define([ var privateData = ctx.metadataMgr.getPrivateData(); funcs.addShortcuts(window, Boolean(privateData.app)); + var mt = Util.find(privateData, ['settings', 'general', 'mediatag-size']); + if (MT.MediaTag && typeof(mt) === "number") { + var maxMtSize = mt === -1 ? Infinity : mt * 1024 * 1024; + MT.MediaTag.setDefaultConfig('maxDownloadSize', maxMtSize); + } + try { var feedback = privateData.feedbackAllowed; Feedback.init(feedback); diff --git a/www/file/inner.js b/www/file/inner.js index 137ca54b1..6cc129de5 100644 --- a/www/file/inner.js +++ b/www/file/inner.js @@ -39,9 +39,6 @@ define([ var Nacl = window.nacl; var APP = window.APP = {}; - MediaTag.setDefaultConfig('download', { - text: Messages.download_mt_button - }); var andThen = function (common) { var $appContainer = $('#cp-app-file-content'); diff --git a/www/settings/app-settings.less b/www/settings/app-settings.less index 1f0da1263..0ac3d10f2 100644 --- a/www/settings/app-settings.less +++ b/www/settings/app-settings.less @@ -74,6 +74,10 @@ margin-right: 100%; } } + & > .fa { + align-self: center; + margin-right: -16px; + } } .cp-settings-info-block { [type="text"] { diff --git a/www/settings/inner.js b/www/settings/inner.js index 8882f5861..9ff57aae7 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -51,7 +51,7 @@ define([ 'cp-settings-info-block', 'cp-settings-displayname', 'cp-settings-language-selector', - 'cp-settings-resettips', + 'cp-settings-mediatag-size', 'cp-settings-change-password', 'cp-settings-delete' ], @@ -62,6 +62,7 @@ define([ 'cp-settings-userfeedback', ], 'drive': [ + 'cp-settings-resettips', 'cp-settings-drive-duplicate', 'cp-settings-thumbnails', 'cp-settings-drive-backup', @@ -576,6 +577,59 @@ define([ cb(form); }, true); + Messages.settings_mediatagSizeTitle = "Autodownload size in MegaBytes (MB)"; // XXX + Messages.settings_mediatagSizeHint = 'Maximum size for automatically loading media elements (images, videos, pdf) embedded into the pads. Elements bigger than the specified size can be loaded manually. Use "-1" to always load the media elements automatically.'; // XXX + makeBlock('mediatag-size', function(cb) { + var $inputBlock = $('
', { + 'class': 'cp-sidebarlayout-input-block', + }); + + var spinner; + var $input = $('', { + 'min': -1, + 'max': 1000, + type: 'number', + }).appendTo($inputBlock); + + var oldVal; + + var todo = function () { + var val = parseInt($input.val()); + if (val === oldVal) { return; } + if (typeof(val) !== 'number') { return UI.warn(Messages.error); } + spinner.spin(); + common.setAttribute(['general', 'mediatag-size'], val, function (err) { + if (err) { + spinner.hide(); + console.error(err); + return UI.warn(Messages.error); + } + spinner.done(); + UI.log(Messages.saved); + }); + }; + var $save = $(h('button.btn.btn-primary', Messages.settings_save)).appendTo($inputBlock); + spinner = UI.makeSpinner($inputBlock); + + $save.click(todo); + $input.on('keyup', function(e) { + if (e.which === 13) { todo(); } + }); + + common.getAttribute(['general', 'mediatag-size'], function(e, val) { + if (e) { return void console.error(e); } + if (typeof(val) !== 'number') { + oldVal = 5; + $input.val(5); + } else { + oldVal = val; + $input.val(val); + } + }); + + cb($inputBlock); + }, true); + // Security makeBlock('safe-links', function(cb) { From d4e6d869612e199a04479a339a6895152a445da3 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 27 Nov 2020 08:21:51 +0530 Subject: [PATCH 070/201] enforce asynchrony in new database offset methods --- lib/storage/file.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/storage/file.js b/lib/storage/file.js index 8cdfa747a..d890cb0b9 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -1222,17 +1222,18 @@ module.exports.create = function (conf, _cb) { // OFFSETS // these exist strictly as an optimization // you can always remove them without data loss - clearOffset: function (channelName, cb) { + clearOffset: function (channelName, _cb) { + var cb = Util.once(Util.mkAsync(_cb)); if (!isValidChannelId(channelName)) { return void cb(new Error('EINVAL')); } clearOffset(env, channelName, cb); }, writeOffset: function (channelName, data, _cb) { - var cb = Util.mkAsync(_cb); + var cb = Util.once(Util.mkAsync(_cb)); if (!isValidChannelId(channelName)) { return void cb(new Error('EINVAL')); } writeOffset(env, channelName, data, cb); }, getOffset: function (channelName, _cb) { - var cb = Util.mkAsync(_cb); + var cb = Util.once(Util.mkAsync(_cb)); if (!isValidChannelId(channelName)) { return void cb(new Error('EINVAL')); } getOffset(env, channelName, cb); }, From 58cdf21defb492b28cf90ddf7c2cdf90ff74d46b Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 27 Nov 2020 17:11:12 +0100 Subject: [PATCH 071/201] Store the blob cache in the outer domain --- www/common/inner/cache.js | 35 +++++++++++++++++++++++++++++++ www/common/media-tag.js | 30 +++++++++++++++++--------- www/common/sframe-common-outer.js | 16 ++++++++++++++ www/common/sframe-common.js | 7 +++++++ 4 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 www/common/inner/cache.js diff --git a/www/common/inner/cache.js b/www/common/inner/cache.js new file mode 100644 index 000000000..f14890797 --- /dev/null +++ b/www/common/inner/cache.js @@ -0,0 +1,35 @@ +define([ +], function () { + var S = {}; + + S.create = function (sframeChan) { + var getBlobCache = function (id, cb) { + sframeChan.query('Q_GET_BLOB_CACHE', {id:id}, function (err, data) { + var e = err || (data && data.error); + if (e) { return void cb(e); } + if (!data || typeof(data) !== "object") { return void cb('EINVAL'); } + var arr = Object.keys(data).map(function (i) { return data[i]; }); + var u8 = Uint8Array.from(arr); + cb(null, u8); + }); + }; + var setBlobCache = function (id, u8, cb) { + sframeChan.query('Q_SET_BLOB_CACHE', { + id: id, + u8: u8 + }, function (err, data) { + var e = err || (data && data.error) || undefined; + cb(e); + }); + }; + + + return { + getBlobCache: getBlobCache, + setBlobCache: setBlobCache + }; + }; + + return S; +}); + diff --git a/www/common/media-tag.js b/www/common/media-tag.js index e4ec9ce2a..b58375302 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -1,5 +1,5 @@ (function (window) { -var factory = function (Cache) { +var factory = function () { var Promise = window.Promise; var cache; var cypherChunkLength = 131088; @@ -199,6 +199,19 @@ var factory = function (Cache) { return cacheKey; }; + var getBlobCache = function (id, cb) { + if (!config.Cache || typeof(config.Cache.getBlobCache) !== "function") { + return void cb('EINVAL'); + } + config.Cache.getBlobCache(id, cb); + }; + var setBlobCache = function (id, u8, cb) { + if (!config.Cache || typeof(config.Cache.setBlobCache) !== "function") { + return void cb('EINVAL'); + } + config.Cache.setBlobCache(id, u8, cb); + }; + var getFileSize = function (src, _cb) { var cb = function (e, res) { _cb(e, res); @@ -224,7 +237,7 @@ var factory = function (Cache) { if (!cacheKey) { return void check(); } - Cache.getBlobCache(cacheKey, function (err, u8) { + getBlobCache(cacheKey, function (err, u8) { if (err || !u8) { return void check(); } cb(null, 0); }); @@ -263,7 +276,7 @@ var factory = function (Cache) { if (arrayBuffer) { var u8 = new Uint8Array(arrayBuffer); if (cacheKey) { - return void Cache.setBlobCache(cacheKey, u8, function () { + return void setBlobCache(cacheKey, u8, function () { cb(null, u8); }); } @@ -276,7 +289,7 @@ var factory = function (Cache) { if (!cacheKey) { return void fetch(); } - Cache.getBlobCache(cacheKey, function (err, u8) { + getBlobCache(cacheKey, function (err, u8) { if (err || !u8) { return void fetch(); } cb(null, u8); }); @@ -628,15 +641,12 @@ var factory = function (Cache) { }; if (typeof(module) !== 'undefined' && module.exports) { - module.exports = factory( - require("./outer/cache-store.js") - ); + module.exports = factory(); } else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) { define([ - '/common/outer/cache-store.js', '/bower_components/es6-promise/es6-promise.min.js' - ], function (Cache) { - return factory(Cache); + ], function () { + return factory(); }); } else { // unsupported initialization diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index a46a94837..12327c720 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -680,6 +680,22 @@ define([ }); }); + sframeChan.on('Q_GET_BLOB_CACHE', function (data, cb) { + Utils.Cache.getBlobCache(data.id, function (err, obj) { + if (err) { return void cb({error: err}); } + cb(obj); + }); + }); + sframeChan.on('Q_SET_BLOB_CACHE', function (data, cb) { + if (!data || !data.u8 || typeof(data.u8) !== "object") { return void cb({error: 'EINVAL'}); } + var arr = Object.keys(data.u8).map(function (i) { return data.u8[i]; }); + var u8 = Uint8Array.from(arr); + Utils.Cache.setBlobCache(data.id, u8, function (err) { + if (err) { return void cb({error: err}); } + cb(); + }); + }); + sframeChan.on('Q_GET_ATTRIBUTE', function (data, cb) { Cryptpad.getAttribute(data.key, function (e, data) { cb({ diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 328bd70c8..67ede0d64 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -11,6 +11,7 @@ define([ '/common/sframe-common-codemirror.js', '/common/sframe-common-cursor.js', '/common/sframe-common-mailbox.js', + '/common/inner/cache.js', '/common/inner/common-mediatag.js', '/common/metadata-manager.js', @@ -36,6 +37,7 @@ define([ CodeMirror, Cursor, Mailbox, + Cache, MT, MetadataMgr, AppConfig, @@ -815,6 +817,11 @@ define([ MT.MediaTag.setDefaultConfig('maxDownloadSize', maxMtSize); } + if (MT.MediaTag && Cache) { + var cache = Cache.create(ctx.sframeChan); + MT.MediaTag.setDefaultConfig('Cache', cache); + } + try { var feedback = privateData.feedbackAllowed; Feedback.init(feedback); From 775630696680233a913d277ac3f4a85cc69229b4 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 27 Nov 2020 17:19:03 +0100 Subject: [PATCH 072/201] Clear cache on logout --- www/common/outer/cache-store.js | 5 +++-- www/common/outer/local-store.js | 12 ++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index 4d5e30ed2..6b6c8604e 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -82,8 +82,9 @@ define([ }); }; - S.clear = function () { - cache.clear(); + S.clear = function (cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); + cache.clear(cb); }; return S; diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index 0aff1f9ce..645493de5 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -1,9 +1,10 @@ define([ '/common/common-constants.js', '/common/common-hash.js', + '/common/outer/cache-store.js', '/bower_components/localforage/dist/localforage.min.js', '/customize/application_config.js', -], function (Constants, Hash, localForage, AppConfig) { +], function (Constants, Hash, Cache, localForage, AppConfig) { var LocalStore = {}; LocalStore.setThumbnail = function (key, value, cb) { @@ -119,7 +120,14 @@ define([ return void AppConfig.customizeLogout(cb); } - if (cb) { cb(); } + cb = cb || function () {}; + + try { + Cache.clear(cb); + } catch (e) { + console.error(e); + cb(); + } }; var loginHandlers = []; LocalStore.loginReload = function () { From b618f17395cf5ae6639521dda350613c2edaa3d6 Mon Sep 17 00:00:00 2001 From: Ludovic Dubost Date: Sun, 29 Nov 2020 16:44:29 +0100 Subject: [PATCH 073/201] Fix start of disabled oodoc and ooslide apps --- www/oodoc/inner.html | 8 +++++--- www/ooslide/inner.html | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/www/oodoc/inner.html b/www/oodoc/inner.html index 884ae5a00..1891839ff 100644 --- a/www/oodoc/inner.html +++ b/www/oodoc/inner.html @@ -9,9 +9,11 @@
-
-
- +
+
+
+ +
diff --git a/www/ooslide/inner.html b/www/ooslide/inner.html index e7c4e111f..53833e600 100644 --- a/www/ooslide/inner.html +++ b/www/ooslide/inner.html @@ -9,9 +9,11 @@
-
-
- +
+
+
+ +
From 82c869f4cd494c255cec5e86ce30dde8b2042bf9 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 30 Nov 2020 12:07:03 +0100 Subject: [PATCH 074/201] Add cryptget and blob cache --- www/common/common-util.js | 79 ++++++++++++++++++++++++-------- www/common/cryptget.js | 8 +++- www/common/cryptpad-common.js | 5 +- www/common/make-backup.js | 14 +++--- www/common/onlyoffice/inner.js | 2 +- www/common/sframe-common-file.js | 1 + www/common/sframe-common.js | 11 +++-- www/settings/inner.js | 2 +- www/teams/inner.js | 2 +- 9 files changed, 88 insertions(+), 36 deletions(-) diff --git a/www/common/common-util.js b/www/common/common-util.js index 435a72280..603e38a30 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -274,32 +274,71 @@ // given a path, asynchronously return an arraybuffer - Util.fetch = function (src, cb, progress) { - var CB = Util.once(cb); + var getCacheKey = function (src) { + var _src = src.replace(/(\/)*$/, ''); // Remove trailing slashes + var idx = _src.lastIndexOf('/'); + var cacheKey = _src.slice(idx+1); + if (!/^[a-f0-9]{48}$/.test(cacheKey)) { cacheKey = undefined; } + return cacheKey; + }; + Util.fetch = function (src, cb, progress, cache) { + var CB = Util.once(Util.mkAsync(cb)); + + var cacheKey = getCacheKey(src); + var getBlobCache = function (id, cb) { + if (!cache || typeof(cache.getBlobCache) !== "function") { return void cb('EINVAL'); } + cache.getBlobCache(id, cb); + }; + var setBlobCache = function (id, u8, cb) { + if (!cache || typeof(cache.setBlobCache) !== "function") { return void cb('EINVAL'); } + cache.setBlobCache(id, u8, cb); + }; - var xhr = new XMLHttpRequest(); - xhr.open("GET", src, true); - if (progress) { - xhr.addEventListener("progress", function (evt) { - if (evt.lengthComputable) { - var percentComplete = evt.loaded / evt.total; - progress(percentComplete); - } - }, false); - } - xhr.responseType = "arraybuffer"; - xhr.onerror = function (err) { CB(err); }; - xhr.onload = function () { - if (/^4/.test(''+this.status)) { - return CB('XHR_ERROR'); + var xhr; + + var fetch = function () { + xhr = new XMLHttpRequest(); + xhr.open("GET", src, true); + if (progress) { + xhr.addEventListener("progress", function (evt) { + if (evt.lengthComputable) { + var percentComplete = evt.loaded / evt.total; + progress(percentComplete); + } + }, false); } - return void CB(void 0, new Uint8Array(xhr.response)); + xhr.responseType = "arraybuffer"; + xhr.onerror = function (err) { CB(err); }; + xhr.onload = function () { + if (/^4/.test(''+this.status)) { + return CB('XHR_ERROR'); + } + + var arrayBuffer = xhr.response; + if (arrayBuffer) { + var u8 = new Uint8Array(arrayBuffer); + if (cacheKey) { + return void setBlobCache(cacheKey, u8, function () { + CB(null, u8); + }); + } + return void CB(void 0, u8); + } + CB('ENOENT'); + }; + xhr.send(null); }; - xhr.send(null); + + if (!cacheKey) { return void fetch(); } + + getBlobCache(cacheKey, function (err, u8) { + if (err || !u8) { return void fetch(); } + CB(void 0, u8); + }); return { cancel: function () { - if (xhr.abort) { xhr.abort(); } + if (xhr && xhr.abort) { xhr.abort(); } } }; }; diff --git a/www/common/cryptget.js b/www/common/cryptget.js index e394788d7..1cbd5056e 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -6,10 +6,11 @@ define([ '/common/common-hash.js', '/common/common-realtime.js', '/common/outer/network-config.js', + '/common/outer/cache-store.js', '/common/pinpad.js', '/bower_components/nthen/index.js', '/bower_components/chainpad/chainpad.dist.js', -], function (Crypto, CPNetflux, Netflux, Util, Hash, Realtime, NetConfig, Pinpad, nThen) { +], function (Crypto, CPNetflux, Netflux, Util, Hash, Realtime, NetConfig, Cache, Pinpad, nThen) { var finish = function (S, err, doc) { if (S.done) { return; } S.cb(err, doc); @@ -92,7 +93,8 @@ define([ validateKey: secret.keys.validateKey || undefined, crypto: Crypto.createEncryptor(secret.keys), logLevel: 0, - initialState: opt.initialState + initialState: opt.initialState, + Cache: Cache }; return config; }; @@ -132,9 +134,11 @@ define([ }; config.onError = function (info) { + console.warn(info); finish(Session, info.error); }; config.onChannelError = function (info) { + console.error(info); finish(Session, info.error); }; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 5dd029def..46f569a3e 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -3,6 +3,7 @@ define([ '/customize/messages.js', '/common/common-util.js', '/common/common-hash.js', + '/common/outer/cache-store.js', '/common/common-messaging.js', '/common/common-constants.js', '/common/common-feedback.js', @@ -14,7 +15,7 @@ define([ '/customize/application_config.js', '/bower_components/nthen/index.js', -], function (Config, Messages, Util, Hash, +], function (Config, Messages, Util, Hash, Cache, Messaging, Constants, Feedback, Visible, UserObject, LocalStore, Channel, Block, AppConfig, Nthen) { @@ -760,7 +761,7 @@ define([ u8 = _u8; }), function (progress) { onProgress(progress * 50); - }); + }, Cache); }).nThen(function (waitFor) { require(["/file/file-crypto.js"], waitFor(function (FileCrypto) { FileCrypto.decrypt(u8, key, waitFor(function (err, _res) { diff --git a/www/common/make-backup.js b/www/common/make-backup.js index 88602c8bb..9ba925fd7 100644 --- a/www/common/make-backup.js +++ b/www/common/make-backup.js @@ -1,17 +1,18 @@ define([ 'jquery', - '/common/cryptget.js', '/file/file-crypto.js', '/common/common-hash.js', '/common/common-util.js', '/common/common-interface.js', '/common/hyperscript.js', '/common/common-feedback.js', + '/common/inner/cache.js', '/customize/messages.js', '/bower_components/nthen/index.js', '/bower_components/saferphore/index.js', '/bower_components/jszip/dist/jszip.min.js', -], function ($, Crypt, FileCrypto, Hash, Util, UI, h, Feedback, Messages, nThen, Saferphore, JsZip) { +], function ($, FileCrypto, Hash, Util, UI, h, Feedback, + Cache, Messages, nThen, Saferphore, JsZip) { var saveAs = window.saveAs; var sanitize = function (str) { @@ -89,7 +90,7 @@ define([ if (updateProgress && updateProgress.progress) { updateProgress.progress(data); } - }); + }, ctx.cache); var cancel = function () { cancelled = true; @@ -291,7 +292,7 @@ define([ }; // Main function. Create the empty zip and fill it starting from drive.root - var create = function (data, getPad, fileHost, cb, progress) { + var create = function (data, getPad, fileHost, cb, progress, cache) { if (!data || !data.uo || !data.uo.drive) { return void cb('EEMPTY'); } var sem = Saferphore.create(5); var ctx = { @@ -305,7 +306,8 @@ define([ sem: sem, updateProgress: progress, max: 0, - done: 0 + done: 0, + cache: cache }; var filesData = data.sharedFolderId && ctx.sf[data.sharedFolderId] ? ctx.sf[data.sharedFolderId].filesData : ctx.data.filesData; progress('reading', -1); @@ -356,7 +358,7 @@ define([ else if (state === "done") { updateProgress.folderProgress(3); } - }); + }, ctx.cache); }; var createExportUI = function (origin) { diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index c9efb1eda..976daa887 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1419,7 +1419,7 @@ define([ console.error(e); callback(""); } - }); + }, void 0, common.getCache()); }; APP.docEditor = new window.DocsAPI.DocEditor("cp-app-oo-placeholder-a", APP.ooconfig); diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index 8a5be3764..13fea31b4 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -654,6 +654,7 @@ define([ fileHost: privateData.fileHost, get: common.getPad, sframeChan: sframeChan, + cache: common.getCache() }; var dl = downloadFunction(ctx, data, function (err, obj) { diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 67ede0d64..9329b467a 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -607,6 +607,10 @@ define([ }); }; + funcs.getCache = function () { + return ctx.cache; + }; + /* funcs.storeLinkToClipboard = function (readOnly, cb) { ctx.sframeChan.query('Q_STORE_LINK_TO_CLIPBOARD', readOnly, function (err) { if (cb) { cb(err); } @@ -805,6 +809,8 @@ define([ modules[type].onEvent(obj.data); }); + ctx.cache = Cache.create(ctx.sframeChan); + ctx.metadataMgr.onReady(waitFor()); }).nThen(function () { @@ -817,9 +823,8 @@ define([ MT.MediaTag.setDefaultConfig('maxDownloadSize', maxMtSize); } - if (MT.MediaTag && Cache) { - var cache = Cache.create(ctx.sframeChan); - MT.MediaTag.setDefaultConfig('Cache', cache); + if (MT.MediaTag && ctx.cache) { + MT.MediaTag.setDefaultConfig('Cache', ctx.cache); } try { diff --git a/www/settings/inner.js b/www/settings/inner.js index 9ff57aae7..9051d3c34 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -831,7 +831,7 @@ define([ Feedback.send('FULL_DRIVE_EXPORT_COMPLETE'); saveAs(blob, filename); }, errors); - }, ui.update); + }, ui.update, common.getCache()); ui.onCancel(function() { ui.close(); bu.stop(); diff --git a/www/teams/inner.js b/www/teams/inner.js index 070d11825..7af3d7afc 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -1071,7 +1071,7 @@ define([ Feedback.send('FULL_TEAMDRIVE_EXPORT_COMPLETE'); saveAs(blob, filename); }, errors); - }, ui.update); + }, ui.update, common.getCache); ui.onCancel(function() { ui.close(); bu.stop(); From d4055f6ef5276dd7eb7af4e678a2d4815a550c25 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 30 Nov 2020 14:40:07 +0100 Subject: [PATCH 075/201] Improve blob cache --- www/common/inner/cache.js | 6 +++--- www/common/inner/common-mediatag.js | 4 ++-- www/common/outer/upload.js | 14 ++++++++++---- www/common/sframe-common-outer.js | 5 +++-- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/www/common/inner/cache.js b/www/common/inner/cache.js index f14890797..7db9635b4 100644 --- a/www/common/inner/cache.js +++ b/www/common/inner/cache.js @@ -8,15 +8,15 @@ define([ var e = err || (data && data.error); if (e) { return void cb(e); } if (!data || typeof(data) !== "object") { return void cb('EINVAL'); } - var arr = Object.keys(data).map(function (i) { return data[i]; }); - var u8 = Uint8Array.from(arr); + var u8 = Uint8Array.from(data); cb(null, u8); }); }; var setBlobCache = function (id, u8, cb) { + var array = [].slice.call(u8); sframeChan.query('Q_SET_BLOB_CACHE', { id: id, - u8: u8 + u8: array }, function (err, data) { var e = err || (data && data.error) || undefined; cb(e); diff --git a/www/common/inner/common-mediatag.js b/www/common/inner/common-mediatag.js index 86900f5bb..72cb1e37b 100644 --- a/www/common/inner/common-mediatag.js +++ b/www/common/inner/common-mediatag.js @@ -24,8 +24,8 @@ define([ }); Messages.mediatag_saveButton = "Save"; // XXX MediaTag.setDefaultConfig('download', { - text: Messages.download_mt_button, - textDl: Messages.mediatag_saveButton + text: Messages.mediatag_saveButton, + textDl: Messages.download_mt_button, }); } MT.MediaTag = MediaTag; diff --git a/www/common/outer/upload.js b/www/common/outer/upload.js index 62f305612..7115b5d4e 100644 --- a/www/common/outer/upload.js +++ b/www/common/outer/upload.js @@ -1,9 +1,11 @@ define([ '/file/file-crypto.js', '/common/common-hash.js', + '/common/common-util.js', + '/common/outer/cache-store.js', '/bower_components/nthen/index.js', '/bower_components/tweetnacl/nacl-fast.min.js', -], function (FileCrypto, Hash, nThen) { +], function (FileCrypto, Hash, Util, Cache, nThen) { var Nacl = window.nacl; var module = {}; @@ -31,9 +33,11 @@ define([ }; var actual = 0; + var encryptedArr = [];; var again = function (err, box) { if (err) { onError(err); } if (box) { + encryptedArr.push(box); actual += box.length; var progressValue = (actual / estimate * 100); progressValue = Math.min(progressValue, 100); @@ -55,9 +59,11 @@ define([ var uri = ['', 'blob', id.slice(0,2), id].join('/'); console.log("encrypted blob is now available as %s", uri); - - - cb(); + var box_u8 = Util.uint8ArrayJoin(encryptedArr); + Cache.setBlobCache(id, box_u8, function (err) { + if (err) { console.warn(err); } + cb(); + }); }); }; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 12327c720..49d52c37b 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -683,12 +683,13 @@ define([ sframeChan.on('Q_GET_BLOB_CACHE', function (data, cb) { Utils.Cache.getBlobCache(data.id, function (err, obj) { if (err) { return void cb({error: err}); } - cb(obj); + var arr = [].slice.call(obj); + cb(arr); }); }); sframeChan.on('Q_SET_BLOB_CACHE', function (data, cb) { if (!data || !data.u8 || typeof(data.u8) !== "object") { return void cb({error: 'EINVAL'}); } - var arr = Object.keys(data.u8).map(function (i) { return data.u8[i]; }); + var arr = data.u8; var u8 = Uint8Array.from(arr); Utils.Cache.setBlobCache(data.id, u8, function (err) { if (err) { return void cb({error: err}); } From 004c242f63a96dd737293edaa8288acdd8500e81 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 30 Nov 2020 17:19:13 +0100 Subject: [PATCH 076/201] Fix duplicate mediatag issues --- www/common/diffMarked.js | 7 +++-- www/common/media-tag.js | 64 ++++++++++++++++++++++++++++---------- www/common/outer/upload.js | 2 +- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index fd3fcafb3..64768f2cb 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -658,7 +658,7 @@ define([ $(contextMenu.menu).find('li').show(); contextMenu.show(e); }); - if ($mt.children().length) { + if ($mt.children().length && $mt[0]._mediaObject) { $mt.off('click dblclick preview'); $mt.on('preview', onPreview($mt)); if ($mt.find('img').length) { @@ -672,10 +672,10 @@ define([ var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList') { - var list_values = slice(mutation.target.children) + var list_values = slice(el.children) .map(function (el) { return el.outerHTML; }) .join(''); - mediaMap[mutation.target.getAttribute('src')] = list_values; + mediaMap[el.getAttribute('src')] = list_values; if (mediaObject.complete) { observer.disconnect(); } } }); @@ -689,6 +689,7 @@ define([ }); observer.observe(el, { attributes: false, + subtree: true, childList: true, characterData: false }); diff --git a/www/common/media-tag.js b/www/common/media-tag.js index b58375302..e391a2530 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -126,6 +126,8 @@ var factory = function () { var makeProgressBar = function (cfg, mediaObject) { // XXX CSP: we'll need to add style in cryptpad's less + if (mediaObject.bar) { return; } + mediaObject.bar = true; var style = (function(){/* .mediatag-progress-container { position: relative; @@ -181,10 +183,15 @@ var factory = function () { var makeDownloadButton = function (cfg, mediaObject, size, cb) { var btn = document.createElement('button'); btn.setAttribute('class', 'btn btn-default'); + btn.setAttribute('data-dl', '1'); btn.innerHTML = '' + cfg.download.textDl + ' (' + size + 'MB)'; btn.addEventListener('click', function () { makeProgressBar(cfg, mediaObject); + var a = document.querySelectorAll('media-tag[src="'+mediaObject.tag.getAttribute('src')+'"] button[data-dl]'); + for(var i = 0; i < a.length; i++) { + if (a[i] !== btn) { a[i].click(); } + } cb(); }); mediaObject.tag.innerHTML = ''; @@ -582,32 +589,50 @@ var factory = function () { emit('error', err); }; + var getCache = function () { + var c = cache[uid]; + if (!c || !c.promise || !c.mt) { console.error(uid);return; } + return c; + }; + var dl = function () { // Download the encrypted blob - cache[uid] = cache[uid] || new Promise(function (resolve, reject) { - download(src, function (err, u8Encrypted) { - if (err) { - return void reject(err); - } - // Decrypt the blob - decrypt(u8Encrypted, strKey, function (errDecryption, u8Decrypted) { - if (errDecryption) { - return void reject(errDecryption); + cache[uid] = getCache() || { + promise: new Promise(function (resolve, reject) { + download(src, function (err, u8Encrypted) { + if (err) { + return void reject(err); } - // Cache and display the decrypted blob - resolve(u8Decrypted); + // Decrypt the blob + decrypt(u8Encrypted, strKey, function (errDecryption, u8Decrypted) { + if (errDecryption) { + return void reject(errDecryption); + } + // Cache and display the decrypted blob + resolve(u8Decrypted); + }, function (progress) { + emit('progress', { + progress: 50+0.5*progress + }); + }); }, function (progress) { emit('progress', { - progress: 50+0.5*progress + progress: 0.5*progress }); }); - }, function (progress) { + }), + mt: mediaObject + }; + if (cache[uid].mt !== mediaObject) { + // Add progress for other instances of this tag + cache[uid].mt.on('progress', function (obj) { + if (!mediaObject.bar) { makeProgressBar(cfg, mediaObject); } emit('progress', { - progress: 0.5*progress + progress: obj.progress }); }); - }); - cache[uid].then(function (u8) { + } + cache[uid].promise.then(function (u8) { end(u8); }, function (err) { error(err); @@ -622,7 +647,12 @@ var factory = function () { if (err) { return void error(err); } - if (!size || size < maxSize) { return void dl(); } + // If the size is smaller than the autodownload limit, load the blob. + // If the blob is already loaded or being loaded, don't show the button. + if (!size || size < maxSize || getCache()) { + makeProgressBar(cfg, mediaObject); + return void dl(); + } var sizeMb = Math.round(10 * size / 1024 / 1024) / 10; makeDownloadButton(cfg, mediaObject, sizeMb, dl); }); diff --git a/www/common/outer/upload.js b/www/common/outer/upload.js index 7115b5d4e..0615ed7a7 100644 --- a/www/common/outer/upload.js +++ b/www/common/outer/upload.js @@ -33,7 +33,7 @@ define([ }; var actual = 0; - var encryptedArr = [];; + var encryptedArr = []; var again = function (err, box) { if (err) { onError(err); } if (box) { From 682e722d72a3a0397b952038f32f2d77888aaaab Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 30 Nov 2020 18:00:34 +0100 Subject: [PATCH 077/201] sframeChan Uint8Array without conversion --- www/common/inner/cache.js | 10 ++++------ www/common/media-tag.js | 2 +- www/common/outer/worker-channel.js | 19 +++++++++++-------- www/common/sframe-common-outer.js | 7 ++----- www/secureiframe/main.js | 2 +- 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/www/common/inner/cache.js b/www/common/inner/cache.js index 7db9635b4..be5b781c2 100644 --- a/www/common/inner/cache.js +++ b/www/common/inner/cache.js @@ -8,19 +8,17 @@ define([ var e = err || (data && data.error); if (e) { return void cb(e); } if (!data || typeof(data) !== "object") { return void cb('EINVAL'); } - var u8 = Uint8Array.from(data); - cb(null, u8); - }); + cb(null, data); + }, { raw: true }); }; var setBlobCache = function (id, u8, cb) { - var array = [].slice.call(u8); sframeChan.query('Q_SET_BLOB_CACHE', { id: id, - u8: array + u8: u8 }, function (err, data) { var e = err || (data && data.error) || undefined; cb(e); - }); + }, { raw: true }); }; diff --git a/www/common/media-tag.js b/www/common/media-tag.js index e391a2530..c855df328 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -591,7 +591,7 @@ var factory = function () { var getCache = function () { var c = cache[uid]; - if (!c || !c.promise || !c.mt) { console.error(uid);return; } + if (!c || !c.promise || !c.mt) { return; } return c; }; diff --git a/www/common/outer/worker-channel.js b/www/common/outer/worker-channel.js index 545ce435b..a359d8836 100644 --- a/www/common/outer/worker-channel.js +++ b/www/common/outer/worker-channel.js @@ -65,11 +65,13 @@ define([ cb(undefined, data.content, msg); }; evReady.reg(function () { - postMsg(JSON.stringify({ + var toSend = { txid: txid, content: content, - q: q - })); + q: q, + raw: opts.raw + }; + postMsg(opts.raw ? toSend : JSON.stringify(toSend)); }); }; @@ -84,12 +86,13 @@ define([ // If the type is a query, your handler will be invoked with a reply function that takes // one argument (the content to reply with). chan.on = function (queryType, handler, quiet) { - var h = function (data, msg) { + var h = function (data, msg, raw) { handler(data.content, function (replyContent) { - postMsg(JSON.stringify({ + var toSend = { txid: data.txid, content: replyContent - })); + }; + postMsg(raw ? toSend : JSON.stringify(toSend)); }, msg); }; (handlers[queryType] = handlers[queryType] || []).push(h); @@ -150,7 +153,7 @@ define([ onMsg.reg(function (msg) { if (!chanLoaded) { return; } if (!msg.data || msg.data === '_READY') { return; } - var data = JSON.parse(msg.data); + var data = typeof(msg.data) === "object" ? msg.data : JSON.parse(msg.data); if (typeof(data.ack) !== "undefined") { if (acks[data.txid]) { acks[data.txid](!data.ack); } } else if (typeof(data.q) === 'string') { @@ -163,7 +166,7 @@ define([ })); } handlers[data.q].forEach(function (f) { - f(data || JSON.parse(msg.data), msg); + f(data || JSON.parse(msg.data), msg, data && data.raw); data = undefined; }); } else { diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 49d52c37b..e43c169ed 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -683,15 +683,12 @@ define([ sframeChan.on('Q_GET_BLOB_CACHE', function (data, cb) { Utils.Cache.getBlobCache(data.id, function (err, obj) { if (err) { return void cb({error: err}); } - var arr = [].slice.call(obj); - cb(arr); + cb(obj); }); }); sframeChan.on('Q_SET_BLOB_CACHE', function (data, cb) { if (!data || !data.u8 || typeof(data.u8) !== "object") { return void cb({error: 'EINVAL'}); } - var arr = data.u8; - var u8 = Uint8Array.from(arr); - Utils.Cache.setBlobCache(data.id, u8, function (err) { + Utils.Cache.setBlobCache(data.id, data.u8, function (err) { if (err) { return void cb({error: err}); } cb(); }); diff --git a/www/secureiframe/main.js b/www/secureiframe/main.js index 1bf5e3e7b..62175c5a0 100644 --- a/www/secureiframe/main.js +++ b/www/secureiframe/main.js @@ -33,7 +33,7 @@ define([ // loading screen setup. var done = waitFor(); var onMsg = function (msg) { - var data = JSON.parse(msg.data); + var data = typeof(msg.data) === "object" ? msg.data : JSON.parse(msg.data); if (data.q !== 'READY') { return; } window.removeEventListener('message', onMsg); var _done = done; From 02a4de58510ad7e9309b6757e99e8164cef91a58 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 30 Nov 2020 18:41:28 +0100 Subject: [PATCH 078/201] Update customLimits example #643 --- config/config.example.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.example.js b/config/config.example.js index 32bcb20cf..fd9902a69 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -228,12 +228,12 @@ module.exports = { */ /* customLimits: { - "https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=": { + "[cryptpad-user1@my.awesome.website/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=]": { limit: 20 * 1024 * 1024 * 1024, plan: 'insider', note: 'storage space donated by my.awesome.website' }, - "https://my.awesome.website/user/#/1/cryptpad-user2/GdflkgdlkjeworijfkldfsdflkjeEAsdlEnkbx1vVOo=": { + "[cryptpad-user2@my.awesome.website/GdflkgdlkjeworijfkldfsdflkjeEAsdlEnkbx1vVOo=]": { limit: 10 * 1024 * 1024 * 1024, plan: 'insider', note: 'storage space donated by my.awesome.website' From 8874f2fdb1b68e88a655d9868abbb0710cb4af06 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 1 Dec 2020 12:05:21 +0100 Subject: [PATCH 079/201] Prevent chainpad issues to reset spreadsheets content --- www/common/onlyoffice/inner.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 28d06dd32..9844d52d0 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -2166,6 +2166,12 @@ define([ url: newLatest.file }, function () { }); newDoc = !content.hashes || Object.keys(content.hashes).length === 0; + } else if (!privateData.isNewFile) { + // This is an empty doc but not a new file: error + UI.errorLoadingScreen(Messages.unableToDisplay, false, function () { + common.gotoURL(''); + }); + throw new Error("Empty chainpad for a non-empty doc"); } else { Title.updateTitle(Title.defaultTitle); } From 0dc7529d4c09cdd7c395c52cffc16ecb1c588950 Mon Sep 17 00:00:00 2001 From: Weblate Date: Tue, 1 Dec 2020 15:00:40 +0100 Subject: [PATCH 080/201] Translated using Weblate (English) Currently translated at 100.0% (1375 of 1375 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1374 of 1374 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ --- www/common/translations/messages.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index f57abe24e..3657d6b58 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -1469,5 +1469,7 @@ "tag_add": "Add", "tag_edit": "Edit", "error_unhelpfulScriptError": "Script Error: See browser console for details", - "documentID": "Document identifier" + "documentID": "Document identifier", + "unableToDisplay": "Unable to display the document. Please press Esc to reload the page.", + "errorPopupBlocked": "CryptPad needs to be able to open new tabs to operate. Please allow popup windows in your browser's address bar. These windows will never be used to show you advertising." } From 99bb5e67f14bf7e1dddcb17a14cdccba9f2efc0b Mon Sep 17 00:00:00 2001 From: Weblate Date: Tue, 1 Dec 2020 15:00:41 +0100 Subject: [PATCH 081/201] Translated using Weblate (Japanese) Currently translated at 30.6% (421 of 1373 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/ja/ --- www/common/translations/messages.ja.json | 71 +++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/www/common/translations/messages.ja.json b/www/common/translations/messages.ja.json index 395aefb64..79e1b57f8 100644 --- a/www/common/translations/messages.ja.json +++ b/www/common/translations/messages.ja.json @@ -249,7 +249,7 @@ "settings_autostoreMaybe": "手動 (確認しない)", "settings_autostoreNo": "手動 (常に確認する)", "settings_autostoreHint": "自動 あなたがアクセスしたすべてのパッドを、あなたの CryptDrive に保存します。
手動 (常に確認する) まだ保存していないパッドにアクセスした場合に、あなたの CryptDrive に保存するかどうか尋ねます。
手動 (確認しない) アクセス先のパッドがあなたの CryptDrive に自動的に保存されなくなります。保存オプションは表示されなくなります。", - "settings_userFeedback": "ユーザーフィードバックを有効化", + "settings_userFeedback": "ユーザーフィードバックを有効にする", "settings_userFeedbackHint2": "あなたのパッドのコンテンツがサーバーと共有されることはありません。", "settings_userFeedbackHint1": "CryptPad は、あなたの経験を向上させる方法を知るために、サーバーにいくつかの非常に基本的なフィードバックを提供します。 ", "settings_userFeedbackTitle": "フィードバック", @@ -356,5 +356,72 @@ "crowdfunding_button": "CryptPad を支援", "crowdfunding_home1": "CryptPad はあなたの支援を必要としています!", "policy_choices_vpn": "私たちがホストするインスタンスを使用したいが、IP アドレスを私たちに公開したくない場合は、Tor Browser または VPN を使用してあなたの IP アドレスを保護できます。", - "contacts_removeHistoryTitle": "チャット履歴を削除" + "contacts_removeHistoryTitle": "チャット履歴を削除", + "properties_passwordSuccessFile": "パスワードは正常に変更されました。", + "drive_sfPasswordError": "誤ったパスワードです", + "team_title": "チーム: {0}", + "password_error": "パッドが存在しません!
このエラーは「誤ったパスワードが入力された」場合、または「パッドがサーバーから削除された」場合に発生します。", + "password_error_seed": "パッドが存在しません!
このエラーは「パスワードが追加・変更された」場合、または「パッドがサーバーから削除された」場合に発生します。", + "password_submit": "送信", + "password_placeholder": "パスワードを入力...", + "password_info": "開こうとしているパッドが存在しないか、パスワードで保護されています。コンテンツにアクセスするには、正しいパスワードを入力してください。", + "properties_confirmNew": "パスワードを追加すると、このパッドの URL が変更され、履歴が削除されます。パスワードを知らないユーザーは、このパッドへアクセスできなくなります。続行してよろしいですか?", + "properties_changePassword": "パスワードの変更", + "password_show": "表示", + "properties_addPassword": "パスワードの追加", + "history_close": "閉じる", + "history_restore": "復元", + "fm_emptyTrashOwned": "ごみ箱に、あなたが所有しているドキュメントが入っています。あなたのドライブからのみ削除するか、すべてのユーザーから完全削除するかを選択できます。", + "access_destroyPad": "このドキュメントまたはフォルダを完全に削除する", + "accessButton": "アクセス", + "access_allow": "リスト", + "makeACopy": "コピーを作成", + "trimHistory_noHistory": "削除可能な履歴がありません", + "areYouSure": "クリックして実行", + "settings_safeLinksCheckbox": "安全なリンクを有効にする", + "settings_safeLinksTitle": "安全なリンク", + "settings_safeLinksHint": "CryptPad では、あなたのパッドを解読するための鍵がリンクに含まれています。これは、あなたのブラウザの閲覧履歴にアクセスできる人が、潜在的にあなたの CryptPad のデータを解読・閲覧できることを意味します。この「ブラウザの閲覧履歴にアクセスできる人」には、デバイス間で履歴を同期させる侵入的なブラウザ拡張機能やブラウザが含まれます。「安全なリンク」を有効にすると、鍵がブラウザの閲覧履歴に残ったり、アドレスバーに表示されたりするのを可能な限り防ぐことができます。この機能を有効にした上で共有メニューを使用することを強く推奨します。", + "settings_autostoreTitle": "CryptDrive へのパッドの保存", + "settings_logoutEverywhereConfirm": "すべてのデバイスでログインが取り消されるため、今後利用する際にもう一度ログインするよう求められます。続行しますか?", + "settings_logoutEverywhere": "他のすべてのウェブセッションからログアウトします", + "settings_logoutEverywhereTitle": "リモートセッションを閉じる", + "loading_state_5": "ドキュメントを再構築", + "loading_state_4": "チームを読み込み", + "loading_state_3": "共有フォルダを読み込み", + "loading_state_2": "コンテンツを更新", + "loading_state_1": "ドライブを読み込み", + "loading_state_0": "インターフェースを構築", + "error_unhelpfulScriptError": "スクリプトエラー: ブラウザコンソールで詳細をご確認ください", + "tag_edit": "編集", + "tag_add": "追加", + "team_exportButton": "ダウンロード", + "admin_cat_quota": "ユーザーストレージ", + "admin_invalKey": "無効な公開鍵です", + "admin_limitPlan": "プラン: {0}", + "admin_registrationAllow": "開く", + "admin_registrationButton": "閉じる", + "oo_version": "バージョン: ", + "snapshots_delete": "削除", + "snapshots_close": "閉じる", + "snapshots_restore": "復元", + "snapshots_open": "開く", + "snapshots_placeholder": "スナップショット名", + "snapshots_new": "新規スナップショット", + "snaphot_title": "スナップショット", + "snapshots_button": "スナップショット", + "filePicker_description": "埋め込むファイルを CryptDrive から選択するか、新規にアップロードしてください", + "uploadButtonTitle": "CryptDrive に新規ファイルをアップロード", + "uploadFolderButton": "フォルダをアップロード", + "uploadButton": "ファイルをアップロード", + "filePicker_filter": "ファイル名で検索", + "toolbar_theme": "テーマ", + "themeButton": "テーマ", + "team_cat_back": "チームに戻る", + "team_cat_list": "チーム", + "allow_checkbox": "アクセスリストを有効にする", + "allow_label": "アクセスリスト: {0}", + "share_contactCategory": "連絡先", + "share_linkCategory": "リンク", + "share_linkEdit": "編集", + "previewButtonTitle": "マークダウンのプレビューを表示または非表示にします" } From 953258afd83dffd724c9a56af197b258a6816d61 Mon Sep 17 00:00:00 2001 From: Weblate Date: Tue, 1 Dec 2020 15:00:41 +0100 Subject: [PATCH 082/201] Translated using Weblate (French) Currently translated at 100.0% (1375 of 1375 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ Translated using Weblate (French) Currently translated at 100.0% (1374 of 1374 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ --- www/common/translations/messages.fr.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index 431faa767..195814bf5 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -1469,5 +1469,7 @@ "tag_edit": "Modifier", "tag_add": "Ajouter", "error_unhelpfulScriptError": "Erreur de script : consultez la console du navigateur pour plus de détails", - "documentID": "Référence du document" + "documentID": "Référence du document", + "unableToDisplay": "Impossible d'afficher le document. Veuillez recharger la page avec la touche Échap.", + "errorPopupBlocked": "CryptPad doit pouvoir ouvrir de nouveaux onglets pour fonctionner. Veuillez autoriser les fenêtres pop-up dans la barre d'adresse de votre navigateur. Ces fenêtres ne seront jamais utilisées pour vous montrer de la publicité." } From ff8c8112276d87aa42db9f0a42bdcdc7456b5991 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 1 Dec 2020 15:01:04 +0100 Subject: [PATCH 083/201] Add a hack to prevent Chrome from hard-crashing when opening links from inner --- www/common/common-ui-elements.js | 20 +++++++++---------- www/common/onlyoffice/inner.js | 4 ++++ .../apps/spreadsheeteditor/main/app.js | 2 +- www/common/sframe-common-file.js | 4 +--- www/common/sframe-common-outer.js | 11 +++++++++- www/common/sframe-common.js | 8 ++++++++ www/common/toolbar.js | 8 ++++---- 7 files changed, 38 insertions(+), 19 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index e977f1f49..3474b2c13 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1598,9 +1598,9 @@ define([ content: h('span', Messages.profileButton), action: function () { if (padType) { - window.open(origin+'/profile/'); + Common.openURL(origin+'/profile/'); } else { - window.parent.location = origin+'/profile/'; + Common.gotoURL(origin+'/profile/'); } }, }); @@ -1645,9 +1645,9 @@ define([ content: h('span', Messages.settingsButton), action: function () { if (padType) { - window.open(origin+'/settings/'); + Common.openURL(origin+'/settings/'); } else { - window.parent.location = origin+'/settings/'; + Common.gotoURL(origin+'/settings/'); } }, }); @@ -1662,9 +1662,9 @@ define([ content: h('span', Messages.adminPage || 'Admin'), action: function () { if (padType) { - window.open(origin+'/admin/'); + Common.openURL(origin+'/admin/'); } else { - window.parent.location = origin+'/admin/'; + Common.gotoURL(origin+'/admin/'); } }, }); @@ -1676,9 +1676,9 @@ define([ content: h('span', Messages.supportPage || 'Support'), action: function () { if (padType) { - window.open(origin+'/support/'); + Common.openURL(origin+'/support/'); } else { - window.parent.location = origin+'/support/'; + Common.gotoURL(origin+'/support/'); } }, }); @@ -1763,7 +1763,7 @@ define([ content: h('span', Messages.logoutEverywhere), action: function () { Common.getSframeChannel().query('Q_LOGOUT_EVERYWHERE', null, function () { - window.parent.location = origin + '/'; + Common.gotoURL(origin + '/'); }); }, }); @@ -1773,7 +1773,7 @@ define([ content: h('span', Messages.logoutButton), action: function () { Common.logout(function () { - window.parent.location = origin+'/'; + Common.gotoURL(origin+'/'); }); }, }); diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 9844d52d0..881db3d06 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1355,6 +1355,10 @@ define([ }); }; + APP.openURL = function (url) { + common.openUnsafeURL(url); + }; + APP.loadingImage = 0; APP.getImageURL = function(name, callback) { var mediasSources = getMediasSources(); diff --git a/www/common/onlyoffice/v2b/web-apps/apps/spreadsheeteditor/main/app.js b/www/common/onlyoffice/v2b/web-apps/apps/spreadsheeteditor/main/app.js index 928c3ab25..ef6a6cab4 100644 --- a/www/common/onlyoffice/v2b/web-apps/apps/spreadsheeteditor/main/app.js +++ b/www/common/onlyoffice/v2b/web-apps/apps/spreadsheeteditor/main/app.js @@ -20,7 +20,7 @@ t.asc_setTooltip(this.inputTip.getValue()),t},onBtnClick:function(t){this._handl this.store.length<1&&this.emptyText.length>0&&$(this.el).find(".inner").addBack().filter(".inner").append('
'+this.emptyText+"
"),_.each(this.dataViewItems,function(t){this.stopListening(t),t.stopListening(t.model)},this),this.dataViewItems=[],this.store.each(this.onAddItem,this),this.allowScrollbar&&(this.scroller=new Common.UI.Scroller({el:$(this.el).find(".inner").addBack().filter(".inner"),useKeyboard:this.enableKeyEvents&&!this.handleSelect,minScrollbarLength:40,wheelSpeed:10,alwaysVisibleY:this.scrollAlwaysVisible})),this.disabled&&this.setDisabled(this.disabled),this.attachKeyEvents(),this.lastSelectedRec=null,this._layoutParams=void 0},onChangeItem:function(t,e){this.isSuspendEvents||this.trigger("item:change",this,t,e)},onRemoveItem:function(t,e){var i=t.$el.data("bs.tooltip");if(i&&(void 0===i.dontShow&&(i.dontShow=!0),i.tip().remove()),this.stopListening(t),t.stopListening(),this.store.length<1&&this.emptyText.length>0){var n=$(this.el).find(".inner").addBack().filter(".inner");n.find(".empty-text").length<=0&&n.append('
'+this.emptyText+"
")}for(var o=0;o=0&&this.dataViewItems.length>n?$(this.dataViewItems[n].el):e.find("#"+t.get("id"));if(!(o.length<=0)){var s=o.offset().top,a=$(this.dataViewItems[0].el),l=a.length>0?a[0].offsetTop:0;(si+e.height())&&(this.scroller&&this.allowScrollbar?this.scroller.scrollTop(e.scrollTop()+s-i-l,0):e.scrollTop(e.scrollTop()+s-i-l))}}},onKeyDown:function(t,e){if(!this.disabled)if(void 0===e&&(e=t),_.indexOf(this.moveKeys,e.keyCode)>-1||e.keyCode==Common.UI.Keys.RETURN){e.preventDefault(),e.stopPropagation();var i=this.getSelectedRec();if(null===this.lastSelectedRec&&(this.lastSelectedRec=i),e.keyCode==Common.UI.Keys.RETURN)this.lastSelectedRec=null,this.selectedBeforeHideRec&&(i=this.selectedBeforeHideRec),this.trigger("item:click",this,this,i,t),this.trigger("item:select",this,this,i,t),this.trigger("entervalue",this,i,t),this.parentMenu&&this.parentMenu.hide();else{var n=_.indexOf(this.store.models,i);if(n<0)if(e.keyCode==Common.UI.Keys.LEFT){var o=$(t.target).closest(".dropdown-submenu.over");o.length>0?(o.removeClass("over"),o.find("> a").focus()):n=0}else n=0;else if("both"==this.options.keyMoveDirection){void 0===this._layoutParams&&this.fillIndexesArray();var s=this.dataViewItems[n].topIdx,a=this.dataViewItems[n].leftIdx;if(n=void 0,e.keyCode==Common.UI.Keys.LEFT)for(;void 0===n;){if(--a<0){var o=$(t.target).closest(".dropdown-submenu.over");if(o.length>0){o.removeClass("over"),o.find("> a").focus();break}a=this._layoutParams.columns-1}n=this._layoutParams.itemsIndexes[s][a]}else if(e.keyCode==Common.UI.Keys.RIGHT)for(;void 0===n;)a++,a>this._layoutParams.columns-1&&(a=0),n=this._layoutParams.itemsIndexes[s][a];else if(e.keyCode==Common.UI.Keys.UP)for(;void 0===n;)s--,s<0&&(s=this._layoutParams.rows-1),n=this._layoutParams.itemsIndexes[s][a];else for(;void 0===n;)s++,s>this._layoutParams.rows-1&&(s=0),n=this._layoutParams.itemsIndexes[s][a]}else n=e.keyCode==Common.UI.Keys.UP||e.keyCode==Common.UI.Keys.LEFT?Math.max(0,n-1):Math.min(this.store.length-1,n+1);void 0!==n&&n>=0&&(i=this.store.at(n)),i&&(this._fromKeyDown=!0,this.selectRecord(i),this._fromKeyDown=!1,this.scrollToRecord(i))}}else this.trigger("item:keydown",this,i,t)},attachKeyEvents:function(){if(this.enableKeyEvents&&this.handleSelect){var t=$(this.el).find(".inner").addBack().filter(".inner");t.addClass("canfocused"),t.attr("tabindex","0"),t.on(this.parentMenu&&this.useBSKeydown?"dataview:keydown":"keydown",_.bind(this.onKeyDown,this))}},showLastSelected:function(){this.lastSelectedRec?(this.selectRecord(this.lastSelectedRec,!0),this.scrollToRecord(this.lastSelectedRec),this.lastSelectedRec=null):this.scrollToRecord(this.getSelectedRec())},setDisabled:function(t){this.disabled=t,$(this.el).find(".inner").addBack().filter(".inner").toggleClass("disabled",t)},isDisabled:function(){return this.disabled},setEmptyText:function(t){this.emptyText=t},alignPosition:function(){var t="menu"===this.parentMenu.cmpEl.attr("role")?this.parentMenu.cmpEl:this.parentMenu.cmpEl.find("[role=menu]"),e=Common.Utils.innerHeight()-10,i=$(this.el).find(".inner").addBack().filter(".inner"),n=i.parent(),o=parseInt(n.css("margin-top"))+parseInt(n.css("margin-bottom"))+parseInt(t.css("margin-top")),s=parseInt(t.css("padding-top"))+parseInt(t.css("padding-bottom")),a=t.outerHeight(),l=parseInt(t.css("top")),r={minScrollbarLength:40};this.scrollAlwaysVisible&&(r.alwaysVisibleY=this.scrollAlwaysVisible),l+a>e?(i.css("max-height",e-l-s-o+"px"),this.allowScrollbar&&this.scroller.update(r)):l+ao&&(o=r,this._layoutParams.itemsIndexes.push([]),s=this._layoutParams.itemsIndexes.length-1),this._layoutParams.itemsIndexes[s][a]=l,this.dataViewItems[l].topIdx=s,this.dataViewItems[l].leftIdx=a,this._layoutParams.columns<%= value %>
')},template:_.template(['
'].join("")),onResetItems:function(){this.innerEl=null,Common.UI.DataView.prototype.onResetItems.call(this),this.trigger("items:reset",this)},onAddItem:function(t,e,i){var n=new Common.UI.DataViewItem({template:this.itemTemplate,model:t});if(this.innerEl||(this.innerEl=$(this.el).find(".inner")),n&&this.innerEl){if(this.dataViewItems.length<1&&this.innerEl.find(".empty-text").remove(),this.options.simpleAddMode)this.innerEl.append(n.render().el),this.dataViewItems.push(n);else{var o=_.indexOf(this.store.models,t),s=this.innerEl.find("> div");o>0?$(s.get(o-1)).after(n.render().el):s.length>0?$(s[o]).before(n.render().el):this.innerEl.append(n.render().el),this.dataViewItems=this.dataViewItems.slice(0,o).concat(n).concat(this.dataViewItems.slice(o))}if(this.listenTo(n,"change",this.onChangeItem),this.listenTo(n,"remove",this.onRemoveItem),this.listenTo(n,"click",this.onClickItem),this.listenTo(n,"dblclick",this.onDblClickItem),this.listenTo(n,"select",this.onSelectItem),t.get("tip")){var a=$(n.el);a.attr("data-toggle","tooltip"),a.tooltip({title:t.get("tip"),placement:"cursor",zIndex:this.tipZIndex})}this.isSuspendEvents||this.trigger("item:add",this,n,t)}}}}())}),define("spreadsheeteditor/main/app/view/ParagraphSettingsAdvanced",["text!spreadsheeteditor/main/app/template/ParagraphSettingsAdvanced.template","common/main/lib/view/AdvancedSettingsWindow","common/main/lib/component/MetricSpinner","common/main/lib/component/CheckBox","common/main/lib/component/RadioBox","common/main/lib/component/ListView"],function(t){"use strict";SSE.Views.ParagraphSettingsAdvanced=Common.Views.AdvancedSettingsWindow.extend(_.extend({options:{contentWidth:370,height:394,toggleGroup:"paragraph-adv-settings-group",storageName:"sse-para-settings-adv-category"},initialize:function(e){var i=this;_.extend(this.options,{title:this.textTitle,items:[{panelId:"id-adv-paragraph-indents",panelCaption:this.strParagraphIndents},{panelId:"id-adv-paragraph-font",panelCaption:this.strParagraphFont},{panelId:"id-adv-paragraph-tabs",panelCaption:this.strTabs}],contentTemplate:_.template(t)({scope:this})},e),Common.Views.AdvancedSettingsWindow.prototype.initialize.call(this,this.options),this._changedProps=null,this.checkGroup=0,this._noApply=!0,this._tabListChanged=!1,this.spinners=[],this.FirstLine=void 0,this.Spacing=null,this.api=this.options.api,this._originalProps=new Asc.asc_CParagraphProperty(this.options.paragraphProps),this._arrLineRule=[{displayValue:this.textAuto,defaultValue:1,value:c_paragraphLinerule.LINERULE_AUTO,minValue:.5,step:.01,defaultUnit:""},{displayValue:this.textExact,defaultValue:5,value:c_paragraphLinerule.LINERULE_EXACT,minValue:.03,step:.01,defaultUnit:"cm"}];var n=this._originalProps.asc_getSpacing().asc_getLineRule(),o=_.findWhere(this._arrLineRule,{value:n});this.CurLineRuleIdx=this._arrLineRule.indexOf(o),this._arrTextAlignment=[{displayValue:this.textTabLeft,value:c_paragraphTextAlignment.LEFT},{displayValue:this.textTabCenter,value:c_paragraphTextAlignment.CENTERED},{displayValue:this.textTabRight,value:c_paragraphTextAlignment.RIGHT},{displayValue:this.textJustified,value:c_paragraphTextAlignment.JUSTIFIED}],this._arrSpecial=[{displayValue:this.textNoneSpecial,value:c_paragraphSpecial.NONE_SPECIAL,defaultValue:0},{displayValue:this.textFirstLine,value:c_paragraphSpecial.FIRST_LINE,defaultValue:12.7},{displayValue:this.textHanging,value:c_paragraphSpecial.HANGING,defaultValue:12.7}],this._arrTabAlign=[{value:1,displayValue:this.textTabLeft},{value:3,displayValue:this.textTabCenter},{value:2,displayValue:this.textTabRight}],this._arrKeyTabAlign=[],this._arrTabAlign.forEach(function(t){i._arrKeyTabAlign[t.value]=t.displayValue})},render:function(){Common.Views.AdvancedSettingsWindow.prototype.render.call(this);var t=this;this.cmbTextAlignment=new Common.UI.ComboBox({el:$("#paragraphadv-spin-text-alignment"),cls:"input-group-nr",editable:!1,data:this._arrTextAlignment,style:"width: 173px;",menuStyle:"min-width: 173px;"}),this.cmbTextAlignment.setValue(""),this.numIndentsLeft=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-indent-left"),step:.1,width:85,defaultUnit:"cm",defaultValue:0,value:"0 cm",maxValue:55.87,minValue:0}),this.numIndentsLeft.on("change",_.bind(function(t,e,i,n){var o=t.getNumberValue();this._changedProps&&(null!==this._changedProps.asc_getInd()&&void 0!==this._changedProps.asc_getInd()||this._changedProps.put_Ind(new Asc.asc_CParagraphInd),this._changedProps.asc_getInd().put_Left(Common.Utils.Metric.fnRecalcToMM(o)))},this)),this.spinners.push(this.numIndentsLeft),this.numIndentsRight=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-indent-right"),step:.1,width:85,defaultUnit:"cm",defaultValue:0,value:"0 cm",maxValue:55.87,minValue:0}),this.numIndentsRight.on("change",_.bind(function(t,e,i,n){this._changedProps&&(null!==this._changedProps.asc_getInd()&&void 0!==this._changedProps.asc_getInd()||this._changedProps.put_Ind(new Asc.asc_CParagraphInd),this._changedProps.asc_getInd().put_Right(Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())))},this)),this.spinners.push(this.numIndentsRight),this.cmbSpecial=new Common.UI.ComboBox({el:$("#paragraphadv-spin-special"),cls:"input-group-nr",editable:!1,data:this._arrSpecial,style:"width: 85px;",menuStyle:"min-width: 85px;"}),this.cmbSpecial.setValue(""),this.cmbSpecial.on("selected",_.bind(this.onSpecialSelect,this)),this.numSpecialBy=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-special-by"),step:.1,width:85,defaultUnit:"cm",defaultValue:0,value:"0 cm",maxValue:55.87,minValue:0}),this.spinners.push(this.numSpecialBy),this.numSpecialBy.on("change",_.bind(this.onFirstLineChange,this)),this.numSpacingBefore=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-spacing-before"),step:.1,width:85,value:"",defaultUnit:"cm",maxValue:55.88,minValue:0,allowAuto:!0,autoText:this.txtAutoText}),this.numSpacingBefore.on("change",_.bind(function(t,e,i,n){if(null===this.Spacing){var o=this._originalProps?this._originalProps:new Asc.asc_CParagraphProperty;this.Spacing=o.asc_getSpacing()}this.Spacing.Before=Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())},this)),this.spinners.push(this.numSpacingBefore),this.numSpacingAfter=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-spacing-after"),step:.1,width:85,value:"",defaultUnit:"cm",maxValue:55.88,minValue:0,allowAuto:!0,autoText:this.txtAutoText}),this.numSpacingAfter.on("change",_.bind(function(t,e,i,n){if(null===this.Spacing){var o=this._originalProps?this._originalProps:new Asc.asc_CParagraphProperty;this.Spacing=o.asc_getSpacing()}this.Spacing.After=Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())},this)),this.spinners.push(this.numSpacingAfter),this.cmbLineRule=new Common.UI.ComboBox({el:$("#paragraphadv-spin-line-rule"),cls:"input-group-nr",editable:!1,data:this._arrLineRule,style:"width: 85px;",menuStyle:"min-width: 85px;"}),this.cmbLineRule.setValue(this.CurLineRuleIdx),this.cmbLineRule.on("selected",_.bind(this.onLineRuleSelect,this)),this.numLineHeight=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-line-height"),step:.01,width:85,value:"",defaultUnit:"",maxValue:132,minValue:.5}),this.spinners.push(this.numLineHeight),this.numLineHeight.on("change",_.bind(this.onNumLineHeightChange,this)),this.chStrike=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-strike"),labelText:this.strStrike}),this.chStrike.on("change",_.bind(this.onStrikeChange,this)),this.chDoubleStrike=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-double-strike"),labelText:this.strDoubleStrike}),this.chDoubleStrike.on("change",_.bind(this.onDoubleStrikeChange,this)),this.chSuperscript=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-superscript"),labelText:this.strSuperscript}),this.chSuperscript.on("change",_.bind(this.onSuperscriptChange,this)),this.chSubscript=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-subscript"),labelText:this.strSubscript}),this.chSubscript.on("change",_.bind(this.onSubscriptChange,this)),this.chSmallCaps=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-small-caps"),labelText:this.strSmallCaps}),this.chSmallCaps.on("change",_.bind(this.onSmallCapsChange,this)),this.chAllCaps=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-all-caps"),labelText:this.strAllCaps}),this.chAllCaps.on("change",_.bind(this.onAllCapsChange,this)),this.numSpacing=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-spacing"),step:.01,width:100,defaultUnit:"cm",defaultValue:0,value:"0 cm",maxValue:55.87,minValue:-55.87}),this.numSpacing.on("change",_.bind(function(t,e,i,n){if(this._changedProps&&this._changedProps.asc_putTextSpacing(Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())),this.api&&!this._noApply){var o=this._originalProps?this._originalProps:new Asc.asc_CParagraphProperty;o.asc_putTextSpacing(Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())),this.api.asc_setDrawImagePlaceParagraph("paragraphadv-font-img",o)}},this)),this.spinners.push(this.numSpacing),this.numTab=new Common.UI.MetricSpinner({el:$("#paraadv-spin-tab"),step:.1,width:108,defaultUnit:"cm",value:"1.25 cm",maxValue:55.87,minValue:0}),this.spinners.push(this.numTab),this.numDefaultTab=new Common.UI.MetricSpinner({el:$("#paraadv-spin-default-tab"),step:.1,width:108,defaultUnit:"cm",value:"1.25 cm",maxValue:55.87,minValue:0}),this.numDefaultTab.on("change",_.bind(function(t,e,i,n){this._changedProps&&this._changedProps.asc_putDefaultTab(parseFloat(Common.Utils.Metric.fnRecalcToMM(t.getNumberValue()).toFixed(1)))},this)),this.spinners.push(this.numDefaultTab),this.tabList=new Common.UI.ListView({el:$("#paraadv-list-tabs"),emptyText:this.noTabs,store:new Common.UI.DataViewStore,template:_.template(['
'].join("")),itemTemplate:_.template(['
','
<%= value %>
','
<%= displayTabAlign %>
',"
"].join(""))}),this.tabList.store.comparator=function(t){return t.get("tabPos")},this.tabList.on("item:select",_.bind(this.onSelectTab,this));var e=function(){t._noApply||(t._tabListChanged=!0)};this.listenTo(this.tabList.store,"add",e),this.listenTo(this.tabList.store,"remove",e),this.listenTo(this.tabList.store,"reset",e),this.cmbAlign=new Common.UI.ComboBox({el:$("#paraadv-cmb-align"),style:"width: 108px;",menuStyle:"min-width: 108px;",editable:!1,cls:"input-group-nr",data:this._arrTabAlign}),this.cmbAlign.setValue(1),this.btnAddTab=new Common.UI.Button({el:$("#paraadv-button-add-tab")}),this.btnAddTab.on("click",_.bind(this.addTab,this)),this.btnRemoveTab=new Common.UI.Button({el:$("#paraadv-button-remove-tab")}),this.btnRemoveTab.on("click",_.bind(this.removeTab,this)),this.btnRemoveAll=new Common.UI.Button({el:$("#paraadv-button-remove-all")}),this.btnRemoveAll.on("click",_.bind(this.removeAllTabs,this)),this.on("show",function(t){t.getChild(".footer .primary").focus()}),this.afterRender()},getSettings:function(){this._tabListChanged&&(null!==this._changedProps.asc_getTabs()&&void 0!==this._changedProps.asc_getTabs()||this._changedProps.asc_putTabs(new Asc.asc_CParagraphTabs),this.tabList.store.each(function(t,e){var i=new Asc.asc_CParagraphTab(Common.Utils.Metric.fnRecalcToMM(t.get("tabPos")),t.get("tabAlign"));this._changedProps.asc_getTabs().add_Tab(i)},this));var t=this.cmbTextAlignment.getValue();return this._changedProps.asc_putJc(void 0!==t&&null!==t?t:c_paragraphTextAlignment.LEFT),null!==this.Spacing&&this._changedProps.asc_putSpacing(this.Spacing),{paragraphProps:this._changedProps}},_setDefaults:function(t){if(t){this._originalProps=new Asc.asc_CParagraphProperty(t),this.FirstLine=null!==t.asc_getInd()?t.asc_getInd().asc_getFirstLine():null,this.numIndentsLeft.setValue(null!==t.asc_getInd()&&null!==t.asc_getInd().asc_getLeft()?Common.Utils.Metric.fnRecalcFromMM(t.asc_getInd().asc_getLeft()):"",!0),this.numIndentsRight.setValue(null!==t.asc_getInd()&&null!==t.asc_getInd().asc_getRight()?Common.Utils.Metric.fnRecalcFromMM(t.asc_getInd().asc_getRight()):"",!0),this.cmbTextAlignment.setValue(void 0!==t.asc_getJc()&&null!==t.asc_getJc()?t.asc_getJc():c_paragraphTextAlignment.CENTERED,!0),void 0===this.CurSpecial&&(this.CurSpecial=0===t.asc_getInd().asc_getFirstLine()?c_paragraphSpecial.NONE_SPECIAL:t.asc_getInd().asc_getFirstLine()>0?c_paragraphSpecial.FIRST_LINE:c_paragraphSpecial.HANGING),this.cmbSpecial.setValue(this.CurSpecial),this.numSpecialBy.setValue(null!==this.FirstLine?Math.abs(Common.Utils.Metric.fnRecalcFromMM(this.FirstLine)):"",!0),this.numSpacingBefore.setValue(null!==t.asc_getSpacing()&&null!==t.asc_getSpacing().asc_getBefore()?Common.Utils.Metric.fnRecalcFromMM(t.asc_getSpacing().asc_getBefore()):"",!0),this.numSpacingAfter.setValue(null!==t.asc_getSpacing()&&null!==t.asc_getSpacing().asc_getAfter()?Common.Utils.Metric.fnRecalcFromMM(t.asc_getSpacing().asc_getAfter()):"",!0);var e=t.asc_getSpacing().asc_getLineRule();this.cmbLineRule.setValue(null!==e?e:"",!0),null!==t.asc_getSpacing()&&null!==t.asc_getSpacing().asc_getLine()?this.numLineHeight.setValue(e==c_paragraphLinerule.LINERULE_AUTO?t.asc_getSpacing().asc_getLine():Common.Utils.Metric.fnRecalcFromMM(t.asc_getSpacing().asc_getLine()),!0):this.numLineHeight.setValue("",!0),this._noApply=!0,this.chStrike.setValue(null!==t.asc_getStrikeout()&&void 0!==t.asc_getStrikeout()?t.asc_getStrikeout():"indeterminate",!0),this.chDoubleStrike.setValue(null!==t.asc_getDStrikeout()&&void 0!==t.asc_getDStrikeout()?t.asc_getDStrikeout():"indeterminate",!0),this.chSubscript.setValue(null!==t.asc_getSubscript()&&void 0!==t.asc_getSubscript()?t.asc_getSubscript():"indeterminate",!0),this.chSuperscript.setValue(null!==t.asc_getSuperscript()&&void 0!==t.asc_getSuperscript()?t.asc_getSuperscript():"indeterminate",!0),this.chSmallCaps.setValue(null!==t.asc_getSmallCaps()&&void 0!==t.asc_getSmallCaps()?t.asc_getSmallCaps():"indeterminate",!0),this.chAllCaps.setValue(null!==t.asc_getAllCaps()&&void 0!==t.asc_getAllCaps()?t.asc_getAllCaps():"indeterminate",!0),this.numSpacing.setValue(null!==t.asc_getTextSpacing()&&void 0!==t.asc_getTextSpacing()?Common.Utils.Metric.fnRecalcFromMM(t.asc_getTextSpacing()):"",!0),this.api.asc_setDrawImagePlaceParagraph("paragraphadv-font-img",this._originalProps),this.numDefaultTab.setValue(null!==t.asc_getDefaultTab()&&void 0!==t.asc_getDefaultTab()?Common.Utils.Metric.fnRecalcFromMM(parseFloat(t.asc_getDefaultTab().toFixed(1))):"",!0);var i=this.tabList.store,n=t.asc_getTabs();if(n){for(var o=[],s=n.asc_getCount(),a=0;an.length-1&&(o=n.length-1),n.length>0&&(this.tabList.selectByIndex(o),this.tabList.scrollToRecord(n.at(o)))}},removeAllTabs:function(t,e){this.tabList.store.reset()},onSelectTab:function(t,e,i){var n={};if(_.isFunction(i.toJSON)){if(!i.get("selected"))return;n=i.toJSON()}else n=i;this.numTab.setValue(n.tabPos),this.cmbAlign.setValue(n.tabAlign)},onSpecialSelect:function(t,e){if(this.CurSpecial=e.value,this.CurSpecial===c_paragraphSpecial.NONE_SPECIAL&&this.numSpecialBy.setValue(0,!0),this._changedProps){null!==this._changedProps.asc_getInd()&&void 0!==this._changedProps.asc_getInd()||this._changedProps.put_Ind(new Asc.asc_CParagraphInd);var i=Common.Utils.Metric.fnRecalcToMM(this.numSpecialBy.getNumberValue());0===i&&(this.numSpecialBy.setValue(Common.Utils.Metric.fnRecalcFromMM(this._arrSpecial[e.value].defaultValue),!0),i=this._arrSpecial[e.value].defaultValue),this.CurSpecial===c_paragraphSpecial.HANGING&&(i=-i),this._changedProps.asc_getInd().put_FirstLine(i)}},onFirstLineChange:function(t,e,i,n){if(this._changedProps){null!==this._changedProps.asc_getInd()&&void 0!==this._changedProps.asc_getInd()||this._changedProps.put_Ind(new Asc.asc_CParagraphInd);var o=Common.Utils.Metric.fnRecalcToMM(t.getNumberValue());this.CurSpecial===c_paragraphSpecial.HANGING?o=-o:this.CurSpecial===c_paragraphSpecial.NONE_SPECIAL&&o>0?(this.CurSpecial=c_paragraphSpecial.FIRST_LINE,this.cmbSpecial.setValue(c_paragraphSpecial.FIRST_LINE)):0===o&&(this.CurSpecial=c_paragraphSpecial.NONE_SPECIAL,this.cmbSpecial.setValue(c_paragraphSpecial.NONE_SPECIAL)),this._changedProps.asc_getInd().put_FirstLine(o)}},onLineRuleSelect:function(t,e){if(null===this.Spacing){var i=this._originalProps?this._originalProps:new Asc.asc_CParagraphProperty;this.Spacing=i.asc_getSpacing()}this.Spacing.LineRule=e.value;var n=_.findWhere(this._arrLineRule,{value:e.value}),o=this._arrLineRule.indexOf(n);this.CurLineRuleIdx!==o&&(this.numLineHeight.setDefaultUnit(this._arrLineRule[o].defaultUnit),this.numLineHeight.setMinValue(this._arrLineRule[o].minValue),this.numLineHeight.setStep(this._arrLineRule[o].step),this.Spacing.LineRule===c_paragraphLinerule.LINERULE_AUTO?this.numLineHeight.setValue(this._arrLineRule[o].defaultValue):this.numLineHeight.setValue(Common.Utils.Metric.fnRecalcFromMM(this._arrLineRule[o].defaultValue)),this.CurLineRuleIdx=o)},onNumLineHeightChange:function(t,e,i,n){if(""!==this.cmbLineRule.getRawValue()){if(null===this.Spacing){var o=this._originalProps?this._originalProps:new Asc.asc_CParagraphProperty;this.Spacing=o.asc_getSpacing()}this.Spacing.Line=this.cmbLineRule.getValue()==c_paragraphLinerule.LINERULE_AUTO?t.getNumberValue():Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())}},textTitle:"Paragraph - Advanced Settings",strIndentsLeftText:"Left",strIndentsRightText:"Right",strParagraphIndents:"Indents & Spacing",strParagraphFont:"Font",cancelButtonText:"Cancel",okButtonText:"Ok",textEffects:"Effects",textCharacterSpacing:"Character Spacing",strDoubleStrike:"Double strikethrough",strStrike:"Strikethrough",strSuperscript:"Superscript",strSubscript:"Subscript",strSmallCaps:"Small caps",strAllCaps:"All caps",strTabs:"Tab",textSet:"Specify",textRemove:"Remove",textRemoveAll:"Remove All",textTabLeft:"Left",textTabRight:"Right",textTabCenter:"Center",textAlign:"Alignment",textTabPosition:"Tab Position",textDefault:"Default Tab",noTabs:"The specified tabs will appear in this field",textJustified:"Justified",strIndentsSpecial:"Special",textNoneSpecial:"(none)",textFirstLine:"First line",textHanging:"Hanging",strIndentsSpacingBefore:"Before",strIndentsSpacingAfter:"After",strIndentsLineSpacing:"Line Spacing",txtAutoText:"Auto",textAuto:"Multiple",textExact:"Exactly",strIndent:"Indents",strSpacing:"Spacing"},SSE.Views.ParagraphSettingsAdvanced||{}))}),define("text!spreadsheeteditor/main/app/template/ImageSettingsAdvanced.template",[],function(){ return'
\n
\n
\n \n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n \n \n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n \n \n
\n \n
\n
\n
'}),define("spreadsheeteditor/main/app/view/ImageSettingsAdvanced",["text!spreadsheeteditor/main/app/template/ImageSettingsAdvanced.template","common/main/lib/view/AdvancedSettingsWindow","common/main/lib/component/InputField","common/main/lib/component/MetricSpinner","common/main/lib/component/CheckBox"],function(t){"use strict";SSE.Views.ImageSettingsAdvanced=Common.Views.AdvancedSettingsWindow.extend(_.extend({options:{contentWidth:300,height:342,toggleGroup:"image-adv-settings-group",properties:null,storageName:"sse-image-settings-adv-category"},initialize:function(e){_.extend(this.options,{title:this.textTitle,items:[{panelId:"id-adv-image-rotate",panelCaption:this.textRotation},{panelId:"id-adv-image-alttext",panelCaption:this.textAlt}],contentTemplate:_.template(t)({scope:this})},e),Common.Views.AdvancedSettingsWindow.prototype.initialize.call(this,this.options),this._originalProps=this.options.imageProps,this._changedProps=null},render:function(){Common.Views.AdvancedSettingsWindow.prototype.render.call(this);var t=this;this.spnAngle=new Common.UI.MetricSpinner({el:$("#image-advanced-spin-angle"),step:1,width:80,defaultUnit:"°",value:"0 °",maxValue:3600,minValue:-3600}),this.chFlipHor=new Common.UI.CheckBox({el:$("#image-advanced-checkbox-hor"),labelText:this.textHorizontally}),this.chFlipVert=new Common.UI.CheckBox({el:$("#image-advanced-checkbox-vert"),labelText:this.textVertically}),this.inputAltTitle=new Common.UI.InputField({el:$("#image-advanced-alt-title"),allowBlank:!0,validateOnBlur:!1,style:"width: 100%;"}).on("changed:after",function(){t.isAltTitleChanged=!0}),this.textareaAltDescription=this.$window.find("textarea"),this.textareaAltDescription.keydown(function(e){e.keyCode==Common.UI.Keys.RETURN&&e.stopPropagation(),t.isAltDescChanged=!0}),this.afterRender()},afterRender:function(){if(this._setDefaults(this._originalProps),this.storageName){var t=Common.localStorage.getItem(this.storageName);this.setActiveCategory(null!==t?parseInt(t):0)}},_setDefaults:function(t){if(t){var e=t.asc_getTitle();this.inputAltTitle.setValue(e||""),e=t.asc_getDescription(),this.textareaAltDescription.val(e||""),e=t.asc_getRot(),this.spnAngle.setValue(void 0==e||null===e?"":Math.floor(180*e/3.14159265358979+.5),!0),this.chFlipHor.setValue(t.asc_getFlipH()),this.chFlipVert.setValue(t.asc_getFlipV());var i=t.asc_getPluginGuid();this.btnsCategory[0].setVisible(null===i||void 0===i),this._changedProps=new Asc.asc_CImgProperty}},getSettings:function(){return this.isAltTitleChanged&&this._changedProps.asc_putTitle(this.inputAltTitle.getValue()),this.isAltDescChanged&&this._changedProps.asc_putDescription(this.textareaAltDescription.val()),this._changedProps.asc_putRot(3.14159265358979*this.spnAngle.getNumberValue()/180),this._changedProps.asc_putFlipH("checked"==this.chFlipHor.getValue()),this._changedProps.asc_putFlipV("checked"==this.chFlipVert.getValue()),{imageProps:this._changedProps}},textTitle:"Image - Advanced Settings",cancelButtonText:"Cancel",okButtonText:"Ok",textAlt:"Alternative Text",textAltTitle:"Title",textAltDescription:"Description",textAltTip:"The alternative text-based representation of the visual object information, which will be read to the people with vision or cognitive impairments to help them better understand what information there is in the image, autoshape, chart or table.",textRotation:"Rotation",textAngle:"Angle",textFlipped:"Flipped",textHorizontally:"Horizontally",textVertically:"Vertically"},SSE.Views.ImageSettingsAdvanced||{}))}),define("spreadsheeteditor/main/app/view/SetValueDialog",["common/main/lib/component/Window","common/main/lib/component/ComboBox"],function(){"use strict";SSE.Views.SetValueDialog=Common.UI.Window.extend(_.extend({options:{width:214,header:!0,style:"min-width: 214px;",cls:"modal-dlg"},initialize:function(t){_.extend(this.options,{title:this.textTitle},t||{}),this.template=['
','
','
',"
",'"].join(""),this.options.tpl=_.template(this.template)(this.options),this.startvalue=this.options.startvalue,this.maxvalue=this.options.maxvalue,this.defaultUnit=this.options.defaultUnit,this.step=this.options.step,Common.UI.Window.prototype.initialize.call(this,this.options)},render:function(){if(Common.UI.Window.prototype.render.call(this),this.spnSize=new Common.UI.MetricSpinner({el:$("#id-spin-set-value"),width:182,step:this.step,defaultUnit:this.defaultUnit,minValue:0,maxValue:this.maxvalue,value:null!==this.startvalue?this.startvalue+" "+this.defaultUnit:""}),null!==this.startvalue){var t=this;setTimeout(function(){var e=t.spnSize.$input[0];document.selection?t.spnSize.$input.select():(e.selectionStart=0,e.selectionEnd=t.startvalue.toString().length)},10)}this.getChild().find(".dlg-btn").on("click",_.bind(this.onBtnClick,this)),this.spnSize.on("entervalue",_.bind(this.onPrimary,this)),this.options.rounding&&this.spnSize.on("change",_.bind(this.onChange,this)),this.spnSize.$el.find("input").focus()},_handleInput:function(t){this.options.handler&&this.options.handler.call(this,this,t),this.close()},onBtnClick:function(t){this._handleInput(t.currentTarget.attributes.result.value)},onChange:function(){var t=this.spnSize.getNumberValue();t/=this.step,t=(t|t)*this.step,this.spnSize.setValue(t,!0)},getSettings:function(){return this.spnSize.getNumberValue()},onPrimary:function(){return this._handleInput("ok"),!1},cancelButtonText:"Cancel",okButtonText:"Ok",txtMinText:"The minimum value for this field is {0}",txtMaxText:"The maximum value for this field is {0}"},SSE.Views.SetValueDialog||{}))}),void 0===Common)var Common={};define("common/main/lib/component/ColorPaletteExt",["common/main/lib/component/BaseView"],function(){"use strict";Common.UI.ColorPaletteExt=Common.UI.BaseView.extend({options:{dynamiccolors:10,allowReselect:!0,cls:"",style:""},template:_.template(['
',"<% var me = this; %>","<% $(colors).each(function(num, item) { %>","<% if (me.isColor(item)) { %>",'
',' ',"
","<% } else if (me.isTransparent(item)) { %>",'
',' ',"
","<% } else if (me.isEffect(item)) { %>",'
',' ',"
","<% } %>","<% }); %>","
"].join("")),colorRe:/(?:^|\s)color-(.{6})(?:\s|$)/,selectedCls:"selected",initialize:function(t){Common.UI.BaseView.prototype.initialize.call(this,t),this.id=this.options.id,this.cls=this.options.cls,this.style=this.options.style,this.colors=this.options.colors||[],this.value=this.options.value,this.options.el&&this.render(),this.options.value&&this.select(this.options.value,!0)},render:function(t){var e=this;return e.rendered?this.cmpEl=$(this.el):(this.cmpEl=$(this.template({id:this.id,cls:this.cls,style:this.style,colors:this.colors})),t?(this.setElement(t,!1),t.html(this.cmpEl)):$(this.el).html(this.cmpEl),this.cmpEl.on("click",_.bind(this.handleClick,this))),e.rendered=!0,this},isColor:function(t){return"string"==typeof t&&/[0-9A-F]{6}/.test(t)},isTransparent:function(t){return"string"==typeof t&&"transparent"==t},isEffect:function(t){return"object"==typeof t&&void 0!==t.effectId},getColor:function(){return this.value},handleClick:function(t){var e,i=this,n=$(t.target).closest("div.palette-color-item");if(0!=n.length)if(n.hasClass("color-transparent"))$(i.el).find("div."+i.selectedCls).removeClass(i.selectedCls),n.addClass(i.selectedCls),i.value="transparent",i.trigger("select",i,"transparent");else if((!/^[a-fA-F0-9]{6}$/.test(i.value)||_.indexOf(i.colors,i.value)<0)&&(i.value=!1),$(i.el).find("div."+i.selectedCls).removeClass(i.selectedCls),n.addClass(i.selectedCls),e=n[0].className.match(i.colorRe)[1],n.hasClass("palette-color-effect")){var o=parseInt(n.attr("effectid"));e&&(i.value=e.toUpperCase(),i.trigger("select",i,{color:e,effectId:o}))}else/#?[a-fA-F0-9]{6}/.test(e)&&(e=/#?([a-fA-F0-9]{6})/.exec(e)[1].toUpperCase(),i.value=e,i.trigger("select",i,e))},select:function(t,e){var i=$(this.el);if(i.find("div."+this.selectedCls).removeClass(this.selectedCls),t)if("object"==typeof t){var n;void 0!==t.effectId?(n=i.find('div[effectid="'+t.effectId+'"]').first(),n.length>0?(n.addClass(this.selectedCls),this.value=n[0].className.match(this.colorRe)[1].toUpperCase()):this.value=!1):void 0!==t.effectValue&&(n=i.find('div[effectvalue="'+t.effectValue+'"].color-'+t.color.toUpperCase()).first(),n.length>0?(n.addClass(this.selectedCls),this.value=n[0].className.match(this.colorRe)[1].toUpperCase()):this.value=!1)}else if(/#?[a-fA-F0-9]{6}/.test(t)&&(t=/#?([a-fA-F0-9]{6})/.exec(t)[1].toUpperCase(),this.value=t),/^[a-fA-F0-9]{6}|transparent$/.test(t)&&_.indexOf(this.colors,t)>=0)_.indexOf(this.colors,this.value)<0&&(this.value=!1),(t!=this.value||this.options.allowReselect)&&("transparent"==t?i.find("div.color-transparent").addClass(this.selectedCls):i.find("div.palette-color.color-"+t).first().addClass(this.selectedCls),this.value=t,!0!==e&&this.fireEvent("select",this,t));else{var o=i.find("#"+t).first();0==o.length&&(o=i.find('div[color="'+t+'"]').first()),o.length>0&&(o.addClass(this.selectedCls),this.value=t.toUpperCase())}},updateColors:function(t){void 0!==t&&(this.colors=t,this.cmpEl=$(this.template({id:this.id,cls:this.cls,style:this.style,colors:this.colors})),$(this.el).html(this.cmpEl),this.cmpEl.on("click",_.bind(this.handleClick,this)))},clearSelection:function(t){$(this.el).find("div."+this.selectedCls).removeClass(this.selectedCls),this.value=void 0}})}),define("spreadsheeteditor/main/app/view/AutoFilterDialog",["common/main/lib/component/Window","common/main/lib/component/ColorPaletteExt"],function(){"use strict";SSE.Views=SSE.Views||{},SSE.Views.DigitalFilterDialog=Common.UI.Window.extend(_.extend({initialize:function(t){var e=this,i={};_.extend(i,{width:501,height:230,contentWidth:180,header:!0,cls:"filter-dlg",contentTemplate:"",title:e.txtTitle,items:[]},t),this.template=t.template||['
','
','",'
','
','
',"
","
",'
','
',"
",'
','
','
',"
","
","
",'
','"].join(""),this.api=t.api,this.handler=t.handler,this.type=t.type||"number",i.tpl=_.template(this.template)(i),Common.UI.Window.prototype.initialize.call(this,i)},render:function(){Common.UI.Window.prototype.render.call(this),this.conditions=[{value:Asc.c_oAscCustomAutoFilter.equals,displayValue:this.capCondition1},{value:Asc.c_oAscCustomAutoFilter.doesNotEqual,displayValue:this.capCondition2},{value:Asc.c_oAscCustomAutoFilter.isGreaterThan,displayValue:this.capCondition3},{value:Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo,displayValue:this.capCondition4},{value:Asc.c_oAscCustomAutoFilter.isLessThan,displayValue:this.capCondition5},{value:Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo,displayValue:this.capCondition6}],"text"==this.type&&(this.conditions=this.conditions.concat([{value:Asc.c_oAscCustomAutoFilter.beginsWith,displayValue:this.capCondition7},{value:Asc.c_oAscCustomAutoFilter.doesNotBeginWith,displayValue:this.capCondition8},{value:Asc.c_oAscCustomAutoFilter.endsWith,displayValue:this.capCondition9},{value:Asc.c_oAscCustomAutoFilter.doesNotEndWith,displayValue:this.capCondition10},{value:Asc.c_oAscCustomAutoFilter.contains,displayValue:this.capCondition11},{value:Asc.c_oAscCustomAutoFilter.doesNotContain,displayValue:this.capCondition12}])),this.cmbCondition1=new Common.UI.ComboBox({el:$("#id-search-begin-digital-combo",this.$window),menuStyle:"min-width: 225px;max-height: 135px;",cls:"input-group-nr",data:this.conditions,scrollAlwaysVisible:!0,editable:!1}),this.cmbCondition1.setValue(Asc.c_oAscCustomAutoFilter.equals),this.conditions.splice(0,0,{value:0,displayValue:this.textNoFilter}),this.cmbCondition2=new Common.UI.ComboBox({el:$("#id-search-end-digital-combo",this.$window),menuStyle:"min-width: 225px;max-height: 135px;",cls:"input-group-nr",data:this.conditions,scrollAlwaysVisible:!0,editable:!1}),this.cmbCondition2.setValue(0),this.rbAnd=new Common.UI.RadioBox({el:$("#id-and-radio",this.$window),labelText:this.capAnd,name:"asc-radio-filter-tab",checked:!0}),this.rbOr=new Common.UI.RadioBox({el:$("#id-or-radio",this.$window),labelText:this.capOr,name:"asc-radio-filter-tab"}),this.cmbValue1=new Common.UI.ComboBox({el:$("#id-sd-cell-search-begin",this.$window),cls:"input-group-nr",menuStyle:"min-width: 225px;max-height: 135px;",scrollAlwaysVisible:!0,data:[]}),this.cmbValue2=new Common.UI.ComboBox({el:$("#id-sd-cell-search-end",this.$window),cls:"input-group-nr",menuStyle:"min-width: 225px;max-height: 135px;",scrollAlwaysVisible:!0,data:[]});var t=function(t,e){var i=t.get("intval"),n=e.get("intval"),o=void 0!==i;return o!==(void 0!==n)?o?-1:1:(!o&&(i=t.get("value").toLowerCase())&&(n=e.get("value").toLowerCase()),i==n?0:""==n||""!==i&&i1?n[1].asc_getOperator()||0:0),this.cmbValue1.setValue(null===n[0].asc_getVal()?"":n[0].asc_getVal()),this.cmbValue2.setValue(n.length>1?null===n[1].asc_getVal()?"":n[1].asc_getVal():"")}}},save:function(){if(this.api&&this.properties&&this.rbOr&&this.rbAnd&&this.cmbCondition1&&this.cmbCondition2&&this.cmbValue1&&this.cmbValue2){var t=this.properties.asc_getFilterObj();t.asc_setFilter(new Asc.CustomFilters),t.asc_setType(Asc.c_oAscAutoFilterTypes.CustomFilters);var e=t.asc_getFilter();e.asc_setCustomFilters(0==this.cmbCondition2.getValue()?[new Asc.CustomFilter]:[new Asc.CustomFilter,new Asc.CustomFilter]);var i=e.asc_getCustomFilters();e.asc_setAnd(this.rbAnd.getValue()),i[0].asc_setOperator(this.cmbCondition1.getValue()),i[0].asc_setVal(this.cmbValue1.getValue()),0!==this.cmbCondition2.getValue()&&(i[1].asc_setOperator(this.cmbCondition2.getValue()||void 0),i[1].asc_setVal(this.cmbValue2.getValue())),this.api.asc_applyAutoFilter(this.properties)}},onPrimary:function(){return this.save(),this.close(),!1},cancelButtonText:"Cancel",capAnd:"And",capCondition1:"equals",capCondition10:"does not end with",capCondition11:"contains",capCondition12:"does not contain",capCondition2:"does not equal",capCondition3:"is greater than",capCondition4:"is greater than or equal to",capCondition5:"is less than",capCondition6:"is less than or equal to",capCondition7:"begins with",capCondition8:"does not begin with",capCondition9:"ends with",capOr:"Or",textNoFilter:"no filter",textShowRows:"Show rows where",textUse1:"Use ? to present any single character",textUse2:"Use * to present any series of character",txtTitle:"Custom Filter"},SSE.Views.DigitalFilterDialog||{})),SSE.Views.Top10FilterDialog=Common.UI.Window.extend(_.extend({initialize:function(t){var e=this,i={};_.extend(i,{width:318,height:160,contentWidth:180,header:!0,cls:"filter-dlg",contentTemplate:"",title:e.txtTitle,items:[]},t),this.template=t.template||['
','
','
','",'
',"
",'
','','
',"
",'
','','
',"
","
","
",'
','"].join(""),this.api=t.api,this.handler=t.handler,i.tpl=_.template(this.template)(i),Common.UI.Window.prototype.initialize.call(this,i)},render:function(){Common.UI.Window.prototype.render.call(this),this.cmbType=new Common.UI.ComboBox({el:$("#id-top10-type-combo",this.$window),style:"width: 85px;",menuStyle:"min-width: 85px;",cls:"input-group-nr",data:[{value:!0,displayValue:this.txtTop},{value:!1,displayValue:this.txtBottom}],editable:!1}),this.cmbType.setValue(!0),this.cmbItem=new Common.UI.ComboBox({el:$("#id-top10-item-combo",this.$window),style:"width: 85px;",menuStyle:"min-width: 85px;",cls:"input-group-nr",data:[{value:!1,displayValue:this.txtItems},{value:!0,displayValue:this.txtPercent}],editable:!1}),this.cmbItem.setValue(!1),this.cmbItem.on("selected",_.bind(function(t,e){this.spnCount.setDefaultUnit(e.value?"%":"")},this)),this.spnCount=new Common.UI.MetricSpinner({el:$("#id-top10-count-spin"),step:1,width:85,defaultUnit:"",value:"10",maxValue:500,minValue:1}),this.$window.find(".dlg-btn").on("click",_.bind(this.onBtnClick,this)),this.loadDefaults()},show:function(){Common.UI.Window.prototype.show.call(this);var t=this;_.defer(function(){t.spnCount&&t.spnCount.$input.focus()},500)},close:function(){this.api&&this.api.asc_enableKeyEvents(!0),Common.UI.Window.prototype.close.call(this)},onBtnClick:function(t){t.currentTarget.attributes&&t.currentTarget.attributes.result&&("ok"===t.currentTarget.attributes.result.value&&this.save(),this.close())},setSettings:function(t){this.properties=t},loadDefaults:function(){if(this.properties){var t=this.properties.asc_getFilterObj();if(t.asc_getType()==Asc.c_oAscAutoFilterTypes.Top10){var e=t.asc_getFilter(),i=e.asc_getTop(),n=e.asc_getPercent();this.cmbType.setValue(i||null===i),this.cmbItem.setValue(n||null===n),this.spnCount.setDefaultUnit(n||null===n?"%":""),this.spnCount.setValue(e.asc_getVal())}}},save:function(){if(this.api&&this.properties){var t=this.properties.asc_getFilterObj();t.asc_setFilter(new Asc.Top10),t.asc_setType(Asc.c_oAscAutoFilterTypes.Top10);var e=t.asc_getFilter();e.asc_setTop(this.cmbType.getValue()),e.asc_setPercent(this.cmbItem.getValue()),e.asc_setVal(this.spnCount.getNumberValue()),this.api.asc_applyAutoFilter(this.properties)}},onPrimary:function(){return this.save(),this.close(),!1},cancelButtonText:"Cancel",okButtonText:"OK",txtTitle:"Top 10 AutoFilter",textType:"Show",txtTop:"Top",txtBottom:"Bottom",txtItems:"Item",txtPercent:"Percent"},SSE.Views.Top10FilterDialog||{})),SSE.Views.AutoFilterDialog=Common.UI.Window.extend(_.extend({initialize:function(t){var e=this,i={},n=void 0,o=void 0;Common.Utils.InternalSettings.get("sse-settings-size-filter-window")&&(n=Common.Utils.InternalSettings.get("sse-settings-size-filter-window")[0],o=Common.Utils.InternalSettings.get("sse-settings-size-filter-window")[1]),_.extend(i,{width:n||450,height:o||265,contentWidth:n-50||400,header:!1,cls:"filter-dlg",contentTemplate:"",title:e.txtTitle,modal:!1,animate:!1,items:[],resizable:!0,minwidth:450,minheight:265},t),this.template=t.template||['
','
','
','','
','
',"
","
",'","
",'","
"].join(""),this.api=t.api,this.handler=t.handler,this.throughIndexes=[],this.filteredIndexes=[],this.curSize=null,i.tpl=_.template(this.template)(i),Common.UI.Window.prototype.initialize.call(this,i),this.on("resizing",_.bind(this.onWindowResizing,this)),this.on("resize",_.bind(this.onWindowResize,this))},render:function(){var t=this;Common.UI.Window.prototype.render.call(this);var e=this.$window.find(".resize-border");this.$window.find(".resize-border.left, .resize-border.top").css({cursor:"default"}),e.css({background:"none",border:"none"}),e.removeClass("left"),e.removeClass("top"),this.$window.find(".btn").on("click",_.bind(this.onBtnClick,this)),this.btnOk=new Common.UI.Button({cls:"btn normal dlg-btn primary",caption:this.okButtonText,style:"margin-right:10px;",enableToggle:!1,allowDepress:!1}),this.btnOk&&(this.btnOk.render($("#id-apply-filter",this.$window)),this.btnOk.on("click",_.bind(this.onApplyFilter,this))),this.miSortLow2High=new Common.UI.MenuItem({caption:this.txtSortLow2High,toggleGroup:"menufiltersort",checkable:!0,checked:!1}),this.miSortLow2High.on("click",_.bind(this.onSortType,this,Asc.c_oAscSortOptions.Ascending)),this.miSortHigh2Low=new Common.UI.MenuItem({caption:this.txtSortHigh2Low,toggleGroup:"menufiltersort",checkable:!0,checked:!1}),this.miSortHigh2Low.on("click",_.bind(this.onSortType,this,Asc.c_oAscSortOptions.Descending)),this.miSortCellColor=new Common.UI.MenuItem({caption:this.txtSortCellColor,toggleGroup:"menufiltersort",checkable:!0,checked:!1,menu:new Common.UI.Menu({style:"min-width: inherit; padding: 0px;",menuAlign:"tl-tr",items:[{template:_.template('
')}]})}),this.miSortFontColor=new Common.UI.MenuItem({caption:this.txtSortFontColor,toggleGroup:"menufiltersort",checkable:!0,checked:!1,menu:new Common.UI.Menu({style:"min-width: inherit; padding: 0px;",menuAlign:"tl-tr",items:[{template:_.template('
')}]})}),this.miNumFilter=new Common.UI.MenuItem({caption:this.txtNumFilter,toggleGroup:"menufilterfilter",checkable:!0,checked:!1,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{value:Asc.c_oAscCustomAutoFilter.equals,caption:this.txtEquals,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.doesNotEqual,caption:this.txtNotEquals,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.isGreaterThan,caption:this.txtGreater,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo,caption:this.txtGreaterEquals,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.isLessThan,caption:this.txtLess,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo,caption:this.txtLessEquals,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:-2,caption:this.txtBetween,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.top10,caption:this.txtTop10,checkable:!0,type:Asc.c_oAscAutoFilterTypes.Top10},{value:Asc.c_oAscDynamicAutoFilter.aboveAverage,caption:this.txtAboveAve,checkable:!0,type:Asc.c_oAscAutoFilterTypes.DynamicFilter},{value:Asc.c_oAscDynamicAutoFilter.belowAverage,caption:this.txtBelowAve,checkable:!0,type:Asc.c_oAscAutoFilterTypes.DynamicFilter},{value:-1,caption:this.btnCustomFilter+"...",checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters}]})});for(var i=this.miNumFilter.menu.items,n=0;n
')}]})}),this.miFilterFontColor=new Common.UI.MenuItem({caption:this.txtFilterFontColor,toggleGroup:"menufilterfilter",checkable:!0,checked:!1,menu:new Common.UI.Menu({style:"min-width: inherit; padding: 0px;",menuAlign:"tl-tr",items:[{template:_.template('
')}]})}),this.miClear=new Common.UI.MenuItem({caption:this.txtClear,checkable:!1}),this.miClear.on("click",_.bind(this.onClear,this)),this.miReapply=new Common.UI.MenuItem({caption:this.txtReapply,checkable:!1}),this.miReapply.on("click",_.bind(this.onReapply,this)),this.filtersMenu=new Common.UI.Menu({items:[this.miSortLow2High,this.miSortHigh2Low,this.miSortCellColor,this.miSortFontColor,{caption:"--"},this.miNumFilter,this.miTextFilter,this.miFilterCellColor,this.miFilterFontColor,this.miClear,{caption:"--"},this.miReapply]});var o=this.$window.find("#menu-container-filters");this.filtersMenu.render(o),this.filtersMenu.cmpEl.attr({tabindex:"-1"}),this.mnuSortColorCellsPicker=new Common.UI.ColorPaletteExt({el:$("#filter-dlg-sort-cells-color"),colors:[]}),this.mnuSortColorCellsPicker.on("select",_.bind(this.onSortColorSelect,this,Asc.c_oAscSortOptions.ByColorFill)),this.mnuSortColorFontPicker=new Common.UI.ColorPaletteExt({el:$("#filter-dlg-sort-font-color"),colors:[]}),this.mnuSortColorFontPicker.on("select",_.bind(this.onSortColorSelect,this,Asc.c_oAscSortOptions.ByColorFont)), this.mnuFilterColorCellsPicker=new Common.UI.ColorPaletteExt({el:$("#filter-dlg-filter-cells-color"),colors:[]}),this.mnuFilterColorCellsPicker.on("select",_.bind(this.onFilterColorSelect,this,!0)),this.mnuFilterColorFontPicker=new Common.UI.ColorPaletteExt({el:$("#filter-dlg-filter-font-color"),colors:[]}),this.mnuFilterColorFontPicker.on("select",_.bind(this.onFilterColorSelect,this,!1)),this.input=new Common.UI.InputField({el:$("#id-sd-cell-search",this.$window),allowBlank:!0,placeHolder:this.txtEmpty,validateOnChange:!0,validation:function(){return!0}}).on("changing",function(e,i){i.length?(i=i.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1"),t.filter=new RegExp(i,"ig")):t.filter=void 0,t.setupDataCells()}),this.cells=new Common.UI.DataViewStore,this.filterExcludeCells=new Common.UI.DataViewStore,this.cells&&(this.cellsList=new Common.UI.ListView({el:$("#id-dlg-filter-values",this.$window),store:this.cells,simpleAddMode:!0,template:_.template(['
'].join("")),itemTemplate:_.template(["
",'",'
','
<%= Common.Utils.String.htmlEncode(value) %>
','<% if (typeof count !=="undefined" && count) { %>','
<%= count%>
',"<% } %>","
","
"].join(""))}),this.cellsList.store.comparator=function(t,e){if("0"==t.get("groupid"))return-1;if("0"==e.get("groupid"))return 1;if("2"==t.get("groupid"))return-1;if("2"==e.get("groupid"))return 1;var i=t.get("intval"),n=e.get("intval"),o=void 0!==i;return o!==(void 0!==n)?o?-1:1:(!o&&(i=t.get("cellvalue").toLowerCase())&&(n=e.get("cellvalue").toLowerCase()),i==n?0:""==n||""!==i&&i1?r[1].asc_getOperator()||0:0,i=null===r[0].asc_getVal()?"":r[0].asc_getVal(),n=r.length>1?null===r[1].asc_getVal()?"":r[1].asc_getVal():""}if(-1!==t.value){var c=new Asc.CustomFilters;c.asc_setCustomFilters(-2==t.value?[new Asc.CustomFilter,new Asc.CustomFilter]:[new Asc.CustomFilter]);var h=c.asc_getCustomFilters();if(h[0].asc_setOperator(-2==t.value?Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo:t.value),-2==t.value){var d=o==Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo&&s==Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo;c.asc_setAnd(!d||a),h[0].asc_setVal(d?i:""),h[1].asc_setOperator(Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo),h[1].asc_setVal(d?n:"")}else c.asc_setAnd(!0),h[0].asc_setVal(t.value==o?i:"");e.asc_setFilter(c),e.asc_setType(Asc.c_oAscAutoFilterTypes.CustomFilters)}var p=this,m=new SSE.Views.DigitalFilterDialog({api:this.api,type:"number"}).on({close:function(){p.close()}});this.close(),m.setSettings(this.configTo),m.show()},onTextFilterMenuClick:function(t,e){var i=this.configTo.asc_getFilterObj(),n="",o=Asc.c_oAscCustomAutoFilter.equals;if(i.asc_getType()==Asc.c_oAscAutoFilterTypes.CustomFilters){var s=i.asc_getFilter(),a=s.asc_getCustomFilters();s.asc_getAnd(),o=a[0].asc_getOperator(),a.length>1?a[1].asc_getOperator()||0:0,n=null===a[0].asc_getVal()?"":a[0].asc_getVal(),a.length>1?null===a[1].asc_getVal()?"":a[1].asc_getVal():""}if(-1!==e.value){var l=new Asc.CustomFilters;l.asc_setCustomFilters([new Asc.CustomFilter]);var r=l.asc_getCustomFilters();l.asc_setAnd(!0),r[0].asc_setOperator(e.value),r[0].asc_setVal(e.value==o?n:""),i.asc_setFilter(l),i.asc_setType(Asc.c_oAscAutoFilterTypes.CustomFilters)}var c=this,h=new SSE.Views.DigitalFilterDialog({api:this.api,type:"text"}).on({close:function(){c.close()}});this.close(),h.setSettings(this.configTo),h.show()},onNumDynamicFilterItemClick:function(t){var e=this.configTo.asc_getFilterObj();e.asc_getType()!==Asc.c_oAscAutoFilterTypes.DynamicFilter&&(e.asc_setFilter(new Asc.DynamicFilter),e.asc_setType(Asc.c_oAscAutoFilterTypes.DynamicFilter)),e.asc_getFilter().asc_setType(t.value),this.api.asc_applyAutoFilter(this.configTo),this.close()},onTop10FilterItemClick:function(t,e){var i=this,n=new SSE.Views.Top10FilterDialog({api:this.api}).on({close:function(){i.close()}});this.close(),n.setSettings(this.configTo),n.show()},onFilterColorSelect:function(t,e,i){var n=this.configTo.asc_getFilterObj();n.asc_getType()!==Asc.c_oAscAutoFilterTypes.ColorFilter&&(n.asc_setFilter(new Asc.ColorFilter),n.asc_setType(Asc.c_oAscAutoFilterTypes.ColorFilter));var o=n.asc_getFilter();o.asc_setCellColor(!!t&&null),o.asc_setCColor(t&&"transparent"==i||!t&&"#000000"==i?null:Common.Utils.ThemeColor.getRgbColor(i)),this.api.asc_applyAutoFilter(this.configTo),this.close()},onSortColorSelect:function(t,e,i){if(this.api&&this.configTo){var n=t==Asc.c_oAscSortOptions.ByColorFill;this.api.asc_sortColFilter(t,this.configTo.asc_getCellId(),this.configTo.asc_getDisplayName(),n&&"transparent"==i||!n&&"#000000"==i?null:Common.Utils.ThemeColor.getRgbColor(i))}this.close()},onCellCheck:function(t,e,i){if(!this.checkCellTrigerBlock){var n="",o="",s=!1,a=null,l=window.event?window.event:window._event;if(l){if(o=l.target.type,n=$(l.currentTarget).find(".list-item"),n.length){a=n.get(0).getBoundingClientRect();var r=l.clientX*Common.Utils.zoom(),c=l.clientY*Common.Utils.zoom();a.left1&&(o[parseInt(t.get("throughIndex"))]=i))});else{e.set("check",i),o[parseInt(e.get("throughIndex"))]=i;for(var s=i,a=0;a0;if(this.miSortFontColor.setVisible(c),this.miFilterFontColor.setVisible(c),c){var h=[];a.forEach(function(t,e){t?h.push(Common.Utils.ThemeColor.getHexColor(t.get_r(),t.get_g(),t.get_b()).toLocaleUpperCase()):h.push("000000")}),this.mnuSortColorFontPicker.updateColors(h),this.mnuFilterColorFontPicker.updateColors(h),this.miFilterFontColor.setChecked(!1,!0),this.miSortFontColor.setChecked(l==Asc.c_oAscSortOptions.ByColorFont,!0),l==Asc.c_oAscSortOptions.ByColorFont&&this.mnuSortColorFontPicker.select(r||"000000",!0)}if(c=s&&s.length>0,this.miSortCellColor.setVisible(c),this.miFilterCellColor.setVisible(c),c){var h=[];s.forEach(function(t,e){t?h.push(Common.Utils.ThemeColor.getHexColor(t.get_r(),t.get_g(),t.get_b()).toLocaleUpperCase()):h.push("transparent")}),this.mnuSortColorCellsPicker.updateColors(h),this.mnuFilterColorCellsPicker.updateColors(h),this.miFilterCellColor.setChecked(!1,!0),this.miSortCellColor.setChecked(l==Asc.c_oAscSortOptions.ByColorFill,!0),l==Asc.c_oAscSortOptions.ByColorFill&&this.mnuSortColorCellsPicker.select(r||"transparent",!0)}if(e){var d=t.asc_getFilter(),p=d.asc_getCustomFilters(),m=(d.asc_getAnd(),p[0].asc_getOperator()),u=p.length>1?p[1].asc_getOperator()||0:0,g=o?this.miTextFilter.menu.items:this.miNumFilter.menu.items,b=!0;1==p.length?g.forEach(function(t){var e=t.options.type==Asc.c_oAscAutoFilterTypes.CustomFilters&&t.value==m;t.setChecked(e,!0),e&&(b=!1)}):!o&&(m==Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo&&u==Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo||m==Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo&&u==Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo)&&(g[6].setChecked(!0,!0),b=!1),b&&g[g.length-1].setChecked(!0,!0)}else if(this.initialFilterType===Asc.c_oAscAutoFilterTypes.ColorFilter){var f=t.asc_getFilter(),C=f.asc_getCColor();C&&(C=Common.Utils.ThemeColor.getHexColor(C.get_r(),C.get_g(),C.get_b()).toLocaleUpperCase()),null===f.asc_getCellColor()?(this.miFilterCellColor.setChecked(!0,!0),this.mnuFilterColorCellsPicker.select(C||"transparent",!0)):!1===f.asc_getCellColor()&&(this.miFilterFontColor.setChecked(!0,!0),this.mnuFilterColorFontPicker.select(C||"000000",!0))}else if(i||n){var v=i?t.asc_getFilter().asc_getType():null,g=this.miNumFilter.menu.items;g.forEach(function(t){t.setChecked(i&&t.options.type==Asc.c_oAscAutoFilterTypes.DynamicFilter&&t.value==v||n&&t.options.type==Asc.c_oAscAutoFilterTypes.Top10,!0)})}this.miClear.setDisabled(this.initialFilterType===Asc.c_oAscAutoFilterTypes.None),this.miReapply.setDisabled(this.initialFilterType===Asc.c_oAscAutoFilterTypes.None),this.btnOk.setDisabled(this.initialFilterType!==Asc.c_oAscAutoFilterTypes.Filters&&this.initialFilterType!==Asc.c_oAscAutoFilterTypes.None)},setupDataCells:function(){function t(t){return!isNaN(parseFloat(t))&&isFinite(t)}var e,i,n,o=this,s=0,a=2,l=!0,r=!1,c=0,h=[],d=[],p=o.filter?o.filteredIndexes:o.throughIndexes;this.configTo.asc_getValues().forEach(function(r){i=r.asc_getText(),e=t(i),l=!0,n=r.asc_getRepeats?r.asc_getRepeats():void 0,o.filter?(null===i.match(o.filter)&&(l=!1),p[a]=l):void 0==p[a]&&(p[a]=r.asc_getVisible()),l?(h.push(new Common.UI.DataViewModel({id:++s,selected:!1,allowSelected:!0,cellvalue:i,value:e?i:i.length>0?i:o.textEmptyItem,intval:e?parseFloat(i):void 0,strval:e?"":i,groupid:"1",check:p[a],throughIndex:a,count:n?n.toString():""})),p[a]&&c++):d.push(new Common.UI.DataViewModel({cellvalue:i})),++a}),c==h.length?r=!0:c>0&&(r="indeterminate"),(o.filter||void 0==p[0])&&(p[0]=!0),(!o.filter||h.length>0)&&h.unshift(new Common.UI.DataViewModel({id:++s,selected:!1,allowSelected:!0,value:o.filter?this.textSelectAllResults:this.textSelectAll,groupid:"0",check:p[0],throughIndex:0})),o.filter&&h.length>1&&(void 0==p[1]&&(p[1]=!1),h.splice(1,0,new Common.UI.DataViewModel({id:++s,selected:!1,allowSelected:!0,value:this.textAddSelection,groupid:"2",check:p[1],throughIndex:1}))),this.cells.reset(h),this.filterExcludeCells.reset(d),this.cells.length&&(this.checkCellTrigerBlock=!0,this.cells.at(0).set("check",r),this.checkCellTrigerBlock=void 0),this.btnOk.setDisabled(this.cells.length<1),this.cellsList.scroller.update({minScrollbarLength:40,alwaysVisibleY:!0,suppressScrollX:!0}),this.cellsList.cmpEl.toggleClass("scroll-padding",this.cellsList.scroller.isVisible())},testFilter:function(){var t=this,e=!1;return this.cells&&(this.filter&&this.filteredIndexes[1]?e=!0:this.cells.forEach(function(t){if("1"==t.get("groupid")&&t.get("check"))return e=!0,!0})),e||(t._skipCheckDocumentClick=!0,Common.UI.warning({title:this.textWarning,msg:this.warnNoSelected,callback:function(){t._skipCheckDocumentClick=!1,_.delay(function(){t.input.$el.find("input").focus()},100,this)}})),e},save:function(){if(this.api&&this.configTo&&this.cells&&this.filterExcludeCells){var t=this.configTo.asc_getValues(),e=!1;if(this.filter&&this.filteredIndexes[1])this.initialFilterType===Asc.c_oAscAutoFilterTypes.CustomFilters&&t.forEach(function(t,e){t.asc_setVisible(!0)}),this.cells.each(function(e){"1"==e.get("groupid")&&t[parseInt(e.get("throughIndex"))-2].asc_setVisible(e.get("check"))}),t.forEach(function(t,i){if(t.asc_getVisible())return e=!0,!0});else{var i=this.filter?this.filteredIndexes:this.throughIndexes;t.forEach(function(t,e){t.asc_setVisible(i[e+2])}),e=!0}e&&(this.configTo.asc_getFilterObj().asc_setType(Asc.c_oAscAutoFilterTypes.Filters),this.api.asc_applyAutoFilter(this.configTo))}},onPrimary:function(){return this.save(),this.close(),!1},onWindowResize:function(t){if(t&&"start"==t[1])this.curSize={resize:!1,height:this.getSize()[1]};else if(this.curSize.resize){var e=this.getSize();this.$window.find(".combo-values").css({height:e[1]-100+"px"}),this.cellsList.scroller.update({minScrollbarLength:40,alwaysVisibleY:!0,suppressScrollX:!0})}},onWindowResizing:function(){if(this.curSize){var t=this.getSize();t[1]!==this.curSize.height&&(this.curSize.resize||(this.curSize.resize=!0,this.cellsList.scroller.update({minScrollbarLength:40,alwaysVisibleY:!1,suppressScrollX:!0})),this.$window.find(".combo-values").css({height:t[1]-100+"px"}),this.curSize.height=t[1]),Common.Utils.InternalSettings.set("sse-settings-size-filter-window",t)}},okButtonText:"Ok",btnCustomFilter:"Custom Filter",textSelectAll:"Select All",txtTitle:"Filter",warnNoSelected:"You must choose at least one value",textWarning:"Warning",cancelButtonText:"Cancel",textEmptyItem:"{Blanks}",txtEmpty:"Enter cell's filter",txtSortLow2High:"Sort Lowest to Highest",txtSortHigh2Low:"Sort Highest to Lowest",txtSortCellColor:"Sort by cells color",txtSortFontColor:"Sort by font color",txtNumFilter:"Number filter",txtTextFilter:"Text filter",txtFilterCellColor:"Filter by cells color",txtFilterFontColor:"Filter by font color",txtClear:"Clear",txtReapply:"Reapply",txtEquals:"Equals...",txtNotEquals:"Does not equal...",txtGreater:"Greater than...",txtGreaterEquals:"Greater than or equal to...",txtLess:"Less than...",txtLessEquals:"Less than or equal to...",txtBetween:"Between...",txtTop10:"Top 10",txtAboveAve:"Above average",txtBelowAve:"Below average",txtBegins:"Begins with...",txtNotBegins:"Does not begin with...",txtEnds:"Ends with...",txtNotEnds:"Does not end with...",txtContains:"Contains...",txtNotContains:"Does not contain...",textSelectAllResults:"Select All Search Results",textAddSelection:"Add current selection to filter"},SSE.Views.AutoFilterDialog||{}))});var c_paragraphLinerule={LINERULE_AUTO:1,LINERULE_EXACT:2},c_paragraphTextAlignment={RIGHT:0,LEFT:1,CENTERED:2,JUSTIFIED:3},c_paragraphSpecial={NONE_SPECIAL:0,FIRST_LINE:1,HANGING:2};if(define("spreadsheeteditor/main/app/controller/DocumentHolder",["core","common/main/lib/util/utils","common/main/lib/util/Shortcuts","common/main/lib/view/CopyWarningDialog","common/main/lib/view/OpenDialog","spreadsheeteditor/main/app/view/DocumentHolder","spreadsheeteditor/main/app/view/HyperlinkSettingsDialog","spreadsheeteditor/main/app/view/ParagraphSettingsAdvanced","spreadsheeteditor/main/app/view/ImageSettingsAdvanced","spreadsheeteditor/main/app/view/SetValueDialog","spreadsheeteditor/main/app/view/AutoFilterDialog"],function(){"use strict";SSE.Controllers.DocumentHolder=Backbone.Controller.extend(_.extend({models:[],collections:[],views:["DocumentHolder"],initialize:function(){var t=this;t.tooltips={hyperlink:{},comment:{},coauth:{ttHeight:20},row_column:{ttHeight:20},filter:{ttHeight:40},func_arg:{},input_msg:{}},t.mouse={},t.popupmenu=!1,t.rangeSelectionMode=!1,t.namedrange_locked=!1,t._currentMathObj=void 0,t._currentParaObjDisabled=!1,t._isDisabled=!1,t._state={},this.wrapEvents={apiHideComment:_.bind(this.onApiHideComment,this)},this.addListeners({DocumentHolder:{createdelayedelements:this.onCreateDelayedElements}});var e={};this.hkComments="alt+h",e[this.hkComments]=function(){return t.onAddComment(),!1},Common.util.Shortcuts.delegateShortcuts({shortcuts:e})},onLaunch:function(){var t=this;t.documentHolder=this.createView("DocumentHolder"),t.documentHolder.render(),t.documentHolder.el.tabIndex=-1,$(document).on("mousewheel",_.bind(t.onDocumentWheel,t)),$(document).on("mousedown",_.bind(t.onDocumentRightDown,t)),$(document).on("mouseup",_.bind(t.onDocumentRightUp,t)),$(document).on("keydown",_.bind(t.onDocumentKeyDown,t)),$(document).on("mousemove",_.bind(t.onDocumentMouseMove,t)),$(window).on("resize",_.bind(t.onDocumentResize,t)),SSE.getController("Viewport").getView("Viewport").hlayout.on("layout:resizedrag",_.bind(t.onDocumentResize,t)),Common.NotificationCenter.on({"window:show":function(e){t.hideHyperlinkTip()},"modal:show":function(e){t.hideCoAuthTips()},"layout:changed":function(e){t.hideHyperlinkTip(),t.hideCoAuthTips(),t.onDocumentResize()},"cells:range":function(e){t.onCellsRange(e)}}),Common.Gateway.on("processmouse",_.bind(t.onProcessMouse,t))},onCreateDelayedElements:function(t){var e=this;e.permissions.isEdit?(t.pmiCut.on("click",_.bind(e.onCopyPaste,e)),t.pmiCopy.on("click",_.bind(e.onCopyPaste,e)),t.pmiPaste.on("click",_.bind(e.onCopyPaste,e)),t.pmiImgCut.on("click",_.bind(e.onCopyPaste,e)),t.pmiImgCopy.on("click",_.bind(e.onCopyPaste,e)),t.pmiImgPaste.on("click",_.bind(e.onCopyPaste,e)),t.pmiTextCut.on("click",_.bind(e.onCopyPaste,e)),t.pmiTextCopy.on("click",_.bind(e.onCopyPaste,e)),t.pmiTextPaste.on("click",_.bind(e.onCopyPaste,e)),t.pmiCommonCut.on("click",_.bind(e.onCopyPaste,e)),t.pmiCommonCopy.on("click",_.bind(e.onCopyPaste,e)),t.pmiCommonPaste.on("click",_.bind(e.onCopyPaste,e)),t.pmiInsertEntire.on("click",_.bind(e.onInsertEntire,e)),t.pmiDeleteEntire.on("click",_.bind(e.onDeleteEntire,e)),t.pmiInsertCells.menu.on("item:click",_.bind(e.onInsertCells,e)),t.pmiDeleteCells.menu.on("item:click",_.bind(e.onDeleteCells,e)),t.pmiSparklines.menu.on("item:click",_.bind(e.onClear,e)),t.pmiSortCells.menu.on("item:click",_.bind(e.onSortCells,e)),t.pmiFilterCells.menu.on("item:click",_.bind(e.onFilterCells,e)),t.pmiReapply.on("click",_.bind(e.onReapply,e)),t.pmiClear.menu.on("item:click",_.bind(e.onClear,e)),t.pmiSelectTable.menu.on("item:click",_.bind(e.onSelectTable,e)),t.pmiInsertTable.menu.on("item:click",_.bind(e.onInsertTable,e)),t.pmiDeleteTable.menu.on("item:click",_.bind(e.onDeleteTable,e)),t.pmiInsFunction.on("click",_.bind(e.onInsFunction,e)),t.menuAddHyperlink.on("click",_.bind(e.onInsHyperlink,e)),t.menuEditHyperlink.on("click",_.bind(e.onInsHyperlink,e)),t.menuRemoveHyperlink.on("click",_.bind(e.onDelHyperlink,e)),t.pmiRowHeight.menu.on("item:click",_.bind(e.onSetSize,e)),t.pmiColumnWidth.menu.on("item:click",_.bind(e.onSetSize,e)),t.pmiEntireHide.on("click",_.bind(e.onEntireHide,e)),t.pmiEntireShow.on("click",_.bind(e.onEntireShow,e)),t.pmiFreezePanes.on("click",_.bind(e.onFreezePanes,e)),t.pmiEntriesList.on("click",_.bind(e.onEntriesList,e)),t.pmiAddComment.on("click",_.bind(e.onAddComment,e)),t.pmiAddNamedRange.on("click",_.bind(e.onAddNamedRange,e)),t.menuImageArrange.menu.on("item:click",_.bind(e.onImgMenu,e)),t.menuImgRotate.menu.on("item:click",_.bind(e.onImgMenu,e)),t.menuImgCrop.menu.on("item:click",_.bind(e.onImgCrop,e)),t.menuImageAlign.menu.on("item:click",_.bind(e.onImgMenuAlign,e)),t.menuParagraphVAlign.menu.on("item:click",_.bind(e.onParagraphVAlign,e)),t.menuParagraphDirection.menu.on("item:click",_.bind(e.onParagraphDirection,e)),t.menuParagraphBullets.menu.on("item:click",_.bind(e.onSelectNoneBullet,e)),t.menuAddHyperlinkShape.on("click",_.bind(e.onInsHyperlink,e)),t.menuEditHyperlinkShape.on("click",_.bind(e.onInsHyperlink,e)),t.menuRemoveHyperlinkShape.on("click",_.bind(e.onRemoveHyperlinkShape,e)),t.pmiTextAdvanced.on("click",_.bind(e.onTextAdvanced,e)),t.mnuShapeAdvanced.on("click",_.bind(e.onShapeAdvanced,e)),t.mnuChartEdit.on("click",_.bind(e.onChartEdit,e)),t.mnuImgAdvanced.on("click",_.bind(e.onImgAdvanced,e)),t.textInShapeMenu.on("render:after",_.bind(e.onTextInShapeAfterRender,e)),t.menuSignatureEditSign.on("click",_.bind(e.onSignatureClick,e)),t.menuSignatureEditSetup.on("click",_.bind(e.onSignatureClick,e)),t.menuImgOriginalSize.on("click",_.bind(e.onOriginalSizeClick,e)),t.menuImgReplace.menu.on("item:click",_.bind(e.onImgReplace,e)),t.pmiNumFormat.menu.on("item:click",_.bind(e.onNumberFormatSelect,e)),t.pmiNumFormat.menu.on("show:after",_.bind(e.onNumberFormatOpenAfter,e)),t.pmiAdvancedNumFormat.on("click",_.bind(e.onCustomNumberFormat,e))):(t.menuViewCopy.on("click",_.bind(e.onCopyPaste,e)),t.menuViewUndo.on("click",_.bind(e.onUndo,e)),t.menuViewAddComment.on("click",_.bind(e.onAddComment,e)),t.menuSignatureViewSign.on("click",_.bind(e.onSignatureClick,e)),t.menuSignatureDetails.on("click",_.bind(e.onSignatureClick,e)),t.menuSignatureViewSetup.on("click",_.bind(e.onSignatureClick,e)),t.menuSignatureRemove.on("click",_.bind(e.onSignatureClick,e)));var i=t.cmpEl;if(i){i.on({mousedown:function(t){"canvas"==t.target.localName&&2!=t.button&&Common.UI.Menu.Manager.hideAll()},click:function(t){e.api&&(e.api.isTextAreaBlur=!1,"canvas"!=t.target.localName||e.isEditFormula||i.focus())}});var n=/Firefox/i.test(navigator.userAgent)?"DOMMouseScroll":"mousewheel";!function(t,e,i){t.addEventListener?t.addEventListener(e,i,!1):t.attachEvent("on"+e,i)}(t.el,n,_.bind(this.onDocumentWheel,this)),e.cellEditor=$("#ce-cell-content")}},loadConfig:function(t){this.editorConfig=t.config},setMode:function(t){this.permissions=t,this.permissions.canCoAuthoring&&this.permissions.canComments?Common.util.Shortcuts.resumeEvents(this.hkComments):Common.util.Shortcuts.suspendEvents(this.hkComments)},setApi:function(t){return this.api=t,this.api.asc_registerCallback("asc_onContextMenu",_.bind(this.onApiContextMenu,this)),this.api.asc_registerCallback("asc_onMouseMove",_.bind(this.onApiMouseMove,this)),this.api.asc_registerCallback("asc_onHideComment",this.wrapEvents.apiHideComment),this.api.asc_registerCallback("asc_onHyperlinkClick",_.bind(this.onApiHyperlinkClick,this)),this.api.asc_registerCallback("asc_onCoAuthoringDisconnect",_.bind(this.onApiCoAuthoringDisconnect,this)),Common.NotificationCenter.on("api:disconnect",_.bind(this.onApiCoAuthoringDisconnect,this)),this.api.asc_registerCallback("asc_onSelectionChanged",_.bind(this.onSelectionChanged,this)),!0===this.permissions.isEdit&&(this.api.asc_registerCallback("asc_onSetAFDialog",_.bind(this.onApiAutofilter,this)),this.api.asc_registerCallback("asc_onEditCell",_.bind(this.onApiEditCell,this)),this.api.asc_registerCallback("asc_onLockDefNameManager",_.bind(this.onLockDefNameManager,this)),this.api.asc_registerCallback("asc_onEntriesListMenu",_.bind(this.onEntriesListMenu,this)),this.api.asc_registerCallback("asc_onFormulaCompleteMenu",_.bind(this.onFormulaCompleteMenu,this)),this.api.asc_registerCallback("asc_onShowSpecialPasteOptions",_.bind(this.onShowSpecialPasteOptions,this)),this.api.asc_registerCallback("asc_onHideSpecialPasteOptions",_.bind(this.onHideSpecialPasteOptions,this)),this.api.asc_registerCallback("asc_onToggleAutoCorrectOptions",_.bind(this.onToggleAutoCorrectOptions,this)),this.api.asc_registerCallback("asc_onFormulaInfo",_.bind(this.onFormulaInfo,this)),this.api.asc_registerCallback("asc_ChangeCropState",_.bind(this.onChangeCropState,this)),this.api.asc_registerCallback("asc_onInputMessage",_.bind(this.onInputMessage,this))),this},resetApi:function(t){this.api.asc_unregisterCallback("asc_onHideComment",this.wrapEvents.apiHideComment),this.api.asc_registerCallback("asc_onHideComment",this.wrapEvents.apiHideComment)},onCopyPaste:function(t){var e=this;if(e.api){if("cut"==t.value?e.api.asc_Cut():"copy"==t.value?e.api.asc_Copy():e.api.asc_Paste())Common.component.Analytics.trackEvent("ToolBar","Copy Warning");else{var i=Common.localStorage.getItem("sse-hide-copywarning");i&&1==parseInt(i)||new Common.Views.CopyWarningDialog({handler:function(t){t&&Common.localStorage.setItem("sse-hide-copywarning",1),Common.NotificationCenter.trigger("edit:complete",e.documentHolder)}}).show()}}Common.NotificationCenter.trigger("edit:complete",e.documentHolder)},onInsertEntire:function(t){if(this.api){switch(this.api.asc_getCellInfo().asc_getFlags().asc_getSelectionType()){case Asc.c_oAscSelectionType.RangeRow:this.api.asc_insertCells(Asc.c_oAscInsertOptions.InsertRows);break;case Asc.c_oAscSelectionType.RangeCol:this.api.asc_insertCells(Asc.c_oAscInsertOptions.InsertColumns)}Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Insert Entire")}},onInsertCells:function(t,e){this.api&&(this.api.asc_insertCells(e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Insert Cells"))},onDeleteEntire:function(t){if(this.api){switch(this.api.asc_getCellInfo().asc_getFlags().asc_getSelectionType()){case Asc.c_oAscSelectionType.RangeRow:this.api.asc_deleteCells(Asc.c_oAscDeleteOptions.DeleteRows);break;case Asc.c_oAscSelectionType.RangeCol:this.api.asc_deleteCells(Asc.c_oAscDeleteOptions.DeleteColumns)}Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Delete Entire")}},onDeleteCells:function(t,e){this.api&&(this.api.asc_deleteCells(e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Delete Cells"))},onSortCells:function(t,e){if(this.api){var i=this.api.asc_sortCellsRangeExpand();if(i){var n={width:500,title:this.txtSorting,msg:this.txtExpandSort,buttons:[{caption:this.txtExpand,primary:!0,value:"expand"},{caption:this.txtSortSelected,primary:!0,value:"sort"},"cancel"],callback:_.bind(function(t){"expand"!=t&&"sort"!=t||this.api.asc_sortColFilter(e.value,"",void 0,e.value==Asc.c_oAscSortOptions.ByColorFill?this.documentHolder.ssMenu.cellColor:this.documentHolder.ssMenu.fontColor,"expand"==t),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Sort Cells")},this)};Common.UI.alert(n)}else this.api.asc_sortColFilter(e.value,"",void 0,e.value==Asc.c_oAscSortOptions.ByColorFill?this.documentHolder.ssMenu.cellColor:this.documentHolder.ssMenu.fontColor,null!==i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Sort Cells")}},onFilterCells:function(t,e){if(this.api){var i=new Asc.AutoFiltersOptions,n=new Asc.AutoFilterObj;if(e.value>0){n.asc_setFilter(new Asc.ColorFilter),n.asc_setType(Asc.c_oAscAutoFilterTypes.ColorFilter);var o=n.asc_getFilter();o.asc_setCellColor(1==e.value&&null),o.asc_setCColor(1==e.value?this.documentHolder.ssMenu.cellColor:this.documentHolder.ssMenu.fontColor)}else{n.asc_setFilter(new Asc.CustomFilters),n.asc_setType(Asc.c_oAscAutoFilterTypes.CustomFilters);var s=n.asc_getFilter();s.asc_setCustomFilters([new Asc.CustomFilter]),s.asc_setAnd(!0);s.asc_getCustomFilters()[0].asc_setOperator(Asc.c_oAscCustomAutoFilter.equals)}i.asc_setFilterObj(n),this.api.asc_applyAutoFilterByType(i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Filter Cells")}},onReapply:function(){this.api.asc_reapplyAutoFilter(this.documentHolder.ssMenu.formatTableName)},onClear:function(t,e){this.api&&(this.api.asc_emptyCells(e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Clear"))},onSelectTable:function(t,e){this.api&&this.documentHolder.ssMenu.formatTableName&&(this.api.asc_changeSelectionFormatTable(this.documentHolder.ssMenu.formatTableName,e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Select Table"))},onInsertTable:function(t,e){this.api&&this.documentHolder.ssMenu.formatTableName&&(this.api.asc_insertCellsInTable(this.documentHolder.ssMenu.formatTableName,e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Insert to Table"))},onDeleteTable:function(t,e){this.api&&this.documentHolder.ssMenu.formatTableName&&(this.api.asc_deleteCellsInTable(this.documentHolder.ssMenu.formatTableName,e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Delete from Table"))},onInsFunction:function(t){var e=this.getApplication().getController("FormulaDialog");e&&this.api&&e.showDialog()},onInsHyperlink:function(t){var e,i,n=this;if(n.api){for(var o=n.api.asc_getWorksheetsCount(),s=-1,a=[];++s-1&&e.value<6?(this.api.asc_setSelectedDrawingObjectAlign(e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Objects Align")):6==e.value?(this.api.asc_DistributeSelectedDrawingObjectHor(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Distribute")):7==e.value&&(this.api.asc_DistributeSelectedDrawingObjectVer(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Distribute")))},onParagraphVAlign:function(t,e){if(this.api){var i=new Asc.asc_CImgProperty;i.asc_putVerticalTextAlign(e.value),this.api.asc_setGraphicObjectProps(i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Paragraph Vertical Align")}},onParagraphDirection:function(t,e){if(this.api){var i=new Asc.asc_CImgProperty;i.asc_putVert(e.options.direction),this.api.asc_setGraphicObjectProps(i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Text Direction")}},onSelectNoneBullet:function(t,e){this.api&&-1==e.options.value&&(this.api.asc_setListType(e.options.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","List Type"))},onSelectBullets:function(t,e,i,n){var o={};if(_.isFunction(i.toJSON)){if(!i.get("selected"))return;o=i.toJSON()}else o=i;this.api&&this.api.asc_setListType(o.type,o.subtype),"click"!==n.type&&this.documentHolder.textInShapeMenu.hide(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","List Type")},onRemoveHyperlinkShape:function(t){this.api&&(this.api.asc_removeHyperlink(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Remove Hyperlink"))},onTextAdvanced:function(t){var e=this;new SSE.Views.ParagraphSettingsAdvanced({paragraphProps:t.textInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.paragraphProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced paragraph settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onShapeAdvanced:function(t){var e=this;new SSE.Views.ShapeSettingsAdvanced({shapeProps:t.shapeInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.shapeProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced shape settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onImgAdvanced:function(t){var e=this;new SSE.Views.ImageSettingsAdvanced({imageProps:t.imageInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.imageProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced image settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onChartEdit:function(t){var e,i=this;i.api&&(e=i.api.asc_getChartObject())&&new SSE.Views.ChartSettingsDlg({chartSettings:e,imageSettings:t.chartInfo,isChart:!0,api:i.api,handler:function(t,e){"ok"==t&&i.api&&(i.api.asc_editChartDrawingObject(e.chartSettings),e.imageSettings&&i.api.asc_setGraphicObjectProps(e.imageSettings)),Common.NotificationCenter.trigger("edit:complete",i)}}).show()},onApiCoAuthoringDisconnect:function(){this.permissions.isEdit=!1},hideCoAuthTips:function(){this.tooltips.coauth.ref&&($(this.tooltips.coauth.ref).remove(),this.tooltips.coauth.ref=void 0,this.tooltips.coauth.x_point=void 0,this.tooltips.coauth.y_point=void 0)},hideHyperlinkTip:function(){!this.tooltips.hyperlink.isHidden&&this.tooltips.hyperlink.ref&&(this.tooltips.hyperlink.ref.hide(),this.tooltips.hyperlink.ref=void 0,this.tooltips.hyperlink.text="",this.tooltips.hyperlink.isHidden=!0)},onApiMouseMove:function(t){if(!this._isFullscreenMenu&&t.length){for(var e,i,n,o,s,a,l=t.length;l>0;l--)switch(t[l-1].asc_getType()){case Asc.c_oAscMouseMoveType.Hyperlink:e=l;break;case Asc.c_oAscMouseMoveType.Comment:i=l;break;case Asc.c_oAscMouseMoveType.LockedObject:n=l;break;case Asc.c_oAscMouseMoveType.ResizeColumn:o=l;break;case Asc.c_oAscMouseMoveType.ResizeRow:s=l;break;case Asc.c_oAscMouseMoveType.Filter:a=l}var r=this,c=[0,0],h=r.tooltips.coauth,d=r.tooltips.comment,p=r.tooltips.hyperlink,m=r.tooltips.row_column,u=r.tooltips.filter,g=[r.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),r.documentHolder.cmpEl.offset().top-$(window).scrollTop()];if(e||r.hideHyperlinkTip(),void 0===o&&void 0===s&&!m.isHidden&&m.ref&&(m.ref.hide(),m.ref=void 0,m.text="",m.isHidden=!0),(r.permissions.isEdit||r.permissions.canViewComments)&&(!i||this.popupmenu)&&(d.moveCommentId=void 0,void 0!=d.viewCommentId)){d={};var b=this.getApplication().getController("Common.Controllers.Comments");b&&(this.permissions.canCoAuthoring&&this.permissions.canViewComments?setTimeout(function(){b.onApiHideComment(!0)},200):b.onApiHideComment(!0))}r.permissions.isEdit&&(n||r.hideCoAuthTips()),(void 0===a||r.dlgFilter&&r.dlgFilter.isVisible()||r.currentMenu&&r.currentMenu.isVisible())&&!u.isHidden&&u.ref&&(u.ref.hide(),u.ref=void 0,u.text="",u.isHidden=!0);if(e){p.parentEl||(p.parentEl=$('
'),r.documentHolder.cmpEl.append(p.parentEl));var f=t[e-1],C=f.asc_getHyperlink();if(C.asc_getType()==Asc.c_oAscHyperlinkType.WebLink){var v=C.asc_getTooltip();v=v?Common.Utils.String.htmlEncode(v)+"
"+r.textCtrlClick+"":C.asc_getHyperlinkUrl()+"
"+r.textCtrlClick+""}else v=C.asc_getTooltip()||C.asc_getLocation(),v+="
"+r.textCtrlClick+"";if(p.ref&&p.ref.isVisible()&&p.text!=v&&(p.ref.hide(),p.ref=void 0,p.text="",p.isHidden=!0),!p.ref||!p.ref.isVisible()){p.text=v,p.ref=new Common.UI.Tooltip({owner:p.parentEl,html:!0,title:v}),p.ref.show([-1e4,-1e4]),p.isHidden=!1,c=[f.asc_getX(),f.asc_getY()],c[0]+=g[0]+6,c[1]+=g[1]-20,c[1]-=p.ref.getBSTip().$tip.height();var _=p.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_),p.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}if(void 0!==o||void 0!==s){m.parentEl||(m.parentEl=$('
'),r.documentHolder.cmpEl.append(m.parentEl));var f=t[void 0!==o?o-1:s-1],y=Common.Utils.String.format(void 0!==o?this.textChangeColumnWidth:this.textChangeRowHeight,f.asc_getSizeCCOrPt().toFixed(2),f.asc_getSizePx().toFixed());if(m.ref&&m.ref.isVisible()&&m.text!=y&&(m.text=y,m.ref.setTitle(y),m.ref.updateTitle()),!m.ref||!m.ref.isVisible()){m.text=y,m.ref=new Common.UI.Tooltip({owner:m.parentEl,html:!0,title:y}),m.ref.show([-1e4,-1e4]),m.isHidden=!1,c=[f.asc_getX(),f.asc_getY()],c[0]+=g[0]+6,c[1]+=g[1]-20-m.ttHeight;var _=m.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_-20),m.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}if((r.permissions.isEdit||r.permissions.canViewComments)&&i&&!this.popupmenu&&(f=t[i-1],!d.editCommentId&&d.moveCommentId!=f.asc_getCommentIndexes()[0])){d.moveCommentId=f.asc_getCommentIndexes()[0],d.moveCommentTimer&&clearTimeout(d.moveCommentTimer);var x=f.asc_getCommentIndexes(),w=f.asc_getX(),S=f.asc_getY(),A=f.asc_getReverseX();d.moveCommentTimer=setTimeout(function(){if(d.moveCommentId&&!d.editCommentId){d.viewCommentId=d.moveCommentId;var t=r.getApplication().getController("Common.Controllers.Comments");t&&(t.isSelectedComment||t.onApiShowComment(x,w,S,A,!1,!0))}},400)}if(r.permissions.isEdit&&n&&(f=t[n-1],h.XY||r.onDocumentResize(),h.x_point!=f.asc_getX()||h.y_point!=f.asc_getY())){r.hideCoAuthTips(),h.x_point=f.asc_getX(),h.y_point=f.asc_getY();var k=$(document.createElement("div")),T=f.asc_getLockedObjectType()==Asc.c_oAscMouseMoveLockedObjectType.Sheet||f.asc_getLockedObjectType()==Asc.c_oAscMouseMoveLockedObjectType.TableProperties;h.ref=k,k.addClass("username-tip"),k.css({height:h.ttHeight+"px",position:"absolute",zIndex:"900",visibility:"visible"}),$(document.body).append(k),c=[T?h.x_point+h.rightMenuWidth:h.bodyWidth-(h.x_point+h.XY[0]),h.y_point+h.XY[1]],c[1]>=h.XY[1]&&c[1]+h.ttHeight
'),r.documentHolder.cmpEl.append(u.parentEl));var f=t[a-1],y=r.makeFilterTip(f.asc_getFilter());if(u.ref&&u.ref.isVisible()&&u.text!=y&&(u.text=y,u.ref.setTitle(y),u.ref.updateTitle()),!u.ref||!u.ref.isVisible()){u.text=y,u.ref=new Common.UI.Tooltip({owner:u.parentEl,html:!0,title:y,cls:"auto-tooltip"}),u.ref.show([-1e4,-1e4]),u.isHidden=!1,c=[f.asc_getX()+g[0]-10,f.asc_getY()+g[1]+20];u.ref.getBSTip().$tip.width();c[1]+u.ttHeight>r.tooltips.coauth.bodyHeight&&(c[1]=r.tooltips.coauth.bodyHeight-u.ttHeight-5,c[0]+=20);var _=u.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_-20),u.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}}},onApiHideComment:function(){this.tooltips.comment.viewCommentId=this.tooltips.comment.editCommentId=this.tooltips.comment.moveCommentId=void 0},onApiHyperlinkClick:function(t){if(!t)return void Common.UI.alert({msg:this.errorInvalidLink,title:this.notcriticalErrorTitle,iconCls:"warn",buttons:["ok"],callback:_.bind(function(t){Common.NotificationCenter.trigger("edit:complete",this.documentHolder)},this)});if(this.api.asc_getUrlType(t)>0){var e=window.open(t,"_blank");e&&e.focus()}},onApiAutofilter:function(t){var e=this;if(!e.tooltips.filter.isHidden&&e.tooltips.filter.ref&&(e.tooltips.filter.ref.hide(),e.tooltips.filter.ref=void 0,e.tooltips.filter.text="",e.tooltips.filter.isHidden=!0),e.permissions.isEdit&&!e.dlgFilter){e.dlgFilter=new SSE.Views.AutoFilterDialog({api:this.api}).on({close:function(){e.api&&e.api.asc_enableKeyEvents(!0),e.dlgFilter=void 0}}),e.api&&e.api.asc_enableKeyEvents(!1),Common.UI.Menu.Manager.hideAll(),e.dlgFilter.setSettings(t);var i=e.documentHolder.cmpEl.offset(),n=t.asc_getCellCoord(),o=n.asc_getX()+n.asc_getWidth()+i.left,s=n.asc_getY()+n.asc_getHeight()+i.top,a=Common.Utils.innerWidth(),l=Common.Utils.innerHeight();o+e.dlgFilter.options.width>a&&(o=a-e.dlgFilter.options.width-5),s+e.dlgFilter.options.height>l&&(s=l-e.dlgFilter.options.height-5),e.dlgFilter.show(o,s)}},makeFilterTip:function(t){var e=t.asc_getFilterObj(),i=e.asc_getType(),n=(t.asc_getIsTextFilter(),t.asc_getColorsFill(),t.asc_getColorsFont(),"");if(i===Asc.c_oAscAutoFilterTypes.CustomFilters){var o=e.asc_getFilter(),s=o.asc_getCustomFilters();n=this.getFilterName(Asc.c_oAscAutoFilterTypes.CustomFilters,s[0].asc_getOperator())+' "'+s[0].asc_getVal()+'"',s.length>1&&(n=n+" "+(o.asc_getAnd()?this.txtAnd:this.txtOr),n=n+" "+this.getFilterName(Asc.c_oAscAutoFilterTypes.CustomFilters,s[1].asc_getOperator())+' "'+s[1].asc_getVal()+'"')}else if(i===Asc.c_oAscAutoFilterTypes.ColorFilter){var a=e.asc_getFilter();null===a.asc_getCellColor()?n=this.txtEqualsToCellColor:!1===a.asc_getCellColor()&&(n=this.txtEqualsToFontColor)}else if(i===Asc.c_oAscAutoFilterTypes.DynamicFilter)n=this.getFilterName(Asc.c_oAscAutoFilterTypes.DynamicFilter,e.asc_getFilter().asc_getType());else if(i===Asc.c_oAscAutoFilterTypes.Top10){var l=e.asc_getFilter(),r=l.asc_getPercent();n=this.getFilterName(Asc.c_oAscAutoFilterTypes.Top10,l.asc_getTop()),n+=" "+l.asc_getVal()+" "+(r||null===r?this.txtPercent:this.txtItems)}else if(i===Asc.c_oAscAutoFilterTypes.Filters){var c=0,h=0,d=void 0,p=t.asc_getValues();p.forEach(function(t){t.asc_getVisible()&&(h++,c<100&&t.asc_getText()&&(n+=t.asc_getText()+"; ",c=n.length)),t.asc_getText()||(d=t.asc_getVisible())}),h==p.length?n=this.txtAll:1==h&&d?n=this.txtEquals+' "'+this.txtBlanks+'"':h==p.length-1&&0==d?n=this.txtNotEquals+' "'+this.txtBlanks+'"':(d&&(n+=this.txtBlanks+"; "),n=this.txtEquals+' "'+n.substring(0,n.length-2)+'"')}else i===Asc.c_oAscAutoFilterTypes.None&&(n=this.txtAll);return n.length>100&&(n=n.substring(0,100)+"..."),n=""+(t.asc_getColumnName()||"("+this.txtColumn+" "+t.asc_getSheetColumnName()+")")+":
"+n},getFilterName:function(t,e){var i="";if(t==Asc.c_oAscAutoFilterTypes.CustomFilters)switch(e){case Asc.c_oAscCustomAutoFilter.equals:i=this.txtEquals;break;case Asc.c_oAscCustomAutoFilter.isGreaterThan:i=this.txtGreater;break;case Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo:i=this.txtGreaterEquals;break;case Asc.c_oAscCustomAutoFilter.isLessThan:i=this.txtLess;break;case Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo:i=this.txtLessEquals;break;case Asc.c_oAscCustomAutoFilter.doesNotEqual:i=this.txtNotEquals;break;case Asc.c_oAscCustomAutoFilter.beginsWith:i=this.txtBegins;break;case Asc.c_oAscCustomAutoFilter.doesNotBeginWith:i=this.txtNotBegins;break;case Asc.c_oAscCustomAutoFilter.endsWith:i=this.txtEnds;break;case Asc.c_oAscCustomAutoFilter.doesNotEndWith:i=this.txtNotEnds;break;case Asc.c_oAscCustomAutoFilter.contains:i=this.txtContains;break;case Asc.c_oAscCustomAutoFilter.doesNotContain:i=this.txtNotContains}else if(t==Asc.c_oAscAutoFilterTypes.DynamicFilter)switch(e){case Asc.c_oAscDynamicAutoFilter.aboveAverage:i=this.txtAboveAve;break;case Asc.c_oAscDynamicAutoFilter.belowAverage:i=this.txtBelowAve}else t==Asc.c_oAscAutoFilterTypes.Top10&&(i=e||null===e?this.txtFilterTop:this.txtFilterBottom);return i},onUndo:function(){this.api&&(this.api.asc_Undo(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder))},onApiContextMenu:function(t){var e=this;_.delay(function(){e.showObjectMenu.call(e,t)},10)},onAfterRender:function(t){},onDocumentResize:function(t){var e=this;e.documentHolder&&(e.tooltips.coauth.XY=[e.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),e.documentHolder.cmpEl.offset().top-$(window).scrollTop()],e.tooltips.coauth.apiHeight=e.documentHolder.cmpEl.height(),e.tooltips.coauth.rightMenuWidth=$("#right-menu").width(),e.tooltips.coauth.bodyWidth=$(window).width(),e.tooltips.coauth.bodyHeight=$(window).height())},onDocumentWheel:function(t){if(this.api&&!this.isEditCell){var e=_.isUndefined(t.originalEvent)?t.wheelDelta:t.originalEvent.wheelDelta;if(_.isUndefined(e)&&(e=t.deltaY),(t.ctrlKey||t.metaKey)&&!t.altKey){var i=this.api.asc_getZoom();e<0?(i=Math.ceil(10*i)/10,(i-=.1)<.5||this.api.asc_setZoom(i)):e>0&&(i=Math.floor(10*i)/10,(i+=.1)>0&&!(i>2)&&this.api.asc_setZoom(i)),t.preventDefault(),t.stopPropagation()}}},onDocumentKeyDown:function(t){if(this.api){var e=t.keyCode;if(!t.ctrlKey&&!t.metaKey||t.shiftKey||t.altKey){if(e==Common.UI.Keys.F10&&t.shiftKey)return this.showObjectMenu(t),t.preventDefault(),t.stopPropagation(),!1}else if(e===Common.UI.Keys.NUM_PLUS||e===Common.UI.Keys.EQUALITY||Common.Utils.isGecko&&e===Common.UI.Keys.EQUALITY_FF||Common.Utils.isOpera&&43==e){if(!this.api.isCellEdited){var i=Math.floor(10*this.api.asc_getZoom())/10;return i+=.1,i>0&&!(i>2)&&this.api.asc_setZoom(i),t.preventDefault(),t.stopPropagation(),!1}}else if((e===Common.UI.Keys.NUM_MINUS||e===Common.UI.Keys.MINUS||Common.Utils.isGecko&&e===Common.UI.Keys.MINUS_FF||Common.Utils.isOpera&&45==e)&&!this.api.isCellEdited)return i=Math.ceil(10*this.api.asc_getZoom())/10,i-=.1,i<.5||this.api.asc_setZoom(i),t.preventDefault(),t.stopPropagation(),!1}},onDocumentRightDown:function(t){0==t.button&&(this.mouse.isLeftButtonDown=!0)},onDocumentRightUp:function(t){0==t.button&&(this.mouse.isLeftButtonDown=!1)},onProcessMouse:function(t){"mouseup"==t.type&&(this.mouse.isLeftButtonDown=!1)},onDocumentMouseMove:function(t){"canvas"!==t.target.localName&&this.hideHyperlinkTip()},showObjectMenu:function(t){!this.api||this.mouse.isLeftButtonDown||this.rangeSelectionMode||(this.permissions.isEdit&&!this._isDisabled?this.fillMenuProps(this.api.asc_getCellInfo(),!0,t):this.fillViewMenuProps(this.api.asc_getCellInfo(),!0,t))},onSelectionChanged:function(t){!this.mouse.isLeftButtonDown&&!this.rangeSelectionMode&&this.currentMenu&&this.currentMenu.isVisible()&&(this.permissions.isEdit&&!this._isDisabled?this.fillMenuProps(t,!0):this.fillViewMenuProps(t,!0))},fillMenuProps:function(t,e,i){var n,o,s,a,l,r,c,h,d,p,m=this.documentHolder,u=t.asc_getFlags().asc_getSelectionType(),g=t.asc_getLocked(),b=!0===t.asc_getLockedTable(),f=!1,C=this.getApplication().getController("Common.Controllers.Comments"),v=this.permissions.isEditMailMerge||this.permissions.isEditDiagram;switch(u){case Asc.c_oAscSelectionType.RangeCells:n=!0;break;case Asc.c_oAscSelectionType.RangeRow:o=!0;break;case Asc.c_oAscSelectionType.RangeCol:s=!0;break;case Asc.c_oAscSelectionType.RangeMax:a=!0;break;case Asc.c_oAscSelectionType.RangeImage:r=!v;break;case Asc.c_oAscSelectionType.RangeShape:h=!v;break;case Asc.c_oAscSelectionType.RangeChart:l=!v;break;case Asc.c_oAscSelectionType.RangeChartText:d=!v;break;case Asc.c_oAscSelectionType.RangeShapeText:c=!v}if(this.api.asc_getHeaderFooterMode()){if(!m.copyPasteMenu||!e&&!m.copyPasteMenu.isVisible())return;e&&this.showPopupMenu(m.copyPasteMenu,{},i)}else if(r||h||l){if(!m.imgMenu||!e&&!m.imgMenu.isVisible())return;r=h=l=!1,m.mnuImgAdvanced.imageInfo=void 0;for(var y,x=!1,w=this.api.asc_getGraphicObjectProps(),S=0;S-1&&e.value<6?(this.api.asc_setSelectedDrawingObjectAlign(e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Objects Align")):6==e.value?(this.api.asc_DistributeSelectedDrawingObjectHor(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Distribute")):7==e.value&&(this.api.asc_DistributeSelectedDrawingObjectVer(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Distribute")))},onParagraphVAlign:function(t,e){if(this.api){var i=new Asc.asc_CImgProperty;i.asc_putVerticalTextAlign(e.value),this.api.asc_setGraphicObjectProps(i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Paragraph Vertical Align")}},onParagraphDirection:function(t,e){if(this.api){var i=new Asc.asc_CImgProperty;i.asc_putVert(e.options.direction),this.api.asc_setGraphicObjectProps(i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Text Direction")}},onSelectNoneBullet:function(t,e){this.api&&-1==e.options.value&&(this.api.asc_setListType(e.options.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","List Type"))},onSelectBullets:function(t,e,i,n){var o={};if(_.isFunction(i.toJSON)){if(!i.get("selected"))return;o=i.toJSON()}else o=i;this.api&&this.api.asc_setListType(o.type,o.subtype),"click"!==n.type&&this.documentHolder.textInShapeMenu.hide(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","List Type")},onRemoveHyperlinkShape:function(t){this.api&&(this.api.asc_removeHyperlink(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Remove Hyperlink"))},onTextAdvanced:function(t){var e=this;new SSE.Views.ParagraphSettingsAdvanced({paragraphProps:t.textInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.paragraphProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced paragraph settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onShapeAdvanced:function(t){var e=this;new SSE.Views.ShapeSettingsAdvanced({shapeProps:t.shapeInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.shapeProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced shape settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onImgAdvanced:function(t){var e=this;new SSE.Views.ImageSettingsAdvanced({imageProps:t.imageInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.imageProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced image settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onChartEdit:function(t){var e,i=this;i.api&&(e=i.api.asc_getChartObject())&&new SSE.Views.ChartSettingsDlg({chartSettings:e,imageSettings:t.chartInfo,isChart:!0,api:i.api,handler:function(t,e){"ok"==t&&i.api&&(i.api.asc_editChartDrawingObject(e.chartSettings),e.imageSettings&&i.api.asc_setGraphicObjectProps(e.imageSettings)),Common.NotificationCenter.trigger("edit:complete",i)}}).show()},onApiCoAuthoringDisconnect:function(){this.permissions.isEdit=!1},hideCoAuthTips:function(){this.tooltips.coauth.ref&&($(this.tooltips.coauth.ref).remove(),this.tooltips.coauth.ref=void 0,this.tooltips.coauth.x_point=void 0,this.tooltips.coauth.y_point=void 0)},hideHyperlinkTip:function(){!this.tooltips.hyperlink.isHidden&&this.tooltips.hyperlink.ref&&(this.tooltips.hyperlink.ref.hide(),this.tooltips.hyperlink.ref=void 0,this.tooltips.hyperlink.text="",this.tooltips.hyperlink.isHidden=!0)},onApiMouseMove:function(t){if(!this._isFullscreenMenu&&t.length){for(var e,i,n,o,s,a,l=t.length;l>0;l--)switch(t[l-1].asc_getType()){case Asc.c_oAscMouseMoveType.Hyperlink:e=l;break;case Asc.c_oAscMouseMoveType.Comment:i=l;break;case Asc.c_oAscMouseMoveType.LockedObject:n=l;break;case Asc.c_oAscMouseMoveType.ResizeColumn:o=l;break;case Asc.c_oAscMouseMoveType.ResizeRow:s=l;break;case Asc.c_oAscMouseMoveType.Filter:a=l}var r=this,c=[0,0],h=r.tooltips.coauth,d=r.tooltips.comment,p=r.tooltips.hyperlink,m=r.tooltips.row_column,u=r.tooltips.filter,g=[r.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),r.documentHolder.cmpEl.offset().top-$(window).scrollTop()];if(e||r.hideHyperlinkTip(),void 0===o&&void 0===s&&!m.isHidden&&m.ref&&(m.ref.hide(),m.ref=void 0,m.text="",m.isHidden=!0),(r.permissions.isEdit||r.permissions.canViewComments)&&(!i||this.popupmenu)&&(d.moveCommentId=void 0,void 0!=d.viewCommentId)){d={};var b=this.getApplication().getController("Common.Controllers.Comments");b&&(this.permissions.canCoAuthoring&&this.permissions.canViewComments?setTimeout(function(){b.onApiHideComment(!0)},200):b.onApiHideComment(!0))}r.permissions.isEdit&&(n||r.hideCoAuthTips()),(void 0===a||r.dlgFilter&&r.dlgFilter.isVisible()||r.currentMenu&&r.currentMenu.isVisible())&&!u.isHidden&&u.ref&&(u.ref.hide(),u.ref=void 0,u.text="",u.isHidden=!0);if(e){p.parentEl||(p.parentEl=$('
'),r.documentHolder.cmpEl.append(p.parentEl));var f=t[e-1],C=f.asc_getHyperlink();if(C.asc_getType()==Asc.c_oAscHyperlinkType.WebLink){var v=C.asc_getTooltip();v=v?Common.Utils.String.htmlEncode(v)+"
"+r.textCtrlClick+"":C.asc_getHyperlinkUrl()+"
"+r.textCtrlClick+""}else v=C.asc_getTooltip()||C.asc_getLocation(),v+="
"+r.textCtrlClick+"";if(p.ref&&p.ref.isVisible()&&p.text!=v&&(p.ref.hide(),p.ref=void 0,p.text="",p.isHidden=!0),!p.ref||!p.ref.isVisible()){p.text=v,p.ref=new Common.UI.Tooltip({owner:p.parentEl,html:!0,title:v}),p.ref.show([-1e4,-1e4]),p.isHidden=!1,c=[f.asc_getX(),f.asc_getY()],c[0]+=g[0]+6,c[1]+=g[1]-20,c[1]-=p.ref.getBSTip().$tip.height();var _=p.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_),p.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}if(void 0!==o||void 0!==s){m.parentEl||(m.parentEl=$('
'),r.documentHolder.cmpEl.append(m.parentEl));var f=t[void 0!==o?o-1:s-1],y=Common.Utils.String.format(void 0!==o?this.textChangeColumnWidth:this.textChangeRowHeight,f.asc_getSizeCCOrPt().toFixed(2),f.asc_getSizePx().toFixed());if(m.ref&&m.ref.isVisible()&&m.text!=y&&(m.text=y,m.ref.setTitle(y),m.ref.updateTitle()),!m.ref||!m.ref.isVisible()){m.text=y,m.ref=new Common.UI.Tooltip({owner:m.parentEl,html:!0,title:y}),m.ref.show([-1e4,-1e4]),m.isHidden=!1,c=[f.asc_getX(),f.asc_getY()],c[0]+=g[0]+6,c[1]+=g[1]-20-m.ttHeight;var _=m.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_-20),m.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}if((r.permissions.isEdit||r.permissions.canViewComments)&&i&&!this.popupmenu&&(f=t[i-1],!d.editCommentId&&d.moveCommentId!=f.asc_getCommentIndexes()[0])){d.moveCommentId=f.asc_getCommentIndexes()[0],d.moveCommentTimer&&clearTimeout(d.moveCommentTimer);var x=f.asc_getCommentIndexes(),w=f.asc_getX(),S=f.asc_getY(),A=f.asc_getReverseX();d.moveCommentTimer=setTimeout(function(){if(d.moveCommentId&&!d.editCommentId){d.viewCommentId=d.moveCommentId;var t=r.getApplication().getController("Common.Controllers.Comments");t&&(t.isSelectedComment||t.onApiShowComment(x,w,S,A,!1,!0))}},400)}if(r.permissions.isEdit&&n&&(f=t[n-1],h.XY||r.onDocumentResize(),h.x_point!=f.asc_getX()||h.y_point!=f.asc_getY())){r.hideCoAuthTips(),h.x_point=f.asc_getX(),h.y_point=f.asc_getY();var k=$(document.createElement("div")),T=f.asc_getLockedObjectType()==Asc.c_oAscMouseMoveLockedObjectType.Sheet||f.asc_getLockedObjectType()==Asc.c_oAscMouseMoveLockedObjectType.TableProperties;h.ref=k,k.addClass("username-tip"),k.css({height:h.ttHeight+"px",position:"absolute",zIndex:"900",visibility:"visible"}),$(document.body).append(k),c=[T?h.x_point+h.rightMenuWidth:h.bodyWidth-(h.x_point+h.XY[0]),h.y_point+h.XY[1]],c[1]>=h.XY[1]&&c[1]+h.ttHeight
'),r.documentHolder.cmpEl.append(u.parentEl));var f=t[a-1],y=r.makeFilterTip(f.asc_getFilter());if(u.ref&&u.ref.isVisible()&&u.text!=y&&(u.text=y,u.ref.setTitle(y),u.ref.updateTitle()),!u.ref||!u.ref.isVisible()){u.text=y,u.ref=new Common.UI.Tooltip({owner:u.parentEl,html:!0,title:y,cls:"auto-tooltip"}),u.ref.show([-1e4,-1e4]),u.isHidden=!1,c=[f.asc_getX()+g[0]-10,f.asc_getY()+g[1]+20];u.ref.getBSTip().$tip.width();c[1]+u.ttHeight>r.tooltips.coauth.bodyHeight&&(c[1]=r.tooltips.coauth.bodyHeight-u.ttHeight-5,c[0]+=20);var _=u.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_-20),u.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}}},onApiHideComment:function(){this.tooltips.comment.viewCommentId=this.tooltips.comment.editCommentId=this.tooltips.comment.moveCommentId=void 0},onApiHyperlinkClick:function(t){if(!t)return void Common.UI.alert({msg:this.errorInvalidLink,title:this.notcriticalErrorTitle,iconCls:"warn",buttons:["ok"],callback:_.bind(function(t){Common.NotificationCenter.trigger("edit:complete",this.documentHolder)},this)});if(this.api.asc_getUrlType(t)>0){return void window.parent.APP.openURL(t)}},onApiAutofilter:function(t){var e=this;if(!e.tooltips.filter.isHidden&&e.tooltips.filter.ref&&(e.tooltips.filter.ref.hide(),e.tooltips.filter.ref=void 0,e.tooltips.filter.text="",e.tooltips.filter.isHidden=!0),e.permissions.isEdit&&!e.dlgFilter){e.dlgFilter=new SSE.Views.AutoFilterDialog({api:this.api}).on({close:function(){e.api&&e.api.asc_enableKeyEvents(!0),e.dlgFilter=void 0}}),e.api&&e.api.asc_enableKeyEvents(!1),Common.UI.Menu.Manager.hideAll(),e.dlgFilter.setSettings(t);var i=e.documentHolder.cmpEl.offset(),n=t.asc_getCellCoord(),o=n.asc_getX()+n.asc_getWidth()+i.left,s=n.asc_getY()+n.asc_getHeight()+i.top,a=Common.Utils.innerWidth(),l=Common.Utils.innerHeight();o+e.dlgFilter.options.width>a&&(o=a-e.dlgFilter.options.width-5),s+e.dlgFilter.options.height>l&&(s=l-e.dlgFilter.options.height-5),e.dlgFilter.show(o,s)}},makeFilterTip:function(t){var e=t.asc_getFilterObj(),i=e.asc_getType(),n=(t.asc_getIsTextFilter(),t.asc_getColorsFill(),t.asc_getColorsFont(),"");if(i===Asc.c_oAscAutoFilterTypes.CustomFilters){var o=e.asc_getFilter(),s=o.asc_getCustomFilters();n=this.getFilterName(Asc.c_oAscAutoFilterTypes.CustomFilters,s[0].asc_getOperator())+' "'+s[0].asc_getVal()+'"',s.length>1&&(n=n+" "+(o.asc_getAnd()?this.txtAnd:this.txtOr),n=n+" "+this.getFilterName(Asc.c_oAscAutoFilterTypes.CustomFilters,s[1].asc_getOperator())+' "'+s[1].asc_getVal()+'"')}else if(i===Asc.c_oAscAutoFilterTypes.ColorFilter){var a=e.asc_getFilter();null===a.asc_getCellColor()?n=this.txtEqualsToCellColor:!1===a.asc_getCellColor()&&(n=this.txtEqualsToFontColor)}else if(i===Asc.c_oAscAutoFilterTypes.DynamicFilter)n=this.getFilterName(Asc.c_oAscAutoFilterTypes.DynamicFilter,e.asc_getFilter().asc_getType());else if(i===Asc.c_oAscAutoFilterTypes.Top10){var l=e.asc_getFilter(),r=l.asc_getPercent();n=this.getFilterName(Asc.c_oAscAutoFilterTypes.Top10,l.asc_getTop()),n+=" "+l.asc_getVal()+" "+(r||null===r?this.txtPercent:this.txtItems)}else if(i===Asc.c_oAscAutoFilterTypes.Filters){var c=0,h=0,d=void 0,p=t.asc_getValues();p.forEach(function(t){t.asc_getVisible()&&(h++,c<100&&t.asc_getText()&&(n+=t.asc_getText()+"; ",c=n.length)),t.asc_getText()||(d=t.asc_getVisible())}),h==p.length?n=this.txtAll:1==h&&d?n=this.txtEquals+' "'+this.txtBlanks+'"':h==p.length-1&&0==d?n=this.txtNotEquals+' "'+this.txtBlanks+'"':(d&&(n+=this.txtBlanks+"; "),n=this.txtEquals+' "'+n.substring(0,n.length-2)+'"')}else i===Asc.c_oAscAutoFilterTypes.None&&(n=this.txtAll);return n.length>100&&(n=n.substring(0,100)+"..."),n=""+(t.asc_getColumnName()||"("+this.txtColumn+" "+t.asc_getSheetColumnName()+")")+":
"+n},getFilterName:function(t,e){var i="";if(t==Asc.c_oAscAutoFilterTypes.CustomFilters)switch(e){case Asc.c_oAscCustomAutoFilter.equals:i=this.txtEquals;break;case Asc.c_oAscCustomAutoFilter.isGreaterThan:i=this.txtGreater;break;case Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo:i=this.txtGreaterEquals;break;case Asc.c_oAscCustomAutoFilter.isLessThan:i=this.txtLess;break;case Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo:i=this.txtLessEquals;break;case Asc.c_oAscCustomAutoFilter.doesNotEqual:i=this.txtNotEquals;break;case Asc.c_oAscCustomAutoFilter.beginsWith:i=this.txtBegins;break;case Asc.c_oAscCustomAutoFilter.doesNotBeginWith:i=this.txtNotBegins;break;case Asc.c_oAscCustomAutoFilter.endsWith:i=this.txtEnds;break;case Asc.c_oAscCustomAutoFilter.doesNotEndWith:i=this.txtNotEnds;break;case Asc.c_oAscCustomAutoFilter.contains:i=this.txtContains;break;case Asc.c_oAscCustomAutoFilter.doesNotContain:i=this.txtNotContains}else if(t==Asc.c_oAscAutoFilterTypes.DynamicFilter)switch(e){case Asc.c_oAscDynamicAutoFilter.aboveAverage:i=this.txtAboveAve;break;case Asc.c_oAscDynamicAutoFilter.belowAverage:i=this.txtBelowAve}else t==Asc.c_oAscAutoFilterTypes.Top10&&(i=e||null===e?this.txtFilterTop:this.txtFilterBottom);return i},onUndo:function(){this.api&&(this.api.asc_Undo(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder))},onApiContextMenu:function(t){var e=this;_.delay(function(){e.showObjectMenu.call(e,t)},10)},onAfterRender:function(t){},onDocumentResize:function(t){var e=this;e.documentHolder&&(e.tooltips.coauth.XY=[e.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),e.documentHolder.cmpEl.offset().top-$(window).scrollTop()],e.tooltips.coauth.apiHeight=e.documentHolder.cmpEl.height(),e.tooltips.coauth.rightMenuWidth=$("#right-menu").width(),e.tooltips.coauth.bodyWidth=$(window).width(),e.tooltips.coauth.bodyHeight=$(window).height())},onDocumentWheel:function(t){if(this.api&&!this.isEditCell){var e=_.isUndefined(t.originalEvent)?t.wheelDelta:t.originalEvent.wheelDelta;if(_.isUndefined(e)&&(e=t.deltaY),(t.ctrlKey||t.metaKey)&&!t.altKey){var i=this.api.asc_getZoom();e<0?(i=Math.ceil(10*i)/10,(i-=.1)<.5||this.api.asc_setZoom(i)):e>0&&(i=Math.floor(10*i)/10,(i+=.1)>0&&!(i>2)&&this.api.asc_setZoom(i)),t.preventDefault(),t.stopPropagation()}}},onDocumentKeyDown:function(t){if(this.api){var e=t.keyCode;if(!t.ctrlKey&&!t.metaKey||t.shiftKey||t.altKey){if(e==Common.UI.Keys.F10&&t.shiftKey)return this.showObjectMenu(t),t.preventDefault(),t.stopPropagation(),!1}else if(e===Common.UI.Keys.NUM_PLUS||e===Common.UI.Keys.EQUALITY||Common.Utils.isGecko&&e===Common.UI.Keys.EQUALITY_FF||Common.Utils.isOpera&&43==e){if(!this.api.isCellEdited){var i=Math.floor(10*this.api.asc_getZoom())/10;return i+=.1,i>0&&!(i>2)&&this.api.asc_setZoom(i),t.preventDefault(),t.stopPropagation(),!1}}else if((e===Common.UI.Keys.NUM_MINUS||e===Common.UI.Keys.MINUS||Common.Utils.isGecko&&e===Common.UI.Keys.MINUS_FF||Common.Utils.isOpera&&45==e)&&!this.api.isCellEdited)return i=Math.ceil(10*this.api.asc_getZoom())/10,i-=.1,i<.5||this.api.asc_setZoom(i),t.preventDefault(),t.stopPropagation(),!1}},onDocumentRightDown:function(t){0==t.button&&(this.mouse.isLeftButtonDown=!0)},onDocumentRightUp:function(t){0==t.button&&(this.mouse.isLeftButtonDown=!1)},onProcessMouse:function(t){"mouseup"==t.type&&(this.mouse.isLeftButtonDown=!1)},onDocumentMouseMove:function(t){"canvas"!==t.target.localName&&this.hideHyperlinkTip()},showObjectMenu:function(t){!this.api||this.mouse.isLeftButtonDown||this.rangeSelectionMode||(this.permissions.isEdit&&!this._isDisabled?this.fillMenuProps(this.api.asc_getCellInfo(),!0,t):this.fillViewMenuProps(this.api.asc_getCellInfo(),!0,t))},onSelectionChanged:function(t){!this.mouse.isLeftButtonDown&&!this.rangeSelectionMode&&this.currentMenu&&this.currentMenu.isVisible()&&(this.permissions.isEdit&&!this._isDisabled?this.fillMenuProps(t,!0):this.fillViewMenuProps(t,!0))},fillMenuProps:function(t,e,i){var n,o,s,a,l,r,c,h,d,p,m=this.documentHolder,u=t.asc_getFlags().asc_getSelectionType(),g=t.asc_getLocked(),b=!0===t.asc_getLockedTable(),f=!1,C=this.getApplication().getController("Common.Controllers.Comments"),v=this.permissions.isEditMailMerge||this.permissions.isEditDiagram;switch(u){case Asc.c_oAscSelectionType.RangeCells:n=!0;break;case Asc.c_oAscSelectionType.RangeRow:o=!0;break;case Asc.c_oAscSelectionType.RangeCol:s=!0;break;case Asc.c_oAscSelectionType.RangeMax:a=!0;break;case Asc.c_oAscSelectionType.RangeImage:r=!v;break;case Asc.c_oAscSelectionType.RangeShape:h=!v;break;case Asc.c_oAscSelectionType.RangeChart:l=!v;break;case Asc.c_oAscSelectionType.RangeChartText:d=!v;break;case Asc.c_oAscSelectionType.RangeShapeText:c=!v}if(this.api.asc_getHeaderFooterMode()){if(!m.copyPasteMenu||!e&&!m.copyPasteMenu.isVisible())return;e&&this.showPopupMenu(m.copyPasteMenu,{},i)}else if(r||h||l){if(!m.imgMenu||!e&&!m.imgMenu.isVisible())return;r=h=l=!1,m.mnuImgAdvanced.imageInfo=void 0;for(var y,x=!1,w=this.api.asc_getGraphicObjectProps(),S=0;S
',t.id)),o.cmpEl.append(a)),t.render(a),t.cmpEl.attr({tabindex:"-1"})),2!==i.button){var l=n.api.asc_getActiveCellCoord(),r={left:0,top:0};s[0]=l.asc_getX()+l.asc_getWidth()+r.left,s[1]=(l.asc_getY()<0?0:l.asc_getY())+l.asc_getHeight()+r.top}a.css({left:s[0],top:s[1]}),_.isFunction(t.options.initMenu)&&(t.options.initMenu(e),t.alignPosition()),_.delay(function(){t.cmpEl.focus()},10),t.show(),n.currentMenu=t}},onEntriesListMenu:function(t){if(t&&t.length>0){for(var e=this,i=e.documentHolder,n=i.entriesMenu,o=i.cmpEl.find(Common.Utils.String.format("#menu-container-{0}",n.id)),s=0;s
',n.id)),i.cmpEl.append(o)),n.render(o),n.cmpEl.attr({tabindex:"-1"}));var a=e.api.asc_getActiveCellCoord(),l={left:0,top:0},r=[a.asc_getX()+l.left,(a.asc_getY()<0?0:a.asc_getY())+a.asc_getHeight()+l.top];o.css({left:r[0],top:r[1]}),n.show(),n.alignPosition(),_.delay(function(){n.cmpEl.focus()},10)}else this.documentHolder.entriesMenu.hide(),Common.UI.warning({title:this.notcriticalErrorTitle,maxwidth:600,msg:this.txtNoChoices,callback:_.bind(function(t){Common.NotificationCenter.trigger("edit:complete",this.documentHolder)},this)})},onFormulaCompleteMenu:function(t){if(this.documentHolder.funcMenu)if(t){for(var e=this,i=e.documentHolder,n=i.funcMenu,o=i.cmpEl.find("#menu-formula-selection"),s=e.getApplication().getController("FormulaDialog").getDescription(Common.Utils.InternalSettings.get("sse-settings-func-locale")),a=0;an?1:0}),_.each(t,function(t,i){var o=t.asc_getType(),a=t.asc_getName(!0),l=e.api.asc_getFormulaNameByLocale(a),r=new Common.UI.MenuItem({iconCls:o==Asc.c_oAscPopUpSelectorType.Func?"mnu-popup-func":o==Asc.c_oAscPopUpSelectorType.Table?"mnu-popup-table":"mnu-popup-range",caption:a,hint:s&&s[l]?s[l].d:""}).on("click",function(t,i){setTimeout(function(){e.api.asc_insertFormula(t.caption,o,!1)},10)});n.addItem(r)}),n.rendered||(o.length<1&&(o=$(Common.Utils.String.format('')),i.cmpEl.append(o)),n.onAfterKeydownMenu=function(t){if(t.keyCode!=Common.UI.Keys.RETURN||!t.ctrlKey&&!t.altKey){var e;if(arguments.length>1&&arguments[1]instanceof KeyboardEvent&&(t=arguments[1]),o.hasClass("open"))if(t.keyCode==Common.UI.Keys.TAB||t.keyCode==Common.UI.Keys.RETURN&&!t.ctrlKey&&!t.altKey)e=o.find("a.focus").closest("li");else if(t.keyCode==Common.UI.Keys.UP||t.keyCode==Common.UI.Keys.DOWN){var i=n.cmpEl,s=i.offset().top,a=o.find("a.focus").closest("li"),l=a.offset().top;(ls+i.height())&&(n.scroller?n.scroller.scrollTop(i.scrollTop()+l-s,0):i.scrollTop(i.scrollTop()+l-s))}e&&(e.length>0&&e.click(),Common.UI.Menu.Manager.hideAll())}},n.on("hide:after",function(){for(var t=0;t'),this.documentHolder.cmpEl.append(e.parentEl));var i=this.getApplication().getController("FormulaDialog").getDescription(Common.Utils.InternalSettings.get("sse-settings-func-locale")),n=(i&&i[t]?this.api.asc_getFormulaLocaleName(t)+i[t].a:"").replace(/[,;]/g,this.api.asc_getFunctionArgumentSeparator());if(e.ref&&e.ref.isVisible()&&e.text!=n&&(e.ref.hide(),e.ref=void 0,e.text="",e.isHidden=!0),!n)return;e.ref&&e.ref.isVisible()||(e.text=n,e.ref=new Common.UI.Tooltip({owner:e.parentEl,html:!0,title:n,cls:"auto-tooltip"}),e.ref.show([-1e4,-1e4]),e.isHidden=!1);var o=[this.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),this.documentHolder.cmpEl.offset().top-$(window).scrollTop()],s=this.api.asc_getActiveCellCoord(),a=[s.asc_getX()+o[0]-3,s.asc_getY()+o[1]-e.ref.getBSTip().$tip.height()-5],l=e.ref.getBSTip().$tip.width();a[0]+l>this.tooltips.coauth.bodyWidth&&(a[0]=this.tooltips.coauth.bodyWidth-l),e.ref.getBSTip().$tip.css({top:a[1]+"px",left:a[0]+"px"})}else!e.isHidden&&e.ref&&(e.ref.hide(),e.ref=void 0,e.text="",e.isHidden=!0)},onInputMessage:function(t,e){var i=this.tooltips.input_msg;if(e){i.parentEl||(i.parentEl=$('
'),this.documentHolder.cmpEl.append(i.parentEl));var n=t?""+(t||"")+"
":"";n+=e||"",i.ref&&i.ref.isVisible()&&i.text!=n&&(i.ref.hide(),i.ref=void 0,i.text="",i.isHidden=!0),i.ref&&i.ref.isVisible()||(i.text=n,i.ref=new Common.UI.Tooltip({owner:i.parentEl,html:!0,title:n}),i.ref.show([-1e4,-1e4]),i.isHidden=!1);var o=[this.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),this.documentHolder.cmpEl.offset().top-$(window).scrollTop()],s=this.api.asc_getActiveCellCoord(),a=[s.asc_getX()+o[0]-3,s.asc_getY()+o[1]-i.ref.getBSTip().$tip.height()-5],l=i.ref.getBSTip().$tip.width();a[0]+l>this.tooltips.coauth.bodyWidth&&(a[0]=this.tooltips.coauth.bodyWidth-l),i.ref.getBSTip().$tip.css({top:a[1]+"px",left:a[0]+"px"})}else!i.isHidden&&i.ref&&(i.ref.hide(),i.ref=void 0,i.text="",i.isHidden=!0)},onShowSpecialPasteOptions:function(t){var e=this,i=e.documentHolder,n=t.asc_getCellCoord(),o=i.cmpEl.find("#special-paste-container"),s=t.asc_getOptions();if(s){if(o.length<1&&(e._arrSpecialPaste=[],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.paste]=[e.txtPaste,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.pasteOnlyFormula]=[e.txtPasteFormulas,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.formulaNumberFormat]=[e.txtPasteFormulaNumFormat,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.formulaAllFormatting]=[e.txtPasteKeepSourceFormat,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.formulaWithoutBorders]=[e.txtPasteBorders,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.formulaColumnWidth]=[e.txtPasteColWidths,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.mergeConditionalFormating]=[e.txtPasteMerge,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.transpose]=[e.txtPasteTranspose,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.pasteOnlyValues]=[e.txtPasteValues,1],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.valueNumberFormat]=[e.txtPasteValNumFormat,1],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.valueAllFormating]=[e.txtPasteValFormat,1],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.pasteOnlyFormating]=[e.txtPasteFormat,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.link]=[e.txtPasteLink,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.picture]=[e.txtPastePicture,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.linkedPicture]=[e.txtPasteLinkPicture,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.sourceformatting]=[e.txtPasteSourceFormat,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.destinationFormatting]=[e.txtPasteDestFormat,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.keepTextOnly]=[e.txtKeepTextOnly,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.useTextImport]=[e.txtUseTextImport,3],o=$('
'),i.cmpEl.append(o),e.btnSpecialPaste=new Common.UI.Button({cls:"btn-toolbar",iconCls:"btn-paste",menu:new Common.UI.Menu({items:[]})}),e.btnSpecialPaste.render($("#id-document-holder-btn-special-paste"))),s.length>0){for(var a=e.btnSpecialPaste.menu,l=0;l0&&(a.addItem(new Common.UI.MenuItem({caption:"--"})),h=!1),_.each(r[l],function(t,e){a.addItem(t),h=!0});a.items.length>0&&a.items[0].setChecked(!0,!0),e._state.lastSpecPasteChecked=a.items.length>0?a.items[0]:null,c&&(a.addItem(new Common.UI.MenuItem({caption:"--"})),a.addItem(c))}if(n[0].asc_getX()<0||n[0].asc_getY()<0)return void(o.is(":visible")&&o.hide());var d=n[0],p=n[1],m=e.tooltips.coauth.bodyWidth-e.tooltips.coauth.XY[0]-e.tooltips.coauth.rightMenuWidth-15,u=e.tooltips.coauth.apiHeight-15,g=[],b=[31,20],f=d.asc_getX()+d.asc_getWidth()+3+b[0],C=d.asc_getY()+d.asc_getHeight()+3+b[1];f>m?(g[0]=void 0!==p?p.asc_getX():m-b[0]-3,C>u&&(g[0]-=b[0]+3),g[0]<0&&(g[0]=m-3-b[0])):g[0]=f-b[0],g[1]=C>u?u-3-b[1]:C-b[1],o.css({left:g[0],top:g[1]}),o.show()}},onHideSpecialPasteOptions:function(){var t=this.documentHolder.cmpEl.find("#special-paste-container");t.is(":visible")&&t.hide()},onToggleAutoCorrectOptions:function(t){if(!t){var e=this.documentHolder.cmpEl.find("#autocorrect-paste-container");return void(e.is(":visible")&&e.hide())}var i=this,n=i.documentHolder,o=t.asc_getCellCoord(),e=n.cmpEl.find("#autocorrect-paste-container"),s=t.asc_getOptions();if(e.length<1&&(i._arrAutoCorrectPaste=[],i._arrAutoCorrectPaste[Asc.c_oAscAutoCorrectOptions.UndoTableAutoExpansion]=i.txtUndoExpansion,i._arrAutoCorrectPaste[Asc.c_oAscAutoCorrectOptions.RedoTableAutoExpansion]=i.txtRedoExpansion,e=$('
'),n.cmpEl.append(e),i.btnAutoCorrectPaste=new Common.UI.Button({cls:"btn-toolbar",iconCls:"btn-paste",menu:new Common.UI.Menu({items:[]})}),i.btnAutoCorrectPaste.render($("#id-document-holder-btn-autocorrect-paste"))),s.length>0){for(var a=i.btnAutoCorrectPaste.menu,l=0;lr||p>c||o.asc_getX()<0||o.asc_getY()<0?e.is(":visible")&&e.hide():(e.css({left:d-h[0],top:p-h[1]}),e.show())},onCellsRange:function(t){this.rangeSelectionMode=t!=Asc.c_oAscSelectionDialogType.None},onApiEditCell:function(t){this.isEditFormula=t==Asc.c_oAscCellEditorState.editFormula,this.isEditCell=t!=Asc.c_oAscCellEditorState.editEnd},onLockDefNameManager:function(t){this.namedrange_locked=t==Asc.c_oAscDefinedNameReason.LockDefNameManager},onChangeCropState:function(t){this.documentHolder.menuImgCrop.menu.items[0].setChecked(t,!0)},initEquationMenu:function(){if(this._currentMathObj){var t,e=this,i=e._currentMathObj.get_Type(),n=e._currentMathObj,o=[];switch(i){case Asc.c_oAscMathInterfaceType.Accent:t=new Common.UI.MenuItem({caption:e.txtRemoveAccentChar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"remove_AccentCharacter"}}),o.push(t);break;case Asc.c_oAscMathInterfaceType.BorderBox:t=new Common.UI.MenuItem({caption:e.txtBorderProps,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:n.get_HideTop()?e.txtAddTop:e.txtHideTop,equationProps:{type:i,callback:"put_HideTop",value:!n.get_HideTop()}},{caption:n.get_HideBottom()?e.txtAddBottom:e.txtHideBottom,equationProps:{type:i,callback:"put_HideBottom",value:!n.get_HideBottom()}},{caption:n.get_HideLeft()?e.txtAddLeft:e.txtHideLeft,equationProps:{type:i,callback:"put_HideLeft",value:!n.get_HideLeft()}},{caption:n.get_HideRight()?e.txtAddRight:e.txtHideRight,equationProps:{type:i,callback:"put_HideRight",value:!n.get_HideRight()}},{caption:n.get_HideHor()?e.txtAddHor:e.txtHideHor,equationProps:{type:i,callback:"put_HideHor",value:!n.get_HideHor()}},{caption:n.get_HideVer()?e.txtAddVer:e.txtHideVer,equationProps:{type:i,callback:"put_HideVer",value:!n.get_HideVer()}},{caption:n.get_HideTopLTR()?e.txtAddLT:e.txtHideLT,equationProps:{type:i,callback:"put_HideTopLTR",value:!n.get_HideTopLTR()}},{caption:n.get_HideTopRTL()?e.txtAddLB:e.txtHideLB,equationProps:{type:i,callback:"put_HideTopRTL",value:!n.get_HideTopRTL()}}]})}),o.push(t);break;case Asc.c_oAscMathInterfaceType.Bar:t=new Common.UI.MenuItem({caption:e.txtRemoveBar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"remove_Bar"}}),o.push(t),t=new Common.UI.MenuItem({caption:n.get_Pos()==Asc.c_oAscMathInterfaceBarPos.Top?e.txtUnderbar:e.txtOverbar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_Pos",value:n.get_Pos()==Asc.c_oAscMathInterfaceBarPos.Top?Asc.c_oAscMathInterfaceBarPos.Bottom:Asc.c_oAscMathInterfaceBarPos.Top}}),o.push(t);break;case Asc.c_oAscMathInterfaceType.Script:var s=n.get_ScriptType();s==Asc.c_oAscMathInterfaceScript.PreSubSup?(t=new Common.UI.MenuItem({caption:e.txtScriptsAfter,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_ScriptType",value:Asc.c_oAscMathInterfaceScript.SubSup}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtRemScripts,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_ScriptType",value:Asc.c_oAscMathInterfaceScript.None}}),o.push(t)):(s==Asc.c_oAscMathInterfaceScript.SubSup&&(t=new Common.UI.MenuItem({caption:e.txtScriptsBefore,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_ScriptType",value:Asc.c_oAscMathInterfaceScript.PreSubSup}}),o.push(t)),s!=Asc.c_oAscMathInterfaceScript.SubSup&&s!=Asc.c_oAscMathInterfaceScript.Sub||(t=new Common.UI.MenuItem({caption:e.txtRemSubscript,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_ScriptType",value:s==Asc.c_oAscMathInterfaceScript.SubSup?Asc.c_oAscMathInterfaceScript.Sup:Asc.c_oAscMathInterfaceScript.None}}),o.push(t)),s!=Asc.c_oAscMathInterfaceScript.SubSup&&s!=Asc.c_oAscMathInterfaceScript.Sup||(t=new Common.UI.MenuItem({caption:e.txtRemSuperscript,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_ScriptType",value:s==Asc.c_oAscMathInterfaceScript.SubSup?Asc.c_oAscMathInterfaceScript.Sub:Asc.c_oAscMathInterfaceScript.None}}),o.push(t)));break;case Asc.c_oAscMathInterfaceType.Fraction:var a=n.get_FractionType();a!=Asc.c_oAscMathInterfaceFraction.Skewed&&a!=Asc.c_oAscMathInterfaceFraction.Linear||(t=new Common.UI.MenuItem({caption:e.txtFractionStacked,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_FractionType",value:Asc.c_oAscMathInterfaceFraction.Bar}}),o.push(t)),a!=Asc.c_oAscMathInterfaceFraction.Bar&&a!=Asc.c_oAscMathInterfaceFraction.Linear||(t=new Common.UI.MenuItem({caption:e.txtFractionSkewed,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_FractionType",value:Asc.c_oAscMathInterfaceFraction.Skewed}}),o.push(t)),a!=Asc.c_oAscMathInterfaceFraction.Bar&&a!=Asc.c_oAscMathInterfaceFraction.Skewed||(t=new Common.UI.MenuItem({caption:e.txtFractionLinear,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_FractionType",value:Asc.c_oAscMathInterfaceFraction.Linear}}),o.push(t)),a!=Asc.c_oAscMathInterfaceFraction.Bar&&a!=Asc.c_oAscMathInterfaceFraction.NoBar||(t=new Common.UI.MenuItem({caption:a==Asc.c_oAscMathInterfaceFraction.Bar?e.txtRemFractionBar:e.txtAddFractionBar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_FractionType",value:a==Asc.c_oAscMathInterfaceFraction.Bar?Asc.c_oAscMathInterfaceFraction.NoBar:Asc.c_oAscMathInterfaceFraction.Bar}}),o.push(t));break;case Asc.c_oAscMathInterfaceType.Limit:t=new Common.UI.MenuItem({caption:n.get_Pos()==Asc.c_oAscMathInterfaceLimitPos.Top?e.txtLimitUnder:e.txtLimitOver,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_Pos",value:n.get_Pos()==Asc.c_oAscMathInterfaceLimitPos.Top?Asc.c_oAscMathInterfaceLimitPos.Bottom:Asc.c_oAscMathInterfaceLimitPos.Top}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtRemLimit,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_Pos",value:Asc.c_oAscMathInterfaceLimitPos.None}}),o.push(t);break;case Asc.c_oAscMathInterfaceType.Matrix:t=new Common.UI.MenuItem({caption:n.get_HidePlaceholder()?e.txtShowPlaceholder:e.txtHidePlaceholder,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HidePlaceholder",value:!n.get_HidePlaceholder()}}),o.push(t),t=new Common.UI.MenuItem({caption:e.insertText,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:e.insertRowAboveText,equationProps:{type:i,callback:"insert_MatrixRow",value:!0}},{caption:e.insertRowBelowText,equationProps:{type:i,callback:"insert_MatrixRow",value:!1}},{caption:e.insertColumnLeftText,equationProps:{type:i,callback:"insert_MatrixColumn",value:!0}},{caption:e.insertColumnRightText,equationProps:{type:i,callback:"insert_MatrixColumn",value:!1}}]})}),o.push(t),t=new Common.UI.MenuItem({caption:e.deleteText,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:e.deleteRowText,equationProps:{type:i,callback:"delete_MatrixRow"}},{caption:e.deleteColumnText,equationProps:{type:i,callback:"delete_MatrixColumn"}}]})}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtMatrixAlign,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:e.txtTop,checkable:!0,checked:n.get_MatrixAlign()==Asc.c_oAscMathInterfaceMatrixMatrixAlign.Top,equationProps:{type:i,callback:"put_MatrixAlign",value:Asc.c_oAscMathInterfaceMatrixMatrixAlign.Top}},{caption:e.centerText,checkable:!0,checked:n.get_MatrixAlign()==Asc.c_oAscMathInterfaceMatrixMatrixAlign.Center,equationProps:{type:i,callback:"put_MatrixAlign",value:Asc.c_oAscMathInterfaceMatrixMatrixAlign.Center}},{caption:e.txtBottom,checkable:!0,checked:n.get_MatrixAlign()==Asc.c_oAscMathInterfaceMatrixMatrixAlign.Bottom,equationProps:{type:i,callback:"put_MatrixAlign",value:Asc.c_oAscMathInterfaceMatrixMatrixAlign.Bottom}}]})}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtColumnAlign,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:e.leftText,checkable:!0,checked:n.get_ColumnAlign()==Asc.c_oAscMathInterfaceMatrixColumnAlign.Left,equationProps:{type:i,callback:"put_ColumnAlign",value:Asc.c_oAscMathInterfaceMatrixColumnAlign.Left}},{caption:e.centerText,checkable:!0,checked:n.get_ColumnAlign()==Asc.c_oAscMathInterfaceMatrixColumnAlign.Center,equationProps:{type:i,callback:"put_ColumnAlign",value:Asc.c_oAscMathInterfaceMatrixColumnAlign.Center}},{caption:e.rightText,checkable:!0,checked:n.get_ColumnAlign()==Asc.c_oAscMathInterfaceMatrixColumnAlign.Right,equationProps:{type:i,callback:"put_ColumnAlign",value:Asc.c_oAscMathInterfaceMatrixColumnAlign.Right}}]})}),o.push(t);break;case Asc.c_oAscMathInterfaceType.EqArray:t=new Common.UI.MenuItem({caption:e.txtInsertEqBefore,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"insert_Equation",value:!0}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtInsertEqAfter,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"insert_Equation",value:!1}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtDeleteEq,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"delete_Equation"}}),o.push(t),t=new Common.UI.MenuItem({caption:e.alignmentText,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:e.txtTop,checkable:!0,checked:n.get_Align()==Asc.c_oAscMathInterfaceEqArrayAlign.Top,equationProps:{type:i,callback:"put_Align",value:Asc.c_oAscMathInterfaceEqArrayAlign.Top}},{caption:e.centerText,checkable:!0,checked:n.get_Align()==Asc.c_oAscMathInterfaceEqArrayAlign.Center,equationProps:{type:i,callback:"put_Align",value:Asc.c_oAscMathInterfaceEqArrayAlign.Center}},{caption:e.txtBottom,checkable:!0,checked:n.get_Align()==Asc.c_oAscMathInterfaceEqArrayAlign.Bottom,equationProps:{type:i,callback:"put_Align",value:Asc.c_oAscMathInterfaceEqArrayAlign.Bottom}}]})}),o.push(t);break;case Asc.c_oAscMathInterfaceType.LargeOperator:t=new Common.UI.MenuItem({caption:e.txtLimitChange,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_LimitLocation",value:n.get_LimitLocation()==Asc.c_oAscMathInterfaceNaryLimitLocation.UndOvr?Asc.c_oAscMathInterfaceNaryLimitLocation.SubSup:Asc.c_oAscMathInterfaceNaryLimitLocation.UndOvr}}),o.push(t),void 0!==n.get_HideUpper()&&(t=new Common.UI.MenuItem({caption:n.get_HideUpper()?e.txtShowTopLimit:e.txtHideTopLimit,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HideUpper",value:!n.get_HideUpper()}}),o.push(t)),void 0!==n.get_HideLower()&&(t=new Common.UI.MenuItem({caption:n.get_HideLower()?e.txtShowBottomLimit:e.txtHideBottomLimit,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HideLower",value:!n.get_HideLower()}}),o.push(t));break;case Asc.c_oAscMathInterfaceType.Delimiter:t=new Common.UI.MenuItem({caption:e.txtInsertArgBefore,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"insert_DelimiterArgument",value:!0}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtInsertArgAfter,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"insert_DelimiterArgument",value:!1}}),o.push(t),n.can_DeleteArgument()&&(t=new Common.UI.MenuItem({caption:e.txtDeleteArg,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"delete_DelimiterArgument"}}),o.push(t)),t=new Common.UI.MenuItem({caption:n.has_Separators()?e.txtDeleteCharsAndSeparators:e.txtDeleteChars,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"remove_DelimiterCharacters"}}),o.push(t),t=new Common.UI.MenuItem({caption:n.get_HideOpeningBracket()?e.txtShowOpenBracket:e.txtHideOpenBracket,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HideOpeningBracket",value:!n.get_HideOpeningBracket()}}),o.push(t),t=new Common.UI.MenuItem({caption:n.get_HideClosingBracket()?e.txtShowCloseBracket:e.txtHideCloseBracket,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HideClosingBracket",value:!n.get_HideClosingBracket()}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtStretchBrackets,equation:!0,disabled:e._currentParaObjDisabled,checkable:!0,checked:n.get_StretchBrackets(),equationProps:{type:i,callback:"put_StretchBrackets",value:!n.get_StretchBrackets()}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtMatchBrackets,equation:!0,disabled:!n.get_StretchBrackets()||e._currentParaObjDisabled,checkable:!0,checked:n.get_StretchBrackets()&&n.get_MatchBrackets(),equationProps:{type:i,callback:"put_MatchBrackets",value:!n.get_MatchBrackets()}}),o.push(t);break;case Asc.c_oAscMathInterfaceType.GroupChar:n.can_ChangePos()&&(t=new Common.UI.MenuItem({caption:n.get_Pos()==Asc.c_oAscMathInterfaceGroupCharPos.Top?e.txtGroupCharUnder:e.txtGroupCharOver,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_Pos",value:n.get_Pos()==Asc.c_oAscMathInterfaceGroupCharPos.Top?Asc.c_oAscMathInterfaceGroupCharPos.Bottom:Asc.c_oAscMathInterfaceGroupCharPos.Top}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtDeleteGroupChar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_Pos",value:Asc.c_oAscMathInterfaceGroupCharPos.None}}),o.push(t));break;case Asc.c_oAscMathInterfaceType.Radical:void 0!==n.get_HideDegree()&&(t=new Common.UI.MenuItem({caption:n.get_HideDegree()?e.txtShowDegree:e.txtHideDegree,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HideDegree",value:!n.get_HideDegree()}}),o.push(t)),t=new Common.UI.MenuItem({caption:e.txtDeleteRadical,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"remove_Radical"}}),o.push(t)}return n.can_IncreaseArgumentSize()&&(t=new Common.UI.MenuItem({caption:e.txtIncreaseArg,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"increase_ArgumentSize"}}),o.push(t)),n.can_DecreaseArgumentSize()&&(t=new Common.UI.MenuItem({caption:e.txtDecreaseArg,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"decrease_ArgumentSize"}}),o.push(t)),n.can_InsertManualBreak()&&(t=new Common.UI.MenuItem({caption:e.txtInsertBreak,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"insert_ManualBreak"}}),o.push(t)),n.can_DeleteManualBreak()&&(t=new Common.UI.MenuItem({caption:e.txtDeleteBreak,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"delete_ManualBreak"}}),o.push(t)),n.can_AlignToCharacter()&&(t=new Common.UI.MenuItem({caption:e.txtAlignToChar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"align_ToCharacter"}}),o.push(t)),o}},addEquationMenu:function(t){var e=this;e.clearEquationMenu(t);var i=e.documentHolder.textInShapeMenu,n=e.initEquationMenu();return n.length>0&&_.each(n,function(n,o){n.menu?_.each(n.menu.items,function(t){t.on("click",_.bind(e.equationCallback,e,t.options.equationProps))}):n.on("click",_.bind(e.equationCallback,e,n.options.equationProps)),i.insertItem(t,n),t++}),n.length},clearEquationMenu:function(t){for(var e=this,i=e.documentHolder.textInShapeMenu,n=t;n')}),e.paraBulletsPicker.on("item:click",_.bind(this.onSelectBullets,this)),i&&e.paraBulletsPicker.selectRecord(i.rec,!0)},onSignatureClick:function(t){var e=t.cmpEl.attr("data-value");switch(t.value){case 0:Common.NotificationCenter.trigger("protect:sign",e);break;case 1:this.api.asc_ViewCertificate(e);break;case 2:Common.NotificationCenter.trigger("protect:signature","visible",this._isDisabled,e);break;case 3:this.api.asc_RemoveSignature(e)}},onOriginalSizeClick:function(t){if(this.api){var e=this.api.asc_getOriginalImageSize(),i=e.asc_getImageWidth(),n=e.asc_getImageHeight(),o=new Asc.asc_CImgProperty;o.asc_putWidth(i),o.asc_putHeight(n),o.put_ResetCrop(!0),this.api.asc_setGraphicObjectProps(o),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Set Image Original Size")}},onImgReplace:function(t,e){var i=this;this.api&&("file"==e.value?setTimeout(function(){i.api&&i.api.asc_changeImageFromFile(),Common.NotificationCenter.trigger("edit:complete",i.documentHolder)},10):new Common.Views.ImageFromUrlDialog({handler:function(t,e){if("ok"==t&&i.api){var n=e.replace(/ /g,"");if(!_.isEmpty(n)){var o=new Asc.asc_CImgProperty;o.asc_putImageUrl(n),i.api.asc_setGraphicObjectProps(o)}}Common.NotificationCenter.trigger("edit:complete",i.documentHolder)}}).show())},onNumberFormatSelect:function(t,e){void 0!==e.value&&"advanced"!==e.value&&this.api&&this.api.asc_setCellFormat(e.options.format),Common.NotificationCenter.trigger("edit:complete",this.documentHolder)},onCustomNumberFormat:function(t){var e=this,i=e.api.asc_getLocale();!i&&(i=e.permissions.lang?parseInt(Common.util.LanguageInfo.getLocalLanguageCode(e.permissions.lang)):1033),new SSE.Views.FormatSettingsDialog({api:e.api,handler:function(t,i){i&&e.api.asc_setCellFormat(i.format),Common.NotificationCenter.trigger("edit:complete",e.documentHolder)},props:{format:t.options.numformat,formatInfo:t.options.numformatinfo,langId:i}}).show(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder)},onNumberFormatOpenAfter:function(t){if(this.api){var e=this,i=e.api.asc_getLocale();if(!i&&(i=e.permissions.lang?parseInt(Common.util.LanguageInfo.getLocalLanguageCode(e.permissions.lang)):1033),this._state.langId!==i){this._state.langId=i;var n=new Asc.asc_CFormatCellsInfo ;n.asc_setType(Asc.c_oAscNumFormatType.None),n.asc_setSymbol(this._state.langId);for(var o=this.api.asc_getFormatCells(n),s=0;sOnly text values from the column can be selected for replacement.",txtExpandSort:"The data next to the selection will not be sorted. Do you want to expand the selection to include the adjacent data or continue with sorting the currently selected cells only?",txtExpand:"Expand and sort",txtSorting:"Sorting",txtSortSelected:"Sort selected",txtPaste:"Paste",txtPasteFormulas:"Paste only formula",txtPasteFormulaNumFormat:"Formula + number format",txtPasteKeepSourceFormat:"Formula + all formatting",txtPasteBorders:"Formula without borders",txtPasteColWidths:"Formula + column width",txtPasteMerge:"Merge conditional formatting",txtPasteTranspose:"Transpose",txtPasteValues:"Paste only value",txtPasteValNumFormat:"Value + number format",txtPasteValFormat:"Value + all formatting",txtPasteFormat:"Paste only formatting",txtPasteLink:"Paste Link",txtPastePicture:"Picture",txtPasteLinkPicture:"Linked Picture",txtPasteSourceFormat:"Source formatting",txtPasteDestFormat:"Destination formatting",txtKeepTextOnly:"Keep text only",txtUseTextImport:"Use text import wizard",txtUndoExpansion:"Undo table autoexpansion",txtRedoExpansion:"Redo table autoexpansion",txtAnd:"and",txtOr:"or",txtEquals:"Equals",txtNotEquals:"Does not equal",txtGreater:"Greater than",txtGreaterEquals:"Greater than or equal to",txtLess:"Less than",txtLessEquals:"Less than or equal to",txtAboveAve:"Above average",txtBelowAve:"Below average",txtBegins:"Begins with",txtNotBegins:"Does not begin with",txtEnds:"Ends with",txtNotEnds:"Does not end with",txtContains:"Contains",txtNotContains:"Does not contain",txtFilterTop:"Top",txtFilterBottom:"Bottom",txtItems:"items",txtPercent:"percent",txtEqualsToCellColor:"Equals to cell color",txtEqualsToFontColor:"Equals to font color",txtAll:"(All)",txtBlanks:"(Blanks)",txtColumn:"Column",txtImportWizard:"Text Import Wizard"},SSE.Controllers.DocumentHolder||{}))}),define("text!spreadsheeteditor/main/app/template/CellEditor.template",[],function(){return'
\r\n \r\n
\r\n \r\n
\r\n
\r\n \r\n
\r\n
\r\n \r\n
\r\n'}),define("spreadsheeteditor/main/app/view/CellEditor",["text!spreadsheeteditor/main/app/template/CellEditor.template","common/main/lib/component/BaseView"],function(t){"use strict";SSE.Views.CellEditor=Common.UI.BaseView.extend(_.extend({template:_.template(t),initialize:function(t){Common.UI.BaseView.prototype.initialize.call(this,t)},render:function(){$(this.el).html(this.template()),this.btnNamedRanges=new Common.UI.Button({menu:new Common.UI.Menu({style:"min-width: 70px;max-width:400px;",maxHeight:250,items:[{caption:this.textManager,value:"manager"},{caption:"--"}]})}),this.btnNamedRanges.render($("#ce-cell-name-menu")),this.btnNamedRanges.setVisible(!1),this.btnNamedRanges.menu.setOffset(-81),this.$cellname=$("#ce-cell-name",this.el),this.$btnexpand=$("#ce-btn-expand",this.el),this.$btnfunc=$("#ce-func-label",this.el);var t=this;return this.$cellname.on("focus",function(e){var i=t.$cellname[0];i.selectionStart=0,i.selectionEnd=i.value.length,i.scrollLeft=i.scrollWidth}),this.$btnfunc.addClass("disabled"),this.$btnfunc.tooltip({title:this.tipFormula,placement:"cursor"}),this},updateCellInfo:function(t){t&&this.$cellname.val("string"==typeof t?t:t.asc_getName())},cellNameDisabled:function(t){t?this.$cellname.attr("disabled","disabled"):this.$cellname.removeAttr("disabled"),this.btnNamedRanges.setDisabled(t)},tipFormula:"Insert Function",textManager:"Manager"},SSE.Views.CellEditor||{}))}),define("text!spreadsheeteditor/main/app/template/NameManagerDlg.template",[],function(){return'
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
'}),define("spreadsheeteditor/main/app/view/NameManagerDlg",["text!spreadsheeteditor/main/app/template/NameManagerDlg.template","common/main/lib/view/AdvancedSettingsWindow","common/main/lib/component/ComboBox","common/main/lib/component/ListView","common/main/lib/component/InputField"],function(t){"use strict";SSE.Views=SSE.Views||{},SSE.Views.NameManagerDlg=Common.Views.AdvancedSettingsWindow.extend(_.extend({options:{alias:"NameManagerDlg",contentWidth:510,height:353},initialize:function(e){_.extend(this.options,{title:this.txtTitle,template:['
','
'+_.template(t)({scope:this})+"
","
",'
','"].join("")},e),this.api=e.api,this.handler=e.handler,this.sheets=e.sheets||[],this.sheetNames=e.sheetNames||[],this.ranges=e.ranges||[],this.props=e.props,this.sort=e.sort||{type:"name",direction:1},this.locked=e.locked||!1,this.userTooltip=!0,this.currentNamedRange=void 0,this.rangesStore=new Common.UI.DataViewStore,this.wrapEvents={onRefreshDefNameList:_.bind(this.onRefreshDefNameList,this),onLockDefNameManager:_.bind(this.onLockDefNameManager,this)},Common.Views.AdvancedSettingsWindow.prototype.initialize.call(this,this.options)},render:function(){Common.Views.AdvancedSettingsWindow.prototype.render.call(this);var t=this;this.cmbFilter=new Common.UI.ComboBox({el:$("#name-manager-combo-filter"),menuStyle:"min-width: 100%;",editable:!1,cls:"input-group-nr",data:[{value:0,displayValue:this.textFilterAll},{value:1,displayValue:this.textFilterDefNames},{value:2,displayValue:this.textFilterTableNames},{value:3,displayValue:this.textFilterSheet},{value:4,displayValue:this.textFilterWorkbook}]}).on("selected",function(e,i){t.refreshRangeList(null,0)}),this.cmbFilter.setValue(0),this.rangeList=new Common.UI.ListView({el:$("#name-manager-range-list",this.$window),store:new Common.UI.DataViewStore,simpleAddMode:!0,emptyText:this.textEmpty,template:_.template(['
'].join("")),itemTemplate:_.template(['
','
','
<%= name %>
','
<%= scopeName %>
','
<%= range %>
',"<% if (lock) { %>",'
<%=lockuser%>
',"<% } %>","
"].join(""))}),this.rangeList.store.comparator=function(e,i){var n=e.get(t.sort.type).toLowerCase(),o=i.get(t.sort.type).toLowerCase();return n==o?0:n0?this.textnoNames:this.textEmpty)}var a=this,l=this.rangeList.store,r=this.rangesStore.models,c=this.cmbFilter.getValue(),h=c<3?2==c:-1,d=c>2?4==c:-1;if(c>0&&(r=this.rangesStore.filter(function(t){return-1!==h?h===t.get("isTable"):-1!==d&&d===(null===t.get("scope"))})),l.reset(r,{silent:!1}),c=l.length,this.btnEditRange.setDisabled(!c),this.btnDeleteRange.setDisabled(!c),c>0){if(void 0!==e&&null!==e||(e=0),_.isNumber(e))e>c-1&&(e=c-1),this.rangeList.selectByIndex(e),setTimeout(function(){a.rangeList.scrollToRecord(l.at(e))},50);else if(e){var p=l.findWhere({name:e.asc_getName(!0),scope:e.asc_getScope()});p&&(this.rangeList.selectRecord(p),setTimeout(function(){a.rangeList.scrollToRecord(p)},50))}!0===this.userTooltip&&this.rangeList.cmpEl.find(".lock-user").length>0&&this.rangeList.cmpEl.on("mouseover",_.bind(a.onMouseOverLock,a)).on("mouseout",_.bind(a.onMouseOutLock,a))}_.delay(function(){a.rangeList.cmpEl.find(".listview").focus(),a.rangeList.scroller.update({alwaysVisibleY:!0})},100,this)},onMouseOverLock:function(t,e,i){if(!0===this.userTooltip&&$(t.target).hasClass("lock-user")){var n=this,o=$(t.target).tooltip({title:this.tipIsLocked,trigger:"manual"}).data("bs.tooltip");this.userTooltip=o.tip(),this.userTooltip.css("z-index",parseInt(this.$window.css("z-index"))+10),o.show(),setTimeout(function(){n.userTipHide()},5e3)}},userTipHide:function(){"object"==typeof this.userTooltip&&(this.userTooltip.remove(),this.userTooltip=void 0,this.rangeList.cmpEl.off("mouseover").off("mouseout"))},onMouseOutLock:function(t,e,i){"object"==typeof this.userTooltip&&this.userTipHide()},onEditRange:function(t){if(this.locked)return void Common.NotificationCenter.trigger("namedrange:locked");var e=this,i=e.$window.offset(),n=this.rangeList.getSelectedRec(),o=(_.indexOf(this.rangeList.store.models,n),t&&n?new Asc.asc_CDefName(n.get("name"),n.get("range"),n.get("scope"),n.get("isTable"),void 0,void 0,void 0,!0):null),s=new SSE.Views.NamedRangeEditDlg({api:e.api,sheets:this.sheets,props:t?o:this.props,isEdit:t,handler:function(i,n){"ok"==i&&n&&(t?(e.currentNamedRange=n,e.api.asc_editDefinedNames(o,n)):(e.cmbFilter.setValue(0),e.currentNamedRange=n,e.api.asc_setDefinedNames(n)))}}).on("close",function(){e.show(),_.delay(function(){e.rangeList.cmpEl.find(".listview").focus()},100,e)});e.hide(),s.show(i.left+65,i.top+77)},onDeleteRange:function(){var t=this.rangeList.getSelectedRec();t&&(this.currentNamedRange=_.indexOf(this.rangeList.store.models,t),this.api.asc_delDefinedNames(new Asc.asc_CDefName(t.get("name"),t.get("range"),t.get("scope"),t.get("isTable"),void 0,void 0,void 0,!0)))},getSettings:function(){return this.sort},onPrimary:function(){return!0},onDlgBtnClick:function(t){this.handler&&this.handler.call(this,t.currentTarget.attributes.result.value),this.close()},onSortNames:function(t){t!==this.sort.type?(this.sort={type:t,direction:1},this.spanSortName.toggleClass("hidden"),this.spanSortScope.toggleClass("hidden")):this.sort.direction=-this.sort.direction;var e="name"==t?this.spanSortName:this.spanSortScope;this.sort.direction>0?e.removeClass("sort-desc"):e.addClass("sort-desc"),this.rangeList.store.sort(),this.rangeList.onResetItems(),this.rangeList.scroller.update({alwaysVisibleY:!0})},getUserName:function(t){var e=SSE.getCollection("Common.Collections.Users");if(e){var i=e.findUser(t);if(i)return i.get("username")}return this.guestText},onSelectRangeItem:function(t,e,i){this.userTipHide();var n={};if(_.isFunction(i.toJSON)){if(!i.get("selected"))return;n=i.toJSON(),this.currentNamedRange=_.indexOf(this.rangeList.store.models,i),this.btnEditRange.setDisabled(n.lock),this.btnDeleteRange.setDisabled(n.lock||n.isTable)}},hide:function(){this.userTipHide(),Common.UI.Window.prototype.hide.call(this)},close:function(){this.userTipHide(),this.api.asc_unregisterCallback("asc_onLockDefNameManager",this.wrapEvents.onLockDefNameManager),this.api.asc_unregisterCallback("asc_onRefreshDefNameList",this.wrapEvents.onRefreshDefNameList),Common.UI.Window.prototype.close.call(this)},onKeyDown:function(t,e,i){i.keyCode!=Common.UI.Keys.DELETE||this.btnDeleteRange.isDisabled()||this.onDeleteRange()},onDblClickItem:function(t,e,i){this.btnEditRange.isDisabled()||this.onEditRange(!0)},onLockDefNameManager:function(t){this.locked=t==Asc.c_oAscDefinedNameReason.LockDefNameManager},txtTitle:"Name Manager",closeButtonText:"Close",okButtonText:"Ok",textDataRange:"Data Range",textNew:"New",textEdit:"Edit",textDelete:"Delete",textRanges:"Named Ranges",textScope:"Scope",textFilter:"Filter",textEmpty:"No named ranges have been created yet.
Create at least one named range and it will appear in this field.",textnoNames:"No named ranges matching your filter could be found.",textFilterAll:"All",textFilterDefNames:"Defined names",textFilterTableNames:"Table names",textFilterSheet:"Names Scoped to Sheet",textFilterWorkbook:"Names Scoped to Workbook",textWorkbook:"Workbook",guestText:"Guest",tipIsLocked:"This element is being edited by another user."},SSE.Views.NameManagerDlg||{}))}),define("spreadsheeteditor/main/app/controller/CellEditor",["core","spreadsheeteditor/main/app/view/CellEditor","spreadsheeteditor/main/app/view/NameManagerDlg"],function(t){"use strict";SSE.Controllers.CellEditor=Backbone.Controller.extend({views:["CellEditor"],events:function(){return{"keyup input#ce-cell-name":_.bind(this.onCellName,this),"keyup textarea#ce-cell-content":_.bind(this.onKeyupCellEditor,this),"blur textarea#ce-cell-content":_.bind(this.onBlurCellEditor,this),"click button#ce-btn-expand":_.bind(this.expandEditorField,this),"click button#ce-func-label":_.bind(this.onInsertFunction,this)}},initialize:function(){this.addListeners({CellEditor:{},Viewport:{"layout:resizedrag":_.bind(this.onLayoutResize,this)},"Common.Views.Header":{"formulabar:hide":function(t){this.editor.setVisible(!t),Common.localStorage.setBool("sse-hidden-formula",t),Common.NotificationCenter.trigger("layout:changed","celleditor",t?"hidden":"showed")}.bind(this)}})},setApi:function(t){return this.api=t,this.api.isCEditorFocused=!1,this.api.asc_registerCallback("asc_onSelectionNameChanged",_.bind(this.onApiCellSelection,this)),this.api.asc_registerCallback("asc_onEditCell",_.bind(this.onApiEditCell,this)),this.api.asc_registerCallback("asc_onCoAuthoringDisconnect",_.bind(this.onApiDisconnect,this)),Common.NotificationCenter.on("api:disconnect",_.bind(this.onApiDisconnect,this)),Common.NotificationCenter.on("cells:range",_.bind(this.onCellsRange,this)),this.api.asc_registerCallback("asc_onLockDefNameManager",_.bind(this.onLockDefNameManager,this)),this.api.asc_registerCallback("asc_onInputKeyDown",_.bind(this.onInputKeyDown,this)),this},setMode:function(t){this.mode=t,this.editor.$btnfunc[this.mode.isEdit?"removeClass":"addClass"]("disabled"),this.editor.btnNamedRanges.setVisible(this.mode.isEdit&&!this.mode.isEditDiagram&&!this.mode.isEditMailMerge),this.mode.isEdit&&this.api.asc_registerCallback("asc_onSelectionChanged",_.bind(this.onApiSelectionChanged,this))},onInputKeyDown:function(t){if(Common.UI.Keys.UP===t.keyCode||Common.UI.Keys.DOWN===t.keyCode||Common.UI.Keys.TAB===t.keyCode||Common.UI.Keys.RETURN===t.keyCode||Common.UI.Keys.ESC===t.keyCode||Common.UI.Keys.LEFT===t.keyCode||Common.UI.Keys.RIGHT===t.keyCode){var e=$("#menu-formula-selection");e.hasClass("open")&&e.find(".dropdown-menu").trigger("keydown",t)}},onLaunch:function(){this.editor=this.createView("CellEditor",{el:"#cell-editing-box"}).render(),this.bindViewEvents(this.editor,this.events),this.editor.$el.parent().find(".after").css({zIndex:"4"}),this.editor.btnNamedRanges.menu.on("item:click",_.bind(this.onNamedRangesMenu,this)).on("show:before",_.bind(this.onNameBeforeShow,this)),this.namedrange_locked=!1},onApiEditCell:function(t){t==Asc.c_oAscCellEditorState.editStart?(this.api.isCellEdited=!0,this.editor.cellNameDisabled(!0)):t==Asc.c_oAscCellEditorState.editInCell?this.api.isCEditorFocused="clear":t==Asc.c_oAscCellEditorState.editEnd&&(this.api.isCellEdited=!1,this.api.isCEditorFocused=!1,this.editor.cellNameDisabled(!1)),this.editor.$btnfunc.toggleClass("disabled",t==Asc.c_oAscCellEditorState.editText)},onApiCellSelection:function(t){this.editor.updateCellInfo(t)},onApiSelectionChanged:function(t){var e=t.asc_getFlags().asc_getSelectionType(),i=!this.mode.isEditMailMerge&&!this.mode.isEditDiagram&&(!0===t.asc_getLocked()||!0===t.asc_getLockedTable()),n=e==Asc.c_oAscSelectionType.RangeChartText,o=e==Asc.c_oAscSelectionType.RangeChart,s=e==Asc.c_oAscSelectionType.RangeShapeText,a=e==Asc.c_oAscSelectionType.RangeShape,l=e==Asc.c_oAscSelectionType.RangeImage,r=s||a||n||o;this.editor.$btnfunc.toggleClass("disabled",l||r||i)},onApiDisconnect:function(){this.mode.isEdit=!1;var t=this.getApplication().getController("FormulaDialog");t&&t.hideDialog(),this.mode.isEdit||($("#ce-func-label",this.editor.el).addClass("disabled"),this.editor.btnNamedRanges.setVisible(!1))},onCellsRange:function(t){this.editor.cellNameDisabled(t!=Asc.c_oAscSelectionDialogType.None),this.editor.$btnfunc.toggleClass("disabled",t!=Asc.c_oAscSelectionDialogType.None)},onLayoutResize:function(t,e){"cell:edit"==e&&(this.editor.$el.height()>19?this.editor.$btnexpand.hasClass("btn-collapse")||this.editor.$btnexpand.addClass("btn-collapse"):this.editor.$btnexpand.removeClass("btn-collapse"))},onCellName:function(t){if(t.keyCode==Common.UI.Keys.RETURN){var e=this.editor.$cellname.val();e&&e.length&&this.api.asc_findCell(e),Common.NotificationCenter.trigger("edit:complete",this.editor)}},onBlurCellEditor:function(){"clear"==this.api.isCEditorFocused?this.api.isCEditorFocused=void 0:this.api.isCellEdited&&(this.api.isCEditorFocused=!0)},onKeyupCellEditor:function(t){t.keyCode!=Common.UI.Keys.RETURN||t.altKey||(this.api.isCEditorFocused="clear")},expandEditorField:function(){this.editor.$el.height()>19?(this.editor.keep_height=this.editor.$el.height(),this.editor.$el.height(19),this.editor.$btnexpand.removeClass("btn-collapse")):(this.editor.$el.height(this.editor.keep_height||74),this.editor.$btnexpand.addClass("btn-collapse")),Common.NotificationCenter.trigger("layout:changed","celleditor"),Common.NotificationCenter.trigger("edit:complete",this.editor,{restorefocus:!0})},onInsertFunction:function(){if(this.mode.isEdit&&!this.editor.$btnfunc.hasClass("disabled")){var t=this.getApplication().getController("FormulaDialog");t&&($("#ce-func-label",this.editor.el).blur(),t.showDialog())}},onNamedRangesMenu:function(t,e){var i=this;if("manager"==e.options.value){for(var n=this.api.asc_getWorksheetsCount(),o=-1,s=[],a=[];++o2)},onLockDefNameManager:function(t){this.namedrange_locked=t==Asc.c_oAscDefinedNameReason.LockDefNameManager}})}),define("common/main/lib/view/ImageFromUrlDialog",["common/main/lib/component/Window"],function(){"use strict";Common.Views.ImageFromUrlDialog=Common.UI.Window.extend(_.extend({options:{width:330,header:!1,cls:"modal-dlg"},initialize:function(t){_.extend(this.options,t||{}),this.template=['
','
',"","
",'
',"
",'"].join(""),this.options.tpl=_.template(this.template)(this.options),Common.UI.Window.prototype.initialize.call(this,this.options)},render:function(){Common.UI.Window.prototype.render.call(this);var t=this;t.inputUrl=new Common.UI.InputField({el:$("#id-dlg-url"),allowBlank:!1,blankError:t.txtEmpty,style:"width: 100%;",validateOnBlur:!1,validation:function(e){return!!/((^https?)|(^ftp)):\/\/.+/i.test(e)||t.txtNotUrl}}),this.getChild().find(".btn").on("click",_.bind(this.onBtnClick,this))},show:function(){Common.UI.Window.prototype.show.apply(this,arguments);var t=this;_.delay(function(){t.getChild("input").focus()},500)},onPrimary:function(t){return this._handleInput("ok"),!1},onBtnClick:function(t){this._handleInput(t.currentTarget.attributes.result.value)},_handleInput:function(t){if(this.options.handler){if("ok"==t&&!0!==this.inputUrl.checkValidate())return void this.inputUrl.cmpEl.find("input").focus();this.options.handler.call(this,t,this.inputUrl.getValue())}this.close()},textUrl:"Paste an image URL:",cancelButtonText:"Cancel",okButtonText:"Ok",txtEmpty:"This field is required",txtNotUrl:'This field should be a URL in the format "http://www.example.com"'},Common.Views.ImageFromUrlDialog||{}))}),void 0===Common)var Common={};if(define("common/main/lib/component/LoadMask",["common/main/lib/component/BaseView"],function(){"use strict";Common.UI.LoadMask=Common.UI.BaseView.extend(function(){var t,e,i;return{options:{cls:"",style:"",title:"Loading...",owner:document.body},template:_.template(['"].join("")),initialize:function(t){Common.UI.BaseView.prototype.initialize.call(this,t),this.template=this.options.template||this.template,this.cls=this.options.cls,this.style=this.options.style,this.title=this.options.title,this.owner=this.options.owner},render:function(){return this},show:function(){if(!e&&!i){if(t=this.owner instanceof Common.UI.BaseView?$(this.owner.el):$(this.owner),t.hasClass("masked"))return this;var n=this;return e=$('
'),i=$(this.template({id:n.id,cls:n.cls,style:n.style,title:n.title})),t.addClass("masked"),t.append(e),t.append(i),i.css({top:Math.round(t.height()/2-(i.height()+parseInt(i.css("padding-top"))+parseInt(i.css("padding-bottom")))/2)+"px",left:Math.round(t.width()/2-(i.width()+parseInt(i.css("padding-left"))+parseInt(i.css("padding-right")))/2)+"px"}),(t.height()<1||t.width()<1)&&i.css({visibility:"hidden"}),Common.util.Shortcuts.suspendEvents(),this}},hide:function(){t&&t.removeClass("masked"),e&&e.remove(),i&&i.remove(),e=null,i=null,Common.util.Shortcuts.resumeEvents()},setTitle:function(e){this.title=e,t&&t.hasClass("masked")&&i&&$(".asc-loadmask-title",i).html(e)},isVisible:function(){return!_.isEmpty(i)},updatePosition:function(){t&&t.hasClass("masked")&&i&&(i.css({top:Math.round(t.height()/2-(i.height()+parseInt(i.css("padding-top"))+parseInt(i.css("padding-bottom")))/2)+"px",left:Math.round(t.width()/2-(i.width()+parseInt(i.css("padding-left"))+parseInt(i.css("padding-right")))/2)+"px"}),i.css({visibility:"visible"}))}}}())}),define("common/main/lib/view/SelectFileDlg",["common/main/lib/component/Window","common/main/lib/component/LoadMask"],function(){"use strict";Common.Views.SelectFileDlg=Common.UI.Window.extend(_.extend({initialize:function(t){var e={};_.extend(e,{title:this.textTitle,width:1024,height:621,header:!0},t),this.template=['
'].join(""),e.tpl=_.template(this.template)(e),this.fileChoiceUrl=t.fileChoiceUrl||"",Common.UI.Window.prototype.initialize.call(this,e)},render:function(){Common.UI.Window.prototype.render.call(this),this.$window.find("> .body").css({height:"auto",overflow:"hidden"});var t=document.createElement("iframe");t.width="100%",t.height=585,t.align="top",t.frameBorder=0,t.scrolling="no",t.onload=_.bind(this._onLoad,this),$("#id-select-file-placeholder").append(t),this.loadMask=new Common.UI.LoadMask({owner:$("#id-select-file-placeholder")}),this.loadMask.setTitle(this.textLoading),this.loadMask.show(), t.src=this.fileChoiceUrl;var e=this;this._eventfunc=function(t){e._onWindowMessage(t)},this._bindWindowEvents.call(this),this.on("close",function(t){e._unbindWindowEvents()})},_bindWindowEvents:function(){window.addEventListener?window.addEventListener("message",this._eventfunc,!1):window.attachEvent&&window.attachEvent("onmessage",this._eventfunc)},_unbindWindowEvents:function(){window.removeEventListener?window.removeEventListener("message",this._eventfunc):window.detachEvent&&window.detachEvent("onmessage",this._eventfunc)},_onWindowMessage:function(t){if(t&&window.JSON)try{this._onMessage.call(this,window.JSON.parse(t.data))}catch(t){}},_onMessage:function(t){if(t&&"onlyoffice"==t.Referer&&void 0!==t.file){Common.NotificationCenter.trigger("window:close",this);var e=this;setTimeout(function(){_.isEmpty(t.file)||e.trigger("selectfile",e,t.file)},50)}},_onLoad:function(){this.loadMask&&this.loadMask.hide()},textTitle:"Select Data Source",textLoading:"Loading"},Common.Views.SelectFileDlg||{}))}),void 0===Common)var Common={};if(define("common/main/lib/util/define",[],function(){"use strict";Common.define={},Common.define.c_oAscMathMainType={Symbol:0,Fraction:1,Script:2,Radical:3,Integral:4,LargeOperator:5,Bracket:6,Function:7,Accent:8,LimitLog:9,Operator:10,Matrix:11},Common.define.c_oAscMathType={Symbol_pm:0,Symbol_infinity:1,Symbol_equals:2,Symbol_neq:3,Symbol_about:4,Symbol_times:5,Symbol_div:6,Symbol_factorial:7,Symbol_propto:8,Symbol_less:9,Symbol_ll:10,Symbol_greater:11,Symbol_gg:12,Symbol_leq:13,Symbol_geq:14,Symbol_mp:15,Symbol_cong:16,Symbol_approx:17,Symbol_equiv:18,Symbol_forall:19,Symbol_additional:20,Symbol_partial:21,Symbol_sqrt:22,Symbol_cbrt:23,Symbol_qdrt:24,Symbol_cup:25,Symbol_cap:26,Symbol_emptyset:27,Symbol_percent:28,Symbol_degree:29,Symbol_fahrenheit:30,Symbol_celsius:31,Symbol_inc:32,Symbol_nabla:33,Symbol_exists:34,Symbol_notexists:35,Symbol_in:36,Symbol_ni:37,Symbol_leftarrow:38,Symbol_uparrow:39,Symbol_rightarrow:40,Symbol_downarrow:41,Symbol_leftrightarrow:42,Symbol_therefore:43,Symbol_plus:44,Symbol_minus:45,Symbol_not:46,Symbol_ast:47,Symbol_bullet:48,Symbol_vdots:49,Symbol_cdots:50,Symbol_rddots:51,Symbol_ddots:52,Symbol_aleph:53,Symbol_beth:54,Symbol_QED:55,Symbol_alpha:65536,Symbol_beta:65537,Symbol_gamma:65538,Symbol_delta:65539,Symbol_varepsilon:65540,Symbol_epsilon:65541,Symbol_zeta:65542,Symbol_eta:65543,Symbol_theta:65544,Symbol_vartheta:65545,Symbol_iota:65546,Symbol_kappa:65547,Symbol_lambda:65548,Symbol_mu:65549,Symbol_nu:65550,Symbol_xsi:65551,Symbol_o:65552,Symbol_pi:65553,Symbol_varpi:65554,Symbol_rho:65555,Symbol_varrho:65556,Symbol_sigma:65557,Symbol_varsigma:65558,Symbol_tau:65559,Symbol_upsilon:65560,Symbol_varphi:65561,Symbol_phi:65562,Symbol_chi:65563,Symbol_psi:65564,Symbol_omega:65565,Symbol_Alpha:131072,Symbol_Beta:131073,Symbol_Gamma:131074,Symbol_Delta:131075,Symbol_Epsilon:131076,Symbol_Zeta:131077,Symbol_Eta:131078,Symbol_Theta:131079,Symbol_Iota:131080,Symbol_Kappa:131081,Symbol_Lambda:131082,Symbol_Mu:131083,Symbol_Nu:131084,Symbol_Xsi:131085,Symbol_O:131086,Symbol_Pi:131087,Symbol_Rho:131088,Symbol_Sigma:131089,Symbol_Tau:131090,Symbol_Upsilon:131091,Symbol_Phi:131092,Symbol_Chi:131093,Symbol_Psi:131094,Symbol_Omega:131095,FractionVertical:16777216,FractionDiagonal:16777217,FractionHorizontal:16777218,FractionSmall:16777219,FractionDifferential_1:16842752,FractionDifferential_2:16842753,FractionDifferential_3:16842754,FractionDifferential_4:16842755,FractionPi_2:16842756,ScriptSup:33554432,ScriptSub:33554433,ScriptSubSup:33554434,ScriptSubSupLeft:33554435,ScriptCustom_1:33619968,ScriptCustom_2:33619969,ScriptCustom_3:33619970,ScriptCustom_4:33619971,RadicalSqrt:50331648,RadicalRoot_n:50331649,RadicalRoot_2:50331650,RadicalRoot_3:50331651,RadicalCustom_1:50397184,RadicalCustom_2:50397185,Integral:67108864,IntegralSubSup:67108865,IntegralCenterSubSup:67108866,IntegralDouble:67108867,IntegralDoubleSubSup:67108868,IntegralDoubleCenterSubSup:67108869,IntegralTriple:67108870,IntegralTripleSubSup:67108871,IntegralTripleCenterSubSup:67108872,IntegralOriented:67174400,IntegralOrientedSubSup:67174401,IntegralOrientedCenterSubSup:67174402,IntegralOrientedDouble:67174403,IntegralOrientedDoubleSubSup:67174404,IntegralOrientedDoubleCenterSubSup:67174405,IntegralOrientedTriple:67174406,IntegralOrientedTripleSubSup:67174407,IntegralOrientedTripleCenterSubSup:67174408,Integral_dx:67239936,Integral_dy:67239937,Integral_dtheta:67239938,LargeOperator_Sum:83886080,LargeOperator_Sum_CenterSubSup:83886081,LargeOperator_Sum_SubSup:83886082,LargeOperator_Sum_CenterSub:83886083,LargeOperator_Sum_Sub:83886084,LargeOperator_Prod:83951616,LargeOperator_Prod_CenterSubSup:83951617,LargeOperator_Prod_SubSup:83951618,LargeOperator_Prod_CenterSub:83951619,LargeOperator_Prod_Sub:83951620,LargeOperator_CoProd:83951621,LargeOperator_CoProd_CenterSubSup:83951622,LargeOperator_CoProd_SubSup:83951623,LargeOperator_CoProd_CenterSub:83951624,LargeOperator_CoProd_Sub:83951625,LargeOperator_Union:84017152,LargeOperator_Union_CenterSubSup:84017153,LargeOperator_Union_SubSup:84017154,LargeOperator_Union_CenterSub:84017155,LargeOperator_Union_Sub:84017156,LargeOperator_Intersection:84017157,LargeOperator_Intersection_CenterSubSup:84017158,LargeOperator_Intersection_SubSup:84017159,LargeOperator_Intersection_CenterSub:84017160,LargeOperator_Intersection_Sub:84017161,LargeOperator_Disjunction:84082688,LargeOperator_Disjunction_CenterSubSup:84082689,LargeOperator_Disjunction_SubSup:84082690,LargeOperator_Disjunction_CenterSub:84082691,LargeOperator_Disjunction_Sub:84082692,LargeOperator_Conjunction:84082693,LargeOperator_Conjunction_CenterSubSup:84082694,LargeOperator_Conjunction_SubSup:84082695,LargeOperator_Conjunction_CenterSub:84082696,LargeOperator_Conjunction_Sub:84082697,LargeOperator_Custom_1:84148224,LargeOperator_Custom_2:84148225,LargeOperator_Custom_3:84148226,LargeOperator_Custom_4:84148227,LargeOperator_Custom_5:84148228,Bracket_Round:100663296,Bracket_Square:100663297,Bracket_Curve:100663298,Bracket_Angle:100663299,Bracket_LowLim:100663300,Bracket_UppLim:100663301,Bracket_Line:100663302,Bracket_LineDouble:100663303,Bracket_Square_OpenOpen:100663304,Bracket_Square_CloseClose:100663305,Bracket_Square_CloseOpen:100663306,Bracket_SquareDouble:100663307,Bracket_Round_Delimiter_2:100728832,Bracket_Curve_Delimiter_2:100728833,Bracket_Angle_Delimiter_2:100728834,Bracket_Angle_Delimiter_3:100728835,Bracket_Round_OpenNone:100794368,Bracket_Round_NoneOpen:100794369,Bracket_Square_OpenNone:100794370,Bracket_Square_NoneOpen:100794371,Bracket_Curve_OpenNone:100794372,Bracket_Curve_NoneOpen:100794373,Bracket_Angle_OpenNone:100794374,Bracket_Angle_NoneOpen:100794375,Bracket_LowLim_OpenNone:100794376,Bracket_LowLim_NoneNone:100794377,Bracket_UppLim_OpenNone:100794378,Bracket_UppLim_NoneOpen:100794379,Bracket_Line_OpenNone:100794380,Bracket_Line_NoneOpen:100794381,Bracket_LineDouble_OpenNone:100794382,Bracket_LineDouble_NoneOpen:100794383,Bracket_SquareDouble_OpenNone:100794384,Bracket_SquareDouble_NoneOpen:100794385,Bracket_Custom_1:100859904,Bracket_Custom_2:100859905,Bracket_Custom_3:100859906,Bracket_Custom_4:100859907,Bracket_Custom_5:100925440,Bracket_Custom_6:100925441,Bracket_Custom_7:100925442,Function_Sin:117440512,Function_Cos:117440513,Function_Tan:117440514,Function_Csc:117440515,Function_Sec:117440516,Function_Cot:117440517,Function_1_Sin:117506048,Function_1_Cos:117506049,Function_1_Tan:117506050,Function_1_Csc:117506051,Function_1_Sec:117506052,Function_1_Cot:117506053,Function_Sinh:117571584,Function_Cosh:117571585,Function_Tanh:117571586,Function_Csch:117571587,Function_Sech:117571588,Function_Coth:117571589,Function_1_Sinh:117637120,Function_1_Cosh:117637121,Function_1_Tanh:117637122,Function_1_Csch:117637123,Function_1_Sech:117637124,Function_1_Coth:117637125,Function_Custom_1:117702656,Function_Custom_2:117702657,Function_Custom_3:117702658,Accent_Dot:134217728,Accent_DDot:134217729,Accent_DDDot:134217730,Accent_Hat:134217731,Accent_Check:134217732,Accent_Accent:134217733,Accent_Grave:134217734,Accent_Smile:134217735,Accent_Tilde:134217736,Accent_Bar:134217737,Accent_DoubleBar:134217738,Accent_CurveBracketTop:134217739,Accent_CurveBracketBot:134217740,Accent_GroupTop:134217741,Accent_GroupBot:134217742,Accent_ArrowL:134217743,Accent_ArrowR:134217744,Accent_ArrowD:134217745,Accent_HarpoonL:134217746,Accent_HarpoonR:134217747,Accent_BorderBox:134283264,Accent_BorderBoxCustom:134283265,Accent_BarTop:134348800,Accent_BarBot:134348801,Accent_Custom_1:134414336,Accent_Custom_2:134414337,Accent_Custom_3:134414338,LimitLog_LogBase:150994944,LimitLog_Log:150994945,LimitLog_Lim:150994946,LimitLog_Min:150994947,LimitLog_Max:150994948,LimitLog_Ln:150994949,LimitLog_Custom_1:151060480,LimitLog_Custom_2:151060481,Operator_ColonEquals:167772160,Operator_EqualsEquals:167772161,Operator_PlusEquals:167772162,Operator_MinusEquals:167772163,Operator_Definition:167772164,Operator_UnitOfMeasure:167772165,Operator_DeltaEquals:167772166,Operator_ArrowL_Top:167837696,Operator_ArrowR_Top:167837697,Operator_ArrowL_Bot:167837698,Operator_ArrowR_Bot:167837699,Operator_DoubleArrowL_Top:167837700,Operator_DoubleArrowR_Top:167837701,Operator_DoubleArrowL_Bot:167837702,Operator_DoubleArrowR_Bot:167837703,Operator_ArrowD_Top:167837704,Operator_ArrowD_Bot:167837705,Operator_DoubleArrowD_Top:167837706,Operator_DoubleArrowD_Bot:167837707,Operator_Custom_1:167903232,Operator_Custom_2:167903233,Matrix_1_2:184549376,Matrix_2_1:184549377,Matrix_1_3:184549378,Matrix_3_1:184549379,Matrix_2_2:184549380,Matrix_2_3:184549381,Matrix_3_2:184549382,Matrix_3_3:184549383,Matrix_Dots_Center:184614912,Matrix_Dots_Baseline:184614913,Matrix_Dots_Vertical:184614914,Matrix_Dots_Diagonal:184614915,Matrix_Identity_2:184680448,Matrix_Identity_2_NoZeros:184680449,Matrix_Identity_3:184680450,Matrix_Identity_3_NoZeros:184680451,Matrix_2_2_RoundBracket:184745984,Matrix_2_2_SquareBracket:184745985,Matrix_2_2_LineBracket:184745986,Matrix_2_2_DLineBracket:184745987,Matrix_Flat_Round:184811520,Matrix_Flat_Square:184811521}}),define("text!spreadsheeteditor/main/app/template/Toolbar.template",[],function(){return'
\n
\n
\n <%= tabsmarkup %>\n
\n
\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n \n \n \n
\n
\n \n \n \n \n \n
\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n
\n \n
\n
\n \n \n \n \n
\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n
\n
\n \n
\n
\n
\n \n \n \n \n \n
\n
\n
\n \n \n
\n
\n
\n \n
\n
\n
\n
\n \n \n \n \n \n
\n
\n
\n \n \n \n \n
\n
\n
\n
\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n \n
\n
\n
\n \n \n
\n \x3c!--
--\x3e\n \x3c!--
--\x3e\n \x3c!----\x3e\n \x3c!--
--\x3e\n \x3c!--
--\x3e\n \x3c!----\x3e\n \x3c!--
--\x3e\n \x3c!--
--\x3e\n
\n
\n
\n
'}),define("text!spreadsheeteditor/main/app/template/ToolbarAnother.template",[],function(){return'
\n <% if ( isEditDiagram ) { %>\n \x3c!-----------------------\x3e\n \x3c!-- Edit diagram mode --\x3e\n \x3c!-----------------------\x3e\n
\n
\n
\n \n \n \n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n
\n \n \n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n <% } else if ( isEditMailMerge ) { %>\n \x3c!-----------------------\x3e\n \x3c!-- Edit mail merge mode --\x3e\n \x3c!-----------------------\x3e\n
\n
\n
\n \n \n \n \n
\n
\n
\n
\n
\n \n \n
\n
\n
\n
\n
\n \n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n <% } %>\n
'}),define("text!spreadsheeteditor/main/app/template/ToolbarView.template",[],function(){return'
\n
\n
\n <%= tabsmarkup %>\n
\n
\n
\n
\n
\n
\n
\n
'}),void 0===Common)var Common={};if(Common.Models=Common.Models||{},define("common/main/lib/model/Font",["backbone"],function(t){"use strict";Common.Models.Font=t.Model.extend({defaults:function(){return{id:Common.UI.getId(),name:null,cloneid:null,imgidx:0,type:0}}})}),void 0===Common)var Common={};if(Common.Collections=Common.Collections||{},define("common/main/lib/collection/Fonts",["backbone","common/main/lib/model/Font"],function(t){"use strict";Common.Collections.Fonts=t.Collection.extend({model:Common.Models.Font,comparator:function(t,e){var i=t.get("name").toLowerCase(),n=e.get("name").toLowerCase();return i==n?0:i","<% if (iconImg) { print(''); } else { %>","<% if (iconCls != \"\") { if (/svgicon/.test(iconCls)) {print('');} else print(' '); }} %>","<% } %>","<% if ( !menu ) { %>",'","<% } else if (split == false) {%>",'
','","
","<% } else { %>",'
','",'","
","<% } %>"].join("")),initialize:function(t){Common.UI.BaseView.prototype.initialize.call(this,t);var e=this;e.id=e.options.id||Common.UI.getId(),e.hint=e.options.hint,e.enableToggle=e.options.enableToggle,e.allowDepress=e.options.allowDepress,e.cls=e.options.cls,e.iconCls=e.options.iconCls,e.menu=e.options.menu,e.split=e.options.split,e.toggleGroup=e.options.toggleGroup,e.disabled=e.options.disabled,e.visible=e.options.visible,e.pressed=e.options.pressed,e.caption=e.options.caption,e.template=e.options.template||e.template,e.style=e.options.style,e.rendered=!1,e.options.el&&e.render()},render:function(t){var e=this;if(e.trigger("render:before",e),e.cmpEl=$(e.el),t&&(e.setElement(t,!1),e.rendered||(/icon-top/.test(e.cls)&&e.caption&&/huge/.test(e.cls)&&(!0===e.split?(!!e.cls&&(e.cls=e.cls.replace(/\s?(?:x-huge|icon-top)/g,"")),this.template=_.template('
')):e.menu?this.template=_.template('
'):this.template=_.template('')),e.cmpEl=$(this.template({id:e.id,cls:e.cls,iconCls:e.iconCls,iconImg:e.options.iconImg,menu:e.menu,split:e.split,disabled:e.disabled,pressed:e.pressed,caption:e.caption,style:e.style})),e.menu&&_.isObject(e.menu)&&_.isFunction(e.menu.render)&&e.menu.render(e.cmpEl),t.html(e.cmpEl),e.$icon=e.$el.find(".icon"))),!e.rendered){var i=e.cmpEl,n=i.hasClass("btn-group"),o=i.hasClass("split");if(e.options.hint){var s=e.cmpEl.closest(".asc-window");if("object"==typeof e.options.hint&&e.options.hint.length>1&&$("button",i).length>0){var a=$("button",i);e.btnEl=$(a[0]),e.btnMenuEl=$(a[1])}else e.btnEl=e.cmpEl,e.btnEl.attr("data-toggle","tooltip");e.btnEl.tooltip({ diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index c9e2eeacd..32ff11052 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -136,13 +136,11 @@ define([ file.uid = Util.uid(); response.expect(file.uid, function (href) { - var mdMgr = common.getMetadataMgr(); - var origin = mdMgr.getPrivateData().origin; $link.prepend($('', {'class': 'fa fa-external-link'})); $link.attr('href', href) .click(function (e) { e.preventDefault(); - window.open(origin + $link.attr('href'), '_blank'); + common.openURL($link.attr('href')); }); var title = metadata.name; if (!config.noStore) { diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index ad128db33..b2c0065e4 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -722,7 +722,16 @@ define([ sframeChan.on('EV_OPEN_URL', function (url) { if (url) { - window.open(url); + var a = window.open(url); + if (!a) { + sframeChan.event('EV_POPUP_BLOCKED'); + } + } + }); + + sframeChan.on('EV_OPEN_UNSAFE_URL', function (url) { + if (url) { + window.open(ApiConfig.httpSafeOrigin + '/bounce/#' + encodeURIComponent(url)); } }); diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 3eb86ff93..bce81932b 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -552,6 +552,10 @@ define([ funcs.gotoURL = function (url) { ctx.sframeChan.event('EV_GOTO_URL', url); }; funcs.openURL = function (url) { ctx.sframeChan.event('EV_OPEN_URL', url); }; funcs.openUnsafeURL = function (url) { + var app = ctx.metadataMgr.getPrivateData().app; + if (app === "sheet") { + return void ctx.sframeChan.event('EV_OPEN_UNSAFE_URL', url); + } var bounceHref = window.location.origin + '/bounce/#' + encodeURIComponent(url); window.open(bounceHref); }; @@ -682,6 +686,10 @@ define([ UI.errorLoadingScreen(Messages.password_error_seed); }); + ctx.sframeChan.on("EV_POPUP_BLOCKED", function () { + UI.alert(Messages.errorPopupBlocked); + }); + ctx.sframeChan.on("EV_EXPIRED_ERROR", function () { funcs.onServerError({ type: 'EEXPIRED' diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 78e4e91f3..8d26c2e19 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -338,7 +338,7 @@ MessengerUI, Messages) { if (data.profile) { $span.addClass('cp-userlist-clickable'); $span.click(function () { - window.open(origin+'/profile/#' + data.profile); + Common.openURL(origin+'/profile/#' + data.profile); }); } Common.displayAvatar($span, data.avatar, name, function () { @@ -830,7 +830,7 @@ MessengerUI, Messages) { title: buttonTitle, 'class': "cp-toolbar-link-logo" }).append(UIElements.getSvgLogo()); - + /*.append($('', { //src: '/customize/images/logo_white.png?' + ApiConfig.requireConf.urlArgs src: '/customize/main-favicon.png?' + ApiConfig.requireConf.urlArgs @@ -838,10 +838,10 @@ MessengerUI, Messages) { var onClick = function (e) { e.preventDefault(); if (e.ctrlKey) { - window.open(href); + Common.openURL(href); return; } - window.parent.location = href; + Common.gotoURL(href); }; var onContext = function (e) { e.stopPropagation(); }; From 1c254c0d33a620d56f2c848b9e7f5ae14fb52fcb Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 1 Dec 2020 15:38:52 +0100 Subject: [PATCH 084/201] Fix more links for the Chromium bug --- www/common/common-ui-elements.js | 48 +++++++++++++++++--------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 3474b2c13..f4dbb44e9 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1609,33 +1609,37 @@ define([ options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'href': origin+'/drive/', 'class': 'fa fa-hdd-o' }, - content: h('span', Messages.type.drive) + content: h('span', Messages.type.drive), + action: function () { + Common.openURL(origin+'/drive/'); + }, + }); }); } if (padType !== 'teams' && accountName) { options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'href': origin+'/teams/', 'class': 'fa fa-users' }, - content: h('span', Messages.type.teams) + content: h('span', Messages.type.teams), + action: function () { + Common.openURL('/teams/'); + }, }); } if (padType !== 'contacts' && accountName) { options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'href': origin+'/contacts/', 'class': 'fa fa-address-book' }, - content: h('span', Messages.type.contacts) + content: h('span', Messages.type.contacts), + action: function () { + Common.openURL('/contacts/'); + }, }); } if (padType !== 'settings') { @@ -1687,13 +1691,11 @@ define([ options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'rel': 'noopener', - 'href': AppConfig.surveyURL, 'class': 'cp-toolbar-survey fa fa-graduation-cap' }, content: h('span', Messages.survey), action: function () { + Common.openUnsafeURL(AppConfig.surveyURL); Feedback.send('SURVEY_CLICKED'); }, }); @@ -1712,11 +1714,12 @@ define([ options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'href': origin+'/index.html', 'class': 'fa fa-home' }, - content: h('span', Messages.homePage) + content: h('span', Messages.homePage), + action: function () { + Common.openURL('/index.html'); + }, }); // Add the change display name button if not in read only mode /* @@ -1732,23 +1735,24 @@ define([ options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'href': priv.plan ? priv.accounts.upgradeURL : origin+'/features.html', 'class': 'fa fa-star-o' }, - content: h('span', priv.plan ? Messages.settings_cat_subscription : Messages.pricing) + content: h('span', priv.plan ? Messages.settings_cat_subscription : Messages.pricing), + action: function () { + Common.openURL(priv.plan ? priv.accounts.upgradeURL :'/features.html'); + }, }); } if (!priv.plan && !Config.removeDonateButton) { options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'rel': 'noopener', - 'href': priv.accounts.donateURL, 'class': 'fa fa-gift' }, - content: h('span', Messages.crowdfunding_button2) + content: h('span', Messages.crowdfunding_button2), + action: function () { + Common.openUnsafeURL(priv.accounts.donateURL); + }, }); } From 5092a6d9e5e78b796cc2c131ee38349822989648 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 1 Dec 2020 15:40:24 +0100 Subject: [PATCH 085/201] Fix syntax error --- www/common/common-ui-elements.js | 1 - 1 file changed, 1 deletion(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index f4dbb44e9..f461bfc11 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1616,7 +1616,6 @@ define([ Common.openURL(origin+'/drive/'); }, }); - }); } if (padType !== 'teams' && accountName) { options.push({ From 20cecbcfa2dae26bf4de8ed5ecf079b621ac149e Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 1 Dec 2020 18:18:16 +0100 Subject: [PATCH 086/201] Fix cached mediatags in pads --- www/common/media-tag.js | 2 +- www/common/outer/cache-store.js | 2 ++ www/pad/inner.js | 11 ++++++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/www/common/media-tag.js b/www/common/media-tag.js index c855df328..8f7276446 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -188,7 +188,7 @@ var factory = function () { cfg.download.textDl + ' (' + size + 'MB)'; btn.addEventListener('click', function () { makeProgressBar(cfg, mediaObject); - var a = document.querySelectorAll('media-tag[src="'+mediaObject.tag.getAttribute('src')+'"] button[data-dl]'); + var a = (cfg.body || document).querySelectorAll('media-tag[src="'+mediaObject.tag.getAttribute('src')+'"] button[data-dl]'); for(var i = 0; i < a.length; i++) { if (a[i] !== btn) { a[i].click(); } } diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index 6b6c8604e..cd88de6f6 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -87,5 +87,7 @@ define([ cache.clear(cb); }; + self.CryptPad_clearIndexedDB = S.clear; + return S; }); diff --git a/www/pad/inner.js b/www/pad/inner.js index 8a99b6101..f9b5dffb9 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -463,7 +463,9 @@ define([ setTimeout(function() { // Just in case var tags = dom.querySelectorAll('media-tag:empty'); Array.prototype.slice.call(tags).forEach(function(el) { - var mediaObject = MediaTag(el); + var mediaObject = MediaTag(el, { + body: dom + }); $(el).on('keydown', function(e) { if ([8, 46].indexOf(e.which) !== -1) { $(el).remove(); @@ -473,14 +475,17 @@ define([ var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList') { - var list_values = [].slice.call(el.children); - mediaTagMap[el.getAttribute('src')] = list_values; + var list_values = slice(el.children) + .map(function (el) { return el.outerHTML; }) + .join(''); + mediaMap[el.getAttribute('src')] = list_values; if (mediaObject.complete) { observer.disconnect(); } } }); }); observer.observe(el, { attributes: false, + subtree: true, childList: true, characterData: false }); From 0deaa428a5c8dd09bd54ae5abe7e64308f8bba47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Wed, 2 Dec 2020 13:28:23 +0000 Subject: [PATCH 087/201] Use existing key for empty chainpad error --- www/common/sframe-app-framework.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index d3a4bc174..8776573d7 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -470,8 +470,9 @@ define([ var noCache = false; // Prevent reload loops var onCorruptedCache = function () { if (noCache) { - // XXX translation key - return UI.errorLoadingScreen("Reload loop: empty chainpad for a non-empty channel"); + UI.errorLoadingScreen(Messages.unableToDisplay, false, function () { + common.gotoURL(''); + }); } noCache = true; var sframeChan = common.getSframeChannel(); From 8173eb4a6fb93eef43cefee1758ec8557a3d1a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Wed, 2 Dec 2020 14:16:13 +0000 Subject: [PATCH 088/201] Add "offline" key --- www/common/toolbar.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 925f6aba3..761ecce78 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -1395,7 +1395,8 @@ MessengerUI, Messages) { toolbar.isErrorState = bool; // Stop kickSpinner toolbar.title.toggleClass('cp-toolbar-unsync', bool); // "read only" next to the title if (bool && toolbar.spinner) { - toolbar.spinner.text("OFFLINE"); // XXX + Messages.offline = "OFFLINE"; // XXX + toolbar.spinner.text(Messages.offline); } else { kickSpinner(toolbar, config); } From 656c81b4378689287a3d4f0a86ba9fadbaeac742 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 2 Dec 2020 17:01:43 +0100 Subject: [PATCH 089/201] Fix duplicate mediatags issues in code and slide --- www/common/diffMarked.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index 64768f2cb..3d5704584 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -303,14 +303,22 @@ define([ return renderParagraph(p); }; + // Note: iframe, video and audio are used in mediatags and are allowed in rich text pads. var forbiddenTags = [ 'SCRIPT', - 'IFRAME', + //'IFRAME', 'OBJECT', 'APPLET', - 'VIDEO', // privacy implications of videos are the same as images - 'AUDIO', // same with audio + //'VIDEO', // privacy implications of videos are the same as images + //'AUDIO', // same with audio + 'SOURCE' + ]; + var restrictedTags = [ + 'IFRAME', + 'VIDEO', + 'AUDIO' ]; + var unsafeTag = function (info) { /*if (info.node && $(info.node).parents('media-tag').length) { // Do not remove elements inside a media-tag @@ -347,9 +355,16 @@ define([ parent.removeChild(node); }; + // Only allow iframe, video and audio with local source + var checkSrc = function (root) { + if (restrictedTags.indexOf(root.nodeName.toUpperCase()) === -1) { return true; } + return root.getAttribute && /^(blob\:|\/)/.test(root.getAttribute('src')); + }; + var removeForbiddenTags = function (root) { if (!root) { return; } if (forbiddenTags.indexOf(root.nodeName.toUpperCase()) !== -1) { removeNode(root); } + if (!checkSrc(root)) { removeNode(root); } slice(root.children).forEach(removeForbiddenTags); }; From 1c4ddf6e7bc2ce20908fc0f416bc5965c9a41b84 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 3 Dec 2020 10:33:43 +0100 Subject: [PATCH 090/201] MediaTag UX --- customize.dist/ckeditor-contents.css | 25 +++- .../src/less2/include/markdown.less | 16 +++ www/common/inner/common-mediatag.js | 3 +- www/common/media-tag.js | 110 +++++++++++++++++- 4 files changed, 145 insertions(+), 9 deletions(-) diff --git a/customize.dist/ckeditor-contents.css b/customize.dist/ckeditor-contents.css index a7939839d..663cd8cb9 100644 --- a/customize.dist/ckeditor-contents.css +++ b/customize.dist/ckeditor-contents.css @@ -232,14 +232,35 @@ media-tag button.btn { transition: none; color: #3F4141; border: 1px solid #3F4141; + max-width: 250px; } +media-tag button.mediatag-download-btn { + flex-flow: column; + min-height: 38px; + justify-content: center; +} +media-tag button.mediatag-download-btn > span { + display: flex; + line-height: 1.5; + align-items: center; + justify-content: center; +} +media-tag button.mediatag-download-btn * { + width: auto; +} +media-tag button.mediatag-download-btn > span.mediatag-download-name b { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + media-tag button.btn:hover, media-tag button.btn:active, media-tag button.btn:focus { background-color: #ccc; } -media-tag button b { +media-tag button.btn b { margin-left: 5px; } -media-tag button .fa { +media-tag button.btn .fa { display: inline; margin-right: 5px; } diff --git a/customize.dist/src/less2/include/markdown.less b/customize.dist/src/less2/include/markdown.less index d7fe13f43..af3ac74d8 100644 --- a/customize.dist/src/less2/include/markdown.less +++ b/customize.dist/src/less2/include/markdown.less @@ -94,8 +94,24 @@ height: 80vh; max-height: 90vh; } + button.mediatag-download-btn { + flex-flow: column; + & > span { + display: flex; + line-height: 1.5; + align-items: center; + &.mediatag-download-name b { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + } button.btn-default { display: inline-flex; + max-width: 250px; + min-height: 38px; + justify-content: center; .fa { margin-right: 5px; } diff --git a/www/common/inner/common-mediatag.js b/www/common/inner/common-mediatag.js index 72cb1e37b..eaf640b0d 100644 --- a/www/common/inner/common-mediatag.js +++ b/www/common/inner/common-mediatag.js @@ -23,9 +23,10 @@ define([ viewer: '/common/pdfjs/web/viewer.html' }); Messages.mediatag_saveButton = "Save"; // XXX + Messages.mediatag_loadButton = "Load attachment"; // XXX MediaTag.setDefaultConfig('download', { text: Messages.mediatag_saveButton, - textDl: Messages.download_mt_button, + textDl: Messages.mediatag_loadButton, }); } MT.MediaTag = MediaTag; diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 8f7276446..446dfdf08 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -181,14 +181,17 @@ var factory = function () { mediaObject.tag.appendChild(text); }; var makeDownloadButton = function (cfg, mediaObject, size, cb) { + var metadata = cfg.metadata || {}; + var i = ''; + var name = metadata.name ? ''+ i +''+ + fixHTML(metadata.name)+'' : ''; var btn = document.createElement('button'); - btn.setAttribute('class', 'btn btn-default'); - btn.setAttribute('data-dl', '1'); - btn.innerHTML = '' + - cfg.download.textDl + ' (' + size + 'MB)'; + btn.setAttribute('class', 'btn btn-default mediatag-download-btn'); + btn.innerHTML = name + '' + (name ? '' : i) + + cfg.download.textDl + ' (' + size + 'MB)'; btn.addEventListener('click', function () { makeProgressBar(cfg, mediaObject); - var a = (cfg.body || document).querySelectorAll('media-tag[src="'+mediaObject.tag.getAttribute('src')+'"] button[data-dl]'); + var a = (cfg.body || document).querySelectorAll('media-tag[src="'+mediaObject.tag.getAttribute('src')+'"] button.mediatag-download-btn'); for(var i = 0; i < a.length; i++) { if (a[i] !== btn) { a[i].click(); } } @@ -346,6 +349,95 @@ var factory = function () { } }; + // The metadata size can go up to 65535 (16 bits - 2 bytes) + // The first 8 bits are stored in A[0] + // The last 8 bits are stored in A[0] + var uint8ArrayJoin = function (AA) { + var l = 0; + var i = 0; + for (; i < AA.length; i++) { l += AA[i].length; } + var C = new Uint8Array(l); + + i = 0; + for (var offset = 0; i < AA.length; i++) { + C.set(AA[i], offset); + offset += AA[i].length; + } + return C; + }; + var fetchMetadata = function (src, _cb) { + var cb = function (e, res) { + _cb(e, res); + cb = function () {}; + }; + + var cacheKey = getCacheKey(src); + + var fetch = function () { + var xhr = new XMLHttpRequest(); + xhr.open('GET', src, true); + xhr.setRequestHeader('Range', 'bytes=0-1'); + xhr.responseType = 'arraybuffer'; + + xhr.onerror = function () { return void cb("XHR_ERROR"); }; + xhr.onload = function () { + // Error? + if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); } + var res = new Uint8Array(xhr.response); + var size = Decrypt.decodePrefix(res); + var xhr2 = new XMLHttpRequest(); + + xhr2.open("GET", src, true); + xhr2.setRequestHeader('Range', 'bytes=2-' + (size + 2)); + xhr2.responseType = 'arraybuffer'; + xhr2.onload = function () { + if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); } + var res2 = new Uint8Array(xhr2.response); + var all = uint8ArrayJoin([res, res2]); + cb(void 0, all); + }; + xhr2.send(null); + }; + + xhr.send(null); + }; + + if (!cacheKey) { return void fetch(); } + + getBlobCache(cacheKey, function (err, u8) { + if (err || !u8) { return void fetch(); } + + var size = Decrypt.decodePrefix(u8.subarray(0,2)); + console.error(size); + + cb(null, u8.subarray(0, size+2)); + }); + }; + var decryptMetadata = function (u8, key) { + var prefix = u8.subarray(0, 2); + var metadataLength = Decrypt.decodePrefix(prefix); + + var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength)); + var metaChunk = window.nacl.secretbox.open(metaBox, Decrypt.createNonce(), key); + + try { + return JSON.parse(window.nacl.util.encodeUTF8(metaChunk)); + } + catch (e) { return null; } + }; + var fetchDecryptedMetadata = function (src, strKey, cb) { + if (typeof(src) !== 'string') { + return window.setTimeout(function () { + cb('NO_SOURCE'); + }); + } + fetchMetadata(src, function (e, buffer) { + if (e) { return cb(e); } + var key = Decrypt.getKeyFromStr(strKey); + cb(void 0, decryptMetadata(buffer, key)); + }); + }; + // Decrypts a Uint8Array with the given key. var decrypt = function (u8, strKey, done, progressCb) { var Nacl = window.nacl; @@ -608,6 +700,7 @@ var factory = function () { if (errDecryption) { return void reject(errDecryption); } + // XXX emit 'metadata' u8Decrypted.metadata // Cache and display the decrypted blob resolve(u8Decrypted); }, function (progress) { @@ -654,7 +747,12 @@ var factory = function () { return void dl(); } var sizeMb = Math.round(10 * size / 1024 / 1024) / 10; - makeDownloadButton(cfg, mediaObject, sizeMb, dl); + fetchDecryptedMetadata(src, strKey, function (err, md) { + if (err) { return void error(err); } + cfg.metadata = md; + // XXX emit 'metadata' + makeDownloadButton(cfg, mediaObject, sizeMb, dl); + }); }); return mediaObject; From 43ad4f0a841be3ba34270e7dffa039bd2e1be4aa Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 3 Dec 2020 15:22:39 +0100 Subject: [PATCH 091/201] Deduplicate code between File and MediaTag --- .../src/less2/include/markdown.less | 45 ++--- .../src/less2/include/modals-ui-elements.less | 2 + www/common/common-thumbnail.js | 6 +- www/common/media-tag.js | 36 ++-- www/file/app-file.less | 5 + www/file/file-crypto.js | 66 -------- www/file/inner.html | 1 - www/file/inner.js | 160 ++++-------------- www/pad/inner.js | 2 +- 9 files changed, 89 insertions(+), 234 deletions(-) diff --git a/customize.dist/src/less2/include/markdown.less b/customize.dist/src/less2/include/markdown.less index af3ac74d8..f88d8ca7c 100644 --- a/customize.dist/src/less2/include/markdown.less +++ b/customize.dist/src/less2/include/markdown.less @@ -64,26 +64,7 @@ } } -.markdown_cryptpad() { - word-wrap: break-word; - - h1, h2, h3, h4, h5, h6 { - font-weight: bold; - padding-bottom: 0.3em; - border-bottom: 1px solid #eee; - } - li { - min-height: 22px; - } - .todo-list-item { - list-style: none; - position: relative; - .fa { - position: absolute; - margin-left: -17px; - margin-top: 4px; - } - } +.mediatag_cryptpad() { media-tag { cursor: pointer; * { @@ -126,6 +107,30 @@ display: inline-block; border: 1px solid #BBB; } +} + +.markdown_cryptpad() { + word-wrap: break-word; + + h1, h2, h3, h4, h5, h6 { + font-weight: bold; + padding-bottom: 0.3em; + border-bottom: 1px solid #eee; + } + li { + min-height: 22px; + } + .todo-list-item { + list-style: none; + position: relative; + .fa { + position: absolute; + margin-left: -17px; + margin-top: 4px; + } + } + + .mediatag_cryptpad(); pre.markmap { border: 1px solid #ddd; diff --git a/customize.dist/src/less2/include/modals-ui-elements.less b/customize.dist/src/less2/include/modals-ui-elements.less index 27eb233da..ff40729a6 100644 --- a/customize.dist/src/less2/include/modals-ui-elements.less +++ b/customize.dist/src/less2/include/modals-ui-elements.less @@ -1,6 +1,7 @@ @import (reference) "./colortheme-all.less"; @import (reference) "./variables.less"; @import (reference) "./browser.less"; +@import (reference) "./markdown.less"; .modals-ui-elements_main() { --LessLoader_require: LessLoader_currentFile(); @@ -214,6 +215,7 @@ flex: 1; min-width: 0; overflow: auto; + .mediatag_cryptpad(); media-tag { & > * { max-width: 100%; diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js index 8c78d9d6d..012c2c9b4 100644 --- a/www/common/common-thumbnail.js +++ b/www/common/common-thumbnail.js @@ -3,9 +3,9 @@ define([ '/common/common-util.js', '/common/visible.js', '/common/common-hash.js', - '/file/file-crypto.js', + '/common/media-tag.js', '/bower_components/tweetnacl/nacl-fast.min.js', -], function ($, Util, Visible, Hash, FileCrypto) { +], function ($, Util, Visible, Hash, MediaTag) { var Nacl = window.nacl; var Thumb = { dimension: 100, @@ -314,7 +314,7 @@ define([ var hexFileName = secret.channel; var src = fileHost + Hash.getBlobPathFromHex(hexFileName); var key = secret.keys && secret.keys.cryptKey; - FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) { + MediaTag.fetchDecryptedMetadata(src, key, function (e, metadata) { if (e) { if (e === 'XHR_ERROR') { return; } return console.error(e); diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 446dfdf08..a00377ff4 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -125,7 +125,6 @@ var factory = function () { }; var makeProgressBar = function (cfg, mediaObject) { - // XXX CSP: we'll need to add style in cryptpad's less if (mediaObject.bar) { return; } mediaObject.bar = true; var style = (function(){/* @@ -151,10 +150,10 @@ var factory = function () { } .mediatag-progress-text { height: 25px; + width: 50px; margin-left: 5px; line-height: 25px; vertical-align: top; - width: auto; display: inline-block; color: #3F4141; font-weight: bold; @@ -618,6 +617,7 @@ var factory = function () { var handlers = cfg.handlers || { 'progress': [], 'complete': [], + 'metadata': [], 'error': [] }; @@ -700,8 +700,7 @@ var factory = function () { if (errDecryption) { return void reject(errDecryption); } - // XXX emit 'metadata' u8Decrypted.metadata - // Cache and display the decrypted blob + emit('metadata', u8Decrypted.metadata); resolve(u8Decrypted); }, function (progress) { emit('progress', { @@ -736,21 +735,18 @@ var factory = function () { var maxSize = typeof(config.maxDownloadSize) === "number" ? config.maxDownloadSize : (5 * 1024 * 1024); - getFileSize(src, function (err, size) { - if (err) { - return void error(err); - } - // If the size is smaller than the autodownload limit, load the blob. - // If the blob is already loaded or being loaded, don't show the button. - if (!size || size < maxSize || getCache()) { - makeProgressBar(cfg, mediaObject); - return void dl(); - } - var sizeMb = Math.round(10 * size / 1024 / 1024) / 10; - fetchDecryptedMetadata(src, strKey, function (err, md) { - if (err) { return void error(err); } - cfg.metadata = md; - // XXX emit 'metadata' + fetchDecryptedMetadata(src, strKey, function (err, md) { + if (err) { return void error(err); } + cfg.metadata = md; + emit('metadata', md); + getFileSize(src, function (err, size) { + // If the size is smaller than the autodownload limit, load the blob. + // If the blob is already loaded or being loaded, don't show the button. + if (!size || size < maxSize || getCache()) { + makeProgressBar(cfg, mediaObject); + return void dl(); + } + var sizeMb = Math.round(10 * size / 1024 / 1024) / 10; makeDownloadButton(cfg, mediaObject, sizeMb, dl); }); }); @@ -765,6 +761,8 @@ var factory = function () { config[key] = value; }; + init.fetchDecryptedMetadata = fetchDecryptedMetadata; + return init; }; diff --git a/www/file/app-file.less b/www/file/app-file.less index d6e8ad69c..be7e067f1 100644 --- a/www/file/app-file.less +++ b/www/file/app-file.less @@ -1,5 +1,6 @@ @import (reference) '../../customize/src/less2/include/tokenfield.less'; @import (reference) '../../customize/src/less2/include/framework.less'; +@import (reference) '../../customize/src/less2/include/markdown.less'; &.cp-app-file { @@ -47,6 +48,7 @@ z-index: -1; } + .mediatag_cryptpad(); media-tag { img { max-width: 100%; @@ -198,6 +200,9 @@ max-height: 100%; max-width: 100%; } + &:empty { + display: none !important; + } } } diff --git a/www/file/file-crypto.js b/www/file/file-crypto.js index 12453bb48..6a0c08816 100644 --- a/www/file/file-crypto.js +++ b/www/file/file-crypto.js @@ -48,69 +48,6 @@ define([ return new Blob(chunks); }; - var concatBuffer = function (a, b) { // TODO make this not so ugly - return new Uint8Array(slice(a).concat(slice(b))); - }; - - var fetchMetadata = function (src, cb) { - var done = false; - var CB = function (err, res) { - if (done) { return; } - done = true; - cb(err, res); - }; - - var xhr = new XMLHttpRequest(); - xhr.open("GET", src, true); - xhr.setRequestHeader('Range', 'bytes=0-1'); - xhr.responseType = 'arraybuffer'; - - xhr.onerror= function () { return CB('XHR_ERROR'); }; - xhr.onload = function () { - if (/^4/.test('' + this.status)) { return CB('XHR_ERROR'); } - var res = new Uint8Array(xhr.response); - var size = decodePrefix(res); - var xhr2 = new XMLHttpRequest(); - - xhr2.open("GET", src, true); - xhr2.setRequestHeader('Range', 'bytes=2-' + (size + 2)); - xhr2.responseType = 'arraybuffer'; - xhr2.onload = function () { - if (/^4/.test('' + this.status)) { return CB('XHR_ERROR'); } - var res2 = new Uint8Array(xhr2.response); - var all = concatBuffer(res, res2); - CB(void 0, all); - }; - xhr2.send(null); - }; - xhr.send(null); - }; - - var decryptMetadata = function (u8, key) { - var prefix = u8.subarray(0, 2); - var metadataLength = decodePrefix(prefix); - - var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength)); - var metaChunk = Nacl.secretbox.open(metaBox, createNonce(), key); - - try { - return JSON.parse(Nacl.util.encodeUTF8(metaChunk)); - } - catch (e) { return null; } - }; - - var fetchDecryptedMetadata = function (src, key, cb) { - if (typeof(src) !== 'string') { - return window.setTimeout(function () { - cb('NO_SOURCE'); - }); - } - fetchMetadata(src, function (e, buffer) { - if (e) { return cb(e); } - cb(void 0, decryptMetadata(buffer, key)); - }); - }; - var decrypt = function (u8, key, done, progress) { var MAX = u8.length; var _progress = function (offset) { @@ -268,8 +205,5 @@ define([ encrypt: encrypt, joinChunks: joinChunks, computeEncryptedSize: computeEncryptedSize, - decryptMetadata: decryptMetadata, - fetchMetadata: fetchMetadata, - fetchDecryptedMetadata: fetchDecryptedMetadata, }; }); diff --git a/www/file/inner.html b/www/file/inner.html index a3e5070e4..45ba831fe 100644 --- a/www/file/inner.html +++ b/www/file/inner.html @@ -16,7 +16,6 @@
- diff --git a/www/file/inner.js b/www/file/inner.js index 6cc129de5..59291c964 100644 --- a/www/file/inner.js +++ b/www/file/inner.js @@ -43,7 +43,6 @@ define([ var andThen = function (common) { var $appContainer = $('#cp-app-file-content'); var $form = $('#cp-app-file-upload-form'); - var $dlform = $('#cp-app-file-download-form'); var $dlview = $('#cp-app-file-download-view'); var $label = $form.find('label'); var $bar = $('.cp-toolbar-container'); @@ -86,36 +85,44 @@ define([ if (!uploadMode) { (function () { - Messages.download = "Download"; // XXX - Messages.decrypt = "Decrypt"; // XXX - - var progress = h('div.cp-app-file-progress'); - var progressTxt = h('span.cp-app-file-progress-txt'); - var $progress = $(progress); - var $progressTxt = $(progressTxt); - var downloadEl = h('span.cp-app-file-progress-dl', Messages.download); - var decryptEl = h('span.cp-app-file-progress-dc', Messages.decrypt); - var progressContainer = h('div.cp-app-file-progress-container', [ - downloadEl, - decryptEl, - progress - ]); - var hexFileName = secret.channel; var src = fileHost + Hash.getBlobPathFromHex(hexFileName); var key = secret.keys && secret.keys.cryptKey; var cryptKey = Nacl.util.encodeBase64(key); - FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) { - if (e) { - if (e === 'XHR_ERROR') { - return void UI.errorLoadingScreen(Messages.download_resourceNotAvailable, false, function () { - common.gotoURL('/file/'); - }); - } - return void console.error(e); + var $mt = $dlview.find('media-tag'); + $mt.attr('src', src); + $mt.attr('data-crypto-key', 'cryptpad:'+cryptKey); + $mt.css('transform', 'scale(2)'); + + var rightsideDisplayed = false; + var metadataReceived = false; + UI.removeLoadingScreen(); + $dlview.show(); + + MediaTag($mt[0]).on('complete', function (decrypted) { + $mt.css('transform', ''); + if (!rightsideDisplayed) { + toolbar.$drawer + .append(common.createButton('export', true, {}, function () { + saveAs(decrypted.content, decrypted.metadata.name); + })); + rightsideDisplayed = true; } + // make pdfs big + var toolbarHeight = $('#cp-toolbar').height(); + $('media-tag iframe').css({ + 'height': 'calc(100vh - ' + toolbarHeight + 'px)', + 'width': '100vw', + 'position': 'absolute', + 'bottom': 0, + 'left': 0, + 'border': 0 + }); + }).on('metadata', function (metadata) { + if (metadataReceived) { return; } + metadataReceived = true; // Add pad attributes when the file is saved in the drive Title.onTitleChange(function () { var owners = metadata.owners; @@ -150,106 +157,11 @@ define([ toolbar.$drawer.append(common.createButton('hashtag', true)); } toolbar.$file.show(); - - var displayFile = function (ev, sizeMb, CB) { - var called_back; - var cb = function (e) { - if (called_back) { return; } - called_back = true; - if (CB) { CB(e); } - }; - - var $mt = $dlview.find('media-tag'); - $mt.attr('src', src); - $mt.attr('data-crypto-key', 'cryptpad:'+cryptKey); - - var rightsideDisplayed = false; - - MediaTag($mt[0], { - force: true // Download starts automatically - }).on('complete', function (decrypted) { - $dlview.show(); - $dlform.hide(); - var $dlButton = $dlview.find('media-tag button'); - if (ev) { $dlButton.click(); } - - if (!rightsideDisplayed) { - toolbar.$drawer - .append(common.createButton('export', true, {}, function () { - saveAs(decrypted.content, decrypted.metadata.name); - })); - rightsideDisplayed = true; - } - - // make pdfs big - var toolbarHeight = $('#cp-toolbar').height(); - var $another_iframe = $('media-tag iframe').css({ - 'height': 'calc(100vh - ' + toolbarHeight + 'px)', - 'width': '100vw', - 'position': 'absolute', - 'bottom': 0, - 'left': 0, - 'border': 0 - }); - - if ($another_iframe.length) { - $another_iframe.load(function () { - cb(); - }); - } else { - cb(); - } - }).on('progress', function (data) { - var p = data.progress +'%'; - $progress.width(p); - $progressTxt.text(Math.floor(data.progress) + '%'); - }).on('error', function (err) { - console.error(err); - }); - }; - - // XXX Update "download_button" key to use "download" first and "decrypt" second - var todoBigFile = function (sizeMb) { - $dlform.show(); - UI.removeLoadingScreen(); - var button = h('button.btn.btn-primary', { - title: Messages.download_button - }, Messages.download_button); - $dlform.append([ - h('h2', Util.fixHTML(metadata.name)), - h('div.cp-button-container', [ - button, - progressTxt - ]), - ]); - - // don't display the size if you don't know it. - if (typeof(sizeMb) === 'number') { - $dlform.find('h2').append(' - ' + - Messages._getKey('formattedMB', [sizeMb])); - } - var decrypting = false; - var onClick = function (ev) { - if (decrypting) { return; } - decrypting = true; - $(button).prop('disabled', 'disabled'); - $dlform.append(progressContainer); - displayFile(ev, sizeMb, function (err) { - $appContainer.css('background-color', - common.getAppConfig().appBackgroundColor); - if (err) { UI.alert(err); } - }); - }; - if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); } - $(button).click(onClick); - }; - common.getFileSize(hexFileName, function (e, data) { - if (e) { - return void UI.errorLoadingScreen(e); - } - var size = Util.bytesToMegabytes(data); - return void todoBigFile(size); - }); + }).on('error', function (err) { + $appContainer.css('background-color', + common.getAppConfig().appBackgroundColor); + UI.warn(Messages.error); + console.error(err); }); })(); return; diff --git a/www/pad/inner.js b/www/pad/inner.js index f9b5dffb9..a22c0d81d 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -478,7 +478,7 @@ define([ var list_values = slice(el.children) .map(function (el) { return el.outerHTML; }) .join(''); - mediaMap[el.getAttribute('src')] = list_values; + mediaTagMap[el.getAttribute('src')] = list_values; if (mediaObject.complete) { observer.disconnect(); } } }); From 7b4a89be92b7c8fbe41cc212c86bf6eb74304209 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 3 Dec 2020 15:36:29 +0100 Subject: [PATCH 092/201] Handle cached mediatags in preview modal --- customize.dist/src/less2/include/markdown.less | 3 +++ www/common/inner/common-mediatag.js | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/customize.dist/src/less2/include/markdown.less b/customize.dist/src/less2/include/markdown.less index f88d8ca7c..677ec13a3 100644 --- a/customize.dist/src/less2/include/markdown.less +++ b/customize.dist/src/less2/include/markdown.less @@ -66,6 +66,9 @@ .mediatag_cryptpad() { media-tag { + &:empty { + display: none !important; + } cursor: pointer; * { max-width: 100%; diff --git a/www/common/inner/common-mediatag.js b/www/common/inner/common-mediatag.js index eaf640b0d..bbc149d5b 100644 --- a/www/common/inner/common-mediatag.js +++ b/www/common/inner/common-mediatag.js @@ -249,7 +249,6 @@ define([ var locked = false; var show = function (_i) { if (locked) { return; } - locked = true; if (_i < 0) { i = 0; } else if (_i > tags.length -1) { i = tags.length - 1; } else { i = _i; } @@ -293,7 +292,6 @@ define([ if (_key) { key = 'cryptpad:' + Nacl.util.encodeBase64(_key); } } if (!src || !key) { - locked = false; $spinner.hide(); return void UI.log(Messages.error); } @@ -307,13 +305,18 @@ define([ locked = false; $spinner.hide(); UI.log(Messages.error); + }).on('progress', function () { + $spinner.hide(); + locked = true; + }).on('complete', function () { + locked = false; + $spinner.hide(); }); }); } var observer = new MutationObserver(function(mutations) { mutations.forEach(function() { - locked = false; $spinner.hide(); }); }); From a857a0e3d4e2f6b14cd45c251111ec4bb9a6e475 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 3 Dec 2020 15:50:20 +0100 Subject: [PATCH 093/201] Fix upload table z-index --- customize.dist/src/less2/include/fileupload.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customize.dist/src/less2/include/fileupload.less b/customize.dist/src/less2/include/fileupload.less index 8fb1c8857..0353b06eb 100644 --- a/customize.dist/src/less2/include/fileupload.less +++ b/customize.dist/src/less2/include/fileupload.less @@ -14,7 +14,7 @@ right: 10vw; bottom: 10vh; box-sizing: border-box; - z-index: 100000; //Z file upload table container + z-index: 100001; //Z file upload table container: just above the file picker display: none; color: darken(@colortheme_drive-bg, 10%); max-height: 180px; From 5f67038dad12d1069b955681882d3eff2bd68014 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 4 Dec 2020 11:15:22 +0100 Subject: [PATCH 094/201] Provide pathname to sframe-common-outer --- www/common/sframe-common-outer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index d17924cf0..e008681fc 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -8,7 +8,7 @@ define([ ], function (nThen, ApiConfig, RequireConfig, Messages, $) { var common = {}; - common.initIframe = function (waitFor, isRt) { + common.initIframe = function (waitFor, isRt, pathname) { var requireConfig = RequireConfig(); var lang = Messages._languageUsed; var req = { @@ -31,7 +31,7 @@ define([ } document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + window.location.pathname + 'inner.html?' + + ApiConfig.httpSafeOrigin + (pathname || window.location.pathname) + 'inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); // This is a cheap trick to avoid loading sframe-channel in parallel with the From 20e96d56dfb5c902b0a5fd944d55252bf81f13b8 Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 4 Dec 2020 14:21:09 +0100 Subject: [PATCH 095/201] Translated using Weblate (English) Currently translated at 100.0% (1399 of 1399 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1399 of 1399 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1398 of 1398 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1398 of 1398 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1397 of 1397 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1396 of 1396 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1395 of 1395 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1394 of 1394 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1393 of 1393 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1392 of 1392 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1391 of 1391 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1390 of 1390 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1389 of 1389 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1388 of 1388 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1388 of 1388 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1387 of 1387 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1386 of 1386 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1385 of 1385 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1384 of 1384 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1383 of 1383 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1382 of 1382 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1381 of 1381 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1380 of 1380 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1379 of 1379 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1378 of 1378 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1377 of 1377 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1376 of 1376 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ --- www/common/translations/messages.json | 32 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index 3657d6b58..3e7869ee2 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -43,7 +43,7 @@ "saved": "Saved", "synced": "Everything is saved", "deleted": "Deleted", - "deletedFromServer": "Pad deleted from the server", + "deletedFromServer": "Document destroyed", "mustLogin": "You must be logged in to access this page", "disabledApp": "This application has been disabled. Contact the administrator of this CryptPad for more information.", "realtime_unrecoverableError": "An unrecoverable error has occured. Click OK to reload.", @@ -571,7 +571,7 @@ "upload_notEnoughSpace": "There is not enough space for this file in your CryptDrive.", "upload_notEnoughSpaceBrief": "Not enough space", "upload_tooLarge": "This file exceeds the maximum upload size allowed for your account.", - "upload_tooLargeBrief": "File too large", + "upload_tooLargeBrief": "File exceeds the {0}MB limit", "upload_choose": "Choose a file", "upload_pending": "Pending", "upload_cancelled": "Cancelled", @@ -1470,6 +1470,30 @@ "tag_edit": "Edit", "error_unhelpfulScriptError": "Script Error: See browser console for details", "documentID": "Document identifier", - "unableToDisplay": "Unable to display the document. Please press Esc to reload the page.", - "errorPopupBlocked": "CryptPad needs to be able to open new tabs to operate. Please allow popup windows in your browser's address bar. These windows will never be used to show you advertising." + "unableToDisplay": "Unable to display the document. Please press Esc to reload the page. If the problem persists, please contact support.", + "errorPopupBlocked": "CryptPad needs to be able to open new tabs to operate. Please allow popup windows in your browser's address bar. These windows will never be used to show you advertising.", + "braveWarning": "It seems you are currently using the Brave browser. We have received reports of users being unable to confirm the payment form due to strict privacy settings on this browser. If the payment button does not work, you can try with a different browser or contact us with the Support form to find an alternative solution.", + "admin_archiveTitle": "Archive documents", + "admin_archiveHint": "Make a document unavailable without deleting it permanently. It will be placed in an 'archive' directory and deleted after a few days (configurable in the server configuration file).", + "admin_archiveButton": "Archive", + "admin_unarchiveTitle": "Restore documents", + "admin_unarchiveHint": "Restore a document that had previously been archived", + "admin_unarchiveButton": "Restore", + "admin_archiveInput": "Document URL", + "admin_archiveInput2": "Document password", + "admin_archiveInval": "Invalid document", + "restoredFromServer": "Document restored", + "archivedFromServer": "Document archived", + "allowNotifications": "Allow notifications", + "fileTableHeader": "Downloads and uploads", + "download_zip": "Building ZIP file...", + "download_zip_file": "File {0}/{1}", + "Offline": "Offline", + "mediatag_saveButton": "Save", + "pad_mediatagShare": "Share file", + "pad_mediatagOpen": "Open file", + "mediatag_notReady": "Please complete the download", + "settings_mediatagSizeTitle": "Automatic download limit", + "settings_mediatagSizeHint": "Maximum size in megabytes (MB) for automatically loading media elements (images, videos, pdf) embedded into documents. Elements bigger than the specified size can be loaded manually. Use \"-1\" to always load the media elements automatically.", + "mediatag_loadButton": "Load attachment" } From c8411d389c8d0e4791b6ea1cc8e942ee1657914e Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 4 Dec 2020 14:21:10 +0100 Subject: [PATCH 096/201] Translated using Weblate (French) Currently translated at 100.0% (1399 of 1399 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ Translated using Weblate (French) Currently translated at 100.0% (1398 of 1398 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ --- www/common/translations/messages.fr.json | 32 +++++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index 195814bf5..ef5bfe47d 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -41,7 +41,7 @@ "saved": "Enregistré", "synced": "Tout est enregistré", "deleted": "Supprimé", - "deletedFromServer": "Pad supprimé du serveur", + "deletedFromServer": "Document supprimé", "mustLogin": "Vous devez être enregistré pour avoir accès à cette page", "disabledApp": "Cette application a été désactivée. Pour plus d'information, veuillez contacter l'administrateur de ce CryptPad.", "realtime_unrecoverableError": "Une erreur critique est survenue. Cliquez sur OK pour recharger la page.", @@ -554,7 +554,7 @@ "upload_notEnoughSpace": "Il n'y a pas assez d'espace libre dans votre CryptDrive pour ce fichier.", "upload_notEnoughSpaceBrief": "Pas assez d'espace", "upload_tooLarge": "Ce fichier dépasse la taille maximale autorisée pour votre compte.", - "upload_tooLargeBrief": "Fichier trop volumineux", + "upload_tooLargeBrief": "Le fichier dépasse la limite de {0} Mo", "upload_choose": "Choisir un fichier", "upload_pending": "En attente", "upload_cancelled": "Annulé", @@ -1470,6 +1470,30 @@ "tag_add": "Ajouter", "error_unhelpfulScriptError": "Erreur de script : consultez la console du navigateur pour plus de détails", "documentID": "Référence du document", - "unableToDisplay": "Impossible d'afficher le document. Veuillez recharger la page avec la touche Échap.", - "errorPopupBlocked": "CryptPad doit pouvoir ouvrir de nouveaux onglets pour fonctionner. Veuillez autoriser les fenêtres pop-up dans la barre d'adresse de votre navigateur. Ces fenêtres ne seront jamais utilisées pour vous montrer de la publicité." + "unableToDisplay": "Impossible d'afficher le document. Veuillez recharger la page avec la touche Échap. Si le problème persiste, veuillez contacter le support.", + "errorPopupBlocked": "CryptPad doit pouvoir ouvrir de nouveaux onglets pour fonctionner. Veuillez autoriser les fenêtres pop-up dans la barre d'adresse de votre navigateur. Ces fenêtres ne seront jamais utilisées pour vous montrer de la publicité.", + "settings_mediatagSizeHint": "Taille maximale en mégaoctets (Mo) pour le chargement automatique des pièces jointes (images, vidéos, pdf) intégrés dans les documents. Les pièces jointes dont la taille est supérieure à la taille spécifiée peuvent être chargés manuellement. Utilisez \"-1\" pour toujours charger automatiquement les pièces jointes.", + "settings_mediatagSizeTitle": "Limite de téléchargement automatique", + "mediatag_notReady": "Merci de compléter le téléchargement", + "pad_mediatagOpen": "Ouvrir ce fichier", + "pad_mediatagShare": "Partager ce fichier", + "mediatag_saveButton": "Sauvegarder", + "Offline": "Déconnecté", + "download_zip_file": "Fichier {0}/{1}", + "download_zip": "Construction du fichier ZIP...", + "fileTableHeader": "Téléchargements et imports", + "allowNotifications": "Autoriser les notifications", + "archivedFromServer": "Document archivé", + "restoredFromServer": "Document restauré", + "admin_archiveInval": "Document invalide", + "admin_archiveInput2": "Mot de passe du document", + "admin_archiveInput": "URL du document", + "admin_unarchiveButton": "Restaurer", + "admin_unarchiveHint": "Restaurer un document qui avait été précédemment archivé", + "admin_unarchiveTitle": "Restaurer les documents", + "admin_archiveButton": "Archiver", + "admin_archiveHint": "Rendre un document indisponible sans le supprimer définitivement. Il sera placé dans un répertoire \"archive\" et supprimé après quelques jours (configurable dans le fichier de configuration du serveur).", + "admin_archiveTitle": "Archiver les documents", + "braveWarning": "Il semble que vous utilisiez actuellement le navigateur Brave. Nous avons reçu des rapports d'utilisateurs ne pouvant pas confirmer le formulaire de paiement en raison de paramètres de confidentialité stricts sur ce navigateur. Si le bouton de paiement ne fonctionne pas, vous pouvez essayer avec un autre navigateur ou nous contacter avec le formulaire de support pour trouver une solution alternative.", + "mediatag_loadButton": "Charger la pièce jointe" } From cca06d252121bd3233f06c17b8d9f19aaa65f6c5 Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 4 Dec 2020 14:21:10 +0100 Subject: [PATCH 097/201] Translated using Weblate (German) Currently translated at 100.0% (1375 of 1375 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/de/ --- www/common/translations/messages.de.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.de.json b/www/common/translations/messages.de.json index fe873ea34..f7e9ce178 100644 --- a/www/common/translations/messages.de.json +++ b/www/common/translations/messages.de.json @@ -1469,5 +1469,7 @@ "loading_state_0": "Oberfläche vorbereiten", "loading_state_5": "Dokument rekonstruieren", "error_unhelpfulScriptError": "Skriptfehler: Siehe Konsole im Browser für Details", - "documentID": "Kennung des Dokuments" + "documentID": "Kennung des Dokuments", + "errorPopupBlocked": "Für die Funktionsweise von CryptPad ist es erforderlich, dass neue Tabs geöffnet werden können. Bitte erlaube Pop-up-Fenster in der Adressleiste deines Browsers. Diese Fenster werden niemals dafür verwendet, dir Werbung anzuzeigen.", + "unableToDisplay": "Das Dokument kann nicht angezeigt werden. Drücke ESC, um die Seite neu zu laden." } From 40093cf9a8ef5cb3a699934cb56e7ad52e3b97fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Fri, 4 Dec 2020 13:41:45 +0000 Subject: [PATCH 098/201] Remove XXX related to translation keys --- www/admin/inner.js | 13 ------------- www/common/toolbar.js | 1 - 2 files changed, 14 deletions(-) diff --git a/www/admin/inner.js b/www/admin/inner.js index 7b88fcbdf..c1f2ccb9d 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -109,19 +109,6 @@ define([ }); return $div; }; - Messages.admin_archiveTitle = "Archive documents"; // XXX - Messages.admin_archiveHint = "Make a document unavailable without deleting it permanently. It will be placed in an 'archive' directory and deleted after a few days (configurable in the server configuration file)."; // XXX - Messages.admin_archiveButton = "Archive"; - - Messages.admin_unarchiveTitle = "Restore archived documents"; // XXX - Messages.admin_unarchiveHint = "Restore a document that has previously been archived"; - Messages.admin_unarchiveButton = "Restore"; - - Messages.admin_archiveInput = "Document URL"; - Messages.admin_archiveInput2 = "Document password"; - Messages.admin_archiveInval = "Invalid document"; - Messages.restoredFromServer = "Document restored"; - Messages.archivedFromServer = "Document archived"; var archiveForm = function (archive, $div, $button) { var label = h('label', { for: 'cp-admin-archive' }, Messages.admin_archiveInput); diff --git a/www/common/toolbar.js b/www/common/toolbar.js index c012d2028..4b72187ed 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -994,7 +994,6 @@ MessengerUI, Messages) { var metadataMgr = config.metadataMgr; var privateData = metadataMgr.getPrivateData(); if (!privateData.notifications) { - Messages.allowNotifications = "Allow notifications"; // XXX var allowNotif = h('div.cp-notifications-gotoapp', h('p', Messages.allowNotifications)); pads_options.unshift(h("hr")); pads_options.unshift(allowNotif); From f816a54cd47c2e569e35262af1ac3f10d403fff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Fri, 4 Dec 2020 13:54:17 +0000 Subject: [PATCH 099/201] Remove XXX related to translation keys --- www/common/inner/common-mediatag.js | 6 ------ www/common/sframe-common-file.js | 3 --- www/common/toolbar.js | 1 - www/settings/inner.js | 2 -- www/whiteboard/inner.js | 2 +- 5 files changed, 1 insertion(+), 13 deletions(-) diff --git a/www/common/inner/common-mediatag.js b/www/common/inner/common-mediatag.js index bbc149d5b..80195c71c 100644 --- a/www/common/inner/common-mediatag.js +++ b/www/common/inner/common-mediatag.js @@ -22,8 +22,6 @@ define([ MediaTag.setDefaultConfig('pdf', { viewer: '/common/pdfjs/web/viewer.html' }); - Messages.mediatag_saveButton = "Save"; // XXX - Messages.mediatag_loadButton = "Load attachment"; // XXX MediaTag.setDefaultConfig('download', { text: Messages.mediatag_saveButton, textDl: Messages.mediatag_loadButton, @@ -373,10 +371,6 @@ define([ }); }; - Messages.pad_mediatagShare = "Share file"; // XXX - Messages.pad_mediatagOpen = "Open file"; // XXX - Messages.mediatag_notReady = "Not ready"; // XXX - var mediatagContextMenu; MT.importMediaTagMenu = function (common) { if (mediatagContextMenu) { return mediatagContextMenu; } diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index c10af2770..7b8601f13 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -47,7 +47,6 @@ define([ return 'cp-fileupload-element-' + String(Math.random()).substring(2); }; - Messages.fileTableHeader = "Downloads and uploads"; // XXX var tableHeader = h('div.cp-fileupload-header', [ h('div.cp-fileupload-header-title', h('span', Messages.fileTableHeader)), h('div.cp-fileupload-header-close', h('span.fa.fa-times')), @@ -626,8 +625,6 @@ define([ * As updateDLProgress but for folders * @param {number} progressValue Progression of download, between 0 and 1 */ - Messages.download_zip = "Building ZIP file..."; // XXX - Messages.download_zip_file = "File {0}/{1}"; // XXX var updateProgress = function (progressValue) { var text = Math.round(progressValue*100) + '%'; if (Array.isArray(data.list)) { diff --git a/www/common/toolbar.js b/www/common/toolbar.js index e1555279e..20cac8337 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -1394,7 +1394,6 @@ MessengerUI, Messages) { toolbar.isErrorState = bool; // Stop kickSpinner toolbar.title.toggleClass('cp-toolbar-unsync', bool); // "read only" next to the title if (bool && toolbar.spinner) { - Messages.offline = "OFFLINE"; // XXX toolbar.spinner.text(Messages.offline); } else { kickSpinner(toolbar, config); diff --git a/www/settings/inner.js b/www/settings/inner.js index 9051d3c34..8b5095cba 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -577,8 +577,6 @@ define([ cb(form); }, true); - Messages.settings_mediatagSizeTitle = "Autodownload size in MegaBytes (MB)"; // XXX - Messages.settings_mediatagSizeHint = 'Maximum size for automatically loading media elements (images, videos, pdf) embedded into the pads. Elements bigger than the specified size can be loaded manually. Use "-1" to always load the media elements automatically.'; // XXX makeBlock('mediatag-size', function(cb) { var $inputBlock = $('
', { 'class': 'cp-sidebarlayout-input-block', diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js index bab1603dc..30c43aef1 100644 --- a/www/whiteboard/inner.js +++ b/www/whiteboard/inner.js @@ -335,7 +335,7 @@ define([ var maxSizeStr = Messages._getKey('formattedMB', [Util.bytesToMegabytes(MAX_IMAGE_SIZE)]); var addImageToCanvas = function (img) { if (img.src && img.src.length > MAX_IMAGE_SIZE) { - UI.warn(Messages._getKey('upload_tooLargeBrief', [maxSizeStr])); // XXX update key + UI.warn(Messages._getKey('upload_tooLargeBrief', [maxSizeStr])); return; } var w = img.width; From 0b4c8678dd6b80fab65aff4ac49aff474d187c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Fri, 4 Dec 2020 15:32:56 +0000 Subject: [PATCH 100/201] Remove bravewarning key --- www/common/translations/messages.json | 1 - 1 file changed, 1 deletion(-) diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index 3e7869ee2..f5acaa6c8 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -1472,7 +1472,6 @@ "documentID": "Document identifier", "unableToDisplay": "Unable to display the document. Please press Esc to reload the page. If the problem persists, please contact support.", "errorPopupBlocked": "CryptPad needs to be able to open new tabs to operate. Please allow popup windows in your browser's address bar. These windows will never be used to show you advertising.", - "braveWarning": "It seems you are currently using the Brave browser. We have received reports of users being unable to confirm the payment form due to strict privacy settings on this browser. If the payment button does not work, you can try with a different browser or contact us with the Support form to find an alternative solution.", "admin_archiveTitle": "Archive documents", "admin_archiveHint": "Make a document unavailable without deleting it permanently. It will be placed in an 'archive' directory and deleted after a few days (configurable in the server configuration file).", "admin_archiveButton": "Archive", From 7fe89921cb90643820de412e89d60327032aad12 Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 4 Dec 2020 16:33:33 +0100 Subject: [PATCH 101/201] Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/ --- www/common/translations/messages.fr.json | 1 - 1 file changed, 1 deletion(-) diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index ef5bfe47d..47980ee0d 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -1494,6 +1494,5 @@ "admin_archiveButton": "Archiver", "admin_archiveHint": "Rendre un document indisponible sans le supprimer définitivement. Il sera placé dans un répertoire \"archive\" et supprimé après quelques jours (configurable dans le fichier de configuration du serveur).", "admin_archiveTitle": "Archiver les documents", - "braveWarning": "Il semble que vous utilisiez actuellement le navigateur Brave. Nous avons reçu des rapports d'utilisateurs ne pouvant pas confirmer le formulaire de paiement en raison de paramètres de confidentialité stricts sur ce navigateur. Si le bouton de paiement ne fonctionne pas, vous pouvez essayer avec un autre navigateur ou nous contacter avec le formulaire de support pour trouver une solution alternative.", "mediatag_loadButton": "Charger la pièce jointe" } From 69664dc0ef87963d531cd4483a205d548e5b9750 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 7 Dec 2020 11:26:08 +0100 Subject: [PATCH 102/201] Only allow blob URLs in restricted tags --- www/common/diffMarked.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index 3d5704584..2bbd51dde 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -358,7 +358,7 @@ define([ // Only allow iframe, video and audio with local source var checkSrc = function (root) { if (restrictedTags.indexOf(root.nodeName.toUpperCase()) === -1) { return true; } - return root.getAttribute && /^(blob\:|\/)/.test(root.getAttribute('src')); + return root.getAttribute && /^blob\:/.test(root.getAttribute('src')); }; var removeForbiddenTags = function (root) { From 3e673bfd0560c06a30e9cedeb2ecd61c36f10ed2 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 7 Dec 2020 11:53:44 +0100 Subject: [PATCH 103/201] Don't store a copy of owned pads in your own drive --- www/common/outer/async-store.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 687326139..65078b287 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1137,7 +1137,7 @@ define([ var ownedByMe = Array.isArray(owners) && owners.indexOf(edPublic) !== -1; // Add the pad if it does not exist in our drive - if (!contains || (ownedByMe && !inMyDrive)) { + if (!contains) { // || (ownedByMe && !inMyDrive)) { var autoStore = Util.find(store.proxy, ['settings', 'general', 'autostore']); if (autoStore !== 1 && !data.forceSave && !data.path && !ownedByMe) { // send event to inner to display the corner popup From 2647acbb78643e651b71d2d4f74c2f66e264a258 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 7 Dec 2020 15:42:25 +0100 Subject: [PATCH 104/201] Expose Content-Length header --- docs/example.nginx.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/example.nginx.conf b/docs/example.nginx.conf index 8319c657b..0a2edcf57 100644 --- a/docs/example.nginx.conf +++ b/docs/example.nginx.conf @@ -177,8 +177,8 @@ server { add_header Cache-Control max-age=31536000; add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; - add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; - add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; + add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Content-Length'; + add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Content-Length'; try_files $uri =404; } From 0aef54be6257bbceeefef73f53de19c521dbb102 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 7 Dec 2020 15:51:58 +0100 Subject: [PATCH 105/201] Clear cache with 'burn this anon drive' --- www/drive/main.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/www/drive/main.js b/www/drive/main.js index ea6345b02..0f2408f5b 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -54,7 +54,13 @@ define([ if (Utils.LocalStore.isLoggedIn()) { return; } Utils.LocalStore.setFSHash(''); Utils.LocalStore.clearThumbnail(); - window.location.reload(); + try { + Utils.Cache.clear(function () { + window.location.reload(); + }); + } catch (e) { + window.location.reload(); + } }); sframeChan.on('Q_DRIVE_USEROBJECT', function (data, cb) { Cryptpad.userObjectCommand(data, cb); From e3eef3abafb7cd2ef567abe6e73b6145001bc283 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 7 Dec 2020 16:21:33 +0100 Subject: [PATCH 106/201] Hide unnecesssary error messages from the console --- customize.dist/loading.js | 2 +- www/common/media-tag.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 22dc69c31..6e4781aaf 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -330,7 +330,7 @@ button:not(.btn).primary:hover{ var el3 = document.querySelector('.cp-loading-progress-container'); if (el3) { el3.innerHTML = makeBar(data); } } catch (e) { - if (!hasErrored) { console.error(e); } + //if (!hasErrored) { console.error(e); } } }; window.CryptPad_updateLoadingProgress = updateLoadingProgress; diff --git a/www/common/media-tag.js b/www/common/media-tag.js index a00377ff4..c328399c5 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -718,7 +718,7 @@ var factory = function () { if (cache[uid].mt !== mediaObject) { // Add progress for other instances of this tag cache[uid].mt.on('progress', function (obj) { - if (!mediaObject.bar) { makeProgressBar(cfg, mediaObject); } + if (!mediaObject.bar && !cfg.force) { makeProgressBar(cfg, mediaObject); } emit('progress', { progress: obj.progress }); From 92df689352508442280a552af579855086af0006 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 7 Dec 2020 16:21:45 +0100 Subject: [PATCH 107/201] Fix debug app with no hash --- www/common/sframe-chainpad-netflux-outer.js | 1 + www/debug/main.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/www/common/sframe-chainpad-netflux-outer.js b/www/common/sframe-chainpad-netflux-outer.js index c19299da2..f47dc812a 100644 --- a/www/common/sframe-chainpad-netflux-outer.js +++ b/www/common/sframe-chainpad-netflux-outer.js @@ -46,6 +46,7 @@ define([], function () { // shim between chainpad and netflux var msgIn = function (peer, msg) { try { + if (/^\[/.test(msg)) { return msg; } // Already decrypted var isHk = peer.length !== 32; var key = isNewHash ? validateKey : false; var decryptedMsg = Crypto.decrypt(msg, key, isHk); diff --git a/www/debug/main.js b/www/debug/main.js index b901beec0..af3d78a57 100644 --- a/www/debug/main.js +++ b/www/debug/main.js @@ -28,6 +28,8 @@ define([ // Loaded in load #2 nThen(function (waitFor) { $(waitFor()); + }).nThen(function (waitFor) { + SFCommonO.initIframe(waitFor); }).nThen(function (waitFor) { var req = { cfg: requireConfig, From f9c4387a6893707b19347b929d3a2c6677563a74 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 7 Dec 2020 16:39:58 +0100 Subject: [PATCH 108/201] Fix rich text mediatag cache --- www/pad/inner.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/www/pad/inner.js b/www/pad/inner.js index a22c0d81d..4968cf4cd 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -498,9 +498,10 @@ define([ Array.prototype.slice.call(tags).forEach(function(tag) { var src = tag.getAttribute('src'); if (mediaTagMap[src]) { - mediaTagMap[src].forEach(function(n) { + tag.innerHTML = mediaTagMap[src]; + /*mediaTagMap[src].forEach(function(n) { tag.appendChild(n.cloneNode(true)); - }); + });*/ } }); }; From 204171c7cf904e86e515dbfa363ae6cd18c5249f Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 7 Dec 2020 17:49:33 +0100 Subject: [PATCH 109/201] Update bower.json --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index bd9cebdd6..f9a3dc5be 100644 --- a/bower.json +++ b/bower.json @@ -30,7 +30,7 @@ "secure-fabric.js": "secure-v1.7.9", "hyperjson": "~1.4.0", "chainpad-crypto": "^0.2.0", - "chainpad-listmap": "^0.9.0", + "chainpad-listmap": "^0.10.0", "chainpad": "^5.2.0", "file-saver": "1.3.1", "alertifyjs": "1.0.11", From 93c807524f6de924d31bff1fafb9599ff2591448 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 8 Dec 2020 10:24:09 +0530 Subject: [PATCH 110/201] update offset debugging info --- lib/workers/db-worker.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/workers/db-worker.js b/lib/workers/db-worker.js index d871bcb97..7f45dfaa2 100644 --- a/lib/workers/db-worker.js +++ b/lib/workers/db-worker.js @@ -230,8 +230,10 @@ const computeIndexFromOffset = function (channelName, offset, cb) { var diff = new_start - start; Env.Log.info('WORKER_OFFSET_UPDATE', { channel: channelName, - old_start: start, - new_start: new_start, + start: start, + startMB: start / 1024 / 1024, + update: new_start, + updateMB: new_start / 1024 / 1024, diff: diff, diffMB: diff / 1024 / 1024, }); From 93d56e5e3d71f05471a6cfb9f8688900a417365e Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 8 Dec 2020 10:44:46 +0530 Subject: [PATCH 111/201] profile worker commands by total running time in seconds --- lib/commands/admin-rpc.js | 6 ++++++ lib/env.js | 1 + lib/workers/index.js | 10 ++++++++++ 3 files changed, 17 insertions(+) diff --git a/lib/commands/admin-rpc.js b/lib/commands/admin-rpc.js index d7f22825d..826c73491 100644 --- a/lib/commands/admin-rpc.js +++ b/lib/commands/admin-rpc.js @@ -56,6 +56,11 @@ var getCacheStats = function (env, server, cb) { }); }; +// CryptPad_AsyncStore.rpc.send('ADMIN', ['GET_WORKER_PROFILES'], console.log) +var getWorkerProfiles = function (Env, Server, cb) { + cb(void 0, Env.commandTimers); +}; + var getActiveSessions = function (Env, Server, cb) { var stats = Server.getSessionStats(); cb(void 0, [ @@ -315,6 +320,7 @@ var commands = { INSTANCE_STATUS: instanceStatus, GET_LIMITS: getLimits, SET_LAST_EVICTION: setLastEviction, + GET_WORKER_PROFILES: getWorkerProfiles, }; Admin.command = function (Env, safeKey, data, _cb, Server) { diff --git a/lib/env.js b/lib/env.js index b1fc6680b..3a876f1f7 100644 --- a/lib/env.js +++ b/lib/env.js @@ -94,6 +94,7 @@ module.exports.create = function (config) { disableIntegratedEviction: config.disableIntegratedEviction || false, lastEviction: +new Date(), evictionReport: {}, + commandTimers: {}, }; (function () { diff --git a/lib/workers/index.js b/lib/workers/index.js index fe868e250..c422a9f54 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -14,6 +14,14 @@ const DEFAULT_QUERY_TIMEOUT = 60000 * 15; // increased from three to fifteen min Workers.initialize = function (Env, config, _cb) { var cb = Util.once(Util.mkAsync(_cb)); + var incrementTime = function (command, start) { + if (!command) { return; } + var end = +new Date(); + var T = Env.commandTimers; + var diff = (end - start); + T[command] = (T[command] || 0) + (diff / 1000); + }; + const workers = []; const response = Util.response(function (errLabel, info) { @@ -112,7 +120,9 @@ Workers.initialize = function (Env, config, _cb) { } const txid = guid(); + var start = +new Date(); var cb = Util.once(Util.mkAsync(Util.both(_cb, function (err /*, value */) { + incrementTime(msg && msg.command, start); if (err !== 'TIMEOUT') { return; } Log.debug("WORKER_TIMEOUT_CAUSE", msg); // in the event of a timeout the user will receive an error From 8d27fe59d6577070e35c5868e9946742b8e3138c Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 8 Dec 2020 10:35:29 +0100 Subject: [PATCH 112/201] Fix overflowing text in mediatags buttons --- customize.dist/ckeditor-contents.css | 7 +++++++ customize.dist/src/less2/include/markdown.less | 3 +++ www/common/media-tag.js | 1 + 3 files changed, 11 insertions(+) diff --git a/customize.dist/ckeditor-contents.css b/customize.dist/ckeditor-contents.css index 663cd8cb9..0d5b5cff7 100644 --- a/customize.dist/ckeditor-contents.css +++ b/customize.dist/ckeditor-contents.css @@ -248,6 +248,9 @@ media-tag button.mediatag-download-btn > span { media-tag button.mediatag-download-btn * { width: auto; } +media-tag button.mediatag-download-btn > span.mediatag-download-name { + max-width: 100%; +} media-tag button.mediatag-download-btn > span.mediatag-download-name b { white-space: nowrap; overflow: hidden; @@ -259,8 +262,12 @@ media-tag button.btn:hover, media-tag button.btn:active, media-tag button.btn:fo } media-tag button.btn b { margin-left: 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } media-tag button.btn .fa { display: inline; margin-right: 5px; + flex: 0; } diff --git a/customize.dist/src/less2/include/markdown.less b/customize.dist/src/less2/include/markdown.less index 677ec13a3..37fd4fce8 100644 --- a/customize.dist/src/less2/include/markdown.less +++ b/customize.dist/src/less2/include/markdown.less @@ -101,6 +101,9 @@ } b { margin-left: 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } } } diff --git a/www/common/media-tag.js b/www/common/media-tag.js index c328399c5..6755df5a2 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -149,6 +149,7 @@ var factory = function () { width: 0%; } .mediatag-progress-text { + font-size: 14px; height: 25px; width: 50px; margin-left: 5px; From 59953009d3b717952a9c3476c41d1385b04c070c Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 8 Dec 2020 10:43:47 +0100 Subject: [PATCH 113/201] Fix OnlyOffice apps --- www/common/onlyoffice/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/onlyoffice/main.js b/www/common/onlyoffice/main.js index db83ef456..7e4e8b8d5 100644 --- a/www/common/onlyoffice/main.js +++ b/www/common/onlyoffice/main.js @@ -12,7 +12,7 @@ define([ nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { - var obj = SFCommonO.initIframe(waitFor, true, true); + var obj = SFCommonO.initIframe(waitFor, true); href = obj.href; hash = obj.hash; var parsed = Hash.parsePadUrl(href); From e8cdbf663018a5122edf67dbbfe0e07491eae442 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 8 Dec 2020 11:19:38 +0100 Subject: [PATCH 114/201] Fix race condition in framework --- www/common/sframe-app-framework.js | 9 ++++++--- www/common/toolbar.js | 4 ++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 8776573d7..5c0f9b511 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -221,6 +221,10 @@ define([ evStart.reg(function () { toolbar.deleted(); }); break; } + case STATE.READY: { + evStart.reg(function () { toolbar.ready(); }); + break; + } default: } var isEditable = (state === STATE.READY && !unsyncMode); @@ -479,7 +483,7 @@ define([ sframeChan.event("EV_CORRUPTED_CACHE"); }; var onCacheReady = function () { - stateChange(STATE.DISCONNECTED); + stateChange(STATE.INITIALIZING); toolbar.offline(true); var newContentStr = cpNfInner.chainpad.getUserDoc(); if (toolbar) { @@ -526,7 +530,6 @@ define([ var privateDat = cpNfInner.metadataMgr.getPrivateData(); var type = privateDat.app; - // contentUpdate may be async so we need an nthen here nThen(function (waitFor) { if (!newPad) { @@ -783,7 +786,7 @@ define([ onRemote: onRemote, onLocal: onLocal, onInit: onInit, - onCacheReady: onCacheReady, + onCacheReady: function () { evStart.reg(onCacheReady); }, onReady: function () { evStart.reg(onReady); }, onConnectionChange: onConnectionChange, onError: onError, diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 20cac8337..e2c5af1dd 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -1318,6 +1318,10 @@ MessengerUI, Messages) { toolbar.spinner.text(Messages.reconnecting); } }; + toolbar.ready = function () { + toolbar.connected = true; + kickSpinner(toolbar, config); + }; toolbar.errorState = function (state, error) { toolbar.isErrorState = state; From 7f534221c0230a058a06c5751dac8000d93ff04c Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 8 Dec 2020 11:34:34 +0100 Subject: [PATCH 115/201] Use prod values for history trim reminder --- www/common/common-ui-elements.js | 1 - www/common/sframe-common.js | 1 - 2 files changed, 2 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index cf1366714..bd9cb784a 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2702,7 +2702,6 @@ define([ var dontShowAgain = function () { var until = (+new Date()) + (7 * 24 * 3600 * 1000); // 7 days from now - until = (+new Date()) + 30000; // XXX 30s from now if (data.drive) { common.setAttribute(['drive', 'trim'], until); return; diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index c665f2ec6..7a28f7f79 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -290,7 +290,6 @@ define([ var priv = ctx.metadataMgr.getPrivateData(); var limit = 100 * 1024 * 1024; // 100MB - limit = 100 * 1024; // XXX 100KB var owned; nThen(function (w) { From 25a5783fb34dae7a33001e537d511dae4b21d9cb Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 8 Dec 2020 11:44:27 +0100 Subject: [PATCH 116/201] Remove drive trim history from settings for anon users --- www/settings/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/settings/inner.js b/www/settings/inner.js index 8b5095cba..b4668efff 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -955,7 +955,7 @@ define([ cb(content); }; makeBlock('trim-history', function(cb, $div) { - if (!common.isLoggedIn()) { return; } + if (!common.isLoggedIn()) { return void cb(false); } redrawTrimHistory(cb, $div); }, true); From a10ed3198c1cf3970d39d2a2237a019d22edd107 Mon Sep 17 00:00:00 2001 From: Weblate Date: Tue, 8 Dec 2020 11:51:54 +0100 Subject: [PATCH 117/201] Translated using Weblate (German) Currently translated at 100.0% (1398 of 1398 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/de/ --- www/common/translations/messages.de.json | 29 +++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/www/common/translations/messages.de.json b/www/common/translations/messages.de.json index f7e9ce178..105e710ba 100644 --- a/www/common/translations/messages.de.json +++ b/www/common/translations/messages.de.json @@ -40,7 +40,7 @@ "saved": "Gespeichert", "synced": "Alles gespeichert", "deleted": "Gelöscht", - "deletedFromServer": "Pad wurde vom Server gelöscht", + "deletedFromServer": "Dokument zerstört", "mustLogin": "Du musst angemeldet sein, um auf diese Seite zuzugreifen", "disabledApp": "Diese Anwendung wurde deaktiviert. Kontaktiere den Administrator dieses CryptPads, um mehr Informationen zu erhalten.", "realtime_unrecoverableError": "Es ist ein nicht reparierbarer Fehler aufgetreten. Klicke auf OK, um neu zu laden.", @@ -547,7 +547,7 @@ "upload_notEnoughSpace": "Der verfügbare Speicherplatz in deinem CryptDrive reicht leider nicht für diese Datei.", "upload_notEnoughSpaceBrief": "Unzureichender Speicherplatz", "upload_tooLarge": "Diese Datei überschreitet die für deinen Account erlaubte maximale Größe.", - "upload_tooLargeBrief": "Datei zu groß", + "upload_tooLargeBrief": "Datei überschreitet die maximale Größe von {0} MB", "upload_choose": "Eine Datei wählen", "upload_pending": "In der Warteschlange", "upload_cancelled": "Abgebrochen", @@ -1471,5 +1471,28 @@ "error_unhelpfulScriptError": "Skriptfehler: Siehe Konsole im Browser für Details", "documentID": "Kennung des Dokuments", "errorPopupBlocked": "Für die Funktionsweise von CryptPad ist es erforderlich, dass neue Tabs geöffnet werden können. Bitte erlaube Pop-up-Fenster in der Adressleiste deines Browsers. Diese Fenster werden niemals dafür verwendet, dir Werbung anzuzeigen.", - "unableToDisplay": "Das Dokument kann nicht angezeigt werden. Drücke ESC, um die Seite neu zu laden." + "unableToDisplay": "Das Dokument kann nicht angezeigt werden. Drücke ESC, um die Seite neu zu laden. Wenn das Problem weiterhin besteht, kontaktiere bitte den Support.", + "mediatag_notReady": "Bitte schließe den Download ab", + "settings_mediatagSizeTitle": "Limit für automatisches Herunterladen", + "admin_archiveHint": "Ein Dokument unzugänglich machen, ohne es endgültig zu löschen. Es wird in einem Verzeichnis \"archive\" abgelegt und nach einigen Tagen gelöscht (konfigurierbar in der Konfigurationsdatei des Servers).", + "mediatag_loadButton": "Anhang laden", + "settings_mediatagSizeHint": "Maximale Größe in Megabytes (MB) für das automatische Laden von Medienelementen (Bilder, Videos, PDFs), die in Dokumenten eingebettet sind. Größere Elemente können manuell geladen werden. Gib \"-1\" ein, um Medienelemente immer automatisch zu laden.", + "mediatag_saveButton": "Speichern", + "admin_archiveInput2": "Passwort für das Dokument", + "admin_archiveInput": "URL des Dokuments", + "admin_archiveButton": "Archivieren", + "Offline": "Offline", + "download_zip": "ZIP-Datei erstellen...", + "fileTableHeader": "Downloads und Uploads", + "allowNotifications": "Benachrichtigungen erlauben", + "admin_unarchiveHint": "Ein zuvor archiviertes Dokument wiederherstellen", + "pad_mediatagOpen": "Datei öffnen", + "pad_mediatagShare": "Datei teilen", + "download_zip_file": "Datei {0}/{1}", + "archivedFromServer": "Dokument archiviert", + "restoredFromServer": "Dokument wiederhergestellt", + "admin_archiveInval": "Ungültiges Dokument", + "admin_unarchiveButton": "Wiederherstellen", + "admin_unarchiveTitle": "Dokumente wiederherstellen", + "admin_archiveTitle": "Dokumente archivieren" } From 34284fcc0caa7999d048ddb93c4cbbbb120e440f Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 8 Dec 2020 12:03:40 +0100 Subject: [PATCH 118/201] Fix issues with deprecated cache --- lib/hk-util.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/hk-util.js b/lib/hk-util.js index 20e0ce5d4..495f4ff81 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -438,13 +438,12 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, _cb) => { to reconcile their differences. */ } - // If our lastKnownHash is older than the 2nd to last checkpoint, - // only send the last 2 checkpoints and ignore "lkh" - // XXX XXX this is probably wrong! ChainPad may not accept checkpoints that are not connected to root - // XXX We probably need to send an EUNKNOWN here so that the client can recreate a new chainpad - /*if (lkh && index.cpIndex.length >= 2 && lkh < index.cpIndex[0].offset) { - return void cb(null, index.cpIndex[0].offset); - }*/ + // If our lastKnownHash is older than the 2nd to last checkpoint, send + // EUNKNOWN to tell the user to empty their cache + if (lkh && index.cpIndex.length >= 2 && lkh < index.cpIndex[0].offset) { + waitFor.abort(); + return void cb(new Error('EUNKNOWN')); + } // Otherwise use our lastKnownHash cb(null, lkh); From d4f94f3091230bed0e7651d86dc795e54e60f96a Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 8 Dec 2020 16:34:28 +0530 Subject: [PATCH 119/201] briefly cache server metadata in-memory to avoid repeated reads --- lib/commands/metadata.js | 9 ++++++++- lib/env.js | 12 ++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/commands/metadata.js b/lib/commands/metadata.js index 3d20f36e6..b189d73be 100644 --- a/lib/commands/metadata.js +++ b/lib/commands/metadata.js @@ -17,7 +17,14 @@ Data.getMetadataRaw = function (Env, channel /* channelName */, _cb) { } Env.batchMetadata(channel, cb, function (done) { - Env.computeMetadata(channel, done); + Env.computeMetadata(channel, function (err, meta) { + if (!err && meta && meta.channel) { + Env.metadata_cache[channel] = meta; + // clear metadata after a delay if nobody has joined the channel within 30s + Env.checkCache(channel); + } + done(err, meta); + }); }); }; diff --git a/lib/env.js b/lib/env.js index 3a876f1f7..066fcd115 100644 --- a/lib/env.js +++ b/lib/env.js @@ -42,6 +42,8 @@ module.exports.create = function (config) { metadata_cache: {}, channel_cache: {}, + cache_checks: {}, + queueStorage: WriteQueue(), queueDeletes: WriteQueue(), queueValidation: WriteQueue(), @@ -117,8 +119,14 @@ module.exports.create = function (config) { } }()); - - + Env.checkCache = function (channel) { + var f = Env.cache_checks[channel] || Util.throttle(function () { + if (Env.channel_cache[channel]) { return; } + delete Env.metadata_cache[channel]; + delete Env.cache_checks[channel]; + }, 30000); + f(); + }; (function () { var custom = config.customLimits; From 6bc103be5f0150b97045d7fc97d20f2363bba509 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 8 Dec 2020 16:43:50 +0530 Subject: [PATCH 120/201] delay cache eviction each time the metadata is requested --- lib/commands/metadata.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/commands/metadata.js b/lib/commands/metadata.js index b189d73be..d21b7103c 100644 --- a/lib/commands/metadata.js +++ b/lib/commands/metadata.js @@ -13,6 +13,7 @@ Data.getMetadataRaw = function (Env, channel /* channelName */, _cb) { var cached = Env.metadata_cache[channel]; if (HK.isMetadataMessage(cached)) { + Env.checkCache(channel); return void cb(void 0, cached); } From 4495d99e492c8ee240d946efee66997b2699692b Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 8 Dec 2020 16:45:57 +0530 Subject: [PATCH 121/201] use the standard method of checking whether metadata is valid --- lib/commands/metadata.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands/metadata.js b/lib/commands/metadata.js index d21b7103c..896c89f31 100644 --- a/lib/commands/metadata.js +++ b/lib/commands/metadata.js @@ -19,7 +19,7 @@ Data.getMetadataRaw = function (Env, channel /* channelName */, _cb) { Env.batchMetadata(channel, cb, function (done) { Env.computeMetadata(channel, function (err, meta) { - if (!err && meta && meta.channel) { + if (!err && HK.isMetadataMessage(meta)) { Env.metadata_cache[channel] = meta; // clear metadata after a delay if nobody has joined the channel within 30s Env.checkCache(channel); From e6a4c68863044f5e1c617d9eb8a8ae6f90c2a88c Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 8 Dec 2020 16:50:37 +0530 Subject: [PATCH 122/201] always delete the throttled cleanup functions once they have run --- lib/env.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/env.js b/lib/env.js index 066fcd115..322f629c6 100644 --- a/lib/env.js +++ b/lib/env.js @@ -121,9 +121,9 @@ module.exports.create = function (config) { Env.checkCache = function (channel) { var f = Env.cache_checks[channel] || Util.throttle(function () { + delete Env.cache_checks[channel]; if (Env.channel_cache[channel]) { return; } delete Env.metadata_cache[channel]; - delete Env.cache_checks[channel]; }, 30000); f(); }; From c29219c96972f42bdc6525baf90811cdf1b5d413 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 8 Dec 2020 13:41:29 +0100 Subject: [PATCH 123/201] Fix restrictedTags regex --- www/common/diffMarked.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index 2bbd51dde..71149b59f 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -351,14 +351,14 @@ define([ if (!(node && node.parentElement)) { return; } var parent = node.parentElement; if (!parent) { return; } - console.log('removing %s tag', node.nodeName); + console.debug('removing %s tag', node.nodeName); parent.removeChild(node); }; // Only allow iframe, video and audio with local source var checkSrc = function (root) { if (restrictedTags.indexOf(root.nodeName.toUpperCase()) === -1) { return true; } - return root.getAttribute && /^blob\:/.test(root.getAttribute('src')); + return root.getAttribute && /^(blob\:|\/common\/pdfjs)/.test(root.getAttribute('src')); }; var removeForbiddenTags = function (root) { From e405dcff7e48c75706cd4d54a0585bc86b1792f2 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 8 Dec 2020 16:46:39 +0100 Subject: [PATCH 124/201] Don't show saved in OO until the ACK is received --- www/common/onlyoffice/inner.js | 14 +++++++++++++- www/common/toolbar.js | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index b4bdeef92..c7f5ce9c2 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -99,6 +99,9 @@ define([ var sessionId = Hash.createChannelId(); var cpNfInner; + var evOnPatch = Util.mkEvent(); + var evOnSync = Util.mkEvent(); + // This structure is used for caching media data and blob urls for each media cryptpad url var mediasData = {}; @@ -261,13 +264,17 @@ define([ }); }, sendMsg: function (msg, cp, cb) { + evOnPatch.fire(); rtChannel.sendCmd({ cmd: 'SEND_MESSAGE', data: { msg: msg, isCp: cp } - }, cb); + }, function (err, h) { + if (!err) { evOnSync.fire(); } + cb(err, h); + }); }, }; @@ -1221,6 +1228,7 @@ define([ } }, "onDocumentReady": function () { + evOnSync.fire(); var onMigrateRdy = Util.mkEvent(); onMigrateRdy.reg(function () { var div = h('div.cp-oo-x2tXls', [ @@ -1963,6 +1971,10 @@ define([ metadataMgr: metadataMgr, readOnly: readOnly, realtime: info.realtime, + spinner: { + onPatch: evOnPatch, + onSync: evOnSync + }, sfCommon: common, $container: $bar, $contentContainer: $('#cp-app-oo-container') diff --git a/www/common/toolbar.js b/www/common/toolbar.js index e2c5af1dd..7794f3fde 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -879,6 +879,14 @@ MessengerUI, Messages) { $spin.text(Messages.saved); }, /*local ? 0 :*/ SPINNER_DISAPPEAR_TIME); }; + if (config.spinner) { + var h = function () { + onSynced(); + try { config.spinner.onSync.unreg(h); } catch (e) { console.error(e); } + }; + config.spinner.onSync.reg(h); + return; + } config.sfCommon.whenRealtimeSyncs(onSynced); }; var ks = function (toolbar, config, local) { @@ -891,6 +899,15 @@ MessengerUI, Messages) { var $spin = $('', {'class': SPINNER_CLS}).appendTo(toolbar.title); $spin.text(Messages.synchronizing); + if (config.spinner) { + config.spinner.onPatch.reg(ks(toolbar, config)); + typing = 0; + setTimeout(function () { + kickSpinner(toolbar, config); + }); + return $spin; + } + if (config.realtime) { config.realtime.onPatch(ks(toolbar, config)); config.realtime.onMessage(ks(toolbar, config, true)); From 30f59472e1daf540a549365f9ea2b7f03701634b Mon Sep 17 00:00:00 2001 From: Weblate Date: Tue, 8 Dec 2020 17:16:27 +0100 Subject: [PATCH 125/201] Translated using Weblate (English) Currently translated at 100.0% (1399 of 1399 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ --- www/common/translations/messages.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index f5acaa6c8..179c0a296 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -1494,5 +1494,6 @@ "mediatag_notReady": "Please complete the download", "settings_mediatagSizeTitle": "Automatic download limit", "settings_mediatagSizeHint": "Maximum size in megabytes (MB) for automatically loading media elements (images, videos, pdf) embedded into documents. Elements bigger than the specified size can be loaded manually. Use \"-1\" to always load the media elements automatically.", - "mediatag_loadButton": "Load attachment" + "mediatag_loadButton": "Load attachment", + "history_trimPrompt": "This document has accumulated {0} of history that may slow down loading time. Consider deleting the history if it is not needed." } From 1aaefb8f5093e0562d4d56caa42b61cb9026214b Mon Sep 17 00:00:00 2001 From: Weblate Date: Tue, 8 Dec 2020 17:16:27 +0100 Subject: [PATCH 126/201] Translated using Weblate (French) Currently translated at 100.0% (1399 of 1399 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ --- www/common/translations/messages.fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index 47980ee0d..b1dccaf48 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -1494,5 +1494,6 @@ "admin_archiveButton": "Archiver", "admin_archiveHint": "Rendre un document indisponible sans le supprimer définitivement. Il sera placé dans un répertoire \"archive\" et supprimé après quelques jours (configurable dans le fichier de configuration du serveur).", "admin_archiveTitle": "Archiver les documents", - "mediatag_loadButton": "Charger la pièce jointe" + "mediatag_loadButton": "Charger la pièce jointe", + "history_trimPrompt": "Ce document a accumulé {0} d'historique qui peut ralentir le temps de chargement. Envisagez de supprimer l'historique s'il n'est pas nécessaire." } From 7bc39b39e1a619c4522a958dba2ab24b3470b5b7 Mon Sep 17 00:00:00 2001 From: Weblate Date: Tue, 8 Dec 2020 17:16:27 +0100 Subject: [PATCH 127/201] Translated using Weblate (German) Currently translated at 100.0% (1399 of 1399 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/de/ --- www/common/translations/messages.de.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.de.json b/www/common/translations/messages.de.json index 105e710ba..eb2e4abcb 100644 --- a/www/common/translations/messages.de.json +++ b/www/common/translations/messages.de.json @@ -1494,5 +1494,6 @@ "admin_archiveInval": "Ungültiges Dokument", "admin_unarchiveButton": "Wiederherstellen", "admin_unarchiveTitle": "Dokumente wiederherstellen", - "admin_archiveTitle": "Dokumente archivieren" + "admin_archiveTitle": "Dokumente archivieren", + "history_trimPrompt": "Dieses Dokument hat einen Verlauf von {0} angesammelt, was das Laden verlangsamen kann. Ziehe in Betracht, den Verlauf zu löschen, sofern er nicht benötigt wird." } From 892fca7d996a14d236986701bc54083a8453a5cf Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 9 Dec 2020 14:54:22 +0100 Subject: [PATCH 128/201] Improve contact requests process --- www/common/outer/async-store.js | 57 +++++++++++++++++--------- www/common/outer/mailbox-handlers.js | 60 +++++++++++++++++++++------- www/common/outer/mailbox.js | 2 +- www/common/outer/messenger.js | 11 +++++ www/common/toolbar.js | 8 ++-- www/profile/inner.js | 29 +++++++++++++- 6 files changed, 130 insertions(+), 37 deletions(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 65078b287..afb799990 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1330,13 +1330,16 @@ define([ store.proxy.friends_pending = store.proxy.friends_pending || {}; - var twoDaysAgo = +new Date() - (2 * 24 * 3600 * 1000); - if (store.proxy.friends_pending[data.curvePublic] && - store.proxy.friends_pending[data.curvePublic] > twoDaysAgo) { - return void cb({error: 'TIMEOUT'}); + var p = store.proxy.friends_pending[data.curvePublic]; + if (p) { + return void cb({error: 'ALREADY_SENT'}); } - store.proxy.friends_pending[data.curvePublic] = +new Date(); + store.proxy.friends_pending[data.curvePublic] = { + time: +new Date(), + channel: data.notifications, + curvePublic: data.curvePublic + }; broadcast([], "UPDATE_METADATA"); store.mailbox.sendTo('FRIEND_REQUEST', { @@ -1348,6 +1351,37 @@ define([ cb(obj); }); }; + Store.cancelFriendRequest = function (data, cb) { + if (!data.curvePublic || !data.notifications) { + return void cb({error: 'EINVAL'}); + } + + var proxy = store.proxy; + var f = Messaging.getFriend(proxy, data.curvePublic); + + if (f) { + // Already friend + console.error("You can't cancel an accepted friend request"); + return void cb({error: 'ALREADY_FRIEND'}); + } + + var pending = Util.find(store, ['proxy', 'friends_pending']) || {}; + if (!pending) { return void cb(); } + + store.mailbox.sendTo('CANCEL_FRIEND_REQUEST', { + user: Messaging.createData(store.proxy) + }, { + channel: data.notifications, + curvePublic: data.curvePublic + }, function (obj) { + if (obj && obj.error) { return void cb(obj); } + delete store.proxy.friends_pending[data.curvePublic]; + broadcast([], "UPDATE_METADATA"); + onSync(null, function () { + cb(obj); + }); + }); + }; Store.anonGetPreviewContent = function (clientId, data, cb) { Team.anonGetPreviewContent({ @@ -2448,18 +2482,6 @@ define([ }); }; - var cleanFriendRequests = function () { - try { - if (!store.proxy.friends_pending) { return; } - var twoDaysAgo = +new Date() - (2 * 24 * 3600 * 1000); - Object.keys(store.proxy.friends_pending).forEach(function (curve) { - if (store.proxy.friends_pending[curve] < twoDaysAgo) { - delete store.proxy.friends_pending[curve]; - } - }); - } catch (e) {} - }; - ////////////////////////////////////////////////////////////////// /////////////////////// Init ///////////////////////////////////// ////////////////////////////////////////////////////////////////// @@ -2543,7 +2565,6 @@ define([ loadUniversal(Profile, 'profile', waitFor); loadUniversal(Team, 'team', waitFor, clientId); loadUniversal(History, 'history', waitFor); - cleanFriendRequests(); }).nThen(function () { var requestLogin = function () { broadcast([], "REQUEST_LOGIN"); diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js index 326b4afc7..17ef80ea2 100644 --- a/www/common/outer/mailbox-handlers.js +++ b/www/common/outer/mailbox-handlers.js @@ -33,11 +33,15 @@ define([ // in memory from the same user, dismiss the new one if (friendRequest[data.msg.author]) { return void cb(true); } - friendRequest[data.msg.author] = true; + friendRequest[data.msg.author] = { + type: box.type, + hash: data.hash + }; // If the user is already in our friend list, automatically accept the request if (Messaging.getFriend(ctx.store.proxy, data.msg.author) || ctx.store.proxy.friends_pending[data.msg.author]) { + delete ctx.store.proxy.friends_pending[data.msg.author]; Messaging.acceptFriendRequest(ctx.store, userData, function (obj) { @@ -49,13 +53,16 @@ define([ realtime: ctx.store.realtime, pinPads: ctx.pinPads }, userData, function (err) { - if (err) { return void console.error(err); } + if (err) { + console.error(err); + return void cb(true); + } if (ctx.store.messenger) { ctx.store.messenger.onFriendAdded(userData); } + ctx.updateMetadata(); + cb(true); }); - ctx.updateMetadata(); - cb(true); }); return; } @@ -97,20 +104,29 @@ define([ ctx.updateMetadata(); if (friendRequestDeclined[data.msg.author]) { return; } - friendRequestDeclined[data.msg.author] = true; box.sendMessage({ type: 'FRIEND_REQUEST_DECLINED', content: { user: userData } - }, function () {}); + }, function (hash) { + friendRequestDeclined[data.msg.author] = { + type: box.type, + hash: hash + }; + }); }, getRandomTimeout(ctx)); }; // UI for declined friend request handlers['FRIEND_REQUEST_DECLINED'] = function (ctx, box, data, cb) { ctx.updateMetadata(); var curve = data.msg.content.user.curvePublic || data.msg.content.user; - if (friendRequestDeclined[curve]) { return void cb(true); } - friendRequestDeclined[curve] = true; - cb(); + var toRemove = friendRequestAccepted[curve]; + delete friendRequestAccepted[curve]; + if (friendRequestDeclined[curve]) { return void cb(true, toRemove); } + friendRequestDeclined[curve] = { + type: box.type, + hash: data.hash + }; + cb(false, toRemove); }; removeHandlers['FRIEND_REQUEST_DECLINED'] = function (ctx, box, data) { var curve = data.content.user.curvePublic || data.content.user; @@ -148,11 +164,15 @@ define([ if (ctx.store.modules['profile']) { ctx.store.modules['profile'].update(); } // Display the "accepted" state in the UI if (friendRequestAccepted[data.msg.author]) { return; } - friendRequestAccepted[data.msg.author] = true; box.sendMessage({ type: 'FRIEND_REQUEST_ACCEPTED', content: { user: userData } - }, function () {}); + }, function (hash) { + friendRequestAccepted[data.msg.author] = { + type: box.type, + hash: hash + }; + }); }); }, getRandomTimeout(ctx)); }; @@ -160,20 +180,32 @@ define([ handlers['FRIEND_REQUEST_ACCEPTED'] = function (ctx, box, data, cb) { ctx.updateMetadata(); var curve = data.msg.content.user.curvePublic || data.msg.content.user; - if (friendRequestAccepted[curve]) { return void cb(true); } - friendRequestAccepted[curve] = true; - cb(); + var toRemove = friendRequestDeclined[curve]; + delete friendRequestDeclined[curve]; + if (friendRequestAccepted[curve]) { return void cb(true, toRemove); } + friendRequestAccepted[curve] = { + type: box.type, + hash: data.hash + }; + cb(false, toRemove); }; removeHandlers['FRIEND_REQUEST_ACCEPTED'] = function (ctx, box, data) { var curve = data.content.user.curvePublic || data.content.user; if (friendRequestAccepted[curve]) { delete friendRequestAccepted[curve]; } }; + handlers['CANCEL_FRIEND_REQUEST'] = function (ctx, box, data, cb) { + var f = friendRequest[data.msg.author]; + if (!f) { return void cb(true); } + cb(true, f); + }; + handlers['UNFRIEND'] = function (ctx, box, data, cb) { var curve = data.msg.author; var friend = Messaging.getFriend(ctx.store.proxy, curve); if (!friend) { return void cb(true); } delete ctx.store.proxy.friends[curve]; + delete ctx.store.proxy.friends_pending[curve]; if (ctx.store.messenger) { ctx.store.messenger.onFriendRemoved(curve, friend.channel); } diff --git a/www/common/outer/mailbox.js b/www/common/outer/mailbox.js index 803d8bc93..d9c0dc4e2 100644 --- a/www/common/outer/mailbox.js +++ b/www/common/outer/mailbox.js @@ -271,7 +271,7 @@ proxy.mailboxes = { hash: hash }; showMessage(ctx, type, message); - cb(); + cb(hash); }, keys.curvePublic); }; box.queue.forEach(function (msg) { diff --git a/www/common/outer/messenger.js b/www/common/outer/messenger.js index 6587d9e4d..b5aab9ca5 100644 --- a/www/common/outer/messenger.js +++ b/www/common/outer/messenger.js @@ -459,6 +459,13 @@ define([ } }; + // Cancel pending friend requests + var cancelFriend = function (ctx, data, _cb) { + var cb = Util.once(_cb); + if (typeof(cb) !== 'function') { return void console.error('NO_CALLBACK'); } + ctx.Store.cancelFriendRequest(data, cb); + }; + var getAllClients = function (ctx) { var all = []; Array.prototype.push.apply(all, ctx.friendsClients); @@ -935,6 +942,7 @@ define([ if (AppConfig.availablePadTypes.indexOf('contacts') === -1) { return; } var ctx = { store: store, + Store: cfg.Store, updateMetadata: cfg.updateMetadata, pinPads: cfg.pinPads, emit: emit, @@ -1047,6 +1055,9 @@ define([ if (cmd === 'REMOVE_FRIEND') { return void removeFriend(ctx, data, cb); } + if (cmd === 'CANCEL_FRIEND') { + return void cancelFriend(ctx, data, cb); + } if (cmd === 'MUTE_USER') { return void muteUser(ctx, data, cb); } diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 7794f3fde..82cc8cbb4 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -229,7 +229,6 @@ MessengerUI, Messages) { // Editors var pendingFriends = Common.getPendingFriends(); // Friend requests sent var friendRequests = Common.getFriendRequests(); // Friend requests received - var friendTo = +new Date() - (2 * 24 * 3600 * 1000); editUsersNames.forEach(function (data) { var name = data.name || Messages.anonymous; var $span = $('', {'class': 'cp-avatar'}); @@ -297,7 +296,7 @@ MessengerUI, Messages) { } } else if (Common.isLoggedIn() && data.curvePublic && !friends[data.curvePublic] && !priv.readOnly) { - if (pendingFriends[data.curvePublic] && pendingFriends[data.curvePublic] > friendTo) { + if (pendingFriends[data.curvePublic]) { $('