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

« back to all changes in this revision

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

  • Committer: wagrant
  • Date: 2008-07-21 07:03:14 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:922
userdb: Add a migration for the change before we moved to migrations.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
 * "text" : When navigating to a text file, the text editor is opened.
32
32
 * "image" : When navigating to an image, the image is displayed (rather than
33
33
 *              going to the text editor).
34
 
 * "video" : When navigation to a video file, a "play" button is presented.
35
34
 * "audio" : When navigating to an audio file, a "play" button is presented.
36
35
 * "binary" : When navigating to a binary file, offer it as a download through
37
36
 *              "serve".
44
43
    "application/x-javascript" : "text",
45
44
    "application/javascript" : "text",
46
45
    "application/json" : "text",
47
 
    "application/xml" : "text",
48
 
    "application/ogg" : "audio",
49
 
    "image/svg+xml": "object"
 
46
    "application/xml" : "text"
50
47
};
51
48
 
52
49
/* Mapping MIME types to icons, just the file's basename */
58
55
default_type_icon = "txt.png";
59
56
 
60
57
/* Relative to IVLE root */
61
 
type_icons_path = "+media/ivle.webapp.core/images/mime";
62
 
type_icons_path_large = "+media/ivle.webapp.core/images/mime/large";
 
58
type_icons_path = "media/images/mime";
 
59
type_icons_path_large = "media/images/mime/large";
63
60
 
