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

« back to all changes in this revision

Viewing changes to www/media/common/util.js

  • Committer: mattgiuca
  • Date: 2008-02-03 03:03:27 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:380
media/browser/browser.css: Transformed browser CSS from intrinsic layout to
position: absolute based layout. This has been applied to top bar and status
bar, so the status bar now sits at the bottom of the canvas.

However the middle section is still a table-based layout, now needing work
(appears strange).

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: JavaScript Utilities
 
19
 * Author: Matt Giuca
 
20
 * Date: 11/1/2008
 
21
 *
 
22
 * Defines some generic JavaScript utility functions.
 
23
 */
 
24
 
 
25
/* Expects the following variables to have been declared by JavaScript in
 
26
 * the HTML generated by the server:
 
27
 * - root_dir
 
28
 * - username
 
29
 */
 
30
 
 
31
/** Removes all children of a given DOM element
 
32
 * \param elem A DOM Element. Will be modified.
 
33
 */
 
34
function dom_removechildren(elem)
 
35
{
 
36
    while (elem.lastChild != null)
 
37
        elem.removeChild(elem.lastChild);
 
38
}
 
39
 
 
40
/** Creates a DOM element with simple text inside it.
 
41
 * \param tagname String. Name of the element's tag (eg. "p").
 
42
 * \param text String. Text to be placed inside the element.
 
43
 * \param title String, optional. Tooltip for the text.
 
44
 *  (Note, title creates a span element around the text).
 
45
 * \return DOM Element object.
 
46
 */
 
47
function dom_make_text_elem(tagname, text, title)
 
48
{
 
49
    if (text == null) text = "";
 
50
    var elem = document.createElement(tagname);
 
51
    var textnode;
 
52
    if (title == null)
 
53
        textnode = document.createTextNode(text);
 
54
    else
 
55
    {
 
56
        textnode = document.createElement("span");
 
57
        textnode.setAttribute("title", title);
 
58
        textnode.appendChild(document.createTextNode(text));
 
59
    }
 
60
    elem.appendChild(textnode);
 
61
    return elem;
 
62
}
 
63
 
 
64
/** Creates a DOM element with hyperlinked text inside it.
 
65
 * \param tagname String. Name of the element's tag (eg. "p").
 
66
 * \param text String. Text to be placed inside the element.
 
67
 * \param title String, optional. Sets a tooltip for the link.
 
68
 * \param href String. URL the text will link to. This is a raw string,
 
69
 *  it will automatically be URL-encoded.
 
70
 * \param onclick Optional string. Will be set as the "onclick" attribute
 
71
 *  of the "a" element.
 
72
 * \param dontencode Optional boolean. If true, will not encode the href.
 
73
 *  if including query strings, you must set this to true and use build_url
 
74
 *  to escape the URI correctly.
 
75
 * \return DOM Element object.
 
76
 */
 
77
function dom_make_link_elem(tagname, text, title, href, onclick, dontencode)
 
78
{
 
79
    if (text == null) text = "";
 
80
    if (href == null) href = "";
 
81
    var elem = document.createElement(tagname);
 
82
    var link = document.createElement("a");
 
83
    if (dontencode != true)
 
84
        href = urlencode_path(href);
 
85
    link.setAttribute("href", href);
 
86
    if (title != null)
 
87
        link.setAttribute("title", title);
 
88
    if (onclick != null)
 
89
        link.setAttribute("onclick", onclick);
 
90
    link.appendChild(document.createTextNode(text));
 
91
    elem.appendChild(link);
 
92
    return elem;
 
93
}
 
94
 
 
95
/** Creates a DOM img element. All parameters are optional except src.
 
96
 * If alt (compulsory in HTML) is omitted, will be set to "".
 
97
 */
 
98
function dom_make_img(src, width, height, title, alt)
 
99
{
 
100
    var img = document.createElement("img");
 
101
    img.setAttribute("src", urlencode_path(src));
 
102
    if (width != null)
 
103
        img.setAttribute("width", width);
 
104
    if (height != null)
 
105
        img.setAttribute("height", height);
 
106
    if (title != null)
 
107
        img.setAttribute("title", title);
 
108
    if (alt == null) alt = "";
 
109
    img.setAttribute("alt", alt);
 
110
    return img;
 
111
}
 
