Merge branch 'exportFolderTests' into exportFolder

pull/1/head
ClemDee 5 years ago
commit 75732ba373

@ -169,6 +169,28 @@ define([], function () {
height: 100%; height: 100%;
background: #5cb85c; background: #5cb85c;
} }
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(1800deg);
}
}
.cp-spinner {
display: inline-block;
box-sizing: border-box;
width: 80px;
height: 80px;
border: 11px solid lightgrey;
border-radius: 50%;
border-top-color: transparent;
animation: spin infinite 3s;
animation-timing-function: cubic-bezier(.6,0.15,0.4,0.85);
}
*/}).toString().slice(14, -3); */}).toString().slice(14, -3);
var urlArgs = window.location.href.replace(/^.*\?([^\?]*)$/, function (all, x) { return x; }); var urlArgs = window.location.href.replace(/^.*\?([^\?]*)$/, function (all, x) { return x; });
var elem = document.createElement('div'); var elem = document.createElement('div');
@ -182,7 +204,7 @@ define([], function () {
'</div>', '</div>',
'<div class="cp-loading-container">', '<div class="cp-loading-container">',
'<div class="cp-loading-spinner-container">', '<div class="cp-loading-spinner-container">',
'<span class="fa fa-spinner fa-pulse fa-4x fa-fw"></span>', '<span class="cp-spinner"></span>',
'</div>', '</div>',
'<p id="cp-loading-message"></p>', '<p id="cp-loading-message"></p>',
'</div>' '</div>'

@ -22,13 +22,16 @@
} }
} }
.dropdown-toggle { .dropdown-toggle {
transform: rotate(270deg);
margin-left: 1rem; margin-left: 1rem;
float: right;
} }
.dropdown-menu { .dropdown-menu {
top: -0.7rem; top: -0.7rem;
left: 100%; left: 100%;
&.left { &.left {
left: -10rem; left: 0%;
transform: translate(-100%);
} }
} }
} }

