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

« back to all changes in this revision

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

  • Committer: mattgiuca
  • Date: 2008-02-05 01:41:15 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:409
Moved www/conf and www/common to a new directory lib. This separates the "web"
part of IVLE from what is becoming less web oriented (at least from Apache's
standpoint).
Modified setup.py to install this lib directory correctly and write conf in
the right place. Also adds the lib directory to ivle.pth.

Show diffs side-by-side

added added

removed removed

Lines of Context:
43
43
    "application/x-javascript" : "text",
44
44
    "application/javascript" : "text",
45
45
    "application/json" : "text",
46
 
    "application/xml" : "text"
 
46
    "application/xml" : "text",
47
47
};
48
48
 
49
49
/* Mapping MIME types to icons, just the file's basename */
50
50
type_icons = {
51
51
    "text/directory": "dir.png",
52
 
    "text/x-python": "py.png"
 
52
    "text/x-python": "py.png",
53
53
};
54
54
 
55
55
default_type_icon = "txt.png";
66
66
    "missing": "missing.png",
67
67
    "deleted": "deleted.png",
68
68
    "modified": "modified.png",
69
 
    "conflicted": "conflicted.png",
70
 
    "revision": "revision.png"
71
69
};
72
70
 
73
71
/* Mapping SVN status to "nice" strings */
81
79
    "modified": "Permanent file (modified)",
82
80
    "merged": "Permanent file (merged)",
83
81
    "conflicted": "Permanent file (conflicted)",
84
 
    "revision": "Past Permanent file (revision)"
85
82
};
86
83
 
87
84
default_svn_icon = null;
96
93
 * application can interpret them.
97
94
 */
98
95
types_exec = [
99
 
    "text/x-python"
 
96
    "text/x-python",
100
97
];
101
98
 
102
99
 
103
100
/* Global variables */
104
101
 
105
 
/** The listing object returned by the server as JSON */
106
 
file_listing = null;
107
 
current_file = null;
108
 
current_revision = null;
109
102
current_path = "";
110
103
 
111
 
/** Filenames of all files selected
112
 
 * (Only used by dir listings, but still needs to be [] for files, so that
113
 
 * update_actions knows that nothing is selected).
114
 
 */
115
 
selected_files = [];
116
 
 
117
 
upload_callback_count = 0;      /* See upload_callback */
118
 
 
119
104
/** Calls the server using Ajax, performing an action on the server side.
120
105
 * Receives the response from the server and performs a refresh of the page
121
106
 * contents, updating it to display the returned data (such as a directory
138
123
function do_action(action, path, args, content_type, ignore_response)
139
124
{
140
125
    args.action = action;
141
 
    /* Callback action, when the server returns */
142
 
    var callback = function(response)
143
 
        {
144
 
            /* Check for action errors reported by the server, and report them
145
 
             * to the user */
146
 
            var error = response.getResponseHeader("X-IVLE-Action-Error");
147
 
            if (error != null)
148
 
                /* Note: This header (in particular) comes URI-encoded, to
149
 
                 * allow multi-line error messages. Decode */
150
 
                alert("Error: " + decodeURIComponent(error.toString()) + ".");
151
 
            /* Now read the response and set up the page accordingly */
152
 
            if (ignore_response != true)
153
 
                handle_response(path, response, true);
154
 
        }
155
126
    /* Call the server and perform the action. This mutates the server. */
156
 
    ajax_call(callback, service_app, path, args, "POST", content_type);
 
127
    response = ajax_call(service_app, path, args, "POST", content_type);
 
128
    /* Check for action errors reported by the server, and report them to the
 
129
     * user */
 
130
    error = response.getResponseHeader("X-IVLE-Action-Error");
 
131
    if (error != null)
 
132
        alert("Error: " + error.toString() + ".");
 
133
    /* Now read the response and set up the page accordingly */
 
134
    if (ignore_response != true)
 
135
        handle_response(path, response);
157
136
}
158
137
 
159
138
/** Calls the server using Ajax, requesting a directory listing. This should
163
142
 * Called "navigate", can also be used for a simple refresh.
164
143
 * Always makes a GET request.
165
144
 * No return value.
166
 
 */
167
 
function navigate(path)
168
 
