diff --git a/customize.dist/messages.js b/customize.dist/messages.js index f1062ada3..bb314e002 100644 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -42,17 +42,21 @@ define(['/customize/languageSelector.js', }); }; + var translateText = function (i, e) { + var $el = $(e); + var key = $el.data('localization'); + $el.html(messages[key]); + }; + var translateTitle = function (i, e) { + var $el = $(this); + var key = $el.data('localization-title'); + $el.attr('title', messages[key]); + }; messages._applyTranslation = function () { - $('[data-localization]').each(function (i, e) { - var $el = $(this); - var key = $el.data('localization'); - $el.html(messages[key]); - }); - $('[data-localization-title]').each(function (i, e) { - var $el = $(this); - var key = $el.data('localization-title'); - $el.attr('title', messages[key]); - }); + $('[data-localization]').each(translateText); + $('#pad-iframe').contents().find('[data-localization]').each(translateText); + $('[data-localization-title]').each(translateTitle); + $('#pad-iframe').contents().find('[data-localization-title]').each(translateTitle); }; // Non translatable keys diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 979a24d0c..5be6d0ae5 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -181,7 +181,15 @@ 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."; - + // File - Context menu + out.fc_newfolder = "Nouveau dossier"; + out.fc_rename = "Renommer"; + out.fc_open = "Ouvrir"; + out.fc_delete = "Supprimer"; + out.fc_restore = "Restaurer"; + out.fc_remove = "Supprimer définitivement"; + out.fc_empty = "Vider la corbeille"; + // fileObject.js (logs) out.fo_moveUnsortedError = "La liste des éléments non triés ne peut pas contenir de dossiers."; 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"; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 31ee969f1..e1369a550 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -181,7 +181,15 @@ 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."; - + // File - Context menu + out.fc_newfolder = "New folder"; + out.fc_rename = "Rename"; + out.fc_open = "Open"; + out.fc_delete = "Delete"; + out.fc_restore = "Restore"; + out.fc_remove = "Delete permanently"; + out.fc_empty = "Empty the trash"; + // fileObject.js (logs) out.fo_moveUnsortedError = "You can't move a folder to the list of unsorted pads"; 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"; diff --git a/www/file/file.css b/www/file/file.css index c9d8b79a4..b5efa3b0f 100644 --- a/www/file/file.css +++ b/www/file/file.css @@ -11,6 +11,11 @@ html, body { overflow: auto; } +body { + display: flex; + flex-flow: row; +} + .fa { /*min-width: 17px;*/ margin-right: 3px; @@ -54,15 +59,13 @@ li { /* TREE */ #tree { - position:absolute; - top: 0; - left: 0; - bottom: 0; - right: 70%; border: 2px solid blue; box-sizing: border-box; background: white; overflow: auto; + resize: horizontal; + width: 30%; + white-space: nowrap; } #tree li { @@ -162,15 +165,13 @@ li { /* CONTENT */ #content { - position: absolute; - top: 0; - left: 30%; - bottom: 0; - right: 0; border: 2px solid green; box-sizing: border-box; background: #eee; overflow: auto; + flex: 1; + display: flex; + flex-flow: column; } .topButtonContainer { @@ -190,6 +191,7 @@ li { #folderContent { padding-right: 10px; + flex: 1; } #content li:hover:not(.header) .name { @@ -270,12 +272,12 @@ li { #content div.grid .listElement { display: none; } -@media screen and (max-width: 1000px) { +@media screen and (max-width: 1200px) { #content .list .element span.title { display: none; } } -@media screen and (min-width: 1001px) { +@media screen and (min-width: 1201px) { #content .list .element span.title { display: inline; } diff --git a/www/file/fileObject.js b/www/file/fileObject.js index 623bea8bd..62be76029 100644 --- a/www/file/fileObject.js +++ b/www/file/fileObject.js @@ -1,8 +1,7 @@ define([ '/customize/messages.js', - '/common/cryptpad-common.js', '/bower_components/jquery/dist/jquery.min.js', -], function (Messages, Cryptpad) { +], function (Messages) { var $ = window.jQuery; var module = {}; @@ -38,19 +37,6 @@ define([ return result; }; - var deleteFromObject = function (path) { - var parentPath = path.slice(); - var key = parentPath.pop(); - 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) { - parentEl.splice(key, 1); - } else { - delete parentEl[key]; - } - }; - var isPathInRoot = exp.isPathInRoot = function (path) { return path[0] && path[0] === ROOT; }; @@ -206,10 +192,55 @@ define([ return ret; }; + var removeFileFromRoot = function (root, href) { + if (isFile(root)) { return; } + for (var e in root) { + if (isFile(root[e])) { + if (compareFiles(href, root[e])) { + delete root[e]; + } + } else { + removeFileFromRoot(root[e], href); + } + } + }; + var isInTrashRoot = exp.isInTrashRoot = function (path) { return path[0] === TRASH && path.length === 4; }; + var checkDeletedFiles = function () { + var rootFiles = getRootFiles().slice(); + var unsortedFiles = getUnsortedFiles().slice(); + var trashFiles = getTrashFiles().slice(); + var toRemove = []; + Object.keys(files[FILES_DATA]).forEach(function (f) { + if (rootFiles.indexOf(f) === -1 + && unsortedFiles.indexOf(f) === -1 + && trashFiles.indexOf(f) === -1) { + toRemove.push(f); + } + }); + toRemove.forEach(function (f) { + debug("Removing", f, "from filesData"); + delete files[FILES_DATA][f]; + }); + }; + + var deleteFromObject = exp.deletePathPermanently = function (path) { + var parentPath = path.slice(); + var key = parentPath.pop(); + 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) { + parentEl.splice(key, 1); + } else { + delete parentEl[key]; + } + checkDeletedFiles(); + }; + // Find an element in a object following a path, resursively var findElement = exp.findElement = function (root, pathInput) { if (!pathInput) { @@ -237,7 +268,7 @@ define([ }; // Data from filesData - var getTitle = function (href) { + var getTitle = exp.getTitle = function (href) { if (!href || !files[FILES_DATA][href]) { error("getTitle called with a non-existing href: ", href); return; @@ -272,7 +303,7 @@ define([ parentPath.pop(); pushToTrash(name, element, parentPath); if (!keepOld) { deleteFromObject(path); } - if(cb) { cb(); } + if (cb) { cb(); } }; var moveElement = exp.moveElement = function (elementPath, newParentPath, cb, keepOld) { @@ -377,24 +408,6 @@ define([ }; - var checkDeletedFiles = function () { - var rootFiles = getRootFiles().slice(); - var unsortedFiles = getUnsortedFiles().slice(); - var trashFiles = getTrashFiles().slice(); - var toRemove = []; - Object.keys(files[FILES_DATA]).forEach(function (f) { - if (rootFiles.indexOf(f) === -1 - && unsortedFiles.indexOf(f) === -1 - && trashFiles.indexOf(f) === -1) { - toRemove.push(f); - } - }); - toRemove.forEach(function (f) { - debug("Removing", f, "from filesData"); - delete files[FILES_DATA][f]; - }); - }; - // Remove an element from the trash root var removeFromTrashArray = function (element, name) { var array = files[TRASH][name]; @@ -490,15 +503,41 @@ define([ cb(); }; + + var forgetPad = exp.forgetPad = function (href) { + var rootFiles = getRootFiles().slice(); + if (rootFiles.indexOf(href) !== -1) { + removeFileFromRoot(files[ROOT], href); + } + var unsortedIdx = getUnsortedFiles().indexOf(href); + if (unsortedIdx !== -1) { + files[UNSORTED].splice(unsortedIdx, 1); + } + var key = getTitle(href); + pushToTrash(key, href, [UNSORTED]); + }; + + var addPad = exp.addPad = function (href, data) { + if (Object.keys(files[FILES_DATA]).indexOf(href) === -1) { + files[FILES_DATA][href] = data; + } + var unsortedFiles = getUnsortedFiles().slice(); + var rootFiles = getRootFiles().slice(); + //var trashFiles = getTrashFiles().slice(); + if (unsortedFiles.indexOf(href) === -1 && rootFiles.indexOf(href) === -1) { + files[UNSORTED].push(href); + } + }; + var fixFiles = exp.fixFiles = function () { // Explore the tree and check that everything is correct: // * 'root', 'trash' and 'filesData' exist and are objects - // * Folders are objects - // * Files are href - // * Trash root contains only arrays, each element of the array is an object {element:.., path:..} - // * Data (title, cdate, adte) are stored in filesData. filesData contains only href keys linking to object with title, cdate, adate. - // * Dates (adate, cdate) can be parsed/formatted - // * All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted' + // * 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. + // - Dates (adate, cdate) can be parsed/formatted + // - All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted' + // * UNSORTED: Contains only files (href), and does not contains files that are in ROOT debug("Cleaning file system..."); // Create a backup @@ -511,6 +550,7 @@ define([ if (typeof(files[TRASH]) !== "object") { debug("TRASH was not an object"); files[TRASH] = {}; } if (typeof(files[FILES_DATA]) !== "object") { debug("FILES_DATA was not an object"); files[FILES_DATA] = {}; } if (!$.isArray(files[UNSORTED])) { debug("UNSORTED was not an array"); files[UNSORTED] = []; } + var fixRoot = function (element) { for (var el in element) { if (!isFile(element[el]) && !isFolder(element[el])) { @@ -522,6 +562,7 @@ define([ } }; fixRoot(files[ROOT]); + var fixTrashRoot = function (tr) { var toClean; var addToClean = function (obj, idx) { @@ -545,19 +586,20 @@ define([ fixTrashRoot(files[TRASH]); var fixUnsorted = function (us) { + var rootFiles = getRootFiles().slice(); var toClean = []; us.forEach(function (el, idx) { - if (!isFile(el)) { + if (!isFile(el) || rootFiles.indexOf(el) !== -1) { toClean.push(idx); } }); }; fixUnsorted(files[UNSORTED]); - var rootFiles = getRootFiles().slice(); - var unsortedFiles = getUnsortedFiles().slice(); - var trashFiles = getTrashFiles().slice(); var fixFilesData = function (fd) { + var rootFiles = getRootFiles().slice(); + var unsortedFiles = getUnsortedFiles().slice(); + var trashFiles = getTrashFiles().slice(); for (var el in fd) { if (typeof(fd[el]) !== "object") { debug("An element in filesData was not an object.", fd[el]); @@ -578,12 +620,11 @@ define([ var backup = JSON.parse(localStorage.oldFileSystem); backup.push(files); localStorage.oldFileSystem = JSON.stringify(backup); - debug("Your file system was corrupted. It has been cleaned so that the file manager application can be used"); + debug("Your file system was corrupted. It has been cleaned so that the pads you visit can be stored safely"); return; } debug("File system was clean"); }; - fixFiles(); return exp; }; diff --git a/www/file/inner.html b/www/file/inner.html index fd17e8251..d7a36dd9e 100644 --- a/www/file/inner.html +++ b/www/file/inner.html @@ -3,9 +3,9 @@ + -
@@ -14,20 +14,26 @@
+ diff --git a/www/file/main.js b/www/file/main.js index 3702c5df1..bdcc88019 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -1,13 +1,17 @@ require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); define([ - '/customize/messages.js?app=pad', + '/api/config?cb=' + Math.random().toString(16).substring(2), + '/bower_components/chainpad-listmap/chainpad-listmap.js', + '/bower_components/chainpad-crypto/crypto.js', + '/bower_components/textpatcher/TextPatcher.amd.js', + '/customize/messages.js?app=file', 'json.sortify', '/common/cryptpad-common.js', '/file/fileObject.js', '/bower_components/jquery/dist/jquery.min.js', '/bower_components/bootstrap/dist/js/bootstrap.min.js', '/customize/pad.js' -], function (Messages, JSONSortify, Cryptpad, FO) { +], function (Config, Listmap, Crypto, TextPatcher, Messages, JSONSortify, Cryptpad, FO) { var module = window.MODULE = {}; var $ = window.jQuery; @@ -15,6 +19,14 @@ define([ var $iframe = $('#pad-iframe').contents(); var ifrw = $('#pad-iframe')[0].contentWindow; + //var hash = Cryptpad.getAttribute('FS_hash', cb); + var hash = localStorage.FS_hash; + if (hash) { + window.location.hash = hash; + } + + var secret = Cryptpad.getSecrets(); + var ROOT = "root"; var ROOT_NAME = Messages.fm_rootName; var UNSORTED = "unsorted"; @@ -50,130 +62,130 @@ define([ "Dir D": { "Dir E": {}, }, - "File a": "#hash_a", - "File b": "#hash_b", - "File c": "#hash_c", - "File d": "#hash_d", - "File e": "#hash_e", - "File f": "#hash_f", - "File g": "#hash_g", - "File h": "#hash_h", - "File i": "#hash_i", - "File j": "#hash_j", - "File k": "#hash_k" + "File a": "https://cryptpad.fr/slide/#hash_a", + "File b": "https://cryptpad.fr/pad/#hash_b", + "File c": "https://cryptpad.fr/pad/#hash_c", + "File d": "https://cryptpad.fr/pad/#hash_d", + "File e": "https://cryptpad.fr/pad/#hash_e", + "File f": "https://cryptpad.fr/pad/#hash_f", + "File g": "https://cryptpad.fr/pad/#hash_g", + "File h": "https://cryptpad.fr/pad/#hash_h", + "File i": "https://cryptpad.fr/pad/#hash_i", + "File j": "https://cryptpad.fr/pad/#hash_j", + "File k": "https://cryptpad.fr/pad/#hash_k" }, "Dir C": {}, "Dir B": {}, - "File A": "#hash_A" + "File A": "https://cryptpad.fr/pad/#hash_A" }, "Directory 2": { - "File B": "#hash_B", - "File C": "#hash_C" + "File B": "https://cryptpad.fr/pad/#hash_B", + "File C": "https://cryptpad.fr/pad/#hash_C" } }, - unsorted: ["#href1", "#href2", "#href3"], + unsorted: ["https://cryptpad.fr/pad/#href1", "https://cryptpad.fr/pad/#href2", "https://cryptpad.fr/pad/#href3"], filesData: { - "#hash_a": { + "https://cryptpad.fr/slide/#hash_a": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Pad A" }, - "#hash_b": { + "https://cryptpad.fr/pad/#hash_b": { ctime: "Mon Nov 07 2016 16:38:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:38:21 GMT+0100 (CET)", title: "Pad B" }, - "#hash_c": { + "https://cryptpad.fr/pad/#hash_c": { ctime: "Tue Nov 08 2016 16:34:21 GMT+0100 (CET)", atime: "Sun Nov 06 2016 12:34:21 GMT+0100 (CET)", title: "Pad C With A Very Very Very Long Title" }, - "#hash_e": { + "https://cryptpad.fr/pad/#hash_e": { ctime: "Tue Nov 08 2016 16:26:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:26:21 GMT+0100 (CET)", title: "Pad E" }, - "#hash_f": { + "https://cryptpad.fr/pad/#hash_f": { ctime: "Tue Nov 08 2016 16:22:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:22:21 GMT+0100 (CET)", title: "Pad F" }, - "#hash_g": { + "https://cryptpad.fr/pad/#hash_g": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Pad A" }, - "#hash_h": { + "https://cryptpad.fr/pad/#hash_h": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Pad A" }, - "#hash_i": { + "https://cryptpad.fr/pad/#hash_i": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Pad A" }, - "#hash_j": { + "https://cryptpad.fr/pad/#hash_j": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Pad A" }, - "#hash_k": { + "https://cryptpad.fr/pad/#hash_k": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Pad A" }, - "#hash_Z": { + "https://cryptpad.fr/pad/#hash_Z": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Code Z" }, - "#hash_A": { + "https://cryptpad.fr/pad/#hash_A": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Code A" }, - "#hash_B": { + "https://cryptpad.fr/pad/#hash_B": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Code B" }, - "#hash_C": { + "https://cryptpad.fr/pad/#hash_C": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Code C" }, - "#hash_1": { + "https://cryptpad.fr/pad/#hash_1": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Code C" }, - "#hash_2": { + "https://cryptpad.fr/pad/#hash_2": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Code C" }, - "#hash_3": { + "https://cryptpad.fr/pad/#hash_3": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Code C" }, - "#hash_4": { + "https://cryptpad.fr/pad/#hash_4": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Code C" }, - "#href1": { + "https://cryptpad.fr/pad/#href1": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Pad unsorted 1" }, - "#href2": { + "https://cryptpad.fr/pad/#href2": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Pad unsorted 2" }, - "#href3": { + "https://cryptpad.fr/pad/#href3": { ctime: "Tue Nov 08 2016 16:42:21 GMT+0100 (CET)", atime: "Tue Nov 08 2016 12:42:21 GMT+0100 (CET)", title: "Pad unsorted 3" @@ -181,7 +193,7 @@ define([ }, trash: { "File Z": [{ - element: "#hash_Z", + element: "https://cryptpad.fr/pad/#hash_Z", path: [ROOT] }] } @@ -267,6 +279,7 @@ define([ var init = function (files) { var filesOp = FO.init(files, config); + filesOp.fixFiles(); var error = filesOp.error; @@ -277,6 +290,7 @@ define([ var $tree = $iframe.find("#tree"); var $content = $iframe.find("#content"); var $contextMenu = $iframe.find("#contextMenu"); + var $contentContextMenu = $iframe.find("#contentContextMenu"); var $trashTreeContextMenu = $iframe.find("#trashTreeContextMenu"); var $trashContextMenu = $iframe.find("#trashContextMenu"); var $folderIcon = $('', {"class": "fa fa-folder folder", style:"color:#FEDE8B;text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;"}); @@ -295,6 +309,29 @@ define([ var $listIcon = $('', {"class": "fa fa-list"}); var $gridIcon = $('', {"class": "fa fa-th"}); + var appStatus = { + isReady: true, + _onReady: [], + onReady: function (handler) { + if (isReady) { + handler(); + return; + } + appStatus._onReady.push(handler); + }, + ready: function (state) { + appStatus.isReady = state; + if (state) { + appStatus._onReady.forEach(function (h) { + h(); + }); + } + } + }; + + var isReady = false; + + var removeSelected = function () { $iframe.find('.selected').removeClass("selected"); }; @@ -356,7 +393,8 @@ define([ removeInput(); } }); - $input.insertAfter($element); + $element.parent().append($input); + //$input.insertAfter($element); $input.focus(); $input.select(); // We don't want to open the file/folder when clicking on the input @@ -427,6 +465,11 @@ define([ }; var openDirectoryContextMenu = function (e) { + var $element = $(e.target).closest('li'); + $contextMenu.find('li').show(); + if ($element.hasClass('file-element')) { + $contextMenu.find('a.newfolder').parent('li').hide(); + } openContextMenu(e, $contextMenu); return false; }; @@ -447,6 +490,22 @@ define([ return false; }; + var openContentContextMenu = function (e) { + module.hideMenu(); + e.stopPropagation(); + var path = $(e.target).closest('#' + FOLDER_CONTENT_ID).data('path'); + if (!path) { return; } + var $menu = $contentContextMenu; + removeSelected(); + $menu.css({ + display: "block", + left: e.pageX, + top: e.pageY + }); + $menu.find('a').data('path', path); + return false; + }; + // filesOp.moveElements is able to move several paths to a new location, including // the Trash or the "Unsorted files" folder var moveElements = function (paths, newPath, force, cb) { @@ -555,14 +614,17 @@ define([ if (typeof(files[FILES_DATA][element]) === "undefined") { return; } + var hrefData = Cryptpad.parsePadUrl(element); var data = files[FILES_DATA][element]; + var type = Messages.type[hrefData.type] || hrefData.type; var $title = $('', {'class': 'title listElement', title: data.title}).text(data.title); + var $type = $('', {'class': 'date listElement', title: type}).text(type); var $adate = $('', {'class': 'date listElement', title: getDate(data.atime)}).text(getDate(data.atime)); var $cdate = $('', {'class': 'date listElement', title: getDate(data.ctime)}).text(getDate(data.ctime)); if (displayTitle) { $span.append($title); } - $span.append($adate).append($cdate); + $span.append($type).append($adate).append($cdate); }; var addFolderData = function (element, key, $span) { @@ -594,8 +656,10 @@ define([ var element = filesOp.findElement(files, newPath); var $icon = $fileIcon.clone(); var spanClass = 'file-element element'; + var liClass = 'file-item'; if (isFolder) { spanClass = 'folder-element element'; + liClass = 'folder-item'; $icon = filesOp.isFolderEmpty(root[key]) ? $folderEmptyIcon.clone() : $folderIcon.clone(); } var $name = $('', { 'class': spanClass }).text(key); @@ -614,6 +678,7 @@ define([ if (isTrash) { return; } openFile(root[key]); }); + $element.addClass(liClass); $element.data('path', newPath); addDragAndDropHandlers($element, newPath, isFolder, !isTrash); $element.click(function(e) { @@ -627,9 +692,9 @@ define([ } var isNewFolder = module.newFolder && filesOp.comparePath(newPath, module.newFolder); if (isNewFolder) { - window.setTimeout(function() { - displayRenameInput($name, newPath); - }, 500); + appStatus.onReady(function () { + window.setTimeout(function () { displayRenameInput($name, newPath); }, 0); + }); delete module.newFolder; } return $element; @@ -752,9 +817,10 @@ define([ var $fihElement = $('', {'class': 'element'}).appendTo($fileHeader); var $fhName = $('', {'class': 'name'}).text(Messages.fm_fileName); var $fhTitle = displayTitle ? $('', {'class': 'title '}).text(Messages.fm_title) : ''; + var $fhType = $('', {'class': 'date'}).text(Messages.table_type); var $fhAdate = $('', {'class': 'date'}).text(Messages.fm_lastAccess); var $fhCdate = $('', {'class': 'date'}).text(Messages.fm_creation); - $fihElement.append($fhName).append($fhTitle).append($fhAdate).append($fhCdate); + $fihElement.append($fhName).append($fhTitle).append($fhType).append($fhAdate).append($fhCdate); return $fileHeader; }; @@ -827,6 +893,7 @@ define([ // Display the selected directory into the content part (rightside) // NOTE: Elements in the trash are not using the same storage structure as the others var displayDirectory = module.displayDirectory = function (path) { + appStatus.ready(false); currentPath = path; $content.html(""); if (!path || path.length === 0) { @@ -852,6 +919,8 @@ define([ var $title = createTitle(path); var $dirContent = $('
', {id: FOLDER_CONTENT_ID}); + $dirContent.data('path', path); + $dirContent.contextmenu(openContentContextMenu); var mode = getViewMode(); if (mode) { $dirContent.addClass(getViewModeClass()); @@ -891,6 +960,7 @@ define([ }); } $content.append($title).append($dirContent); + appStatus.ready(true); }; var createTreeElement = function (name, $icon, path, draggable, collapsable, active) { @@ -1040,6 +1110,7 @@ define([ $contextMenu.hide(); $trashTreeContextMenu.hide(); $trashContextMenu.hide(); + $contentContextMenu.hide(); }; $contextMenu.on("click", "a", function(e) { @@ -1060,6 +1131,26 @@ define([ else if ($(this).hasClass('open')) { $element.dblclick(); } + else if ($(this).hasClass('newfolder')) { + var onCreated = function (info) { + module.newFolder = info.newPath; + module.displayDirectory(path);; + }; + filesOp.createNewFolder(path, null, onCreated); + } + module.hideMenu(); + }); + + $contentContextMenu.on('click', 'a', function (e) { + e.stopPropagation(); + var path = $(this).data('path'); + if ($(this).hasClass("newfolder")) { + var onCreated = function (info) { + module.newFolder = info.newPath; + refresh(); + }; + filesOp.createNewFolder(path, null, onCreated); + } module.hideMenu(); }); @@ -1127,8 +1218,8 @@ define([ $(ifrw).on('keyup', function (e) { pressKey(e.which, false); }); - $(ifrw).on('keypress', function (e) { - if (e.which === 0) { + $(ifrw).on('keydown', function (e) { + if (e.which === 46) { var $selected = $iframe.find('.selected'); if (!$selected.length) { return; } var paths = []; @@ -1136,7 +1227,14 @@ define([ if (!$(elmt).data('path')) { return; } paths.push($(elmt).data('path')); }); - if (filesOp.isPathInTrash(currentPath)) { + // If we are in the trash or if we are holding the "shift" key, delete permanently, + // else move to trash + if (filesOp.isPathInTrash(currentPath) || e.shiftKey) { + var todo = filesOp.removeFromTrash; + if (!filesOp.isPathInTrash(currentPath)) { + // If we are not in the trash, we just have to remove the key from root/unsorted + todo = filesOp.deletePathPermanently; + } // If we are already in the trash, delete the elements permanently var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]); if (paths.length === 1) { @@ -1146,7 +1244,7 @@ define([ } Cryptpad.confirm(msg, function(res) { paths.forEach(function(p) { - filesOp.removeFromTrash(p); + todo(p); }); refresh(); }); @@ -1156,6 +1254,67 @@ define([ } }); }; + + /* initLSOpened(); init(filesObject); + */ + + + + var listmapConfig = module.config = { + data: {}, + websocketURL: Cryptpad.getWebsocketURL(), + channel: secret.channel, + readOnly: false, + validateKey: secret.keys.validateKey || undefined, + crypto: Crypto.createEncryptor(secret.keys), + }; + + // don't initialize until the store is ready. + Cryptpad.ready(function () { + var rt = window.rt = module.rt = Listmap.create(listmapConfig); + rt.proxy.on('create', function (info) { + var realtime = module.realtime = info.realtime; + + var editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys); + window.location.hash = editHash; + //Cryptpad.setAttribute("FS_hash", editHash, cb, store); + localStorage.FS_hash = editHash; + + module.patchText = TextPatcher.create({ + realtime: realtime, + logging: true, + }); + /*Cryptpad.getPadTitle(function (err, title) { + title = document.title = title || info.channel.slice(0, 8); + + Cryptpad.setPadTitle(title, function (err, data) { + if (err) { + console.log("unable to remember pad"); + console.log(err); + return; + } + }); + });*/ + }).on('ready', function () { + if (JSON.stringify(rt.proxy) === '{}') { + var store = Cryptpad.getStore(); + store.get(Cryptpad.storageKey, function (err, s) { + rt.proxy.filesData = s; + initLSOpened(); + init(rt.proxy); + }); + return; + } + initLSOpened(); + init(rt.proxy); + }) + .on('disconnect', function () { + //setEditable(false); + Cryptpad.alert(Messages.common_connectionLost); + }); + }); + + });