112
 
 
113
/** Given a number of bytes, returns a string representing the file size in a
 
114
 * human-readable format.
 
115
 * eg. nice_filesize(6) -> "6 bytes"
 
116
 *     nice_filesize(81275) -> "79.4 kB"
 
117
 *     nice_filesize(13498346) -> "12.9 MB"
 
118
 * \param bytes Number of bytes. Must be an integer.
 
119
 * \return String.
 
120
 */
 
121
function nice_filesize(bytes)
 
122
{
 
123
    if (bytes == null) return "";
 
124
    var size;
 
125
    if (bytes < 1024)
 
126
        return bytes.toString() + " B";
 
127
    size = bytes / 1024;
 
128
    if (size < 1024)
 
129
        return size.toFixed(1) + " kB";
 
130
    size = size / 1024;
 
131
    if (size < 1024)
 
132
        return size.toFixed(1) + " MB";
 
133
    size = size / 1024;
 
134
    return size.toFixed(1) + " GB";
 
135
}
 
136
 
 
137
/** Given a URL, returns an object containing a number of attributes
 
138
 * describing the components of the URL, similar to CGI request variables.
 
139
 * The object has the following attributes:
 
140
 * - scheme
 
141
 * - server_name
 
142
 * - server_port
 
143
 * - path
 
144
 * - query_string
 
145
 * - args
 
146
 * The first five of these are strings, which comprise the URL as follows:
 
147
 * <scheme> "://" <server_name> ":" <server_port> <path> "?" <query_string>
 
148
 * Any of these strings may be set to null if not found.
 
149
 *
 
150
 * "args" is an object whose attributes are the query_string arguments broken
 
151
 * up.
 
152
 * Args values are strings for single values, arrays of strings for values
 
153
 * whose names appear multiple times.
 
154
 * args is never null, though it may be empty.
 
155
 *
 
156
 * All strings are decoded/unescaped. Reserved characters
 
157
 * (; , / ? : @ & = + * $) are not decoded except in args and path.
 
158
 *
 
159
 * \param url String. A URL. To read from the current browser window, use
 
160
 *  window.location.href.
 
161
 * \return The above described object.
 
162
 */
 
163
function parse_url(url)
 
164
{
 
165
    var obj = {};
 
166
    var index;
 
167
    var serverpart;
 
168
    var args;
 
169
 
 
170
    /* Split scheme from rest */
 
171
    index = url.indexOf("://");
 
172
    if (index < 0)
 
173
        obj.scheme = null;
 
174
    else
 
175
    {
 
176
        obj.scheme = url.substr(0, index);
 
177
        url = url.substr(index+3);
 
178
    }
 
179
 
 
180
    /* Split server name/port from rest */
 
181
    index = url.indexOf("/");
 
182
    if (index < 0)
 
183
    {
 
184
        serverpart = url;
 
185
        url = null;
 
186
    }
 
187
    else
 
188
    {
 
189
        serverpart = url.substr(0, index);
 
190
        url = url.substr(index);
 
191
    }
 
192
 
 
193
    /* Split server name from port */
 
194
    index = serverpart.indexOf(":");
 
195
    if (index < 0)
 
196
    {
 
197
        obj.server_name = serverpart;
 
198
        obj.server_port = null;
 
199
    }
 
200
    else
 
201
    {
 
202
        obj.server_name = serverpart.substr(0, index);
 
203
        obj.server_port = serverpart.substr(index+1);
 
204
    }
 
205
 
 
206
    /* Split path from query string */
 
207
    if (url == null)
 
208
    {
 
209
        obj.path = null;
 
210
        obj.query_string = null;
 
211
    }
 
212
    else
 
213
    {
 
214
        index = url.indexOf("?");
 
215
        if (index < 0)
 
216
        {
 
217
            obj.path = url;
 
218
            obj.query_string = null;
 
219
        }
 
220
        else
 
221
        {
 
222
            obj.path = url.substr(0, index);
 
223
            obj.query_string = url.substr(index+1);
 
224
        }
 
225
    }
 
226
    obj.path = decodeURIComponent(obj.path);
 
227
 
 
228
    /* Split query string into arguments */
 
229
    args = {};
 
230
    if (obj.query_string != null)
 
231
    {
 
232
        var args_strs = obj.query_string.split("&");
 
233
        var arg_str;
 
234
        var arg_key, arg_val;
 
235
        for (var i=0; i<args_strs.length; i++)
 
236
        {
 
237
            arg_str = args_strs[i];
 
238
            index = arg_str.indexOf("=");
 
239
            /* Ignore malformed args */
 
240
            if (index >= 0)
 
241
            {
 
242
                arg_key = decodeURIComponent(arg_str.substr(0, index));
 
243
                arg_val = decodeURIComponent(arg_str.substr(index+1));
 
244
                if (arg_key in args)
 
245
                {
 
246
                    /* Collision - make an array */
 
247
                    if (args[arg_key] instanceof Array)
 
248
                        args[arg_key][args[arg_key].length] = arg_val;
 
249
                    else
 
250
                        args[arg_key] = [args[arg_key], arg_val];
 
251
                }
 
252
                else
 
253
                    args[arg_key] = arg_val;
 
254
            }
 
255
        }
 
256
    }
 
257
    obj.args = args;
 
258
 
 
259
    return obj;
 
260
}
 
