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