{
169
 
    callback = function(response)
170
 
        {
171
 
            /* Read the response and set up the page accordingly */
172
 
            handle_response(path, response, false, url.args);
173
 
        }
174
 
    /* Get any query strings */
175
 
    url = parse_url(window.location.href);
176
 
    
177
 
    /* Call the server and request the listing. */
178
 
    ajax_call(callback, service_app, path, url.args, "GET");
179
 
}
180
 
 
181
 
/* Refreshes the current view.
182
 
 * Calls navigate on the current path.
183
 
 */
184
 
function refresh()
185
 
{
186
 
    if (maybe_save('All changes since the last save will be lost!'))
187
 
        navigate(current_path);
 
145
 * \param editmode Optional boolean. If true, then the user navigated here
 
146
 * with an "edit" URL so we should favour using the editor.
 
147
 */
 
148
function navigate(path, editmode)
 
149
{
 
150
    /* Call the server and request the listing. This mutates the server. */
 
151
    response = ajax_call(service_app, path, null, "GET");
 
152
    /* Now read the response and set up the page accordingly */
 
153
    handle_response(path, response, editmode);
188
154
}
189
155
 
190
156
/** Determines the "handler type" from a MIME type.
216
182
 * things) be used to update the URL in the location bar.
217
183
 * \param response XMLHttpRequest object returned by the server. Should
218
184
 * contain all the response data.
219
 
 * \param is_action Boolean. True if this is the response to an action, false
220
 
 * if this is the response to a simple listing. This is used in handling the
221
 
 * error.
222
 
 * \param url_args Arguments dict, for the arguments passed to the URL
223
 
 * in the browser's address bar (will be forwarded along).
 
185
 * \param editmode Optional boolean. If true, then the user navigated here
 
186
 * with an "edit" URL so we should favour using the editor.
224
187
 */
225
 
function handle_response(path, response, is_action, url_args)
 
188
function handle_response(path, response, editmode)
226
189
{
227
190
    /* TODO: Set location bar to "path" */
228
191
    current_path = path;
229
192
 
230
193
    /* Clear away the existing page contents */
231
194
    clearpage();
 
195
    /* Display the path at the top, for navigation */
 
196
    presentpath(path);
232
197
 
233
198
    /* Check the status, and if not 200, read the error and handle this as an
234
199
     * error. */
241
206
        return;
242
207
    }
243
208
 
244
 
    var subjects = null;
245
 
    var top_level_dir = path==username;
246
 
    if (top_level_dir)
247
 
    {
248
 
        var req = ajax_call(null, "userservice", "get_enrolments", null, "GET")
249
 
        subjects = decode_response(req);
250
 
    }
251
 
 
252
 
 
253
 
    /* This will always return a listing, whether it is a dir or a file.
254
 
     */
255
 
    var listing = response.responseText;
256
 
    /* The listing SHOULD be valid JSON text. Parse it into an object. */
257
 
    try
258
 
    {
259
 
        listing = JSON.parse(listing);
260
 
        file_listing = listing.listing;     /* Global */
261
 
    }
262
 
    catch (e)
263
 
    {
264
 
        if (is_action)
265
 
        {
266
 
            var err = document.createElement("div");
267
 
            var p = dom_make_text_elem("p", "Error: "
268
 
                    + "There was an unexpected server error processing "
269
 
                    + "the selected command.");
270
 
            err.appendChild(p);
271
 
            p = dom_make_text_elem("p", "If the problem persists, please "
272
 
                    + "contact the system administrator.")
273
 
            err.appendChild(p);
274
 
            p = document.createElement("p");
275
 
            var refresh = document.createElement("input");
276
 
            refresh.setAttribute("type", "button");
277
 
            refresh.setAttribute("value", "Back to file view");
278
 
            refresh.setAttribute("onclick", "refresh()");
279
 
            p.appendChild(refresh);
280
 
            err.appendChild(p);
281
 
            handle_error(err);
282
 
        }
283
 
        else
284
 
        {
285
 
            var err = document.createElement("div");
286
 
            var p = dom_make_text_elem("p", "Error: "
287
 
                    + "There was an unexpected server error retrieving "
288
 
                    + "the requested file or directory.");
289
 
            err.appendChild(p);
290
 
            p = dom_make_text_elem("p", "If the problem persists, please "
291
 
                    + "contact the system administrator.")
292
 
            err.appendChild(p);
293
 
            handle_error(err);
294
 
        }
295
 
        return;
296
 
    }
297
 
    /* Get "." out, it's special */
298
 
    current_file = file_listing["."];     /* Global */
299
 
    delete file_listing["."];
300
 
 
301
 
    if ('revision' in listing)
302
 
    {
303
 
        current_revision = listing.revision;
304
 
    }
305
 
 
306
209
    /* Check if this is a directory listing or file contents */
307
210
    var isdir = response.getResponseHeader("X-IVLE-Return") == "Dir";
308
 
    if (isdir)
 
211
    if (!editmode && isdir)
309
212
    {
310
 
        setup_for_dir_listing(listing, subjects);
 
213
        var listing = response.responseText;
 
214
        /* The listing SHOULD be valid JSON text. Parse it into an object. */
 
215
        try
 
216
        {
 
217
            listing = JSON.parse(listing);
 
218
        }
 
219
        catch (e)
 
220
        {
 
221
            handle_error("The server returned an invalid directory listing");
 
222
            return;
 
223
        }
311
224
        handle_dir_listing(path, listing);
312
225
    }
313
226
    else
314
227
    {
315
 
        /* Need to make a 2nd ajax call, this time get the actual file
316
 
         * contents */
317
 
        callback = function(response)
318
 
            {
319
 
                /* Read the response and set up the page accordingly */
320
 
                handle_contents_response(path, response);
321
 
            }
322
 
        /* Call the server and request the listing. */
323
 
        if (url_args)
324
 
            args = shallow_clone_object(url_args);
325
 
        else
326
 
            args = {};
327
 
        /* This time, get the contents of the file, not its metadata */
328
 
        args['return'] = "contents";
329
 
        ajax_call(callback, service_app, path, args, "GET");
330
 
    }
331
 
    update_actions(isdir);
332
 
}
333
 
 
334
 
