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

170 by mattgiuca
browser: Added CSS and JS files (not much in them).
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: File Browser (client)
19
 * Author: Matt Giuca
20
 * Date: 11/1/2008
21
 */
22
188 by mattgiuca
browser.js: Can now (shakily) handle directory listings. (lots of code!)
23
/* Url names for apps */
24
this_app = "files";
208 by mattgiuca
dispatch.html, ivle.css: "apptabs" is now an ID, not a class.
25
edit_app = "edit";
188 by mattgiuca
browser.js: Can now (shakily) handle directory listings. (lots of code!)
26
service_app = "fileservice";
205 by mattgiuca
browser.js: Added appropriate handlers for error, text, and binary data.
27
serve_app = "serve";
28
download_app = "download";
188 by mattgiuca
browser.js: Can now (shakily) handle directory listings. (lots of code!)
29
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
30
/* Mapping MIME types onto handlers.
31
 * "text" : When navigating to a text file, the text editor is opened.
32
 * "image" : When navigating to an image, the image is displayed (rather than
33
 *              going to the text editor).
34
 * "audio" : When navigating to an audio file, a "play" button is presented.
35
 * "binary" : When navigating to a binary file, offer it as a download through
36
 *              "serve".
37
 *
38
 * If a file is not on the list, its default action is determined by the first
39
 * part of its content type, where "text/*", "image/*" and "audio/*" are
40
 * treated as above, and other types are simply treated as binary.
41
 */
42
type_handlers = {
43
    "application/x-javascript" : "text",
44
    "application/javascript" : "text",
45
    "application/json" : "text",
489 by agdimech
browser.js, editor.js: Fixed the syntax error which was resulting due to a trailing comma.
46
    "application/xml" : "text"
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
47
};
48
190 by mattgiuca
util: Added function dom_make_img, creates <img> elements.
49
/* Mapping MIME types to icons, just the file's basename */
50
type_icons = {
51
    "text/directory": "dir.png",
489 by agdimech
browser.js, editor.js: Fixed the syntax error which was resulting due to a trailing comma.
52
    "text/x-python": "py.png"
190 by mattgiuca
util: Added function dom_make_img, creates <img> elements.
53
};
54
55
default_type_icon = "txt.png";
56
57
/* Relative to IVLE root */
58
type_icons_path = "media/images/mime";
59
type_icons_path_large = "media/images/mime/large";
60
61
/* Mapping SVN status to icons, just the file's basename */
62
svn_icons = {
230 by mattgiuca
Removed "unversioned" icon from subversion status. Now unversioned files do
63
    "unversioned": null,
190 by mattgiuca
util: Added function dom_make_img, creates <img> elements.
64
    "normal": "normal.png",
213 by mattgiuca
Fileservice / Files (Python and JS files):
65
    "added": "added.png",
66
    "missing": "missing.png",
67
    "deleted": "deleted.png",
578 by dcoles
fileservice: Added code to allow browing of past revisons in the File Browser
68
    "modified": "modified.png",
69
    "revision": "revision.png"
190 by mattgiuca
util: Added function dom_make_img, creates <img> elements.
70
};
71
213 by mattgiuca
Fileservice / Files (Python and JS files):
72
/* Mapping SVN status to "nice" strings */
73
svn_nice = {
74
    "unversioned": "Temporary file",
75
    "normal": "Permanent file",
76
    "added": "Temporary file (scheduled to be added)",
77
    "missing": "Permanent file (missing)",
78
    "deleted": "Permanent file (scheduled for deletion)",
79
    "replaced": "Permanent file (replaced)",
80
    "modified": "Permanent file (modified)",
81
    "merged": "Permanent file (merged)",
578 by dcoles
fileservice: Added code to allow browing of past revisons in the File Browser
82
    "conflicted": "Permanent file (conflicted)",
83
    "revision": "Past Permanent file (revision)"
213 by mattgiuca
Fileservice / Files (Python and JS files):
84
};
85
230 by mattgiuca
Removed "unversioned" icon from subversion status. Now unversioned files do
86
default_svn_icon = null;
213 by mattgiuca
Fileservice / Files (Python and JS files):
87
default_svn_nice = "Unknown status";
190 by mattgiuca
util: Added function dom_make_img, creates <img> elements.
88
89
svn_icons_path = "media/images/svn";
90
269 by mattgiuca
browser: Added publish functionality to JavaScript client side.
91
published_icon = "media/images/interface/published.png";
92
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
93
/* List of MIME types considered "executable" by the system.
94
 * Executable files offer a "run" link, implying that the "serve"
95
 * application can interpret them.
96
 */
97
types_exec = [
489 by agdimech
browser.js, editor.js: Fixed the syntax error which was resulting due to a trailing comma.
98
    "text/x-python"
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
99
];
100
211 by mattgiuca
fileservice/listing: Slight change to date format.
101
102
/* Global variables */
103
602 by mattgiuca
util.js: Added shallow_copy_object function.
104
/** The listing object returned by the server as JSON */
105
file_listing = null;
106
current_file = null;
211 by mattgiuca
fileservice/listing: Slight change to date format.
107
current_path = "";
108
597 by mattgiuca
Major refactor of actions in File browser.
109
/** Filenames of all files selected
110
 * (Only used by dir listings, but still needs to be [] for files, so that
111
 * update_actions knows that nothing is selected).
112
 */
