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