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

« back to all changes in this revision

Viewing changes to www/media/browser/browser.js

  • Committer: drtomc
  • Date: 2008-02-04 04:29:12 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:401
tutorialservice: Fixed "subjects" directory being searched for problem files, now looks in "problems". (A hang over from an earlier change to split them up).
This fixes the issue of problem submissions not working.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
23
/* Url names for apps */
 
24
this_app = "files";
 
25
edit_app = "edit";
 
26
service_app = "fileservice";
 
27
serve_app = "serve";
 
28
download_app = "download";
 
29
 
 
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",
 
46
    "application/xml" : "text",
 
47
};
 
48
 
 
49
/* Mapping MIME types to icons, just the file's basename */
 
50
type_icons = {
 
51
    "text/directory": "dir.png",
 
52
    "text/x-python": "py.png",
 
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 = {
 
63
    "unversioned": null,
 
64
    "normal": "normal.png",
 
65
    "added": "added.png",
 
66
    "missing": "missing.png",
 
67
    "deleted": "deleted.png",
 
68
    "modified": "modified.png",
 
69
};
 
70
 
 
71
/* Mapping SVN status to "nice" strings */
 
72
svn_nice = {
 
73
    "unversioned": "Temporary file",
 
74
    "normal": "Permanent file",
 
75
    "added": "Temporary file (scheduled to be added)",
 
76
    "missing": "Permanent file (missing)",
 
77
    "deleted": "Permanent file (scheduled for deletion)",
 
78
    "replaced": "Permanent file (replaced)",
 
79
    "modified": "Permanent file (modified)",
 
80
    "merged": "Permanent file (merged)",
 
81
    "conflicted": "Permanent file (conflicted)",
 
82
};
 
83
 
 
84
default_svn_icon = null;
 
85
default_svn_nice = "Unknown status";
 
86
 
 
87
svn_icons_path = "media/images/svn";
 
88
 
 
89
published_icon = "media/images/interface/published.png";
 
90
 
 
91
/* List of MIME types considered "executable" by the system.
 
92
 * Executable files offer a "run" link, implying that the "serve"
 
93
 * application can interpret them.
 
94
 */
 
95
types_exec = [
 
96
    "text/x-python",
 
97
];
 
98
 
 
99
 
 
100
/* Global variables */
 
101
 
 
102
current_path = "";
 
103
 
 
104
/** Calls the server using Ajax, performing an action on the server side.
 
105
 * Receives the response from the server and performs a refresh of the page
 
106
 * contents, updating it to display the returned data (such as a directory
 
107
 * listing, file preview, or editor pane).
 
108
 * Always makes a POST request.
 
109
 * No return value.
 
110
 *
 
111
 * \param action String. Name of the action to perform, as defined in the
 
112
 *     fileservice API.
 
113
 * \param path URL path to make the request to, within the application.
 
114
 * \param args Argument object, as described in util.parse_url and friends.
 
115
 *      This should contain the arguments to the action, but NOT the action
 
116
 *      itself. (Also a minor side-effect; the "args" object will be mutated
 
117
 *      to include the action attribute).
 
118
 * \param content_type String, optional.
 
119
 *      May be "application/x-www-form-urlencoded" or "multipart/form-data".
 
120
 *      Defaults to "application/x-www-form-urlencoded".
 
121
 *      "multipart/form-data" is recommended for large uploads.
 
122
 */
 
123
function do_action(action, path, args, content_type, ignore_response)
 
124
{
 
125
    args.action = action;
 
126
    /* Call the server and perform the action. This mutates the server. */
 
127
    response = ajax_call(service_app, path, args, "POST", content_type);
 
128
    /* Check for action errors reported by the server, and report them to the
 
129
     * user */
 
130
    error = response.getResponseHeader("X-IVLE-Action-Error");
 
131
    if (error != null)
 
132
        alert("Error: " + error.toString() + ".");
 
133
    /* Now read the response and set up the page accordingly */
 
134
    if (ignore_response != true)
 
135
        handle_response(path, response);
 
136
}
 
137
 
 
138
/** Calls the server using Ajax, requesting a directory listing. This should
 
139
 * not modify the server in any way. Receives the response from the server and
 
140
 * performs a refresh of the page contents, updating it to display the
 
141
 * returned data (such as a directory listing, file preview, or editor pane).
 
142
 * Called "navigate", can also be used for a simple refresh.
 
143
 * Always makes a GET request.
 
144
 * No return value.
 
145
 * \param editmode Optional boolean. If true, then the user navigated here
 
146
 * with an "edit" URL so we should favour using the editor.
 
147
 */
 
148
function navigate(path, editmode)
 
149
{
 
150
    /* Call the server and request the listing. This mutates the server. */
 
151
    response = ajax_call(service_app, path, null, "GET");
 
152
    /* Now read the response and set up the page accordingly */
 
153
    handle_response(path, response, editmode);
 
154
}
 
155
 
 
156
/** Determines the "handler type" from a MIME type.
 
157
 * The handler type is a string, either "text", "image", "audio" or "binary".
 
158
 */
 
159
function get_handler_type(content_type)
 
160
{
 
161
    if (!content_type)
 
162
        return null;
 
163
    if (content_type in type_handlers)
 
164
        return type_handlers[content_type];
 
165
    else
 
166
    {   /* Based on the first part of the MIME type */
 
167
        var handler_type = content_type.split('/')[0];
 
168
        if (handler_type != "text" && handler_type != "image" &&
 
169
            handler_type != "audio")
 
170
            handler_type = "binary";
 
171
        return handler_type;
 
172
    }
 
173
}
 
174
 
 
175
/** Given an HTTP response object, cleans up and rebuilds the contents of the
 
176
 * page using the response data. This does not navigate away from the page, it
 
177
 * merely rebuilds most of the data.
 
178
 * Note that depending on the type of data returned, this could result in a
 
179
 * directory listing, an image preview, an editor pane, etc.
 
180
 * Figures out the type and calls the appropriate function.
 
181
 * \param path URL path which the request was made for. This can (among other
 
182
 * things) be used to update the URL in the location bar.
 
183
 * \param response XMLHttpRequest object returned by the server. Should
 
184
 * contain all the response data.
 
185
 * \param editmode Optional boolean. If true, then the user navigated here
 
186
 * with an "edit" URL so we should favour using the editor.
 
187
 */
 
188
function handle_response(path, response, editmode)
 
189
{
 
190
    /* TODO: Set location bar to "path" */
 
191
    current_path = path;
 
192
 
 
193
    /* Clear away the existing page contents */
 
194
    clearpage();
 
195
    /* Display the path at the top, for navigation */
 
196
    presentpath(path);
 
197
 
 
198
    /* Check the status, and if not 200, read the error and handle this as an
 
199
     * error. */
 
200
    if (response.status != 200)
 
201
    {
 
202
        var error = response.getResponseHeader("X-IVLE-Return-Error");
 
203
        if (error == null)
 
204
            error = response.statusText;
 
205
        handle_error(error);
 
206
        return;
 
207
    }
 
208
 
 
209
    /* Check if this is a directory listing or file contents */
 
210
    var isdir = response.getResponseHeader("X-IVLE-Return") == "Dir";
 
211
    if (!editmode && isdir)
 
212
    {
 
213
        var listing = response.responseText;
 
214
        /* The listing SHOULD be valid JSON text. Parse it into an object. */
 
215
        try
 
216
        {
 
217
            listing = JSON.parse(listing);
 
218
        }
 
219
        catch (e)
 
220
        {
 
221
            handle_error("The server returned an invalid directory listing");
 
222
            return;
 
223
        }
 
224
        handle_dir_listing(path, listing);
 
225
    }
 
226
    else
 
227
    {
 
228
        /* Treat this as an ordinary file. Get the file type. */
 
229
        var content_type = response.getResponseHeader("Content-Type");
 
230
        var handler_type = get_handler_type(content_type);
 
231
        /* If we're in "edit mode", always treat this file as text */
 
232
        would_be_handler_type = handler_type;
 
233
        if (editmode) handler_type = "text";
 
234
        /* handler_type should now be set to either
 
235
         * "text", "image", "audio" or "binary". */
 
236
        switch (handler_type)
 
237
        {
 
238
        case "text":
 
239
            if (isdir)
 
240
            {
 
241
                handle_text(path_join(path, "untitled"), "",
 
242
                    would_be_handler_type);
 
243
            }
 
244
            else
 
245
            {
 
246
                handle_text(path, response.responseText,
 
247
                    would_be_handler_type);
 
248
            }
 
249
            break;
 
250
        case "image":
 
251
            /* TODO: Custom image handler */
 
252
            handle_binary(path, response.responseText);
 
253
            break;
 
254
        case "audio":
 
255
            /* TODO: Custom audio handler */
 
256
            handle_binary(path, response.responseText);
 
257
            break;
 
258
        case "binary":
 
259
            handle_binary(path);
 
260
            break;
 
261
        }
 
262
    }
 
263
}
 
264
 
 
265
/** Deletes all "dynamic" content on the page.
 
266
 * This returns the page back to the state it is in when the HTML arrives to
 
267
 * the browser, ready for another handler to populate it.
 
268
 */
 
269
function clearpage()
 
270
{
 
271
    dom_removechildren(document.getElementById("path"));
 
272
    dom_removechildren(document.getElementById("filesbody"));
 
273
}
 
274
 
 
275
/** Deletes all "dynamic" content on the page necessary to navigate from
 
276
 * one directory listing to another (does not clear as much as clearpage
 
277
 * does).
 
278
 * This is the equivalent of calling clearpage() then
 
279
 * setup_for_dir_listing(), assuming the page is already on a dir listing.
 
280
 */
 
281
function clearpage_dir()
 
282
{
 
283
    dom_removechildren(document.getElementById("path"));
 
284
    dom_removechildren(document.getElementById("files"));
 
285
    dom_removechildren(document.getElementById("sidepanel"));
 
286
}
 
287
 
 
288
/** Sets the mode to either "file browser" or "text editor" mode.
 
289
 * This modifies the window icon, and selected tab.
 
290
 * \param editmode If True, editor mode. Else, file browser mode.
 
291
 */
 
292
function setmode(editmode)
 
293
{
 
294
    /* Find the DOM elements for the file browser and editor tabs */
 
295
    var tabs = document.getElementById("apptabs");
 
296
    var tab_files = null;
 
297
    var tab_edit = null;
 
298
    var a;
 
299
    var href;
 
300
    for (var i=0; i<tabs.childNodes.length; i++)
 
301
    {
 
302
        /* Find the href of the link within */
 
303
        if (!tabs.childNodes[i].getElementsByTagName) continue;
 
304
        a = tabs.childNodes[i].getElementsByTagName("a");
 
305
        if (a.length == 0) continue;
 
306
        href = a[0].getAttribute("href");
 
307
        if (href == null) continue;
 
308
        if (endswith(href, this_app))
 
309
            tab_files = tabs.childNodes[i];
 
310
        else if (endswith(href, edit_app))
 
311
            tab_edit = tabs.childNodes[i];
 
312
    }
 
313
 
 
314
    if (editmode)
 
315
    {
 
316
        tab_files.removeAttribute("class");
 
317
        tab_edit.setAttribute("class", "thisapp");
 
318
    }
 
319
    else
 
320
    {
 
321
        tab_edit.removeAttribute("class");
 
322
        tab_files.setAttribute("class", "thisapp");
 
323
    }
 
324
}
 
325
 
 
326
/*** HANDLERS for different types of responses (such as dir listing, file,
 
327
 * etc). */
 
328
 
 
329
function handle_error(message)
 
330
{
 
331
    setmode(false);
 
332
    var files = document.getElementById("filesbody");
 
333
    var txt_elem = dom_make_text_elem("div", "Error: "
 
334
        + message.toString() + ".")
 
335
    txt_elem.setAttribute("class", "padding error");
 
336
    files.appendChild(txt_elem);
 
337
}
 
338
 
 
339
/** Presents a path list (address bar inside the page) for clicking.
 
340
 */
 
341
function presentpath(path)
 
342
{
 
343
    var dom_path = document.getElementById("path");
 
344
    var href_path = make_path(this_app);
 
345
    var nav_path = "";
 
346
    var dir;
 
347
 
 
348
    /* Also set the document title */
 
349
    document.title = path_basename(path) + " - IVLE";
 
350
    /* Create all of the paths */
 
351
    var pathlist = path.split("/");
 
352
    for (var i=0; i<pathlist.length; i++)
 
353
    {
 
354
        dir = pathlist[i];
 
355
        if (dir == "") continue;
 
356
        /* Make an 'a' element */
 
357
        href_path = path_join(href_path, dir);
 
358
        nav_path = path_join(nav_path, dir);
 
359
        var link = dom_make_link_elem("a", dir, "Navigate to " + nav_path,
 
360
                href_path/*, "navigate(" + repr(href_path) + ")"*/);
 
361
        dom_path.appendChild(link);
 
362
        dom_path.appendChild(document.createTextNode("/"));
 
363
    }
 
364
    dom_path.removeChild(dom_path.lastChild);
 
365
}
 
366
 
 
367
/** Given a mime type, returns the path to the icon.
 
368
 * \param type String, Mime type.
 
369
 * \param sizelarge Boolean, optional.
 
370
 * \return Path to the icon. Has applied make_path, so it is relative to site
 
371
 * root.
 
372
 */
 
373
function mime_type_to_icon(type, sizelarge)
 
374
{
 
375
    var filename;
 
376
    if (type in type_icons)
 
377
        filename = type_icons[type];
 
378
    else
 
379
        filename = default_type_icon;
 
380
    if (sizelarge)
 
381
        return make_path(path_join(type_icons_path_large, filename));
 
382
    else
 
383
        return make_path(path_join(type_icons_path, filename));
 
384
}
 
385
 
 
386
/** Given an svnstatus, returns the path to the icon.
 
387
 * \param type String, svn status.
 
388
 * \return Path to the icon. Has applied make_path, so it is relative to site
 
389
 * root. May return null to indicate no SVN icon.
 
390
 */
 
391
function svnstatus_to_icon(svnstatus)
 
392
{
 
393
    var filename;
 
394
    if (svnstatus in svn_icons)
 
395
        filename = svn_icons[svnstatus];
 
396
    else
 
397
        filename = default_svn_icon;
 
398
    if (filename == null) return null;
 
399
    return make_path(path_join(svn_icons_path, filename));
 
400
}
 
401
 
 
402
/** Given an svnstatus, returns the "nice" string.
 
403
 */
 
404
function svnstatus_to_string(svnstatus)
 
405
{
 
406
    if (svnstatus in svn_nice)
 
407
        return svn_nice[svnstatus];
 
408
    else
 
409
        return default_svn_nice;
 
410
}
 
411
 
 
412
/** Displays a download link to the binary file.
 
413
 */
 
414
function handle_binary(path)
 
415
{
 
416
    setmode(false);
 
417
    var files = document.getElementById("filesbody");
 
418
    var div = document.createElement("div");
 
419
    files.appendChild(div);
 
420
    div.setAttribute("class", "padding");
 
421
    var download_link = app_path(download_app, path);
 
422
    var par1 = dom_make_text_elem("p",
 
423
        "The file " + path + " is a binary file. To download this file, " +
 
424
        "click the following link:");
 
425
    var par2 = dom_make_link_elem("p",
 
426
        "Download " + path, "Download " + path, download_link);
 
427
    div.appendChild(par1);
 
428
    div.appendChild(par2);
 
429
}
 
430
 
 
431
/** Called when the page loads initially.
 
432
 */
 
433
window.onload = function()
 
434
{
 
435
    /* Navigate (internally) to the path in the URL bar.
 
436
     * This causes the page to be populated with whatever is at that address,
 
437
     * whether it be a directory or a file.
 
438
     */
 
439
    var path = parse_url(window.location.href).path;
 
440
    /* Strip out root_dir + "/files" from the front of the path */
 
441
    var strip = make_path(this_app);
 
442
    var editmode = false;
 
443
    if (path.substr(0, strip.length) == strip)
 
444
        path = path.substr(strip.length+1);
 
445
    else
 
446
    {
 
447
        /* See if this is an edit path */
 
448
        strip = make_path(edit_app);
 
449
        if (path.substr(0, strip.length) == strip)
 
450
        {
 
451
            path = path.substr(strip.length+1);
 
452
            editmode = true;
 
453
        }
 
454
    }
 
455
 
 
456
    if (path.length == 0)
 
457
    {
 
458
        /* Navigate to the user's home directory by default */
 
459
        /* TEMP? */
 
460
        path = username;
 
461
    }
 
462
 
 
463
    navigate(path, editmode);
 
464
}