2 * (C) Copyright 2004-2007 Shawn Betts
3 * (C) Copyright 2007-2008 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 require("content-buffer.js");
12 define_hook("quit_hook");
16 var appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
17 .getService(Ci.nsIAppStartup);
18 appStartup.quit(appStartup.eAttemptQuit);
24 interactive("confirm-quit",
25 "Quit Conkeror with confirmation",
27 let result = yield I.window.minibuffer.read_single_character_option(
28 $prompt = "Quit Conkeror? (y/n)",
29 $options = ["y", "n"]);
34 function show_conkeror_version (window) {
35 window.minibuffer.message(conkeror.version);
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.",
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 function delete_window (window) {
58 window.window.close();
60 interactive("delete-window",
61 "Delete the current window.",
62 function (I) { delete_window(I.window); });
64 interactive("jsconsole",
65 "Open the JavaScript console.",
66 "find-url-new-buffer",
67 $browser_object = "chrome://global/content/console.xul");
70 * Given a callback func and an interactive context I, call func, passing either
71 * a focused field, or the minibuffer's input element if the minibuffer is
72 * active. Afterward, call `ensure_index_is_visible' on the field. See
73 * `paste_x_primary_selection' and `open_line' for examples.
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 ensure_index_is_visible(I.window, e, e.selectionStart);
85 if (s && s.handle_input)
90 * Replace the current region with modifier(selection). Deactivates region and
91 * sets point to the end of the inserted text, unless keep_point is true, in
92 * which case the point will be left at the beginning of the inserted text.
94 function modify_region (field, modifier, keep_point) {
96 modifier(field.value.substring(field.selectionStart, field.selectionEnd+1));
97 var point = field.selectionStart;
99 field.value.substr(0, field.selectionStart) + replacement +
100 field.value.substr(field.selectionEnd);
101 if (!keep_point) point += replacement.length;
102 field.setSelectionRange(point, point);
105 function paste_x_primary_selection (field) {
106 modify_region(field, function (str) read_from_x_primary_selection());
108 interactive("paste-x-primary-selection",
109 "Insert the contents of the X primary selection into the selected field or "+
110 "minibuffer. Deactivates the region if it is active, and leaves the point "+
111 "after the inserted text.",
112 function (I) call_on_focused_field(I, paste_x_primary_selection));
114 function open_line (field) {
115 modify_region(field, function() "\n", true);
118 interactive("open-line",
119 "If there is an active region, replace is with a newline, otherwise just "+
120 "insert a newline. In both cases leave point before the inserted newline.",
121 function (I) call_on_focused_field(I, open_line));
123 function transpose_chars (field) {
124 var value = field.value;
125 var caret = field.selectionStart; // Caret position.
126 var length = value.length;
128 // If we have less than two character in the field or if we are at the
129 // beginning of the field, do nothing.
130 if (length <= 2 || caret == 0)
133 // If we are at the end of the field, switch places on the two last
134 // characters. TODO: This should happen at the end of every line, not only
135 // at the end of the field.
139 // Do the transposing.
140 field.value = switch_subarrays(value, caret - 1, caret, caret, caret + 1);
142 // Increment the caret position. If this is not done, the caret is left at
143 // the end of the field as a result of the replacing of contents.
144 field.selectionStart = caret + 1;
145 field.selectionEnd = caret + 1;
148 interactive("transpose-chars",
149 "Interchange characters around point, moving forward one character.",
150 function (I) call_on_focused_field(I, transpose_chars));
152 function meta_x (buffer, prefix, command, browser_object) {
153 var I = new interactive_context(buffer);
154 I.prefix_argument = prefix;
155 I.browser_object = browser_object;
156 call_interactively(I, command);
158 interactive("execute-extended-command",
159 "Execute a Conkeror command specified in the minibuffer.",
162 var boc = I.browser_object;
165 prompt += ' ['+boc.name+']';
166 if (prefix !== null && prefix !== undefined) {
167 if (typeof prefix == "object")
168 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
170 prompt += " "+prefix;
172 meta_x(I.buffer, I.P,
173 (yield I.minibuffer.read_command(
174 $prompt = "M-x" + prompt)),
178 /// built in commands
179 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
181 // Performs a command on a browser buffer content area
184 define_builtin_commands(
186 function (I, command) {
187 var buffer = I.buffer;
189 buffer.do_command(command);
191 /* Ignore exceptions */
195 I.buffer.mark_active = !I.buffer.mark_active;
197 function (I) I.buffer.mark_active,
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,
217 function get_link_text () {
218 var e = document.commandDispatcher.focusedElement;
219 if (e && e.getAttribute("href")) {
220 return e.getAttribute("href");
227 function copy_email_address (loc)
229 // Copy the comma-separated list of email addresses only.
230 // There are other ways of embedding email addresses in a mailto:
231 // link, but such complex parsing is beyond us.
232 var qmark = loc.indexOf( "?" );
235 if ( qmark > 7 ) { // 7 == length of "mailto:"
236 addresses = loc.substring( 7, qmark );
238 addresses = loc.substr( 7 );
241 //XXX: the original code, which we got from firefox, unescapes the string
242 // using the current character set. To do this in conkeror, we
243 // *should* use an interactive method that gives us the character set,
244 // rather than fetching it by side-effect.
246 // // Let's try to unescape it using a character set
247 // // in case the address is not ASCII.
249 // var characterSet = this.target.ownerDocument.characterSet;
250 // const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
251 // .getService(Components.interfaces.nsITextToSubURI);
252 // addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
258 writeToClipboard(addresses);
259 message("Copied '" + addresses + "'");
261 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
264 /* FIXME: fix this command */
266 interactive("source",
267 "Load a JavaScript file.",
268 function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
270 function reinit (window) {
274 window.minibuffer.message("Loaded: " + path);
276 window.minibuffer.message("Failed to load: "+path);
280 interactive("reinit",
281 "Reload the Conkeror rc file.",
282 function (I) { reinit(I.window); });
284 interactive("help-page", "Open the Conkeror help page.",
285 "find-url-new-buffer",
286 $browser_object = "chrome://conkeror-help/content/help.html");
288 interactive("tutorial", "Open the Conkeror tutorial.",
289 "find-url-new-buffer",
290 $browser_object = "chrome://conkeror-help/content/tutorial.html");
292 function univ_arg_to_number (prefix, default_value) {
293 if (prefix == null) {
294 if (default_value == null)
297 return default_value;
299 if (typeof prefix == "object")
304 function eval_expression (window, s) {
305 // eval in the global scope.
307 // In addition, the following variables are available:
309 var buffer = window.buffers.current;
310 var result = eval(s);
311 if (result !== undefined) {
312 window.minibuffer.message(String(result));
315 interactive("eval-expression",
316 "Evaluate JavaScript statements.",
320 (yield I.minibuffer.read($prompt = "Eval:",
321 $history = "eval-expression",
322 $completer = javascript_completer(I.buffer))));
326 function show_extension_manager () {
327 return conkeror.window_watcher.openWindow(
329 "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
331 "resizable=yes,dialog=no",
334 interactive("extensions",
335 "Open the extensions manager in a new window.",
336 show_extension_manager);
338 function print_buffer (buffer) {
339 buffer.top_frame.print();
341 interactive("print-buffer",
342 "Print the currently loaded page.",
343 function (I) { print_buffer(I.buffer); });
345 function view_partial_source (window, charset, selection) {
347 charset = "charset=" + charset;
348 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
349 "_blank", "scrollbars,resizable,chrome,dialog=no",
350 null, charset, selection, 'selection');
352 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
355 function view_mathml_source (window, charset, target) {
357 charset = "charset=" + charset;
358 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
359 "_blank", "scrollbars,resizable,chrome,dialog=no",
360 null, charset, target, 'mathml');
364 function send_key_as_event (window, element, combo) {
365 var split = unformat_key_combo(combo);
366 var event = window.document.createEvent("KeyboardEvent");
379 return element.dispatchEvent (event);
381 return window.dispatchEvent (event);
386 function ensure_content_focused (buffer) {
387 var foc = buffer.focused_frame_or_null;
389 buffer.top_frame.focus();
391 interactive("ensure-content-focused", "Ensure that the content document has focus.",
392 function (I) { ensure_content_focused(I.buffer); });
394 function network_set_online_status (status) {
396 io_service.manageOfflineStatus = false;
397 io_service.offline = status;
400 interactive("network-go-online", "Work online.",
401 function (I) { network_set_online_status (true); });
402 interactive("network-go-offline", "Work offline.",
403 function (I) { network_set_online_status (false); });
406 interactive("submit-form",
407 "Submit the form to which the focused element belongs.",
409 var el = I.buffer.focused_element.parentNode;
410 while (el && el.tagName != "FORM")
417 * Browser Object Commands
419 interactive("follow", null,
420 alternates(follow, follow_new_buffer, follow_new_window),
421 $browser_object = browser_object_links);
423 interactive("follow-top", null,
424 alternates(follow_current_buffer, follow_current_frame),
425 $browser_object = browser_object_frames,
428 interactive("follow-new-buffer",
429 "Follow a link in a new buffer",
430 alternates(follow_new_buffer, follow_new_window),
431 $browser_object = browser_object_links,
434 interactive("follow-new-buffer-background",
435 "Follow a link in a new buffer in the background",
436 alternates(follow_new_buffer_background, follow_new_window),
437 $browser_object = browser_object_links,
440 interactive("follow-new-window",
441 "Follow a link in a new window",
443 $browser_object = browser_object_links,
446 interactive("follow-current",
447 "Follow current object, normally the focused element.",
448 alternates(follow, follow_new_buffer, follow_new_window),
449 $browser_object = browser_object_focused_element);
451 interactive("follow-current-new-buffer",
452 "Follow current object, normally the focused element, in "+
454 alternates(follow_new_buffer, follow_new_window),
455 $browser_object = browser_object_focused_element,
458 interactive("follow-current-new-buffer-background",
459 "Follow current object, normally the focused element, in "+
460 "a new background buffer.",
461 alternates(follow_new_buffer_background, follow_new_window),
462 $browser_object = browser_object_focused_element,
465 interactive("follow-current-new-window",
466 "Follow current object, normally the focused element, in "+
469 $browser_object = browser_object_focused_element,
472 interactive("find-url", "Open a URL in the current buffer",
473 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
474 $browser_object = browser_object_url);
476 interactive("find-url-new-buffer",
477 "Open a URL in a new buffer",
478 alternates(follow_new_buffer, follow_new_window),
479 $browser_object = browser_object_url,
480 $prompt = "Find url");
482 interactive("find-url-new-window", "Open a URL in a new window",
484 $browser_object = browser_object_url,
485 $prompt = "Find url");
487 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
490 define_browser_object_class("alternate-url", null,
491 function (I, prompt) {
492 check_buffer(I.buffer, content_buffer);
493 var result = yield I.buffer.window.minibuffer.read_url(
495 $initial_value = I.buffer.display_uri_string);
496 yield co_return(result);
498 $prompt = "Find url");
501 interactive("up", "Go to the parent directory of the current URL",
503 $browser_object = browser_object_up_url);
506 "Go to the homepage in the current buffer.", "follow",
507 $browser_object = function () { return homepage; });
509 interactive("make-window",
510 "Make a new window with the homepage.",
512 $browser_object = function () { return homepage; });
514 interactive("focus", null,
516 var element = yield read_browser_object(I);
517 browser_element_focus(I.buffer, element);
519 $browser_object = browser_object_frames);
523 "Save a browser object.",
525 var element = yield read_browser_object(I);
526 var spec = load_spec(element);
528 panel = create_info_panel(I.window, "download-panel",
530 element_get_operation_label(element, "Saving"),
531 load_spec_uri_string(spec)],
532 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
534 var file = yield I.minibuffer.read_file_check_overwrite(
535 $prompt = "Save as:",
536 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
545 $browser_object = browser_object_links);
548 interactive("copy", null,
550 var element = yield read_browser_object(I);
551 browser_element_copy(I.buffer, element);
553 $browser_object = browser_object_links);
555 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
556 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
557 $browser_object = browser_object_paste_url);
559 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
560 alternates(follow_new_buffer, follow_new_window),
561 $browser_object = browser_object_paste_url);
563 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
565 $browser_object = browser_object_paste_url);
567 interactive("view-source", null,
568 alternates(view_source, view_source_new_buffer, view_source_new_window),
569 $browser_object = browser_object_frames);
572 interactive("shell-command-on-url",
573 "Run a shell command on the url of a browser object.",
575 var cwd = I.local.cwd;
576 var element = yield read_browser_object(I);
577 var spec = load_spec(element);
578 var uri = load_spec_uri_string(spec);
580 panel = create_info_panel(I.window, "download-panel",
582 element_get_operation_label(element, "Running on", "URI"),
583 load_spec_uri_string(spec)],
584 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
586 var cmd = yield I.minibuffer.read_shell_command(
588 $initial_value = load_spec_default_shell_command(spec));
592 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
594 $browser_object = browser_object_url,
595 $prompt = "Shell command");
598 interactive("shell-command-on-file",
599 "Download a document to a temporary file and run a shell command on it.",
601 var cwd = I.local.cwd;
602 var element = yield read_browser_object(I);
603 var spec = load_spec(element);
604 var uri = load_spec_uri_string(spec);
606 panel = create_info_panel(I.window, "download-panel",
608 element_get_operation_label(element, "Running on"),
609 load_spec_uri_string(spec)],
610 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
612 var cmd = yield I.minibuffer.read_shell_command(
614 $initial_value = load_spec_default_shell_command(spec));
618 yield browser_element_shell_command(I.buffer, element, cmd, cwd);
620 $browser_object = browser_object_links,
621 $prompt = "Shell command");
624 interactive("bookmark",
625 "Create a bookmark.",
627 var element = yield read_browser_object(I);
628 var spec = load_spec(element);
629 var uri_string = load_spec_uri_string(spec);
631 panel = create_info_panel(I.window, "bookmark-panel",
633 element_get_operation_label(element, "Bookmarking"),
636 var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
640 add_bookmark(uri_string, title);
641 I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
643 $browser_object = browser_object_frames);
646 interactive("save-page",
647 "Save a document, not including any embedded documents such as images "+
650 check_buffer(I.buffer, content_buffer);
651 var element = yield read_browser_object(I);
652 var spec = load_spec(element);
653 if (!load_spec_document(spec))
654 throw interactive_error("Element is not associated with a document.");
655 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
658 panel = create_info_panel(I.window, "download-panel",
660 element_get_operation_label(element, "Saving"),
661 load_spec_uri_string(spec)],
662 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
665 var file = yield I.minibuffer.read_file_check_overwrite(
666 $prompt = "Save page as:",
668 $initial_value = suggested_path);
673 save_uri(spec, file, $buffer = I.buffer);
675 $browser_object = browser_object_frames);
678 interactive("save-page-as-text",
679 "Save a page as plain text.",
681 check_buffer(I.buffer, content_buffer);
682 var element = yield read_browser_object(I);
683 var spec = load_spec(element);
685 if (!(doc = load_spec_document(spec)))
686 throw interactive_error("Element is not associated with a document.");
687 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
690 panel = create_info_panel(I.window, "download-panel",
692 element_get_operation_label(element, "Saving", "as text"),
693 load_spec_uri_string(spec)],
694 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
697 var file = yield I.minibuffer.read_file_check_overwrite(
698 $prompt = "Save page as text:",
700 $initial_value = suggested_path);
705 save_document_as_text(doc, file, $buffer = I.buffer);
707 $browser_object = browser_object_frames);
710 interactive("save-page-complete",
711 "Save a page and all supporting documents, including images, css, "+
712 "and child frame documents.",
714 check_buffer(I.buffer, content_buffer);
715 var element = yield read_browser_object(I);
716 var spec = load_spec(element);
718 if (!(doc = load_spec_document(spec)))
719 throw interactive_error("Element is not associated with a document.");
720 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
723 panel = create_info_panel(I.window, "download-panel",
725 element_get_operation_label(element, "Saving complete"),
726 load_spec_uri_string(spec)],
727 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
730 var file = yield I.minibuffer.read_file_check_overwrite(
731 $prompt = "Save page complete:",
733 $initial_value = suggested_path);
734 // FIXME: use proper read function
735 var dir = yield I.minibuffer.read_file(
736 $prompt = "Data Directory:",
738 $initial_value = file.path + ".support");
743 save_document_complete(doc, file, dir, $buffer = I.buffer);
745 $browser_object = browser_object_frames);
748 function view_as_mime_type (I, target) {
750 var element = yield read_browser_object(I);
751 var spec = load_spec(element);
754 target = FOLLOW_CURRENT_FRAME;
756 if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
757 throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
761 var mime_type = load_spec_mime_type(spec);
762 panel = create_info_panel(I.window, "download-panel",
764 element_get_operation_label(element, "View in browser"),
765 load_spec_uri_string(spec)],
766 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
770 let suggested_type = mime_type;
771 if (gecko_viewable_mime_type_list.indexOf(suggested_type) == -1)
772 suggested_type = "text/plain";
773 mime_type = yield I.minibuffer.read_gecko_viewable_mime_type(
774 $prompt = "View internally as",
775 $initial_value = suggested_type,
777 override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
778 browser_object_follow(I.buffer, target, spec);
783 function view_as_mime_type_new_buffer (I) {
784 yield view_as_mime_type(I, OPEN_NEW_BUFFER);
786 function view_as_mime_type_new_window (I) {
787 yield view_as_mime_type(I, OPEN_NEW_WINDOW);
789 interactive("view-as-mime-type",
790 "Display a browser object in the browser using the specified MIME type.",
791 alternates(view_as_mime_type,
792 view_as_mime_type_new_buffer,
793 view_as_mime_type_new_window),
794 $browser_object = browser_object_frames);
797 interactive("reload-with-charset",
798 null, // TODO: Add a docstring.
800 var forced_charset = yield I.minibuffer.read($prompt = "Charset:");
801 reload(I.buffer, false, null, forced_charset);
805 "Paste the contents of the clipboard",
807 I.buffer.mark_active = false;
808 I.buffer.do_command("cmd_paste");
811 interactive("kill-region",
812 "Kill (\"cut\") the selected text.",
814 I.buffer.mark_active = false;
815 I.buffer.do_command("cmd_cut");
818 interactive("kill-ring-save",
819 "Save the region as if killed, but don't kill it.",
821 I.buffer.mark_active = false;
822 I.buffer.do_command("cmd_copy");