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

« back to all changes in this revision

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

  • Committer: mattgiuca
  • Date: 2007-12-12 01:38:59 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:37
Added src/dispatch/request.py: The IVLE Request class.
dispatch module now transforms the Apache request object into an IVLE one, and
goes through it. (Note this temporarily means the content type is no longer
set, because the request object is missing functionality).

Added brief description of this object in notes/apps/dispatch.txt.

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 + "\n"
432
 
            + "Content-Disposition: form-data; name=\"" + arg_key
433
 
            + "\"\n\n"
434
 
            + arg_val + "\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 + "--\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