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

« back to all changes in this revision

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

Added module ivle.config, which takes care of some work interfacing with
    configobj, including searching for the file and opening the object.
ivle.conf.conf now uses this instead of having its own search.

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
 * - public_host
 
29
 * - username
 
30
 */
 
31
 
 
32
/** Removes all children of a given DOM element
 
33
 * \param elem A DOM Element. Will be modified.
 
34
 */
 
35
function dom_removechildren(elem)
 
36
{
 
37
    while (elem.lastChild != null)
 
38
        elem.removeChild(elem.lastChild);
 
39
}
 
40
 
 
41
/** Creates a DOM element with simple text inside it.
 
42
 * \param tagname String. Name of the element's tag (eg. "p").
 
43
 * \param text String. Text to be placed inside the element.
 
44
 * \param title String, optional. Tooltip for the text.
 
45
 *  (Note, title creates a span element around the text).
 
46
 * \return DOM Element object.
 
47
 */
 
48
function dom_make_text_elem(tagname, text, title)
 
49
{
 
50
    if (text == null) text = "";
 
51
    var elem = document.createElement(tagname);
 
52
    var textnode;
 
53
    if (title == null)
 
54
        textnode = document.createTextNode(text);
 
55
    else
 
56
    {
 
57
        textnode = document.createElement("span");
 
58
        textnode.setAttribute("title", title);
 
59
        textnode.appendChild(document.createTextNode(text));
 
60
    }
 
61
    elem.appendChild(textnode);
 
62
    return elem;
 
63
}
 
64
 
 
65
/** Creates a DOM element with hyperlinked text inside it.
 
66
 * \param tagname String. Name of the element's tag (eg. "p").
 
67
 * \param text String. Text to be placed inside the element.
 
68
 * \param title String, optional. Sets a tooltip for the link.
 
69
 * \param href String. URL the text will link to. This is a raw string,
 
70
 *  it will automatically be URL-encoded.
 
71
 * \param onclick Optional string. Will be set as the "onclick" attribute
 
72
 *  of the "a" element.
 
73
 * \param dontencode Optional boolean. If true, will not encode the href.
 
74
 *  if including query strings, you must set this to true and use build_url
 
75
 *  to escape the URI correctly.
 
76
 * \return DOM Element object.
 
77
 */
 
78
function dom_make_link_elem(tagname, text, title, href, onclick, dontencode)
 
79
{
 
80
    if (text == null) text = "";
 
81
    if (href == null) href = "";
 
82
    var elem = document.createElement(tagname);
 
83
    var link = document.createElement("a");
 
84
    if (dontencode != true)
 
85
        href = urlencode_path(href);
 
86
    link.setAttribute("href", href);
 
87
    if (title != null)
 
88
        link.setAttribute("title", title);
 
89
    if (onclick != null)
 
90
        link.setAttribute("onclick", onclick);
 
91
    link.appendChild(document.createTextNode(text));
 
92
    elem.appendChild(link);
 
93
    return elem;
 
94
}
 
95
 
 
96
/** Creates a DOM img element. All parameters are optional except src.
 
97
 * If alt (compulsory in HTML) is omitted, will be set to "".
 
98
 */
 
99
function dom_make_img(src, width, height, title, alt)
 
100
{
 
101
    var img = document.createElement("img");
 
102
    img.setAttribute("src", urlencode_path(src));
 
103
    if (width != null)
 
104
        img.setAttribute("width", width);
 
105
    if (height != null)
 
106
        img.setAttribute("height", height);
 
107
    if (title != null)
 
108
        img.setAttribute("title", title);
 
109
    if (alt == null) alt = "";
 
110
    img.setAttribute("alt", alt);
 
111
    return img;
 
112
}
 
113
 
 
114
/** Given a number of bytes, returns a string representing the file size in a
 
115
 * human-readable format.
 
116
 * eg. nice_filesize(6) -> "6 bytes"
 
117
 *     nice_filesize(81275) -> "79.4 kB"
 
118
 *     nice_filesize(13498346) -> "12.9 MB"
 
119
 * \param bytes Number of bytes. Must be an integer.
 
120
 * \return String.
 
121
 */
 
122
function nice_filesize(bytes)
 
