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);
111 "If there is an active region, replace is with a newline, otherwise just " +
112 "insert a newline. In both cases leave point before the inserted newline.",
113 function (I) call_on_focused_field(I, open_line)
116 function meta_x (window, prefix, command, browser_object)
118 call_interactively({window: window,
119 prefix_argument: prefix,
120 browser_object: browser_object}, command);
122 interactive("execute-extended-command",
123 "Execute a Conkeror command specified in the minibuffer.",
126 var boc = I.browser_object;
129 prompt += ' ['+boc.name+']';
130 if (prefix !== null && prefix !== undefined) {
131 if (typeof prefix == "object")
132 prompt += prefix[0] == 4 ? " C-u" : " "+prefix[0];
134 prompt += " "+prefix;
136 meta_x(I.window, I.P,
137 (yield I.minibuffer.read_command(
138 $prompt = "M-x" + prompt)),
142 /// built in commands
143 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
145 // Performs a command on a browser buffer content area
148 define_builtin_commands(
150 function (I, command) {
151 var buffer = I.buffer;
153 buffer.do_command(command);
155 /* Ignore exceptions */
159 I.buffer.mark_active = !I.buffer.mark_active;
161 function (I) I.buffer.mark_active,
165 define_builtin_commands(
167 function (I, command) {
168 var buffer = I.buffer;
170 buffer.do_command(command);
172 /* Ignore exceptions */
176 I.buffer.mark_active = !I.buffer.mark_active;
178 function (I) I.buffer.mark_active,
181 function get_link_text()
183 var e = document.commandDispatcher.focusedElement;
184 if (e && e.getAttribute("href")) {
185 return e.getAttribute("href");
192 function copy_email_address (loc)
194 // Copy the comma-separated list of email addresses only.
195 // There are other ways of embedding email addresses in a mailto:
196 // link, but such complex parsing is beyond us.
197 var qmark = loc.indexOf( "?" );
200 if ( qmark > 7 ) { // 7 == length of "mailto:"
201 addresses = loc.substring( 7, qmark );
203 addresses = loc.substr( 7 );
206 //XXX: the original code, which we got from firefox, unescapes the string
207 // using the current character set. To do this in conkeror, we
208 // *should* use an interactive method that gives us the character set,
209 // rather than fetching it by side-effect.
211 // // Let's try to unescape it using a character set
212 // // in case the address is not ASCII.
214 // var characterSet = this.target.ownerDocument.characterSet;
215 // const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
216 // .getService(Components.interfaces.nsITextToSubURI);
217 // addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
223 writeToClipboard(addresses);
224 message("Copied '" + addresses + "'");
226 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
229 /* FIXME: fix this command */
231 interactive("source",
232 "Load a JavaScript file.",
233 function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
235 function reinit (window, fn)
239 window.minibuffer.message ("Loaded: " + fn);
241 window.minibuffer.message ("Failed to load: "+fn);
245 interactive ("reinit",
246 "Reload the Conkeror rc file.",
248 reinit(I.window, get_pref("conkeror.rcfile"));
251 interactive("help-page", "Open the Conkeror help page.",
252 "find-url-new-buffer",
253 $browser_object = "chrome://conkeror/content/help.html");
255 interactive("help-with-tutorial", "Open the Conkeror tutorial.",
256 "find-url-new-buffer",
257 $browser_object = "chrome://conkeror/content/tutorial.html");
259 function univ_arg_to_number(prefix, default_value)
261 if (prefix == null) {
262 if (default_value == null)
265 return default_value;
267 if (typeof prefix == "object")
272 function eval_expression(window, s)
274 // eval in the global scope.
276 // In addition, the following variables are available:
278 var buffer = window.buffers.current;
279 var result = eval(s);
280 if (result !== undefined) {
281 window.minibuffer.message(String(result));
284 interactive("eval-expression",
285 "Evaluate JavaScript statements.",
289 (yield I.minibuffer.read($prompt = "Eval:",
290 $history = "eval-expression",
291 $completer = javascript_completer(I.buffer))));
295 function show_extension_manager () {
296 return conkeror.window_watcher.openWindow (
298 "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
300 "resizable=yes,dialog=no",
303 interactive("extensions",
304 "Open the extensions manager in a new window.",
305 show_extension_manager);
307 function print_buffer(buffer)
309 buffer.top_frame.print();
311 interactive("print-buffer",
312 "Print the currently loaded page.",
313 function (I) {print_buffer(I.buffer);});
315 function view_partial_source (window, charset, selection) {
316 if (charset) { charset = "charset=" + charset; }
317 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
318 "_blank", "scrollbars,resizable,chrome,dialog=no",
319 null, charset, selection, 'selection');
321 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
324 function view_mathml_source (window, charset, target) {
325 if (charset) { charset = "charset=" + charset; }
326 window.window.openDialog("chrome://global/content/viewPartialSource.xul",
327 "_blank", "scrollbars,resizable,chrome,dialog=no",
328 null, charset, target, 'mathml');
332 function send_key_as_event (window, element, key) {
334 var event = window.document.createEvent ("KeyboardEvent");
340 key.modifiers & MOD_CTRL, // ctrl
341 key.modifiers & MOD_META, // alt
342 key.modifiers & MOD_SHIFT, // shift
343 key.modifiers & MOD_META, // meta
346 // bit of a hack here.. we have to fake a keydown event for conkeror
347 window.keyboard.last_key_down_event = copy_event (event);
349 return element.dispatchEvent (event);
351 return window.dispatchEvent (event);
358 send_key_as_event (I.window, I.buffer.focused_element, "return");
361 function ensure_content_focused(buffer) {
362 var foc = buffer.focused_frame_or_null;
364 buffer.top_frame.focus();
366 interactive("ensure-content-focused", "Ensure that the content document has focus.",
367 function (I) { ensure_content_focused(I.buffer); });
369 function network_set_online_status (status) {
371 io_service.manageOfflineStatus = false;
372 io_service.offline = status;
375 interactive("network-go-online", "Work online.",
376 function (I) {network_set_online_status (true);});
377 interactive("network-go-offline", "Work offline.",
378 function (I) {network_set_online_status (false);});
381 interactive("submit-form", null,
383 var el = I.buffer.focused_element.parentNode;
384 while (el && el.tagName != "FORM")
391 * Browser Object Commands
393 interactive("follow", null,
394 alternates(follow, follow_new_buffer, follow_new_window),
395 $browser_object = browser_object_links);
397 interactive("follow-top", null,
398 alternates(follow_top, follow_current_frame),
399 $browser_object = browser_object_frames,
402 interactive("follow-new-buffer",
403 "Follow a link in a new buffer",
404 alternates(follow_new_buffer, follow_new_window),
405 $browser_object = browser_object_links,
408 interactive("follow-new-buffer-background",
409 "Follow a link in a new buffer in the background",
410 alternates(follow_new_buffer_background, follow_new_window),
411 $browser_object = browser_object_links,
414 interactive("follow-new-window",
415 "Follow a link in a new window",
417 $browser_object = browser_object_links,
420 interactive("find-url", "Open a URL in the current buffer",
421 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
422 $browser_object = browser_object_url);
424 interactive("find-url-new-buffer",
425 "Open a URL in a new buffer",
426 alternates(follow_new_buffer, follow_new_window),
427 $browser_object = browser_object_url,
428 $prompt = "Find url");
430 interactive("find-url-new-window", "Open a URL in a new window",
432 $browser_object = browser_object_url,
433 $prompt = "Find url");
435 interactive("find-alternate-url", "Edit the current URL in the minibuffer",
438 define_browser_object_class(
439 "alternate-url", null, null,
440 function (I, prompt) {
441 check_buffer (I.buffer, content_buffer);
442 var result = yield I.buffer.window.minibuffer.read_url (
444 $initial_value = I.buffer.display_URI_string);
445 yield co_return (result);
447 $prompt = "Find url");
450 interactive("go-up", "Go to the parent directory of the current URL",
453 define_browser_object_class(
454 "up-url", null, null,
455 function (I, prompt) {
456 check_buffer (I.buffer, content_buffer);
457 var up = compute_url_up_path (I.buffer.current_URI.spec);
458 return I.buffer.current_URI.resolve (up);
461 interactive("make-window",
462 "Make a new window with the homepage.",
464 $browser_object = function () { return homepage; });
467 interactive("focus", null,
469 var element = yield read_browser_object(I);
470 browser_element_focus(I.buffer, element);
472 $browser_object = browser_object_frames);
474 interactive("save", null, function (I) {
475 var element = yield read_browser_object(I);
477 var spec = element_get_load_spec(element);
479 throw interactive_error("Element has no associated URI");
482 panel = create_info_panel(I.window, "download-panel",
484 element_get_operation_label(element, "Saving"),
485 load_spec_uri_string(spec)],
486 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
489 var file = yield I.minibuffer.read_file_check_overwrite(
490 $prompt = "Save as:",
491 $initial_value = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer),
502 $browser_object = browser_object_links);
505 interactive("copy", null,
507 var element = yield read_browser_object(I);
508 browser_element_copy(I.buffer, element);
510 $browser_object = browser_object_links);
512 interactive("paste-url", "Open a URL from the clipboard in the current buffer.",
513 alternates(follow_current_buffer, follow_new_buffer, follow_new_window),
514 $browser_object = browser_object_pasteurl);
516 interactive("paste-url-new-buffer", "Open a URL from the clipboard in a new buffer.",
517 alternates(follow_new_buffer, follow_new_window),
518 $browser_object = browser_object_pasteurl);
520 interactive("paste-url-new-window", "Open a URL from the clipboard in a new window.",
522 $browser_object = browser_object_pasteurl);
524 interactive("view-source", null,
525 alternates(view_source, view_source_new_buffer, view_source_new_window),
526 $browser_object = browser_object_frames);
528 interactive("shell-command-on-url", null, function (I) {
530 var element = yield read_browser_object(I);
531 var spec = element_get_load_spec(element);
533 throw interactive_error("Unable to obtain URI from element");
535 var uri = load_spec_uri_string(spec);
538 panel = create_info_panel(I.window, "download-panel",
540 element_get_operation_label(element, "Running on", "URI"),
541 load_spec_uri_string(spec)],
542 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
545 var cmd = yield I.minibuffer.read_shell_command(
547 $initial_value = load_spec_default_shell_command(spec));
552 shell_command_with_argument_blind(cmd, uri, $cwd = cwd);
554 $browser_object = browser_object_url,
555 $prompt = "Shell command");
558 interactive("shell-command-on-file", null, function (I) {
560 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"),
572 load_spec_uri_string(spec)],
573 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
577 var cmd = yield I.minibuffer.read_shell_command(
579 $initial_value = load_spec_default_shell_command(spec));
584 /* FIXME: specify cwd as well */
585 yield browser_element_shell_command(I.buffer, element, cmd);
587 $browser_object = browser_object_links,
588 $prompt = "Shell command");
590 interactive("bookmark", null, function (I) {
591 var element = yield read_browser_object(I);
592 var spec = element_get_load_spec(element);
594 throw interactive_error("Element has no associated URI");
595 var uri_string = load_spec_uri_string(spec);
597 panel = create_info_panel(I.window, "bookmark-panel",
599 element_get_operation_label(element, "Bookmarking"),
602 var title = yield I.minibuffer.read($prompt = "Bookmark with title:", $initial_value = load_spec_title(spec) || "");
606 add_bookmark(uri_string, title);
607 I.minibuffer.message("Added bookmark: " + uri_string + " - " + title);
609 $browser_object = browser_object_frames);
611 interactive("save-page", null, function (I) {
612 check_buffer(I.buffer, content_buffer);
613 var element = yield read_browser_object(I);
614 var spec = element_get_load_spec(element);
615 if (!spec || !load_spec_document(spec))
616 throw interactive_error("Element is not associated with a document.");
617 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
620 panel = create_info_panel(I.window, "download-panel",
622 element_get_operation_label(element, "Saving"),
623 load_spec_uri_string(spec)],
624 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
627 var file = yield I.minibuffer.read_file_check_overwrite(
628 $prompt = "Save page as:",
630 $initial_value = suggested_path);
635 save_uri(spec, file, $buffer = I.buffer);
637 $browser_object = browser_object_frames);
639 interactive("save-page-as-text", null, function (I) {
640 check_buffer(I.buffer, content_buffer);
641 var element = yield read_browser_object(I);
642 var spec = element_get_load_spec(element);
644 if (!spec || !(doc = load_spec_document(spec)))
645 throw interactive_error("Element is not associated with a document.");
646 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec, "txt"), I.buffer);
649 panel = create_info_panel(I.window, "download-panel",
651 element_get_operation_label(element, "Saving", "as text"),
652 load_spec_uri_string(spec)],
653 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
656 var file = yield I.minibuffer.read_file_check_overwrite(
657 $prompt = "Save page as text:",
659 $initial_value = suggested_path);
664 save_document_as_text(doc, file, $buffer = I.buffer);
666 $browser_object = browser_object_frames);
668 interactive("save-page-complete", null, function (I) {
669 check_buffer(I.buffer, content_buffer);
670 var element = yield read_browser_object(I);
671 var spec = element_get_load_spec(element);
673 if (!spec || !(doc = load_spec_document(spec)))
674 throw interactive_error("Element is not associated with a document.");
675 var suggested_path = suggest_save_path_from_file_name(suggest_file_name(spec), I.buffer);
678 panel = create_info_panel(I.window, "download-panel",
680 element_get_operation_label(element, "Saving complete"),
681 load_spec_uri_string(spec)],
682 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
685 var file = yield I.minibuffer.read_file_check_overwrite(
686 $prompt = "Save page complete:",
688 $initial_value = suggested_path);
689 // FIXME: use proper read function
690 var dir = yield I.minibuffer.read_file(
691 $prompt = "Data Directory:",
693 $initial_value = file.path + ".support");
698 save_document_complete(doc, file, dir, $buffer = I.buffer);
700 $browser_object = browser_object_frames);
703 function view_as_mime_type (I, target) {
705 var element = yield read_browser_object(I);
706 var spec = element_get_load_spec(element);
709 target = FOLLOW_CURRENT_FRAME;
712 throw interactive_error("Element is not associated with a URI");
714 if (!can_override_mime_type_for_uri(load_spec_uri(spec)))
715 throw interactive_error("Overriding the MIME type is not currently supported for non-HTTP URLs.");
719 var mime_type = load_spec_mime_type(spec);
720 panel = create_info_panel(I.window, "download-panel",
722 element_get_operation_label(element, "View in browser"),
723 load_spec_uri_string(spec)],
724 ["mime-type", "Mime type:", load_spec_mime_type(spec)]]);
728 let suggested_type = mime_type;
729 if (gecko_viewable_mime_type_list.indexOf(suggested_type) == -1)
730 suggested_type = "text/plain";
731 mime_type = yield I.minibuffer.read_gecko_viewable_mime_type(
732 $prompt = "View internally as",
733 $initial_value = suggested_type,
735 override_mime_type_for_next_load(load_spec_uri(spec), mime_type);
736 browser_object_follow(I.buffer, target, spec);
741 function view_as_mime_type_new_buffer (I) {
742 yield view_as_mime_type(I, OPEN_NEW_BUFFER);
744 function view_as_mime_type_new_window (I) {
745 yield view_as_mime_type(I, OPEN_NEW_WINDOW);
747 interactive("view-as-mime-type",
748 "Display a browser object in the browser using the specified MIME type.",
749 alternates(view_as_mime_type,
750 view_as_mime_type_new_buffer,
751 view_as_mime_type_new_window));