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