123
{
 
124
    if (bytes == null) return "";
 
125
    var size;
 
126
    if (bytes < 1024)
 
127
        return bytes.toString() + " B";
 
128
    size = bytes / 1024;
 
129
    if (size < 1024)
 
130
        return size.toFixed(1) + " kB";
 
131
    size = size / 1024;
 
132
    if (size < 1024)
 
133
        return size.toFixed(1) + " MB";
 
134
    size = size / 1024;
 
135
    return size.toFixed(1) + " GB";
 
136
}
 
137
 
 
138
/** Given a URL, returns an object containing a number of attributes
 
139
 * describing the components of the URL, similar to CGI request variables.
 
140
 * The object has the following attributes:
 
141
 * - scheme
 
142
 * - server_name
 
143
 * - server_port
 
144
 * - path
 
145
 * - query_string
 
146
 * - args
 
147
 * The first five of these are strings, which comprise the URL as follows:
 
148
 * <scheme> "://" <server_name> ":" <server_port> <path> "?" <query_string>
 
149
 * Any of these strings may be set to null if not found.
 
150
 *
 
151
 * "args" is an object whose attributes are the query_string arguments broken
 
152
 * up.
 
153
 * Args values are strings for single values, arrays of strings for values
 
154
 * whose names appear multiple times.
 
155
 * args is never null, though it may be empty.
 
156
 *
 
157
 * All strings are decoded/unescaped. Reserved characters
 
158
 * (; , / ? : @ & = + * $) are not decoded except in args and path.
 
159
 *
 
160
 * \param url String. A URL. To read from the current browser window, use
 
161
 *  window.location.href.
 
162
 * \return The above described object.
 
163
 */
 
164
function parse_url(url)
 
165
{
 
166
    var obj = {};
 
167
    var index;
 
168
    var serverpart;
 
169
    var args;
 
170
 
 
171
    /* Split scheme from rest */
 
172
    index = url.indexOf("://");
 
173
    if (index < 0)
 
174
        obj.scheme = null;
 
175
    else
 
176
    {
 
177
        obj.scheme = url.substr(0, index);
 
178
        url = url.substr(index+3);
 
179
    }
 
180
 
 
181
    /* Split server name/port from rest */
 
182
    index = url.indexOf("/");
 
183
    if (index < 0)
 
184
    {
 
185
        serverpart = url;
 
186
        url = null;
 
187
    }
 
188
    else
 
189
    {
 
190
        serverpart = url.substr(0, index);
 
191
        url = url.substr(index);
 
192
    }
 
193
 
 
194
    /* Split server name from port */
 
195
    index = serverpart.indexOf(":");
 
196
    if (index < 0)
 
197
    {
 
198
        obj.server_name = serverpart;
 
199
        obj.server_port = null;
 
200
    }
 
201
    else
 
202
    {
 
203
        obj.server_name = serverpart.substr(0, index);
 
204
        obj.server_port = serverpart.substr(index+1);
 
205
    }
 
206
 
 
207
    /* Split path from query string */
 
208
    if (url == null)
 
209
    {
 
210
        obj.path = null;
 
211
        obj.query_string = null;
 
212
    }
 
213
    else
 
214
    {
 
215
        index = url.indexOf("?");
 
216
        if (index < 0)
 
217
        {
 
218
            obj.path = url;
 
219
            obj.query_string = null;
 
220
        }
 
221
        else
 
222
        {
 
223
            obj.path = url.substr(0, index);
 
224
            obj.query_string = url.substr(index+1);
 
225
        }
 
226
    }
 
227
    obj.path = decodeURIComponent(obj.path);
 
228
 
 
229
    /* Split query string into arguments */
 
230
    args = {};
 
231
    if (obj.query_string != null)
 
232
    {
 
233
        var args_strs = obj.query_string.split("&");
 
234
        var arg_str;
 
235
        var arg_key, arg_val;
 
236
        for (var i=0; i<args_strs.length; i++)
 
237
        {
 
238
            arg_str = args_strs[i];
 
239
            index = arg_str.indexOf("=");
 
240
            /* Ignore malformed args */
 
241
            if (index >= 0)
 
242
            {
 
243
                arg_key = decodeURIComponent(arg_str.substr(0, index));
 
244
                arg_val = decodeURIComponent(arg_str.substr(index+1));
 
245
                if (arg_key in args)
 
246
                {
 
247
                    /* Collision - make an array */
 
248
                    if (args[arg_key] instanceof Array)
 
249
                        args[arg_key][args[arg_key].length] = arg_val;
 
250
                    else
 
251
                        args[arg_key] = [args[arg_key], arg_val];
 
252
                }
 
253
                else
 
254
                    args[arg_key] = arg_val;
 
255
            }
 
256
        }
 
257
    }
 
258
    obj.args = args;
 
259
 
 
260
    return obj;
 
261
}
 