113
selected_files = [];
114
622 by mattgiuca
browser.js: Reinstated upload_callback (previously removed from listing.js).
115
upload_callback_count = 0;      /* See upload_callback */
116
181 by mattgiuca
browser.js: Added functions do_action, navigate, handle_response (stubs).
117
/** Calls the server using Ajax, performing an action on the server side.
118
 * Receives the response from the server and performs a refresh of the page
119
 * contents, updating it to display the returned data (such as a directory
120
 * listing, file preview, or editor pane).
121
 * Always makes a POST request.
122
 * No return value.
123
 *
124
 * \param action String. Name of the action to perform, as defined in the
125
 *     fileservice API.
126
 * \param path URL path to make the request to, within the application.
127
 * \param args Argument object, as described in util.parse_url and friends.
128
 *      This should contain the arguments to the action, but NOT the action
129
 *      itself. (Also a minor side-effect; the "args" object will be mutated
130
 *      to include the action attribute).
131
 * \param content_type String, optional.
132
 *      May be "application/x-www-form-urlencoded" or "multipart/form-data".
133
 *      Defaults to "application/x-www-form-urlencoded".
134
 *      "multipart/form-data" is recommended for large uploads.
135
 */
385 by mattgiuca
browser/editor: Clicking "Save" in the editor does not cause a page refresh
136
function do_action(action, path, args, content_type, ignore_response)
181 by mattgiuca
browser.js: Added functions do_action, navigate, handle_response (stubs).
137
{
138
    args.action = action;
559 by mattgiuca
Major JavaScript refactor: util.ajax_call is now asynchronous, not
139
    /* Callback action, when the server returns */
140
    var callback = function(response)
141
        {
142
            /* Check for action errors reported by the server, and report them
143
             * to the user */
144
            var error = response.getResponseHeader("X-IVLE-Action-Error");
145
            if (error != null)
785 by mattgiuca
fileservice_lib/__init__.py: X-IVLE-Action-Error HTTP response header is now
146
                /* Note: This header (in particular) comes URI-encoded, to
147
                 * allow multi-line error messages. Decode */
148
                alert("Error: " + decodeURIComponent(error.toString()) + ".");
559 by mattgiuca
Major JavaScript refactor: util.ajax_call is now asynchronous, not
149
            /* Now read the response and set up the page accordingly */
150
            if (ignore_response != true)
789 by mattgiuca
browser.js: Replaced the old "The server returned an invalid directory
151
                handle_response(path, response, true);
559 by mattgiuca
Major JavaScript refactor: util.ajax_call is now asynchronous, not
152
        }
181 by mattgiuca
browser.js: Added functions do_action, navigate, handle_response (stubs).
153
    /* Call the server and perform the action. This mutates the server. */
559 by mattgiuca
Major JavaScript refactor: util.ajax_call is now asynchronous, not
154
    ajax_call(callback, service_app, path, args, "POST", content_type);
181 by mattgiuca
browser.js: Added functions do_action, navigate, handle_response (stubs).
155
}
156
157
/** Calls the server using Ajax, requesting a directory listing. This should
158
 * not modify the server in any way. Receives the response from the server and
159
 * performs a refresh of the page contents, updating it to display the
160
 * returned data (such as a directory listing, file preview, or editor pane).
161
 * Called "navigate", can also be used for a simple refresh.
162
 * Always makes a GET request.
163
 * No return value.
164
 */
603 by mattgiuca
Console now starts up in the user's home directory.
165
function navigate(path)
181 by mattgiuca
browser.js: Added functions do_action, navigate, handle_response (stubs).
166
{
559 by mattgiuca
Major JavaScript refactor: util.ajax_call is now asynchronous, not
167
    callback = function(response)
168
        {
169
            /* Read the response and set up the page accordingly */
789 by mattgiuca
browser.js: Replaced the old "The server returned an invalid directory
170
            handle_response(path, response, false, url.args);
559 by mattgiuca
Major JavaScript refactor: util.ajax_call is now asynchronous, not
171
        }
578 by dcoles
fileservice: Added code to allow browing of past revisons in the File Browser
172
    /* Get any query strings */
173
    url = parse_url(window.location.href);
174
    
602 by mattgiuca
util.js: Added shallow_copy_object function.
175
    /* Call the server and request the listing. */
578 by dcoles
fileservice: Added code to allow browing of past revisons in the File Browser
176
    ajax_call(callback, service_app, path, url.args, "GET");
181 by mattgiuca
browser.js: Added functions do_action, navigate, handle_response (stubs).
177
}
178
597 by mattgiuca
Major refactor of actions in File browser.
179
/* Refreshes the current view.
180
 * Calls navigate on the current path.
181
 */
182
function refresh()
183
{
617 by mattgiuca
Added upload panel to the topbar instead of being on the side.
184
    navigate(current_path);
597 by mattgiuca
Major refactor of actions in File browser.
185
}
186
211 by mattgiuca
fileservice/listing: Slight change to date format.
187
/** Determines the "handler type" from a MIME type.
188
 * The handler type is a string, either "text", "image", "audio" or "binary".
189
 */
