~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-24 22:09:16 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:553
Added new app: Settings (UI for userservice).
    (Added app dir, media dir with empty JS file, icon).
apps/__init__.py: Added error message.
dispatch/__init__.py: Added error message.
dispatch/html.py: Added link to Settings at the top
    (a special app).
    Fixed HTML icons (correct size link).
    Catch keyerror on icon which would otherwise be thrown all
    the way up on certain errors.

Show diffs side-by-side

added added

removed removed

Lines of Context:
65
65
    "added": "added.png",
66
66
    "missing": "missing.png",
67
67
    "deleted": "deleted.png",
68
 
    "modified": "modified.png",
69
 
    "conflicted": "conflicted.png",
70
 
    "revision": "revision.png"
 
68
    "modified": "modified.png"
71
69
};
72
70
 
73
71
/* Mapping SVN status to "nice" strings */
80
78
    "replaced": "Permanent file (replaced)",
81
79
    "modified": "Permanent file (modified)",
82
80
    "merged": "Permanent file (merged)",
83
 
    "conflicted": "Permanent file (conflicted)",
84
 
    "revision": "Past Permanent file (revision)"
 
81
    "conflicted": "Permanent file (conflicted)"
85
82
};
86
83
 
87
84
default_svn_icon = null;
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
 
    /* This will always return a listing, whether it is a dir or a file.
245
 
     */
246
 
    var listing = response.responseText;
247
 
    /* The listing SHOULD be valid JSON text. Parse it into an object. */
248
 
    try
249
 
    {
250
 
        listing = JSON.parse(listing);
251
 
        file_listing = listing.listing;     /* Global */
252
 
    }
253
 
    catch (e)
254
 
    {
255
 
        if (is_action)
256
 
        {
257
 
            var err = document.createElement("div");
258
 
            var p = dom_make_text_elem("p", "Error: "
259
 
                    + "There was an unexpected server error processing "
260
 
                    + "the selected command.");
261
 
            err.appendChild(p);
262
 
            p = dom_make_text_elem("p", "If the problem persists, please "
263
 
                    + "contact the system administrator.")
264
 
            err.appendChild(p);
265
 
            p = document.createElement("p");
266
 
            var refresh = document.createElement("input");
267
 
            refresh.setAttribute("type", "button");
268
 
            refresh.setAttribute("value", "Back to file view");
269
 
            refresh.setAttribute("onclick", "refresh()");
270
 
            p.appendChild(refresh);
271
 
            err.appendChild(p);
272
 
            handle_error(err);
273
 
        }
274
 
        else
275
 
        {
276
 
            var err = document.createElement("div");
277
 
            var p = dom_make_text_elem("p", "Error: "
278
 
                    + "There was an unexpected server error retrieving "
279
 
                    + "the requested file or directory.");
280
 
            err.appendChild(p);
281
 
            p = dom_make_text_elem("p", "If the problem persists, please "
282
 
                    + "contact the system administrator.")
283
 
            err.appendChild(p);
284
 
            handle_error(err);
285
 
        }
286
 
        return;
287
 
    }
288
 
    /* Get "." out, it's special */
289
 
    current_file = file_listing["."];     /* Global */
290
 
    delete file_listing["."];
291
 
 
292
 
    if ('revision' in listing)
293
 
    {
294
 
        current_revision = listing.revision;
295
 
    }
296
 
 
297
209
    /* Check if this is a directory listing or file contents */
298
210
    var isdir = response.getResponseHeader("X-IVLE-Return") == "Dir";
299
 
    if (isdir)
 
211
    if (!editmode && isdir)
300
212
    {
 
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
        }
301
224
        handle_dir_listing(path, listing);
302
225
    }
303
226
    else
304
227
    {
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);
315
 
        else
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");
320
 
    }
321
 
    update_actions(isdir);
322
 
}
323
 
 
324
 
function handle_contents_response(path, response)
325
 