261
 
 
262
/** Builds a query_string from an args object. Encodes the arguments.
 
263
 * \param args Args object as described in parse_url.
 
264
 * \return Query string portion of a URL.
 
265
 */
 
266
function make_query_string(args)
 
267
{
 
268
    var query_string = "";
 
269
    var arg_val;
 
270
    for (var arg_key in args)
 
271
    {
 
272
        arg_val = args[arg_key];
 
273
        if (arg_val instanceof Array)
 
274
            for (var i=0; i<arg_val.length; i++)
 
275
                query_string += "&" + encodeURIComponent(arg_key) + "=" +
 
276
                    encodeURIComponent(arg_val[i]);
 
277
        else
 
278
            query_string += "&" + encodeURIComponent(arg_key) + "=" +
 
279
                encodeURIComponent(arg_val);
 
280
    }
 
281
    if (query_string == "")
 
282
        query_string = null;
 
283
    else
 
284
        /* Drop the first "&" */
 
285
        query_string = query_string.substr(1);
 
286
 
 
287
    return query_string;
 
288
}
 
289
 
 
290
/** Given an object exactly of the form described for the output of parseurl,
 
291
 * returns a URL string built from those parameters. The URL is properly
 
292
 * encoded.
 
293
 * parseurl and buildurl are strict inverses of each other.
 
294
 * Note that either query_string or args may be supplied. If both are
 
295
 * supplied, query_string is preferred (because it keeps the argument order).
 
296
 * If you take a url from parseurl, modify args, and pass to buildurl,
 
297
 * you need to set query_string to null to use the new args.
 
298
 * \param obj Object as returned by parseurl.
 
299
 * \return String, a URL.
 
300
 */
 
301
function build_url(obj)
 
302
{
 
303
    var url = "";
 
304
    var query_string = null;
 
305
 
 
306
    if (("scheme" in obj) && obj.scheme != null)
 
307
        url = obj.scheme.toString() + "://";
 
308
    if (("server_name" in obj) && obj.server_name != null)
 
309
        url += obj.server_name.toString();
 
310
    if (("server_port" in obj) && obj.server_port != null)
 
311
        url += ":" + obj.server_port.toString();
 
312
    if (("path" in obj) && obj.path != null)
 
313
    {
 
314
        var path = urlencode_path(obj.path.toString());
 
315
        if (url.length > 0 && path.length > 0 && path[0] != "/")
 
316
            path = "/" + path;
 
317
        url += path;
 
318
    }
 
319
    if (("query_string" in obj) && obj.query_string != null)
 
320
        query_string = encodeURI(obj.query_string.toString());
 
321
    else if (("args" in obj) && obj.args != null)
 
322
        query_string = make_query_string(obj.args);
 
323
 
 
324
    if (query_string != null)
 
325
        url += "?" + query_string;
 
326
 
 
327
    return url;
 
328
}
 
329
 
 
330
/** URL-encodes a path. This is a special case of URL encoding as all
 
331
 * characters *except* the slash must be encoded.
 
332
 */
 
333
function urlencode_path(path)
 
334
{
 
335
    /* Split up the path, URLEncode each segment with encodeURIComponent,
 
336
     * and rejoin.
 
337
     */
 
338
    var split = path.split('/');
 
339
    for (var i=0; i<split.length; i++)
 
340
        split[i] = encodeURIComponent(split[i]);
 
341
    path = path_join.apply(null, split);
 
342
    if (split[0] == "" && split.length > 1) path = "/" + path;
 
343
    return path;
 
344
}
 