function handle_contents_response(path, response)
335
 
{
336
 
    /* Treat this as an ordinary file. Get the file type. */
337
 
    var content_type = response.getResponseHeader("Content-Type");
338
 
    var handler_type = get_handler_type(content_type);
339
 
    would_be_handler_type = handler_type;
340
 
    /* handler_type should now be set to either
341
 
     * "text", "image", "audio" or "binary". */
342
 
    switch (handler_type)
343
 
    {
344
 
    case "text":
345
 
        handle_text(path, response.responseText,
346
 
            would_be_handler_type);
347
 
        break;
348
 
    case "image":
349
 
        /* TODO: Custom image handler */
350
 
        handle_binary(path, response.responseText);
351
 
        break;
352
 
    case "audio":
353
 
        /* TODO: Custom audio handler */
354
 
        handle_binary(path, response.responseText);
355
 
        break;
356
 
    case "binary":
357
 
        handle_binary(path);
358
 
        break;
359
 
    }
360
 
}
361
 
 
362
 
/* Called when a form upload comes back (from an iframe).
363
 
 * Refreshes the page.
364
 
 */
365
 
function upload_callback()
366
 
{
367
 
    /* This has a pretty nasty hack, which happens to work.
368
 
     * upload_callback is set as the "onload" callback for the iframe which
369
 
     * receives the response from the server for uploading a file.
370
 
     * This means it gets called twice. Once when initialising the iframe, and
371
 
     * a second time when the actual response comes back.
372
 
     * All we want to do is call navigate to refresh the page. But we CAN'T do
373
 
     * that on the first load or it will just go into an infinite cycle of
374
 
     * refreshing. We need to refresh the page ONLY on the second refresh.
375
 
     * upload_callback_count is reset to 0 just before the iframe is created.
376
 
     */
377
 
    upload_callback_count++;
378
 
    if (upload_callback_count >= 2)
379
 
    {
380
 
        document.getElementsByName('data')[0].value = '';
381
 
        refresh();
 
228
        /* Treat this as an ordinary file. Get the file type. */
 
229
        var content_type = response.getResponseHeader("Content-Type");
 
230
        var handler_type = get_handler_type(content_type);
 
231
        /* If we're in "edit mode", always treat this file as text */
 
232
        would_be_handler_type = handler_type;
 
233
        if (editmode) handler_type = "text";
 
234
        /* handler_type should now be set to either
 
235
         * "text", "image", "audio" or "binary". */
 
236
        switch (handler_type)
 
237
        {
 
238
        case "text":
 
239
            if (isdir)
 
240
            {
 
241
                handle_text(path_join(path, "untitled"), "",
 
242
                    would_be_handler_type);
 
243
            }
 
244
            else
 
245
            {
 
246
                handle_text(path, response.responseText,
 
247
                    would_be_handler_type);
 
248
            }
 
249
            break;
 
250
        case "image":
 
251
            /* TODO: Custom image handler */
 
252
            handle_binary(path, response.responseText);
 
253
            break;
 
254
        case "audio":
 
255
            /* TODO: Custom audio handler */
 
256
            handle_binary(path, response.responseText);
 
257
            break;
 
258
        case "binary":
 
259
            handle_binary(path);
 
260
            break;
 
261
        }
382
262
    }
383
263
}
384
264
 
