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");
17 var appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
18 .getService(Ci.nsIAppStartup);
19 appStartup.quit(appStartup.eAttemptQuit);
26 function show_conkeror_version (window)
28 window.minibuffer.message (conkeror.version);
30 interactive ("conkeror-version",
31 "Show version information for Conkeror.",
32 function (I) {show_conkeror_version(I.window);});
34 /* FIXME: maybe this should be supported for non-browser buffers */
35 function scroll_horiz_complete (buffer, n)
37 var w = buffer.focused_frame;
38 w.scrollTo (n > 0 ? w.scrollMaxX : 0, w.scrollY);
40 interactive("scroll-beginning-of-line",
41 "Scroll the current frame all the way to the left.",
42 function (I) {scroll_horiz_complete(I.buffer, -1);});
44 interactive("scroll-end-of-line",
45 "Scroll the current frame all the way to the right.",
46 function (I) {scroll_horiz_complete(I.buffer, 1);});
48 function delete_window (window)
50 window.window.close();
52 interactive("delete-window",
53 "Delete the current window.",
54 function (I) {delete_window(I.window);});
56 interactive("jsconsole",
57 "Open the JavaScript console.",
58 "find-url-new-buffer",
59 $browser_object = "chrome://global/content/console.xul");
62 * Given a callback func and an interactive context I, call func, passing either
63 * a focused field, or the minibuffer's input element if the minibuffer is
64 * active. Afterward, call `ensure_index_is_visible' on the field. See
65 * `paste_x_primary_selection' and `open_line' for examples.
67 function call_on_focused_field(I, func) {
68 var m = I.window.minibuffer;
69 var s = m.current_state;
70 if (m._input_mode_enabled) {
71 m._restore_normal_state();
72 var e = m.input_element;
73 } else var e = I.buffer.focused_element;
75 ensure_index_is_visible (I.window, e, e.selectionStart);
76 if (s && s.handle_input) s.handle_input(m);
80 * Replace the current region with modifier(selection). Deactivates region and
81 * sets point to the end of the inserted text, unless keep_point is true, in
82 * which case the point will be left at the beginning of the inserted text.
84 function modify_region(field, modifier, keep_point) {
86 modifier(field.value.substring(field.selectionStart, field.selectionEnd+1));
87 var point = field.selectionStart;
89 field.value.substr(0, field.selectionStart) + replacement +
90 field.value.substr(field.selectionEnd);
91 if (!keep_point) point += replacement.length;
92 field.setSelectionRange(point, point);
95 function paste_x_primary_selection (field) {
96 modify_region(field, function(str) read_from_x_primary_selection());
99 "paste-x-primary-selection",
100 "Insert the contents of the X primary selection into the selected field or " +
101 "minibuffer. Deactivates the region if it is active, and leaves the point " +
102 "after the inserted text.",
103 function (I) call_on_focused_field(I, paste_x_primary_selection)
106 function open_line(field) {
107 modify_region(field, function() "\n", true);
112 "If there is an active region, replace is with a newline, otherwise just " +
113 "insert a newline. In both cases leave point before the inserted newline.",
114 function (I) call_on_focused_field(I, open_line)
117 function transpose_chars(field) {
118 var value = field.value;
119 var caret = field.selectionStart; // Caret position.
120 var length = value.length;
122 // If we have less than two character in the field or if we are at the
123 // beginning of the field, do nothing.
124 if (length <= 2 || caret == 0)
127 // If we are at the end of the field, switch places on the two last
128 // characters. TODO: This should happen at the end of every line, not only
129 // at the end of the field.
133 // Do the transposing.
134 field.value = switch_subarrays(value, caret - 1, caret, caret, caret + 1);
136 // Increment the caret position. If this is not done, the caret is left at
137 // the end of the field as a result of the replacing of contents.
138 field.selectionStart = caret + 1;
139 field.selectionEnd = caret + 1;
144 "Interchange characters around point, moving forward one character.",
145 function (I) call_on_focused_field(I, transpose_chars)
148 function meta_x (window, prefix, command, browser_object)
150 call_interactively({window: window,
151 prefix_argument: prefix,
152 browser_object: browser_object}, command);
154 interactive("execute-extended-command",
155 "Execute a Conkeror command specified in the minibuffer.",
158 var boc = I.browser_object;
161 prompt += ' ['+boc.name+']';
162 if (prefix !== null && prefix !== undefined) {
163 if (typeof prefix == "object")
164 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
166 prompt += " "+prefix;
168 meta_x(I.window, I.P,
169 (yield I.minibuffer.read_command(
170 $prompt = "M-x" + prompt)),
174 /// built in commands
175 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
177 // Performs a command on a browser buffer content area
180 define_builtin_commands(
182 function (I, command) {
183 var buffer = I.buffer;
185 buffer.do_command(command);
187 /* Ignore exceptions */
191 I.buffer.mark_active = !I.buffer.mark_active;
193 function (I) I.buffer.mark_active,
197 define_builtin_commands(
199 function (I, command) {
200 var buffer = I.buffer;
202 buffer.do_command(command);
204 /* Ignore exceptions */
208 I.buffer.mark_active = !I.buffer.mark_active;
210 function (I) I.buffer.mark_active,
213 function get_link_text()
215 var e = document.commandDispatcher.focusedElement;
216 if (e && e.getAttribute("href")) {
217 return e.getAttribute("href");
224 function copy_email_address (loc)
226 // Copy the comma-separated list of email addresses only.
227 // There are other ways of embedding email addresses in a mailto:
228 // link, but such complex parsing is beyond us.
229 var qmark = loc.indexOf( "?" );
232 if ( qmark > 7 ) { // 7 == length of "mailto:"
233 addresses = loc.substring( 7, qmark );
235 addresses = loc.substr( 7 );
238 //XXX: the original code, which we got from firefox, unescapes the string
239 // using the current character set. To do this in conkeror, we
240 // *should* use an interactive method that gives us the character set,
241 // rather than fetching it by side-effect.
243 // // Let's try to unescape it using a character set
244 // // in case the address is not ASCII.
246 // var characterSet = this.target.ownerDocument.characterSet;
247 // const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
248 // .getService(Components.interfaces.nsITextToSubURI);
249 // addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
255 writeToClipboard(addresses);
256 message("Copied '" + addresses + "'");
258 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
261 /* FIXME: fix this command */
263 interactive("source",
264 "Load a JavaScript file.",
265 function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
267 function reinit (window, fn)
271 window.minibuffer.message ("Loaded: " + fn);
273 window.minibuffer.message ("Failed to load: "+fn);
277 interactive ("reinit",
278 "Reload the Conkeror rc file.",
280 reinit(I.window, get_pref("conkeror.rcfile"));
283 interactive("help-page", "Open the Conkeror help page.",
284 "find-url-new-buffer",
285 $browser_object = "chrome://conkeror-help/content/help.html");
287 interactive("help-with-tutorial", "Open the Conkeror tutorial.",
288 "find-url-new-buffer",
289 $browser_object = "chrome://conkeror-help/content/tutorial.html");
291 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)
306 // eval in the global scope.
308 // In addition, the following variables are available:
310 var buffer = window.buffers.current;
311 var result = eval(s);
312 if (result !== undefined) {
313 window.minibuffer.message(String(result));
316 interactive("eval-expression",
317 "Evaluate JavaScript statements.",
321 (yield I.minibuffer.read($prompt = "Eval:",
322 $history = "eval-expression",
323 $completer = javascript_completer(I.buffer))));
327 function show_extension_manager () {
328 return conkeror.window_watcher.openWindow (
330 "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
332 "resizable=yes,dialog=no",
335 interactive("extensions",
336 "Open the extensions manager in a new window.",
337 show_extension_manager);
339 function print_buffer(buffer)
341 buffer.top_frame.print();
343 interactive("print-buffer",
344 "Print the currently loaded page.",
345 function (I) {print_buffer(I.buffer);});
347 function view_partial_source (window, charset, selection) {
348 if (charset) { charset = "charset=" + charset; }
349 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
350 "_blank", "scrollbars,resizable,chrome,dialog=no",
351 null, charset, selection, 'selection');
353 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
356 function view_mathml_source (window, charset, target) {
357 if (charset) { 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);
388 send_key_as_event(I.window, I.buffer.focused_element, "return");
391 function ensure_content_focused(buffer) {
392 var foc = buffer.focused_frame_or_null;
394 buffer.top_frame.focus();
396 interactive("ensure-content-focused", "Ensure that the content document has focus.",
397 function (I) { ensure_content_focused(I.buffer); });
399 function network_set_online_status (status) {
401 io_service.manageOfflineStatus = false;
402 io_service.offline = status;
405 interactive("network-go-online", "Work online.",
406 function (I) {network_set_online_status (true);});
407 interactive("network-go-offline", "Work offline.",
408 function (I) {network_set_online_status (false);});
411 interactive("submit-form",
412 "Submit the form to which the focused element belongs.",
414 var el = I.buffer.focused_element.parentNode;
415 while (el && el.tagName != "FORM")
422 * Browser Object Commands
424 interactive("follow", null,
425 alternates(follow, follow_new_buffer, follow_new_window),
426 $browser_object = browser_object_links);
428 interactive("follow-top", null,
429 alternates(follow_top, follow_current_frame),
430 $browser_object = browser_object_frames,
433 interactive("follow-new-buffer",
434 "Follow a link in a new buffer",
435 alternates(follow_new_buffer, follow_new_window),
436 $browser_object = browser_object_links,
439 interactive("follow-new-buffer-background",
440 "Follow a link in a new buffer in the background",
441 alternates(follow_new_buffer_background, follow_new_window),
442 $browser_object = browser_object_links,
445 interactive("follow-new-window",
446 "Follow a link in a new window",
448 $browser_object = browser_object_links,
451 interactive("find-url", "Open a URL in the current buffer",
452 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
453 $browser_object = browser_object_url);
455 interactive("find-url-new-buffer",
456 "Open a URL in a new buffer",
457 alternates(follow_new_buffer, follow_new_window),
458 $browser_object = browser_object_url,
459 $prompt = "Find url");
461 interactive("find-url-new-window", "Open a URL in a new window",
463 $browser_object = browser_object_url,
464 $prompt = "Find url");
466 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
469 define_browser_object_class(
470 "alternate-url", null, null,
471 function (I, prompt) {
472 check_buffer (I.buffer, content_buffer);
473 var result = yield I.buffer.window.minibuffer.read_url (
475 $initial_value = I.buffer.display_URI_string);
476 yield co_return (result);
478 $prompt = "Find url");
481 interactive("go-up", "Go to the parent directory of the current URL",
484 define_browser_object_class(
485 "up-url", null, null,
486 function (I, prompt) {
487 check_buffer (I.buffer, content_buffer);
488 var up = compute_url_up_path (I.buffer.current_URI.spec);
489 return I.buffer.current_URI.resolve (up);
492 interactive("make-window",
493 "Make a new window with the homepage.",
495 $browser_object = function () { return homepage; });
498 interactive("focus", null,
500 var element = yield read_browser_object(I);
501 browser_element_focus(I.buffer, element);
503 $browser_object = browser_object_frames);
505 interactive("save", null, function (I) {
506 var element = yield read_browser_object(I);
508 var spec = element_get_load_spec(element);
510 throw interactive_error("Element has no associated URI");
513 panel = create_info_panel(I.window, "download-panel",
515 element_get_operation_label(element, "Saving"),
516 load_spec_uri_string(spec)],
517 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
520 var file = yield I.minibuffer.read_file_check_overwrite(
521 $prompt = "Save as:",
522 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
533 $browser_object = browser_object_links);
536 interactive("copy", null,
538 var element = yield read_browser_object(I);
539 browser_element_copy(I.buffer, element);
541 $browser_object = browser_object_links);
543 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
544 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
545 $browser_object = browser_object_pasteurl);
547 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
548 alternates(follow_new_buffer, follow_new_window),
549 $browser_object = browser_object_pasteurl);
551 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
553 $browser_object = browser_object_pasteurl);
555 interactive("view-source", null,
556 alternates(view_source, view_source_new_buffer, view_source_new_window),
557 $browser_object = browser_object_frames);
559 interactive("shell-command-on-url", null, function (I) {
561 var element = yield read_browser_object(I);
562 var spec = element_get_load_spec(element);
564 throw interactive_error("Unable to obtain URI from element");
566 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)]]);
576 var cmd = yield I.minibuffer.read_shell_command(
578 $initial_value = load_spec_default_shell_command(spec));
583 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
585 $browser_object = browser_object_url,
586 $prompt = "Shell command");
589 interactive("shell-command-on-file", null, function (I) {
591 var element = yield read_browser_object(I);
593 var spec = element_get_load_spec(element);
595 throw interactive_error("Unable to obtain URI from element");
597 var uri = load_spec_uri_string(spec);
600 panel = create_info_panel(I.window, "download-panel",
602 element_get_operation_label(element, "Running on"),
603 load_spec_uri_string(spec)],
604 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
608 var cmd = yield I.minibuffer.read_shell_command(
610 $initial_value = load_spec_default_shell_command(spec));
615 /* FIXME: specify cwd as well */
616 yield browser_element_shell_command(I.buffer, element, cmd);
618 $browser_object = browser_object_links,
619 $prompt = "Shell command");
621 interactive("bookmark", null, function (I) {
622 var element = yield read_browser_object(I);
623 var spec = element_get_load_spec(element);
625 throw interactive_error("Element has no associated URI");
626 var uri_string = load_spec_uri_string(spec);
628 panel = create_info_panel(I.window, "bookmark-panel",
630 element_get_operation_label(element, "Bookmarking"),
633 var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
637 add_bookmark(uri_string, title);
638 I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
640 $browser_object = browser_object_frames);
642 interactive("save-page", null, function (I) {
643 check_buffer(I.buffer, content_buffer);
644 var element = yield read_browser_object(I);
645 var spec = element_get_load_spec(element);
646 if (!spec || !load_spec_document(spec))
647 throw interactive_error("Element is not associated with a document.");
648 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
651 panel = create_info_panel(I.window, "download-panel",
653 element_get_operation_label(element, "Saving"),
654 load_spec_uri_string(spec)],
655 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
658 var file = yield I.minibuffer.read_file_check_overwrite(
659 $prompt = "Save page as:",
661 $initial_value = suggested_path);
666 save_uri(spec, file, $buffer = I.buffer);
668 $browser_object = browser_object_frames);
670 interactive("save-page-as-text", null, function (I) {
671 check_buffer(I.buffer, content_buffer);
672 var element = yield read_browser_object(I);
673 var spec = element_get_load_spec(element);
675 if (!spec || !(doc = load_spec_document(spec)))
676 throw interactive_error("Element is not associated with a document.");
677 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
680 panel = create_info_panel(I.window, "download-panel",
682 element_get_operation_label(element, "Saving", "as text"),
683 load_spec_uri_string(spec)],
684 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
687 var file = yield I.minibuffer.read_file_check_overwrite(
688 $prompt = "Save page as text:",
690 $initial_value = suggested_path);
695 save_document_as_text(doc, file, $buffer = I.buffer);
697 $browser_object = browser_object_frames);
699 interactive("save-page-complete", null, function (I) {
700 check_buffer(I.buffer, content_buffer);
701 var element = yield read_browser_object(I);
702 var spec = element_get_load_spec(element);
704 if (!spec || !(doc = load_spec_document(spec)))
705 throw interactive_error("Element is not associated with a document.");
706 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
709 panel = create_info_panel(I.window, "download-panel",
711 element_get_operation_label(element, "Saving complete"),
712 load_spec_uri_string(spec)],
713 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
716 var file = yield I.minibuffer.read_file_check_overwrite(
717 $prompt = "Save page complete:",
719 $initial_value = suggested_path);
720 // FIXME: use proper read function
721 var dir = yield I.minibuffer.read_file(
722 $prompt = "Data Directory:",
724 $initial_value = file.path + ".support");
729 save_document_complete(doc, file, dir, $buffer = I.buffer);
731 $browser_object = browser_object_frames);
734 function view_as_mime_type (I, target) {
736 var element = yield read_browser_object(I);
737 var spec = element_get_load_spec(element);
740 target = FOLLOW_CURRENT_FRAME;
743 throw interactive_error("Element is not associated with a URI");
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 (gecko_viewable_mime_type_list.indexOf(suggested_type) == -1)
761 suggested_type = "text/plain";
762 mime_type = yield I.minibuffer.read_gecko_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));