Change youtube-mode and google-video-mode to set suggest_filename_from_uri = false
[conkeror.git] / modules / content-buffer-input.js
blobea863b22180eba4d4572d3c14fbc24d689432f66
1 require("content-buffer.js");
3 define_current_buffer_hook("current_buffer_input_mode_change_hook", "input_mode_change_hook");
5 var content_buffer_input_mode_keymaps = {};
7 function define_input_mode(base_name, display_name, keymap_name, doc) {
8     var name = base_name + "_input_mode";
9     content_buffer_input_mode_keymaps[name] = keymap_name;
10     define_buffer_mode(name,
11                                display_name,
12                                $class = "input_mode",
13                                $enable = function (buffer) { check_buffer(buffer, content_buffer);
14                                                      content_buffer_update_keymap_for_input_mode(buffer); },
15                                $disable = false,
16                                $doc = doc);
18 ignore_function_for_get_caller_source_code_reference("define_input_mode");
20 function content_buffer_update_keymap_for_input_mode(buffer) {
21     if (buffer.input_mode)
22         buffer.keymap = buffer.get(content_buffer_input_mode_keymaps[buffer.input_mode]);
25 add_hook("page_mode_change_hook", content_buffer_update_keymap_for_input_mode);
27 define_input_mode("normal", null, "content_buffer_normal_keymap");
29 // For SELECT elements
30 define_input_mode("select", "input:SELECT", "content_buffer_select_keymap");
32 // For text INPUT and TEXTAREA elements
33 define_input_mode("text", "input:TEXT", "content_buffer_text_keymap");
34 define_input_mode("textarea", "input:TEXTAREA", "content_buffer_textarea_keymap");
36 define_input_mode(
37     "quote_next", "input:PASS-THROUGH(next)", "content_buffer_quote_next_keymap",
38     "This input mode sends the next key combo to the buffer, "+
39         "bypassing Conkeror's normal key handling.  The mode disengages "+
40         "after one key combo.");
41 define_input_mode(
42     "quote", "input:PASS-THROUGH", "content_buffer_quote_keymap",
43     "This input mode sends all key combos to the buffer, "+
44         "bypassing Conkeror's normal key handling, until the "+
45         "Escape key is pressed.");
47 function content_buffer_update_input_mode_for_focus(buffer, force) {
48     var mode = buffer.input_mode;
49     var form_input_mode_enabled = (mode == "text_input_mode" ||
50                                                    mode == "textarea_input_mode" ||
51                                                    mode == "select_input_mode");
53     if (force || form_input_mode_enabled || mode == "normal_input_mode") {
54             var elem = buffer.focused_element;
56             if (elem) {
57                 var input_mode_function = null;
58                 if (elem instanceof Ci.nsIDOMHTMLInputElement) {
59                         var type = elem.getAttribute("type");
60                         if (type != null) type = type.toLowerCase();
61                         if (type != "radio" &&
62                             type != "checkbox" &&
63                             type != "submit" &&
64                             type != "reset")
65                             input_mode_function = text_input_mode;
66                 }
67                 else if (elem instanceof Ci.nsIDOMHTMLTextAreaElement)
68                         input_mode_function = textarea_input_mode;
70                 else if (elem instanceof Ci.nsIDOMHTMLSelectElement)
71                         input_mode_function = select_input_mode;
72             
73                 if (input_mode_function) {
74                         if (!force &&
75                     browser_prevent_automatic_form_focus_mode_enabled &&
76                             !form_input_mode_enabled &&
77                             (buffer.last_user_input_received == null ||
78                              (Date.now() - buffer.last_user_input_received)
79                              > browser_automatic_form_focus_window_duration)) {
80                             // Automatic focus attempt blocked
81                             elem.blur();
82                         } else
83                             input_mode_function(buffer);
84                         return;
85                 }
86             }
87             normal_input_mode(buffer);
88     }
91 add_hook("content_buffer_focus_change_hook", function (buf) { content_buffer_update_input_mode_for_focus(buf, false); } );
93 interactive("content-buffer-update-input-mode-for-focus", function (I) {
94     content_buffer_update_input_mode_for_focus(I.buffer, true);
95 });
97 function minibuffer_input_mode_indicator(window) {
98     this.window = window;
99     this.hook_func = method_caller(this, this.update);
100     add_hook.call(window, "select_buffer_hook", this.hook_func);
101     add_hook.call(window, "current_buffer_input_mode_change_hook", this.hook_func);
102     this.update();
105 minibuffer_input_mode_indicator.prototype = {
106     update : function () {
107         var buf = this.window.buffers.current;
108         var mode = buf.input_mode;
109         if (mode)
110             this.window.minibuffer.element.className = "minibuffer-" + buf.input_mode.replace("_","-","g");
111     },
112     uninstall : function () {
113         remove_hook.call(window, "select_buffer_hook", this.hook_func);
114         remove_hook.call(window, "current_buffer_input_mode_change_hook", this.hook_func);
115     }
118 define_global_window_mode("minibuffer_input_mode_indicator", "window_initialize_hook");
119 minibuffer_input_mode_indicator_mode(true);
121 // Milliseconds
122 define_variable("browser_automatic_form_focus_window_duration", 20, "Time window (in milliseconds) during which a form element is allowed to gain focus following a mouse click or key press, if `browser_prevent_automatic_form_focus_mode' is enabled.");;
124 define_global_mode("browser_prevent_automatic_form_focus_mode",
125                    function () {}, // enable
126                    function () {} // disable
127                    );
128 browser_prevent_automatic_form_focus_mode(true);
130 define_variable(
131     "browser_form_field_xpath_expression",
132     "//input[" + (
133 //        "translate(@type,'RADIO','radio')!='radio' and " +
134 //        "translate(@type,'CHECKBOX','checkbox')!='checkbox' and " +
135         "translate(@type,'HIDEN','hiden')!='hidden'"
136 //        "translate(@type,'SUBMIT','submit')!='submit' and " +
137 //        "translate(@type,'REST','rest')!='reset'"
138     ) +  "] | " +
139         "//xhtml:input[" + (
140 //        "translate(@type,'RADIO','radio')!='radio' and " +
141 //        "translate(@type,'CHECKBOX','checkbox')!='checkbox' and " +
142             "translate(@type,'HIDEN','hiden')!='hidden'"
143 //        "translate(@type,'SUBMIT','submit')!='submit' and " +
144 //        "translate(@type,'REST','rest')!='reset'"
145         ) +  "] |" +
146         "//select | //xhtml:select | " +
147         "//textarea | //xhtml:textarea | " +
148         "//textbox | //xul:textbox",
149     "XPath expression matching elements to be selected by `browser-focus-next-form-field' " +
150         "and `browser-focus-previous-form-field.'");
152 function browser_focus_next_form_field(buffer, count, xpath_expr) {
153     var focused_elem = buffer.focused_element;
154     if (count == 0)
155         return; // invalid count
157     function helper(win, skip_win) {
158         if (win == skip_win)
159             return null;
160         var doc = win.document;
161         var res = doc.evaluate(xpath_expr, doc, xpath_lookup_namespace,
162                                Ci.nsIDOMXPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
163                                null /* existing results */);
164         var length = res.snapshotLength;
165         if (length > 0) {
166             var index = null;
167             if (focused_elem != null) {
168                 for (var i = 0; i < length; ++i) {
169                     if (res.snapshotItem(i) == focused_elem) {
170                         index = i;
171                         break;
172                     }
173                 }
174             }
175             if (index == null) {
176                 if (count > 0)
177                     index = count - 1;
178                 else
179                     index = -count;
180             }
181             else
182                 index = index + count;
183             index = index % length;
184             if (index < 0)
185                 index += length;
187             return res.snapshotItem(index);
188         }
190         // Recurse on sub-frames
191         for (var i = 0; i < win.frames.length; ++i) {
192             var elem = helper(win.frames[i], skip_win);
193             if (elem)
194                 return elem;
195         }
196         return null;
197     }
199     var focused_win = buffer.focused_frame;
200     var elem = helper(focused_win, null);
201     if (!elem)
202         elem = helper(buffer.top_frame, focused_win);
203     if (elem) {
204         browser_element_focus(buffer, elem);
205     } else
206         throw interactive_error("No form field found");
209 interactive("browser-focus-next-form-field", "Focus the next element matching `browser_form_field_xpath_expression'.",
210             function (I) {
211                 browser_focus_next_form_field(I.buffer, I.p, browser_form_field_xpath_expression);
212             });
214 interactive("browser-focus-previous-form-field",  "Focus the previous element matching `browser_form_field_xpath_expression'.",
215             function (I) {
216                 browser_focus_next_form_field(I.buffer, -I.p, browser_form_field_xpath_expression);
219 function edit_field_in_external_editor(buffer, elem) {
220     if (elem instanceof Ci.nsIDOMHTMLInputElement) {
221         var type = elem.getAttribute("type");
222         if (type != null)
223             type = type.toLowerCase();
224         if (type == "hidden" || type == "checkbox" || type == "radio")
225             throw interactive_error("Element is not a text field.");
226     } else if (!(elem instanceof Ci.nsIDOMHTMLTextAreaElement))
227         throw interactive_error("Element is not a text field.");
229     var name = elem.getAttribute("name");
230     if (!name || name.length == 0)
231         name = elem.getAttribute("id");
232     if (!name)
233         name = "";
234     name = name.replace(/[^a-zA-Z0-9\-_]/g, "");
235     if (name.length == 0)
236         name = "text";
237     name += ".txt";
238     var file = get_temporary_file(name);
240     // Write to file
241     try {
242         write_text_file(file, elem.value);
243     } catch (e) {
244         file.remove(false);
245         throw e;
246     }
248     // FIXME: decide if we should do this
249     var old_class = elem.className;
250     elem.className = "__conkeror_textbox_edited_externally " + old_class;
252     try {
253         yield open_file_with_external_editor(file);
255         elem.value = read_text_file(file);
256     } finally {
257         elem.className = old_class;
259         file.remove(false);
260     }
262 interactive("edit-current-field-in-external-editor", "Edit the contents of the currently-focused text field in an external editor.",
263             function (I) {
264     var buf = I.buffer;
265     yield edit_field_in_external_editor(buf, buf.focused_element);
266     unfocus(buf);
269 define_variable("kill_whole_line", false, "If true, `kill-line' with no arg at beg of line kills the whole line.");
271 function cut_to_end_of_line (buffer) {
272     var elem = buffer.focused_element;
273     var st = elem.selectionStart;
274     var en = elem.selectionEnd;
275     if (st == en) {
276         // there is no selection.  set one up.
277         var eol = elem.value.indexOf ("\n", en);
278         if (eol == -1)
279         {
280             elem.selectionEnd = elem.textLength;
281         } else if (eol == st) {
282             elem.selectionEnd = eol + 1;
283         } else if (kill_whole_line &&
284                    (st == 0 || elem.value[st - 1] == "\n"))
285         {
286             elem.selectionEnd = eol + 1;
287         } else {
288             elem.selectionEnd = eol;
289         }
290     }
291     buffer.do_command ('cmd_cut');
294 interactive (
295     "cut-to-end-of-line",
296     function (I) {
297         cut_to_end_of_line (I.buffer);
298     });