{
326
 
    /* Treat this as an ordinary file. Get the file type. */
327
 
    var content_type = response.getResponseHeader("Content-Type");
328
 
    var handler_type = get_handler_type(content_type);
329
 
    would_be_handler_type = handler_type;
330
 
    /* handler_type should now be set to either
331
 
     * "text", "image", "audio" or "binary". */
332
 
    switch (handler_type)
333
 
    {
334
 
    case "text":
335
 
        handle_text(path, response.responseText,
336
 
            would_be_handler_type);
337
 
        break;
338
 
    case "image":
339
 
        /* TODO: Custom image handler */
340
 
        handle_binary(path, response.responseText);
341
 
        break;
342
 
    case "audio":
343
 
        /* TODO: Custom audio handler */
344
 
        handle_binary(path, response.responseText);
345
 
        break;
346
 
    case "binary":
347
 
        handle_binary(path);
348
 
        break;
349
 
    }
350
 
}
351
 
 
352
 
/* Called when a form upload comes back (from an iframe).
353
 
 * Refreshes the page.
354
 
 */
355
 
function upload_callback()
356
 
{
357
 
    /* This has a pretty nasty hack, which happens to work.
358
 
     * upload_callback is set as the "onload" callback for the iframe which
359
 
     * receives the response from the server for uploading a file.
360
 
     * This means it gets called twice. Once when initialising the iframe, and
361
 
     * a second time when the actual response comes back.
362
 
     * All we want to do is call navigate to refresh the page. But we CAN'T do
363
 
     * that on the first load or it will just go into an infinite cycle of
364
 
     * refreshing. We need to refresh the page ONLY on the second refresh.
365
 
     * upload_callback_count is reset to 0 just before the iframe is created.
366
 
     */
367
 
    upload_callback_count++;
368
 
    if (upload_callback_count >= 2)
369
 
    {
370
 
        document.getElementsByName('data')[0].value = '';
371
 
        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
        }
372
262
    }
373
263
}
374
264
 
378
268
 */
379
269
function clearpage()
380
270
{
 
271
    dom_removechildren(document.getElementById("path"));
381
272
    dom_removechildren(document.getElementById("filesbody"));
382
273
}
383
274
 
384
 
/* Checks if a file needs to be saved. If it does, the user will be asked
385
 
 * if they want to continue anyway. The caller must specify a warning
386
 
 * sentence which indicates the consequences of continuing.
387
 
 * Returns true if we should continue, and false if we should not.
388
 
 */
389
 
function maybe_save(warning)
390
 
{
391
 
    if (warning == null) warning = '';
392
 
    if (current_file.isdir) return true;
393
 
    if (document.getElementById("save_button").disabled) return true;
394
 
    return confirm("This file has unsaved changes. " + warning +
395
 
                   "\nAre you sure you wish to continue?");
396
 
}
397
 
 
398
275
/** Deletes all "dynamic" content on the page necessary to navigate from
399
276
 * one directory listing to another (does not clear as much as clearpage
400
277
 * does).
408
285
    dom_removechildren(document.getElementById("sidepanel"));
409
286
}
410
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
 
411
326
/*** HANDLERS for different types of responses (such as dir listing, file,
412
327
 * etc). */
413
328
 
414
 
/* handle_error.
415
 
 * message may either be a string, or a DOM node, which will be placed inside
416
 
 * a div.
417
 
 */
418
329
function handle_error(message)
419
330
{
 
331
    setmode(false);
420
332
    var files = document.getElementById("filesbody");
421
 
    var txt_elem;
422
 
    if (typeof(message) == "string")
423
 
    {
424
 
        txt_elem = dom_make_text_elem("div", "Error: "
425
 
                   + message.toString() + ".")
426
 
    }
427
 
    else
428
 
    {
429
 
        /* Assume message is a DOM node */
430
 
        txt_elem = document.createElement("div");
431
 
        txt_elem.appendChild(message);
432
 
    }
 
333
    var txt_elem = dom_make_text_elem("div", "Error: "
 
334
        + message.toString() + ".")
433
335
    txt_elem.setAttribute("class", "padding error");
434
336
    files.appendChild(txt_elem);
435
337
}
436
338
 
437
 
