1 | // summary: |
---|
2 | // BiDiComplex module handles complex expression issues known when using BiDi characters |
---|
3 | // in File Paths, URLs, E-mail Address, XPATH, etc. |
---|
4 | // this module adds property listeners to the text fields to correct the text representation |
---|
5 | // in both static text and dynamic text during user input. |
---|
6 | |
---|
7 | define(["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/connect", "dojo/_base/sniff", |
---|
8 | "dojo/keys"], |
---|
9 | function(dojo, lang, arr, hub, has, keys){ |
---|
10 | dojo.experimental("dojox.string.BidiComplex"); |
---|
11 | var bdc = lang.getObject("string.BidiComplex", true, dojox); |
---|
12 | |
---|
13 | var _str0 = []; //FIXME: shared reference here among various functions means the functions can't be reused |
---|
14 | |
---|
15 | bdc.attachInput = function(/*DOMNode*/field, /*String*/pattern){ |
---|
16 | // summary: |
---|
17 | // Attach key listeners to the INPUT field to accomodate dynamic complex BiDi expressions |
---|
18 | // field: INPUT DOM node |
---|
19 | // pattern: Complex Expression Pattern type. One of "FILE_PATH", "URL", "EMAIL", "XPATH" |
---|
20 | |
---|
21 | field.alt = pattern; |
---|
22 | |
---|
23 | hub.connect(field, "onkeydown", this, "_ceKeyDown"); |
---|
24 | hub.connect(field, "onkeyup", this, "_ceKeyUp"); |
---|
25 | |
---|
26 | hub.connect(field, "oncut", this, "_ceCutText"); |
---|
27 | hub.connect(field, "oncopy", this, "_ceCopyText"); |
---|
28 | |
---|
29 | field.value = bdc.createDisplayString(field.value, field.alt); |
---|
30 | }; |
---|
31 | |
---|
32 | bdc.createDisplayString = function(/*String*/str, /*String*/pattern){ |
---|
33 | // summary: |
---|
34 | // Create the display string by adding the Unicode direction Markers |
---|
35 | // pattern: Complex Expression Pattern type. One of "FILE_PATH", "URL", "EMAIL", "XPATH" |
---|
36 | |
---|
37 | str = bdc.stripSpecialCharacters(str); |
---|
38 | var segmentsPointers = bdc._parse(str, pattern); |
---|
39 | |
---|
40 | var buf = '\u202A'/*LRE*/ + str; |
---|
41 | var shift = 1; |
---|
42 | arr.forEach(segmentsPointers, function(n){ |
---|
43 | if(n != null){ |
---|
44 | var preStr = buf.substring(0, n + shift); |
---|
45 | var postStr = buf.substring(n + shift, buf.length); |
---|
46 | buf = preStr + '\u200E'/*LRM*/ + postStr; |
---|
47 | shift++; |
---|
48 | } |
---|
49 | }); |
---|
50 | return buf; |
---|
51 | }; |
---|
52 | |
---|
53 | bdc.stripSpecialCharacters = function(str){ |
---|
54 | // summary: |
---|
55 | // removes all Unicode directional markers from the string |
---|
56 | |
---|
57 | return str.replace(/[\u200E\u200F\u202A-\u202E]/g, ""); // String |
---|
58 | }; |
---|
59 | |
---|
60 | bdc._ceKeyDown = function(event){ |
---|
61 | var elem = has("ie") ? event.srcElement : event.target; |
---|
62 | _str0 = elem.value; |
---|
63 | }; |
---|
64 | |
---|
65 | bdc._ceKeyUp = function(event){ |
---|
66 | var LRM = '\u200E'; |
---|
67 | var elem = has("ie") ? event.srcElement : event.target; |
---|
68 | |
---|
69 | var str1 = elem.value; |
---|
70 | var ieKey = event.keyCode; |
---|
71 | |
---|
72 | if((ieKey == keys.HOME) |
---|
73 | || (ieKey == keys.END) |
---|
74 | || (ieKey == keys.SHIFT)){ |
---|
75 | return; |
---|
76 | } |
---|
77 | |
---|
78 | var cursorStart, cursorEnd; |
---|
79 | var selection = bdc._getCaretPos(event, elem); |
---|
80 | if(selection){ |
---|
81 | cursorStart = selection[0]; |
---|
82 | cursorEnd = selection[1]; |
---|
83 | } |
---|
84 | |
---|
85 | //Jump over a cursor processing |
---|
86 | if(has("ie")){ |
---|
87 | var cursorStart1 = cursorStart, cursorEnd1 = cursorEnd; |
---|
88 | |
---|
89 | if(ieKey == keys.LEFT_ARROW){ |
---|
90 | if((str1.charAt(cursorEnd-1) == LRM) |
---|
91 | && (cursorStart == cursorEnd)){ |
---|
92 | bdc._setSelectedRange(elem,cursorStart - 1, cursorEnd - 1); |
---|
93 | } |
---|
94 | return; |
---|
95 | } |
---|
96 | |
---|
97 | if(ieKey == keys.RIGHT_ARROW){ |
---|
98 | if(str1.charAt(cursorEnd-1) == LRM){ |
---|
99 | cursorEnd1 = cursorEnd + 1; |
---|
100 | if(cursorStart == cursorEnd){ |
---|
101 | cursorStart1 = cursorStart + 1; |
---|
102 | } |
---|
103 | } |
---|
104 | |
---|
105 | bdc._setSelectedRange(elem, cursorStart1, cursorEnd1); |
---|
106 | return; |
---|
107 | } |
---|
108 | }else{ //Firefox |
---|
109 | if(ieKey == keys.LEFT_ARROW){ |
---|
110 | if(str1.charAt(cursorEnd-1) == LRM){ |
---|
111 | bdc._setSelectedRange(elem, cursorStart - 1, cursorEnd - 1); |
---|
112 | } |
---|
113 | return; |
---|
114 | } |
---|
115 | if(ieKey == keys.RIGHT_ARROW){ |
---|
116 | if(str1.charAt(cursorEnd-1) == LRM){ |
---|
117 | bdc._setSelectedRange(elem, cursorStart + 1, cursorEnd + 1); |
---|
118 | } |
---|
119 | return; |
---|
120 | } |
---|
121 | } |
---|
122 | |
---|
123 | var str2 = bdc.createDisplayString(str1, elem.alt); |
---|
124 | |
---|
125 | if(str1 != str2) |
---|
126 | { |
---|
127 | window.status = str1 + " c=" + cursorEnd; |
---|
128 | elem.value = str2; |
---|
129 | |
---|
130 | if((ieKey == keys.DELETE) && (str2.charAt(cursorEnd)==LRM)){ |
---|
131 | elem.value = str2.substring(0, cursorEnd) + str2.substring(cursorEnd+2, str2.length); |
---|
132 | } |
---|
133 | |
---|
134 | if(ieKey == keys.DELETE){ |
---|
135 | bdc._setSelectedRange(elem,cursorStart,cursorEnd); |
---|
136 | }else if(ieKey == keys.BACKSPACE){ |
---|
137 | if((_str0.length >= cursorEnd) && (_str0.charAt(cursorEnd-1)==LRM)){ |
---|
138 | bdc._setSelectedRange(elem, cursorStart - 1, cursorEnd - 1); |
---|
139 | }else{ |
---|
140 | bdc._setSelectedRange(elem, cursorStart, cursorEnd); |
---|
141 | } |
---|
142 | }else if(elem.value.charAt(cursorEnd) != LRM){ |
---|
143 | bdc._setSelectedRange(elem, cursorStart + 1, cursorEnd + 1); |
---|
144 | } |
---|
145 | } |
---|
146 | }; |
---|
147 | |
---|
148 | bdc._processCopy = function(elem, text, isReverse){ |
---|
149 | // summary: |
---|
150 | // This function strips the unicode directional controls when the text copied to the Clipboard |
---|
151 | |
---|
152 | if(text == null){ |
---|
153 | if(has("ie")){ |
---|
154 | var range = document.selection.createRange(); |
---|
155 | text = range.text; |
---|
156 | }else{ |
---|
157 | text = elem.value.substring(elem.selectionStart, elem.selectionEnd); |
---|
158 | } |
---|
159 | } |
---|
160 | |
---|
161 | var textToClipboard = bdc.stripSpecialCharacters(text); |
---|
162 | |
---|
163 | if(has("ie")){ |
---|
164 | window.clipboardData.setData("Text", textToClipboard); |
---|
165 | } |
---|
166 | return true; |
---|
167 | }; |
---|
168 | |
---|
169 | bdc._ceCopyText = function(elem){ |
---|
170 | if(has("ie")){ |
---|
171 | elem.returnValue = false; |
---|
172 | } |
---|
173 | return bdc._processCopy(elem, null, false); |
---|
174 | }; |
---|
175 | |
---|
176 | bdc._ceCutText = function(elem){ |
---|
177 | |
---|
178 | var ret = bdc._processCopy(elem, null, false); |
---|
179 | if(!ret){ |
---|
180 | return false; |
---|
181 | } |
---|
182 | |
---|
183 | if(has("ie")){ |
---|
184 | // curPos = elem.selectionStart; |
---|
185 | document.selection.clear(); |
---|
186 | }else{ |
---|
187 | var curPos = elem.selectionStart; |
---|
188 | elem.value = elem.value.substring(0, curPos) + elem.value.substring(elem.selectionEnd); |
---|
189 | elem.setSelectionRange(curPos, curPos); |
---|
190 | } |
---|
191 | |
---|
192 | return true; |
---|
193 | }; |
---|
194 | |
---|
195 | // is there dijit code to do this? |
---|
196 | bdc._getCaretPos = function(event, elem){ |
---|
197 | if(has("ie")){ |
---|
198 | var position = 0, |
---|
199 | range = document.selection.createRange().duplicate(), |
---|
200 | range2 = range.duplicate(), |
---|
201 | rangeLength = range.text.length; |
---|
202 | |
---|
203 | if(elem.type == "textarea"){ |
---|
204 | range2.moveToElementText(elem); |
---|
205 | }else{ |
---|
206 | range2.expand('textedit'); |
---|
207 | } |
---|
208 | while(range.compareEndPoints('StartToStart', range2) > 0){ |
---|
209 | range.moveStart('character', -1); |
---|
210 | ++position; |
---|
211 | } |
---|
212 | |
---|
213 | return [position, position + rangeLength]; |
---|
214 | } |
---|
215 | |
---|
216 | return [event.target.selectionStart, event.target.selectionEnd]; |
---|
217 | }; |
---|
218 | |
---|
219 | // is there dijit code to do this? |
---|
220 | bdc._setSelectedRange = function(elem,selectionStart,selectionEnd){ |
---|
221 | if(has("ie")){ |
---|
222 | var range = elem.createTextRange(); |
---|
223 | if(range){ |
---|
224 | if(elem.type == "textarea"){ |
---|
225 | range.moveToElementText(elem); |
---|
226 | }else{ |
---|
227 | range.expand('textedit'); |
---|
228 | } |
---|
229 | |
---|
230 | range.collapse(); |
---|
231 | range.moveEnd('character', selectionEnd); |
---|
232 | range.moveStart('character', selectionStart); |
---|
233 | range.select(); |
---|
234 | } |
---|
235 | }else{ |
---|
236 | elem.selectionStart = selectionStart; |
---|
237 | elem.selectionEnd = selectionEnd; |
---|
238 | } |
---|
239 | }; |
---|
240 | |
---|
241 | var _isBidiChar = function(c){ |
---|
242 | return (c >= '\u0030' && c <= '\u0039') || (c > '\u00ff'); |
---|
243 | }; |
---|
244 | |
---|
245 | var _isLatinChar = function(c){ |
---|
246 | return (c >= '\u0041' && c <= '\u005A') || (c >= '\u0061' && c <= '\u007A'); |
---|
247 | }; |
---|
248 | |
---|
249 | var _isCharBeforeBiDiChar = function(buffer, i, previous){ |
---|
250 | while(i > 0){ |
---|
251 | if(i == previous){ |
---|
252 | return false; |
---|
253 | } |
---|
254 | i--; |
---|
255 | if(_isBidiChar(buffer.charAt(i))){ |
---|
256 | return true; |
---|
257 | } |
---|
258 | if(_isLatinChar(buffer.charAt(i))){ |
---|
259 | return false; |
---|
260 | } |
---|
261 | } |
---|
262 | return false; |
---|
263 | }; |
---|
264 | |
---|
265 | |
---|
266 | bdc._parse = function(/*String*/str, /*String*/pattern){ |
---|
267 | var previous = -1, segmentsPointers = []; |
---|
268 | var delimiters = { |
---|
269 | FILE_PATH: "/\\:.", |
---|
270 | URL: "/:.?=&#", |
---|
271 | XPATH: "/\\:.<>=[]", |
---|
272 | EMAIL: "<>@.,;" |
---|
273 | }[pattern]; |
---|
274 | |
---|
275 | switch(pattern){ |
---|
276 | case "FILE_PATH": |
---|
277 | case "URL": |
---|
278 | case "XPATH": |
---|
279 | arr.forEach(str, function(ch, i){ |
---|
280 | if(delimiters.indexOf(ch) >= 0 && _isCharBeforeBiDiChar(str, i, previous)){ |
---|
281 | previous = i; |
---|
282 | segmentsPointers.push(i); |
---|
283 | } |
---|
284 | }); |
---|
285 | break; |
---|
286 | case "EMAIL": |
---|
287 | var inQuotes = false; // FIXME: unused? |
---|
288 | |
---|
289 | arr.forEach(str, function(ch, i){ |
---|
290 | if(ch== '\"'){ |
---|
291 | if(_isCharBeforeBiDiChar(str, i, previous)){ |
---|
292 | previous = i; |
---|
293 | segmentsPointers.push(i); |
---|
294 | } |
---|
295 | i++; |
---|
296 | var i1 = str.indexOf('\"', i); |
---|
297 | if(i1 >= i){ |
---|
298 | i = i1; |
---|
299 | } |
---|
300 | if(_isCharBeforeBiDiChar(str, i, previous)){ |
---|
301 | previous = i; |
---|
302 | segmentsPointers.push(i); |
---|
303 | } |
---|
304 | } |
---|
305 | |
---|
306 | if(delimiters.indexOf(ch) >= 0 && _isCharBeforeBiDiChar(str, i, previous)){ |
---|
307 | previous = i; |
---|
308 | segmentsPointers.push(i); |
---|
309 | } |
---|
310 | }); |
---|
311 | } |
---|
312 | return segmentsPointers; |
---|
313 | }; |
---|
314 | return bdc; |
---|
315 | }); |
---|