190
function get_handler_type(content_type)
191
{
192
    if (!content_type)
193
        return null;
194
    if (content_type in type_handlers)
195
        return type_handlers[content_type];
196
    else
197
    {   /* Based on the first part of the MIME type */
198
        var handler_type = content_type.split('/')[0];
199
        if (handler_type != "text" && handler_type != "image" &&
200
            handler_type != "audio")
201
            handler_type = "binary";
202
        return handler_type;
203
    }
204
}
205
181 by mattgiuca
browser.js: Added functions do_action, navigate, handle_response (stubs).
206
/** Given an HTTP response object, cleans up and rebuilds the contents of the
207
 * page using the response data. This does not navigate away from the page, it
208
 * merely rebuilds most of the data.
209
 * Note that depending on the type of data returned, this could result in a
210
 * directory listing, an image preview, an editor pane, etc.
211
 * Figures out the type and calls the appropriate function.
212
 * \param path URL path which the request was made for. This can (among other
213
 * things) be used to update the URL in the location bar.
214
 * \param response XMLHttpRequest object returned by the server. Should
215
 * contain all the response data.
789 by mattgiuca
browser.js: Replaced the old "The server returned an invalid directory
216
 * \param is_action Boolean. True if this is the response to an action, false
217
 * if this is the response to a simple listing. This is used in handling the
218
 * error.
602 by mattgiuca
util.js: Added shallow_copy_object function.
219
 * \param url_args Arguments dict, for the arguments passed to the URL
220
 * in the browser's address bar (will be forwarded along).
181 by mattgiuca
browser.js: Added functions do_action, navigate, handle_response (stubs).
221
 */
789 by mattgiuca
browser.js: Replaced the old "The server returned an invalid directory
222
function handle_response(path, response, is_action, url_args)
181 by mattgiuca
browser.js: Added functions do_action, navigate, handle_response (stubs).
223
{
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
224
    /* TODO: Set location bar to "path" */
211 by mattgiuca
fileservice/listing: Slight change to date format.
225
    current_path = path;
189 by mattgiuca
browser.js: Top-level handler now presents the path nav panel, not dir
226
227
    /* Clear away the existing page contents */
228
    clearpage();
229
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
230
    /* Check the status, and if not 200, read the error and handle this as an
231
     * error. */
232
    if (response.status != 200)
233
    {
234
        var error = response.getResponseHeader("X-IVLE-Return-Error");
235
        if (error == null)
236
            error = response.statusText;
237
        handle_error(error);
238
        return;
239
    }
240
602 by mattgiuca
util.js: Added shallow_copy_object function.
241
    /* This will always return a listing, whether it is a dir or a file.
242
     */
243
    var listing = response.responseText;
244
    /* The listing SHOULD be valid JSON text. Parse it into an object. */
245
    try
246
    {
247
        listing = JSON.parse(listing);
248
        file_listing = listing.listing;     /* Global */
249
    }
250
    catch (e)
251
    {
789 by mattgiuca
browser.js: Replaced the old "The server returned an invalid directory
252
        if (is_action)
253
        {
254
            var err = document.createElement("div");
255
            var p = dom_make_text_elem("p", "Error: "
256
                    + "There was an unexpected server error processing "
257
                    + "the selected command.");
258
            err.appendChild(p);
259
            p = dom_make_text_elem("p", "If the problem persists, please "
260
                    + "contact the system administrator.")
261
            err.appendChild(p);
262
            p = document.createElement("p");
263
            var refresh = document.createElement("input");
264
            refresh.setAttribute("type", "button");
265
            refresh.setAttribute("value", "Back to file view");
266
            refresh.setAttribute("onclick", "refresh()");
267
            p.appendChild(refresh);
268
            err.appendChild(p);
269
            handle_error(err);
270
        }
271
        else
272
        {
273
            var err = document.createElement("div");
274
            var p = dom_make_text_elem("p", "Error: "
275
                    + "There was an unexpected server error retrieving "
276
                    + "the requested file or directory.");
277
            err.appendChild(p);
278
            p = dom_make_text_elem("p", "If the problem persists, please "
279
                    + "contact the system administrator.")
280
            err.appendChild(p);
281
            handle_error(err);
282
        }
602 by mattgiuca
util.js: Added shallow_copy_object function.
283
        return;
284
    }
285
    /* Get "." out, it's special */
286
    current_file = file_listing["."];     /* Global */
287
    delete file_listing["."];
288
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
289
    /* Check if this is a directory listing or file contents */
222 by mattgiuca
browser.js:
290
    var isdir = response.getResponseHeader("X-IVLE-Return") == "Dir";
602 by mattgiuca
util.js: Added shallow_copy_object function.
291
    if (isdir)
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
292
    {
293
        handle_dir_listing(path, listing);
294
    }
295
    else
296
    {
602 by mattgiuca
util.js: Added shallow_copy_object function.
297
        /* Need to make a 2nd ajax call, this time get the actual file
298
         * contents */
299
        callback = function(response)
300
            {
301
                /* Read the response and set up the page accordingly */
302
                handle_contents_response(path, response);
303
            }
304
        /* Call the server and request the listing. */
305
        if (url_args)
306
            args = shallow_clone_object(url_args);
307
        else
308
            args = {};
309
        /* This time, get the contents of the file, not its metadata */
310
        args['return'] = "contents";
311
        ajax_call(callback, service_app, path, args, "GET");
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
312
    }
597 by mattgiuca
Major refactor of actions in File browser.
313
    update_actions(isdir);
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
314
}
315
602 by mattgiuca
util.js: Added shallow_copy_object function.
316
function handle_contents_response(path, response)
317
{
318
    /* Treat this as an ordinary file. Get the file type. */
319
    var content_type = response.getResponseHeader("Content-Type");
320
    var handler_type = get_handler_type(content_type);
321
    would_be_handler_type = handler_type;
322
    /* handler_type should now be set to either
323
     * "text", "image", "audio" or "binary". */
324
    switch (handler_type)
325
    {
326
    case "text":
327
        handle_text(path, response.responseText,
328
            would_be_handler_type);
329
        break;
330
    case "image":
331
        /* TODO: Custom image handler */
332
        handle_binary(path, response.responseText);
333
        break;
334
    case "audio":
335
        /* TODO: Custom audio handler */
336
        handle_binary(path, response.responseText);
337
        break;
338
    case "binary":
339
        handle_binary(path);
340
        break;
341
    }
342
}
343
622 by mattgiuca
browser.js: Reinstated upload_callback (previously removed from listing.js).
344
/* Called when a form upload comes back (from an iframe).
345
 * Refreshes the page.
346
 */
