<@doc> Implements a predictive parser for dynamic expressions (c) SAP AG 2003-2006. All rights reserved. #INCLUDE[defs.inc] /////////////////////////////////////////////////////////////////////// // CLASS DEFINITION class DynExpParser inherit gml:Object; /////////////////////////////////////////////////////////////////////// // PROPERTIES // public properties <@doc>Gets the source expression string readonly property source = ''; <@doc>Gets the last parsing error, if any readonly property error = ''; <@doc>Gets the last parsing error, if any readonly property warning = ''; <@doc>Indicates whether the expression is empty readonly property isEmpty = true; <@doc>Indicates whether the expression is a simple literal readonly property isLiteral = false; <@doc>Indicates whether the expression is a simple field reference readonly property isField = false; <@doc>Indicates whether the expression is a foreign field reference readonly property isForeignField = false; <@doc>Indicates whether the expression is a more complex expression readonly property isComplex = false; <@doc>type of the calculated expression readonly property expressionType = ''; <@doc>cached processed funcTable readonly property funcTable={}; <@doc>list of all the rules the parser checks on the expression readonly property ruleList = null; <@doc>list of all the functions that need validation readonly virtual property funcValidationList = null; <@doc>Indicates whether the value is an expression that starts with '=' readonly property hasEqualSign = false; <@doc>Lists the supported function groups readonly property supportedFuncGroups = {s:true, n:true, d:true, t:true, c:true, q:true, b:true, m:true, o:true}; // private properties readonly property lexpos = 0; readonly property tree = null; readonly property infosetEnum = null; readonly property currInfoset = null; readonly property field = null; readonly property GIFLocations = {}; readonly property parseByID = true; readonly property transType = ''; readonly property propName = ''; readonly property element = null; readonly property expectedType = #[STRING]; readonly property checkForceExpected = false; //<@doc>list of recently used functions/operators readonly property recentDETmp = []; <@doc>constants of data type - arguments of function and return type readonly property ARG_TYPE_STRING = 's'; readonly property ARG_TYPE_OPTIONAL_LIST = '*'; readonly property ARG_TYPE_OPTIONAL = '?'; <@doc>consts of function type readonly property TOKEN_FUNC = 'FUNC'; readonly property TOKEN_FUNC_TRANSLATE = 'TRANSLATE'; readonly property TOKEN_FUNC_FORMAT = 'FORMAT'; readonly property FORMAT_OBLIGATORY_ARGS = 2; readonly property VALIDATION_ERROR = "!ERROR!"; <@doc scope="private"> Parser constructor. ================= It is possible to inherit DynExpParser and to add more parsing rules to this list under this conditions- 1) naming convension - the name should begin with is 2) the rule should be added to the end of this list constructor() //init the rules to be checked on the expression //extra rules can be added by inheriting class this.addRule(this.isWhitespace); this.addRule(this.isLeftSeparator); this.addRule(this.isRightSeparator); this.addRule(this.isComma); this.addRule(this.isBooleanRule); this.addRule(this.isDateTimeRule); this.addRule(this.isDateRule); this.addRule(this.isTimeRule); this.addRule(this.isNumberRule); this.addRule(this.isStringRule); this.addRule(this.isBOField); this.addRule(this.isFunction); this.addRule(this.isOperator); this.addFuncValidation('DATE', this.validateFuncDATE); this.addFuncValidation('TIME', this.validateFuncTIME); this.addFuncValidation('TRANSLATE', this.validateTransFunc); this.addFuncValidation('FORMAT', this.validateTransFunc); this.addFuncValidation('DVAL', this.validateFuncDVAL); this.helper = $ENV.createObject('core.dev:DEHelper', this); end <@doc scope="private"> add check rules to the list rule function method addRule(rule) if(!this.ruleList) this.ruleList=[]; this.ruleList.push(rule); end <@doc scope="private"> Add funcation validation to the list. Function validation is used to do static validation to ensure certain functions' validity, like DATE, FORMAT, etc. It is triggered AFTER the function was validated for correct number of parameters function name the corresponding validation function method addFuncValidation(fName, validationFunc) if(!this.funcValidationList) this.funcValidationList={}; this.funcValidationList[fName]=validationFunc; end <@doc scope="private"> execute functions validation function name the parsed token that contains the function method validateFunc(fName, fToken) if(!this.funcValidationList || !this.funcValidationList[fName]) return(''); var vf = this.funcValidationList[fName]; //vf holds the specific validation function return(vf.apply(this,[fToken])); //launch the function with the parsed token as input end <@doc scope="private"> validate DATE() the parsed token that contains the function method validateFuncDATE(fToken) var childs = fToken.children, childsNum = childs.length; for (var i = 0; i validate DVAL() the parsed token that contains the function method validateFuncDVAL(fToken) var childs = fToken.children, childsNum = childs.length; for (var i = 0; i validate TIME() the parsed token that contains the function method validateFuncTIME(fToken) var childs = fToken.children, childsNum = childs.length; for (var i = 0; i Sets the GIF files location, needed by the editor that uses this class The Dynmaic Expressions icons location method setGIFLocation(GIFLocations) this.GIFLocations = GIFLocations; end <@doc scope="public"> Sets the parser context object The Dynamic Expressions context object method setDynExpContext(contextObj) if(ISA(contextObj, 'dev:DynExpContext')) { this.InitContext(contextObj.infosetEnum, contextObj.infoset, contextObj.field, true, contextObj.element, contextObj.propName, contextObj.transType); this.setExpectedType(contextObj.exprType); } else #LOG[4, 'Error - Failed creating the DE context - wrong function paramter']; end <@doc scope="public"> Sets the parser context Notice! If you'll call the setParseById method - call it after setContext (or the setDynExpContext rule) because setContext will override the parseById flag The context infoset enumerator The context infoset The context field optional - true by default - if the expression is by field ID method setContext(infosetEnum, infoset, field, parseByID) this.InitContext(infosetEnum, infoset, field, parseByID); end <@doc scope="private"> init the parser context parameters - this is private method!!! for inner use. The context infoset enumerator The context infoset The context field optional - true by default - if the expression is by field ID optional - translation object which hold translation logic optional - translated property optional - translation type method InitContext(infosetEnum, infoset, field, parseByID, element, propName, transType) this.setContextInfosetEnum(infosetEnum); this.setContextInfoset(infoset); this.setContextField(field); // Reset translation paramters: this.setContextTranslation(element, propName, transType); this.parseByID = NVL(parseByID,true); end <@doc scope="public"> Sets the parser's context infoset enumerator The context infoset enumerator method setContextInfosetEnum(infosetEnum) this.infosetEnum = ISA(infosetEnum, 'dev:IInfosetEnumerator')? infosetEnum : null; end <@doc scope="public"> Sets the parser's context fields from an infoset The context infoset method setContextInfoset(infoset) this.currInfoset = ISA(infoset, 'dev:IInfoset')? infoset : null; end <@doc scope="public"> Sets the parser's context field The context field// TODO:IS THIS THE CLASS method setContextField(field) this.field = ISA(field, 'dev:IField') ? field : null; end <@doc scope="public"> Sets the property name and the translation type for the property that holds the expression. A GML element that contains the property The propertyName The type (XTIT,XBTN...) method setContextTranslation(element, propName, transType) this.element = element || null; this.propName = propName || null; // Tranalation type validation?? this.transType = transType || "XFLD"; //default type is label end <@doc scope="public"> parse the expression by the field id and not name Notice! call this method only after setContext, because setContext (or the setDynExpContext rule) will override the flag parse by name or by id method setParsingByID(bParseID) //in patterns - infoset and field are either by ID or be name // in freestyle - field in always by name, infoset is either by ID or name this.parseByID = bParseID; end <@doc scope="public"> Sets expected expression type (affects validation during parsing) One of supported data types method setExpectedType(expectedType) this.expectedType = expectedType || #[STRING]; end <@doc scope="public"> Skip checking implicit conversion or not skip checking implicit conversion or not method setCheckForceExpectedType(bool) end <@doc scope="public"> Parses a given expression The source expression string The method invokes the predictive parser and updates the parser properties according to the results. method parse(source) try { this.lexpos = 0; this.error = ''; this.warning = ''; this.tree = null; this.isEmpty = true; this.isLiteral = false; this.hasEqualSign = false; this.isField = false; this.isForeignField = false; this.isComplex = false; this.expressionType = ''; var self = this; var stack = []; //reset translation properties which are different in Ptn and FS this.resetTranslation(); //check if the expression is ready for parsing if(!this.validateSource(source)) return true; // check if the expression is dynamic expression or literal if (this.source.charAt(0) == '=') { this.source = this.source.slice(1); this.hasEqualSign = true; } else { this.isLiteral = true; if (this.expectedType == #[ANY] || this.expectedType.indexOf(#[STRING]) >= 0) { // A string literal containing symbols (e.g. @/&/....) ask the user if he intended an expression and // forgot the equal sign: this.IsContainSymbolsLiteral(this.source); if (TRIM(this.source)=="null" && this.isNull(this.source)) // special handling so that the "null" leteral // will not be converted to string by xglPrint this.tree = {token:'VAL', value:this.source, type:'u'}; else this.tree = {token:'VAL', value:this.source, type:'s'}; this.expressionType = this.tree.type; return true; } } //clear the recently used functions arr, to collect the current functions this.resetRecentDE(); var next = this.nexttoken(); expr(); if (next) throw new Error(-1, '#TEXT[XMSG_ERROR_SYNTAX_NEAR]'.replace('{0}',next.value)); this.tree = stack[0]; this.expressionType = this.tree.type; if (!this.error && this.expectedType) { var forceExpected = this.checkForceExpected && (this.tree.token=='FLD'); this.checkForceExpected = false; this.error = this.checkType(this.expectedType, this.tree.type, forceExpected); } this.isLiteral = (this.tree.token == 'VAL'); if(!this.hasEqualSign && !this.isLiteral && ! this.error) { // When not expression must be a literal: throw new Error(-1, '#TEXT[XMSG_ERROR_MISSING_EQ] '); } } catch(e) { if(this.parseByID) this.error = this.handleFieldPersistence(e.description); else this.error = e.description; } if (this.error) return false; this.isEmpty = false; this.isField = (this.tree.token == 'FLD'); this.isForeignField = (this.tree.token == 'FLD' && this.source.charAt(0)!='@'); this.isComplex = (!this.isLiteral && !this.isField); return true; function expr() { exprOR(); } function exprOR() { exprAND(); while (next && next.token == 'OR') { var temp=next; next=self.nexttoken(); exprAND(); emit(temp); } } function exprAND() { exprREL(); while (next && next.token == 'AND') { var temp=next; next=self.nexttoken(); exprREL(); emit(temp); } } function exprREL() { exprADD(); while (next && next.token == 'REL') { var temp=next; next=self.nexttoken(); exprADD(); emit(temp); } } function exprADD() { exprMUL(); while (next && next.token == 'ADD') { var temp=next; next=self.nexttoken(); exprMUL(); emit(temp); } } function exprMUL() { factor(); while (next && next.token == 'MUL') { var temp=next; next=self.nexttoken(); factor(); emit(temp); } } function factor() { switch (next && next.token) { case 'LEFT': //matchToken('LEFT'); expr(); matchToken('RIGHT'); break; var temp=next; matchToken('LEFT'); exprlist(temp); matchToken('RIGHT'); emit(temp); break; case self.TOKEN_FUNC: var temp=next; next=self.nexttoken(); matchToken('LEFT'); exprlist(temp); matchToken('RIGHT'); emit(temp); break; case 'NOT': var temp=next; next=self.nexttoken(); factor(); emit(temp); break; case 'ADD': var temp = self.lookup(next.value+'U'); if (!temp) throw new Error(-1, '#TEXT[XMSG_ERROR_INVALID_UNARY] '+next.value); next=self.nexttoken(); if(next &&(next.type == "t" || next.type == "d")) throw new Error(-1, '#TEXT[XMSG_ERROR_INVALID_UNARY] '+next.value); factor(); emit(temp); break; case 'VAL': case 'FLD': case 'ERROR': emit(next); next=self.nexttoken(); break; case 'RIGHT': throw new Error(-1, '#TEXT[XMSG_ERROR_SYNTAX_NEAR]'.replace('{0}',next.value) + '. #TEXT[XMSG_ERR_SYNTAX_NEAR_EXPECT_ARG]'); break; default: if (next) { throw new Error(-1, '#TEXT[XMSG_ERROR_SYNTAX_NEAR]'.replace('{0}',next.value)); break; } else { throw new Error(-1, '#TEXT[XMSG_ERROR_SYNTAX]'); break; } } } //eval the function's params and check that no. of params match the expected params no. function exprlist(token) { var total=token.size, count=1; var lastArg = RIGHT(token.args[token.args.length-1], 1); //if function defined as has no arguments if (total == 0) return; //special care for optional parameters function which might have no args if(next && next.token == 'RIGHT' && //function has no args total == 1 && //can have no params (defined as args[s*]) lastArg == self.ARG_TYPE_OPTIONAL_LIST) //optional params function type count = 0; else expr(); //eval the first parameter //eval rest of the function's arguments while (next && next.token == 'COMMA') { next = self.nexttoken(); expr(); count++; } //dynamic no. of params //===================== if(lastArg == self.ARG_TYPE_OPTIONAL_LIST) { token.size = count; return; } //regular function //===================== //check that the no. of params is as expected if (count > total) throw new Error(-1, '#TEXT[XMSG_ERROR_TOO_MANY_ARGS] '+token.value); if (count < total - token.optParams) throw new Error(-1, '#TEXT[XMSG_ERROR_MISSING_ARG_FUNC] '+token.value); token.size = count; //update the no. of params from definition to actual (in case of optional params) } //end exprlist function matchToken(tokenType) { if (next && next.token == tokenType) { next = self.nexttoken(); } else { //throw new Error('#TEXT[XMSG_EXPECTED] '+token); var missing = ''; if(tokenType == 'LEFT') missing = '('; else if(tokenType == 'RIGHT') missing = ')'; throw new Error('#TEXT[XMSG_ERROR_SYNTAX_NEAR]'.replace('{0}',missing) + ". #TEXT[XMSG_ERR_SYNTAX_NEAR_EXPECT_ARG]"); } } //end matchToken function emit(t) { var size = t.size; // token.size holds the actual number of arguments var argToCheck, checkTypeRetCode; if (size > 0) { t.children = new Array(size); var infiniteArg = t.args[t.args.length-1]; // emit is called only after number of function's args was tested OK for (var i=size-1, children=[]; i>=0; i--) { t.children[i] = stack.pop(); //validate that the arguments' actual type matches thier expected type argToCheck = t.args[i] || infiniteArg; // if child num > args num, additional args are of the type of the infinite arg that is always the last arg if ((checkTypeRetCode = self.checkType(argToCheck,t.children[i].type, false, "#TEXT[XMSG_EXPECTED_VALUE_ARGUMENT]"))!='') { //checkType returns an error message (or empty string if types match) self.error = checkTypeRetCode + ":#TEXT[XMSG_ARG_TYPE_MISMTACH_LOCATION]".replace(/\{0\}/g,"
- "). replace("{1}",t.value).replace("{2}",i+1).replace("{3}",self.genericPrint(true, t.children[i])); } } if (!self.error) self.error = self.validateFunc(t.value, t); } //check validation of translation macros (TRANSLATE/FORMAT) //self.validateTransFunc(t); /////////////////// QUANTITY - Check some rules: if(t.token == 'ADD' && (t.value=='+' || t.value =='-')) { // Make sure that if the 2 arguments are quantities than the type of the // opertator is overloaded to quantity: if((t.children[0].type == 'q') && (t.children[1].type == 'q')) { t.type = 'q'; } } if(t.token == 'MUL') { if(t.value=='*') { if(((t.children[0].type == 'q') || (t.children[1].type == 'q')) && ((t.children[0].type == 'n') || (t.children[1].type == 'n'))) { t.type = 'q'; } } if(t.value=='/') { if((t.children[0].type == 'q') && (t.children[1].type == 'n')) { t.type = 'q'; } } } if(t.token == 'ADD' || t.token == 'MUL' || t.token == 'REL') { var child0 = t.children[0]; var child1 = t.children[1]; if(child0 && child1 && (child0.token == 'VAL') && (child1.token == 'VAL')) { if((child0.type == 'q') && (child1.type == 'q')) { // Quantity unit comparison: if(child0.unit != child1.unit) { // The units are known but differ. This can cause undefined results in runtime: // e.g. what is the meaning of multiplying dollars and meters?? throw new Error(-1, '#TEXT[XMSG_ERROR_DIFF_QUANTITY_UNIT] '); } } } } stack.push(t); } //end emit end //of function parse() <@doc scope="public"> handle the special case of FORMAT func which have dynamic no. of arguments if parseByID=false then do not check the no. of params to FORMAT func since it has the trans key instead of the text itself Return: the expected no. of expected arguments the func handled first argument of the func handled method handleDynamicArgs(token, firstArgVal) //handle the case of FORMAT function if(token.value == this.TOKEN_FUNC_FORMAT) { //get all the {i} place holders in the string var placeHolderArray = firstArgVal.match(/{\d+}/g); var maxPlaceHolder = 0; var placeHolderLength = 0; if(placeHolderArray != null) placeHolderLength = placeHolderArray.length; // when !Display then different check of params since there's no place holders (the text replaced by trans key) //placeHolderLength>0 means there was an error and the text was not replaced by key if (!this.parseByID || placeHolderLength>0) { //get the max no. of the place holders ({}) for(i=0; i Validates the syntax of TRANSLATE/FORMAT the func handled method validateTransFunc(token) return true; end /////////////////////////////////////////////////////////////////////// // METHODS // The stream tokenizer <@doc scope="private"> Parses the nest token in the expression, check what is the next token in the expression method nexttoken() var buf=this.source, len=buf.length, lastpos=0; while (this.lexpos < len) { var tail = buf.substring(this.lexpos); var token; for(var i=0; i prepare the sent expression for parsing. if is not valid an exception will be thrown method validateSource(source) //if source is null turn it into an empty string and it it's not of type String - turn it to String obj this.source = NVL(source,'') + ''; //if expression is empty - nothing to parse - return false if(this.emptyExpression()) return false; // Quantity literals not supported yet (- May06 Mickey Hoter). if(this.expectedType == #[QUANTITY]) { throw new Error(-1, '#TEXT[XMSG_DE_QUANTITY_LITERAL] '); } //check there's no /* mark in the expression (not allowed because used for comments) if(this.source.indexOf('#[COMMENTIN]') >= 0) throw new Error(-1, '#TEXT[XMSG_ERR_COMMENT_CHAR]'.replace('{0}', '#[COMMENTIN]')); return true; end <@doc scope="private"> warn the user in case that he used method IsContainSymbolsLiteral(expr) if (expr.match(/[\!\(\)\%\&\*\+\-\/\<\=\>\|\@]/)) this.warning += '#TEXT[XMSG_FORMULA_USING_SYMBOLS] '; end <@doc scope="protected"> handle the case of empty expression. (for VC CE - no handling, for VC A1S - empty boolean expression is not allowed method emptyExpression() return !this.source; end <@doc scope="protected"> reset translation properties method resetTranslation() return; end // ===== RULES ====== // WHITESPACE method isWhitespace(expr) if (expr.match(/^\s+/)) { //this.lexpos += RegExp.lastIndex; //CHECK THIS!!!!! //expr = expr.substring(RegExp.lastIndex); this.lexpos += RegExp.lastIndex; var tkn = this.nexttoken(); if(!tkn) throw new Error(-1, '#TEXT[XMSG_ERROR_SPACE_AS_LAST_CHAR]'); return tkn; } return null; end // SEPARATOR method isLeftSeparator(expr) if (expr.charAt(0)=='(') { this.lexpos++; return {token:'LEFT', value:'', type:'a', args:['s'], size:1}; } return null; end method isRightSeparator(expr) if (expr.charAt(0)==')') { this.lexpos++; return {token:'RIGHT', value:')'}; } return null; end method isComma(expr) if (expr.charAt(0)==',') { this.lexpos++; return {token:'COMMA', value:','}; } return null; end //NadavL null literal - default implementation - needed here because isNull is now used in prase() method isNull(expr) return(false); end // BOOLEAN LITERAL <@doc scope="protected"> checks if the expression start with a boolean value expression method isBooleanRule(expr) if (this.isBoolean(expr)) { this.lexpos += RegExp.lastIndex; return {token:'VAL', value:RegExp.lastMatch, type:'b'}; } return null; end <@doc scope="public"> checks if the expression is a boolean value expression method isBoolean(expr) return (expr.match(/^(true|false)/) != null); end // STRING LITERAL <@doc scope="protected"> checks if the expression start with a string expression method isStringRule(expr) //if simple strign literal just take the epression itself if(!this.hasEqualSign && this.lexpos == 0) { this.IsContainSymbolsLiteral(expr); this.lexpos += expr.length; return {token:'VAL', value:expr, type:'s'}; } if (this.isString(expr)) { this.lexpos += RegExp.lastIndex; var str=RegExp.lastMatch; if (str.length <= 1 || str.charAt(0) != str.charAt(str.length-1)) throw new Error(-1, '#TEXT[XMSG_ERROR_UNTERMINATED_STR]'+str); str = this.normalizeStringToken(str); return {token:'VAL', value:str, type:'s'}; } return null; end <@doc scope="protected"> prepare string token expression method normalizeStringToken(str) str = str.slice(1,-1).replace(/\\\'/g,"'").replace(/\\\"/g,'"'); return str; end <@doc scope="public"> checks if the expression start with a number expression method isString(expr) return (expr.match(/^(\'(\\[\'\"\\]|[^\'\\])*(\')|\"(\\[\'\"\\]|[^\"\\])*(\"))/) != null); end // DATE LITERAL <@doc scope="protected"> checks if the expression start with a date expression method isDateRule(expr) if(this.isDate(expr)) { var dateexpr = this.formatDate(RegExp.$1, RegExp.$2, RegExp.$3); if (dateexpr == this.VALIDATION_ERROR) throw new Error(-1, '#TEXT[XMSG_INVALID_DATE]'.replace("{1}", RegExp.lastMatch)); if(dateexpr.length > 0) { this.lexpos += RegExp.lastIndex; return {token:'VAL', value:dateexpr, type:'d'}; } } return null; end <@doc scope="public"> check if expression is date expression method isDate(expr) return (expr.match(/^(\d+)[\/](\d+)[\/](\d+)/) != null); end <@doc scope="public"> check if expression is a valid date expression method isValidDate(expr) return (expr.match(/^([0]?[1-9]|[1|2][0-9]|[3][0|1])[./-]([0]?[1-9]|[1][0-2])[./-]([0-9]{4}|[0-9]{2})$/) != null); end <@doc scope="protected"> change the date to full date format day month year method formatDate(dd, mm, yy, getExprFunc) dd = parseInt(dd, 10); mm = parseInt(mm, 10); yy = parseInt(yy, 10); if (yy < 70) yy += 2000; else if (yy < 100) yy += 1900; if (mm<1 || mm>12 || dd<1 || dd>31 || (dd>30 && (mm==4||mm==6||mm==9||mm==11)) || (mm==2 && ((yy%4==0 && dd>29) || (yy%4!=0 && dd>28)))) return(this.VALIDATION_ERROR); return ('00'+dd).slice(-2)+'/'+('00'+mm).slice(-2)+'/'+yy; end // TIME LITERAL <@doc scope="protected"> checks if the expression start with a time expression method isTimeRule(expr) if(this.isTime(expr)) { var timeexpr = this.formatTime(RegExp.$1, RegExp.$2, (RegExp.$3||'').slice(1)); if (timeexpr == this.VALIDATION_ERROR) throw new Error(-1, '#TEXT[XMSG_INVALID_TIME]'.replace("{1}", RegExp.lastMatch)); if(timeexpr.length > 0) { this.lexpos += RegExp.lastIndex; return {token:'VAL', value:timeexpr, type:'t'}; } } return null; end <@doc scope="public"> checks if the expression start with a time expression method isTime(expr) return (expr.match(/^(\d+)\:(\d+)(\:\d+)?/) != null); end <@doc scope="protected"> checks validation of time expression expression method formatTime(hh, min, sec) var secLen = sec.toString().length; var minLen = min.toString().length; var hhLen = hh.toString().length; hh=parseInt(hh,10); min=parseInt(min,10); sec=parseInt(sec,10)||0; if (hh>23 || min>59 || sec>59 || secLen>2 || minLen>2 || hhLen>2) return(this.VALIDATION_ERROR); return ('00'+hh).slice(-2)+':'+('00'+min).slice(-2)+':'+('00'+sec).slice(-2); end // DATETIME LITERAL <@doc scope="protected"> checks if the expression start with a datetime expression method isDateTimeRule(expr) return null; end <@doc scope="public"> checks if the expression start with a datetime expression method isDateTime(expr) return (expr.match(/^(\d+)[\/](\d+)[\/](\d+)\s(\d+)\:(\d+)(\:\d+)?(\s([A-Z]{3}|[Z]|([+-](\d{1,2})[:](\d{1,2}))))?/) != null); end // NUMERIC LITERAL <@doc scope="protected"> checks if the expression start with a number expression method isNumberRule(expr) if (this.isLiteral) { //pure literal ==> can start with a sign if (this.isNumber(expr)) { this.lexpos += RegExp.lastIndex; return {token:'VAL', value:RegExp.lastMatch, type:'n'}; } } else // not a pure literal ==> cannot start with a sign if (this.isNumberInExpr(expr)) { this.lexpos += RegExp.lastIndex; return {token:'VAL', value:RegExp.lastMatch, type:'n'}; } return null; end <@doc scope="protected"> checks if the expression start with a number. Inside expressions (that start with "=") a number literal cannot start with a sign, because the sign as part of the literal causes problems with (simple) expressions like 1+2, as the parser thinks it has only two literals (1) and (+2), but the operator is missing !! expression method isNumberInExpr(expr) return (expr.match(/^(\d*\.)?\d+([eE][\-\+]?\d+)?/) != null); end <@doc scope="public"> checks if the expression start with a number If the expression is a pure literal it can start with a sign expression method isNumber(expr) return (expr.match(/^([+-])?(\d*\.)?\d+([eE][\-\+]?\d+)?/) != null); // Note- this includes an optioanl sign end // BUILTIN FUNCTION method isFunction(expr) //, silent) if (expr.match(/^\w+/)) { this.lexpos += RegExp.lastIndex; var def=this.lookup(RegExp.lastMatch); if (def && this.supportedFuncGroups[def.group]) // check if function is supported { //add the function to the recently used list this.addRecentDE(RegExp.lastMatch); return def; } throw new Error(-1, '#TEXT[XMSG_ERROR_BAD_IDENTIFIER]'.replace("{1}",RegExp.lastMatch)); } return null; end // OPERATOR method isOperator(expr) if (expr.match(/^[\!\%\&\*\+\-\/\<\=\>\|][\=\>\&\|]?/)) { this.lexpos += RegExp.lastIndex; var def=this.lookup(RegExp.lastMatch); if (def) { //add the function to the recently used list this.addRecentDE(RegExp.lastMatch); return def; } throw new Error(-1, '#TEXT[XMSG_ERROR_UNKNOWN_OPERATOR] '+RegExp.lastMatch); } return null; end // FIELD ON INFOSET - handles fields and infoshapes inside current infoset, and fields and infoshapes in another infosets on page //is overriden by inheriting class method isBOField(expr) return null; end // ===== end RULES ====== /* This method prepare the dynamic expression for persistence or for UI in a case of error in the DE. It replaces the field name with its id for persistence, and id with name for UI. It does not handle the TRANSLATE case (where the text is replaced with key). The purpose is that field name will be persisted correctly even if there's an error */ method handleFieldPersistence(expr1) var expr = ""; if(expr1) expr = expr1; else expr = this.source; var parseByID = this.parseByID; var lastPos = 0; var tail = expr; var def; var match; while(tail.indexOf("@")>=0) { var i = 0, j =0 , tmp = ""; //get an instance of string (like "aaa") //if(tail.match(/(\'(\\\'|[^\'])*\@(\\\'|[^\'])*(\')|\"(\\\"|[^\"])*\@(\\\"|[^\"])*(\"))/)) if(tail.match(/(\'(\\\'|[^\'])*\@(\\\'|[^\'])*(\'|.$)?|\"(\\\"|[^\"])*\@(\\\"|[^\"])*(\"|.$)?)/)) { i = RegExp.lastIndex; tmp = RegExp.lastMatch; } //get an instance of field (like @Name) if(tail.match(/(\[([^\]]+)\]|([\w\.^\]]+))?\@(\[([^\]]+)\]|(\@)|([\w\.]+))+/)) j = RegExp.lastIndex; else return expr; //if we got a string then skip on it. try to avoid the case when a field name is inside "" like "Root@Name" if(ji-tmp.length) { lastPos += i; tail = expr.substring(lastPos); continue; } lastPos += j; tail = expr.substring(lastPos); match = RegExp.lastMatch; //check the field found, if valid and replace it with it's ID in the expression try { def = null; def = this.isBOField(match); } catch(e){} if(def) //check for error - def is empty then no field was found { if(this.parseByID) expr = expr.substring(0, lastPos-match.length) + def.display; else expr = expr.substring(0, lastPos-match.length) + def.ref; lastPos = expr.length; expr += tail; } } return expr; end <@doc scope="public"> handle names with special chars: for [ . @ chars - add \ before them and also add [] around the name throw error when must have [] wraping the text Return: string - the altered name, string to check if to extract the name from [] method handleReservedChar(name, bExtract) if(name.match(/\W/)) { if(bExtract) { // remove [] in the infoset part [---] if(name.charAt(0) != '[' || name.charAt(name.length-1) != ']') throw new Error(-1, name + ' #TEXT[XMSG_ERROR_MISSING_BRACKETS]'); name = name.slice(1, name.length-1); // Just in case someone is using \[ or \. inside an infoset name name = REPLACE(name, '\\[', '['); } else { // Just in case we have [ or '.' than it is represented as '\[' name = REPLACE(name, '[', '\\['); name = '[' + name + ']'; } } return name; end <@doc scope="public"> split the string by the given seperator string to split seperator char array which will hold the seperated names if to break the loop after first found method split(name, seperator, names, bBreak) // MUST REVIEW!!!! // TODO: cannot this be replaced with javascript split ??? var counter = 0, iLast = 0; if(!name) return; if(names.length > 0) names.length = 0; for(var i=0; i Verify that the actual type matches the expected type (are equal or implicit conversion exists) The expected type The actual type ~false if implicit conversion can be used, ~true otherwise // Checks the type of the parsed expression method checkType(expectedType, actualType, forceExpected, errorStr) if (this.error) return(""); var dt = actualType; var gt = this.castType(expectedType); if ('a' == gt || 'a' == dt) // expectedType or actualType are 'any', which means no check is required return(""); var types = expectedType.split(' '); for (var i=0, errors = 0, failedTypes = '', length=types.length; i check implicit conversion according to XGL2. // Can we convert the result type (dt) to the expected type (gt)? if (this.implicitConversion(dt, gt) == false) { // Illegal type conversion: failedTypes += this.Class.metadata.dtLabels[gt] + ', '; errors++; continue; } else // types have a corresponding implicit conversion return(""); } else // types match return(""); } if (errors == length) { // all expectedTypes failed errorStr = errorStr || "#TEXT[XMSG_EXPECTED_VALUE]"; return(errorStr.replace("{1}",this.helper.typeToString(actualType).toLowerCase())). replace("{2}", failedTypes.substr(0, failedTypes.length-2)); } return(""); end method implicitConversion(fromType, toType) switch (fromType+toType) { case 'bn': //Boolean to Number IF(expr, 1, 0) case 'bs': //Boolean to String IF(expr, "true", "false") case 'cd': //DateTime to Date DVAL(DTSTR(expr, 'DATE')) case 'cn': //DateTime to Number ELAPSED(expr)/(24*60*60) case 'cs': //DateTime to String DTSTR(expr, 'DATETIME') case 'ct': //DateTime to Time TVAL(DTSTR(expr, 'TIME')) case 'dc': //Date to DateTime DTVAL(DSTR(expr, 'DATE')+ " 00:00:00") case 'dn': //Date to Number FLOOR(ELAPSED(expr)/(24*60*60)) case 'ds': //Date to String DSTR(expr, 'DATE') case 'nb': //Number to Boolean IF(expr <> 0, true, false) case 'ns': //Number to String NSTR(expr, 'NORMAL') case 'qn': //Quantity to Number QGET(expr) case 'qs': //Quantity to String QSTR(expr) case 'sb': //String to Boolean BOOL(expr) case 'sc': //String to DateTime DTVAL(expr) case 'sd': //String to Date DVAL(expr) case 'sn': //String to Number NVAL(expr) case 'sq': //String to Quantity QVAL(expr) case 'st': //String to Time TVAL(expr) case 'tc': //Time to DateTime DTVAL("01/01/1900 "+TSTR(expr, 'TIME')) case 'tn': //Time to Number ELAPSED(expr) case 'ts': //Time to String TSTR(expr,'TIME') case 'ub': //nulltype to Boolean case 'uc': //nulltype to DateTime case 'ud': //nulltype to Date case 'un': //nulltype to Number case 'uq': //nulltype to Quantity case 'us': //nulltype to String case 'ut': //nulltype to Time return true; } // Any other conversion is forbidden: return false; end // Gets the friendly display label for a given apptype or gmltype method getTypeLabel(type) return this.Class.metadata.dtLabels[this.castType(type)] || ''; end // Gets the display icon for a given apptype or gmltype method getTypeIcon(type) return this.GIFLocations[this.castType(type)] || "this.GIFLocations['blank']"; /////////////////////////////MHOTER end // Converts a given apptype or gmltype into a dynexp type code method castType(type) var c = this.Class.metadata.dtTypes[LOWER(type)] || ''; if(!c) { c = LOWER(type).charAt(0); c = (c in this.Class.metadata.dtLabels) ? c : ''; } return c; end // Lookups and returns new token method lookup(name) name = name.toUpperCase(); var def = this.funcTable[name]; if (def !== undefined) //found in cache return(CLONE(def,false));//shallow clone - e.g. args can remain the original def=this.Class.metadata.funcTable[name]; if (def && def.alias) { name=def.alias.toUpperCase(); def=this.Class.metadata.funcTable[name]; } if (def) { //function found var args=def.args||[]; var optionalParams = calcOptionalParams(args, this.ARG_TYPE_OPTIONAL); def = {token:def.token, value:name, type:def.type, args:args, size:args.length, group:def.group, optParams:optionalParams}; } else // no such function def=null; this.funcTable[name] = def; //populate cache return(CLONE(def,false)); function calcOptionalParams(args, optChar) { var count = 0; for (var i = 0, len=args.length;i set the expression for display or for persistence. It might be that the persistence is in different format then what the user sees displying the expr in UI or for persistence a subtree to print - used for error reprting purposes representation of the expression for UI or for persistence method genericPrint(b4UI, subTree) this.error = ''; var token = subTree || this.tree; //if the expression is not valid, we still want an accurate persistence, so parse the field names in the expr if there're any if(token == null) { return '='+this.handleFieldPersistence(); } if(!this.isLiteral || subTree) { try { pexp = this.genericPrintEngine(token, b4UI); } catch(e) { this.error = e.description; return '='+this.handleFieldPersistence(); } if (!subTree) pexp='='+ pexp; //add '=' only for the whole tree (but not for error messages) } else { pexp = token.value; } return pexp; end <@doc scope="protected"> set the token for display or for persistence. It might be that the persistence is in different format then what the user sees token currently handles displying the expr in UI or for persistence representation of the token for UI or for persistence method genericPrintEngine(token, b4UI) var t=token.token, v=token.value, c=token.children; // literal or token with error if (t == 'VAL' || t == 'ERROR') { if(token.type == 's') { v = REPLACE(v, '\"', '\\\"'); v= '\"' + v + '\"'; } return v; } // field if (t == 'FLD') { if(b4UI) return token.display; return token.ref; } // function if (t == this.TOKEN_FUNC || t == 'LEFT') { var buf=[]; var tmpIndex = 0; tmpIndex = this.setDisplayTransFunc(token, b4UI, buf) || 0; // Recursive creation of the output function string: for (var i=tmpIndex, len=token.size; i for TRANLSATE/FORMAT the persistence might be different from UI token currently handles displying the expr in UI or for persistence holds the name of macro and arguments method setDisplayTransFunc(token, b4UI, buf) return 0; end <@doc scope="public"> return the list of recently used functions in dynamic expr editor method getRecentDE() return this.recentDETmp; end <@doc scope="public"> clear the list of recently used functions in dynamic expr editor method resetRecentDE() this.recentDETmp = []; end <@doc scope="public"> add a function/operator to the list of recently used function in dynamic expr editor function/operator method addRecentDE(elem) this.recentDETmp.push(elem); end <@doc scope="public"> remove the last added function/operator from the list of recently used functions method removeRecentDE() this.recentDETmp.pop(); end <@doc scope="public"> clear the comments within /* */ from the expression Return: array of the expression itself and its comments expression to be devided to expression and comments method handleExprComments(expr) expr = expr+''; if(expr.length==0) return ['','']; var position = expr.indexOf("#[COMMENTIN]"); if(position < 0) //no comment return [expr,'']; else return [expr.substring(0, position),expr.substring(position+2, expr.length-2)]; end <@doc scope="public"> gets the comments from an expression Return: the comments expression to be devided to expression and comments method getExprComments(expr) return this.setExprComments(this.handleExprComments(expr)[1]); end <@doc scope="public"> add to the comments /* and */ Return: the comments comments string method setExprComments(expr) if(expr.length > 0) expr = '#[COMMENTIN]' + expr + '#[COMMENTOUT]'; return expr; end <@doc scope="public"> gets the expression only without comments Return: the expression expression to be devided to expression and comments method clearExprComments(expr) return this.handleExprComments(expr)[0]; end <@doc scope="public"> check if the translation type is correct If not valid, an exception is thrown translation type method checkTranslationType(ttype) if(!this.Class.metadata.translationTypes[ttype]) throw new Error(-1, '#TEXT[XMSG_ERROR_MISSING_TRANS_TYPE]'.replace('{0}','/'+this.TOKEN_FUNC_FORMAT)); end //////////// GENERAL METHODS //////////// <@doc scope="public"> Determines whether a given expression is a simple field reference from the context infoset The Dynamic Expression to check method isLocalFieldRef(value) value = this.handleExprComments(value)[0]; return (/^=@\w+(\.\w+)*$/.test(value)); end <@doc scope="public"> Determines whether a given expression is a simple field reference, possibly from a foreign infoset The Dynamic Expression to check method isFieldRef(value) value = this.handleExprComments(value)[0]; return (/^=\w*\@\w+(\.\w+)*$/.test(value)); end <@doc scope="public"> Handy method for parsing a DE The Dynamic Expression to check The expected type - default is String method parseExpr(expr, type) expr = NVL(expr,'') + ''; if (!expr) return true; this.setExpectedType(type); return this.parse(this.clearExprComments(expr)); // check that return value is identical to the old method end /////////////////////////////////////////////////////////////////////// // PARSER METDATA metadata dtLabels = { b:'boolean', s:'text', n:'numeric', d:'date', t:'time', q:'quantity', c:'datetime', a:'any' } /*metadata dtTypes = { b:LOWER(#[BOOLEAN]), s:LOWER(#[STRING]), n:LOWER(#[NUMBER]), n:'int', n:'integer', n:'float', d:LOWER(#[DATE]), t:LOWER(#[TIME]), q:LOWER(#[QUANTITY]), c:LOWER(#[DATETIME]) }*/ metadata dtTypes = { boolean:'b', string:'s', number:'n', int:'n', integer:'n', float:'n', date:'d', time:'t', quantity:'q', datetime:'c', nulltype:'u', any:'a' } metadata funcGroups = { 's': '#TEXT[XTND_TEXT_FUNC]', 'n': '#TEXT[XTND_NUM_FUNC]', 'd': '#TEXT[XTND_DATE_FUNC]', 't': '#TEXT[XTND_TIME_FUNC]', 'c': '#TEXT[XTND_DATETIME_FUNC]', 'q': '#TEXT[XTND_QUANTITY_FUNC]', 'b': '#TEXT[XTND_COND_FUNC]', 'm': '#TEXT[XTND_SCF_FUNC]', 'o': '#TEXT[XTND_OPERATORS]' } metadata funcTable = { // operators '*': {token:'MUL', type:'n', args:['n','n'], group:'o', label:'*', descr:'#TEXT[XMSG_FUNC_MULTI_DESC]'}, '/': {token:'MUL', type:'n', args:['n','n'], group:'o', label:'/', descr:'#TEXT[XMSG_FUNC_DIV_DESC]'}, '%': {token:'MUL', type:'n', args:['n','n'], group:'o', label:'%', descr:'#TEXT[XMSG_FUNC_MOD_DESC]'}, '+': {token:'ADD', type:'n', args:['n','n'], group:'o', label:'+', descr:'#TEXT[XMSG_FUNC_ADD_DESC]'}, '-': {token:'ADD', type:'n', args:['n','n'], group:'o', label:'-', descr:'#TEXT[XMSG_FUNC_SUB_DESC]'}, '+U': {token:'ADD', type:'n', args:['n'], group:'o', label:null, descr:'#TEXT[XTOL_OPERATOR_UNP]'}, '-U': {token:'ADD', type:'n', args:['n'], group:'o', label:null, descr:'#TEXT[XTOL_OPERATOR_UNM]'}, '&': {token:'ADD', type:'s', args:['s','s'], group:'o', label:'&', descr:'#TEXT[XTOL_OPERATOR_CON]'}, '<': {token:'REL', type:'b', args:['a','a'], group:'o', label:'<', descr:'#TEXT[XMSG_FUNC_LESS_THAN_DESC]'}, '<=': {token:'REL', type:'b', args:['a','a'], group:'o', label:'<=', descr:'#TEXT[XMSG_FUNC_LESS_EQ_DESC]'}, '>': {token:'REL', type:'b', args:['a','a'], group:'o', label:'>', descr:'#TEXT[XMSG_FUNC_GREAT_THAN_DESC]'}, '>=': {token:'REL', type:'b', args:['a','a'], group:'o', label:'>=', descr:'#TEXT[XMSG_FUNC_GREAT_EQ_DESC]'}, '==': {token:'REL', type:'b', args:['a','a'], group:'o', label:'==', descr:'#TEXT[XMSG_FUNC_EQUAL_TO_DESC]'}, '!=': {token:'REL', type:'b', args:['a','a'], group:'o', label:'!=', descr:'#TEXT[XMSG_FUNC_NOT_EQUAL_TO_DESC]'}, '<>': {alias:'!=', type:'b', group:'o', label:'<>', descr:'#TEXT[XMSG_FUNC_NOT_EQUAL_TO_DESC]'}, '&&': {token:'AND', type:'b', args:['b','b'], group:'o', label:'&&', descr:'#TEXT[XMSG_FUNC_AND_DESC]'}, 'AND': {alias:'&&', type:'b', group:'o', label:'AND',descr:'#TEXT[XMSG_FUNC_AND_DESC]'}, '||': {token:'OR', type:'b', args:['b','b'], group:'o', label:'||', descr:'#TEXT[XMSG_FUNC_OR_DESC]'}, 'OR': {alias:'||', type:'b', group:'o', label:'OR', descr:'#TEXT[XMSG_FUNC_OR_DESC]'}, '!': {token:'NOT', type:'b', args:['b'], group:'o', label:'!', descr:'#TEXT[XMSG_FUNC_NOT_DESC]'}, 'NOT': {alias:'!', type:'b', group:'o', label:'NOT',descr:'#TEXT[XMSG_FUNC_NOT_DESC]'}, // string functions 'ASC': {token:'FUNC', type:'n', args:['s'], group:'s', label:'ASC(char)', descr:'#TEXT[XTOL_FUNCTION_ASC]'}, 'AT': {token:'FUNC', type:'s', args:['s','n'], group:'s', label:'AT(text,pos)', descr:'#TEXT[XTOL_FUNCTION_AT]'}, 'BEGINS': {token:'FUNC', type:'b', args:['s','s'], group:'s', label:'BEGINS(text,pattern)', descr:'#TEXT[XTOL_FUNCTION_BEGINS]'}, 'CAPITAL': {token:'FUNC', type:'s', args:['s'], group:'s', label:'CAPITAL(text)', descr:'#TEXT[XTOL_FUNCTION_CAPITAL]'}, 'CAPITALW': {token:'FUNC', type:'s', args:['s'], group:'s', label:'CAPITALW(text)', descr:'#TEXT[XTOL_FUNCTION_CAPITALW]'}, 'CHR': {token:'FUNC', type:'s', args:['n'], group:'s', label:'CHR(code)', descr:'#TEXT[XTOL_FUNCTION_CHR]'}, 'COMPACT': {token:'FUNC', type:'s', args:['s'], group:'s', label:'COMPACT(text)', descr:'#TEXT[XTOL_FUNCTION_COMPACT]'}, 'CONTAINS': {token:'FUNC', type:'b', args:['s','s'], group:'s', label:'CONTAINS(text,pattern)', descr:'#TEXT[XTOL_FUNCTION_CONTAINS]'}, 'ENDS': {token:'FUNC', type:'b', args:['s','s'], group:'s', label:'ENDS(text,pattern)', descr:'#TEXT[XTOL_FUNCTION_ENDS]'}, 'FILL': {token:'FUNC', type:'s', args:['n','s?'], group:'s', label:'FILL(len,[pad])', descr:'#TEXT[XTOL_FUNCTION_FILL]'}, 'FORMAT': {token:'FUNC', type:'s', args:['s','s','s*'], group:'s', label:'FORMAT(text,tType,parameter list)', descr:"#TEXT[YINF_FUNCTION_FORMAT]"}, 'LEFT': {token:'FUNC', type:'s', args:['s','n?'], group:'s', label:'LEFT(text,[len])', descr:'#TEXT[XTOL_FUNCTION_LEFT]'}, 'LEN': {token:'FUNC', type:'n', args:['s'], group:'s', label:'LEN(text)', descr:'#TEXT[XTOL_FUNCTION_LEN]'}, 'LIKE': {token:'FUNC', type:'b', args:['s','s'], group:'s', label:'LIKE(text,pattern)', descr:'#TEXT[XTOL_FUNCTION_LIKE]'}, 'LOWER': {token:'FUNC', type:'s', args:['s'], group:'s', label:'LOWER(text)', descr:'#TEXT[XTOL_FUNCTION_LOWER]'}, 'LPAD': {token:'FUNC', type:'s', args:['s','n','s?'], group:'s', label:'LPAD(text,len,[pad])', descr:'#TEXT[XTOL_FUNCTION_LPAD]'}, 'LTRIM': {token:'FUNC', type:'s', args:['s'], group:'s', label:'LTRIM(text)', descr:'#TEXT[XTOL_FUNCTION_LTRIM]'}, 'MID': {token:'FUNC', type:'s', args:['s','n','n'], group:'s', label:'MID(text,start,len)', descr:'#TEXT[XTOL_FUNCTION_MID]'}, 'PREF': {token:'FUNC', type:'s', args:['s','n?'], group:'s', label:'PREF(text,[len])', descr:'#TEXT[XTOL_FUNCTION_PREF]'}, 'REPLACE': {token:'FUNC', type:'s', args:['s','s','s'], group:'s', label:'REPLACE(text,pattern,repstr)', descr:'#TEXT[XTOL_FUNCTION_REPLACE]'}, 'RIGHT': {token:'FUNC', type:'s', args:['s','n?'], group:'s', label:'RIGHT(text,[len])', descr:'#TEXT[XTOL_FUNCTION_RIGHT]'}, 'RPAD': {token:'FUNC', type:'s', args:['s','n','s?'], group:'s', label:'RPAD(text,len,[pad])', descr:'#TEXT[XTOL_FUNCTION_RPAD]'}, 'RTRIM': {token:'FUNC', type:'s', args:['s'], group:'s', label:'RTRIM(text)', descr:'#TEXT[XTOL_FUNCTION_RTRIM]'}, 'TRANSLATE':{token:'FUNC', type:'s', args:['s','s?'], group:'s', label:'TRANSLATE(text,[tType])', descr:'#TEXT[XTOL_FUNCTION_TRANSLATE]'}, 'TRIM': {token:'FUNC', type:'s', args:['s'], group:'s', label:'TRIM(text)', descr:'#TEXT[XTOL_FUNCTION_TRIM]'}, 'UPPER': {token:'FUNC', type:'s', args:['s'], group:'s', label:'UPPER(text)', descr:'#TEXT[XTOL_FUNCTION_UPPER]'}, 'ZPAD': {token:'FUNC', type:'s', args:['s','n'], group:'s', label:'ZPAD(text,len)', descr:'#TEXT[XTOL_FUNCTION_ZPAD]'}, //'TRNS': {token:'FUNC', type:'s', args:['s'], group:'s', label:'TRNS(text)', descr:'#TEXT[XMSG_FUNCTION_TRANSLATE]'}, // numeric functions 'ABS': {token:'FUNC', type:'n', args:['n'], group:'n', label:'ABS(n)', descr:'#TEXT[XTOL_FUNCTION_ABS]'}, 'CEIL': {token:'FUNC', type:'n', args:['n'], group:'n', label:'CEIL(n)', descr:'#TEXT[XTOL_FUNCTION_CEIL]'}, 'FLOAT': {token:'FUNC', type:'n', args:['s'], group:'n', label:'FLOAT(str)', descr:'#TEXT[XTOL_FUNCTION_FLOAT]'}, 'FLOOR': {token:'FUNC', type:'n', args:['n'], group:'n', label:'FLOOR(n)', descr:'#TEXT[XTOL_FUNCTION_FLOOR]'}, 'HEX': {token:'FUNC', type:'n', args:['s'], group:'n', label:'HEX(str)', descr:'#TEXT[XTOL_FUNCTION_HEX]'}, 'INT': {token:'FUNC', type:'n', args:['s'], group:'n', label:'INT(str)', descr:'#TEXT[XTOL_FUNCTION_INT]'}, 'LIMIT': {token:'FUNC', type:'n', args:['n','n','n'], group:'n', label:'LIMIT(lower,n,upper)', descr:'#TEXT[XTOL_FUNCTION_LIMIT]'}, 'MAX': {token:'FUNC', type:'n', args:['n*'], group:'n', label:'MAX(number list)', descr:'#TEXT[XTOL_FUNCTION_MAX]'}, 'MIN': {token:'FUNC', type:'n', args:['n*'], group:'n', label:'MIN(number list)', descr:'#TEXT[XTOL_FUNCTION_MIN]'}, 'NSTR': {token:'FUNC', type:'s', args:['n','s?'], group:'n', label:'NSTR(n,[mask])', descr:'#TEXT[XTOL_FUNCTION_NSTR]'}, 'NVAL': {token:'FUNC', type:'n', args:['s'], group:'n', label:'NVAL(str)', descr:'#TEXT[XTOL_FUNCTION_NVAL]'}, 'POS': {token:'FUNC', type:'n', args:['s'], group:'n', label:'POS(str)', descr:'#TEXT[XTOL_FUNCTION_POS]'}, 'RND': {token:'FUNC', type:'n', args:['n'], group:'n', label:'RND(max)', descr:'#TEXT[XTOL_FUNCTION_RND]'}, 'ROUND': {token:'FUNC', type:'n', args:['n'], group:'n', label:'ROUND(n)', descr:'#TEXT[XTOL_FUNCTION_ROUND]'}, 'SIGN': {token:'FUNC', type:'n', args:['n'], group:'n', label:'SIGN (n)', descr:'#TEXT[XTOL_FUNCTION_SIGN]'}, // math & trig functions 'ACOS': {token:'FUNC', type:'n', args:['n'], group:'m', label:'ACOS(n)', descr:'#TEXT[XTOL_FUNCTION_ACOS]'}, 'ASIN': {token:'FUNC', type:'n', args:['n'], group:'m', label:'ASIN(n)', descr:'#TEXT[XTOL_FUNCTION_ASIN]'}, 'ATAN': {token:'FUNC', type:'n', args:['n'], group:'m', label:'ATAN(n)', descr:'#TEXT[XTOL_FUNCTION_ATAN]'}, 'ATAN2': {token:'FUNC', type:'n', args:['n','n'], group:'m', label:'ATAN2(y,x)', descr:'#TEXT[XTOL_FUNCTION_ATAN2]'}, 'COS': {token:'FUNC', type:'n', args:['n'], group:'m', label:'COS(angle)', descr:'#TEXT[XTOL_FUNCTION_COS]'}, 'EXP': {token:'FUNC', type:'n', args:['n'], group:'m', label:'EXP(n)', descr:'#TEXT[XTOL_FUNCTION_EXP]'}, 'LOGN': {token:'FUNC', type:'n', args:['n'], group:'m', label:'LOGN(n)', descr:'#TEXT[XTOL_FUNCTION_LOGN]'}, 'POW': {token:'FUNC', type:'n', args:['n','n'], group:'m', label:'POW(base,n)', descr:'#TEXT[XTOL_FUNCTION_POW]'}, 'SIN': {token:'FUNC', type:'n', args:['n'], group:'m', label:'SIN(angle)', descr:'#TEXT[XTOL_FUNCTION_SIN]'}, 'SQ': {token:'FUNC', type:'n', args:['n'], group:'m', label:'SQ(n)', descr:'#TEXT[XTOL_FUNCTION_SQ]'}, 'SQRT': {token:'FUNC', type:'n', args:['n'], group:'m', label:'SQRT(n)', descr:'#TEXT[XTOL_FUNCTION_SQRT]'}, 'TAN': {token:'FUNC', type:'n', args:['n'], group:'m', label:'TAN(angle)', descr:'#TEXT[XTOL_FUNCTION_TAN]'}, // date functions 'DATE': {token:'FUNC', type:'d', args:['n','n','n'], group:'d', label:'DATE(year,month,day)', descr:'#TEXT[XTOL_FUNCTION_DATE]'}, 'DADD': {token:'FUNC', type:'d', args:['d','n','s?'], group:'d', label:'DADD(d,n,[unit])', descr:'#TEXT[XTOL_FUNCTION_DADD]'}, 'DGET': {token:'FUNC', type:'n', args:['d','s'], group:'d', label:'DGET(d,part)', descr:'#TEXT[XTOL_FUNCTION_DGET]'}, 'DSTR': {token:'FUNC', type:'s', args:['d','s?'], group:'d', label:'DSTR(d,[format])', descr:'#TEXT[XTOL_FUNCTION_DSTR]'}, 'DSUB': {token:'FUNC', type:'n', args:['d','d','s?'], group:'d', label:'DSUB(d1,d2,[unit])', descr:'#TEXT[XTOL_FUNCTION_DSUB]'}, 'DVAL': {token:'FUNC', type:'d', args:['s'], group:'d', label:'DVAL(str)', descr:'#TEXT[XTOL_FUNCTION_DVAL]'}, 'NOW': {token:'FUNC', type:'d', args:[], group:'d', label:'NOW()', descr:'#TEXT[XTOL_FUNCTION_NOW]'}, // time functions 'TIME': {token:'FUNC', type:'t', args:['n','n','n'], group:'t', label:'TIME(hour,min,sec)', descr:'#TEXT[XTOL_FUNCTION_TIME]'}, 'TADD': {token:'FUNC', type:'t', args:['t','n','s?'], group:'t', label:'TADD(t,n,[unit])', descr:'#TEXT[XTOL_FUNCTION_TADD]'}, 'TGET': {token:'FUNC', type:'n', args:['t','s'], group:'t', label:'TGET(t,part)', descr:'#TEXT[XTOL_FUNCTION_TGET]'}, 'TSTR': {token:'FUNC', type:'s', args:['t','s?'], group:'t', label:'TSTR(t,[format])', descr:'#TEXT[XTOL_FUNCTION_TSTR]'}, 'TSUB': {token:'FUNC', type:'n', args:['t','t','s?'], group:'t', label:'TSUB(t1,t2,[unit])', descr:'#TEXT[XTOL_FUNCTION_TSUB]'}, 'TVAL': {token:'FUNC', type:'t', args:['s'], group:'t', label:'TVAL(str)', descr:'#TEXT[XTOL_FUNCTION_TVAL]'}, 'TNOW': {token:'FUNC', type:'t', args:[], group:'t', label:'TNOW()', descr:'#TEXT[XTOL_FUNCTION_TNOW]'}, // datetime functions 'DATETIME': {token:'FUNC', type:'c', args:['n','n','n','n','n','n?','s?'], group:'c', label:'DATETIME(year,month,day,hour,minute,[second],[timezone])', descr:'#TEXT[XTOL_FUNCTION_DATETIME]'}, 'DTADD': {token:'FUNC', type:'c', args:['c','n','s?'], group:'c', label:'DTADD(c,n,[unit])', descr:'#TEXT[XTOL_FUNCTION_DTADD]'}, 'DTGET': {token:'FUNC', type:'n', args:['c','s'], group:'c', label:'DTGET(c,part)', descr:'#TEXT[XTOL_FUNCTION_DTGET]'}, 'DTSTR': {token:'FUNC', type:'s', args:['c','s?'], group:'c', label:'DTSTR(c,[format])', descr:'#TEXT[XTOL_FUNCTION_DTSTR]'}, 'DTSUB': {token:'FUNC', type:'n', args:['c','c','s?'], group:'c', label:'DTSUB(c1,c2,[unit])', descr:'#TEXT[XTOL_FUNCTION_DTSUB]'}, 'DTVAL': {token:'FUNC', type:'c', args:['s'], group:'c', label:'DTVAL(str)', descr:'#TEXT[XTOL_FUNCTION_DTVAL]'}, 'DTNOW': {token:'FUNC', type:'c', args:[], group:'c', label:'DTNOW()', descr:'#TEXT[XTOL_FUNCTION_DTNOW]'}, // quantity functions 'QSTR': {token:'FUNC', type:'s', args:['q','s?'], group:'q', label:'QSTR(q,[mask])', descr:'#TEXT[XTOL_FUNCTION_QSTR]'}, 'QVAL': {token:'FUNC', type:'q', args:['s'], group:'q', label:'QVAL(text)', descr:'#TEXT[XTOL_FUNCTION_QVAL]'}, 'QGET': {token:'FUNC', type:'n', args:['q'], group:'q', label:'QGET(q)', descr:'#TEXT[XTOL_FUNCTION_QGET]'}, 'QGETUNIT': {token:'FUNC', type:'s', args:['q'], group:'q', label:'QGETUNIT(q)', descr:'#TEXT[XTOL_FUNCTION_QGETUNIT]'}, // conditional functions 'BOOL': {token:'FUNC', type:'b', args:['s'], group:'b', label:'BOOL(value)', descr:'#TEXT[XTOL_FUNCTION_BOOL]'}, 'IF': {token:'FUNC', type:'s', args:['b','s','s'], group:'b', label:'IF(test,expr1,expr2)', descr:'#TEXT[XTOL_FUNCTION_IF]'}, 'ISNULL': {token:'FUNC', type:'b', args:['s'], group:'b', label:'ISNULL(value)', descr:'#TEXT[XTOL_FUNCTION_ISNULL]'}, 'NVL': {token:'FUNC', type:'s', args:['s*'], group:'b', label:'NVL(string list)', descr:'#TEXT[YINF_FUNCTION_NVL]'} } metadata funcHelpTable = { // operators '*': {description:'#TEXT[XMSG_FUNC_MULTI_DESC]', syntax:null , examples:null}, '/': {description:'#TEXT[XMSG_FUNC_DIV_DESC]', syntax:null , examples:null}, '%': {description:'#TEXT[XMSG_FUNC_MOD_DESC]', syntax:null , examples:null}, '+': {description:'#TEXT[XMSG_FUNC_ADD_DESC]', syntax:null , examples:null}, '-': {description:'#TEXT[XMSG_FUNC_SUB_DESC]', syntax:null , examples:null}, '+U': {description:'#TEXT[XTOL_OPERATOR_UNP]', syntax:null , examples:null}, '-U': {description:'#TEXT[XTOL_OPERATOR_UNM]', syntax:null , examples:null}, '&': {description:'#TEXT[XTOL_OPERATOR_CON]', syntax:null , examples:null}, '<': {description:'#TEXT[XMSG_FUNC_LESS_THAN_DESC]', syntax:null , examples:null}, '<=': {description:'#TEXT[XMSG_FUNC_LESS_EQ_DESC]', syntax:null , examples:null}, '>': {description:'#TEXT[XMSG_FUNC_GREAT_THAN_DESC]', syntax:null , examples:null}, '>=': {description:'#TEXT[XMSG_FUNC_GREAT_EQ_DESC]', syntax:null , examples:null}, '==': {description:'#TEXT[XMSG_FUNC_EQUAL_TO_DESC]', syntax:null , examples:null}, '!=': {description:'#TEXT[XMSG_FUNC_NOT_EQUAL_TO_DESC]', syntax:null , examples:null}, '<>': {description:'#TEXT[XMSG_FUNC_NOT_EQUAL_TO_DESC]', syntax:null , examples:null}, '&&': {description:'#TEXT[XMSG_FUNC_AND_DESC]', syntax:null , examples:null}, 'AND': {description:'#TEXT[XMSG_FUNC_AND_DESC]', syntax:null , examples:null}, '||': {description:'#TEXT[XMSG_FUNC_OR_DESC]', syntax:null , examples:null}, 'OR': {description:'#TEXT[XMSG_FUNC_OR_DESC]', syntax:null , examples:null}, '!': {description:'#TEXT[XMSG_FUNC_NOT_DESC]', syntax:null , examples:null}, 'NOT': {description:'#TEXT[XMSG_FUNC_NOT_DESC]', syntax:null , examples:null}, // string functions 'ASC': {description:'#TEXT[XMSG_FUNC_ASC_DESC]', syntax:'#TEXT[XMSG_FUNC_ASC_SYNTAX]', examples:'#TEXT[XMSG_FUNC_ASC_EXAMPLES]'.replace("{0}","ASC('A')")}, 'AT': {description:'#TEXT[XMSG_FUNC_AT_DESC]', syntax:'#TEXT[XMSG_FUNC_AT_SYNTAX]'.replace("{0}","AT(text,[pos])"), examples:'#TEXT[XMSG_FUNC_AT_EXAMPLES]'.replace("{0}","AT(Middletown,6)")}, 'BEGINS': {description:'#TEXT[XMSG_FUNC_BEGINS_DESC]'.replace("{0}","true").replace("{1}","false"), syntax:'#TEXT[XMSG_FUNC_BEGINS_SYNTAX]'.replace("{0}","BEGINS(text,pattern)"), examples:'#TEXT[YMSG_FUNC_BEGINS_EXAMPLES]'.replace("{0}","BEGINS('hello world', 'world')").replace("{1}","world").replace("{2}","false").replace("{3}","BEGINS('hello world', 'hello')").replace("{4}","true")}, 'CAPITAL': {description:'#TEXT[XMSG_FUNC_CAPITAL_DESC]', syntax:'CAPITAL(text)', examples:'#TEXT[XMSG_FUNC_CAPITAL_EXAMPLES]'.replace("{0}","CAPITAL(@COMP_NAME)").replace("{1}","Atlas city")}, 'CAPITALW': {description:'#TEXT[XMSG_FUNC_CAPITALW_DESC]', syntax:'CAPITALW(text)', examples:'#TEXT[XMSG_FUNC_CAPITALW_EXAMPLES]'.replace("{0}","CAPITALW(@COMP_NAME)").replace("{1}","Atlas City")}, 'CHR': {description:'#TEXT[XMSG_FUNC_CHR_DESC]', syntax:'CHAR(code)', examples:'#TEXT[XMSG_FUNC_CHR_EXAMPLES]'.replace("{0}","CHR('65')").replace("{1}","A")}, 'COMPACT': {description:'#TEXT[XMSG_FUNC_COMPACT_DESC]', syntax:'COMPACT(text)', examples:'#TEXT[YMSG_FUNC_COMPACT_EXAMPLES]'.replace("{0}","COMPACT(' hello world ') == 'hello world'").replace("{1}","'hello world'")}, 'CONTAINS': {description:'#TEXT[XMSG_FUNC_CONTAINS_DESC]'.replace("{0}","true").replace("{1}","false"), syntax:'#TEXT[XMSG_FUNC_CONTAINS_SYNTAX]'.replace("{0}","CONTAINS(text,pattern)").replace("{1}","pattern"), examples:'#TEXT[YMSG_FUNC_CONTAINS_EXAMPLES]'.replace("{0}","CONTAINS('hello world', 'wor')").replace("{1}","'wor'").replace("{2}","true").replace("{3}","CONTAINS('hello world', 'hall)").replace("{4}","false")}, 'ENDS': {description:'#TEXT[XMSG_FUNC_ENDS_DESC]'.replace("{0}","true").replace("{1}","false"), syntax:'#TEXT[XMSG_FUNC_ENDS_SYNTAX]'.replace("{0}","ENDS(text,[pattern])").replace("{1}","pattern"), examples:'#TEXT[YMSG_FUNC_ENDS_EXAMPLES]'.replace("{0}","ENDS('hello world', 'world')").replace("{1}","'world'").replace("{2}","true").replace("{3}","ENDS('hello world', 'hello')").replace("{4}","false")}, 'FILL': {description:'#TEXT[XMSG_FUNC_FILL_DESC]'.replace("{0}","pad"), syntax:'#TEXT[XMSG_FUNC_FILL_SYNTAX]'.replace("{0}","FILL(len,pad)").replace("{1}","len").replace("{2}","pad"), examples:'#TEXT[YMSG_FUNC_FILL_EXAMPLES]'.replace("{0}","FILL(8,*)").replace("{1}","HOME").replace("{2}","HOME****")}, 'FORMAT': {description:'#TEXT[XMSG_FUNC_FORMAT_DESC]', syntax:'#TEXT[YMSG_FUNC_FORMAT_SYNTAX]'.replace("{0}","FORMAT(text,parameter list)").replace("{1}","text").replace("{2}","parameter list"), examples:'#TEXT[YMSG_FUNC_FORMAT_EXAMPLES]'.replace("{0}","FORMAT('Mr. (1) (2)', @FIRSTNAME, @LASTNAME)").replace("{1}","@FIRSTNAME").replace("{2}","@LASTNAME").replace("{3}","Mr. John Smith")}, 'LEFT': {description:'#TEXT[YMSG_FUNC_LEFT_RIGHT_DESC]'.replace("{0}","RIGHT").replace("{1}","LEFT"), syntax:'LEFT(text,len)
RIGHT(text,len)
', examples:'#TEXT[YMSG_FUNC_LEFT_RIGHT_EXAMPLES]'.replace("{0}","LEFT(@@,3)").replace("{1}","DOCUMENTATION").replace("{2}","DOC").replace("{3}","RIGHT(@SHORT_TEXT,4)").replace("{4}","FINALCOST").replace("{5}","COST")}, 'LEN': {description:'#TEXT[XMSG_FUNC_LEN_DESC]', syntax:'LEN(text)', examples:'#TEXT[XMSG_FUNC_LEN_EXAMPLES]'.replace("{0}","LEN('hello world')")}, 'LIKE': {description:'#TEXT[YMSG_FUNC_LIKE_DESC]'.replace("{0}","true").replace("{1}","false"), syntax:'#TEXT[YMSG_FUNC_LIKE_SYNTAX]'.replace("{0}","LIKE(text,[pattern])").replace("{1}","pattern"), examples:'#TEXT[YMSG_FUNC_LIKE_EXAMPLES]'.replace("{0}","LIKE('hello world', 'war')").replace("{1}","'*world'").replace("{2}","true").replace("{3}","LIKE('hello world', '*war')").replace("{4}","false")}, 'LOWER': {description:'#TEXT[XMSG_FUNC_LOWER_UPPER_DESC]', syntax:'LOWER(text)
UPPER(text)', examples:'#TEXT[YMSG_FUNC_LOWER_UPPER_EXAMPLES]'.replace("{0}","LOWER(@COUNTRY_NAME)").replace("{1}","usa").replace("{2}","UPPER(@@)").replace("{3}","ATLAS")}, 'LPAD': {description:'#TEXT[YMSG_FUNC_LPAD_RPAD_ZPAD_DESC]'.replace("{0}","ZPAD"), syntax:'#TEXT[YMSG_FUNC_LPAD_RPAD_ZPAD_SYNTAX]'.replace("{0}","LPAD(text,len,[pad])").replace("{1}","len").replace("{2}","pad"), examples:'#TEXT[YMSG_FUNC_LPAD_RPAD_ZPAD_EXAMPLES]'.replace("{0}","LPAD('hello',8,'*')").replace("{1}","***hello").replace("{2}","RPAD('hello',4,'*')").replace("{3}","hell").replace("{4}","ZPAD('1000',10)").replace("{5}","0000001000").replace("{6}","ZPAD('hello',10)").replace("{7}","hello")}, 'LTRIM': {description:'#TEXT[YMSG_FUNC_TRIM_LTRIM_RTRIM_DESC]'.replace("{0}","LTRIM").replace("{1}","RTRIM"), syntax:'TRIM(text)
LTRIM(text)
RTRIM(text)', examples:'#TEXT[YMSG_FUNC_TRIM_LTRIM_RTRIM_EXAMPLES]'.replace("{0}","TRIM(@YEAR_TO_END)").replace("{1}","' ANNUAL TURNOVER '").replace("{2}","ANNUAL TURNOVER").replace("{3}","LTRIM(' hello world ')").replace("{4}","hello world")}, 'MID': {description:'#TEXT[YMSG_FUNC_MID_DESC]', syntax:'#TEXT[YMSG_FUNC_MID_SYNTAX]'.replace("{0}","MID(text,start,len)").replace("{1}","start").replace("{2}","len"), examples:'#TEXT[YMSG_FUNC_MID_EXAMPLES]'.replace("{0}","MID(@SHORT_TEXT,5,3)").replace("{1}","DOCUMENTATION").replace("{2}","ENT")}, 'PREF': {description:'#TEXT[XMSG_FUNC_PREF_DESC]'.replace("{0}","n"), syntax:'#TEXT[XMSG_FUNC_PREF_SYNTAX]'.replace("{0}","PREF(text,[len])").replace("{1}","len"), examples:'#TEXT[YMSG_FUNC_PREF_EXAMPLES]'.replace("{0}","PREF('hello world',5)").replace("{1}","HELLO")}, 'REPLACE': {description:'#TEXT[YMSG_FUNC_REPLACE_DESC]', syntax:'#TEXT[XMSG_FUNC_REPLACE_SYNTAX]'.replace("{0}","REPLACE(text,pattern,repstr)").replace("{1}","pattern").replace("{2}","repstr"), examples:'#TEXT[YMSG_FUNC_REPLACE_EXAMPLES]'.replace("{0}","REPLACE('abcabc','b','***')").replace("{1}","b").replace("{2}","***").replace("{3}","a***ca***c").replace("{4}","REPLACE('abcabc','bc','')").replace("{5}","aa")}, 'RIGHT': {description:'#TEXT[XMSG_FUNC_LEFT_RIGHT_DESC]'.replace("{0}","RIGHT").replace("{1}","LEFT"), syntax:'#TEXT[XMSG_FUNC_LEFT_RIGHT_SYNTAX]'.replace("{0}","LEFT(text,len)").replace("{1}","RIGHT(text,len)"), examples:'#TEXT[YMSG_FUNC_LEFT_RIGHT_EXAMPLES]'.replace("{0}","LEFT(@@,3)").replace("{1}","DOCUMENTATION").replace("{2}","DOC").replace("{3}","RIGHT(@SHORT_TEXT,4)").replace("{4}","FINALCOST").replace("{5}","COST")}, 'RPAD': {description:'#TEXT[YMSG_FUNC_LPAD_RPAD_ZPAD_DESC]'.replace("{0}","ZPAD"), syntax:'#TEXT[YMSG_FUNC_LPAD_RPAD_ZPAD_SYNTAX]'.replace("{0}","LPAD(text,len,[pad])").replace("{1}","len").replace("{2}","pad"), examples:'#TEXT[YMSG_FUNC_LPAD_RPAD_ZPAD_EXAMPLES]'.replace("{0}","LPAD('hello',8,'*')").replace("{1}","***hello").replace("{2}","RPAD('hello',4,'*')").replace("{3}","hell").replace("{4}","ZPAD('1000',10)").replace("{5}","0000001000").replace("{6}","ZPAD('hello',10)").replace("{7}","hello")}, 'RTRIM': {description:'#TEXT[YMSG_FUNC_TRIM_LTRIM_RTRIM_DESC]'.replace("{0}","LTRIM").replace("{1}","RTRIM"), syntax:'TRIM(text)
LTRIM(text)
RTRIM(text)', examples:'#TEXT[YMSG_FUNC_TRIM_LTRIM_RTRIM_EXAMPLES]'.replace("{0}","TRIM(@YEAR_TO_END)").replace("{1}","' ANNUAL TURNOVER '").replace("{2}","ANNUAL TURNOVER").replace("{3}","LTRIM(' hello world ')").replace("{4}","hello world")}, 'TRANSLATE':{description:'#TEXT[XMSG_FUNC_TRANSLATE_DESC]', syntax:'#TEXT[YMSG_FUNC_TRANSLATE_SYNTAX]'.replace("{0}","TRANSLATE(text,[tType])").replace("{1}","text").replace("{2}","tType").replace("{3}",''), examples:'#TEXT[YMSG_FUNC_TRANSLATE_EXAMPLES]'.replace("{0}","IF(@Status=='1', TRANSLATE('green'), TRANSLATE('red'))").replace("{1}","green").replace("{2}","red").replace("{3}","(TRANSLATE('Football', 'XTIT')=='Football')").replace("{4}","'football'").replace("{5}","'football'").replace("{6}","XTIT").replace("{7}","TRANSLATE").replace("{8}","TRANSLATE")}, 'TRIM': {description:'#TEXT[YMSG_FUNC_TRIM_LTRIM_RTRIM_DESC]'.replace("{0}","LTRIM").replace("{1}","RTRIM"), syntax:'TRIM(text)
LTRIM(text)
RTRIM(text)', examples:'#TEXT[YMSG_FUNC_TRIM_LTRIM_RTRIM_EXAMPLES]'.replace("{0}","TRIM(@YEAR_TO_END)").replace("{1}","' ANNUAL TURNOVER '").replace("{2}","ANNUAL TURNOVER").replace("{3}","LTRIM(' hello world ')").replace("{4}","hello world")}, 'UPPER': {description:'#TEXT[XMSG_FUNC_LOWER_UPPER_DESC]', syntax:'#TEXT[XMSG_FUNC_LOWER_UPPER_SYNTAX]'.replace("{0}","LOWER(text)").replace("{1}","UPPER(text)"), examples:'#TEXT[YMSG_FUNC_LOWER_UPPER_EXAMPLES]'.replace("{0}","LOWER(@COUNTRY_NAME)").replace("{1}","usa").replace("{2}","UPPER(@@)").replace("{3}","ATLAS")}, 'ZPAD': {description:'#TEXT[YMSG_FUNC_LPAD_RPAD_ZPAD_DESC]'.replace("{0}","ZPAD"), syntax:'#TEXT[YMSG_FUNC_LPAD_RPAD_ZPAD_SYNTAX]'.replace("{0}","LPAD(text,len,[pad])").replace("{1}","len").replace("{2}","pad"), examples:'#TEXT[YMSG_FUNC_LPAD_RPAD_ZPAD_EXAMPLES]'.replace("{0}","LPAD('hello',8,'*')").replace("{1}","***hello").replace("{2}","RPAD('hello',4,'*')").replace("{3}","hell").replace("{4}","ZPAD('1000',10)").replace("{5}","0000001000").replace("{6}","ZPAD('hello',10)").replace("{7}","hello")}, // numeric functions 'ABS': {description:'#TEXT[XMSG_FUNC_ABS_DESC]', syntax:'ABS(n)', examples:'#TEXT[XMSG_FUNC_ABS_EXAMPLES]'.replace("{0}","ABS(-3)")}, 'CEIL': {description:'#TEXT[XMSG_FUNC_CEIL_DESC]', syntax:'CEIL(n)', examples:'#TEXT[XMSG_FUNC_CEIL_EXAMPLES]'.replace("{0}","CEIL(3.14)")}, 'FLOAT': {description:'#TEXT[XMSG_FUNC_FLOAT_DESC]', syntax:'FLOAT(str)', examples:'#TEXT[YMSG_FUNC_FLOAT_EXAMPLES]'.replace("{0}","FLOAT('-3.14')")}, 'FLOOR': {description:'#TEXT[XMSG_FUNC_FLOOR_DESC]', syntax:'FLOOR(n)', examples:'#TEXT[XMSG_FUNC_FLOOR_EXAMPLES]'.replace("{0}","FLOOR(3.14)")}, 'HEX': {description:'#TEXT[XMSG_FUNC_HEX_DESC]', syntax:'HEX(str)', examples:'#TEXT[YMSG_FUNC_HEX_EXAMPLES]'.replace("{0}","HEX('6EA5')").replace("{1}","HEX('100')")}, 'INT': {description:'#TEXT[XMSG_FUNC_INT_DESC]', syntax:'INT(str)', examples:'#TEXT[YMSG_FUNC_INT_EXAMPLES]'.replace("{0}","INT('100')").replace("{1}","INT('-3.14')")}, 'LIMIT': {description:'#TEXT[XMSG_FUNC_LIMIT_DESC]', syntax:'#TEXT[YMSG_FUNC_LIMIT_SYNTAX]'.replace("{0}","LIMIT(lower,n,upper)").replace("{1}","lower").replace("{2}","upper"), examples:'#TEXT[YMSG_FUNC_LIMIT_EXAMPLES]'.replace("{0}","LIMIT(0,3,10)").replace("{1}","n").replace("{2}","n").replace("{3}","n").replace("{4}","LIMIT(0,13,10)")}, 'MAX': {description:'#TEXT[XMSG_FUNC_MAX_DESC]', syntax:'MAX(number list)', examples:'#TEXT[XMSG_FUNC_MAX_EXAMPLES]'.replace("{0}","MAX(1,2,3,null)").replace("{1}","MAX(null,null)").replace("{2}","undefined")}, 'MIN': {description:'#TEXT[XMSG_FUNC_MIN_DESC]', syntax:'MIN(number list)', examples:'#TEXT[XMSG_FUNC_MIN_EXAMPLES]'.replace("{0}","MIN(1,2,3,null)").replace("{1}","MIN(null,null)").replace("{2}","undefined")}, 'NSTR': {description:'#TEXT[XMSG_FUNC_NSTR_DESC]', syntax:'#TEXT[XMSG_FUNC_NSTR_SYNTAX]'.replace("{0}","NSTR(n,mask)").replace("{1}","mask").replace("{2}",''), examples:'#TEXT[YMSG_FUNC_NSTR_EXAMPLES]'.replace("{0}","NSTR(@@,'B')").replace("{1}","NSTR(@SD_DOC,'Z')").replace("{2}","@SD_DOC").replace("{3}","NSTR(@@,'10.2')").replace("{4}","NSTR(@TOTAL_PRICE,'C')").replace("{5}","@TOTAL_PRICE").replace("{6}","NSTR").replace("{7}","Logical AND").replace("{8}","NSTR(@FIRST,NORMAL)&NSTR(@SECOND,NORMAL)")}, 'NVAL': {description:'#TEXT[XMSG_FUNC_NVAL_DESC]', syntax:'#TEXT[XMSG_FUNC_NVAL_SYNTAX]'.replace("{0}","NVAL(str)"), examples:'#TEXT[YMSG_FUNC_NVAL_EXAMPLES]'.replace("{0}","NVAL('0xFF')").replace("{1}","NVAL('83')").replace("{2}","NVAL('holiday')")}, 'POS': {description:'#TEXT[XMSG_FUNC_POS_DESC]', syntax:'POS(str)', examples:'#TEXT[YMSG_FUNC_POS_EXAMPLES]'.replace("{0}","POS('100')").replace("{1}","POS('-3.14')")}, 'RND': {description:'#TEXT[XMSG_FUNC_RND_DESC]', syntax:'#TEXT[XMSG_FUNC_RND_SYNTAX]'.replace("{0}","RND(max)").replace("{1}","max"), examples:'#TEXT[YMSG_FUNC_RND_EXAMPLES]'.replace("{0}","RND(68)")}, 'ROUND': {description:'#TEXT[XMSG_FUNC_ROUND_DESC]', syntax:'ROUND(n)', examples:'#TEXT[YMSG_FUNC_ROUND_EXAMPLES]'.replace("{0}","ROUND(11.75)").replace("{1}","ROUND(11.50)").replace("{2}","POS(11.25)")}, 'SIGN': {description:'#TEXT[XMSG_FUNC_SIGN_DESC]', syntax:'SIGN(n)', examples:'#TEXT[YMSG_FUNC_SIGN_EXAMPLES]'.replace("{0}","SIGN(13) ").replace("{1}","SIGN(-5.3)")}, // math & trig functions 'ACOS': {description:'#TEXT[XMSG_FUNC_ACOS_DESC]'.replace("{0}","n").replace("{1}","\u03C0").replace("{2}","n"), syntax:'ACOS(n)', examples:'#TEXT[XMSG_FUNC_ACOS_EXAMPLES]'.replace("{0}","ACOS(0.540302306)")}, 'ASIN': {description:'#TEXT[XMSG_FUNC_ASIN_DESC]'.replace("{0}","n").replace("{1}","-\u03C0/2...\u03C0/2").replace("{2}","n"), syntax:'ASIN(n)', examples:'#TEXT[XMSG_FUNC_ASIN_EXAMPLES]'.replace("{0}","SIN(1)")}, 'ATAN': {description:'#TEXT[XMSG_FUNC_ATAN_DESC]'.replace("{0}","n").replace("{1}","-\u03C0/2...\u03C0/2").replace("{2}","n"), syntax:'ATAN(n)', examples:'#TEXT[XMSG_FUNC_ATAN_EXAMPLES]'.replace("{0}","ATAN(1.557407725)")}, 'ATAN2': {description:'#TEXT[XMSG_FUNC_ATAN2_DESC]'.replace("{0}","-\u03C0/2...\u03C0/2"), syntax:'ATAN2(y,x)', examples:'#TEXT[XMSG_FUNC_ATAN2_EXAMPLES]'.replace("{0}","ATAN2(2,2)")}, 'COS': {description:'#TEXT[XMSG_FUNC_COS_DESC]'.replace("{0}","n").replace("{1}","n"), syntax:'COS(angle)', examples:'#TEXT[XMSG_FUNC_COS_EXAMPLES]'.replace("{0}","COS(1)")}, 'EXP': {description:'#TEXT[XMSG_FUNC_EXP_DESC]'.replace("{0}","e").replace("{1}","n"), syntax:'EXP(n)', examples:'#TEXT[XMSG_FUNC_EXP_EXAMPLES]'.replace("{0}","EXP(2.5)")}, 'LOGN': {description:'#TEXT[XMSG_FUNC_LOGN_DESC]'.replace("{0}","n"), syntax:'LOGN(n)', examples:'#TEXT[XMSG_FUNC_LOGN_EXAMPLES]'.replace("{0}","LOGN(12.18249396)")}, 'POW': {description:'#TEXT[XMSG_FUNC_POW_DESC]'.replace("{0}","n"), syntax:'#TEXT[XMSG_FUNC_POW_SYNTAX]'.replace("{0}","POW(base,n)").replace("{1}","base").replace("{}","n"), examples:'#TEXT[XMSG_FUNC_POW_EXAMPLES]'.replace("{0}","POW(2,10) == 1024")}, 'SIN': {description:'#TEXT[XMSG_FUNC_SIN_DESC]'.replace("{0}","n"), syntax:'SIN(angle)', examples:'#TEXT[XMSG_FUNC_SIN_EXAMPLES]'.replace("{0}","SIN(1)")}, 'SQ': {description:'#TEXT[XMSG_FUNC_SQ_DESC]', syntax:'SQ(n)', examples:'#TEXT[XMSG_FUNC_SQ_EXAMPLES]'.replace("{0}","SQ(6)")}, 'SQRT': {description:'#TEXT[XMSG_FUNC_SQRT_DESC]'.replace("{0}","n"), syntax:'#TEXT[XMSG_FUNC_SQRT_SYNTAX]'.replace("{0}","SQRT(n)"), examples:'#TEXT[XMSG_FUNC_SQRT_EXAMPLES]'.replace("{0}","SQRT(256)")}, 'TAN': {description:'#TEXT[XMSG_FUNC_TAN_DESC]', syntax:'TAN(angle)', examples:'#TEXT[XMSG_FUNC_TAN_EXAMPLES]'.replace("{0}","TAN(1)")}, // date functions 'DATE': {description:'#TEXT[XMSG_FUNC_DATE_DESC]', syntax:'DATE(year,month,day)', examples:'#TEXT[YMSG_FUNC_DATE_EXAMPLES]'.replace("{0}","DATE(2005,4,23)")}, 'DADD': {description:'#TEXT[YMSG_FUNC_DADD_DESC]'.replace("{0}","d").replace("{1}","unit"), syntax:'#TEXT[XMSG_FUNC_DADD_SYNTAX]'.replace("{0}","DADD(d,n,[unit]").replace("{1}","unit").replace("{2}",''), examples:'#TEXT[YMSG_FUNC_DADD_EXAMPLES]'.replace("{0}","DADD(@CREATE_DATE,4,'D')").replace("{1}","DADD(@CREATE_DATE,11,'M')").replace("{2}","DADD(@CREATE_DATE,-3,'Y')")}, 'DGET': {description:'#TEXT[XMSG_FUNC_DGET_DESC]'.replace("{0}","d"), syntax:'#TEXT[XMSG_FUNC_DGET_SYNTAX]'.replace("{0}","DGET(d,part)").replace("{1}","unit").replace("{2}",''), examples:'#TEXT[YMSG_FUNC_DGET_EXAMPLES]'.replace("{0}","DGET(@CREATE_DATE,'M')")}, 'DSTR': {description:'#TEXT[XMSG_FUNC_DSTR_DESC]'.replace("{0}","format").replace("{1}","'DATE'"), syntax:'#TEXT[XMSG_FUNC_DSTR_SYNTAX]'.replace("{0}","DSTR (d,[format])").replace("{1}","format").replace("{2}",''), examples:'#TEXT[YMSG_FUNC_DSTR_EXAMPLES]'.replace("{0}","DSTR(@DATE_FIELD,'MON DD, YYYY')").replace("{1}","DSTR(DADD(@CREATE_DATE,13,'D'),'XML_DATE')").replace("{2}","DADD")}, 'DSUB': {description:'#TEXT[YMSG_FUNC_DSUB_DESC]'.replace("{0}","unit"), syntax:'#TEXT[XMSG_FUNC_DSUB_SYNTAX]'.replace("{0}","DSUB(d1,d2,[unit])").replace("{1}","unit").replace("{2}",''), examples:'#TEXT[YMSG_FUNC_DSUB_EXAMPLES]'.replace("{0}","DSUB(NOW(),@CREATE_DATE,'D')")}, 'DVAL': {description:'#TEXT[XMSG_FUNC_DVAL_DESC]', syntax:'DVAL(str)', examples:'#TEXT[YMSG_FUNC_DVAL_EXAMPLES]'.replace("{0}","DVAL(@TIME_FIELD)")}, 'NOW': {description:'#TEXT[XMSG_FUNC_NOW_DESC]', syntax:'NOW()', examples:'#TEXT[XMSG_FUNC_NOW_EXAMPLES]'.replace("{0}","NOW()")}, // time functions 'TIME': {description:'#TEXT[XMSG_FUNC_TIME_DESC]'.replace("{0}","hour").replace("{1}","min").replace("{2}","sec"), syntax:'TIME(hour,min,sec)', examples:'#TEXT[YMSG_FUNC_TIME_EXAMPLES]'.replace("{0}","TIME(14,30,45) == 14:30:45")}, 'TADD': {description:'#TEXT[YMSG_FUNC_TADD_DESC]'.replace("{0}","t").replace("{1}","unit"), syntax:'#TEXT[XMSG_FUNC_TADD_SYNTAX]'.replace("{0}","TADD (t,n,[unit])").replace("{1}","unit").replace("{2}",''), examples:'#TEXT[YMSG_FUNC_TADD_EXAMPLES]'.replace("{0}","TADD(@CREATE_TIME,13,'H')").replace("{1}","HH:NN:SS")}, 'TGET': {description:'#TEXT[XMSG_FUNC_TGET_DESC]'.replace("{0}","t"), syntax:'#TEXT[XMSG_FUNC_TGET_SYNTAX]'.replace("{0}","TGET(t,part)").replace("{1}","unit").replace("{2}",''), examples:'#TEXT[YMSG_FUNC_TGET_EXAMPLES]'.replace("{0}","TGET(@CREATE_TIME,'S')")}, 'TSTR': {description:'#TEXT[XMSG_FUNC_TSTR_DESC]'.replace("{0}","format").replace("{1}","'TIME'"), syntax:'#TEXT[XMSG_FUNC_TSTR_SYNTAX]'.replace("{0}","TSTR (t,[format])").replace("{1}","format").replace("{2}",''), examples:'#TEXT[YMSG_FUNC_TSTR_EXAMPLES]'.replace("{0}","TSTR(@TIME_FIELD,'H12:NN AM')")}, 'TSUB': {description:'#TEXT[YMSG_FUNC_TSUB_DESC]'.replace("{0}","unit"), syntax:'#TEXT[XMSG_FUNC_TSUB_SYNTAX]'.replace("{0}","TSUB (t1,t2,[unit])").replace("{1}","unit").replace("{2}",''), examples:'#TEXT[YMSG_FUNC_TSUB_EXAMPLES]'.replace("{0}","TSUB(NOW(),@CREATE_TIME,'S')")}, 'TVAL': {description:'#TEXT[XMSG_FUNC_TVAL_DESC]', syntax:'TVAL(str)', examples:'#TEXT[YMSG_FUNC_TVAL_EXAMPLES]'.replace("{0}","TVAL(@TIME_FIELD)")}, 'TNOW': {description:'#TEXT[XMSG_FUNC_TNOW_DESC]', syntax:'TNOW()', examples:'#TEXT[XMSG_FUNC_TNOW_EXAMPLES]'.replace("{0}","TNOW()")}, // datetime functions 'DATETIME': {description:'#TEXT[YMSG_FUNC_DATETIME_DESC]'.replace("{0}","year, month, day, hour, minute, second,").replace("{1}","timezone").replace("{2}","second").replace("{3}","timezone"), syntax:'#TEXT[YMSG_FUNC_DATETIME_SYNTAX]'.replace("{0}","DATETIME(year,month, day,hour, minute,[second],[timezone])").replace("{1}","timezone").replace("{2}","Z"), examples:'#TEXT[YMSG_FUNC_DATETIME_EXAMPLES]'.replace("{0}","DATETIME(2005,4,23,13,30)").replace("{1}","DATETIME(2000,7,4,10,00,Z)").replace("{2}","DATETIME(2000,7,4,10,00,+02:00)").replace("{3}","DATETIME(2000,7,4,10,00,PST)")}, 'DTADD': {description:'#TEXT[YMSG_FUNC_DTADD_DESC]'.replace("{0}","c").replace("{1}","unit"), syntax:'#TEXT[XMSG_FUNC_DTADD_SYNTAX]'.replace("{0}","DTADD (c,n,[unit])").replace("{1}","unit").replace("{2}",'').replace("{3}",''), examples:'#TEXT[YMSG_FUNC_DTADD_EXAMPLES]'.replace("{0}","DTADD(@23/4/2009 13:30,4,'D')")}, 'DTGET': {description:'#TEXT[XMSG_FUNC_DTGET_DESC]'.replace("{0}","c"), syntax:'#TEXT[XMSG_FUNC_DTGET_SYNTAX]'.replace("{0}","DTGET(c,unit)").replace("{1}","unit").replace("{2}",'').replace("{3}",''), examples:'#TEXT[YMSG_FUNC_DTGET_EXAMPLES]'.replace("{0}","DTGET(@CREATE_DATETIME,'D')")}, 'DTSTR': {description:'#TEXT[XMSG_FUNC_DTSTR_DESC]'.replace("{0}","datetime").replace("{1}","mask").replace("{2}","'DATETIME'"), syntax:'#TEXT[XMSG_FUNC_DTSTR_SYNTAX]'.replace("{0}","DTSTR (c,[mask])").replace("{1}","mask").replace("{2}",''), examples:'#TEXT[YMSG_FUNC_DTSTR_EXAMPLES]'.replace("{0}","DTSTR(@DATETIME_FIELD,'HH:NN, MON D, YYYY')").replace("{1}","datetime")}, 'DTSUB': {description:'#TEXT[YMSG_FUNC_DTSUB_DESC]'.replace("{0}","datetime").replace("{1}","unit"), syntax:'#TEXT[XMSG_FUNC_DTSUB_SYNTAX]'.replace("{0}","DTSUB (dt1,dt2,[unit])").replace("{1}","unit").replace("{2}",'').replace("{3}",''), examples:'#TEXT[YMSG_FUNC_DTSUB_EXAMPLES]'.replace("{0}","DTSUB(24/4/2005 14:30, 23/4/2005 13:30,'H')").replace("{1}","datetime")}, 'DTVAL': {description:'#TEXT[XMSG_FUNC_DTVAL_DESC]'.replace("{0}","datetime").replace("{1}","datetime"), syntax:'DTVAL(str)', examples:'#TEXT[YMSG_FUNC_DTVAL_EXAMPLES]'.replace("{0}","DTVAL(23/4/2009 13:30)").replace("{1}","datetime").replace("{2}","datetime").replace("{3}","datetime")}, 'DTNOW': {description:'#TEXT[XMSG_FUNC_DTNOW_DESC]', syntax:'DTNOW()', examples:'#TEXT[XMSG_FUNC_DTNOW_EXAMPLES]'.replace("{0}","DTNOW()")}, // conditional functions 'BOOL': {description:'#TEXT[YMSG_FUNC_BOOL_DESC]'.replace("{0}","true").replace("{1}","true").replace("{2}","t").replace("{3}","yes").replace("{4}","Y").replace("{5}","false"), syntax:'BOOL(value)', examples:'#TEXT[YMSG_FUNC_BOOL_EXAMPLES]'.replace("{0}","BOOL('YES')").replace("{1}","true").replace("{2}","BOOL('NO')").replace("{3}","false").replace("{4}","BOOL(1)").replace("{5}","true")}, 'IF': {description:'#TEXT[YMSG_FUNC_IF_DESC]', syntax:'IF(text,expr1,expr2)', examples:'#TEXT[YMSG_FUNC_IF_EXAMPLES]'.replace("{0}","IF(@NET_PRICE>1000,'Expensive','Cheap')").replace("{1}","TRUE").replace("{2}","FALSE").replace("{3}","IF(@@=='DE','Germany','Other')").replace("{4}","DE").replace("{5}","Germany").replace("{6}","Other").replace("{7}","IF(@BANK_CTRY=='US',United States', IF(BANK_CTRY=='GB','England',IF(@BANK_ CTRY=='IL','Israel',IF(@BANK_CTRY=='DE', 'Germany','Other'))))").replace("{8}","IF").replace("{9}","USnot, GB, IL").replace("{10}","DE").replace("{11}","Other")}, 'ISNULL': {description:'#TEXT[XMSG_FUNC_ISNULL_DESC]'.replace("{0}","TRUE").replace("{1}","FALSE"), syntax:'ISNULL(value)', examples:'#TEXT[YMSG_FUNC_ISNULL_EXAMPLES]'.replace("{0}","IF(ISNULL(@@),'NO FAX NUMBER', @@)").replace("{1)","NO FAX NUMBER")}, 'NVL': {description:'#TEXT[YMSG_FUNC_NVL_DESC]', syntax:'NVL(string list)', examples:'#TEXT[YMSG_FUNC_NVL_EXAMPLES]'.replace("{0}","NVL('a', 'b')").replace("{1}","a").replace("{2}","NVL('', 'b')").replace("{3}","b")}, //Keyword 'null': {description:'#TEXT[YMSG_KW_NULL_DESC]'.replace(/\{0\}/g,"null"), syntax:'',examples:'#TEXT[YMSG_KW_NULL_EXAMPLES]'.replace("{0}","23 - 5 * null").replace("{1}","LEFT(null, 5)").replace("{2}","LEFT('ABC', null)").replace("{3}","'A'")+'#TEXT[YMSG_KW_NULL_SPECAILS]'.replace(/\{0\}/g,"null").replace("{1}","'ABC' & null ").replace("{2}","'ABC'").replace("{3}"," null & null ").replace("{4}","true OR null").replace("{5}","false AND null").replace("{6}","null OR true , null AND false").replace("{7}","FORMAT('{1}-{2}', null, 10)").replace("{8}","FORMAT(null, 'A', null)")}, 'true': {description:'#TEXT[XMSG_KW_TRUE_DESC]',syntax:'', examples:'#TEXT[XMSG_KW_TRUE_EXAMPLES]'.replace("{0}","IF(true,")}, 'false': {description:'#TEXT[XMSG_KW_FALSE_DESC]',syntax:'',examples:'#TEXT[XMSG_KW_FALSE_EXAMPLES]'.replace("{0}","IF(false,")} } metadata translationTypes = { 'XACT':'accessibility', // Accessibility 'XALT':'alternativetext', // Alternative text 'XBCB':'breadcrumbstep', 'XBLI':'listitem', // Bullet list item text 'XBUT':'button', 'XCAP':'caption', 'XCEL':'cell', 'XCKL':'checkbox', // Label for checkbox 'XCOL':'tableColumnHeading', 'XCRD':'tabStrip', 'XDAT':'datanavigationtext', 'XFLD':'label', 'XFRM':'frame', 'XGLS':'term', 'XGRP':'grouptitle', 'XHED':'heading', 'XLGD':'legendtext', 'XLNK':'hyperlink', 'XLOG':'logentry', 'XLST':'listbox', 'XMEN':'menu', 'XMIT':'menuitem', 'XMSG':'messagetext', 'XRBL':'radio', 'XRMP':'roadMapStep', 'XROW':'tableRowHeading', 'XSEL':'selectiontext', 'XTBS':'tab', 'XTIT':'tableTitle', 'XTND':'treeNode', 'XTOL':'quickInfo', 'XTXT':'generaltext', 'YACT':'accessibilitylong', 'YBLI':'list', 'YDEF':'definition', 'YDES':'description', 'YEXP':'explanation', 'YFAA':'faqa', 'YFAQ':'faq', 'YGLS':'glossarydefinition', 'YINF':'informationtextlong', 'YINS':'instruction', 'YLOG':'logEntrylong', 'YMSE':'errorMessage', 'YMSG':'messagetextlong', 'YMSI':'informationMessage', 'YMSW':'warningMessage', 'YTEC':'technicaltextlong', 'YTIC':'ticker', 'YTXT':'generaltextlong', 'ZFTX':'formattedtext' }; metadata tmzToOffset={ ACT:"+09:30", AET:"+10:00", AGT:"-03:00", ART:"+02:00", AST:"-09:00", BET:"-03:00", BST:"+06:00", CAT:"+02:00", CET:"+01:00", CNT:"-03:30", CST:"-06:00", CTT:"+08:00", EAT:"+03:00", ECT:"+01:00", EET:"+02:00", EST:"-05:00", GMT:"Z", HST:"-10:00", IET:"-05:00", IST:"+05:30", JST:"+09:00", MET:"+01:00", MIT:"-11:00", MST:"-07:00", NET:"+04:00", NST:"+12:00", PLT:"+05:00", PNT:"-07:00", PRC:"+08:00", PRT:"-04:00", PST:"-08:00", ROK:"+09:00", SST:"+11:00", UCT:"Z", UTC:"Z", VST:"+07:00", WET:"Z" }; /////////////////////////////////////////////////////////////////////////// // END OF CLASS