388
268
 */
389
269
function clearpage()
390
270
{
 
271
    dom_removechildren(document.getElementById("path"));
391
272
    dom_removechildren(document.getElementById("filesbody"));
392
273
}
393
274
 
394
 
/* Checks if a file needs to be saved. If it does, the user will be asked
395
 
 * if they want to continue anyway. The caller must specify a warning
396
 
 * sentence which indicates the consequences of continuing.
397
 
 * Returns true if we should continue, and false if we should not.
398
 
 */
399
 
function maybe_save(warning)
400
 
{
401
 
    if (warning == null) warning = '';
402
 
    if (current_file.isdir) return true;
403
 
    if (document.getElementById("save_button").disabled) return true;
404
 
    return confirm("This file has unsaved changes. " + warning +
405
 
                   "\nAre you sure you wish to continue?");
406
 
}
407
 
 
408
275
/** Deletes all "dynamic" content on the page necessary to navigate from
409
276
 * one directory listing to another (does not clear as much as clearpage
410
277
 * does).
418
285
    dom_removechildren(document.getElementById("sidepanel"));
419
286
}
420
287
 
 
288
/** Sets the mode to either "file browser" or "text editor" mode.
 
289
 * This modifies the window icon, and selected tab.
 
290
 * \param editmode If True, editor mode. Else, file browser mode.
 
291
 */
 
292
function setmode(editmode)
 
293
{
 
294
    /* Find the DOM elements for the file browser and editor tabs */
 
295
    var tabs = document.getElementById("apptabs");
 
296
    var tab_files = null;
 
297
    var tab_edit = null;
 
298
    var a;
 
299
    var href;
 
300
    for (var i=0; i<tabs.childNodes.length; i++)
 
301
    {
 
302
        /* Find the href of the link within */
 
303
        if (!tabs.childNodes[i].getElementsByTagName) continue;
 
304
        a = tabs.childNodes[i].getElementsByTagName("a");
 
305
        if (a.length == 0) continue;
 
306
        href = a[0].getAttribute("href");
 
307
        if (href == null) continue;
 
308
        if (endswith(href, this_app))
 
309
            tab_files = tabs.childNodes[i];
 
310
        else if (endswith(href, edit_app))
 
311
            tab_edit = tabs.childNodes[i];
 
312
    }
 
313
 
 
314
    if (editmode)
 
315
    {
 
316
        tab_files.removeAttribute("class");
 
317
        tab_edit.setAttribute("class", "thisapp");
 
318
    }
 
319
    else
 
320
    {
 
321
        tab_edit.removeAttribute("class");
 
322
        tab_files.setAttribute("class", "thisapp");
 
323
    }
 
324
}
 
325
 
421
326
/*** HANDLERS for different types of responses (such as dir listing, file,
422
327
 * etc). */
423
328
 
424
 
/* handle_error.
425
 
 * message may either be a string, or a DOM node, which will be placed inside
426
 
 * a div.
427
 
 */
428
329
function handle_error(message)
429
330
{
 
331
    setmode(false);
430
332
    var files = document.getElementById("filesbody");
431
 
    var txt_elem;
432
 
    if (typeof(message) == "string")
433
 
    {
434
 
        txt_elem = dom_make_text_elem("div", "Error: "
435
 
                   + message.toString() + ".")
436
 
    }
437
 
    else
438
 
    {
439
 
        /* Assume message is a DOM node */
440
 
        txt_elem = document.createElement("div");
441
 
        txt_elem.appendChild(message);
442
 
    }
 
333
    var txt_elem = dom_make_text_elem("div", "Error: "
 
334
        + message.toString() + ".")
443
335
    txt_elem.setAttribute("class", "padding error");
444
336
    files.appendChild(txt_elem);
445
337
}
446
338
 
