|
|
@ -52,7 +52,7 @@ define([
|
|
|
|
$: $
|
|
|
|
$: $
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var CHECKPOINT_INTERVAL = 50;
|
|
|
|
var CHECKPOINT_INTERVAL = 100;
|
|
|
|
var DISPLAY_RESTORE_BUTTON = false;
|
|
|
|
var DISPLAY_RESTORE_BUTTON = false;
|
|
|
|
var NEW_VERSION = 2;
|
|
|
|
var NEW_VERSION = 2;
|
|
|
|
var PENDING_TIMEOUT = 30000;
|
|
|
|
var PENDING_TIMEOUT = 30000;
|
|
|
@ -76,6 +76,7 @@ define([
|
|
|
|
var privateData = metadataMgr.getPrivateData();
|
|
|
|
var privateData = metadataMgr.getPrivateData();
|
|
|
|
var readOnly = false;
|
|
|
|
var readOnly = false;
|
|
|
|
var offline = false;
|
|
|
|
var offline = false;
|
|
|
|
|
|
|
|
var ooLoaded = false;
|
|
|
|
var pendingChanges = {};
|
|
|
|
var pendingChanges = {};
|
|
|
|
var config = {};
|
|
|
|
var config = {};
|
|
|
|
var content = {
|
|
|
|
var content = {
|
|
|
@ -221,10 +222,12 @@ define([
|
|
|
|
|
|
|
|
|
|
|
|
var now = function () { return +new Date(); };
|
|
|
|
var now = function () { return +new Date(); };
|
|
|
|
|
|
|
|
|
|
|
|
var getLastCp = function (old) {
|
|
|
|
var getLastCp = function (old, i) {
|
|
|
|
var hashes = old ? oldHashes : content.hashes;
|
|
|
|
var hashes = old ? oldHashes : content.hashes;
|
|
|
|
if (!hashes || !Object.keys(hashes).length) { return {}; }
|
|
|
|
if (!hashes || !Object.keys(hashes).length) { return {}; }
|
|
|
|
var lastIndex = Math.max.apply(null, Object.keys(hashes).map(Number));
|
|
|
|
i = i || 0;
|
|
|
|
|
|
|
|
var idx = Object.keys(hashes).map(Number).sort();
|
|
|
|
|
|
|
|
var lastIndex = idx[idx.length - 1 - i];
|
|
|
|
var last = JSON.parse(JSON.stringify(hashes[lastIndex]));
|
|
|
|
var last = JSON.parse(JSON.stringify(hashes[lastIndex]));
|
|
|
|
return last;
|
|
|
|
return last;
|
|
|
|
};
|
|
|
|
};
|
|
|
@ -267,6 +270,8 @@ define([
|
|
|
|
// so that the messages we send to the realtime channel are
|
|
|
|
// so that the messages we send to the realtime channel are
|
|
|
|
// loadable by users joining after the checkpoint
|
|
|
|
// loadable by users joining after the checkpoint
|
|
|
|
var fixSheets = function () {
|
|
|
|
var fixSheets = function () {
|
|
|
|
|
|
|
|
var hasDrawings = checkDrawings();
|
|
|
|
|
|
|
|
if (hasDrawings) { return; } // XXX we need a migration for old sheets...
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
var editor = getEditor();
|
|
|
|
var editor = getEditor();
|
|
|
|
// if we are not in the sheet app
|
|
|
|
// if we are not in the sheet app
|
|
|
@ -291,13 +296,24 @@ define([
|
|
|
|
console.error(err);
|
|
|
|
console.error(err);
|
|
|
|
return void UI.alert(Messages.oo_saveError);
|
|
|
|
return void UI.alert(Messages.oo_saveError);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var i = Math.floor(ev.index / CHECKPOINT_INTERVAL);
|
|
|
|
// Get the last cp idx
|
|
|
|
|
|
|
|
var all = Object.keys(content.hashes || {}).map(Number).sort();
|
|
|
|
|
|
|
|
var current = all[all.length - 1] || 0;
|
|
|
|
|
|
|
|
// Get the expected cp idx
|
|
|
|
|
|
|
|
var _i = Math.floor(ev.index / CHECKPOINT_INTERVAL);
|
|
|
|
|
|
|
|
// Take the max of both
|
|
|
|
|
|
|
|
var i = Math.max(_i, current);
|
|
|
|
content.hashes[i] = {
|
|
|
|
content.hashes[i] = {
|
|
|
|
file: data.url,
|
|
|
|
file: data.url,
|
|
|
|
hash: ev.hash,
|
|
|
|
hash: ev.hash,
|
|
|
|
index: ev.index
|
|
|
|
index: ev.index
|
|
|
|
};
|
|
|
|
};
|
|
|
|
oldHashes = JSON.parse(JSON.stringify(content.hashes));
|
|
|
|
oldHashes = JSON.parse(JSON.stringify(content.hashes));
|
|
|
|
|
|
|
|
var hasDrawings = checkDrawings();
|
|
|
|
|
|
|
|
if (hasDrawings) {
|
|
|
|
|
|
|
|
content.locks = {};
|
|
|
|
|
|
|
|
content.ids = {};
|
|
|
|
|
|
|
|
}
|
|
|
|
// If this is a migration, set the new version
|
|
|
|
// If this is a migration, set the new version
|
|
|
|
if (APP.migrate) {
|
|
|
|
if (APP.migrate) {
|
|
|
|
delete content.migration;
|
|
|
|
delete content.migration;
|
|
|
@ -344,6 +360,38 @@ define([
|
|
|
|
};
|
|
|
|
};
|
|
|
|
APP.FM = common.createFileManager(fmConfig);
|
|
|
|
APP.FM = common.createFileManager(fmConfig);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var checkDrawings = function () {
|
|
|
|
|
|
|
|
var editor = getEditor();
|
|
|
|
|
|
|
|
if (!editor) { return false; }
|
|
|
|
|
|
|
|
var s = editor.GetSheets();
|
|
|
|
|
|
|
|
var hasDrawings = false;
|
|
|
|
|
|
|
|
s.forEach(function (obj, i) {
|
|
|
|
|
|
|
|
obj.worksheet.Drawings.forEach(function (d) {
|
|
|
|
|
|
|
|
console.log(d.graphicObject, d.graphicObject.Id);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
return s.some(function (obj, i) {
|
|
|
|
|
|
|
|
return obj.worksheet.Drawings.length;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var resetData = function (blob, type) {
|
|
|
|
|
|
|
|
if (!isLockedModal.modal) {
|
|
|
|
|
|
|
|
isLockedModal.modal = UI.openCustomModal(isLockedModal.content);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
myUniqueOOId = undefined;
|
|
|
|
|
|
|
|
setMyId();
|
|
|
|
|
|
|
|
APP.docEditor.destroyEditor(); // Kill the old editor
|
|
|
|
|
|
|
|
$('iframe[name="frameEditor"]').after(h('div#cp-app-oo-placeholder')).remove();
|
|
|
|
|
|
|
|
ooLoaded = false;
|
|
|
|
|
|
|
|
oldLocks = {};
|
|
|
|
|
|
|
|
Object.keys(pendingChanges).forEach(function (key) {
|
|
|
|
|
|
|
|
clearTimeout(pendingChanges[key]);
|
|
|
|
|
|
|
|
delete pendingChanges[key];
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
startOO(blob, type, true);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var saveToServer = function () {
|
|
|
|
var saveToServer = function () {
|
|
|
|
var text = getContent();
|
|
|
|
var text = getContent();
|
|
|
|
var blob = new Blob([text], {type: 'plain/text'});
|
|
|
|
var blob = new Blob([text], {type: 'plain/text'});
|
|
|
@ -354,6 +402,20 @@ define([
|
|
|
|
index: ooChannel.cpIndex
|
|
|
|
index: ooChannel.cpIndex
|
|
|
|
};
|
|
|
|
};
|
|
|
|
fixSheets();
|
|
|
|
fixSheets();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var hasDrawings = checkDrawings();
|
|
|
|
|
|
|
|
console.log(hasDrawings);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (hasDrawings) {
|
|
|
|
|
|
|
|
ooChannel.ready = false;
|
|
|
|
|
|
|
|
ooChannel.queue = [];
|
|
|
|
|
|
|
|
data.callback = function () {
|
|
|
|
|
|
|
|
console.error('reload');
|
|
|
|
|
|
|
|
resetData(blob, file);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
APP.FM.handleFile(blob, data);
|
|
|
|
APP.FM.handleFile(blob, data);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@ -468,6 +530,7 @@ define([
|
|
|
|
var getParticipants = function () {
|
|
|
|
var getParticipants = function () {
|
|
|
|
var users = metadataMgr.getMetadata().users;
|
|
|
|
var users = metadataMgr.getMetadata().users;
|
|
|
|
var i = 1;
|
|
|
|
var i = 1;
|
|
|
|
|
|
|
|
var myIdx = false;
|
|
|
|
var p = Object.keys(content.ids || {}).map(function (id) {
|
|
|
|
var p = Object.keys(content.ids || {}).map(function (id) {
|
|
|
|
var nId = id.slice(0,32);
|
|
|
|
var nId = id.slice(0,32);
|
|
|
|
var ooId = content.ids[id].ooid;
|
|
|
|
var ooId = content.ids[id].ooid;
|
|
|
@ -838,9 +901,8 @@ define([
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var ooLoaded = false;
|
|
|
|
var startOO = function (blob, file, force) {
|
|
|
|
var startOO = function (blob, file) {
|
|
|
|
if (APP.ooconfig && !force) { return void console.error('already started'); }
|
|
|
|
if (APP.ooconfig) { return void console.error('already started'); }
|
|
|
|
|
|
|
|
var url = URL.createObjectURL(blob);
|
|
|
|
var url = URL.createObjectURL(blob);
|
|
|
|
var lock = readOnly || APP.migrate;
|
|
|
|
var lock = readOnly || APP.migrate;
|
|
|
|
|
|
|
|
|
|
|
@ -910,7 +972,6 @@ define([
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"onDocumentReady": function () {
|
|
|
|
"onDocumentReady": function () {
|
|
|
|
|
|
|
|
|
|
|
|
// The doc is ready, fix the worksheets IDs and push the queue
|
|
|
|
// The doc is ready, fix the worksheets IDs and push the queue
|
|
|
|
fixSheets();
|
|
|
|
fixSheets();
|
|
|
|
// Push changes since last cp
|
|
|
|
// Push changes since last cp
|
|
|
@ -928,6 +989,12 @@ define([
|
|
|
|
// Allow edition
|
|
|
|
// Allow edition
|
|
|
|
setEditable(true);
|
|
|
|
setEditable(true);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isLockedModal.modal && force) {
|
|
|
|
|
|
|
|
isLockedModal.modal.closeModal();
|
|
|
|
|
|
|
|
delete isLockedModal.modal;
|
|
|
|
|
|
|
|
$('#cp-app-oo-editor > iframe')[0].contentWindow.focus();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (APP.migrate && !readOnly) {
|
|
|
|
if (APP.migrate && !readOnly) {
|
|
|
|
var div = h('div.cp-oo-x2tXls', [
|
|
|
|
var div = h('div.cp-oo-x2tXls', [
|
|
|
|
h('span.fa.fa-spin.fa-spinner'),
|
|
|
|
h('span.fa.fa-spin.fa-spinner'),
|
|
|
@ -1365,9 +1432,7 @@ define([
|
|
|
|
}, 100);
|
|
|
|
}, 100);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var loadLastDocument = function () {
|
|
|
|
var loadLastDocument = function (lastCp, onCpError, cb) {
|
|
|
|
var lastCp = getLastCp();
|
|
|
|
|
|
|
|
if (!lastCp) { return; }
|
|
|
|
|
|
|
|
ooChannel.cpIndex = lastCp.index || 0;
|
|
|
|
ooChannel.cpIndex = lastCp.index || 0;
|
|
|
|
var parsed = Hash.parsePadUrl(lastCp.file);
|
|
|
|
var parsed = Hash.parsePadUrl(lastCp.file);
|
|
|
|
var secret = Hash.getSecrets('file', parsed.hash);
|
|
|
|
var secret = Hash.getSecrets('file', parsed.hash);
|
|
|
@ -1381,6 +1446,7 @@ define([
|
|
|
|
xhr.responseType = 'arraybuffer';
|
|
|
|
xhr.responseType = 'arraybuffer';
|
|
|
|
xhr.onload = function () {
|
|
|
|
xhr.onload = function () {
|
|
|
|
if (/^4/.test('' + this.status)) {
|
|
|
|
if (/^4/.test('' + this.status)) {
|
|
|
|
|
|
|
|
onCpError();
|
|
|
|
return void console.error('XHR error', this.status);
|
|
|
|
return void console.error('XHR error', this.status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var arrayBuffer = xhr.response;
|
|
|
|
var arrayBuffer = xhr.response;
|
|
|
@ -1389,19 +1455,32 @@ define([
|
|
|
|
FileCrypto.decrypt(u8, key, function (err, decrypted) {
|
|
|
|
FileCrypto.decrypt(u8, key, function (err, decrypted) {
|
|
|
|
if (err) { return void console.error(err); }
|
|
|
|
if (err) { return void console.error(err); }
|
|
|
|
var blob = new Blob([decrypted.content], {type: 'plain/text'});
|
|
|
|
var blob = new Blob([decrypted.content], {type: 'plain/text'});
|
|
|
|
|
|
|
|
if (cb) {
|
|
|
|
|
|
|
|
return cb(blob, getFileType());
|
|
|
|
|
|
|
|
}
|
|
|
|
startOO(blob, getFileType());
|
|
|
|
startOO(blob, getFileType());
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
xhr.onerror = function () {
|
|
|
|
|
|
|
|
onCpError();
|
|
|
|
|
|
|
|
};
|
|
|
|
xhr.send(null);
|
|
|
|
xhr.send(null);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
var loadDocument = function (noCp, useNewDefault) {
|
|
|
|
var loadDocument = function (noCp, useNewDefault, i) {
|
|
|
|
if (ooLoaded) { return; }
|
|
|
|
if (ooLoaded) { return; }
|
|
|
|
var type = common.getMetadataMgr().getPrivateData().ooType;
|
|
|
|
var type = common.getMetadataMgr().getPrivateData().ooType;
|
|
|
|
var file = getFileType();
|
|
|
|
var file = getFileType();
|
|
|
|
if (!noCp) {
|
|
|
|
if (!noCp) {
|
|
|
|
|
|
|
|
var lastCp = getLastCp(false, i);
|
|
|
|
|
|
|
|
// If the last checkpoint is empty, load the "initial" doc instead
|
|
|
|
|
|
|
|
if (!lastCp || !lastCp.file) { return void loadDocument(true, useNewDefault); }
|
|
|
|
// Load latest checkpoint
|
|
|
|
// Load latest checkpoint
|
|
|
|
return void loadLastDocument();
|
|
|
|
return void loadLastDocument(lastCp, function () {
|
|
|
|
|
|
|
|
// Checkpoint error: load the previous one
|
|
|
|
|
|
|
|
i = i || 0;
|
|
|
|
|
|
|
|
loadDocument(noCp, useNewDefault, ++i);
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var newText;
|
|
|
|
var newText;
|
|
|
|
switch (type) {
|
|
|
|
switch (type) {
|
|
|
@ -1659,6 +1738,19 @@ define([
|
|
|
|
|
|
|
|
|
|
|
|
var reloadPopup = false;
|
|
|
|
var reloadPopup = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var checkNewCheckpoint = function () {
|
|
|
|
|
|
|
|
var hasDrawings = checkDrawings();
|
|
|
|
|
|
|
|
if (hasDrawings) {
|
|
|
|
|
|
|
|
console.error('reload');
|
|
|
|
|
|
|
|
var lastCp = getLastCp();
|
|
|
|
|
|
|
|
loadLastDocument(lastCp, function () {
|
|
|
|
|
|
|
|
// On error, do nothing
|
|
|
|
|
|
|
|
}, function (blob, type) {
|
|
|
|
|
|
|
|
resetData(blob, type);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
config.onRemote = function () {
|
|
|
|
config.onRemote = function () {
|
|
|
|
if (initializing) { return; }
|
|
|
|
if (initializing) { return; }
|
|
|
|
var userDoc = APP.realtime.getUserDoc();
|
|
|
|
var userDoc = APP.realtime.getUserDoc();
|
|
|
@ -1684,10 +1776,18 @@ define([
|
|
|
|
var latest = getLastCp(true);
|
|
|
|
var latest = getLastCp(true);
|
|
|
|
var newLatest = getLastCp();
|
|
|
|
var newLatest = getLastCp();
|
|
|
|
if (newLatest.index > latest.index) {
|
|
|
|
if (newLatest.index > latest.index) {
|
|
|
|
|
|
|
|
var hasDrawings = checkDrawings();
|
|
|
|
|
|
|
|
if (hasDrawings) {
|
|
|
|
|
|
|
|
ooChannel.ready = false;
|
|
|
|
|
|
|
|
ooChannel.queue = [];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// New checkpoint
|
|
|
|
sframeChan.query('Q_OO_SAVE', {
|
|
|
|
sframeChan.query('Q_OO_SAVE', {
|
|
|
|
hash: newLatest.hash,
|
|
|
|
hash: newLatest.hash,
|
|
|
|
url: newLatest.file
|
|
|
|
url: newLatest.file
|
|
|
|
}, function () { });
|
|
|
|
}, function () {
|
|
|
|
|
|
|
|
checkNewCheckpoint();
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
oldHashes = JSON.parse(JSON.stringify(content.hashes));
|
|
|
|
oldHashes = JSON.parse(JSON.stringify(content.hashes));
|
|
|
|
}
|
|
|
|
}
|
|
|
|