262
 
 
263
/** Builds a query_string from an args object. Encodes the arguments.
 
264
 * \param args Args object as described in parse_url.
 
265
 * \return Query string portion of a URL.
 
266
 */
 
267
function make_query_string(args)
 
268
{
 
269
    var query_string = "";
 
270
    var arg_val;
 
271
    for (var arg_key in args)
 
272
    {
 
273
        arg_val = args[arg_key];
 
274
        if (arg_val instanceof Array)
 
275
            for (var i=0; i<arg_val.length; i++)
 
276
                query_string += "&" + encodeURIComponent(arg_key) + "=" +
 
277
                    encodeURIComponent(arg_val[i]);
 
278
        else
 
279
            query_string += "&" + encodeURIComponent(arg_key) + "=" +
 
280
                encodeURIComponent(arg_val);
 
281
    }
 
282
    if (query_string != "")
 
283
        /* Drop the first "&" */
 
284
        query_string = query_string.substr(1);
 
285
 
 
286
    return query_string;
 
287
}
 
288
 
 
289
/** Given an object exactly of the form described for the output of parseurl,
 
290
 * returns a URL string built from those parameters. The URL is properly
 
291
 * encoded.
 
292
 * parseurl and buildurl are strict inverses of each other.
 
293
 * Note that either query_string or args may be supplied. If both are
 
294
 * supplied, query_string is preferred (because it keeps the argument order).
 
295
 * If you take a url from parseurl, modify args, and pass to buildurl,
 
296
 * you need to set query_string to null to use the new args.
 
297
 * \param obj Object as returned by parseurl.
 
298
 * \return String, a URL.
 
299
 */
 
300
function build_url(obj)
 
301
{
 
302
    var url = "";
 
303
    var query_string = null;
 
304
 
 
305
    if (("scheme" in obj) && obj.scheme != null)
 
306
        url = obj.scheme.toString() + "://";
 
307
    if (("server_name" in obj) && obj.server_name != null)
 
308
        url += obj.server_name.toString();
 
309
    if (("server_port" in obj) && obj.server_port != null)
 
310
        url += ":" + obj.server_port.toString();
 
311
    if (("path" in obj) && obj.path != null)
 
312
    {
 
313
        var path = urlencode_path(obj.path.toString());
 
314
        if (url.length > 0 && path.length > 0 && path.charAt(0) != "/")
 
315
            path = "/" + path;
 
316
        url += path;
 
317
    }
 
318
    if (("query_string" in obj) && obj.query_string != null)
 
319
        query_string = encodeURI(obj.query_string.toString());
 
320
    else if (("args" in obj) && obj.args != null)
 
321
        query_string = make_query_string(obj.args);
 
322
 
 
323
    if (query_string != "" && query_string != null)
 
324
        url += "?" + query_string;
 
325
 
 
326
    return url;
 
327
}
 
328
 
 
329
/** URL-encodes a path. This is a special case of URL encoding as all
 
330
 * characters *except* the slash must be encoded.
 
331
 */
 
332
function urlencode_path(path)
 
333
{
 
334
    /* Split up the path, URLEncode each segment with encodeURIComponent,
 
335
     * and rejoin.
 
336
     */
 
337
    var split = path.split('/');
 
338
    for (var i=0; i<split.length; i++)
 
339
        split[i] = encodeURIComponent(split[i]);
 
340
    path = path_join.apply(null, split);
 
341
    if (split[0] == "" && split.length > 1) path = "/" + path;
 
342
    return path;
 
343
}
 
344
 
 
345
/** Writes a JSONable object to the cookie under a particular key
 
346
 * (JSON encoded and URL encoded).
 
347
 */
 
348
function write_cookie(key, value)
 
349
{
 
350
    var sendstr = encodeURIComponent(key) + "="
 
351
        + encodeURIComponent(JSON.stringify(value))
 
352
        + "; path=" + urlencode_path(root_dir);
 
353
    /* This actually just assigns to the key, not replacing the whole cookie
 
354
     * as it appears to. */
 
355
    document.cookie = sendstr;
 
356
}
 