/** Given a path, filename and optional revision, returns a URL to open that
438
 
 *  revision of that file.
 
339
/** Presents a path list (address bar inside the page) for clicking.
439
340
 */
440
 
function build_revision_url(path, filename, revision)
 
341
function presentpath(path)
441
342
{
442
 
    bits = {'path': app_path(this_app, path, filename)};
443
 
    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++)
444
353
    {
445
 
        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("/"));
446
363
    }
447
 
    return build_url(bits);
 
364
    dom_path.removeChild(dom_path.lastChild);
448
365
}
449
366
 
450
367
/** Given a mime type, returns the path to the icon.
496
413
 */
497
414
function handle_binary(path)
498
415
{
 
416
    setmode(false);
499
417
    var files = document.getElementById("filesbody");
500
418
    var div = document.createElement("div");
501
419
    files.appendChild(div);
510
428
    div.appendChild(par2);
511
429
}
512
430
 
513
 
/* Enable or disable actions1 moreactions actions. Takes either a single
514
 
 * name, or an array of them.*/
515
 
function set_action_state(names, which, allow_on_revision)
516
 
{
517
 
    if (!(names instanceof Array)) names = Array(names);
518
 
 
519
 
    for (var i=0; i < names.length; i++)
520
 
    {
521
 
        element = document.getElementById('act_' + names[i]);
522
 
        if (which &&
523
 
            !(current_file.svnstatus == 'revision' && !allow_on_revision))
524
 
        {
525
 
            /* Enabling */
526
 
            element.setAttribute("class", "choice");
527
 
            element.removeAttribute("disabled");
528
 
        }
529
 
        else
530
 
        {
531
 
            /* Disabling */
532
 
            element.setAttribute("class", "disabled");
533
 
            element.setAttribute("disabled", "disabled");
534
 
        }
535
 
    }
536
 
}
537
 
 
538
 
function update_actions()
539
 
