From 6ae5da3d10bae67abd2001670cff880752eaca27 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 24 Jul 2019 14:41:37 +0200 Subject: [PATCH 01/36] 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 02/36] 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 03/36] 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 04/36] 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 05/36] 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 06/36] 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 07/36] 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 08/36] 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 09/36] 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 3de90213976a649aeb9b1e5539104c53252ece23 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Mon, 29 Jul 2019 14:30:42 +0200 Subject: [PATCH 10/36] Add missing file extensions for code donwload --- www/common/modes.js | 196 ++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/www/common/modes.js b/www/common/modes.js index ffed08b17..668a00e1a 100644 --- a/www/common/modes.js +++ b/www/common/modes.js @@ -6,127 +6,127 @@ define([ // mode language (extension) var list = Modes.list = [ "APL apl .apl", - "ASCII-Armor asciiarmor", - "ASN.1 asn.1", + "ASCII-Armor asciiarmor .asc", + "ASN.1 asn.1 .asn1", "Asterisk asterisk", "Brainfuck brainfuck .b", "C text/x-csrc .c", "C text/x-c++src .cpp", - "C-like clike", - "Clojure clojure", - "CMake cmake", - "COBOL cobol", - "CoffeeScript coffeescript", - "Common_Lisp commonlisp", - "Crystal crystal", + "C-like clike .c", + "Clojure clojure .clj", + "CMake cmake", /* no extension */ + "COBOL cobol .cbl", + "CoffeeScript coffeescript .coffee", + "Common_Lisp commonlisp .lisp", + "Crystal crystal .cr", "CSS css .css", - "Cypher cypher", - "D d", - "Dart dart", - "Diff diff", - "Django django", - "Dockerfile dockerfile", - "DTD dtd", - "Dylan dylan", - "EBNF ebnf", - "ECL ecl", - "Eiffel eiffel", + "Cypher cypher .cypher", + "D d .d", + "Dart dart .dart", + "Diff diff .diff", + "Django django .py", + "Dockerfile dockerfile", /* no extension */ + "DTD dtd .dtd", + "Dylan dylan .dylan", + "EBNF ebnf .ebnf", + "ECL ecl .ecl", + "Eiffel eiffel .e", "Elm elm .elm", - "Erlang erlang", - "Factor factor", - "FCL fcl", - "Forth forth", - "Fortran fortran", - "GAS gas", - "Gherkin gherkin", - "Go go", - "Groovy groovy", - "Haml haml", - "Handlebars handlebars", + "Erlang erlang .erl", + "Factor factor .factor", + "FCL fcl .fcl", + "Forth forth .fs", + "Fortran fortran .f90", + "GAS gas .gas", + "Gherkin gherkin .feature", + "Go go .go", + "Groovy groovy .groovy", + "Haml haml .haml", + "Handlebars handlebars .hbs", "Haskell haskell .hs", - "Haskell-Literate haskell-literate", - "Haxe haxe", + "Haskell-Literate haskell-literate .lhs", + "Haxe haxe .hx", "HTML htmlmixed .html", - "HTTP http", - "IDL idl", - "JADE jade", + "HTTP http", /* no extension */ + "IDL idl .idl", + "JADE jade .jade", "Java text/x-java .java", "JavaScript javascript .js", - "Jinja2 jinja2", + "Jinja2 jinja2 .j2", "JSX jsx .jsx", - "Julia julia", - "LiveScript livescript", - "Lua lua", + "Julia julia .jl", + "LiveScript livescript .ls", + "Lua lua .lua", "Markdown gfm .md", //"markdown markdown .md", - "Mathematica mathematica", - "mIRC mirc", - "ML mllike", - "Modelica modelica", - "MscGen mscgen", - "MUMPS mumps", - "Nginx nginx", - "NSIS nsis", - "N-Triples ntriples", + "Mathematica mathematica .nb", + "mIRC mirc .irc", + "ML mllike", /* no extension */ + "Modelica modelica .mo", + "MscGen mscgen .mscgen", + "MUMPS mumps .m", + "Nginx nginx .conf", + "NSIS nsis .nsi", + "N-Triples ntriples .nq", "Objective-C text/x-objectivec .m", - "Octave octave", + "Octave octave .m", "Org-mode orgmode .org", - "Oz oz", - "Pascal pascal", - "PEG.js pegjs", - "Perl perl", - "PHP php", - "Pig pig", - "PowerShell powershell", - "Properties properties", - "Protocol_Buffers protobuf", - "Puppet puppet", + "Oz oz .oz", + "Pascal pascal .pas", + "PEG.js pegjs .pegjs", + "Perl perl .pl", + "PHP php .php", + "Pig pig .pig", + "PowerShell powershell .ps1", + "Properties properties .properties", + "Protocol_Buffers protobuf .proto", + "Puppet puppet .pp", "Python python .py", - "Q q", - "R r", - "RPM rpm", - "RST rst", - "Ruby ruby", - "Rust rust", - "Sass sass", + "Q q .q", + "R r .r", + "RPM rpm .rpm", + "RST rst .rst", + "Ruby ruby .rb", + "Rust rust .rs", + "Sass sass .sass", "Scheme scheme .scm", "Shell shell .sh", - "Sieve sieve", - "Slim slim", - "Smalltalk smalltalk", - "Smarty smarty", - "Solr solr", - "Soy soy", - "SPARQL sparql", - "Spreadsheet spreadsheet", - "SQL sql", - "sTeX stex", - "Stylus stylus", - "Swift swift", - "Tcl tcl", + "Sieve sieve .sieve", + "Slim slim .slim", + "Smalltalk smalltalk", /* no extension */ + "Smarty smarty", /* no extension */ + "Solr solr", /* no extension */ + "Soy soy .soy", + "SPARQL sparql .rq", + "Spreadsheet spreadsheet .xls", + "SQL sql .sql", + "sTeX stex .stex", + "Stylus stylus .styl", + "Swift swift .swift", + "Tcl tcl .tcl", "Text text .txt", - "Textile textile", - "TiddlyWiki tiddlywiki", - "Tiki tiki", - "TOML toml", - "Tornado tornado", - "troff troff", + "Textile textile .textile", + "TiddlyWiki tiddlywiki .tw", + "Tiki tiki", /* no extension */ + "TOML toml .toml", + "Tornado tornado .tornado", + "troff troff .troff", "TTCN ttcn", "TTCN-cfg ttcn-cfg", - "Turtle turtle", - "Twig twig", - "Visual_Basic vb", - "VBScript vbscript", - "Velocity velocity", - "Verilog verilog", - "VHDL vhdl", - "Vue vue", - "XML xml", + "Turtle turtle .ttl", + "Twig twig .twig", + "Visual_Basic vb .vb", + "VBScript vbscript .vbs", + "Velocity velocity .vm", + "Verilog verilog .v", + "VHDL vhdl .vhdl", + "Vue vue .vue", + "XML xml .xml", //"xwiki xwiki21", - "XQuery xquery", + "XQuery xquery .xquery", "YAML yaml .yaml", - "YAML_Frontmatter yaml-frontmatter", - "Z80 z80" + "YAML_Frontmatter yaml-frontmatter", /* no extension */ + "Z80 z80 .z80" ].map(function (line) { var kv = line.split(/\s/); return { From 3e918ec1cf76adff741109b9c5ee4d8add4625f1 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Mon, 29 Jul 2019 16:30:48 +0200 Subject: [PATCH 11/36] Fix issues of no file extension for pad export --- www/code/export.js | 2 +- www/common/modes.js | 24 ++++++++++++------------ www/common/sframe-app-framework.js | 4 ++-- www/common/sframe-common-codemirror.js | 3 ++- www/kanban/export.js | 4 +++- www/kanban/inner.js | 2 +- www/pad/export.js | 2 +- www/pad/inner.js | 2 +- www/poll/export.js | 4 +++- www/settings/make-backup.js | 2 +- www/slide/export.js | 2 +- www/whiteboard/export.js | 2 +- www/whiteboard/inner.js | 2 +- 13 files changed, 30 insertions(+), 25 deletions(-) diff --git a/www/code/export.js b/www/code/export.js index 23d689361..04616a192 100644 --- a/www/code/export.js +++ b/www/code/export.js @@ -8,7 +8,7 @@ define([ module.main = function (userDoc, cb) { var mode = userDoc.highlightMode || 'gfm'; var content = userDoc.content; - module.type = SFCodeMirror.getContentExtension(mode); + module.ext = SFCodeMirror.getContentExtension(mode); cb(SFCodeMirror.fileExporter(content)); }; diff --git a/www/common/modes.js b/www/common/modes.js index 668a00e1a..6d005da30 100644 --- a/www/common/modes.js +++ b/www/common/modes.js @@ -14,7 +14,7 @@ define([ "C text/x-c++src .cpp", "C-like clike .c", "Clojure clojure .clj", - "CMake cmake", /* no extension */ + "CMake cmake _", /* no extension */ "COBOL cobol .cbl", "CoffeeScript coffeescript .coffee", "Common_Lisp commonlisp .lisp", @@ -25,7 +25,7 @@ define([ "Dart dart .dart", "Diff diff .diff", "Django django .py", - "Dockerfile dockerfile", /* no extension */ + "Dockerfile dockerfile _", /* no extension */ "DTD dtd .dtd", "Dylan dylan .dylan", "EBNF ebnf .ebnf", @@ -47,7 +47,7 @@ define([ "Haskell-Literate haskell-literate .lhs", "Haxe haxe .hx", "HTML htmlmixed .html", - "HTTP http", /* no extension */ + "HTTP http _", /* no extension */ "IDL idl .idl", "JADE jade .jade", "Java text/x-java .java", @@ -61,7 +61,7 @@ define([ //"markdown markdown .md", "Mathematica mathematica .nb", "mIRC mirc .irc", - "ML mllike", /* no extension */ + "ML mllike _", /* no extension */ "Modelica modelica .mo", "MscGen mscgen .mscgen", "MUMPS mumps .m", @@ -93,9 +93,9 @@ define([ "Shell shell .sh", "Sieve sieve .sieve", "Slim slim .slim", - "Smalltalk smalltalk", /* no extension */ - "Smarty smarty", /* no extension */ - "Solr solr", /* no extension */ + "Smalltalk smalltalk _", /* no extension */ + "Smarty smarty _", /* no extension */ + "Solr solr _", /* no extension */ "Soy soy .soy", "SPARQL sparql .rq", "Spreadsheet spreadsheet .xls", @@ -107,7 +107,7 @@ define([ "Text text .txt", "Textile textile .textile", "TiddlyWiki tiddlywiki .tw", - "Tiki tiki", /* no extension */ + "Tiki tiki _", /* no extension */ "TOML toml .toml", "Tornado tornado .tornado", "troff troff .troff", @@ -125,22 +125,22 @@ define([ //"xwiki xwiki21", "XQuery xquery .xquery", "YAML yaml .yaml", - "YAML_Frontmatter yaml-frontmatter", /* no extension */ + "YAML_Frontmatter yaml-frontmatter _", /* no extension */ "Z80 z80 .z80" ].map(function (line) { var kv = line.split(/\s/); return { language: kv[0].replace(/_/g, ' '), mode: kv[1], - ext: kv[2], + ext: kv[2] === '_' ? '' : kv[2], }; }); Modes.extensionOf = function (mode) { - var ext = ''; + var ext; list.some(function (o) { if (o.mode !== mode) { return; } - ext = o.ext || ''; + ext = o.ext; return true; }); return ext; diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index af856d1d7..e2a0bd0a2 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -401,7 +401,7 @@ define([ var ext = (typeof(extension) === 'function') ? extension() : extension; var suggestion = title.suggestTitle('cryptpad-document'); UI.prompt(Messages.exportPrompt, - Util.fixFileName(suggestion) + '.' + ext, function (filename) + Util.fixFileName(suggestion) + ext, function (filename) { if (!(typeof(filename) === 'string' && filename)) { return; } if (async) { @@ -454,7 +454,7 @@ define([ return; } if (!mediaTagEmbedder) { console.log('mediaTagEmbedder missing'); return; } - if (data.type !== 'file') { console.log('unhandled embed type ' + data.type); return; } + if (data.type !== 'file') { console.log('unhandled embed type ' + data.type); return; } var privateDat = cpNfInner.metadataMgr.getPrivateData(); var origin = privateDat.fileHost || privateDat.origin; var src = data.src = origin + data.src; diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js index 8a0e2de63..ca39717ad 100644 --- a/www/common/sframe-common-codemirror.js +++ b/www/common/sframe-common-codemirror.js @@ -39,7 +39,8 @@ define([ }; module.getContentExtension = function (mode) { - return (Modes.extensionOf(mode) || '.txt').slice(1); + var ext = Modes.extensionOf(mode); + return ext !== undefined ? ext : '.txt'; }; module.fileExporter = function (content) { return new Blob([ content ], { type: 'text/plain;charset=utf-8' }); diff --git a/www/kanban/export.js b/www/kanban/export.js index 2240031fe..9ee770ac7 100644 --- a/www/kanban/export.js +++ b/www/kanban/export.js @@ -2,7 +2,9 @@ // Pads from the code app will be exported using this format instead of plain text. define([ ], function () { - var module = {}; + var module = { + ext: '.json' + }; module.main = function (userDoc, cb) { var content = userDoc.content; diff --git a/www/kanban/inner.js b/www/kanban/inner.js index 29a4d9c88..609e5ac31 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -367,7 +367,7 @@ define([ }); } - framework.setFileExporter('json', function () { + framework.setFileExporter('.json', function () { return new Blob([JSON.stringify(kanban.getBoardsJSON(), 0, 2)], { type: 'application/json', }); diff --git a/www/pad/export.js b/www/pad/export.js index 263593a54..d1deef146 100644 --- a/www/pad/export.js +++ b/www/pad/export.js @@ -5,7 +5,7 @@ define([ '/bower_components/nthen/index.js', ], function ($, Util, Hyperjson, nThen) { var module = { - type: 'html' + ext: '.html' }; var exportMediaTags = function (inner, cb) { diff --git a/www/pad/inner.js b/www/pad/inner.js index 9b56e56bb..f201b8720 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -786,7 +786,7 @@ define([ }); }, true); - framework.setFileExporter(Exporter.type, function (cb) { + framework.setFileExporter(Exporter.ext, function (cb) { Exporter.main(inner, cb); }, true); diff --git a/www/poll/export.js b/www/poll/export.js index 2b915cced..e080ec9b4 100644 --- a/www/poll/export.js +++ b/www/poll/export.js @@ -3,7 +3,9 @@ define([ '/customize/messages.js', ], function (Messages) { - var module = {}; + var module = { + ext: '.csv' + }; var copyObject = function (obj) { return JSON.parse(JSON.stringify(obj)); diff --git a/www/settings/make-backup.js b/www/settings/make-backup.js index 3e2876598..674eaba68 100644 --- a/www/settings/make-backup.js +++ b/www/settings/make-backup.js @@ -34,7 +34,7 @@ define([ var path = '/' + type + '/export.js'; require([path], function (Exporter) { Exporter.main(json, function (data) { - result.ext = '.' + Exporter.type; + result.ext = Exporter.ext || ''; result.data = data; cb(result); }); diff --git a/www/slide/export.js b/www/slide/export.js index 814950306..4e76f041d 100644 --- a/www/slide/export.js +++ b/www/slide/export.js @@ -4,7 +4,7 @@ define([ '/common/sframe-common-codemirror.js', ], function (SFCodeMirror) { var module = { - type: 'md' + ext: '.md' }; module.main = function (userDoc, cb) { diff --git a/www/whiteboard/export.js b/www/whiteboard/export.js index e9f68e311..d3ea70f37 100644 --- a/www/whiteboard/export.js +++ b/www/whiteboard/export.js @@ -14,7 +14,7 @@ define([ var canvas = new Fabric.Canvas(canvas_node); var content = userDoc.content; canvas.loadFromJSON(content, function () { - module.type = 'svg'; + module.ext = '.svg'; cb(canvas.toSVG()); }); }; diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js index 18a66a635..1c1e5c563 100644 --- a/www/whiteboard/inner.js +++ b/www/whiteboard/inner.js @@ -415,7 +415,7 @@ define([ setEditable(!locked); }); - framework.setFileExporter('png', function (cb) { + framework.setFileExporter('.png', function (cb) { $canvas[0].toBlob(function (blob) { cb(blob); }); From 3bd09d986e3a478fe90ecc0ec23baf1fef798d8a Mon Sep 17 00:00:00 2001 From: ClemDee Date: Mon, 29 Jul 2019 16:36:49 +0200 Subject: [PATCH 12/36] Fix slides extension to .md when exported from /slide --- www/slide/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/slide/inner.js b/www/slide/inner.js index 1be2e5f3b..b5171d94f 100644 --- a/www/slide/inner.js +++ b/www/slide/inner.js @@ -538,7 +538,7 @@ define([ editor.on('change', framework.localChange); - framework.setFileExporter(CodeMirror.getContentExtension, CodeMirror.fileExporter); + framework.setFileExporter(".md", CodeMirror.fileExporter); framework.setFileImporter({}, CodeMirror.fileImporter); framework.start(); From 38b414bbe1ae0f6235badd9579770ea456e3f4e9 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Mon, 29 Jul 2019 16:46:52 +0200 Subject: [PATCH 13/36] Fix folder metadata appearing as folder in downloaded drive zip --- www/settings/make-backup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/settings/make-backup.js b/www/settings/make-backup.js index 674eaba68..b86f1f3b2 100644 --- a/www/settings/make-backup.js +++ b/www/settings/make-backup.js @@ -163,12 +163,12 @@ define([ var existingNames = []; Object.keys(root).forEach(function (k) { var el = root[k]; - if (typeof el === "object") { + if (typeof el === "object" && el.metadata !== true) { // if folder var fName = getUnique(sanitize(k), '', existingNames); existingNames.push(fName.toLowerCase()); return void makeFolder(ctx, el, zip.folder(fName), fd); } - if (ctx.data.sharedFolders[el]) { + if (ctx.data.sharedFolders[el]) { // if shared folder var sfData = ctx.sf[el].metadata; var sfName = getUnique(sanitize(sfData.title || 'Folder'), '', existingNames); existingNames.push(sfName.toLowerCase()); From dee3ae406a728cc031522a74f59a5edbb57eb38d Mon Sep 17 00:00:00 2001 From: ClemDee Date: Tue, 30 Jul 2019 10:11:20 +0200 Subject: [PATCH 14/36] Fix whiteboard export to .png from the app --- www/whiteboard/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js index 1c1e5c563..b0b382314 100644 --- a/www/whiteboard/inner.js +++ b/www/whiteboard/inner.js @@ -419,7 +419,7 @@ define([ $canvas[0].toBlob(function (blob) { cb(blob); }); - }); + }, true); framework.setNormalizer(function (c) { return { From 1f9635a9dea06cb26096770590427876553c112f Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 31 Jul 2019 17:33:43 +0200 Subject: [PATCH 15/36] 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 16/36] 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 17/36] 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 18/36] 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 19/36] 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 = $('