347
function upload_callback()
348
{
349
    /* This has a pretty nasty hack, which happens to work.
350
     * upload_callback is set as the "onload" callback for the iframe which
351
     * receives the response from the server for uploading a file.
352
     * This means it gets called twice. Once when initialising the iframe, and
353
     * a second time when the actual response comes back.
354
     * All we want to do is call navigate to refresh the page. But we CAN'T do
355
     * that on the first load or it will just go into an infinite cycle of
356
     * refreshing. We need to refresh the page ONLY on the second refresh.
357
     * upload_callback_count is reset to 0 just before the iframe is created.
358
     */
359
    upload_callback_count++;
360
    if (upload_callback_count >= 2)
361
        refresh();
362
}
363
188 by mattgiuca
browser.js: Can now (shakily) handle directory listings. (lots of code!)
364
/** Deletes all "dynamic" content on the page.
365
 * This returns the page back to the state it is in when the HTML arrives to
366
 * the browser, ready for another handler to populate it.
367
 */
368
function clearpage()
369
{
203 by mattgiuca
browser: Removed all directory-listing specific HTML from the Python-generated
370
    dom_removechildren(document.getElementById("filesbody"));
371
}
372
373
/** Deletes all "dynamic" content on the page necessary to navigate from
374
 * one directory listing to another (does not clear as much as clearpage
375
 * does).
376
 * This is the equivalent of calling clearpage() then
377
 * setup_for_dir_listing(), assuming the page is already on a dir listing.
378
 */
379
function clearpage_dir()
380
{
188 by mattgiuca
browser.js: Can now (shakily) handle directory listings. (lots of code!)
381
    dom_removechildren(document.getElementById("path"));
382
    dom_removechildren(document.getElementById("files"));
383
    dom_removechildren(document.getElementById("sidepanel"));
384
}
385
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
386
/*** HANDLERS for different types of responses (such as dir listing, file,
387
 * etc). */
388
789 by mattgiuca
browser.js: Replaced the old "The server returned an invalid directory
389
/* handle_error.
390
 * message may either be a string, or a DOM node, which will be placed inside
391
 * a div.
392
 */
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
393
function handle_error(message)
394
{
205 by mattgiuca
browser.js: Added appropriate handlers for error, text, and binary data.
395
    var files = document.getElementById("filesbody");
789 by mattgiuca
browser.js: Replaced the old "The server returned an invalid directory
396
    var txt_elem;
397
    if (typeof(message) == "string")
398
    {
399
        txt_elem = dom_make_text_elem("div", "Error: "
400
                   + message.toString() + ".")
401
    }
402
    else
403
    {
404
        /* Assume message is a DOM node */
405
        txt_elem = document.createElement("div");
406
        txt_elem.appendChild(message);
407
    }
205 by mattgiuca
browser.js: Added appropriate handlers for error, text, and binary data.
408
    txt_elem.setAttribute("class", "padding error");
409
    files.appendChild(txt_elem);
188 by mattgiuca
browser.js: Can now (shakily) handle directory listings. (lots of code!)
410
}
411
190 by mattgiuca
util: Added function dom_make_img, creates <img> elements.
412
/** Given a mime type, returns the path to the icon.
413
 * \param type String, Mime type.
414
 * \param sizelarge Boolean, optional.
415
 * \return Path to the icon. Has applied make_path, so it is relative to site
416
 * root.
417
 */
418
function mime_type_to_icon(type, sizelarge)
419
{
420
    var filename;
421
    if (type in type_icons)
422
        filename = type_icons[type];
423
    else
424
        filename = default_type_icon;
425
    if (sizelarge)
426
        return make_path(path_join(type_icons_path_large, filename));
427
    else
428
        return make_path(path_join(type_icons_path, filename));
429
}
430
431
/** Given an svnstatus, returns the path to the icon.
432
 * \param type String, svn status.
433
 * \return Path to the icon. Has applied make_path, so it is relative to site
230 by mattgiuca
Removed "unversioned" icon from subversion status. Now unversioned files do
434
 * root. May return null to indicate no SVN icon.
190 by mattgiuca
util: Added function dom_make_img, creates <img> elements.
435
 */
436
function svnstatus_to_icon(svnstatus)
437
{
438
    var filename;
439
    if (svnstatus in svn_icons)
440
        filename = svn_icons[svnstatus];
441
    else
442
        filename = default_svn_icon;
230 by mattgiuca
Removed "unversioned" icon from subversion status. Now unversioned files do
443
    if (filename == null) return null;
190 by mattgiuca
util: Added function dom_make_img, creates <img> elements.
444
    return make_path(path_join(svn_icons_path, filename));
445
}
446
213 by mattgiuca
Fileservice / Files (Python and JS files):
447
/** Given an svnstatus, returns the "nice" string.
448
 */
