UNPKG

dojox

Version:

Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.

534 lines (487 loc) 20.1 kB
<html><head> <script type="text/javascript"> Array.prototype._toString = Array.prototype.toString; Array.prototype.toString = function(){ return "[" + this._toString() + "]"; }; Number.prototype._toString = Number.prototype.toString; Number.prototype.toString = function(radix){ if(!radix || radix==10){ return ""+this._toString() } if(radix==2){ return "0b"+this._toString(radix); } if(radix==8){ return "0o"+this._toString(radix); } if(radix==16){ return "0x"+this._toString(radix); } return this._toString(radix) + "#" + radix._toString(); }; (function(){ // env is private parseFloat = function(str){ var obj = null; function getRadixObject(whole, numberStr, base){ obj = {number:numberStr, radix:base}; return ""; } function getBaseString(whole, base, numberStr){ var baseNum = (base == '0b') ? 2 : (base == '0o') ? 8 : 16; return numberStr+"#"+baseNum; } var str = str.replace(/(0b|0o|0x)(([0-9a-zA-Z]+\.[0-9a-zA-Z]*|\.[0-9a-zA-Z]+|[0-9a-zA-Z]+))/, getBaseString); var radix; var poundRegExp = /([0-9a-zA-Z]+\.[0-9a-zA-Z]*|\.[0-9a-zA-Z]+|[0-9a-zA-Z]+)\#([0-9a-zA-Z]+)/g; if(poundRegExp.test(str)){ // define obj in getRadixObject str.replace(poundRegExp, getRadixObject); radix = obj.radix; str = obj.number; } if(!radix){ radix = 10; } var decimal = str.indexOf('.'); if(decimal>=0){ var left = str.substring(0,decimal); if(left == ""){ left = "0"; } var right = str.substring(decimal+1, str.length); if(right == ""){ right = "0"; } var num = parseInt(left, radix); var dec = parseInt(right, radix); var length = dec._toString().length; dec /= Math.pow(radix, right.length); return num+dec; } return parseInt(str, radix); }; var env = { window: undefined, "delete": undefined, "new": undefined, // invalidate access to the globals for security so that window.blah is invalid as is delete window prompt: function(label, defaultValue){ // summary: // prompt the user for input without making IE8 mad. // label: // the String name of the variable you are asking the user to input // defaultValue: primitive // the prompt will show this value initially label = label || ''; defaultValue = defaultValue || ''; try{ return this.outerPrompt(label, defaultValue); }catch(e){ return window.prompt(label, defaultValue); } }, val: function(local, name, obj){ // summary: // allow the user to use locals, globals, or give input for undefined values // local: a primitive object // what value might've been passed to val as a local parameter. // name: String // String name of the object. It is passed to see if there is an available global or to set the global. var objValue = obj[name]; local = (objValue == undefined) ? (local == undefined ? window[name] : local) : objValue ; if(typeof local == 'undefined'){ if(obj.graphing){ return undefined; } local=this.prompt('Enter the value for ' + name, undefined, name); return local == null ? undefined : (isNaN(Number(local)) ? local : Number(local)); } return local; }, _makeKeywordsLiteral: function (text){ // summary: // surround reserved words with hex characters // ==, then >=. the <=, then !=, then = and surround them with \x0n first text = text.replace(/(!=(?!=)|==|>=|<=|=)/g, "\x02$1\x02"); return text.replace(/\b(abstract|boolean|break|byte|case|catch|char|class|const|continue|debugger|default|do|double|else|enum|export|extends|false|final|finally|float|for|function|goto|if|implements|import|in| instanceof|int|interface|long|native|null|package|private|protected|public|return|short|static|super| switch|synchronized|this|throw|throws|transient|true|try|typeof|var|void|volatile|while|with)\b/g, "\x02$1\x02"); }, switchToGermanNumbers: function(text){ // summary: // change the format of some javascript allowed numbers to an alternate form 3.5 goes to 3,5 var num = 0; var numbers = []; function substituteCommas(whole, junk, text){ numbers['_'+num+'_'] = text.replace(/\,/g, ';'); return junk+'_'+(num++)+'_'; } function substituteNumbers(wholematch, stuff, junk, text, stuff2){ return stuff+numbers[text]+stuff2; } if(typeof text != "string"){ text = text.toString(); } if(text.indexOf('\x02')<0){ text = this._makeKeywordsLiteral(text); //text = text.replace(/(!=(?!=)|==|>=|<=|=)/g, "\x02$1\x02"); } // same as semiColonRegExp var commaRegExp = /([^\x02\s]*)(\([^\(\)]+\)|\[[^\[\]]+\])/; while(commaRegExp.test(text)){ text = text.replace(commaRegExp , substituteCommas); } while(/((\W)*)(\_[0-9]+\_)((\W)*)/.test(text)){ text = text.replace(/((\W)*)(\_[0-9]+\_)((\W)*)/g, substituteNumbers); } text = text.replace(/([0-9]*)\.([0-9]+)/g, "$1"+','+"$2"); // get rid of keyword characters (duplicate code from parse()) return text.replace(/\x02((.|\s)*?)\x02/g, "$1"); }, normalizeTextLanguage: function(text){ // summary: // change text to use javascript list separators and decimal points instead of commas // text: String // string like "pow(3,1; 4);" // for that input, this method should return "pow(3.1, 4);" var num = 0; var numbers = []; function substituteSemicolons(whole, junk, text){ numbers['_'+num+'_'] = text.replace(/\;/g, ','); return junk+'_'+(num++)+'_'; } function substituteNumbers(wholematch, stuff, junk, text, stuff2){ return stuff+numbers[text]+stuff2; } if(text.indexOf('\x02')<0){ text = this._makeKeywordsLiteral(text); } // change all comma-type decimals into periods text = text.replace(/([0-9]*)\,([0-9]+)/g, "$1"+'.'+"$2"); //alert(text); var semiColonRegExp = /([^\x02\s]*)(\([^\(\)]+\)|\[[^\[\]]+\])/; // /([^\x02\s]+)(\([^\(\)]+\)|\[[^\[\]]+\])/; // /[^\x02\s]+\([^\(\)]+\)|[^\x02\s]+\[[^\[\]]+\]/; // /[^\x02]\s*\([^\(]+\)/; while(semiColonRegExp.test(text)){ text = text.replace(semiColonRegExp, substituteSemicolons); } while(/((\W)*)(\_[0-9]+\_)((\W)*)/.test(text)){ text = text.replace(/((\W)*)(\_[0-9]+\_)((\W)*)/g, substituteNumbers); } // get rid of keyword characters (duplicate code from parse()) return text.replace(/\x02((.|\s)*?)\x02/g, "$1"); }, preparse: function(text){ // summary: // change the numbers to javascript allowed numbers if it is from a different locale if(!this.isJavaScriptLanguage){ text = this.normalizeTextLanguage(text); } return text; }, // parseFloat(Number(4.5).toString(3)) should return 4.5 postparse: function(text){ // summary: // make the javascript numbers match the locale if(!this.isJavaScriptLanguage){ text = this.switchToGermanNumbers(text); } return text; }, toDecimalRegExp: function(whole, base, number){ // summary: // get the decimal number from a float function getBase(base){ switch(base){ case '0b': return 2; case '0o': return 8; case '0x': return 16; default: return 10; } } //return parseInt(base, getBase(base)); return parseFloat(base+number); }, parse: function(text){ // summary: // This parses augmented mathematical syntax and converts it to executable JavaScript. // text: String // should be a String which represents an expression, a function, a number, etc // this array holds the substitutions that represent an expression's parentheses and functions in a simplified way. var numbers = [], // characters functionContentsChar = '\x03', // these are the regular expression needed to find and replace operators factorialRE = /((\w|\.)+)\s*\!/, unaryRightsideRE = /(^|[\+\-\/\*\!\^\u221A]+\s*)([\+\-]+)\s*((\w|\.)+)(?!.*[\+\-])/, unaryRE = /(^|[^a-zA-Z0-9_\.\s]+\s*)([\+\-]+|\u221A)\s*((\w|\.)+)/, // /(^|[^a-zA-Z0-9_\.]\s*)([\+\-]+|\u221A)\s*((\w|\.)+)/, caretRE = /((\w|\.)+)\s*([\^])\s*((\w|\.)+)/, // /((\w|\.)+)\s*([\^])\s*(-?\s*(\w|\.)+)/, radicalRE = /((\w|\.)+)\s*(\u221A)\s*([+-]?\s*(\w|\.)+)/, // /((\w|\.)+)\s*(\u221A)\s*([+-]?\s*(\w|\.)+)(?!.*\u221A)/, binaryHighRE = /((\w|\.)+)\s*([*/])\s*((\w|\.)+)/, binaryLowRE = /((\w|\.)+)\s*([\+\-])+\s*((\w|\.)+)/; // get rid of all new and delete statements text = exterminateNewAndDelete(text); // make all keywords and operators literal (like '!=' or 'return') by surrounding them with unprintable characters text = this._makeKeywordsLiteral(text); function getBaseTen(whole, numberStr, base){ return ""+parseFloat(numberStr+"#"+parseInt(base)); } text = text.replace(/(0b)([0-1]+\.[0-1]*|\.[0-1]+|[0-1]+)/g, this.toDecimalRegExp); text = text.replace(/(0o)([0-7]+\.[0-7]*|\.[0-7]+|[0-7]+)/g, this.toDecimalRegExp); text = text.replace(/(0x)([0-9a-zA-Z]+\.[0-9a-zA-Z]*|\.[0-9a-zA-Z]+|[0-9a-zA-Z]+)/g, this.toDecimalRegExp); text = text.replace(/([0-9a-zA-Z]+\.[0-9a-zA-Z]*|\.[0-9a-zA-Z]+|[0-9a-zA-Z]+)\#([0-9a-zA-Z]+)/g, getBaseTen); // parse the input to form JavaScript return MathParser(text); function MathParser(text){ // Pi is better represented by \u03C0, but isn't computable text = text.replace(/\u03C0/g,"Math.PI"); text = text.replace(/\u03F5/g,"Math.E"); // Microsoft word will give you these '\u2013' for a subtraction sign sometimes, so I'll fix that here text = text.replace(/\u2013/g, "-"); // add unreadable characters in front of functions so it is easier to manipulate text = text.replace(/(((\w|\.)+\s*)\()/g,putFuncChar); // change 10e-10 or 10e10 or 10e+10 to an constant right away text = scientificNotationSimplifier(text); // recursively track down and replace all simplified parts of the expression until everything is simple; then move the simple text into the correct JavaScript format text = parseRecursive(text); // now resubstitute the real text back into the string now that it is arranged correctly while (hasArrayIndexBasedOnUnderscore(text)){ text = text.replace(/\_(\d+)\_/g, function(whole, one){return numbers[parseInt(one)]}); } // get rid of the function character from the functions now text = text.replace(/\x01/g,""); // get rid of keyword characters too text = text.replace(/\x02((.|\s)*?)\x02/g, "$1"); return text; } function exterminateNewAndDelete(text){ return text.replace(/\b(delete|new|this\.*)\b/, ""); } // A simple grouping has no parentheses inside the initial pair. function isSimpleGrouping(text){ return (/^\([^()]*\)|\W\([^()]*\)/).test(text); } function ReplaceSimpleGrouping(text){ return text.replace((/(^|\W)(\([^()]*\))/), replaceWithNum); } function replaceWithNum(wholeMatch, partMatch1, partMatch2){ numbers.push(partMatch2); return partMatch1+"_"+(numbers.length-1)+"_"; } function replaceFuncWithNum(wholeMatch){ numbers.push(wholeMatch); return "_"+(numbers.length-1)+"_"; } // A simple function has no parentheses within its parentheses, and has the special character \x01 leading up to the name and (...) function isSimpleFunction(text){ return (/\x01((\w|\.)+\s*)\([^()]*\)/).test(text); } function ReplaceSimpleFunction(text){ return text.replace(/\x01((\w|\.)+\s*)\([^()]*\)/, replaceFuncWithNum); } function FactorialParser(text){ // loop this until all ! are gone (in main) return text.replace(factorialRE, "\x01"+"factorial($1)"); } function putFuncChar(wholematch){ return '\x01'+wholematch; } function putInPiece(wholematch){ return numbers[currentNum]; } function caretParser(text){ return text.replace(caretRE, replaceBinaryOp); } // the unary radical is inside the unaryOperatorParse(text) function function radicalOperatorParse(text){ return text.replace(radicalRE, replaceBinaryOp); } function replaceBinaryOp(wholeMatch, operand1, nothing1, operator, operand2){ switch(operator.charAt(0)){ case '^': return "\x01"+"pow("+operand1+","+operand2+")"; case '\u221A': return "\x01"+"pow("+operand2+", 1/"+operand1+")"; case '*': case '/': return replaceFuncWithNum(wholeMatch); case '+': case '-': case 'e': return replaceFuncWithNum(simplifyPlusAnsMinus(wholeMatch)); } } // e is treated as a binary operator function scientificNotationSimplifier(text){ //return text.replace(/((\w|\.)+)\s*([\e])\s*([+-]*\s*(\w|\.)+)/g, replaceBinaryOp); return text.replace(/(([0-9]+\.?[0-9]*))(e)([+-]*([0-9]+))/g, replaceBinaryOp); } // this handles * and / function binaryHighPriorityOperatorParse(text){ return text.replace(binaryHighRE, replaceBinaryOp); } // this handles + and - with two operands function binaryLowPriorityOperatorParse(text){ return text.replace(binaryLowRE, replaceBinaryOp); } // this replaces the one operand +'s, -'s, and radicals function replaceUnaryOp(wholeMatch, garbage, operator0, operand0){ // see what operator it is switch(operator0.charAt(0)){ case '\u221A': return garbage+"\x01"+"sqrt("+operand0+")"; case '+': case '-': return garbage+replaceFuncWithNum(simplifyPlusAnsMinus(wholeMatch.substr(garbage.length))); } } // this handles one operand operators -, +, radical function unaryOperatorParse(text){ return text.replace(unaryRE, replaceUnaryOp); } // this handles one operand operators on the right side of everything function unaryOperatorRightsideParse(text){ return text.replace(unaryRightsideRE, replaceUnaryOp); } function parseRecursive(text){ // keep going until there is nothing left that can be simplified while (isSimpleGrouping(text)||isSimpleFunction(text)||factorialRE.test(text)||caretRE.test(text)||radicalRE.test(text)||unaryRE.test(text)||binaryHighRE.test(text)||binaryLowRE.test(text)){ var sTextOriginal = text; if(isSimpleFunction(text)){ var debugText = text; text = ReplaceSimpleFunction(text); var s = numbers[numbers.length-1], pL = s.indexOf("("), funcName = s.substring(s.indexOf("\x01")+1, pL),//take out the function character content = s.substring(pL+1, s.length-1); numbers[numbers.length-1] = funcName+"("+parseRecursive(functionContentsChar+content)+")"; continue; } if(isSimpleGrouping(text)){ text = ReplaceSimpleGrouping(text); var s = numbers[numbers.length-1], content = s.substring(1, s.length-1); numbers[numbers.length-1] = "("+parseRecursive(content)+")"; continue; } // factorials must come first for 5^2! cases, it equals 25 text = FactorialParser(text); if(text!=sTextOriginal){ continue; } text = caretParser(text); if(text!=sTextOriginal){ continue; } text = radicalOperatorParse(text); if(text!=sTextOriginal){ continue; } text = unaryOperatorRightsideParse(text); if(text!=sTextOriginal){ // put parentheses around the unary operators so 0--2 will pass numbers[numbers.length-1] = "("+numbers[numbers.length-1]+")"; continue; } text = unaryOperatorParse(text); if(text!=sTextOriginal){ // put parentheses around the unary operators so 0--2 will pass numbers[numbers.length-1] = "("+numbers[numbers.length-1]+")"; continue; } text = binaryHighPriorityOperatorParse(text); if(text!=sTextOriginal){ continue; } text = binaryLowPriorityOperatorParse(text); if(text!=sTextOriginal){ continue; } // it should never ever make it to this point. If it does, I'll want to know about it. throw("Parse Error"); } // make assignments within function calls be arranged to be (x=y, undefined) if(text.charAt(0) == functionContentsChar){ text = text.substring(1, text.length); if(text.indexOf('=') >= 0){ text = ReplaceFunctionContentAssignments(text); } } return text; } function ReplaceFunctionContentAssignments(text){ return text.replace(/((\w|\.)+)\x02=\x02((\w|\.)+)/g, "("+"$1"+'\x02=\x02'+"$3"+", {_name:'"+"$1"+"', _value:"+"$3"+"})"); } function hasArrayIndexBasedOnUnderscore(text){ return /\_\d+\_/.test(text); } function simplifyPlusAnsMinus(text){ function hasUnsimplifiedPlusMinus(text){ return /\-\s*\-/.test(text)||/\+\s*\+/.test(text)||/\+\s*\-/.test(text)||/\-\s*\+/.test(text); } while(hasUnsimplifiedPlusMinus(text)){ text = text.replace(/((\-\s*\-)|(\+\s*\+))/g, "+"); text = text.replace(/((\+\s*\-)|(\-\s*\+))/g, "-"); } return text; } }, eval: function(text){ // summary: // create an anonymous function to run the code the parser generates from the user input. // text: String // is the user input that needs to be parsed // this try catch is necessary to ensure that any incorrect input will receive NaN (it won't cause a self destruct from a user typo) try{ var value = this.Function(undefined, '', 'return ' + text).call(this); // make sure it is called within the correct scope. //value = ((value instanceof Array || typeof value == "array") ? ("["+value+"]") : value.toString()) return this.postparse(value); }catch(e){ return NaN; } }, normalizedFunction: function(name, args, body){ // summary: // create an anonymous function to run the code the parser generates from the user input. // name: String // this argument is simply a String that represents the name of the function being evaluated. It can be undefined, but in that case the function is a one time use. // args: String // the function arguments // body: String // the function body var argMultiVals = '', // make an array of parameters with what's given (if nothing is given, make an empty array) params = args ? (args||'').split(/\W+/) : []; argMultiVals += 'var _localObjectContainer = {};\n'+ 'for(var _objCnt = 0; _objCnt < arguments.length; _objCnt++){\n'+ 'if(typeof arguments[_objCnt] == "object" && "_name" in arguments[_objCnt] && "_value" in arguments[_objCnt]){\n'+ '_localObjectContainer.graphing = arguments[_objCnt]._graphing;\n'+ '_localObjectContainer[arguments[_objCnt]._name]=arguments[_objCnt]._value;\n'+ 'arguments[_objCnt]=undefined;\n}\n}\n'; // make the parameters fit into the val function so it can use either globals, locals, or if all are null, prompt the user for input for(var i=0; i < params.length; i++){ var param = params[i]; argMultiVals += param + '=val(' + param + ',"' + param + '", _localObjectContainer);\n'; argMultiVals += 'if(typeof '+param+' == "undefined"){\n'+ 'return null;}\n'; } var _f_ = window.Function.apply(this, [ args, 'with(Math){with(this.dojox.math){with(this){\n' + argMultiVals + this.parse(body) + '\n}}}' ] ); if(name){ return this[name] = _f_; } return _f_; }, Function: function(name, args, body){ // summary: // create an anonymous function to run the code the parser generates from the user input. It also normalizes the language format. // name: String // this argument is simply a String that represents the name of the function being evaluated. It can be undefined, but in that case the function is a one time use. // args: String // the function arguments // body: String // the function body body = this.preparse(body); return this.normalizedFunction(name, args, body); } }; for(var i in window){ if(!(i in env) && i != "alert" && i != "confirm"){ env[i] = undefined; } // invalidate access to the window object's attributes for security so that document.blah is invalid } frameElement.onload(env); })(); </script> </head><body> <script type="text/javascript"> document.documentElement.removeChild(document.documentElement.firstChild); // remove HEAD to remove debugger access for extra security </script> </body></html>