447
 
/** Given a path, filename and optional revision, returns a URL to open that
448
 
 *  revision of that file.
 
339
/** Presents a path list (address bar inside the page) for clicking.
449
340
 */
450
 
function build_revision_url(path, filename, revision)
 
341
function presentpath(path)
451
342
{
452
 
    bits = {'path': app_path(this_app, path, filename)};
453
 
    if (current_revision)
 
343
    var dom_path = document.getElementById("path");
 
344
    var href_path = make_path(this_app);
 
345
    var nav_path = "";
 
346
    var dir;
 
347
 
 
348
    /* Also set the document title */
 
349
    document.title = path_basename(path) + " - IVLE";
 
350
    /* Create all of the paths */
 
351
    var pathlist = path.split("/");
 
352
    for (var i=0; i<pathlist.length; i++)
454
353
    {
455
 
        bits['query_string'] = 'r=' + revision;
 
354
        dir = pathlist[i];
 
355
        if (dir == "") continue;
 
356
        /* Make an 'a' element */
 
357
        href_path = path_join(href_path, dir);
 
358
        nav_path = path_join(nav_path, dir);
 
359
        var link = dom_make_link_elem("a", dir, "Navigate to " + nav_path,
 
360
                href_path/*, "navigate(" + repr(href_path) + ")"*/);
 
361
        dom_path.appendChild(link);
 
362
        dom_path.appendChild(document.createTextNode("/"));
456
363
    }
457
 
    return build_url(bits);
 
364
    dom_path.removeChild(dom_path.lastChild);
458
365
}
459
366
 
460
367
/** Given a mime type, returns the path to the icon.
506
413
 */
507
414
function handle_binary(path)
508
415
{
 
416
    setmode(false);
509
417
    var files = document.getElementById("filesbody");
510
418
    var div = document.createElement("div");
511
419
    files.appendChild(div);
520
428
    div.appendChild(par2);
521
429
}
522
430
 
523
 
/* Enable or disable actions1 moreactions actions. Takes either a single
524
 
 * name, or an array of them.*/
525
 
function set_action_state(names, which, allow_on_revision)
526
 
{
527
 
    if (!(names instanceof Array)) names = Array(names);
528
 
 
529
 
    for (var i=0; i < names.length; i++)
530
 
    {
531
 
        element = document.getElementById('act_' + names[i]);
532
 
        if (which &&
533
 
            !(current_file.svnstatus == 'revision' && !allow_on_revision))
534
 
        {
535
 
            /* Enabling */
536
 
            element.setAttribute("class", "choice");
537
 
            element.removeAttribute("disabled");
538
 
        }
539
 
        else
540
 
        {
541
 
            /* Disabling */
542
 
            element.setAttribute("class", "disabled");
543
 
            element.setAttribute("disabled", "disabled");
544
 
        }
545
 
    }
546
 
}
547
 
 
548
 
function update_actions()
549
 
