define load_paths user variable in conkeror.js
[conkeror/arlinius.git] / modules / commands.js
blob2f7175bca108d1382458a85ae73676bac6f49778
1 /**
2  * (C) Copyright 2004-2007 Shawn Betts
3  * (C) Copyright 2007-2009 John J. Foerch
4  * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
5  *
6  * Use, modification, and distribution are subject to the terms specified in the
7  * COPYING file.
8 **/
10 define_hook("quit_hook");
12 function quit () {
13     quit_hook.run();
14     var appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
15         .getService(Ci.nsIAppStartup);
16     appStartup.quit(appStartup.eAttemptQuit);
18 interactive("quit",
19             "Quit Conkeror",
20             quit);
22 interactive("confirm-quit",
23             "Quit Conkeror with confirmation",
24             function (I) {
25                 let result = yield I.window.minibuffer.read_single_character_option(
26                     $prompt = "Quit Conkeror? (y/n)",
27                     $options = ["y", "n"]);
28                 if (result == "y")
29                     quit();
30             });
32 function show_conkeror_version (window) {
33     var xulrunner_version = Cc['@mozilla.org/xre/app-info;1']
34         .getService(Ci.nsIXULAppInfo)
35         .platformVersion;
36     window.minibuffer.message("Conkeror "+conkeror.version+
37                               " (XULRunner "+xulrunner_version+
38                               ", "+get_os()+")");
40 interactive("conkeror-version",
41             "Show version information for Conkeror.",
42             function (I) { show_conkeror_version(I.window); });
43 interactive("version",
44             "Show version information for Conkeror.",
45             "conkeror-version");
47 /* FIXME: maybe this should be supported for non-browser buffers */
48 function scroll_horiz_complete (buffer, n) {
49     var w = buffer.focused_frame;
50     w.scrollTo (n > 0 ? w.scrollMaxX : 0, w.scrollY);
52 interactive("scroll-beginning-of-line",
53             "Scroll the current frame all the way to the left.",
54             function (I) { scroll_horiz_complete(I.buffer, -1); });
56 interactive("scroll-end-of-line",
57             "Scroll the current frame all the way to the right.",
58             function (I) { scroll_horiz_complete(I.buffer, 1); });
60 function delete_window (window) {
61     window.window.close();
63 interactive("delete-window",
64             "Delete the current window.",
65             function (I) { delete_window(I.window); });
67 interactive("jsconsole",
68             "Open the JavaScript console.",
69             "find-url-new-buffer",
70             $browser_object = "chrome://global/content/console.xul");
73 /**
74  * Given a callback func and an interactive context I, call func, passing
75  * either a focused field, or the minibuffer's input element if the
76  * minibuffer is active. Afterward, call `scroll_selection_into_view' on
77  * the field. See `paste_x_primary_selection' and `open_line' for
78  * examples.
79  */
80 function call_on_focused_field (I, func) {
81     var m = I.window.minibuffer;
82     var s = m.current_state;
83     if (m._input_mode_enabled) {
84         m._restore_normal_state();
85         var e = m.input_element;
86     } else
87         var e = I.buffer.focused_element;
88     func(e);
89     scroll_selection_into_view(e);
90     if (s && s.handle_input)
91         s.handle_input(m);
95 /**
96  * Replace the current region with modifier(selection). Deactivates region and
97  * sets point to the end of the inserted text, unless keep_point is true, in
98  * which case the point will be left at the beginning of the inserted text.
99  */
100 function modify_region (field, modifier, keep_point) {
101     if (field.getAttribute("contenteditable") == 'true') {
102         // richedit
103         var doc = field.ownerDocument;
104         var win = doc.defaultView;
105         doc.execCommand("insertHTML", false,
106                         html_escape(modifier(win.getSelection().toString()))
107                             .replace(/\n/g, '<br>')
108                             .replace(/  /g, ' &nbsp;'));
109     } else {
110         // normal text field
111         var replacement =
112             modifier(field.value.substring(field.selectionStart, field.selectionEnd+1));
113         var point = field.selectionStart;
114         field.value =
115             field.value.substr(0, field.selectionStart) + replacement +
116             field.value.substr(field.selectionEnd);
117         if (!keep_point) point += replacement.length;
118         field.setSelectionRange(point, point);
119     }
123 function paste_x_primary_selection (field) {
124     modify_region(field, function (str) read_from_x_primary_selection());
126 interactive("paste-x-primary-selection",
127     "Insert the contents of the X primary selection into the selected field or "+
128     "minibuffer. Deactivates the region if it is active, and leaves the point "+
129     "after the inserted text.",
130     function (I) call_on_focused_field(I, paste_x_primary_selection));
133 function open_line (field) {
134     modify_region(field, function() "\n", true);
136 interactive("open-line",
137     "If there is an active region, replace is with a newline, otherwise just "+
138     "insert a newline. In both cases leave point before the inserted newline.",
139     function (I) call_on_focused_field(I, open_line));
142 function transpose_chars (field) {
143     var value = field.value;
144     var caret = field.selectionStart; // Caret position.
145     var length = value.length;
147     // If we have less than two character in the field or if we are at the
148     // beginning of the field, do nothing.
149     if (length < 2 || caret == 0)
150         return;
152     // If we are at the end of the field, switch places on the two last
153     // characters. TODO: This should happen at the end of every line, not only
154     // at the end of the field.
155     if (caret == length)
156         caret--;
158     // Do the transposing.
159     field.value = switch_subarrays(value, caret - 1, caret, caret, caret + 1);
161     // Increment the caret position. If this is not done, the caret is left at
162     // the end of the field as a result of the replacing of contents.
163     field.selectionStart = caret + 1;
164     field.selectionEnd = caret + 1;
166 interactive("transpose-chars",
167     "Interchange characters around point, moving forward one character.",
168     function (I) call_on_focused_field(I, transpose_chars));
171 interactive("execute-extended-command",
172     "Call a command specified in the minibuffer.",
173     function (I) {
174         var prefix = I.P;
175         var boc = I.browser_object;
176         var prompt = "M-x";
177         if (I.key_sequence)
178             prompt = I.key_sequence.join(" ");
179         if (boc)
180             prompt += ' ['+boc.name+']';
181         if (prefix !== null && prefix !== undefined) {
182             if (typeof prefix == "object")
183                 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
184             else
185                 prompt += " "+prefix;
186         }
187         var command = yield I.minibuffer.read_command($prompt = prompt);
188         call_after_timeout(function () {
189             input_handle_command.call(I.window, new command_event(command));
190         }, 0);
191     },
192     $prefix = true);
195 /// built in commands
196 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
198 // Performs a command on a browser buffer content area
201 define_builtin_commands(
202     "",
203     function (I, command) {
204         var buffer = I.buffer;
205         try {
206             buffer.do_command(command);
207         } catch (e) {
208             /* Ignore exceptions */
209         }
210     },
211     function (I) {
212         I.buffer.mark_active = !I.buffer.mark_active;
213     },
214     function (I) I.buffer.mark_active,
215     false
218 define_builtin_commands(
219     "caret-",
220     function (I, command) {
221         var buffer = I.buffer;
222         try {
223             buffer.do_command(command);
224         } catch (e) {
225             /* Ignore exceptions */
226         }
227     },
228     function (I) {
229         I.buffer.mark_active = !I.buffer.mark_active;
230     },
231     function (I) I.buffer.mark_active,
232     'caret');
234 function get_link_text () {
235     var e = document.commandDispatcher.focusedElement;
236     if (e && e.getAttribute("href")) {
237         return e.getAttribute("href");
238     }
239     return null;
244 function copy_email_address (loc)
246     // Copy the comma-separated list of email addresses only.
247     // There are other ways of embedding email addresses in a mailto:
248     // link, but such complex parsing is beyond us.
249     var qmark = loc.indexOf( "?" );
250     var addresses;
252     if ( qmark > 7 ) {                   // 7 == length of "mailto:"
253         addresses = loc.substring( 7, qmark );
254     } else {
255         addresses = loc.substr( 7 );
256     }
258     //XXX: the original code, which we got from firefox, unescapes the string
259     //     using the current character set.  To do this in conkeror, we
260     //     *should* use an interactive method that gives us the character set,
261     //     rather than fetching it by side-effect.
263     //     // Let's try to unescape it using a character set
264     //     // in case the address is not ASCII.
265     //     try {
266     //         var characterSet = this.target.ownerDocument.characterSet;
267     //         const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
268     //             .getService(Components.interfaces.nsITextToSubURI);
269     //         addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
270     //     }
271     //     catch(ex) {
272     //         // Do nothing.
273     //     }
275     writeToClipboard(addresses);
276     message("Copied '" + addresses + "'");
278 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
281 /* FIXME: fix this command */
283 interactive("source",
284             "Load a JavaScript file.",
285             function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
287 function reinit (window) {
288     var path;
289     try {
290         path = load_rc();
291         window.minibuffer.message("Loaded: " + path);
292     } catch (e) {
293         window.minibuffer.message("Failed to load: "+path);
294     }
297 interactive("reinit",
298             "Reload the Conkeror rc file.",
299             function (I) { reinit(I.window); });
301 interactive("help-page", "Open the Conkeror help page.",
302             "find-url-new-buffer",
303             $browser_object = "chrome://conkeror-help/content/help.html");
305 interactive("tutorial", "Open the Conkeror tutorial.",
306             "find-url-new-buffer",
307             $browser_object = "chrome://conkeror-help/content/tutorial.html");
309 function univ_arg_to_number (prefix, default_value) {
310     if (prefix == null) {
311         if (default_value == null)
312             return 1;
313         else
314             return default_value;
315     }
316     if (typeof prefix == "object")
317         return prefix[0];
318     return prefix;
321 function eval_expression (window, s) {
322     // eval in the global scope.
324     // In addition, the following variables are available:
325     // var window;
326     var buffer = window.buffers.current;
327     var result = eval(s);
328     if (result !== undefined) {
329         window.minibuffer.message(String(result));
330     }
332 interactive("eval-expression",
333             "Evaluate JavaScript statements.",
334             function (I) {
335                 eval_expression(
336                     I.window,
337                     (yield I.minibuffer.read($prompt = "Eval:",
338                                              $history = "eval-expression",
339                                              $completer = javascript_completer(I.buffer))));
340             });
343 function show_extension_manager () {
344     return conkeror.window_watcher.openWindow(
345         null,
346         "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
347         "ExtensionsWindow",
348         "resizable=yes,dialog=no",
349         null);
351 interactive("extensions",
352             "Open the extensions manager in a new window.",
353             show_extension_manager);
355 function print_buffer (buffer) {
356     buffer.top_frame.print();
358 interactive("print-buffer",
359             "Print the currently loaded page.",
360             function (I) { print_buffer(I.buffer); });
362 function view_partial_source (window, charset, selection) {
363     if (charset)
364         charset = "charset=" + charset;
365     window.window.openDialog("chrome://global/content/viewPartialSource.xul",
366                              "_blank", "scrollbars,resizable,chrome,dialog=no",
367                              null, charset, selection, 'selection');
369 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
372 function  view_mathml_source (window, charset, target) {
373     if (charset)
374         charset = "charset=" + charset;
375     window.window.openDialog("chrome://global/content/viewPartialSource.xul",
376                              "_blank", "scrollbars,resizable,chrome,dialog=no",
377                              null, charset, target, 'mathml');
381 function send_key_as_event (window, element, combo) {
382     var split = unformat_key_combo(combo);
383     var event = window.document.createEvent("KeyboardEvent");
384     event.initKeyEvent(
385         "keypress",
386         true,
387         true,
388         null,
389         split.ctrlKey,
390         split.altKey,
391         split.shiftKey,
392         split.metaKey,
393         split.keyCode,
394         split.charCode);
395     if (element) {
396         return element.dispatchEvent(event);
397     } else {
398         return window.dispatchEvent(event);
399     }
403 function ensure_content_focused (buffer) {
404     var foc = buffer.focused_frame_or_null;
405     if (!foc)
406         buffer.top_frame.focus();
408 interactive("ensure-content-focused", "Ensure that the content document has focus.",
409             function (I) { ensure_content_focused(I.buffer); });
412 function network_set_online_status (status) {
413     const io_service = Cc["@mozilla.org/network/io-service;1"]
414         .getService(Ci.nsIIOService2);
415     status = !status;
416     io_service.manageOfflineStatus = false;
417     io_service.offline = status;
419 interactive("network-go-online", "Work online.",
420             function (I) { network_set_online_status(true); });
421 interactive("network-go-offline", "Work offline.",
422             function (I) { network_set_online_status(false); });
425 interactive("submit-form",
426     "Submit the form to which the focused element belongs.",
427     function (I) {
428         var el = I.buffer.focused_element.parentNode;
429         while (el && el.tagName != "FORM")
430             el = el.parentNode;
431         if (el) {
432             var inputs = el.getElementsByTagName("input");
433             for (var i = 0, ilen = inputs.length; i < ilen; i++) {
434                 if (inputs[i].getAttribute("type") == "submit")
435                     return browser_object_follow(I.buffer, FOLLOW_DEFAULT,
436                                                  inputs[i]);
437             }
438             el.submit();
439         }
440     });
444  * Browser Object Commands
445  */
446 interactive("follow", null,
447             alternates(follow, follow_new_buffer, follow_new_window),
448             $browser_object = browser_object_links);
450 interactive("follow-top", null,
451             alternates(follow_current_buffer, follow_current_frame),
452             $browser_object = browser_object_frames,
453             $prompt = "Follow");
455 interactive("follow-new-buffer",
456             "Follow a link in a new buffer",
457             alternates(follow_new_buffer, follow_new_window),
458             $browser_object = browser_object_links,
459             $prompt = "Follow");
461 interactive("follow-new-buffer-background",
462             "Follow a link in a new buffer in the background",
463             alternates(follow_new_buffer_background, follow_new_window),
464             $browser_object = browser_object_links,
465             $prompt = "Follow");
467 interactive("follow-new-window",
468             "Follow a link in a new window",
469             follow_new_window,
470             $browser_object = browser_object_links,
471             $prompt = "Follow");
473 interactive("find-url", "Open a URL in the current buffer",
474             alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
475             $browser_object = browser_object_url);
477 interactive("find-url-new-buffer",
478             "Open a URL in a new buffer",
479             alternates(follow_new_buffer, follow_new_window),
480             $browser_object = browser_object_url,
481             $prompt = "Find url");
483 interactive("find-url-new-window", "Open a URL in a new window",
484             follow_new_window,
485             $browser_object = browser_object_url,
486             $prompt = "Find url");
488 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
489             "find-url",
490             $browser_object =
491                 define_browser_object_class("alternate-url", null,
492                     function (I, prompt) {
493                         check_buffer(I.buffer, content_buffer);
494                         var result = yield I.buffer.window.minibuffer.read_url(
495                             $prompt = prompt,
496                             $initial_value = I.buffer.display_uri_string);
497                         yield co_return(result);
498                     }),
499             $prompt = "Find url");
502 interactive("up", "Go to the parent directory of the current URL",
503             "find-url",
504             $browser_object = browser_object_up_url);
506 interactive("home",
507             "Go to the homepage in the current buffer.", "follow",
508             $browser_object = function () { return homepage; });
510 interactive("make-window",
511             "Make a new window with the homepage.",
512             follow_new_window,
513             $browser_object = function () { return homepage; });
515 interactive("focus", null,
516             function (I) {
517                 var element = yield read_browser_object(I);
518                 browser_element_focus(I.buffer, element);
519             },
520             $browser_object = browser_object_frames);
523 interactive("save",
524     "Save a browser object.",
525     function (I) {
526         var element = yield read_browser_object(I);
527         var spec = load_spec(element);
528         var panel;
529         panel = create_info_panel(I.window, "download-panel",
530                                   [["downloading",
531                                     element_get_operation_label(element, "Saving"),
532                                     load_spec_uri_string(spec)],
533                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
534         try {
535             var file = yield I.minibuffer.read_file_check_overwrite(
536                 $prompt = "Save as:",
537                 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
538                 $history = "save");
539         } finally {
540             panel.destroy();
541         }
542         save_uri(spec, file,
543                  $buffer = I.buffer,
544                  $use_cache = false);
545     },
546     $browser_object = browser_object_links);
549 interactive("copy", null,
550             function (I) {
551                 var element = yield read_browser_object(I);
552                 browser_element_copy(I.buffer, element);
553             },
554             $browser_object = browser_object_links);
556 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
557             alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
558             $browser_object = browser_object_paste_url);
560 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
561             alternates(follow_new_buffer, follow_new_window),
562             $browser_object = browser_object_paste_url);
564 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
565             follow_new_window,
566             $browser_object = browser_object_paste_url);
568 interactive("view-source", null,
569             alternates(view_source, view_source_new_buffer, view_source_new_window),
570             $browser_object = browser_object_frames);
573 interactive("shell-command-on-url",
574     "Run a shell command on the url of a browser object.",
575     function (I) {
576         var cwd = I.local.cwd;
577         var element = yield read_browser_object(I);
578         var spec = load_spec(element);
579         var uri = load_spec_uri_string(spec);
580         var panel;
581         panel = create_info_panel(I.window, "download-panel",
582                                   [["downloading",
583                                     element_get_operation_label(element, "Running on", "URI"),
584                                     load_spec_uri_string(spec)],
585                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
586         try {
587             var cmd = yield I.minibuffer.read_shell_command(
588                 $cwd = cwd,
589                 $initial_value = load_spec_default_shell_command(spec));
590         } finally {
591             panel.destroy();
592         }
593         shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
594     },
595     $browser_object = browser_object_url,
596     $prompt = "Shell command");
599 interactive("shell-command-on-file",
600     "Download a document to a temporary file and run a shell command on it.",
601     function (I) {
602         var cwd = I.local.cwd;
603         var element = yield read_browser_object(I);
604         var spec = load_spec(element);
605         var uri = load_spec_uri_string(spec);
606         var panel;
607         panel = create_info_panel(I.window, "download-panel",
608                                   [["downloading",
609                                     element_get_operation_label(element, "Running on"),
610                                     load_spec_uri_string(spec)],
611                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
612         try {
613             var cmd = yield I.minibuffer.read_shell_command(
614                 $cwd = cwd,
615                 $initial_value = load_spec_default_shell_command(spec));
616         } finally {
617             panel.destroy();
618         }
619         yield browser_element_shell_command(I.buffer, element, cmd, cwd);
620     },
621     $browser_object = browser_object_links,
622     $prompt = "Shell command");
625 interactive("bookmark",
626     "Create a bookmark.",
627     function (I) {
628         var element = yield read_browser_object(I);
629         var spec = load_spec(element);
630         var uri_string = load_spec_uri_string(spec);
631         var panel;
632         panel = create_info_panel(I.window, "bookmark-panel",
633                                   [["bookmarking",
634                                     element_get_operation_label(element, "Bookmarking"),
635                                     uri_string]]);
636         try {
637             var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
638         } finally {
639             panel.destroy();
640         }
641         add_bookmark(uri_string, title);
642         I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
643     },
644     $browser_object = browser_object_frames);
647 interactive("save-page",
648     "Save a document, not including any embedded documents such as images "+
649     "and css.",
650     function (I) {
651         check_buffer(I.buffer, content_buffer);
652         var element = yield read_browser_object(I);
653         var spec = load_spec(element);
654         if (!load_spec_document(spec))
655             throw interactive_error("Element is not associated with a document.");
656         var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
658         var panel;
659         panel = create_info_panel(I.window, "download-panel",
660                                   [["downloading",
661                                     element_get_operation_label(element, "Saving"),
662                                     load_spec_uri_string(spec)],
663                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
665         try {
666             var file = yield I.minibuffer.read_file_check_overwrite(
667                 $prompt = "Save page as:",
668                 $history = "save",
669                 $initial_value = suggested_path);
670         } finally {
671             panel.destroy();
672         }
674         save_uri(spec, file, $buffer = I.buffer);
675     },
676     $browser_object = browser_object_frames);
679 interactive("save-page-as-text",
680     "Save a page as plain text.",
681     function (I) {
682         check_buffer(I.buffer, content_buffer);
683         var element = yield read_browser_object(I);
684         var spec = load_spec(element);
685         var doc;
686         if (!(doc = load_spec_document(spec)))
687             throw interactive_error("Element is not associated with a document.");
688         var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
690         var panel;
691         panel = create_info_panel(I.window, "download-panel",
692                                   [["downloading",
693                                     element_get_operation_label(element, "Saving", "as text"),
694                                     load_spec_uri_string(spec)],
695                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
697         try {
698             var file = yield I.minibuffer.read_file_check_overwrite(
699                 $prompt = "Save page as text:",
700                 $history = "save",
701                 $initial_value = suggested_path);
702         } finally {
703             panel.destroy();
704         }
706         save_document_as_text(doc, file, $buffer = I.buffer);
707     },
708     $browser_object = browser_object_frames);
711 interactive("save-page-complete",
712     "Save a page and all supporting documents, including images, css, "+
713     "and child frame documents.",
714     function (I) {
715         check_buffer(I.buffer, content_buffer);
716         var element = yield read_browser_object(I);
717         var spec = load_spec(element);
718         var doc;
719         if (!(doc = load_spec_document(spec)))
720             throw interactive_error("Element is not associated with a document.");
721         var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
723         var panel;
724         panel = create_info_panel(I.window, "download-panel",
725                                   [["downloading",
726                                     element_get_operation_label(element, "Saving complete"),
727                                     load_spec_uri_string(spec)],
728                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
730         try {
731             var file = yield I.minibuffer.read_file_check_overwrite(
732                 $prompt = "Save page complete:",
733                 $history = "save",
734                 $initial_value = suggested_path);
735             // FIXME: use proper read function
736             var dir = yield I.minibuffer.read_file(
737                 $prompt = "Data Directory:",
738                 $history = "save",
739                 $initial_value = file.path + ".support");
740         } finally {
741             panel.destroy();
742         }
744         save_document_complete(doc, file, dir, $buffer = I.buffer);
745     },
746     $browser_object = browser_object_frames);
749 function view_as_mime_type (I, target) {
750     I.target = target;
751     var element = yield read_browser_object(I);
752     var spec = load_spec(element);
754     if (target == null)
755         target = FOLLOW_CURRENT_FRAME;
757     if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
758         throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
760     var panel;
762     var mime_type = load_spec_mime_type(spec);
763     panel = create_info_panel(I.window, "download-panel",
764                               [["downloading",
765                                 element_get_operation_label(element, "View in browser"),
766                                 load_spec_uri_string(spec)],
767                                ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
770     try {
771         let suggested_type = mime_type;
772         if (viewable_mime_type_list.indexOf(suggested_type) == -1)
773             suggested_type = "text/plain";
774         mime_type = yield I.minibuffer.read_viewable_mime_type(
775             $prompt = "View internally as",
776             $initial_value = suggested_type,
777             $select);
778         override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
779         browser_object_follow(I.buffer, target, spec);
780     } finally {
781         panel.destroy();
782     }
784 function view_as_mime_type_new_buffer (I) {
785     yield view_as_mime_type(I, OPEN_NEW_BUFFER);
787 function view_as_mime_type_new_window (I) {
788     yield view_as_mime_type(I, OPEN_NEW_WINDOW);
790 interactive("view-as-mime-type",
791             "Display a browser object in the browser using the specified MIME type.",
792             alternates(view_as_mime_type,
793                        view_as_mime_type_new_buffer,
794                        view_as_mime_type_new_window),
795             $browser_object = browser_object_frames);
798 interactive("charset-prefix",
799     "A prefix command that prompts for a charset to use in a "+
800     "subsequent navigation command.",
801     function (I) {
802         var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
803             .getService(Ci.nsICharsetConverterManager);
804         var decoders = ccman.getDecoderList()
805         var charsets = [];
806         while (decoders.hasMore())
807             charsets.push(decoders.getNext());
808         I.forced_charset = yield I.minibuffer.read(
809             $prompt = "Charset:",
810             $completer = prefix_completer(
811                 $completions = charsets,
812                 $get_string = function (x) x.toLowerCase()),
813             $match_required);
814     },
815     $prefix);
818 interactive("reload-with-charset",
819     "Prompt for a charset, and reload the current page, forcing use "+
820     "of that charset.",
821     function (I) {
822         var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
823             .getService(Ci.nsICharsetConverterManager);
824         var decoders = ccman.getDecoderList()
825         var charsets = [];
826         while (decoders.hasMore())
827             charsets.push(decoders.getNext());
828         var forced_charset = yield I.minibuffer.read(
829             $prompt = "Charset:",
830             $completer = prefix_completer(
831                 $completions = charsets,
832                 $get_string = function (x) x.toLowerCase()),
833             $match_required);
834         reload(I.buffer, false, null, forced_charset);
835     });
838 interactive("yank",
839             "Paste the contents of the clipboard",
840             function (I) {
841                 I.buffer.mark_active = false;
842                 I.buffer.do_command("cmd_paste");
843             });
845 interactive("kill-region",
846             "Kill (\"cut\") the selected text.",
847             function (I) {
848                 I.buffer.mark_active = false;
849                 I.buffer.do_command("cmd_cut");
850             });
852 interactive("kill-ring-save",
853             "Save the region as if killed, but don't kill it.",
854             function (I) {
855                 I.buffer.mark_active = false;
856                 I.buffer.do_command("cmd_copy");
857             });