Merge branch 'master' into fix-#254
This commit is contained in:
@@ -1,16 +1,14 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) "../../customize/src/less2/include/framework.less";
|
||||
@import (reference) "../../customize/src/less2/include/browser.less";
|
||||
@import (reference) "../../customize/src/less2/include/markdown.less";
|
||||
@import (reference) "../../customize/src/less2/include/framework.less";
|
||||
|
||||
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_code-bg,
|
||||
@warn-color: @colortheme_code-warn,
|
||||
@color: @colortheme_code-color
|
||||
);
|
||||
|
||||
// body
|
||||
&.cp-app-code {
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_code-bg,
|
||||
@warn-color: @colortheme_code-warn,
|
||||
@color: @colortheme_code-color
|
||||
);
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-height: 100%;
|
||||
|
||||
@@ -37,6 +37,8 @@ define([
|
||||
'cm/addon/fold/comment-fold',
|
||||
'cm/addon/display/placeholder',
|
||||
|
||||
'less!/code/app-code.less'
|
||||
|
||||
], function (
|
||||
$,
|
||||
DiffMd,
|
||||
|
||||
@@ -4,9 +4,11 @@ const define = (x:any, y:any) => {};
|
||||
const require = define;
|
||||
*/
|
||||
define([
|
||||
'/api/config'
|
||||
], function (Config) { /*::});module.exports = (function() {
|
||||
'/api/config',
|
||||
'/bower_components/nthen/index.js'
|
||||
], function (Config, nThen) { /*::});module.exports = (function() {
|
||||
const Config = (undefined:any);
|
||||
const nThen = (undefined:any);
|
||||
*/
|
||||
|
||||
var module = { exports: {} };
|
||||
@@ -100,6 +102,10 @@ define([
|
||||
require(['/bower_components/less/dist/less.min.js'], function (Less) {
|
||||
if (lessEngine) { return void cb(lessEngine); }
|
||||
lessEngine = Less;
|
||||
Less.functions.functionRegistry.add('LessLoader_currentFile', function () {
|
||||
return new Less.tree.UnicodeDescriptor('"' +
|
||||
fixURL(this.currentFileInfo.filename) + '"');
|
||||
});
|
||||
var doXHR = lessEngine.FileManager.prototype.doXHR;
|
||||
lessEngine.FileManager.prototype.doXHR = function (url, type, callback, errback) {
|
||||
url = fixURL(url);
|
||||
@@ -133,24 +139,38 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.load = function (url /*:string*/, cb /*:()=>void*/) {
|
||||
var btime = +new Date();
|
||||
var loadSubmodulesAndInject = function (css, url, cb, stack) {
|
||||
inject(css, url);
|
||||
nThen(function (w) {
|
||||
css.replace(/\-\-LessLoader_require\:\s*"([^"]*)"\s*;/g, function (all, u) {
|
||||
u = u.replace(/\?.*$/, '');
|
||||
module.exports.load(u, w(), stack);
|
||||
return '';
|
||||
});
|
||||
}).nThen(function () { cb(); });
|
||||
};
|
||||
|
||||
module.exports.load = function (url /*:string*/, cb /*:()=>void*/, stack /*:?Array<string>*/) {
|
||||
var btime = stack ? null : +new Date();
|
||||
stack = stack || [];
|
||||
if (stack.indexOf(url) > -1) { return void cb(); }
|
||||
var timeout = setTimeout(function () { console.log('failed', url); }, 10000);
|
||||
var done = function () {
|
||||
console.log("Compiling [" + url + "] took " + (+new Date() - btime) + "ms");
|
||||
clearTimeout(timeout);
|
||||
if (btime) {
|
||||
console.log("Compiling [" + url + "] took " + (+new Date() - btime) + "ms");
|
||||
}
|
||||
cb();
|
||||
};
|
||||
stack.push(url);
|
||||
cacheGet(url, function (css) {
|
||||
if (css) {
|
||||
inject(css, url);
|
||||
return void done();
|
||||
}
|
||||
if (css) { return void loadSubmodulesAndInject(css, url, done, stack); }
|
||||
console.log('CACHE MISS ' + url);
|
||||
((/\.less([\?\#].*)?$/.test(url)) ? loadLess : loadCSS)(url, function (err, css) {
|
||||
if (!css) { return void console.error(err); }
|
||||
var output = fixAllURLs(css, url);
|
||||
cachePut(url, output);
|
||||
inject(output, url);
|
||||
done();
|
||||
loadSubmodulesAndInject(output, url, done, stack);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -126,5 +126,11 @@ define(function() {
|
||||
// Warning: This is an experimental feature. It will be enabled by default once we're sure it's stable.
|
||||
config.disableWorkers = true;
|
||||
|
||||
// Shared folder are in a beta-test state. They are likely to disappear from a user's drive
|
||||
// spontaneously, resulting in the deletion of the entire folder's content.
|
||||
// We highly recommend to keep them disabled until they are stable enough to be enabled
|
||||
// by default by the CryptPad developers.
|
||||
config.disableSharedFolders = true;
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
@@ -398,10 +398,16 @@ Version 1
|
||||
Hash.findWeaker = function (href, channel, recents) {
|
||||
var parsed = parsePadUrl(href);
|
||||
if (!parsed.hash) { return false; }
|
||||
// We can't have a weaker hash if we're already in view mode
|
||||
if (parsed.hashData && parsed.hashData.mode === 'view') { return; }
|
||||
var weaker;
|
||||
Object.keys(recents).some(function (id) {
|
||||
var pad = recents[id];
|
||||
var p = parsePadUrl(pad.href);
|
||||
if (pad.href || !pad.roHref) {
|
||||
// This pad has an edit link, so it can't be weaker
|
||||
return;
|
||||
}
|
||||
var p = parsePadUrl(pad.roHref);
|
||||
if (p.type !== parsed.type) { return; } // Not the same type
|
||||
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
|
||||
if (channel !== pad.channel) { return; } // Not the same channel
|
||||
@@ -430,6 +436,10 @@ Version 1
|
||||
var stronger;
|
||||
Object.keys(recents).some(function (id) {
|
||||
var pad = recents[id];
|
||||
if (!pad.href) {
|
||||
// This pad doesn't have an edit link, so it can't be stronger
|
||||
return;
|
||||
}
|
||||
var p = parsePadUrl(pad.href);
|
||||
if (p.type !== parsed.type) { return; } // Not the same type
|
||||
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
if (!document.querySelector("#alertifyCSS")) {
|
||||
// Prevent alertify from injecting CSS, we create our own in alertify.less.
|
||||
// see: https://github.com/alertifyjs/alertify.js/blob/v1.0.11/src/js/alertify.js#L414
|
||||
var head = document.getElementsByTagName("head")[0];
|
||||
var css = document.createElement("span");
|
||||
css.id = "alertifyCSS";
|
||||
css.setAttribute('data-but-why', 'see: common-interface.js');
|
||||
head.insertBefore(css, head.firstChild);
|
||||
}
|
||||
define([
|
||||
'jquery',
|
||||
'/customize/messages.js',
|
||||
@@ -665,7 +674,7 @@ define([
|
||||
// Update the current state
|
||||
loading.driveState = data.state;
|
||||
data.progress = data.progress || 100;
|
||||
data.msg = Messages['loading_drive_'+data.state] || '';
|
||||
data.msg = Messages['loading_drive_'+ Math.floor(data.state)] || '';
|
||||
$progress.html(data.msg);
|
||||
if (data.progress) {
|
||||
$progress.append(h('div.cp-loading-progress-bar', [
|
||||
@@ -761,7 +770,7 @@ define([
|
||||
UI.getFileIcon = function (data) {
|
||||
var $icon = UI.getIcon();
|
||||
if (!data) { return $icon; }
|
||||
var href = data.href;
|
||||
var href = data.href || data.roHref;
|
||||
var type = data.type;
|
||||
if (!href && !type) { return $icon; }
|
||||
|
||||
@@ -826,13 +835,13 @@ define([
|
||||
var out = false;
|
||||
var xId = $(x).attr('aria-describedby');
|
||||
if (xId) {
|
||||
if (xId.indexOf('tippy-tooltip-') === 0) {
|
||||
if (xId.indexOf('tippy-') === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$(x).find('[aria-describedby]').each(function (i, el) {
|
||||
var id = el.getAttribute('aria-describedby');
|
||||
if (id.indexOf('tippy-tooltip-') !== 0) { return; }
|
||||
if (id.indexOf('tippy-') !== 0) { return; }
|
||||
out = true;
|
||||
});
|
||||
return out;
|
||||
|
||||
@@ -13,6 +13,7 @@ define([
|
||||
'/customize/messages.js',
|
||||
'/customize/application_config.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'css!/customize/fonts/cptools/style.css'
|
||||
], function ($, Config, Util, Hash, Language, UI, Constants, Feedback, h, MediaTag, Clipboard,
|
||||
Messages, AppConfig, NThen) {
|
||||
var UIElements = {};
|
||||
@@ -73,26 +74,14 @@ define([
|
||||
data.password = val;
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
var base = common.getMetadataMgr().getPrivateData().origin;
|
||||
common.getPadAttribute('href', waitFor(function (err, val) {
|
||||
var base = common.getMetadataMgr().getPrivateData().origin;
|
||||
|
||||
var parsed = Hash.parsePadUrl(val);
|
||||
if (parsed.hashData.mode === "view") {
|
||||
data.roHref = base + val;
|
||||
return;
|
||||
}
|
||||
|
||||
// We're not in a read-only pad
|
||||
if (!val) { return; }
|
||||
data.href = base + val;
|
||||
|
||||
// Get Read-only href
|
||||
if (parsed.hashData.type !== "pad") { return; }
|
||||
var i = data.href.indexOf('#') + 1;
|
||||
var hBase = data.href.slice(0, i);
|
||||
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
||||
if (!hrefsecret.keys) { return; }
|
||||
var viewHash = Hash.getViewHashFromKeys(hrefsecret);
|
||||
data.roHref = hBase + viewHash;
|
||||
}));
|
||||
common.getPadAttribute('roHref', waitFor(function (err, val) {
|
||||
if (!val) { return; }
|
||||
data.roHref = base + val;
|
||||
}));
|
||||
common.getPadAttribute('channel', waitFor(function (err, val) {
|
||||
data.channel = val;
|
||||
@@ -137,82 +126,86 @@ define([
|
||||
id: 'cp-app-prop-owners',
|
||||
}));
|
||||
|
||||
var expire = Messages.creation_expireFalse;
|
||||
if (data.expire && typeof (data.expire) === "number") {
|
||||
expire = new Date(data.expire).toLocaleString();
|
||||
}
|
||||
$('<label>', {'for': 'cp-app-prop-expire'}).text(Messages.creation_expiration)
|
||||
.appendTo($d);
|
||||
$d.append(UI.dialog.selectable(expire, {
|
||||
id: 'cp-app-prop-expire',
|
||||
}));
|
||||
|
||||
var hasPassword = data.password;
|
||||
if (hasPassword) {
|
||||
$('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
|
||||
.appendTo($d);
|
||||
var password = UI.passwordInput({
|
||||
id: 'cp-app-prop-password',
|
||||
readonly: 'readonly'
|
||||
});
|
||||
var $pwInput = $(password).find('.cp-password-input');
|
||||
$pwInput.val(data.password).click(function () {
|
||||
$pwInput[0].select();
|
||||
});
|
||||
$d.append(password);
|
||||
}
|
||||
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
if (owned && parsed.hashData.type === 'pad') {
|
||||
var sframeChan = common.getSframeChannel();
|
||||
var changePwTitle = Messages.properties_changePassword;
|
||||
var changePwConfirm = Messages.properties_confirmChange;
|
||||
if (!hasPassword) {
|
||||
changePwTitle = Messages.properties_addPassword;
|
||||
changePwConfirm = Messages.properties_confirmNew;
|
||||
if (!data.noExpiration) {
|
||||
var expire = Messages.creation_expireFalse;
|
||||
if (data.expire && typeof (data.expire) === "number") {
|
||||
expire = new Date(data.expire).toLocaleString();
|
||||
}
|
||||
$('<label>', {'for': 'cp-app-prop-change-password'})
|
||||
.text(changePwTitle).appendTo($d);
|
||||
var newPassword = UI.passwordInput({
|
||||
id: 'cp-app-prop-change-password',
|
||||
style: 'flex: 1;'
|
||||
});
|
||||
var passwordOk = h('button', Messages.properties_changePasswordButton);
|
||||
var changePass = h('span.cp-password-container', [
|
||||
newPassword,
|
||||
passwordOk
|
||||
]);
|
||||
$(passwordOk).click(function () {
|
||||
var newPass = $(newPassword).find('input').val();
|
||||
if (data.password === newPass ||
|
||||
(!data.password && !newPass)) {
|
||||
return void UI.alert(Messages.properties_passwordSame);
|
||||
$('<label>', {'for': 'cp-app-prop-expire'}).text(Messages.creation_expiration)
|
||||
.appendTo($d);
|
||||
$d.append(UI.dialog.selectable(expire, {
|
||||
id: 'cp-app-prop-expire',
|
||||
}));
|
||||
}
|
||||
|
||||
if (!data.noPassword) {
|
||||
var hasPassword = data.password;
|
||||
if (hasPassword) {
|
||||
$('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
|
||||
.appendTo($d);
|
||||
var password = UI.passwordInput({
|
||||
id: 'cp-app-prop-password',
|
||||
readonly: 'readonly'
|
||||
});
|
||||
var $pwInput = $(password).find('.cp-password-input');
|
||||
$pwInput.val(data.password).click(function () {
|
||||
$pwInput[0].select();
|
||||
});
|
||||
$d.append(password);
|
||||
}
|
||||
|
||||
var parsed = Hash.parsePadUrl(data.href || data.roHref);
|
||||
if (owned && parsed.hashData.type === 'pad') {
|
||||
var sframeChan = common.getSframeChannel();
|
||||
var changePwTitle = Messages.properties_changePassword;
|
||||
var changePwConfirm = Messages.properties_confirmChange;
|
||||
if (!hasPassword) {
|
||||
changePwTitle = Messages.properties_addPassword;
|
||||
changePwConfirm = Messages.properties_confirmNew;
|
||||
}
|
||||
UI.confirm(changePwConfirm, function (yes) {
|
||||
if (!yes) { return; }
|
||||
sframeChan.query("Q_PAD_PASSWORD_CHANGE", {
|
||||
href: data.href,
|
||||
password: newPass
|
||||
}, function (err, data) {
|
||||
if (err || data.error) {
|
||||
return void UI.alert(Messages.properties_passwordError);
|
||||
}
|
||||
UI.findOKButton().click();
|
||||
// If we didn't have a password, we have to add the /p/
|
||||
// If we had a password and we changed it to a new one, we just have to reload
|
||||
// If we had a password and we removed it, we have to remove the /p/
|
||||
if (data.warning) {
|
||||
return void UI.alert(Messages.properties_passwordWarning, function () {
|
||||
common.gotoURL(hasPassword && newPass ? undefined : data.href);
|
||||
}, {force: true});
|
||||
}
|
||||
return void UI.alert(Messages.properties_passwordSuccess, function () {
|
||||
common.gotoURL(hasPassword && newPass ? undefined : data.href);
|
||||
}, {force: true});
|
||||
$('<label>', {'for': 'cp-app-prop-change-password'})
|
||||
.text(changePwTitle).appendTo($d);
|
||||
var newPassword = UI.passwordInput({
|
||||
id: 'cp-app-prop-change-password',
|
||||
style: 'flex: 1;'
|
||||
});
|
||||
var passwordOk = h('button', Messages.properties_changePasswordButton);
|
||||
var changePass = h('span.cp-password-container', [
|
||||
newPassword,
|
||||
passwordOk
|
||||
]);
|
||||
$(passwordOk).click(function () {
|
||||
var newPass = $(newPassword).find('input').val();
|
||||
if (data.password === newPass ||
|
||||
(!data.password && !newPass)) {
|
||||
return void UI.alert(Messages.properties_passwordSame);
|
||||
}
|
||||
UI.confirm(changePwConfirm, function (yes) {
|
||||
if (!yes) { return; }
|
||||
sframeChan.query("Q_PAD_PASSWORD_CHANGE", {
|
||||
href: data.href || data.roHref,
|
||||
password: newPass
|
||||
}, function (err, data) {
|
||||
if (err || data.error) {
|
||||
return void UI.alert(Messages.properties_passwordError);
|
||||
}
|
||||
UI.findOKButton().click();
|
||||
// If we didn't have a password, we have to add the /p/
|
||||
// If we had a password and we changed it to a new one, we just have to reload
|
||||
// If we had a password and we removed it, we have to remove the /p/
|
||||
if (data.warning) {
|
||||
return void UI.alert(Messages.properties_passwordWarning, function () {
|
||||
common.gotoURL(hasPassword && newPass ? undefined : (data.href || data.roHref));
|
||||
}, {force: true});
|
||||
}
|
||||
return void UI.alert(Messages.properties_passwordSuccess, function () {
|
||||
common.gotoURL(hasPassword && newPass ? undefined : (data.href || data.roHref));
|
||||
}, {force: true});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
$d.append(changePass);
|
||||
$d.append(changePass);
|
||||
}
|
||||
}
|
||||
|
||||
cb(void 0, $d);
|
||||
@@ -242,17 +235,21 @@ define([
|
||||
}));
|
||||
}
|
||||
|
||||
$('<label>', {'for': 'cp-app-prop-ctime'}).text(Messages.fm_creation)
|
||||
.appendTo($d);
|
||||
$d.append(UI.dialog.selectable(new Date(data.ctime).toLocaleString(), {
|
||||
id: 'cp-app-prop-ctime',
|
||||
}));
|
||||
if (data.ctime) {
|
||||
$('<label>', {'for': 'cp-app-prop-ctime'}).text(Messages.fm_creation)
|
||||
.appendTo($d);
|
||||
$d.append(UI.dialog.selectable(new Date(data.ctime).toLocaleString(), {
|
||||
id: 'cp-app-prop-ctime',
|
||||
}));
|
||||
}
|
||||
|
||||
$('<label>', {'for': 'cp-app-prop-atime'}).text(Messages.fm_lastAccess)
|
||||
.appendTo($d);
|
||||
$d.append(UI.dialog.selectable(new Date(data.atime).toLocaleString(), {
|
||||
id: 'cp-app-prop-atime',
|
||||
}));
|
||||
if (data.atime) {
|
||||
$('<label>', {'for': 'cp-app-prop-atime'}).text(Messages.fm_lastAccess)
|
||||
.appendTo($d);
|
||||
$d.append(UI.dialog.selectable(new Date(data.atime).toLocaleString(), {
|
||||
id: 'cp-app-prop-atime',
|
||||
}));
|
||||
}
|
||||
|
||||
if (common.isLoggedIn() && AppConfig.enablePinning) {
|
||||
// check the size of this file...
|
||||
@@ -463,6 +460,7 @@ define([
|
||||
var pathname = config.pathname;
|
||||
var hashes = config.hashes;
|
||||
var common = config.common;
|
||||
var fileData = config.fileData;
|
||||
|
||||
if (!hashes.fileHash) { throw new Error("You must provide a file hash"); }
|
||||
var url = origin + pathname + '#' + hashes.fileHash;
|
||||
@@ -498,7 +496,7 @@ define([
|
||||
UI.dialog.selectable(common.getMediatagScript()),
|
||||
h('p', Messages.fileEmbedTag),
|
||||
h('br'),
|
||||
UI.dialog.selectable(common.getMediatagFromHref(url)),
|
||||
UI.dialog.selectable(common.getMediatagFromHref(fileData)),
|
||||
]);
|
||||
var embedButtons = [{
|
||||
name: Messages.cancel,
|
||||
@@ -508,7 +506,7 @@ define([
|
||||
className: 'primary',
|
||||
name: Messages.share_mediatagCopy,
|
||||
onClick: function () {
|
||||
var v = common.getMediatagFromHref(url);
|
||||
var v = common.getMediatagFromHref(fileData);
|
||||
var success = Clipboard.copy(v);
|
||||
if (success) { UI.log(Messages.shareSuccess); }
|
||||
},
|
||||
@@ -533,6 +531,35 @@ define([
|
||||
}
|
||||
return tabs;
|
||||
};
|
||||
UIElements.createSFShareModal = function (config) {
|
||||
var origin = config.origin;
|
||||
var pathname = config.pathname;
|
||||
var hashes = config.hashes;
|
||||
|
||||
if (!hashes.editHash) { throw new Error("You must provide a valid hash"); }
|
||||
var url = origin + pathname + '#' + hashes.editHash;
|
||||
|
||||
// Share link tab
|
||||
var link = h('div.cp-share-modal', [
|
||||
h('label', Messages.sharedFolders_share),
|
||||
h('br'),
|
||||
UI.dialog.selectable(url, { id: 'cp-share-link-preview', tabindex: 1 })
|
||||
]);
|
||||
var linkButtons = [{
|
||||
name: Messages.cancel,
|
||||
onClick: function () {},
|
||||
keys: [27]
|
||||
}, {
|
||||
className: 'primary',
|
||||
name: Messages.share_linkCopy,
|
||||
onClick: function () {
|
||||
var success = Clipboard.copy(url);
|
||||
if (success) { UI.log(Messages.shareSuccess); }
|
||||
},
|
||||
keys: [13]
|
||||
}];
|
||||
return UI.dialog.customModal(link, {buttons: linkButtons});
|
||||
};
|
||||
|
||||
UIElements.createButton = function (common, type, rightside, data, callback) {
|
||||
var AppConfig = common.getAppConfig();
|
||||
@@ -695,17 +722,27 @@ define([
|
||||
button
|
||||
.click(common.prepareFeedback(type))
|
||||
.click(function() {
|
||||
var msg = common.isLoggedIn() ? Messages.forgetPrompt : Messages.fm_removePermanentlyDialog;
|
||||
UI.confirm(msg, function (yes) {
|
||||
if (!yes) { return; }
|
||||
sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
|
||||
if (err) { return void callback(err); }
|
||||
var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted;
|
||||
var msg = common.fixLinks($('<div>').html(cMsg));
|
||||
UI.alert(msg);
|
||||
callback();
|
||||
sframeChan.query('Q_IS_ONLY_IN_SHARED_FOLDER', null, function (err, res) {
|
||||
if (err || res.error) { return void console.log(err || res.error); }
|
||||
var msg = Messages.forgetPrompt;
|
||||
if (res) {
|
||||
UI.alert(Messages.sharedFolders_forget);
|
||||
return;
|
||||
} else if (!common.isLoggedIn()) {
|
||||
msg = Messages.fm_removePermanentlyDialog;
|
||||
}
|
||||
UI.confirm(msg, function (yes) {
|
||||
if (!yes) { return; }
|
||||
sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
|
||||
if (err) { return void callback(err); }
|
||||
var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted;
|
||||
var msg = common.fixLinks($('<div>').html(cMsg));
|
||||
UI.alert(msg);
|
||||
callback();
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
break;
|
||||
@@ -2010,12 +2047,14 @@ define([
|
||||
allData.unshift({
|
||||
name: Messages.creation_newTemplate,
|
||||
id: -1,
|
||||
icon: h('span.fa.fa-bookmark')
|
||||
//icon: h('span.fa.fa-bookmark')
|
||||
icon: h('span.cptools.cptools-new-template')
|
||||
});
|
||||
allData.unshift({
|
||||
name: Messages.creation_noTemplate,
|
||||
id: 0,
|
||||
icon: h('span.fa.fa-file')
|
||||
//icon: h('span.fa.fa-file')
|
||||
icon: UI.getFileIcon({type: type})
|
||||
});
|
||||
var redraw = function (index) {
|
||||
if (index < 0) { i = 0; }
|
||||
@@ -2031,7 +2070,9 @@ define([
|
||||
}).appendTo($container);
|
||||
$span.data('id', obj.id);
|
||||
if (idx === selected) { $span.addClass('cp-creation-template-selected'); }
|
||||
$span.append(obj.icon || UI.getFileIcon({type: type}));
|
||||
if (!obj.thumbnail) {
|
||||
$span.append(obj.icon || h('span.cptools.cptools-template'));
|
||||
}
|
||||
$('<span>', {'class': 'cp-creation-template-element-name'}).text(name)
|
||||
.appendTo($span);
|
||||
$span.click(function () {
|
||||
|
||||
@@ -84,6 +84,11 @@ define([
|
||||
cb(obj);
|
||||
});
|
||||
};
|
||||
common.getSharedFolder = function (id, cb) {
|
||||
postMessage("GET_SHARED_FOLDER", id, function (obj) {
|
||||
cb(obj);
|
||||
});
|
||||
};
|
||||
// Settings and ready
|
||||
common.mergeAnonDrive = function (cb) {
|
||||
var data = {
|
||||
@@ -99,6 +104,27 @@ define([
|
||||
common.userObjectCommand = function (data, cb) {
|
||||
postMessage("DRIVE_USEROBJECT", data, cb);
|
||||
};
|
||||
common.restoreDrive = function (data, cb) {
|
||||
postMessage("SET", {
|
||||
key:['drive'],
|
||||
value: data
|
||||
}, function (obj) {
|
||||
cb(obj);
|
||||
}, {
|
||||
timeout: 5 * 60 * 1000
|
||||
});
|
||||
};
|
||||
common.addSharedFolder = function (secret, cb) {
|
||||
postMessage("ADD_SHARED_FOLDER", {
|
||||
path: ['root'],
|
||||
folderData: {
|
||||
href: '/drive/#' + Hash.getEditHashFromKeys(secret),
|
||||
roHref: '/drive/#' + Hash.getViewHashFromKeys(secret),
|
||||
channel: secret.channel,
|
||||
ctime: +new Date()
|
||||
}
|
||||
}, cb);
|
||||
};
|
||||
common.drive = {};
|
||||
common.drive.onLog = Util.mkEvent();
|
||||
common.drive.onChange = Util.mkEvent();
|
||||
@@ -294,6 +320,13 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
common.isOnlyInSharedFolder = function (data, cb) {
|
||||
postMessage("IS_ONLY_IN_SHARED_FOLDER", data, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj.error); }
|
||||
cb(null, obj);
|
||||
});
|
||||
};
|
||||
|
||||
common.setDisplayName = function (value, cb) {
|
||||
postMessage("SET_DISPLAY_NAME", value, cb);
|
||||
};
|
||||
@@ -506,7 +539,8 @@ define([
|
||||
|
||||
if (common.initialPath) {
|
||||
if (!data.path) {
|
||||
data.path = common.initialPath;
|
||||
data.path = Array.isArray(common.initialPath) ? common.initialPath
|
||||
: decodeURIComponent(common.initialPath).split(',');
|
||||
delete common.initialPath;
|
||||
}
|
||||
}
|
||||
@@ -620,7 +654,7 @@ define([
|
||||
if (!parsed.hash) { return void cb({ error: 'EINVAL_HREF' }); }
|
||||
|
||||
var warning = false;
|
||||
var newHash;
|
||||
var newHash, newRoHref;
|
||||
var oldChannel;
|
||||
var newSecret;
|
||||
|
||||
@@ -693,6 +727,11 @@ define([
|
||||
common.setPadAttribute('channel', newSecret.channel, waitFor(function (err) {
|
||||
if (err) { warning = true; }
|
||||
}), href);
|
||||
var viewHash = Hash.getViewHashFromKeys(newSecret);
|
||||
newRoHref = '/' + parsed.type + '/#' + viewHash;
|
||||
common.setPadAttribute('roHref', newRoHref, waitFor(function (err) {
|
||||
if (err) { warning = true; }
|
||||
}), href);
|
||||
|
||||
if (parsed.hashData.password && newPassword) { return; } // same hash
|
||||
common.setPadAttribute('href', newHref, waitFor(function (err) {
|
||||
@@ -702,7 +741,8 @@ define([
|
||||
cb({
|
||||
warning: warning,
|
||||
hash: newHash,
|
||||
href: newHref
|
||||
href: newHref,
|
||||
roHref: newRoHref
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1290,12 +1330,12 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
postMessage = function (cmd, data, cb) {
|
||||
postMessage = function (cmd, data, cb, opts) {
|
||||
cb = cb || function () {};
|
||||
chan.query(cmd, data, function (err, data) {
|
||||
if (err) { return void cb ({error: err}); }
|
||||
cb(data);
|
||||
});
|
||||
}, opts);
|
||||
};
|
||||
|
||||
console.log('Posting CONNECT');
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
@import (once) '../customize/src/less2/include/colortheme-all.less';
|
||||
@import '../customize/src/less2/include/modal.less';
|
||||
@import (reference) '../customize/src/less2/include/colortheme-all.less';
|
||||
@import (reference) '../customize/src/less2/include/modal.less';
|
||||
|
||||
.fileDialog_main () {
|
||||
#fileDialog {
|
||||
.modal_main();
|
||||
display: none;
|
||||
.cp-modal {
|
||||
.fileContainer {
|
||||
|
||||
@@ -6,84 +6,6 @@ define([
|
||||
], function (Crypt, FO, Hash, Realtime) {
|
||||
var exp = {};
|
||||
|
||||
var getType = function (el) {
|
||||
if (el === null) { return "null"; }
|
||||
return Array.isArray(el) ? "array" : typeof(el);
|
||||
};
|
||||
|
||||
var findAvailableKey = function (obj, key) {
|
||||
if (typeof (obj[key]) === "undefined") { return key; }
|
||||
var i = 1;
|
||||
var nkey = key;
|
||||
while (typeof (obj[nkey]) !== "undefined") {
|
||||
nkey = key + '_' + i;
|
||||
i++;
|
||||
}
|
||||
return nkey;
|
||||
};
|
||||
|
||||
var createFromPath = function (proxy, oldFo, path, id) {
|
||||
var root = proxy.drive;
|
||||
|
||||
if (!oldFo.isFile(id)) { return; }
|
||||
|
||||
var error = function (msg) {
|
||||
console.error(msg || "Unable to find that path", path);
|
||||
};
|
||||
|
||||
if (oldFo.isInTrashRoot(path)) {
|
||||
id = oldFo.find(path.slice(0,3));
|
||||
path.pop();
|
||||
}
|
||||
|
||||
var next, nextRoot;
|
||||
path.forEach(function (p, i) {
|
||||
if (!root) { return; }
|
||||
if (typeof(p) === "string") {
|
||||
if (getType(root) !== "object") { root = undefined; error(); return; }
|
||||
if (i === path.length - 1) {
|
||||
root[Hash.createChannelId()] = id;
|
||||
return;
|
||||
}
|
||||
next = getType(path[i+1]);
|
||||
nextRoot = getType(root[p]);
|
||||
if (nextRoot !== "undefined") {
|
||||
if (next === "string" && nextRoot === "object" || next === "number" && nextRoot === "array") {
|
||||
root = root[p];
|
||||
return;
|
||||
}
|
||||
p = findAvailableKey(root, p);
|
||||
}
|
||||
if (next === "number") {
|
||||
root[p] = [];
|
||||
root = root[p];
|
||||
return;
|
||||
}
|
||||
root[p] = {};
|
||||
root = root[p];
|
||||
return;
|
||||
}
|
||||
// Path contains a non-string element: it's an array index
|
||||
if (typeof(p) !== "number") { root = undefined; error(); return; }
|
||||
if (getType(root) !== "array") { root = undefined; error(); return; }
|
||||
if (i === path.length - 1) {
|
||||
if (root.indexOf(id) === -1) { root.push(id); }
|
||||
return;
|
||||
}
|
||||
next = getType(path[i+1]);
|
||||
if (next === "number") {
|
||||
error('2 consecutives arrays in the user object');
|
||||
root = undefined;
|
||||
//root.push([]);
|
||||
//root = root[root.length - 1];
|
||||
return;
|
||||
}
|
||||
root.push({});
|
||||
root = root[root.length - 1];
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
exp.anonDriveIntoUser = function (proxyData, fsHash, cb) {
|
||||
// Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb
|
||||
if (!fsHash || !proxyData.loggedIn) {
|
||||
@@ -105,43 +27,38 @@ define([
|
||||
if (parsed) {
|
||||
var proxy = proxyData.proxy;
|
||||
var oldFo = FO.init(parsed.drive, {
|
||||
loggedIn: proxyData.loggedIn,
|
||||
pinPads: function () {} // without pinPads /outer/userObject.js won't be loaded
|
||||
loggedIn: true,
|
||||
outer: true
|
||||
});
|
||||
var onMigrated = function () {
|
||||
oldFo.fixFiles(true);
|
||||
var newFo = proxyData.userObject;
|
||||
var oldRecentPads = parsed.drive[newFo.FILES_DATA];
|
||||
var newRecentPads = proxy.drive[newFo.FILES_DATA];
|
||||
var oldFiles = oldFo.getFiles([newFo.FILES_DATA]);
|
||||
var newHrefs = Object.keys(newRecentPads).map(function (id) {
|
||||
return newRecentPads[id].href;
|
||||
});
|
||||
var manager = proxyData.manager;
|
||||
var oldFiles = oldFo.getFiles([oldFo.FILES_DATA]);
|
||||
oldFiles.forEach(function (id) {
|
||||
var href = oldRecentPads[id].href;
|
||||
// Do not migrate a pad if we already have it, it would create a duplicate in the drive
|
||||
if (newHrefs.indexOf(href) !== -1) { return; }
|
||||
// If we have a stronger version, do not add the current href
|
||||
if (Hash.findStronger(href, oldRecentPads[id].channel, newRecentPads)) { return; }
|
||||
// If we have a weaker version, replace the href by the new one
|
||||
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted
|
||||
var weaker = Hash.findWeaker(href, oldRecentPads[id].channel, newRecentPads);
|
||||
if (weaker) {
|
||||
// Update RECENTPADS
|
||||
weaker.href = href;
|
||||
// Update the file in the drive
|
||||
newFo.replace(weaker.href, href);
|
||||
var data = oldFo.getFileData(id);
|
||||
var channel = data.channel;
|
||||
|
||||
var datas = manager.findChannel(channel, true);
|
||||
// Do not migrate a pad if we already have it, it would create a duplicate
|
||||
// in the drive
|
||||
if (datas.length !== 0) {
|
||||
// We want to merge a read-only pad: it can't be stronger than what
|
||||
// we already have so abort
|
||||
if (!data.href) { return; }
|
||||
|
||||
// We want to merge an edit pad: check if we have the same channel
|
||||
// but read-only and upgrade it in that case
|
||||
datas.forEach(function (pad) {
|
||||
if (!pad.href) { data.href = pad.href; }
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Here it means we have a new href, so we should add it to the drive at its old location
|
||||
var paths = oldFo.findFile(id);
|
||||
if (paths.length === 0) { return; }
|
||||
// Add the file data in our array and use the id to add the file
|
||||
var data = oldFo.getFileData(id);
|
||||
// Here it means we have a new href, so we should add it to the drive
|
||||
if (data) {
|
||||
newFo.pushData(data, function (err, id) {
|
||||
if (err) { return void console.error("Cannot import file:", data, err); }
|
||||
createFromPath(proxy, oldFo, paths[0], id);
|
||||
manager.addPad(null, data, function (err) {
|
||||
if (err) {
|
||||
return void console.error("Cannot import file:", data, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -153,7 +70,12 @@ define([
|
||||
Realtime.whenRealtimeSyncs(proxyData.realtime, cb);
|
||||
}
|
||||
};
|
||||
oldFo.migrate(onMigrated);
|
||||
if (oldFo && typeof(oldFo.migrate) === 'function') {
|
||||
oldFo.migrate(onMigrated);
|
||||
} else {
|
||||
console.log('oldFo.migrate is not a function');
|
||||
onMigrated();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (typeof(cb) === "function") { cb(); }
|
||||
|
||||
@@ -123,12 +123,58 @@ define([
|
||||
}));
|
||||
});
|
||||
});
|
||||
n.nThen(waitFor());
|
||||
n.nThen(waitFor(function () {
|
||||
Feedback.send('Migrate-6', true);
|
||||
userObject.version = version = 6;
|
||||
}));
|
||||
};
|
||||
if (version < 6) {
|
||||
addChannelId();
|
||||
Feedback.send('Migrate-6', true);
|
||||
userObject.version = version = 6;
|
||||
}
|
||||
}).nThen(function (waitFor) {
|
||||
var addRoHref = function () {
|
||||
var data = userObject.drive.filesData;
|
||||
var el, parsed;
|
||||
var n = nThen(function () {});
|
||||
var padsLength = Object.keys(data).length;
|
||||
Object.keys(data).forEach(function (k, i) {
|
||||
n = n.nThen(function (w) {
|
||||
setTimeout(w(function () {
|
||||
el = data[k];
|
||||
if (!el.href || (el.roHref && false)) {
|
||||
// Already migrated
|
||||
return void progress(7, Math.round(100*i/padsLength));
|
||||
}
|
||||
parsed = Hash.parsePadUrl(el.href);
|
||||
if (parsed.hashData.type !== "pad") {
|
||||
// No read-only mode for files
|
||||
return void progress(7, Math.round(100*i/padsLength));
|
||||
}
|
||||
if (parsed.hashData.mode === "view") {
|
||||
// This is a read-only pad in our drive
|
||||
el.roHref = el.href;
|
||||
delete el.href;
|
||||
console.log('Move href to roHref in filesData ', el.roHref);
|
||||
} else {
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||
var hash = Hash.getViewHashFromKeys(secret);
|
||||
if (hash) {
|
||||
// Version 0 won't have a view hash available
|
||||
el.roHref = '/' + parsed.type + '/#' + hash;
|
||||
console.log('Adding missing roHref in filesData ', el.href);
|
||||
}
|
||||
}
|
||||
progress(6, Math.round(100*i/padsLength));
|
||||
}));
|
||||
});
|
||||
});
|
||||
n.nThen(waitFor(function () {
|
||||
Feedback.send('Migrate-7', true);
|
||||
userObject.version = version = 7;
|
||||
}));
|
||||
};
|
||||
if (version < 7) {
|
||||
addRoHref();
|
||||
}
|
||||
/*}).nThen(function (waitFor) {
|
||||
// Test progress bar in the loading screen
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
define([
|
||||
'json.sortify',
|
||||
'/common/userObject.js',
|
||||
'/common/proxy-manager.js',
|
||||
'/common/migrate-user-object.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-util.js',
|
||||
@@ -18,7 +19,7 @@ define([
|
||||
'/bower_components/chainpad-listmap/chainpad-listmap.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/bower_components/saferphore/index.js',
|
||||
], function (Sortify, UserObject, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger,
|
||||
], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger,
|
||||
CpNfWorker, NetConfig, AppConfig,
|
||||
Crypto, ChainPad, Listmap, nThen, Saferphore) {
|
||||
var Store = {};
|
||||
@@ -27,17 +28,23 @@ define([
|
||||
var postMessage = function () {};
|
||||
var broadcast = function () {};
|
||||
var sendDriveEvent = function () {};
|
||||
var registerProxyEvents = function () {};
|
||||
|
||||
var storeHash;
|
||||
|
||||
var store = window.CryptPad_AsyncStore = {};
|
||||
|
||||
|
||||
var onSync = function (cb) {
|
||||
Realtime.whenRealtimeSyncs(store.realtime, cb);
|
||||
nThen(function (waitFor) {
|
||||
Realtime.whenRealtimeSyncs(store.realtime, waitFor());
|
||||
if (store.sharedFolders) {
|
||||
for (var k in store.sharedFolders) {
|
||||
Realtime.whenRealtimeSyncs(store.sharedFolders[k].realtime, waitFor());
|
||||
}
|
||||
}
|
||||
}).nThen(function () { cb(); });
|
||||
};
|
||||
|
||||
|
||||
Store.get = function (clientId, key, cb) {
|
||||
cb(Util.find(store.proxy, key));
|
||||
};
|
||||
@@ -55,6 +62,13 @@ define([
|
||||
onSync(cb);
|
||||
};
|
||||
|
||||
Store.getSharedFolder = function (clientId, id, cb) {
|
||||
if (store.manager.folders[id]) {
|
||||
return void cb(store.manager.folders[id].proxy);
|
||||
}
|
||||
cb({});
|
||||
};
|
||||
|
||||
Store.hasSigningKeys = function () {
|
||||
if (!store.proxy) { return; }
|
||||
return typeof(store.proxy.edPrivate) === 'string' &&
|
||||
@@ -78,17 +92,9 @@ define([
|
||||
if (!userChannel) { return null; }
|
||||
|
||||
// Get the list of pads' channel ID in your drive
|
||||
// This list is filtered so that it doesn't include pad owned by other users (you should
|
||||
// not pin these pads)
|
||||
var files = store.userObject.getFiles([store.userObject.FILES_DATA]);
|
||||
var edPublic = store.proxy.edPublic;
|
||||
var list = files.map(function (id) {
|
||||
var d = store.userObject.getFileData(id);
|
||||
if (d.owners && d.owners.length && edPublic &&
|
||||
d.owners.indexOf(edPublic) === -1) { return; }
|
||||
return d.channel;
|
||||
})
|
||||
.filter(function (x) { return x; });
|
||||
// This list is filtered so that it doesn't include pad owned by other users
|
||||
// It now includes channels from shared folders
|
||||
var list = store.manager.getChannelsList('pin');
|
||||
|
||||
// Get the avatar
|
||||
var profile = store.proxy.profile;
|
||||
@@ -111,19 +117,7 @@ define([
|
||||
};
|
||||
|
||||
var getExpirableChannelList = function () {
|
||||
var list = [];
|
||||
store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) {
|
||||
var data = store.userObject.getFileData(id);
|
||||
var edPublic = store.proxy.edPublic;
|
||||
|
||||
// Push channels owned by someone else or channel that should have expired
|
||||
// because of the expiration time
|
||||
if ((data.owners && data.owners.length && data.owners.indexOf(edPublic) === -1) ||
|
||||
(data.expire && data.expire < (+new Date()))) {
|
||||
list.push(data.channel);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
return store.manager.getChannelsList('expirable');
|
||||
};
|
||||
|
||||
var getCanonicalChannelList = function (expirable) {
|
||||
@@ -380,7 +374,7 @@ define([
|
||||
|
||||
Store.getDeletedPads = function (clientId, data, cb) {
|
||||
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
|
||||
var list = getCanonicalChannelList(true);
|
||||
var list = (data && data.list) || getCanonicalChannelList(true);
|
||||
if (!Array.isArray(list)) {
|
||||
return void cb({error: 'INVALID_FILE_LIST'});
|
||||
}
|
||||
@@ -435,10 +429,11 @@ define([
|
||||
cb(JSON.parse(JSON.stringify(metadata)));
|
||||
};
|
||||
|
||||
var makePad = function (href, title) {
|
||||
var makePad = function (href, roHref, title) {
|
||||
var now = +new Date();
|
||||
return {
|
||||
href: href,
|
||||
roHref: roHref,
|
||||
atime: now,
|
||||
ctime: now,
|
||||
title: title || Hash.getDefaultName(Hash.parsePadUrl(href)),
|
||||
@@ -446,16 +441,21 @@ define([
|
||||
};
|
||||
|
||||
Store.addPad = function (clientId, data, cb) {
|
||||
if (!data.href) { return void cb({error:'NO_HREF'}); }
|
||||
var pad = makePad(data.href, data.title);
|
||||
if (!data.href && !data.roHref) { return void cb({error:'NO_HREF'}); }
|
||||
if (!data.roHref) {
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
if (parsed.hashData.type === "pad") {
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
||||
data.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
|
||||
}
|
||||
}
|
||||
var pad = makePad(data.href, data.roHref, data.title);
|
||||
if (data.owners) { pad.owners = data.owners; }
|
||||
if (data.expire) { pad.expire = data.expire; }
|
||||
if (data.password) { pad.password = data.password; }
|
||||
if (data.channel) { pad.channel = data.channel; }
|
||||
store.userObject.pushData(pad, function (e, id) {
|
||||
if (e) { return void cb({error: "Error while adding a template:"+ e}); }
|
||||
var path = data.path || ['root'];
|
||||
store.userObject.add(id, path);
|
||||
store.manager.addPad(data.path, pad, function (e) {
|
||||
if (e) { return void cb({error: "Error while adding the pad:"+ e}); }
|
||||
sendDriveEvent('DRIVE_CHANGE', {
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
}, clientId);
|
||||
@@ -464,17 +464,7 @@ define([
|
||||
};
|
||||
|
||||
var getOwnedPads = function () {
|
||||
var list = [];
|
||||
store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) {
|
||||
var data = store.userObject.getFileData(id);
|
||||
var edPublic = store.proxy.edPublic;
|
||||
|
||||
// Push channels owned by someone else or channel that should have expired
|
||||
// because of the expiration time
|
||||
if (data.owners && data.owners.length === 1 && data.owners.indexOf(edPublic) !== -1) {
|
||||
list.push(data.channel);
|
||||
}
|
||||
});
|
||||
var list = store.manager.getChannelsList('owned');
|
||||
if (store.proxy.todo) {
|
||||
// No password for todo
|
||||
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
|
||||
@@ -596,33 +586,6 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
var getAttributeObject = function (attr) {
|
||||
if (typeof attr === "string") {
|
||||
console.error('DEPRECATED: use setAttribute with an array, not a string');
|
||||
return {
|
||||
path: ['settings'],
|
||||
obj: store.proxy.settings,
|
||||
key: attr
|
||||
};
|
||||
}
|
||||
if (!Array.isArray(attr)) { return void console.error("Attribute must be string or array"); }
|
||||
if (attr.length === 0) { return void console.error("Attribute can't be empty"); }
|
||||
var obj = store.proxy.settings;
|
||||
attr.forEach(function (el, i) {
|
||||
if (i === attr.length-1) { return; }
|
||||
if (!obj[el]) {
|
||||
obj[el] = {};
|
||||
}
|
||||
else if (typeof obj[el] !== "object") { return void console.error("Wrong attribute"); }
|
||||
obj = obj[el];
|
||||
});
|
||||
return {
|
||||
path: ['settings'].concat(attr),
|
||||
obj: obj,
|
||||
key: attr[attr.length-1]
|
||||
};
|
||||
};
|
||||
|
||||
// Set the display name (username) in the proxy
|
||||
Store.setDisplayName = function (clientId, value, cb) {
|
||||
store.proxy[Constants.displayNameKey] = value;
|
||||
@@ -651,7 +614,7 @@ define([
|
||||
* - value (String)
|
||||
*/
|
||||
Store.setPadAttribute = function (clientId, data, cb) {
|
||||
store.userObject.setPadAttribute(data.href, data.attr, data.value, function () {
|
||||
store.manager.setPadAttribute(data, function () {
|
||||
sendDriveEvent('DRIVE_CHANGE', {
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
}, clientId);
|
||||
@@ -659,11 +622,38 @@ define([
|
||||
});
|
||||
};
|
||||
Store.getPadAttribute = function (clientId, data, cb) {
|
||||
store.userObject.getPadAttribute(data.href, data.attr, function (err, val) {
|
||||
store.manager.getPadAttribute(data, function (err, val) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(val);
|
||||
});
|
||||
};
|
||||
|
||||
var getAttributeObject = function (attr) {
|
||||
if (typeof attr === "string") {
|
||||
console.error('DEPRECATED: use setAttribute with an array, not a string');
|
||||
return {
|
||||
path: ['settings'],
|
||||
obj: store.proxy.settings,
|
||||
key: attr
|
||||
};
|
||||
}
|
||||
if (!Array.isArray(attr)) { return void console.error("Attribute must be string or array"); }
|
||||
if (attr.length === 0) { return void console.error("Attribute can't be empty"); }
|
||||
var obj = store.proxy.settings;
|
||||
attr.forEach(function (el, i) {
|
||||
if (i === attr.length-1) { return; }
|
||||
if (!obj[el]) {
|
||||
obj[el] = {};
|
||||
}
|
||||
else if (typeof obj[el] !== "object") { return void console.error("Wrong attribute"); }
|
||||
obj = obj[el];
|
||||
});
|
||||
return {
|
||||
path: ['settings'].concat(attr),
|
||||
obj: obj,
|
||||
key: attr[attr.length-1]
|
||||
};
|
||||
};
|
||||
Store.setAttribute = function (clientId, data, cb) {
|
||||
try {
|
||||
var object = getAttributeObject(data.attr);
|
||||
@@ -681,11 +671,12 @@ define([
|
||||
|
||||
// Tags
|
||||
Store.listAllTags = function (clientId, data, cb) {
|
||||
cb(store.userObject.getTagsList());
|
||||
cb(store.manager.getTagsList());
|
||||
};
|
||||
|
||||
// Templates
|
||||
Store.getTemplates = function (clientId, data, cb) {
|
||||
// No templates in shared folders: we don't need the manager here
|
||||
var templateFiles = store.userObject.getFiles(['template']);
|
||||
var res = [];
|
||||
templateFiles.forEach(function (f) {
|
||||
@@ -695,6 +686,7 @@ define([
|
||||
cb(res);
|
||||
};
|
||||
Store.incrementTemplateUse = function (clientId, href) {
|
||||
// No templates in shared folders: we don't need the manager here
|
||||
store.userObject.getPadAttribute(href, 'used', function (err, data) {
|
||||
// This is a not critical function, abort in case of error to make sure we won't
|
||||
// create any issue with the user object or the async store
|
||||
@@ -705,6 +697,17 @@ define([
|
||||
};
|
||||
|
||||
// Pads
|
||||
Store.isOnlyInSharedFolder = function (clientId, channel, cb) {
|
||||
var res = store.manager.findChannel(channel);
|
||||
|
||||
// A pad is only in a shared worker if:
|
||||
// 1. this pad is in at least one proxy
|
||||
// 2. no proxy containing this pad is the main drive
|
||||
return cb (res.length && !res.some(function (obj) {
|
||||
// Main drive doesn't have an fId (folder ID)
|
||||
return !obj.fId;
|
||||
}));
|
||||
};
|
||||
Store.moveToTrash = function (clientId, data, cb) {
|
||||
var href = Hash.getRelativeHref(data.href);
|
||||
store.userObject.forget(href);
|
||||
@@ -728,78 +731,48 @@ define([
|
||||
if (channelData && channelData.wc && channel === channelData.wc.id) {
|
||||
owners = channelData.data.owners || undefined;
|
||||
}
|
||||
if (data.owners) {
|
||||
owners = data.owners;
|
||||
}
|
||||
|
||||
var expire;
|
||||
if (channelData && channelData.wc && channel === channelData.wc.id) {
|
||||
expire = +channelData.data.expire || undefined;
|
||||
}
|
||||
|
||||
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
|
||||
var isStronger;
|
||||
|
||||
// If we don't find the new channel in our existing pads, we'll have to add the pads
|
||||
// to filesData
|
||||
var contains;
|
||||
|
||||
// Update all pads that use the same channel but with a weaker hash
|
||||
// Edit > Edit (present) > View > View (present)
|
||||
for (var id in allPads) {
|
||||
var pad = allPads[id];
|
||||
if (!pad.href) { continue; }
|
||||
|
||||
var p2 = Hash.parsePadUrl(pad.href);
|
||||
var h2 = p2.hashData;
|
||||
|
||||
// Different types, proceed to the next one
|
||||
// No hash data: corrupted pad?
|
||||
if (p.type !== p2.type || !h2) { continue; }
|
||||
// Different channel: continue
|
||||
if (pad.channel !== channel) { continue; }
|
||||
|
||||
var shouldUpdate = p.hash.replace(/\/$/, '') === p2.hash.replace(/\/$/, '');
|
||||
|
||||
// If the hash is different but represents the same channel, check if weaker or stronger
|
||||
if (!shouldUpdate && h.version !== 0) {
|
||||
// We had view & now we have edit, update
|
||||
if (h2.mode === 'view' && h.mode === 'edit') { shouldUpdate = true; }
|
||||
// Same mode and we had present URL, update
|
||||
else if (h.mode === h2.mode && h2.present) { shouldUpdate = true; }
|
||||
// If we're here it means we have a weaker URL:
|
||||
// update the date but keep the existing hash
|
||||
else {
|
||||
pad.atime = +new Date();
|
||||
contains = true;
|
||||
continue;
|
||||
}
|
||||
var datas = store.manager.findChannel(channel);
|
||||
var contains = datas.length !== 0;
|
||||
datas.forEach(function (obj) {
|
||||
var pad = obj.data;
|
||||
pad.atime = +new Date();
|
||||
pad.title = title;
|
||||
if (owners || h.type !== "file") {
|
||||
// OWNED_FILES
|
||||
// Never remove owner for files
|
||||
pad.owners = owners;
|
||||
}
|
||||
pad.expire = expire;
|
||||
if (h.mode === 'view') { return; }
|
||||
|
||||
if (shouldUpdate) {
|
||||
contains = true;
|
||||
pad.atime = +new Date();
|
||||
pad.title = title;
|
||||
if (owners || h.type !== "file") {
|
||||
// OWNED_FILES
|
||||
// Never remove owner for files
|
||||
pad.owners = owners;
|
||||
}
|
||||
pad.expire = expire;
|
||||
|
||||
// If the href is different, it means we have a stronger one
|
||||
if (href !== pad.href) { isStronger = true; }
|
||||
pad.href = href;
|
||||
// If we only have rohref, it means we have a stronger href
|
||||
if (!pad.href) {
|
||||
// If we have a stronger url, remove the possible weaker from the trash.
|
||||
// If all of the weaker ones were in the trash, add the stronger to ROOT
|
||||
obj.userObject.restoreHref(href);
|
||||
}
|
||||
}
|
||||
|
||||
if (isStronger) {
|
||||
// If we have a stronger url, remove the possible weaker from the trash.
|
||||
// If all of the weaker ones were in the trash, add the stronger to ROOT
|
||||
store.userObject.restoreHref(href);
|
||||
}
|
||||
pad.href = href;
|
||||
});
|
||||
|
||||
// Add the pad if it does not exist in our drive
|
||||
if (!contains) {
|
||||
var roHref;
|
||||
if (h.mode === "view") {
|
||||
roHref = href;
|
||||
href = undefined;
|
||||
}
|
||||
Store.addPad(clientId, {
|
||||
href: href,
|
||||
roHref: roHref,
|
||||
channel: channel,
|
||||
title: title,
|
||||
owners: owners,
|
||||
@@ -819,7 +792,6 @@ define([
|
||||
// Filepicker app
|
||||
Store.getSecureFilesList = function (clientId, query, cb) {
|
||||
var list = {};
|
||||
var hashes = [];
|
||||
var types = query.types;
|
||||
var where = query.where;
|
||||
var filter = query.filter || {};
|
||||
@@ -834,19 +806,19 @@ define([
|
||||
}
|
||||
return filtered;
|
||||
};
|
||||
store.userObject.getFiles(where).forEach(function (id) {
|
||||
var data = store.userObject.getFileData(id);
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
store.manager.getSecureFilesList(where).forEach(function (obj) {
|
||||
var data = obj.data;
|
||||
var id = obj.id;
|
||||
var parsed = Hash.parsePadUrl(data.href || data.roHref);
|
||||
if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) &&
|
||||
hashes.indexOf(parsed.hash) === -1 &&
|
||||
!isFiltered(parsed.type, data)) {
|
||||
hashes.push(parsed.hash);
|
||||
list[id] = data;
|
||||
}
|
||||
});
|
||||
cb(list);
|
||||
};
|
||||
Store.getPadData = function (clientId, id, cb) {
|
||||
// FIXME: this is only used for templates at the moment, so we don't need the manager
|
||||
cb(store.userObject.getFileData(id));
|
||||
};
|
||||
|
||||
@@ -1192,37 +1164,60 @@ define([
|
||||
}]));
|
||||
};
|
||||
|
||||
// SHARED FOLDERS
|
||||
var loadSharedFolder = function (id, data, cb) {
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
var secret = Hash.getSecrets('drive', parsed.hash, data.password);
|
||||
var owners = data.owners;
|
||||
var listmapConfig = {
|
||||
data: {},
|
||||
websocketURL: NetConfig.getWebsocketURL(),
|
||||
channel: secret.channel,
|
||||
readOnly: false,
|
||||
validateKey: secret.keys.validateKey || undefined,
|
||||
crypto: Crypto.createEncryptor(secret.keys),
|
||||
userName: 'sharedFolder',
|
||||
logLevel: 1,
|
||||
ChainPad: ChainPad,
|
||||
classic: true,
|
||||
owners: owners
|
||||
};
|
||||
var rt = Listmap.create(listmapConfig);
|
||||
store.sharedFolders[id] = rt;
|
||||
rt.proxy.on('ready', function (info) {
|
||||
store.manager.addProxy(id, rt.proxy, info.leave);
|
||||
cb(rt, info.metadata);
|
||||
});
|
||||
if (store.driveEvents) {
|
||||
registerProxyEvents(rt.proxy, id);
|
||||
}
|
||||
return rt;
|
||||
};
|
||||
Store.addSharedFolder = function (clientId, data, cb) {
|
||||
Store.userObjectCommand(clientId, {
|
||||
cmd: 'addSharedFolder',
|
||||
data: data
|
||||
}, cb);
|
||||
};
|
||||
|
||||
// Drive
|
||||
Store.userObjectCommand = function (clientId, cmdData, cb) {
|
||||
if (!cmdData || !cmdData.cmd) { return; }
|
||||
var data = cmdData.data;
|
||||
//var data = cmdData.data;
|
||||
var cb2 = function (data2) {
|
||||
var paths = data.paths || [data.path] || [];
|
||||
paths = paths.concat(data.newPath || []);
|
||||
paths.forEach(function (p) {
|
||||
//var paths = data.paths || [data.path] || [];
|
||||
//paths = paths.concat(data.newPath ? [data.newPath] : []);
|
||||
//paths.forEach(function (p) {
|
||||
sendDriveEvent('DRIVE_CHANGE', {
|
||||
//path: ['drive', UserObject.FILES_DATA]
|
||||
path: ['drive'].concat(p)
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
//path: ['drive'].concat(p)
|
||||
}, clientId);
|
||||
//});
|
||||
onSync(function () {
|
||||
cb(data2);
|
||||
});
|
||||
cb(data2);
|
||||
};
|
||||
switch (cmdData.cmd) {
|
||||
case 'move':
|
||||
store.userObject.move(data.paths, data.newPath, cb2); break;
|
||||
case 'restore':
|
||||
store.userObject.restore(data.path, cb2); break;
|
||||
case 'addFolder':
|
||||
store.userObject.addFolder(data.path, data.name, cb2); break;
|
||||
case 'delete':
|
||||
store.userObject.delete(data.paths, cb2, data.nocheck, data.isOwnPadRemoved); break;
|
||||
case 'emptyTrash':
|
||||
store.userObject.emptyTrash(cb2); break;
|
||||
case 'rename':
|
||||
store.userObject.rename(data.path, data.newName, cb2); break;
|
||||
default:
|
||||
cb();
|
||||
}
|
||||
store.manager.command(cmdData, cb2);
|
||||
};
|
||||
|
||||
// Clients management
|
||||
@@ -1259,32 +1254,41 @@ define([
|
||||
|
||||
// Special events
|
||||
|
||||
var driveEventInit = false;
|
||||
sendDriveEvent = function (q, data, sender) {
|
||||
driveEventClients.forEach(function (cId) {
|
||||
if (cId === sender) { return; }
|
||||
postMessage(cId, q, data);
|
||||
});
|
||||
};
|
||||
registerProxyEvents = function (proxy, fId) {
|
||||
proxy.on('change', [], function (o, n, p) {
|
||||
sendDriveEvent('DRIVE_CHANGE', {
|
||||
id: fId,
|
||||
old: o,
|
||||
new: n,
|
||||
path: p
|
||||
});
|
||||
});
|
||||
proxy.on('remove', [], function (o, p) {
|
||||
sendDriveEvent('DRIVE_REMOVE', {
|
||||
id: fId,
|
||||
old: o,
|
||||
path: p
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Store._subscribeToDrive = function (clientId) {
|
||||
if (driveEventClients.indexOf(clientId) === -1) {
|
||||
driveEventClients.push(clientId);
|
||||
}
|
||||
if (!driveEventInit) {
|
||||
store.proxy.on('change', [], function (o, n, p) {
|
||||
sendDriveEvent('DRIVE_CHANGE', {
|
||||
old: o,
|
||||
new: n,
|
||||
path: p
|
||||
});
|
||||
if (!store.driveEvents) {
|
||||
store.driveEvents = true;
|
||||
registerProxyEvents(store.proxy);
|
||||
Object.keys(store.manager.folders).forEach(function (fId) {
|
||||
var proxy = store.manager.folders[fId].proxy;
|
||||
registerProxyEvents(proxy, fId);
|
||||
});
|
||||
store.proxy.on('remove', [], function (o, p) {
|
||||
sendDriveEvent(clientId, 'DRIVE_REMOVE', {
|
||||
old: o,
|
||||
path: p
|
||||
});
|
||||
});
|
||||
driveEventInit = true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1340,12 +1344,60 @@ define([
|
||||
/////////////////////// Init /////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
var loadSharedFolders = function (waitFor) {
|
||||
store.sharedFolders = {};
|
||||
var shared = Util.find(store.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {};
|
||||
// Check if any of our shared folder is expired or deleted by its owner.
|
||||
// If we don't check now, Listmap will create an empty proxy if it no longer exists on
|
||||
// the server.
|
||||
nThen(function (waitFor) {
|
||||
var edPublic = store.proxy.edPublic;
|
||||
var checkExpired = Object.keys(shared).filter(function (fId) {
|
||||
var d = shared[fId];
|
||||
return (Array.isArray(d.owners) && d.owners.length &&
|
||||
(!edPublic || d.owners.indexOf(edPublic) === -1))
|
||||
|| (d.expire && d.expire < (+new Date()));
|
||||
}).map(function (fId) {
|
||||
return shared[fId].channel;
|
||||
});
|
||||
Store.getDeletedPads(null, {list: checkExpired}, waitFor(function (chans) {
|
||||
if (chans && chans.error) { return void console.error(chans.error); }
|
||||
if (!Array.isArray(chans) || !chans.length) { return; }
|
||||
var toDelete = [];
|
||||
Object.keys(shared).forEach(function (fId) {
|
||||
if (chans.indexOf(shared[fId].channel) !== -1
|
||||
&& toDelete.indexOf(fId) === -1) {
|
||||
toDelete.push(fId);
|
||||
}
|
||||
});
|
||||
toDelete.forEach(function (fId) {
|
||||
var paths = store.userObject.findFile(Number(fId));
|
||||
store.userObject.delete(paths, waitFor(), true);
|
||||
delete shared[fId];
|
||||
});
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
Object.keys(shared).forEach(function (id) {
|
||||
var sf = shared[id];
|
||||
loadSharedFolder(id, sf, waitFor());
|
||||
});
|
||||
}).nThen(waitFor());
|
||||
};
|
||||
|
||||
var onReady = function (clientId, returned, cb) {
|
||||
var proxy = store.proxy;
|
||||
var userObject = store.userObject = UserObject.init(proxy.drive, {
|
||||
pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
|
||||
unpinPads: function (data, cb) { Store.unpinPads(null, data, cb); },
|
||||
removeOwnedChannel: function (data, cb) { Store.removeOwnedChannel(null, data, cb); },
|
||||
var unpin = function (data, cb) {
|
||||
if (!store.loggedIn) { return void cb(); }
|
||||
Store.unpinPads(null, data, cb);
|
||||
};
|
||||
var pin = function (data, cb) {
|
||||
if (!store.loggedIn) { return void cb(); }
|
||||
Store.pinPads(null, data, cb);
|
||||
};
|
||||
var manager = store.manager = ProxyManager.create(proxy.drive, proxy.edPublic,
|
||||
pin, unpin, loadSharedFolder, {
|
||||
outer: true,
|
||||
removeOwnedChannel: function (data, cb) { Store.removeOwnedChannel('', data, cb); },
|
||||
edPublic: store.proxy.edPublic,
|
||||
loggedIn: store.loggedIn,
|
||||
log: function (msg) {
|
||||
@@ -1353,6 +1405,7 @@ define([
|
||||
sendDriveEvent("DRIVE_LOG", msg);
|
||||
}
|
||||
});
|
||||
var userObject = store.userObject = manager.user.userObject;
|
||||
nThen(function (waitFor) {
|
||||
postMessage(clientId, 'LOADING_DRIVE', {
|
||||
state: 2
|
||||
@@ -1361,16 +1414,18 @@ define([
|
||||
}).nThen(function (waitFor) {
|
||||
Migrate(proxy, waitFor(), function (version, progress) {
|
||||
postMessage(clientId, 'LOADING_DRIVE', {
|
||||
state: 2,
|
||||
state: (2 + (version / 10)),
|
||||
progress: progress
|
||||
});
|
||||
});
|
||||
}).nThen(function () {
|
||||
Store.initAnonRpc(null, null, waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
postMessage(clientId, 'LOADING_DRIVE', {
|
||||
state: 3
|
||||
});
|
||||
userObject.fixFiles();
|
||||
|
||||
loadSharedFolders(waitFor);
|
||||
}).nThen(function () {
|
||||
var requestLogin = function () {
|
||||
broadcast([], "REQUEST_LOGIN");
|
||||
};
|
||||
|
||||
@@ -19,127 +19,130 @@ var debug = function (msg) { console.log(msg); };
|
||||
var init = function (client, cb) {
|
||||
debug('SW INIT');
|
||||
|
||||
require([
|
||||
'/common/requireconfig.js'
|
||||
], function (RequireConfig) {
|
||||
require.config(RequireConfig());
|
||||
require(['/api/config?cb=' + (+new Date()).toString(16)], function (ApiConfig) {
|
||||
if (ApiConfig.requireConf) { require.config(ApiConfig.requireConf); }
|
||||
require([
|
||||
'/common/common-util.js',
|
||||
'/common/outer/worker-channel.js',
|
||||
'/common/outer/store-rpc.js'
|
||||
], function (Util, Channel, SRpc) {
|
||||
debug('SW Required ressources loaded');
|
||||
var msgEv = Util.mkEvent();
|
||||
'/common/requireconfig.js'
|
||||
], function (RequireConfig) {
|
||||
require.config(RequireConfig());
|
||||
require([
|
||||
'/common/common-util.js',
|
||||
'/common/outer/worker-channel.js',
|
||||
'/common/outer/store-rpc.js'
|
||||
], function (Util, Channel, SRpc) {
|
||||
debug('SW Required ressources loaded');
|
||||
var msgEv = Util.mkEvent();
|
||||
|
||||
if (!self.Rpc) {
|
||||
self.Rpc = SRpc();
|
||||
}
|
||||
var Rpc = self.Rpc;
|
||||
if (!self.Rpc) {
|
||||
self.Rpc = SRpc();
|
||||
}
|
||||
var Rpc = self.Rpc;
|
||||
|
||||
var postToClient = function (data) {
|
||||
postMsg(client, data);
|
||||
};
|
||||
Channel.create(msgEv, postToClient, function (chan) {
|
||||
debug('SW Channel created');
|
||||
var postToClient = function (data) {
|
||||
postMsg(client, data);
|
||||
};
|
||||
Channel.create(msgEv, postToClient, function (chan) {
|
||||
debug('SW Channel created');
|
||||
|
||||
var clientId = client.id;
|
||||
self.tabs[clientId].chan = chan;
|
||||
Object.keys(Rpc.queries).forEach(function (q) {
|
||||
if (q === 'CONNECT') { return; }
|
||||
if (q === 'JOIN_PAD') { return; }
|
||||
if (q === 'SEND_PAD_MSG') { return; }
|
||||
chan.on(q, function (data, cb) {
|
||||
try {
|
||||
Rpc.queries[q](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query ' + q);
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
if (q === "DISCONNECT") {
|
||||
console.log('Deleting existing store!');
|
||||
delete self.Rpc;
|
||||
delete self.store;
|
||||
}
|
||||
});
|
||||
});
|
||||
chan.on('CONNECT', function (cfg, cb) {
|
||||
debug('SW Connect callback');
|
||||
if (self.store) {
|
||||
debug('Store already exists!');
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (cfg.messenger) {
|
||||
Rpc._subscribeToMessenger(clientId);
|
||||
}
|
||||
return void cb(self.store);
|
||||
}
|
||||
|
||||
debug('Loading new async store');
|
||||
// One-time initialization (init async-store)
|
||||
cfg.query = function (cId, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
var clientId = client.id;
|
||||
self.tabs[clientId].chan = chan;
|
||||
Object.keys(Rpc.queries).forEach(function (q) {
|
||||
if (q === 'CONNECT') { return; }
|
||||
if (q === 'JOIN_PAD') { return; }
|
||||
if (q === 'SEND_PAD_MSG') { return; }
|
||||
chan.on(q, function (data, cb) {
|
||||
try {
|
||||
Rpc.queries[q](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query ' + q);
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
if (q === "DISCONNECT") {
|
||||
console.log('Deleting existing store!');
|
||||
delete self.Rpc;
|
||||
delete self.store;
|
||||
}
|
||||
});
|
||||
};
|
||||
cfg.broadcast = function (excludes, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
Object.keys(self.tabs).forEach(function (cId) {
|
||||
if (excludes.indexOf(cId) !== -1) { return; }
|
||||
});
|
||||
chan.on('CONNECT', function (cfg, cb) {
|
||||
debug('SW Connect callback');
|
||||
if (self.store) {
|
||||
debug('Store already exists!');
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (cfg.messenger) {
|
||||
Rpc._subscribeToMessenger(clientId);
|
||||
}
|
||||
return void cb(self.store);
|
||||
}
|
||||
|
||||
debug('Loading new async store');
|
||||
// One-time initialization (init async-store)
|
||||
cfg.query = function (cId, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
};
|
||||
cfg.broadcast = function (excludes, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
Object.keys(self.tabs).forEach(function (cId) {
|
||||
if (excludes.indexOf(cId) !== -1) { return; }
|
||||
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
});
|
||||
};
|
||||
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (cfg.messenger) {
|
||||
Rpc._subscribeToMessenger(clientId);
|
||||
}
|
||||
if (data && data.state === "ALREADY_INIT") {
|
||||
return void cb(data.returned);
|
||||
}
|
||||
self.store = data;
|
||||
cb(data);
|
||||
});
|
||||
};
|
||||
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (cfg.messenger) {
|
||||
Rpc._subscribeToMessenger(clientId);
|
||||
}
|
||||
if (data && data.state === "ALREADY_INIT") {
|
||||
return void cb(data.returned);
|
||||
}
|
||||
self.store = data;
|
||||
cb(data);
|
||||
});
|
||||
});
|
||||
chan.on('JOIN_PAD', function (data, cb) {
|
||||
self.tabs[clientId].channelId = data.channel;
|
||||
try {
|
||||
Rpc.queries['JOIN_PAD'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query JOIN_PAD');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
chan.on('SEND_PAD_MSG', function (msg, cb) {
|
||||
var data = {
|
||||
msg: msg,
|
||||
channel: self.tabs[clientId].channelId
|
||||
};
|
||||
try {
|
||||
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query SEND_PAD_MSG');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
cb();
|
||||
}, true);
|
||||
chan.on('JOIN_PAD', function (data, cb) {
|
||||
self.tabs[clientId].channelId = data.channel;
|
||||
try {
|
||||
Rpc.queries['JOIN_PAD'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query JOIN_PAD');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
chan.on('SEND_PAD_MSG', function (msg, cb) {
|
||||
var data = {
|
||||
msg: msg,
|
||||
channel: self.tabs[clientId].channelId
|
||||
};
|
||||
try {
|
||||
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query SEND_PAD_MSG');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
cb();
|
||||
}, true);
|
||||
|
||||
self.tabs[client.id].msgEv = msgEv;
|
||||
self.tabs[client.id].msgEv = msgEv;
|
||||
|
||||
self.tabs[client.id].close = function () {
|
||||
Rpc._removeClient(client.id);
|
||||
};
|
||||
self.tabs[client.id].close = function () {
|
||||
Rpc._removeClient(client.id);
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -19,128 +19,131 @@ var debug = function (msg) { console.log(msg); };
|
||||
var init = function (client, cb) {
|
||||
debug('SharedW INIT');
|
||||
|
||||
require([
|
||||
'/common/requireconfig.js'
|
||||
], function (RequireConfig) {
|
||||
require.config(RequireConfig());
|
||||
require(['/api/config?cb=' + (+new Date()).toString(16)], function (ApiConfig) {
|
||||
if (ApiConfig.requireConf) { require.config(ApiConfig.requireConf); }
|
||||
require([
|
||||
'/common/common-util.js',
|
||||
'/common/outer/worker-channel.js',
|
||||
'/common/outer/store-rpc.js'
|
||||
], function (Util, Channel, SRpc) {
|
||||
debug('SharedW Required ressources loaded');
|
||||
var msgEv = Util.mkEvent();
|
||||
'/common/requireconfig.js'
|
||||
], function (RequireConfig) {
|
||||
require.config(RequireConfig());
|
||||
require([
|
||||
'/common/common-util.js',
|
||||
'/common/outer/worker-channel.js',
|
||||
'/common/outer/store-rpc.js'
|
||||
], function (Util, Channel, SRpc) {
|
||||
debug('SharedW Required ressources loaded');
|
||||
var msgEv = Util.mkEvent();
|
||||
|
||||
if (!self.Rpc) {
|
||||
self.Rpc = SRpc();
|
||||
}
|
||||
var Rpc = self.Rpc;
|
||||
if (!self.Rpc) {
|
||||
self.Rpc = SRpc();
|
||||
}
|
||||
var Rpc = self.Rpc;
|
||||
|
||||
var postToClient = function (data) {
|
||||
postMsg(client, data);
|
||||
};
|
||||
Channel.create(msgEv, postToClient, function (chan) {
|
||||
debug('SharedW Channel created');
|
||||
var postToClient = function (data) {
|
||||
postMsg(client, data);
|
||||
};
|
||||
Channel.create(msgEv, postToClient, function (chan) {
|
||||
debug('SharedW Channel created');
|
||||
|
||||
var clientId = client.id;
|
||||
client.chan = chan;
|
||||
Object.keys(Rpc.queries).forEach(function (q) {
|
||||
if (q === 'CONNECT') { return; }
|
||||
if (q === 'JOIN_PAD') { return; }
|
||||
if (q === 'SEND_PAD_MSG') { return; }
|
||||
chan.on(q, function (data, cb) {
|
||||
try {
|
||||
Rpc.queries[q](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query ' + q);
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
if (q === "DISCONNECT") {
|
||||
console.log('Deleting existing store!');
|
||||
delete self.Rpc;
|
||||
delete self.store;
|
||||
}
|
||||
});
|
||||
});
|
||||
chan.on('CONNECT', function (cfg, cb) {
|
||||
debug('SharedW connecting to store...');
|
||||
if (self.store) {
|
||||
debug('Store already exists!');
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (cfg.messenger) {
|
||||
Rpc._subscribeToMessenger(clientId);
|
||||
}
|
||||
return void cb(self.store);
|
||||
}
|
||||
|
||||
debug('Loading new async store');
|
||||
// One-time initialization (init async-store)
|
||||
cfg.query = function (cId, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
var clientId = client.id;
|
||||
client.chan = chan;
|
||||
Object.keys(Rpc.queries).forEach(function (q) {
|
||||
if (q === 'CONNECT') { return; }
|
||||
if (q === 'JOIN_PAD') { return; }
|
||||
if (q === 'SEND_PAD_MSG') { return; }
|
||||
chan.on(q, function (data, cb) {
|
||||
try {
|
||||
Rpc.queries[q](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query ' + q);
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
if (q === "DISCONNECT") {
|
||||
console.log('Deleting existing store!');
|
||||
delete self.Rpc;
|
||||
delete self.store;
|
||||
}
|
||||
});
|
||||
};
|
||||
cfg.broadcast = function (excludes, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
Object.keys(self.tabs).forEach(function (cId) {
|
||||
if (excludes.indexOf(cId) !== -1) { return; }
|
||||
});
|
||||
chan.on('CONNECT', function (cfg, cb) {
|
||||
debug('SharedW connecting to store...');
|
||||
if (self.store) {
|
||||
debug('Store already exists!');
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (cfg.messenger) {
|
||||
Rpc._subscribeToMessenger(clientId);
|
||||
}
|
||||
return void cb(self.store);
|
||||
}
|
||||
|
||||
debug('Loading new async store');
|
||||
// One-time initialization (init async-store)
|
||||
cfg.query = function (cId, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
};
|
||||
cfg.broadcast = function (excludes, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
Object.keys(self.tabs).forEach(function (cId) {
|
||||
if (excludes.indexOf(cId) !== -1) { return; }
|
||||
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
});
|
||||
};
|
||||
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (cfg.messenger) {
|
||||
Rpc._subscribeToMessenger(clientId);
|
||||
}
|
||||
if (data && data.state === "ALREADY_INIT") {
|
||||
self.store = data.returned;
|
||||
return void cb(data.returned);
|
||||
}
|
||||
self.store = data;
|
||||
cb(data);
|
||||
});
|
||||
};
|
||||
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (cfg.messenger) {
|
||||
Rpc._subscribeToMessenger(clientId);
|
||||
}
|
||||
if (data && data.state === "ALREADY_INIT") {
|
||||
self.store = data.returned;
|
||||
return void cb(data.returned);
|
||||
}
|
||||
self.store = data;
|
||||
cb(data);
|
||||
});
|
||||
});
|
||||
chan.on('JOIN_PAD', function (data, cb) {
|
||||
client.channelId = data.channel;
|
||||
try {
|
||||
Rpc.queries['JOIN_PAD'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query JOIN_PAD');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
chan.on('SEND_PAD_MSG', function (msg, cb) {
|
||||
var data = {
|
||||
msg: msg,
|
||||
channel: client.channelId
|
||||
};
|
||||
try {
|
||||
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query SEND_PAD_MSG');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
cb();
|
||||
}, true);
|
||||
chan.on('JOIN_PAD', function (data, cb) {
|
||||
client.channelId = data.channel;
|
||||
try {
|
||||
Rpc.queries['JOIN_PAD'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query JOIN_PAD');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
chan.on('SEND_PAD_MSG', function (msg, cb) {
|
||||
var data = {
|
||||
msg: msg,
|
||||
channel: client.channelId
|
||||
};
|
||||
try {
|
||||
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query SEND_PAD_MSG');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
cb();
|
||||
}, true);
|
||||
|
||||
client.msgEv = msgEv;
|
||||
client.msgEv = msgEv;
|
||||
|
||||
client.close = function () {
|
||||
Rpc._removeClient(client.id);
|
||||
};
|
||||
client.close = function () {
|
||||
Rpc._removeClient(client.id);
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ define([
|
||||
MOVE_TO_TRASH: Store.moveToTrash,
|
||||
RESET_DRIVE: Store.resetDrive,
|
||||
GET_METADATA: Store.getMetadata,
|
||||
IS_ONLY_IN_SHARED_FOLDER: Store.isOnlyInSharedFolder,
|
||||
SET_DISPLAY_NAME: Store.setDisplayName,
|
||||
SET_PAD_ATTRIBUTE: Store.setPadAttribute,
|
||||
GET_PAD_ATTRIBUTE: Store.getPadAttribute,
|
||||
@@ -53,6 +54,8 @@ define([
|
||||
GET_PAD_DATA: Store.getPadData,
|
||||
GET_STRONGER_HASH: Store.getStrongerHash,
|
||||
INCREMENT_TEMPLATE_USE: Store.incrementTemplateUse,
|
||||
GET_SHARED_FOLDER: Store.getSharedFolder,
|
||||
ADD_SHARED_FOLDER: Store.addSharedFolder,
|
||||
// Messaging
|
||||
INVITE_FROM_USERLIST: Store.inviteFromUserlist,
|
||||
ADD_DIRECT_MESSAGE_HANDLERS: Store.addDirectMessageHandlers,
|
||||
|
||||
@@ -92,7 +92,8 @@ define([
|
||||
href: href,
|
||||
path: path,
|
||||
password: password,
|
||||
channel: id
|
||||
channel: id,
|
||||
owners: metadata.owners
|
||||
};
|
||||
common.setPadTitle(data, function (err) {
|
||||
if (err) { return void console.error(err); }
|
||||
|
||||
@@ -14,15 +14,11 @@ define([
|
||||
};
|
||||
|
||||
module.init = function (config, exp, files) {
|
||||
var unpinPads = config.unpinPads || function () {
|
||||
console.error("unpinPads was not provided");
|
||||
};
|
||||
var pinPads = config.pinPads;
|
||||
var removeOwnedChannel = config.removeOwnedChannel || function () {
|
||||
console.error("removeOwnedChannel was not provided");
|
||||
};
|
||||
var loggedIn = config.loggedIn;
|
||||
var workgroup = config.workgroup;
|
||||
var sharedFolder = config.sharedFolder;
|
||||
var edPublic = config.edPublic;
|
||||
|
||||
var ROOT = exp.ROOT;
|
||||
@@ -31,6 +27,7 @@ define([
|
||||
var UNSORTED = exp.UNSORTED;
|
||||
var TRASH = exp.TRASH;
|
||||
var TEMPLATE = exp.TEMPLATE;
|
||||
var SHARED_FOLDERS = exp.SHARED_FOLDERS;
|
||||
|
||||
var debug = exp.debug;
|
||||
|
||||
@@ -50,35 +47,31 @@ define([
|
||||
var data = exp.getFileData(id);
|
||||
cb(null, clone(data[attr]));
|
||||
};
|
||||
var removePadAttribute = exp.removePadAttribute = function (f) {
|
||||
if (typeof(f) !== 'string') {
|
||||
console.error("Can't find pad attribute for an undefined pad");
|
||||
return;
|
||||
}
|
||||
Object.keys(files).forEach(function (key) {
|
||||
var hash = f.indexOf('#') !== -1 ? f.slice(f.indexOf('#') + 1) : null;
|
||||
if (hash && key.indexOf(hash) === 0) {
|
||||
exp.debug("Deleting pad attribute in the realtime object");
|
||||
delete files[key];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exp.pushData = function (data, cb) {
|
||||
if (typeof cb !== "function") { cb = function () {}; }
|
||||
var todo = function () {
|
||||
var id = Util.createRandomInteger();
|
||||
files[FILES_DATA][id] = data;
|
||||
cb(null, id);
|
||||
};
|
||||
if (!loggedIn || !AppConfig.enablePinning || config.testMode) {
|
||||
return void todo();
|
||||
var id = Util.createRandomInteger();
|
||||
files[FILES_DATA][id] = data;
|
||||
cb(null, id);
|
||||
};
|
||||
|
||||
exp.pushSharedFolder = function (data, cb) {
|
||||
if (typeof cb !== "function") { cb = function () {}; }
|
||||
|
||||
// Check if we already have this shared folder in our drive
|
||||
if (Object.keys(files[SHARED_FOLDERS]).some(function (k) {
|
||||
return files[SHARED_FOLDERS][k].channel === data.channel;
|
||||
})) {
|
||||
return void cb ('EEXISTS');
|
||||
}
|
||||
if (!pinPads) { return; }
|
||||
pinPads([data.channel], function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj.error); }
|
||||
todo();
|
||||
});
|
||||
|
||||
// Add the folder
|
||||
if (!loggedIn || !AppConfig.enablePinning || config.testMode) {
|
||||
return void cb("EAUTH");
|
||||
}
|
||||
var id = Util.createRandomInteger();
|
||||
files[SHARED_FOLDERS][id] = data;
|
||||
cb(null, id);
|
||||
};
|
||||
|
||||
// FILES DATA
|
||||
@@ -87,21 +80,21 @@ define([
|
||||
};
|
||||
|
||||
// Find files in FILES_DATA that are not anymore in the drive, and remove them from
|
||||
// FILES_DATA. If there are owned pads, remove them from server too, unless the flag tells
|
||||
// us they're already removed
|
||||
exp.checkDeletedFiles = function (isOwnPadRemoved) {
|
||||
// Nothing in FILES_DATA for workgroups
|
||||
if (workgroup || (!loggedIn && !config.testMode)) { return; }
|
||||
// FILES_DATA. If there are owned pads, remove them from server too.
|
||||
exp.checkDeletedFiles = function (cb) {
|
||||
if (!loggedIn && !config.testMode) { return void cb(); }
|
||||
|
||||
var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]);
|
||||
var toClean = [];
|
||||
exp.getFiles([FILES_DATA]).forEach(function (id) {
|
||||
var ownedRemoved = [];
|
||||
exp.getFiles([FILES_DATA, SHARED_FOLDERS]).forEach(function (id) {
|
||||
if (filesList.indexOf(id) === -1) {
|
||||
var fd = exp.getFileData(id);
|
||||
var fd = exp.isSharedFolder(id) ? files[SHARED_FOLDERS][id] : exp.getFileData(id);
|
||||
var channelId = fd.channel;
|
||||
// If trying to remove an owned pad, remove it from server also
|
||||
if (!isOwnPadRemoved &&
|
||||
fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
|
||||
if (!sharedFolder && fd.owners && fd.owners.indexOf(edPublic) !== -1
|
||||
&& channelId) {
|
||||
if (channelId) { ownedRemoved.push(channelId); }
|
||||
removeOwnedChannel(channelId, function (obj) {
|
||||
if (obj && obj.error) {
|
||||
// If the error is that the file is already removed, nothing to
|
||||
@@ -116,14 +109,15 @@ define([
|
||||
});
|
||||
}
|
||||
if (channelId) { toClean.push(channelId); }
|
||||
spliceFileData(id);
|
||||
if (exp.isSharedFolder(id)) {
|
||||
delete files[SHARED_FOLDERS][id];
|
||||
} else {
|
||||
spliceFileData(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!toClean.length) { return; }
|
||||
unpinPads(toClean, function (response) {
|
||||
if (response && response.error) { return console.error(response.error); }
|
||||
// console.error(response);
|
||||
});
|
||||
if (!toClean.length) { return void cb(); }
|
||||
cb(null, toClean, ownedRemoved);
|
||||
};
|
||||
var deleteHrefs = function (ids) {
|
||||
ids.forEach(function (obj) {
|
||||
@@ -137,7 +131,7 @@ define([
|
||||
files[TRASH][obj.name].splice(idx, 1);
|
||||
});
|
||||
};
|
||||
exp.deleteMultiplePermanently = function (paths, nocheck, isOwnPadRemoved) {
|
||||
exp.deleteMultiplePermanently = function (paths, nocheck, cb) {
|
||||
var hrefPaths = paths.filter(function(x) { return exp.isPathIn(x, ['hrefArray']); });
|
||||
var rootPaths = paths.filter(function(x) { return exp.isPathIn(x, [ROOT]); });
|
||||
var trashPaths = paths.filter(function(x) { return exp.isPathIn(x, [TRASH]); });
|
||||
@@ -145,14 +139,11 @@ define([
|
||||
|
||||
if (!loggedIn && !config.testMode) {
|
||||
allFilesPaths.forEach(function (path) {
|
||||
var el = exp.find(path);
|
||||
if (!el) { return; }
|
||||
var id = exp.getIdFromHref(el.href);
|
||||
var id = path[1];
|
||||
if (!id) { return; }
|
||||
spliceFileData(id);
|
||||
removePadAttribute(el.href);
|
||||
});
|
||||
return;
|
||||
return void cb();
|
||||
}
|
||||
|
||||
var ids = [];
|
||||
@@ -192,11 +183,69 @@ define([
|
||||
deleteMultipleTrashRoot(trashRoot);
|
||||
|
||||
// In some cases, we want to remove pads from a location without removing them from
|
||||
// OLD_FILES_DATA (replaceHref)
|
||||
if (!nocheck) { exp.checkDeletedFiles(isOwnPadRemoved); }
|
||||
// FILES_DATA (replaceHref)
|
||||
if (!nocheck) { exp.checkDeletedFiles(cb); }
|
||||
else { cb(); }
|
||||
};
|
||||
|
||||
// Move
|
||||
|
||||
// From another drive
|
||||
exp.copyFromOtherDrive = function (path, element, data, key) {
|
||||
// Copy files data
|
||||
// We have to remove pads that are already in the current proxy to make sure
|
||||
// we won't create duplicates
|
||||
|
||||
var toRemove = [];
|
||||
Object.keys(data).forEach(function (id) {
|
||||
id = Number(id);
|
||||
// Find and maybe update existing pads with the same channel id
|
||||
var d = data[id];
|
||||
var found = false;
|
||||
for (var i in files[FILES_DATA]) {
|
||||
if (files[FILES_DATA][i].channel === d.channel) {
|
||||
// Update href?
|
||||
if (!files[FILES_DATA][i].href) { files[FILES_DATA][i].href = d.href; }
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
toRemove.push(id);
|
||||
return;
|
||||
}
|
||||
files[FILES_DATA][id] = data[id];
|
||||
});
|
||||
|
||||
// Remove existing pads from the "element" variable
|
||||
if (exp.isFile(element) && toRemove.indexOf(element) !== -1) {
|
||||
exp.log(Messages.sharedFolders_duplicate);
|
||||
return;
|
||||
} else if (exp.isFolder(element)) {
|
||||
var _removeExisting = function (root) {
|
||||
for (var k in root) {
|
||||
if (exp.isFile(root[k])) {
|
||||
if (toRemove.indexOf(root[k]) !== -1) {
|
||||
exp.log(Messages.sharedFolders_duplicate);
|
||||
delete root[k];
|
||||
}
|
||||
} else if (exp.isFolder(root[k])) {
|
||||
_removeExisting(root[k]);
|
||||
}
|
||||
}
|
||||
};
|
||||
_removeExisting(element);
|
||||
}
|
||||
|
||||
|
||||
// Copy file or folder
|
||||
var newParent = exp.find(path);
|
||||
var tempName = exp.isFile(element) ? Hash.createChannelId() : key;
|
||||
var newName = exp.getAvailableName(newParent, tempName);
|
||||
newParent[newName] = element;
|
||||
};
|
||||
|
||||
// From the same drive
|
||||
var pushToTrash = function (name, element, path) {
|
||||
var trash = files[TRASH];
|
||||
if (typeof(trash[name]) === "undefined") { trash[name] = []; }
|
||||
@@ -259,7 +308,6 @@ define([
|
||||
if (!id) { return; }
|
||||
if (!loggedIn && !config.testMode) {
|
||||
// delete permanently
|
||||
exp.removePadAttribute(href);
|
||||
spliceFileData(id);
|
||||
return;
|
||||
}
|
||||
@@ -268,14 +316,7 @@ define([
|
||||
};
|
||||
|
||||
// REPLACE
|
||||
exp.replace = function (o, n) {
|
||||
var idO = exp.getIdFromHref(o);
|
||||
if (!idO || !exp.isFile(idO)) { return; }
|
||||
var data = exp.getFileData(idO);
|
||||
if (!data) { return; }
|
||||
data.href = n;
|
||||
};
|
||||
// If all the occurences of an href are in the trash, remvoe them and add the file in root.
|
||||
// If all the occurences of an href are in the trash, remove them and add the file in root.
|
||||
// This is use with setPadTitle when we open a stronger version of a deleted pad
|
||||
exp.restoreHref = function (href) {
|
||||
var idO = exp.getIdFromHref(href);
|
||||
@@ -300,9 +341,9 @@ define([
|
||||
};
|
||||
|
||||
exp.add = function (id, path) {
|
||||
// TODO WW
|
||||
if (!loggedIn && !config.testMode) { return; }
|
||||
var data = files[FILES_DATA][id];
|
||||
id = Number(id);
|
||||
var data = files[FILES_DATA][id] || files[SHARED_FOLDERS][id];
|
||||
if (!data || typeof(data) !== "object") { return; }
|
||||
var newPath = path, parentEl;
|
||||
if (path && !Array.isArray(path)) {
|
||||
@@ -406,7 +447,6 @@ define([
|
||||
});
|
||||
delete files[OLD_FILES_DATA];
|
||||
delete files.migrate;
|
||||
console.log('done');
|
||||
todo();
|
||||
};
|
||||
if (exp.rt) {
|
||||
@@ -473,6 +513,7 @@ define([
|
||||
}
|
||||
};
|
||||
var fixTrashRoot = function () {
|
||||
if (sharedFolder) { return; }
|
||||
if (typeof(files[TRASH]) !== "object") { debug("TRASH was not an object"); files[TRASH] = {}; }
|
||||
var tr = files[TRASH];
|
||||
var toClean;
|
||||
@@ -516,6 +557,7 @@ define([
|
||||
}
|
||||
};
|
||||
var fixTemplate = function () {
|
||||
if (sharedFolder) { return; }
|
||||
if (!Array.isArray(files[TEMPLATE])) { debug("TEMPLATE was not an array"); files[TEMPLATE] = []; }
|
||||
files[TEMPLATE] = Util.deduplicateString(files[TEMPLATE].slice());
|
||||
var us = files[TEMPLATE];
|
||||
@@ -547,7 +589,7 @@ define([
|
||||
});
|
||||
};
|
||||
var fixFilesData = function () {
|
||||
if (typeof files[FILES_DATA] !== "object") { debug("OLD_FILES_DATA was not an object"); files[FILES_DATA] = {}; }
|
||||
if (typeof files[FILES_DATA] !== "object") { debug("FILES_DATA was not an object"); files[FILES_DATA] = {}; }
|
||||
var fd = files[FILES_DATA];
|
||||
var rootFiles = exp.getFiles([ROOT, TRASH, 'hrefArray']);
|
||||
var root = exp.find([ROOT]);
|
||||
@@ -563,33 +605,45 @@ define([
|
||||
continue;
|
||||
}
|
||||
// Clean missing href
|
||||
var parsed;
|
||||
if (el.href) {
|
||||
if (!el.href) {
|
||||
debug("Removing an element in filesData with a missing href.", el);
|
||||
toClean.push(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
parsed = Hash.parsePadUrl(el.href);
|
||||
// Clean invalid hash
|
||||
if (!parsed.hash) {
|
||||
debug("Removing an element in filesData with a invalid href.", el);
|
||||
toClean.push(id);
|
||||
continue;
|
||||
}
|
||||
// Clean invalid type
|
||||
if (!parsed.type) {
|
||||
debug("Removing an element in filesData with a invalid type.", el);
|
||||
toClean.push(id);
|
||||
continue;
|
||||
}
|
||||
} else if (!el.roHref) {
|
||||
if (!el.href && !el.roHref) {
|
||||
debug("Removing an element in filesData with a missing href.", el);
|
||||
toClean.push(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
var parsed = Hash.parsePadUrl(el.href || el.roHref);
|
||||
var secret;
|
||||
|
||||
// Clean invalid hash
|
||||
if (!parsed.hash) {
|
||||
debug("Removing an element in filesData with a invalid href.", el);
|
||||
toClean.push(id);
|
||||
continue;
|
||||
}
|
||||
// Clean invalid type
|
||||
if (!parsed.type) {
|
||||
debug("Removing an element in filesData with a invalid type.", el);
|
||||
toClean.push(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have an edit link, check the view link
|
||||
if (el.href && parsed.hashData.type === "pad") {
|
||||
if (parsed.hashData.mode === "view") {
|
||||
el.roHref = el.href;
|
||||
delete el.href;
|
||||
} else if (!el.roHref) {
|
||||
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||
el.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
|
||||
} else {
|
||||
var parsed2 = Hash.parsePadUrl(el.roHref);
|
||||
if (!parsed2.hash || !parsed2.type) {
|
||||
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||
el.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix href
|
||||
if (el.href && /^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
|
||||
// Fix creation time
|
||||
@@ -597,11 +651,14 @@ define([
|
||||
// Fix title
|
||||
if (!el.title) { el.title = Hash.getDefaultName(parsed); }
|
||||
// Fix channel
|
||||
if (el.href && !el.channel) {
|
||||
if (!el.channel) {
|
||||
try {
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||
if (!secret) {
|
||||
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||
}
|
||||
el.channel = secret.channel;
|
||||
console.log('Adding missing channel in filesData ', el.channel);
|
||||
console.log(el);
|
||||
debug('Adding missing channel in filesData ', el.channel);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -618,6 +675,21 @@ define([
|
||||
spliceFileData(id);
|
||||
});
|
||||
};
|
||||
var fixSharedFolders = function () {
|
||||
if (sharedFolder) { return; }
|
||||
if (typeof(files[SHARED_FOLDERS]) !== "object") { debug("SHARED_FOLDER was not an object"); files[SHARED_FOLDERS] = {}; }
|
||||
var sf = files[SHARED_FOLDERS];
|
||||
var rootFiles = exp.getFiles([ROOT]);
|
||||
var root = exp.find([ROOT]);
|
||||
for (var id in sf) {
|
||||
id = Number(id);
|
||||
if (rootFiles.indexOf(id) === -1) {
|
||||
console.log('missing' + id);
|
||||
var newName = Hash.createChannelId();
|
||||
root[newName] = id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var fixDrive = function () {
|
||||
Object.keys(files).forEach(function (key) {
|
||||
@@ -627,11 +699,10 @@ define([
|
||||
|
||||
fixRoot();
|
||||
fixTrashRoot();
|
||||
if (!workgroup) {
|
||||
fixTemplate();
|
||||
fixFilesData();
|
||||
}
|
||||
fixTemplate();
|
||||
fixFilesData();
|
||||
fixDrive();
|
||||
fixSharedFolders();
|
||||
|
||||
if (JSON.stringify(files) !== before) {
|
||||
debug("Your file system was corrupted. It has been cleaned so that the pads you visit can be stored safely");
|
||||
|
||||
@@ -7,94 +7,97 @@ localStorage = {
|
||||
getItem: function (k) { return localStorage[k]; }
|
||||
};
|
||||
|
||||
require([
|
||||
'/common/requireconfig.js'
|
||||
], function (RequireConfig) {
|
||||
require.config(RequireConfig());
|
||||
require(['/api/config?cb=' + (+new Date()).toString(16)], function (ApiConfig) {
|
||||
if (ApiConfig.requireConf) { require.config(ApiConfig.requireConf); }
|
||||
require([
|
||||
'/common/common-util.js',
|
||||
'/common/outer/worker-channel.js',
|
||||
'/common/outer/store-rpc.js'
|
||||
], function (Util, Channel, SRpc) {
|
||||
var msgEv = Util.mkEvent();
|
||||
'/common/requireconfig.js'
|
||||
], function (RequireConfig) {
|
||||
require.config(RequireConfig());
|
||||
require([
|
||||
'/common/common-util.js',
|
||||
'/common/outer/worker-channel.js',
|
||||
'/common/outer/store-rpc.js'
|
||||
], function (Util, Channel, SRpc) {
|
||||
var msgEv = Util.mkEvent();
|
||||
|
||||
var Rpc = SRpc();
|
||||
var Rpc = SRpc();
|
||||
|
||||
Channel.create(msgEv, postMessage, function (chan) {
|
||||
var clientId = '1';
|
||||
Object.keys(Rpc.queries).forEach(function (q) {
|
||||
if (q === 'CONNECT') { return; }
|
||||
if (q === 'JOIN_PAD') { return; }
|
||||
if (q === 'SEND_PAD_MSG') { return; }
|
||||
chan.on(q, function (data, cb) {
|
||||
Channel.create(msgEv, postMessage, function (chan) {
|
||||
var clientId = '1';
|
||||
Object.keys(Rpc.queries).forEach(function (q) {
|
||||
if (q === 'CONNECT') { return; }
|
||||
if (q === 'JOIN_PAD') { return; }
|
||||
if (q === 'SEND_PAD_MSG') { return; }
|
||||
chan.on(q, function (data, cb) {
|
||||
try {
|
||||
Rpc.queries[q](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query ' + q);
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
chan.on('CONNECT', function (cfg, cb) {
|
||||
// load Store here, with cfg, and pass a "query" (chan.query)
|
||||
// cId is a clientId used in ServiceWorker or SharedWorker
|
||||
cfg.query = function (cId, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
};
|
||||
cfg.broadcast = function (excludes, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
if (excludes.indexOf(clientId) !== -1) { return; }
|
||||
chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
};
|
||||
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
|
||||
if (data && data.state === "ALREADY_INIT") {
|
||||
return void cb(data);
|
||||
}
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (cfg.messenger) {
|
||||
Rpc._subscribeToMessenger(clientId);
|
||||
}
|
||||
cb(data);
|
||||
});
|
||||
});
|
||||
var chanId;
|
||||
chan.on('JOIN_PAD', function (data, cb) {
|
||||
chanId = data.channel;
|
||||
try {
|
||||
Rpc.queries[q](clientId, data, cb);
|
||||
Rpc.queries['JOIN_PAD'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query ' + q);
|
||||
console.error('Error in webworker when executing query JOIN_PAD');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
chan.on('CONNECT', function (cfg, cb) {
|
||||
// load Store here, with cfg, and pass a "query" (chan.query)
|
||||
// cId is a clientId used in ServiceWorker or SharedWorker
|
||||
cfg.query = function (cId, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
};
|
||||
cfg.broadcast = function (excludes, cmd, data, cb) {
|
||||
cb = cb || function () {};
|
||||
if (excludes.indexOf(clientId) !== -1) { return; }
|
||||
chan.query(cmd, data, function (err, data2) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(data2);
|
||||
});
|
||||
};
|
||||
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
|
||||
if (data && data.state === "ALREADY_INIT") {
|
||||
return void cb(data);
|
||||
chan.on('SEND_PAD_MSG', function (msg, cb) {
|
||||
var data = {
|
||||
msg: msg,
|
||||
channel: chanId
|
||||
};
|
||||
try {
|
||||
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query SEND_PAD_MSG');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
if (cfg.driveEvents) {
|
||||
Rpc._subscribeToDrive(clientId);
|
||||
}
|
||||
if (cfg.messenger) {
|
||||
Rpc._subscribeToMessenger(clientId);
|
||||
}
|
||||
cb(data);
|
||||
});
|
||||
});
|
||||
var chanId;
|
||||
chan.on('JOIN_PAD', function (data, cb) {
|
||||
chanId = data.channel;
|
||||
try {
|
||||
Rpc.queries['JOIN_PAD'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query JOIN_PAD');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
chan.on('SEND_PAD_MSG', function (msg, cb) {
|
||||
var data = {
|
||||
msg: msg,
|
||||
channel: chanId
|
||||
};
|
||||
try {
|
||||
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query SEND_PAD_MSG');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
}, true);
|
||||
|
||||
onmessage = function (e) {
|
||||
msgEv.fire(e);
|
||||
};
|
||||
onmessage = function (e) {
|
||||
msgEv.fire(e);
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,12 +32,14 @@ define([
|
||||
var chan = {};
|
||||
|
||||
// Send a query. channel.query('Q_SOMETHING', { args: "whatever" }, function (reply) { ... });
|
||||
chan.query = function (q, content, cb) {
|
||||
chan.query = function (q, content, cb, opts) {
|
||||
var txid = mkTxid();
|
||||
opts = opts || {};
|
||||
var to = opts.timeout || 30000;
|
||||
var timeout = setTimeout(function () {
|
||||
delete queries[txid];
|
||||
//console.log("Timeout making query " + q);
|
||||
}, 30000);
|
||||
}, to);
|
||||
queries[txid] = function (data, msg) {
|
||||
clearTimeout(timeout);
|
||||
delete queries[txid];
|
||||
|
||||
1018
www/common/proxy-manager.js
Normal file
1018
www/common/proxy-manager.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,6 @@ define([
|
||||
'/bower_components/file-saver/FileSaver.min.js',
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
], function (
|
||||
$,
|
||||
Hyperjson,
|
||||
|
||||
@@ -21,16 +21,18 @@ define([
|
||||
var chan = {};
|
||||
|
||||
// Send a query. channel.query('Q_SOMETHING', { args: "whatever" }, function (reply) { ... });
|
||||
chan.query = function (q, content, cb) {
|
||||
chan.query = function (q, content, cb, opts) {
|
||||
if (!otherWindow) { throw new Error('not yet initialized'); }
|
||||
if (!SFrameProtocol[q]) {
|
||||
throw new Error('please only make queries are defined in sframe-protocol.js');
|
||||
}
|
||||
opts = opts || {};
|
||||
var txid = mkTxid();
|
||||
var to = opts.timeout || 30000;
|
||||
var timeout = setTimeout(function () {
|
||||
delete queries[txid];
|
||||
console.log("Timeout making query " + q);
|
||||
}, 30000);
|
||||
}, to);
|
||||
queries[txid] = function (data, msg) {
|
||||
clearTimeout(timeout);
|
||||
delete queries[txid];
|
||||
|
||||
@@ -266,8 +266,12 @@ define([
|
||||
isDeleted: isNewFile && window.location.hash.length > 0,
|
||||
forceCreationScreen: forceCreationScreen,
|
||||
password: password,
|
||||
channel: secret.channel
|
||||
channel: secret.channel,
|
||||
enableSF: localStorage.CryptPad_SF === "1" // TODO to remove when enabled by default
|
||||
};
|
||||
if (window.CryptPad_newSharedFolder) {
|
||||
additionalPriv.newSharedFolder = window.CryptPad_newSharedFolder;
|
||||
}
|
||||
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
|
||||
|
||||
if (cfg.addData) {
|
||||
@@ -421,10 +425,19 @@ define([
|
||||
});
|
||||
});
|
||||
sframeChan.on('Q_GET_HISTORY_RANGE', function (data, cb) {
|
||||
var crypto = Crypto.createEncryptor(secret.keys);
|
||||
var nSecret = secret;
|
||||
if (cfg.isDrive) {
|
||||
var hash = Utils.LocalStore.getUserHash() || Utils.LocalStore.getFSHash();
|
||||
if (hash) {
|
||||
nSecret = Utils.Hash.getSecrets('drive', hash);
|
||||
}
|
||||
}
|
||||
var channel = nSecret.channel;
|
||||
var validate = nSecret.keys.validateKey;
|
||||
var crypto = Crypto.createEncryptor(nSecret.keys);
|
||||
Cryptpad.getHistoryRange({
|
||||
channel: secret.channel,
|
||||
validateKey: secret.keys.validateKey,
|
||||
channel: channel,
|
||||
validateKey: validate,
|
||||
lastKnownHash: data.lastKnownHash
|
||||
}, function (data) {
|
||||
cb({
|
||||
@@ -484,6 +497,12 @@ define([
|
||||
cb();
|
||||
});
|
||||
|
||||
sframeChan.on('Q_IS_ONLY_IN_SHARED_FOLDER', function (data, cb) {
|
||||
Cryptpad.isOnlyInSharedFolder(secret.channel, function (err, t) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
cb(t);
|
||||
});
|
||||
});
|
||||
|
||||
// Present mode URL
|
||||
sframeChan.on('Q_PRESENT_URL_GET_VALUE', function (data, cb) {
|
||||
|
||||
@@ -112,9 +112,14 @@ define([
|
||||
var origin = ctx.metadataMgr.getPrivateData().origin;
|
||||
return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>';
|
||||
};
|
||||
funcs.getMediatagFromHref = function () {
|
||||
funcs.getMediatagFromHref = function (obj) {
|
||||
var data = ctx.metadataMgr.getPrivateData();
|
||||
var secret = Hash.getSecrets('file', data.availableHashes.fileHash, data.password);
|
||||
var secret;
|
||||
if (obj) {
|
||||
secret = Hash.getSecrets('file', obj.hash, obj.password);
|
||||
} else {
|
||||
secret = Hash.getSecrets('file', data.availableHashes.fileHash, data.password);
|
||||
}
|
||||
if (secret.keys && secret.channel) {
|
||||
var key = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
|
||||
var hexFileName = secret.channel;
|
||||
|
||||
@@ -114,6 +114,10 @@ define({
|
||||
'Q_GET_PAD_ATTRIBUTE': true,
|
||||
'Q_SET_PAD_ATTRIBUTE': true,
|
||||
|
||||
// Check if a pad is only in a shared folder or (also) in the main drive.
|
||||
// This allows us to change the behavior of some buttons (trash icon...)
|
||||
'Q_IS_ONLY_IN_SHARED_FOLDER': true,
|
||||
|
||||
// Open/close the File picker (sent from the iframe to the outside)
|
||||
'EV_FILE_PICKER_OPEN': true,
|
||||
'EV_FILE_PICKER_CLOSE': true,
|
||||
@@ -206,6 +210,7 @@ define({
|
||||
// Inner drive needs to send command and receive updates from the async store
|
||||
'Q_DRIVE_USEROBJECT': true,
|
||||
'Q_DRIVE_GETOBJECT': true,
|
||||
'Q_DRIVE_RESTORE': true,
|
||||
// Get the pads deleted from the server by other users to remove them from the drive
|
||||
'Q_DRIVE_GETDELETED': true,
|
||||
// Store's userObject need to send log messages to inner to display them in the UI
|
||||
|
||||
@@ -13,10 +13,10 @@ define([
|
||||
var UNSORTED = module.UNSORTED = "unsorted";
|
||||
var TRASH = module.TRASH = "trash";
|
||||
var TEMPLATE = module.TEMPLATE = "template";
|
||||
var SHARED_FOLDERS = module.SHARED_FOLDERS = "sharedFolders";
|
||||
|
||||
module.init = function (files, config) {
|
||||
var exp = {};
|
||||
var pinPads = config.pinPads;
|
||||
var sframeChan = config.sframeChan;
|
||||
|
||||
var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Constants.storageKey;
|
||||
@@ -28,14 +28,21 @@ define([
|
||||
exp.UNSORTED = UNSORTED;
|
||||
exp.TRASH = TRASH;
|
||||
exp.TEMPLATE = TEMPLATE;
|
||||
exp.SHARED_FOLDERS = SHARED_FOLDERS;
|
||||
|
||||
var sharedFolder = exp.sharedFolder = config.sharedFolder;
|
||||
exp.id = config.id;
|
||||
|
||||
// Logging
|
||||
var logging = function () {
|
||||
console.log.apply(console, arguments);
|
||||
};
|
||||
var log = config.log || logging;
|
||||
var log = exp.log = config.log || logging;
|
||||
var logError = config.logError || logging;
|
||||
var debug = exp.debug = config.debug || logging;
|
||||
|
||||
exp.fixFiles = function () {}; // Overriden by OuterFO
|
||||
|
||||
var error = exp.error = function() {
|
||||
if (sframeChan) {
|
||||
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||
@@ -46,12 +53,10 @@ define([
|
||||
exp.fixFiles();
|
||||
}
|
||||
console.error.apply(console, arguments);
|
||||
exp.fixFiles();
|
||||
};
|
||||
|
||||
// TODO: workgroup
|
||||
var workgroup = config.workgroup;
|
||||
|
||||
if (pinPads) {
|
||||
if (config.outer) {
|
||||
// Extend "exp" with methods used only outside of the iframe (requires access to store)
|
||||
OuterFO.init(config, exp, files);
|
||||
}
|
||||
@@ -76,7 +81,12 @@ define([
|
||||
|
||||
var compareFiles = function (fileA, fileB) { return fileA === fileB; };
|
||||
|
||||
var isSharedFolder = exp.isSharedFolder = function (element) {
|
||||
if (sharedFolder) { return false; } // No recursive shared folders
|
||||
return Boolean(files[SHARED_FOLDERS] && files[SHARED_FOLDERS][element]);
|
||||
};
|
||||
var isFile = exp.isFile = function (element, allowStr) {
|
||||
if (isSharedFolder(element)) { return false; }
|
||||
return typeof(element) === "number" ||
|
||||
((typeof(files[OLD_FILES_DATA]) !== "undefined" || allowStr)
|
||||
&& typeof(element) === "string");
|
||||
@@ -85,15 +95,11 @@ define([
|
||||
exp.isReadOnlyFile = function (element) {
|
||||
if (!isFile(element)) { return false; }
|
||||
var data = exp.getFileData(element);
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
if (!parsed) { return false; }
|
||||
var pHash = parsed.hashData;
|
||||
if (!pHash || pHash.type !== "pad") { return; }
|
||||
return pHash && pHash.mode === 'view';
|
||||
return Boolean(data.roHref && !data.href);
|
||||
};
|
||||
|
||||
var isFolder = exp.isFolder = function (element) {
|
||||
return typeof(element) === "object";
|
||||
return typeof(element) === "object" || isSharedFolder(element);
|
||||
};
|
||||
exp.isFolderEmpty = function (element) {
|
||||
if (!isFolder(element)) { return false; }
|
||||
@@ -144,9 +150,11 @@ define([
|
||||
|
||||
// Data from filesData
|
||||
var getTitle = exp.getTitle = function (file, type) {
|
||||
if (workgroup) { debug("No titles in workgroups"); return; }
|
||||
if (isSharedFolder(file)) {
|
||||
return '??';
|
||||
}
|
||||
var data = getFileData(file);
|
||||
if (!file || !data || !data.href) {
|
||||
if (!file || !data || !(data.href || data.roHref)) {
|
||||
error("getTitle called with a non-existing file id: ", file, data);
|
||||
return;
|
||||
}
|
||||
@@ -216,14 +224,16 @@ define([
|
||||
|
||||
// GET FILES
|
||||
|
||||
var getFilesRecursively = function (root, arr) {
|
||||
var getFilesRecursively = exp.getFilesRecursively = function (root, arr) {
|
||||
arr = arr || [];
|
||||
for (var e in root) {
|
||||
if (isFile(root[e])) {
|
||||
if (isFile(root[e]) || isSharedFolder(root[e])) {
|
||||
if(arr.indexOf(root[e]) === -1) { arr.push(root[e]); }
|
||||
} else {
|
||||
getFilesRecursively(root[e], arr);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
var _getFiles = {};
|
||||
_getFiles['array'] = function (cat) {
|
||||
@@ -235,6 +245,7 @@ define([
|
||||
});
|
||||
_getFiles['hrefArray'] = function () {
|
||||
var ret = [];
|
||||
if (sharedFolder) { return ret; }
|
||||
getHrefArray().forEach(function (c) {
|
||||
ret = ret.concat(_getFiles[c]());
|
||||
});
|
||||
@@ -249,7 +260,7 @@ define([
|
||||
var root = files[TRASH];
|
||||
var ret = [];
|
||||
var addFiles = function (el) {
|
||||
if (isFile(el.element)) {
|
||||
if (isFile(el.element) || isSharedFolder(el.element)) {
|
||||
if(ret.indexOf(el.element) === -1) { ret.push(el.element); }
|
||||
} else {
|
||||
getFilesRecursively(el.element, ret);
|
||||
@@ -279,10 +290,15 @@ define([
|
||||
if (!files[FILES_DATA]) { return ret; }
|
||||
return Object.keys(files[FILES_DATA]).map(Number);
|
||||
};
|
||||
_getFiles[SHARED_FOLDERS] = function () {
|
||||
var ret = [];
|
||||
if (!files[SHARED_FOLDERS]) { return ret; }
|
||||
return Object.keys(files[SHARED_FOLDERS]).map(Number);
|
||||
};
|
||||
var getFiles = exp.getFiles = function (categories) {
|
||||
var ret = [];
|
||||
if (!categories || !categories.length) {
|
||||
categories = [ROOT, 'hrefArray', TRASH, OLD_FILES_DATA, FILES_DATA];
|
||||
categories = [ROOT, 'hrefArray', TRASH, OLD_FILES_DATA, FILES_DATA, SHARED_FOLDERS];
|
||||
}
|
||||
categories.forEach(function (c) {
|
||||
if (typeof _getFiles[c] === "function") {
|
||||
@@ -295,11 +311,11 @@ define([
|
||||
var getIdFromHref = exp.getIdFromHref = function (href) {
|
||||
var result;
|
||||
getFiles([FILES_DATA]).some(function (id) {
|
||||
if (files[FILES_DATA][id].href === href) {
|
||||
if (files[FILES_DATA][id].href === href ||
|
||||
files[FILES_DATA][id].roHref === href) {
|
||||
result = id;
|
||||
return true;
|
||||
}
|
||||
return;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
@@ -315,7 +331,7 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
if (isFile(root)) {
|
||||
if (isFile(root) || isSharedFolder(root)) {
|
||||
if (compareFiles(file, root)) {
|
||||
if (paths.indexOf(path) === -1) {
|
||||
paths.push(path);
|
||||
@@ -335,6 +351,7 @@ define([
|
||||
return _findFileInRoot([ROOT], file);
|
||||
};
|
||||
var _findFileInHrefArray = function (rootName, file) {
|
||||
if (sharedFolder) { return []; }
|
||||
if (!files[rootName]) { return []; }
|
||||
var unsorted = files[rootName].slice();
|
||||
var ret = [];
|
||||
@@ -345,6 +362,7 @@ define([
|
||||
return ret;
|
||||
};
|
||||
var _findFileInTrash = function (path, file) {
|
||||
if (sharedFolder) { return []; }
|
||||
var root = find(path);
|
||||
var paths = [];
|
||||
var addPaths = function (p) {
|
||||
@@ -561,29 +579,29 @@ define([
|
||||
// DELETE
|
||||
// Permanently delete multiple files at once using a list of paths
|
||||
// NOTE: We have to be careful when removing elements from arrays (trash root, unsorted or template)
|
||||
exp.delete = function (paths, cb, nocheck, isOwnPadRemoved) {
|
||||
exp.delete = function (paths, cb, nocheck) {
|
||||
if (sframeChan) {
|
||||
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||
cmd: "delete",
|
||||
data: {
|
||||
paths: paths,
|
||||
nocheck: nocheck,
|
||||
isOwnPadRemoved: isOwnPadRemoved
|
||||
}
|
||||
}, cb);
|
||||
}
|
||||
exp.deleteMultiplePermanently(paths, nocheck, isOwnPadRemoved);
|
||||
if (typeof cb === "function") { cb(); }
|
||||
cb = cb || function () {};
|
||||
exp.deleteMultiplePermanently(paths, nocheck, cb);
|
||||
//if (typeof cb === "function") { cb(); }
|
||||
};
|
||||
exp.emptyTrash = function (cb) {
|
||||
cb = cb || function () {};
|
||||
if (sframeChan) {
|
||||
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||
cmd: "emptyTrash"
|
||||
}, cb);
|
||||
}
|
||||
files[TRASH] = {};
|
||||
exp.checkDeletedFiles();
|
||||
if(cb) { cb(); }
|
||||
exp.checkDeletedFiles(cb);
|
||||
};
|
||||
|
||||
// RENAME
|
||||
@@ -605,7 +623,7 @@ define([
|
||||
var element = find(path);
|
||||
|
||||
// Folders
|
||||
if (isFolder(element)) {
|
||||
if (isFolder(element) && !isSharedFolder(element)) {
|
||||
var parentPath = path.slice();
|
||||
var oldName = parentPath.pop();
|
||||
if (!newName || !newName.trim() || oldName === newName) { return; }
|
||||
@@ -620,8 +638,13 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
// Files
|
||||
var data = files[FILES_DATA][element];
|
||||
// Files or Shared folder
|
||||
var data;
|
||||
if (isSharedFolder(element)) {
|
||||
data = files[SHARED_FOLDERS][element];
|
||||
} else {
|
||||
data = files[FILES_DATA][element];
|
||||
}
|
||||
if (!data) { return; }
|
||||
if (!newName || newName.trim() === "") {
|
||||
delete data.filename;
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/avatar.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_friends-bg,
|
||||
@warn-color: @colortheme_friends-warn,
|
||||
@color: @colortheme_friends-color
|
||||
);
|
||||
@import (reference) '../../customize/src/less2/include/avatar.less';
|
||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
// body
|
||||
&.cp-app-contacts {
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_friends-bg,
|
||||
@warn-color: @colortheme_friends-warn,
|
||||
@color: @colortheme_friends-color
|
||||
);
|
||||
|
||||
@keyframes example {
|
||||
0% {
|
||||
background: rgba(0,0,0,0.1);
|
||||
|
||||
@@ -12,7 +12,7 @@ define([
|
||||
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/contacts/app-contacts.less',
|
||||
], function (
|
||||
$,
|
||||
Crypto,
|
||||
|
||||
@@ -247,7 +247,7 @@ define([
|
||||
|
||||
// insert a newline if they're holding either
|
||||
var val = this.value;
|
||||
var start = this.selectionState;
|
||||
var start = this.selectionStart;
|
||||
var end = this.selectionEnd;
|
||||
|
||||
if (![start,end].some(function (x) {
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/tools.less';
|
||||
@import (once) '../../customize/src/less2/include/tokenfield.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.tokenfield_main();
|
||||
.framework_min_main();
|
||||
@import (reference) '../../customize/src/less2/include/tokenfield.less';
|
||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
// body
|
||||
&.cp-app-debug {
|
||||
.tokenfield_main();
|
||||
.framework_min_main();
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
|
||||
@@ -16,7 +16,7 @@ define([
|
||||
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/debug/app-debug.less',
|
||||
], function (
|
||||
$,
|
||||
Crypto,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -37,10 +37,25 @@ define([
|
||||
window.addEventListener('message', onMsg);
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
var getSecrets = function (Cryptpad, Utils, cb) {
|
||||
var hash = window.location.hash.slice(1) || Utils.LocalStore.getUserHash() ||
|
||||
Utils.LocalStore.getFSHash();
|
||||
var hash = window.location.hash.slice(1);
|
||||
var secret = Utils.Hash.getSecrets('drive', hash);
|
||||
if (hash) {
|
||||
// Add a shared folder!
|
||||
// XXX password?
|
||||
Cryptpad.addSharedFolder(secret, function (id) {
|
||||
window.CryptPad_newSharedFolder = id;
|
||||
// Update the hash in the address bar
|
||||
var ohc = window.onhashchange;
|
||||
window.onhashchange = function () {};
|
||||
window.location.hash = "";
|
||||
window.onhashchange = ohc;
|
||||
ohc({reset:true});
|
||||
cb(null, secret);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// No password for drive
|
||||
cb(null, Utils.Hash.getSecrets('drive', hash));
|
||||
cb(null, secret);
|
||||
};
|
||||
var addRpc = function (sframeChan, Cryptpad, Utils) {
|
||||
sframeChan.on('EV_BURN_ANON_DRIVE', function () {
|
||||
@@ -52,7 +67,16 @@ define([
|
||||
sframeChan.on('Q_DRIVE_USEROBJECT', function (data, cb) {
|
||||
Cryptpad.userObjectCommand(data, cb);
|
||||
});
|
||||
sframeChan.on('Q_DRIVE_RESTORE', function (data, cb) {
|
||||
Cryptpad.restoreDrive(data, cb);
|
||||
});
|
||||
sframeChan.on('Q_DRIVE_GETOBJECT', function (data, cb) {
|
||||
if (data && data.sharedFolder) {
|
||||
Cryptpad.getSharedFolder(data.sharedFolder, function (obj) {
|
||||
cb(obj);
|
||||
});
|
||||
return;
|
||||
}
|
||||
Cryptpad.getUserObject(function (obj) {
|
||||
cb(obj);
|
||||
});
|
||||
@@ -82,8 +106,10 @@ define([
|
||||
SFCommonO.start({
|
||||
getSecrets: getSecrets,
|
||||
noHash: true,
|
||||
noRealtime: true,
|
||||
driveEvents: true,
|
||||
addRpc: addRpc
|
||||
addRpc: addRpc,
|
||||
isDrive: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ define([
|
||||
|
||||
module.test = function (assert) {
|
||||
var config = {
|
||||
pinPads: Cryptpad.pinPads,
|
||||
outer: true,
|
||||
workgroup: false,
|
||||
testMode: true,
|
||||
loggedIn: false
|
||||
@@ -237,7 +237,8 @@ define([
|
||||
&& typeof files.template[0] === "number"
|
||||
&& typeof files.filesData[files.template[0]] === "object"
|
||||
&& !files.filesData[files.template[0]].filename
|
||||
&& files.filesData[files.template[0]].href === href3
|
||||
&& !files.filesData[files.template[0]].href
|
||||
&& files.filesData[files.template[0]].roHref === href3
|
||||
&& typeof fileId2 === "number"
|
||||
&& typeof files.filesData[fileId2] === "object"
|
||||
&& files.filesData[fileId2].filename === "Trash"
|
||||
@@ -392,11 +393,6 @@ define([
|
||||
console.log("DRIVE operations: rename");
|
||||
return cb();
|
||||
}
|
||||
fo.replace(href1, href2);
|
||||
if (fo.getFileData(id1).href !== href2) {
|
||||
console.log("DRIVE operations: replace");
|
||||
return cb();
|
||||
}
|
||||
|
||||
cb(true);
|
||||
}, "DRIVE operations");
|
||||
|
||||
@@ -1,151 +1,152 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/tokenfield.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
@import (reference) '../../customize/src/less2/include/tokenfield.less';
|
||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_file-bg,
|
||||
@warn-color: @colortheme_file-warn,
|
||||
@color: @colortheme_file-color
|
||||
);
|
||||
.tokenfield_main();
|
||||
&.cp-app-file {
|
||||
|
||||
@button-border: 2px;
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_file-bg,
|
||||
@warn-color: @colortheme_file-warn,
|
||||
@color: @colortheme_file-color
|
||||
);
|
||||
.tokenfield_main();
|
||||
|
||||
// body
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
@button-border: 2px;
|
||||
|
||||
#cp-app-file-content {
|
||||
flex: 1;
|
||||
// body
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-flow: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#cp-app-file-content.ready {
|
||||
//background: url('/customize/bg3.jpg') no-repeat center center;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
#cp-app-file-upfile, #cp-app-file-dlfile {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: @button-border solid black;
|
||||
}
|
||||
|
||||
.cp-app-file-input {
|
||||
width: 0.1px;
|
||||
height: 0.1px;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
media-tag {
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: ~"calc(100vh - 96px)";
|
||||
}
|
||||
}
|
||||
|
||||
#cp-app-file-upload-form, #cp-app-file-download-form {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
|
||||
position: relative;
|
||||
width: 50vh;
|
||||
height: 50vh;
|
||||
display: block;
|
||||
margin: 50px auto;
|
||||
max-width: 80vw;
|
||||
label {
|
||||
line-height: ~"calc(50vh - 20px)";
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
height: 50vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
#cp-app-file-download-form {
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
span {
|
||||
width: 50vh;
|
||||
max-width: 80vw;
|
||||
text-align: center;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-file-hovering {
|
||||
background-color: rgba(255, 0, 115, 0.5) !important;
|
||||
}
|
||||
|
||||
.cp-app-file-block {
|
||||
display: block;
|
||||
}
|
||||
.cp-app-file-hidden {
|
||||
display: none;
|
||||
}
|
||||
.cp-app-file-input + label {
|
||||
//border: 2px solid black;
|
||||
//background-color: rgba(50, 50, 50, .10);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cp-app-file-input:focus + label,
|
||||
.cp-app-file-input + label:hover {
|
||||
//background-color: rgba(50, 50, 50, 0.30);
|
||||
}
|
||||
|
||||
#cp-app-file-dlprogress {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
|
||||
|
||||
transition: width 200ms;
|
||||
width: 0%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
background-color: rgba(255, 0, 115, 0.75);
|
||||
z-index: 10000;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#cp-app-file-download-view {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-flow: column;
|
||||
media-tag {
|
||||
#cp-app-file-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
max-width: 100vw;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&> * {
|
||||
max-height: 100%;
|
||||
align-items: center;
|
||||
flex-flow: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#cp-app-file-content.ready {
|
||||
//background: url('/customize/bg3.jpg') no-repeat center center;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
#cp-app-file-upfile, #cp-app-file-dlfile {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: @button-border solid black;
|
||||
}
|
||||
|
||||
.cp-app-file-input {
|
||||
width: 0.1px;
|
||||
height: 0.1px;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
media-tag {
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: ~"calc(100vh - 96px)";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#cp-app-file-upload-form, #cp-app-file-download-form {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
|
||||
position: relative;
|
||||
width: 50vh;
|
||||
height: 50vh;
|
||||
display: block;
|
||||
margin: 50px auto;
|
||||
max-width: 80vw;
|
||||
label {
|
||||
line-height: ~"calc(50vh - 20px)";
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
height: 50vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
#cp-app-file-download-form {
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
span {
|
||||
width: 50vh;
|
||||
max-width: 80vw;
|
||||
text-align: center;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-file-hovering {
|
||||
background-color: rgba(255, 0, 115, 0.5) !important;
|
||||
}
|
||||
|
||||
.cp-app-file-block {
|
||||
display: block;
|
||||
}
|
||||
.cp-app-file-hidden {
|
||||
display: none;
|
||||
}
|
||||
.cp-app-file-input + label {
|
||||
//border: 2px solid black;
|
||||
//background-color: rgba(50, 50, 50, .10);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cp-app-file-input:focus + label,
|
||||
.cp-app-file-input + label:hover {
|
||||
//background-color: rgba(50, 50, 50, 0.30);
|
||||
}
|
||||
|
||||
#cp-app-file-dlprogress {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
|
||||
|
||||
transition: width 200ms;
|
||||
width: 0%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
background-color: rgba(255, 0, 115, 0.75);
|
||||
z-index: 10000;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#cp-app-file-download-view {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-flow: column;
|
||||
media-tag {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
max-width: 100vw;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&> * {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ define([
|
||||
var increment = function (N) {
|
||||
var l = N.length;
|
||||
while (l-- > 1) {
|
||||
/* jshint probably suspects this is unsafe because we lack types
|
||||
/* our linter suspects this is unsafe because we lack types
|
||||
but as long as this is only used on nonces, it should be safe */
|
||||
if (N[l] !== 255) { return void N[l]++; } // jshint ignore:line
|
||||
if (l === 0) { throw new Error('E_NONCE_TOO_LARGE'); }
|
||||
|
||||
@@ -17,7 +17,7 @@ define([
|
||||
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/file/app-file.less',
|
||||
|
||||
], function (
|
||||
$,
|
||||
|
||||
@@ -1,83 +1,85 @@
|
||||
@import (once) '../../customize/src/less2/include/colortheme-all.less';
|
||||
@import (once) '../../customize/src/less2/include/modal.less';
|
||||
@import (once) '../../customize/src/less2/include/icon-colors.less';
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/tippy.less';
|
||||
@import (once) '../../customize/src/less2/include/checkmark.less';
|
||||
@import (once) '../../customize/src/less2/include/password-input.less';
|
||||
@import (reference) '../../customize/src/less2/include/colortheme-all.less';
|
||||
@import (reference) '../../customize/src/less2/include/modal.less';
|
||||
@import (reference) '../../customize/src/less2/include/icon-colors.less';
|
||||
@import (reference) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (reference) '../../customize/src/less2/include/alertify.less';
|
||||
@import (reference) '../../customize/src/less2/include/tippy.less';
|
||||
@import (reference) '../../customize/src/less2/include/checkmark.less';
|
||||
@import (reference) '../../customize/src/less2/include/password-input.less';
|
||||
|
||||
.iconColors_main();
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
.tippy_main();
|
||||
.checkmark_main(20px);
|
||||
.password_main();
|
||||
&.cp-app-filepicker {
|
||||
.iconColors_main();
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
.tippy_main();
|
||||
.checkmark_main(20px);
|
||||
.password_main();
|
||||
.modal_main();
|
||||
|
||||
#cp-filepicker-dialog {
|
||||
display: none;
|
||||
.cp-modal {
|
||||
.cp-filepicker-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.cp-filepicker-content-element {
|
||||
@darker: darken(@colortheme_modal-fg, 30%);
|
||||
|
||||
width: 125px;
|
||||
//min-width: 200px;
|
||||
//height: 1em;
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
|
||||
display: inline-flex;
|
||||
flex-flow: column;
|
||||
|
||||
box-sizing: content-box;
|
||||
|
||||
text-align: left;
|
||||
line-height: 1em;
|
||||
cursor: pointer;
|
||||
|
||||
background-color: @colortheme_modal-bg;
|
||||
box-shadow: 2px 2px 5px #000;
|
||||
color: @darker;
|
||||
|
||||
transition: all 0.1s;
|
||||
|
||||
&:hover {
|
||||
color: @colortheme_modal-fg;
|
||||
#cp-filepicker-dialog {
|
||||
display: none;
|
||||
.cp-modal {
|
||||
.cp-filepicker-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
align-items: center;
|
||||
.cp-filepicker-content-element {
|
||||
@darker: darken(@colortheme_modal-fg, 30%);
|
||||
|
||||
img {
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
background: #fff;
|
||||
}
|
||||
width: 125px;
|
||||
//min-width: 200px;
|
||||
//height: 1em;
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
|
||||
.cp-filepicker-content-element-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin-top: 5px;
|
||||
max-width: 100%;
|
||||
}
|
||||
.fa {
|
||||
display: inline-flex;
|
||||
flex-flow: column;
|
||||
|
||||
box-sizing: content-box;
|
||||
|
||||
text-align: left;
|
||||
line-height: 1em;
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
font-size: 70px;
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
|
||||
background-color: @colortheme_modal-bg;
|
||||
box-shadow: 2px 2px 5px #000;
|
||||
color: @darker;
|
||||
|
||||
transition: all 0.1s;
|
||||
|
||||
&:hover {
|
||||
color: @colortheme_modal-fg;
|
||||
}
|
||||
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.cp-filepicker-content-element-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin-top: 5px;
|
||||
max-width: 100%;
|
||||
}
|
||||
.fa {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
font-size: 70px;
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@ define([
|
||||
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/filepicker/app-filepicker.less',
|
||||
], function (
|
||||
$,
|
||||
Crypto,
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/framework.less";
|
||||
@import (once) "../../customize/src/less2/include/tools.less";
|
||||
|
||||
.framework_main( @bg-color: @colortheme_kanban-bg,
|
||||
@warn-color: @colortheme_kanban-warn,
|
||||
@color: @colortheme_kanban-color);
|
||||
@import (reference) "../../customize/src/less2/include/browser.less";
|
||||
@import (reference) "../../customize/src/less2/include/framework.less";
|
||||
@import (reference) "../../customize/src/less2/include/tools.less";
|
||||
|
||||
// body
|
||||
&.cp-app-kanban {
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_kanban-bg,
|
||||
@warn-color: @colortheme_kanban-warn,
|
||||
@color: @colortheme_kanban-color
|
||||
);
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-height: 100%;
|
||||
|
||||
@@ -12,6 +12,8 @@ define([
|
||||
'/kanban/jkanban.js',
|
||||
'/kanban/jscolor.js',
|
||||
'css!/kanban/jkanban.css',
|
||||
|
||||
'less!/kanban/app-kanban.less'
|
||||
], function (
|
||||
$,
|
||||
Sortify,
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
@import (once) "../../customize/src/less2/include/framework.less";
|
||||
@import (reference) "../../customize/src/less2/include/framework.less";
|
||||
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_pad-bg,
|
||||
@warn-color: @colortheme_pad-warn,
|
||||
@color: @colortheme_pad-color
|
||||
);
|
||||
.alertify_main();
|
||||
body.cp-app-pad {
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_pad-bg,
|
||||
@warn-color: @colortheme_pad-warn,
|
||||
@color: @colortheme_pad-color
|
||||
);
|
||||
|
||||
// body
|
||||
&.cp-app-pad {
|
||||
.tokenfield_main();
|
||||
#cke_1_top {
|
||||
overflow: visible;
|
||||
padding: 0px;
|
||||
@@ -46,17 +43,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.cke_wysiwyg_frame {
|
||||
min-width: 60%;
|
||||
}
|
||||
|
||||
@media print {
|
||||
#cke_1_top {
|
||||
display:none !important;
|
||||
.cke_wysiwyg_frame {
|
||||
min-width: 60%;
|
||||
}
|
||||
&.cp-app-pad .cp-toolbar-userlist-drawer {
|
||||
display:none;
|
||||
|
||||
@media print {
|
||||
#cke_1_top {
|
||||
display:none !important;
|
||||
}
|
||||
&.cp-app-pad .cp-toolbar-userlist-drawer {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
www/pad/disable-base64.js
Normal file
40
www/pad/disable-base64.js
Normal file
@@ -0,0 +1,40 @@
|
||||
( function() {
|
||||
CKEDITOR.plugins.add('blockbase64', {
|
||||
init: function (editor) {
|
||||
|
||||
var replaceImgText = function (html) {
|
||||
var ret = html.replace( /<img[^>]*src="data:image\/(bmp|dds|gif|jpg|jpeg|png|psd|pspimage|tga|thm|tif|tiff|yuv|ai|eps|ps|svg);base64,.*?"[^>]*>/gi,
|
||||
function () {
|
||||
console.error("Direct image paste is not allowed.");
|
||||
return '';
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
/*var chkImg = function () {
|
||||
// don't execute code if the editor is readOnly
|
||||
if (editor.readOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout( function() {
|
||||
editor.document.$.body.innerHTML = replaceImgText(editor.document.$.body.innerHTML);
|
||||
},100);
|
||||
};
|
||||
|
||||
editor.on('contentDom', function () {
|
||||
// For Firefox
|
||||
editor.document.on('drop', chkImg);
|
||||
// For IE
|
||||
editor.document.getBody().on('drop', chkImg);
|
||||
});*/
|
||||
|
||||
editor.on('paste', function(e) {
|
||||
var html = e.data.dataValue;
|
||||
if (!html) { return; }
|
||||
e.data.dataValue = replaceImgText(html);
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -32,12 +32,13 @@ define([
|
||||
'/common/common-util.js',
|
||||
'/bower_components/chainpad/chainpad.dist.js',
|
||||
'/customize/application_config.js',
|
||||
'/common/test.js',
|
||||
|
||||
'/bower_components/diff-dom/diffDOM.js',
|
||||
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/pad/app-pad.less'
|
||||
], function (
|
||||
$,
|
||||
Hyperjson,
|
||||
@@ -52,7 +53,9 @@ define([
|
||||
Hash,
|
||||
Util,
|
||||
ChainPad,
|
||||
AppConfig)
|
||||
AppConfig,
|
||||
Test
|
||||
)
|
||||
{
|
||||
var DiffDom = window.diffDOM;
|
||||
|
||||
@@ -160,7 +163,14 @@ define([
|
||||
];
|
||||
|
||||
var getHTML = function (inner) {
|
||||
return ('<!DOCTYPE html>\n' + '<html>\n' + inner.innerHTML);
|
||||
return ('<!DOCTYPE html>\n' + '<html>\n' +
|
||||
' <head><meta charset="utf-8"></head>\n <body>' +
|
||||
inner.innerHTML.replace(/<img[^>]*class="cke_anchor"[^>]*data-cke-realelement="([^"]*)"[^>]*>/g,
|
||||
function(match,realElt){
|
||||
//console.log("returning realElt \"" + unescape(realElt)+ "\".");
|
||||
return decodeURIComponent(realElt); }) +
|
||||
' </body>\n</html>'
|
||||
);
|
||||
};
|
||||
|
||||
var CKEDITOR_CHECK_INTERVAL = 100;
|
||||
@@ -730,6 +740,7 @@ define([
|
||||
'.cke_body_width body > *:first-child { margin-top: 0; }';
|
||||
Ckeditor.addCss(newCss);
|
||||
Ckeditor.plugins.addExternal('mediatag','/pad/', 'mediatag-plugin.js');
|
||||
Ckeditor.plugins.addExternal('blockbase64','/pad/', 'disable-base64.js');
|
||||
module.ckeditor = editor = Ckeditor.replace('editor1', {
|
||||
customConfig: '/customize/ckeditor-config.js',
|
||||
});
|
||||
@@ -753,6 +764,79 @@ define([
|
||||
}).nThen(waitFor());
|
||||
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
function launchAnchorTest(test) {
|
||||
// -------- anchor test: make sure the exported anchor contains <a name="..."> -------
|
||||
console.log('---- anchor test: make sure the exported anchor contains <a name="..."> -----.');
|
||||
|
||||
function tryAndTestExport() {
|
||||
console.log("Starting tryAndTestExport.");
|
||||
editor.on( 'dialogShow', function( evt ) {
|
||||
console.log("Anchor dialog detected.");
|
||||
var dialog = evt.data;
|
||||
$(dialog.parts.contents.$).find("input").val('xx-' + Math.round(Math.random()*1000));
|
||||
dialog.click(window.CKEDITOR.dialog.okButton(editor).id);
|
||||
} );
|
||||
var existingText = editor.getData();
|
||||
editor.insertText("A bit of text");
|
||||
console.log("Launching anchor command.");
|
||||
editor.execCommand(editor.ui.get('Anchor').command);
|
||||
console.log("Anchor command launched.");
|
||||
|
||||
var waitH = window.setInterval(function() {
|
||||
console.log("Waited 2s for the dialog to appear");
|
||||
var anchors = window.CKEDITOR.plugins["link"].getEditorAnchors(editor);
|
||||
if(!anchors || anchors.length===0) {
|
||||
test.fail("No anchors found. Please adjust document");
|
||||
} else {
|
||||
console.log(anchors.length + " anchors found.");
|
||||
var exported = getHTML(window.inner);
|
||||
console.log("Obtained exported: " + exported);
|
||||
var allFound = true;
|
||||
for(var i=0; i<anchors.length; i++) {
|
||||
var anchor = anchors[i];
|
||||
console.log("Anchor " + anchor.name);
|
||||
var expected = "<a id=\"" + anchor.id + "\" name=\"" + anchor.name + "\" ";
|
||||
var found = exported.indexOf(expected)>=0;
|
||||
console.log("Found " + expected + " " + found + ".");
|
||||
allFound = allFound && found;
|
||||
}
|
||||
|
||||
console.log("Cleaning up.");
|
||||
if(allFound) {
|
||||
// clean-up
|
||||
editor.execCommand('undo');
|
||||
editor.execCommand('undo');
|
||||
var nint = window.setInterval(function(){
|
||||
console.log("Waiting for undo to yield same result.");
|
||||
if(existingText === editor.getData()) {
|
||||
window.clearInterval(nint);
|
||||
test.pass();
|
||||
}
|
||||
}, 500);
|
||||
} else
|
||||
{
|
||||
test.fail("Not all expected a elements found for document at " + window.top.location + ".");
|
||||
}
|
||||
}
|
||||
window.clearInterval(waitH);
|
||||
},2000);
|
||||
|
||||
|
||||
}
|
||||
var intervalHandle = window.setInterval(function() {
|
||||
if(editor.status==="ready") {
|
||||
window.clearInterval(intervalHandle);
|
||||
console.log("Editor is ready.");
|
||||
tryAndTestExport();
|
||||
} else {
|
||||
console.log("Waiting for editor to be ready.");
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
Test(function(test) {
|
||||
|
||||
launchAnchorTest(test);
|
||||
});
|
||||
andThen2(editor, Ckeditor, framework);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview The Image plugin.
|
||||
*/
|
||||
|
||||
( function() {
|
||||
|
||||
CKEDITOR.plugins.add( 'mediatag', {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@ define([
|
||||
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/poll/app-poll.less',
|
||||
], function (
|
||||
$,
|
||||
Toolbar,
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
@import (reference) '../../customize/src/less2/include/sidebar-layout.less';
|
||||
|
||||
@import (once) '../../customize/src/less2/include/avatar.less';
|
||||
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
|
||||
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_profile-bg,
|
||||
@warn-color: @colortheme_profile-warn,
|
||||
@color: @colortheme_profile-color
|
||||
);
|
||||
.sidebar-layout_main();
|
||||
|
||||
// body
|
||||
&.cp-app-profile {
|
||||
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_profile-bg,
|
||||
@warn-color: @colortheme_profile-warn,
|
||||
@color: @colortheme_profile-color
|
||||
);
|
||||
.sidebar-layout_main();
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
#cp-app-profile-header {
|
||||
|
||||
@@ -20,7 +20,7 @@ define([
|
||||
'css!/bower_components/codemirror/addon/fold/foldgutter.css',
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/profile/app-profile.less',
|
||||
'/bower_components/croppie/croppie.min.js',
|
||||
'css!/bower_components/croppie/croppie.css',
|
||||
], function (
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
|
||||
@import (once) "../../customize/src/less2/include/limit-bar.less";
|
||||
@import (once) "../../customize/src/less2/include/creation.less";
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
@import (reference) '../../customize/src/less2/include/sidebar-layout.less';
|
||||
@import (reference) "../../customize/src/less2/include/limit-bar.less";
|
||||
@import (reference) "../../customize/src/less2/include/creation.less";
|
||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_settings-bg,
|
||||
@warn-color: @colortheme_settings-warn,
|
||||
@color: @colortheme_settings-color
|
||||
);
|
||||
.sidebar-layout_main();
|
||||
.limit-bar_main();
|
||||
.creation_main();
|
||||
|
||||
// body
|
||||
&.cp-app-settings {
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_settings-bg,
|
||||
@warn-color: @colortheme_settings-warn,
|
||||
@color: @colortheme_settings-color
|
||||
);
|
||||
.sidebar-layout_main();
|
||||
.limit-bar_main();
|
||||
.creation_main();
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
font: @colortheme_app-font;
|
||||
|
||||
@@ -16,7 +16,7 @@ define([
|
||||
'/bower_components/file-saver/FileSaver.min.js',
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/settings/app-settings.less',
|
||||
], function (
|
||||
$,
|
||||
Toolbar,
|
||||
@@ -428,6 +428,10 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldPassword === newPassword) {
|
||||
return void UI.alert(Messages.settings_changePasswordNewPasswordSameAsOld);
|
||||
}
|
||||
|
||||
UI.confirm(Messages.settings_changePasswordConfirm,
|
||||
function (yes) {
|
||||
if (!yes) { return; }
|
||||
|
||||
@@ -1,417 +1,420 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) "../../customize/src/less2/include/mediatag.less";
|
||||
@import (once) "../../customize/src/less2/include/framework.less";
|
||||
@import (reference) "../../customize/src/less2/include/markdown.less";
|
||||
@import (reference) "../../customize/src/less2/include/mediatag.less";
|
||||
@import (reference) "../../customize/src/less2/include/framework.less";
|
||||
|
||||
.mediatag_base();
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_slide-bg,
|
||||
@warn-color: @colortheme_slide-warn,
|
||||
@color: @colortheme_slide-color
|
||||
);
|
||||
&.cp-app-slide {
|
||||
|
||||
// body
|
||||
font-size: unset;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
.mediatag_base();
|
||||
|
||||
@slide-default-bg: #000;
|
||||
.size (@n) {
|
||||
// font-size: @n * 1vmin;
|
||||
// line-height: @n * 1.1vmin;
|
||||
font-size: @n * 10%;
|
||||
// line-height: @n * 11%;
|
||||
line-height: 110%;
|
||||
}
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_slide-bg,
|
||||
@warn-color: @colortheme_slide-warn,
|
||||
@color: @colortheme_slide-color
|
||||
);
|
||||
|
||||
h1 { font-size: 40px; }
|
||||
h2 { font-size: 37px; }
|
||||
h3 { font-size: 34px; }
|
||||
h4 { font-size: 31px; }
|
||||
h5 { font-size: 27px; }
|
||||
h6 { font-size: 24px; }
|
||||
|
||||
#cp-app-slide-editor-container {
|
||||
display: inline-flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
width: 50%;
|
||||
min-width: 20%;
|
||||
max-width: 80%;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
.CodeMirror {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
font-size: initial;
|
||||
}
|
||||
.CodeMirror-focused .cm-matchhighlight {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
|
||||
background-position: bottom;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
#cp-app-slide-colorpicker {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#cme_toolbox {
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
#cp-app-slide-editor {
|
||||
flex: 1;
|
||||
// body
|
||||
font-size: unset;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
.CodeMirror {
|
||||
resize: none;
|
||||
//width: 100vw;
|
||||
flex-flow: column;
|
||||
|
||||
@slide-default-bg: #000;
|
||||
.size (@n) {
|
||||
// font-size: @n * 1vmin;
|
||||
// line-height: @n * 1.1vmin;
|
||||
font-size: @n * 10%;
|
||||
// line-height: @n * 11%;
|
||||
line-height: 110%;
|
||||
}
|
||||
&.cp-app-slide-preview {
|
||||
|
||||
h1 { font-size: 40px; }
|
||||
h2 { font-size: 37px; }
|
||||
h3 { font-size: 34px; }
|
||||
h4 { font-size: 31px; }
|
||||
h5 { font-size: 27px; }
|
||||
h6 { font-size: 24px; }
|
||||
|
||||
#cp-app-slide-editor-container {
|
||||
display: inline-flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
width: 50%;
|
||||
min-width: 20%;
|
||||
max-width: 80%;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
.CodeMirror {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
font-size: initial;
|
||||
}
|
||||
.CodeMirror-focused .cm-matchhighlight {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
|
||||
background-position: bottom;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
#cp-app-slide-colorpicker {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#cme_toolbox {
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
#cp-app-slide-editor {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
.CodeMirror {
|
||||
//resize: horizontal;
|
||||
//width: 50vw;
|
||||
resize: none;
|
||||
//width: 100vw;
|
||||
}
|
||||
&.cp-app-slide-preview {
|
||||
.CodeMirror {
|
||||
//resize: horizontal;
|
||||
//width: 50vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-slide-shown {
|
||||
.cp-app-slide-container {
|
||||
.cp-app-slide-shown {
|
||||
.cp-app-slide-container {
|
||||
position: relative;
|
||||
&> media-tag {
|
||||
position: absolute;
|
||||
top:0; right: 0; bottom: 0; left: 0;
|
||||
z-index: -1;
|
||||
&> img {
|
||||
width: 100vw;
|
||||
height: 56.25vw; // height:width ratio = 9/16 = .5625
|
||||
max-height: 100vh;
|
||||
max-width: 177.78vh; // 16/9 = 1.778
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-slide-preview {
|
||||
.cp-app-slide-viewer {
|
||||
width: 50vw;
|
||||
overflow: hidden;
|
||||
div#cp-app-slide-modal:not(.cp-app-slide-shown) {
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: auto;
|
||||
width: auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
#cp-app-slide-modal-content {
|
||||
.cp-app-slide-container {
|
||||
position: relative;
|
||||
&> media-tag {
|
||||
position: absolute;
|
||||
top:0; right: 0; bottom: 0; left: 0;
|
||||
z-index: -1;
|
||||
&> img {
|
||||
width: 50vw;
|
||||
height: 28.125vw;
|
||||
max-height: ~"calc(100vh - 96px)";
|
||||
max-width: ~"calc(16 / 9 * (100vh - 96px))";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-slide-container {
|
||||
width: 100%;
|
||||
}
|
||||
.cp-app-slide-frame {
|
||||
width: 50vw;
|
||||
height: 28.125vw; // height:width ratio = 9/16 = .5625
|
||||
max-height: ~"calc(100vh - 96px)";
|
||||
max-width: ~"calc(16 / 9 * (100vh - 96px))";
|
||||
//max-height: 100vh;
|
||||
//max-width: 177.78vh; // 16/9 = 1.778
|
||||
}
|
||||
}
|
||||
#cp-app-slide-modal-exit {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
.CodeMirror {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Slide position (print mode) */
|
||||
@ratio:0.9;
|
||||
@media print {
|
||||
#cp-app-slide-editor-container {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
#cp-app-slide-print {
|
||||
position: relative;
|
||||
&> media-tag {
|
||||
display: none;
|
||||
font-size: @ratio*11.25vw;
|
||||
.cp-app-slide-frame {
|
||||
display: flex !important;
|
||||
flex-flow: column;
|
||||
padding: 0.5em;
|
||||
margin: auto;
|
||||
border: 1px solid black;
|
||||
height: @ratio*56.25vw;
|
||||
width: @ratio*100vw;
|
||||
page-break-after: always;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
li {
|
||||
min-width: @ratio*50vw;
|
||||
}
|
||||
h1 {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
}
|
||||
.cp-app-slide-container {
|
||||
position: relative;
|
||||
&> media-tag {
|
||||
position: absolute;
|
||||
top:0; right: 0; bottom: 0; left: 0;
|
||||
z-index: -1;
|
||||
&> img {
|
||||
width: 90vw;
|
||||
height: 50.625vw;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-slide-container {
|
||||
width: 90vw;
|
||||
height: 100vh;
|
||||
margin: 0vh 5vw !important;
|
||||
display: flex;
|
||||
&:last-child {
|
||||
height: ~"calc(100vh - 2px)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slide position (present mode) */
|
||||
div#cp-app-slide-modal {
|
||||
display: none;
|
||||
background-color: black;
|
||||
color: white;
|
||||
|
||||
.cp-app-slide-isempty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Navigation buttons */
|
||||
.cp-app-slide-modal-button {
|
||||
position: absolute;
|
||||
top:0; right: 0; bottom: 0; left: 0;
|
||||
z-index: -1;
|
||||
&> img {
|
||||
cursor: pointer;
|
||||
font-size: 30px;
|
||||
opacity: 0.6;
|
||||
display: none;
|
||||
z-index: 9001;
|
||||
}
|
||||
.cp-app-slide-modal-button:hover {
|
||||
opacity: 1;
|
||||
display: block !important;
|
||||
}
|
||||
#cp-app-slide-modal-exit {
|
||||
left: 20px;
|
||||
top: 20px;
|
||||
}
|
||||
#cp-app-slide-modal-left {
|
||||
left: 6vw;
|
||||
bottom: 10vh;
|
||||
}
|
||||
#cp-app-slide-modal-right {
|
||||
right: 6vw;
|
||||
bottom: 10vh;
|
||||
}
|
||||
&.cp-app-slide-shown {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 1000000; // one order of magnitude higher than alertify
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
#cp-app-slide-modal-content {
|
||||
font-size: 20vh;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
.cp-app-slide-frame {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-flow: column !important;
|
||||
|
||||
box-sizing: border-box;
|
||||
border: 1px solid;
|
||||
white-space: normal;
|
||||
|
||||
vertical-align: middle;
|
||||
|
||||
padding: 0.5em;
|
||||
width: 100vw;
|
||||
height: 56.25vw; // height:width ratio = 9/16 = .5625
|
||||
max-height: 100vh;
|
||||
max-width: 177.78vh; // 16/9 = 1.778
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-slide-preview {
|
||||
.cp-app-slide-viewer {
|
||||
width: 50vw;
|
||||
overflow: hidden;
|
||||
div#cp-app-slide-modal:not(.cp-app-slide-shown) {
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: auto;
|
||||
width: auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
#cp-app-slide-modal-content {
|
||||
.cp-app-slide-container {
|
||||
display: inline-flex;
|
||||
height: 100%; width: 100vw;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
&.cp-app-slide-transition {
|
||||
.cp-app-slide-container {
|
||||
position: relative;
|
||||
&> media-tag {
|
||||
position: absolute;
|
||||
top:0; right: 0; bottom: 0; left: 0;
|
||||
z-index: -1;
|
||||
&> img {
|
||||
width: 50vw;
|
||||
height: 28.125vw;
|
||||
max-height: ~"calc(100vh - 96px)";
|
||||
max-width: ~"calc(16 / 9 * (100vh - 96px))";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-slide-container {
|
||||
width: 100%;
|
||||
}
|
||||
.cp-app-slide-frame {
|
||||
width: 50vw;
|
||||
height: 28.125vw; // height:width ratio = 9/16 = .5625
|
||||
max-height: ~"calc(100vh - 96px)";
|
||||
max-width: ~"calc(16 / 9 * (100vh - 96px))";
|
||||
//max-height: 100vh;
|
||||
//max-width: 177.78vh; // 16/9 = 1.778
|
||||
transition: margin-left 1s;
|
||||
}
|
||||
}
|
||||
#cp-app-slide-modal-exit {
|
||||
visibility: hidden;
|
||||
media-tag button {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.CodeMirror {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Slide position (print mode) */
|
||||
@ratio:0.9;
|
||||
@media print {
|
||||
#cp-app-slide-editor-container {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
#cp-app-slide-print {
|
||||
position: relative;
|
||||
display: none;
|
||||
font-size: @ratio*11.25vw;
|
||||
.cp-app-slide-frame {
|
||||
display: flex !important;
|
||||
flex-flow: column;
|
||||
padding: 0.5em;
|
||||
margin: auto;
|
||||
border: 1px solid black;
|
||||
height: @ratio*56.25vw;
|
||||
width: @ratio*100vw;
|
||||
page-break-after: always;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
li {
|
||||
min-width: @ratio*50vw;
|
||||
}
|
||||
h1 {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
}
|
||||
.cp-app-slide-container {
|
||||
position: relative;
|
||||
&> media-tag {
|
||||
position: absolute;
|
||||
top:0; right: 0; bottom: 0; left: 0;
|
||||
z-index: -1;
|
||||
&> img {
|
||||
width: 90vw;
|
||||
height: 50.625vw;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-slide-container {
|
||||
width: 90vw;
|
||||
height: 100vh;
|
||||
margin: 0vh 5vw !important;
|
||||
display: flex;
|
||||
&:last-child {
|
||||
height: ~"calc(100vh - 2px)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slide position (present mode) */
|
||||
div#cp-app-slide-modal {
|
||||
display: none;
|
||||
background-color: black;
|
||||
color: white;
|
||||
|
||||
.cp-app-slide-isempty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Navigation buttons */
|
||||
.cp-app-slide-modal-button {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
font-size: 30px;
|
||||
opacity: 0.6;
|
||||
display: none;
|
||||
z-index: 9001;
|
||||
}
|
||||
.cp-app-slide-modal-button:hover {
|
||||
opacity: 1;
|
||||
display: block !important;
|
||||
}
|
||||
#cp-app-slide-modal-exit {
|
||||
left: 20px;
|
||||
top: 20px;
|
||||
}
|
||||
#cp-app-slide-modal-left {
|
||||
left: 6vw;
|
||||
bottom: 10vh;
|
||||
}
|
||||
#cp-app-slide-modal-right {
|
||||
right: 6vw;
|
||||
bottom: 10vh;
|
||||
}
|
||||
&.cp-app-slide-shown {
|
||||
display: block;
|
||||
position: fixed;
|
||||
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 1000000; // one order of magnitude higher than alertify
|
||||
height: 100vh;
|
||||
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: none;
|
||||
|
||||
background-color: @slide-default-bg;
|
||||
}
|
||||
#cp-app-slide-modal-content {
|
||||
font-size: 20vh;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
|
||||
/* Slide content */
|
||||
div#cp-app-slide-modal #cp-app-slide-modal-content, #cp-app-slide-print {
|
||||
.cp-app-slide-frame {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-flow: column !important;
|
||||
|
||||
box-sizing: border-box;
|
||||
border: 1px solid;
|
||||
white-space: normal;
|
||||
|
||||
vertical-align: middle;
|
||||
|
||||
padding: 0.5em;
|
||||
width: 100vw;
|
||||
height: 56.25vw; // height:width ratio = 9/16 = .5625
|
||||
max-height: 100vh;
|
||||
max-width: 177.78vh; // 16/9 = 1.778
|
||||
margin: auto;
|
||||
}
|
||||
.cp-app-slide-container {
|
||||
display: inline-flex;
|
||||
height: 100%; width: 100vw;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
&.cp-app-slide-transition {
|
||||
.cp-app-slide-container {
|
||||
transition: margin-left 1s;
|
||||
}
|
||||
}
|
||||
media-tag button {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
box-sizing: border-box;
|
||||
z-index: 9001;
|
||||
position: fixed;
|
||||
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: none;
|
||||
|
||||
background-color: @slide-default-bg;
|
||||
}
|
||||
|
||||
/* Slide content */
|
||||
div#cp-app-slide-modal #cp-app-slide-modal-content, #cp-app-slide-print {
|
||||
.cp-app-slide-frame {
|
||||
* {
|
||||
.size(2.75);
|
||||
* {
|
||||
font-size: 1em;
|
||||
line-height: 1em;
|
||||
.size(2.75);
|
||||
* {
|
||||
font-size: 1em;
|
||||
line-height: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
ul, ol {
|
||||
ul, ol {
|
||||
margin: 0;
|
||||
ul, ol {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h1 { .size(5); }
|
||||
h2 { .size(4.2); }
|
||||
h3 { .size(3.6); }
|
||||
h4 { .size (3); }
|
||||
h5 { .size(2.2); }
|
||||
h6 { .size(1.6); }
|
||||
h1 { .size(5); }
|
||||
h2 { .size(4.2); }
|
||||
h3 { .size(3.6); }
|
||||
h4 { .size (3); }
|
||||
h5 { .size(2.2); }
|
||||
h6 { .size(1.6); }
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: inherit;
|
||||
text-align: center;
|
||||
padding-top: 0;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.markdown_preformatted-code;
|
||||
|
||||
ul, ol {
|
||||
min-width: 50%;
|
||||
max-width: 100%;
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
padding-left: 0.3em;
|
||||
}
|
||||
|
||||
// fixes image overflowing
|
||||
media-tag {
|
||||
height: 100%;
|
||||
flex-flow: row;
|
||||
justify-content: center;
|
||||
|
||||
& + * {
|
||||
margin-top: 1rem;
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: inherit;
|
||||
text-align: center;
|
||||
padding-top: 0;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
img { flex: unset; }
|
||||
}
|
||||
|
||||
img {
|
||||
.markdown_preformatted-code;
|
||||
|
||||
ul, ol {
|
||||
min-width: 50%;
|
||||
max-width: 100%;
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
padding-left: 0.3em;
|
||||
}
|
||||
|
||||
// fixes image overflowing
|
||||
media-tag {
|
||||
height: 100%;
|
||||
flex-flow: row;
|
||||
justify-content: center;
|
||||
|
||||
& + * {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
img { flex: unset; }
|
||||
}
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
min-width: 1%;
|
||||
margin: auto;
|
||||
}
|
||||
.cp-app-slide-number {
|
||||
position: absolute;
|
||||
right: 5vh;
|
||||
bottom: 5vh;
|
||||
.size(1);
|
||||
}
|
||||
.cp-app-slide-date {
|
||||
position: absolute;
|
||||
left: 5vh;
|
||||
bottom: 5vh;
|
||||
.size(1);
|
||||
}
|
||||
.cp-app-slide-title {
|
||||
position: absolute;
|
||||
bottom: 5vh;
|
||||
left: 0px; right: 0px;
|
||||
text-align: center;
|
||||
.size(1);
|
||||
}
|
||||
text-align: left;
|
||||
position: relative;
|
||||
min-width: 1%;
|
||||
margin: auto;
|
||||
}
|
||||
.cp-app-slide-number {
|
||||
position: absolute;
|
||||
right: 5vh;
|
||||
bottom: 5vh;
|
||||
.size(1);
|
||||
}
|
||||
.cp-app-slide-date {
|
||||
position: absolute;
|
||||
left: 5vh;
|
||||
bottom: 5vh;
|
||||
.size(1);
|
||||
}
|
||||
.cp-app-slide-title {
|
||||
position: absolute;
|
||||
bottom: 5vh;
|
||||
left: 0px; right: 0px;
|
||||
text-align: center;
|
||||
.size(1);
|
||||
}
|
||||
text-align: left;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.cp-app-slide-frame * {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
.cp-app-slide-frame * {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
min-height:0;
|
||||
min-width:0;
|
||||
}
|
||||
p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
min-height:0;
|
||||
min-width:0;
|
||||
}
|
||||
|
||||
pre.cp-slide-css-error {
|
||||
color: white;
|
||||
}
|
||||
pre.cp-slide-css-error {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ define([
|
||||
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/slide/app-slide.less',
|
||||
|
||||
'css!cm/lib/codemirror.css',
|
||||
'css!cm/addon/dialog/dialog.css',
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/avatar.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_todo-bg,
|
||||
@warn-color: @colortheme_todo-warn,
|
||||
@color: @colortheme_todo-color
|
||||
);
|
||||
|
||||
// body
|
||||
&.cp-app-todo {
|
||||
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_todo-bg,
|
||||
@warn-color: @colortheme_todo-warn,
|
||||
@color: @colortheme_todo-color
|
||||
);
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ define([
|
||||
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/todo/app-todo.less',
|
||||
], function (
|
||||
$,
|
||||
Crypto,
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/tools.less';
|
||||
@import (once) "../../customize/src/less2/include/framework.less";
|
||||
@import (reference) '../../customize/src/less2/include/tools.less';
|
||||
@import (reference) "../../customize/src/less2/include/framework.less";
|
||||
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_whiteboard-bg,
|
||||
@warn-color: @colortheme_whiteboard-warn,
|
||||
@color: @colortheme_whiteboard-color
|
||||
);
|
||||
|
||||
// body
|
||||
&.cp-app-whiteboard {
|
||||
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_whiteboard-bg,
|
||||
@warn-color: @colortheme_whiteboard-warn,
|
||||
@color: @colortheme_whiteboard-color
|
||||
);
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
|
||||
@@ -15,6 +15,7 @@ define([
|
||||
'/bower_components/chainpad/chainpad.dist.js',
|
||||
|
||||
'/bower_components/secure-fabric.js/dist/fabric.min.js',
|
||||
'less!/whiteboard/app-whiteboard.less'
|
||||
], function (
|
||||
$,
|
||||
Sortify,
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/avatar.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
@import (reference) "../../customize/src/less2/include/colortheme-all.less";
|
||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.framework_min_main();
|
||||
|
||||
// body
|
||||
&.cp-app-worker {
|
||||
.framework_min_main();
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ define([
|
||||
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/customize/src/less2/main.less',
|
||||
'less!/worker/app-worker.less',
|
||||
], function (
|
||||
$,
|
||||
Toolbar,
|
||||
|
||||
Reference in New Issue
Block a user