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
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"
52
49
/* Mapping MIME types to icons, just the file's basename */
58
55
default_type_icon = "txt.png";
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";
64
61
/* Mapping SVN status to icons, just the file's basename */
66
"unversioned": "unversioned.png",
67
"ignored": null, /* Supposed to be innocuous */
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"
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.
146
function do_action(action, path, args, content_type, callback)
138
function do_action(action, path, args, content_type, ignore_response)
148
140
args.action = action;
149
141
/* Callback action, when the server returns */
150
var callback_inner = function(response)
142
var callback = function(response)
152
144
/* Check for action errors reported by the server, and report them
154
146
var error = response.getResponseHeader("X-IVLE-Action-Error");
155
if (error != null && error != "")
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);
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);
167
159
/** Calls the server using Ajax, requesting a directory listing. This should
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;
260
var req = ajax_call(null, "userservice", "get_enrolments", null, "GET")
261
subjects = decode_response(req);
265
244
/* This will always return a listing, whether it is a dir or a file.
267
246
var listing = response.responseText;
319
298
var isdir = response.getResponseHeader("X-IVLE-Return") == "Dir";
325
/* Top-level dir, with subjects */
326
special_home_listing(listing, subjects, path);
301
handle_dir_listing(path, listing);
305
/* Need to make a 2nd ajax call, this time get the actual file
307
callback = function(response)
309
/* Read the response and set up the page accordingly */
310
handle_contents_response(path, response);
312
/* Call the server and request the listing. */
314
args = shallow_clone_object(url_args);
330
/* Not the top-level dir. Do a normal dir listing. */
331
handle_dir_listing(path, listing.listing);
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);
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");
341
321
update_actions(isdir);
344
function handle_contents_response(path, content_type)
324
function handle_contents_response(path, response)
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)
354
handle_text(path, content_type);
335
handle_text(path, response.responseText,
336
would_be_handler_type);
360
handle_html5_media(path, content_type, "video");
339
/* TODO: Custom image handler */
340
handle_binary(path, response.responseText);
363
handle_html5_media(path, content_type, "audio");
366
handle_object(path, content_type);
343
/* TODO: Custom audio handler */
344
handle_binary(path, response.responseText);
369
347
handle_binary(path);
389
367
upload_callback_count++;
390
368
if (upload_callback_count >= 2)
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.
396
var pre = myFrame.firstChild.getElementsByTagName(
397
'body')[0].firstChild;
398
var data = pre.innerText || pre.textContent;
399
data = JSON.parse(data);
401
alert("Error: " + decodeURIComponent(data['Error']));
402
370
document.getElementsByName('data')[0].value = '';
524
492
return default_svn_nice;
527
/** Returns true if a file is versioned (not unversioned or ignored).
529
function svnstatus_versioned(svnstatus)
531
return svnstatus != "unversioned" && svnstatus != "ignored";
534
495
/** Displays a download link to the binary file.
536
497
function handle_binary(path)
538
// Disable save button
539
using_codepress = false;
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);
557
/** Displays an image file.
559
function handle_image(path)
561
/* Disable save button */
562
using_codepress = false;
566
var url = app_url(service_app, path) + "?return=contents";
569
var img = $("<img />");
570
img.attr("alt", path);
571
img.attr("src", url);
574
var div = $('<div class="padding" />');
575
div.append('<h1>Image Preview</h1>');
577
$("#filesbody").append(div);
580
/* Displays a media file using an HTML5 <audio> or <video> tag.
581
* Falls back to <object> if the format is unsupported.
583
function handle_html5_media(path, type, tag_name)
585
/* Disable save button and hide the save panel */
586
using_codepress = false;
590
var url = app_url(service_app, path) + "?return=contents";
591
var download_url = app_url(download_app, path);
593
/* Fallback download 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);
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));
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.
609
if (support == "probably" || support == "maybe") {
610
var element = html5_element;
612
var element = $('<object />');
613
element.attr("type", type);
614
element.attr("data", url);
616
element.append(link);
619
var div = $('<div class="padding" />');
620
div.append('<h1>File preview</h1>');
622
$("#filesbody").append(div);
625
/** Display generic object content
627
function handle_object(path, content_type)
629
/* Disable save button and hide the save panel */
630
using_codepress = false;
634
var url = app_url(service_app, path) + "?return=contents";
635
var download_url = app_url(download_app, path);
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);
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');
650
var div = $('<div class="padding" />');
651
div.append('<h1>Preview</h1>');
654
$("#filesbody").append(div);
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)
749
591
"return maybe_save('The last saved version will be served.')");
751
593
serve.setAttribute("href",
752
app_url(serve_app, current_path));
594
app_path(serve_app, current_path));
754
596
serve.setAttribute("href",
755
app_url(serve_app, current_path, filename));
597
app_path(serve_app, current_path, filename));
851
693
var pubcond = numsel <= 1 && file.isdir;
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)
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";
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";
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);
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
915
750
/* Log should be available for revisions as well. */
916
751
set_action_state("svnlog", single_versioned_path, true);
918
/* Cleanup should be available for revisions as well. */
919
set_action_state("svncleanup", single_versioned_path, true);
921
single_ivle_versioned_path = (
923
(numsel == 1 && (stat = file_listing[selected_files[0]])) ||
924
(numsel == 0 && (stat = current_file))
925
) && svnstatus_versioned(stat.svnstatus)
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);
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
976
807
action_unpublish(selected_files);
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')
982
if (selected_files.length == 1)
983
stat = file_listing[selected_files[0]];
986
url = stat.svnurl.substr(svn_base.length); // URL-encoded
987
path = decodeURIComponent(url);
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
994
$.post(app_path(service_app, current_path),
995
{"action": "svnrepostat", "path": path},
998
window.location = path_join(app_path('+submit'), url) + '?revision=' + result.svnrevision;
815
alert("Not yet implemented: Submit");
1004
818
action_rename(filename);
1028
842
action_add(selected_files);
1030
844
case "svnremove":
1031
action_svnremove(selected_files);
1034
action_svnrename(selected_files);
845
action_remove(selected_files);
1036
847
case "svnrevert":
1037
848
action_revert(selected_files);
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] || '');
1042
853
case "svnupdate":
1043
854
action_update(selected_files);