require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); define([ '/customize/messages.js?app=pad', '/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-netflux/chainpad-netflux.js', '/bower_components/hyperjson/hyperjson.js', '/common/toolbar.js', '/common/cursor.js', '/bower_components/chainpad-json-validator/json-ot.js', '/common/TypingTests.js', 'json.sortify', '/bower_components/textpatcher/TextPatcher.amd.js', '/common/cryptpad-common.js', '/common/visible.js', '/common/notify.js', '/bower_components/file-saver/FileSaver.min.js', '/bower_components/diff-dom/diffDOM.js', '/bower_components/jquery/dist/jquery.min.js', '/bower_components/bootstrap/dist/js/bootstrap.min.js', '/customize/pad.js' ], function (Messages, Crypto, realtimeInput, Hyperjson, Toolbar, Cursor, JsonOT, TypingTest, JSONSortify, TextPatcher, Cryptpad, Visible, Notify) { var module = window.MODULE = {}; var $ = window.jQuery; var saveAs = window.saveAs; var $iframe = $('#pad-iframe').contents(); var ifrw = $('#pad-iframe')[0].contentWindow; var ROOT = "root"; var ROOT_NAME = "My files"; var UNSORTED = "unsorted"; var UNSORTED_NAME = "Unsorted files"; var FILES_DATA = "filesData"; var FILES_DATA_NAME = "All files"; var TRASH = "trash"; var TRASH_NAME = "Trash"; var TIME_BEFORE_RENAME = 1000; var LOCALSTORAGE_LAST = "cryptpad-file-lastOpened"; var LOCALSTORAGE_OPENED = "cryptpad-file-openedFolders"; var LOCALSTORAGE_VIEWMODE = "cryptpad-file-viewMode"; var FOLDER_CONTENT_ID = "folderContent"; var NEW_FOLDER_NAME = "New folder"; //TODO translate var DEBUG = true; var debug = DEBUG ? console.log : function() {return;}; var fixFiles = function () { // debug("Fixing file system..."); // Not implemented yet // 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 // debug("File system fixed"); }; var error = function() { fixFiles(); console.error.apply(arguments); }; var logError = console.error; var log = Cryptpad.log; var files = module.files = { root: { "Directory 1": { "Dir A": { "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" }, "Dir C": {}, "Dir B": {}, "File A": "#hash_A" }, "Directory 2": { "File B": "#hash_B", "File C": "#hash_C" } }, unsorted: ["#href1", "#href2", "#href3"], filesData: { "#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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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": { 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" } }, trash: { "File Z": [{ element: "#hash_Z", path: [ROOT] }] } }; module.defaultFiles = JSON.parse(JSON.stringify(files)); // TODO translate // TODO translate contextmenu in inner.html var getLastOpenedFolder = function () { var path; try { path = localStorage[LOCALSTORAGE_LAST] ? JSON.parse(localStorage[LOCALSTORAGE_LAST]) : [ROOT]; } catch (e) { path = [ROOT]; } return path; }; var setLastOpenedFolder = function (path) { localStorage[LOCALSTORAGE_LAST] = JSON.stringify(path); }; var initLSOpened = function () { try { var store = JSON.parse(localStorage[LOCALSTORAGE_OPENED]); if (!$.isArray(store)) { localStorage[LOCALSTORAGE_OPENED] = '[]'; } } catch (e) { localStorage[LOCALSTORAGE_OPENED] = '[]'; } }; initLSOpened(); var wasFolderOpened = function (path) { var store = JSON.parse(localStorage[LOCALSTORAGE_OPENED]); return store.indexOf(JSON.stringify(path)) !== -1; }; var setFolderOpened = function (path, opened) { var s = JSON.stringify(path); var store = JSON.parse(localStorage[LOCALSTORAGE_OPENED]); if (opened && store.indexOf(s) === -1) { store.push(s); } if (!opened) { var idx = store.indexOf(s); if (idx !== -1) { store.splice(idx, 1); } } localStorage[LOCALSTORAGE_OPENED] = JSON.stringify(store); }; var getViewModeClass = function () { var mode = localStorage[LOCALSTORAGE_VIEWMODE]; if (mode === 'list') { return 'list'; } return 'grid'; }; var getViewMode = function () { return localStorage[LOCALSTORAGE_VIEWMODE] || 'grid'; }; var setViewMode = function (mode) { if (typeof(mode) !== "string") { console.error("Incorrect view mode: ", mode); return; } localStorage[LOCALSTORAGE_VIEWMODE] = mode; }; var DEBUG = window.DEBUG = { resetLocalStorage : function () { delete localStorage[LOCALSTORAGE_OPENED]; delete localStorage[LOCALSTORAGE_LAST]; } }; var currentPath = module.currentPath = getLastOpenedFolder(); var lastSelectTime; var selectedElement; var $tree = $iframe.find("#tree"); var $content = $iframe.find("#content"); var $contextMenu = $iframe.find("#contextMenu"); var $trashTreeContextMenu = $iframe.find("#trashTreeContextMenu"); var $trashContextMenu = $iframe.find("#trashContextMenu"); var $folderIcon = $('', {"class": "fa fa-folder folder"}); var $folderEmptyIcon = $('', {"class": "fa fa-folder-o folder"}); var $folderOpenedIcon = $('', {"class": "fa fa-folder-open folder"}); var $folderOpenedEmptyIcon = $('', {"class": "fa fa-folder-open-o folder"}); var $fileIcon = $('', {"class": "fa fa-file file"}); var $upIcon = $('', {"class": "fa fa-arrow-circle-up"}); var $unsortedIcon = $('', {"class": "fa fa-files-o"}); var $trashIcon = $('', {"class": "fa fa-trash"}); var $trashEmptyIcon = $('', {"class": "fa fa-trash-o"}); var $collapseIcon = $('', {"class": "fa fa-minus-square-o expcol"}); var $expandIcon = $('', {"class": "fa fa-plus-square-o expcol"}); var $listIcon = $('', {"class": "fa fa-list"}); var $gridIcon = $('', {"class": "fa fa-th"}); var removeSelected = function () { $iframe.find('.selected').removeClass("selected"); }; var removeInput = function () { $iframe.find('li > span:hidden').show(); $iframe.find('li > input').remove(); }; var comparePath = function (a, b) { if (!a || !b || !$.isArray(a) || !$.isArray(b)) { return false; } if (a.length !== b.length) { return false; } var result = true; var i = a.length - 1; while (result && i >= 0) { result = a[i] === b[i]; i--; } return result; }; var deleteFromObject = function (path) { var parentPath = path.slice(); var key = parentPath.pop(); var parentEl = 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 now = function () { return new Date().getTime(); }; var isPathInRoot = function (path) { return path[0] && path[0] === ROOT; }; var isPathInUnsorted = function (path) { return path[0] && path[0] === UNSORTED; }; var isPathInTrash = function (path) { return path[0] && path[0] === TRASH; }; var isFile = function (element) { return typeof(element) === "string"; }; var isFolder = function (element) { return typeof(element) !== "string"; }; var isFolderEmpty = function (element) { if (typeof(element) !== "object") { return false; } return Object.keys(element).length === 0; }; var hasSubfolder = function (element, trashRoot) { if (typeof(element) !== "object") { return false; } var subfolder = 0; for (var f in element) { if (trashRoot) { if ($.isArray(element[f])) { element[f].forEach(function (el, idx) { subfolder += isFolder(el.element) ? 1 : 0; }); } } else { subfolder += isFolder(element[f]) ? 1 : 0; } } return subfolder; }; var hasFile = function (element, trashRoot) { if (typeof(element) !== "object") { return false; } var file = 0; for (var f in element) { if (trashRoot) { if ($.isArray(element[f])) { element[f].forEach(function (el, idx) { file += isFile(el.element) ? 1 : 0; }); } } else { file += isFile(element[f]) ? 1 : 0; } } return file; }; var isSubpath = function (path, parentPath) { var pathA = parentPath.slice(); var pathB = path.slice(0, pathA.length); return comparePath(pathA, pathB); }; var getAvailableName = function (parentEl, name) { if (typeof(parentEl[name]) === "undefined") { return name; } var newName = name; var i = 1; while (typeof(parentEl[newName]) !== "undefined") { newName = name + "_" + i; i++; } return newName; }; var compareFiles = function (fileA, fileB) { // Compare string, might change in the future return fileA === fileB; }; var isInTree = function (file, root) { if (isFile(root)) { return compareFiles(file, root); } var inTree = false; for (var e in root) { inTree = isInTree(file, root[e]); if (inTree) { break; } }; return inTree; }; var isInTrash = function (file) { var inTrash = false; var root = files[TRASH]; for (var e in root) { if (!$.isArray(root[e])) { error("Trash contains a non-array element"); return; } root[e].some(function (trashEl, idx) { inTrash = isInTree(file, trashEl.element); return inTrash; }); if (inTrash) { break; } } return inTrash; }; var isInTrashRoot = function (path) { return path[0] === TRASH && path.length === 4; }; var getTrashElementData = function (trashPath) { if (!isInTrashRoot) { debug("Called getTrashElementData on a element not in trash root: ", trashpath); return; } var parentPath = trashPath.slice(); parentPath.pop(); return findElement(files, parentPath); }; var getUnsortedFiles = function () { return files[UNSORTED]; }; var compareDays = function (date1, date2) { var day1 = Date.UTC(date1.getFullYear(), date1.getMonth(), date1.getDate()); var day2 = Date.UTC(date2.getFullYear(), date2.getMonth(), date2.getDate()); var ms = Math.abs(day1-day2); return Math.floor(ms/1000/60/60/24); }; var getDate = function (sDate) { var ret = sDate.toString(); try { var date = new Date(sDate); var today = new Date(); var diff = compareDays(date, today); if (diff === 0) { ret = date.toLocaleTimeString(); } else { ret = date.toLocaleDateString(); } } catch (e) { error("Unable to format that string to a date with .toLocaleString", sDate, e); } return ret; }; // Data from filesData var getTitle = function (href) { if (!files[FILES_DATA][href]) { error("getTitle called with a non-existing href: ", href); return; } return files[FILES_DATA][href].title; }; // Find an element in a object following a path, resursively var findElement = function (root, pathInput) { if (!pathInput) { error("Invalid path:\n", pathInput, "\nin root\n", root); return; } if (pathInput.length === 0) { return root; } var path = pathInput.slice(); var key = path.shift(); if (typeof root[key] === "undefined") { debug("Unable to find the key '" + key + "' in the root object provided:\n", root); return; } return findElement(root[key], path); }; var moveElement = function (elementPath, newParentPath) { if (comparePath(elementPath, newParentPath)) { return; } // Nothing to do... if (newParentPath[0] && newParentPath[0] === TRASH) { debug("Moving to trash is forbidden. You have to use the removeElement function"); return; } var element = findElement(files, elementPath); var newParent = findElement(files, newParentPath); if (isPathInUnsorted(newParentPath)) { if (isFolder(element)) { //TODO translate log("You can't move a folder to the list of unsorted pads"); return; } else { if (files[UNSORTED].indexOf(element) === -1) { files[UNSORTED].push(element); } deleteFromObject(elementPath); module.displayDirectory(currentPath); return; } } var name; if (isPathInUnsorted(elementPath)) { name = getTitle(element); } else if (elementPath.length === 4 && elementPath[0] === TRASH) { // Element from the trash root: elementPath = [TRASH, "{dirName}", 0, 'element'] name = elementPath[1]; } else { name = elementPath[elementPath.length-1]; } 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."); return; } newParent[newName] = element; deleteFromObject(elementPath); module.displayDirectory(currentPath); }; var createNewFolder = function (folderPath, name) { var parentEl = findElement(files, folderPath); var folderName = getAvailableName(parentEl, name || NEW_FOLDER_NAME); parentEl[folderName] = {}; var newPath = folderPath.slice(); newPath.push(folderName); module.newFolder = newPath; displayDirectory(currentPath); }; var pushToTrash = function (name, element, path) { var trash = findElement(files, [TRASH]); if (typeof(trash[name]) === "undefined") { trash[name] = []; } var trashArray = trash[name]; var trashElement = { element: element, path: path }; trashArray.push(trashElement); }; // Move to trash var removeElement = function (path, displayTrash, force) { if (!path || path.length < 2 || path[0] === TRASH) { debug("Calling removeElement from a wrong path: ", path); return; } var element = findElement(files, path); var key = path[path.length - 1]; var name = isPathInUnsorted(path) ? getTitle(element) : key; var andThen = function () { var parentPath = path.slice(); parentPath.pop(); pushToTrash(name, element, parentPath); deleteFromObject(path); if (displayTrash) { module.displayDirectory([TRASH]); } else { module.displayDirectory(currentPath); } }; if (force) { andThen(); return; } Cryptpad.confirm("Are you sure you want to move " + name + " to the trash?", function(res) { if (!res) { return; } andThen(); }); }; var removeFromTrashArray = function (element, name) { var array = files[TRASH][name]; if (!array || !$.isArray(array)) { return; } // Remove the element from the trash array var index = array.indexOf(element); if (index > -1) { array.splice(index, 1); } // Remove the array is empty to have a cleaner object in chainpad if (array.length === 0) { delete files[TRASH][name]; } }; var restoreTrash = function (path) { if (!path || path.length !== 4 || path[0] !== TRASH) { debug("restoreTrash was called from an element not in the trash root: ", path); return; } 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]); module.displayDirectory(currentPath); } return; } // Find the new parent element var newParentEl = findElement(files, newPath); var name = getAvailableName(newParentEl, path[1]); // Move the element newParentEl[name] = element; removeFromTrashArray(parentEl, path[1]); module.displayDirectory(currentPath); }; // Delete permanently var removeFromTrash = function (path) { if (!path || path.length < 4 || path[0] !== TRASH) { return; } // Remove the last element from the path to get the parent path and the element name var parentPath = path.slice(); var name; if (path.length === 4) { // Trash root name = path[1]; parentPath.pop(); var parentElement = findElement(files, parentPath); removeFromTrashArray(parentElement, name); module.displayDirectory(currentPath); return; } name = parentPath.pop(); var parentEl = findElement(files, parentPath); if (typeof(parentEl[name]) === "undefined") { console.error("Unable to locate the element to remove from trash: ", path); return; } delete parentEl[name]; module.displayDirectory(currentPath); }; var emptyTrash = function () { files[TRASH] = {}; module.displayDirectory(currentPath); }; var openFile = function (fileEl) { window.location.hash = fileEl; }; var renameElement = function (path, newName) { if (path.length <= 1) { logError('Renaming `root` is forbidden'); return; } if (!newName || newName.trim() === "") { return; } var isCurrentDirectory = comparePath(path, currentPath); // Copy the element path and remove the last value to have the parent path and the old name var element = findElement(files, path); var parentPath = path.slice(); var oldName = parentPath.pop(); if (oldName === newName) { return; } var parentEl = findElement(files, parentPath); if (typeof(parentEl[newName]) !== "undefined") { log('Name already used in that directory. Please choose another one.'); //TODO translate return; } parentEl[newName] = element; delete parentEl[oldName]; module.resetTree(); module.displayDirectory(currentPath); }; var displayRenameInput = function ($element, path) { if (!path || path.length < 2) { logError("Renaming a top level element (root, trash or filesData) is forbidden."); return; } $element.hide(); removeSelected(); var name = path[path.length - 1]; var $input = $('', { placeholder: name, value: name }); $input.on('keyup', function (e) { if (e.which === 13) { renameElement(path, $input.val()); removeInput(); } }); $input.insertAfter($element); $input.focus(); $input.select(); // 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 // triggering our drag/drop event handlers $input.on('dragstart dragleave drag drop', function (e) { e.preventDefault(); e.stopPropagation(); }); // Make the parent element non-draggable when selecting text in the field // since it would remove the input $input.on('mousedown', function () { $input.parents('li').attr("draggable", false); }); $input.on('mouseup', function () { $input.parents('li').attr("draggable", true); }); }; var onElementClick = function ($element, path) { // If the element was already selected, check if the rename action is available /*if ($element.hasClass("selected")) { if($content.find('.selected').length === 1 && lastSelectTime && (now() - lastSelectTime) > TIME_BEFORE_RENAME) { //$element. renameElement(path, "File renamed"); } return; }*/ removeSelected(); if (!$element.is('li')) { $element = $element.closest('li'); } if (!$element.length) { // TODO translate log("Unable to select the targetted element. If the problem persist, try to reload the page"); return; } if (!$element.hasClass("selected")) { $element.addClass("selected"); lastSelectTime = now(); } }; var openContextMenu = function (e, $menu) { module.hideMenu(); e.stopPropagation(); var path = $(e.target).closest('li').data('path'); if (!path) { return; } $menu.css({ display: "block", left: e.pageX, top: e.pageY }); // $element should be the , find it if it's not the case var $element = $(e.target).closest('li').children('span.element'); onElementClick($element); if (!$element.length) { console.error("Unable to locate the .element tag", e.target); $menu.hide(); log("Unable to open the context menu for that element. If the problem persist, try to reload the page"); return; } $menu.find('a').data('path', path); $menu.find('a').data('element', $element); return false; }; var openDirectoryContextMenu = function (e) { openContextMenu(e, $contextMenu); return false; }; var openTrashTreeContextMenu = function (e) { openContextMenu(e, $trashTreeContextMenu); return false; }; var openTrashContextMenu = function (e) { var path = $(e.target).closest('li').data('path'); if (!path) { return; } $trashContextMenu.find('li').show(); if (path.length > 4) { $trashContextMenu.find('a.restore').parent('li').hide(); } openContextMenu(e, $trashContextMenu); return false; }; var onDrag = function (ev, path) { var data = { 'path': path }; ev.dataTransfer.setData("text", JSON.stringify(data)); }; var onDrop = function (ev) { ev.preventDefault(); $iframe.find('.droppable').removeClass('droppable'); var data = ev.dataTransfer.getData("text"); var oldPath = JSON.parse(data).path; var newPath = $(ev.target).data('path') || $(ev.target).parent('li').data('path'); if (!oldPath || !newPath) { return; } // Call removeElement when trying to move something into the trash if (newPath[0] === TRASH) { removeElement(oldPath, true); return; } moveElement(oldPath, newPath); }; var addDragAndDropHandlers = function ($element, path, isFolder, droppable) { // "dragenter" is fired for an element and all its children // "dragleave" may be fired when entering a child // --> We store the number of enter/leave and the element entered and we remove the // highlighting only when we have left everything var counter = 0; var dragenterList = []; $element.on('dragstart', function (e) { e.stopPropagation(); counter = 0; dragenterList = []; onDrag(e.originalEvent, path); }); // Add drop handlers if we are not in the trash and if the element is a folder if (!droppable || !isFolder) { return; } $element.on('dragover', function (e) { e.preventDefault(); }); $element.on('drop', function (e) { onDrop(e.originalEvent); }); $element.on('dragenter', function (e) { e.preventDefault(); e.stopPropagation(); if (dragenterList.indexOf(e.target) !== -1) { return; } dragenterList.push(e.target); counter++; $element.addClass('droppable'); }); $element.on('dragleave', function (e) { e.preventDefault(); e.stopPropagation(); var idx = dragenterList.indexOf(e.target); dragenterList.splice(idx, 1); counter--; if (counter <= 0) { $element.removeClass('droppable'); } }); }; var addFileData = function (element, key, $span, displayTitle) { if (!isFile(element)) { return; } // The element with the class '.name' is underlined when the 'li' is hovered var $name = $('', {'class': 'name', title: key}).text(key); $span.html(''); $span.append($name); if (typeof(files[FILES_DATA][element]) === "undefined") { return; } var data = files[FILES_DATA][element]; var $title = $('', {'class': 'title listElement', title: data.title}).text(data.title); 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); }; var addFolderData = function (element, key, $span) { if (!element || !isFolder(element)) { return; } $span.html(''); // The element with the class '.name' is underlined when the 'li' is hovered var sf = hasSubfolder(element); var files = hasFile(element); var $name = $('', {'class': 'name', title: key}).text(key); var $subfolders = $('', {'class': 'folders listElement', title: sf}).text(sf); var $files = $('', {'class': 'files listElement', title: files}).text(files); $span.append($name).append($subfolders).append($files); }; var createElement = function (path, elPath, root, isFolder) { // Forbid drag&drop inside the trash var isTrash = path[0] === TRASH; var newPath = path.slice(); var key; if (isTrash && $.isArray(elPath)) { key = elPath[0]; elPath.forEach(function (k) { newPath.push(k); }); } else { key = elPath; newPath.push(key); } var element = findElement(files, newPath); var $icon = $fileIcon.clone(); var spanClass = 'file-element element'; if (isFolder) { spanClass = 'folder-element element'; $icon = isFolderEmpty(root[key]) ? $folderEmptyIcon.clone() : $folderIcon.clone(); } var $name = $('', { 'class': spanClass }).text(key); if (isFolder) { addFolderData(element, key, $name); } else { addFileData(element, key, $name, true); } var $element = $('
  • ', { draggable: true }).append($icon).append($name).dblclick(function () { if (isFolder) { module.displayDirectory(newPath); return; } if (isTrash) { return; } openFile(root[key]); }); $element.data('path', newPath); addDragAndDropHandlers($element, newPath, isFolder, !isTrash); $element.click(function(e) { e.stopPropagation(); onElementClick($element, newPath); }); if (!isTrash) { $element.contextmenu(openDirectoryContextMenu); } else { $element.contextmenu(openTrashContextMenu); } var isNewFolder = module.newFolder && comparePath(newPath, module.newFolder); if (isNewFolder) { window.setTimeout(function() { displayRenameInput($name, newPath); }, 500); delete module.newFolder; }; return $element; }; // Display the full path in the title when displaying a directory from the trash var getTrashTitle = function (path) { if (!path[0] || path[0] !== TRASH) { return; } var title = TRASH_NAME; for (var i=1; i').text(name); if (path.length > 1) { var $parentFolder = $upIcon.clone().addClass("parentFolder") .click(function() { var newPath = path.slice(); newPath.pop(); if (isTrash && path.length === 4) { // path = [TRASH, "{DirName}", 0, 'element'] // --> parent is TRASH newPath = [TRASH]; } module.displayDirectory(newPath); }); $title.append($parentFolder); } return $title; }; var createViewModeButton = function () { var $block = $('
    ', { 'class': 'btn-group topButtonContainer changeViewModeContainer' }); var $listButton = $('