// Licensed Materials - Property of IBM // IBM Cognos Products: cclcore // (C) Copyright IBM Corp. 2012 // US Government Users Restricted Rights – Use, duplication or disclosure restricted // by GSA ADP Schedule Contract with IBM Corp. // // Common bidi javascript library - BidiUtils. // Singleton containing useable bidi methods. function BidiUtils(){ this.baseTextDirection = "ltr"; this.guiOrientation = "ltr"; this.productLocale = "en"; this.LRM = '\u200E'; this.RLM = '\u200F'; this.LRE = '\u202A'; this.RLE = '\u202B'; this.PDF = '\u202C'; this.ZWNJ= '\u200C'; this.BACKREF = '$1'; this.BACKREF_PDF = '\u202C$1'; this.caretPos = null; this.LTR_DIR=1; this.RTL_DIR=2; this.multiemail="multiemail"; this._K_BACK = 0x8; this._K_SHIFT = 0x10; this._K_END = 0x23; this._K_HOME = 0x24; this._K_LEFT = 0x25; this._K_RIGHT = 0x27; this._K_DELETE = 0x2e; this._K_PAGEUP = 0x21; this._K_PAGEDOWN = 0x22; this._K_UP = 0x26; this._K_DOWN = 0x28; this._K_ENTER = 0xD; this._K_ESC = 0x1B; this._K_INSERT = 0x2D; this.isIE = navigator.userAgent.indexOf("MSIE") >=0; this.isFF = navigator.userAgent.toLowerCase().indexOf("firefox") >=0; }; BidiUtils.instance = null; // This function returns the singleton instance of BidiUtils. BidiUtils.getInstance = function(){ if (BidiUtils.instance == null){ BidiUtils.instance = new BidiUtils(); } return BidiUtils.instance; }; // The function is used to set properties, needed for correct work of singleton methods. // @bTextDir: base text direction // @orient: orientation of the current page // @pLocale: the locale (product language) BidiUtils.prototype.setProperties = function(bTextDir, orient, pLocale){ if (bTextDir){ this.baseTextDirection = bTextDir; } if (orient){ this.guiOrientation = orient; } if (pLocale){ this.productLocale = pLocale; } }; // The function is used to add bidi specific handler for focus event, which in its turn // is used to add all event handlers, needed for given element // in accordance with its type and specifics. // @objOrId: element or its id. BidiUtils.prototype.attachAllListeners = function(objOrId){ var object = document.getElementById(objOrId); this._patchMethod(object,"onfocus"); }; // The function is the bidi-specific handler for focus event. // First call of this function allows to add all event handlers, needed for given element // in accordance with its type and specifics. // @object: element, whose onfocus handler should be added or updated BidiUtils.prototype.onfocus = function(object,event){ if (!object.isHandled){ object.isHandled = true; if (object.attributes["stttype"]){ object.stttype = object.attributes["stttype"].nodeValue; } this._attachListeners(object); } }; // The function is used to add for given element all bidi-specific handlers. // @object: element that we need to add bidi-specific handlers BidiUtils.prototype._attachListeners = function(object){ if (!object.stttype){ if (this.baseTextDirection == "auto"){ this._patchMethod(object,"onkeyup"); } } else { if (typeof this["_parse_" + object.stttype] !== "function"){ console.log("Can't add listeners for object, which name is " + object.name + ": parser for STT " + object.stttype + " isn't defined"); return; } this._patchMethod(object,"onkeyup"); this._patchMethod(object,"onkeydown"); this._patchMethod(object,"oncopy"); this._patchMethod(object,"onpaste"); this._patchMethod(object,"oncut"); this._patchMethod(object,"onmousedown"); } }; BidiUtils.prototype._patchMethod = function(object,method){ var oldMethod = object[method]; var self = this; object[method] = function(event){ self[method](object,event); if (oldMethod && typeof oldMethod === "function"){ oldMethod.apply(object, arguments); } }; }; // The function is the bidi-specific handler for keyup event. // @object: element, whose onkeyup handler should be added or updated // @event: keyup event object BidiUtils.prototype.onkeyup = function(object,event){ if (!object.stttype){ object.dir = this.resolveStrBtd(object.value); } else{ this._handleKey(object,event); } }; // The function is the bidi-specific handler for keyup event. // @object: element, whose onkeyup handler should be added or updated // @event: keydown event object BidiUtils.prototype.onkeydown = function(object,event){ if (!object.stttype){ return; } this._processBackspaceDelete(object,event); }; // The function is the bidi-specific handler for mousedown event. // @object: element, whose onmousedown handler should be added or updated // @event: event object BidiUtils.prototype.onmousedown = function(object,event){ if (!object.stttype){ return; } if (this.isIE){ event = window.event; } this._adjustCaret(object,event,null); }; // The function is the bidi-specific handler for copy event. // @object: element, whose oncopy handler should be added or updated // @event: event object BidiUtils.prototype.oncopy = function(object,event){ if (!object.stttype){ return; } this._processCopy(object,event); }; // The function is the bidi-specific handler for paste event. // @object: element, whose onpaste handler should be added or updated // @event: event object BidiUtils.prototype.onpaste = function(object,event){ if (!object.stttype){ return; } this._processPaste(object,event); }; // The function is the bidi-specific handler for cut event. // @object: element, whose oncut handler should be added or updated // @event: event object BidiUtils.prototype.oncut = function(object,event){ if (!object.stttype){ return; } this._processCut(object,event); }; // The function is the bidi-specific handler for submit event. // It removes bidi markers (UCC) from values of the text fields, // which are placed in the given form and contain structured text. // @form: form containing text fields, values of which should be handled BidiUtils.prototype.onsubmit = function(form){ var nType = ["input","textarea"]; var nList; for (var i=0; i<2; i++){ nList = form.getElementsByTagName(nType[i]); for (var i=0; i= 0x0600 && c <= 0x0669 || c >= 0x06fa && c <= 0x07ff || c >= 0xfb50 && c <= 0xfdff || c >= 0xfe70 && c <= 0xfefc) { return true; } else{ return false; } }; //The function checks if the character is a Hebrew character or not //@c: character to be tested. BidiUtils.prototype._isHebrewChar = function(c){ if (c >= 0x05d0 && c <= 0x05ff) return true; else return false; }; BidiUtils.prototype._isLTRChar = function(c){ if (c >= 0x0061 && c <= 0x007A || c >= 0x0041 && c <= 0x005A || c == 0x200E) return true; else return false; }; // This function checks whether character been passed as parameter is Bidi character BidiUtils.prototype._isBidiChar = function(c){ return (this._isArabicChar(c)||this._isHebrewChar(c)); }; // This function checks whether character been passed as parameter is Latin character BidiUtils.prototype._isLatinChar = function(c){ if((c > 64 && c < 91)||(c > 96 && c < 123)) return true; else return false; }; // This function checks whether character been passed as parameter is digit BidiUtils.prototype._isDigit = function(c){ if((c >= 48 && c <= 57)||(c >= 0x0660 && c <= 0x0669)) return true; else return false; }; // This function checks whether character found in str (first paramter) at position pos (second parameter) // and backwards [but not before position previousPos (third parameter)] is Bidi character. BidiUtils.prototype._isCharBeforeBiDiChar = function(str, pos, previousPos) { while (pos > 0){ if(pos == previousPos){ return false; } if(this._isBidiChar(str.charCodeAt(pos-1))){ return true; } else if(this._isLatinChar(str.charCodeAt(pos-1))){ return false; } pos--; } return false; }; // This is auxiliary function called from keyup event callback. // @object: field where the typing takes place // @event: keydown event object BidiUtils.prototype._handleKey = function(object,event){ if (!event) { event = window.event; } var code = event.keyCode; if (!code){ code = event.which; } if ((code == this._K_HOME) || (code == this._K_END) || (code == this._K_SHIFT) || (code == this._K_ENTER) || (code == this._K_INSERT) || (code == this._K_ESC)) { return; } else if ((code == this._K_UP) || (code == this._K_DOWN) || (code == this._K_PAGEDOWN) || (code == this._K_PAGEUP)) { this._adjustCaret(object,event,null); return; } var str1 = object.value; var cursorStart, cursorEnd,cursorStart2,cursorEnd2; var selection = this._getCaretPos(object,event); var selection2; if (selection) { cursorStart = selection[0]; cursorEnd = selection[1]; } if(code == this._K_LEFT){ if (this._checkMarkers(str1.charAt(cursorStart - 1)) && cursorStart == cursorEnd){ this._setSelectedRange(object, cursorStart - 1, cursorEnd - 1); } return; } else if(code == this._K_RIGHT){ if (this._checkMarkers(str1.charAt(cursorStart - 1)) && cursorStart == cursorEnd){ cursorStart = (this.isIE) ? (cursorStart + 1) : cursorStart; this._setSelectedRange(object, cursorStart, cursorStart); } return; } var useArabicRulesForMultiEmail=this._useArabicRulesForMultiEmail(object, object.value); if (useArabicRulesForMultiEmail) { var markersCount = this._markersCount(str1, cursorStart); str2 = this.removeUCCFromStr(str1) ; this._setSelectedRange(object, cursorStart - markersCount, cursorEnd - markersCount); } else { str2 = this.removeUCCFromStr(str1); } if(useArabicRulesForMultiEmail) { str2=this._handle_ArabicEmail(str2,object); var selection2 = this._getCaretPos(object,event); if (selection2) { cursorStart2 = selection2[0]; cursorEnd2 = selection2[1]; } } else { str2 = this.sttInjectUCCIntoStr(str2,object.stttype); } if(str1 != str2){ object.value = str2; if (code != this._K_DELETE && code != this._K_BACK){ if(useArabicRulesForMultiEmail) { if (!this._checkUCC(object.value.charAt(cursorEnd))){ this._setSelectedRange(object, cursorStart2+1 , cursorEnd2+1 ); } } else { if (!this._checkMarkers(object.value.charAt(cursorEnd))){ cursorStart += 1; cursorEnd += 1; } this._setSelectedRange(object, cursorStart, cursorEnd); } } else { if(useArabicRulesForMultiEmail) { this._setSelectedRange(object,cursorStart2,cursorEnd2); } else { this._setSelectedRange(object,cursorStart,cursorEnd); } } } }; // This is auxiliary function called from keydown event callback. // Prevents Bidi markers from being erased on 'delete' and 'backspace' operations // @object: field where the typing takes place // @event: keydown event object BidiUtils.prototype._processBackspaceDelete = function(object,event){ if (!event) { event = window.event; } var code = event.keyCode; if (!code){ code = event.which; } if(code != this._K_DELETE && code != this._K_BACK) { return; } var cursorStart, cursorEnd; var selection = this._getCaretPos(object,event); if (selection) { cursorStart = selection[0]; cursorEnd = selection[1]; var str1,str2; if(cursorStart == cursorEnd) { if(this._useArabicRulesForMultiEmail(object, object.value)) { if ((code == this._K_DELETE) && (this._checkUCC(object.value.charAt(cursorEnd))) && (cursorEnd < object.value.length - 1)) { object.value = object.value.substring(0, cursorEnd) + object.value.substring(cursorEnd + 1); this._setSelectedRange(object, cursorEnd, cursorEnd); } else if ((code == this._K_BACK) && (this._checkUCC(object.value.charAt(cursorEnd - 1)))) { object.value = object.value.substring(0, cursorEnd - 1) + object.value.substring(cursorEnd); this._setSelectedRange(object, cursorEnd - 1, cursorEnd - 1); } } else { if ((code == this._K_DELETE) && (this._checkMarkers(object.value.charAt(cursorEnd))) && (cursorEnd < object.value.length - 1)) { object.value = object.value.substring(0, cursorEnd) + object.value.substring(cursorEnd + 1); this._setSelectedRange(object, cursorEnd, cursorEnd); } else if ((code == this._K_BACK) && (this._checkMarkers(object.value.charAt(cursorEnd - 1)))) { object.value = object.value.substring(0, cursorEnd - 1) + object.value.substring(cursorEnd); this._setSelectedRange(object, cursorEnd - 1, cursorEnd - 1); } } } } }; // This is auxiliary function called from different event callbacks. // Ensures that caret placement doesn't lead to erasing Bidi marker. // @object: field where the operation takes place // @event: event object // @caretPosition: current caret position BidiUtils.prototype._adjustCaret = function(object,event,caretPosition){ if(caretPosition || (event.button != 2)){ var selection = (caretPosition) ? caretPosition : this._getCaretPos(object,event); if (selection){ if (selection[0] == selection[1]) { if(this._checkMarkers(object.value.charAt(selection[1] - 1))) { selection[1] += 1; selection[0] = null; } if(caretPosition || !selection[0]) { this._setSelectedRange(object, selection[1], selection[1]); } } } } }; // This is auxiliary function called from 'copy' event callback // in order to ensure correct STT processing // @object: field where the operation takes place // @event: event object BidiUtils.prototype._processCopy = function(object,event){ var text = ""; try{ if (this.isIE) { var w = object.document.parentWindow; event = w.event; var range = object.document.selection.createRange(); text = range.text; } else { text = object.value.substring(object.selectionStart,object.selectionEnd); } var textToClipboard = this.removeUCCFromStr(text); if (window.clipboardData) { window.clipboardData.setData("Text", textToClipboard); if (this.isIE || event.returnValue){ event.returnValue = false; } else if(event.preventDefault){ event.preventDefault(); } } }catch(ex){console.log("Exception in copy");} }; // This is auxiliary function called from 'paste' event callback // in order to ensure correct STT processing // @object: field where the operation takes place // @event: event object BidiUtils.prototype._processPaste = function(object, event){ if(this.isIE) { try { var w = object.document.parentWindow; event = w.event; this.caretPos = this._getCaretPos(object,event); var range = document.selection.createRange(); var sClipboardData = window.clipboardData.getData("Text"); range.text = sClipboardData; this.caretPos[0] = this.caretPos[1] = this.caretPos[0] + this.sttInjectUCCIntoStr(sClipboardData,object.stttype).length; this.removeUCCFromObj(object); this.sttInjectUCCIntoObj(object); var thisObj = this; setTimeout(function() { thisObj._adjustCaret(object,event,thisObj.caretPos); }, 100); event.returnValue = false; }catch(ex){console.log("Exception in paste");} } }; // This is auxiliary function called from 'cut' event callback // in order to ensure correct STT processing // @object: field where the operation takes place // @event: event object BidiUtils.prototype._processCut = function(object,event){ this._processCopy(object,event); this.caretPos = this._getCaretPos(object,event); if(this.isIE) { document.selection.clear(); } else{ object.value = object.value.substring(0,object.selectionStart) + object.value.substring(object.selectionEnd); } this.caretPos[0] = this.caretPos[1] = Math.min(this.caretPos[0],this.caretPos[1]); this.removeUCCFromObj(object); this.sttInjectUCCIntoObj(object); this._adjustCaret(object,event,this.caretPos); }; // This is auxiliary function called from different event callbacks // Returns current caret position // @object: field where the typing takes place // @event: event object BidiUtils.prototype._getCaretPos = function(object,event){ if (!this.isIE) return new Array(object.selectionStart, object.selectionEnd); else { var range = document.selection.createRange().duplicate(); var rangeLength = range.text.length; range.moveStart('character', -object.value.length); return new Array(range.text.length - rangeLength, range.text.length); } }; // This is auxiliary function called from different event callbacks // Sets the selection (caret position) at selectionStart-selectionEnd BidiUtils.prototype._setSelectedRange = function(object,selectionStart,selectionEnd){ if(this.isIE) { var range = object.createTextRange(); if (range) { if (object.type == "textarea"){ range.moveToElementText(object); } else{ range.expand('textedit'); } range.collapse(); range.moveEnd('character', selectionEnd); range.moveStart('character', selectionStart); range.select(); } } else { object.selectionStart=selectionStart; object.selectionEnd=selectionEnd; } }; // This function checks whether character been passed as parameter is LRM/RLM BidiUtils.prototype._checkMarkers = function(ch) { return (ch == this.LRM || ch == this.RLM); }; //The function checks if the character at position @pos is a Unicode Control Character //@str: String to be tested. //@pos: position of character to be checked. BidiUtils.prototype._checkUCC = function(str,pos) { if (pos >= str.length){ return false; } if(pos < 0) { return false; } var c = str.charAt(pos); if (this._isUCChar(c)) { return true; } return false; }; //The function checks if the character is a Unicode Control Character // @c: character to be tested. BidiUtils.prototype._isUCChar = function(c) { if (c == this.LRM || c == this.RLM || c == this.LRE || c == this.RLE || c == this.PDF || c == this.ZWNJ){ return true; } return false; }; // The function removes bidi markers (UCC) from the string, // containing structured text. // @str: structured text, containing UCC BidiUtils.prototype.removeUCCFromStr = function(str){ if (!str) { return str; } return str.replace(/\u200E|\u200F|\u200F|\u202A|\u202B|\u200C|\u202C/g,"") }; //The function removes bidi markers (UCC) from the value //of the object, containing structured text. // @object: object, containing structured text with UCC BidiUtils.prototype.removeUCCFromObj = function(object) { if (object && object.value){ object.value = this.removeUCCFromStr(object.value); } }; // The function injects UCC into given string in accordance with the // given type of structured text. // @str: structured text without UCC // @sttType: type of the structure BidiUtils.prototype.sttInjectUCCIntoStr = function(str, sttType){ var useArabicRulesForMultiEmail = false; if (sttType == this.multiemail) { if(this._isArabicText(str)) { useArabicRulesForMultiEmail = true; } } if (sttType == "multiemail" && useArabicRulesForMultiEmail) { return this._handle_ArabicEmail_Static(str); } else if(sttType == 'crumb') { return this._processBreadCrumb(str); } else { var txt = str.replace(/\\/g,"\\\\"); txt = txt.replace(/'/g,"\\'"); var buf = str; var sPoints = this["_parse_" + sttType](txt); if (typeof sPoints == "undefined"){ console.log("Parser for STT '" + sttType + "' isn't correct"); return str; } var shift = 0; var n, preStr, postStr; for (var i = 0; i< sPoints.length; i++) { n = sPoints[i]; if(n != null){ preStr = buf.substring(0, n + shift); postStr = buf.substring(n + shift, buf.length); buf = preStr + this.LRM + postStr; shift++; } } return buf; } }; // The function injects UCC into value of the given object in accordance // with its type of structured text. // @object: object, containig structured text without UCC BidiUtils.prototype.sttInjectUCCIntoObj = function(object){ if (!object.stttype){ if (object.attributes["stttype"]){ object.stttype = object.attributes["stttype"].nodeValue; } else{ return; } } object.value = this.sttInjectUCCIntoStr(object.value, object.stttype); }; // This is auxiliary function called from _processBreadCrumb // Inserts Bidi marks in proper places of bread crumb segment BidiUtils.prototype._processSegment = function(sText) { var sDir = this.baseTextDirection; if(sDir == 'auto') { sDir = (this.resolveStrBtd(sText) ? 'rtl' : 'ltr'); } var directionMark = this.BACKREF; directionMark += (sDir == 'rtl') ? this.RLE : this.LRE; sText = sText.replace(/(^\s{0,})/,directionMark); return sText.replace(/(\s{0,}$)/,this.BACKREF_PDF); }; /** * Inserts unicode Bidi directional marks before (LRE/RLE) and after (PDF) every * subsegment and fixes the delimiter positions by strong directional mark LRM * * @param {string} sText, containing complex expression to be processed * @return {string} processed input string */ BidiUtils.prototype._processBreadCrumb = function(sText) { var sBuff = ""; var strongSeparator = (this.guiOrientation == 'ltr') ? this.LRM : this.RLM; var splitArray = sText.split(':'); if(splitArray.length > 1) { sText = splitArray[1]; sBuff = splitArray[0] + ':' + strongSeparator; } var splitArray = sText.split('>'); for (var i = 0; i < splitArray.length; i++) { splitArray[i] = this._processSegment(splitArray[i]); } sBuff += splitArray.join('>' + strongSeparator); return sBuff; }; /** * Parses string containing complex expression. * * @param {string} str, input string to be parsed * @param {string} delimiters,set of delimiters dedining the type of complex expression * @return {Array} array containing points in input string where UCC marks should be inserted */ BidiUtils.prototype._parse_complex_string = function(str, delimiters){ var sPoints = new Array(); var previous = -1; for (var i = 0; i < str.length; i++){ if ((delimiters.indexOf(str.charAt(i)) >= 0) && this._isCharBeforeBiDiChar(str,i,previous)){ previous = i; sPoints.push(i); } } return sPoints; }; /** * Parses string containing filepath complex expression. * * @param {string} str, input string to be parsed */ BidiUtils.prototype._parse_filepath = function(str){ var delimiters = "/\\."; return this._parse_complex_string(str,delimiters); }; /** * Parses string containing url complex expression. * * @param {string} str, input string to be parsed */ BidiUtils.prototype._parse_url = function(str){ var delimiters = "/.?:&=;"; return this._parse_complex_string(str,delimiters); }; /** * Parses string containing multiemail complex expression. * * @param {string} str, input string to be parsed */ BidiUtils.prototype._parse_multiemail = function(str){ var delimiters = "<>@.,;"; return this._parse_complex_string(str,delimiters); }; /** * Parses string containing email complex expression. * * @param {string} str, input string to be parsed */ BidiUtils.prototype._parse_email = function(str){ var delimiters = "<>@.,;"; var sPoints = new Array(); var previous = -1, j; for (var i = 0; i < str.length; i++){ if (str.charAt(i) == '\"') { if (this._isCharBeforeBiDiChar(str,i,previous)){ previous = i; sPoints.push(i); } i++; j = str.indexOf('\"', i); if(j >= i){ i = j; } else{ continue; } if (this._isCharBeforeBiDiChar(str,i,previous)){ previous = i; sPoints.push(i); } } if ((delimiters.indexOf(str.charAt(i)) >= 0) && this._isCharBeforeBiDiChar(str,i,previous)){ previous = i; sPoints.push(i); } } return sPoints; }; //The function counts the Unicode Control Characters before character at position @len //@str: String to be tested. //@len: number of characters to count the markers in them. BidiUtils.prototype._markersCount = function(str, len){ var chr; var markersCount = 0; var i; if (str.length < 1) return 0; for( i = 0; i < len - 1; i++) { chr = str.charAt(i); if (this._isUCChar(chr)) markersCount += 1; } return markersCount; }; //The function inserts Unicode Control characters into string containing //multiple arabic emails and modify cursor position to keep the text well //formated during editing //@sttString: String containing Email to be handled. //@object: Object holding cursor info to be handled. BidiUtils.prototype._handle_ArabicEmail = function(sttString, object){ var resultStr = ""; var chr; var lastSeparatorIndex = -1, lastDelimiterIndex = -1; var isLastRTL = false, isLastLTR = false; var currentPosition = -1, newPosition = -1; var markersCount = 0; var i, j; var selectionStart; var mailDelimiters = "@.<>"; var mailSeparators = ",;"; var mailSign = "@"; var prevEmailDir = 0; var cEmbeding, cMarker, cEmailDir = 0; var mailSignAvailable = false; var isAdded = false; var selection = this._getCaretPos(object, null); if (selection) { var selectionStart = selection[0]; var selEnd = selection[1]; } currentPosition = selectionStart; newPosition = selectionStart; markersCount = this._markersCount(sttString, currentPosition); sttString = this.removeUCCFromStr(sttString); newPosition = newPosition - markersCount; if (sttString.length > 0 && mailDelimiters.length > 0) { cMarker = this.LRM; cEmbeding = this.LRE; cEmailDir = this.LTR_DIR; resultStr = cEmbeding + resultStr; for (i = 0; i < sttString.length; i++) { chrCode = sttString.charCodeAt(i); chr = sttString.charAt(i); if (mailDelimiters.indexOf(chr) != -1) { if (mailSign.indexOf(chr) != -1) { mailSignAvailable = true; } if ((cEmailDir == this.LTR_DIR && isLastRTL) || (cEmailDir == this.RTL_DIR && isLastLTR)) { lastDelimiterIndex = resultStr.length; } } else if (mailSeparators.indexOf(chr) != -1) { mailSignAvailable = false; lastSeparatorIndex = i; resultStr = resultStr + this.PDF; lastDelimiterIndex = resultStr.length; } else if (this._isDigit(chrCode)) { if (lastDelimiterIndex != -1) { resultStr = resultStr.substring(0, lastDelimiterIndex) + cMarker + resultStr.substring(lastDelimiterIndex); lastDelimiterIndex = -1; isLastRTL = false; isLastLTR = false; } } else if (this._isBidiChar(chrCode) || this._isLTRChar(chrCode)) { if (mailSignAvailable) { var nextSeparatorIndex = this._nextSeparator(sttString, i); var mailSegment = sttString.substring(lastSeparatorIndex + 1, nextSeparatorIndex + 1); var cEmailDir = this._getArabicEmailDirection(mailSegment); if ( cEmailDir == this.RTL_DIR ) { cMarker = this.RLM; cEmbeding = this.RLE; } else { cMarker = this.LRM; cEmbeding = this.LRE; } if (prevEmailDir != cEmailDir && cEmailDir != 0) { var lastMailSeparator = this._lastMailSeparator(resultStr, resultStr.length); var tmpStr = this.removeUCCFromStr(sttString.substring(lastSeparatorIndex + 1, i + 1)); var mailSTT = this._handle_ArabicSingleEmail(tmpStr, cEmailDir); if (lastMailSeparator > 0) resultStr = resultStr.substring(0, lastMailSeparator + 1) + mailSTT.substring(0, mailSTT.length - 1); else resultStr = mailSTT.substring(0, mailSTT.length - 1); isAdded = true; } prevEmailDir = cEmailDir; } if (this._isBidiChar(chrCode)) { if ((cEmailDir == this.LTR_DIR) && lastDelimiterIndex != -1) { resultStr = resultStr.substring(0, lastDelimiterIndex) + this.LRM + resultStr.substring(lastDelimiterIndex); lastDelimiterIndex = -1; } isLastRTL = true; isLastLTR = false; } else { if ((cEmailDir == this.RTL_DIR) && lastDelimiterIndex != -1) { resultStr = resultStr.substring(0, lastDelimiterIndex) + this.RLM + resultStr.substring(lastDelimiterIndex); lastDelimiterIndex = -1; } isLastLTR = true; isLastRTL = false; } } if (isAdded) { isAdded = false; } else { resultStr = resultStr + chr; } if (mailSeparators.indexOf(chr) != -1) { resultStr = resultStr + cEmbeding; } } resultStr = resultStr + this.PDF; } var newPosition=0; var j = currentPosition; while(newPosition < j && newPosition < resultStr.length) { if (this._isUCChar(resultStr.charAt(newPosition))) { j++; } newPosition++; } object.value=resultStr; this._setSelectedRange(object, newPosition, newPosition); return resultStr; }; //The function inserts markers into single Arabic email //@sttString: String containing email. //@dir: base text direction of the email. BidiUtils.prototype._handle_ArabicSingleEmail = function(sttString, dir){ var resultStr = ""; var chr; var lastDelimiterIndex = -1; var isLastRTL = false, isLastLTR = false; var cEmbeding, cMarker; var i, j; var mailDelimiters = "@.<>"; var cEmailDir = 0; cEmailDir = dir; if ( cEmailDir == this.RTL_DIR ) { cMarker = this.RLM; cEmbeding = this.RLE; } else { cMarker = this.LRM; cEmbeding = this.LRE; } if (sttString.length > 0 && mailDelimiters.length > 0) { for (i = 0; i < sttString.length; i++) { chrCode = sttString.charCodeAt(i); chr = sttString.charAt(i); if (mailDelimiters.indexOf(chr) != -1) { if ((cEmailDir == this.LTR_DIR && isLastRTL) || (cEmailDir == this.RTL_DIR && isLastLTR)) { lastDelimiterIndex = resultStr.length; } } else if (this._isDigit(chrCode)) { if (lastDelimiterIndex != -1) { resultStr = resultStr.substring(0, lastDelimiterIndex) + cMarker + resultStr.substring(lastDelimiterIndex); lastDelimiterIndex = -1; isLastRTL = false; isLastLTR = false; } } else if (this._isBidiChar(chrCode) || this._isLTRChar(chrCode)) { if (this._isBidiChar(chrCode)) { if ((cEmailDir == this.LTR_DIR) && lastDelimiterIndex != -1) { resultStr = resultStr.substring(0, lastDelimiterIndex) + this.LRM + resultStr.substring(lastDelimiterIndex); lastDelimiterIndex = -1; } isLastRTL = true; isLastLTR = false; } else { if ((cEmailDir == this.RTL_DIR) && lastDelimiterIndex != -1) { resultStr = resultStr.substring(0, lastDelimiterIndex) + this.RLM + resultStr.substring(lastDelimiterIndex); lastDelimiterIndex = -1; } isLastLTR = true; isLastRTL = false; } } resultStr = resultStr + chr; } resultStr = cEmbeding + resultStr + this.PDF; } return resultStr; }; //The function inserts markers into static emails containing Arabic characters //@str: String containing email addresses. BidiUtils.prototype._handle_ArabicEmail_Static = function(str) { if (str != null && str.length > 0) { var cPosition = 0; var result = ""; var separators=";,"; var lastCharIsSeparator=false; while (cPosition < str.length) { var nextSeparatorIndex = this._nextSeparator(str, cPosition); var mail = str.substring(cPosition, nextSeparatorIndex); var dir = this._getArabicEmailDirection(mail); if (nextSeparatorIndex < str.length-1) { result = result + this._handle_ArabicSingleEmail(mail, dir) + str.charAt(nextSeparatorIndex) ; } else { if(separators.indexOf(str.charAt(nextSeparatorIndex))>-1) { lastCharIsSeparator=true; } else { lastCharIsSeparator=false; } if(lastCharIsSeparator) { result = result + this._handle_ArabicSingleEmail(mail, dir)+ str.charAt(nextSeparatorIndex) ; } else { mail=mail+str.charAt(nextSeparatorIndex); result = result + this._handle_ArabicSingleEmail(mail, dir) ; } } cPosition = nextSeparatorIndex + 1; } return result; } return str; }; //The function determines the position of the last email separator in the string //@str: String to be tested. //@pos: current position (to start searching from the character after it) BidiUtils.prototype._lastMailSeparator = function(str, start){ var lastComma = str.lastIndexOf(',', start); var lastSemiColon = str.lastIndexOf(';', start); return Math.max(lastComma, lastSemiColon, 0); }; //The function determines the position of the next email separator in the string, starting from the current position //@str: String to be tested. //@pos: current position (to start searching from the character after it) BidiUtils.prototype._nextSeparator = function(str,start){ var nextComma=str.indexOf(',',start); var len = str.length; var nextSemiColon=str.indexOf(';',start); if(nextComma<0) { nextComma=len-1; } if(nextSemiColon<0) { nextSemiColon=len-1; } return Math.min(nextComma,nextSemiColon); }; //The function checks if the string should be considered Arabic or not //The string is considered Arabic if the first Bidi character found was Arabic //@str: string to be tested. BidiUtils.prototype._isArabicText = function(str){ if(!str) { return false; } var len=str.length; if(len<1) return false; for(var i=0;i