{
540
 
    var file;
541
 
    var numsel = selected_files.length;
542
 
    if (numsel <= 1)
543
 
    {
544
 
        if (numsel == 0)
545
 
        {
546
 
            /* Display information about the current directory instead */
547
 
            filename = path_basename(current_path);
548
 
            file = current_file;
549
 
        }
550
 
        else if (numsel == 1)
551
 
        {
552
 
            filename = selected_files[0];
553
 
            file = file_listing[filename];
554
 
        }
555
 
 
556
 
        /* Update each action node in the topbar.
557
 
         * This includes enabling/disabling actions as appropriate, and
558
 
         * setting href/onclick attributes. */
559
 
    }
560
 
 
561
 
    /* Open */
562
 
    /* Available if exactly one file is selected */
563
 
    var open = document.getElementById("act_open");
564
 
    if (numsel == 1)
565
 
    {
566
 
        open.setAttribute("class", "choice");
567
 
        if (file.isdir)
568
 
            open.setAttribute("title",
569
 
                "Navigate to this directory in the file browser");
570
 
        else
571
 
            open.setAttribute("title",
572
 
                "Edit or view this file");
573
 
        open.setAttribute("href", build_revision_url(current_path, filename,
574
 
                                                     current_revision));
575
 
    }
576
 
    else
577
 
    {
578
 
        open.setAttribute("class", "disabled");
579
 
        open.removeAttribute("title");
580
 
        open.removeAttribute("href");
581
 
    }
582
 
 
583
 
    /* Serve */
584
 
    /* Available if zero or one files are selected,
585
 
     * and only if this is a file, not a directory */
586
 
    var serve = document.getElementById("act_serve");
587
 
    if (numsel <= 1 && !file.isdir && current_file.svnstatus != 'revision')
588
 
    {
589
 
        serve.setAttribute("class", "choice");
590
 
        serve.setAttribute("onclick",
591
 
              "return maybe_save('The last saved version will be served.')");
592
 
        if (numsel == 0)
593
 
            serve.setAttribute("href",
594
 
                app_path(serve_app, current_path));
595
 
        else
596
 
            serve.setAttribute("href",
597
 
                app_path(serve_app, current_path, filename));
598
 
    }
599
 
    else
600
 
    {
601
 
        serve.setAttribute("class", "disabled");
602
 
        serve.removeAttribute("href");
603
 
        serve.removeAttribute("onclick");
604
 
    }
605
 
 
606
 
    /* Run */
607
 
    /* Available if exactly one file is selected,
608
 
     * and it is a Python file.
609
 
     */
610
 
    var run = document.getElementById("act_run");
611
 
     
612
 
    if (!file.isdir && file.type == "text/x-python" && numsel <= 1
613
 
        && current_file.svnstatus != 'revision')
614
 
    {
615
 
        if (numsel == 0)
616
 
        {
617
 
            // In the edit window
618
 
            var localpath = path_join('/home', current_path);
619
 
        }
620
 
        else
621
 
        {
622
 
            // In the browser window
623
 
            var localpath = path_join('/home', current_path, filename);
624
 
        }
625
 
        run.setAttribute("class", "choice");
626
 
        run.setAttribute("onclick", "runfile('" + localpath + "')");
627
 
    }
628
 
    else
629
 
    {
630
 
        run.setAttribute("class", "disabled");
631
 
        run.removeAttribute("onclick");
632
 
    }
633
 
 
634
 
    /* Download */
635
 
    /* Always available for current files.
636
 
     * If 0 files selected, download the current file or directory as a ZIP.
637
 
     * If 1 directory selected, download it as a ZIP.
638
 
     * If 1 non-directory selected, download it.
639
 
     * If >1 files selected, download them all as a ZIP.
640
 
     */
641
 
    var download = document.getElementById("act_download");
642
 
    if (current_file.svnstatus == 'revision')
643
 
    {
644
 
        download.setAttribute("class", "disabled");
645
 
        download.removeAttribute("onclick");
646
 
    }
647
 
    else if (numsel <= 1)
648
 
    {
649
 
        download.setAttribute("class", "choice")
650
 
        if (numsel == 0)
651
 
        {
652
 
            download.setAttribute("href",
653
 
                app_path(download_app, current_path));
654
 
            if (file.isdir)
655
 
                download.setAttribute("title",
656
 
                    "Download the current directory as a ZIP file");
657
 
            else
658
 
                download.setAttribute("title",
659
 
                    "Download the current file");
660
 
        }
661
 
        else
662
 
        {
663
 
            download.setAttribute("href",
664
 
                app_path(download_app, current_path, filename));
665
 
            if (file.isdir)
666
 
                download.setAttribute("title",
667
 
                    "Download the selected directory as a ZIP file");
668
 
            else
669
 
                download.setAttribute("title",
670
 
                    "Download the selected file");
671
 
        }
672
 
    }
673
 
    else
674
 
    {
675
 
        /* Make a query string with all the files to download */
676
 
        var dlpath = urlencode_path(app_path(download_app, current_path)) + "?";
677
 
        for (var i=0; i<numsel; i++)
678
 
            dlpath += "path=" + encodeURIComponent(selected_files[i]) + "&";
679
 
        dlpath = dlpath.substr(0, dlpath.length-1);
680
 
        download.setAttribute("class", "choice")
681
 
        download.setAttribute("href", dlpath);
682
 
        download.setAttribute("title",
683
 
            "Download the selected files as a ZIP file");
684
 
    }
685
 
 
686
 
    /* Refresh - No changes required */
687
 
 
688
 
    /* Publish and Submit */
689
 
    /* If this directory is under subversion and selected/unselected file is a
690
 
     * directory. */
691
 
    var publish = document.getElementById("act_publish");
692
 
    var submit = document.getElementById("act_submit");
693
 
    var pubcond = numsel <= 1 && file.isdir;
694
 
    if (pubcond)
695
 
    {
696
 
        /* TODO: Work out of file is svn'd */
697
 
        /* If this dir is already published, call it "Unpublish" */
698
 
        if (file.published)
699
 
        {
700
 
            publish.setAttribute("value", "unpublish");
701
 
            publish.setAttribute("title" ,"Make it so this directory "
702
 
                + "can not be seen by anyone on the web");
703
 
            publish.textContent = "Unpublish";
704
 
        } else {
705
 
            publish.setAttribute("value", "publish");
706
 
            publish.setAttribute("title","Make it so this directory "
707
 
                + "can be seen by anyone on the web");
708
 
            publish.textContent = "Publish";
709
 
        }
710
 
    }
711
 
    set_action_state(["publish", "submit"], pubcond);
712
 
 
713
 
    /* Share */
714
 
    /* If exactly 1 non-directory file is selected, and its parent
715
 
     * directory is published.
716
 
     */
717
 
    set_action_state("share", numsel == 1 && !file.isdir &&
718
 
                     current_file.published);
719
 
 
720
 
    /* Rename */
721
 
    /* If exactly 1 file is selected */
722
 
    set_action_state("rename", numsel == 1);
723
 
 
724
 
    /* Delete, cut, copy */
725
 
    /* If >= 1 file is selected */
726
 
    set_action_state(["delete", "cut", "copy"], numsel >= 1);
727
 
 
728
 
    /* Paste, new file, new directory, upload */
729
 
    /* Disable if the current file is not a directory */
730
 
    set_action_state(["paste", "newfile", "mkdir", "upload"], current_file.isdir);
731
 
 
732
 
    /* Subversion actions */
733
 
    /* These are only useful if we are in a versioned directory and have some
734
 
     * files selected. */
735
 
    set_action_state(["svnadd", "svnremove", "svnrevert", "svncommit"], numsel >= 1 && current_file.svnstatus);
736
 
 
737
 
    /* Diff, log and update only support one path at the moment, so we must
738
 
     * have 0 or 1 versioned files selected. If 0, the directory must be
739
 
     * versioned. */
740
 
    single_versioned_path = (
741
 
         (
742
 
          (numsel == 1 && (svnst = file_listing[selected_files[0]].svnstatus)) ||
743
 
          (numsel == 0 && (svnst = current_file.svnstatus))
744
 
         ) && svnst != "unversioned");
745
 
    set_action_state(["svndiff", "svnupdate"], single_versioned_path);
746
 
 
747
 
    /* We can resolve if we have a file selected and it is conflicted. */
748
 
    set_action_state("svnresolved", single_versioned_path && numsel == 1 && svnst == "conflicted");
749
 
 
750
 
    /* Log should be available for revisions as well. */
751
 
    set_action_state("svnlog", single_versioned_path, true);
752
 
 
753
 
    /* current_path == username: We are at the top level */
754
 
    set_action_state("svncheckout", current_path == username);
755
 
 
756
 
    /* There is currently nothing on the More Actions menu of use
757
 
     * when the current file is not a directory. Hence, just remove
758
 
     * it entirely.
759
 
     * (This makes some of the above decisions somewhat redundant).
760
 
     * We also take this opportunity to show the appropriate actions2
761
 
     * bar for this path. It should either be a save or upload widget.
762
 
     */
763
 
    if (current_file.isdir)
764
 
    {
765
 
        var actions2_directory = document.getElementById("actions2_directory");
766
 
        actions2_directory.setAttribute("style", "display: inline;");
767
 
        var moreactions = document.getElementById("moreactions_area");
768
 
        moreactions.setAttribute("style", "display: inline;");
769
 
    }
770
 
    else
771
 
    {
772
 
        var actions2_file = document.getElementById("actions2_file");
773
 
        actions2_file.setAttribute("style", "display: inline;");
774
 
    }
775
 
 
776
 
    return;
777
 
}
778
 
 
779
 
