From 5e1257e630debf241a0001f96b5f1fee7646c7e5 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 5 Jul 2019 10:19:34 +0200 Subject: [PATCH 01/60] Make contextmenu separators hide for submenus too --- www/drive/inner.js | 91 ++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index 510eabbe1..b06d56da2 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -327,34 +327,56 @@ define([ 'tabindex': '-1', 'data-icon': faColor, }, Messages.fc_color)), -// h('li.dropdown-submenu', [ -// h('a.cp-app-drive-context-test.dropdown-item', { -// 'tabindex': '-1', -// 'data-icon': faFolderOpen, -// }, "TEST"), -// h("ul.dropdown-menu", [ -// h('li', h('a.cp-app-drive-context-subtest1.dropdown-item', { -// 'tabindex': '-1', -// 'data-icon': faFolderOpen, -// }, "Sub test 1")), -// h('li.dropdown-submenu', [ -// h('a.cp-app-drive-context-test.dropdown-item', { -// 'tabindex': '-1', -// 'data-icon': faFolderOpen, -// }, "TEST"), -// h("ul.dropdown-menu", [ -// h('li', h('a.cp-app-drive-context-subtest2.dropdown-item', { -// 'tabindex': '-1', -// 'data-icon': faFolderOpen, -// }, "Sub test 2")), -// h('li', h('a.cp-app-drive-context-subtest3.dropdown-item', { -// 'tabindex': '-1', -// 'data-icon': faFolderOpen, -// }, "Sub test 3")), -// ]), -// ]), -// ]), -// ]), + h('li.dropdown-submenu', [ + h('a.cp-app-drive-context-test.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faFolderOpen, + }, "TEST"), + h("ul.dropdown-menu", [ + h('li', h('a.cp-app-drive-context-subtest1.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faFolderOpen, + }, "Sub test 1")), + h('li.dropdown-submenu', [ + h('a.cp-app-drive-context-test.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faFolderOpen, + }, "TEST"), + h("ul.dropdown-menu", [ + h('li', h('a.cp-app-drive-context-subtest2.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faFolderOpen, + }, "Sub test 2")), + h('li', h('a.cp-app-drive-context-subtest3.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faFolderOpen, + }, "Sub test 3")), + ]), + ]), + $separator.clone()[0], + h('li', h('a.cp-app-drive-context-subtest4.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faFolderOpen, + }, "Sub test 4")), + $separator.clone()[0], + h('li.dropdown-submenu', [ + h('a.cp-app-drive-context-test.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faFolderOpen, + }, "TEST"), + h("ul.dropdown-menu", [ + h('li', h('a.cp-app-drive-context-subtest5.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faFolderOpen, + }, "Sub test 5")), + h('li', h('a.cp-app-drive-context-subtest6.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faFolderOpen, + }, "Sub test 6")), + ]), + ]), + ]), + ]), h('li', h('a.cp-app-drive-context-download.dropdown-item', { 'tabindex': '-1', 'data-icon': faDownload, @@ -1297,12 +1319,11 @@ define([ updateContextButton(); }; - var displayMenu = function (e) { - var $menu = $contextMenu; + // show / hide dropdown separators + var hideSeparators = function ($menu) { var showSep = false; var $lastVisibleSep = null; - // show / hide drop-down divider - $menu.find(".dropdown-menu").children().each(function (i, el) { + $menu.children().each(function (i, el) { var $el = $(el); if ($el.is(".dropdown-divider")) { $el.css("display", showSep ? "list-item" : "none"); @@ -1314,6 +1335,12 @@ define([ } }); if (!showSep && $lastVisibleSep) { $lastVisibleSep.css("display", "none"); } // remove last divider if no options after + } + var displayMenu = function (e) { + var $menu = $contextMenu; + $menu.find(".dropdown-menu").each(function (i, menu) { + hideSeparators($(menu)); + }); // show / hide submenus $menu.find(".dropdown-submenu").each(function (i, el) { var $el = $(el); From b63354532ceeb8f3f170a23e60eb55c93a2f957c Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 5 Jul 2019 12:30:21 +0200 Subject: [PATCH 02/60] Context submenu now respond to click event --- www/drive/inner.js | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index b06d56da2..de78d6da3 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -328,7 +328,7 @@ define([ 'data-icon': faColor, }, Messages.fc_color)), h('li.dropdown-submenu', [ - h('a.cp-app-drive-context-test.dropdown-item', { + h('a.cp-app-drive-context-test1.dropdown-item', { 'tabindex': '-1', 'data-icon': faFolderOpen, }, "TEST"), @@ -338,7 +338,7 @@ define([ 'data-icon': faFolderOpen, }, "Sub test 1")), h('li.dropdown-submenu', [ - h('a.cp-app-drive-context-test.dropdown-item', { + h('a.cp-app-drive-context-test2.dropdown-item', { 'tabindex': '-1', 'data-icon': faFolderOpen, }, "TEST"), @@ -360,7 +360,7 @@ define([ }, "Sub test 4")), $separator.clone()[0], h('li.dropdown-submenu', [ - h('a.cp-app-drive-context-test.dropdown-item', { + h('a.cp-app-drive-context-test3.dropdown-item', { 'tabindex': '-1', 'data-icon': faFolderOpen, }, "TEST"), @@ -477,17 +477,34 @@ define([ var $el = $(el); var $a = $el.children().filter("a"); var $sub = $el.find(".dropdown-menu").first(); + var showSubmenu = function () { + $sub.toggleClass("left", $el.offset().left + $el.outerWidth() + $sub.outerWidth() > $(window).width()); + $sub.show(); + }; + var hideSubmenu = function () { + $sub.hide(); + $sub.removeClass("left"); + }; // Add submenu expand icon $a.append(h("span.dropdown-toggle")); // Show / hide submenu $el.hover(function () { - setTimeout(function () { // wait for dom to update - $sub.toggleClass("left", $el.offset().left + $el.outerWidth() + $sub.outerWidth() > $(window).width()); - $sub.show(); - }); + showSubmenu(); }, function () { - $sub.hide(); - $sub.removeClass("left"); + hideSubmenu(); + }); + $el.click(function (e) { + e.stopPropagation(); + if ($el.children().filter(".dropdown-menu:visible").length !== 0) { + console.log("leave", $a[0]); + $el.find(".dropdown-menu").hide(); + hideSubmenu(); + } + else { + console.log("enter", $a[0]); + $el.siblings().find(".dropdown-menu").hide(); + showSubmenu(); + } }); }); return $(menu); @@ -1137,7 +1154,7 @@ define([ show = ['newfolder', 'newsharedfolder', 'newdoc']; break; case 'tree': - show = ['open', 'openro', 'expandall', 'collapseall', 'color', 'download', 'share', 'rename', 'delete', 'deleteowned', 'removesf', 'properties', 'hashtag', 'subtest1', 'subtest2', 'subtest3']; + show = ['open', 'openro', 'expandall', 'collapseall', 'color', 'download', 'share', 'rename', 'delete', 'deleteowned', 'removesf', 'properties', 'hashtag', 'subtest1', 'subtest2', 'subtest3', 'subtest4', 'subtest5', 'subtest6']; break; case 'default': show = ['open', 'openro', 'share', 'openparent', 'delete', 'deleteowned', 'properties', 'hashtag']; @@ -1335,7 +1352,7 @@ define([ } }); if (!showSep && $lastVisibleSep) { $lastVisibleSep.css("display", "none"); } // remove last divider if no options after - } + }; var displayMenu = function (e) { var $menu = $contextMenu; $menu.find(".dropdown-menu").each(function (i, menu) { @@ -1454,7 +1471,7 @@ define([ }); cb(); }; - if (paths.some(function (p) { return manager.comparePath(newPath, p) })) { return void cb(); } + if (paths.some(function (p) { return manager.comparePath(newPath, p); })) { return void cb(); } manager.move(paths, newPath, newCb, copy); }; // Delete paths from the drive and/or shared folders (without moving them to the trash) @@ -3423,6 +3440,7 @@ define([ }; APP.hideMenu = function (e) { + console.error("HIDEMENU", e ? e.target : "e is undefined"); $contextMenu.hide(); $trashTreeContextMenu.hide(); $trashContextMenu.hide(); @@ -3525,7 +3543,7 @@ define([ if (paths.length !== 1) { return; } 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); pickFolderColor(paths[0].element, currentColor, function (color) { paths.forEach(function (p) { @@ -3770,6 +3788,7 @@ define([ $appContainer.on('mouseup', function (e) { //if (sel.down) { 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); //removeSelected(e); }); From 6ae5da3d10bae67abd2001670cff880752eaca27 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 24 Jul 2019 14:41:37 +0200 Subject: [PATCH 03/60] Move stripTags function to Util --- www/common/common-util.js | 6 ++++++ www/common/diffMarked.js | 8 +------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/www/common/common-util.js b/www/common/common-util.js index 9fd2305c3..03c9e321b 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -319,6 +319,12 @@ define([], function () { return window.innerHeight < 800 || window.innerWidth < 800; }; + Util.stripTags = function (text) { + var div = document.createElement("div"); + div.innerHTML = text; + return div.innerText; + }; + return Util; }); }(self)); diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index 35633c4cd..44a2f56a4 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -84,12 +84,6 @@ define([ } }; - var stripTags = function (text) { - var div = document.createElement("div"); - div.innerHTML = text; - return div.innerText; - }; - renderer.heading = function (text, level) { var i = 0; var safeText = text.toLowerCase().replace(/[^\w]+/g, '-'); @@ -105,7 +99,7 @@ define([ toc.push({ level: level, id: id, - title: stripTags(text) + title: Util.stripTags(text) }); return "" + text + ""; }; From e1690c6f1a98ba9bcb489f9681a73690df6b4f3c Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 24 Jul 2019 15:27:10 +0200 Subject: [PATCH 04/60] Escape titles inside links for Code and Slide titles --- www/common/sframe-common-codemirror.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js index 8a0e2de63..eb2298e3d 100644 --- a/www/common/sframe-common-codemirror.js +++ b/www/common/sframe-common-codemirror.js @@ -98,7 +98,15 @@ define([ // lines beginning with a hash are potentially valuable // works for markdown, python, bash, etc. var hash = /^#+(.*?)$/; + var hashAndLink = /^#+\s*\[(.*?)\]\(.*\)\s*$/; 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 = one; + }); + return true; + } line.replace(hash, function (a, one) { text = one; }); From 5cbfad3433ee0260df78e7267056eb7954740d40 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Thu, 25 Jul 2019 11:48:16 +0200 Subject: [PATCH 05/60] Fix markdown unwanted renders in chats --- www/common/diffMarked.js | 27 +++++++++++++++++++++++++-- www/common/messenger-ui.js | 4 ++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index 35633c4cd..a3ae55e06 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -15,6 +15,7 @@ define([ var DiffDOM = window.diffDOM; var renderer = new Marked.Renderer(); + var restrictedRenderer = new Marked.Renderer(); var Mermaid = { init: function () {} @@ -61,13 +62,20 @@ define([ return h('div.cp-md-toc', content).outerHTML; }; - DiffMd.render = function (md, sanitize) { + DiffMd.render = function (md, sanitize, restrictedMd) { + console.error("DIFFMD RENDER", restrictedMd); + console.log("md:\n", md); + Marked.setOptions({ + renderer: restrictedMd ? restrictedRenderer : renderer, + }); var r = Marked(md, { sanitize: sanitize }); // Add Table of Content - r = r.replace(/
<\/div>/g, getTOC()); + if (!restrictedMd) { + r = r.replace(/
<\/div>/g, getTOC()); + } toc = []; return r; @@ -83,6 +91,7 @@ define([ return defaultCode.apply(renderer, arguments); } }; + restrictedRenderer.code = renderer.code; var stripTags = function (text) { var div = document.createElement("div"); @@ -109,6 +118,9 @@ define([ }); return "" + text + ""; }; + restrictedRenderer.heading = function (text) { + return text; + }; // Tasks list var checkedTaskItemPtn = /^\s*(

)?\[[xX]\](<\/p>)?\s*/; @@ -138,6 +150,13 @@ define([ var cls = (isCheckedTaskItem || isUncheckedTaskItem || hasBogusInput) ? ' class="todo-list-item"' : ''; return '' + text + '\n'; }; + restrictedRenderer.listitem = function (text) { + if (bogusCheckPtn.test(text)) { + text = text.replace(bogusCheckPtn, ''); + } + return '

  • ' + text + '
  • \n'; + } + renderer.image = function (href, title, text) { if (href.slice(0,6) === '/file/') { // DEPRECATED @@ -162,6 +181,7 @@ define([ out += this.options.xhtml ? '/>' : '>'; return out; }; + restrictedRenderer.image = renderer.image; renderer.paragraph = function (p) { if (p === '[TOC]') { @@ -169,6 +189,9 @@ define([ } return //i.test(p)? p + '\n': '

    ' + p + '

    \n'; }; + restrictedRenderer.paragraph = function (p) { + return //i.test(p)? p + '\n': '

    ' + p + '

    \n'; + }; var MutationObserver = window.MutationObserver; var forbiddenTags = [ diff --git a/www/common/messenger-ui.js b/www/common/messenger-ui.js index 682535d98..223f67bb3 100644 --- a/www/common/messenger-ui.js +++ b/www/common/messenger-ui.js @@ -151,7 +151,7 @@ define([ }); try { 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"); // override link clicking, because we're in an iframe @@ -197,7 +197,7 @@ define([ var getChat = function (id) { return $messages.find(dataQuery(id)); }; - + var scrollChatToBottom = function () { var $messagebox = $('.cp-app-contacts-messages'); $messagebox.scrollTop($messagebox[0].scrollHeight); From a204740ac2ebfcfab2cccd5bd1d65f4bf114eb77 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Thu, 25 Jul 2019 11:50:19 +0200 Subject: [PATCH 06/60] Fix one annoying missing semicolon --- www/common/diffMarked.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index a3ae55e06..5c4942dbc 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -155,7 +155,7 @@ define([ text = text.replace(bogusCheckPtn, ''); } return '
  • ' + text + '
  • \n'; - } + }; renderer.image = function (href, title, text) { if (href.slice(0,6) === '/file/') { From f3bb56925b57bcaeda50c594077552cab9423599 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Thu, 25 Jul 2019 14:13:14 +0200 Subject: [PATCH 07/60] Change browser color picker to jscolor picker for cursor color --- www/settings/app-settings.less | 10 ++++++++-- www/settings/inner.js | 29 +++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/www/settings/app-settings.less b/www/settings/app-settings.less index 0c21bfed0..185b22741 100644 --- a/www/settings/app-settings.less +++ b/www/settings/app-settings.less @@ -111,8 +111,14 @@ vertical-align: middle; margin-right: 5px; } - input[type="color"] { - width: 100px; + .cp-settings-cursor-color-picker { + display: inline-block; + vertical-align: middle; + height: 25px; + width: 70px; + margin-right: 10px; + cursor: pointer; + border: 1px solid black; } .cp-settings-language-selector { button.btn { diff --git a/www/settings/inner.js b/www/settings/inner.js index d785391d0..73b48f871 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -15,6 +15,7 @@ define([ '/settings/make-backup.js', '/common/common-feedback.js', + '/common/jscolor.js', '/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', @@ -1191,13 +1192,13 @@ define([ var $inputBlock = $('
    ').appendTo($div); + var $colorPicker = $("
    ", { class: "cp-settings-cursor-color-picker"}); var $ok = $('', {'class': 'fa fa-check', title: Messages.saved}); var $spinner = $('', {'class': 'fa fa-spinner fa-pulse'}); - var $input = $('', { - type: 'color', - }).on('change', function () { - var val = $input.val(); + // when jscolor picker value change + var onchange = function (colorL) { + var val = "#" + colorL.toString(); if (!/^#[0-9a-fA-F]{6}$/.test(val)) { return; } $spinner.show(); $ok.hide(); @@ -1205,15 +1206,27 @@ define([ $spinner.hide(); $ok.show(); }); - }).appendTo($inputBlock); + $colorPicker.css('bakcground-color', val); + }; - $ok.hide().appendTo($inputBlock); - $spinner.hide().appendTo($inputBlock); + // jscolor picker + 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) { if (e) { return void console.error(e); } - $input.val(val || ''); + val = val || "#000"; + $colorPicker.css('bakcground-color', val); + jscolorL.fromString(val); }); + + $colorPicker.appendTo($inputBlock); + $ok.hide().appendTo($inputBlock); + $spinner.hide().appendTo($inputBlock); + return $div; }; From aceff466b99fe4f56423babffa09294aa6ab082a Mon Sep 17 00:00:00 2001 From: ClemDee Date: Thu, 25 Jul 2019 14:15:44 +0200 Subject: [PATCH 08/60] Remove useless jscolor picker import --- www/drive/inner.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index fd7723bca..b5e851891 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -17,8 +17,6 @@ define([ '/bower_components/chainpad-listmap/chainpad-listmap.js', '/customize/messages.js', - '/common/jscolor.js', - 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/drive/app-drive.less', From 83f6a29f15ca34be0c64f7729f961e5b36ff2773 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Thu, 25 Jul 2019 14:50:46 +0200 Subject: [PATCH 09/60] Disallow to click more than once on the button to store a shared pad in drive --- www/common/common-ui-elements.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 207d00336..4f00ce589 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2766,7 +2766,7 @@ define([ UIElements.displayCrowdfunding(common); modal.delete(); }); - $(store).click(function () { + $(store).one("click", function () { common.getSframeChannel().query("Q_AUTOSTORE_STORE", null, function (err, obj) { var error = err || (obj && obj.error); if (error) { From 666fbe098d00dc6583b6639115dca93bec1ee78d Mon Sep 17 00:00:00 2001 From: ClemDee Date: Thu, 25 Jul 2019 15:55:20 +0200 Subject: [PATCH 10/60] Change loading spinner to custom spinner --- customize.dist/loading.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index e8f8a9453..765ec56f9 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -169,6 +169,28 @@ define([], function () { height: 100%; 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); var urlArgs = window.location.href.replace(/^.*\?([^\?]*)$/, function (all, x) { return x; }); var elem = document.createElement('div'); @@ -182,7 +204,7 @@ define([], function () { '
    ', '
    ', '
    ', - '', + '', '
    ', '

    ', '
    ' From d405b8541b33c7f055b312efd1e6dc1e7389a4bf Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 26 Jul 2019 14:35:02 +0200 Subject: [PATCH 11/60] Fix horizontal scrollbar in drive --- www/drive/app-drive.less | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/www/drive/app-drive.less b/www/drive/app-drive.less index c85cd3c88..70b03ae92 100644 --- a/www/drive/app-drive.less +++ b/www/drive/app-drive.less @@ -107,7 +107,7 @@ .cp-app-drive-container { flex: 1; - overflow: auto; + overflow-x: auto; width: 100%; display: flex; flex-flow: row; @@ -121,6 +121,7 @@ #cp-app-drive-tree { resize: none; width: 100% !important; + min-width: unset; max-width: unset; max-height: unset; border-bottom: 1px solid @drive_mobile-tree-border-col; @@ -239,7 +240,6 @@ max-height: 100%; .cp-app-drive-tree-categories-container { flex: 1; - max-width: 500px; overflow: auto; } img.cp-app-drive-icon { @@ -438,13 +438,13 @@ flex: 1; // Needed to avoid the folder's path to overflows // https://stackoverflow.com/questions/38223879/white-space-nowrap-breaks-flexbox-layout - min-width: 0; + // min-width: 0; } #cp-app-drive-content { box-sizing: border-box; background: @drive_content-bg; color: @drive_content-fg; - overflow: auto; + overflow-y: auto; flex: 1; display: flex; flex-flow: column; From 1f9635a9dea06cb26096770590427876553c112f Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 31 Jul 2019 17:33:43 +0200 Subject: [PATCH 12/60] Keep selected elements when refresh drive --- www/drive/inner.js | 149 ++++++++++++++++++++++++++++++--------------- 1 file changed, 99 insertions(+), 50 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index f1350414b..6ff116d0b 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -299,6 +299,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 menu = h('div.cp-contextmenu.dropdown.cp-unselectable', [ h('ul.dropdown-menu', { @@ -564,7 +591,8 @@ define([ var sel = {}; 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'); if (!$container.length) { return; } $container.html(''); @@ -674,7 +702,9 @@ define([ delete sel.move; $content.find('.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(); }); @@ -709,7 +739,9 @@ define([ // Ctrl+A select all if (e.which === 65 && (e.ctrlKey || (e.metaKey && APP.isMac))) { $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; } @@ -722,7 +754,7 @@ define([ 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]); } var lastIndex = typeof sel.endSelected === "number" ? sel.endSelected : @@ -841,12 +873,12 @@ define([ return; } removeInput(); - removeSelected(); var $name = $element.find('.cp-app-drive-element-name'); if (!$name.length) { $name = $element.find('> .cp-app-drive-element'); } $name.hide(); + var isFolder = $element.is(".cp-app-drive-element-folder:not(.cp-app-drive-element-sharedf)"); var el = manager.find(path); var name = manager.isFile(el) ? manager.getTitle(el) : path[path.length - 1]; if (manager.isSharedFolder(el)) { @@ -868,14 +900,21 @@ define([ var newName = $input.val(); if (JSON.stringify(path) === JSON.stringify(currentPath)) { manager.rename(path, $input.val(), function () { - renameFoldersOpened(path, newName); - path[path.length - 1] = newName; + if (isFolder) { + renameFoldersOpened(path, newName); + path[path.length - 1] = newName; + } APP.displayDirectory(path); }); } else { 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(); }); } @@ -896,7 +935,6 @@ define([ // We don't want to open the file/folder when clicking on the input $input.on('click dblclick', function (e) { - removeSelected(); e.stopPropagation(); }); // Remove the browser ability to drag text from the input to avoid @@ -1116,8 +1154,9 @@ define([ var getSelectedPaths = function ($element) { var paths = []; - if ($('.cp-app-drive-element-selected').length > 1) { - var $selected = $('.cp-app-drive-element-selected'); + if ($element.length === 0) { return paths; } + if (findSelectedElements().length > 1) { + var $selected = findSelectedElements(); $selected.each(function (idx, elmt) { var ePath = $(elmt).data('path'); if (ePath) { @@ -1146,7 +1185,7 @@ define([ } else { $driveToolbar.find('cp-app-drive-toolbar-emptytrash').hide(); } - var $li = $content.find('.cp-app-drive-element-selected'); + var $li = findSelectedElements(); if ($li.length === 0) { $li = findDataHolder($tree.find('.cp-app-drive-element-active')); } @@ -1213,6 +1252,7 @@ define([ if (pos+eh <= h && pos >= 0) { return; } $content.scrollTop(v); }; + // Add the "selected" class to the "li" corresponding to the clicked element var onElementClick = APP.onElementClick = function (e, $element) { // If "Ctrl" is pressed, do not remove the current selection @@ -1249,23 +1289,23 @@ define([ var $el; removeSelected(true); sel.oldSelection.forEach(function (el) { - if (!$(el).hasClass("cp-app-drive-element-selected")) { - $(el).addClass("cp-app-drive-element-selected"); + if (!isElementSelected($(el))) { + selectElement($(el)); } }); for (var i = Math.min(sel.startSelected, sel.endSelected); i <= Math.max(sel.startSelected, sel.endSelected); i++) { $el = $($elements.get(i)); - if (!$el.hasClass("cp-app-drive-element-selected")) { - $el.addClass("cp-app-drive-element-selected"); + if (!isElementSelected($el)) { + selectElement($el); } } } else { - if (!$element.hasClass("cp-app-drive-element-selected")) { - $element.addClass("cp-app-drive-element-selected"); + if (!isElementSelected($element)) { + selectElement($element); } else { - $element.removeClass("cp-app-drive-element-selected"); + unselectElement($element); } } updateContextButton(); @@ -1352,6 +1392,17 @@ define([ } else { 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 (!$element.length) { @@ -1360,8 +1411,8 @@ define([ return false; } - if (!$element.hasClass('cp-app-drive-element-selected')) { - onElementClick(undefined, $element); + if (!isElementSelected($element)) { + selectElement($element); } paths = getSelectedPaths($element); @@ -1398,6 +1449,7 @@ define([ var newCb = function () { paths.forEach(function (path) { moveFoldersOpened(path, newPath); + // removeSelected(); }); cb(); }; @@ -1431,6 +1483,7 @@ define([ if (!res) { return; } manager.delete(pathsList, function () { pathsList.forEach(removeFoldersOpened); + removeSelected(); refresh(); }); }, null, true); @@ -1441,7 +1494,7 @@ define([ var paths = []; var $element = findDataHolder($(ev.target)); if ($element.hasClass('cp-app-drive-element-selected')) { - var $selected = $('.cp-app-drive-element-selected'); + var $selected = findSelectedElements(); $selected.each(function (idx, elmt) { var ePath = $(elmt).data('path'); if (ePath) { @@ -1458,7 +1511,7 @@ define([ }); } else { removeSelected(); - $element.addClass('cp-app-drive-element-selected'); + selectElement($element); var val = manager.find(path); if (!val) { return; } // The element is not in the object paths = [{ @@ -1738,12 +1791,9 @@ define([ draggable: true, 'class': 'cp-app-drive-element-row' }); - if (!isFolder && Array.isArray(APP.selectedFiles)) { - var idx = APP.selectedFiles.indexOf(element); - if (idx !== -1) { - $element.addClass('cp-app-drive-element-selected'); - APP.selectedFiles.splice(idx, 1); - } + $element.data('path', newPath); + if (isElementSelected($element)) { + selectElement($element); } $element.prepend($icon).dblclick(function () { if (isFolder) { @@ -1759,11 +1809,10 @@ define([ addFileData(element, $element); } $element.addClass(liClass); - $element.data('path', newPath); addDragAndDropHandlers($element, newPath, isFolder, !isTrash); $element.click(function(e) { e.stopPropagation(); - onElementClick(e, $element, newPath); + onElementClick(e, $element); }); if (!isTrash) { $element.contextmenu(openContextMenu('tree')); @@ -2551,22 +2600,19 @@ define([ 'class': 'cp-app-drive-element cp-app-drive-element-file cp-app-drive-element-row' + roClass, draggable: draggable }); - if (Array.isArray(APP.selectedFiles)) { - var sidx = APP.selectedFiles.indexOf(id); - if (sidx !== -1) { - $element.addClass('cp-app-drive-element-selected'); - APP.selectedFiles.splice(sidx, 1); - } + + var path = [rootName, idx]; + $element.data('path', path); + if (isElementSelected($element)) { + selectElement($element); } $element.prepend($icon).dblclick(function () { openFile(id); }); addFileData(id, $element); - var path = [rootName, idx]; - $element.data('path', path); $element.click(function(e) { e.stopPropagation(); - onElementClick(e, $element, path); + onElementClick(e, $element); }); $element.contextmenu(openContextMenu('default')); $element.data('context', 'default'); @@ -2693,8 +2739,8 @@ define([ e.preventDefault(); if (manager.isInTrashRoot(parentPath)) { parentPath = [TRASH]; } else { parentPath.pop(); } - APP.selectedFiles = [r.id]; APP.displayDirectory(parentPath); + APP.selectedFiles = path.slice(-1); }).appendTo($openDir); } $('').text(Messages.fc_prop).click(function () { @@ -2785,7 +2831,7 @@ define([ $element.data('path', path); $element.click(function(e) { e.stopPropagation(); - onElementClick(e, $element, path); + onElementClick(e, $element); }); $element.contextmenu(openContextMenu('default')); $element.data('context', 'default'); @@ -3009,7 +3055,7 @@ define([ $context.click(function (e) { e.preventDefault(); e.stopPropagation(); - var $li = $content.find('.cp-app-drive-element-selected'); + var $li = findSelectedElements(); if ($li.length !== 1) { $li = findDataHolder($tree.find('.cp-app-drive-element-active')); } @@ -3091,7 +3137,7 @@ define([ } });*/ - var $sel = $content.find('.cp-app-drive-element-selected'); + var $sel = findSelectedElements(); if ($sel.length) { $sel[0].scrollIntoView(); } else { @@ -3103,6 +3149,9 @@ define([ if (history.isHistoryMode) { return void _displayDirectory(path, force); } + if (!manager.comparePath(currentPath, path)) { + removeSelected(); + } updateObject(sframeChan, proxy, function () { copyObjectValue(files, proxy.drive); updateSharedFolders(sframeChan, manager, files, folders, function () { @@ -3451,6 +3500,7 @@ define([ if (!res) { return; } manager.delete(pathsList, function () { pathsList.forEach(removeFoldersOpened); + removeSelected(); refresh(); }); }); @@ -3694,16 +3744,15 @@ define([ var parentPath = paths[0].path.slice(); if (manager.isInTrashRoot(parentPath)) { parentPath = [TRASH]; } else { parentPath.pop(); } - el = manager.find(paths[0].path); - APP.selectedFiles = [el]; APP.displayDirectory(parentPath); + APP.selectedFiles = paths[0].path.slice(-1); } APP.hideMenu(); }); - $content.on("keydown", function (e) { - if (e.which === 113) { - var paths = $contextMenu.data('paths'); + $(window).on("keydown", function (e) { + if (e.which === 113) { // if F2 key pressed + var paths = getSelectedPaths(findSelectedElements().first()); if (paths.length !== 1) { return; } displayRenameInput(paths[0].element, paths[0].path); } @@ -3737,7 +3786,7 @@ define([ if (manager.isPathIn(currentPath, [FILES_DATA]) && APP.loggedIn) { return; // We can't remove elements directly from filesData } - var $selected = $('.cp-app-drive-element-selected'); + var $selected = findSelectedElements(); if (!$selected.length) { return; } var paths = []; var isTrash = manager.isPathIn(currentPath, [TRASH]); From 1395c1a8ac2d6e0dce98b0f6ed7868c639caf191 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Thu, 1 Aug 2019 11:38:47 +0200 Subject: [PATCH 13/60] Fix image flicker when the drive is refreshed --- www/common/common-thumbnail.js | 13 ++++++++++- www/drive/inner.js | 42 ++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js index 2a03715cd..4dfab1457 100644 --- a/www/common/common-thumbnail.js +++ b/www/common/common-thumbnail.js @@ -230,9 +230,20 @@ define([ if (!Visible.currently()) { to = window.setTimeout(interval, Thumb.UPDATE_FIRST); } }; + var stringToBlobToUrl = function (b64) { + var byteString = atob(b64.split(',')[1]); + var ab = new ArrayBuffer(byteString.length); + var ia = new Uint8Array(ab); + for (var i = 0; i < byteString.length; i++) { + ia[i] = byteString.charCodeAt(i); + } + var blob = new Blob([ab], {type: "image/png"}); + var url = URL.createObjectURL(blob); + return url; + }; var addThumbnail = function (err, thumb, $span, cb) { var img = new Image(); - img.src = thumb.slice(0,5) === 'data:' ? thumb : 'data:image/png;base64,'+thumb; + img.src = stringToBlobToUrl(thumb); $span.find('.cp-icon').hide(); $span.prepend(img); cb($(img)); diff --git a/www/drive/inner.js b/www/drive/inner.js index 6ff116d0b..ae7dc60df 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -1658,7 +1658,8 @@ define([ $owner.attr('title', Messages.fm_padIsOwnedOther); } }; - var addFileData = function (element, $span) { + var thumbsUrls = {}; + var addFileData = function (element, $element) { if (!manager.isFile(element)) { return; } var data = manager.getFileData(element); @@ -1667,7 +1668,7 @@ define([ var hrefData = Hash.parsePadUrl(href); if (hrefData.type) { - $span.addClass('cp-border-color-'+hrefData.type); + $element.addClass('cp-border-color-'+hrefData.type); } var $state = $('', {'class': 'cp-app-drive-element-state'}); @@ -1687,25 +1688,36 @@ define([ var $expire = $expirableIcon.clone().appendTo($state); $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); // The element with the class '.name' is underlined when the 'li' is hovered var $name = $('', {'class': 'cp-app-drive-element-name'}).text(name); - $span.append($name); - $span.append($state); - $span.attr('title', name); + $element.append($name); + $element.append($state); + $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'); + } + 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'); + thumbsUrls[element] = $thumb[0].src; + }); + } 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 = $('', { 'class': 'cp-app-drive-element-type cp-app-drive-element-list' }).text(type); @@ -1715,7 +1727,7 @@ define([ var $cdate = $('', { 'class': 'cp-app-drive-element-ctime cp-app-drive-element-list' }).text(getDate(data.ctime)); - $span.append($type).append($adate).append($cdate); + $element.append($type).append($adate).append($cdate); }; var addFolderData = function (element, key, $span) { From aae71c43d2972dbac1833a1339e1f9470aafdcf3 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Thu, 1 Aug 2019 14:18:17 +0200 Subject: [PATCH 14/60] Add possibility to drop files on the path bar to move them in drive --- www/drive/app-drive.less | 11 ++++++++--- www/drive/inner.js | 10 +++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/www/drive/app-drive.less b/www/drive/app-drive.less index c85cd3c88..f4415e7f6 100644 --- a/www/drive/app-drive.less +++ b/www/drive/app-drive.less @@ -30,6 +30,7 @@ @drive_content-bg-ro: darken(@drive_content-bg, 10%); @drive_selected-bg: #888; + @drive_droppable-bg: #FE9A2E; /* PAGE */ @@ -156,7 +157,7 @@ } .cp-app-drive-element-droppable { - background-color: #FE9A2E; + background-color: @drive_droppable-bg; color: #222; } @@ -939,6 +940,7 @@ overflow: hidden; text-overflow: ellipsis; transition: all 0.15s; + cursor: pointer; &:first-child { flex-shrink: 1; @@ -946,17 +948,20 @@ &.cp-app-drive-path-separator { color: #ccc; + cursor: default; } &.cp-app-drive-path-collapse { 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) { background-color: darken(@colortheme_drive-bg, 15%); text-decoration: underline; - cursor: pointer; } & ~ .cp-app-drive-path-element { background-color: darken(@colortheme_drive-bg, 15%); diff --git a/www/drive/inner.js b/www/drive/inner.js index ae7dc60df..8c8b5a4bf 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -1530,7 +1530,13 @@ define([ var findDropPath = function (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 dropEl = newPath && manager.find(newPath); if (newPath && manager.isSharedFolder(dropEl)) { @@ -1975,6 +1981,8 @@ define([ } else if (idx > 0 && manager.isFile(el)) { 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); } else { From 08c006e68236dc386da13962eca2922aaac3b96b Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 2 Aug 2019 12:21:37 +0200 Subject: [PATCH 15/60] Use Nacl to decode base64 thumbnail data --- www/common/common-thumbnail.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js index 4dfab1457..62771223a 100644 --- a/www/common/common-thumbnail.js +++ b/www/common/common-thumbnail.js @@ -230,20 +230,15 @@ define([ if (!Visible.currently()) { to = window.setTimeout(interval, Thumb.UPDATE_FIRST); } }; - var stringToBlobToUrl = function (b64) { - var byteString = atob(b64.split(',')[1]); - var ab = new ArrayBuffer(byteString.length); - var ia = new Uint8Array(ab); - for (var i = 0; i < byteString.length; i++) { - ia[i] = byteString.charCodeAt(i); - } - var blob = new Blob([ab], {type: "image/png"}); - var url = URL.createObjectURL(blob); - return url; - }; + 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(); - img.src = stringToBlobToUrl(thumb); + img.src = url; $span.find('.cp-icon').hide(); $span.prepend(img); cb($(img)); From 925b147e6cb1b7c15800ad6ec1fca2e02672a0fe Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 2 Aug 2019 15:02:00 +0200 Subject: [PATCH 16/60] Fix contextmenu disappear on narrow layout --- www/drive/inner.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index 8c8b5a4bf..a6f669d2e 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -43,7 +43,7 @@ define([ { var APP = window.APP = { editable: false, - mobile: function () { return $('body').width() <= 600; }, // Menu and content area are not inline-block anymore for mobiles + mobile: $('body').width() <= 600, // Menu and content area are not inline-block anymore for mobiles isMac: navigator.platform === "MacIntel", }; @@ -1339,7 +1339,14 @@ define([ }); }); $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 w = $menu.outerWidth(); var wH = window.innerHeight; @@ -1945,7 +1952,7 @@ define([ var createTitle = function ($container, path, noStyle) { if (!path || path.length === 0) { return; } var isTrash = manager.isPathIn(path, [TRASH]); - if (APP.mobile() && !noStyle) { // noStyle means title in search result + if (APP.mobile && !noStyle) { // noStyle means title in search result return $container; } var isVirtual = virtualCategories.indexOf(path[0]) !== -1; @@ -3023,7 +3030,7 @@ define([ APP.resetTree(); if (displayedCategories.indexOf(SEARCH) !== -1 && $tree.find('#cp-app-drive-tree-search-input').length) { // in history mode we want to focus the version number input - if (!history.isHistoryMode && !APP.mobile()) { + if (!history.isHistoryMode && !APP.mobile) { var st = $tree.scrollTop() || 0; $tree.find('#cp-app-drive-tree-search-input').focus(); $tree.scrollTop(st); @@ -3066,7 +3073,7 @@ define([ createTitle($toolbar.find('.cp-app-drive-path'), path); - if (APP.mobile()) { + if (APP.mobile) { var $context = $('