Changed M-< behavior to be more like emacs
[conkeror/arlinius.git] / modules / commands.js
blobf1ac821df815e3a9acd320b0f5db9775e81b8aca
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 interactive("scroll-top-left",
56     "Scroll the current frame all the way to the top left",
57      function (I) { I.buffer.do_command("cmd_scrollTop");
58                     scroll_horiz_complete(I.buffer, -1); });
61 function delete_window (window) {
62     window.window.close();
64 interactive("delete-window",
65             "Delete the current window.",
66             function (I) { delete_window(I.window); });
68 interactive("jsconsole",
69             "Open the JavaScript console.",
70             "find-url-new-buffer",
71             $browser_object = "chrome://global/content/console.xul");
74 /**
75  * Given a callback func and an interactive context I, call func, passing
76  * either a focused field, or the minibuffer's input element if the
77  * minibuffer is active. Afterward, call `scroll_selection_into_view' on
78  * the field. See `paste_x_primary_selection' and `open_line' for
79  * examples.
80  */
81 function call_on_focused_field (I, func) {
82     var m = I.window.minibuffer;
83     var s = m.current_state;
84     if (m._input_mode_enabled) {
85         m._restore_normal_state();
86         var e = m.input_element;
87     } else
88         var e = I.buffer.focused_element;
89     func(e);
90     scroll_selection_into_view(e);
91     if (s && s.handle_input)
92         s.handle_input(m);
96 /**
97  * Replace the current region with modifier(selection). Deactivates region and
98  * sets point to the end of the inserted text, unless keep_point is true, in
99  * which case the point will be left at the beginning of the inserted text.
100  */
101 function modify_region (field, modifier, keep_point) {
102     if (field.getAttribute("contenteditable") == 'true') {
103         // richedit
104         var doc = field.ownerDocument;
105         var win = doc.defaultView;
106         doc.execCommand("insertHTML", false,
107                         html_escape(modifier(win.getSelection().toString()))
108                             .replace(/\n/g, '<br>')
109                             .replace(/  /g, ' &nbsp;'));
110     } else {
111         // normal text field
112         var replacement =
113             modifier(field.value.substring(field.selectionStart, field.selectionEnd+1));
114         var point = field.selectionStart;
115         field.value =
116             field.value.substr(0, field.selectionStart) + replacement +
117             field.value.substr(field.selectionEnd);
118         if (!keep_point) point += replacement.length;
119         field.setSelectionRange(point, point);
120     }
124 function paste_x_primary_selection (field) {
125     modify_region(field, function (str) read_from_x_primary_selection());
127 interactive("paste-x-primary-selection",
128     "Insert the contents of the X primary selection into the selected field or "+
129     "minibuffer. Deactivates the region if it is active, and leaves the point "+
130     "after the inserted text.",
131     function (I) call_on_focused_field(I, paste_x_primary_selection));
134 function open_line (field) {
135     modify_region(field, function() "\n", true);
137 interactive("open-line",
138     "If there is an active region, replace is with a newline, otherwise just "+
139     "insert a newline. In both cases leave point before the inserted newline.",
140     function (I) call_on_focused_field(I, open_line));
143 function transpose_chars (field) {
144     var value = field.value;
145     var caret = field.selectionStart; // Caret position.
146     var length = value.length;
148     // If we have less than two character in the field or if we are at the
149     // beginning of the field, do nothing.
150     if (length < 2 || caret == 0)
151         return;
153     // If we are at the end of the field, switch places on the two last
154     // characters. TODO: This should happen at the end of every line, not only
155     // at the end of the field.
156     if (caret == length)
157         caret--;
159     // Do the transposing.
160     field.value = switch_subarrays(value, caret - 1, caret, caret, caret + 1);
162     // Increment the caret position. If this is not done, the caret is left at
163     // the end of the field as a result of the replacing of contents.
164     field.selectionStart = caret + 1;
165     field.selectionEnd = caret + 1;
167 interactive("transpose-chars",
168     "Interchange characters around point, moving forward one character.",
169     function (I) call_on_focused_field(I, transpose_chars));
172 interactive("execute-extended-command",
173     "Call a command specified in the minibuffer.",
174     function (I) {
175         var prefix = I.P;
176         var boc = I.browser_object;
177         var prompt = "M-x";
178         if (I.key_sequence)
179             prompt = I.key_sequence.join(" ");
180         if (boc)
181             prompt += ' ['+boc.name+']';
182         if (prefix !== null && prefix !== undefined) {
183             if (typeof prefix == "object")
184                 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
185             else
186                 prompt += " "+prefix;
187         }
188         var command = yield I.minibuffer.read_command($prompt = prompt);
189         call_after_timeout(function () {
190             input_handle_command.call(I.window, new command_event(command));
191         }, 0);
192     },
193     $prefix = true);
196 /// built in commands
197 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
199 // Performs a command on a browser buffer content area
202 define_builtin_commands(
203     "",
204     function (I, command) {
205         var buffer = I.buffer;
206         try {
207             buffer.do_command(command);
208         } catch (e) {
209             /* Ignore exceptions */
210         }
211     },
212     function (I) {
213         I.buffer.mark_active = !I.buffer.mark_active;
214     },
215     function (I) I.buffer.mark_active,
216     false
219 define_builtin_commands(
220     "caret-",
221     function (I, command) {
222         var buffer = I.buffer;
223         try {
224             buffer.do_command(command);
225         } catch (e) {
226             /* Ignore exceptions */
227         }
228     },
229     function (I) {
230         I.buffer.mark_active = !I.buffer.mark_active;
231     },
232     function (I) I.buffer.mark_active,
233     'caret');
235 function get_link_text () {
236     var e = document.commandDispatcher.focusedElement;
237     if (e && e.getAttribute("href")) {
238         return e.getAttribute("href");
239     }
240     return null;
245 function copy_email_address (loc)
247     // Copy the comma-separated list of email addresses only.
248     // There are other ways of embedding email addresses in a mailto:
249     // link, but such complex parsing is beyond us.
250     var qmark = loc.indexOf( "?" );
251     var addresses;
253     if ( qmark > 7 ) {                   // 7 == length of "mailto:"
254         addresses = loc.substring( 7, qmark );
255     } else {
256         addresses = loc.substr( 7 );
257     }
259     //XXX: the original code, which we got from firefox, unescapes the string
260     //     using the current character set.  To do this in conkeror, we
261     //     *should* use an interactive method that gives us the character set,
262     //     rather than fetching it by side-effect.
264     //     // Let's try to unescape it using a character set
265     //     // in case the address is not ASCII.
266     //     try {
267     //         var characterSet = this.target.ownerDocument.characterSet;
268     //         const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
269     //             .getService(Components.interfaces.nsITextToSubURI);
270     //         addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
271     //     }
272     //     catch(ex) {
273     //         // Do nothing.
274     //     }
276     writeToClipboard(addresses);
277     message("Copied '" + addresses + "'");
279 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
282 /* FIXME: fix this command */
284 interactive("source",
285             "Load a JavaScript file.",
286             function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
288 function reinit (window) {
289     var path;
290     try {
291         path = load_rc();
292         window.minibuffer.message("Loaded: " + path);
293     } catch (e) {
294         window.minibuffer.message("Failed to load: "+path);
295     }
298 interactive("reinit",
299             "Reload the Conkeror rc file.",
300             function (I) { reinit(I.window); });
302 interactive("help-page", "Open the Conkeror help page.",
303             "find-url-new-buffer",
304             $browser_object = "chrome://conkeror-help/content/help.html");
306 interactive("tutorial", "Open the Conkeror tutorial.",
307             "find-url-new-buffer",
308             $browser_object = "chrome://conkeror-help/content/tutorial.html");
310 function univ_arg_to_number (prefix, default_value) {
311     if (prefix == null) {
312         if (default_value == null)
313             return 1;
314         else
315             return default_value;
316     }
317     if (typeof prefix == "object")
318         return prefix[0];
319     return prefix;
322 function eval_expression (window, s) {
323     // eval in the global scope.
325     // In addition, the following variables are available:
326     // var window;
327     var buffer = window.buffers.current;
328     var result = eval(s);
329     if (result !== undefined) {
330         window.minibuffer.message(String(result));
331     }
333 interactive("eval-expression",
334             "Evaluate JavaScript statements.",
335             function (I) {
336                 eval_expression(
337                     I.window,
338                     (yield I.minibuffer.read($prompt = "Eval:",
339                                              $history = "eval-expression",
340                                              $completer = javascript_completer(I.buffer))));
341             });
344 function show_extension_manager () {
345     return conkeror.window_watcher.openWindow(
346         null,
347         "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
348         "ExtensionsWindow",
349         "resizable=yes,dialog=no",
350         null);
352 interactive("extensions",
353             "Open the extensions manager in a new window.",
354             show_extension_manager);
356 function print_buffer (buffer) {
357     buffer.top_frame.print();
359 interactive("print-buffer",
360             "Print the currently loaded page.",
361             function (I) { print_buffer(I.buffer); });
363 function view_partial_source (window, charset, selection) {
364     if (charset)
365         charset = "charset=" + charset;
366     window.window.openDialog("chrome://global/content/viewPartialSource.xul",
367                              "_blank", "scrollbars,resizable,chrome,dialog=no",
368                              null, charset, selection, 'selection');
370 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
373 function  view_mathml_source (window, charset, target) {
374     if (charset)
375         charset = "charset=" + charset;
376     window.window.openDialog("chrome://global/content/viewPartialSource.xul",
377                              "_blank", "scrollbars,resizable,chrome,dialog=no",
378                              null, charset, target, 'mathml');
382 function send_key_as_event (window, element, combo) {
383     var split = unformat_key_combo(combo);
384     var event = window.document.createEvent("KeyboardEvent");
385     event.initKeyEvent(
386         "keypress",
387         true,
388         true,
389         null,
390         split.ctrlKey,
391         split.altKey,
392         split.shiftKey,
393         split.metaKey,
394         split.keyCode,
395         split.charCode);
396     if (element) {
397         return element.dispatchEvent(event);
398     } else {
399         return window.dispatchEvent(event);
400     }
404 function ensure_content_focused (buffer) {
405     var foc = buffer.focused_frame_or_null;
406     if (!foc)
407         buffer.top_frame.focus();
409 interactive("ensure-content-focused", "Ensure that the content document has focus.",
410             function (I) { ensure_content_focused(I.buffer); });
413 function network_set_online_status (status) {
414     const io_service = Cc["@mozilla.org/network/io-service;1"]
415         .getService(Ci.nsIIOService2);
416     status = !status;
417     io_service.manageOfflineStatus = false;
418     io_service.offline = status;
420 interactive("network-go-online", "Work online.",
421             function (I) { network_set_online_status(true); });
422 interactive("network-go-offline", "Work offline.",
423             function (I) { network_set_online_status(false); });
426 interactive("submit-form",
427     "Submit the form to which the focused element belongs.",
428     function (I) {
429         var el = I.buffer.focused_element.parentNode;
430         while (el && el.tagName != "FORM")
431             el = el.parentNode;
432         if (el) {
433             var inputs = el.getElementsByTagName("input");
434             for (var i = 0, ilen = inputs.length; i < ilen; i++) {
435                 if (inputs[i].getAttribute("type") == "submit")
436                     return browser_object_follow(I.buffer, FOLLOW_DEFAULT,
437                                                  inputs[i]);
438             }
439             el.submit();
440         }
441     });
445  * Browser Object Commands
446  */
447 interactive("follow", null,
448             alternates(follow, follow_new_buffer, follow_new_window),
449             $browser_object = browser_object_links);
451 interactive("follow-top", null,
452             alternates(follow_current_buffer, follow_current_frame),
453             $browser_object = browser_object_frames,
454             $prompt = "Follow");
456 interactive("follow-new-buffer",
457             "Follow a link in a new buffer",
458             alternates(follow_new_buffer, follow_new_window),
459             $browser_object = browser_object_links,
460             $prompt = "Follow");
462 interactive("follow-new-buffer-background",
463             "Follow a link in a new buffer in the background",
464             alternates(follow_new_buffer_background, follow_new_window),
465             $browser_object = browser_object_links,
466             $prompt = "Follow");
468 interactive("follow-new-window",
469             "Follow a link in a new window",
470             follow_new_window,
471             $browser_object = browser_object_links,
472             $prompt = "Follow");
474 interactive("find-url", "Open a URL in the current buffer",
475             alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
476             $browser_object = browser_object_url);
478 interactive("find-url-new-buffer",
479             "Open a URL in a new buffer",
480             alternates(follow_new_buffer, follow_new_window),
481             $browser_object = browser_object_url,
482             $prompt = "Find url");
484 interactive("find-url-new-window", "Open a URL in a new window",
485             follow_new_window,
486             $browser_object = browser_object_url,
487             $prompt = "Find url");
489 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
490             "find-url",
491             $browser_object =
492                 define_browser_object_class("alternate-url", null,
493                     function (I, prompt) {
494                         check_buffer(I.buffer, content_buffer);
495                         var result = yield I.buffer.window.minibuffer.read_url(
496                             $prompt = prompt,
497                             $initial_value = I.buffer.display_uri_string);
498                         yield co_return(result);
499                     }),
500             $prompt = "Find url");
503 interactive("up", "Go to the parent directory of the current URL",
504             "find-url",
505             $browser_object = browser_object_up_url);
507 interactive("home",
508             "Go to the homepage in the current buffer.", "follow",
509             $browser_object = function () { return homepage; });
511 interactive("make-window",
512             "Make a new window with the homepage.",
513             follow_new_window,
514             $browser_object = function () { return homepage; });
516 interactive("focus", null,
517             function (I) {
518                 var element = yield read_browser_object(I);
519                 browser_element_focus(I.buffer, element);
520             },
521             $browser_object = browser_object_frames);
524 interactive("save",
525     "Save a browser object.",
526     function (I) {
527         var element = yield read_browser_object(I);
528         var spec = load_spec(element);
529         var panel;
530         panel = create_info_panel(I.window, "download-panel",
531                                   [["downloading",
532                                     element_get_operation_label(element, "Saving"),
533                                     load_spec_uri_string(spec)],
534                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
535         try {
536             var file = yield I.minibuffer.read_file_check_overwrite(
537                 $prompt = "Save as:",
538                 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
539                 $history = "save");
540         } finally {
541             panel.destroy();
542         }
543         save_uri(spec, file,
544                  $buffer = I.buffer,
545                  $use_cache = false);
546     },
547     $browser_object = browser_object_links);
550 interactive("copy", null,
551             function (I) {
552                 var element = yield read_browser_object(I);
553                 browser_element_copy(I.buffer, element);
554             },
555             $browser_object = browser_object_links);
557 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
558             alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
559             $browser_object = browser_object_paste_url);
561 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
562             alternates(follow_new_buffer, follow_new_window),
563             $browser_object = browser_object_paste_url);
565 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
566             follow_new_window,
567             $browser_object = browser_object_paste_url);
569 interactive("view-source", null,
570             alternates(view_source, view_source_new_buffer, view_source_new_window),
571             $browser_object = browser_object_frames);
574 interactive("shell-command-on-url",
575     "Run a shell command on the url of a browser object.",
576     function (I) {
577         var cwd = I.local.cwd;
578         var element = yield read_browser_object(I);
579         var spec = load_spec(element);
580         var uri = load_spec_uri_string(spec);
581         var panel;
582         panel = create_info_panel(I.window, "download-panel",
583                                   [["downloading",
584                                     element_get_operation_label(element, "Running on", "URI"),
585                                     load_spec_uri_string(spec)],
586                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
587         try {
588             var cmd = yield I.minibuffer.read_shell_command(
589                 $cwd = cwd,
590                 $initial_value = load_spec_default_shell_command(spec));
591         } finally {
592             panel.destroy();
593         }
594         shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
595     },
596     $browser_object = browser_object_url,
597     $prompt = "Shell command");
600 interactive("shell-command-on-file",
601     "Download a document to a temporary file and run a shell command on it.",
602     function (I) {
603         var cwd = I.local.cwd;
604         var element = yield read_browser_object(I);
605         var spec = load_spec(element);
606         var uri = load_spec_uri_string(spec);
607         var panel;
608         panel = create_info_panel(I.window, "download-panel",
609                                   [["downloading",
610                                     element_get_operation_label(element, "Running on"),
611                                     load_spec_uri_string(spec)],
612                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
613         try {
614             var cmd = yield I.minibuffer.read_shell_command(
615                 $cwd = cwd,
616                 $initial_value = load_spec_default_shell_command(spec));
617         } finally {
618             panel.destroy();
619         }
620         yield browser_element_shell_command(I.buffer, element, cmd, cwd);
621     },
622     $browser_object = browser_object_links,
623     $prompt = "Shell command");
626 interactive("bookmark",
627     "Create a bookmark.",
628     function (I) {
629         var element = yield read_browser_object(I);
630         var spec = load_spec(element);
631         var uri_string = load_spec_uri_string(spec);
632         var panel;
633         panel = create_info_panel(I.window, "bookmark-panel",
634                                   [["bookmarking",
635                                     element_get_operation_label(element, "Bookmarking"),
636                                     uri_string]]);
637         try {
638             var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
639         } finally {
640             panel.destroy();
641         }
642         add_bookmark(uri_string, title);
643         I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
644     },
645     $browser_object = browser_object_frames);
648 interactive("save-page",
649     "Save a document, not including any embedded documents such as images "+
650     "and css.",
651     function (I) {
652         check_buffer(I.buffer, content_buffer);
653         var element = yield read_browser_object(I);
654         var spec = load_spec(element);
655         if (!load_spec_document(spec))
656             throw interactive_error("Element is not associated with a document.");
657         var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
659         var panel;
660         panel = create_info_panel(I.window, "download-panel",
661                                   [["downloading",
662                                     element_get_operation_label(element, "Saving"),
663                                     load_spec_uri_string(spec)],
664                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
666         try {
667             var file = yield I.minibuffer.read_file_check_overwrite(
668                 $prompt = "Save page as:",
669                 $history = "save",
670                 $initial_value = suggested_path);
671         } finally {
672             panel.destroy();
673         }
675         save_uri(spec, file, $buffer = I.buffer);
676     },
677     $browser_object = browser_object_frames);
680 interactive("save-page-as-text",
681     "Save a page as plain text.",
682     function (I) {
683         check_buffer(I.buffer, content_buffer);
684         var element = yield read_browser_object(I);
685         var spec = load_spec(element);
686         var doc;
687         if (!(doc = load_spec_document(spec)))
688             throw interactive_error("Element is not associated with a document.");
689         var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
691         var panel;
692         panel = create_info_panel(I.window, "download-panel",
693                                   [["downloading",
694                                     element_get_operation_label(element, "Saving", "as text"),
695                                     load_spec_uri_string(spec)],
696                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
698         try {
699             var file = yield I.minibuffer.read_file_check_overwrite(
700                 $prompt = "Save page as text:",
701                 $history = "save",
702                 $initial_value = suggested_path);
703         } finally {
704             panel.destroy();
705         }
707         save_document_as_text(doc, file, $buffer = I.buffer);
708     },
709     $browser_object = browser_object_frames);
712 interactive("save-page-complete",
713     "Save a page and all supporting documents, including images, css, "+
714     "and child frame documents.",
715     function (I) {
716         check_buffer(I.buffer, content_buffer);
717         var element = yield read_browser_object(I);
718         var spec = load_spec(element);
719         var doc;
720         if (!(doc = load_spec_document(spec)))
721             throw interactive_error("Element is not associated with a document.");
722         var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
724         var panel;
725         panel = create_info_panel(I.window, "download-panel",
726                                   [["downloading",
727                                     element_get_operation_label(element, "Saving complete"),
728                                     load_spec_uri_string(spec)],
729                                    ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
731         try {
732             var file = yield I.minibuffer.read_file_check_overwrite(
733                 $prompt = "Save page complete:",
734                 $history = "save",
735                 $initial_value = suggested_path);
736             // FIXME: use proper read function
737             var dir = yield I.minibuffer.read_file(
738                 $prompt = "Data Directory:",
739                 $history = "save",
740                 $initial_value = file.path + ".support");
741         } finally {
742             panel.destroy();
743         }
745         save_document_complete(doc, file, dir, $buffer = I.buffer);
746     },
747     $browser_object = browser_object_frames);
750 function view_as_mime_type (I, target) {
751     I.target = target;
752     var element = yield read_browser_object(I);
753     var spec = load_spec(element);
755     if (target == null)
756         target = FOLLOW_CURRENT_FRAME;
758     if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
759         throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
761     var panel;
763     var mime_type = load_spec_mime_type(spec);
764     panel = create_info_panel(I.window, "download-panel",
765                               [["downloading",
766                                 element_get_operation_label(element, "View in browser"),
767                                 load_spec_uri_string(spec)],
768                                ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
771     try {
772         let suggested_type = mime_type;
773         if (viewable_mime_type_list.indexOf(suggested_type) == -1)
774             suggested_type = "text/plain";
775         mime_type = yield I.minibuffer.read_viewable_mime_type(
776             $prompt = "View internally as",
777             $initial_value = suggested_type,
778             $select);
779         override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
780         browser_object_follow(I.buffer, target, spec);
781     } finally {
782         panel.destroy();
783     }
785 function view_as_mime_type_new_buffer (I) {
786     yield view_as_mime_type(I, OPEN_NEW_BUFFER);
788 function view_as_mime_type_new_window (I) {
789     yield view_as_mime_type(I, OPEN_NEW_WINDOW);
791 interactive("view-as-mime-type",
792             "Display a browser object in the browser using the specified MIME type.",
793             alternates(view_as_mime_type,
794                        view_as_mime_type_new_buffer,
795                        view_as_mime_type_new_window),
796             $browser_object = browser_object_frames);
799 interactive("charset-prefix",
800     "A prefix command that prompts for a charset to use in a "+
801     "subsequent navigation command.",
802     function (I) {
803         var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
804             .getService(Ci.nsICharsetConverterManager);
805         var decoders = ccman.getDecoderList()
806         var charsets = [];
807         while (decoders.hasMore())
808             charsets.push(decoders.getNext());
809         I.forced_charset = yield I.minibuffer.read(
810             $prompt = "Charset:",
811             $completer = prefix_completer(
812                 $completions = charsets,
813                 $get_string = function (x) x.toLowerCase()),
814             $match_required);
815     },
816     $prefix);
819 interactive("reload-with-charset",
820     "Prompt for a charset, and reload the current page, forcing use "+
821     "of that charset.",
822     function (I) {
823         var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
824             .getService(Ci.nsICharsetConverterManager);
825         var decoders = ccman.getDecoderList()
826         var charsets = [];
827         while (decoders.hasMore())
828             charsets.push(decoders.getNext());
829         var forced_charset = yield I.minibuffer.read(
830             $prompt = "Charset:",
831             $completer = prefix_completer(
832                 $completions = charsets,
833                 $get_string = function (x) x.toLowerCase()),
834             $match_required);
835         reload(I.buffer, false, null, forced_charset);
836     });
839 interactive("yank",
840             "Paste the contents of the clipboard",
841             function (I) {
842                 I.buffer.mark_active = false;
843                 I.buffer.do_command("cmd_paste");
844             });
846 interactive("kill-region",
847             "Kill (\"cut\") the selected text.",
848             function (I) {
849                 I.buffer.mark_active = false;
850                 I.buffer.do_command("cmd_cut");
851             });
853 interactive("kill-ring-save",
854             "Save the region as if killed, but don't kill it.",
855             function (I) {
856                 I.buffer.mark_active = false;
857                 I.buffer.do_command("cmd_copy");
858             });