hfs 0.1.6 → 0.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +674 -0
- package/README.md +102 -8
- package/admin/assets/index.dcc78777.css +1 -0
- package/admin/assets/index.f056db34.js +282 -0
- package/admin/assets/sha512.3c0e384c.js +8 -0
- package/admin/index.html +17 -0
- package/admin/logo.svg +36 -0
- package/frontend/assets/index.55c710c2.js +85 -0
- package/frontend/assets/index.ee805a6c.css +1 -0
- package/frontend/assets/sha512.634b743e.js +8 -0
- package/frontend/fontello.css +77 -0
- package/frontend/fontello.woff2 +0 -0
- package/frontend/index.html +18 -0
- package/package.json +93 -28
- package/plugins/antibrute/plugin.js +38 -0
- package/plugins/download-counter/plugin.js +47 -0
- package/plugins/download-counter/public/hits.js +5 -0
- package/plugins/updater-disabled/plugin.js +44 -0
- package/plugins/vhosting/plugin.js +42 -0
- package/src/QuickZipStream.js +285 -0
- package/src/ThrottledStream.js +93 -0
- package/src/adminApis.js +169 -0
- package/src/api.accounts.js +59 -0
- package/src/api.auth.js +130 -0
- package/src/api.file_list.js +103 -0
- package/src/api.helpers.js +32 -0
- package/src/api.monitor.js +102 -0
- package/src/api.plugins.js +125 -0
- package/src/api.vfs.js +164 -0
- package/src/apiMiddleware.js +136 -0
- package/src/block.js +33 -0
- package/src/commands.js +105 -0
- package/src/config.js +172 -0
- package/src/connections.js +57 -0
- package/src/const.js +83 -0
- package/src/crypt.js +21 -0
- package/src/debounceAsync.js +48 -0
- package/src/events.js +9 -0
- package/src/frontEndApis.js +38 -0
- package/src/github.js +102 -0
- package/src/index.js +53 -0
- package/src/listen.js +226 -0
- package/src/log.js +137 -0
- package/src/middlewares.js +154 -0
- package/src/misc.js +160 -0
- package/src/pbkdf2.js +74 -0
- package/src/perm.js +176 -0
- package/src/plugins.js +338 -0
- package/src/serveFile.js +104 -0
- package/src/serveGuiFiles.js +113 -0
- package/src/sse.js +29 -0
- package/src/throttler.js +91 -0
- package/src/update.js +69 -0
- package/src/util-files.js +141 -0
- package/src/util-generators.js +30 -0
- package/src/util-http.js +30 -0
- package/src/vfs.js +227 -0
- package/src/watchLoad.js +73 -0
- package/src/zip.js +69 -0
- package/.npmignore +0 -19
- package/admin-server.js +0 -212
- package/cli.js +0 -33
- package/file-server.js +0 -100
- package/lib/common.js +0 -10
- package/lib/extending.js +0 -158
- package/lib/mime.js +0 -19
- package/lib/misc.js +0 -75
- package/lib/serving.js +0 -81
- package/lib/vfs.js +0 -403
- package/main.js +0 -24
- package/note.txt +0 -104
- package/speedtest.js +0 -21
- package/static/backend.css +0 -14
- package/static/backend.html +0 -32
- package/static/backend.js +0 -694
- package/static/extending.js +0 -187
- package/static/frontend.css +0 -29
- package/static/frontend.html +0 -23
- package/static/frontend.js +0 -230
- package/static/icons/files/archive.png +0 -0
- package/static/icons/files/audio.png +0 -0
- package/static/icons/files/file.png +0 -0
- package/static/icons/files/folder.png +0 -0
- package/static/icons/files/image.png +0 -0
- package/static/icons/files/link.png +0 -0
- package/static/icons/files/video.png +0 -0
- package/static/jquery.js +0 -4
- package/static/jquery.rule-1.0.2.js +0 -273
- package/static/misc.js +0 -194
- package/static/tpl.js +0 -17
- package/todo.txt +0 -25
package/static/backend.js
DELETED
|
@@ -1,694 +0,0 @@
|
|
|
1
|
-
var socket = io.connect(window.location.origin);
|
|
2
|
-
|
|
3
|
-
var tpl = {
|
|
4
|
-
item: "<li>"
|
|
5
|
-
+"<span class='expansion-button'></span>"
|
|
6
|
-
+"<span class='icon'></span>"
|
|
7
|
-
+"<span class='label'></span>"
|
|
8
|
-
+"</li>",
|
|
9
|
-
noChildren: "<span class='no-children'>nothing</span>",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
var virtualFocus = 'vfs';
|
|
13
|
-
|
|
14
|
-
$(function(){ // dom ready
|
|
15
|
-
$(tpl.item).addClass('item').appendTo($('<ul>').appendTo('#vfs')); // create the root element
|
|
16
|
-
|
|
17
|
-
// hide expansion button
|
|
18
|
-
var style = document.styleSheets[0];
|
|
19
|
-
style.addRule('#vfs .expansion-button','opacity:0');
|
|
20
|
-
expansionCss = style.rules[style.rules.length-1].style;
|
|
21
|
-
|
|
22
|
-
vfsUpdateButtons();
|
|
23
|
-
setupEventHandlers();
|
|
24
|
-
socket.on('connect', function(){ // socket ready
|
|
25
|
-
socket.emit('info.get', {}, function(data){
|
|
26
|
-
serverInfo = data||{};
|
|
27
|
-
reloadVFS();
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
socket.on('vfs.changed', function(data){
|
|
31
|
-
if (!log('vfs.changed',data)) return; // something wrong
|
|
32
|
-
var folder = data.uri.substr(0, data.uri.lastIndexOf('/')+1);
|
|
33
|
-
var it = getItemFromURI(folder);
|
|
34
|
-
if (!it) return; // not in the visible tree: ignore
|
|
35
|
-
if (!isExpanded(it)) return; // not expanded, we don't see its content, no need to reload
|
|
36
|
-
reloadVFS(it);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
function sameFileName(a,b) {
|
|
41
|
-
return serverInfo.caseSensitiveFileNames ? a === b : a.same(b);
|
|
42
|
-
} // sameFileName
|
|
43
|
-
|
|
44
|
-
/* display a dialog for input.
|
|
45
|
-
Currently just a wrapper of prompt().
|
|
46
|
-
Arguments:
|
|
47
|
-
text, callback
|
|
48
|
-
*/
|
|
49
|
-
function inputBox() {
|
|
50
|
-
var a = arguments, text = a[0], def, cb;
|
|
51
|
-
switch (typeof a[1]) {
|
|
52
|
-
case 'function':
|
|
53
|
-
cb = a[1];
|
|
54
|
-
break;
|
|
55
|
-
case 'string':
|
|
56
|
-
def = a[1];
|
|
57
|
-
cb = a[2];
|
|
58
|
-
break;
|
|
59
|
-
default:
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
cb(prompt(text, def));
|
|
63
|
-
} // inputBox
|
|
64
|
-
|
|
65
|
-
function msgBox(message) { alert(message) }
|
|
66
|
-
|
|
67
|
-
function bindItem() {
|
|
68
|
-
var it = getFirstSelectedFolder();
|
|
69
|
-
if (!it) return;
|
|
70
|
-
inputBox('Enter path', function(s){
|
|
71
|
-
if (!s) return;
|
|
72
|
-
socket.emit('vfs.set', { uri:getURI(it), resource:s }, function(result){
|
|
73
|
-
result.ok
|
|
74
|
-
? reloadVFS(it)
|
|
75
|
-
: msgBox(result.error);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
} // bindItem
|
|
79
|
-
|
|
80
|
-
function renameItem() {
|
|
81
|
-
var it = getFirstSelectedItem();
|
|
82
|
-
if (!it || isRoot(it) || isDeleted(it)) return;
|
|
83
|
-
inputBox('Enter new name', it.name, function(s){
|
|
84
|
-
s = $.trim(s);
|
|
85
|
-
if (!s || s == it.name) return; // no change
|
|
86
|
-
socket.emit('vfs.set', { uri:getURI(it), name:s }, function(result){
|
|
87
|
-
if (!result.ok) {
|
|
88
|
-
msgBox(result.error);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
it.name = s; // update object
|
|
92
|
-
bindItemToDOM(it, it.element); // update GUI
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
} // renameItem
|
|
96
|
-
|
|
97
|
-
function deleteItem() {
|
|
98
|
-
// who's gonna be deleted?
|
|
99
|
-
var li = getFirstSelected();
|
|
100
|
-
if (isDeleted(li)) { // this a deleted one, what we really want is restore it
|
|
101
|
-
restoreItem();
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
// we're not going to delete items that are root, or we are already deleting, or that are not! (special items)
|
|
105
|
-
var it = asItem(li);
|
|
106
|
-
if (!it || isRoot(it) || it.deleting) return;
|
|
107
|
-
it.deleting = true; // mark it, so we avoid overlapping operations
|
|
108
|
-
var parent = getParent(li);
|
|
109
|
-
var pit = asItem(parent);
|
|
110
|
-
var oldNumber = pit.deletedItems.length; // we'll use this to see if the number has changed at the end
|
|
111
|
-
li.attr('deleting',1).fadeTo(100, 0.5); // mark it visually and at DOM level
|
|
112
|
-
vfsSelectNearby(li); // renew selection
|
|
113
|
-
// server, please do it
|
|
114
|
-
socket.emit('vfs.delete', { uri:getURI(it) }, function(result){
|
|
115
|
-
if (!log(result).ok) { // something went wrong
|
|
116
|
-
// ugh, forget it.... (unmark)
|
|
117
|
-
it.deleting = false;
|
|
118
|
-
li.removeAttr('deleting');
|
|
119
|
-
// let the user know
|
|
120
|
-
msgBox(result.error);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
// if this number has changed, then we need to do a little extra work: the item became a deleted item.
|
|
124
|
-
if (oldNumber !== result.folderDeletedCount) {
|
|
125
|
-
pit.deletedItems.push(result.dynamicItem);
|
|
126
|
-
updateDeletedItems(parent, result.dynamicItem);
|
|
127
|
-
}
|
|
128
|
-
// GUI refresh
|
|
129
|
-
li.fadeOut(100, function(){
|
|
130
|
-
li.remove();
|
|
131
|
-
if (!getFirstChild(parent).size()) {
|
|
132
|
-
parent.find('ul:first').append(tpl.noChildren); // deleted last item of a folder
|
|
133
|
-
}
|
|
134
|
-
})
|
|
135
|
-
});
|
|
136
|
-
} // deleteItem
|
|
137
|
-
|
|
138
|
-
function restoreItem(it) {
|
|
139
|
-
if (it === undefined)
|
|
140
|
-
it = getFirstSelectedItem();
|
|
141
|
-
if (!it || !isDeleted(it)) return;
|
|
142
|
-
var li = $(it.element);
|
|
143
|
-
var folder = li.closest('li.item');
|
|
144
|
-
vfsSelectNearby(li);
|
|
145
|
-
socket.emit('vfs.restore', { uri:getURI(folder), resource:it.name }, function(result){
|
|
146
|
-
if (!result.ok) return;
|
|
147
|
-
// do the job locally: remove the element from the array
|
|
148
|
-
var a = asItem(folder).deletedItems;
|
|
149
|
-
var i = a.length;
|
|
150
|
-
while (i--) {
|
|
151
|
-
if (sameFileName(a[i].excludeTrailing('/'), it.name)) {
|
|
152
|
-
a.splice(i,1);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
// refresh GUI
|
|
156
|
-
li.remove();
|
|
157
|
-
updateDeletedItems(folder);
|
|
158
|
-
addItemUnder(folder, result.item);
|
|
159
|
-
// we are not going to automatically select the restored item, so it's easier to restore more files
|
|
160
|
-
});
|
|
161
|
-
} // restoreItem
|
|
162
|
-
|
|
163
|
-
function addItem() {
|
|
164
|
-
var it = getFirstSelectedFolder() || getRootItem();
|
|
165
|
-
inputBox('Enter name or path', function(s){
|
|
166
|
-
if (!s) return;
|
|
167
|
-
socket.emit('vfs.add', { uri:getURI(it), resource:s }, function(result){
|
|
168
|
-
if (!log(result).ok) {
|
|
169
|
-
msgBox(result.error);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (result.item.nodeKind === 'temp') { // this is a dynamic element, was actually restored from the "deleted" list
|
|
173
|
-
updateDeletedItems(it);
|
|
174
|
-
}
|
|
175
|
-
setExpanded(it);
|
|
176
|
-
addItemUnder(it, result.item);
|
|
177
|
-
vfsSelect(result.item);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
} // addItem
|
|
181
|
-
|
|
182
|
-
function vfsSelectNearby(li) {
|
|
183
|
-
var selector = 'li:not([deleting])';
|
|
184
|
-
var go = li.next(selector);
|
|
185
|
-
if (!go.size()) {
|
|
186
|
-
go = li.prev(selector);
|
|
187
|
-
}
|
|
188
|
-
if (!go.size()) {
|
|
189
|
-
go = getParent(li);
|
|
190
|
-
}
|
|
191
|
-
vfsSelect(go);
|
|
192
|
-
} // vfsSelectNearby
|
|
193
|
-
|
|
194
|
-
function hideExpansionButtons() { animate(expansionCss, 'opacity', 0) }
|
|
195
|
-
|
|
196
|
-
function showExpansionButtons(state /** optional */) {
|
|
197
|
-
if (typeof state !== 'undefined' && !state) {
|
|
198
|
-
hideExpansionButtons();
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
animate(expansionCss, 'opacity', 1, {duration:0.2});
|
|
202
|
-
} // showExpansionButtons
|
|
203
|
-
|
|
204
|
-
function expandOrCollapseFolder(li) {
|
|
205
|
-
isExpanded(li) ? setExpanded(li, false) : expandAndLoad(li);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function setupEventHandlers() {
|
|
209
|
-
|
|
210
|
-
$('#vfs').click(function(){
|
|
211
|
-
vfsSelect(null); // deselect
|
|
212
|
-
});
|
|
213
|
-
$('#vfs li').live({
|
|
214
|
-
click: function(ev){
|
|
215
|
-
ev.stopImmediatePropagation();
|
|
216
|
-
removeBrowserSelection();
|
|
217
|
-
vfsSelect(this);
|
|
218
|
-
},
|
|
219
|
-
dblclick: function(ev){
|
|
220
|
-
ev.stopImmediatePropagation();
|
|
221
|
-
removeBrowserSelection();
|
|
222
|
-
var it = getFirstSelected();
|
|
223
|
-
if (isFolder(it)) {
|
|
224
|
-
expandOrCollapseFolder(it);
|
|
225
|
-
}
|
|
226
|
-
},
|
|
227
|
-
mouseover: function(ev){
|
|
228
|
-
ev.stopImmediatePropagation();
|
|
229
|
-
$('#vfs li.hovered').removeClass('hovered');
|
|
230
|
-
$(this).addClass('hovered');
|
|
231
|
-
$('#vfs .expansion-button').fadeIn();
|
|
232
|
-
},
|
|
233
|
-
mouseout: function(ev){
|
|
234
|
-
ev.stopImmediatePropagation();
|
|
235
|
-
$(this).removeClass('hovered');
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
$('#vfs .expansion-button').live({
|
|
239
|
-
click: function(ev){
|
|
240
|
-
ev.stopImmediatePropagation();
|
|
241
|
-
removeBrowserSelection();
|
|
242
|
-
var li = $(ev.target).closest('li');
|
|
243
|
-
expandOrCollapseFolder(li);
|
|
244
|
-
},
|
|
245
|
-
mouseover: function(ev){
|
|
246
|
-
$('#vfs .expansion-button.hovered').removeClass('hovered');
|
|
247
|
-
$(this).addClass('hovered');
|
|
248
|
-
},
|
|
249
|
-
mouseout: function(ev){
|
|
250
|
-
$(this).removeClass('hovered');
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
$('#vfs').hover(showExpansionButtons, hideExpansionButtons);
|
|
254
|
-
$('body').keydown(function(ev){
|
|
255
|
-
if (!(ev.target instanceof HTMLBodyElement)) return; // focused elsewhere, but the event propagated till here
|
|
256
|
-
if (virtualFocusEventHandler(ev) === false) {
|
|
257
|
-
ev.stopImmediatePropagation();
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
$('#bindItem').click(bindItem);
|
|
262
|
-
$('#addItem').click(addItem);
|
|
263
|
-
$('#renameItem').click(renameItem);
|
|
264
|
-
$('#deleteItem').click(deleteItem);
|
|
265
|
-
} // setupEventHandlers
|
|
266
|
-
|
|
267
|
-
function virtualFocusEventHandler(ev) {
|
|
268
|
-
// there's a couple of possible event handlers to be called
|
|
269
|
-
var v = 'eventHandler_'+virtualFocus;
|
|
270
|
-
var fns = [v+'_'+ev.type, v];
|
|
271
|
-
|
|
272
|
-
for (var i=0, l=fns.length; i<l; ++i) {
|
|
273
|
-
var fn = window[fns[i]]; // try to get it
|
|
274
|
-
if (typeof fn == 'function') { // if it exists, run it
|
|
275
|
-
if (fn(ev) === false) return false; // it can decide to interrupt the event handling
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
} // virtualFocusEventHandler
|
|
279
|
-
|
|
280
|
-
function eventHandler_vfs_keydown(ev) {
|
|
281
|
-
var sel = getFirstSelected();
|
|
282
|
-
var go;
|
|
283
|
-
switch (ev.keyCode) {
|
|
284
|
-
case 38: // up
|
|
285
|
-
go = sel.prev();
|
|
286
|
-
if (go.size()) break;
|
|
287
|
-
go = getParent(sel);
|
|
288
|
-
break;
|
|
289
|
-
case 40: // down
|
|
290
|
-
if (!sel.size()) { // initialize with root
|
|
291
|
-
go = getRoot();
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
294
|
-
if (isExpanded(sel)) { // try first child
|
|
295
|
-
go = getFirstChild(sel);
|
|
296
|
-
if (go.size()) break;
|
|
297
|
-
}
|
|
298
|
-
go = sel.next() // try the sibling
|
|
299
|
-
if (go.size()) break;
|
|
300
|
-
// try the parent's sibling
|
|
301
|
-
var v = sel;
|
|
302
|
-
while ((v = getParent(v)).size()) {
|
|
303
|
-
if (v.next().size()) {
|
|
304
|
-
go = v.next();
|
|
305
|
-
break;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
break;
|
|
309
|
-
case 37: // left
|
|
310
|
-
isFolder(sel) && isExpanded(sel)
|
|
311
|
-
? setExpanded(sel, false)
|
|
312
|
-
: go = getParent(sel);
|
|
313
|
-
break;
|
|
314
|
-
case 39: // right
|
|
315
|
-
if (!isFolder(sel)) break;
|
|
316
|
-
isExpanded(sel)
|
|
317
|
-
? go = getFirstChild(sel)
|
|
318
|
-
: expandAndLoad(sel);
|
|
319
|
-
break;
|
|
320
|
-
case 36: // home
|
|
321
|
-
if (sel.size() && !isRoot(sel)) {
|
|
322
|
-
go = getFirstChild(getParent(sel));
|
|
323
|
-
if (go.size() && !go.is(sel)) break;
|
|
324
|
-
}
|
|
325
|
-
go = getRoot();
|
|
326
|
-
break;
|
|
327
|
-
case 35: // end
|
|
328
|
-
go = getLastChild(getParent(sel));
|
|
329
|
-
if (go.size() && !go.is(sel)) break;
|
|
330
|
-
go = getRoot();
|
|
331
|
-
// go down till it's possible
|
|
332
|
-
while ((v = getLastChild(go)).size()) {
|
|
333
|
-
go = v;
|
|
334
|
-
}
|
|
335
|
-
break;
|
|
336
|
-
case 113: // F2
|
|
337
|
-
renameItem();
|
|
338
|
-
break;
|
|
339
|
-
case 46: // delete
|
|
340
|
-
deleteItem();
|
|
341
|
-
break;
|
|
342
|
-
default:
|
|
343
|
-
//log(ev.keyCode);
|
|
344
|
-
return; // unsupported key, don't handle
|
|
345
|
-
}
|
|
346
|
-
if (go && go.size()) {
|
|
347
|
-
showExpansionButtons();
|
|
348
|
-
vfsSelect(go);
|
|
349
|
-
}
|
|
350
|
-
return false;
|
|
351
|
-
} // eventHandler_vfs_keydown
|
|
352
|
-
|
|
353
|
-
function vfsSelect(el) {
|
|
354
|
-
$('#vfs .selected').removeClass('selected');
|
|
355
|
-
asLI(el).addClass('selected').scrollToMe({duration:'fast'});
|
|
356
|
-
vfsUpdateButtons();
|
|
357
|
-
} // vfsSelect
|
|
358
|
-
|
|
359
|
-
function getSelectedItems() {
|
|
360
|
-
var res = [];
|
|
361
|
-
$('#vfs .selected').each(function(i,e){
|
|
362
|
-
var it = $(e).data('item');
|
|
363
|
-
res.push(it);
|
|
364
|
-
});
|
|
365
|
-
return res;
|
|
366
|
-
} // getSelectedItems
|
|
367
|
-
|
|
368
|
-
function getSelected() { return $('#vfs .selected') }
|
|
369
|
-
|
|
370
|
-
function getFirstSelected() { return $('#vfs .selected:first') }
|
|
371
|
-
|
|
372
|
-
function getFirstSelectedItem() { return getFirstSelected().data('item') }
|
|
373
|
-
|
|
374
|
-
function getFirstSelectedFolder() {
|
|
375
|
-
var res = getSelectedItems();
|
|
376
|
-
for (var i=0, l=res.length; i<l; ++i) {
|
|
377
|
-
if (isFolder(res[i])) {
|
|
378
|
-
return res[i];
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
return false;
|
|
382
|
-
} // getFirstSelectedFolder
|
|
383
|
-
|
|
384
|
-
function getRoot() { return $('#vfs li:first'); }
|
|
385
|
-
|
|
386
|
-
function getRootItem() { return getRoot().data('item'); }
|
|
387
|
-
|
|
388
|
-
/** return children in the same format of the parameter: jQuery, array of items or array of HTMLElements */
|
|
389
|
-
function getChildren(x) {
|
|
390
|
-
if (!x) return false;
|
|
391
|
-
var item = !!x.element;
|
|
392
|
-
var children = $(x.element || x).find('ul:first>li');
|
|
393
|
-
if (x instanceof $) return children;
|
|
394
|
-
var res = [];
|
|
395
|
-
children.each(function(idx,el){
|
|
396
|
-
res.push(item ? asItem(el) : el);
|
|
397
|
-
});
|
|
398
|
-
return res;
|
|
399
|
-
} // getChildren
|
|
400
|
-
|
|
401
|
-
/** return first child of "parent" matching "pattern" */
|
|
402
|
-
function getFirstChild(parent, pattern /** optional */) {
|
|
403
|
-
var inputType = getType(parent);
|
|
404
|
-
parent = asLI(parent);
|
|
405
|
-
assert(parent.size(), 'parent');
|
|
406
|
-
assert(!pattern || typeof pattern == 'object', 'pattern');
|
|
407
|
-
var res = false;
|
|
408
|
-
parent.find('ul:first>li').each(function(idx,el){
|
|
409
|
-
var child = asItem(el)
|
|
410
|
-
if (pattern) { // do we have a pattern?
|
|
411
|
-
if (!child) return;
|
|
412
|
-
// does it match?
|
|
413
|
-
for (var k in pattern) {
|
|
414
|
-
if (!pattern.hasOwnProperty(k)) continue;
|
|
415
|
-
var match = (k === 'name')
|
|
416
|
-
? sameFileName(pattern[k], child[k])
|
|
417
|
-
: pattern[k] === child[k];
|
|
418
|
-
if (!match) return; // ...nope, skip
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
res = el; // found! mark it
|
|
422
|
-
return false; // stop it
|
|
423
|
-
});
|
|
424
|
-
return toType(inputType, res); // convert output to the same type of the input
|
|
425
|
-
} // getFirstChild
|
|
426
|
-
|
|
427
|
-
function getLastChild(parent) {
|
|
428
|
-
var e = asLI(parent).find('ul:first>li:last');
|
|
429
|
-
return toType(getType(parent), e);
|
|
430
|
-
} // getLastChild
|
|
431
|
-
|
|
432
|
-
/** determine the type of the element */
|
|
433
|
-
function getType(x) {
|
|
434
|
-
if (!x) return false;
|
|
435
|
-
if (x.element) return 'item';
|
|
436
|
-
if (x instanceof HTMLElement) return 'html';
|
|
437
|
-
if (x instanceof $) return 'jquery';
|
|
438
|
-
return false;
|
|
439
|
-
} // getType
|
|
440
|
-
|
|
441
|
-
/** convert the element to the specified type */
|
|
442
|
-
function toType(to, x) {
|
|
443
|
-
if (!to) return false; // bad
|
|
444
|
-
var from = getType(x);
|
|
445
|
-
if (from == to) return x; // no need for conversion
|
|
446
|
-
switch (from) {
|
|
447
|
-
case false:
|
|
448
|
-
return (to == 'jquery') ? $('') : false;
|
|
449
|
-
case 'item':
|
|
450
|
-
if (to == 'html') return x.element;
|
|
451
|
-
// to == 'jquery'
|
|
452
|
-
return $(x.element);
|
|
453
|
-
case 'html':
|
|
454
|
-
if (to == 'jquery') return $(x).closest('li');
|
|
455
|
-
// to == 'item'
|
|
456
|
-
return $(x).closest('li').data('item');
|
|
457
|
-
case 'jquery':
|
|
458
|
-
if (to == 'html') return x[0];
|
|
459
|
-
// to == 'item'
|
|
460
|
-
return x.data('item');
|
|
461
|
-
default:
|
|
462
|
-
assert('unsupported type: '+from);
|
|
463
|
-
}
|
|
464
|
-
} // setType
|
|
465
|
-
|
|
466
|
-
function getParent(it) {
|
|
467
|
-
var p = asLI(it).parent().closest('li');
|
|
468
|
-
return toType(getType(it), p);
|
|
469
|
-
} // getParent
|
|
470
|
-
|
|
471
|
-
function isFolder(it) {
|
|
472
|
-
if (asLI(it).hasClass('deleted-items')) {
|
|
473
|
-
return true;
|
|
474
|
-
}
|
|
475
|
-
it = asItem(it);
|
|
476
|
-
return it && it.itemKind.endsBy('folder');
|
|
477
|
-
} // isFolder
|
|
478
|
-
|
|
479
|
-
function isRoot(it) { return getURI(it) == '/' }
|
|
480
|
-
|
|
481
|
-
function isExpanded(x) { return asLI(x).hasClass('expanded') }
|
|
482
|
-
|
|
483
|
-
function isDeleted(x) { return asLI(x).closest('.deleted-items').size() }
|
|
484
|
-
|
|
485
|
-
function getURI(item) {
|
|
486
|
-
item = asItem(item);
|
|
487
|
-
if (!item) return false;
|
|
488
|
-
var p = getParent(item);
|
|
489
|
-
return (p ? getURI(p) : '') // recursion
|
|
490
|
-
+ encodeURI(item.name)
|
|
491
|
-
+ (p && isFolder(item) ? '/' : '');
|
|
492
|
-
} // getURI
|
|
493
|
-
|
|
494
|
-
/** get the item from the uri, but only if it's currently in our tree */
|
|
495
|
-
function getItemFromURI(uri, from) {
|
|
496
|
-
var run = asItem(from) || getRootItem();
|
|
497
|
-
for (var i=0, a=uri.split('/'), l=a.length; i<l; ++i) {
|
|
498
|
-
var name = a[i];
|
|
499
|
-
if (!name) continue;
|
|
500
|
-
run = getFirstChild(run, {name:name});
|
|
501
|
-
if (!run) return false;
|
|
502
|
-
}
|
|
503
|
-
return run;
|
|
504
|
-
} // getItemFromURI
|
|
505
|
-
|
|
506
|
-
function vfsUpdateButtons() {
|
|
507
|
-
var li = getFirstSelected();
|
|
508
|
-
var it = asItem(li);
|
|
509
|
-
enableButton('addItem', !li.size() || it && !it.deleted);
|
|
510
|
-
enableButton('bindItem', it && it.itemKind === 'virtual folder');
|
|
511
|
-
enableButton('renameItem', it && !isRoot(it) && !isDeleted(it));
|
|
512
|
-
enableButton('deleteItem', it && !isRoot(it));
|
|
513
|
-
} // vfsUpdateButtons
|
|
514
|
-
|
|
515
|
-
function enableButton(name, condition) {
|
|
516
|
-
$('#'+name).attr({disabled: !condition});
|
|
517
|
-
} // enableButton
|
|
518
|
-
|
|
519
|
-
function reloadVFS(item, cb) {
|
|
520
|
-
var e = item ? asLI(item) : getRoot();
|
|
521
|
-
e.children('ul').empty().append(tpl.noChildren); // remove possible children
|
|
522
|
-
socket.emit('vfs.get', { uri:item ? getURI(item) : '/', depth:1 }, function(data){
|
|
523
|
-
if (!log('vfs.get',data)) return;
|
|
524
|
-
bindItemToDOM(data, e);
|
|
525
|
-
setExpanded(e);
|
|
526
|
-
updateDeletedItems(e);
|
|
527
|
-
data.children.forEach(function(it){
|
|
528
|
-
addItemUnder(e, it);
|
|
529
|
-
});
|
|
530
|
-
if (cb) cb();
|
|
531
|
-
});
|
|
532
|
-
} // reloadVFS
|
|
533
|
-
|
|
534
|
-
/** util function for those functions who want to accept several types but work with the $(LI) */
|
|
535
|
-
function asLI(x) { return toType('jquery', x).closest('li') }
|
|
536
|
-
|
|
537
|
-
/** util function for those functions who want to accept several types but work with the $(LI) */
|
|
538
|
-
function asItem(x) { return toType('item', x) }
|
|
539
|
-
|
|
540
|
-
function setExpanded(item, state) {
|
|
541
|
-
if (state == undefined) state = true;
|
|
542
|
-
var li = asLI(item);
|
|
543
|
-
if (!li.size()) return;
|
|
544
|
-
item = asItem(item);
|
|
545
|
-
var button = li.find('.expansion-button:first');
|
|
546
|
-
button.text(state ? '▲' : '▷');
|
|
547
|
-
if (!isFolder(li)
|
|
548
|
-
|| item && item.deleted) { // deleted folders are not expandable
|
|
549
|
-
button.css({visibility:'hidden'});
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
if (isExpanded(li) == state) return;
|
|
553
|
-
li.addClass(state ? 'expanded' : 'collapsed')
|
|
554
|
-
.removeClass(!state ? 'expanded' : 'collapsed');
|
|
555
|
-
// deal with the container of children
|
|
556
|
-
var ul = li.find('ul:first');
|
|
557
|
-
if (state) {
|
|
558
|
-
if (!ul.size())
|
|
559
|
-
ul = $('<ul>').appendTo(li);
|
|
560
|
-
}
|
|
561
|
-
else {
|
|
562
|
-
ul.remove();
|
|
563
|
-
}
|
|
564
|
-
return true;
|
|
565
|
-
} // setExpanded
|
|
566
|
-
|
|
567
|
-
function expandAndLoad(li) {
|
|
568
|
-
li = asLI(li);
|
|
569
|
-
setExpanded(li);
|
|
570
|
-
if (li.hasClass('item'))
|
|
571
|
-
return reloadVFS(li);
|
|
572
|
-
if (li.hasClass('deleted-items')) {
|
|
573
|
-
asItem(getParent(li)).deletedItems.forEach(function(name){
|
|
574
|
-
addItemUnder(li, name);
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
} // expandAndLoad
|
|
578
|
-
|
|
579
|
-
function bindItemToDOM(item, element) {
|
|
580
|
-
var li = asLI(element);
|
|
581
|
-
item.element = li[0]; // bind the item to the html element, so we can get to it
|
|
582
|
-
if (isFolder(item) && !item.deletedItems) {
|
|
583
|
-
item.deletedItems = []; // having this always present will ease the rest of the code
|
|
584
|
-
}
|
|
585
|
-
li.data({item:item});
|
|
586
|
-
li.find('.label:first').text(item.name);
|
|
587
|
-
var icon = isFolder(item) ? 'folder' : item.itemKind;
|
|
588
|
-
if (icon=='file') {
|
|
589
|
-
icon = nameToType(item.name) || icon;
|
|
590
|
-
}
|
|
591
|
-
li.find('.icon:first').html("<img src='"+getIconURI(icon)+"' />");
|
|
592
|
-
if (isFolder(item)) {
|
|
593
|
-
updateDeletedItems(item);
|
|
594
|
-
}
|
|
595
|
-
setExpanded(li, false);
|
|
596
|
-
return element;
|
|
597
|
-
} // bindItemToDOM
|
|
598
|
-
|
|
599
|
-
/** updates the pseudo-folder containing removed temp items */
|
|
600
|
-
function updateDeletedItems(it, adding) {
|
|
601
|
-
var folder = asLI(it);
|
|
602
|
-
var ul = folder.children('ul');
|
|
603
|
-
it = asItem(it);
|
|
604
|
-
if (!it.deletedItems || !it.deletedItems.length) { // no items?
|
|
605
|
-
// remove the pseudo-folder
|
|
606
|
-
if (!ul.size()) return;
|
|
607
|
-
var li = ul.children('li.deleted-items');
|
|
608
|
-
if (!li.size()) return;
|
|
609
|
-
if (getFirstSelected().is(li)) {
|
|
610
|
-
vfsSelectNearby(li);
|
|
611
|
-
}
|
|
612
|
-
li.remove();
|
|
613
|
-
// if there is no more children in the containing folder, remove the UL as well
|
|
614
|
-
if (!ul.children().size()) {
|
|
615
|
-
ul.remove();
|
|
616
|
-
}
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
// ensure there's a UL for the containing folder
|
|
620
|
-
if (!ul.size()) {
|
|
621
|
-
ul = $('<ul>').appendTo(folder);
|
|
622
|
-
}
|
|
623
|
-
// ensure there's a LI of the pseudo-folder
|
|
624
|
-
var li = ul.children('li.deleted-items');
|
|
625
|
-
if (!li.size()) {
|
|
626
|
-
li = $(tpl.item).addClass('deleted-items').prependTo(ul);
|
|
627
|
-
li.find('.icon:first').html("<img src='"+getIconURI('folder')+"' />");
|
|
628
|
-
setExpanded(li, false);
|
|
629
|
-
}
|
|
630
|
-
// update the label
|
|
631
|
-
li.find('.label:first').text('Deleted: {0} item(s)'.format(it.deletedItems.length));
|
|
632
|
-
// eventually add the item
|
|
633
|
-
if (adding && isExpanded(li)) {
|
|
634
|
-
addItemUnder(li, adding);
|
|
635
|
-
}
|
|
636
|
-
} // updateDeletedItems
|
|
637
|
-
|
|
638
|
-
/** put a new element under $under with bound object $item
|
|
639
|
-
* @position {undefined|string} admitted values: 'sorted' (default), 'top', 'bottom'
|
|
640
|
-
*/
|
|
641
|
-
function addItemUnder(under, item, position) {
|
|
642
|
-
if (!item) return false;
|
|
643
|
-
if (position === undefined) position = 'sorted';
|
|
644
|
-
under = asLI(under);
|
|
645
|
-
if (under.hasClass('deleted-items')
|
|
646
|
-
&& typeof item == 'string') { // automatic construction of the special item
|
|
647
|
-
item = {
|
|
648
|
-
deleted: true,
|
|
649
|
-
itemKind: item.endsBy('/') ? 'folder' : 'file',
|
|
650
|
-
name: item.excludeTrailing('/'),
|
|
651
|
-
};
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// the UL element is the place for children, have one or create it
|
|
655
|
-
var x = under.children('ul');
|
|
656
|
-
under = x.size() ? x : $('<ul>').appendTo(under);
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
under.children('span.no-children').remove(); // remove any place holder
|
|
660
|
-
var el = $(tpl.item).addClass(item.deleted ? 'deleted' : 'item');
|
|
661
|
-
var beforeThis; // where to put the new item
|
|
662
|
-
if (position.isIn('top','sorted')) { // both require to put the item after special items (so skip them)
|
|
663
|
-
beforeThis = getFirstChild(under); // go down one level
|
|
664
|
-
// skip special items
|
|
665
|
-
while (beforeThis.size() && !beforeThis.hasClass('item')) {
|
|
666
|
-
beforeThis = beforeThis.next();
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
if (position === 'sorted') {
|
|
670
|
-
// skip elements until we find one that has a "higher" name (case-insensitively)
|
|
671
|
-
var name = item.name.low();
|
|
672
|
-
while (beforeThis.size()
|
|
673
|
-
&& beforeThis.hasClass('item')
|
|
674
|
-
&& beforeThis.find('.label').text().low() < name) {
|
|
675
|
-
beforeThis = beforeThis.next();
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
// add the item at the calculated position, or at the end
|
|
679
|
-
(beforeThis && beforeThis.size())
|
|
680
|
-
? el.insertBefore(beforeThis)
|
|
681
|
-
: el.appendTo(under);
|
|
682
|
-
// do the final setup
|
|
683
|
-
return bindItemToDOM(item, el);
|
|
684
|
-
} // addItemUnder
|
|
685
|
-
|
|
686
|
-
function removeBrowserSelection() {
|
|
687
|
-
if (window.getSelection) { // all browsers, except IE before version 9
|
|
688
|
-
return getSelection().removeAllRanges();
|
|
689
|
-
}
|
|
690
|
-
if (document.selection.createRange) { // Internet Explorer
|
|
691
|
-
var range = document.selection.createRange();
|
|
692
|
-
document.selection.empty();
|
|
693
|
-
}
|
|
694
|
-
} // removeBrowserSelection
|