{
550
 
    var file;
551
 
    var numsel = selected_files.length;
552
 
    if (numsel <= 1)
553
 
    {
554
 
        if (numsel == 0)
555
 
        {
556
 
            /* Display information about the current directory instead */
557
 
            filename = path_basename(current_path);
558
 
            file = current_file;
559
 
        }
560
 
        else if (numsel == 1)
561
 
        {
562
 
            filename = selected_files[0];
563
 
            file = file_listing[filename];
564
 
        }
565
 
 
566
 
        /* Update each action node in the topbar.
567
 
         * This includes enabling/disabling actions as appropriate, and
568
 
         * setting href/onclick attributes. */
569
 
    }
570
 
 
571
 
    /* Open */
572
 
    /* Available if exactly one file is selected */
573
 
    var open = document.getElementById("act_open");
574
 
    if (numsel == 1)
575
 
    {
576
 
        open.setAttribute("class", "choice");
577
 
        if (file.isdir)
578
 
            open.setAttribute("title",
579
 
                "Navigate to this directory in the file browser");
580
 
        else
581
 
            open.setAttribute("title",
582
 
                "Edit or view this file");
583
 
        open.setAttribute("href", build_revision_url(current_path, filename,
584
 
                                                     current_revision));
585
 
    }
586
 
    else
587
 
    {
588
 
        open.setAttribute("class", "disabled");
589
 
        open.removeAttribute("title");
590
 
        open.removeAttribute("href");
591
 
    }
592
 
 
593
 
    /* Serve */
594
 
    /* Available if zero or one files are selected,
595
 
     * and only if this is a file, not a directory */
596
 
    var serve = document.getElementById("act_serve");
597
 
    if (numsel <= 1 && !file.isdir && current_file.svnstatus != 'revision')
598
 
    {
599
 
        serve.setAttribute("class", "choice");
600
 
        serve.setAttribute("onclick",
601
 
              "return maybe_save('The last saved version will be served.')");
602
 
        if (numsel == 0)
603
 
            serve.setAttribute("href",
604
 
                app_path(serve_app, current_path));
605
 
        else
606
 
            serve.setAttribute("href",
607
 
                app_path(serve_app, current_path, filename));
608
 
    }
609
 
    else
610
 
    {
611
 
        serve.setAttribute("class", "disabled");
612
 
        serve.removeAttribute("href");
613
 
        serve.removeAttribute("onclick");
614
 
    }
615
 
 
616
 
    /* Run */
617
 
    /* Available if exactly one file is selected,
618
 
     * and it is a Python file.
619
 
     */
620
 
    var run = document.getElementById("act_run");
621
 
     
622
 
    if (!file.isdir && file.type == "text/x-python" && numsel <= 1
623
 
        && current_file.svnstatus != 'revision')
624
 
    {
625
 
        if (numsel == 0)
626
 
        {
627
 
            // In the edit window
628
 
            var localpath = path_join('/home', current_path);
629
 
        }
630
 
        else
631
 
        {
632
 
            // In the browser window
633
 
            var localpath = path_join('/home', current_path, filename);
634
 
        }
635
 
        run.setAttribute("class", "choice");
636
 
        run.setAttribute("onclick", "runfile('" + localpath + "')");
637
 
    }
638
 
    else
639
 
    {
640
 
        run.setAttribute("class", "disabled");
641
 
        run.removeAttribute("onclick");
642
 
    }
643
 
 
644
 
    /* Download */
645
 
    /* Always available for current files.
646
 
     * If 0 files selected, download the current file or directory as a ZIP.
647
 
     * If 1 directory selected, download it as a ZIP.
648
 
     * If 1 non-directory selected, download it.
649
 
     * If >1 files selected, download them all as a ZIP.
650
 
     */
651
 
    var download = document.getElementById("act_download");
652
 
    if (current_file.svnstatus == 'revision')
653
 
    {
654
 
        download.setAttribute("class", "disabled");
655
 
        download.removeAttribute("onclick");
656
 
    }
657
 
    else if (numsel <= 1)
658
 
    {
659
 
        download.setAttribute("class", "choice")
660
 
        if (numsel == 0)
661
 
        {
662
 
            download.setAttribute("href",
663
 
                app_path(download_app, current_path));
664
 
            if (file.isdir)
665
 
                download.setAttribute("title",
666
 
                    "Download the current directory as a ZIP file");
667
 
            else
668
 
                download.setAttribute("title",
669
 
                    "Download the current file");
670
 
        }
671
 
        else
672
 
        {
673
 
            download.setAttribute("href",
674
 
                app_path(download_app, current_path, filename));
675
 
            if (file.isdir)
676
 
                download.setAttribute("title",
677
 
                    "Download the selected directory as a ZIP file");
678
 
            else
679
 
                download.setAttribute("title",
680
 
                    "Download the selected file");
681
 
        }
682
 
    }
683
 
    else
684
 
    {
685
 
        /* Make a query string with all the files to download */
686
 
        var dlpath = urlencode_path(app_path(download_app, current_path)) + "?";
687
 
        for (var i=0; i<numsel; i++)
688
 
            dlpath += "path=" + encodeURIComponent(selected_files[i]) + "&";
689
 
        dlpath = dlpath.substr(0, dlpath.length-1);
690
 
        download.setAttribute("class", "choice")
691
 
        download.setAttribute("href", dlpath);
692
 
        download.setAttribute("title",
693
 
            "Download the selected files as a ZIP file");
694
 
    }
695
 
 
696
 
    /* Refresh - No changes required */
697
 
 
698
 
    /* Publish and Submit */
699
 
    /* If this directory is under subversion and selected/unselected file is a
700
 
     * directory. */
701
 
    var publish = document.getElementById("act_publish");
702
 
    var submit = document.getElementById("act_submit");
703
 
    var pubcond = numsel <= 1 && file.isdir;
704
 
    if (pubcond)
705
 
    {
706
 
        /* TODO: Work out of file is svn'd */
707
 
        /* If this dir is already published, call it "Unpublish" */
708
 
        if (file.published)
709
 
        {
710
 
            publish.setAttribute("value", "unpublish");
711
 
            publish.setAttribute("title" ,"Make it so this directory "
712
 
                + "can not be seen by anyone on the web");
713
 
            publish.textContent = "Unpublish";
714
 
        } else {
715
 
            publish.setAttribute("value", "publish");
716
 
            publish.setAttribute("title","Make it so this directory "
717
 
                + "can be seen by anyone on the web");
718
 
            publish.textContent = "Publish";
719
 
        }
720
 
    }
721
 
    set_action_state(["publish", "submit"], pubcond);
722
 
 
723
 
    /* Share */
724
 
    /* If exactly 1 non-directory file is selected, and its parent
725
 
     * directory is published.
726
 
     */
727
 
    set_action_state("share", numsel == 1 && !file.isdir &&
728
 
                     current_file.published);
729
 
 
730
 
    /* Rename */
731
 
    /* If exactly 1 file is selected */
732
 
    set_action_state("rename", numsel == 1);
733
 
 
734
 
    /* Delete, cut, copy */
735
 
    /* If >= 1 file is selected */
736
 
    set_action_state(["delete", "cut", "copy"], numsel >= 1);
737
 
 
738
 
    /* Paste, new file, new directory, upload */
739
 
    /* Disable if the current file is not a directory */
740
 
    set_action_state(["paste", "newfile", "mkdir", "upload"], current_file.isdir);
741
 
 
742
 
    /* Subversion actions */
743
 
    /* These are only useful if we are in a versioned directory and have some
744
 
     * files selected. */
745
 
    set_action_state(["svnadd", "svnremove", "svnrevert", "svncommit"], numsel >= 1 && current_file.svnstatus);
746
 
 
747
 
    /* Diff, log and update only support one path at the moment, so we must
748
 
     * have 0 or 1 versioned files selected. If 0, the directory must be
749
 
     * versioned. */
750
 
    single_versioned_path = (
751
 
         (
752
 
          (numsel == 1 && (svnst = file_listing[selected_files[0]].svnstatus)) ||
753
 
          (numsel == 0 && (svnst = current_file.svnstatus))
754
 
         ) && svnst != "unversioned");
755
 
    set_action_state(["svndiff", "svnupdate"], single_versioned_path);
756
 
 
757
 
    /* We can resolve if we have a file selected and it is conflicted. */
758
 
    set_action_state("svnresolved", single_versioned_path && numsel == 1 && svnst == "conflicted");
759
 
 
760
 
    /* Log should be available for revisions as well. */
761
 
    set_action_state("svnlog", single_versioned_path, true);
762
 
 
763
 
    /* There is currently nothing on the More Actions menu of use
764
 
     * when the current file is not a directory. Hence, just remove
765
 
     * it entirely.
766
 
     * (This makes some of the above decisions somewhat redundant).
767
 
     * We also take this opportunity to show the appropriate actions2
768
 
     * bar for this path. It should either be a save or upload widget.
769
 
     */
770
 
    if (current_file.isdir)
771
 
    {
772
 
        var actions2_directory = document.getElementById("actions2_directory");
773
 
        actions2_directory.setAttribute("style", "display: inline;");
774
 
        var moreactions = document.getElementById("moreactions_area");
775
 
        moreactions.setAttribute("style", "display: inline;");
776
 
    }
777
 
    else
778
 
    {
779
 
        var actions2_file = document.getElementById("actions2_file");
780
 
        actions2_file.setAttribute("style", "display: inline;");
781
 
    }
782
 
 
783
 
    return;
784
 
}
785
 
 
786
 
