call_on_focused_field, modify_region: support richedit frames
[conkeror.git] / modules / text.js
blobe476c31397506c314c1b1bf806fb87b78890b076
1 /**
2  * (C) Copyright 2004-2007 Shawn Betts
3  * (C) Copyright 2007-2010 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 in_module(null);
12 /**
13  * Given a callback func and an interactive context I, call func, passing
14  * either a focused field, or the minibuffer's input element if the
15  * minibuffer is active. Afterward, call `scroll_selection_into_view' on
16  * the field. See `paste_x_primary_selection' and `open_line' for
17  * examples.
18  */
19 function call_on_focused_field (I, func, clear_mark) {
20     var m = I.window.minibuffer;
21     var s = m.current_state;
22     var e = null;
23     var frame;
24     if (m._input_mode_enabled) {
25         m._restore_normal_state();
26         e = m.input_element;
27     } else if (I.buffer.focused_element) {
28         e = I.buffer.focused_element;
29     } else if ((frame = I.buffer.focused_frame) &&
30                frame.document.designMode &&
31                frame.document.designMode == "on") {
32         e = frame.document;
33     }
34     func(e);
35     if (clear_mark) {
36         if (m._input_mode_enabled)
37             s.mark_active = false;
38         else
39             I.buffer.mark_active = false;
40     }
41     scroll_selection_into_view(e);
45 /**
46  * Replace the current region with modifier(selection). Deactivates region
47  * and sets point to the end of the inserted text.  The modifier can
48  * control the exact placement of point by returning an array
49  * [replacement, point_offset] instead of just a string.
50  */
51 function modify_region (field, modifier) {
52     if (field instanceof Ci.nsIDOMDocument ||
53         (field.getAttribute("contenteditable") == 'true'))
54     {
55         // richedit
56         var doc = (field instanceof Ci.nsIDOMDocument ?
57                    field :
58                    field.ownerDocument);
59         var win = doc.defaultView;
60         var sel = win.getSelection();
61         let replacement = modifier(win.getSelection().toString());
62         let point = null;
63         if (array_p(replacement)) {
64             point = replacement[1];
65             replacement = replacement[0];
66         } else
67             point = replacement.length;
68         doc.execCommand("insertHTML", false,
69                         html_escape(replacement)
70                             .replace(/\n/g, '<br>')
71                             .replace(/  /g, ' &nbsp;'));
72         if (point != replacement.length) {
73             sel.extend(sel.focusNode, point);
74             sel.collapse(sel.focusNode, point);
75         }
76     } else {
77         // normal text field
78         let replacement =
79             modifier(field.value.substring(field.selectionStart, field.selectionEnd));
80         let point = field.selectionStart;
81         if (array_p(replacement)) {
82             point += replacement[1];
83             replacement = replacement[0];
84         } else
85             point += replacement.length;
86         field.value =
87             field.value.substr(0, field.selectionStart) + replacement +
88             field.value.substr(field.selectionEnd);
89         field.setSelectionRange(point, point);
90     }
94 /**
95  * Takes an interactive context and a function to call with the word
96  * at point as its sole argument, and which returns a modified word.
97  */
98 //XXX: this should be implemented in terms of modify_region,
99 //     in order to work in richedit fields.
100 function modify_word_at_point (field, func) {
101     // Skip any whitespaces at point and move point to the right place.
102     var point = field.selectionStart;
103     var rest = field.value.substring(point);
105     // Skip any whitespaces.
106     for (var i = 0, rlen = rest.length; i < rlen; i++) {
107         if (" \n".indexOf(rest.charAt(i)) == -1) {
108             point += i;
109             break;
110         }
111     }
113     // Find the next whitespace, as it is the end of the word.  If no next
114     // whitespace is found, we're at the end of input.  TODO: Add "\n" support.
115     var goal = field.value.indexOf(" ", point);
116     if (goal == -1)
117         goal = field.value.length;
119     // Change the value of the text field.
120     var input = field.value;
121     field.value =
122         input.substring(0, point) +
123         func(input.substring(point, goal)) +
124         input.substring(goal);
126     // Move point.
127     field.selectionStart = goal;
128     field.selectionEnd = goal;
131 provide("text");