357
/** Reads a cookie which has a JSONable object encoded as its value.
 
358
 * Returns the object, parsed from JSON.
 
359
 */
 
360
function read_cookie(key)
 
361
{
 
362
    var cookies = document.cookie.split(";");
 
363
    var checkstart = encodeURIComponent(key) + "=";
 
364
    var checklen = checkstart.length;
 
365
    for (var i=0; i<cookies.length; i++)
 
366
    {
 
367
        var cookie = cookies[i];
 
368
        while (cookie[0] == ' ')
 
369
            cookie = cookie.substr(1);
 
370
        if (cookie.substr(0, checklen) == checkstart)
 
371
        {
 
372
            var valstr = cookie.substr(checklen);
 
373
            valstr = decodeURIComponent(valstr);
 
374
            return JSON.parse(valstr);
 
375
        }
 
376
    }
 
377
}
 
378
 
 
379
/** Given an argument map, as output in the args parameter of the return of
 
380
 * parseurl, gets the first occurence of an argument in the URL string.
 
381
 * If the argument was not found, returns null.
 
382
 * If there was a single argument, returns the argument.
 
383
 * If there were multiple arguments, returns the first.
 
384
 * \param args Object mapping arguments to strings or arrays of strings.
 
385
 * \param arg String. Argument name.
 
386
 * \return String.
 
387
 */
 
388
function arg_getfirst(args, arg)
 
389
{
 
390
    if (!(arg in args))
 
391
        return null;
 
392
    var r = args[arg];
 
393
    if (r instanceof Array)
 
394
        return r[0];
 
395
    else
 
396
        return r;
 
397
}
 
398
 
 
399
/** Given an argument map, as output in the args parameter of the return of
 
400
 * parseurl, gets all occurences of an argument in the URL string, as an
 
401
 * array.
 
402
 * If the argument was not found, returns [].
 
403
 * Otherwise, returns all occurences as an array, even if there was only one.
 
404
 * \param args Object mapping arguments to strings or arrays of strings.
 
405
 * \param arg String. Argument name.
 
406
 * \return Array of strings.
 
407
 */
 
408
function arg_getlist(args, arg)
 
409
{
 
410
    if (!(arg in args))
 
411
        return [];
 
412
    var r = args[arg];
 
413
    if (r instanceof Array)
 
414
        return r;
 
415
    else
 
416
        return [r];
 
417
}
 
418
 
 
419
/** Joins one or more paths together. Accepts 1 or more arguments.
 
420
 */
 
421
function path_join(path1 /*, path2, ... */)
 
422
{
 
423
    var arg;
 
424
    var path = "";
 
425
    for (var i=0; i<arguments.length; i++)
 
426
    {
 
427
        arg = arguments[i];
 
428
        if (arg.length == 0) continue;
 
429
        if (arg.charAt(0) == '/')
 
430
            path = arg;
 
431
        else
 
432
        {
 
433
            if (path.length > 0 && path.charAt(path.length-1) != '/')
 
434
                path += '/';
 
435
            path += arg;
 
436
        }
 
437
    }
 
438
    return path;
 
439
}
 
440
 
 
441
 
 
442
/** Builds a multipart_formdata string from an args object. Similar to
 
443
 * make_query_string, but it returns data of type "multipart/form-data"
 
444
 * instead of "application/x-www-form-urlencoded". This is good for
 
445
 * encoding large strings such as text objects from the editor.
 
446
 * Should be written with a Content-Type of
 
447
 * "multipart/form-data, boundary=<boundary>".
 
448
 * All fields are sent with a Content-Type of text/plain.
 
449
 * \param args Args object as described in parse_url.
 
450
 * \param boundary Random "magic" string which DOES NOT appear in any of
 
451
 *  the argument values. This should match the "boundary=" value written to
 
452
 *  the Content-Type header.
 
453
 * \return String in multipart/form-data format.
 
454
 */
 
455
function make_multipart_formdata(args, boundary)
 