449
function svnstatus_to_string(svnstatus)
450
{
451
    if (svnstatus in svn_nice)
452
        return svn_nice[svnstatus];
453
    else
454
        return default_svn_nice;
455
}
456
183 by mattgiuca
browser: Added top level of response handling. Now determines handler type and
457
/** Displays a download link to the binary file.
458
 */
459
function handle_binary(path)
460
{
205 by mattgiuca
browser.js: Added appropriate handlers for error, text, and binary data.
461
    var files = document.getElementById("filesbody");
462
    var div = document.createElement("div");
463
    files.appendChild(div);
464
    div.setAttribute("class", "padding");
224 by mattgiuca
util.js: Removed urlencoding support from "encoded_app_path" (now called
465
    var download_link = app_path(download_app, path);
205 by mattgiuca
browser.js: Added appropriate handlers for error, text, and binary data.
466
    var par1 = dom_make_text_elem("p",
467
        "The file " + path + " is a binary file. To download this file, " +
468
        "click the following link:");
469
    var par2 = dom_make_link_elem("p",
470
        "Download " + path, "Download " + path, download_link);
471
    div.appendChild(par1);
472
    div.appendChild(par2);
181 by mattgiuca
browser.js: Added functions do_action, navigate, handle_response (stubs).
473
}
474
597 by mattgiuca
Major refactor of actions in File browser.
475
function update_actions()
476
{
477
    var file;
478
    var numsel = selected_files.length;
479
    if (numsel <= 1)
480
    {
481
        if (numsel == 0)
482
        {
483
            /* Display information about the current directory instead */
484
            filename = path_basename(current_path);
602 by mattgiuca
util.js: Added shallow_copy_object function.
485
            file = current_file;
597 by mattgiuca
Major refactor of actions in File browser.
486
        }
487
        else if (numsel == 1)
488
        {
489
            filename = selected_files[0];
490
            file = file_listing[filename];
491
        }
492
493
        /* Update each action node in the topbar.
494
         * This includes enabling/disabling actions as appropriate, and
495
         * setting href/onclick attributes. */
496
    }
497
498
    /* Open */
499
    /* Available if exactly one file is selected */
500
    var open = document.getElementById("act_open");
501
    if (numsel == 1)
502
    {
503
        open.setAttribute("class", "choice");
504
        if (file.isdir)
505
            open.setAttribute("title",
506
                "Navigate to this directory in the file browser");
507
        else
508
            open.setAttribute("title",
509
                "Edit or view this file");
510
        open.setAttribute("href", app_path(this_app, current_path, filename));
511
    }
512
    else
513
    {
514
        open.setAttribute("class", "disabled");
515
        open.removeAttribute("title");
516
        open.removeAttribute("href");
517
    }
518
519
    /* Serve */
775 by mattgiuca
browser.js: Updated logic to decide when to enable/disable certain actions.
520
    /* Available if zero or one files are selected,
597 by mattgiuca
Major refactor of actions in File browser.
521
     * and only if this is a file, not a directory */
522
    var serve = document.getElementById("act_serve");
775 by mattgiuca
browser.js: Updated logic to decide when to enable/disable certain actions.
523
    if (numsel <= 1 && !file.isdir)
597 by mattgiuca
Major refactor of actions in File browser.
524
    {
525
        serve.setAttribute("class", "choice");
775 by mattgiuca
browser.js: Updated logic to decide when to enable/disable certain actions.
526
        if (numsel == 0)
527
            serve.setAttribute("href",
528
                app_path(serve_app, current_path));
529
        else
530
            serve.setAttribute("href",
531
                app_path(serve_app, current_path, filename));
597 by mattgiuca
Major refactor of actions in File browser.
532
    }
533
    else
534
    {
535
        serve.setAttribute("class", "disabled");
536
        serve.removeAttribute("href");
537
    }
538
539
    /* Run */
540
    /* Available if exactly one file is selected,
541
     * and it is a Python file.
542
     */
713 by dcoles
browser: Added console and run button to allow Python files to be executed in
543
    var run = document.getElementById("act_run");
544
     
545
    if (numsel == 0 && !file.isdir && file.type == "text/x-python")
546
    {
547
        // In the edit window
548
        run.setAttribute("class", "choice");
549
        localpath = app_path('home',current_path);
550
        run.setAttribute("onclick", "runfile('" + localpath + "')");
551
    }
552
    else if (numsel == 1 && !file.isdir && file.type == "text/x-python")
553
    {
554
        // In the browser window
555
        run.setAttribute("class", "choice");
556
        localpath = app_path('home',current_path,filename);
557
        run.setAttribute("onclick", "runfile('" + localpath + "')");
558
    }
559
    else
560
    {
561
        run.setAttribute("class", "disabled");
562
        run.removeAttribute("onclick");
563
    }
597 by mattgiuca
Major refactor of actions in File browser.
564
565
    /* Download */
566
    /* Always available.
567
     * If 0 files selected, download the current file or directory as a ZIP.
568
     * If 1 directory selected, download it as a ZIP.
569
     * If 1 non-directory selected, download it.
570
     * If >1 files selected, download them all as a ZIP.
571
     */
572
    var download = document.getElementById("act_download");
573
    if (numsel <= 1)
574
    {
575
        if (numsel == 0)
576
        {
577
            download.setAttribute("href",
578
                app_path(download_app, current_path));
579
            if (file.isdir)
580
                download.setAttribute("title",
581
                    "Download the current directory as a ZIP file");
582
            else
583
                download.setAttribute("title",
584
                    "Download the current file");
585
        }
586
        else
587
        {
588
            download.setAttribute("href",
589
                app_path(download_app, current_path, filename));
590
            if (file.isdir)
591
                download.setAttribute("title",
592
                    "Download the selected directory as a ZIP file");
593
            else
594
                download.setAttribute("title",
595
                    "Download the selected file");
596
        }
597
    }
598
    else
599
    {
600
        /* Make a query string with all the files to download */
601
        var dlpath = urlencode_path(app_path(download_app, current_path)) + "?";
602
        for (var i=0; i<numsel; i++)
603
            dlpath += "path=" + encodeURIComponent(selected_files[i]) + "&";
604
        dlpath = dlpath.substr(0, dlpath.length-1);
605
        download.setAttribute("href", dlpath);
606
        download.setAttribute("title",
607
            "Download the selected files as a ZIP file");
608
    }
609
610
    /* Refresh - No changes required */
611
612
    /* Publish and Submit */
613
    /* If this directory is under subversion and selected/unselected file is a
614
     * directory. */
615
    var publish = document.getElementById("act_publish");
616
    var submit = document.getElementById("act_submit");
617
    if (numsel <= 1 && file.isdir)
618
    {
619
        /* TODO: Work out of file is svn'd */
620
        publish.setAttribute("class", "choice");
621
        publish.removeAttribute("disabled");
727 by dcoles
Browser: Added UI code to let you publish and unpublish folders
622
        /* If this dir is already published, call it "Unpublish" */
623
        if (file.published)
624
        {
625
            publish.setAttribute("value", "unpublish");
626
            publish.setAttribute("title" ,"Make it so this directory "
627
                + "can not be seen by anyone on the web");
628
            publish.textContent = "Unpublish";
629
        } else {
630
            publish.setAttribute("value", "publish");
631
            publish.setAttribute("title","Make it so this directory "
632
                + "can be seen by anyone on the web");
633
            publish.textContent = "Publish";
634
        }
597 by mattgiuca
Major refactor of actions in File browser.
635
        submit.setAttribute("class", "choice");
636
        submit.removeAttribute("disabled");
637
    }
638
    else
639
    {
640
        publish.setAttribute("class", "disabled");
641
        publish.setAttribute("disabled", "disabled");
642
        submit.setAttribute("class", "disabled");
643
        submit.setAttribute("disabled", "disabled");
644
    }
645
646
    /* Share */
647
    /* If exactly 1 non-directory file is selected/opened, and its parent
648
     * directory is published.
649
     */
650
    var share = document.getElementById("act_share");
651
    if (numsel <= 1 && !file.isdir)
652
    {
728 by dcoles
Browser: Added support for the 'Share this file' button. At the moment it opens
653
        /* Work out if parent dir is published */
654
        parentdir = current_file;
655
        if (parentdir.published)
656
        {
657
            share.setAttribute("class", "choice");
658
            share.removeAttribute("disabled");
659
        } else {
660
            share.setAttribute("class", "disabled");
661
            share.setAttribute("disabled", "disabled");
662
        }
597 by mattgiuca
Major refactor of actions in File browser.
663
    }
664
    else
665
    {
666
        share.setAttribute("class", "disabled");
667
        share.setAttribute("disabled", "disabled");
668
    }
669
670
    /* Rename */
671
    /* If exactly 1 file is selected */
672
    var rename = document.getElementById("act_rename");
673
    if (numsel == 1)
674
    {
675
        rename.setAttribute("class", "choice");
676
        rename.removeAttribute("disabled");
677
    }
678
    else
679
    {
680
        rename.setAttribute("class", "disabled");
681
        rename.setAttribute("disabled", "disabled");
682
    }
683
684
    /* Delete, cut, copy */
685
    /* If >= 1 file is selected */
686
    var act_delete = document.getElementById("act_delete");
687
    var cut = document.getElementById("act_cut");
688
    var copy = document.getElementById("act_copy");
689
    if (numsel >= 1)
690
    {
691
        act_delete.setAttribute("class", "choice");
692
        act_delete.removeAttribute("disabled");
693
        cut.setAttribute("class", "choice");
694
        cut.removeAttribute("disabled");
695
        copy.setAttribute("class", "choice");
696
        copy.removeAttribute("disabled");
697
    }
698
    else
699
    {
700
        act_delete.setAttribute("class", "disabled");
701
        act_delete.setAttribute("disabled", "disabled");
702
        cut.setAttribute("class", "disabled");
703
        cut.setAttribute("disabled", "disabled");
704
        copy.setAttribute("class", "disabled");
705
        copy.setAttribute("disabled", "disabled");
706
    }
707
708
    /* Paste, new file, new directory, upload */
775 by mattgiuca
browser.js: Updated logic to decide when to enable/disable certain actions.
709
    /* Disable if the current file is not a directory */
777 by mattgiuca
browser: Now hides the "More actions" box altogether if the current file is
710
    if (!current_file.isdir)
775 by mattgiuca
browser.js: Updated logic to decide when to enable/disable certain actions.
711
    {
712
        var paste = document.getElementById("act_paste");
713
        var newfile = document.getElementById("act_newfile");
714
        var mkdir = document.getElementById("act_mkdir");
715
        var upload = document.getElementById("act_upload");
716
        paste.setAttribute("class", "disabled");
717
        paste.setAttribute("disabled", "disabled");
718
        newfile.setAttribute("class", "disabled");
719
        newfile.setAttribute("disabled", "disabled");
720
        mkdir.setAttribute("class", "disabled");
721
        mkdir.setAttribute("disabled", "disabled");
722
        upload.setAttribute("class", "disabled");
723
        upload.setAttribute("disabled", "disabled");
724
    }
597 by mattgiuca
Major refactor of actions in File browser.
725
726
    /* Subversion actions */
727
    var svnadd = document.getElementById("act_svnadd");
829 by wagrant
Add an svndiff action, and give it an option in the action listbox.
728
    var svndiff = document.getElementById("act_svndiff");
597 by mattgiuca
Major refactor of actions in File browser.
729
    var svnrevert = document.getElementById("act_svnrevert");
730
    var svncommit = document.getElementById("act_svncommit");
832 by wagrant
Add svnlog to the file actions dropdown.
731
    var svnlog = document.getElementById("act_svnlog");
829 by wagrant
Add an svndiff action, and give it an option in the action listbox.
732
    /* These are only useful if we are in a versioned directory and have some
733
     * files selected. */
734
    if (numsel >= 1 && current_file.svnstatus)
597 by mattgiuca
Major refactor of actions in File browser.
735
    {
736
        svnadd.setAttribute("class", "choice");
737
        svnadd.removeAttribute("disabled");
738
        svnrevert.setAttribute("class", "choice");
739
        svnrevert.removeAttribute("disabled");
740
        svncommit.setAttribute("class", "choice");
741
        svncommit.removeAttribute("disabled");
742
    }
829 by wagrant
Add an svndiff action, and give it an option in the action listbox.
743
    else
744
    {
745
        svnadd.setAttribute("class", "disabled");
746
        svnadd.setAttribute("disabled", "disabled");
747
        svnrevert.setAttribute("class", "disabled");
748
        svnrevert.setAttribute("disabled", "disabled");
749
        svncommit.setAttribute("class", "disabled");
750
        svncommit.setAttribute("disabled", "disabled");
751
    }
752
832 by wagrant
Add svnlog to the file actions dropdown.
753
    /* Diff and log only support one path at the moment. */
829 by wagrant
Add an svndiff action, and give it an option in the action listbox.
754
    if (numsel == 1)
755
    {
756
        svnst = file_listing[selected_files[0]].svnstatus;
757
832 by wagrant
Add svnlog to the file actions dropdown.
758
        /* Diff and log also don't like unversioned paths, and diffs on unchanged
829 by wagrant
Add an svndiff action, and give it an option in the action listbox.
759
         * files are pointless. */
832 by wagrant
Add svnlog to the file actions dropdown.
760
        if (svnst && svnst != "unversioned")
829 by wagrant
Add an svndiff action, and give it an option in the action listbox.
761
        {
832 by wagrant
Add svnlog to the file actions dropdown.
762
            if (svnst != "normal")
763
            {
764
                svndiff.setAttribute("class", "choice");
765
                svndiff.removeAttribute("disabled");
766
            }
767
            else
768
            {
769
                svndiff.setAttribute("class", "disabled");
770
                svndiff.setAttribute("disabled", "disabled");
771
            }
772
        
773
            svnlog.setAttribute("class", "choice");
774
            svnlog.removeAttribute("disabled");
829 by wagrant
Add an svndiff action, and give it an option in the action listbox.
775
        }
776
    }
777
    else
778
    {
779
        svndiff.setAttribute("class", "disabled");
780
        svndiff.setAttribute("disabled", "disabled");
832 by wagrant
Add svnlog to the file actions dropdown.
781
        svnlog.setAttribute("class", "disabled");
782
        svnlog.setAttribute("disabled", "disabled");
829 by wagrant
Add an svndiff action, and give it an option in the action listbox.
783
    }
784
679 by mattgiuca
Browser: Added new action "Subversion Re-Checkout" which checks out fresh
785
    var svncheckout = document.getElementById("act_svncheckout");
786
    /* current_path == username: We are at the top level */
787
    if (current_path == username)
788
    {
789
        svncheckout.setAttribute("class", "choice");
790
        svncheckout.removeAttribute("disabled");
791
    }
792
    else
793
    {
794
        svncheckout.setAttribute("class", "disabled");
795
        svncheckout.setAttribute("disabled", "disabled");
796
    }
597 by mattgiuca
Major refactor of actions in File browser.
797
777 by mattgiuca
browser: Now hides the "More actions" box altogether if the current file is
798
    /* There is currently nothing on the More Actions menu of use
799
     * when the current file is not a directory. Hence, just remove
800
     * it entirely.
801
     * (This makes some of the above decisions somewhat redundant).
802
     */
803
    if (!(current_file.isdir))
804
    {
805
        var moreactions = document.getElementById("moreactions_area");
806
        moreactions.setAttribute("style", "display: none;");
807
    }
808
597 by mattgiuca
Major refactor of actions in File browser.
809
    return;
810
}
811
812
/** Event handler for when an item of the "More actions..." dropdown box is
813
 * selected. Performs the selected action. */
814
function handle_moreactions()
815
{
816
    var moreactions = document.getElementById("moreactions");
817
    if (moreactions.value == "top")
818
        return;
819
    var selectedaction = moreactions.value;
820
    /* Reset to "More actions..." */
821
    moreactions.selectedIndex = 0;
822
607 by mattgiuca
browser/browser.js: All the actions in "more actions" now actually take
823
    /* If 0 files selected, filename is the name of the current dir.
824
     * If 1 file selected, filename is that file.
825
     */
826
    if (selected_files.length == 0)
827
        filename = path_basename(current_path);
828
    else if (selected_files.length == 1)
829
        filename = selected_files[0];
830
    else
831
        filename = null;
832
597 by mattgiuca
Major refactor of actions in File browser.
833
    /* Now handle the selected action */
607 by mattgiuca
browser/browser.js: All the actions in "more actions" now actually take
834
    switch(selectedaction)
835
    {
836
    case "publish":
837
        action_publish(selected_files);
838
        break;
839
    case "unpublish":
840
        action_unpublish(selected_files);
841
        break;
842
    case "share":
728 by dcoles
Browser: Added support for the 'Share this file' button. At the moment it opens
843
        //alert("Not yet implemented: Sharing files");
844
        window.open(public_app_path(serve_app, current_path, filename), 'share')
607 by mattgiuca
browser/browser.js: All the actions in "more actions" now actually take
845
        break;
846
    case "submit":
847
        // TODO
848
        alert("Not yet implemented: Submit");
849
        break;
850
    case "rename":
851
        action_rename(filename);
852
        break;
853
    case "delete":
854
        action_remove(selected_files);
855
        break;
856
    case "copy":
857
        action_copy(selected_files);
858
        break;
859
    case "cut":
860
        action_cut(selected_files);
861
        break;
862
    case "paste":
863
        action_paste();
864
        break;
865
    case "newfile":
611 by mattgiuca
"New File" now works. (This is a MUCH better replacement for having to go to
866
        action_newfile();
607 by mattgiuca
browser/browser.js: All the actions in "more actions" now actually take
867
        break;
868
    case "mkdir":
869
        action_mkdir();
870
        break;
871
    case "upload":
872
        show_uploadpanel(true);
873
        break;
874
    case "svnadd":
875
        action_add(selected_files);
876
        break;
877
    case "svnrevert":
878
        action_revert(selected_files);
879
        break;
829 by wagrant
Add an svndiff action, and give it an option in the action listbox.
880
    case "svndiff":
881
        window.location = path_join(app_path('diff'), current_path, selected_files[0]);
882
        break;
607 by mattgiuca
browser/browser.js: All the actions in "more actions" now actually take
883
    case "svncommit":
884
        action_commit(selected_files);
885
        break;
832 by wagrant
Add svnlog to the file actions dropdown.
886
    case "svnlog":
887
        window.location = path_join(app_path('svnlog'), current_path, selected_files[0]);
888
        break;
679 by mattgiuca
Browser: Added new action "Subversion Re-Checkout" which checks out fresh
889
    case "svncheckout":
890
        action_checkout();
891
        break;
607 by mattgiuca
browser/browser.js: All the actions in "more actions" now actually take
892
    }
597 by mattgiuca
Major refactor of actions in File browser.
893
}
894
713 by dcoles
browser: Added console and run button to allow Python files to be executed in
895
/** User clicks "Run" button.
896
 * Do an Ajax call and print the test output.
897
 */
898
function runfile(localpath)
899
{
900
    /* Dump the entire file to the console */
901
    var callback = function()
902
    {
903
        console_enter_line("execfile('" + localpath + "')", "block");
904
    }
905
    start_server(callback)
906
    return;
907
}
908
170 by mattgiuca
browser: Added CSS and JS files (not much in them).
909
/** Called when the page loads initially.
910
 */
911
window.onload = function()
912
{
188 by mattgiuca
browser.js: Can now (shakily) handle directory listings. (lots of code!)
913
    /* Navigate (internally) to the path in the URL bar.
914
     * This causes the page to be populated with whatever is at that address,
915
     * whether it be a directory or a file.
916
     */
917
    var path = parse_url(window.location.href).path;
918
    /* Strip out root_dir + "/files" from the front of the path */
208 by mattgiuca
dispatch.html, ivle.css: "apptabs" is now an ID, not a class.
919
    var strip = make_path(this_app);
920
    if (path.substr(0, strip.length) == strip)
921
        path = path.substr(strip.length+1);
922
    else
923
    {
924
        /* See if this is an edit path */
925
        strip = make_path(edit_app);
926
        if (path.substr(0, strip.length) == strip)
927
        {
928
            path = path.substr(strip.length+1);
929
        }
930
    }
188 by mattgiuca
browser.js: Can now (shakily) handle directory listings. (lots of code!)
931
200 by mattgiuca
fileservice.listing: Now returns a nicer date format for mtime_nice.
932
    if (path.length == 0)
933
    {
934
        /* Navigate to the user's home directory by default */
935
        /* TEMP? */
936
        path = username;
937
    }
938
603 by mattgiuca
Console now starts up in the user's home directory.
939
    navigate(path);
713 by dcoles
browser: Added console and run button to allow Python files to be executed in
940
941
    /* Set up the console plugin to display as a popup window */
942
    console_init(true);
170 by mattgiuca
browser: Added CSS and JS files (not much in them).
943
}