[483] | 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 | }); |
---|