345
 
 
346
/** Given an argument map, as output in the args parameter of the return of
 
347
 * parseurl, gets the first occurence of an argument in the URL string.
 
348
 * If the argument was not found, returns null.
 
349
 * If there was a single argument, returns the argument.
 
350
 * If there were multiple arguments, returns the first.
 
351
 * \param args Object mapping arguments to strings or arrays of strings.
 
352
 * \param arg String. Argument name.
 
353
 * \return String.
 
354
 */
 
355
function arg_getfirst(args, arg)
 
356
{
 
357
    if (!(arg in args))
 
358
        return null;
 
359
    var r = args[arg];
 
360
    if (r instanceof Array)
 
361
        return r[0];
 
362
    else
 
363
        return r;
 
364
}
 
365
 
 
366
/** Given an argument map, as output in the args parameter of the return of
 
367
 * parseurl, gets all occurences of an argument in the URL string, as an
 
368
 * array.
 
369
 * If the argument was not found, returns [].
 
370
 * Otherwise, returns all occurences as an array, even if there was only one.
 
371
 * \param args Object mapping arguments to strings or arrays of strings.
 
372
 * \param arg String. Argument name.
 
373
 * \return Array of strings.
 
374
 */
 
375
function arg_getlist(args, arg)
 
376
{
 
377
    if (!(arg in args))
 
378
        return [];
 
379
    var r = args[arg];
 
380
    if (r instanceof Array)
 
381
        return r;
 
382
    else
 
383
        return [r];
 
384
}
 
385
 
 
386
/** Joins one or more paths together. Accepts 1 or more arguments.
 
387
 */
 
388
function path_join(path1 /*, path2, ... */)
 
389
{
 
390
    var arg;
 
391
    var path = "";
 
392
    for (var i=0; i<arguments.length; i++)
 
393
    {
 
394
        arg = arguments[i];
 
395
        if (arg.length == 0) continue;
 
396
        if (arg[0] == '/')
 
397
            path = arg;
 
398
        else
 
399
        {
 
400
            if (path.length > 0 && path[path.length-1] != '/')
 
401
                path += '/';
 
402
            path += arg;
 
403
        }
 
404
    }
 
405
    return path;
 
406
}
 
407
 
 
408
 
 
409
/** Builds a multipart_formdata string from an args object. Similar to
 
410
 * make_query_string, but it returns data of type "multipart/form-data"
 
411
 * instead of "application/x-www-form-urlencoded". This is good for
 
412
 * encoding large strings such as text objects from the editor.
 
413
 * Should be written with a Content-Type of
 
414
 * "multipart/form-data, boundary=<boundary>".
 
415
 * All fields are sent with a Content-Type of text/plain.
 
416
 * \param args Args object as described in parse_url.
 
417
 * \param boundary Random "magic" string which DOES NOT appear in any of
 
418
 *  the argument values. This should match the "boundary=" value written to
 
419
 *  the Content-Type header.
 
420
 * \return String in multipart/form-data format.
 
421
 */
 
422
function make_multipart_formdata(args, boundary)
 
423
{
 
424
    var data = "";
 
425
    var arg_val;
 
426
    /* Mutates data */
 
427
    var extend_data = function(arg_key, arg_val)
 
428
    {
 
429
        /* FIXME: Encoding not supported here (should not matter if we
 
430
         * only use ASCII names */
 
431
        data += "--" + boundary + "\r\n"
 
432
            + "Content-Disposition: form-data; name=\"" + arg_key
 
433
            + "\"\r\n\r\n"
 
434
            + arg_val + "\r\n";
 
435
    }
 
436
 
 
437
    for (var arg_key in args)
 
438
    {
 
439
        arg_val = args[arg_key];
 
440
        if (arg_val instanceof Array)
 
441
            for (var i=0; i<arg_val.length; i++)
 
442
            {
 
443
                extend_data(arg_key, arg_val[i]);
 
444
            }
 
445
        else
 
446
            extend_data(arg_key, arg_val);
 
447
    }
 
448
    /* End boundary */
 
449
    data += "--" + boundary + "--\r\n";
 
450
 
 
451
    return data;
 
452
}
 
453
 
 
454
/** Converts a list of directories into a path name, with a slash at the end.
 
455
 * \param pathlist List of strings.
 
456
 * \return String.
 
457
 */
 
458
function pathlist_to_path(pathlist)
 
459
{
 
460
    ret = path_join.apply(null, pathlist);
 
461
    if (ret[ret.length-1] != '/')
 
462
        ret += '/';
 
463
    return ret;
 
464
}
 