@ -230,9 +230,15 @@ define([
if (!Visible.currently()) { to = window.setTimeout(interval, Thumb.UPDATE_FIRST); } if (!Visible.currently()) { to = window.setTimeout(interval, Thumb.UPDATE_FIRST); }
}; };
var addThumbnail = function (err, thumb, $span, cb) { var addThumbnail = function (err, thumb, $span, cb) {
var u8 = Nacl.util.decodeBase64(thumb.split(',')[1]);
var blob = new Blob([u8], {
type: 'image/png'
});
var url = URL.createObjectURL(blob);
var img = new Image(); var img = new Image();
img.src = thumb.slice(0,5) === 'data:' ? thumb : 'data:image/png;base64,'+thumb; img.src = url;
$span.find('.cp-icon').hide(); $span.find('.cp-icon').hide();
$span.prepend(img); $span.prepend(img);
cb($(img)); cb($(img));

@ -573,9 +573,9 @@ define([
onFriendShare.reg(saveValue); onFriendShare.reg(saveValue);
var getLinkValue = function (initValue) { var getLinkValue = function (initValue) {
var val = initValue || {}; var val = initValue || {};
var edit = initValue ? val.edit : Util.isChecked($(link).find('#cp-share-editable-true')); var edit = val.edit !== undefined ? val.edit : Util.isChecked($(link).find('#cp-share-editable-true'));
var embed = initValue ? val.embed : Util.isChecked($(link).find('#cp-share-embed')); var embed = val.embed !== undefined ? val.embed : Util.isChecked($(link).find('#cp-share-embed'));
var present = initValue ? val.present : Util.isChecked($(link).find('#cp-share-present')); var present = val.present !== undefined ? val.present : Util.isChecked($(link).find('#cp-share-present'));
var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash : hashes.viewHash; var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash : hashes.viewHash;
var href = origin + pathname + '#' + hash; var href = origin + pathname + '#' + hash;
@ -2104,6 +2104,9 @@ define([
}; };
UIElements.createNewPadModal = function (common) { UIElements.createNewPadModal = function (common) {
// if in drive, show new pad modal instead
if ($("body.cp-app-drive").length !== 0) { return void $(".cp-app-drive-element-row.cp-app-drive-new-ghost").click(); }
var $modal = UIElements.createModal({ var $modal = UIElements.createModal({
id: 'cp-app-toolbar-creation-dialog', id: 'cp-app-toolbar-creation-dialog',
$body: $('body') $body: $('body')
@ -2766,8 +2769,12 @@ define([
UIElements.displayCrowdfunding(common); UIElements.displayCrowdfunding(common);
modal.delete(); modal.delete();
}); });
var waitingForStoringCb = false;
$(store).click(function () { $(store).click(function () {
if (waitingForStoringCb) { return; }
waitingForStoringCb = true;
common.getSframeChannel().query("Q_AUTOSTORE_STORE", null, function (err, obj) { common.getSframeChannel().query("Q_AUTOSTORE_STORE", null, function (err, obj) {
waitingForStoringCb = false;
var error = err || (obj && obj.error); var error = err || (obj && obj.error);
if (error) { if (error) {
if (error === 'E_OVER_LIMIT') { if (error === 'E_OVER_LIMIT') {
@ -2854,11 +2861,27 @@ define([
'aria-labelledBy': 'dropdownMenu', 'aria-labelledBy': 'dropdownMenu',
'style': 'display:block;position:static;margin-bottom:5px;' 'style': 'display:block;position:static;margin-bottom:5px;'
}, [ }, [
h('li', h('a.dropdown-item', { h('li', h('a.cp-app-code-context-saveindrive.dropdown-item', {
'tabindex': '-1',
'data-icon': "fa-cloud-upload",
}, Messages.pad_mediatagImport)),
h('li', h('a.cp-app-code-context-download.dropdown-item', {
'tabindex': '-1', 'tabindex': '-1',
}, Messages.pad_mediatagImport)) 'data-icon': "fa-download",
}, Messages.download_mt_button)),
]) ])
]); ]);
// create the icon for each contextmenu option
$(menu).find("li a.dropdown-item").each(function (i, el) {
var $icon = $("<span>");
if ($(el).attr('data-icon')) {
var font = $(el).attr('data-icon').indexOf('cptools') === 0 ? 'cptools' : 'fa';
$icon.addClass(font).addClass($(el).attr('data-icon'));
} else {
$icon.text($(el).text());
}
$(el).prepend($icon);
});
var m = createContextMenu(menu); var m = createContextMenu(menu);
mediatagContextMenu = m; mediatagContextMenu = m;
@ -2868,7 +2891,13 @@ define([
e.stopPropagation(); e.stopPropagation();
m.hide(); m.hide();
var $mt = $menu.data('mediatag'); var $mt = $menu.data('mediatag');
common.importMediaTag($mt); if ($(this).hasClass("cp-app-code-context-saveindrive")) {
common.importMediaTag($mt);
}
else if ($(this).hasClass("cp-app-code-context-download")) {
var media = $mt[0]._mediaObject;
window.saveAs(media._blob.content, media.name);
}
}); });
return m; return m;

@ -319,6 +319,12 @@ define([], function () {
return window.innerHeight < 800 || window.innerWidth < 800; return window.innerHeight < 800 || window.innerWidth < 800;
}; };
Util.stripTags = function (text) {
var div = document.createElement("div");
div.innerHTML = text;
return div.innerText;
};
return Util; return Util;
}); });
}(self)); }(self));

@ -21,6 +21,11 @@ define([
S.leave(); S.leave();
} catch (e) { console.log(e); } } catch (e) { console.log(e); }
} }
if (S.session && S.session.stop) {
try {
S.session.stop();
} catch (e) { console.error(e); }
}
var abort = Util.find(S, ['session', 'realtime', 'abort']); var abort = Util.find(S, ['session', 'realtime', 'abort']);
if (typeof(abort) === 'function') { if (typeof(abort) === 'function') {
S.session.realtime.sync(); S.session.realtime.sync();
@ -52,11 +57,12 @@ define([
Object.keys(b).forEach(function (k) { a[k] = b[k]; }); Object.keys(b).forEach(function (k) { a[k] = b[k]; });
}; };
var get = function (hash, cb, opt) { var get = function (hash, cb, opt, progress) {
if (typeof(cb) !== 'function') { if (typeof(cb) !== 'function') {
throw new Error('Cryptget expects a callback'); throw new Error('Cryptget expects a callback');
} }
opt = opt || {}; opt = opt || {};
progress = progress || function () {};
var config = makeConfig(hash, opt); var config = makeConfig(hash, opt);
var Session = { cb: cb, hasNetwork: Boolean(opt.network) }; var Session = { cb: cb, hasNetwork: Boolean(opt.network) };
@ -65,6 +71,7 @@ define([
var rt = Session.session = info.realtime; var rt = Session.session = info.realtime;
Session.network = info.network; Session.network = info.network;
Session.leave = info.leave; Session.leave = info.leave;
progress(1);
finish(Session, void 0, rt.getUserDoc()); finish(Session, void 0, rt.getUserDoc());
}; };
@ -72,6 +79,16 @@ define([
finish(Session, info.error); finish(Session, info.error);
}; };
// We use the new onMessage handler to compute the progress:
// we should receive 2 checkpoints max, so 100 messages max
// We're going to consider that 1 message = 1%, and we'll send 100%
// at the end
var i = 0;
config.onMessage = function () {
i++;
progress(Math.min(0.99, i/100));
};
overwrite(config, opt); overwrite(config, opt);
Session.realtime = CPNetflux.start(config); Session.realtime = CPNetflux.start(config);

@ -254,8 +254,12 @@ define([
common.clearOwnedChannel = function (channel, cb) { common.clearOwnedChannel = function (channel, cb) {
postMessage("CLEAR_OWNED_CHANNEL", channel, cb); postMessage("CLEAR_OWNED_CHANNEL", channel, cb);
}; };
common.removeOwnedChannel = function (channel, cb) { // "force" allows you to delete your drive ID
postMessage("REMOVE_OWNED_CHANNEL", channel, cb); common.removeOwnedChannel = function (channel, cb, force) {
postMessage("REMOVE_OWNED_CHANNEL", {
channel: channel,
force: force
}, cb);
}; };
common.getDeletedPads = function (data, cb) { common.getDeletedPads = function (data, cb) {
@ -950,7 +954,7 @@ define([
common.logoutFromAll(waitFor(function () { common.logoutFromAll(waitFor(function () {
postMessage("DISCONNECT"); postMessage("DISCONNECT");
})); }));
})); }), true);
} }
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
if (!oldIsOwned) { if (!oldIsOwned) {

@ -15,6 +15,7 @@ define([
var DiffDOM = window.diffDOM; var DiffDOM = window.diffDOM;
var renderer = new Marked.Renderer(); var renderer = new Marked.Renderer();
var restrictedRenderer = new Marked.Renderer();
var Mermaid = { var Mermaid = {
init: function () {} init: function () {}
@ -61,13 +62,18 @@ define([
return h('div.cp-md-toc', content).outerHTML; return h('div.cp-md-toc', content).outerHTML;
}; };
DiffMd.render = function (md, sanitize) { DiffMd.render = function (md, sanitize, restrictedMd) {
Marked.setOptions({
renderer: restrictedMd ? restrictedRenderer : renderer,
});
var r = Marked(md, { var r = Marked(md, {
sanitize: sanitize sanitize: sanitize
}); });
// Add Table of Content // Add Table of Content
r = r.replace(/<div class="cp-md-toc"><\/div>/g, getTOC()); if (!restrictedMd) {
r = r.replace(/<div class="cp-md-toc"><\/div>/g, getTOC());
}
toc = []; toc = [];
return r; return r;
@ -83,12 +89,7 @@ define([
return defaultCode.apply(renderer, arguments); return defaultCode.apply(renderer, arguments);
} }
}; };
restrictedRenderer.code = renderer.code;
var stripTags = function (text) {
var div = document.createElement("div");
div.innerHTML = text;
return div.innerText;
};
renderer.heading = function (text, level) { renderer.heading = function (text, level) {
var i = 0; var i = 0;
@ -105,10 +106,13 @@ define([
toc.push({ toc.push({
level: level, level: level,
id: id, id: id,
title: stripTags(text) title: Util.stripTags(text)
}); });
return "<h" + level + " id=\"" + id + "\"><a href=\"#" + id + "\" class=\"anchor\"></a>" + text + "</h" + level + ">"; return "<h" + level + " id=\"" + id + "\"><a href=\"#" + id + "\" class=\"anchor\"></a>" + text + "</h" + level + ">";
}; };
restrictedRenderer.heading = function (text) {
return text;
};
// Tasks list // Tasks list
var checkedTaskItemPtn = /^\s*(<p>)?\[[xX]\](<\/p>)?\s*/; var checkedTaskItemPtn = /^\s*(<p>)?\[[xX]\](<\/p>)?\s*/;
@ -138,6 +142,13 @@ define([
var cls = (isCheckedTaskItem || isUncheckedTaskItem || hasBogusInput) ? ' class="todo-list-item"' : ''; var cls = (isCheckedTaskItem || isUncheckedTaskItem || hasBogusInput) ? ' class="todo-list-item"' : '';
return '<li'+ cls + '>' + text + '</li>\n'; return '<li'+ cls + '>' + text + '</li>\n';
}; };
restrictedRenderer.listitem = function (text) {
if (bogusCheckPtn.test(text)) {
text = text.replace(bogusCheckPtn, '');
}
return '<li>' + text + '</li>\n';
};
renderer.image = function (href, title, text) { renderer.image = function (href, title, text) {
if (href.slice(0,6) === '/file/') { if (href.slice(0,6) === '/file/') {
// DEPRECATED // DEPRECATED
@ -162,12 +173,19 @@ define([
out += this.options.xhtml ? '/>' : '>'; out += this.options.xhtml ? '/>' : '>';
return out; return out;
}; };
restrictedRenderer.image = renderer.image;
var renderParagraph = function (p) {
return /<media\-tag[\s\S]*>/i.test(p)? p + '\n': '<p>' + p + '</p>\n';
};
renderer.paragraph = function (p) { renderer.paragraph = function (p) {
if (p === '[TOC]') { if (p === '[TOC]') {
return '<p><div class="cp-md-toc"></div></p>'; return '<p><div class="cp-md-toc"></div></p>';
} }
return /<media\-tag[\s\S]*>/i.test(p)? p + '\n': '<p>' + p + '</p>\n'; return renderParagraph(p);
};
restrictedRenderer.paragraph = function (p) {
return renderParagraph(p);
}; };
var MutationObserver = window.MutationObserver; var MutationObserver = window.MutationObserver;

@ -1,11 +1,13 @@
define([ define([
'/common/cryptget.js', '/common/cryptget.js',
'/file/file-crypto.js',
'/common/common-hash.js', '/common/common-hash.js',
'/common/sframe-common-file.js', '/common/common-util.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/bower_components/saferphore/index.js', '/bower_components/saferphore/index.js',
'/bower_components/jszip/dist/jszip.min.js', '/bower_components/jszip/dist/jszip.min.js',
], function (Crypt, Hash, SFCFile, nThen, Saferphore, JsZip) { ], function (Crypt, FileCrypto, Hash, Util, nThen, Saferphore, JsZip) {
var saveAs = window.saveAs;
var sanitize = function (str) { var sanitize = function (str) {
return str.replace(/[\\/?%*:|"<>]/gi, '_')/*.toLowerCase()*/; return str.replace(/[\\/?%*:|"<>]/gi, '_')/*.toLowerCase()*/;
@ -43,6 +45,88 @@ define([
}); });
}; };
var _downloadFile = function (ctx, fData, cb, updateProgress) {
var cancelled = false;
var cancel = function () {
cancelled = true;
};
var parsed = Hash.parsePadUrl(fData.href || fData.roHref);
var hash = parsed.hash;
var name = fData.filename || fData.title;
var secret = Hash.getSecrets('file', hash, fData.password);
var src = Hash.getBlobPathFromHex(secret.channel);
var key = secret.keys && secret.keys.cryptKey;
Util.fetch(src, function (err, u8) {
if (cancelled) { return; }
if (err) { return void cb('E404'); }
FileCrypto.decrypt(u8, key, function (err, res) {
if (cancelled) { return; }
if (err) { return void cb(err); }
if (!res.content) { return void cb('EEMPTY'); }
var dl = function () {
saveAs(res.content, name || res.metadata.name);
};
cb(null, {
metadata: res.metadata,
content: res.content,
download: dl
});
}, updateProgress && updateProgress.progress2);
}, updateProgress && updateProgress.progress);
return {
cancel: cancel
};
};
var _downloadPad = function (ctx, pData, cb, updateProgress) {
var cancelled = false;
var cancel = function () {
cancelled = true;
};
var parsed = Hash.parsePadUrl(pData.href || pData.roHref);
var name = pData.filename || pData.title;
var opts = {
password: pData.password
};
var done = false;
ctx.sframeChan.on("EV_CRYPTGET_PROGRESS", function (data) {
if (done || data.hash !== parsed.hash) { return; }
updateProgress.progress(data.progress);
if (data.progress === 1) {
done = true;
updateProgress.progress2(1);
}
});
ctx.get({
hash: parsed.hash,
opts: opts
}, function (err, val) {
if (cancelled) { return; }
if (err) { return; }
if (!val) { return; }
transform(ctx, parsed.type, val, function (res) {
if (cancelled) { return; }
if (!res.data) { return; }
var dl = function () {
saveAs(res.data, Util.fixFileName(name));
};
cb(null, {
metadata: res.metadata,
content: res.data,
download: dl
});
});
});
return {
cancel: cancel
};
};
// Add a file to the zip. We have to cryptget&transform it if it's a pad // Add a file to the zip. We have to cryptget&transform it if it's a pad
// or fetch&decrypt it if it's a file. // or fetch&decrypt it if it's a file.
var addFile = function (ctx, zip, fData, existingNames) { var addFile = function (ctx, zip, fData, existingNames) {
@ -126,7 +210,7 @@ define([
// Files (mediatags...) // Files (mediatags...)
var todoFile = function () { var todoFile = function () {
var it; var it;
var dl = SFCFile.downloadFile(fData, function (err, res) { var dl = _downloadFile(ctx, fData, function (err, res) {
if (it) { clearInterval(it); } if (it) { clearInterval(it); }
if (err) { return void error(err); } if (err) { return void error(err); }
var opts = { var opts = {
@ -189,6 +273,7 @@ define([
var ctx = { var ctx = {
get: getPad, get: getPad,
data: data.uo.drive, data: data.uo.drive,
folder: data.folder || ctx.data.root,
sf: data.sf, sf: data.sf,
zip: new JsZip(), zip: new JsZip(),
errors: [], errors: [],
@ -197,11 +282,12 @@ define([
max: 0, max: 0,
done: 0 done: 0
}; };
var filesData = data.sharedFolderId && ctx.sf[data.sharedFolderId] ? ctx.sf[data.sharedFolderId].filesData : ctx.data.filesData;
progress('reading', -1); progress('reading', -1);
nThen(function (waitFor) { nThen(function (waitFor) {
ctx.waitFor = waitFor; ctx.waitFor = waitFor;
var zipRoot = ctx.zip.folder('Root'); var zipRoot = ctx.zip.folder('Root');
makeFolder(ctx, ctx.data.root, zipRoot, ctx.data.filesData); makeFolder(ctx, ctx.folder, zipRoot, filesData);
progress('download', {}); progress('download', {});
}).nThen(function () { }).nThen(function () {
console.log(ctx.zip); console.log(ctx.zip);
@ -222,7 +308,33 @@ define([
}; };
}; };
var _downloadFolder = function (ctx, data, cb, updateProgress) {
create(data, ctx.get, function (blob, errors) {
console.error(errors); // TODO show user errors
var dl = function () {
saveAs(blob, data.folderName);
};
cb(null, {download: dl});
}, function (state, progress) {
if (state === "reading") {
updateProgress.folderProgress(0);
}
if (state === "download") {
if (typeof progress.current !== "number") { return; }
updateProgress.folderProgress(progress.current / progress.max);
}
else if (state === "done") {
updateProgress.folderProgress(1);
}
});
};
return { return {
create: create create: create,
downloadFile: _downloadFile,
downloadPad: _downloadPad,
downloadFolder: _downloadFolder,
}; };
}); });

@ -30,9 +30,22 @@
}; };
var isplainTextFile = function (metadata) {
// does its type begins with "text/"
if (metadata.type.indexOf("text/") === 0) { return true; }
// no type and no file extension -> let's guess it's plain text
var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(metadata.name) || [];
if (!metadata.type && !parsedName[2]) { return true; }
// other exceptions
if (metadata.type === 'application/x-javascript') { return true; }
if (metadata.type === 'application/xml') { return true; }
return false;
};
// Default config, can be overriden per media-tag call // Default config, can be overriden per media-tag call
var config = { var config = {
allowed: [ allowed: [
'text/plain',
'image/png', 'image/png',
'image/jpeg', 'image/jpeg',
'image/jpg', 'image/jpg',
@ -53,6 +66,23 @@
text: "Download" text: "Download"
}, },
Plugins: { Plugins: {
/**
* @param {object} metadataObject {name, metadatatype, owners} containing metadata of the file
* @param {strint} url Url of the blob object
* @param {Blob} content Blob object containing the data of the file
* @param {object} cfg Object {Plugins, allowed, download, pdf} containing infos about plugins
* @param {function} cb Callback function: (err, pluginElement) => {}
*/
text: function (metadata, url, content, cfg, cb) {
var plainText = document.createElement('div');
plainText.className = "plain-text-reader";
var reader = new FileReader();
reader.addEventListener('loadend', function (e) {
plainText.innerText = e.srcElement.result;
cb(void 0, plainText);
});
reader.readAsText(content);
},
image: function (metadata, url, content, cfg, cb) { image: function (metadata, url, content, cfg, cb) {
var img = document.createElement('img'); var img = document.createElement('img');
img.setAttribute('src', url); img.setAttribute('src', url);
@ -271,6 +301,9 @@
var blob = decrypted.content; var blob = decrypted.content;
var mediaType = getType(mediaObject, metadata, cfg); var mediaType = getType(mediaObject, metadata, cfg);
if (isplainTextFile(metadata)) {
mediaType = "text";
}
if (mediaType === 'application') { if (mediaType === 'application') {
mediaType = mediaObject.extension; mediaType = mediaObject.extension;

@ -151,7 +151,7 @@ define([
}); });
try { try {
var $d = $(d); var $d = $(d);
DiffMd.apply(DiffMd.render(md || '', true), $d, common); DiffMd.apply(DiffMd.render(md || '', true, true), $d, common);
$d.addClass("cp-app-contacts-content"); $d.addClass("cp-app-contacts-content");
// override link clicking, because we're in an iframe // override link clicking, because we're in an iframe
@ -197,7 +197,7 @@ define([
var getChat = function (id) { var getChat = function (id) {
return $messages.find(dataQuery(id)); return $messages.find(dataQuery(id));
}; };
var scrollChatToBottom = function () { var scrollChatToBottom = function () {
var $messagebox = $('.cp-app-contacts-messages'); var $messagebox = $('.cp-app-contacts-messages');
$messagebox.scrollTop($messagebox[0].scrollHeight); $messagebox.scrollTop($messagebox[0].scrollHeight);

@ -99,6 +99,7 @@ define(['json.sortify'], function (Sortify) {
var addAuthor = function () { var addAuthor = function () {
if (!meta.user || !meta.user.netfluxId || !priv || !priv.edPublic) { return; } if (!meta.user || !meta.user.netfluxId || !priv || !priv.edPublic) { return; }
var authors = metadataObj.authors || {}; var authors = metadataObj.authors || {};
var old = Sortify(authors);
if (!authors[priv.edPublic]) { if (!authors[priv.edPublic]) {
authors[priv.edPublic] = { authors[priv.edPublic] = {
nId: [meta.user.netfluxId], nId: [meta.user.netfluxId],
@ -110,9 +111,11 @@ define(['json.sortify'], function (Sortify) {
authors[priv.edPublic].nId.push(meta.user.netfluxId); authors[priv.edPublic].nId.push(meta.user.netfluxId);
} }
} }
metadataObj.authors = authors; if (Sortify(authors) !== old) {
metadataLazyObj.authors = JSON.parse(JSON.stringify(authors)); metadataObj.authors = authors;
change(); metadataLazyObj.authors = JSON.parse(JSON.stringify(authors));
change();
}
}; };
var netfluxId; var netfluxId;

@ -34,7 +34,7 @@ define([
var sendDriveEvent = function () {}; var sendDriveEvent = function () {};
var registerProxyEvents = function () {}; var registerProxyEvents = function () {};
var storeHash; var storeHash, storeChannel;
var store = window.CryptPad_AsyncStore = { var store = window.CryptPad_AsyncStore = {
modules: {} modules: {}
@ -239,6 +239,20 @@ define([
Store.removeOwnedChannel = function (clientId, data, cb) { Store.removeOwnedChannel = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
// "data" used to be a string (channelID), now it can also be an object
// data.force tells us we can safely remove the drive ID
var channel = data;
var force = false;
if (data && typeof(data) === "object") {
channel = data.channel;
force = data.force;
}
if (channel === storeChannel && !force) {
return void cb({error: 'User drive removal blocked!'});
}
store.rpc.removeOwnedChannel(data, function (err) { store.rpc.removeOwnedChannel(data, function (err) {
cb({error:err}); cb({error:err});
}); });
@ -573,7 +587,10 @@ define([
})); }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
// Delete Drive // Delete Drive
Store.removeOwnedChannel(clientId, secret.channel, waitFor()); Store.removeOwnedChannel(clientId, {
channel: secret.channel,
force: true
}, waitFor());
}).nThen(function () { }).nThen(function () {
store.network.disconnect(); store.network.disconnect();
cb({ cb({
@ -786,6 +803,7 @@ define([
var h = p.hashData; var h = p.hashData;
if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); } if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); }
if (p.type === "debug") { return void cb(); }
var channelData = Store.channels && Store.channels[channel]; var channelData = Store.channels && Store.channels[channel];
@ -1090,7 +1108,6 @@ define([
var channels = Store.channels = store.channels = {}; var channels = Store.channels = store.channels = {};
Store.joinPad = function (clientId, data) { Store.joinPad = function (clientId, data) {
console.log('joining', data.channel);
var isNew = typeof channels[data.channel] === "undefined"; var isNew = typeof channels[data.channel] === "undefined";
var channel = channels[data.channel] = channels[data.channel] || { var channel = channels[data.channel] = channels[data.channel] || {
queue: [], queue: [],
@ -1915,6 +1932,7 @@ define([
} }
// No password for drive // No password for drive
var secret = Hash.getSecrets('drive', hash); var secret = Hash.getSecrets('drive', hash);
storeChannel = secret.channel;
var listmapConfig = { var listmapConfig = {
data: {}, data: {},
websocketURL: NetConfig.getWebsocketURL(), websocketURL: NetConfig.getWebsocketURL(),

@ -99,9 +99,17 @@ define([
// lines beginning with a hash are potentially valuable // lines beginning with a hash are potentially valuable
// works for markdown, python, bash, etc. // works for markdown, python, bash, etc.
var hash = /^#+(.*?)$/; var hash = /^#+(.*?)$/;
var hashAndLink = /^#+\s*\[(.*?)\]\(.*\)\s*$/;
if (hash.test(line)) { if (hash.test(line)) {
// test for link inside the title, and set text just to the name of the link
if (hashAndLink.test(line)) {
line.replace(hashAndLink, function (a, one) {
text = Util.stripTags(one);
});
return true;
}
line.replace(hash, function (a, one) { line.replace(hash, function (a, one) {
text = one; text = Util.stripTags(one);
}); });
return true; return true;
} }
@ -387,21 +395,32 @@ define([
exp.mkIndentSettings = function (metadataMgr) { exp.mkIndentSettings = function (metadataMgr) {
var setIndentation = function (units, useTabs, fontSize, spellcheck) { var setIndentation = function (units, useTabs, fontSize, spellcheck) {
if (typeof(units) !== 'number') { return; } if (typeof(units) !== 'number') { return; }
var doc = editor.getDoc();
editor.setOption('indentUnit', units); editor.setOption('indentUnit', units);
editor.setOption('tabSize', units); editor.setOption('tabSize', units);
editor.setOption('indentWithTabs', useTabs); editor.setOption('indentWithTabs', useTabs);
editor.setOption('spellcheck', spellcheck); editor.setOption('spellcheck', spellcheck);
if (!useTabs) { editor.setOption("extraKeys", {
editor.setOption("extraKeys", { Tab: function() {
Tab: function() { if (doc.somethingSelected()) {
editor.replaceSelection(Array(units + 1).join(" ")); editor.execCommand("indentMore");
} }
}); else {
} else { if (!useTabs) { editor.execCommand("insertSoftTab"); }
editor.setOption("extraKeys", { else { editor.execCommand("insertTab"); }
Tab: undefined, }
}); },
} "Shift-Tab": function () {
editor.execCommand("indentLess");
},
"Backspace": function () {
var cursor = doc.getCursor();
var line = doc.getLine(cursor.line);
if (line.substring(0, cursor.ch).trim() === "") { editor.execCommand("indentLess"); }
else { editor.execCommand("delCharBefore"); }
},
});
$('.CodeMirror').css('font-size', fontSize+'px'); $('.CodeMirror').css('font-size', fontSize+'px');
}; };

@ -1,6 +1,7 @@
define([ define([
'jquery', 'jquery',
'/file/file-crypto.js', '/file/file-crypto.js',
'/common/make-backup.js',
'/common/common-thumbnail.js', '/common/common-thumbnail.js',
'/common/common-interface.js', '/common/common-interface.js',
'/common/common-ui-elements.js', '/common/common-ui-elements.js',
@ -11,9 +12,8 @@ define([
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, FileCrypto, Thumb, UI, UIElements, Util, Hash, h, Messages) { ], function ($, FileCrypto, MakeBackup, Thumb, UI, UIElements, Util, Hash, h, Messages) {
var Nacl = window.nacl; var Nacl = window.nacl;
var saveAs = window.saveAs;
var module = {}; var module = {};
var blobToArrayBuffer = function (blob, cb) { var blobToArrayBuffer = function (blob, cb) {
@ -446,124 +446,108 @@ define([
createUploader(config.dropArea, config.hoverArea, config.body); createUploader(config.dropArea, config.hoverArea, config.body);
File.downloadFile = function (fData, cb) { var updateProgressbar = function (file, data, downloadFunction, cb) {
var parsed = Hash.parsePadUrl(fData.href || fData.roHref); if (queue.inProgress) { return; }
var hash = parsed.hash; queue.inProgress = true;
var name = fData.filename || fData.title; var id = file.id;
var secret = Hash.getSecrets('file', hash, fData.password);
var src = Hash.getBlobPathFromHex(secret.channel);
var key = secret.keys && secret.keys.cryptKey;
common.getFileSize(secret.channel, function (e, data) {
var todo = function (file) {
if (queue.inProgress) { return; }
queue.inProgress = true;
var id = file.id;
var $row = $table.find('tr[id="'+id+'"]');
var $pv = $row.find('.cp-fileupload-table-progress-value');
var $pb = $row.find('.cp-fileupload-table-progress-container');
var $pc = $row.find('.cp-fileupload-table-progress');
var $link = $row.find('.cp-fileupload-table-link');
var done = function () {
$row.find('.cp-fileupload-table-cancel').text('-');
queue.inProgress = false;
queue.next();
};
var updateDLProgress = function (progressValue) { var $row = $table.find('tr[id="'+id+'"]');
var text = Math.round(progressValue*100) + '%'; var $pv = $row.find('.cp-fileupload-table-progress-value');
text += ' ('+ Messages.download_step1 +'...)'; var $pb = $row.find('.cp-fileupload-table-progress-container');
$pv.text(text); var $pc = $row.find('.cp-fileupload-table-progress');
$pb.css({ var $link = $row.find('.cp-fileupload-table-link');
width: progressValue * $pc.width()+'px'
});
};
var updateProgress = function (progressValue) {
var text = Math.round(progressValue*100) + '%';
text += progressValue === 1 ? '' : ' ('+ Messages.download_step2 +'...)';
$pv.text(text);
$pb.css({
width: progressValue * $pc.width()+'px'
});
};
var dl = module.downloadFile(fData, function (err, obj) { var done = function () {
$link.prepend($('<span>', {'class': 'fa fa-external-link'})) $row.find('.cp-fileupload-table-cancel').text('-');
.attr('href', '#') queue.inProgress = false;
.click(function (e) { queue.next();
e.preventDefault(); };
obj.download();
});
done();
if (obj) { obj.download(); }
cb(err, obj);
}, {
src: src,
key: key,
name: name,
progress: updateDLProgress,
progress2: updateProgress,
});
var $cancel = $('<span>', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { var updateDLProgress = function (progressValue) {
dl.cancel(); var text = Math.round(progressValue * 100) + '%';
$cancel.remove(); text += ' (' + Messages.download_step1 + '...)';
$row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); $pv.text(text);
done(); $pb.css({
}); width: progressValue * $pc.width() + 'px'
$row.find('.cp-fileupload-table-cancel').html('').append($cancel); });
}; };
var updateDecryptProgress = function (progressValue) {
var text = Math.round(progressValue*100) + '%';
text += progressValue === 1 ? '' : ' (' + Messages.download_step2 + '...)';
$pv.text(text);
$pb.css({
width: progressValue * $pc.width()+'px'
});
};
var updateProgress = function (progressValue) {
var text = Math.round(progressValue*100) + '%';
$pv.text(text);
$pb.css({
width: progressValue * $pc.width()+'px'
});
};
var ctx = {
get: common.getPad,
sframeChan: sframeChan,
};
downloadFunction(ctx, data, function (err, obj) {
$link.prepend($('<span>', {'class': 'fa fa-external-link'}))
.attr('href', '#')
.click(function (e) {
e.preventDefault();
obj.download();
});
done();
if (obj) { obj.download(); }
cb(err, obj);
}, {
progress: updateDLProgress,
progress2: updateDecryptProgress,
folderProgress: updateProgress,
});
// var $cancel = $('<span>', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () {
// dl.cancel();
// $cancel.remove();
// $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled);
// done();
// });
// $row.find('.cp-fileupload-table-cancel').html('').append($cancel);
$row.find('.cp-fileupload-table-cancel').html('');
};
File.downloadFile = function (fData, cb) {
var name = fData.filename || fData.title;
common.getFileSize(fData.channel, function (e, data) {
queue.push({ queue.push({
dl: todo, dl: function (file) { updateProgressbar(file, fData, MakeBackup.downloadFile, cb); },
size: data, size: data,
name: name name: name
}); });
}); });
}; };
return File; File.downloadPad = function (pData, cb) {
}; queue.push({
dl: function (file) { updateProgressbar(file, pData, MakeBackup.downloadPad, cb); },
module.downloadFile = function (fData, cb, obj) { size: 0,
var cancelled = false; name: pData.title,
var cancel = function () { });
cancelled = true;
}; };
var src, key, name;
if (obj && obj.src && obj.key && obj.name) { File.downloadFolder = function (data, cb) {
src = obj.src; queue.push({
key = obj.key; dl: function (file) { updateProgressbar(file, data, MakeBackup.downloadFolder, cb); },
name = obj.name; size: 0,
} else { name: data.folderName,
var parsed = Hash.parsePadUrl(fData.href || fData.roHref); });
var hash = parsed.hash;
name = fData.filename || fData.title;
var secret = Hash.getSecrets('file', hash, fData.password);
src = Hash.getBlobPathFromHex(secret.channel);
key = secret.keys && secret.keys.cryptKey;
}
Util.fetch(src, function (err, u8) {
if (cancelled) { return; }
if (err) { return void cb('E404'); }
FileCrypto.decrypt(u8, key, function (err, res) {
if (cancelled) { return; }
if (err) { return void cb(err); }
if (!res.content) { return void cb('EEMPTY'); }
var dl = function () {
saveAs(res.content, name || res.metadata.name);
};
cb(null, {
metadata: res.metadata,
content: res.content,
download: dl
});
}, obj && obj.progress2);
}, obj && obj.progress);
return {
cancel: cancel
}; };
return File;
}; };
return module; return module;
}); });

@ -838,13 +838,6 @@ define([
Cryptpad.setLanguage(data, cb); Cryptpad.setLanguage(data, cb);
}); });
sframeChan.on('Q_CLEAR_OWNED_CHANNEL', function (channel, cb) {
Cryptpad.clearOwnedChannel(channel, cb);
});
sframeChan.on('Q_REMOVE_OWNED_CHANNEL', function (channel, cb) {
Cryptpad.removeOwnedChannel(channel, cb);
});
sframeChan.on('Q_GET_ALL_TAGS', function (data, cb) { sframeChan.on('Q_GET_ALL_TAGS', function (data, cb) {
Cryptpad.listAllTags(function (err, tags) { Cryptpad.listAllTags(function (err, tags) {
cb({ cb({
@ -871,6 +864,9 @@ define([
Cryptpad.removeLoginBlock(data, cb); Cryptpad.removeLoginBlock(data, cb);
}); });
// It seems we have performance issues when we open and close a lot of channels over
// the same network, maybe a memory leak. To fix this, we kill and create a new
// network every 30 cryptget calls (1 call = 1 channel)
var cgNetwork; var cgNetwork;
var whenCGReady = function (cb) { var whenCGReady = function (cb) {
if (cgNetwork && cgNetwork !== true) { console.log(cgNetwork); return void cb(); } if (cgNetwork && cgNetwork !== true) { console.log(cgNetwork); return void cb(); }
@ -887,7 +883,12 @@ define([
error: err, error: err,
data: val data: val
}); });
}, data.opts); }, data.opts, function (progress) {
sframeChan.event("EV_CRYPTGET_PROGRESS", {
hash: data.hash,
progress: progress,
});
});
}; };
//return void todo(); //return void todo();
if (i > 30) { if (i > 30) {

@ -459,6 +459,14 @@ define([
}); });
}; */ }; */
funcs.getPad = function (data, cb) {
ctx.sframeChan.query("Q_CRYPTGET", data, function (err, obj) {
if (err) { return void cb(err); }
if (obj.error) { return void cb(obj.error); }
cb(null, obj.data);
}, { timeout: 60000 });
};
funcs.gotoURL = function (url) { ctx.sframeChan.event('EV_GOTO_URL', url); }; funcs.gotoURL = function (url) { ctx.sframeChan.event('EV_GOTO_URL', url); };
funcs.openURL = function (url) { ctx.sframeChan.event('EV_OPEN_URL', url); }; funcs.openURL = function (url) { ctx.sframeChan.event('EV_OPEN_URL', url); };
funcs.openUnsafeURL = function (url) { funcs.openUnsafeURL = function (url) {

@ -30,6 +30,7 @@
@drive_content-bg-ro: darken(@drive_content-bg, 10%); @drive_content-bg-ro: darken(@drive_content-bg, 10%);
@drive_selected-bg: #888; @drive_selected-bg: #888;
@drive_droppable-bg: #FE9A2E;
/* PAGE */ /* PAGE */
@ -107,7 +108,7 @@
.cp-app-drive-container { .cp-app-drive-container {
flex: 1; flex: 1;
overflow: auto; overflow-x: auto;
width: 100%; width: 100%;
display: flex; display: flex;
flex-flow: row; flex-flow: row;
@ -121,6 +122,7 @@
#cp-app-drive-tree { #cp-app-drive-tree {
resize: none; resize: none;
width: 100% !important; width: 100% !important;
min-width: unset;
max-width: unset; max-width: unset;
max-height: unset; max-height: unset;
border-bottom: 1px solid @drive_mobile-tree-border-col; border-bottom: 1px solid @drive_mobile-tree-border-col;
@ -156,7 +158,7 @@
} }
.cp-app-drive-element-droppable { .cp-app-drive-element-droppable {
background-color: #FE9A2E; background-color: @drive_droppable-bg;
color: #222; color: #222;
} }
@ -239,7 +241,6 @@
max-height: 100%; max-height: 100%;
.cp-app-drive-tree-categories-container { .cp-app-drive-tree-categories-container {
flex: 1; flex: 1;
max-width: 500px;
overflow: auto; overflow: auto;
} }
img.cp-app-drive-icon { img.cp-app-drive-icon {
@ -438,13 +439,13 @@
flex: 1; flex: 1;
// Needed to avoid the folder's path to overflows // Needed to avoid the folder's path to overflows
// https://stackoverflow.com/questions/38223879/white-space-nowrap-breaks-flexbox-layout // https://stackoverflow.com/questions/38223879/white-space-nowrap-breaks-flexbox-layout
min-width: 0; // min-width: 0;
} }
#cp-app-drive-content { #cp-app-drive-content {
box-sizing: border-box; box-sizing: border-box;
background: @drive_content-bg; background: @drive_content-bg;
color: @drive_content-fg; color: @drive_content-fg;
overflow: auto; overflow-y: auto;
flex: 1; flex: 1;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
@ -939,6 +940,7 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
transition: all 0.15s; transition: all 0.15s;
cursor: pointer;
&:first-child { &:first-child {
flex-shrink: 1; flex-shrink: 1;
@ -946,17 +948,20 @@
&.cp-app-drive-path-separator { &.cp-app-drive-path-separator {
color: #ccc; color: #ccc;
cursor: default;
} }
&.cp-app-drive-path-collapse { &.cp-app-drive-path-collapse {
position: relative; position: relative;
} }
&:hover { &.cp-app-drive-element-droppable {
background-color: @drive_droppable-bg;
}
&:not(.cp-app-drive-element-droppable):hover {
&:not(.cp-app-drive-path-separator) { &:not(.cp-app-drive-path-separator) {
background-color: darken(@colortheme_drive-bg, 15%); background-color: darken(@colortheme_drive-bg, 15%);
text-decoration: underline; text-decoration: underline;
cursor: pointer;
} }
& ~ .cp-app-drive-path-element { & ~ .cp-app-drive-path-element {
background-color: darken(@colortheme_drive-bg, 15%); background-color: darken(@colortheme_drive-bg, 15%);

@ -17,8 +17,6 @@ define([
'/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-listmap/chainpad-listmap.js',
'/customize/messages.js', '/customize/messages.js',
'/common/jscolor.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/drive/app-drive.less', 'less!/drive/app-drive.less',
@ -43,7 +41,10 @@ define([
{ {
var APP = window.APP = { var APP = window.APP = {
editable: false, editable: false,
mobile: function () { return $('body').width() <= 600; }, // Menu and content area are not inline-block anymore for mobiles mobile: function () {
if (window.matchMedia) { return !window.matchMedia('(any-pointer:fine)').matches; }
else { return $('body').width() <= 600; }
},
isMac: navigator.platform === "MacIntel", isMac: navigator.platform === "MacIntel",
}; };
@ -299,6 +300,33 @@ define([
}); });
}; };
APP.selectedFiles = [];
var isElementSelected = function ($element) {
var elementId = $element.data("path").slice(-1)[0];
return APP.selectedFiles.indexOf(elementId) !== -1;
};
var selectElement = function ($element) {
var elementId = $element.data("path").slice(-1)[0];
if (APP.selectedFiles.indexOf(elementId) === -1) {
APP.selectedFiles.push(elementId);
}
$element.addClass("cp-app-drive-element-selected");
};
var unselectElement = function ($element) {
var elementId = $element.data("path").slice(-1)[0];
var index = APP.selectedFiles.indexOf(elementId);
if (index !== -1) {
APP.selectedFiles.splice(index, 1);
}
$element.removeClass("cp-app-drive-element-selected");
};
var findSelectedElements = function () {
return $(".cp-app-drive-element-selected");
};
var createContextMenu = function () { var createContextMenu = function () {
var menu = h('div.cp-contextmenu.dropdown.cp-unselectable', [ var menu = h('div.cp-contextmenu.dropdown.cp-unselectable', [
h('ul.dropdown-menu', { h('ul.dropdown-menu', {
@ -363,16 +391,34 @@ define([
'data-icon': AppConfig.applicationsIcon.slide, 'data-icon': AppConfig.applicationsIcon.slide,
'data-type': 'slide' 'data-type': 'slide'
}, Messages.button_newslide)), }, Messages.button_newslide)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', { h('li.dropdown-submenu', [
'tabindex': '-1', h('a.cp-app-drive-context-newdocmenu.dropdown-item', {
'data-icon': AppConfig.applicationsIcon.poll, 'tabindex': '-1',
'data-type': 'poll' 'data-icon': "fa-plus",
}, Messages.button_newpoll)), }, Messages.fm_morePads || "More pads"), //XXX
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', { h("ul.dropdown-menu", [
'tabindex': '-1', h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'data-icon': AppConfig.applicationsIcon.whiteboard, 'tabindex': '-1',
'data-type': 'whiteboard' 'data-icon': AppConfig.applicationsIcon.sheet,
}, Messages.button_newwhiteboard)), 'data-type': 'sheet'
}, Messages.button_newsheet)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.whiteboard,
'data-type': 'whiteboard'
}, Messages.button_newwhiteboard)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.kanban,
'data-type': 'kanban'
}, Messages.button_newkanban)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.poll,
'data-type': 'poll'
}, Messages.button_newpoll)),
]),
]),
$separator.clone()[0], $separator.clone()[0],
h('li', h('a.cp-app-drive-context-empty.dropdown-item.cp-app-drive-context-editable', { h('li', h('a.cp-app-drive-context-empty.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1', 'tabindex': '-1',
@ -419,6 +465,7 @@ define([
}, Messages.fc_prop)), }, Messages.fc_prop)),
]) ])
]); ]);
// add icons to the contextmenu options
$(menu).find("li a.dropdown-item").each(function (i, el) { $(menu).find("li a.dropdown-item").each(function (i, el) {
var $icon = $("<span>"); var $icon = $("<span>");
if ($(el).attr('data-icon')) { if ($(el).attr('data-icon')) {
@ -429,21 +476,46 @@ define([
} }
$(el).prepend($icon); $(el).prepend($icon);
}); });
// add events handlers for the contextmenu submenus
$(menu).find(".dropdown-submenu").each(function (i, el) { $(menu).find(".dropdown-submenu").each(function (i, el) {
var $el = $(el); var $el = $(el);
var $a = $el.children().filter("a"); var $a = $el.children().filter("a");
var $sub = $el.find(".dropdown-menu").first(); var $sub = $el.find(".dropdown-menu").first();
var timeoutId;
var showSubmenu = function () {
clearTimeout(timeoutId);
$sub.toggleClass("left", $el.offset().left + $el.outerWidth() + $sub.outerWidth() > $(window).width());
$el.siblings().find(".dropdown-menu").hide();
$sub.show();
};
var hideSubmenu = function () {
$sub.hide();
$sub.removeClass("left");
};
var mouseOutSubmenu = function () {
// don't hide immediately the submenu
timeoutId = setTimeout(hideSubmenu, 100);
};
// Add submenu expand icon // Add submenu expand icon
$a.append(h("span.dropdown-toggle")); $a.append(h("span.dropdown-toggle"));
// Show / hide submenu // Show / hide submenu
$el.hover(function () { $el.hover(function () {
setTimeout(function () { // wait for dom to update showSubmenu();
$sub.toggleClass("left", $el.offset().left + $el.outerWidth() + $sub.outerWidth() > $(window).width());
$sub.show();
});
}, function () { }, function () {
$sub.hide(); mouseOutSubmenu();
$sub.removeClass("left"); });
// handle click event
$el.click(function (e) {
var targetItem = $(e.target).closest(".dropdown-item")[0]; // don't close contextmenu if open submenu
var elTarget = $el.children(".dropdown-item")[0];
if (targetItem === elTarget) { e.stopPropagation(); }
if ($el.children().filter(".dropdown-menu:visible").length !== 0) {
$el.find(".dropdown-menu").hide();
hideSubmenu();
}
else {
showSubmenu();
}
}); });
}); });
return $(menu); return $(menu);
@ -569,7 +641,8 @@ define([
var sel = {}; var sel = {};
var removeSelected = function (keepObj) { var removeSelected = function (keepObj) {
$('.cp-app-drive-element-selected').removeClass("cp-app-drive-element-selected"); APP.selectedFiles = [];
findSelectedElements().removeClass("cp-app-drive-element-selected");
var $container = $driveToolbar.find('#cp-app-drive-toolbar-contextbuttons'); var $container = $driveToolbar.find('#cp-app-drive-toolbar-contextbuttons');
if (!$container.length) { return; } if (!$container.length) { return; }
$container.html(''); $container.html('');
@ -679,7 +752,9 @@ define([
delete sel.move; delete sel.move;
$content.find('.cp-app-drive-element-selected-tmp') $content.find('.cp-app-drive-element-selected-tmp')
.removeClass('cp-app-drive-element-selected-tmp') .removeClass('cp-app-drive-element-selected-tmp')
.addClass('cp-app-drive-element-selected'); .each(function (idx, element) {
selectElement($(element));
});
e.stopPropagation(); e.stopPropagation();
}); });
@ -714,7 +789,9 @@ define([
// Ctrl+A select all // Ctrl+A select all
if (e.which === 65 && (e.ctrlKey || (e.metaKey && APP.isMac))) { if (e.which === 65 && (e.ctrlKey || (e.metaKey && APP.isMac))) {
$content.find('.cp-app-drive-element:not(.cp-app-drive-element-selected)') $content.find('.cp-app-drive-element:not(.cp-app-drive-element-selected)')
.addClass('cp-app-drive-element-selected'); .each(function (idx, element) {
selectElement($(element));
});
return; return;
} }
@ -727,7 +804,7 @@ define([
APP.onElementClick(ev, $(el)); APP.onElementClick(ev, $(el));
}; };
var $selection = $content.find('.cp-app-drive-element.cp-app-drive-element-selected'); var $selection = findSelectedElements();
if ($selection.length === 0) { return void click($elements.first()[0]); } if ($selection.length === 0) { return void click($elements.first()[0]); }
var lastIndex = typeof sel.endSelected === "number" ? sel.endSelected : var lastIndex = typeof sel.endSelected === "number" ? sel.endSelected :
@ -846,12 +923,12 @@ define([
return; return;
} }
removeInput(); removeInput();
removeSelected();
var $name = $element.find('.cp-app-drive-element-name'); var $name = $element.find('.cp-app-drive-element-name');
if (!$name.length) { if (!$name.length) {
$name = $element.find('> .cp-app-drive-element'); $name = $element.find('> .cp-app-drive-element');
} }
$name.hide(); $name.hide();
var isFolder = $element.is(".cp-app-drive-element-folder:not(.cp-app-drive-element-sharedf)");
var el = manager.find(path); var el = manager.find(path);
var name = manager.isFile(el) ? manager.getTitle(el) : path[path.length - 1]; var name = manager.isFile(el) ? manager.getTitle(el) : path[path.length - 1];
if (manager.isSharedFolder(el)) { if (manager.isSharedFolder(el)) {
@ -873,14 +950,21 @@ define([
var newName = $input.val(); var newName = $input.val();
if (JSON.stringify(path) === JSON.stringify(currentPath)) { if (JSON.stringify(path) === JSON.stringify(currentPath)) {
manager.rename(path, $input.val(), function () { manager.rename(path, $input.val(), function () {
renameFoldersOpened(path, newName); if (isFolder) {
path[path.length - 1] = newName; renameFoldersOpened(path, newName);
path[path.length - 1] = newName;
}
APP.displayDirectory(path); APP.displayDirectory(path);
}); });
} }
else { else {
manager.rename(path, $input.val(), function () { manager.rename(path, $input.val(), function () {
renameFoldersOpened(path, newName); if (isFolder) {
renameFoldersOpened(path, newName);
unselectElement($element);
$element.data("path", $element.data("path").slice(0, -1).concat(newName));
selectElement($element);
}
refresh(); refresh();
}); });
} }
@ -901,7 +985,6 @@ define([
// We don't want to open the file/folder when clicking on the input // We don't want to open the file/folder when clicking on the input
$input.on('click dblclick', function (e) { $input.on('click dblclick', function (e) {
removeSelected();
e.stopPropagation(); e.stopPropagation();
}); });
// Remove the browser ability to drag text from the input to avoid // Remove the browser ability to drag text from the input to avoid
@ -1122,8 +1205,9 @@ define([
var getSelectedPaths = function ($element) { var getSelectedPaths = function ($element) {
var paths = []; var paths = [];
if ($('.cp-app-drive-element-selected').length > 1) { if (!$element || $element.length === 0) { return paths; }
var $selected = $('.cp-app-drive-element-selected'); if (findSelectedElements().length > 1) {
var $selected = findSelectedElements();
$selected.each(function (idx, elmt) { $selected.each(function (idx, elmt) {
var ePath = $(elmt).data('path'); var ePath = $(elmt).data('path');
if (ePath) { if (ePath) {
@ -1152,7 +1236,7 @@ define([
} else { } else {
$driveToolbar.find('cp-app-drive-toolbar-emptytrash').hide(); $driveToolbar.find('cp-app-drive-toolbar-emptytrash').hide();
} }
var $li = $content.find('.cp-app-drive-element-selected'); var $li = findSelectedElements();
if ($li.length === 0) { if ($li.length === 0) {
$li = findDataHolder($tree.find('.cp-app-drive-element-active')); $li = findDataHolder($tree.find('.cp-app-drive-element-active'));
} }
@ -1219,6 +1303,7 @@ define([
if (pos+eh <= h && pos >= 0) { return; } if (pos+eh <= h && pos >= 0) { return; }
$content.scrollTop(v); $content.scrollTop(v);
}; };
// Add the "selected" class to the "li" corresponding to the clicked element // Add the "selected" class to the "li" corresponding to the clicked element
var onElementClick = APP.onElementClick = function (e, $element) { var onElementClick = APP.onElementClick = function (e, $element) {
// If "Ctrl" is pressed, do not remove the current selection // If "Ctrl" is pressed, do not remove the current selection
@ -1255,34 +1340,33 @@ define([
var $el; var $el;
removeSelected(true); removeSelected(true);
sel.oldSelection.forEach(function (el) { sel.oldSelection.forEach(function (el) {
if (!$(el).hasClass("cp-app-drive-element-selected")) { if (!isElementSelected($(el))) {
$(el).addClass("cp-app-drive-element-selected"); selectElement($(el));
} }
}); });
for (var i = Math.min(sel.startSelected, sel.endSelected); for (var i = Math.min(sel.startSelected, sel.endSelected);
i <= Math.max(sel.startSelected, sel.endSelected); i <= Math.max(sel.startSelected, sel.endSelected);
i++) { i++) {
$el = $($elements.get(i)); $el = $($elements.get(i));
if (!$el.hasClass("cp-app-drive-element-selected")) { if (!isElementSelected($el)) {
$el.addClass("cp-app-drive-element-selected"); selectElement($el);
} }
} }
} else { } else {
if (!$element.hasClass("cp-app-drive-element-selected")) { if (!isElementSelected($element)) {
$element.addClass("cp-app-drive-element-selected"); selectElement($element);
} else { } else {
$element.removeClass("cp-app-drive-element-selected"); unselectElement($element);
} }
} }
updateContextButton(); updateContextButton();
}; };
var displayMenu = function (e) { // show / hide dropdown separators
var $menu = $contextMenu; var hideSeparators = function ($menu) {
var showSep = false; var showSep = false;
var $lastVisibleSep = null; var $lastVisibleSep = null;
// show / hide drop-down divider $menu.children().each(function (i, el) {
$menu.find(".dropdown-menu").children().each(function (i, el) {
var $el = $(el); var $el = $(el);
if ($el.is(".dropdown-divider")) { if ($el.is(".dropdown-divider")) {
$el.css("display", showSep ? "list-item" : "none"); $el.css("display", showSep ? "list-item" : "none");
@ -1294,18 +1378,36 @@ define([
} }
}); });
if (!showSep && $lastVisibleSep) { $lastVisibleSep.css("display", "none"); } // remove last divider if no options after if (!showSep && $lastVisibleSep) { $lastVisibleSep.css("display", "none"); } // remove last divider if no options after
};
// prepare and display contextmenu
var displayMenu = function (e) {
var $menu = $contextMenu;
// show / hide submenus // show / hide submenus
$menu.find(".dropdown-submenu").each(function (i, el) { $menu.find(".dropdown-submenu").each(function (i, el) {
var $el = $(el); var $el = $(el);
$el.children(".dropdown-menu").css("display", "none");
$el.find("li").each(function (i, li) { $el.find("li").each(function (i, li) {
if ($(li).css("display") !== "none") { if ($(li).css("display") !== "none") {
$(el).css("display", "block"); $el.css("display", "block");
return; return;
} }
}); });
}); });
// show / hide separators
$menu.find(".dropdown-menu").each(function (i, menu) {
hideSeparators($(menu));
});
// show contextmenu at cursor position
$menu.css({ display: "block" }); $menu.css({ display: "block" });
if (APP.mobile()) { return; } if (APP.mobile()) {
$menu.css({
top: ($("#cp-app-drive-toolbar-context-mobile").offset().top + 32) + 'px',
right: '0px',
left: ''
});
return;
}
var h = $menu.outerHeight(); var h = $menu.outerHeight();
var w = $menu.outerWidth(); var w = $menu.outerWidth();
var wH = window.innerHeight; var wH = window.innerHeight;
@ -1358,6 +1460,17 @@ define([
} else { } else {
var $element = findDataHolder($(e.target)); var $element = findDataHolder($(e.target));
// if clicked from tree
var fromTree = $element.closest("#cp-app-drive-tree").length;
if (fromTree) {
removeSelected();
}
// if clicked on non selected element
if (!isElementSelected($element)) {
removeSelected();
}
if (type === 'trash' && !$element.data('path')) { return; } if (type === 'trash' && !$element.data('path')) { return; }
if (!$element.length) { if (!$element.length) {
@ -1366,8 +1479,8 @@ define([
return false; return false;
} }
if (!$element.hasClass('cp-app-drive-element-selected')) { if (!isElementSelected($element)) {
onElementClick(undefined, $element); selectElement($element);
} }
paths = getSelectedPaths($element); paths = getSelectedPaths($element);
@ -1437,6 +1550,7 @@ define([
if (!res) { return; } if (!res) { return; }
manager.delete(pathsList, function () { manager.delete(pathsList, function () {
pathsList.forEach(removeFoldersOpened); pathsList.forEach(removeFoldersOpened);
removeSelected();
refresh(); refresh();
}); });
}, null, true); }, null, true);
@ -1447,7 +1561,7 @@ define([
var paths = []; var paths = [];
var $element = findDataHolder($(ev.target)); var $element = findDataHolder($(ev.target));
if ($element.hasClass('cp-app-drive-element-selected')) { if ($element.hasClass('cp-app-drive-element-selected')) {
var $selected = $('.cp-app-drive-element-selected'); var $selected = findSelectedElements();
$selected.each(function (idx, elmt) { $selected.each(function (idx, elmt) {
var ePath = $(elmt).data('path'); var ePath = $(elmt).data('path');
if (ePath) { if (ePath) {
@ -1464,7 +1578,7 @@ define([
}); });
} else { } else {
removeSelected(); removeSelected();
$element.addClass('cp-app-drive-element-selected'); selectElement($element);
var val = manager.find(path); var val = manager.find(path);
if (!val) { return; } // The element is not in the object if (!val) { return; } // The element is not in the object
paths = [{ paths = [{
@ -1483,7 +1597,13 @@ define([
var findDropPath = function (target) { var findDropPath = function (target) {
var $target = $(target); var $target = $(target);
var $el = findDataHolder($target); var $el;
if ($target.is(".cp-app-drive-path-element")) {
$el = $target;
}
else {
$el = findDataHolder($target);
}
var newPath = $el.data('path'); var newPath = $el.data('path');
var dropEl = newPath && manager.find(newPath); var dropEl = newPath && manager.find(newPath);
if (newPath && manager.isSharedFolder(dropEl)) { if (newPath && manager.isSharedFolder(dropEl)) {
@ -1611,7 +1731,8 @@ define([
$owner.attr('title', Messages.fm_padIsOwnedOther); $owner.attr('title', Messages.fm_padIsOwnedOther);
} }
}; };
var addFileData = function (element, $span) { var thumbsUrls = {};
var addFileData = function (element, $element) {
if (!manager.isFile(element)) { return; } if (!manager.isFile(element)) { return; }
var data = manager.getFileData(element); var data = manager.getFileData(element);
@ -1620,7 +1741,7 @@ define([
var hrefData = Hash.parsePadUrl(href); var hrefData = Hash.parsePadUrl(href);
if (hrefData.type) { if (hrefData.type) {
$span.addClass('cp-border-color-'+hrefData.type); $element.addClass('cp-border-color-'+hrefData.type);
} }
var $state = $('<span>', {'class': 'cp-app-drive-element-state'}); var $state = $('<span>', {'class': 'cp-app-drive-element-state'});
@ -1640,25 +1761,38 @@ define([
var $expire = $expirableIcon.clone().appendTo($state); var $expire = $expirableIcon.clone().appendTo($state);
$expire.attr('title', Messages._getKey('fm_expirablePad', [new Date(data.expire).toLocaleString()])); $expire.attr('title', Messages._getKey('fm_expirablePad', [new Date(data.expire).toLocaleString()]));
} }
_addOwnership($span, $state, data); _addOwnership($element, $state, data);
var name = manager.getTitle(element); var name = manager.getTitle(element);
// The element with the class '.name' is underlined when the 'li' is hovered // The element with the class '.name' is underlined when the 'li' is hovered
var $name = $('<span>', {'class': 'cp-app-drive-element-name'}).text(name); var $name = $('<span>', {'class': 'cp-app-drive-element-name'}).text(name);
$span.append($name); $element.append($name);
$span.append($state); $element.append($state);
$span.attr('title', name); $element.attr('title', name);
// display the thumbnail
// if the thumbnail has already been displayed once, do not reload it, keep the same url
if (thumbsUrls[element]) {
var img = new Image();
img.src = thumbsUrls[element];
$element.find('.cp-icon').addClass('cp-app-drive-element-list');
$element.prepend(img);
$(img).addClass('cp-app-drive-element-grid cp-app-drive-element-thumbnail');
$(img).attr("draggable", false);
}
else {
common.displayThumbnail(href || data.roHref, data.channel, data.password, $element, function ($thumb) {
// Called only if the thumbnail exists
// Remove the .hide() added by displayThumnail() because it hides the icon in list mode too
$element.find('.cp-icon').removeAttr('style').addClass('cp-app-drive-element-list');
$thumb.addClass('cp-app-drive-element-grid cp-app-drive-element-thumbnail');
$thumb.attr("draggable", false);
thumbsUrls[element] = $thumb[0].src;
});
}
var type = Messages.type[hrefData.type] || hrefData.type; var type = Messages.type[hrefData.type] || hrefData.type;
common.displayThumbnail(href || data.roHref, data.channel, data.password, $span, function ($thumb) {
// Called only if the thumbnail exists
// Remove the .hide() added by displayThumnail() because it hides the icon in
// list mode too
$span.find('.cp-icon').removeAttr('style').addClass('cp-app-drive-element-list');
$thumb.addClass('cp-app-drive-element-grid')
.addClass('cp-app-drive-element-thumbnail');
});
var $type = $('<span>', { var $type = $('<span>', {
'class': 'cp-app-drive-element-type cp-app-drive-element-list' 'class': 'cp-app-drive-element-type cp-app-drive-element-list'
}).text(type); }).text(type);
@ -1668,7 +1802,7 @@ define([
var $cdate = $('<span>', { var $cdate = $('<span>', {
'class': 'cp-app-drive-element-ctime cp-app-drive-element-list' 'class': 'cp-app-drive-element-ctime cp-app-drive-element-list'
}).text(getDate(data.ctime)); }).text(getDate(data.ctime));
$span.append($type).append($adate).append($cdate); $element.append($type).append($adate).append($cdate);
}; };
var addFolderData = function (element, key, $span) { var addFolderData = function (element, key, $span) {
@ -1744,12 +1878,9 @@ define([
draggable: true, draggable: true,
'class': 'cp-app-drive-element-row' 'class': 'cp-app-drive-element-row'
}); });
if (!isFolder && Array.isArray(APP.selectedFiles)) { $element.data('path', newPath);
var idx = APP.selectedFiles.indexOf(element); if (isElementSelected($element)) {
if (idx !== -1) { selectElement($element);
$element.addClass('cp-app-drive-element-selected');
APP.selectedFiles.splice(idx, 1);
}
} }
$element.prepend($icon).dblclick(function () { $element.prepend($icon).dblclick(function () {
if (isFolder) { if (isFolder) {
@ -1765,11 +1896,10 @@ define([
addFileData(element, $element); addFileData(element, $element);
} }
$element.addClass(liClass); $element.addClass(liClass);
$element.data('path', newPath);
addDragAndDropHandlers($element, newPath, isFolder, !isTrash); addDragAndDropHandlers($element, newPath, isFolder, !isTrash);
$element.click(function(e) { $element.click(function(e) {
e.stopPropagation(); e.stopPropagation();
onElementClick(e, $element, newPath); onElementClick(e, $element);
}); });
if (!isTrash) { if (!isTrash) {
$element.contextmenu(openContextMenu('tree')); $element.contextmenu(openContextMenu('tree'));
@ -1920,6 +2050,8 @@ define([
} else if (idx > 0 && manager.isFile(el)) { } else if (idx > 0 && manager.isFile(el)) {
name = getElementName(path); name = getElementName(path);
} }
$span.data("path", path.slice(0, idx + 1));
addDragAndDropHandlers($span, path.slice(0, idx), true, true);
if (idx === 0) { name = p === SHARED_FOLDER ? name : getPrettyName(p); } if (idx === 0) { name = p === SHARED_FOLDER ? name : getPrettyName(p); }
else { else {
@ -2564,22 +2696,19 @@ define([
'class': 'cp-app-drive-element cp-app-drive-element-file cp-app-drive-element-row' + roClass, 'class': 'cp-app-drive-element cp-app-drive-element-file cp-app-drive-element-row' + roClass,
draggable: draggable draggable: draggable
}); });
if (Array.isArray(APP.selectedFiles)) {
var sidx = APP.selectedFiles.indexOf(id); var path = [rootName, idx];
if (sidx !== -1) { $element.data('path', path);
$element.addClass('cp-app-drive-element-selected'); if (isElementSelected($element)) {
APP.selectedFiles.splice(sidx, 1); selectElement($element);
}
} }
$element.prepend($icon).dblclick(function () { $element.prepend($icon).dblclick(function () {
openFile(id); openFile(id);
}); });
addFileData(id, $element); addFileData(id, $element);
var path = [rootName, idx];
$element.data('path', path);
$element.click(function(e) { $element.click(function(e) {
e.stopPropagation(); e.stopPropagation();
onElementClick(e, $element, path); onElementClick(e, $element);
}); });
$element.contextmenu(openContextMenu('default')); $element.contextmenu(openContextMenu('default'));
$element.data('context', 'default'); $element.data('context', 'default');
@ -2706,8 +2835,8 @@ define([
e.preventDefault(); e.preventDefault();
if (manager.isInTrashRoot(parentPath)) { parentPath = [TRASH]; } if (manager.isInTrashRoot(parentPath)) { parentPath = [TRASH]; }
else { parentPath.pop(); } else { parentPath.pop(); }
APP.selectedFiles = [r.id];
APP.displayDirectory(parentPath); APP.displayDirectory(parentPath);
APP.selectedFiles = path.slice(-1);
}).appendTo($openDir); }).appendTo($openDir);
} }
$('<a>').text(Messages.fc_prop).click(function () { $('<a>').text(Messages.fc_prop).click(function () {
@ -2798,7 +2927,7 @@ define([
$element.data('path', path); $element.data('path', path);
$element.click(function(e) { $element.click(function(e) {
e.stopPropagation(); e.stopPropagation();
onElementClick(e, $element, path); onElementClick(e, $element);
}); });
$element.contextmenu(openContextMenu('default')); $element.contextmenu(openContextMenu('default'));
$element.data('context', 'default'); $element.data('context', 'default');
@ -3022,7 +3151,7 @@ define([
$context.click(function (e) { $context.click(function (e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
var $li = $content.find('.cp-app-drive-element-selected'); var $li = findSelectedElements();
if ($li.length !== 1) { if ($li.length !== 1) {
$li = findDataHolder($tree.find('.cp-app-drive-element-active')); $li = findDataHolder($tree.find('.cp-app-drive-element-active'));
} }
@ -3032,11 +3161,6 @@ define([
return; return;
} }
// Open the menu // Open the menu
$('.cp-contextmenu').css({
top: ($context.offset().top + 32) + 'px',
right: '0px',
left: ''
});
$li.contextmenu(); $li.contextmenu();
}); });
} else { } else {
@ -3104,7 +3228,7 @@ define([
} }
});*/ });*/
var $sel = $content.find('.cp-app-drive-element-selected'); var $sel = findSelectedElements();
if ($sel.length) { if ($sel.length) {
$sel[0].scrollIntoView(); $sel[0].scrollIntoView();
} else { } else {
@ -3116,6 +3240,9 @@ define([
if (history.isHistoryMode) { if (history.isHistoryMode) {
return void _displayDirectory(path, force); return void _displayDirectory(path, force);
} }
if (!manager.comparePath(currentPath, path)) {
removeSelected();
}
updateObject(sframeChan, proxy, function () { updateObject(sframeChan, proxy, function () {
copyObjectValue(files, proxy.drive); copyObjectValue(files, proxy.drive);
updateSharedFolders(sframeChan, manager, files, folders, function () { updateSharedFolders(sframeChan, manager, files, folders, function () {
@ -3466,15 +3593,37 @@ define([
if (!res) { return; } if (!res) { return; }
manager.delete(pathsList, function () { manager.delete(pathsList, function () {
pathsList.forEach(removeFoldersOpened); pathsList.forEach(removeFoldersOpened);
removeSelected();
refresh(); refresh();
}); });
}); });
}; };
var downloadFolder = function (folderElement, folderName, sfId) {
var todo = function (data) {
data.folder = folderElement;
data.sharedFolderId = sfId;
data.folderName = Util.fixFileName(folderName) + '.zip';
APP.FM.downloadFolder(data, function (err, obj) {
console.log(err, obj);
console.log('DONE');
});
};
todo({
uo: proxy,
sf: folders,
});
};
$contextMenu.on("click", "a", function(e) { $contextMenu.on("click", "a", function(e) {
e.stopPropagation(); e.stopPropagation();
var paths = $contextMenu.data('paths'); var paths = $contextMenu.data('paths');
var pathsList = []; var pathsList = [];
var type = $contextMenu.attr('data-menu-type'); var type = $contextMenu.attr('data-menu-type');
var $this = $(this);
var el, data; var el, data;
if (paths.length === 0) { if (paths.length === 0) {
@ -3483,11 +3632,11 @@ define([
return; return;
} }
if ($(this).hasClass("cp-app-drive-context-rename")) { if ($this.hasClass("cp-app-drive-context-rename")) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
displayRenameInput(paths[0].element, paths[0].path); displayRenameInput(paths[0].element, paths[0].path);
} }
if ($(this).hasClass("cp-app-drive-context-color")) { else if ($this.hasClass("cp-app-drive-context-color")) {
var currentColor = getFolderColor(paths[0].path); var currentColor = getFolderColor(paths[0].path);
pickFolderColor(paths[0].element, currentColor, function (color) { pickFolderColor(paths[0].element, currentColor, function (color) {
paths.forEach(function (p) { paths.forEach(function (p) {
@ -3496,24 +3645,24 @@ define([
refresh(); refresh();
}); });
} }
else if($(this).hasClass("cp-app-drive-context-delete")) { else if($this.hasClass("cp-app-drive-context-delete")) {
if (!APP.loggedIn) { if (!APP.loggedIn) {
return void deletePaths(paths); return void deletePaths(paths);
} }
paths.forEach(function (p) { pathsList.push(p.path); }); paths.forEach(function (p) { pathsList.push(p.path); });
moveElements(pathsList, [TRASH], false, refresh); moveElements(pathsList, [TRASH], false, refresh);
} }
else if ($(this).hasClass('cp-app-drive-context-deleteowned')) { else if ($this.hasClass('cp-app-drive-context-deleteowned')) {
deleteOwnedPaths(paths); deleteOwnedPaths(paths);
} }
else if ($(this).hasClass('cp-app-drive-context-open')) { else if ($this.hasClass('cp-app-drive-context-open')) {
paths.forEach(function (p) { paths.forEach(function (p) {
var $element = p.element; var $element = p.element;
$element.click(); $element.click();
$element.dblclick(); $element.dblclick();
}); });
} }
else if ($(this).hasClass('cp-app-drive-context-openro')) { else if ($this.hasClass('cp-app-drive-context-openro')) {
paths.forEach(function (p) { paths.forEach(function (p) {
var el = manager.find(p.path); var el = manager.find(p.path);
if (paths[0].path[0] === SHARED_FOLDER && APP.newSharedFolder) { if (paths[0].path[0] === SHARED_FOLDER && APP.newSharedFolder) {
@ -3531,10 +3680,10 @@ define([
openFile(null, href); openFile(null, href);
}); });
} }
else if ($(this).hasClass('cp-app-drive-context-expandall') || else if ($this.hasClass('cp-app-drive-context-expandall') ||
$(this).hasClass('cp-app-drive-context-collapseall')) { $this.hasClass('cp-app-drive-context-collapseall')) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
var opened = $(this).hasClass('cp-app-drive-context-expandall'); var opened = $this.hasClass('cp-app-drive-context-expandall');
var openRecursive = function (path) { var openRecursive = function (path) {
setFolderOpened(path, opened); setFolderOpened(path, opened);
var folderContent = manager.find(path); var folderContent = manager.find(path);
@ -3557,16 +3706,49 @@ define([
openRecursive(paths[0].path); openRecursive(paths[0].path);
refresh(); refresh();
} }
else if ($(this).hasClass('cp-app-drive-context-download')) {
else if ($this.hasClass('cp-app-drive-context-download')) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
el = manager.find(paths[0].path); var path = paths[0];
data = manager.getFileData(el); el = manager.find(path.path);
APP.FM.downloadFile(data, function (err, obj) { // folder
console.log(err, obj); if (manager.isFolder(el)) {
console.log('DONE'); // folder
}); var name, folderEl;
if (!manager.isSharedFolder(el)) {
name = path.path[path.path.length - 1];
folderEl = el;
downloadFolder(folderEl, name);
}
// shared folder
else {
data = manager.getSharedFolderData(el);
name = data.title;
folderEl = manager.find(path.path.concat("root"));
downloadFolder(folderEl, name, el);
}
}
// file
else if (manager.isFile(el)) {
// imported file
if (path.element.is(".cp-border-color-file")) {
data = manager.getFileData(el);
APP.FM.downloadFile(data, function (err, obj) {
console.log(err, obj);
console.log('DONE');
});
}
// pad
else {
data = manager.getFileData(el);
APP.FM.downloadPad(data, function (err, obj) {
console.log(err, obj);
console.log('DONE');
});
}
}
} }
else if ($(this).hasClass('cp-app-drive-context-share')) { else if ($this.hasClass('cp-app-drive-context-share')) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
el = manager.find(paths[0].path); el = manager.find(paths[0].path);
var parsed, modal; var parsed, modal;
@ -3617,7 +3799,7 @@ define([
wide: Object.keys(friends).length !== 0 wide: Object.keys(friends).length !== 0
}); });
} }
else if ($(this).hasClass('cp-app-drive-context-newfolder')) { else if ($this.hasClass('cp-app-drive-context-newfolder')) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
var onFolderCreated = function (err, info) { var onFolderCreated = function (err, info) {
if (err) { return void logError(err); } if (err) { return void logError(err); }
@ -3630,21 +3812,21 @@ define([
} }
manager.addFolder(paths[0].path, null, onFolderCreated); manager.addFolder(paths[0].path, null, onFolderCreated);
} }
else if ($(this).hasClass('cp-app-drive-context-newsharedfolder')) { else if ($this.hasClass('cp-app-drive-context-newsharedfolder')) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
addSharedFolderModal(function (obj) { addSharedFolderModal(function (obj) {
if (!obj) { return; } if (!obj) { return; }
manager.addSharedFolder(paths[0].path, obj, refresh); manager.addSharedFolder(paths[0].path, obj, refresh);
}); });
} }
else if ($(this).hasClass("cp-app-drive-context-newdoc")) { else if ($this.hasClass("cp-app-drive-context-newdoc")) {
var ntype = $(this).data('type') || 'pad'; var ntype = $this.data('type') || 'pad';
var path2 = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath; var path2 = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
common.sessionStorage.put(Constants.newPadPathKey, path2, function () { common.sessionStorage.put(Constants.newPadPathKey, path2, function () {
common.openURL('/' + ntype + '/'); common.openURL('/' + ntype + '/');
}); });
} }
else if ($(this).hasClass("cp-app-drive-context-properties")) { else if ($this.hasClass("cp-app-drive-context-properties")) {
if (type === 'trash') { if (type === 'trash') {
var pPath = paths[0].path; var pPath = paths[0].path;
if (paths.length !== 1 || pPath.length !== 4) { return; } if (paths.length !== 1 || pPath.length !== 4) { return; }
@ -3664,7 +3846,7 @@ define([
UI.alert($prop[0], undefined, true); UI.alert($prop[0], undefined, true);
}); });
} }
else if ($(this).hasClass("cp-app-drive-context-hashtag")) { else if ($this.hasClass("cp-app-drive-context-hashtag")) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
el = manager.find(paths[0].path); el = manager.find(paths[0].path);
data = manager.getFileData(el); data = manager.getFileData(el);
@ -3672,7 +3854,7 @@ define([
var href = data.href || data.roHref; var href = data.href || data.roHref;
common.updateTags(href); common.updateTags(href);
} }
else if ($(this).hasClass("cp-app-drive-context-empty")) { else if ($this.hasClass("cp-app-drive-context-empty")) {
if (paths.length !== 1 || !paths[0].element if (paths.length !== 1 || !paths[0].element
|| !manager.comparePath(paths[0].path, [TRASH])) { || !manager.comparePath(paths[0].path, [TRASH])) {
log(Messages.fm_forbidden); log(Messages.fm_forbidden);
@ -3683,13 +3865,13 @@ define([
manager.emptyTrash(refresh); manager.emptyTrash(refresh);
}); });
} }
else if ($(this).hasClass("cp-app-drive-context-remove")) { else if ($this.hasClass("cp-app-drive-context-remove")) {
return void deletePaths(paths); return void deletePaths(paths);
} }
else if ($(this).hasClass("cp-app-drive-context-removesf")) { else if ($this.hasClass("cp-app-drive-context-removesf")) {
return void deletePaths(paths); return void deletePaths(paths);
} }
else if ($(this).hasClass("cp-app-drive-context-restore")) { else if ($this.hasClass("cp-app-drive-context-restore")) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
var restorePath = paths[0].path; var restorePath = paths[0].path;
var restoreName = paths[0].path[paths[0].path.length - 1]; var restoreName = paths[0].path[paths[0].path.length - 1];
@ -3706,21 +3888,20 @@ define([
manager.restore(restorePath, refresh); manager.restore(restorePath, refresh);
}); });
} }
else if ($(this).hasClass("cp-app-drive-context-openparent")) { else if ($this.hasClass("cp-app-drive-context-openparent")) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
var parentPath = paths[0].path.slice(); var parentPath = paths[0].path.slice();
if (manager.isInTrashRoot(parentPath)) { parentPath = [TRASH]; } if (manager.isInTrashRoot(parentPath)) { parentPath = [TRASH]; }
else { parentPath.pop(); } else { parentPath.pop(); }
el = manager.find(paths[0].path);
APP.selectedFiles = [el];
APP.displayDirectory(parentPath); APP.displayDirectory(parentPath);
APP.selectedFiles = paths[0].path.slice(-1);
} }
APP.hideMenu(); APP.hideMenu();
}); });
$content.on("keydown", function (e) { $(window).on("keydown", function (e) {
if (e.which === 113) { if (e.which === 113) { // if F2 key pressed
var paths = $contextMenu.data('paths'); var paths = getSelectedPaths(findSelectedElements().first());
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
displayRenameInput(paths[0].element, paths[0].path); displayRenameInput(paths[0].element, paths[0].path);
} }
@ -3732,10 +3913,9 @@ define([
e.preventDefault(); e.preventDefault();
}); });
$appContainer.on('mouseup', function (e) { $appContainer.on('mouseup', function (e) {
//if (sel.down) { return; }
if (e.which !== 1) { return ; } if (e.which !== 1) { return ; }
if ($(e.target).is(".dropdown-submenu a, .dropdown-submenu a span")) { return; } // if we click on dropdown-submenu, don't close menu
APP.hideMenu(e); APP.hideMenu(e);
//removeSelected(e);
}); });
$appContainer.on('click', function (e) { $appContainer.on('click', function (e) {
if (e.which !== 1) { return ; } if (e.which !== 1) { return ; }
@ -3754,7 +3934,7 @@ define([
if (manager.isPathIn(currentPath, [FILES_DATA]) && APP.loggedIn) { if (manager.isPathIn(currentPath, [FILES_DATA]) && APP.loggedIn) {
return; // We can't remove elements directly from filesData return; // We can't remove elements directly from filesData
} }
var $selected = $('.cp-app-drive-element-selected'); var $selected = findSelectedElements();
if (!$selected.length) { return; } if (!$selected.length) { return; }
var paths = []; var paths = [];
var isTrash = manager.isPathIn(currentPath, [TRASH]); var isTrash = manager.isPathIn(currentPath, [TRASH]);

@ -52,6 +52,16 @@
max-width: 100%; max-width: 100%;
max-height: ~"calc(100vh - 96px)"; max-height: ~"calc(100vh - 96px)";
} }
.plain-text-reader {
align-self: flex-start;
width: 90vw;
height: 100%;
padding: 2em;
background-color: white;
overflow-y: auto;
word-wrap: break-word;
white-space: pre-wrap;
}
} }
#cp-app-file-upload-form, #cp-app-file-download-form { #cp-app-file-upload-form, #cp-app-file-download-form {

@ -111,8 +111,14 @@
vertical-align: middle; vertical-align: middle;
margin-right: 5px; margin-right: 5px;
} }
input[type="color"] { .cp-settings-cursor-color-picker {
width: 100px; display: inline-block;
vertical-align: middle;
height: 25px;
width: 70px;
margin-right: 10px;
cursor: pointer;
border: 1px solid black;
} }
.cp-settings-language-selector { .cp-settings-language-selector {
button.btn { button.btn {

@ -15,6 +15,7 @@ define([
'/common/make-backup.js', '/common/make-backup.js',
'/common/common-feedback.js', '/common/common-feedback.js',
'/common/jscolor.js',
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
@ -1082,18 +1083,9 @@ define([
var exportDrive = function () { var exportDrive = function () {
Feedback.send('FULL_DRIVE_EXPORT_START'); Feedback.send('FULL_DRIVE_EXPORT_START');
var todo = function (data, filename) { var todo = function (data, filename) {
var getPad = function (data, cb) {
sframeChan.query("Q_CRYPTGET", data, function (err, obj) {
if (err) { return void cb(err); }
if (obj.error) { return void cb(obj.error); }
cb(null, obj.data);
}, { timeout: 60000 });
};
var ui = createExportUI(); var ui = createExportUI();
var bu = Backup.create(data, getPad, function (blob, errors) { var bu = Backup.create(data, common.getPad, function (blob, errors) {
console.log(blob);
saveAs(blob, filename); saveAs(blob, filename);
sframeChan.event('EV_CRYPTGET_DISCONNECT'); sframeChan.event('EV_CRYPTGET_DISCONNECT');
ui.complete(function () { ui.complete(function () {
@ -1191,13 +1183,13 @@ define([
var $inputBlock = $('<div>').appendTo($div); var $inputBlock = $('<div>').appendTo($div);
var $colorPicker = $("<div>", { class: "cp-settings-cursor-color-picker"});
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}); var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}); var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
var $input = $('<input>', { // when jscolor picker value change
type: 'color', var onchange = function (colorL) {
}).on('change', function () { var val = "#" + colorL.toString();
var val = $input.val();
if (!/^#[0-9a-fA-F]{6}$/.test(val)) { return; } if (!/^#[0-9a-fA-F]{6}$/.test(val)) { return; }
$spinner.show(); $spinner.show();
$ok.hide(); $ok.hide();
@ -1205,15 +1197,25 @@ define([
$spinner.hide(); $spinner.hide();
$ok.show(); $ok.show();
}); });
}).appendTo($inputBlock); };
$ok.hide().appendTo($inputBlock); // jscolor picker
$spinner.hide().appendTo($inputBlock); var jscolorL = new window.jscolor($colorPicker[0],{showOnClick: false, onFineChange: onchange, valueElement:undefined});
$colorPicker.click(function () {
jscolorL.show();
});
// set default color
common.getAttribute(['general', 'cursor', 'color'], function (e, val) { common.getAttribute(['general', 'cursor', 'color'], function (e, val) {
if (e) { return void console.error(e); } if (e) { return void console.error(e); }
$input.val(val || ''); val = val || "#000";
jscolorL.fromString(val);
}); });
$colorPicker.appendTo($inputBlock);
$ok.hide().appendTo($inputBlock);
$spinner.hide().appendTo($inputBlock);
return $div; return $div;
}; };

@ -257,7 +257,7 @@ define([
metadataMgr.onChange(function () { metadataMgr.onChange(function () {
var md = metadataMgr.getMetadata(); var md = metadataMgr.getMetadata();
if (md.palette) { if (md.palette) {
updateLocalPalette(md.palette); updatePalette(md.palette);
} }
}); });

Loading…
Cancel
Save