/** Event handler for when an item of the "More actions..." dropdown box is
780
 
 * selected. Performs the selected action. */
781
 
function handle_moreactions()
782
 
{
783
 
    var moreactions = document.getElementById("moreactions");
784
 
    if (moreactions.value == "top")
785
 
        return;
786
 
    var selectedaction = moreactions.value;
787
 
    /* Reset to "More actions..." */
788
 
    moreactions.selectedIndex = 0;
789
 
 
790
 
    /* If 0 files selected, filename is the name of the current dir.
791
 
     * If 1 file selected, filename is that file.
792
 
     */
793
 
    if (selected_files.length == 0)
794
 
        filename = path_basename(current_path);
795
 
    else if (selected_files.length == 1)
796
 
        filename = selected_files[0];
797
 
    else
798
 
        filename = null;
799
 
 
800
 
    /* Now handle the selected action */
801
 
    switch(selectedaction)
802
 
    {
803
 
    case "publish":
804
 
        action_publish(selected_files);
805
 
        break;
806
 
    case "unpublish":
807
 
        action_unpublish(selected_files);
808
 
        break;
809
 
    case "share":
810
 
        //alert("Not yet implemented: Sharing files");
811
 
        window.open(public_app_path(serve_app, current_path, filename), 'share')
812
 
        break;
813
 
    case "submit":
814
 
        // TODO
815
 
        alert("Not yet implemented: Submit");
816
 
        break;
817
 
    case "rename":
818
 
        action_rename(filename);
819
 
        break;
820
 
    case "delete":
821
 
        action_delete(selected_files);
822
 
        break;
823
 
    case "copy":
824
 
        action_copy(selected_files);
825
 
        break;
826
 
    case "cut":
827
 
        action_cut(selected_files);
828
 
        break;
829
 
    case "paste":
830
 
        action_paste();
831
 
        break;
832
 
    case "newfile":
833
 
        action_newfile();
834
 
        break;
835
 
    case "mkdir":
836
 
        action_mkdir();
837
 
        break;
838
 
    case "upload":
839
 
        show_uploadpanel(true);
840
 
        break;
841
 
    case "svnadd":
842
 
        action_add(selected_files);
843
 
        break;
844
 
    case "svnremove":
845
 
        action_remove(selected_files);
846
 
        break;
847
 
    case "svnrevert":
848
 
        action_revert(selected_files);
849
 
        break;
850
 
    case "svndiff":
851
 
        window.location = path_join(app_path('diff'), current_path, selected_files[0] || '');
852
 
        break;
853
 
    case "svnupdate":
854
 
        action_update(selected_files);
855
 
        break;
856
 
    case "svnresolved":
857
 
        action_resolved(selected_files);
858
 
        break;
859
 
    case "svncommit":
860
 
        action_commit(selected_files);
861
 
        break;
862
 
    case "svnlog":
863
 
        window.location = path_join(app_path('svnlog'), current_path, selected_files[0] || '');
864
 
        break;
865
 
    case "svncheckout":
866
 
        action_checkout();
867
 
        break;
868
 
    }
869
 
}
870
 
 
871
 
