2 * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
3 * (C) Copyright 2008-2009 John J. Foerch
5 * Use, modification, and distribution are subject to the terms specified in the
11 require("content-buffer.js");
14 // note: The apparent misspellings here are not a bug.
15 // see https://developer.mozilla.org/en/XPath/Functions/translate
18 "browser_form_field_xpath_expression",
20 // "translate(@type,'RADIO','radio')!='radio' and " +
21 // "translate(@type,'CHECKBOX','checkbox')!='checkbox' and " +
22 "translate(@type,'HIDEN','hiden')!='hidden'"
23 // "translate(@type,'SUBMIT','submit')!='submit' and " +
24 // "translate(@type,'REST','rest')!='reset'"
27 // "translate(@type,'RADIO','radio')!='radio' and " +
28 // "translate(@type,'CHECKBOX','checkbox')!='checkbox' and " +
29 "translate(@type,'HIDEN','hiden')!='hidden'"
30 // "translate(@type,'SUBMIT','submit')!='submit' and " +
31 // "translate(@type,'REST','rest')!='reset'"
33 "//select | //xhtml:select | " +
34 "//textarea | //xhtml:textarea | " +
35 "//textbox | //xul:textbox",
36 "XPath expression matching elements to be selected by `browser-focus-next-form-field' " +
37 "and `browser-focus-previous-form-field.'");
39 function focus_next (buffer, count, xpath_expr, name) {
40 var focused_elem = buffer.focused_element;
42 return; // invalid count
44 function helper (win, skip_win) {
47 var doc = win.document;
48 var res = doc.evaluate(xpath_expr, doc, xpath_lookup_namespace,
49 Ci.nsIDOMXPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
50 null /* existing results */);
51 var length = res.snapshotLength;
54 for (let i = 0; i < length; ++i) {
55 let elem = res.snapshotItem(i);
56 if (elem.offsetWidth == 0 ||
57 elem.offsetHeight == 0)
59 let style = win.getComputedStyle(elem, "");
60 if (style.display == "none" || style.visibility == "hidden")
62 valid_nodes.push(elem);
65 if (valid_nodes.length > 0) {
67 if (focused_elem != null)
68 index = valid_nodes.indexOf(focused_elem);
76 index = index + count;
77 index = index % valid_nodes.length;
79 index += valid_nodes.length;
81 return valid_nodes[index];
84 // Recurse on sub-frames
85 for (var i = 0, nframes = win.frames.length; i < nframes; ++i) {
86 var elem = helper(win.frames[i], skip_win);
93 var focused_win = buffer.focused_frame;
94 var elem = helper(focused_win, null);
96 // if focused_frame is top_frame, we're doing twice as much
98 elem = helper(buffer.top_frame, focused_win);
100 browser_element_focus(buffer, elem);
102 throw interactive_error("No "+name+" found");
105 interactive("browser-focus-next-form-field",
106 "Focus the next element matching "+
107 "`browser_form_field_xpath_expression'.",
109 focus_next(I.buffer, I.p,
110 browser_form_field_xpath_expression,
114 interactive("browser-focus-previous-form-field",
115 "Focus the previous element matching "+
116 "`browser_form_field_xpath_expression'.",
118 focus_next(I.buffer, -I.p,
119 browser_form_field_xpath_expression,
124 define_variable("links_xpath_expression",
125 "//*[@onclick or @onmouseover or @onmousedown or "+
126 "@onmouseup or @oncommand or @role='link'] | " +
127 "//input[not(@type='hidden')] | //a | //area | "+
128 "//iframe | //textarea | //button | //select",
129 "XPath expression matching elements to be selected by "+
130 "`focus-next-link' and `focus-previous-link.'");
132 interactive("focus-next-link",
133 "Focus the next element matching `links_xpath_expression'.",
135 focus_next(I.buffer, I.p,
136 links_xpath_expression,
140 interactive("focus-previous-link",
141 "Focus the previous element matching `links_xpath_expression'.",
143 focus_next(I.buffer, -I.p,
144 links_xpath_expression,
149 define_variable('edit_field_in_external_editor_extension', "txt",
150 "File extension for the temp files created by "+
151 "edit-current-field-in-external-editor.");
153 function get_filename_for_current_textfield (doc, elem) {
156 + ( elem.getAttribute("name")
157 || elem.getAttribute("id")
160 // get rid filesystem unfriendly chars
161 name = name.replace(doc.location.protocol, "")
162 .replace(/[^a-zA-Z0-9]+/g, "-")
163 .replace(/(^-+|-+$)/g, "")
164 + '.' + edit_field_in_external_editor_extension;
169 function edit_field_in_external_editor (buffer, elem) {
170 if (elem instanceof Ci.nsIDOMHTMLInputElement) {
171 var type = elem.getAttribute("type");
173 type = type.toLowerCase();
174 if (type == "hidden" || type == "checkbox" || type == "radio")
175 throw interactive_error("Element is not a text field.");
176 } else if (!(elem instanceof Ci.nsIDOMHTMLTextAreaElement))
177 throw interactive_error("Element is not a text field.");
179 var name = get_filename_for_current_textfield(buffer.document, elem);
180 var file = get_temporary_file(name);
184 write_text_file(file, elem.value);
190 // FIXME: decide if we should do this
191 var old_class = elem.className;
192 elem.className = "__conkeror_textbox_edited_externally " + old_class;
195 yield open_file_with_external_editor(file);
197 elem.value = read_text_file(file);
199 elem.className = old_class;
205 interactive("edit-current-field-in-external-editor",
206 "Edit the contents of the currently-focused text field in an external editor.",
209 var elem = buf.focused_element;
210 yield edit_field_in_external_editor(buf, elem);
214 define_variable("kill_whole_line", false,
215 "If true, `kill-line' with no arg at beg of line kills the whole line.");
217 function cut_to_end_of_line (buffer) {
218 var elem = buffer.focused_element;
220 var st = elem.selectionStart;
221 var en = elem.selectionEnd;
223 // there is no selection. set one up.
224 var eol = elem.value.indexOf("\n", en);
226 elem.selectionEnd = elem.textLength;
228 elem.selectionEnd = eol + 1;
229 else if (kill_whole_line &&
230 (st == 0 || elem.value[st - 1] == "\n"))
231 elem.selectionEnd = eol + 1;
233 elem.selectionEnd = eol;
235 buffer.do_command('cmd_cut');
237 /* FIXME: Make this work for richedit mode as well */
240 interactive("cut-to-end-of-line",
243 cut_to_end_of_line(I.buffer);
247 function downcase_word (I) {
248 modify_word_at_point(I, function (word) { return word.toLocaleLowerCase(); });
250 interactive("downcase-word",
251 "Convert following word to lower case, moving over.",
255 function upcase_word (I) {
256 modify_word_at_point(I, function (word) { return word.toLocaleUpperCase(); });
258 interactive("upcase-word",
259 "Convert following word to upper case, moving over.",
263 function capitalize_word (I) {
264 modify_word_at_point(I, function (word) {
265 if (word.length > 0) {
266 return word[0].toLocaleUpperCase() + word.substring(1);
271 interactive("capitalize-word",
272 "Capitalize the following word (or arg words), moving over.",
275 provide("content-buffer-input");