BidiComplex.js 8.1 KB

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