/** Event handler for when an item of the "More actions..." dropdown box is
787
 
 * selected. Performs the selected action. */
788
 
function handle_moreactions()
789
 
{
790
 
    var moreactions = document.getElementById("moreactions");
791
 
    if (moreactions.value == "top")
792
 
        return;
793
 
    var selectedaction = moreactions.value;
794
 
    /* Reset to "More actions..." */
795
 
    moreactions.selectedIndex = 0;
796
 
 
797
 
    /* If 0 files selected, filename is the name of the current dir.
798
 
     * If 1 file selected, filename is that file.
799
 
     */
800
 
    if (selected_files.length == 0)
801
 
        filename = path_basename(current_path);
802
 
    else if (selected_files.length == 1)
803
 
        filename = selected_files[0];
804
 
    else
805
 
        filename = null;
806
 
 
807
 
    /* Now handle the selected action */
808
 
    switch(selectedaction)
809
 
    {
810
 
    case "publish":
811
 
        action_publish(selected_files);
812
 
        break;
813
 
    case "unpublish":
814
 
        action_unpublish(selected_files);
815
 
        break;
816
 
    case "share":
817
 
        //alert("Not yet implemented: Sharing files");
818
 
        window.open(public_app_path(serve_app, current_path, filename), 'share')
819
 
        break;
820
 
    case "submit":
821
 
        // TODO
822
 
        alert("Not yet implemented: Submit");
823
 
        break;
824
 
    case "rename":
825
 
        action_rename(filename);
826
 
        break;
827
 
    case "delete":
828
 
        action_delete(selected_files);
829
 
        break;
830
 
    case "copy":
831
 
        action_copy(selected_files);
832
 
        break;
833
 
    case "cut":
834
 
        action_cut(selected_files);
835
 
        break;
836
 
    case "paste":
837
 
        action_paste();
838
 
        break;
839
 
    case "newfile":
840
 
        action_newfile();
841
 
        break;
842
 
    case "mkdir":
843
 
        action_mkdir();
844
 
        break;
845
 
    case "upload":
846
 
        show_uploadpanel(true);
847
 
        break;
848
 
    case "svnadd":
849
 
        action_add(selected_files);
850
 
        break;
851
 
    case "svnremove":
852
 
        action_remove(selected_files);
853
 
        break;
854
 
    case "svnrevert":
855
 
        action_revert(selected_files);
856
 
        break;
857
 
    case "svndiff":
858
 
        window.location = path_join(app_path('diff'), current_path, selected_files[0] || '');
859
 
        break;
860
 
    case "svnupdate":
861
 
        action_update(selected_files);
862
 
        break;
863
 
    case "svnresolved":
864
 
        action_resolved(selected_files);
865
 
        break;
866
 
    case "svncommit":
867
 
        action_commit(selected_files);
868
 
        break;
869
 
    case "svnlog":
870
 
        window.location = path_join(app_path('svnlog'), current_path, selected_files[0] || '');
871
 
        break;
872
 
    }
873
 
}
874
 
 
875
 
