You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
533 lines
20 KiB
533 lines
20 KiB
<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>
|
|
|