Add "restore" from trash, improve drag and drop and update css

pull/1/head
yflory 8 years ago
parent 8e1bff706b
commit c9cd06514c

@ -41,6 +41,7 @@
"diff-dom": "#gh-pages", "diff-dom": "#gh-pages",
"alertifyjs": "^1.0.11", "alertifyjs": "^1.0.11",
"spin.js": "^2.3.2", "spin.js": "^2.3.2",
"scrypt-async": "^1.2.0" "scrypt-async": "^1.2.0",
"bootstrap": "^3.3.7"
} }
} }

@ -0,0 +1,163 @@
/* PAGE */
html, body {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
position: relative;
}
.fa {
width: 17px;
font-family: FontAwesome;
}
ul {
list-style: none;
padding-left: 10px;
}
li {
padding: 0px 5px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
li > span.element:hover {
text-decoration: underline;
}
.folder, .file {
margin-right: 5px;
}
.contextMenu {
display: none;
position: absolute;
}
.droppable {
background-color: #FE9A2E;
color: #222;
}
.selected {
border: 1px dotted #bbb;
background: #666;
color: #eee;
}
/* TREE */
#tree {
position:absolute;
top: 0;
left: 0;
bottom: 0;
right: 70%;
border: 2px solid blue;
box-sizing: border-box;
background: white;
}
#tree li {
cursor: auto;
}
#tree span.element {
cursor: pointer;
}
#tree .active {
text-decoration: underline;
}
#tree #trashTree {
margin-top: 2em;
}
#tree .fa.expcol {
margin-left: -10px;
}
#tree .non-collapsable {
padding-left: 12px;
}
/* Tree lines */
#tree ul {
margin: 0px 0px 0px 10px;
list-style: none;
}
#tree ul li {
position: relative;
}
#tree ul li:before {
position: absolute;
left: -15px;
top: 0px;
content: '';
display: block;
border-left: 1px solid #ddd;
height: 0.75em;
border-bottom: 1px solid #ddd;
width: 10px;
}
#tree ul li.non-collapsable:before {
width: 27px;
}
#tree ul li:after {
position: absolute;
left: -15px;
bottom: -7px;
content: '';
display: block;
border-left: 1px solid #ddd;
height: 100%;
}
#tree ul li.root {
margin: 0px 0px 0px -10px;
}
#tree ul li.root:before {
display: none;
}
#tree ul li.root:after {
display: none;
}
#tree ul li:last-child:after {
display: none;
}
/* CONTENT */
#content {
position: absolute;
top: 0;
left: 30%;
bottom: 0;
right: 0;
border: 2px solid green;
box-sizing: border-box;
background: #eee;
}
.parentFolder {
cursor: pointer;
margin-left: 10px;
}
.parentFolder:hover {
text-decoration: underline;
}
#folderContent {
display: inline-block;
}

@ -12,13 +12,24 @@
</div> </div>
<div id="content"> <div id="content">
</div> </div>
<div id="contextMenu" class="dropdown clearfix"> <div id="contextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;"> <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="open">Open</a></li> <li><a tabindex="-1" href="#" class="open">Open</a></li>
<li><a tabindex="-1" href="#" class="rename">Rename</a></li> <li><a tabindex="-1" href="#" class="rename">Rename</a></li>
<li><a tabindex="-1" href="#" class="delete">Delete</a></li> <li><a tabindex="-1" href="#" class="delete">Delete</a></li>
</ul> </ul>
</div> </div>
<div id="trashTreeContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="empty">Empty the trash</a></li>
</ul>
</div>
<div id="trashContextMenu" class="contextMenu dropdown clearfix" oncontextmenu="return false;">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" href="#" class="remove">Delete permanently</a></li>
<li><a tabindex="-1" href="#" class="restore">Restore</a></li>
</ul>
</div>
</body> </body>
</html> </html>

