Added API to manipulate external launchers for MIME types.
[conkeror.git] / modules / commands.js
blob8c0d2773ca29cd066021eaa164ba116cc01cedfd
1 /**
2  * (C) Copyright 2004-2007 Shawn Betts
3  * (C) Copyright 2007-2008 John J. Foerch
4  * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
5  *
6  * Use, modification, and distribution are subject to the terms specified in the
7  * COPYING file.
8 **/
10 require("content-buffer.js");
12 define_hook("quit_hook");
14 function quit ()
16     quit_hook.run();
17     var appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
18         .getService(Ci.nsIAppStartup);
19     appStartup.quit(appStartup.eAttemptQuit);
21 interactive("quit",
22             "Quit Conkeror",
23             quit);
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 interactive("make-window",
49             "Make a new window.",
50             function (I) {
51                 make_window(buffer_creator(content_buffer,
52                                            { load: homepage,
53                                              configuration: I.buffer.configuration }));
54             });
56 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             function (I) {
67                 open_in_browser(I.buffer,
68                                 I.browse_target("jsconsole"),
69                                 "chrome://global/content/console.xul");
70             });
71 default_browse_targets["jsconsole"] = "find-url-new-buffer";
73 /**
74  * Given a callback func and an interactive context I, call func, passing either
75  * a focused field, or the minibuffer's input element if the minibuffer is
76  * active. Afterward, call `ensure_index_is_visible' on the field. See
77  * `paste_x_primary_selection' and `open_line' for examples.
78  */
79 function call_on_focused_field(I, func) {
80   var m = I.window.minibuffer;
81   var s = m.current_state;
82   if (m._input_mode_enabled) {
83     m._restore_normal_state();
84     var e = m.input_element;
85   } else var e = I.buffer.focused_element;
86   func(e);
87   ensure_index_is_visible (I.window, e, e.selectionStart);
88   if (s && s.handle_input) s.handle_input(m);
91 /**
92  * Replace the current region with modifier(selection). Deactivates region and
93  * sets point to the end of the inserted text, unless keep_point is true, in
94  * which case the point will be left at the beginning of the inserted text.
95  */
96 function modify_region(field, modifier, keep_point) {
97   var replacement =
98     modifier(field.value.substring(field.selectionStart, field.selectionEnd+1));
99   var point = field.selectionStart;
100   field.value =
101     field.value.substr(0, field.selectionStart) + replacement +
102     field.value.substr(field.selectionEnd);
103   if (!keep_point) point += replacement.length;
104   field.setSelectionRange(point, point);
107 function paste_x_primary_selection (field) {
108   modify_region(field, function(str) read_from_x_primary_selection());
110 interactive (
111   "paste-x-primary-selection",
112   "Insert the contents of the X primary selection into the selected field or " +
113   "minibuffer. Deactivates the region if it is active, and leaves the point " +
114   "after the inserted text.",
115   function (I) call_on_focused_field(I, paste_x_primary_selection)
118 function open_line(field) {
119   modify_region(field, function() "\n", true);
121 interactive(
122   "open-line",
123   "If there is an active region, replace is with a newline, otherwise just " +
124   "insert a newline. In both cases leave point before the inserted newline.",
125   function (I) call_on_focused_field(I, open_line)
128 function meta_x (window, prefix, command)
130     call_interactively({window: window, prefix_argument: prefix}, command);
132 interactive("execute-extended-command",
133             "Execute a Conkeror command specified in the minibuffer.",
134             function (I) {
135                 var prefix = I.P;
136                 var prompt = "";
137                 if (prefix == null)
138                     prompt = "";
139                 else if (typeof prefix == "object")
140                     prompt = prefix[0] == 4 ? "C-u " : prefix[0] + " ";
141                 else
142                     prompt = prefix + " ";
143                 meta_x(I.window, I.P,
144                        (yield I.minibuffer.read_command(
145                            $prompt = "M-x" + prompt)));
146             });
148 /// built in commands
149 // see: http://www.xulplanet.com/tutorials/xultu/commandupdate.html
151 // Performs a command on a browser buffer content area
154 define_builtin_commands(
155     "",
156     function (I, command) {
157         var buffer = I.buffer;
158         try {
159             buffer.do_command(command);
160         } catch (e) {
161             /* Ignore exceptions */
162         }
163     },
164     function (I) {
165         I.buffer.mark_active = !I.buffer.mark_active;
166     },
167     function (I) I.buffer.mark_active,
168     false
171 define_builtin_commands(
172     "caret-",
173     function (I, command) {
174         var buffer = I.buffer;
175         try {
176             buffer.do_command(command);
177         } catch (e) {
178             /* Ignore exceptions */
179         }
180     },
181     function (I) {
182         I.buffer.mark_active = !I.buffer.mark_active;
183     },
184     function (I) I.buffer.mark_active,
185     'caret');
187 function get_link_text()
189     var e = document.commandDispatcher.focusedElement;
190     if (e && e.getAttribute("href")) {
191         return e.getAttribute("href");
192     }
193     return null;
198 function copy_email_address (loc)
200     // Copy the comma-separated list of email addresses only.
201     // There are other ways of embedding email addresses in a mailto:
202     // link, but such complex parsing is beyond us.
203     var qmark = loc.indexOf( "?" );
204     var addresses;
206     if ( qmark > 7 ) {                   // 7 == length of "mailto:"
207         addresses = loc.substring( 7, qmark );
208     } else {
209         addresses = loc.substr( 7 );
210     }
212     //XXX: the original code, which we got from firefox, unescapes the string
213     //     using the current character set.  To do this in conkeror, we
214     //     *should* use an interactive method that gives us the character set,
215     //     rather than fetching it by side-effect.
217     //     // Let's try to unescape it using a character set
218     //     // in case the address is not ASCII.
219     //     try {
220     //         var characterSet = this.target.ownerDocument.characterSet;
221     //         const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
222     //             .getService(Components.interfaces.nsITextToSubURI);
223     //         addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
224     //     }
225     //     catch(ex) {
226     //         // Do nothing.
227     //     }
229     writeToClipboard(addresses);
230     message("Copied '" + addresses + "'");
232 interactive("copy-email-address", copy_email_address, ['focused_link_url']);
235 /* FIXME: fix this command */
237 interactive("source",
238             "Load a JavaScript file.",
239             function (fo) { load_rc (fo.path); }, [['f', function (a) { return "Source File: "; }, null, "source"]]);
241 function reinit (window, fn)
243     try {
244         load_rc (fn);
245         window.minibuffer.message ("Loaded: " + fn);
246     } catch (e) {
247         window.minibuffer.message ("Failed to load: "+fn);
248     }
251 interactive ("reinit",
252              "Reload the Conkeror rc file.",
253              function (I) {
254                  reinit(I.window, get_pref("conkeror.rcfile"));
255              });
257 interactive("help-page",
258             "Open the Conkeror help page.",
259             function (I) {
260                 open_in_browser(I.buffer, I.browse_target("find-url"),
261                                 "chrome://conkeror/content/help.html");
262             });
264 interactive("help-with-tutorial",
265             "Open the Conkeror tutorial.",
266             function (I) {
267                 open_in_browser(I.buffer, I.browse_target("find-url"),
268                                 "chrome://conkeror/content/tutorial.html");
269             });
271 function univ_arg_to_number(prefix, default_value)
273     if (prefix == null) {
274         if (default_value == null)
275             return 1;
276         else
277             return default_value;
278     }
279     if (typeof prefix == "object")
280         return prefix[0];
281     return prefix;
284 function eval_expression(window, s)
286     // eval in the global scope.
288     // In addition, the following variables are available:
289     // var window;
290     var buffer = window.buffers.current;
291     var result = eval(s);
292     if (result !== undefined) {
293         window.minibuffer.message(String(result));
294     }
296 interactive("eval-expression",
297             "Evaluate JavaScript statements.",
298             function (I) {
299                 eval_expression(
300                     I.window,
301                     (yield I.minibuffer.read($prompt = "Eval:",
302                                              $history = "eval-expression",
303                                              $completer = javascript_completer(I.buffer))));
304             });
307 function show_extension_manager () {
308     return conkeror.window_watcher.openWindow (
309         null,
310         "chrome://mozapps/content/extensions/extensions.xul?type=extensions",
311         "ExtensionsWindow",
312         "resizable=yes,dialog=no",
313         null);
315 interactive("extensions",
316             "Open the extensions manager in a new window.",
317             show_extension_manager);
319 function print_buffer(buffer)
321     buffer.top_frame.print();
323 interactive("print-buffer",
324             "Print the currently loaded page.",
325             function (I) {print_buffer(I.buffer);});
327 function view_partial_source (window, charset, selection) {
328     if (charset) { charset = "charset=" + charset; }
329     window.window.openDialog("chrome://global/content/viewPartialSource.xul",
330                             "_blank", "scrollbars,resizable,chrome,dialog=no",
331                             null, charset, selection, 'selection');
333 //interactive ('view-partial-source', view_partial_source, I.current_window, I.content_charset, I.content_selection);
336 function  view_mathml_source (window, charset, target) {
337     if (charset) { charset = "charset=" + charset; }
338     window.window.openDialog("chrome://global/content/viewPartialSource.xul",
339                             "_blank", "scrollbars,resizable,chrome,dialog=no",
340                             null, charset, target, 'mathml');
344 function send_key_as_event (window, element, key) {
345     key = kbd (key)[0];
346     var event = window.document.createEvent ("KeyboardEvent");
347     event.initKeyEvent (
348         "keypress",
349         true,
350         true,
351         null,
352         key.modifiers & MOD_CTRL, // ctrl
353         key.modifiers & MOD_META, // alt
354         key.modifiers & MOD_SHIFT, // shift
355         key.modifiers & MOD_META, // meta
356         key.keyCode,
357         null);    // charcode
358     // bit of a hack here.. we have to fake a keydown event for conkeror
359     window.keyboard.last_key_down_event = copy_event (event);
360     if (element) {
361         return element.dispatchEvent (event);
362     } else {
363         return window.dispatchEvent (event);
364     }
366 interactive (
367     "send-ret",
368     null,
369     function (I) {
370         send_key_as_event (I.window, I.buffer.focused_element, "return");
371     });
373 function ensure_content_focused(buffer) {
374     var foc = buffer.focused_frame_or_null;
375     if (!foc)
376         buffer.top_frame.focus();
378 interactive("ensure-content-focused", "Ensure that the content document has focus.",
379             function (I) { ensure_content_focused(I.buffer); });
381 function network_set_online_status (status) {
382     status = !status;
383     io_service.manageOfflineStatus = false;
384     io_service.offline = status;
387 interactive("network-go-online", "Work online.",
388             function (I) {network_set_online_status (true);});
389 interactive("network-go-offline", "Work offline.",
390             function (I) {network_set_online_status (false);});