/*
note: view source; this is a js file, not an html file Function prototypes, code generators, and dynamic wrappers. maybe re-bind a few like crass to make them not need fn mod, but can be called from outside ###### Provides: ###################### Tool LINE# TITLE Strong 50 Strong Typing for JavaScript. Enforce types on functions using Hungarian Notation: arguments, special strong vars, and returns Crass 255 Stronger Constructor functions Sentry 343 Provides sync-like syntax for using async functions Meth 484 Methods from functions. Publish private functions to a module or globally Expr 580 Create Dynamic Expressions that behave like variables Track 661 Logs each calling of a function on a function._hist property delay 710 Delay a function's execution until a # of ms has elapsed or a passed watch function returns true benchCalls 733 Benchmark a function by creating a new wrapper function that logs its execution times on its _perf property getScriptVars 777 given a url, finds the global variables defined by the script file unbind 813 turns methods into handy callable functions, or re-bind methods a2p 857 a quick way to make property descriptors for feeding into Object.defineProperty() ###################################################################################*/ // line # finder: // el("data").value.split("\n").map(function(a,b){ a=a.trim(); var n; if(n=a.match(/^function\s\w+/)){return n[0]+"\t"+b;}}).filter(Boolean).join("\n"); //################################################################################################################## //################################################################################################################## /* Strong: Strong typing for javascript functions -Provides: -argument type checking using Hungarian notation. rewrites "int_a" into "a". -*variable type checking using Hungarian labels. injects with() and a scope object. -*return type checking using Hungarian notation on the function. injects try/finally and a return object. *///############################## function jsmin(comment,input,level){if(input===undefined){input=comment;comment="";level=2;}else if(level===undefined||level<1||level>3){level=2;}if(comment.length>0){comment+="\n";}var a="",b="",EOF=-1,LETTERS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",DIGITS="0123456789",ALNUM=LETTERS+DIGITS+"_$\\",theLookahead=EOF;function isAlphanum(c){return c!=EOF&&(ALNUM.has(c)||c.charCodeAt(0)>126);} function get(){var c=theLookahead;if(get.i==get.l){return EOF;}theLookahead=EOF;if(c==EOF){c=input.charAt(get.i);++get.i;}if(c>=" "||c=="\n"){return c;}if(c=="\r"){return"\n";}return" ";} get.i=0;get.l=input.length;function peek(){theLookahead=get();return theLookahead;} function next(){var c=get();if(c=="/"){switch(peek()){case"/":for(;;){c=get();if(c<="\n"){return c;}}break;case"*":get();for(;;){switch(get()){case"*":if(peek()=="/"){get();return" ";}break;case EOF:throw"Error: Unterminated comment.";default:;}}break;default:return c;}}return c;} function action(d){var r=[];if(d==1){r.push(a);}if(d<3){a=b;if(a=="'"||a=="\""){for(;;){r.push(a);a=get();if(a==b){break;}if(a<="\n"){throw"Error: unterminated string literal: "+a;}if(a=="\\"){r.push(a);a=get();}}}}b=next();if(b=="/"&&"(,=:[!&|".has(a)){r.push(a);r.push(b);for(;;){a=get();if(a=="/"){break;}else if(a=="\\"){r.push(a);a=get();}else if(a<="\n"){throw"Error: unterminated Regular Expression literal";}r.push(a);}b=next();}return r.join("");} function m(){var r=[];a="\n";r.push(action(3));while(a!=EOF){switch(a){case" ":if(isAlphanum(b)){r.push(action(1));}else{r.push(action(2));}break;case"\n":switch(b){case"{":case"[":case"(":case"+":case"-":r.push(action(1));break;case" ":r.push(action(3));break;default:if(isAlphanum(b)){r.push(action(1));}else{if(level==1&&b!="\n"){r.push(action(1));}else{r.push(action(2));}}}break;default:switch(b){case" ":if(isAlphanum(a)){r.push(action(1));break;}r.push(action(3));break;case"\n":if(level==1&&a!="\n"){r.push(action(1));}else{switch(a){case"}":case"]":case")":case"+":case"-":case"\"":case"'":if(level==3){r.push(action(3));}else{r.push(action(1));}break;default:if(isAlphanum(a)){r.push(action(1));}else{r.push(action(3));}}}break;default:r.push(action(1));break;}}}return r.join("");} jsmin.oldSize=input.length;ret=m(input);jsmin.newSize=ret.length;return comment+ret;} String.prototype.has=function(c){return this.indexOf(c)>-1;}; function Integer(n){ return Math.floor(n); }; var TYPES={ num: Number, int: Number, str: String, bln: Boolean, arr: Array, obj: Object, rx: RegExp, fn: Function, elm: Element, inp: Element, frm: Element, } Function.prototype.strong=function strong(){ var src=jsmin("", this.toString(), 2), fnName=this.name, args=src.split(")")[0].split("(")[1].split(/\s*,\s*/); var guards2=[]; function addGuards(name, hung, alt){ var guards=guards2; if(alt && alt.join){guards=[];} switch(hung){ case "int": guards.push("if(Math.floor("+name+")!=="+name+"){throw new TypeError('"+name+" is not an Integer')}"); case "num": guards.push("if(typeof "+name+"!=='number'){throw new TypeError('"+name+" is not a number')}"); break; case "str": guards.push("if(typeof "+name+"!=='string'){throw new TypeError('"+name+" is not a string')}"); break; case "fn": guards.push("if(typeof "+name+"!=='function'){throw new TypeError('"+name+" is not a function')}"); break; case "bln": guards.push("if(typeof "+name+"!=='boolean'){throw new TypeError('"+name+" is not boolean')}"); break; case "obj": guards.push("if(typeof "+name+"!=='object') {throw new TypeError('"+name+" is not an object');}");break case "frm": guards.push("if((("+name+"||'').nodeName||'').toLowerCase()!='form'){throw new TypeError('"+name+" is not a form')}"); case "rx": guards.push("if("+name+" instanceof RegExp){throw new TypeError('"+name+" is not a RegExp')}");break; case "arr": guards.push("if("+name+" instanceof Array){throw new TypeError('"+name+" is not an Array')}");break; case "elm":case "frm":case "inp": guards.push("if("+name+" instanceof Element){throw new TypeError('"+name+" is not an Element')}"); break; }//end switch return guards; }//end addGuards var sob=[]; var trackers=[]; var varNames=[]; args.map(function(a,b){ var hung=a.split("_")[0], name=a.split("_")[1]||a, type=TYPES[hung]; if(!type){return;} //addGuards(name, hung); src=src.replace(a,name); // sob.push(name+":"+name); varNames.push("_"+name); trackers.push("Object.defineProperty(_SCOPE_, '"+name+"',{configurable:true,"+ "get:function(){return _"+name+";},set:function("+name+"){ "+addGuards(name, hung, []).join("\n") + ";_"+name+"="+name+";} "+ " });"+name+"=arguments["+b+"];") // if(type=="int"){} }); var ss=src; ss.split(/\s*\bVar:\s*/).slice(1).map(function(a){ ss=ss.replace(/\bVar:\s*/gm,""); if(a.match(/^end\b/)){ ss=ss.replace(a, a.slice(3).trim().replace(/}$/m,"")); return; } if(a.match(/^start\b/)){ss=ss.replace(a,""); return; } ss=ss.replace(a,""); var vr=a.split("=",1)[0].split("_"); var name=vr[1], hung=vr[0]; // sob.push(vr[1].trim()+":"+a.split("=").slice(1).join("=").trim().replace(/;$/,"")); // addGuards(vr[1].trim(), vr[0].trim()); varNames.push("_"+name); trackers.push("Object.defineProperty(_SCOPE_, '"+name+"',{configurable:true,"+ "get:function(){return _"+name+";},set:function("+name+"){ "+addGuards(name, hung, []).join("\n") + ";_"+name+"="+name+";} "+ " });"+name+"="+ a.split("=").slice(1).join("=").trim().replace(/;$/,"") +";") return vr }).filter(Boolean).join("\n"); if(src.match(/\bVar:\s*/)){ ss=ss.trim()+"\n }"; } //if function itself has Hungarian name, enforce return typing: var p=fnName.split("_"); if(p.length>1){ varNames.push("RETURN"); ss=ss.replace("{", "{try{")+"finally{ "+addGuards('RETURN', p[0], []).join("\n")+"; return RETURN;}}" //guards; ss=ss.replace(/\breturn\s+/g, "return RETURN="); }//end if Hung function name //prepend trackers ss=ss.replace("{", "{\n "+trackers.join("\n ")+"\n");; //trackers; //prepend guards ss=ss.replace("{", "{\n "+guards2.join("\n ")+"\n");; //guards; //prepend local vars for strong vars: ss=ss.replace("{", "{\n var "+varNames.join(",")+";\n");; //varNames; //prepend with for Var checking: ss=ss.replace("{", "{\n"+" var _SCOPE_={"+sob.join(",")+"}; with(_SCOPE_){"+"\n"); ss=ss.replace(/\}\s*$/," }\n}") return eval("0||"+ss); };//end Strong() ###################### /* function int_sum2( num_x, num_y){ Var:start; Var: int_n=12345; Var: str_s=" world"; Var: bln_b=false; Var:end return a+b; } alert(int_sum2.strong()); //end Strong demos */ //################################################################################################################## /*################################## /* ################################## Crass: stronger Constructor functions. lets you easily specify visibility and scope of object properties Roles: public: A normal this.property style property private: A variable unreachable from outside the constructor, but still available later to anything defined within the constructor. static: A variable held by the constructor's function object. Available to all instance of the class. eg(Person.lastBorn) global: A variable that is pushed to the top scope, becoming available to all scripts as a property of global object instead of the new object. prototype: A static property available to and inherited by all instance of the object. Any changes to prototype properties are instantly inherited by all instances. Often used for utility functions, inside of which this refers to the object. lambda: A special type of variable. Instead of holding a primitive property, it holds an expression in an anonymous function that returns the primitive you need. This can be used to provide extra properties without building each ahead of time. It should be used read-only, assigning a value to a lambda property will break it. Example code: function Person(strName){ var born=new Date; // private property (default) private: var safeName= escape(strName);// (default) static: var lastBorn= born; // bound to constructor object proto: var previous= 7567; // prototype property (inherently static) global: var lastRun= born.getTime(); // global property (not in resulting object) public: var bornOn= born; // regular properties are usually public public: var getBirth= function(){ // regular methods are usually public return String(this.bornOn); }//end getBirthday lambda: var age= function(){return (new Date).getTime() - born.getTime(); } this.api=(Crass(eval(Crass(arguments,this)))); // run Crass! }//end Person() ################################# */ function Crass(fna, scope){ // class builder by dandavis. var X=Crass; if(scope){ var ExportList =[], Flags={}, fn=fna.callee; var varList=( fn.toString2().match(/(\n[\t ]*\w+\:\s+)+var \w+/g)||[]); varList.map(function(a){ halves=a.split(/var \s*/); var vName=halves[1].split(/[\s]+/g)[0], tokens ={}; halves[0].split(/[\s:]+/g).filter(String).map(function(z){tokens[z]=1;}); ExportList.push(vName); Flags[vName]=tokens; }); //end variable list iteration X.flags=Flags; //1 X.pack=ExportList; //1 X.that=scope; X.called=fn; //1 return ("(["+ X.pack.join(",") +"])"); }//scope? return obValsl(fna).map(function(a,n){ var key=Crass.pack[n]; var ob= this ; var Fs=X.flags[key]; if(Fs){ if(Fs.proto===1){ ob= (this.Crass == X) ? Object.prototype : Crass.called.prototype; ob[key] = a; } if(Fs.global===1){ window[key] = a } if(Fs.private===1){ // this is the default, nothing to do here for now... } if(Fs.static===1){ X.called[key] = a } if(Fs.public===1){ ob[key] = a } if(Fs.lambda===1){ // lambda expression (use sparingly) ob[key] = { valueOf:function(){return a.call(ob,ob);}, toString:function(){return this.valueOf(); } }; } }else{ ob[key] = a; } //end if flags return key; } , Crass.that); // Crass depends: function obValsl(ob){var r=[],i=0,mx=ob.length; for(var z=0;z2){ throw new TypeError( "Sentry: duplicate 'endSentry' keyword detected in "+caller.name+"() at line #"+ (wr.slice(0,2).join(" ").split("\n").length).toString()); } var pc2=str.toString().replace("new Sentry(arguments)","new Sentry").split(/\bendSentry\b/)[0].split(/\bnew Sentry\b/)[1].split(/\bcontinueSentry/); var myCBs=pc.map(function(a,b,c){ var varName=a.split(/Sentry\(([^\)]+)\)/)[1]; var cmd=varName; var newCB=String("function _sentry_"+varName+"(value){"+varName+"=value; "+ "var sentryOb="+fnName+".sentry.callBacks['"+varName+"']; sentryOb.expired=true;sentryOb.runtime=(sentryOb.finished=+new Date)-sentryOb.born; if(!--_left){tail();}}"); var ret= {name: varName, cb: newCB, born: +new Date, expired: false}; return lut[varName]=ret; }); var bod=pc2.map(function(a){return a.replace(/Sentry\(([\w_$]+)\)/g, function(j,nam){ return lut[nam].cb; }); }).join("\n"); var myCode=[head, bod,tail,"}"].join("\n"); var myFN=eval("0||"+myCode); var out={complete:tail, callBacks: lut, count: left, caller: caller.name+"("+ar+")", born:+new Date }; myFN.sentry=out; myFN.apply(); return out; }//end Sentry() /* Sentry demos: //AJAX Demo: //normal async ajax function: function aGet(turl, callback) { var XHRt=new XMLHttpRequest; XHRt.onreadystatechange = function () {if (XHRt.readyState == 4 && XHRt.status == 200) {callback(XHRt.responseText, XHRt);}}; XHRt.open("GET", turl, true); XHRt.send(""); return XHRt; };window.aGet=aGet; function addScript(u) { var sc2 = document.createElement("script"); sc2.src = u; document.getElementsByTagName("*")[1].appendChild(sc2); } function jsonp(url, cb){ var cbn="cb"+(Math.random()*9e14).toString(36).slice(0,7); jsonp[cbn]=cb; addScript(url+"."+cbn); } var demos={ ajax: function demoAJAX(){ function getSomePages(page){ var page, site, snip; //defines placeholders for async operation returns //plucks the title from an html page's string source: function getTitle(strHTML){ return strHTML.split("")[0].split(" ").slice(-1)[0]; } //install sentry by returning an invocation BEFORE you do anything else: return new Sentry(arguments); aGet(page, Sentry(page) ); continueSentry; aGet("/", Sentry(site) ); continueSentry; aGet("/snips.htm", Sentry(snip) ); endSentry; document.getElementById("demo").innerHTML=( getTitle(page)+" | "+getTitle(site)+ " || " + getTitle(snip) ); } window.gg=getSomePages(location.href); }, jsonp: function demoJSONP(searchTerm){ var goog, yah, wiki, twit; searchTerm=searchTerm||"obama"; return new Sentry(arguments); jsonp("http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20answers.search%20where%20query%3D%22"+encodeURIComponent(searchTerm)+"%22%20&format=json&callback=jsonp", Sentry(yah)); continueSentry; jsonp("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q="+encodeURIComponent(searchTerm)+"&callback=jsonp", Sentry(goog)); continueSentry; jsonp("http://search.twitter.com/search.json?q="+encodeURIComponent(searchTerm)+"&rpp=5&include_entities=true&result_type=mixed&callback=jsonp", Sentry(twit)); continueSentry; jsonp("http://en.wikipedia.org/w/api.php?action=opensearch&search="+encodeURIComponent(searchTerm)+"&namespace=0&callback=jsonp", Sentry(wiki)); endSentry; document.getElementById("demo").innerHTML=( "Google:\n"+goog.responseData.results.map(function(a){ return a.titleNoFormatting.link(a.unescapedUrl);}).join("\n")+"\n\n\n" + "Wikipedia Suggestions:\n"+ wiki[1].join(", ") +"\n\n\n" + "Twitter:\n"+twit.results.map(function(a){ return a.text;}).join("\n")+"\n\n\n" + "Yahoo Answers:\n"+yah.query.results.Question.slice(2,8).map(function(a){ return a.Subject.link(a.Link);}).join("\n") ); } }//end demos */ //################################################################################################################## //################################################################################################################## // Meth: Methods from functions. Publish named functions inside another function to global scope or a module object. function Meth(f, s){ //publishes functions in a function to the outer scope unless they start with "_" var M= Meth, r; if(s){ r = String(f).match(M.rxf) || []; M.p=r.map(M.fnf); M.t=s; return "([" + M.p.join(",") + "])"; } if(M.t!==this){f.map(M.fnx, M.t );return M.t;}else{ return f.map(M.fnx, M.t ); } }//end fn Meth.fnf=function (a) {a = a.slice(9, -1).replace(Meth.rxc, "");return a;}; Meth.fnx=function (a, n) {var b = Meth.p[n];this[b] = a;return b;}; Meth.rxf=/\bfunction\b\s+[a-zA-Z$][\w$]+\(/g; Meth.rxc=/[^\w_$]/g; //end Meth() /* Meth demos: var myMod=( new function(name){ //class-like usage using new and this: function sum(a,b){return a+b;} function times(a,b){return a*b;} function arg2(a,b){return b;} var nameUC=String(name).toUpperCase(); //variables are private: function _big(str){return String(str).big();} //functions starting with "_" are not published: //non-function properties in the return object are added using this: this.born=+new Date; this.name=_big(nameUC||"ANON"); //call Meth and feed it 'this' to publish floating functions to the return object as methods: Meth(eval(Meth(arguments.callee, this )));//blank module destination (returns object of methods) }('dave'));//end anon myMod//==Object {born=1234567897384, name="dave", sum=sum(), times=times(), arg2=arg2()} var myMod=(function(){ //publish to blank module object: function sum(a,b){return a+b;} function times(a,b){return a*b;} function arg2(a,b){return b;} var mod=Meth(eval(Meth(arguments.callee, {} )));//blank module destination (returns object of methods) return mod; }());//end anon //myMod==Object { sum=sum(), times=times(), arg2=arg2()} var myModList=(function(){ //publish to window (unpack functions). // note: if not using "new", only function are grabbed, so Meth can be called at the top and still find all hoisted functions. return Meth(eval(Meth(arguments.callee, this ))); //global/inherited scope destination (returns list of method names added) function sum(a,b){return a+b;} function times(a,b){return a*b;} function arg2(a,b){return b;} }());//end anon //myModList==["sum", "times", "arg2"] //named function as methods of a Node.JS (CommonJS) module: exports=(function(){ //publish to module. return Meth(eval(Meth(arguments.callee, typof exports!=="undefined" ? exports || {} ))); function sum(a,b){return a+b;} function times(a,b){return a*b;} function arg2(a,b){return b;} }());//end anon //myModList==["sum", "times", "arg2"] */ //################################################################################################################## //################################################################################################################## //Expression Objects function Expr(s,p){ if(this.Array===Array){return new Expr(s,p);} if(p && p.constructor===Object && !p._val){ for(it in p){if(p.hasOwnProperty(it)){this[it]=p[it];}} }else{ this.x=p; } this._val=s; return this; }; if(Object.defineProperty){ Object.defineProperty(Expr.prototype, "valueOf", { get:function(){ var t=this;return function(){ with(t){return eval(t._val);} }}, enumerable : false, configurable : false }); Object.defineProperty(Expr.prototype, "toString", { get:function(){ var t=this;return function(){ with(t){return String(eval(t._val));} }}, enumerable : false, configurable : false }); }else{ Expr.prototype=({ valueOf: function(){with(this){return eval(this._val);}}, toString: function(){with(this){return eval(this._val);}} });//end Expr constructor }//end if ecma5? /* //examples: var n=10, n2=Expr("n*n"); //alert([+n2,n=4,+n2, n=33, +n2 ]) //100,4,16,33,1089 var rgb=new Expr("((r*0.29)+(g*0.6)+(b*0.31))", {r:100,g:50,b:200}); //alert(rgb);//"121" var x=new Expr("x=x+10, x",0); //alert([x,x,x,x,x])//"10,20,30,40,50" var TIMER=new Expr("x.getTime()",{x:new Date}); //alert(TIMER) //show current unix time var time=Expr("x=(new Date).toTimeString().split(' ')[0].split(':'),x[0]=x[0]>12?x[0]-12:x[0],x.join(':')"); //alert(time)//3:05:07 var flip=new Expr("x=!x"); //alert([flip,flip,flip,flip,flip])//"true,false,true,false,true" var random=Expr("Math.round( Math.random()*255);") //alert([random,random,random,random]) var rndColor=Expr("'rgb('+[x,x,x]+')'" , Expr("Math.round( Math.random()*255);")) //alert(rndColor);//rgb(47,237,12) var h=Expr( "x.slice(0,++y)" , {x:"Hello", y:0} ); //alert([h,h,h,h,h])//"H,He,Hel,Hell,Hello" var since=Expr( "(new Date)-x" , new Date ); //setTimeout(function(){ alert(since); }, 1000);//1006 var add=Expr( "x" , { x:1, add:function(n){this.x=this.x+n; return this;}} ); //alert( [add," ",+add,+add.add(15),+add.add(1000),+add.add(760)] )//"3551, ,1776,1791,2791,3551" var Log=Expr( "x" , { x:"", log:function(n){Log.x=(Log.x||"")+n+"|"; return Log.x;}} ), Log=Log.log; //alert( [Log("a"), 0,Log("b"), 0,Log("c"),0, Log.x ]);//"a|,0,a|b|,0,a|b|c|,0,a|b|c|" var canTry=Expr("0