@ -21,7 +21,7 @@ define([
], function (Messages, Crypto, realtimeInput, Hyperjson, ], function (Messages, Crypto, realtimeInput, Hyperjson,
Toolbar, Cursor, JsonOT, TypingTest, JSONSortify, TextPatcher, Cryptpad, Toolbar, Cursor, JsonOT, TypingTest, JSONSortify, TextPatcher, Cryptpad,
Visible, Notify) { Visible, Notify) {
var module = {}; var module = window.MODULE = {};
var $ = window.jQuery; var $ = window.jQuery;
var saveAs = window.saveAs; var saveAs = window.saveAs;
@ -43,57 +43,46 @@ define([
} }
}, },
trash: { trash: {
"File Z": "#hash_Z" "File Z": [{
element: "#hash_Z",
path: [ROOT]
}]
} }
}; };
var currentPath = module.currentPath = ['root'];
var lastSelectTime;
var selectedElement;
// TODO translate // TODO translate
// TODO translate contextmenu in inner.html // TODO translate contextmenu in inner.html
var ROOT = "root";
var ROOT_NAME = "My files"; var ROOT_NAME = "My files";
var TRASH = "trash";
var TRASH_NAME = "Trash"; var TRASH_NAME = "Trash";
var TIME_BEFORE_RENAME = 1000; var TIME_BEFORE_RENAME = 1000;
var currentPath = module.currentPath = [ROOT];
var lastSelectTime;
var selectedElement;
var $tree = $iframe.find("#tree"); var $tree = $iframe.find("#tree");
var $content = $iframe.find("#content"); var $content = $iframe.find("#content");
var $contextMenu = $iframe.find("#contextMenu"); var $contextMenu = $iframe.find("#contextMenu");
var $folderIcon = $('<span>', { var $trashTreeContextMenu = $iframe.find("#trashTreeContextMenu");
"class": "fa fa-folder folder", var $trashContextMenu = $iframe.find("#trashContextMenu");
style: "font-family: FontAwesome" var $folderIcon = $('<span>', {"class": "fa fa-folder folder"});
}); var $folderEmptyIcon = $('<span>', {"class": "fa fa-folder-o folder"});
var $folderEmptyIcon = $('<span>', { var $folderOpenedIcon = $('<span>', {"class": "fa fa-folder-open folder"});
"class": "fa fa-folder-o folder", var $folderOpenedEmptyIcon = $('<span>', {"class": "fa fa-folder-open-o folder"});
style: "font-family: FontAwesome" var $fileIcon = $('<span>', {"class": "fa fa-file file"});
}); var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
var $folderOpenedIcon = $('<span>', { var $trashIcon = $('<span>', {"class": "fa fa-trash"});
"class": "fa fa-folder-open folder", var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
style: "font-family: FontAwesome" var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o expcol"});
}); var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o expcol"});
var $folderOpenedEmptyIcon = $('<span>', {
"class": "fa fa-folder-open-o folder",
style: "font-family: FontAwesome"
});
var $fileIcon = $('<span>', {
"class": "fa fa-file file",
style: "font-family: FontAwesome"
});
var $upIcon = $('<span>', {
"class": "fa fa-arrow-circle-up",
style: "font-family: FontAwesome"
});
var $trashIcon = $('<span>', {
"class": "fa fa-trash",
style: "font-family: FontAwesome"
});
var removeSelected = function () { var removeSelected = function () {
$content.find('.selected').removeClass("selected"); $iframe.find('.selected').removeClass("selected");
}; };
var removeInput = function () { var removeInput = function () {
$content.find('li > span:hidden').show(); $iframe.find('li > span:hidden').show();
$content.find('li > input').remove(); $iframe.find('li > input').remove();
}; };
var comparePath = function (a, b) { var comparePath = function (a, b) {
@ -112,6 +101,14 @@ define([
return new Date().getTime(); return new Date().getTime();
}; };
var isFile = function (element) {
return typeof(element) === "string";
};
var isFolder = function (element) {
return typeof(element) !== "string";
};
// Find an element in a object following a path, resursively // Find an element in a object following a path, resursively
var findElement = function (root, pathInput) { var findElement = function (root, pathInput) {
if (!pathInput) { if (!pathInput) {
@ -132,27 +129,141 @@ define([
var moveElement = function (elementPath, newParentPath) { var moveElement = function (elementPath, newParentPath) {
if (comparePath(elementPath, newParentPath)) { return; } // Nothing to do... if (comparePath(elementPath, newParentPath)) { return; } // Nothing to do...
if (newParentPath[0] && newParentPath[0] === TRASH) {
// TODO
console.error("Moving to trash is forbidden. You have to use the removeElement function");
return;
}
var element = findElement(files, elementPath); var element = findElement(files, elementPath);
var parentPath = elementPath.slice();
var name = parentPath.pop();
var parentEl = findElement(files, parentPath);
var newParent = findElement(files, newParentPath); var newParent = findElement(files, newParentPath);
if (typeof(newParent[name]) !== "undefined") { var parentPath, name, newName;
if (elementPath.length === 4 && elementPath[0] === TRASH) {
// Element from the trash root:
// elementPath = [TRASH, "{dirName}", 0, 'element']
parentPath = [TRASH];
name = elementPath[1];
// Rename automatically if the name is already taken since it is impossible to rename
// a file or a folder directly from the trash
newName = getAvailableName(newParent, name);
} else {
parentPath = elementPath.slice();
name = parentPath.pop();
newName = name;
}
var parentEl = findElement(files, parentPath);
if (typeof(newParent[newName]) !== "undefined") {
console.error("A file with the same name already exist at the new location"); console.error("A file with the same name already exist at the new location");
//TODO //TODO
return; return;
} }
newParent[name] = element; newParent[newName] = element;
delete parentEl[name]; delete parentEl[name];
displayDirectory(newParentPath); displayDirectory(newParentPath);
}; };
var removeElement = function (path) { // Move to trash
moveElement(path, ['trash']); var removeElement = function (path, displayTrash) {
if (!path || path.length < 2 || path[0] !== ROOT) { return; }
var name = path[path.length - 1];
var andThen = function () {
var element = findElement(files, path);
var parentPath = path.slice();
var name = parentPath.pop();
var parentEl = findElement(files, parentPath);
var trash = findElement(files, [TRASH]);
if (typeof(trash[name]) === "undefined") {
trash[name] = [];
}
var trashArray = trash[name];
var trashElement = {
element: element,
path: parentPath
};
trashArray.push(trashElement);
delete parentEl[name];
if (displayTrash) {
displayDirectory([TRASH]);
} else {
displayDirectory(currentPath);
}
};
Cryptpad.confirm("Are you sure you want to move " + name + " to the trash?", function(res) {
if (!res) { return; }
andThen();
});
};
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 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) { return; }
var element = findElement(files, path);
var parentPath = path.slice();
parentPath.pop();
var parentEl = findElement(files, parentPath);
var newPath = parentEl.path;
var newParentEl = findElement(files, newPath);
var name = getAvailableName(newParentEl, path[1]);
newParentEl[name] = element;
removeFromTrashArray(parentEl, path[1]);
displayDirectory(currentPath);
};
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
console.log(path);
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);
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];
displayDirectory(currentPath);
};
var emptyTrash = function () {
files.trash = {};
displayDirectory(currentPath);
}; };
var onDrag = function (ev, path) { var onDrag = function (ev, path) {
console.log("dragging", path);
var data = { var data = {
'path': path 'path': path
}; };
@ -164,14 +275,22 @@ define([
var data = ev.dataTransfer.getData("data"); var data = ev.dataTransfer.getData("data");
var oldPath = JSON.parse(data).path; var oldPath = JSON.parse(data).path;
var newPath = $(ev.target).data('path') || $(ev.target).parent('li').data('path'); var newPath = $(ev.target).data('path') || $(ev.target).parent('li').data('path');
console.log("dropping ", oldPath, " to ", newPath);
if (!oldPath || !newPath) { return; } 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); moveElement(oldPath, newPath);
}; };
var openFile = function (fileEl) {
window.location.hash = fileEl;
};
var renameElement = function (path, newName) { var renameElement = function (path, newName) {
if (path.length <= 1) { if (path.length <= 1) {
console.error('Renaming "root" is forbidden'); console.error('Renaming `root` is forbidden');
//TODO //TODO
return; return;
} }
@ -216,10 +335,25 @@ define([
$input.insertAfter($element); $input.insertAfter($element);
$input.focus(); $input.focus();
$input.select(); $input.select();
$input.click(function (e) { // We don't want to open the file/folder when clicking on the input
$input.on('click dblclick', function (e) {
removeSelected(); removeSelected();
e.stopPropagation(); 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) { var onElementClick = function ($element, path) {
@ -234,7 +368,7 @@ define([
return; return;
}*/ }*/
removeSelected(); removeSelected();
if ($element.not('li')) { if (!$element.is('li')) {
$element = $element.parent('li'); $element = $element.parent('li');
} }
if (!$element.length) { return ; } //TODO error if (!$element.length) { return ; } //TODO error
@ -245,6 +379,7 @@ define([
}; };
var openContextMenu = function (e) { var openContextMenu = function (e) {
hideMenu();
onElementClick($(e.target)); onElementClick($(e.target));
e.stopPropagation(); e.stopPropagation();
var path = $(e.target).data('path') || $(e.target).parent('li').data('path'); var path = $(e.target).data('path') || $(e.target).parent('li').data('path');
@ -254,116 +389,264 @@ define([
left: e.pageX, left: e.pageX,
top: e.pageY top: e.pageY
}); });
var $element = $(e.target);
// $element should be the <span class="element">, find it if it's not the case
if ($element.is('li')) {
$element = $element.children('span.element');
} else if (!$element.is('.element')) {
$element = $element.parent('li').children('span.element');
}
if (!$element.length) {
console.error("Unable to locate the .element tag", e.target);
$contextMenu.hide();
// TODO error
return;
}
$contextMenu.find('a').data('path', path); $contextMenu.find('a').data('path', path);
$contextMenu.find('a').data('element', $(e.target)); $contextMenu.find('a').data('element', $element);
return false; return false;
}; };
var displayDirectory = function (path) { var openTrashTreeContextMenu = function (e) {
currentPath = path; hideMenu();
module.resetTree(); onElementClick($(e.target));
$content.html(""); e.stopPropagation();
if (!path || path.length === 0) { var path = $(e.target).data('path') || $(e.target).parent('li').data('path');
path = ['root']; if (!path) { return; }
$trashTreeContextMenu.css({
display: "block",
left: e.pageX,
top: e.pageY
});
$trashTreeContextMenu.find('a').data('path', path);
$trashTreeContextMenu.find('a').data('element', $(e.target));
return false;
};
var openTrashContextMenu = function (e) {
hideMenu();
onElementClick($(e.target));
e.stopPropagation();
var path = $(e.target).data('path') || $(e.target).parent('li').data('path');
if (!path) { return; }
$trashContextMenu.find('li').show();
if (path.length > 4) {
$trashContextMenu.find('a.restore').parent('li').hide();
} }
var root = findElement(files, path); $trashContextMenu.css({
if (typeof(root) === "undefined") { display: "block",
// TODO translate left: e.pageX,
// TODO error top: e.pageY
$content.html("Unable to locate the selected directory..."); });
return; $trashContextMenu.find('a').data('path', path);
$trashContextMenu.find('a').data('element', $(e.target));
return false;
};
var addDragAndDropHandlers = function ($element, path, isFolder, droppable) {
$element.on('dragstart', function (e) {
e.stopPropagation();
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);
});
var counter = 0;
$element.on('dragenter', function (e) {
e.preventDefault();
e.stopPropagation();
counter++;
$element.addClass('droppable');
});
$element.on('dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
counter--;
if (counter === 0) {
$element.removeClass('droppable');
} }
});
};
var createElement = function (path, elPath, root, isFolder) {
// Forbid drag&drop inside the trash // Forbid drag&drop inside the trash
var droppable = root[0] !== "trash"; var isTrash = path[0] === TRASH;
var newPath = path.slice();
if (isTrash && $.isArray(elPath)) {
key = elPath[0];
elPath.forEach(function (k) { newPath.push(k); });
} else {
key = elPath;
newPath.push(key);
}
var $icon = $fileIcon.clone();
var spanClass = 'file-element element';
if (isFolder) {
spanClass = 'folder-element element';
$icon = Object.keys(root[key]).length === 0 ? $folderEmptyIcon.clone() : $folderIcon.clone();
}
var $name = $('<span>', { 'class': spanClass }).text(key);
var $element = $('<li>', {
draggable: true
}).append($icon).append($name).dblclick(function () {
if (isFolder) {
displayDirectory(newPath);
return;
}
// Prevent users from opening files from the trash TODO ??
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(openContextMenu);
} else {
$element.contextmenu(openTrashContextMenu);
}
return $element;
};
// Display title and "Up" icon // 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<path.length; i++) {
if (i === 3 && path[i] === 'element') {}
else if (i === 2 && parseInt(path[i]) === path[i]) {
if (path[i] !== 0) {
title += " [" + path[i] + "]";
}
} else {
title += " / " + path[i];
}
}
return title;
}
var createTitle = function (path) {
var isTrash = path[0] === TRASH;
// Create title and "Up" icon
var name = path[path.length - 1]; var name = path[path.length - 1];
if (name === "root" && path.length === 1) { name = ROOT_NAME; } if (name === ROOT && path.length === 1) { name = ROOT_NAME; }
else if (name === "trash" && path.length === 1) { name = TRASH_NAME; } else if (name === TRASH && path.length === 1) { name = TRASH_NAME; }
else if (path.length > 1 && path[0] === TRASH) { name = getTrashTitle(path); }
var $title = $('<h1>').text(name); var $title = $('<h1>').text(name);
if (path.length > 1) { if (path.length > 1) {
var $parentFolder = $upIcon.clone().addClass("parentFolder") var $parentFolder = $upIcon.clone().addClass("parentFolder")
.click(function() { .click(function() {
var newPath = path.slice(); var newPath = path.slice();
newPath.pop(); newPath.pop();
var name = newPath[newPath.length -1]; if (isTrash && path.length === 4) {
if (name === "root" && newPath.length === 1) { name = ROOT_NAME; } // path = [TRASH, "{DirName}", 0, 'element']
// --> parent is TRASH
newPath = [TRASH];
}
displayDirectory(newPath); displayDirectory(newPath);
}); });
$title.append($parentFolder); $title.append($parentFolder);
} }
return $title;
};
// 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 = function (path) {
currentPath = path;
module.resetTree();
$content.html("");
if (!path || path.length === 0) {
path = [ROOT];
}
var isTrashRoot = comparePath(path, [TRASH]);
var root = findElement(files, path);
if (typeof(root) === "undefined") {
// TODO translate
// TODO error
// What to do? display the root element ? [ROOT] or [TRASH] depending on where we were?
console.log("Unable to locate the selected directory: ", path);
var parentPath = path.slice();
parentPath.pop();
displayDirectory(parentPath);
return;
}
var $title = createTitle(path);
var $dirContent = $('<div>', {id: "folderContent"}); var $dirContent = $('<div>', {id: "folderContent"});
var $list = $('<ul>').appendTo($dirContent); var $list = $('<ul>').appendTo($dirContent);
// display sub directories if (isTrashRoot) {
// Elements in the trash are JS arrays (several elements can have the same name)
Object.keys(root).forEach(function (key) { Object.keys(root).forEach(function (key) {
if (typeof(root[key]) === "string") { return; } if (!$.isArray(root[key])) {
var newPath = path.slice(); console.error("Trash element has a wrong type", root[key]);
newPath.push(key); return;
var $icon = Object.keys(root[key]).length === 0 ? $folderEmptyIcon.clone() : $folderIcon.clone();
var $name = $('<span>', { 'class': 'folder-element element' }).text(key);
var $element = $('<li>', {
draggable: true
}).append($icon).append($name).dblclick(function () {
displayDirectory(newPath);
});
$element.data('path', newPath);
$element.on('dragstart', function (e) {
onDrag(e.originalEvent, newPath);
});
if (droppable) {
$element.on('dragover', function (e) {
e.preventDefault();
});
$element.on('drop', function (e) {
onDrop(e.originalEvent);
});
} }
$element.click(function(e) { // display sub directories
e.stopPropagation(); root[key].forEach(function (el, idx) {
onElementClick($element, newPath); if (isFile(el.element)) { return; }
}); var spath = [key, idx, 'element'];
$element.contextmenu(openContextMenu); var $element = createElement(path, spath, root, true);
$element.appendTo($list); $element.appendTo($list);
}); });
// display files // display files
Object.keys(root).forEach(function (key) { root[key].forEach(function (el, idx) {
if (typeof(root[key]) !== "string") { return; } if (isFolder(el.element)) { return; }
var newPath = path.slice(); var spath = [key, idx, 'element'];
newPath.push(key); var $element = createElement(path, spath, root, false);
var $name = $('<span>', { 'class': 'file-element element' }).text(key); $element.appendTo($list);
var $element = $('<li>', {
draggable: true
}).append($fileIcon.clone()).append($name).dblclick(function () {
window.location.hash = root[key];
}); });
$element.data('path', newPath);
$element.on('dragstart', function (e) {
console.log(e.target);
onDrag(e.originalEvent, newPath);
}); });
$element.click(function(e) { } else {
e.stopPropagation(); // display sub directories
onElementClick($element, newPath); Object.keys(root).forEach(function (key) {
if (isFile(root[key])) { return; }
var $element = createElement(path, key, root, true);
$element.appendTo($list);
}); });
$element.contextmenu(openContextMenu); // display files
Object.keys(root).forEach(function (key) {
if (isFolder(root[key])) { return; }
var $element = createElement(path, key, root, false);
$element.appendTo($list); $element.appendTo($list);
}); });
}
$content.append($title).append($dirContent); $content.append($title).append($dirContent);
}; };
// TODO: add + and - in the tree (collapse), and link elements with lines var createTreeElement = function (name, $icon, path, draggable, collapsable, active) {
// Cf: https://codepen.io/khoama/pen/hpljA var $name = $('<span>', { 'class': 'folder-element element' }).text(name)
var createTreeElement = function (name, $icon, path, draggable) {
var $name = $('<span>', { 'class': 'folder-element' }).text(name).prepend($icon)
.click(function () { .click(function () {
displayDirectory(path); displayDirectory(path);
}); });
var $collapse;
if (collapsable) {
$collapse = $collapseIcon.clone();
}
var $element = $('<li>', { var $element = $('<li>', {
draggable: draggable draggable: draggable
}).append($name); }).append($collapse).append($icon).append($name);
if (!collapsable) {
$element.addClass('non-collapsable');
}
$element.data('path', path); $element.data('path', path);
addDragAndDropHandlers($element, path, true, true);
$element.on('dragstart', function (e) { $element.on('dragstart', function (e) {
e.stopPropagation(); e.stopPropagation();
onDrag(e.originalEvent, path); onDrag(e.originalEvent, path);
@ -374,20 +657,38 @@ define([
$element.on('drop', function (e) { $element.on('drop', function (e) {
onDrop(e.originalEvent); onDrop(e.originalEvent);
}); });
var counter = 0;
$element.on('dragenter', function (e) {
e.preventDefault();
e.stopPropagation();
counter++;
$element.addClass('droppable');
});
$element.on('dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
counter--;
if (counter === 0) {
$element.removeClass('droppable');
}
});
if (active) { $name.addClass('active'); }
return $element; return $element;
}; };
var createTree = function ($container, path) { var createTree = function ($container, path) {
var root = findElement(files, path); var root = findElement(files, path);
if (Object.keys(root).length === 0) { return; } if (Object.keys(root).length === 0) { return; }
// Display the root elemnt in the tree // Display the root element in the tree
var displayingRoot = comparePath(['root'], path); var displayingRoot = comparePath([ROOT], path);
if (displayingRoot) { if (displayingRoot) {
var isRootOpened = comparePath(['root'], currentPath); var isRootOpened = comparePath([ROOT], currentPath);
var $rootIcon = Object.keys(files['root']).length === 0 ? var $rootIcon = Object.keys(files[ROOT]).length === 0 ?
(isRootOpened ? $folderOpenedEmptyIcon : $folderEmptyIcon) : (isRootOpened ? $folderOpenedEmptyIcon : $folderEmptyIcon) :
(isRootOpened ? $folderOpenedIcon : $folderIcon); (isRootOpened ? $folderOpenedIcon : $folderIcon);
var $rootElement = createTreeElement(ROOT_NAME, $rootIcon.clone(), ['root'], false); var $rootElement = createTreeElement(ROOT_NAME, $rootIcon.clone(), [ROOT], false, false, isRootOpened);
$rootElement.addClass('root');
var $root = $('<ul>').append($rootElement).appendTo($container); var $root = $('<ul>').append($rootElement).appendTo($container);
$container = $rootElement; $container = $rootElement;
} }
@ -396,49 +697,55 @@ define([
var $list = $('<ul>').appendTo($container); var $list = $('<ul>').appendTo($container);
Object.keys(root).forEach(function (key) { Object.keys(root).forEach(function (key) {
// Do not display files in the menu // Do not display files in the menu
if (typeof(root[key]) === "string") { return; } if (isFile(root[key])) { return; }
var newPath = path.slice(); var newPath = path.slice();
newPath.push(key); newPath.push(key);
var isCurrentFolder = comparePath(newPath, currentPath); var isCurrentFolder = comparePath(newPath, currentPath);
var $icon = Object.keys(root[key]).length === 0 ? var isEmpty = Object.keys(root[key]).length === 0;
var $icon = isEmpty ?
(isCurrentFolder ? $folderOpenedEmptyIcon : $folderEmptyIcon) : (isCurrentFolder ? $folderOpenedEmptyIcon : $folderEmptyIcon) :
(isCurrentFolder ? $folderOpenedIcon : $folderIcon); (isCurrentFolder ? $folderOpenedIcon : $folderIcon);
var $element = createTreeElement(key, $icon.clone(), newPath, true); var $element = createTreeElement(key, $icon.clone(), newPath, true, !isEmpty, isCurrentFolder);
$element.appendTo($list); $element.appendTo($list);
$element.contextmenu(openContextMenu);
createTree($element, newPath); createTree($element, newPath);
}); });
}; };
var createTrash = function ($container, path) { var createTrash = function ($container, path) {
var $icon = Object.keys(files.trash).length === 0 ? $trashEmptyIcon.clone() : $trashIcon.clone();
var isOpened = comparePath(path, currentPath);
var $trash = $('<span>', { var $trash = $('<span>', {
'class': 'tree-trash' 'class': 'tree-trash element'
}).text(TRASH_NAME).prepend($trashIcon.clone()) }).text(TRASH_NAME).prepend($icon)
.click(function () { .click(function () {
displayDirectory(path); displayDirectory(path);
}); });
$trash.data('path', ['trash']);
var $trashElement = $('<li>').append($trash); var $trashElement = $('<li>').append($trash);
$trashElement.on('dragover', function (e) { $trashElement.addClass('root');
e.preventDefault(); $trashElement.data('path', [TRASH]);
}); addDragAndDropHandlers($trashElement, path, true, true);
$trashElement.on('drop', function (e) { $trashElement.contextmenu(openTrashTreeContextMenu);
onDrop(e.originalEvent); if (isOpened) { $trash.addClass('active'); }
});
var $trashList = $('<ul>').append($trashElement); var $trashList = $('<ul>', { id: 'trashTree' }).append($trashElement);
$container.append($trashList); $container.append($trashList);
}; };
var resetTree = module.resetTree = function () { var resetTree = module.resetTree = function () {
$tree.html(''); $tree.html('');
createTree($tree, ['root']); createTree($tree, [ROOT]);
createTrash($tree, ['trash']); createTrash($tree, [TRASH]);
}; };
displayDirectory(currentPath); displayDirectory(currentPath);
//resetTree(); //already called by displayDirectory //resetTree(); //already called by displayDirectory
var hideMenu = function () { var hideMenu = function () {
$contextMenu.hide(); $contextMenu.hide();
$trashTreeContextMenu.hide();
$trashContextMenu.hide();
}; };
$contextMenu.on("click", "a", function(e) { $contextMenu.on("click", "a", function(e) {
e.stopPropagation(); e.stopPropagation();
var path = $(this).data('path'); var path = $(this).data('path');
@ -448,16 +755,51 @@ define([
displayRenameInput($element, path); displayRenameInput($element, path);
} }
else if($(this).hasClass("delete")) { else if($(this).hasClass("delete")) {
removeElement(path, false);
}
else if ($(this).hasClass('open')) {
$element.dblclick();
}
hideMenu();
});
$trashTreeContextMenu.on('click', 'a', function (e) {
e.stopPropagation();
var path = $(this).data('path');
var $element = $(this).data('element');
if (!$element || !comparePath(path, [TRASH])) { return; } // TODO: error
if ($(this).hasClass("empty")) {
// TODO translate
Cryptpad.confirm("Are you sure you want to empty the trash?", function(res) {
if (!res) { return; }
emptyTrash();
});
}
hideMenu();
});
$trashContextMenu.on('click', 'a', function (e) {
e.stopPropagation();
var path = $(this).data('path');
var $element = $(this).data('element');
if (!$element || !path || path.length < 2) { return; } // TODO: error
if ($(this).hasClass("remove")) {
var name = path[path.length - 1]; var name = path[path.length - 1];
if (path.length === 4) { name = path[1]; }
// TODO translate // TODO translate
Cryptpad.confirm("Are you sure you want to move " + name + " to the trash?", function(res) { Cryptpad.confirm("Are you sure you want to remove " + name + " from the trash permanently?", function(res) {
if (!res) { return; } if (!res) { return; }
console.log("Removing ", path); removeFromTrash(path);
removeElement(path);
}); });
} }
else if ($(this).hasClass('open')) { else if ($(this).hasClass("restore")) {
$element.dblclick(); var name = path[path.length - 1];
if (path.length === 4) { name = path[1]; }
// TODO translate
Cryptpad.confirm("Are you sure you want to restore " + name + " to its previous location?", function(res) {
if (!res) { return; }
restoreTrash(path);
});
} }
hideMenu(); hideMenu();
}); });
@ -468,47 +810,11 @@ define([
removeInput(e); removeInput(e);
hideMenu(e); hideMenu(e);
}); });
$(ifrw).on('drag drop', function (e) {
/* var displayDirectory = function (name, root, path) { removeInput(e);
$content.html(""); hideMenu(e);
var $title = $('<h1>').text(name);
var $dirContent = $('<div>', {id: "folderContent"});
var $list = $('<ul>').appendTo($dirContent);
// display sub directories
Object.keys(root).forEach(function (key) {
if (typeof(root[key]) === "string") { return; }
var $name = $('<span>', { 'class': 'folder-element' }).text(key).prepend($folderIcon.clone());
var $element = $('<li>').append($name).dblclick(function () {
displayDirectory(key, root[key]);
});
$element.appendTo($list);
});
// display files
Object.keys(root).forEach(function (key) {
if (typeof(root[key]) !== "string") { return; }
var $name = $('<span>', { 'class': 'file-element' }).text(key).prepend($fileIcon.clone());
var $element = $('<li>').append($name).dblclick(function () {
window.location.hash = root[key];
});
$element.appendTo($list);
});
$content.append($title).append($dirContent);
};
var createTree = function ($container, root, path) {
if (Object.keys(root).length === 0) { return; }
var $list = $('<ul>').appendTo($container);
Object.keys(root).forEach(function (key) {
// Do not display files in the menu
if (typeof(root[key]) === "string") { return; }
var $icon = Object.keys(root[key]).length === 0 ? $folderEmptyIcon.clone() : $folderIcon.clone();
var $name = $('<span>', { 'class': 'folder-element' }).text(key).prepend($icon)
.click(function () {
displayDirectory(key, root[key]);
}); });
var $element = $('<li>').append($name); $(ifrw).on('mouseup drop', function (e) {
$element.appendTo($list); $iframe.find('.droppable').removeClass('droppable');
createTree(root[key], $element[0]);
}); });
};*/
}); });

Loading…
Cancel
Save