/** User clicks "Run" button.
876
 
 * Do an Ajax call and print the test output.
877
 
 */
878
 
function runfile(localpath)
879
 
{
880
 
    if (!maybe_save('The last saved version will be run.')) return false;
881
 
 
882
 
    /* Dump the entire file to the console */
883
 
    var callback = function()
884
 
    {
885
 
        console_enter_line("execfile('" + localpath + "')", "block");
886
 
    }
887
 
    start_server(callback)
888
 
    return;
889
 
}
890
 
 
891
431
/** Called when the page loads initially.
892
432
 */
893
 
function browser_init()
 
433
window.onload = function()
894
434
{
895
435
    /* Navigate (internally) to the path in the URL bar.
896
436
     * This causes the page to be populated with whatever is at that address,
899
439
    var path = parse_url(window.location.href).path;
900
440
    /* Strip out root_dir + "/files" from the front of the path */
901
441
    var strip = make_path(this_app);
 
442
    var editmode = false;
902
443
    if (path.substr(0, strip.length) == strip)
903
444
        path = path.substr(strip.length+1);
904
445
    else
908
449
        if (path.substr(0, strip.length) == strip)
909
450
        {
910
451
            path = path.substr(strip.length+1);
 
452
            editmode = true;
911
453
        }
912
454
    }
913
455
 
918
460
        path = username;
919
461
    }
920
462
 
921
 
    navigate(path);
 
463
    navigate(path, editmode);
922
464
}