456
{
 
457
    var data = "";
 
458
    var arg_val;
 
459
    /* Mutates data */
 
460
    var extend_data = function(arg_key, arg_val)
 
461
    {
 
462
        /* FIXME: Encoding not supported here (should not matter if we
 
463
         * only use ASCII names */
 
464
        data += "--" + boundary + "\r\n"
 
465
            + "Content-Disposition: form-data; name=\"" + arg_key
 
466
            + "\"\r\n\r\n"
 
467
            + arg_val + "\r\n";
 
468
    }
 
469
 
 
470
    for (var arg_key in args)
 
471
    {
 
472
        arg_val = args[arg_key];
 
473
        if (arg_val instanceof Array)
 
474
            for (var i=0; i<arg_val.length; i++)
 
475
            {
 
476
                extend_data(arg_key, arg_val[i]);
 
477
            }
 
478
        else
 
479
            extend_data(arg_key, arg_val);
 
480
    }
 
481
    /* End boundary */
 
482
    data += "--" + boundary + "--\r\n";
 
483
 
 
484
    return data;
 
485
}
 
486
 
 
487
/** Converts a list of directories into a path name, with a slash at the end.
 
488
 * \param pathlist List of strings.
 
489
 * \return String.
 
490
 */
 
491
function pathlist_to_path(pathlist)
 
492
{
 
493
    ret = path_join.apply(null, pathlist);
 
494
    if (ret.charAt(ret.length-1) != '/')
 
495
        ret += '/';
 
496
    return ret;
 
497
}
 
498
 
 
499
/** Given a path relative to the IVLE root, gives a path relative to
 
500
 * the site root.
 
501
 */
 
502
function make_path(path)
 
503
{
 
504
    return path_join(root_dir, path);
 
505
}
 
506
 
 
507
/** Shorthand for make_path(path_join(app, ...))
 
508
 * Creates an absolute path for a given path within a given app.
 
509
 */
 
510
function app_path(app /*,...*/)
 
511
{
 
512
    return make_path(path_join.apply(null, arguments));
 
513
}
 
514
 
 
515
/** Generates an absolute URL to a public application
 
516
 */
 
517
function public_app_path(app /*,...*/)
 
518
{
 
519
    return location.protocol + "//" + public_host
 
520
        + make_path(path_join.apply(null, arguments));
 
521
}
 
522
 
 
523
/** Given a path, gets the "basename" (the last path segment).
 
524
 */
 
525
function path_basename(path)
 
526
{
 
527
    segments = path.split("/");
 
528
    if (segments[segments.length-1].length == 0)
 
529
        return segments[segments.length-2];
 
530
    else
 
531
        return segments[segments.length-1];
 
532
}
 
533
 
 
534
/** Given a string str, determines whether it ends with substr */
 
535
function endswith(str, substring)
 
536
{
 
537
    if (str.length < substring.length) return false;
 
538
    return str.substr(str.length - substring.length) == substring;
 
539
}
 
540
 
 
541
/** Removes all occurences of a value from an array.
 
542
 */
 
543
Array.prototype.removeall = function(val)
 
544
{
 
545
    var i, j;
 
546
    var arr = this;
 
547
    j = 0;
 
548
    for (i=0; i<arr.length; i++)
 
549
    {
 
550
        arr[j] = arr[i];
 
551
        if (arr[i] != val) j++;
 
552
    }
 
553
    arr.splice(j, i-j);
 
554
}
 
555
 
 
556
/** Shallow-clones an object */
 
557
function shallow_clone_object(obj)
 
558
{
 
559
    o = {};
 
560
    for (k in obj)
 
561
        o[k] = obj[k];
 
562
    return o;
 
563
}
 
564
 
 
565
/** Returns a new XMLHttpRequest object, in a somewhat browser-agnostic
 
566
 * fashion.
 
567
 */
 
568
function new_xmlhttprequest()
 
569
{
 
570
    try
 
571
    {
 
572
        /* Real Browsers */
 
573
        return new XMLHttpRequest();
 
574
    }
 
575
    catch (e)
 
576
    {
 
577
        /* Internet Explorer */
 
578
        try
 
579
        {
 
580
            return new ActiveXObject("Msxml2.XMLHTTP");
 
581
        }
 
582
        catch (e)
 
583
        {
 
584
            try
 
585
            {
 
586
                return new ActiveXObject("Microsoft.XMLHTTP");
 
587
            }
 
588
            catch (e)
 
589
            {
 
590
                throw("Your browser does not support AJAX. "
 
591
                    + "IVLE requires a modern browser.");
 
592
            }
 
593
        }
 
594
    }
 
595
}
 
596
 
 
597
/** Creates a random string of length length,
 
598
 * consisting of alphanumeric characters.
 
599
 */
 