465
 
 
466
/** Given a path relative to the IVLE root, gives a path relative to
 
467
 * the site root.
 
468
 */
 
469
function make_path(path)
 
470
{
 
471
    return path_join(root_dir, path);
 
472
}
 
473
 
 
474
/** Shorthand for make_path(path_join(app, ...))
 
475
 * Creates an absolute path for a given path within a given app.
 
476
 */
 
477
function app_path(app /*,...*/)
 
478
{
 
479
    return make_path(path_join.apply(null, arguments));
 
480
}
 
481
 
 
482
/** Given a path, gets the "basename" (the last path segment).
 
483
 */
 
484
function path_basename(path)
 
485
{
 
486
    segments = path.split("/");
 
487
    if (segments[segments.length-1].length == 0)
 
488
        return segments[segments.length-2];
 
489
    else
 
490
        return segments[segments.length-1];
 
491
}
 
492
 
 
493
/** Given a string str, determines whether it ends with substr */
 
494
function endswith(str, substring)
 
495
{
 
496
    if (str.length < substring.length) return false;
 
497
    return str.substr(str.length - substring.length) == substring;
 
498
}
 
499
 
 
500
/** Equivalent to Python's repr.
 
501
 * Gets the JavaScript string representation.
 
502
 * Actually just calls JSON.stringify.
 
503
 */
 
504
function repr(str)
 
505
{
 
506
    return JSON.stringify(str);
 
507
}
 
508
 
 
509
/** Removes all occurences of a value from an array.
 
510
 */
 
511
Array.prototype.removeall = function(val)
 
512
{
 
513
    var i, j;
 
514
    var arr = this;
 
515
    j = 0;
 
516
    for (i=0; i<arr.length; i++)
 
517
    {
 
518
        arr[j] = arr[i];
 
519
        if (arr[i] != val) j++;
 
520
    }
 
521
    arr.splice(j, i-j);
 
522
}
 
523
 
 
524
/** Makes an XMLHttpRequest call to the server. Waits (synchronously) for a
 
525
 * response, and returns an XMLHttpRequest object containing the completed
 
526
 * response.
 
527
 *
 
528
 * \param app IVLE app to call (such as "fileservice").
 
529
 * \param path URL path to make the request to, within the application.
 
530
 * \param args Argument object, as described in parse_url and friends.
 
531
 * \param method String; "GET" or "POST"
 
532
 * \param content_type String, optional. Only applies if method is "POST".
 
533
 *      May be "application/x-www-form-urlencoded" or "multipart/form-data".
 
534
 *      Defaults to "application/x-www-form-urlencoded".
 
535
 * \return An XMLHttpRequest object containing the completed response.
 
536
 */
 
537
function ajax_call(app, path, args, method, content_type)
 
538
{
 
539
    if (content_type != "multipart/form-data")
 
540
        content_type = "application/x-www-form-urlencoded";
 
541
    path = app_path(app, path);
 
542
    var url;
 
543
    /* A random string, for multipart/form-data
 
544
     * (This is not checked against anywhere else, it is solely defined and
 
545
     * used within this function) */
 
546
    var boundary = "48234n334nu7n4n2ynonjn234t683jyh80j";
 
547
    var xhr = new XMLHttpRequest();
 
548
    if (method == "GET")
 
549
    {
 
550
        /* GET sends the args in the URL */
 
551
        url = build_url({"path": path, "args": args});
 
552
        /* open's 3rd argument = false -> SYNCHRONOUS (wait for response)
 
553
         * (No need for a callback function) */
 
554
        xhr.open(method, url, false);
 
555
        xhr.send(null);
 
556
    }
 
557
    else
 
558
    {
 
559
        /* POST sends the args in application/x-www-form-urlencoded */
 
560
        url = encodeURI(path);
 
561
        xhr.open(method, url, false);
 
562
        var message;
 
563
        if (content_type == "multipart/form-data")
 
564
        {
 
565
            xhr.setRequestHeader("Content-Type",
 
566
                "multipart/form-data, boundary=" + boundary);
 
567
            message = make_multipart_formdata(args, boundary);
 
568
        }
 
569
        else
 
570
        {
 
571
            xhr.setRequestHeader("Content-Type", content_type);
 
572
            message = make_query_string(args);
 
573
        }
 
574
        xhr.send(message);
 
575
    }
 
576
    return xhr;
 
577
}
 
578