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 window.minibuffer.message("Conkeror "+conkeror.version);
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.",
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");
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
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;
82 var e = I.buffer.focused_element;
84 scroll_selection_into_view(e);
85 if (s && s.handle_input)
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.
95 function modify_region (field, modifier, keep_point) {
96 if (field.getAttribute("contenteditable") == 'true') {
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, ' '));
107 modifier(field.value.substring(field.selectionStart, field.selectionEnd+1));
108 var point = field.selectionStart;
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);
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)
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.
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.",
170 var boc = I.browser_object;
173 prompt = I.key_sequence.join(" ");
175 prompt += ' ['+boc.name+']';
176 if (prefix !== null && prefix !== undefined) {
177 if (typeof prefix == "object")
178 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
180 prompt += " "+prefix;
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));
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(
198 function (I, command) {
199 var buffer = I.buffer;
201 buffer.do_command(command);
203 /* Ignore exceptions */
207 I.buffer.mark_active = !I.buffer.mark_active;
209 function (I) I.buffer.mark_active,
213 define_builtin_commands(
215 function (I, command) {
216 var buffer = I.buffer;
218 buffer.do_command(command);
220 /* Ignore exceptions */
224 I.buffer.mark_active = !I.buffer.mark_active;
226 function (I) I.buffer.mark_active,
229 function get_link_text () {
230 var e = document.commandDispatcher.focusedElement;
231 if (e && e.getAttribute("href")) {
232 return e.getAttribute("href");
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( "?" );
247 if ( qmark > 7 ) { // 7 == length of "mailto:"
248 addresses = loc.substring( 7, qmark );
250 addresses = loc.substr( 7 );
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.
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);
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) {
286 window.minibuffer.message("Loaded: " + path);
288 window.minibuffer.message("Failed to load: "+path);
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)
309 return default_value;
311 if (typeof prefix == "object")
316 function eval_expression (window, s) {
317 // eval in the global scope.
319 // In addition, the following variables are available:
321 var buffer = window.buffers.current;
322 var result = eval(s);
323 if (result !== undefined) {
324 window.minibuffer.message(String(result));
327 interactive("eval-expression",
328 "Evaluate JavaScript statements.",
332 (yield I.minibuffer.read($prompt = "Eval:",
333 $history = "eval-expression",
334 $completer = javascript_completer(I.buffer))));
338 function show_extension_manager () {
339 return conkeror.window_watcher.openWindow(
341 "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
343 "resizable=yes,dialog=no",
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) {
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) {
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");
391 return element.dispatchEvent(event);
393 return window.dispatchEvent(event);
398 function ensure_content_focused (buffer) {
399 var foc = buffer.focused_frame_or_null;
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) {
409 io_service.manageOfflineStatus = false;
410 io_service.offline = status;
412 interactive("network-go-online", "Work online.",
413 function (I) { network_set_online_status(true); });
414 interactive("network-go-offline", "Work offline.",
415 function (I) { network_set_online_status(false); });
418 interactive("submit-form",
419 "Submit the form to which the focused element belongs.",
421 var el = I.buffer.focused_element.parentNode;
422 while (el && el.tagName != "FORM")
424 //FIXME: submit method does not trigger onsubmit or other
425 // event handlers which may be on the "submit" button.
432 * Browser Object Commands
434 interactive("follow", null,
435 alternates(follow, follow_new_buffer, follow_new_window),
436 $browser_object = browser_object_links);
438 interactive("follow-top", null,
439 alternates(follow_current_buffer, follow_current_frame),
440 $browser_object = browser_object_frames,
443 interactive("follow-new-buffer",
444 "Follow a link in a new buffer",
445 alternates(follow_new_buffer, follow_new_window),
446 $browser_object = browser_object_links,
449 interactive("follow-new-buffer-background",
450 "Follow a link in a new buffer in the background",
451 alternates(follow_new_buffer_background, follow_new_window),
452 $browser_object = browser_object_links,
455 interactive("follow-new-window",
456 "Follow a link in a new window",
458 $browser_object = browser_object_links,
461 interactive("find-url", "Open a URL in the current buffer",
462 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
463 $browser_object = browser_object_url);
465 interactive("find-url-new-buffer",
466 "Open a URL in a new buffer",
467 alternates(follow_new_buffer, follow_new_window),
468 $browser_object = browser_object_url,
469 $prompt = "Find url");
471 interactive("find-url-new-window", "Open a URL in a new window",
473 $browser_object = browser_object_url,
474 $prompt = "Find url");
476 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
479 define_browser_object_class("alternate-url", null,
480 function (I, prompt) {
481 check_buffer(I.buffer, content_buffer);
482 var result = yield I.buffer.window.minibuffer.read_url(
484 $initial_value = I.buffer.display_uri_string);
485 yield co_return(result);
487 $prompt = "Find url");
490 interactive("up", "Go to the parent directory of the current URL",
492 $browser_object = browser_object_up_url);
495 "Go to the homepage in the current buffer.", "follow",
496 $browser_object = function () { return homepage; });
498 interactive("make-window",
499 "Make a new window with the homepage.",
501 $browser_object = function () { return homepage; });
503 interactive("focus", null,
505 var element = yield read_browser_object(I);
506 browser_element_focus(I.buffer, element);
508 $browser_object = browser_object_frames);
512 "Save a browser object.",
514 var element = yield read_browser_object(I);
515 var spec = load_spec(element);
517 panel = create_info_panel(I.window, "download-panel",
519 element_get_operation_label(element, "Saving"),
520 load_spec_uri_string(spec)],
521 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
523 var file = yield I.minibuffer.read_file_check_overwrite(
524 $prompt = "Save as:",
525 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
534 $browser_object = browser_object_links);
537 interactive("copy", null,
539 var element = yield read_browser_object(I);
540 browser_element_copy(I.buffer, element);
542 $browser_object = browser_object_links);
544 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
545 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
546 $browser_object = browser_object_paste_url);
548 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
549 alternates(follow_new_buffer, follow_new_window),
550 $browser_object = browser_object_paste_url);
552 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
554 $browser_object = browser_object_paste_url);
556 interactive("view-source", null,
557 alternates(view_source, view_source_new_buffer, view_source_new_window),
558 $browser_object = browser_object_frames);
561 interactive("shell-command-on-url",
562 "Run a shell command on the url of a browser object.",
564 var cwd = I.local.cwd;
565 var element = yield read_browser_object(I);
566 var spec = load_spec(element);
567 var uri = load_spec_uri_string(spec);
569 panel = create_info_panel(I.window, "download-panel",
571 element_get_operation_label(element, "Running on", "URI"),
572 load_spec_uri_string(spec)],
573 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
575 var cmd = yield I.minibuffer.read_shell_command(
577 $initial_value = load_spec_default_shell_command(spec));
581 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
583 $browser_object = browser_object_url,
584 $prompt = "Shell command");
587 interactive("shell-command-on-file",
588 "Download a document to a temporary file and run a shell command on it.",
590 var cwd = I.local.cwd;
591 var element = yield read_browser_object(I);
592 var spec = load_spec(element);
593 var uri = load_spec_uri_string(spec);
595 panel = create_info_panel(I.window, "download-panel",
597 element_get_operation_label(element, "Running on"),
598 load_spec_uri_string(spec)],
599 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
601 var cmd = yield I.minibuffer.read_shell_command(
603 $initial_value = load_spec_default_shell_command(spec));
607 yield browser_element_shell_command(I.buffer, element, cmd, cwd);
609 $browser_object = browser_object_links,
610 $prompt = "Shell command");
613 interactive("bookmark",
614 "Create a bookmark.",
616 var element = yield read_browser_object(I);
617 var spec = load_spec(element);
618 var uri_string = load_spec_uri_string(spec);
620 panel = create_info_panel(I.window, "bookmark-panel",
622 element_get_operation_label(element, "Bookmarking"),
625 var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
629 add_bookmark(uri_string, title);
630 I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
632 $browser_object = browser_object_frames);
635 interactive("save-page",
636 "Save a document, not including any embedded documents such as images "+
639 check_buffer(I.buffer, content_buffer);
640 var element = yield read_browser_object(I);
641 var spec = load_spec(element);
642 if (!load_spec_document(spec))
643 throw interactive_error("Element is not associated with a document.");
644 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
647 panel = create_info_panel(I.window, "download-panel",
649 element_get_operation_label(element, "Saving"),
650 load_spec_uri_string(spec)],
651 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
654 var file = yield I.minibuffer.read_file_check_overwrite(
655 $prompt = "Save page as:",
657 $initial_value = suggested_path);
662 save_uri(spec, file, $buffer = I.buffer);
664 $browser_object = browser_object_frames);
667 interactive("save-page-as-text",
668 "Save a page as plain text.",
670 check_buffer(I.buffer, content_buffer);
671 var element = yield read_browser_object(I);
672 var spec = load_spec(element);
674 if (!(doc = load_spec_document(spec)))
675 throw interactive_error("Element is not associated with a document.");
676 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
679 panel = create_info_panel(I.window, "download-panel",
681 element_get_operation_label(element, "Saving", "as text"),
682 load_spec_uri_string(spec)],
683 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
686 var file = yield I.minibuffer.read_file_check_overwrite(
687 $prompt = "Save page as text:",
689 $initial_value = suggested_path);
694 save_document_as_text(doc, file, $buffer = I.buffer);
696 $browser_object = browser_object_frames);
699 interactive("save-page-complete",
700 "Save a page and all supporting documents, including images, css, "+
701 "and child frame documents.",
703 check_buffer(I.buffer, content_buffer);
704 var element = yield read_browser_object(I);
705 var spec = load_spec(element);
707 if (!(doc = load_spec_document(spec)))
708 throw interactive_error("Element is not associated with a document.");
709 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
712 panel = create_info_panel(I.window, "download-panel",
714 element_get_operation_label(element, "Saving complete"),
715 load_spec_uri_string(spec)],
716 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
719 var file = yield I.minibuffer.read_file_check_overwrite(
720 $prompt = "Save page complete:",
722 $initial_value = suggested_path);
723 // FIXME: use proper read function
724 var dir = yield I.minibuffer.read_file(
725 $prompt = "Data Directory:",
727 $initial_value = file.path + ".support");
732 save_document_complete(doc, file, dir, $buffer = I.buffer);
734 $browser_object = browser_object_frames);
737 function view_as_mime_type (I, target) {
739 var element = yield read_browser_object(I);
740 var spec = load_spec(element);
743 target = FOLLOW_CURRENT_FRAME;
745 if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
746 throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
750 var mime_type = load_spec_mime_type(spec);
751 panel = create_info_panel(I.window, "download-panel",
753 element_get_operation_label(element, "View in browser"),
754 load_spec_uri_string(spec)],
755 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
759 let suggested_type = mime_type;
760 if (viewable_mime_type_list.indexOf(suggested_type) == -1)
761 suggested_type = "text/plain";
762 mime_type = yield I.minibuffer.read_viewable_mime_type(
763 $prompt = "View internally as",
764 $initial_value = suggested_type,
766 override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
767 browser_object_follow(I.buffer, target, spec);
772 function view_as_mime_type_new_buffer (I) {
773 yield view_as_mime_type(I, OPEN_NEW_BUFFER);
775 function view_as_mime_type_new_window (I) {
776 yield view_as_mime_type(I, OPEN_NEW_WINDOW);
778 interactive("view-as-mime-type",
779 "Display a browser object in the browser using the specified MIME type.",
780 alternates(view_as_mime_type,
781 view_as_mime_type_new_buffer,
782 view_as_mime_type_new_window),
783 $browser_object = browser_object_frames);
786 interactive("reload-with-charset",
787 "Prompt for a charset, and reload the current page, forcing use "+
790 var ccman = Cc["@mozilla.org/charset-converter-manager;1"]
791 .getService(Ci.nsICharsetConverterManager);
792 var decoders = ccman.getDecoderList()
794 while (decoders.hasMore())
795 charsets.push(decoders.getNext());
796 var forced_charset = yield I.minibuffer.read(
797 $prompt = "Charset:",
798 $completer = prefix_completer(
799 $completions = charsets,
800 $get_string = function (x) x.toLowerCase()),
802 reload(I.buffer, false, null, forced_charset);
807 "Paste the contents of the clipboard",
809 I.buffer.mark_active = false;
810 I.buffer.do_command("cmd_paste");
813 interactive("kill-region",
814 "Kill (\"cut\") the selected text.",
816 I.buffer.mark_active = false;
817 I.buffer.do_command("cmd_cut");
820 interactive("kill-ring-save",
821 "Save the region as if killed, but don't kill it.",
823 I.buffer.mark_active = false;
824 I.buffer.do_command("cmd_copy");