/** User clicks "Run" button.
872
 
 * Do an Ajax call and print the test output.
873
 
 */
874
 
function runfile(localpath)
875
 
{
876
 
    if (!maybe_save('The last saved version will be run.')) return false;
877
 
 
878
 
    /* Dump the entire file to the console */
879
 
    var callback = function()
880
 
    {
881
 
        console_enter_line("execfile('" + localpath + "')", "block");
882
 
    }
883
 
    start_server(callback)
884
 
    return;
885
 
}
886
 
 
887
431
/** Called when the page loads initially.
888
432
 */
889
 
function browser_init()
 
433
window.onload = function()
890
434
{
891
435
    /* Navigate (internally) to the path in the URL bar.
892
436
     * This causes the page to be populated with whatever is at that address,
895
439
    var path = parse_url(window.location.href).path;
896
440
    /* Strip out root_dir + "/files" from the front of the path */
897
441
    var strip = make_path(this_app);
 
442
    var editmode = false;
898
443
    if (path.substr(0, strip.length) == strip)
899
444
        path = path.substr(strip.length+1);
900
445
    else
904
449
        if (path.substr(0, strip.length) == strip)
905
450
        {
906
451
            path = path.substr(strip.length+1);
 
452
            editmode = true;
907
453
        }
908
454
    }
909
455
 
914
460
        path = username;
915
461
    }
916
462
 
917
 
    navigate(path);
 
463
    navigate(path, editmode);
918
464
}