64
61
/* Mapping SVN status to icons, just the file's basename */
65
62
svn_icons = {
66
 
    "unversioned": "unversioned.png",
67
 
    "ignored": null,                    /* Supposed to be innocuous */
 
63
    "unversioned": null,
68
64
    "normal": "normal.png",
69
65
    "added": "added.png",
70
66
    "missing": "missing.png",
71
67
    "deleted": "deleted.png",
72
 
    "replaced": "replaced.png",
73
68
    "modified": "modified.png",
74
69
    "conflicted": "conflicted.png",
75
70
    "revision": "revision.png"
78
73
/* Mapping SVN status to "nice" strings */
79
74
svn_nice = {
80
75
    "unversioned": "Temporary file",
81
 
    "ignored": "Temporary file (ignored)",
82
76
    "normal": "Permanent file",
83
77
    "added": "Temporary file (scheduled to be added)",
84
78
    "missing": "Permanent file (missing)",
93
87
default_svn_icon = null;
94
88
default_svn_nice = "Unknown status";
95
89
 
96
 
svn_icons_path = "+media/ivle.webapp.core/images/svn";
 
90
svn_icons_path = "media/images/svn";
97
91
 
98
 
published_icon = "+media/ivle.webapp.core/images/interface/published.png";
 
92
published_icon = "media/images/interface/published.png";
99
93
 
100
94
/* List of MIME types considered "executable" by the system.
101
95
 * Executable files offer a "run" link, implying that the "serve"
140
134
 *      May be "application/x-www-form-urlencoded" or "multipart/form-data".
141
135
 *      Defaults to "application/x-www-form-urlencoded".
142
136
 *      "multipart/form-data" is recommended for large uploads.
143
 
 * \param callback, optional.
144
 
 *      A callback function for after the action has been handled.
145
137
 */
146
 
function do_action(action, path, args, content_type, callback)
 
138
function do_action(action, path, args, content_type, ignore_response)
147
139
{
148
140
    args.action = action;
149
141
    /* Callback action, when the server returns */
150
 
    var callback_inner = function(response)
 
142
    var callback = function(response)
151
143
        {
152
144
            /* Check for action errors reported by the server, and report them
153
145
             * to the user */
154
146
            var error = response.getResponseHeader("X-IVLE-Action-Error");
155
 
            if (error != null && error != "")
 
147
            if (error != null)
156
148
                /* Note: This header (in particular) comes URI-encoded, to
157
149
                 * allow multi-line error messages. Decode */
158
150
                alert("Error: " + decodeURIComponent(error.toString()) + ".");
159
151
            /* Now read the response and set up the page accordingly */
160
 
            if (callback != null)
161
 
                callback(path, response);
 
152
            if (ignore_response != true)
 
153
                handle_response(path, response, true);
162
154
        }
163
155
    /* Call the server and perform the action. This mutates the server. */
164
 
    ajax_call(callback_inner, service_app, path, args, "POST", content_type);
 
156
    ajax_call(callback, service_app, path, args, "POST", content_type);
165
157
}
166
158
 
167
159
/** Calls the server using Ajax, requesting a directory listing. This should
196
188
}
197
189
 
198
190
/** Determines the "handler type" from a MIME type.
199
 
 * The handler type is a string, either "text", "image", "video", "audio", 
200
 
 * "object" or "binary".
 
191
 * The handler type is a string, either "text", "image", "audio" or "binary".
201
192
 */
202
193
function get_handler_type(content_type)
203
194
{
209
200
    {   /* Based on the first part of the MIME type */
210
201
        var handler_type = content_type.split('/')[0];
211
202
        if (handler_type != "text" && handler_type != "image" &&
212
 
            handler_type != "video" && handler_type != "audio")
 
203
            handler_type != "audio")
213
204
            handler_type = "binary";
214
205
        return handler_type;
215
206
    }
250
241
        return;
251
242
    }
252
243
 
253
 
    var subjects = null;
254
 
    /* Remove trailing slash (or path==username won't compare properly) */
255
 
    if (path[path.length-1] == "/")
256
 
        path = path.substr(0, path.length-1);
257
 
    var top_level_dir = path==username;
258
 
    if (top_level_dir)
259
 
    {
260
 
        var req = ajax_call(null, "userservice", "get_enrolments", null, "GET")
261
 
        subjects = decode_response(req);
262
 
    }
263
 
 
264
 
 
265
244
    /* This will always return a listing, whether it is a dir or a file.
266
245
     */
267
246
    var listing = response.responseText;
319
298
    var isdir = response.getResponseHeader("X-IVLE-Return") == "Dir";
320
299
    if (isdir)
321
300
    {
322
 
        setup_for_listing();
323
 
        if (top_level_dir)
324
 
        {
325
 
            /* Top-level dir, with subjects */
326
 
            special_home_listing(listing, subjects, path);
327
 
        }
 
301
        handle_dir_listing(path, listing);
 
302
    }
 
303
    else
 
304
    {
 
305
        /* Need to make a 2nd ajax call, this time get the actual file
 
306
         * contents */
 
307
        callback = function(response)
 
308
            {
 
309
                /* Read the response and set up the page accordingly */
 
310
                handle_contents_response(path, response);
 
311
            }
 
312
        /* Call the server and request the listing. */
 
313
        if (url_args)
 
314
            args = shallow_clone_object(url_args);
328
315
        else
329
 
        {
330
 
            /* Not the top-level dir. Do a normal dir listing. */
331
 
            handle_dir_listing(path, listing.listing);
332
 
        }
333
 
    }
334
 
    else
335
 
    {
336
 
        /* Read the response and set up the page accordingly */
337
 
        var content_type = current_file.type;
338
 
        handle_contents_response(path, content_type, url_args);
339
 
 
 
316
            args = {};
 
317
        /* This time, get the contents of the file, not its metadata */
 
318
        args['return'] = "contents";
 
319
        ajax_call(callback, service_app, path, args, "GET");
340
320
    }
341
321
    update_actions(isdir);
342
322
}
343
323
 
344
 
function handle_contents_response(path, content_type)
 
