~azzar1/unity/add-show-desktop-key

209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
1
/* IVLE - Informatics Virtual Learning Environment
2
 * Copyright (C) 2007-2008 The University of Melbourne
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 2 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
 *
18
 * Module: Listing (File Browser, client)
19
 * Author: Matt Giuca
20
 * Date: 13/1/2008
21
 *
22
 * Handles directory listings on the client side.
23
 */
24
234 by mattgiuca
browser/listing.js:
25
/* Note: The DOM "tr" nodes of the file listing have extra attributes added
235 by mattgiuca
listing.js: Added sorting functions and a bit of infrastructure to facilitate
26
 * to them:
27
 *  filename: String.
28
 *  fileinfo: The file object as returned by the server.
29
 */
215 by mattgiuca
listing.js:
30
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
31
/* DOM nodeType constants */
32
ELEMENT_NODE = 1;
33
34
/** Filenames of all files selected */
35
selected_files = [];
36
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
37
/** The listing object returned by the server as JSON */
38
file_listing = null;
211 by mattgiuca
fileservice/listing: Slight change to date format.
39
thisdir = null;
40
240 by mattgiuca
browser/listing.js: Wrote real comparison function and set up sort_order array
41
/** The current sort order (a list of fields to sort by, in order of
42
 * priority), and whether it is ascending or descending. */
43
sort_order = [];
44
sort_ascending = true;
45
229 by mattgiuca
Images: Reduced "small" icons from 22x22 to 16x16. Reduced "large" icons from
46
/** The width/height of filetype, svnstatus and publishstatus icons */
47
icon_size = 16;
48
215 by mattgiuca
listing.js:
49
/** ACTIONS **/
50
51
function action_rename(fromfilename)
52
{
219 by mattgiuca
Browser.js: Added handlers for subversion actions.
53
    var tofilename = prompt("Rename file \"" + fromfilename + "\" to?");
215 by mattgiuca
listing.js:
54
    if (tofilename == null) return;
55
    do_action("move", current_path, {"from":fromfilename, "to":tofilename});
56
    return false;
57
}
58
59
function action_remove(files)
60
{
61
    do_action("remove", current_path, {"path":files});
62
    return false;
63
}
64
65
function action_copy(files)
66
{
67
    do_action("copy", current_path, {"path":files});
68
    return false;
69
}
70
71
function action_cut(files)
72
{
73
    do_action("cut", current_path, {"path":files});
74
    return false;
75
}
76
77
function action_paste()
78
{
79
    do_action("paste", current_path, {"path":"."});
80
    return false;
81
}
82
219 by mattgiuca
Browser.js: Added handlers for subversion actions.
83
function action_add(files)
84
{
85
    do_action("svnadd", current_path, {"path":files});
86
    return false;
87
}
88
89
function action_revert(files)
90
{
91
    do_action("svnrevert", current_path, {"path":files});
92
    return false;
93
}
94
269 by mattgiuca
browser: Added publish functionality to JavaScript client side.
95
function action_publish(files)
96
{
97
    do_action("svnpublish", current_path, {"path":files});
98
    return false;
99
}
100
101
function action_unpublish(files)
102
{
103
    do_action("svnunpublish", current_path, {"path":files});
104
    return false;
105
}
106
219 by mattgiuca
Browser.js: Added handlers for subversion actions.
107
function action_commit(files)
108
{
109
    /* Get a commit log from the user */
110
    var logmsg = prompt("Enter commit log:");
223 by mattgiuca
util.js: Added "urlencode_path" which is specially designed to encode paths.
111
    if (logmsg == null) return;
219 by mattgiuca
Browser.js: Added handlers for subversion actions.
112
    do_action("svncommit", current_path, {"path":files, "logmsg": logmsg});
113
    return false;
114
}
115
387 by mattgiuca
Implemented file uploads.
116
/* Shows or hides the "upload panel" in the side panel.
117
 * toshow is true for showing, false for hiding.
118
 */
388 by mattgiuca
browser/listing: Upload button now toggles the upload panel instead of just
119
uploadpanel_shown = false;
387 by mattgiuca
Implemented file uploads.
120
function show_uploadpanel(toshow)
121
{
388 by mattgiuca
browser/listing: Upload button now toggles the upload panel instead of just
122
    if (toshow == null)
123
        uploadpanel_shown = !uploadpanel_shown;
124
    else
125
        uploadpanel_shown = toshow;
387 by mattgiuca
Implemented file uploads.
126
    document.getElementById("uploadpanel").setAttribute("style",
388 by mattgiuca
browser/listing: Upload button now toggles the upload panel instead of just
127
        "display: " + (uploadpanel_shown ? "auto" : "none") + ";");
387 by mattgiuca
Implemented file uploads.
128
    return false;
129
}
130
131
/* Called when a form upload comes back (from an iframe).
132
 * Refreshes the page.
133
 */
