From e9418af88bde83fd6d5d9304f9418babe52f2883 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 20 Dec 2016 18:59:50 +0100 Subject: [PATCH 1/6] Add an info box in the file manager --- customize.dist/translations/messages.fr.js | 4 ++ customize.dist/translations/messages.js | 4 ++ www/file/file.css | 8 ++++ www/file/main.js | 53 ++++++++++++++++++---- 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 91c840b33..66d475e09 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -190,6 +190,10 @@ define(function () { out.fm_unknownFolderError = "Le dossier sélectionné ou le dernier dossier visité n'existe plus. Ouverture du dossier parent..."; out.fm_contextMenuError = "Impossible d'ouvrir le menu contextuel pour cet élément. Si le problème persiste, essayez de rechercher la page."; out.fm_selectError = "Impossible de sélectionner l'élément ciblé. Si le problème persiste, essayez de recharger la page."; + out.fm_info_root = "Créez ici autant de dossiers/sous-dossiers que vous le souhaitez pour trier vos fichiers."; + out.fm_info_unsorted = 'Contains all the files you\'ve visited that are not yet sorted in "My Documents" or moved to the "Trash".'; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName" + out.fm_info_trash = 'Files deleted from the trash are also removed from "All files" and it is impossible to recover them from the file manager.'; // Same here for "All files" and "out.fm_filesDataName" + out.fm_info_allFiles = 'Contains all the files from "My Documents", "Unsorted" and "Trash". You can\'t move or remove files from here.'; // Same here // File - Context menu out.fc_newfolder = "Nouveau dossier"; out.fc_rename = "Renommer"; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 1ebaf7091..bd189b064 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -189,6 +189,10 @@ define(function () { out.fm_unknownFolderError = "The selected or last visited directory no longer exist. Opening the parent folder..."; out.fm_contextMenuError = "Unable to open the context menu for that element. If the problem persist, try to reload the page."; out.fm_selectError = "Unable to select the targetted element. If the problem persist, try to reload the page."; + out.fm_info_root = "Create as many folders/subfolders here as you want to sort your files."; + out.fm_info_unsorted = 'Contains all the files you\'ve visited that are not yet sorted in "My Documents" or moved to the "Trash".'; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName" + out.fm_info_trash = 'Files deleted from the trash are also removed from "All files" and it is impossible to recover them from the file manager.'; // Same here for "All files" and "out.fm_filesDataName" + out.fm_info_allFiles = 'Contains all the files from "My Documents", "Unsorted" and "Trash". You can\'t move or remove files from here.'; // Same here // File - Context menu out.fc_newfolder = "New folder"; out.fc_rename = "Rename"; diff --git a/www/file/file.css b/www/file/file.css index 72caad18e..72947e93c 100644 --- a/www/file/file.css +++ b/www/file/file.css @@ -186,6 +186,14 @@ li { padding-left: 10px; } +#content .info-box { + margin: 0px auto; + padding: 5px; + background: #ddddff; + border: 1px solid #bbb; + border-radius: 5px; +} + .topButtonContainer { border: 1px solid #ccc; float: right; diff --git a/www/file/main.js b/www/file/main.js index 73066590a..38b919d28 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -58,9 +58,9 @@ define([ var getLastOpenedFolder = function () { var path; try { - path = localStorage[LOCALSTORAGE_LAST] ? JSON.parse(localStorage[LOCALSTORAGE_LAST]) : [ROOT]; + path = localStorage[LOCALSTORAGE_LAST] ? JSON.parse(localStorage[LOCALSTORAGE_LAST]) : [UNSORTED]; } catch (e) { - path = [ROOT]; + path = [UNSORTED]; } return path; }; @@ -147,7 +147,9 @@ define([ // TOOLBAR var getLastName = function (cb) { - cb(null, files['cryptpad.username'] || ''); + Cryptpad.getAttribute('username', function (err, userName) { + cb(err, userName || ''); + }); }; var setName = APP.setName = function (newName) { @@ -157,11 +159,16 @@ define([ myUserNameTemp = myUserNameTemp.substr(0, 32); } var myUserName = myUserNameTemp; - files['cryptpad.username'] = myUserName; - APP.userName.lastName = myUserName; - var $button = APP.$userNameButton; - var $span = $('
').append($button.find('span').clone()).html(); - $button.html($span + myUserName); + Cryptpad.setAttribute('username', myUserName, function (err, data) { + if (err) { + logError("Couldn't set username", err); + return; + } + APP.userName.lastName = myUserName; + var $button = APP.$userNameButton; + var $span = $('
').append($button.find('span').clone()).html(); + $button.html($span + myUserName); + }); }; var $userBlock = APP.$bar.find('.' + Toolbar.constants.username); @@ -682,6 +689,33 @@ define([ return $title; }; + var createInfoBox = function (path) { + var $box = $('
', {'class': 'info-box'}); + var msg; + switch (path[0]) { + case 'root': + msg = Messages.fm_info_root; + break; + case 'unsorted': + msg = Messages.fm_info_unsorted; + break; + case 'trash': + msg = Messages.fm_info_trash; + break; + case Cryptpad.storageKey: + msg = Messages.fm_info_allFiles; + break; + default: + msg = undefined; + } + if (!msg} { + $box.hide(); + } else { + $box.text(msg); + } + return $box; + }; + // Create the button allowing the user to switch from list to icons modes var createViewModeButton = function () { var $block = $('
', { @@ -1012,6 +1046,7 @@ define([ setLastOpenedFolder(path); var $title = createTitle(path); + var $info = createInfoBox(path); var $dirContent = $('
', {id: FOLDER_CONTENT_ID}); $dirContent.data('path', path); @@ -1059,7 +1094,7 @@ define([ $element.appendTo($list); }); } - $content.append($title).append($dirContent); + $content.append($title).append($info).append($dirContent); appStatus.ready(true); }; From 403ba539805e3661f6ceb32e5924573b29a75212 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 21 Dec 2016 10:22:48 +0100 Subject: [PATCH 2/6] Fix typo error --- www/file/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/file/main.js b/www/file/main.js index 38b919d28..58af28d5e 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -708,7 +708,7 @@ define([ default: msg = undefined; } - if (!msg} { + if (!msg) { $box.hide(); } else { $box.text(msg); From 046220f239be0124dc27576bb63a1247e2340eaf Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 21 Dec 2016 11:46:49 +0100 Subject: [PATCH 3/6] Add info boxes for the different categories --- customize.dist/translations/messages.fr.js | 8 ++++---- customize.dist/translations/messages.js | 2 +- www/file/file.css | 11 +++++++---- www/file/main.js | 12 +++++++++++- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 66d475e09..3fa0bd5a1 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -190,10 +190,10 @@ define(function () { out.fm_unknownFolderError = "Le dossier sélectionné ou le dernier dossier visité n'existe plus. Ouverture du dossier parent..."; out.fm_contextMenuError = "Impossible d'ouvrir le menu contextuel pour cet élément. Si le problème persiste, essayez de rechercher la page."; out.fm_selectError = "Impossible de sélectionner l'élément ciblé. Si le problème persiste, essayez de recharger la page."; - out.fm_info_root = "Créez ici autant de dossiers/sous-dossiers que vous le souhaitez pour trier vos fichiers."; - out.fm_info_unsorted = 'Contains all the files you\'ve visited that are not yet sorted in "My Documents" or moved to the "Trash".'; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName" - out.fm_info_trash = 'Files deleted from the trash are also removed from "All files" and it is impossible to recover them from the file manager.'; // Same here for "All files" and "out.fm_filesDataName" - out.fm_info_allFiles = 'Contains all the files from "My Documents", "Unsorted" and "Trash". You can\'t move or remove files from here.'; // Same here + out.fm_info_root = "Créez ici autant de dossiers que vous le souhaitez pour trier vos fichiers."; + out.fm_info_unsorted = 'Contient tous les documents que vous avez ouvert et qui ne sont pas triés dans "Mes documents" ou déplacés vers la "Corbeille".'; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName" + out.fm_info_trash = 'Les fichiers supprimés dans la corbeille sont également enlevés de "Tous les fichiers" et il est impossible de les récupérer depuis l\'explorateur de fichiers.'; // Same here for "All files" and "out.fm_filesDataName" + out.fm_info_allFiles = 'Contient tous les fichiers de "Mes documents", "Fichiers non triés" et "Corbeille". Vous ne pouvez pas supprimer ou déplacer des fichiers d\'ici.'; // Same here // File - Context menu out.fc_newfolder = "Nouveau dossier"; out.fc_rename = "Renommer"; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index bd189b064..3bb537252 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -189,7 +189,7 @@ define(function () { out.fm_unknownFolderError = "The selected or last visited directory no longer exist. Opening the parent folder..."; out.fm_contextMenuError = "Unable to open the context menu for that element. If the problem persist, try to reload the page."; out.fm_selectError = "Unable to select the targetted element. If the problem persist, try to reload the page."; - out.fm_info_root = "Create as many folders/subfolders here as you want to sort your files."; + out.fm_info_root = "Create as many nested folders here as you want to sort your files."; out.fm_info_unsorted = 'Contains all the files you\'ve visited that are not yet sorted in "My Documents" or moved to the "Trash".'; // "My Documents" should match with the "out.fm_rootName" key, and "Trash" with "out.fm_trashName" out.fm_info_trash = 'Files deleted from the trash are also removed from "All files" and it is impossible to recover them from the file manager.'; // Same here for "All files" and "out.fm_filesDataName" out.fm_info_allFiles = 'Contains all the files from "My Documents", "Unsorted" and "Trash". You can\'t move or remove files from here.'; // Same here diff --git a/www/file/file.css b/www/file/file.css index 72947e93c..a42f94208 100644 --- a/www/file/file.css +++ b/www/file/file.css @@ -192,6 +192,12 @@ li { background: #ddddff; border: 1px solid #bbb; border-radius: 5px; + margin-bottom: 10px; +} +#content .info-box span { + cursor: pointer; + margin-left: 10px; + float: right; } .topButtonContainer { @@ -257,13 +263,10 @@ li { flex: 1; } -#content .list li.file-header { - margin-top: 20px; -} - #content .list li.header { cursor: default; color: #008; + margin-top: 10px; } #content .list li.header .element span:not(.fa) { border-right: 1px solid #CCC; diff --git a/www/file/main.js b/www/file/main.js index 58af28d5e..1f7c40772 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -213,6 +213,7 @@ define([ var $gridIcon = $('', {"class": "fa fa-th"}); var $sortAscIcon = $('', {"class": "fa fa-angle-up"}); var $sortDescIcon = $('', {"class": "fa fa-angle-down"}); + var $closeIcon = $('', {"class": "fa fa-window-close"}); if (!APP.readOnly) { setEditable(true); @@ -708,10 +709,19 @@ define([ default: msg = undefined; } - if (!msg) { + if (!msg || Cryptpad.getLSAttribute('hide-info-' + path[0]) === '1') { $box.hide(); } else { $box.text(msg); + var $close = $closeIcon.clone().css({ + 'cursor': 'pointer', + 'margin-left': '10px', + title: Messages.fm_closeInfoBox + }).on('click', function () { + $box.hide(); + Cryptpad.setLSAttribute('hide-info-' + path[0], '1'); + }); + $box.prepend($close); } return $box; }; From 87abfff66bae684c1b5ff7de5b8d5f2933df998e Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 21 Dec 2016 18:33:21 +0100 Subject: [PATCH 4/6] Fix Flash Of Unstyled Content (fouc) --- customize.dist/DecorateToolbar.js | 3 +- customize.dist/fsStore.js | 7 +---- customize.dist/main.css | 31 +++++++++++++++++++ customize.dist/src/cryptpad.less | 30 +++++++++++++++++++ customize.dist/src/variables.less | 1 + customize.dist/translations/messages.fr.js | 5 ++++ customize.dist/translations/messages.js | 4 +++ www/code/main.js | 9 ++---- www/common/cryptpad-common.js | 35 +++++++++++++++++----- www/file/fileObject.js | 19 +++++++----- www/file/main.js | 22 ++++++++++---- www/pad/main.js | 9 ++---- www/poll/test/main.js | 17 +++++++---- www/slide/main.js | 8 ++--- 14 files changed, 149 insertions(+), 51 deletions(-) diff --git a/customize.dist/DecorateToolbar.js b/customize.dist/DecorateToolbar.js index 49a30c79f..023c929e1 100644 --- a/customize.dist/DecorateToolbar.js +++ b/customize.dist/DecorateToolbar.js @@ -10,8 +10,7 @@ define([ var main = function () { var url = window.location.pathname; var isHtml = /\.html/.test(url) || url === '/' || url === ''; - var isPoll = /\/poll\//.test(url); - if (!isHtml && !isPoll) { + if (!isHtml) { Messages._applyTranslation(); return; } diff --git a/customize.dist/fsStore.js b/customize.dist/fsStore.js index 7910a5cf3..f600819b1 100644 --- a/customize.dist/fsStore.js +++ b/customize.dist/fsStore.js @@ -146,11 +146,6 @@ define([ if (!Cryptpad.getUserHash()) { localStorage.FS_hash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); } - window.patchText = TextPatcher.create({ - realtime: realtime, - logging: true, - }); - }).on('ready', function () { if (!rt.proxy[Cryptpad.storageKey] || !Cryptpad.isArray(rt.proxy[Cryptpad.storageKey])) { var oldStore = Cryptpad.getStore(true); @@ -171,7 +166,7 @@ define([ } return; } - Cryptpad.alert(Messages.common_connectionLost); + //Cryptpad.alert(Messages.common_connectionLost); }); }; diff --git a/customize.dist/main.css b/customize.dist/main.css index b9c517a63..14cf8bf32 100644 --- a/customize.dist/main.css +++ b/customize.dist/main.css @@ -315,6 +315,37 @@ tr { font-family: lato, Helvetica, sans-serif; font-size: 1.02em; } +#loading { + position: fixed; + z-index: 9999; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; + background: #302B28; + text-align: center; + font-size: 1.5em; +} +#loading .loadingContainer { + margin-top: 50vh; + transform: translateY(-50%); +} +#loading .cryptofist { + margin-left: auto; + margin-right: auto; +} +@media screen and (max-height: 450px) { + #loading .cryptofist { + display: none; + } +} +#loading .spinnerContainer { + position: relative; + height: 100px; +} +#loading .spinnerContainer > div { + height: 100px; +} #main { width: 70vw; margin: auto; diff --git a/customize.dist/src/cryptpad.less b/customize.dist/src/cryptpad.less index 07fc138aa..2e1b44467 100644 --- a/customize.dist/src/cryptpad.less +++ b/customize.dist/src/cryptpad.less @@ -142,6 +142,36 @@ p, pre, td, a, table, tr { .lato; } +#loading { + position: fixed; + z-index: 9999; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; + background: @bg-loading; + text-align: center; + font-size: 1.5em; + .loadingContainer { + margin-top: 50vh; + transform: translateY(-50%); + } + .cryptofist { + margin-left: auto; + margin-right: auto; + @media screen and (max-height: 450px) { + display: none; + } + } + .spinnerContainer { + position: relative; + height: 100px; + > div { + height: 100px; + } + } +} + #main { width: 70vw; margin: auto; diff --git a/customize.dist/src/variables.less b/customize.dist/src/variables.less index c6d555219..f3db72022 100644 --- a/customize.dist/src/variables.less +++ b/customize.dist/src/variables.less @@ -32,3 +32,4 @@ @alertify-input-bg: @base; @alertify-input-fg: @fore; +@bg-loading: @base; diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 3fa0bd5a1..ac72aefc6 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -19,6 +19,10 @@ define(function () { ].join(''); out.common_connectionLost = 'Connexion au serveur perdue'; + out.websocketError = 'Impossible de se connecter au serveur WebSocket...'; + + out.loading = "Chargement..."; + out.error = "Erreur"; out.disconnected = 'Déconnecté'; out.synchronizing = 'Synchronisation'; @@ -212,6 +216,7 @@ define(function () { out.fo_existingNameError = "Ce nom est déjà utilisé dans ce répertoire. Veuillez en choisir un autre."; out.fo_moveFolderToChildError = "Vous ne pouvez pas déplacer un dossier dans un de ses descendants"; out.fo_unableToRestore = "Impossible de restaurer ce fichier à son emplacement d'origine. Vous pouvez essayer de le déplacer à un nouvel emplacement."; + out.fo_unavailableName = "Un fichier ou dossier avec le même nom existe déjà au nouvel emplacement. Renommez cet élément avant d'essayer à nouveau."; // index.html diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 3bb537252..359985acd 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -23,6 +23,9 @@ define(function () { out.common_connectionLost = 'Server Connection Lost'; out.websocketError = 'Unable to connect to the websocket server...'; + out.loading = "Loading..."; + out.error = "Error"; + out.disconnected = 'Disconnected'; out.synchronizing = 'Synchronizing'; out.reconnecting = 'Reconnecting...'; @@ -211,6 +214,7 @@ define(function () { out.fo_existingNameError = "Name already used in that directory. Please choose another one."; out.fo_moveFolderToChildError = "You can't move a folder into one of its descendants"; out.fo_unableToRestore = "Unable to restore that file to its original location. You can try to move it to a new location."; + out.fo_unavailableName = "A file or a folder with the same name already exist at the new location. Rename the element and try again."; // login out.login_login = "log in"; diff --git a/www/code/main.js b/www/code/main.js index 54e166fed..1f22b4e92 100644 --- a/www/code/main.js +++ b/www/code/main.js @@ -21,12 +21,10 @@ define([ var module = window.APP = { Cryptpad: Cryptpad, - spinner: Cryptpad.spinner(document.body), }; Cryptpad.styleAlerts(); - - module.spinner.show(); + Cryptpad.addLoadingScreen(); var ifrw = module.ifrw = $('#pad-iframe')[0].contentWindow; var stringify = function (obj) { @@ -43,8 +41,7 @@ define([ } var onConnectError = function (info) { - module.spinner.hide(); - Cryptpad.alert(Messages.websocketError); + Cryptpad.errorLoadingScreen(Messages.websocketError); }; var andThen = function (CMeditor) { @@ -565,7 +562,7 @@ define([ }); } - $(module.spinner.get().el).fadeOut(750); + Cryptpad.removeLoadingScreen(); setEditable(true); initializing = false; //Cryptpad.log("Your document is ready"); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 09f44d3d9..545f9b519 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -735,6 +735,26 @@ define([ }); }; + var LOADING = 'loading'; + common.addLoadingScreen = function () { + var $loading = $('
', {id: LOADING}); + var $container = $('
', {'class': 'loadingContainer'}); + $container.append(''); + var $spinner = $('
', {'class': 'spinnerContainer'}); + loadingSpinner = common.spinner($spinner).show(); + var $text = $('

').text(Messages.loading); + $container.append($spinner).append($text); + $loading.append($container); + $('body').append($loading); + }; + common.removeLoadingScreen = function () { + $('#' + LOADING).fadeOut(750); + }; + common.errorLoadingScreen = function (error) { + $('.spinnerContainer').hide(); + $('#' + LOADING).find('p').text(error || Messages.error); + }; + /* * Saving files */ @@ -1066,17 +1086,17 @@ define([ $(parent).append($target); var opts = { - lines: 9, // The number of lines to draw - length: 12, // The length of each line - width: 11, // The line thickness - radius: 20, // The radius of the inner circle + lines: 20, // The number of lines to draw + length: 5, // The length of each line + width: 2, // The line thickness + radius: 15, // The radius of the inner circle scale: 2, // Scales overall size of the spinner corners: 1, // Corner roundness (0..1) - color: '#777', // #rgb or #rrggbb or array of colors + color: '#ddd', // #rgb or #rrggbb or array of colors opacity: 0.3, // Opacity of the lines rotate: 31, // The rotation offset direction: 1, // 1: clockwise, -1: counterclockwise - speed: 0.9, // Rounds per second + speed: 1, // Rounds per second trail: 49, // Afterglow percentage fps: 20, // Frames per second when using setTimeout() as a fallback for CSS zIndex: 2e9, // The z-index (defaults to 2000000000) @@ -1085,7 +1105,8 @@ define([ left: '50%', // Left position relative to parent shadow: false, // Whether to render a shadow hwaccel: false, // Whether to use hardware acceleration - position: 'absolute', // Element positioning + position: 'relative', // Element positioning + height: '100px' }; var spinner = new Spinner(opts).spin($target[0]); diff --git a/www/file/fileObject.js b/www/file/fileObject.js index 2a339b9cc..d7d7f0a4a 100644 --- a/www/file/fileObject.js +++ b/www/file/fileObject.js @@ -254,7 +254,7 @@ define([ var parentEl = exp.findElement(files, parentPath); if (path.length === 4 && path[0] === TRASH) { files[TRASH][path[1]].splice(path[2], 1); - } else if (path[0] === UNSORTED) { + } else if (path[0] === UNSORTED) { //TODO || === TEMPLATE parentEl.splice(key, 1); } else { parentEl[key] = undefined; @@ -264,6 +264,7 @@ define([ }; // Find an element in a object following a path, resursively + // NOTE: it is always used with an absolute path and root === files in our code var findElement = exp.findElement = function (root, pathInput) { if (!pathInput) { error("Invalid path:\n", pathInput, "\nin root\n", root); @@ -279,6 +280,7 @@ define([ return findElement(root[key], path); }; + // Get the object {element: element, path: [path]} from a trash root path var getTrashElementData = exp.getTrashElementData = function (trashPath) { if (!isInTrashRoot) { debug("Called getTrashElementData on a element not in trash root: ", trashPath); @@ -289,6 +291,7 @@ define([ return findElement(files, parentPath); }; + // Get data from AllFiles (Cryptpad_RECENTPADS) var getFileData = exp.getFileData = function (file) { if (!file) { return; } var res; @@ -328,6 +331,7 @@ define([ }; // Move to trash + // TODO: rename the function var removeElement = exp.removeElement = function (path, cb, keepOld) { if (!path || path.length < 2 || path[0] === TRASH) { debug("Calling removeElement from a wrong path: ", path); @@ -343,6 +347,7 @@ define([ if (cb) { cb(); } }; + //TODO add suport for TEMPLATE here var moveElement = exp.moveElement = function (elementPath, newParentPath, cb, keepOld) { if (comparePath(elementPath, newParentPath)) { return; } // Nothing to do... if (isPathInTrash(newParentPath)) { @@ -353,12 +358,13 @@ define([ var newParent = findElement(files, newParentPath); + // Never move a folder in one of its children if (isFolder(element) && isSubpath(newParentPath, elementPath)) { log(Messages.fo_moveFolderToChildError); return; } - if (isPathInUnsorted(newParentPath)) { + if (isPathInUnsorted(newParentPath)) { //TODO || TEMPLATE if (isFolder(element)) { log(Messages.fo_moveUnsortedError); return; @@ -386,7 +392,7 @@ define([ var newName = !isPathInRoot(elementPath) ? getAvailableName(newParent, name) : name; if (typeof(newParent[newName]) !== "undefined") { - log("A file with the same name already exist at the new location. Rename the file and try again."); + log(Messages.fo_unavailableName); return; } newParent[newName] = element; @@ -523,6 +529,7 @@ define([ var emptyTrash = exp.emptyTrash = function (cb) { files[TRASH] = {}; + checkDeletedFiles(); if(cb) { cb(); } }; @@ -592,7 +599,7 @@ define([ var fixFiles = exp.fixFiles = function () { // Explore the tree and check that everything is correct: - // * 'root', 'trash' and 'filesData' exist and are objects + // * 'root', 'trash', 'unsorted' and 'filesData' exist and are objects // * ROOT: Folders are objects, files are href // * TRASH: Trash root contains only arrays, each element of the array is an object {element:.., path:..} // * FILES_DATA: - Data (title, cdate, adte) are stored in filesData. filesData contains only href keys linking to object with title, cdate, adate. @@ -601,10 +608,6 @@ define([ // * UNSORTED: Contains only files (href), and does not contains files that are in ROOT debug("Cleaning file system..."); - // Create a backup - if (typeof(localStorage.oldFileSystem) === "undefined") { - localStorage.oldFileSystem = '[]'; - } var before = JSON.stringify(files); if (typeof(files[ROOT]) !== "object") { debug("ROOT was not an object"); files[ROOT] = {}; } diff --git a/www/file/main.js b/www/file/main.js index 1f7c40772..f62b2cd39 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -18,6 +18,11 @@ define([ var $iframe = $('#pad-iframe').contents(); var ifrw = $('#pad-iframe')[0].contentWindow; + Cryptpad.addLoadingScreen(); + var onConnectError = function (info) { + Cryptpad.errorLoadingScreen(Messages.websocketError); + }; + var APP = window.APP = { $bar: $iframe.find('#toolbar'), editable: false @@ -48,12 +53,6 @@ define([ console.error.apply(console, arguments); }; var log = config.log = Cryptpad.log; - var DEBUG_LS = APP.DEBUG_LS = { - resetLocalStorage : function () { - delete localStorage[LOCALSTORAGE_OPENED]; - delete localStorage[LOCALSTORAGE_LAST]; - } - }; var getLastOpenedFolder = function () { var path; @@ -459,6 +458,7 @@ define([ var andThen = function () { filesOp.moveElements(paths, newPath, cb); }; + // "force" is currently unused but may be configurable by user if (newPath[0] !== TRASH || force) { andThen(); return; @@ -1405,6 +1405,7 @@ define([ pressKey(e.which, false); }); $(ifrw).on('keydown', function (e) { + // "Del" if (e.which === 46) { var $selected = $iframe.find('.selected'); if (!$selected.length) { return; } @@ -1552,6 +1553,7 @@ define([ var $backupButton = Cryptpad.createButton('', true); $backupButton.on('click', function() { var url = window.location.origin + window.location.pathname + '#' + editHash; + //TODO change text & transalte Cryptpad.alert("Backup URL for this pad. It is highly recommended that you do not share it with other people.
Anybody with that URL can remove all the files in your file manager.
" + url); }); $userBlock.append($backupButton); @@ -1566,12 +1568,15 @@ define([ proxy[FILES_DATA] = s; initLocalStorage(); init(proxy); + APP.userList.onChange(); + Cryptpad.removeLoadingScreen(); }); return; } initLocalStorage(); init(proxy); APP.userList.onChange(); + Cryptpad.removeLoadingScreen(); }; var onDisconnect = function (info) { setEditable(false); @@ -1593,6 +1598,11 @@ define([ onDisconnect(); }); }); + Cryptpad.onError(function (info) { + if (info) { + onConnectError(); + } + }); }); diff --git a/www/pad/main.js b/www/pad/main.js index 1b929b09f..8f59a898c 100644 --- a/www/pad/main.js +++ b/www/pad/main.js @@ -27,6 +27,7 @@ define([ var DiffDom = window.diffDOM; Cryptpad.styleAlerts(); + Cryptpad.addLoadingScreen(); var stringify = function (obj) { return JSONSortify(obj); @@ -60,7 +61,6 @@ define([ logFights: true, fights: [], Cryptpad: Cryptpad, - spinner: Cryptpad.spinner(document.body), }; var toolbar; @@ -78,8 +78,7 @@ define([ }; var onConnectError = function (info) { - module.spinner.hide(); - Cryptpad.alert(Messages.websocketError); + Cryptpad.errorLoadingScreen(Messages.websocketError); }; var andThen = function (Ckeditor) { @@ -140,9 +139,6 @@ define([ $(inner).css({ color: '#333', }); - $(module.spinner.get().el).fadeOut(750); - } else { - module.spinner.show(); } if (!readOnly || !bool) { inner.setAttribute('contenteditable', bool); @@ -657,6 +653,7 @@ define([ console.log("Unlocking editor"); setEditable(true); initializing = false; + Cryptpad.removeLoadingScreen(); // Update the toolbar list: // Add the current user in the metadata if he has edit rights if (readOnly) { return; } diff --git a/www/poll/test/main.js b/www/poll/test/main.js index 3f72e47ac..4642f40cf 100644 --- a/www/poll/test/main.js +++ b/www/poll/test/main.js @@ -22,6 +22,11 @@ define([ var secret = Cryptpad.getSecrets(); var readOnly = secret.keys && !secret.keys.editKeyStr; + Cryptpad.addLoadingScreen(); + var onConnectError = function (info) { + Cryptpad.errorLoadingScreen(Messages.websocketError); + }; + var APP = window.APP = { Toolbar: Toolbar, Hyperjson: Hyperjson, @@ -566,6 +571,7 @@ define([ } else { publish(true); } + Cryptpad.removeLoadingScreen(); // Update the toolbar list: // Add the current user in the metadata if he has edit rights @@ -662,11 +668,7 @@ define([ var disconnect = function (info) { //setEditable(false); // TODO - if (info.error) { - Cryptpad.alert(Messages.websocketError); - return; - } - //Cryptpad.alert(Messages.common_connectionLost); // TODO + Cryptpad.alert(Messages.common_connectionLost); }; var config = { @@ -715,5 +717,10 @@ define([ } }); }); + Cryptpad.onError(function (info) { + if (info) { + onConnectError(); + } + }); }); diff --git a/www/slide/main.js b/www/slide/main.js index d1dfe212e..2dc6bc913 100644 --- a/www/slide/main.js +++ b/www/slide/main.js @@ -22,7 +22,6 @@ define([ var module = window.APP = { Cryptpad: Cryptpad, - spinner: Cryptpad.spinner(document.body), TextPatcher: TextPatcher, Slide: Slide, }; @@ -32,7 +31,7 @@ define([ var SLIDE_COLOR_ID = "cryptpad-color"; Cryptpad.styleAlerts(); - module.spinner.show(); + Cryptpad.addLoadingScreen(); var stringify = function (obj) { return JSONSortify(obj); @@ -60,8 +59,7 @@ define([ var presentMode = Slide.isPresentURL(); var onConnectError = function (info) { - module.spinner.hide(); - Cryptpad.alert(Messages.websocketError); + Cryptpad.errorLoadingScreen(Messages.websocketError); }; var andThen = function (CMeditor) { @@ -663,7 +661,7 @@ define([ document.title = APP.title; }); - $(module.spinner.get().el).fadeOut(750); + Cryptpad.removeLoadingScreen(); setEditable(true); initializing = false; //Cryptpad.log("Your document is ready"); From 1772cc93c8ce6706c501b79821715174fd0b810d Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 22 Dec 2016 11:02:12 +0100 Subject: [PATCH 5/6] Fix the taildoc bug --- customize.dist/main.js | 3 ++- www/pad/main.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/customize.dist/main.js b/customize.dist/main.js index 52804a105..af0afa95b 100644 --- a/customize.dist/main.js +++ b/customize.dist/main.js @@ -139,6 +139,8 @@ define([ }); }; + + displayCreateButtons(); Cryptpad.ready(function () { console.log("ready"); @@ -149,7 +151,6 @@ define([ DecorateToolbar.main($('#bottom-bar')); Cryptpad.styleAlerts(); - displayCreateButtons(); refreshTable(); if (Cryptpad.store && Cryptpad.store.change) { Cryptpad.store.change(function (data) { diff --git a/www/pad/main.js b/www/pad/main.js index 8f59a898c..b7f717704 100644 --- a/www/pad/main.js +++ b/www/pad/main.js @@ -121,7 +121,6 @@ define([ el.setAttribute('class', 'non-realtime'); }); - editor.execCommand('maximize'); var documentBody = ifrw.$('iframe')[0].contentDocument.body; var inner = window.inner = documentBody; @@ -629,6 +628,8 @@ define([ // this should only ever get called once, when the chain syncs var onReady = realtimeOptions.onReady = function (info) { + editor.execCommand('maximize'); + module.patchText = TextPatcher.create({ realtime: info.realtime, //logging: true, From 9b6c9e3891d07a56cf017c4fadd2b7ae021c0f9b Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 22 Dec 2016 16:53:35 +0100 Subject: [PATCH 6/6] Add support for templates in the file manager --- customize.dist/fsStore.js | 8 ++ customize.dist/translations/messages.js | 1 + www/common/cryptpad-common.js | 15 +++ www/file/file.css | 2 +- www/file/fileObject.js | 132 +++++++++++++++++++----- www/file/main.js | 80 +++++++++----- 6 files changed, 184 insertions(+), 54 deletions(-) diff --git a/customize.dist/fsStore.js b/customize.dist/fsStore.js index f600819b1..81b2a9f05 100644 --- a/customize.dist/fsStore.js +++ b/customize.dist/fsStore.js @@ -86,6 +86,14 @@ define([ cb(); }; + Store.addTemplate = function (href) { + filesOp.addTemplate(href); + }; + + Store.listTemplates = function () { + return filesOp.listTemplates(); + }; + Store.getProxy = function () { return exp; }; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 359985acd..e73f501a2 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -172,6 +172,7 @@ define(function () { out.fm_trashName = "Trash"; out.fm_unsortedName = "Unsorted files"; out.fm_filesDataName = "All files"; + out.fm_templateName = "Templates"; out.fm_newFolder = "New folder"; out.fm_newFolderButton = "NEW FOLDER"; out.fm_folderName = "Folder name"; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 545f9b519..8d177309c 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -451,6 +451,21 @@ define([ return localStorage[attr]; }; + // STORAGE - TEMPLATES + var listTemplates = common.listTemplates = function (type) { + var allTemplates = getStore().listTemplates(); + if (!type) { return allTemplates; } + + var templates = allTemplates.filter(function (f) { + var parsed = parsePadUrl(f.href); + return parsed.type === type; + }); + return templates; + }; + var addTemplate = common.addTemplate = function (href) { + getStore().addTemplate(href); + }; + // STORAGE /* fetch and migrate your pad history from localStorage */ diff --git a/www/file/file.css b/www/file/file.css index a42f94208..1d2f7e1ec 100644 --- a/www/file/file.css +++ b/www/file/file.css @@ -97,7 +97,7 @@ li { text-decoration: underline; } -#tree #trashTree, #tree #unsortedTree, #tree #allfilesTree { +#tree #trashTree, #tree #unsortedTree, #tree #templateTree, #tree #allfilesTree { margin-top: 2em; } diff --git a/www/file/fileObject.js b/www/file/fileObject.js index d7d7f0a4a..df1f80169 100644 --- a/www/file/fileObject.js +++ b/www/file/fileObject.js @@ -9,6 +9,7 @@ define([ var UNSORTED = "unsorted"; var FILES_DATA = "filesData"; var TRASH = "trash"; + var TEMPLATE = "template"; var NEW_FOLDER_NAME = Messages.fm_newFolder; var init = module.init = function (files, config) { @@ -46,6 +47,12 @@ define([ var isPathInUnsorted = exp.isPathInUnsorted = function (path) { return path[0] && path[0] === UNSORTED; }; + var isPathInTemplate = exp.isPathInTemplate = function (path) { + return path[0] && path[0] === TEMPLATE; + }; + var isPathInHrefArray = exp.isPathInHrefArray = function (path) { + return isPathInUnsorted(path) || isPathInTemplate(path); + }; var isPathInTrash = exp.isPathInTrash = function (path) { return path[0] && path[0] === TRASH; }; @@ -162,6 +169,13 @@ define([ return files[UNSORTED].slice(); }; + var getTemplateFiles = exp.getTemplateFiles = function () { + if (!files[TEMPLATE]) { + files[TEMPLATE] = []; + } + return files[TEMPLATE].slice(); + }; + var getFilesRecursively = function (root, arr) { for (var e in root) { if (isFile(root[e])) { @@ -229,13 +243,15 @@ define([ var checkDeletedFiles = function () { var rootFiles = getRootFiles(); var unsortedFiles = getUnsortedFiles(); + var templateFiles = getTemplateFiles(); var trashFiles = getTrashFiles(); var toRemove = []; files[FILES_DATA].forEach(function (arr) { var f = arr.href; if (rootFiles.indexOf(f) === -1 && unsortedFiles.indexOf(f) === -1 - && trashFiles.indexOf(f) === -1) { + && trashFiles.indexOf(f) === -1 + && templateFiles.indexOf(f) === -1) { toRemove.push(arr); } }); @@ -339,7 +355,7 @@ define([ } var element = findElement(files, path); var key = path[path.length - 1]; - var name = isPathInUnsorted(path) ? getTitle(element) : key; + var name = isPathInHrefArray(path) ? getTitle(element) : key; var parentPath = path.slice(); parentPath.pop(); pushToTrash(name, element, parentPath); @@ -347,7 +363,6 @@ define([ if (cb) { cb(); } }; - //TODO add suport for TEMPLATE here var moveElement = exp.moveElement = function (elementPath, newParentPath, cb, keepOld) { if (comparePath(elementPath, newParentPath)) { return; } // Nothing to do... if (isPathInTrash(newParentPath)) { @@ -364,14 +379,15 @@ define([ return; } - if (isPathInUnsorted(newParentPath)) { //TODO || TEMPLATE + if (isPathInHrefArray(newParentPath)) { if (isFolder(element)) { - log(Messages.fo_moveUnsortedError); + log(Messages.fo_moveUnsortedError); //TODO or template return; } else { - if (isPathInUnsorted(elementPath)) { return; } - if (files[UNSORTED].indexOf(element) === -1) { - files[UNSORTED].push(element); + if (elementPath[0] === newParentPath[0]) { return; } + var fileRoot = newParentPath[0]; + if (files[fileRoot].indexOf(element) === -1) { + files[fileRoot].push(element); } if (!keepOld) { deleteFromObject(elementPath); } if(cb) { cb(); } @@ -381,7 +397,7 @@ define([ var name; - if (isPathInUnsorted(elementPath)) { + if (isPathInHrefArray(elementPath)) { name = getTitle(element); } else if (isInTrashRoot(elementPath)) { // Element from the trash root: elementPath = [TRASH, "{dirName}", 0, 'element'] @@ -403,13 +419,16 @@ define([ // "Unsorted" is an array of href: we can't move several of them using "moveElement" in a // loop because moveElement removes the href from the array and it changes the path for all // the other elements. We have to move them all and then remove them from unsorted - var moveUnsortedElements = exp.moveUnsortedElements = function (paths, newParentPath, cb) { + var moveHrefArrayElements = exp.moveHrefArrayElements = function (paths, newParentPath, cb) { if (!paths || paths.length === 0) { return; } - if (isPathInUnsorted(newParentPath)) { return; } + //if (isPathInHrefArray(newParentPath)) { return; } var elements = {}; // Get the elements paths.forEach(function (p) { - if (!isPathInUnsorted(p)) { return; } + // Here we move only files from array categories (unsorted, template...) + if (!isPathInHrefArray(p)) { return; } + // And we check that we don't want to move to the same location + if (p[0] === newParentPath[0]) { return; } var el = findElement(files, p); if (el) { elements[el] = p; } }); @@ -419,22 +438,21 @@ define([ }); // Remove the elements from their old location Object.keys(elements).forEach(function (el) { - var idx = files[UNSORTED].indexOf(el); + var fileRoot = elements[el][0]; + var idx = files[fileRoot].indexOf(el); if (idx !== -1) { - files[UNSORTED].splice(idx, 1); + files[fileRoot].splice(idx, 1); } }); if (cb) { cb(); } }; var moveElements = exp.moveElements = function (paths, newParentPath, cb) { - var unsortedPaths = paths.filter(function (p) { - return p[0] === UNSORTED; - }); - moveUnsortedElements(unsortedPaths, newParentPath); + var unsortedPaths = paths.filter(isPathInHrefArray); + moveHrefArrayElements(unsortedPaths, newParentPath); // Copy the elements to their new location paths.forEach(function (p) { - if (isPathInUnsorted(p)) { return; } + if (isPathInHrefArray(p)) { return; } moveElement(p, newParentPath, null); }); if(cb) { cb(); } @@ -477,12 +495,13 @@ define([ var element = findElement(files, path); var parentEl = getTrashElementData(path); var newPath = parentEl.path; - if (isPathInUnsorted(newPath)) { - if (files[UNSORTED].indexOf(element) === -1) { - files[UNSORTED].push(element); - removeFromTrashArray(parentEl, path[1]); - cb(); + if (isPathInHrefArray(newPath)) { + var fileRoot = newPath[0]; + if (files[fileRoot].indexOf(element) === -1) { + files[fileRoot].push(element); } + removeFromTrashArray(parentEl, path[1]); + cb(); return; } // Find the new parent element @@ -576,7 +595,12 @@ define([ var unsortedFiles = getUnsortedFiles(); var rootFiles = getRootFiles(); var trashFiles = getTrashFiles(); - if (path && name) { + var templateFiles = getTemplateFiles(); + if (path && isPathInHrefArray(path)) { + var parentEl = findElement(files, newPath); + parentEl.push(href); + } + else if (path && name) { var newPath = decodeURIComponent(path).split(','); var parentEl = findElement(files, newPath); if (parentEl) { @@ -585,11 +609,38 @@ define([ return; } } - if (unsortedFiles.indexOf(href) === -1 && rootFiles.indexOf(href) === -1 && trashFiles.indexOf(href) === -1) { + if (unsortedFiles.indexOf(href) === -1 && rootFiles.indexOf(href) === -1&& templateFiles.indexOf(href) === -1 && trashFiles.indexOf(href) === -1) { files[UNSORTED].push(href); } }; + // addTemplate is called when we want to add a new pad, never visited, to the templates list + // first, we must add it to FILES_DATA, so the input has to be an fileDAta object + var addTemplate = exp.addTemplate = function (fileData) { + if (typeof fileData !== "object" || !fileData.href || !fileData.title) { return; } + + var href = fileData.href; + var test = files[FILES_DATA].some(function (o) { + o.href === href; + }); + if (!test) { + files[FILES_DATA].push(fileData); + } + if (files[TEMPLATE].indexOf(href) === -1) { + files[TEMPLATE].push(href); + } + }; + + var listTemplates = exp.listTemplates = function (type) { + var templateFiles = getTemplateFiles(); + var res = []; + templateFiles.forEach(function (f) { + var data = getFileData(f); + res.push(JSON.parse(JSON.stringify(data))); + }); + return res; + }; + var uniq = function (a) { var seen = {}; return a.filter(function(item) { @@ -614,6 +665,7 @@ define([ if (typeof(files[TRASH]) !== "object") { debug("TRASH was not an object"); files[TRASH] = {}; } if (!$.isArray(files[FILES_DATA])) { debug("FILES_DATA was not an array"); files[FILES_DATA] = []; } if (!$.isArray(files[UNSORTED])) { debug("UNSORTED was not an array"); files[UNSORTED] = []; } + if (!$.isArray(files[TEMPLATE])) { debug("TEMPLATE was not an array"); files[TEMPLATE] = []; } var fixRoot = function (element) { for (var el in element) { @@ -653,16 +705,42 @@ define([ var fixUnsorted = function (us) { var rootFiles = getRootFiles().slice(); + var templateFiles = getTemplateFiles(); var toClean = []; us.forEach(function (el, idx) { - if (!isFile(el) || rootFiles.indexOf(el) !== -1) { + if (!isFile(el) || rootFiles.indexOf(el) !== -1 || templateFiles.indexOf(el) !== -1) { toClean.push(idx); } }); + toClean.forEach(function (el) { + var idx = us.indexOf(el); + if (idx !== -1) { + us.splice(idx, 1); + } + }); }; files[UNSORTED] = uniq(files[UNSORTED]); fixUnsorted(files[UNSORTED]); + var fixTemplate = function (us) { + var rootFiles = getRootFiles().slice(); + var unsortedFiles = getUnsortedFiles(); + var toClean = []; + us.forEach(function (el, idx) { + if (!isFile(el) || rootFiles.indexOf(el) !== -1 || unsortedFiles.indexOf(el) !== -1) { + toClean.push(idx); + } + }); + toClean.forEach(function (el) { + var idx = us.indexOf(el); + if (idx !== -1) { + us.splice(idx, 1); + } + }); + }; + files[TEMPLATE] = uniq(files[TEMPLATE]); + fixUnsorted(files[TEMPLATE]); + var fixFilesData = function (fd) { var rootFiles = getRootFiles(); var unsortedFiles = getUnsortedFiles(); diff --git a/www/file/main.js b/www/file/main.js index f62b2cd39..237c4c5c7 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -18,14 +18,15 @@ define([ var $iframe = $('#pad-iframe').contents(); var ifrw = $('#pad-iframe')[0].contentWindow; - Cryptpad.addLoadingScreen(); + //Cryptpad.addLoadingScreen(); var onConnectError = function (info) { Cryptpad.errorLoadingScreen(Messages.websocketError); }; var APP = window.APP = { $bar: $iframe.find('#toolbar'), - editable: false + editable: false, + Cryptpad: Cryptpad }; var ROOT = "root"; @@ -34,6 +35,8 @@ define([ var UNSORTED_NAME = Messages.fm_unsortedName; var FILES_DATA = Cryptpad.storageKey; var FILES_DATA_NAME = Messages.fm_filesDataName; + var TEMPLATE = "template"; + var TEMPLATE_NAME = Messages.fm_templateName; var TRASH = "trash"; var TRASH_NAME = Messages.fm_trashName; var LOCALSTORAGE_LAST = "cryptpad-file-lastOpened"; @@ -204,6 +207,7 @@ define([ var $fileIcon = $('', {"class": "fa fa-file-text-o file"}); var $upIcon = $('', {"class": "fa fa-arrow-circle-up"}); var $unsortedIcon = $('', {"class": "fa fa-files-o"}); + var $templateIcon = $('', {"class": "fa fa-cubes"}); var $trashIcon = $('', {"class": "fa fa-trash"}); var $trashEmptyIcon = $('', {"class": "fa fa-trash-o"}); var $collapseIcon = $('', {"class": "fa fa-minus-square-o expcol"}); @@ -396,7 +400,7 @@ define([ var openDirectoryContextMenu = function (e) { var $element = $(e.target).closest('li'); $contextMenu.find('li').show(); - if ($element.hasClass('file-element')) { + if ($element.find('.file-element').length) { $contextMenu.find('a.newfolder').parent('li').hide(); } openContextMenu(e, $contextMenu); @@ -670,6 +674,7 @@ define([ if (name === ROOT && path.length === 1) { name = ROOT_NAME; } else if (name === TRASH && path.length === 1) { name = TRASH_NAME; } else if (name === UNSORTED && path.length === 1) { name = UNSORTED_NAME; } + else if (name === TEMPLATE && path.length === 1) { name = TEMPLATE_NAME; } else if (name === FILES_DATA && path.length === 1) { name = FILES_DATA_NAME; } else if (filesOp.isPathInTrash(path)) { name = getTrashTitle(path); } var $title = $('

').text(name); @@ -787,17 +792,25 @@ define([ var SORT_FOLDER_DESC = 'sortFoldersDesc'; var SORT_FILE_BY = 'sortFilesBy'; var SORT_FILE_DESC = 'sortFilesDesc'; + + var getSortFileDesc = function () { + return Cryptpad.getLSAttribute(SORT_FILE_DESC) === "true"; + }; + var getSortFolderDesc = function () { + return Cryptpad.getLSAttribute(SORT_FOLDER_DESC) === "true"; + }; + var onSortByClick = function (e) { var $span = $(this); var value; if ($span.hasClass('foldername')) { - value = Cryptpad.getLSAttribute(SORT_FOLDER_DESC); + value = getSortFolderDesc(); Cryptpad.setLSAttribute(SORT_FOLDER_DESC, value ? false : true); refresh(); return; } value = Cryptpad.getLSAttribute(SORT_FILE_BY); - var descValue = Cryptpad.getLSAttribute(SORT_FILE_DESC); + var descValue = getSortFileDesc(); if ($span.hasClass('filename')) { if (value === '') { descValue = descValue ? false : true; @@ -807,7 +820,7 @@ define([ } } else { var found = false; - ['title', 'atime', 'ctime'].forEach(function (c) { + ['title', 'type', 'atime', 'ctime'].forEach(function (c) { if (!found && $span.hasClass(c)) { found = true; if (value === c) { descValue = descValue ? false : true; } @@ -826,7 +839,7 @@ define([ var addFolderSortIcon = function ($list) { var $icon = $sortAscIcon.clone(); - if (Cryptpad.getLSAttribute(SORT_FOLDER_DESC)) { + if (getSortFolderDesc()) { $icon = $sortDescIcon.clone(); } if (typeof(Cryptpad.getLSAttribute(SORT_FOLDER_DESC)) !== "undefined") { @@ -845,7 +858,7 @@ define([ }; var addFileSortIcon = function ($list) { var $icon = $sortAscIcon.clone(); - if (Cryptpad.getLSAttribute(SORT_FILE_DESC)) { + if (getSortFileDesc()) { $icon = $sortDescIcon.clone(); } var classSorted; @@ -860,7 +873,7 @@ define([ var $fihElement = $('', {'class': 'element'}).appendTo($fileHeader); var $fhName = $('', {'class': 'name filename'}).text(Messages.fm_fileName).click(onSortByClick); var $fhTitle = $('', {'class': 'title '}).text(Messages.fm_title).click(onSortByClick); - var $fhType = $('', {'class': 'type'}).text(Messages.table_type); + var $fhType = $('', {'class': 'type'}).text(Messages.table_type).click(onSortByClick); var $fhAdate = $('', {'class': 'atime'}).text(Messages.fm_lastAccess).click(onSortByClick); var $fhCdate = $('', {'class': 'ctime'}).text(Messages.fm_creation).click(onSortByClick); $fihElement.append($fhName); @@ -891,6 +904,10 @@ define([ if (prop) { var element = useHref || useData ? el : root[el]; var e = useData ? element : filesOp.getFileData(element); + if (prop === 'type') { + var hrefData = Cryptpad.parsePadUrl(e.href); + return hrefData.type; + } if (prop === 'atime' || prop === 'ctime') { return new Date(e[prop]); } @@ -933,20 +950,20 @@ define([ }; // Unsorted element are represented by "href" in an array: they don't have a filename // and they don't hav a hierarchical structure (folder/subfolders) - var displayUnsorted = function ($container) { - var unsorted = files[UNSORTED]; - if (allFilesSorted()) { return; } + var displayHrefArray = function ($container, rootName) { + var unsorted = files[rootName]; + if (rootName === UNSORTED && allFilesSorted()) { return; } var $fileHeader = getFileListHeader(false); $container.append($fileHeader); var keys = unsorted; - var sortedFiles = sortElements(false, [UNSORTED], keys, Cryptpad.getLSAttribute(SORT_FILE_BY), !Cryptpad.getLSAttribute(SORT_FILE_DESC), true); + var sortedFiles = sortElements(false, [rootName], keys, Cryptpad.getLSAttribute(SORT_FILE_BY), !getSortFileDesc(), true); sortedFiles.forEach(function (href) { var file = filesOp.getFileData(href); if (!file) { - debug("getUnsortedFiles returns an element not present in filesData: ", href); + debug("Unsorted or template returns an element not present in filesData: ", href); return; } - var idx = files[UNSORTED].indexOf(href); + var idx = files[rootName].indexOf(href); var $icon = $fileIcon.clone(); var $name = $('', { 'class': 'file-element element' }); addFileData(href, file.title, $name, false); @@ -955,7 +972,7 @@ define([ }).append($icon).append($name).dblclick(function () { openFile(href); }); - var path = [UNSORTED, idx]; + var path = [rootName, idx]; $element.data('path', path); $element.click(function(e) { e.stopPropagation(); @@ -972,7 +989,7 @@ define([ var $fileHeader = getFileListHeader(false); $container.append($fileHeader); var keys = allfiles; - var sortedFiles = sortElements(false, [FILES_DATA], keys, Cryptpad.getLSAttribute(SORT_FILE_BY), !Cryptpad.getLSAttribute(SORT_FILE_DESC), false, true); + var sortedFiles = sortElements(false, [FILES_DATA], keys, Cryptpad.getLSAttribute(SORT_FILE_BY), !getSortFileDesc(), false, true); sortedFiles.forEach(function (file) { var $icon = $fileIcon.clone(); var $name = $('', { 'class': 'file-element element' }); @@ -1013,8 +1030,8 @@ define([ }); }); }); - var sortedFolders = sortTrashElements(true, filesList, null, !Cryptpad.getLSAttribute(SORT_FOLDER_DESC)); - var sortedFiles = sortTrashElements(false, filesList, Cryptpad.getLSAttribute(SORT_FILE_BY), !Cryptpad.getLSAttribute(SORT_FILE_DESC)); + var sortedFolders = sortTrashElements(true, filesList, null, !getSortFolderDesc()); + var sortedFiles = sortTrashElements(false, filesList, Cryptpad.getLSAttribute(SORT_FILE_BY), !getSortFileDesc()); if (filesOp.hasSubfolder(root, true)) { $list.append($folderHeader); } sortedFolders.forEach(function (f) { var $element = createElement([TRASH], f.spath, root, true); @@ -1039,6 +1056,7 @@ define([ } var isTrashRoot = filesOp.comparePath(path, [TRASH]); var isUnsorted = filesOp.comparePath(path, [UNSORTED]); + var isTemplate = filesOp.comparePath(path, [TEMPLATE]); var isAllFiles = filesOp.comparePath(path, [FILES_DATA]); var root = filesOp.findElement(files, path); @@ -1077,8 +1095,8 @@ define([ var $folderHeader = getFolderListHeader(); var $fileHeader = getFileListHeader(true); - if (isUnsorted) { - displayUnsorted($list); + if (isUnsorted || isTemplate) { + displayHrefArray($list, path[0]); } else if (isAllFiles) { displayAllFiles($list); } else if (isTrashRoot) { @@ -1089,8 +1107,8 @@ define([ if (filesOp.hasSubfolder(root)) { $list.append($folderHeader); } // display sub directories var keys = Object.keys(root); - var sortedFolders = sortElements(true, path, keys, null, !Cryptpad.getLSAttribute(SORT_FOLDER_DESC)); - var sortedFiles = sortElements(false, path, keys, Cryptpad.getLSAttribute(SORT_FILE_BY), !Cryptpad.getLSAttribute(SORT_FILE_DESC)); + var sortedFolders = sortElements(true, path, keys, null, !getSortFolderDesc()); + var sortedFiles = sortElements(false, path, keys, Cryptpad.getLSAttribute(SORT_FILE_BY), !getSortFileDesc()); sortedFolders.forEach(function (key) { if (filesOp.isFile(root[key])) { return; } var $element = createElement(path, key, root, true); @@ -1211,6 +1229,15 @@ define([ $container.append($unsortedList); }; + var createTemplate = function ($container, path) { + var $icon = $templateIcon.clone(); + var isOpened = filesOp.comparePath(path, currentPath); + var $element = createTreeElement(TEMPLATE_NAME, $icon, [TEMPLATE], false, true, false, isOpened); + $element.addClass('root'); + var $list = $('
    ', { id: 'templateTree' }).append($element); + $container.append($list); + }; + var createAllFiles = function ($container, path) { var $icon = $unsortedIcon.clone(); var isOpened = filesOp.comparePath(path, currentPath); @@ -1244,6 +1271,7 @@ define([ $tree.html(''); createTree($tree, [ROOT]); createUnsorted($tree, [UNSORTED]); + createTemplate($tree, [TEMPLATE]); createAllFiles($tree, [FILES_DATA]); createTrash($tree, [TRASH]); }; @@ -1427,7 +1455,7 @@ define([ if (paths.length === 1) { var path = paths[0]; var element = filesOp.findElement(files, path); - var name = filesOp.isInTrashRoot(path) ? path[1] : (filesOp.isPathInUnsorted(path) ? filesOp.getTitle(element) : path[path.length - 1]); + var name = filesOp.isInTrashRoot(path) ? path[1] : (filesOp.isPathInHrefArray(path) ? filesOp.getTitle(element) : path[path.length - 1]); msg = Messages._getKey("fm_removePermanentlyDialog", [name]); } Cryptpad.confirm(msg, function(res) { @@ -1454,6 +1482,7 @@ define([ files.on('change', [], function (o, n, p) { var path = arguments[2]; if ((filesOp.isPathInUnsorted(currentPath) && filesOp.isPathInUnsorted(path)) || + (filesOp.isPathInTemplate(currentPath) && filesOp.isPathInTemplate(path)) || (path.length >= currentPath.length && filesOp.isSubpath(path, currentPath)) || (filesOp.isPathInTrash(currentPath) && filesOp.isPathInTrash(path))) { // Reload after a few ms to make sure all the change events have been received @@ -1466,6 +1495,7 @@ define([ }).on('remove', [], function (o, p) { var path = arguments[1]; if ((filesOp.isPathInUnsorted(currentPath) && filesOp.isPathInUnsorted(path)) || + (filesOp.isPathInTemplate(currentPath) && filesOp.isPathInTemplate(path)) || (path.length >= currentPath.length && filesOp.isSubpath(path, currentPath)) || (filesOp.isPathInTrash(currentPath) && filesOp.isPathInTrash(path))) { // Reload after a few to make sure all the change events have been received @@ -1478,8 +1508,6 @@ define([ refresh(); }; - - // don't initialize until the store is ready. Cryptpad.ready(function () { var storeObj = Cryptpad.getStore().getProxy && Cryptpad.getStore().getProxy().proxy ? Cryptpad.getStore().getProxy() : undefined;