324
function handle_contents_response(path, response)
345
325
{
346
326
    /* Treat this as an ordinary file. Get the file type. */
347
 
    //var content_type = response.getResponseHeader("Content-Type");
 
327
    var content_type = response.getResponseHeader("Content-Type");
348
328
    var handler_type = get_handler_type(content_type);
 
329
    would_be_handler_type = handler_type;
349
330
    /* handler_type should now be set to either
350
 
     * "text", "image", "video", "audio" or "binary". */
 
331
     * "text", "image", "audio" or "binary". */
351
332
    switch (handler_type)
352
333
    {
353
334
    case "text":
354
 
        handle_text(path, content_type);
 
335
        handle_text(path, response.responseText,
 
336
            would_be_handler_type);
355
337
        break;
356
338
    case "image":
357
 
        handle_image(path);
358
 
        break;
359
 
    case "video":
360
 
        handle_html5_media(path, content_type, "video");
 
339
        /* TODO: Custom image handler */
 
340
        handle_binary(path, response.responseText);
361
341
        break;
362
342
    case "audio":
363
 
        handle_html5_media(path, content_type, "audio");
364
 
        break;
365
 
    case "object":
366
 
        handle_object(path, content_type);
 
343
        /* TODO: Custom audio handler */
 
344
        handle_binary(path, response.responseText);
367
345
        break;
368
346
    case "binary":
369
347
        handle_binary(path);
389
367
    upload_callback_count++;
390
368
    if (upload_callback_count >= 2)
391
369
    {
392
 
        myFrame = frames['upload_iframe'].document;
393
 
        /* Browsers will turn the raw returned JSON into an HTML document. We
394
 
         * need to get the <pre> from inside the <body>, and look at its text.
395
 
         */
396
 
        var pre = myFrame.firstChild.getElementsByTagName(
397
 
            'body')[0].firstChild;
398
 
        var data = pre.innerText || pre.textContent;
399
 
        data = JSON.parse(data);
400
 
        if ('Error' in data)
401
 
            alert("Error: " + decodeURIComponent(data['Error']));
402
370
        document.getElementsByName('data')[0].value = '';
403
371
        refresh();
404
372
    }
421
389
function maybe_save(warning)
422
390
{
423
391
    if (warning == null) warning = '';
424
 
    if (current_file == null || current_file.isdir) return true;
 
392
    if (current_file.isdir) return true;
425
393
    if (document.getElementById("save_button").disabled) return true;
426
394
    return confirm("This file has unsaved changes. " + warning +
427
395
                   "\nAre you sure you wish to continue?");
524
492
        return default_svn_nice;
525
493
}
526
494
 
527
 
/** Returns true if a file is versioned (not unversioned or ignored).
528
 
 */
529
 
function svnstatus_versioned(svnstatus)
530
 
{
531
 
    return svnstatus != "unversioned" && svnstatus != "ignored";
532
 
}
533
 
 
534
495
/** Displays a download link to the binary file.
535
496
 */
536
497
function handle_binary(path)
537
498
{
538
 
    // Disable save button
539
 
    using_codepress = false;
540
 
    disable_save();
541
 
 
542
 
    // Show download link
543
499
    var files = document.getElementById("filesbody");
544
500
    var div = document.createElement("div");
545
501
    files.appendChild(div);
546
502
    div.setAttribute("class", "padding");
547
 
    var download_link = app_url(download_app, path);
 
503
    var download_link = app_path(download_app, path);
548
504
    var par1 = dom_make_text_elem("p",
549
505
        "The file " + path + " is a binary file. To download this file, " +
550
506
        "click the following link:");
554
510
    div.appendChild(par2);
555
511
}
556
512
 
557
 
/** Displays an image file.
558
 
 */
559
 
function handle_image(path)
560
 
{
561
 
    /* Disable save button */
562
 
    using_codepress = false;
563
 
    disable_save();
564
 
 
565
 
    /* URL */
566
 
    var url = app_url(service_app, path) + "?return=contents";
567
 
 
568
 
    /* Image Preview */
569
 
    var img = $("<img />");
570
 
    img.attr("alt", path);
571
 
    img.attr("src", url);
572
 
 
573
 
    /* Show Preview */
574
 
    var div = $('<div class="padding" />');
575
 
    div.append('<h1>Image Preview</h1>');
576
 
    div.append(img);
577
 
    $("#filesbody").append(div);
578
 
}
579
 
 
580
 
/* Displays a media file using an HTML5 <audio> or <video> tag.
581
 
 * Falls back to <object> if the format is unsupported.
582
 
 */
583
 
function handle_html5_media(path, type, tag_name)
584
 
{
585
 
    /* Disable save button and hide the save panel */
586
 
    using_codepress = false;
587
 
    disable_save();
588
 
 
589
 
    /* URL */
590
 
    var url = app_url(service_app, path) + "?return=contents";
591
 
    var download_url = app_url(download_app, path);
592
 
 
593
 
    /* Fallback download link */
594
 
    var link = $(
595
 
        '<p>Could not play ' + tag_name + ' file. ' +
596
 
        'Try <a>downloading it</a> instead.</p>');
597
 
    link.find('a').attr("href", download_url);
598
 
 
599
 
    /* HTML 5 media element */
600
 
    var html5_element = $(
601
 
        '<' + tag_name + ' controls="true" autoplay="true" />');
602
 
    html5_element.attr("src", url);
603
 
    var support = (html5_element[0].canPlayType &&
604
 
                   html5_element[0].canPlayType(type));
605
 
 
606
 
    /* If the browser thinks it might be able to play it, use the HTML5
607
 
     * element. Otherwise, fall back to an <object>, which might work.
608
 
     */
609
 
    if (support == "probably" || support == "maybe") {
610
 
        var element = html5_element;
611
 
    } else {
612
 
        var element = $('<object />');
613
 
        element.attr("type", type);
614
 
        element.attr("data", url);
615
 
    }
616
 
    element.append(link);
617
 
 
618
 
    /* Show Preview */
619
 
    var div = $('<div class="padding" />');
620
 
    div.append('<h1>File preview</h1>');
621
 
    div.append(element);
622
 
    $("#filesbody").append(div);
623
 
}
624
 
 
625
 
/** Display generic object content
626
 
 */
627
 
function handle_object(path, content_type)
628
 
{
629
 
    /* Disable save button and hide the save panel */
630
 
    using_codepress = false;
631
 
    disable_save();
632
 
 
633
 
    /* URL */
634
 
    var url = app_url(service_app, path) + "?return=contents";
635
 
    var download_url = app_url(download_app, path);
636
 
 
637
 
    /* Fallback Download Link */
638
 
    var link = $('<p><a /></p>');
639
 
    var a = link.find('a');
640
 
    a.attr("href", download_url);
641
 
    a.text("Download " + path);
642
 
 
643
 
    /* Object Tag */
644
 
    var obj = $('<object width="100%" height="500px" />');
645
 
    obj.attr("type", content_type);
646
 
    obj.attr("data", url);
647
 
    obj.append('Could not load object');
648
 
 
649
 
    /* Show Preview */
650
 
    var div = $('<div class="padding" />');
651
 
    div.append('<h1>Preview</h1>');
652
 
    div.append(obj);
653
 
    div.append(link);
654
 
    $("#filesbody").append(div);
655
 
}
656
 
 
657
513
/* Enable or disable actions1 moreactions actions. Takes either a single
658
514
 * name, or an array of them.*/
659
515
function set_action_state(names, which, allow_on_revision)
679
535
    }
680
536
}
681
537
 