134
function upload_callback()
135
{
136
    /* This has a pretty nasty hack, which happens to work.
137
     * upload_callback is set as the "onload" callback for the iframe which
138
     * receives the response from the server for uploading a file.
139
     * This means it gets called twice. Once when initialising the iframe, and
140
     * a second time when the actual response comes back.
141
     * All we want to do is call navigate to refresh the page. But we CAN'T do
142
     * that on the first load or it will just go into an infinite cycle of
143
     * refreshing. We need to refresh the page ONLY on the second refresh.
144
     * upload_callback_count is reset to 0 just before the iframe is created.
145
     */
146
    upload_callback_count++;
147
    if (upload_callback_count == 2)
148
    {
149
        navigate(current_path);
150
        /* Keep upload panel open */
151
        show_uploadpanel(true);
152
    }
153
}
154
215 by mattgiuca
listing.js:
155
/** END ACTIONS **/
156
211 by mattgiuca
fileservice/listing: Slight change to date format.
157
/** Updates the side-panel. Expects selected_files reflects the current
158
 * selected files.
159
 */
160
function update_sidepanel(total_file_size_sel)
161
{
162
    var sidepanel = document.getElementById("sidepanel");
163
    var filename;
164
    var file;
165
    var p;
387 by mattgiuca
Implemented file uploads.
166
    var div;
211 by mattgiuca
fileservice/listing: Slight change to date format.
167
    /* Is this dir under svn? */
168
    var under_subversion = "svnstatus" in thisdir;
169
    dom_removechildren(sidepanel);
170
    if (selected_files.length <= 1)
171
    {
172
        if (selected_files.length == 0)
173
        {
174
            /* Display information about the current directory instead */
175
            filename = path_basename(current_path);
176
            file = thisdir;
177
        }
178
        else if (selected_files.length == 1)
179
        {
215 by mattgiuca
listing.js:
180
            filename = selected_files[0];
211 by mattgiuca
fileservice/listing: Slight change to date format.
181
            file = file_listing[filename];
182
        }
183
        var filetype;
184
        if ("isdir" in file && file.isdir)
185
            filetype = "text/directory";
186
        else if ("type" in file)
187
            filetype = file.type;
188
        else
189
            filetype = "text/plain";
190
213 by mattgiuca
Fileservice / Files (Python and JS files):
191
        if ("type_nice" in file)
192
            filetype_nice = file.type_nice;
193
        else
194
            filetype_nice = "File";
195
211 by mattgiuca
fileservice/listing: Slight change to date format.
196
        p = document.createElement("p");
197
        sidepanel.appendChild(p);
198
        p.appendChild(dom_make_img(mime_type_to_icon(filetype, true),
199
            null, null, filetype));
200
        p = dom_make_text_elem("h2", filename);
201
        sidepanel.appendChild(p);
213 by mattgiuca
Fileservice / Files (Python and JS files):
202
        p = dom_make_text_elem("p", filetype_nice);
211 by mattgiuca
fileservice/listing: Slight change to date format.
203
        sidepanel.appendChild(p);
269 by mattgiuca
browser: Added publish functionality to JavaScript client side.
204
        var mini_icons = document.createElement("p");
205
        sidepanel.appendChild(mini_icons);
206
        var icon;
213 by mattgiuca
Fileservice / Files (Python and JS files):
207
        if (under_subversion)
208
        {
269 by mattgiuca
browser: Added publish functionality to JavaScript client side.
209
            icon = svnstatus_to_icon(file.svnstatus);
230 by mattgiuca
Removed "unversioned" icon from subversion status. Now unversioned files do
210
            if (icon)
269 by mattgiuca
browser: Added publish functionality to JavaScript client side.
211
                mini_icons.appendChild(dom_make_img(icon, icon_size, icon_size,
230 by mattgiuca
Removed "unversioned" icon from subversion status. Now unversioned files do
212
                    svnstatus_to_string(file.svnstatus)));
213 by mattgiuca
Fileservice / Files (Python and JS files):
213
            p = dom_make_text_elem("p", svnstatus_to_string(file.svnstatus));
214
            sidepanel.appendChild(p);
215
        }
269 by mattgiuca
browser: Added publish functionality to JavaScript client side.
216
        if ("published" in file && file.published)
217
        {
218
            icon = make_path(path_join(published_icon));
219
            if (icon)
220
            {
221
                if (mini_icons.childNodes.length > 0)
222
                    mini_icons.appendChild(document.createTextNode(" "));
223
                mini_icons.appendChild(dom_make_img(icon, icon_size, icon_size,
327 by mattgiuca
browser: Renamed "Published directory" label to "Published to the web".
224
                    "Published to the web"));
269 by mattgiuca
browser: Added publish functionality to JavaScript client side.
225
            }
327 by mattgiuca
browser: Renamed "Published directory" label to "Published to the web".
226
            p = dom_make_text_elem("p", "Published to the web");
269 by mattgiuca
browser: Added publish functionality to JavaScript client side.
227
            p.setAttribute("title",
228
                "Anybody on the web can view the files in this directory.");
229
            sidepanel.appendChild(p);
230
        }
231
        /* If we never wrote any mini-icons, remove this element */
232
        if (mini_icons.childNodes.length == 0)
233
            sidepanel.removeChild(mini_icons);
211 by mattgiuca
fileservice/listing: Slight change to date format.
234
        if ("size" in file)
235
        {
236
            p = dom_make_text_elem("p", "Size: " + nice_filesize(file.size));
237
            sidepanel.appendChild(p);
238
        }
239
        if ("mtime_nice" in file)
240
        {
241
            p = dom_make_text_elem("p", "Modified: " + file.mtime_nice);
242
            sidepanel.appendChild(p);
243
        }
244
    }
245
    else
246
    {
247
        /* Multiple files selected */
248
        p = document.createElement("p");
249
        sidepanel.appendChild(p);
250
        p.appendChild(dom_make_img(
224 by mattgiuca
util.js: Removed urlencoding support from "encoded_app_path" (now called
251
            app_path(type_icons_path_large, "multi.png"),
211 by mattgiuca
fileservice/listing: Slight change to date format.
252
            null, null, "Multiple files"));
253
        p = dom_make_text_elem("h2",
254
            selected_files.length.toString() + " files selected");
255
        sidepanel.appendChild(p);
256
        p = dom_make_text_elem("p", "Total size: "
257
            + nice_filesize(total_file_size_sel));
258
        sidepanel.appendChild(p);
259
    }
260
261
    p = dom_make_text_elem("h3", "Actions");
262
    sidepanel.appendChild(p);
263
264
    if (selected_files.length <= 1)
265
    {
330 by mattgiuca
browser/listing.js: Bugfix - error on multiple files selected when updating
266
        if (file.isdir)
267
        {
268
            /* Publish/unpublish */
269
            if (selected_files.length == 0)
270
                path = ".";
271
            else
272
                path = filename;
273
            if ("published" in file && file.published)
274
            {
275
                p = dom_make_link_elem("p", "Unpublish",
276
                    "Make it so this directory cannot be seen by anyone but you",
277
                    null,
278
                    "return action_unpublish(" + repr(path) + ")");
279
                sidepanel.appendChild(p);
280
            }
281
            else
282
            {
283
                p = dom_make_link_elem("p", "Publish",
284
                    "Make it so this directory can be seen by anyone on the web",
285
                    null,
286
                    "return action_publish(" + repr(path) + ")");
287
                sidepanel.appendChild(p);
288
            }
289
        }
290
211 by mattgiuca
fileservice/listing: Slight change to date format.
291
        var handler_type = null;
292
        if ("type" in file)
293
            handler_type = get_handler_type(file.type);
294
        /* Action: Use the "files" / "edit" app */
215 by mattgiuca
listing.js:
295
        var path;
296
        if (selected_files.length == 1)
297
        {
298
            /* Don't have "Browse" if this is the current dir */
299
            if (file.isdir)
300
                p = dom_make_link_elem("p", "Browse",
301
                    "Navigate to this directory in the file browser",
224 by mattgiuca
util.js: Removed urlencoding support from "encoded_app_path" (now called
302
                    app_path(this_app, current_path, filename));
215 by mattgiuca
listing.js:
303
            else if (handler_type == "text")
304
                p = dom_make_link_elem("p", "Edit", "Edit this file",
224 by mattgiuca
util.js: Removed urlencoding support from "encoded_app_path" (now called
305
                    app_path(edit_app, current_path, filename));
215 by mattgiuca
listing.js:
306
            else
307
                p = dom_make_link_elem("p", "Browse",
308
                    "View this file in the file browser",
224 by mattgiuca
util.js: Removed urlencoding support from "encoded_app_path" (now called
309
                    app_path(this_app, current_path, filename));
215 by mattgiuca
listing.js:
310
            sidepanel.appendChild(p);
311
        }
211 by mattgiuca
fileservice/listing: Slight change to date format.
312
313
        /* Action: Use the "serve" app */
314
        /* TODO: Figure out if this file is executable,
315
         * and change the link to "Run" */
316
        p = null;
317
        if (file.isdir || handler_type == "binary") {}
318
        else
319
            p = dom_make_link_elem("p", "View",
215 by mattgiuca
listing.js:
320
                "View this file",
224 by mattgiuca
util.js: Removed urlencoding support from "encoded_app_path" (now called
321
                app_path(serve_app, current_path, filename));
211 by mattgiuca
fileservice/listing: Slight change to date format.
322
        if (p)
323
            sidepanel.appendChild(p);
324
325
        /* Action: Use the "download" app */
326
        p = null;
242 by mattgiuca
browser/listing: Fix: Download link for current directory, wrong path.
327
        if (selected_files.length == 0)
328
            path = app_path(download_app, current_path);
329
        else
330
            path = app_path(download_app, current_path, filename);
211 by mattgiuca
fileservice/listing: Slight change to date format.
331
        if (file.isdir)
332
            p = dom_make_link_elem("p", "Download as zip",
215 by mattgiuca
listing.js:
333
                "Download this directory as a ZIP file", path);
211 by mattgiuca
fileservice/listing: Slight change to date format.
334
        else
335
            p = dom_make_link_elem("p", "Download",
215 by mattgiuca
listing.js:
336
                "Download this file to your computer", path);
211 by mattgiuca
fileservice/listing: Slight change to date format.
337
        if (p)
338
            sidepanel.appendChild(p);
339
269 by mattgiuca
browser: Added publish functionality to JavaScript client side.
340
        if (selected_files.length > 0)
341
        {   /* Can't rename if we're in it */
342
            p = dom_make_link_elem("p", "Rename",
343
                "Change the name of this file", null,
344
                "return action_rename(" + repr(filename) + ")");
345
            sidepanel.appendChild(p);
346
        }
211 by mattgiuca
fileservice/listing: Slight change to date format.
347
    }
348
    else
349
    {
241 by mattgiuca
util.js: dom_make_link_elem: Added argument dontencode, necessary to sometimes
350
        path = urlencode_path(app_path(download_app, current_path)) + "?";
215 by mattgiuca
listing.js:
351
        for (var i=0; i<selected_files.length; i++)
352
            path += "path=" + encodeURIComponent(selected_files[i]) + "&";
353
        path = path.substr(0, path.length-1);
211 by mattgiuca
fileservice/listing: Slight change to date format.
354
        /* Multiple files selected */
355
        p = dom_make_link_elem("p", "Download as zip",
241 by mattgiuca
util.js: dom_make_link_elem: Added argument dontencode, necessary to sometimes
356
            "Download the selected files as a ZIP file", path, null, true);
211 by mattgiuca
fileservice/listing: Slight change to date format.
357
        sidepanel.appendChild(p);
358
    }
359
360
    /* Common actions */
269 by mattgiuca
browser: Added publish functionality to JavaScript client side.
361
    if (selected_files.length > 0)
362
    {
363
        p = dom_make_link_elem("p", "Delete",
364
            "Delete the selected files", null,
365
            "return action_remove(selected_files)");
366
        sidepanel.appendChild(p);
367
        p = dom_make_link_elem("p", "Cut",
368
            "Prepare to move the selected files to another directory", null,
369
            "return action_cut(selected_files)");
370
        sidepanel.appendChild(p);
371
        p = dom_make_link_elem("p", "Copy",
372
            "Prepare to copy the selected files to another directory", null,
373
            "return action_copy(selected_files)");
374
        sidepanel.appendChild(p);
375
    }
211 by mattgiuca
fileservice/listing: Slight change to date format.
376
    p = dom_make_link_elem("p", "Paste",
215 by mattgiuca
listing.js:
377
        "Paste the copied or cut files to the current directory", null,
378
        "return action_paste()");
211 by mattgiuca
fileservice/listing: Slight change to date format.
379
    sidepanel.appendChild(p);
387 by mattgiuca
Implemented file uploads.
380
    p = dom_make_link_elem("p", "Upload",
381
        "Upload a file to the current directory", null,
388 by mattgiuca
browser/listing: Upload button now toggles the upload panel instead of just
382
        "return show_uploadpanel()");
387 by mattgiuca
Implemented file uploads.
383
    sidepanel.appendChild(p);
384
    /* The "Upload" button expands the following panel with upload tools */
385
    /* This panel has a form for submitting the file to, and an iframe to load
386
     * the target page in (this avoids the entire page being refreshed) */
387
    div = document.createElement("div");
388
    div.setAttribute("id", "uploadpanel");
389
    /* This deliberately hides the upload panel whenever the selection
390
     * changes. It can be re-shown by clicking "upload". */
391
    div.setAttribute("style", "display: none;");
392
    sidepanel.appendChild(div);
393
    p = dom_make_text_elem("h3", "Upload File");
394
    div.appendChild(p);
395
    var form = document.createElement("form");
396
    form.setAttribute("method", "POST");
397
    form.setAttribute("enctype", "multipart/form-data");
398
    form.setAttribute("action", app_path("fileservice", current_path));
399
    form.setAttribute("target", "upload_iframe");
400
    div.appendChild(form);
401
    var input;
402
    input = document.createElement("input");
403
    input.setAttribute("type", "hidden");
404
    input.setAttribute("name", "action");
405
    input.setAttribute("value", "putfiles");
406
    form.appendChild(input);
407
408
    input = document.createElement("input");
409
    input.setAttribute("type", "hidden");
410
    input.setAttribute("name", "path");
411
    input.setAttribute("value", "");
412
    form.appendChild(input);
413
414
    p = document.createElement("p");
415
    form.appendChild(p);
416
    input = document.createElement("input");
417
    input.setAttribute("type", "file");
418
    input.setAttribute("name", "data");
419
    p.appendChild(input);
420
421
    p = document.createElement("p");
422
    form.appendChild(p);
423
    input = document.createElement("input");
390 by mattgiuca
Added automatic unzipping on file upload.
424
    input.setAttribute("type", "checkbox");
425
    input.setAttribute("name", "unpack");
426
    input.setAttribute("value", "true");
427
    input.setAttribute("checked", "on");
428
    p.appendChild(input);
429
    p.appendChild(document.createTextNode(" Unpack zip file"));
430
431
    p = document.createElement("p");
432
    form.appendChild(p);
433
    input = document.createElement("input");
387 by mattgiuca
Implemented file uploads.
434
    input.setAttribute("type", "button");
435
    input.setAttribute("value", "Hide");
436
    input.setAttribute("onclick", "show_uploadpanel(false)");
437
    p.appendChild(input);
438
    p.appendChild(document.createTextNode(" "));
439
    input = document.createElement("input");
440
    input.setAttribute("type", "submit");
441
    input.setAttribute("value", "Send");
442
    p.appendChild(input);
443
444
    /* Now we create an invisible iframe which will receive the upload.
445
     * The form submits to fileservice, loading the result into this iframe
446
     * instead of the whole browser window (this is an alternative to Ajax,
447
     * since Ajax doesn't allow reading the file from the user's disk).
448
     * Note this iframe's id is the same as the form's target.
449
     */
450
    var upload_iframe = document.createElement("iframe");
451
    upload_iframe.setAttribute("id", "upload_iframe");
452
    upload_iframe.setAttribute("name", "upload_iframe");
453
    upload_iframe.setAttribute("style", "display: none;");
454
    /* When we get a callback, simply cause a nav to the current path, so we
455
     * update the directory listing. */
456
    upload_callback_count = 0;      /* See upload_callback */
457
    upload_iframe.setAttribute("onload", "upload_callback()");
458
    div.appendChild(upload_iframe);
459
    /* END Upload panel */
211 by mattgiuca
fileservice/listing: Slight change to date format.
460
461
    if (under_subversion)
462
    {
219 by mattgiuca
Browser.js: Added handlers for subversion actions.
463
        /* TODO: Only show relevant links */
211 by mattgiuca
fileservice/listing: Slight change to date format.
464
        p = dom_make_text_elem("h3", "Subversion");
465
        sidepanel.appendChild(p);
219 by mattgiuca
Browser.js: Added handlers for subversion actions.
466
467
        /* TODO: if any selected files are unversioned */
468
        p = dom_make_link_elem("p", "Add",
469
            "Schedule the selected temporary files to be added permanently",
470
            null,
471
            "return action_add(selected_files)");
472
        sidepanel.appendChild(p);
473
        p = dom_make_link_elem("p", "Revert",
474
            "Restore the selected files back to their last committed state",
475
            null,
476
            "return action_revert(selected_files)");
477
        sidepanel.appendChild(p);
478
        /* TODO: Update */
479
        p = dom_make_link_elem("p", "Commit",
480
            "Commit any changes to the permanent repository",
481
            null,
482
            "return action_commit(selected_files)");
483
        sidepanel.appendChild(p);
211 by mattgiuca
fileservice/listing: Slight change to date format.
484
    }
485
486
}
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
487
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
488
/** Updates the side-panel and status bar to reflect the current set of
489
 * selected files. This is done by inspecting the states of the check boxes.
490
 * Also changes the styling to highlight selected files.
491
 */
492
function update_selection()
493
{
494
    /* First get a list of all files that are selected, and
495
     * reset the styling on each file's row. */
496
    var files_children = document.getElementById("files").childNodes;
497
    var tr;
498
    var checkbox;
499
    var row_toggle = 1;
500
    selected_files = [];  /* Clear global selected_files */
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
501
502
    var total_file_size = 0;    /* In bytes */
503
    var total_file_size_sel = 0;
504
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
505
    /* Children are trs */
237 by mattgiuca
listing: bugfix, update_selection no longer dependent on dictionary ordering
506
    var filename;
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
507
    var file;
508
    if (file_listing == null) return;
237 by mattgiuca
listing: bugfix, update_selection no longer dependent on dictionary ordering
509
    for (var i=0; i<files_children.length; i++)
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
510
    {
237 by mattgiuca
listing: bugfix, update_selection no longer dependent on dictionary ordering
511
        filename = files_children[i].filename;
512
        file = files_children[i].fileinfo;
513
        /* Count total file size so we can write to the status bar later
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
514
         */
515
        if ("size" in file)
516
            total_file_size += file.size;
517
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
518
        tr = files_children[i];
519
        checked = tr.firstChild.firstChild.checked;
520
        /* Set the class for every row based on both the checked state,
521
         * and whether it is odd or even */
522
        tr.setAttribute("class", "row" + row_toggle.toString() +
523
            (checked ? "sel" : ""))
524
        row_toggle = row_toggle == 1 ? 2 : 1;
525
        if (checked)
526
        {
527
            /* Add the filename (column 3) to the selected_files list */
528
            selected_files[selected_files.length] = filename;
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
529
            if ("size" in file)
530
                total_file_size_sel += file.size;
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
531
        }
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
532
    }
533
211 by mattgiuca
fileservice/listing: Slight change to date format.
534
    /* Write to the side-panel */
535
    update_sidepanel(total_file_size_sel);
536
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
537
    /* Write to the status bar */
538
    var statusbar = document.getElementById("statusbar");
539
    var statusmsg;
540
    var file_plural;
541
    if (selected_files.length > 0)
542
    {
543
        statusmsg = selected_files.length.toString() + " file"
544
            + (selected_files.length == 1 ? "" : "s") + " selected, "
545
            + nice_filesize(total_file_size_sel);
546
    }
547
    else
548
    {
237 by mattgiuca
listing: bugfix, update_selection no longer dependent on dictionary ordering
549
        statusmsg = files_children.length.toString() + " file"
550
            + (files_children.length == 1 ? "" : "s") + ", "
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
551
            + nice_filesize(total_file_size);
552
    }
553
    dom_removechildren(statusbar);
554
    statusbar.appendChild(document.createTextNode(statusmsg));
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
555
}
556
235 by mattgiuca
listing.js: Added sorting functions and a bit of infrastructure to facilitate
557
/** SORTING FUNCTIONS **/
558
559
/** Sorts the file table. Physically manipulates the DOM table to reflect the
560
 * sorted nodes, and also updates the little sort arrow.
561
 *
562
 * \param sort_field The name of the field to sort on primarily. This can
563
 * either be "filename", or one of the fields of a fileinfo object. Note that
564
 * while this determines the primary sort key, the secondary sort keys are
565
 * determined by the global sort_order. Calling sort_listing reorders
566
 * sort_order, bringing the specified sort_field to the top.
567
 * Also note that sorting by "isdir" is more prominent than whatever field is
568
 * provided here.
240 by mattgiuca
browser/listing.js: Wrote real comparison function and set up sort_order array
569
 * \param ascending If true, sorts ascending. If false, descending.
235 by mattgiuca
listing.js: Added sorting functions and a bit of infrastructure to facilitate
570
 */
240 by mattgiuca
browser/listing.js: Wrote real comparison function and set up sort_order array
571
function sort_listing(sort_field, ascending)
235 by mattgiuca
listing.js: Added sorting functions and a bit of infrastructure to facilitate
572
{
573
    var i;
574
    var files = document.getElementById("files");
575
    var files_children = files.childNodes;
576
    var files_array = new Array(files_children.length);
577
    /* Update sort_order, bringing sort_field to the top. */
240 by mattgiuca
browser/listing.js: Wrote real comparison function and set up sort_order array
578
    sort_order.removeall(sort_field);
579
    sort_order.push(sort_field);
580
    sort_ascending = ascending != false ? true : false;
235 by mattgiuca
listing.js: Added sorting functions and a bit of infrastructure to facilitate
581
582
    /* Build an array of DOM tr elements (with the additional 'filename' and
583
     * 'fileinfo' attributes as written when the listing is created). */
584
    /* Note: Must manually create an array from files_children, which is a DOM
585
     * NodeList, not an array. */
586
    for (i=0; i<files_children.length; i++)
587
        files_array[i] = files_children[i];
588
589
    /* Sort this array */
590
    files_array.sort(compare_files);
591
592
    /* Clean out the table (the TRs are safely stored in the array) */
593
    dom_removechildren(files);
594
595
    /* Insert the TRs back into the table, in their sorted order */
596
    for (i=0; i<files_array.length; i++)
597
        files.appendChild(files_array[i]);
598
599
    /* Fix the coloring classes on the rows so they are interleaved. */
600
    update_selection();
601
602
    return false;
603
}
604
605
/** Comparison function used for sorting. Compares two DOM tr nodes (with
606
 * the additional 'filename' and 'fileinfo' attributes as written when the
607
 * listing is created).
608
 * Returns an integer, which is -1 if a < b, 0 if a == b, and 1 if a > b.
609
 * The fields to compare by are determined by the global variable sort_order.
610
 */
611
function compare_files(a, b)
612
{
236 by mattgiuca
listing.js: Correct sorting by directory. (Now sorts all directories to the
613
    /* First sort by whether or not it is a directory */
614
    var aisdir = a.fileinfo.isdir == true;
615
    var bisdir = b.fileinfo.isdir == true;
240 by mattgiuca
browser/listing.js: Wrote real comparison function and set up sort_order array
616
    var LESS = sort_ascending == true ? -1 : 1;
617
    var GREATER = -LESS;
618
    if (aisdir > bisdir) return LESS;
619
    else if (aisdir < bisdir) return GREATER;
235 by mattgiuca
listing.js: Added sorting functions and a bit of infrastructure to facilitate
620
240 by mattgiuca
browser/listing.js: Wrote real comparison function and set up sort_order array
621
    /* Reverse order of sort_order. (top is highest priority) */
622
    for (var i=sort_order.length-1; i>=0; i--)
623
    {
624
        var field = sort_order[i];
625
        if (field == "filename")
626
        {
627
            if (a.filename < b.filename) return LESS;
628
            else if (a.filename > b.filename) return GREATER;
629
        }
630
        else
631
        {
632
            /* null > anything else (so it appears at the bottom) */
633
            if (!(field in a))
634
                if (field in b) return GREATER; else break;
635
            if (!(field in b)) return LESS;
636
            if (a.fileinfo[field] < b.fileinfo[field]) return LESS;
637
            else if (a.fileinfo[field] > b.fileinfo[field]) return GREATER;
638
        }
639
    }
235 by mattgiuca
listing.js: Added sorting functions and a bit of infrastructure to facilitate
640
641
    return 0;
642
}
643
644
/** END SORTING **/
645
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
646
/** Clears all selected files and causes the single file specified to become
647
 * selected.
234 by mattgiuca
browser/listing.js:
648
 * \param filename The file in the list to select.
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
649
 */
234 by mattgiuca
browser/listing.js:
650
function select_file(filename)
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
651
{
652
    var files_children = document.getElementById("files").childNodes;
653
    var checkbox;
234 by mattgiuca
browser/listing.js:
654
    var tr;
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
655
    for (var i=0; i<files_children.length; i++)
656
    {
657
        tr = files_children[i];
658
        checkbox = tr.firstChild.firstChild;
234 by mattgiuca
browser/listing.js:
659
        checkbox.checked = tr.filename == filename;
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
660
    }
661
    update_selection();
662
}
663
664
/** Initialises the DOM elements required to present a dir listing,
665
 * assuming that clear_page has just been called or the page just
666
 * loaded for the first time.
667
 */
668
function setup_for_dir_listing()
669
{
670
    var filesbody = document.getElementById("filesbody");
671
381 by mattgiuca
media/browser/listing.js: Replaced table-based layout in file listing with
672
    /* There are 2 divs in the filesbody: middle and statusbar
673
     * middle has 2 divs: filetable, sidepanel
674
     */
675
    /* Middle */
676
    var middle = document.createElement("div");
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
677
    filesbody.appendChild(middle);
678
    middle.setAttribute("id", "middle");
381 by mattgiuca
media/browser/listing.js: Replaced table-based layout in file listing with
679
    /* File table */
680
    var filetable = document.createElement("div");
681
    middle.appendChild(filetable);
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
682
    filetable.setAttribute("id", "filetable");
683
    var filetablediv = document.createElement("div");
684
    filetable.appendChild(filetablediv);
685
    filetablediv.setAttribute("id", "filetablediv");
686
    /* A nested table within this div - the actual files listing */
687
    var filetabletable = document.createElement("table");
688
    filetablediv.appendChild(filetabletable);
689
    filetabletable.setAttribute("width", "100%");
690
    var filetablethead = document.createElement("thead");
691
    filetabletable.appendChild(filetablethead);
692
    var filetablethead_tr = document.createElement("tr");
693
    filetablethead.appendChild(filetablethead_tr);
694
    filetablethead_tr.setAttribute("class", "rowhead");
695
    /* Row headers */
696
    var filetablethead_th = document.createElement("th");
697
    filetablethead_tr.appendChild(filetablethead_th);
698
    filetablethead_th.setAttribute("class", "col-check");
699
    filetablethead_th = dom_make_link_elem("th", "Filename",
235 by mattgiuca
listing.js: Added sorting functions and a bit of infrastructure to facilitate
700
        "Sort by filename", null, "return sort_listing(\"filename\")");
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
701
    filetablethead_tr.appendChild(filetablethead_th);
702
    filetablethead_th.setAttribute("class", "col-filename");
703
    filetablethead_th.setAttribute("colspan", 3);
704
    filetablethead_th = dom_make_link_elem("th", "Size",
235 by mattgiuca
listing.js: Added sorting functions and a bit of infrastructure to facilitate
705
        "Sort by file size", null, "return sort_listing(\"size\")");
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
706
    filetablethead_tr.appendChild(filetablethead_th);
707
    filetablethead_th.setAttribute("class", "col-size");
708
    filetablethead_th = dom_make_link_elem("th", "Modified",
235 by mattgiuca
listing.js: Added sorting functions and a bit of infrastructure to facilitate
709
        "Sort by date modified", null, "return sort_listing(\"mtime\")");
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
710
    filetablethead_tr.appendChild(filetablethead_th);
711
    filetablethead_th.setAttribute("class", "col-date");
712
    /* Empty body */
713
    var filetabletbody = document.createElement("tbody");
714
    filetabletable.appendChild(filetabletbody);
715
    filetabletbody.setAttribute("id", "files");
716
381 by mattgiuca
media/browser/listing.js: Replaced table-based layout in file listing with
717
    /* Side-panel */
718
    /* 2 nested divs, so we can set the width exactly and have padding inside
719
     * of that */
720
    var sidepanel_outer = document.createElement("div");
721
    middle.appendChild(sidepanel_outer);
722
    sidepanel_outer.setAttribute("id", "sidepanel_outer");
723
    var sidepanel = document.createElement("div");
724
    sidepanel_outer.appendChild(sidepanel);
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
725
    sidepanel.setAttribute("id", "sidepanel");
726
727
    /* Now after the table "middle", there is a status bar */
381 by mattgiuca
media/browser/listing.js: Replaced table-based layout in file listing with
728
    var statusbar_outer = document.createElement("div");
729
    filesbody.appendChild(statusbar_outer);
730
    statusbar_outer.setAttribute("id", "statusbar_outer");
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
731
    var statusbar = document.createElement("div");
381 by mattgiuca
media/browser/listing.js: Replaced table-based layout in file listing with
732
    statusbar_outer.appendChild(statusbar);
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
733
    statusbar.setAttribute("id", "statusbar");
734
}
735
736
/** Presents the directory listing.
737
 */
738
function handle_dir_listing(path, listing)
739
{
740
    setmode(false);
741
    setup_for_dir_listing();
742
    var row_toggle = 1;
743
    /* Nav through the top-level of the JSON to the actual listing object. */
744
    var listing = listing.listing;
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
745
    file_listing = listing;     /* Global */
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
746
747
    /* Get "." out, it's special */
211 by mattgiuca
fileservice/listing: Slight change to date format.
748
    thisdir = listing["."];     /* Global */
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
749
    delete listing["."];
750
    /* Is this dir under svn? */
751
    var under_subversion = "svnstatus" in thisdir;
752
753
    var files = document.getElementById("files");
754
    var file;
755
    var row;
756
    var td;
757
    var checkbox;
758
759
    var selection_string;
760
386 by mattgiuca
browser/listing: Fixed long-standing bug, files get deselected when you
761
    /* Convert selected_files array into a dictionary which can be efficiently
762
     * searched. */
763
    sel_files_dict = {};
764
    for (var i=0; i<selected_files.length; i++)
765
    {
766
        sel_files_dict[selected_files[i]] = true;
767
    }
768
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
769
    /* Create all of the files */
770
    for (var filename in listing)
771
    {
234 by mattgiuca
browser/listing.js:
772
        selection_string = "select_file(" + repr(filename) + ")";
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
773
        file = listing[filename];
234 by mattgiuca
browser/listing.js:
774
        /* Make a 'tr' element. Store the filename and fileinfo in
775
         * here. */
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
776
        row = document.createElement("tr");
234 by mattgiuca
browser/listing.js:
777
        row.filename = filename;
778
        row.fileinfo = file;
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
779
        /* Column 1: Selection checkbox */
780
        row.setAttribute("class", "row" + row_toggle.toString())
781
        row_toggle = row_toggle == 1 ? 2 : 1;
782
        td = document.createElement("td");
783
        checkbox = document.createElement("input");
784
        checkbox.setAttribute("type", "checkbox");
785
        checkbox.setAttribute("title", "Select this file");
786
        checkbox.setAttribute("onchange", "update_selection()");
386 by mattgiuca
browser/listing: Fixed long-standing bug, files get deselected when you
787
        /* Check the box if selected_files says it's selected */
788
        checkbox.checked = filename in sel_files_dict;
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
789
        td.appendChild(checkbox);
790
        row.appendChild(td);
791
        if (file.isdir)
792
        {
793
            /* Column 2: Filetype and subversion icons. */
794
            td = document.createElement("td");
795
            td.setAttribute("class", "thincol");
796
            td.setAttribute("onclick", selection_string);
797
            td.appendChild(dom_make_img(mime_type_to_icon("text/directory"),
229 by mattgiuca
Images: Reduced "small" icons from 22x22 to 16x16. Reduced "large" icons from
798
                icon_size, icon_size, file.type_nice));
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
799
            row.appendChild(td);
800
            td = document.createElement("td");
801
            td.setAttribute("class", "thincol");
802
            if (under_subversion)
230 by mattgiuca
Removed "unversioned" icon from subversion status. Now unversioned files do
803
            {
804
                var icon = svnstatus_to_icon(file.svnstatus);
805
                if (icon)
806
                    td.appendChild(dom_make_img(icon, icon_size, icon_size,
807
                        svnstatus_to_string(file.svnstatus)));
808
            }
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
809
            row.appendChild(td);
810
            /* Column 3: Filename */
811
            td = dom_make_link_elem("td", filename,
812
                "Navigate to " + path_join(path, filename),
224 by mattgiuca
util.js: Removed urlencoding support from "encoded_app_path" (now called
813
                app_path(this_app, path, filename)/*,
215 by mattgiuca
listing.js:
814
                "navigate(" + repr(path_join(path, filename)) + ")"*/);
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
815
            td.setAttribute("onclick", selection_string);
816
            row.appendChild(td);
817
        }
818
        else
819
        {
820
            /* Column 2: Filetype and subversion icons. */
821
            td = document.createElement("td");
822
            td.setAttribute("class", "thincol");
823
            td.appendChild(dom_make_img(mime_type_to_icon(file.type),
229 by mattgiuca
Images: Reduced "small" icons from 22x22 to 16x16. Reduced "large" icons from
824
                icon_size, icon_size, file.type_nice));
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
825
            row.appendChild(td);
826
            td = document.createElement("td");
827
            td.setAttribute("class", "thincol");
828
            if (under_subversion)
230 by mattgiuca
Removed "unversioned" icon from subversion status. Now unversioned files do
829
            {
830
                var icon = svnstatus_to_icon(file.svnstatus);
831
                if (icon)
832
                    td.appendChild(dom_make_img(icon, icon_size, icon_size,
833
                        svnstatus_to_string(file.svnstatus)));
834
            }
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
835
            row.appendChild(td);
836
            /* Column 3: Filename */
837
            td = dom_make_text_elem("td", filename);
838
            td.setAttribute("onclick", selection_string);
839
            row.appendChild(td);
840
        }
841
        /* Column 4: Size */
842
        td = dom_make_text_elem("td", nice_filesize(file.size));
843
        td.setAttribute("onclick", selection_string);
844
        row.appendChild(td);
845
        /* Column 4: Date */
846
        td = dom_make_text_elem("td", file.mtime_short, file.mtime_nice);
847
        td.setAttribute("onclick", selection_string);
848
        row.appendChild(td);
849
        files.appendChild(row);
850
    }
851
237 by mattgiuca
listing: bugfix, update_selection no longer dependent on dictionary ordering
852
    /* Apply an initial sort by filename */
853
    sort_listing("filename");
854
210 by mattgiuca
File browser: listing.js - The status bar is now updated whenever the
855
    /* Do a selection update (create initial elements for side panel and
856
     * status bar). */
237 by mattgiuca
listing: bugfix, update_selection no longer dependent on dictionary ordering
857
    /* Commented out; already called by sort_listing */
858
    /*update_selection();*/
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
859
}
860