600
var rand_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ"
 
601
               + "abcdefghiklmnopqrstuvwxyz";
 
602
function random_string(length)
 
603
{
 
604
    var str = Array(length);
 
605
    var v;
 
606
    for (var i=0; i<length; i++)
 
607
    {
 
608
        v = Math.floor(Math.random() * rand_chars.length);
 
609
        str[i] = rand_chars.charAt(v);
 
610
    }
 
611
    return str.join('');
 
612
}
 
613
 
 
614
/** Makes an XMLHttpRequest call to the server.
 
615
 * Sends the XMLHttpRequest object containing the completed response to a
 
616
 * specified callback function.
 
617
 *
 
618
 * \param callback A callback function. Will be called when the response is
 
619
 *      complete. Passed 1 parameter, an XMLHttpRequest object containing the
 
620
 *      completed response. If callback is null this is a syncronous request 
 
621
 *      otherwise this is an asynchronous request.
 
622
 * \param app IVLE app to call (such as "fileservice").
 
623
 * \param path URL path to make the request to, within the application.
 
624
 * \param args Argument object, as described in parse_url and friends.
 
625
 * \param method String; "GET", "POST", "PUT", or "PATCH"
 
626
 * \param content_type String, optional.
 
627
 *      Defaults to "application/x-www-form-urlencoded".
 
628
 */
 
629
function ajax_call(callback, app, path, args, method, content_type)
 
630
{
 
631
    if (!content_type)
 
632
        content_type = "application/x-www-form-urlencoded";
 
633
    path = app_path(app, path);
 
634
    var url;
 
635
    /* A random string, for multipart/form-data
 
636
     * (This is not checked against anywhere else, it is solely defined and
 
637
     * used within this function) */
 
638
    var boundary = random_string(20);
 
639
    var xhr = new_xmlhttprequest();
 
640
    var asyncronous = callback != null;
 
641
    if (asyncronous)
 
642
    {
 
643
        xhr.onreadystatechange = function()
 
644
            {
 
645
                if (xhr.readyState == 4)
 
646
                {
 
647
                    callback(xhr);
 
648
                }
 
649
            }
 
650
    }
 
651
    if (method == "GET")
 
652
    {
 
653
        /* GET sends the args in the URL */
 
654
        url = build_url({"path": path, "args": args});
 
655
        /* open's 3rd argument = true -> asynchronous */
 
656
        xhr.open(method, url, asyncronous);
 
657
        xhr.send(null);
 
658
    }
 
659
    else
 
660
    {
 
661
        /* POST & PUT & PATCH sends the args in the request body */
 
662
        url = encodeURI(path);
 
663
        xhr.open(method, url, asyncronous);
 
664
        var message;
 
665
        if (content_type == "multipart/form-data")
 
666
        {
 
667
            xhr.setRequestHeader("Content-Type",
 
668
                "multipart/form-data; boundary=" + boundary);
 
669
            message = make_multipart_formdata(args, boundary);
 
670
        }
 
671
        else if (content_type == "application/x-www-form-urlencoded")
 
672
        {
 
673
            xhr.setRequestHeader("Content-Type", content_type);
 
674
            message = make_query_string(args);
 
675
        }
 
676
        else if (content_type == "application/json")
 
677
        {
 
678
            xhr.setRequestHeader("Content-Type", content_type);
 
679
            message = JSON.stringify(args);
 
680
        }
 
681
        else
 
682
        {
 
683
            xhr.setRequestHeader("Content-Type", content_type);
 
684
            message = args;
 
685
        }
 
686
        xhr.send(message);
 
687
    }
 
688
    /* Only return the XHR for syncronous requests */
 
689
    if (!asyncronous)
 
690
    { 
 
691
        return xhr;
 
692
    }
 
693
}
 
694
 
 
695
/** Attempts to JSON decodes a response object
 
696
 * If a non-200 response or the JSON decode fails then returns null
 
697
 */
 
698
function decode_response(response)
 
699
{
 
700
    if (response.status == 200)
 
701
    {
 
702
        try
 
703
        {
 
704
            var responseText = response.responseText;
 
705
            return JSON.parse(responseText);
 
706
        }
 
707
        catch (e)
 
708
        {
 
709
            // Pass
 
710
        }
 
711
     }
 
712
    
 
713
     return null;
 
714
}