1 require("content-buffer.js");
3 define_buffer_local_hook("content_buffer_input_mode_change_hook");
4 define_current_buffer_hook("current_content_buffer_input_mode_change_hook", "content_buffer_input_mode_change_hook");
6 function define_content_buffer_input_mode(base_name, keymap_name) {
7 var name = "content_buffer_" + base_name + "_input_mode";
8 buffer[name + "_enabled"] = false;
9 define_buffer_local_hook(name + "_enable_hook");
10 define_buffer_local_hook(name + "_disable_hook");
11 conkeror[name] = function (buffer) {
12 if (buffer[name + "_enabled"])
14 if (buffer.current_input_mode) {
15 conkeror[buffer.current_input_mode + "_disable_hook"].run(buffer);
16 buffer[buffer.current_input_mode + "_enabled"] = false;
18 buffer.current_input_mode = name;
19 buffer[name + "_enabled"] = true;
20 buffer.keymap = conkeror[keymap_name];
21 conkeror[name + "_enable_hook"].run(buffer);
22 content_buffer_input_mode_change_hook.run(buffer);
24 var hyphen_name = name.replace("_","-","g");
25 interactive(hyphen_name, function(I) {conkeror[name](check_buffer(I.buffer,content_buffer));});
28 define_content_buffer_input_mode("normal", "content_buffer_normal_keymap");
30 // For SELECT elements
31 define_content_buffer_input_mode("select", "content_buffer_select_keymap");
33 // For text INPUT and TEXTAREA elements
34 define_content_buffer_input_mode("text", "content_buffer_text_keymap");
35 define_content_buffer_input_mode("textarea", "content_buffer_textarea_keymap");
37 define_content_buffer_input_mode("quote_next", "content_buffer_quote_next_keymap");
38 define_content_buffer_input_mode("quote", "content_buffer_quote_keymap");
40 add_hook("content_buffer_focus_change_hook", function (buffer) {
41 var form_input_mode_enabled =
42 buffer.content_buffer_text_input_mode_enabled ||
43 buffer.content_buffer_textarea_input_mode_enabled ||
44 buffer.content_buffer_select_input_mode_enabled;
46 if (form_input_mode_enabled || buffer.content_buffer_normal_input_mode_enabled) {
47 var elem = buffer.focused_element;
50 var input_mode_function = null;
51 if (elem instanceof Ci.nsIDOMHTMLInputElement) {
52 var type = elem.getAttribute("type");
53 if (type != null) type = type.toLowerCase();
54 if (type != "radio" &&
58 input_mode_function = content_buffer_text_input_mode;
60 else if (elem instanceof Ci.nsIDOMHTMLTextAreaElement)
61 input_mode_function = content_buffer_textarea_input_mode;
63 else if (elem instanceof Ci.nsIDOMHTMLSelectElement)
64 input_mode_function = content_buffer_select_input_mode;
66 if (input_mode_function) {
67 if (browser_prevent_automatic_form_focus_mode_enabled &&
68 !form_input_mode_enabled &&
69 (buffer.last_user_input_received == null ||
70 (Date.now() - buffer.last_user_input_received)
71 > browser_automatic_form_focus_window_duration)) {
72 // Automatic focus attempt blocked
75 input_mode_function(buffer);
79 content_buffer_normal_input_mode(buffer);
83 function browser_input_minibuffer_status(window) {
85 var element = create_XUL(window, "label");
86 element.setAttribute("id", "minibuffer-input-status");
87 element.collapsed = true;
88 element.setAttribute("class", "minibuffer");
89 var insert_before = window.document.getElementById("minibuffer-prompt");
90 insert_before.parentNode.insertBefore(element, insert_before);
91 this.element = element;
92 this.select_buffer_hook_func = add_hook.call(window, "select_buffer_hook", method_caller(this, this.update));
93 this.mode_change_hook_func = add_hook.call(window, "current_content_buffer_input_mode_change_hook",
94 method_caller(this, this.update));
97 browser_input_minibuffer_status.prototype = {
98 update : function () {
99 var buf = this.window.buffers.current;
100 if (buf.content_buffer_normal_input_mode_enabled) {
101 this.element.collapsed = true;
102 this.window.minibuffer.element.className = "";
105 var className = null;
106 if (buf.content_buffer_text_input_mode_enabled) {
107 name = "[TEXT INPUT]";
109 } else if (buf.content_buffer_textarea_input_mode_enabled) {
110 name = "[TEXTAREA INPUT]";
111 className = "textarea";
112 } else if (buf.content_buffer_select_input_mode_enabled) {
113 name = "[SELECT INPUT]";
114 className = "select";
115 } else if (buf.content_buffer_quote_next_input_mode_enabled) {
116 name = "[PASS THROUGH (next)]";
117 className = "quote-next";
118 } else if (buf.content_buffer_quote_input_mode_enabled) {
119 name = "[PASS THROUGH]";
123 this.element.value = name;
124 this.element.collapsed = false;
125 this.window.minibuffer.element.className = "minibuffer-" + className + "-input-mode";
128 uninstall : function () {
129 remove_hook.call(window, "select_buffer_hook", this.select_buffer_hook_func);
130 remove_hook.call(window, "current_content_buffer_input_mode_change_hook",
131 method_caller(this, this.update), this.mode_change_hook_func);
132 this.element.parentNode.removeChild(this.element);
136 function browser_input_minibuffer_status_install(window) {
137 if (window.browser_input_minibuffer_status)
138 throw new Error("browser input minibuffer status already initialized for window");
139 window.browser_input_minibuffer_status = new browser_input_minibuffer_status(window);
142 function browser_input_minibuffer_status_uninstall(window) {
143 if (window.browser_input_minibuffer_status)
145 window.browser_input_minibuffer_status.uninstall();
146 delete window.browser_input_minibuffer_status;
149 define_global_mode("browser_input_minibuffer_status_mode",
150 function () { // enable
151 add_hook("window_initialize_hook", browser_input_minibuffer_status_install);
152 for_each_window(browser_input_minibuffer_status_install);
154 function () { // disable
155 remove_hook("window_initialize_hook", browser_input_minibuffer_status_install);
156 for_each_window(browser_input_minibuffer_status_uninstall);
158 browser_input_minibuffer_status_mode(true);
160 /* USER PREFERENCE */
162 var browser_automatic_form_focus_window_duration = 20;
164 define_global_mode("browser_prevent_automatic_form_focus_mode",
165 function () {}, // enable
166 function () {} // disable
168 browser_prevent_automatic_form_focus_mode(true);
170 var browser_form_field_xpath_expression =
172 // "translate(@type,'RADIO','radio')!='radio' and " +
173 // "translate(@type,'CHECKBOX','checkbox')!='checkbox' and " +
174 "translate(@type,'HIDEN','hiden')!='hidden'"
175 // "translate(@type,'SUBMIT','submit')!='submit' and " +
176 // "translate(@type,'REST','rest')!='reset'"
179 // "translate(@type,'RADIO','radio')!='radio' and " +
180 // "translate(@type,'CHECKBOX','checkbox')!='checkbox' and " +
181 "translate(@type,'HIDEN','hiden')!='hidden'"
182 // "translate(@type,'SUBMIT','submit')!='submit' and " +
183 // "translate(@type,'REST','rest')!='reset'"
185 "//select | //xhtml:select | " +
186 "//textarea | //xhtml:textarea | " +
187 "//textbox | //xul:textbox";
189 function browser_focus_next_form_field(buffer, count, xpath_expr) {
190 var focused_elem = buffer.focused_element;
192 return; // invalid count
194 function helper(win, skip_win) {
197 var doc = win.document;
198 var res = doc.evaluate(xpath_expr, doc, xpath_lookup_namespace,
199 Ci.nsIDOMXPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
200 null /* existing results */);
201 var length = res.snapshotLength;
204 if (focused_elem != null) {
205 for (var i = 0; i < length; ++i) {
206 if (res.snapshotItem(i) == focused_elem) {
219 index = index + count;
220 index = index % length;
224 return res.snapshotItem(index);
227 // Recurse on sub-frames
228 for (var i = 0; i < win.frames.length; ++i) {
229 var elem = helper(win.frames[i], skip_win);
236 var focused_win = buffer.focused_frame;
237 var elem = helper(focused_win, null);
239 elem = helper(buffer.top_frame, focused_win);
241 browser_element_focus(buffer, elem);
243 throw interactive_error("No form field found");
246 interactive("browser-focus-next-form-field", function (I) {
247 browser_focus_next_form_field(I.buffer, I.p, browser_form_field_xpath_expression);
250 interactive("browser-focus-previous-form-field", function (I) {
251 browser_focus_next_form_field(I.buffer, -I.p, browser_form_field_xpath_expression);
254 function edit_field_in_external_editor(buffer, elem) {
255 if (elem instanceof Ci.nsIDOMHTMLInputElement) {
256 var type = elem.getAttribute("type");
258 type = type.toLowerCase();
259 if (type == "hidden" || type == "checkbox" || type == "radio")
260 throw interactive_error("Element is not a text field.");
261 } else if (!(elem instanceof Ci.nsIDOMHTMLTextAreaElement))
262 throw interactive_error("Element is not a text field.");
264 var file = file_locator.get("TmpD", Ci.nsIFile);
265 var name = elem.getAttribute("name") || "";
266 name = name.replace(/[^a-zA-Z0-9\-_]/g, "");
267 if (name.length == 0)
271 file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
275 write_text_file(file, elem.value);
281 var old_readonly = elem.getAttribute("readonly");
282 elem.setAttribute("readonly", "true");
283 // FIXME: decide if we should do this
284 //var oldBg = elem.style.backgroundColor;
285 //elem.style.backgroundColor = "#bbbbbb";
288 elem.setAttribute("readonly", old_readonly);
290 elem.removeAttribute("readonly");
293 open_file_with_external_editor(
295 $callback = function () {
297 elem.value = read_text_file(file);
300 // FIXME: flash the textbox?
303 $failure_callback = cleanup);
305 interactive("edit-current-field-in-external-editor", function (I) {
307 edit_field_in_external_editor(buf, buf.focused_element);