682
 
/* Updates the list of available actions based on files selected */
683
538
function update_actions()
684
539
{
685
540
    var file;
686
541
    var numsel = selected_files.length;
687
 
    var svn_selection = false;
688
 
    
689
 
    if (numsel > 0)
690
 
    {
691
 
        svn_selection = true;
692
 
        for (var i = 0; i < selected_files.length; i++){
693
 
            if (!svnstatus_versioned(file_listing[selected_files[i]].svnstatus))
694
 
            {
695
 
                svn_selection = false;
696
 
            }
697
 
        }
698
 
    }
699
 
    
700
542
    if (numsel <= 1)
701
543
    {
702
544
        if (numsel == 0)
749
591
              "return maybe_save('The last saved version will be served.')");
750
592
        if (numsel == 0)
751
593
            serve.setAttribute("href",
752
 
                app_url(serve_app, current_path));
 
594
                app_path(serve_app, current_path));
753
595
        else
754
596
            serve.setAttribute("href",
755
 
                app_url(serve_app, current_path, filename));
 
597
                app_path(serve_app, current_path, filename));
756
598
    }
757
599
    else
758
600
    {
767
609
     */
768
610
    var run = document.getElementById("act_run");
769
611
     
770
 
    if (numsel <= 1 && !file.isdir && file.type == "text/x-python" 
771
 
            && current_file.svnstatus != 'revision')
 
612
    if (!file.isdir && file.type == "text/x-python" && numsel <= 1
 
613
        && current_file.svnstatus != 'revision')
772
614
    {
773
615
        if (numsel == 0)
774
616
        {
808
650
        if (numsel == 0)
809
651
        {
810
652
            download.setAttribute("href",
811
 
                app_url(download_app, current_path));
 
653
                app_path(download_app, current_path));
812
654
            if (file.isdir)
813
655
                download.setAttribute("title",
814
656
                    "Download the current directory as a ZIP file");
819
661
        else
820
662
        {
821
663
            download.setAttribute("href",
822
 
                app_url(download_app, current_path, filename));
 
664
                app_path(download_app, current_path, filename));
823
665
            if (file.isdir)
824
666
                download.setAttribute("title",
825
667
                    "Download the selected directory as a ZIP file");
831
673
    else
832
674
    {
833
675
        /* Make a query string with all the files to download */
834
 
        var dlpath = app_url(download_app, current_path) + "?";
 
676
        var dlpath = urlencode_path(app_path(download_app, current_path)) + "?";
835
677
        for (var i=0; i<numsel; i++)
836
678
            dlpath += "path=" + encodeURIComponent(selected_files[i]) + "&";
837
679
        dlpath = dlpath.substr(0, dlpath.length-1);
851
693
    var pubcond = numsel <= 1 && file.isdir;
852
694
    if (pubcond)
853
695
    {
 
696
        /* TODO: Work out of file is svn'd */
854
697
        /* If this dir is already published, call it "Unpublish" */
855
698
        if (file.published)
856
699
        {
857
700
            publish.setAttribute("value", "unpublish");
858
701
            publish.setAttribute("title" ,"Make it so this directory "
859
702
                + "can not be seen by anyone on the web");
860
 
            publish.firstChild.nodeValue = "Unpublish";
 
703
            publish.textContent = "Unpublish";
861
704
        } else {
862
705
            publish.setAttribute("value", "publish");
863
706
            publish.setAttribute("title","Make it so this directory "
864
707
                + "can be seen by anyone on the web");
865
 
            publish.firstChild.nodeValue = "Publish";
 
708
            publish.textContent = "Publish";
866
709
        }
867
710
    }
868
711
    set_action_state(["publish", "submit"], pubcond);
889
732
    /* Subversion actions */
890
733
    /* These are only useful if we are in a versioned directory and have some
891
734
     * files selected. */
892
 
    set_action_state(["svnrename"], numsel == 1 && current_file.svnstatus);
893
 
    set_action_state(["svnadd"], numsel >= 1 && current_file.svnstatus);
894
 
    /* And these are only useful is ALL the selected files are versioned */
895
 
    set_action_state(["svnremove", "svnrevert", "svncopy", "svncut"],
896
 
            numsel >= 1 && current_file.svnstatus && svn_selection);
897
 
    /* Commit is useful if ALL selected files are versioned, or the current
898
 
     * directory is versioned */
899
 
    set_action_state(["svncommit"], current_file.svnstatus &&
900
 
            (numsel >= 1 && svn_selection || numsel == 0));
 
735
    set_action_state(["svnadd", "svnremove", "svnrevert", "svncommit"], numsel >= 1 && current_file.svnstatus);
901
736
 
902
737
    /* Diff, log and update only support one path at the moment, so we must
903
738
     * have 0 or 1 versioned files selected. If 0, the directory must be
906
741
         (
907
742
          (numsel == 1 && (svnst = file_listing[selected_files[0]].svnstatus)) ||
908
743
          (numsel == 0 && (svnst = current_file.svnstatus))
909
 
         ) && svnstatus_versioned(svnst));
 
744
         ) && svnst != "unversioned");
910
745
    set_action_state(["svndiff", "svnupdate"], single_versioned_path);
911
746
 
912
747
    /* We can resolve if we have a file selected and it is conflicted. */
915
750
    /* Log should be available for revisions as well. */
916
751
    set_action_state("svnlog", single_versioned_path, true);
917
752
 
918
 
    /* Cleanup should be available for revisions as well. */
919
 
    set_action_state("svncleanup", single_versioned_path, true);
920
 
 
921
 
    single_ivle_versioned_path = (
922
 
         (
923
 
          (numsel == 1 && (stat = file_listing[selected_files[0]])) ||
924
 
          (numsel == 0 && (stat = current_file))
925
 
         ) && svnstatus_versioned(stat.svnstatus)
926
 
           && stat.svnurl
927
 
           && stat.svnurl.substr(0, svn_base.length) == svn_base);
928
 
    set_action_state(["submit"], single_ivle_versioned_path);
 
753
    /* current_path == username: We are at the top level */
 
754
    set_action_state("svncheckout", current_path == username);
929
755
 
930
756
    /* There is currently nothing on the More Actions menu of use
931
757
     * when the current file is not a directory. Hence, just remove
941
767
        var moreactions = document.getElementById("moreactions_area");
942
768
        moreactions.setAttribute("style", "display: inline;");
943
769
    }
 
770
    else
 
771
    {
 
772
        var actions2_file = document.getElementById("actions2_file");
 
773
        actions2_file.setAttribute("style", "display: inline;");
 
774
    }
944
775
 
945
776
    return;
946
777
}
976
807
        action_unpublish(selected_files);
977
808
        break;
978
809
    case "share":
979
 
        window.open(public_app_url("~" + current_path, filename), 'share')
 
810
        //alert("Not yet implemented: Sharing files");
 
811
        window.open(public_app_path(serve_app, current_path, filename), 'share')
980
812
        break;
981
813
    case "submit":
982
 
        if (selected_files.length == 1)
983
 
            stat = file_listing[selected_files[0]];
984
 
        else
985
 
            stat = current_file;
986
 
        url = stat.svnurl.substr(svn_base.length);      // URL-encoded
987
 
        path = decodeURIComponent(url);
988
 
 
989
 
        /* The working copy might not have an up-to-date version of the
990
 
         * directory. While submitting like this could yield unexpected
991
 
         * results, we should really submit the latest revision to minimise
992
 
         * terrible mistakes - so we run off and ask fileservice for the
993
 
         * latest revision.*/
994
 
        $.post(app_path(service_app, current_path),
995
 
            {"action": "svnrepostat", "path": path},
996
 
            function(result)
997
 
            {
998
 
                window.location = path_join(app_path('+submit'), url) + '?revision=' + result.svnrevision;
999
 
            },
1000
 
            "json");
1001
 
 
 
814
        // TODO
 
815
        alert("Not yet implemented: Submit");
1002
816
        break;
1003
817
    case "rename":
1004
818
        action_rename(filename);
1028
842
        action_add(selected_files);
1029
843
        break;
1030
844
    case "svnremove":
1031
 
        action_svnremove(selected_files);
1032
 
        break;
1033
 
    case "svnrename":
1034
 
        action_svnrename(selected_files);
 
845
        action_remove(selected_files);
1035
846
        break;
1036
847
    case "svnrevert":
1037
848
        action_revert(selected_files);
1038
849
        break;
1039
850
    case "svndiff":
1040
 
        window.location = path_join(app_url('diff'), current_path, selected_files[0] || '');
 
851
        window.location = path_join(app_path('diff'), current_path, selected_files[0] || '');
1041
852
        break;
1042
853
    case "svnupdate":
1043
854
        action_update(selected_files);
1049
860
        action_commit(selected_files);
1050
861
        break;
1051
862
    case "svnlog":
1052
 
        window.location = path_join(app_url('svnlog'), current_path, selected_files[0] || '');
1053
 
        break;
1054
 
    case "svncopy":
1055
 
        action_svncopy(selected_files);
1056
 
        break;
1057
 
    case "svncut":
1058
 
        action_svncut(selected_files);
1059
 
        break;
1060
 
    case "svncleanup":
1061
 
        action_svncleanup(".");
 
863
        window.location = path_join(app_path('svnlog'), current_path, selected_files[0] || '');
 
864
        break;
 
865
    case "svncheckout":
 
866
        action_checkout();
1062
867
        break;
1063
868
    }
1064
869
}
1087
892
     * This causes the page to be populated with whatever is at that address,
1088
893
     * whether it be a directory or a file.
1089
894
     */
1090
 
    var path = get_path();
1091
 
    navigate(path);
1092
 
}
1093
 
 
1094
 
/** Gets the current path of the window */
1095
 
function get_path() {
1096
895
    var path = parse_url(window.location.href).path;
1097
896
    /* Strip out root_dir + "/files" from the front of the path */
1098
897
    var strip = make_path(this_app);
1115
914
        path = username;
1116
915
    }
1117
916
 
1118
 
    return path;
 
917
    navigate(path);
1119
918
}