diff options
Diffstat (limited to 'js/dojo/dojox/lang')
41 files changed, 3960 insertions, 0 deletions
diff --git a/js/dojo/dojox/lang/LICENSE b/js/dojo/dojox/lang/LICENSE new file mode 100644 index 0000000..d84a6aa --- /dev/null +++ b/js/dojo/dojox/lang/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2007 Oliver Steele + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/js/dojo/dojox/lang/README b/js/dojo/dojox/lang/README new file mode 100644 index 0000000..1967de4 --- /dev/null +++ b/js/dojo/dojox/lang/README @@ -0,0 +1,71 @@ +------------------------------------------------------------------------------- +dojox.lang +------------------------------------------------------------------------------- +Version 0.991 +Release date: 07/30/2007 +------------------------------------------------------------------------------- +Project state: +beta +------------------------------------------------------------------------------- +Credits + Eugene Lazutkin (eugene.lazutkin@gmail.com) + Kris Zyp (kris@sitepen.com) +------------------------------------------------------------------------------- +Project description + +dojox.lang.functional - Provides lambda functions, and common functional +operations. + +dojox.lang.aspect - Provides a framework for aspect-oriented programming. + +dojox.lang.oo - Provides mixers to support traits and mixins for object-oriented +programming. + +dojox.lang.async - Provides helpers for event-driven programming. + +dojox.lang.observable - Provides construction of objects that such that +property access and modification can be controlled, i.e. provides a form of +getters/setters. + +dojox.lang.typed - Provides type checking for JavaScript classes, enforcing +types on properties and method parameters using JSON Schema definitions. + +dojox.lang.docs - Provides schemas on Dojo's classes from the API +documentation. This can used for runtime access to class metadata information +such as descriptions and type information. This can be used in conjunction with +dojox.lang.typed to enforce typing on Dojo's classes using the API information. + + +------------------------------------------------------------------------------- +Dependencies: + +None. +------------------------------------------------------------------------------- +Documentation + +For now: + +dojox.lang.functional: +http://lazutkin.com/blog/2008/jan/12/functional-fun-javascript-dojo/ +http://lazutkin.com/blog/2008/jun/30/using-recursion-combinators-javascript/ + +dojox.lang.aspect: +http://lazutkin.com/blog/2008/may/18/aop-aspect-javascript-dojo/ + +------------------------------------------------------------------------------- +Installation instructions + +Grab the following from the Dojo SVN Repository: +http://svn.dojotoolkit.org/src/dojo/dojox/trunk/lang/* + +Install into the following directory structure: +/dojox/lang/ + +...which should be at the same level as your Dojo checkout. +------------------------------------------------------------------------------- +Additional Notes + +See tests and the source for more details. + +LICENSE in this directory contains the MIT license by Oliver Steele for +dojox.lang.functional.lambda, which was derived from his original implementation. diff --git a/js/dojo/dojox/lang/aspect.js b/js/dojo/dojox/lang/aspect.js new file mode 100644 index 0000000..f79c683 --- /dev/null +++ b/js/dojo/dojox/lang/aspect.js @@ -0,0 +1,372 @@ +//>>built +// wrapped by build app +define("dojox/lang/aspect", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.aspect"); + +(function(){ + var d = dojo, aop = dojox.lang.aspect, ap = Array.prototype, + contextStack = [], context; + + // this class implements a topic-based double-linked list + var Advice = function(){ + this.next_before = this.prev_before = + this.next_around = this.prev_around = + this.next_afterReturning = this.prev_afterReturning = + this.next_afterThrowing = this.prev_afterThrowing = + this; + this.counter = 0; + }; + d.extend(Advice, { + add: function(advice){ + var dyn = d.isFunction(advice), + node = {advice: advice, dynamic: dyn}; + this._add(node, "before", "", dyn, advice); + this._add(node, "around", "", dyn, advice); + this._add(node, "after", "Returning", dyn, advice); + this._add(node, "after", "Throwing", dyn, advice); + ++this.counter; + return node; + }, + _add: function(node, topic, subtopic, dyn, advice){ + var full = topic + subtopic; + if(dyn || advice[topic] || (subtopic && advice[full])){ + var next = "next_" + full, prev = "prev_" + full; + (node[prev] = this[prev])[next] = node; + (node[next] = this)[prev] = node; + } + }, + remove: function(node){ + this._remove(node, "before"); + this._remove(node, "around"); + this._remove(node, "afterReturning"); + this._remove(node, "afterThrowing"); + --this.counter; + }, + _remove: function(node, topic){ + var next = "next_" + topic, prev = "prev_" + topic; + if(node[next]){ + node[next][prev] = node[prev]; + node[prev][next] = node[next]; + } + }, + isEmpty: function(){ + return !this.counter; + } + }); + + var getDispatcher = function(){ + + return function(){ + + var self = arguments.callee, // the join point + advices = self.advices, // list of advices for this joinpoint + ret, i, a, e, t; + + // push context + if(context){ contextStack.push(context); } + context = { + instance: this, // object instance + joinPoint: self, // join point + depth: contextStack.length, // current level of depth starting from 0 + around: advices.prev_around, // pointer to the current around advice + dynAdvices: [], // array of dynamic advices if any + dynIndex: 0 // index of a dynamic advice + }; + + try{ + // process before events + for(i = advices.prev_before; i != advices; i = i.prev_before){ + if(i.dynamic){ + // instantiate a dynamic advice + context.dynAdvices.push(a = new i.advice(context)); + if(t = a.before){ // intentional assignment + t.apply(a, arguments); + } + }else{ + t = i.advice; + t.before.apply(t, arguments); + } + } + + // process around and after events + try{ + // call the around advice or the original method + ret = (advices.prev_around == advices ? self.target : aop.proceed).apply(this, arguments); + }catch(e){ + // process after throwing and after events + context.dynIndex = context.dynAdvices.length; + for(i = advices.next_afterThrowing; i != advices; i = i.next_afterThrowing){ + a = i.dynamic ? context.dynAdvices[--context.dynIndex] : i.advice; + if(t = a.afterThrowing){ // intentional assignment + t.call(a, e); + } + if(t = a.after){ // intentional assignment + t.call(a); + } + } + // continue the exception processing + throw e; + } + // process after returning and after events + context.dynIndex = context.dynAdvices.length; + for(i = advices.next_afterReturning; i != advices; i = i.next_afterReturning){ + a = i.dynamic ? context.dynAdvices[--context.dynIndex] : i.advice; + if(t = a.afterReturning){ // intentional assignment + t.call(a, ret); + } + if(t = a.after){ // intentional assignment + t.call(a); + } + } + // process dojo.connect() listeners + var ls = self._listeners; + for(i in ls){ + if(!(i in ap)){ + ls[i].apply(this, arguments); + } + } + }finally{ + // destroy dynamic advices + for(i = 0; i < context.dynAdvices.length; ++i){ + a = context.dynAdvices[i]; + if(a.destroy){ + a.destroy(); + } + } + // pop context + context = contextStack.length ? contextStack.pop() : null; + } + + return ret; + }; + }; + + aop.advise = function(/*Object*/ obj, + /*String|RegExp|Array*/ method, + /*Object|Function|Array*/ advice + ){ + // summary: + // Attach AOP-style advices to a method. + // + // description: + // Attaches AOP-style advices to a method. Can attach several + // advices at once and operate on several methods of an object. + // The latter is achieved when a RegExp is specified as + // a method name, or an array of strings and regular expressions + // is used. In this case all functional methods that + // satisfy the RegExp condition are processed. This function + // returns a handle, which can be used to unadvise, or null, + // if advising has failed. + // + // This function is a convenience wrapper for + // dojox.lang.aspect.adviseRaw(). + // + // obj: + // A source object for the advised function. Cannot be a DOM node. + // If this object is a constructor, its prototype is advised. + // + // method: + // A string name of the function in obj. In case of RegExp all + // methods of obj matching the regular expression are advised. + // + // advice: + // An object, which defines advises, or a function, which + // returns such object, or an array of previous items. + // The advice object can define following member functions: + // before, around, afterReturning, afterThrowing, after. + // If the function is supplied, it is called with a context + // object once per call to create a temporary advice object, which + // is destroyed after the processing. The temporary advice object + // can implement a destroy() method, if it wants to be called when + // not needed. + + if(typeof obj != "object"){ + obj = obj.prototype; + } + + var methods = []; + if(!(method instanceof Array)){ + method = [method]; + } + + // identify advised methods + for(var j = 0; j < method.length; ++j){ + var t = method[j]; + if(t instanceof RegExp){ + for(var i in obj){ + if(d.isFunction(obj[i]) && t.test(i)){ + methods.push(i); + } + } + }else{ + if(d.isFunction(obj[t])){ + methods.push(t); + } + } + } + + if(!d.isArray(advice)){ advice = [advice]; } + + return aop.adviseRaw(obj, methods, advice); // Object + }; + + aop.adviseRaw = function(/*Object*/ obj, + /*Array*/ methods, + /*Array*/ advices + ){ + // summary: + // Attach AOP-style advices to methods. + // + // description: + // Attaches AOP-style advices to object's methods. Can attach several + // advices at once and operate on several methods of the object. + // The latter is achieved when a RegExp is specified as + // a method name. In this case all functional methods that + // satisfy the RegExp condition are processed. This function + // returns a handle, which can be used to unadvise, or null, + // if advising has failed. + // + // obj: + // A source object for the advised function. + // Cannot be a DOM node. + // + // methods: + // An array of method names (strings) to be advised. + // + // advices: + // An array of advices represented by objects or functions that + // return such objects on demand during the event processing. + // The advice object can define following member functions: + // before, around, afterReturning, afterThrowing, after. + // If the function is supplied, it is called with a context + // object once per call to create a temporary advice object, which + // is destroyed after the processing. The temporary advice object + // can implement a destroy() method, if it wants to be called when + // not needed. + + if(!methods.length || !advices.length){ return null; } + + // attach advices + var m = {}, al = advices.length; + for(var i = methods.length - 1; i >= 0; --i){ + var name = methods[i], o = obj[name], ao = new Array(al), t = o.advices; + // create a stub, if needed + if(!t){ + var x = obj[name] = getDispatcher(); + x.target = o.target || o; + x.targetName = name; + x._listeners = o._listeners || []; + x.advices = new Advice; + t = x.advices; + } + // attach advices + for(var j = 0; j < al; ++j){ + ao[j] = t.add(advices[j]); + } + m[name] = ao; + } + + return [obj, m]; // Object + }; + + aop.unadvise = function(/*Object*/ handle){ + // summary: + // Detach previously attached AOP-style advices. + // + // handle: + // The object returned by dojox.lang.aspect.advise(). + + if(!handle){ return; } + var obj = handle[0], methods = handle[1]; + for(var name in methods){ + var o = obj[name], t = o.advices, ao = methods[name]; + for(var i = ao.length - 1; i >= 0; --i){ + t.remove(ao[i]); + } + if(t.isEmpty()){ + // check if we can remove all stubs + var empty = true, ls = o._listeners; + if(ls.length){ + for(i in ls){ + if(!(i in ap)){ + empty = false; + break; + } + } + } + if(empty){ + // revert to the original method + obj[name] = o.target; + }else{ + // replace with the dojo.connect() stub + var x = obj[name] = d._listener.getDispatcher(); + x.target = o.target; + x._listeners = ls; + } + } + } + }; + + aop.getContext = function(){ + // summary: + // Returns the context information for the advice in effect. + + return context; // Object + }; + + aop.getContextStack = function(){ + // summary: + // Returns the context stack, which reflects executing advices + // up to this point. The array is ordered from oldest to newest. + // In order to get the active context use dojox.lang.aspect.getContext(). + + return contextStack; // Array + }; + + aop.proceed = function(){ + // summary: + // Call the original function (or the next level around advice) in an around advice code. + // + // description: + // Calls the original function (or the next level around advice). + // Accepts and passes on any number of arguments, and returns a value. + // This function is valid only in the content of around calls. + + var joinPoint = context.joinPoint, advices = joinPoint.advices; + for(var c = context.around; c != advices; c = context.around){ + context.around = c.prev_around; // advance the pointer + if(c.dynamic){ + var a = context.dynAdvices[context.dynIndex++], t = a.around; + if(t){ + return t.apply(a, arguments); + } + }else{ + return c.advice.around.apply(c.advice, arguments); + } + } + return joinPoint.target.apply(context.instance, arguments); + }; +})(); + +/* +Aspect = { + before: function(arguments){...}, + around: function(arguments){...returns value...}, + afterReturning: function(ret){...}, + afterThrowing: function(excp){...}, + after: function(){...} +}; + +Context = { + instance: ..., // the instance we operate on + joinPoint: ..., // Object (see below) + depth: ... // current depth of the context stack +}; + +JoinPoint = { + target: ..., // the original function being wrapped + targetName: ... // name of the method +}; +*/ + +}); diff --git a/js/dojo/dojox/lang/aspect/cflow.js b/js/dojo/dojox/lang/aspect/cflow.js new file mode 100644 index 0000000..90bb134 --- /dev/null +++ b/js/dojo/dojox/lang/aspect/cflow.js @@ -0,0 +1,47 @@ +//>>built +// wrapped by build app +define("dojox/lang/aspect/cflow", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.aspect.cflow"); + + +(function(){ + var aop = dojox.lang.aspect; + + aop.cflow = function(/*Object*/ instance, /*String|RegExp|Array?*/ method){ + // summary: + // Returns true if the context stack contains a context for a given + // instance that satisfies a given method name criteria. + // + // instance: + // An instance to be matched. If null, any context will be examined. + // Otherwise the context should belong to this instance. + // + // method: + // An optional pattern to be matched against a method name. Can be a string, + // a RegExp object or an array of strings and RegExp objects. + // If it is omitted, any name will satisfy the criteria. + + if(arguments.length > 1 && !(method instanceof Array)){ + method = [method]; + } + + var contextStack = aop.getContextStack(); + for(var i = contextStack.length - 1; i >= 0; --i){ + var c = contextStack[i]; + // check if instance matches + if(instance && c.instance != instance){ continue; } + if(!method){ return true; } + var n = c.joinPoint.targetName; + for(var j = method.length - 1; j >= 0; --j){ + var m = method[j]; + if(m instanceof RegExp){ + if(m.test(n)){ return true; } + }else{ + if(n == m){ return true; } + } + } + } + return false; // Boolean + }; +})(); +}); diff --git a/js/dojo/dojox/lang/aspect/counter.js b/js/dojo/dojox/lang/aspect/counter.js new file mode 100644 index 0000000..cb17981 --- /dev/null +++ b/js/dojo/dojox/lang/aspect/counter.js @@ -0,0 +1,31 @@ +//>>built +// wrapped by build app +define("dojox/lang/aspect/counter", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.aspect.counter"); + +(function(){ + var aop = dojox.lang.aspect; + + var Counter = function(){ + this.reset(); + }; + dojo.extend(Counter, { + before: function(/*arguments*/){ + ++this.calls; + }, + afterThrowing: function(/*excp*/){ + ++this.errors; + }, + reset: function(){ + this.calls = this.errors = 0; + } + }); + + aop.counter = function(){ + // summary: + // Returns an object, which can be used to count calls to methods. + + return new Counter; // Object + }; +})(); +}); diff --git a/js/dojo/dojox/lang/aspect/memoizer.js b/js/dojo/dojox/lang/aspect/memoizer.js new file mode 100644 index 0000000..4692aed --- /dev/null +++ b/js/dojo/dojox/lang/aspect/memoizer.js @@ -0,0 +1,49 @@ +//>>built +// wrapped by build app +define("dojox/lang/aspect/memoizer", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.aspect.memoizer"); + +(function(){ + var aop = dojox.lang.aspect; + + var memoize1 = { + around: function(key){ + var ctx = aop.getContext(), self = ctx.joinPoint, that = ctx.instance, t, u, ret; + if((t = that.__memoizerCache) && (t = t[self.targetName]) && (key in t)){ + return t[key]; + } + var ret = aop.proceed.apply(null, arguments); + if(!(t = that.__memoizerCache)){ t = that.__memoizerCache = {}; } + if(!(u = t[self.targetName])){ u = t[self.targetName] = {}; } + return u[key] = ret; + } + }; + + var memoizeN = function(/*Function*/keyMaker){ + return { + around: function(/*arguments*/){ + var ctx = aop.getContext(), self = ctx.joinPoint, that = ctx.instance, t, u, ret, + key = keyMaker.apply(that, arguments); + if((t = that.__memoizerCache) && (t = t[self.targetName]) && (key in t)){ + return t[key]; + } + var ret = aop.proceed.apply(null, arguments); + if(!(t = that.__memoizerCache)){ t = that.__memoizerCache = {}; } + if(!(u = t[self.targetName])){ u = t[self.targetName] = {}; } + return u[key] = ret; + } + }; + }; + + aop.memoizer = function(/*Function?*/ keyMaker){ + // summary: + // Returns an object, which can be used to count calls to methods. + // + // keyMaker: + // the function, which takes method's arguments and returns a key, + // which can be used to index the result. + + return arguments.length == 0 ? memoize1 : memoizeN(keyMaker); // Object + }; +})(); +}); diff --git a/js/dojo/dojox/lang/aspect/memoizerGuard.js b/js/dojo/dojox/lang/aspect/memoizerGuard.js new file mode 100644 index 0000000..81f5e52 --- /dev/null +++ b/js/dojo/dojox/lang/aspect/memoizerGuard.js @@ -0,0 +1,37 @@ +//>>built +// wrapped by build app +define("dojox/lang/aspect/memoizerGuard", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.aspect.memoizerGuard"); + +(function(){ + var aop = dojox.lang.aspect, + reset = function(/*String|Array?*/ method){ + var that = aop.getContext().instance, t; + if(!(t = that.__memoizerCache)){ return; } + if(arguments.length == 0){ + delete that.__memoizerCache; + }else if(dojo.isArray(method)){ + dojo.forEach(method, function(m){ delete t[m]; }); + }else{ + delete t[method]; + } + }; + + + aop.memoizerGuard = function(/*String|Array?*/ method){ + // summary: + // Invalidates the memoizer's cache (see dojox.lang.aspect.memoizer) + // after calling certain methods. + // + // method: + // Optional method's name to be guarded: only cache for + // this method will be invalidated on call. Can be a string + // or an array of method names. If omitted the whole cache + // will be invalidated. + + return { // Object + after: function(){ reset(method); } + }; + }; +})(); +}); diff --git a/js/dojo/dojox/lang/aspect/profiler.js b/js/dojo/dojox/lang/aspect/profiler.js new file mode 100644 index 0000000..cec4500 --- /dev/null +++ b/js/dojo/dojox/lang/aspect/profiler.js @@ -0,0 +1,37 @@ +//>>built +// wrapped by build app +define("dojox/lang/aspect/profiler", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.aspect.profiler"); + +(function(){ + var aop = dojox.lang.aspect, + uniqueNumber = 0; + + var Profiler = function(title){ + this.args = title ? [title] : []; + this.inCall = 0; + }; + dojo.extend(Profiler, { + before: function(/*arguments*/){ + if(!(this.inCall++)){ + console.profile.apply(console, this.args); + } + }, + after: function(/*excp*/){ + if(!--this.inCall){ + console.profileEnd(); + } + } + }); + + aop.profiler = function(/*String?*/ title){ + // summary: + // Returns an object, which can be used to time calls to methods. + // + // title: + // The optional name of the profile section. + + return new Profiler(title); // Object + }; +})(); +}); diff --git a/js/dojo/dojox/lang/aspect/timer.js b/js/dojo/dojox/lang/aspect/timer.js new file mode 100644 index 0000000..6989932 --- /dev/null +++ b/js/dojo/dojox/lang/aspect/timer.js @@ -0,0 +1,37 @@ +//>>built +// wrapped by build app +define("dojox/lang/aspect/timer", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.aspect.timer"); + +(function(){ + var aop = dojox.lang.aspect, + uniqueNumber = 0; + + var Timer = function(name){ + this.name = name || ("DojoAopTimer #" + ++uniqueNumber); + this.inCall = 0; + }; + dojo.extend(Timer, { + before: function(/*arguments*/){ + if(!(this.inCall++)){ + console.time(this.name); + } + }, + after: function(/*excp*/){ + if(!--this.inCall){ + console.timeEnd(this.name); + } + } + }); + + aop.timer = function(/*String?*/ name){ + // summary: + // Returns an object, which can be used to time calls to methods. + // + // name: + // The optional unique name of the timer. + + return new Timer(name); // Object + }; +})(); +}); diff --git a/js/dojo/dojox/lang/aspect/tracer.js b/js/dojo/dojox/lang/aspect/tracer.js new file mode 100644 index 0000000..9186bcf --- /dev/null +++ b/js/dojo/dojox/lang/aspect/tracer.js @@ -0,0 +1,48 @@ +//>>built +// wrapped by build app +define("dojox/lang/aspect/tracer", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.aspect.tracer"); + +(function(){ + var aop = dojox.lang.aspect; + + var Tracer = function(/*Boolean*/ grouping){ + this.method = grouping ? "group" : "log"; + if(grouping){ + this.after = this._after; + } + }; + dojo.extend(Tracer, { + before: function(/*arguments*/){ + var context = aop.getContext(), joinPoint = context.joinPoint, + args = Array.prototype.join.call(arguments, ", "); + console[this.method](context.instance, "=>", joinPoint.targetName + "(" + args + ")"); + }, + afterReturning: function(retVal){ + var joinPoint = aop.getContext().joinPoint; + if(typeof retVal != "undefined"){ + console.log(joinPoint.targetName + "() returns:", retVal); + }else{ + console.log(joinPoint.targetName + "() returns"); + } + }, + afterThrowing: function(excp){ + console.log(aop.getContext().joinPoint.targetName + "() throws:", excp); + }, + _after: function(excp){ + console.groupEnd(); + } + }); + + aop.tracer = function(/*Boolean*/ grouping){ + // summary: + // Returns an object, which can be used to trace calls with Firebug's console. + // Prints argument, a return value, or an exception. + // + // grouping: + // The flag to group output. If true, indents embedded console messages. + + return new Tracer(grouping); // Object + }; +})(); +}); diff --git a/js/dojo/dojox/lang/async.js b/js/dojo/dojox/lang/async.js new file mode 100644 index 0000000..26e901d --- /dev/null +++ b/js/dojo/dojox/lang/async.js @@ -0,0 +1,204 @@ +//>>built +// wrapped by build app +define("dojox/lang/async", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.async"); + +(function(){ + var d = dojo, Deferred = d.Deferred, each = d.forEach, some = d.some, + async = dojox.lang.async, aps = Array.prototype.slice, + opts = Object.prototype.toString; + + async.seq = function(x){ + // summary: + // Executes functions sequentially. Waits if any of them returns Deferred. + var fs = opts.call(x) == "[object Array]" ? x : arguments; + return function(init){ + var x = new Deferred(); + each(fs, function(f){ x.addCallback(f); }); + x.callback(init); + return x; + }; + }; + + async.par = function(x){ + // summary: + // Executes functions in parallel. Waits for all of them to finish. + var fs = opts.call(x) == "[object Array]" ? x : arguments; + return function(init){ + var results = new Array(fs.length), + cancel = function(){ + each(results, function(v){ + if(v instanceof Deferred && v.fired < 0){ + v.cancel(); + } + }); + }, + x = new Deferred(cancel), + ready = fs.length; + each(fs, function(f, i){ + var x; + try { + x = f(init); + }catch(e){ + x = e; + } + results[i] = x; + }); + var failed = some(results, function(v){ + if(v instanceof Error){ + cancel(); + x.errback(v); + return true; + } + return false; + }); + if(!failed){ + each(results, function(v, i){ + if(v instanceof Deferred){ + v.addCallbacks( + function(v){ + results[i] = v; + if(!--ready){ + x.callback(results); + } + }, + function(v){ + cancel(); + x.errback(v); + } + ); + }else{ + --ready; + } + }); + } + if(!ready){ + x.callback(results); + } + return x; + }; + }; + + async.any = function(x){ + // summary: + // Executes functions in parallel. As soon as one of them finishes + // cancels the rest. + var fs = opts.call(x) == "[object Array]" ? x : arguments; + return function(init){ + var results = new Array(fs.length), noResult = true; + cancel = function(index){ + each(results, function(v, i){ + if(i != index && v instanceof Deferred && v.fired < 0){ + v.cancel(); + } + }); + }, + x = new Deferred(cancel); + each(fs, function(f, i){ + var x; + try { + x = f(init); + }catch(e){ + x = e; + } + results[i] = x; + }); + var done = some(results, function(v, i){ + if(!(v instanceof Deferred)){ + cancel(i); + x.callback(v); + return true; + } + return false; + }); + if(!done){ + each(results, function(v, i){ + v.addBoth( + function(v){ + if(noResult){ + noResult = false; + cancel(i); + x.callback(v); + } + } + ); + }); + } + return x; + }; + }; + + async.select = function(cond, x){ + // summary: + // Executes a condition, waits for it if necessary, and executes + // Nth function from list. + var fs = opts.call(x) == "[object Array]" ? x : aps.call(arguments, 1); + return function(init){ + return new Deferred().addCallback(cond).addCallback(function(v){ + if(typeof v == "number" && v >= 0 && v < fs.length){ + return fs[v](init); + }else{ + return new Error("async.select: out of range"); + } + }).callback(init); + }; + }; + + async.ifThen = function(cond, ifTrue, ifFalse){ + // summary: + // Executes a condition, waits for it if necessary, and executes + // one of two functions. + return function(init){ + return new Deferred().addCallback(cond).addCallback(function(v){ + return (v ? ifTrue : ifFalse)(init); + }).callback(init); + }; + }; + + async.loop = function(cond, body){ + // summary: + // Executes a condition, waits for it if necessary, and executes + // the body, if truthy value was returned. + // Then it repeats the cycle until the condition function returns + // a falsy value. + return function(init){ + var x, y = new Deferred(function(){ x.cancel(); }); + function ifErr(v){ y.errback(v); } + function loop(v){ + if(v){ + x.addCallback(body).addCallback(setUp); + }else{ + y.callback(v); + } + return v; + } + function setUp(init){ + x = new Deferred(). + addCallback(cond). + addCallback(loop). + addErrback(ifErr); + x.callback(init); + } + setUp(init); + return y; + }; + }; +})(); + +/* +Design decisions: + +seq() - behaves like the normal Deferred callback chain. + +par() - if error, all pending Deferreds are cancelled and the error is signaled, +otherwise return an array of all results. + +any() - just like par() but only one result is returned. + +select() - any error is returned, otherwise the selected result is returned. + +loop() - any error is returned, otherwise the last result is returned. + +*/ + +}); diff --git a/js/dojo/dojox/lang/async/event.js b/js/dojo/dojox/lang/async/event.js new file mode 100644 index 0000000..a3122a3 --- /dev/null +++ b/js/dojo/dojox/lang/async/event.js @@ -0,0 +1,46 @@ +//>>built +// wrapped by build app +define("dojox/lang/async/event", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.async.event"); + +// Source of Deferred for events + +(function(){ + var d = dojo, event = dojox.lang.async.event; + + event.from = function(src, name){ + return function(){ + var h, cancel = function(){ + if(h){ + d.disconnect(h); + h = null; + } + }, + x = new d.Deferred(cancel); + h = d.connect(src, name, function(evt){ + cancel(); + x.callback(evt); + }); + return x; + }; + }; + + event.failOn = function(src, name){ + return function(){ + var h, cancel = function(){ + if(h){ + d.disconnect(h); + h = null; + } + }, + x = new d.Deferred(cancel); + h = d.connect(src, name, function(evt){ + cancel(); + x.errback(new Error(evt)); + }); + return x; + }; + }; +})(); + +}); diff --git a/js/dojo/dojox/lang/async/timeout.js b/js/dojo/dojox/lang/async/timeout.js new file mode 100644 index 0000000..7c5b69d --- /dev/null +++ b/js/dojo/dojox/lang/async/timeout.js @@ -0,0 +1,45 @@ +//>>built +// wrapped by build app +define("dojox/lang/async/timeout", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.async.timeout"); + +// Source of Deferred for timeouts + +(function(){ + var d = dojo, timeout = dojox.lang.async.timeout; + + timeout.from = function(ms){ + return function(){ + var h, cancel = function(){ + if(h){ + clearTimeout(h); + h = null; + } + }, + x = new d.Deferred(cancel); + h = setTimeout(function(){ + cancel(); + x.callback(ms); + }, ms); + return x; + }; + }; + + timeout.failOn = function(ms){ + return function(){ + var h, cancel = function(){ + if(h){ + clearTimeout(h); + h = null; + } + }, + x = new d.Deferred(cancel); + h = setTimeout(function(){ + cancel(); + x.errback(ms); + }, ms); + return x; + }; + }; +})(); +}); diff --git a/js/dojo/dojox/lang/async/topic.js b/js/dojo/dojox/lang/async/topic.js new file mode 100644 index 0000000..b2ac15e --- /dev/null +++ b/js/dojo/dojox/lang/async/topic.js @@ -0,0 +1,45 @@ +//>>built +// wrapped by build app +define("dojox/lang/async/topic", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.async.topic"); + +// Source of Deferred for topics + +(function(){ + var d = dojo, topic = dojox.lang.async.topic; + + topic.from = function(topic){ + return function(){ + var h, cancel = function(){ + if(h){ + d.unsubscribe(h); + h = null; + } + }, + x = new d.Deferred(cancel); + h = d.subscribe(topic, function(){ + cancel(); + x.callback(arguments); + }); + return x; + }; + }; + + topic.failOn = function(topic){ + return function(){ + var h, cancel = function(){ + if(h){ + d.unsubscribe(h); + h = null; + } + }, + x = new d.Deferred(cancel); + h = d.subscribe(topic, function(evt){ + cancel(); + x.errback(new Error(arguments)); + }); + return x; + }; + }; +})(); +}); diff --git a/js/dojo/dojox/lang/docs.js b/js/dojo/dojox/lang/docs.js new file mode 100644 index 0000000..0174630 --- /dev/null +++ b/js/dojo/dojox/lang/docs.js @@ -0,0 +1,219 @@ +//>>built +// wrapped by build app +define("dojox/lang/docs", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.docs"); + +// Extracts information from the API docs to apply a schema representation to dojo classes. +// This can be utilized for runtime metadata retrieval and type checking +(function(){ + function error(error){ + console.log("Warning, the API docs must be available at ../util/docscripts/api.json "+ + "or ../util/docscripts/api/*.json "+ + "in order for dojox.lang.docs to supply schema information, but it could not be loaded: " + error); + } + + var declaredClasses = {}; + var requiredModules = []; + var _docs = dojox.lang.docs._loadedDocs = {}; + + var schemifyClass = function(clazz, name){ + // initial implementation records classes until they are ready + declaredClasses[name] = clazz; + }; + var getType = function(typeDef){ + var type = typeDef.type || ''; + var typeObj, optional = false, array = false, dontModify; + type = type.replace(/\?/, function(){ + optional = true; + return ''; + }); + type = type.replace(/\[\]/, function(){ + array = true; + return ''; + }); + if(type.match(/HTML/)){ + // HTML String and other "types" of strings are really just strings + type = "string"; + }else if(type == 'String' || type == 'Number' || + type == 'Boolean' || type == 'Object' || + type == 'Array' || type == 'Integer' || type == "Function"){ + type = type.toLowerCase(); + }else if(type == "bool"){ + type = "boolean"; + }else if(type){ + typeObj = dojo.getObject(type) || {}; + dontModify = true; + }else{ + typeObj = {}; + } + typeObj = typeObj || {type:type}; + if(array){ + typeObj = {items:typeObj, type:"array"}; + dontModify = false; + } + if(!dontModify){ + if(optional){ + typeObj.optional = true; + } + if(/const/.test(typeDef.tags)){ + typeObj.readonly = true; + } + } + return typeObj; + }; + var actualSchemifyClass = function(clazz, name){ + var docForClass = _docs[name]; + if(docForClass){ + clazz.description = docForClass.description; + clazz.properties = {}; + clazz.methods = {}; + + if(docForClass.properties){ + var props = docForClass.properties; + for(var i=0, l=props.length; i<l; i++){ + if(props[i].scope == "prototype"){ + var propDef = clazz.properties[props[i].name] = getType(props[i]); + propDef.description = props[i].summary; + } + } + } + + // translate the methods to JSON Schema + if(docForClass.methods){ + var methods = docForClass.methods; + for(i=0, l=methods.length; i<l; i++){ + name = methods[i].name; + if(name && methods[i].scope == "prototype"){ + var methodDef = clazz.methods[name] = {}; + methodDef.description = methods[i].summary; + var parameters = methods[i].parameters; + if(parameters){ + methodDef.parameters = []; + for(var j=0, k=parameters.length; j<k; j++){ + var param = parameters[j]; + var paramDef = methodDef.parameters[j] = getType(param); + paramDef.name = param.name; + paramDef.optional = "optional" == param.usage; + } + } + var ret = methods[i]['return-types']; + if(ret && ret[0]){ + var returns = getType(ret[0]); + if(returns.type){ + methodDef.returns = returns; + } + } + } + } + } + + var superclass = docForClass.superclass; + if(superclass){ + clazz["extends"] = dojo.getObject(superclass); + } + } + }; + var requireDocs = function(moduleName){ + requiredModules.push(moduleName); + }; + + // hook into all declared classes + var defaultDeclare = dojo.declare; + dojo.declare = function(name){ + var clazz = defaultDeclare.apply(this, arguments); + schemifyClass(clazz, name); + return clazz; + }; + dojo.mixin(dojo.declare, defaultDeclare); + var initialized; + + // hook into dojo.require + var defaultRequire = dojo.require; + dojo.require = function(moduleName){ + requireDocs(moduleName); + var module = defaultRequire.apply(this, arguments); + return module; + }; + + dojox.lang.docs.init = function(/*Boolean*/async){ + // summary: + // Loads the documentation and applies it to the previously defined classes + // and any future defined classes + // + // async: + // If true, the documentation will be loaded asynchronously + function loadFullDocs(){ + dojo.require = defaultRequire; + requiredModules = null; + try{ + dojo.xhrGet({ + sync:!async, + url: dojo.baseUrl + '../util/docscripts/api.json', + handleAs: 'text' + }).addCallbacks(function(obj){ + _docs = (new Function("return " + obj))(); + obj = null; + schemifyClass = actualSchemifyClass; + + for(var i in declaredClasses){ + schemifyClass(declaredClasses[i], i); + } + declaredClasses = null; + }, error); + }catch(e){ + error(e); + } + } + + if(initialized){ + return null; + } + initialized = true; + + var getSplitDocs = function(moduleName, sync){ + return dojo.xhrGet({ + sync: sync||!async, + url: dojo.baseUrl + '../util/docscripts/api/' + moduleName + '.json', + handleAs: 'text' + }).addCallback(function(obj){ + obj = (new Function("return " + obj))(); + for(var clazz in obj){ + if(!_docs[clazz]){ + _docs[clazz] = obj[clazz]; + } + } + }); + }; + try{ + var firstMod = requiredModules.shift(); + getSplitDocs(firstMod, true).addCallbacks(function(){ + requireDocs = function(moduleName){ + if(!_docs[moduleName]){ + try{ + getSplitDocs(moduleName); + }catch(e){ + _docs[moduleName] = {}; + } + } + }; + //console.log(requiredModules); + dojo.forEach(requiredModules, function(mod){ + requireDocs(mod); + }); + requiredModules = null; + + schemifyClass = actualSchemifyClass; + + for(i in declaredClasses){ + schemifyClass(declaredClasses[i], i); + } + declaredClasses = null; + },loadFullDocs); + }catch(e){ + loadFullDocs(); + } + return null; + } +})(); + +}); diff --git a/js/dojo/dojox/lang/functional.js b/js/dojo/dojox/lang/functional.js new file mode 100644 index 0000000..ab30517 --- /dev/null +++ b/js/dojo/dojox/lang/functional.js @@ -0,0 +1,4 @@ +//>>built +define("dojox/lang/functional", ["./functional/lambda", "./functional/array", "./functional/object"], function(df){ + return df; +}); diff --git a/js/dojo/dojox/lang/functional/array.js b/js/dojo/dojox/lang/functional/array.js new file mode 100644 index 0000000..8c47b5c --- /dev/null +++ b/js/dojo/dojox/lang/functional/array.js @@ -0,0 +1,168 @@ +//>>built +define("dojox/lang/functional/array", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/window", "./lambda"], + function(dojo, lang, arr, win, df){ + +// This module adds high-level functions and related constructs: +// - array-processing functions similar to standard JS functions + +// Notes: +// - this module provides JS standard methods similar to high-level functions in dojo/_base/array.js: +// forEach, map, filter, every, some + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument +// - take an iterator objects as the array argument + + var empty = {}; + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // JS 1.6 standard array functions, which can take a lambda as a parameter. + // Consider using dojo._base.array functions, if you don't need the lambda support. + filter: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with all elements that pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var t = [], v, i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; ++i){ + v = a[i]; + if(f.call(o, v, i, a)){ t.push(v); } + } + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext();){ + v = a.next(); + if(f.call(o, v, i++, a)){ t.push(v); } + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + v = a[i]; + if(f.call(o, v, i, a)){ t.push(v); } + } + } + } + return t; // Array + }, + forEach: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: executes a provided function once per array element. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; f.call(o, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext(); f.call(o, a.next(), i++, a)); + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + f.call(o, a[i], i, a); + } + } + } + return o; // Object + }, + map: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with the results of calling + // a provided function on every element in this array. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var t, n, i; + if(lang.isArray(a)){ + // array + t = new Array(n = a.length); + for(i = 0; i < n; t[i] = f.call(o, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + t = []; + for(i = 0; a.hasNext(); t.push(f.call(o, a.next(), i++, a))); + }else{ + // object/dictionary + t = []; + for(i in a){ + if(!(i in empty)){ + t.push(f.call(o, a[i], i, a)); + } + } + } + return t; // Array + }, + every: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether all elements in the array pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; ++i){ + if(!f.call(o, a[i], i, a)){ + return false; // Boolean + } + } + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext();){ + if(!f.call(o, a.next(), i++, a)){ + return false; // Boolean + } + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + if(!f.call(o, a[i], i, a)){ + return false; // Boolean + } + } + } + } + return true; // Boolean + }, + some: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether some element in the array passes the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; ++i){ + if(f.call(o, a[i], i, a)){ + return true; // Boolean + } + } + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext();){ + if(f.call(o, a.next(), i++, a)){ + return true; // Boolean + } + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + if(f.call(o, a[i], i, a)){ + return true; // Boolean + } + } + } + } + return false; // Boolean + } + }); + + return df; +}); diff --git a/js/dojo/dojox/lang/functional/binrec.js b/js/dojo/dojox/lang/functional/binrec.js new file mode 100644 index 0000000..f32ec8e --- /dev/null +++ b/js/dojo/dojox/lang/functional/binrec.js @@ -0,0 +1,177 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/binrec", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda,dojox/lang/functional/util"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.binrec"); + +dojo.require("dojox.lang.functional.lambda"); +dojo.require("dojox.lang.functional.util"); + +// This module provides recursion combinators: +// - a binary recursion combinator. + +// Acknoledgements: +// - recursion combinators are inspired by Manfred von Thun's article +// "Recursion Theory and Joy" +// (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html) + +// Notes: +// - recursion combinators produce a function, which implements +// their respective recusion patterns. String lambdas are inlined, if possible. + +(function(){ + var df = dojox.lang.functional, inline = df.inlineLambda, + _x ="_x", _z_r_r_z_a = ["_z.r", "_r", "_z.a"]; + + df.binrec = function( + /*Function|String|Array*/ cond, + /*Function|String|Array*/ then, + /*Function|String|Array*/ before, + /*Function|String|Array*/ after){ + // summary: + // Generates a function for the binary recursion pattern. + // All parameter functions are called in the context of "this" object. + // cond: + // The lambda expression, which is used to detect the termination of recursion. + // It accepts the same parameter as the generated recursive function itself. + // This function should return "true", if the recursion should be stopped, + // and the "then" part should be executed. Otherwise the recursion will proceed. + // then: + // The lambda expression, which is called upon termination of the recursion. + // It accepts the same parameters as the generated recursive function itself. + // The returned value will be returned as the value of the generated function. + // before: + // The lambda expression, which is called before the recursive step. + // It accepts the same parameter as the generated recursive function itself. + // The returned value should be an array of two variable, which are used to call + // the generated function recursively twice in row starting from the first item. + // above: + // The lambda expression, which is called after the recursive step. + // It accepts three parameters: two returned values from recursive steps, and + // the original array of parameters used with all other functions. + // The returned value will be returned as the value of the generated function. + + var c, t, b, a, cs, ts, bs, as, dict1 = {}, dict2 = {}, + add2dict = function(x){ dict1[x] = 1; }; + if(typeof cond == "string"){ + cs = inline(cond, _x, add2dict); + }else{ + c = df.lambda(cond); + cs = "_c.apply(this, _x)"; + dict2["_c=_t.c"] = 1; + } + if(typeof then == "string"){ + ts = inline(then, _x, add2dict); + }else{ + t = df.lambda(then); + ts = "_t.apply(this, _x)"; + } + if(typeof before == "string"){ + bs = inline(before, _x, add2dict); + }else{ + b = df.lambda(before); + bs = "_b.apply(this, _x)"; + dict2["_b=_t.b"] = 1; + } + if(typeof after == "string"){ + as = inline(after, _z_r_r_z_a, add2dict); + }else{ + a = df.lambda(after); + as = "_a.call(this, _z.r, _r, _z.a)"; + dict2["_a=_t.a"] = 1; + } + var locals1 = df.keys(dict1), locals2 = df.keys(dict2), + f = new Function([], "var _x=arguments,_y,_z,_r".concat( // Function + locals1.length ? "," + locals1.join(",") : "", + locals2.length ? ",_t=_x.callee," + locals2.join(",") : "", + t ? (locals2.length ? ",_t=_t.t" : "_t=_x.callee.t") : "", + ";while(!", + cs, + "){_r=", + bs, + ";_y={p:_y,a:_r[1]};_z={p:_z,a:_x};_x=_r[0]}for(;;){do{_r=", + ts, + ";if(!_z)return _r;while(\"r\" in _z){_r=", + as, + ";if(!(_z=_z.p))return _r}_z.r=_r;_x=_y.a;_y=_y.p}while(", + cs, + ");do{_r=", + bs, + ";_y={p:_y,a:_r[1]};_z={p:_z,a:_x};_x=_r[0]}while(!", + cs, + ")}" + )); + if(c){ f.c = c; } + if(t){ f.t = t; } + if(b){ f.b = b; } + if(a){ f.a = a; } + return f; + }; +})(); + +/* +For documentation only: + +1) The original recursive version: + +var binrec1 = function(cond, then, before, after){ + var cond = df.lambda(cond), + then = df.lambda(then), + before = df.lambda(before), + after = df.lambda(after); + return function(){ + if(cond.apply(this, arguments)){ + return then.apply(this, arguments); + } + var args = before.apply(this, arguments); + var ret1 = arguments.callee.apply(this, args[0]); + var ret2 = arguments.callee.apply(this, args[1]); + return after.call(this, ret1, ret2, arguments); + }; +}; + +2) The original iterative version (before minification and inlining): + +var binrec2 = function(cond, then, before, after){ + var cond = df.lambda(cond), + then = df.lambda(then), + before = df.lambda(before), + after = df.lambda(after); + return function(){ + var top1, top2, ret, args = arguments; + // first part: start the pump + while(!cond.apply(this, args)){ + ret = before.apply(this, args); + top1 = {prev: top1, args: ret[1]}; + top2 = {prev: top2, args: args}; + args = ret[0]; + } + for(;;){ + // second part: mop up + do{ + ret = then.apply(this, args); + if(!top2){ + return ret; + } + while("ret" in top2){ + ret = after.call(this, top2.ret, ret, top2.args); + if(!(top2 = top2.prev)){ + return ret; + } + } + top2.ret = ret; + args = top1.args; + top1 = top1.prev; + }while(cond.apply(this, args)); + // first part (encore) + do{ + ret = before.apply(this, args); + top1 = {prev: top1, args: ret[1]}; + top2 = {prev: top2, args: args}; + args = ret[0]; + }while(!cond.apply(this, args)); + } + }; +}; + +*/ +}); diff --git a/js/dojo/dojox/lang/functional/curry.js b/js/dojo/dojox/lang/functional/curry.js new file mode 100644 index 0000000..6f79f01 --- /dev/null +++ b/js/dojo/dojox/lang/functional/curry.js @@ -0,0 +1,98 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/curry", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.curry"); + +dojo.require("dojox.lang.functional.lambda"); + +// This module adds high-level functions and related constructs: +// - currying and partial functions +// - argument pre-processing: mixer and flip + +// Acknoledgements: +// - partial() is based on work by Oliver Steele +// (http://osteele.com/sources/javascript/functional/functional.js) +// which was published under MIT License + +// Defined methods: +// - take any valid lambda argument as the functional argument + +(function(){ + var df = dojox.lang.functional, ap = Array.prototype; + + var currying = function(/*Object*/ info){ + return function(){ // Function + var args = info.args.concat(ap.slice.call(arguments, 0)); + if(arguments.length + info.args.length < info.arity){ + return currying({func: info.func, arity: info.arity, args: args}); + } + return info.func.apply(this, args); + }; + }; + + dojo.mixin(df, { + // currying and partial functions + curry: function(/*Function|String|Array*/ f, /*Number?*/ arity){ + // summary: curries a function until the arity is satisfied, at + // which point it returns the calculated value. + f = df.lambda(f); + arity = typeof arity == "number" ? arity : f.length; + return currying({func: f, arity: arity, args: []}); // Function + }, + arg: {}, // marker for missing arguments + partial: function(/*Function|String|Array*/ f){ + // summary: creates a function where some arguments are bound, and + // some arguments (marked as dojox.lang.functional.arg) are will be + // accepted by the final function in the order they are encountered. + // description: This method is used to produce partially bound + // functions. If you want to change the order of arguments, use + // dojox.lang.functional.mixer() or dojox.lang.functional.flip(). + var a = arguments, l = a.length, args = new Array(l - 1), p = [], i = 1, t; + f = df.lambda(f); + for(; i < l; ++i){ + t = a[i]; + args[i - 1] = t; + if(t === df.arg){ + p.push(i - 1); + } + } + return function(){ // Function + var t = ap.slice.call(args, 0), // clone the array + i = 0, l = p.length; + for(; i < l; ++i){ + t[p[i]] = arguments[i]; + } + return f.apply(this, t); + }; + }, + // argument pre-processing + mixer: function(/*Function|String|Array*/ f, /*Array*/ mix){ + // summary: changes the order of arguments using an array of + // numbers mix --- i-th argument comes from mix[i]-th place + // of supplied arguments. + f = df.lambda(f); + return function(){ // Function + var t = new Array(mix.length), i = 0, l = mix.length; + for(; i < l; ++i){ + t[i] = arguments[mix[i]]; + } + return f.apply(this, t); + }; + }, + flip: function(/*Function|String|Array*/ f){ + // summary: changes the order of arguments by reversing their + // order. + f = df.lambda(f); + return function(){ // Function + // reverse arguments + var a = arguments, l = a.length - 1, t = new Array(l + 1), i = 0; + for(; i <= l; ++i){ + t[l - i] = a[i]; + } + return f.apply(this, t); + }; + } + }); +})(); + +}); diff --git a/js/dojo/dojox/lang/functional/fold.js b/js/dojo/dojox/lang/functional/fold.js new file mode 100644 index 0000000..43a8922 --- /dev/null +++ b/js/dojo/dojox/lang/functional/fold.js @@ -0,0 +1,122 @@ +//>>built +define("dojox/lang/functional/fold", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/window", "./lambda"], + function(lang, arr, win, df){ + +// This module adds high-level functions and related constructs: +// - "fold" family of functions + +// Notes: +// - missing high-level functions are provided with the compatible API: +// foldl, foldl1, foldr, foldr1 +// - missing JS standard functions are provided with the compatible API: +// reduce, reduceRight +// - the fold's counterpart: unfold + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument +// - take an iterator objects as the array argument (only foldl, foldl1, and reduce) + + var empty = {}; + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // classic reduce-class functions + foldl: function(/*Array|String|Object*/ a, /*Function*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right using a seed value as a starting point; returns the final + // value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; z = f.call(o, z, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext(); z = f.call(o, z, a.next(), i++, a)); + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + z = f.call(o, z, a[i], i, a); + } + } + } + return z; // Object + }, + foldl1: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right; returns the final value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var z, i, n; + if(lang.isArray(a)){ + // array + z = a[0]; + for(i = 1, n = a.length; i < n; z = f.call(o, z, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + if(a.hasNext()){ + z = a.next(); + for(i = 1; a.hasNext(); z = f.call(o, z, a.next(), i++, a)); + } + }else{ + // object/dictionary + var first = true; + for(i in a){ + if(!(i in empty)){ + if(first){ + z = a[i]; + first = false; + }else{ + z = f.call(o, z, a[i], i, a); + } + } + } + } + return z; // Object + }, + foldr: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left using a seed value as a starting point; returns the final + // value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length; i > 0; --i, z = f.call(o, z, a[i], i, a)); + return z; // Object + }, + foldr1: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left; returns the final value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var n = a.length, z = a[n - 1], i = n - 1; + for(; i > 0; --i, z = f.call(o, z, a[i], i, a)); + return z; // Object + }, + // JS 1.8 standard array functions, which can take a lambda as a parameter. + reduce: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ z){ + // summary: apply a function simultaneously against two values of the array + // (from left-to-right) as to reduce it to a single value. + return arguments.length < 3 ? df.foldl1(a, f) : df.foldl(a, f, z); // Object + }, + reduceRight: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ z){ + // summary: apply a function simultaneously against two values of the array + // (from right-to-left) as to reduce it to a single value. + return arguments.length < 3 ? df.foldr1(a, f) : df.foldr(a, f, z); // Object + }, + // the fold's counterpart: unfold + unfold: function(/*Function|String|Array*/ pr, /*Function|String|Array*/ f, + /*Function|String|Array*/ g, /*Object*/ z, /*Object?*/ o){ + // summary: builds an array by unfolding a value + o = o || win.global; f = df.lambda(f); g = df.lambda(g); pr = df.lambda(pr); + var t = []; + for(; !pr.call(o, z); t.push(f.call(o, z)), z = g.call(o, z)); + return t; // Array + } + }); +}); diff --git a/js/dojo/dojox/lang/functional/lambda.js b/js/dojo/dojox/lang/functional/lambda.js new file mode 100644 index 0000000..b5a9f20 --- /dev/null +++ b/js/dojo/dojox/lang/functional/lambda.js @@ -0,0 +1,133 @@ +//>>built +define("dojox/lang/functional/lambda", ["../..", "dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array"], function(dojox, dojo, lang, arr){ + var df = lang.getObject("lang.functional", true, dojox); + +// This module adds high-level functions and related constructs: +// - anonymous functions built from the string + +// Acknoledgements: +// - lambda() is based on work by Oliver Steele +// (http://osteele.com/sources/javascript/functional/functional.js) +// which was published under MIT License + +// Notes: +// - lambda() produces functions, which after the compilation step are +// as fast as regular JS functions (at least theoretically). + +// Lambda input values: +// - returns functions unchanged +// - converts strings to functions +// - converts arrays to a functional composition + + var lcache = {}; + + // split() is augmented on IE6 to ensure the uniform behavior + var split = "ab".split(/a*/).length > 1 ? String.prototype.split : + function(sep){ + var r = this.split.call(this, sep), + m = sep.exec(this); + if(m && m.index == 0){ r.unshift(""); } + return r; + }; + + var lambda = function(/*String*/ s){ + var args = [], sects = split.call(s, /\s*->\s*/m); + if(sects.length > 1){ + while(sects.length){ + s = sects.pop(); + args = sects.pop().split(/\s*,\s*|\s+/m); + if(sects.length){ sects.push("(function(" + args + "){return (" + s + ")})"); } + } + }else if(s.match(/\b_\b/)){ + args = ["_"]; + }else{ + var l = s.match(/^\s*(?:[+*\/%&|\^\.=<>]|!=)/m), + r = s.match(/[+\-*\/%&|\^\.=<>!]\s*$/m); + if(l || r){ + if(l){ + args.push("$1"); + s = "$1" + s; + } + if(r){ + args.push("$2"); + s = s + "$2"; + } + }else{ + // the point of the long regex below is to exclude all well-known + // lower-case words from the list of potential arguments + var vars = s. + replace(/(?:\b[A-Z]|\.[a-zA-Z_$])[a-zA-Z_$\d]*|[a-zA-Z_$][a-zA-Z_$\d]*:|this|true|false|null|undefined|typeof|instanceof|in|delete|new|void|arguments|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|escape|eval|isFinite|isNaN|parseFloat|parseInt|unescape|dojo|dijit|dojox|window|document|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/g, ""). + match(/([a-z_$][a-z_$\d]*)/gi) || [], t = {}; + arr.forEach(vars, function(v){ + if(!(v in t)){ + args.push(v); + t[v] = 1; + } + }); + } + } + return {args: args, body: s}; // Object + }; + + var compose = function(/*Array*/ a){ + return a.length ? + function(){ + var i = a.length - 1, x = df.lambda(a[i]).apply(this, arguments); + for(--i; i >= 0; --i){ x = df.lambda(a[i]).call(this, x); } + return x; + } + : + // identity + function(x){ return x; }; + }; + + lang.mixin(df, { + // lambda + rawLambda: function(/*String*/ s){ + // summary: + // builds a function from a snippet, or array (composing), + // returns an object describing the function; functions are + // passed through unmodified. + // description: + // This method is to normalize a functional representation (a + // text snippet) to an object that contains an array of + // arguments, and a body , which is used to calculate the + // returning value. + return lambda(s); // Object + }, + buildLambda: function(/*String*/ s){ + // summary: + // builds a function from a snippet, returns a string, which + // represents the function. + // description: + // This method returns a textual representation of a function + // built from the snippet. It is meant to be evaled in the + // proper context, so local variables can be pulled from the + // environment. + s = lambda(s); + return "function(" + s.args.join(",") + "){return (" + s.body + ");}"; // String + }, + lambda: function(/*Function|String|Array*/ s){ + // summary: + // builds a function from a snippet, or array (composing), + // returns a function object; functions are passed through + // unmodified. + // description: + // This method is used to normalize a functional + // representation (a text snippet, an array, or a function) to + // a function object. + if(typeof s == "function"){ return s; } + if(s instanceof Array){ return compose(s); } + if(s in lcache){ return lcache[s]; } + s = lambda(s); + return lcache[s] = new Function(s.args, "return (" + s.body + ");"); // Function + }, + clearLambdaCache: function(){ + // summary: + // clears internal cache of lambdas + lcache = {}; + } + }); + + return df; +}); diff --git a/js/dojo/dojox/lang/functional/linrec.js b/js/dojo/dojox/lang/functional/linrec.js new file mode 100644 index 0000000..066ff54 --- /dev/null +++ b/js/dojo/dojox/lang/functional/linrec.js @@ -0,0 +1,147 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/linrec", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda,dojox/lang/functional/util"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.linrec"); + +dojo.require("dojox.lang.functional.lambda"); +dojo.require("dojox.lang.functional.util"); + +// This module provides recursion combinators: +// - a linear recursion combinator. + +// Acknoledgements: +// - recursion combinators are inspired by Manfred von Thun's article +// "Recursion Theory and Joy" +// (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html) + +// Notes: +// - recursion combinators produce a function, which implements +// their respective recusion patterns. String lambdas are inlined, if possible. + +(function(){ + var df = dojox.lang.functional, inline = df.inlineLambda, + _x ="_x", _r_y_a = ["_r", "_y.a"]; + + df.linrec = function( + /*Function|String|Array*/ cond, + /*Function|String|Array*/ then, + /*Function|String|Array*/ before, + /*Function|String|Array*/ after){ + // summary: + // Generates a function for the linear recursion pattern. + // All parameter functions are called in the context of "this" object. + // cond: + // The lambda expression, which is used to detect the termination of recursion. + // It accepts the same parameter as the generated recursive function itself. + // This function should return "true", if the recursion should be stopped, + // and the "then" part should be executed. Otherwise the recursion will proceed. + // then: + // The lambda expression, which is called upon termination of the recursion. + // It accepts the same parameters as the generated recursive function itself. + // The returned value will be returned as the value of the generated function. + // before: + // The lambda expression, which is called before the recursive step. + // It accepts the same parameter as the generated recursive function itself. + // The returned value should be an array, which is used to call + // the generated function recursively. + // above: + // The lambda expression, which is called after the recursive step. + // It accepts two parameters: the returned value from the recursive step, and + // the original array of parameters used with all other functions. + // The returned value will be returned as the value of the generated function. + + var c, t, b, a, cs, ts, bs, as, dict1 = {}, dict2 = {}, + add2dict = function(x){ dict1[x] = 1; }; + if(typeof cond == "string"){ + cs = inline(cond, _x, add2dict); + }else{ + c = df.lambda(cond); + cs = "_c.apply(this, _x)"; + dict2["_c=_t.c"] = 1; + } + if(typeof then == "string"){ + ts = inline(then, _x, add2dict); + }else{ + t = df.lambda(then); + ts = "_t.t.apply(this, _x)"; + } + if(typeof before == "string"){ + bs = inline(before, _x, add2dict); + }else{ + b = df.lambda(before); + bs = "_b.apply(this, _x)"; + dict2["_b=_t.b"] = 1; + } + if(typeof after == "string"){ + as = inline(after, _r_y_a, add2dict); + }else{ + a = df.lambda(after); + as = "_a.call(this, _r, _y.a)"; + dict2["_a=_t.a"] = 1; + } + var locals1 = df.keys(dict1), locals2 = df.keys(dict2), + f = new Function([], "var _x=arguments,_y,_r".concat( // Function + locals1.length ? "," + locals1.join(",") : "", + locals2.length ? ",_t=_x.callee," + locals2.join(",") : t ? ",_t=_x.callee" : "", + ";for(;!", + cs, + ";_x=", + bs, + "){_y={p:_y,a:_x}}_r=", + ts, + ";for(;_y;_y=_y.p){_r=", + as, + "}return _r" + )); + if(c){ f.c = c; } + if(t){ f.t = t; } + if(b){ f.b = b; } + if(a){ f.a = a; } + return f; + }; +})(); + +/* +For documentation only: + +1) The original recursive version: + +var linrec1 = function(cond, then, before, after){ + var cond = df.lambda(cond), + then = df.lambda(then), + before = df.lambda(before), + after = df.lambda(after); + return function(){ + if(cond.apply(this, arguments)){ + return then.apply(this, arguments); + } + var args = before.apply(this, arguments); + var ret = arguments.callee.apply(this, args); + return after.call(this, ret, arguments); + }; +}; + +2) The original iterative version (before minification and inlining): + +var linrec2 = function(cond, then, before, after){ + var cond = df.lambda(cond), + then = df.lambda(then), + before = df.lambda(before), + after = df.lambda(after); + return function(){ + var args = arguments, top, ret; + // 1st part + for(; !cond.apply(this, args); args = before.apply(this, args)){ + top = {prev: top, args: args}; + } + ret = then.apply(this, args); + //2nd part + for(; top; top = top.prev){ + ret = after.call(this, ret, top.args); + } + return ret; + }; +}; + +*/ +}); diff --git a/js/dojo/dojox/lang/functional/listcomp.js b/js/dojo/dojox/lang/functional/listcomp.js new file mode 100644 index 0000000..e73384e --- /dev/null +++ b/js/dojo/dojox/lang/functional/listcomp.js @@ -0,0 +1,55 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/listcomp", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.listcomp"); + +// This module adds high-level functions and related constructs: +// - list comprehensions similar to JavaScript 1.7 + +// Notes: +// - listcomp() produces functions, which after the compilation step are +// as fast as regular JS functions (at least theoretically). + +(function(){ + var g_re = /\bfor\b|\bif\b/gm; + + var listcomp = function(/*String*/ s){ + var frag = s.split(g_re), act = s.match(g_re), + head = ["var r = [];"], tail = [], i = 0, l = act.length; + while(i < l){ + var a = act[i], f = frag[++i]; + if(a == "for" && !/^\s*\(\s*(;|var)/.test(f)){ + f = f.replace(/^\s*\(/, "(var "); + } + head.push(a, f, "{"); + tail.push("}"); + } + return head.join("") + "r.push(" + frag[0] + ");" + tail.join("") + "return r;"; // String + }; + + dojo.mixin(dojox.lang.functional, { + buildListcomp: function(/*String*/ s){ + // summary: builds a function from a text snippet, which represents a valid + // JS 1.7 list comprehension, returns a string, which represents the function. + // description: This method returns a textual representation of a function + // built from the list comprehension text snippet (conformant to JS 1.7). + // It is meant to be evaled in the proper context, so local variable can be + // pulled from the environment. + return "function(){" + listcomp(s) + "}"; // String + }, + compileListcomp: function(/*String*/ s){ + // summary: builds a function from a text snippet, which represents a valid + // JS 1.7 list comprehension, returns a function object. + // description: This method returns a function built from the list + // comprehension text snippet (conformant to JS 1.7). It is meant to be + // reused several times. + return new Function([], listcomp(s)); // Function + }, + listcomp: function(/*String*/ s){ + // summary: executes the list comprehension building an array. + return (new Function([], listcomp(s)))(); // Array + } + }); +})(); + +}); diff --git a/js/dojo/dojox/lang/functional/multirec.js b/js/dojo/dojox/lang/functional/multirec.js new file mode 100644 index 0000000..0f50668 --- /dev/null +++ b/js/dojo/dojox/lang/functional/multirec.js @@ -0,0 +1,169 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/multirec", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda,dojox/lang/functional/util"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.multirec"); + +dojo.require("dojox.lang.functional.lambda"); +dojo.require("dojox.lang.functional.util"); + +// This module provides recursion combinators: +// - a multi-way recursion combinator. + +// Acknoledgements: +// - recursion combinators are inspired by Manfred von Thun's article +// "Recursion Theory and Joy" +// (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html) + +// Notes: +// - recursion combinators produce a function, which implements +// their respective recusion patterns. String lambdas are inlined, if possible. + +(function(){ + var df = dojox.lang.functional, inline = df.inlineLambda, + _x ="_x", _y_r_y_o = ["_y.r", "_y.o"]; + + df.multirec = function( + /*Function|String|Array*/ cond, + /*Function|String|Array*/ then, + /*Function|String|Array*/ before, + /*Function|String|Array*/ after){ + // summary: + // Generates a function for the multi-way recursion pattern. + // All parameter functions are called in the context of "this" object. + // cond: + // The lambda expression, which is used to detect the termination of recursion. + // It accepts the same parameter as the generated recursive function itself. + // This function should return "true", if the recursion should be stopped, + // and the "then" part should be executed. Otherwise the recursion will proceed. + // then: + // The lambda expression, which is called upon termination of the recursion. + // It accepts the same parameters as the generated recursive function itself. + // The returned value will be returned as the value of the generated function. + // before: + // The lambda expression, which is called before the recursive step. + // It accepts the same parameter as the generated recursive function itself. + // The returned value should be an array, which is used to call + // the generated function recursively. Each member of the array should be + // an array of parameters. The length of it defines how many times + // the generated function is called recursively. + // above: + // The lambda expression, which is called after the recursive step. + // It accepts two parameters: the array of returned values from recursive steps, + // and the original array of parameters used with all other functions. + // The returned value will be returned as the value of the generated function. + + var c, t, b, a, cs, ts, bs, as, dict1 = {}, dict2 = {}, + add2dict = function(x){ dict1[x] = 1; }; + if(typeof cond == "string"){ + cs = inline(cond, _x, add2dict); + }else{ + c = df.lambda(cond); + cs = "_c.apply(this, _x)"; + dict2["_c=_t.c"] = 1; + } + if(typeof then == "string"){ + ts = inline(then, _x, add2dict); + }else{ + t = df.lambda(then); + ts = "_t.apply(this, _x)"; + } + if(typeof before == "string"){ + bs = inline(before, _x, add2dict); + }else{ + b = df.lambda(before); + bs = "_b.apply(this, _x)"; + dict2["_b=_t.b"] = 1; + } + if(typeof after == "string"){ + as = inline(after, _y_r_y_o, add2dict); + }else{ + a = df.lambda(after); + as = "_a.call(this, _y.r, _y.o)"; + dict2["_a=_t.a"] = 1; + } + var locals1 = df.keys(dict1), locals2 = df.keys(dict2), + f = new Function([], "var _y={a:arguments},_x,_r,_z,_i".concat( // Function + locals1.length ? "," + locals1.join(",") : "", + locals2.length ? ",_t=arguments.callee," + locals2.join(",") : "", + t ? (locals2.length ? ",_t=_t.t" : "_t=arguments.callee.t") : "", + ";for(;;){for(;;){if(_y.o){_r=", + as, + ";break}_x=_y.a;if(", + cs, + "){_r=", + ts, + ";break}_y.o=_x;_x=", + bs, + ";_y.r=[];_z=_y;for(_i=_x.length-1;_i>=0;--_i){_y={p:_y,a:_x[_i],z:_z}}}if(!(_z=_y.z)){return _r}_z.r.push(_r);_y=_y.p}" + )); + if(c){ f.c = c; } + if(t){ f.t = t; } + if(b){ f.b = b; } + if(a){ f.a = a; } + return f; + }; +})(); + +/* +For documentation only: + +1) The original recursive version: + +var multirec1 = function(cond, then, before, after){ + var cond = df.lambda(cond), + then = df.lambda(then), + before = df.lambda(before), + after = df.lambda(after); + return function(){ + if(cond.apply(this, arguments)){ + return then.apply(this, arguments); + } + var args = before.apply(this, arguments), + ret = new Array(args.length); + for(var i = 0; i < args.length; ++i){ + ret[i] = arguments.callee.apply(this, args[i]); + } + return after.call(this, ret, arguments); + }; +}; + +2) The original iterative version (before minification and inlining): + +var multirec2 = function(cond, then, before, after){ + var cond = df.lambda(cond), + then = df.lambda(then), + before = df.lambda(before), + after = df.lambda(after); + return function(){ + var top = {args: arguments}, args, ret, parent, i; + for(;;){ + for(;;){ + if(top.old){ + ret = after.call(this, top.ret, top.old); + break; + } + args = top.args; + if(cond.apply(this, args)){ + ret = then.apply(this, args); + break; + } + top.old = args; + args = before.apply(this, args); + top.ret = []; + parent = top; + for(i = args.length - 1; i >= 0; --i){ + top = {prev: top, args: args[i], parent: parent}; + } + } + if(!(parent = top.parent)){ + return ret; + } + parent.ret.push(ret); + top = top.prev; + } + }; +}; + +*/ + +}); diff --git a/js/dojo/dojox/lang/functional/numrec.js b/js/dojo/dojox/lang/functional/numrec.js new file mode 100644 index 0000000..b9e76c9 --- /dev/null +++ b/js/dojo/dojox/lang/functional/numrec.js @@ -0,0 +1,95 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/numrec", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda,dojox/lang/functional/util"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.numrec"); + +dojo.require("dojox.lang.functional.lambda"); +dojo.require("dojox.lang.functional.util"); + +// This module provides recursion combinators: +// - a simplified numeric linear recursion combinator. + +// Acknoledgements: +// - recursion combinators are inspired by Manfred von Thun's article +// "Recursion Theory and Joy" +// (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html) + +// Notes: +// - recursion combinators produce a function, which implements +// their respective recusion patterns. String lambdas are inlined, if possible. + +(function(){ + var df = dojox.lang.functional, inline = df.inlineLambda, + _r_i = ["_r", "_i"]; + + df.numrec = function(/*Object*/ then, /*Function|String|Array*/ after){ + // summary: + // Generates a function for the simplified numeric linear recursion pattern. + // All parameter functions are called in the context of "this" object. + // description: + // This is a simplification of the linear recursion combinator: + // - the generated function takes one numeric parameter "x", + // - the "cond" is fixed and checks for 0. + // - the "before" is fixed and the generated function is called with "x - 1". + // - the "above is called with two parameters: the return from the generated + // function, and with "x". + // - as you can see the recursion is done by decreasing the parameter, + // and calling itself until it reaches 0. + // then: + // The value, which is used upon termination of the recursion. + // It will be returned as the value of the generated function. + // above: + // The lambda expression, which is called after the recursive step. + // It accepts two parameters: the returned value from the recursive step, and + // the original parameter. The returned value will be returned as the value of + // the generated function. + + var a, as, dict = {}, + add2dict = function(x){ dict[x] = 1; }; + if(typeof after == "string"){ + as = inline(after, _r_i, add2dict); + }else{ + a = df.lambda(after); + as = "_a.call(this, _r, _i)"; + } + var locals = df.keys(dict), + f = new Function(["_x"], "var _t=arguments.callee,_r=_t.t,_i".concat( // Function + locals.length ? "," + locals.join(",") : "", + a ? ",_a=_t.a" : "", + ";for(_i=1;_i<=_x;++_i){_r=", + as, + "}return _r" + )); + f.t = then; + if(a){ f.a = a; } + return f; + }; +})(); + +/* +For documentation only: + +1) The original recursive version: + +var numrec1 = function(then, after){ + var after = df.lambda(after); + return function(x){ + return x ? after.call(this, arguments.callee.call(this, x - 1), x) : then; + }; +}; + +2) The original iterative version (before minification and inlining): + +var numrec2 = function(then, after){ + var after = df.lambda(after); + return function(x){ + var ret = then, i; + for(i = 1; i <= x; ++i){ + ret = after.call(this, ret, i); + } + return ret; + }; +}; + +*/ +}); diff --git a/js/dojo/dojox/lang/functional/object.js b/js/dojo/dojox/lang/functional/object.js new file mode 100644 index 0000000..c35ef4f --- /dev/null +++ b/js/dojo/dojox/lang/functional/object.js @@ -0,0 +1,77 @@ +//>>built +define("dojox/lang/functional/object", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/window", "./lambda"], function(dojo, lang, win, df){ + +// This module adds high-level functions and related constructs: +// - object/dictionary helpers + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - skip all attributes that are present in the empty object +// (IE and/or 3rd-party libraries). + + var empty = {}; + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // object helpers + keys: function(/*Object*/ obj){ + // summary: returns an array of all keys in the object + var t = []; + for(var i in obj){ + if(!(i in empty)){ + t.push(i); + } + } + return t; // Array + }, + values: function(/*Object*/ obj){ + // summary: returns an array of all values in the object + var t = []; + for(var i in obj){ + if(!(i in empty)){ + t.push(obj[i]); + } + } + return t; // Array + }, + filterIn: function(/*Object*/ obj, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates new object with all attributes that pass the test + // implemented by the provided function. + o = o || win.global; f = df.lambda(f); + var t = {}, v, i; + for(i in obj){ + if(!(i in empty)){ + v = obj[i]; + if(f.call(o, v, i, obj)){ t[i] = v; } + } + } + return t; // Object + }, + forIn: function(/*Object*/ obj, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: iterates over all object attributes. + o = o || win.global; f = df.lambda(f); + for(var i in obj){ + if(!(i in empty)){ + f.call(o, obj[i], i, obj); + } + } + return o; // Object + }, + mapIn: function(/*Object*/ obj, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates new object with the results of calling + // a provided function on every attribute in this object. + o = o || win.global; f = df.lambda(f); + var t = {}, i; + for(i in obj){ + if(!(i in empty)){ + t[i] = f.call(o, obj[i], i, obj); + } + } + return t; // Object + } + }); + + return df; +}); diff --git a/js/dojo/dojox/lang/functional/reversed.js b/js/dojo/dojox/lang/functional/reversed.js new file mode 100644 index 0000000..d049be9 --- /dev/null +++ b/js/dojo/dojox/lang/functional/reversed.js @@ -0,0 +1,76 @@ +//>>built +define("dojox/lang/functional/reversed", ["dojo/_base/lang", "dojo/_base/window" ,"./lambda"], + function(lang, win, df){ +// This module adds high-level functions and related constructs: +// - reversed versions of array-processing functions similar to standard JS functions + +// Notes: +// - this module provides reversed versions of standard array-processing functions: +// forEachRev, mapRev, filterRev + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // JS 1.6 standard array functions, which can take a lambda as a parameter. + // Consider using dojo._base.array functions, if you don't need the lambda support. + filterRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with all elements that pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var t = [], v, i = a.length - 1; + for(; i >= 0; --i){ + v = a[i]; + if(f.call(o, v, i, a)){ t.push(v); } + } + return t; // Array + }, + forEachRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: executes a provided function once per array element. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length - 1; i >= 0; f.call(o, a[i], i, a), --i); + }, + mapRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with the results of calling + // a provided function on every element in this array. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var n = a.length, t = new Array(n), i = n - 1, j = 0; + for(; i >= 0; t[j++] = f.call(o, a[i], i, a), --i); + return t; // Array + }, + everyRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether all elements in the array pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length - 1; i >= 0; --i){ + if(!f.call(o, a[i], i, a)){ + return false; // Boolean + } + } + return true; // Boolean + }, + someRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether some element in the array passes the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length - 1; i >= 0; --i){ + if(f.call(o, a[i], i, a)){ + return true; // Boolean + } + } + return false; // Boolean + } + }); + + return df; +}); diff --git a/js/dojo/dojox/lang/functional/scan.js b/js/dojo/dojox/lang/functional/scan.js new file mode 100644 index 0000000..8abbe47 --- /dev/null +++ b/js/dojo/dojox/lang/functional/scan.js @@ -0,0 +1,104 @@ +//>>built +define("dojox/lang/functional/scan", ["dojo/_base/kernel", "dojo/_base/lang", "./lambda"], function(d, darray, df){ + +// This module adds high-level functions and related constructs: +// - "scan" family of functions + +// Notes: +// - missing high-level functions are provided with the compatible API: +// scanl, scanl1, scanr, scanr1 + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument +// - take an iterator objects as the array argument (only scanl, and scanl1) + + var empty = {}; + + d.mixin(df, { + // classic reduce-class functions + scanl: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right using a seed value as a starting point; returns an array + // of values produced by foldl() at that point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var t, n, i; + if(d.isArray(a)){ + // array + t = new Array((n = a.length) + 1); + t[0] = z; + for(i = 0; i < n; z = f.call(o, z, a[i], i, a), t[++i] = z); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + t = [z]; + for(i = 0; a.hasNext(); t.push(z = f.call(o, z, a.next(), i++, a))); + }else{ + // object/dictionary + t = [z]; + for(i in a){ + if(!(i in empty)){ + t.push(z = f.call(o, z, a[i], i, a)); + } + } + } + return t; // Array + }, + scanl1: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right; returns an array of values produced by foldl1() at that + // point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var t, n, z, first = true; + if(d.isArray(a)){ + // array + t = new Array(n = a.length); + t[0] = z = a[0]; + for(var i = 1; i < n; t[i] = z = f.call(o, z, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + if(a.hasNext()){ + t = [z = a.next()]; + for(i = 1; a.hasNext(); t.push(z = f.call(o, z, a.next(), i++, a))); + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + if(first){ + t = [z = a[i]]; + first = false; + }else{ + t.push(z = f.call(o, z, a[i], i, a)); + } + } + } + } + return t; // Array + }, + scanr: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left using a seed value as a starting point; returns an array + // of values produced by foldr() at that point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var n = a.length, t = new Array(n + 1), i = n; + t[n] = z; + for(; i > 0; --i, z = f.call(o, z, a[i], i, a), t[i] = z); + return t; // Array + }, + scanr1: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left; returns an array of values produced by foldr1() at that + // point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var n = a.length, t = new Array(n), z = a[n - 1], i = n - 1; + t[i] = z; + for(; i > 0; --i, z = f.call(o, z, a[i], i, a), t[i] = z); + return t; // Array + } + }); +}); diff --git a/js/dojo/dojox/lang/functional/sequence.js b/js/dojo/dojox/lang/functional/sequence.js new file mode 100644 index 0000000..3ba4d9e --- /dev/null +++ b/js/dojo/dojox/lang/functional/sequence.js @@ -0,0 +1,39 @@ +//>>built +define("dojox/lang/functional/sequence", ["dojo/_base/lang", "./lambda"], function(lang, df){ + +// This module adds high-level functions and related constructs: +// - sequence generators + +// If you want more general sequence builders check out listcomp.js and +// unfold() (in fold.js). + +// Defined methods: +// - take any valid lambda argument as the functional argument + +/*===== + var df = dojox.lang.functional; + =====*/ + + lang.mixin(df, { + // sequence generators + repeat: function(/*Number*/ n, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: builds an array by repeatedly applying a unary function N times + // with a seed value Z. N should be greater than 0. + o = o || dojo.global; f = df.lambda(f); + var t = new Array(n), i = 1; + t[0] = z; + for(; i < n; t[i] = z = f.call(o, z), ++i); + return t; // Array + }, + until: function(/*Function|String|Array*/ pr, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: builds an array by repeatedly applying a unary function with + // a seed value Z until the predicate is satisfied. + o = o || dojo.global; f = df.lambda(f); pr = df.lambda(pr); + var t = []; + for(; !pr.call(o, z); t.push(z), z = f.call(o, z)); + return t; // Array + } + }); + + return df; +}); diff --git a/js/dojo/dojox/lang/functional/tailrec.js b/js/dojo/dojox/lang/functional/tailrec.js new file mode 100644 index 0000000..442e2be --- /dev/null +++ b/js/dojo/dojox/lang/functional/tailrec.js @@ -0,0 +1,120 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/tailrec", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda,dojox/lang/functional/util"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.tailrec"); + +dojo.require("dojox.lang.functional.lambda"); +dojo.require("dojox.lang.functional.util"); + +// This module provides recursion combinators: +// - a tail recursion combinator. + +// Acknoledgements: +// - recursion combinators are inspired by Manfred von Thun's article +// "Recursion Theory and Joy" +// (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html) + +// Notes: +// - recursion combinators produce a function, which implements +// their respective recusion patterns. String lambdas are inlined, if possible. + +(function(){ + var df = dojox.lang.functional, inline = df.inlineLambda, _x ="_x"; + + df.tailrec = function( + /*Function|String|Array*/ cond, + /*Function|String|Array*/ then, + /*Function|String|Array*/ before){ + // summary: + // Generates a function for the tail recursion pattern. This is the simplified + // version of the linear recursive combinator without the "after" function, + // and with the modified "before" function. All parameter functions are called + // in the context of "this" object. + // cond: + // The lambda expression, which is used to detect the termination of recursion. + // It accepts the same parameter as the generated recursive function itself. + // This function should return "true", if the recursion should be stopped, + // and the "then" part should be executed. Otherwise the recursion will proceed. + // then: + // The lambda expression, which is called upon termination of the recursion. + // It accepts the same parameters as the generated recursive function itself. + // The returned value will be returned as the value of the generated function. + // before: + // The lambda expression, which is called before the recursive step. + // It accepts the same parameter as the generated recursive function itself, + // and returns an array of arguments for the next recursive call of + // the generated function. + + var c, t, b, cs, ts, bs, dict1 = {}, dict2 = {}, + add2dict = function(x){ dict1[x] = 1; }; + if(typeof cond == "string"){ + cs = inline(cond, _x, add2dict); + }else{ + c = df.lambda(cond); + cs = "_c.apply(this, _x)"; + dict2["_c=_t.c"] = 1; + } + if(typeof then == "string"){ + ts = inline(then, _x, add2dict); + }else{ + t = df.lambda(then); + ts = "_t.t.apply(this, _x)"; + } + if(typeof before == "string"){ + bs = inline(before, _x, add2dict); + }else{ + b = df.lambda(before); + bs = "_b.apply(this, _x)"; + dict2["_b=_t.b"] = 1; + } + var locals1 = df.keys(dict1), locals2 = df.keys(dict2), + f = new Function([], "var _x=arguments,_t=_x.callee,_c=_t.c,_b=_t.b".concat( // Function + locals1.length ? "," + locals1.join(",") : "", + locals2.length ? ",_t=_x.callee," + locals2.join(",") : t ? ",_t=_x.callee" : "", + ";for(;!", + cs, + ";_x=", + bs, + ");return ", + ts + )); + if(c){ f.c = c; } + if(t){ f.t = t; } + if(b){ f.b = b; } + return f; + }; +})(); + +/* +For documentation only: + +1) The original recursive version: + +var tailrec1 = function(cond, then, before){ + var cond = df.lambda(cond), + then = df.lambda(then), + before = df.lambda(before); + return function(){ + if(cond.apply(this, arguments)){ + return then.apply(this, arguments); + } + var args = before.apply(this, arguments); + return arguments.callee.apply(this, args); + }; +}; + +2) The original iterative version (before minification and inlining): + +var tailrec2 = function(cond, then, before){ + var cond = df.lambda(cond), + then = df.lambda(then), + before = df.lambda(before); + return function(){ + var args = arguments; + for(; !cond.apply(this, args); args = before.apply(this, args)); + return then.apply(this, args); + }; +}; + +*/ +}); diff --git a/js/dojo/dojox/lang/functional/util.js b/js/dojo/dojox/lang/functional/util.js new file mode 100644 index 0000000..57c4fe5 --- /dev/null +++ b/js/dojo/dojox/lang/functional/util.js @@ -0,0 +1,51 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/util", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.util"); + +dojo.require("dojox.lang.functional.lambda"); + +// This module provides helpers: +// - inlining string lambda functions. + +(function(){ + var df = dojox.lang.functional; + + dojo.mixin(df, { + inlineLambda: function(/*String*/ lambda, /*String|Array*/ init, /*Function?*/ add2dict){ + // summary: + // Creates the inlined version of a string lambda. + // lambda: + // The String variable representing the lambda function. + // init: + // Conveys how to initialize parameters. If it is a String, then the apply() method + // would be emulated treating "init" as a list of input parameters. + // It it is an Array, then the call() method is emulated treating array members + // as input parameters. + // add2dict: + // The optional function, which is used to record names of lambda parameters. + // If supplied, this function is called with a name of every parameter. + + var s = df.rawLambda(lambda); + if(add2dict){ + df.forEach(s.args, add2dict); + } + var ap = typeof init == "string", // apply or call? + n = ap ? s.args.length : Math.min(s.args.length, init.length), + a = new Array(4 * n + 4), i, j = 1; + for(i = 0; i < n; ++i){ + a[j++] = s.args[i]; + a[j++] = "="; + a[j++] = ap ? init + "[" + i + "]": init[i]; + a[j++] = ","; + } + a[0] = "("; + a[j++] = "("; + a[j++] = s.body; + a[j] = "))"; + return a.join(""); // String + } + }); +})(); + +}); diff --git a/js/dojo/dojox/lang/functional/zip.js b/js/dojo/dojox/lang/functional/zip.js new file mode 100644 index 0000000..7b721f8 --- /dev/null +++ b/js/dojo/dojox/lang/functional/zip.js @@ -0,0 +1,45 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/zip", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.zip"); + +// This module adds high-level functions and related constructs: +// - zip combiners + +// Defined methods: +// - operate on dense arrays + +(function(){ + var df = dojox.lang.functional; + + dojo.mixin(df, { + // combiners + zip: function(){ + // summary: returns an array of arrays, where the i-th array + // contains the i-th element from each of the argument arrays. + // description: This is the venerable zip combiner (for example, + // see Python documentation for general details). The returned + // array is truncated to match the length of the shortest input + // array. + var n = arguments[0].length, m = arguments.length, i = 1, t = new Array(n), j, p; + for(; i < m; n = Math.min(n, arguments[i++].length)); + for(i = 0; i < n; ++i){ + p = new Array(m); + for(j = 0; j < m; p[j] = arguments[j][i], ++j); + t[i] = p; + } + return t; // Array + }, + unzip: function(/*Array*/ a){ + // summary: similar to dojox.lang.functional.zip(), but takes + // a single array of arrays as the input. + // description: This function is similar to dojox.lang.functional.zip() + // and can be used to unzip objects packed by + // dojox.lang.functional.zip(). It is here mostly to provide + // a short-cut for the different method signature. + return df.zip.apply(null, a); // Array + } + }); +})(); + +}); diff --git a/js/dojo/dojox/lang/observable.js b/js/dojo/dojox/lang/observable.js new file mode 100644 index 0000000..e5b1176 --- /dev/null +++ b/js/dojo/dojox/lang/observable.js @@ -0,0 +1,269 @@ +//>>built +// wrapped by build app +define("dojox/lang/observable", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.observable"); +// Used to create a wrapper object with monitored reads and writes +// +dojo.experimental("dojox.lang.observable"); +// IMPORTANT DISCLAIMER: +// This is experimental and based on hideous hacks. +// There are severe limitations on the ability of wrapper objects: +// Only properties that have vbscript-legal names are accessible (similar to JavaScript, but they can't start with an underscore). +// The wrapper objects are not expando in IE, because they are built +// from VBScript objects. This means you can't add new properties after an object is created. +// The wrapper objects can not be used a prototype for other objects. +// Only properties with primitive values can be wrapped. +// This has performance implications as well. +dojox.lang.observable = function(/*Object*/wrapped,/*function*/onRead,/*function*/onWrite,/*function*/onInvoke){ + // summary: + // Creates a wrapper object, which can be observed. The wrapper object + // is a proxy to the wrapped object. If you will be making multiple wrapper + // objects with the same set of listeners, it is recommended that you + // use makeObservable, as it is more memory efficient. + // + // wrapped: + // The object to be wrapped and monitored for property access and modification + // + // onRead: + // See dojox.lang.makeObservable.onRead + // onWrite: + // See dojox.lang.makeObservable.onWrite + // onInvoke: + // See dojox.lang.makeObservable.onInvoke + + return dojox.lang.makeObservable(onRead,onWrite,onInvoke)(wrapped); +} +dojox.lang.makeObservable = function(/*function*/onRead,/*function*/onWrite,/*function*/onInvoke,/*Object*/hiddenFunctions){ + + // summary: + // Creates and returns an observable creator function. All the objects that + // are created with the returned constructor will use the provided onRead and + // onWrite listeners. + // The created constructor should be called with a single argument, + // the object that will be wrapped to be observed. The constructor will + // return the wrapper object. + // + // onRead: + // This is called whenever one of the wrapper objects created + // from the constructor has a property that is accessed. onRead + // will be called with two arguments, the first being the wrapped object, + // and the second is the name of property that is being accessed. + // The value that onRead returns will be used as the value returned + // by the property access + // + // onWrite: + // This is called whenever one of the wrapper objects created + // from the constructor has a property that is modified. onWrite + // will be called with three arguments, the first being the wrapped object, + // the second is the name of property that is being modified, and the + // third is the value that is being set on the property. + // + // onInvoke: + // This is called when a method on the object is invoked. The first + // argument is the wrapper object, the second is the original wrapped object, + // the third is the method name, and the fourth is the arguments. + // + // hiddenFunctions: + // allows you to define functions that should be delegated + // but may not be enumerable on the wrapped objects, so they must be + // explicitly included + // + // example: + // The following could be used to create a wrapper that would + // prevent functions from being accessed on an object: + // | function onRead(obj,prop){ + // | return typeof obj[prop] == 'function' ? null : obj[prop]; + // | } + // | var observable = dojox.lang.makeObservable(onRead,onWrite); + // | var obj = {foo:1,bar:function(){}}; + // | obj = observable(obj); + // | obj.foo -> 1 + // | obj.bar -> null + // + hiddenFunctions = hiddenFunctions || {}; + onInvoke = onInvoke || function(scope,obj,method,args){ + // default implementation for onInvoke, just passes the call through + return obj[method].apply(scope,args); + }; + function makeInvoker(scope,wrapped,i){ + return function(){ + // this is function used for all methods in the wrapper object + return onInvoke(scope,wrapped,i,arguments); + }; + } + + if(dojox.lang.lettableWin){ // create the vb class + var factory = dojox.lang.makeObservable; + factory.inc = (factory.inc || 0) + 1; + // create globals for the getters and setters so they can be accessed from the vbscript + var getName = "gettable_"+factory.inc; + dojox.lang.lettableWin[getName] = onRead; + var setName = "settable_"+factory.inc; + dojox.lang.lettableWin[setName] = onWrite; + var cache = {}; + return function(wrapped){ + if(wrapped.__observable){ // if it already has an observable, use that + return wrapped.__observable; + } + if(wrapped.data__){ + throw new Error("Can wrap an object that is already wrapped"); + } + // create the class + var props = [], i, l; + for(i in hiddenFunctions){ + props.push(i); + } + var vbReservedWords = {type:1,event:1}; + // find the unique signature for the class so we can reuse it if possible + for(i in wrapped){ + if(i.match(/^[a-zA-Z][\w\$_]*$/) && !(i in hiddenFunctions) && !(i in vbReservedWords)){ //can only do properties with valid vb names/tokens and primitive values + props.push(i); + } + } + var signature = props.join(","); + var prop,clazz = cache[signature]; + if(!clazz){ + var tname = "dj_lettable_"+(factory.inc++); + var gtname = tname+"_dj_getter"; + var cParts = [ + "Class "+tname, + " Public data__" // this our reference to the original object + ]; + for(i=0, l=props.length; i<l; i++){ + prop = props[i]; + var type = typeof wrapped[prop]; + if(type == 'function' || hiddenFunctions[prop]){ // functions must go in regular properties for delegation:/ + cParts.push(" Public " + prop); + }else if(type != 'object'){ // the getters/setters can only be applied to primitives + cParts.push( + " Public Property Let "+prop+"(val)", + " Call "+setName+"(me.data__,\""+prop+"\",val)", + " End Property", + " Public Property Get "+prop, + " "+prop+" = "+getName+"(me.data__,\""+prop+"\")", + " End Property"); + } + } + cParts.push("End Class"); + cParts.push( + "Function "+gtname+"()", + " Dim tmp", + " Set tmp = New "+tname, + " Set "+gtname+" = tmp", + "End Function"); + dojox.lang.lettableWin.vbEval(cParts.join("\n")); + + // Put the new class in the cache + cache[signature] = clazz = function(){ + return dojox.lang.lettableWin.construct(gtname); // the class can't be accessed, only called, so we have to wrap it with a function + }; + } + console.log("starting5"); + var newObj = clazz(); + newObj.data__ = wrapped; + console.log("starting6"); + try { + wrapped.__observable = newObj; + } catch(e){ // some objects are not expando + } + for(i = 0, l = props.length; i < l; i++){ + prop = props[i]; + try { + var val = wrapped[prop]; + } + catch(e){ + console.log("error ",prop,e); + } + if(typeof val == 'function' || hiddenFunctions[prop]){ // we can make a delegate function here + newObj[prop] = makeInvoker(newObj,wrapped,prop); + } + } + return newObj; + }; + }else{ + return function(wrapped){ // do it with getters and setters + if(wrapped.__observable){ // if it already has an observable, use that + return wrapped.__observable; + } + var newObj = wrapped instanceof Array ? [] : {}; + newObj.data__ = wrapped; + for(var i in wrapped){ + if(i.charAt(0) != '_'){ + if(typeof wrapped[i] == 'function'){ + newObj[i] = makeInvoker(newObj,wrapped,i); // TODO: setup getters and setters so we can detect when this changes + }else if(typeof wrapped[i] != 'object'){ + (function(i){ + newObj.__defineGetter__(i,function(){ + return onRead(wrapped,i); + }); + newObj.__defineSetter__(i,function(value){ + return onWrite(wrapped,i,value); + }); + })(i); + } + } + } + for(i in hiddenFunctions){ + newObj[i] = makeInvoker(newObj,wrapped,i); + } + wrapped.__observable = newObj; + return newObj; + }; + } +}; +if(!{}.__defineGetter__){ + if(dojo.isIE){ + // to setup the crazy lettable hack we need to + // introduce vb script eval + // the only way that seems to work for adding a VBScript to the page is with a document.write + // document.write is not always available, so we use an iframe to do the document.write + // the iframe also provides a good hiding place for all the global variables that we must + // create in order for JScript and VBScript to interact. + var frame; + if(document.body){ // if the DOM is ready we can add it + frame = document.createElement("iframe"); + document.body.appendChild(frame); + }else{ // other we have to write it out + document.write("<iframe id='dj_vb_eval_frame'></iframe>"); + frame = document.getElementById("dj_vb_eval_frame"); + } + frame.style.display="none"; + var doc = frame.contentWindow.document; + dojox.lang.lettableWin = frame.contentWindow; + doc.write('<html><head><script language="VBScript" type="text/VBScript">' + + 'Function vb_global_eval(code)' + + 'ExecuteGlobal(code)' + + 'End Function' + + '</script>' + + '<script type="text/javascript">' + + 'function vbEval(code){ \n' + // this has to be here to call it from another frame + 'return vb_global_eval(code);' + + '}' + + 'function construct(name){ \n' + // and this too + 'return window[name]();' + + '}' + + '</script>' + + '</head><body>vb-eval</body></html>'); + doc.close(); + }else{ + throw new Error("This browser does not support getters and setters"); + } +} + +dojox.lang.ReadOnlyProxy = +// summary: +// Provides a read only proxy to another object, this can be +// very useful in object-capability systems +// example: +// | var obj = {foo:"bar"}; +// | var readonlyObj = dojox.lang.ReadOnlyProxy(obj); +// | readonlyObj.foo = "test" // throws an error +// | obj.foo = "new bar"; +// | readonlyObj.foo -> returns "new bar", always reflects the current value of the original (it is not just a copy) +dojox.lang.makeObservable(function(obj,i){ + return obj[i]; + },function(obj,i,value){ + // just ignore, exceptions don't seem to propagate through the VB stack. +}); + +}); diff --git a/js/dojo/dojox/lang/oo/Decorator.js b/js/dojo/dojox/lang/oo/Decorator.js new file mode 100644 index 0000000..42f18c7 --- /dev/null +++ b/js/dojo/dojox/lang/oo/Decorator.js @@ -0,0 +1,41 @@ +//>>built +// wrapped by build app +define("dojox/lang/oo/Decorator", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.oo.Decorator"); + +(function(){ + var oo = dojox.lang.oo, + + D = oo.Decorator = function(value, decorator){ + // summary: + // The base class for all decorators. + // description: + // This object holds an original function or another decorator + // object, and implements a special mixin algorithm to be used + // by dojox.lang.oo.mixin. + // value: Object: + // a payload to be processed by the decorator. + // decorator: Function|Object: + // a function to handle the custom assignment, or an object with exec() + // method. The signature is: + // decorator(/*String*/ name, /*Function*/ newValue, /*Function*/ oldValue). + this.value = value; + this.decorator = typeof decorator == "object" ? + function(){ return decorator.exec.apply(decorator, arguments); } : decorator; + }; + + oo.makeDecorator = function(decorator){ + // summary: + // creates new custom decorator creator + // decorator: Function|Object: + // a function to handle the custom assignment, + // or an object with exec() method + // returns: Function: + // new decorator constructor + return function(value){ + return new D(value, decorator); + }; + }; +})(); + +}); diff --git a/js/dojo/dojox/lang/oo/Filter.js b/js/dojo/dojox/lang/oo/Filter.js new file mode 100644 index 0000000..f85eb10 --- /dev/null +++ b/js/dojo/dojox/lang/oo/Filter.js @@ -0,0 +1,49 @@ +//>>built +// wrapped by build app +define("dojox/lang/oo/Filter", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.oo.Filter"); + +(function(){ + var oo = dojox.lang.oo, + + F = oo.Filter = function(bag, filter){ + // summary: + // Filter to control mixing in objects by skipping + // properties and renaming them. + // description: + // This object is used as a holder of an original object + // (whose properites are to be copied), and a filter + // function used while copying by dojox.lang.oo.mixin. + // bag: Object: + // object to be filtered + // filter: Function|Object: + // a function to handle the name filtering, + // or an object with exec() method + this.bag = bag; + this.filter = typeof filter == "object" ? + function(){ return filter.exec.apply(filter, arguments); } : filter; + }, + + // the default map-based filter object + MapFilter = function(map){ + this.map = map; + }; + + MapFilter.prototype.exec = function(name){ + return this.map.hasOwnProperty(name) ? this.map[name] : name; + }; + + oo.filter = function(bag, map){ + // summary: + // creates a simple filter object + // bag: Object: + // object to be filtered + // map: Object: + // the dictionary for renaming/removing while copying + // returns: + // new dojox.lang.oo.Filter object + return new F(bag, new MapFilter(map)); + }; +})(); + +}); diff --git a/js/dojo/dojox/lang/oo/aop.js b/js/dojo/dojox/lang/oo/aop.js new file mode 100644 index 0000000..df2f03b --- /dev/null +++ b/js/dojo/dojox/lang/oo/aop.js @@ -0,0 +1,79 @@ +//>>built +// wrapped by build app +define("dojox/lang/oo/aop", ["dijit","dojo","dojox","dojo/require!dojox/lang/oo/Decorator,dojox/lang/oo/general"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.oo.aop"); + +dojo.require("dojox.lang.oo.Decorator"); +dojo.require("dojox.lang.oo.general"); + +(function(){ + var oo = dojox.lang.oo, md = oo.makeDecorator, oog = oo.general, ooa = oo.aop, + isF = dojo.isFunction; + + // five decorators implementing light-weight AOP weaving + + /*===== + ooa.before = md(function(name, newValue, oldValue){ + // summary: creates a "before" advise, by calling new function + // before the old one + + // dummy body + }); + + ooa.around = md(function(name, newValue, oldValue){ + // summary: creates an "around" advise, + // the previous value is passed as a first argument and can be null, + // arguments are passed as a second argument + + // dummy body + }); + =====*/ + + // reuse existing decorators + ooa.before = oog.before; + ooa.around = oog.wrap; + + ooa.afterReturning = md(function(name, newValue, oldValue){ + // summary: creates an "afterReturning" advise, + // the returned value is passed as the only argument + return isF(oldValue) ? + function(){ + var ret = oldValue.apply(this, arguments); + newValue.call(this, ret); + return ret; + } : function(){ newValue.call(this); }; + }); + + ooa.afterThrowing = md(function(name, newValue, oldValue){ + // summary: creates an "afterThrowing" advise, + // the exception is passed as the only argument + return isF(oldValue) ? + function(){ + var ret; + try{ + ret = oldValue.apply(this, arguments); + }catch(e){ + newValue.call(this, e); + throw e; + } + return ret; + } : oldValue; + }); + + ooa.after = md(function(name, newValue, oldValue){ + // summary: creates an "after" advise, + // it takes no arguments + return isF(oldValue) ? + function(){ + var ret; + try{ + ret = oldValue.apply(this, arguments); + }finally{ + newValue.call(this); + } + return ret; + } : function(){ newValue.call(this); } + }); +})(); + +}); diff --git a/js/dojo/dojox/lang/oo/general.js b/js/dojo/dojox/lang/oo/general.js new file mode 100644 index 0000000..874b857 --- /dev/null +++ b/js/dojo/dojox/lang/oo/general.js @@ -0,0 +1,65 @@ +//>>built +// wrapped by build app +define("dojox/lang/oo/general", ["dijit","dojo","dojox","dojo/require!dojox/lang/oo/Decorator"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.oo.general"); + +dojo.require("dojox.lang.oo.Decorator"); + +(function(){ + var oo = dojox.lang.oo, md = oo.makeDecorator, oog = oo.general, + isF = dojo.isFunction; + + // generally useful decorators + + oog.augment = md(function(name, newValue, oldValue){ + // summary: add property, if it was not defined before + return typeof oldValue == "undefined" ? newValue : oldValue; + }); + + oog.override = md(function(name, newValue, oldValue){ + // summary: override property only if it was already present + return typeof oldValue != "undefined" ? newValue : oldValue; + }); + + oog.shuffle = md(function(name, newValue, oldValue){ + // summary: replaces arguments for an old method + return isF(oldValue) ? + function(){ + return oldValue.apply(this, newValue.apply(this, arguments)); + } : oldValue; + }); + + oog.wrap = md(function(name, newValue, oldValue){ + // summary: wraps the old values with a supplied function + return function(){ return newValue.call(this, oldValue, arguments); }; + }); + + oog.tap = md(function(name, newValue, oldValue){ + // summary: always returns "this" ignoring the actual return + return function(){ newValue.apply(this, arguments); return this; }; + }); + + oog.before = md(function(name, newValue, oldValue){ + // summary: + // creates a chain of calls where the new method is called + // before the old method + return isF(oldValue) ? + function(){ + newValue.apply(this, arguments); + return oldValue.apply(this, arguments); + } : newValue; + }); + + oog.after = md(function(name, newValue, oldValue){ + // summary: + // creates a chain of calls where the new method is called + // after the old method + return isF(oldValue) ? + function(){ + oldValue.apply(this, arguments); + return newValue.apply(this, arguments); + } : newValue; + }); +})(); + +}); diff --git a/js/dojo/dojox/lang/oo/mixin.js b/js/dojo/dojox/lang/oo/mixin.js new file mode 100644 index 0000000..ec2c060 --- /dev/null +++ b/js/dojo/dojox/lang/oo/mixin.js @@ -0,0 +1,139 @@ +//>>built +// wrapped by build app +define("dojox/lang/oo/mixin", ["dijit","dojo","dojox","dojo/require!dojox/lang/oo/Filter,dojox/lang/oo/Decorator"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.oo.mixin"); + +dojo.experimental("dojox.lang.oo.mixin"); + +dojo.require("dojox.lang.oo.Filter"); +dojo.require("dojox.lang.oo.Decorator"); + +(function(){ + var oo = dojox.lang.oo, Filter = oo.Filter, Decorator = oo.Decorator, empty = {}, + defaultFilter = function(name){ return name; }, + defaultDecorator = function(name, newValue, oldValue){ return newValue; }, + defaultMixer = function(target, name, newValue, oldValue){ target[name] = newValue; }, + defaults = {}, // for the internal use in the mixin() + extraNames = dojo._extraNames, extraLen = extraNames.length, + + applyDecorator = oo.applyDecorator = function(decorator, name, newValue, oldValue){ + // summary: + // applies a decorator unraveling all embedded decorators + // decorator: Function: + // top-level decorator to apply + // name: String: + // name of the property + // newValue: Object: + // new value of the property + // oldValue: Object: + // old value of the property + // returns: Object: + // returns the final value of the property + if(newValue instanceof Decorator){ + var d = newValue.decorator; + newValue = applyDecorator(decorator, name, newValue.value, oldValue); + return d(name, newValue, oldValue); + } + return decorator(name, newValue, oldValue); + }; + + /*===== + dojox.lang.oo.__MixinDefaults = function(){ + // summary: + // a dict of default parameters for dojox.lang.oo._mixin + // decorator: Function: + // a decorator function to be used in absence of other decorators + // filter: Function: + // a filter function to be used in absence of other filters + // mixer: Function: + // a mixer function to be used to mix in new properties + this.decorator = decorator; + this.filter = filter; + this.mixer = mixer; + }; + =====*/ + + oo.__mixin = function(target, source, decorator, filter, mixer){ + // summary: + // mixes in two objects processing decorators and filters + // target: Object: + // target to receive new/updated properties + // source: Object: + // source of properties + // defaults: dojox.lang.oo.__MixinDefaults?: + // default functions for various aspects of mixing + // returns: Object: + // target + + var name, targetName, prop, newValue, oldValue, i; + + // start mixing in properties + for(name in source){ + prop = source[name]; + if(!(name in empty) || empty[name] !== prop){ + targetName = filter(name, target, source, prop); + if(targetName && (!(targetName in target) || !(targetName in empty) || empty[targetName] !== prop)){ + // name is accepted + oldValue = target[targetName]; + newValue = applyDecorator(decorator, targetName, prop, oldValue); + if(oldValue !== newValue){ + mixer(target, targetName, newValue, oldValue); + } + } + } + } + if(extraLen){ + for(i = 0; i < extraLen; ++i){ + name = extraNames[i]; + // repeating the body above + prop = source[name]; + if(!(name in empty) || empty[name] !== prop){ + targetName = filter(name, target, source, prop); + if(targetName && (!(targetName in target) || !(targetName in empty) || empty[targetName] !== prop)){ + // name is accepted + oldValue = target[targetName]; + newValue = applyDecorator(decorator, targetName, prop, oldValue); + if(oldValue !== newValue){ + mixer(target, targetName, newValue, oldValue); + } + } + } + } + } + + return target; // Object + }; + + oo.mixin = function(target, source){ + // summary: + // mixes in two or more objects processing decorators and filters + // using defaults as a fallback + // target: Object: + // target to receive new/updated properties + // source: Object...: + // source of properties, more than one source is allowed + // returns: Object: + // target + + var decorator, filter, i = 1, l = arguments.length; + for(; i < l; ++i){ + source = arguments[i]; + if(source instanceof Filter){ + filter = source.filter; + source = source.bag; + }else{ + filter = defaultFilter; + } + if(source instanceof Decorator){ + decorator = source.decorator; + source = source.value; + }else{ + decorator = defaultDecorator; + } + oo.__mixin(target, source, decorator, filter, defaultMixer); + } + return target; // Object + }; +})(); + +}); diff --git a/js/dojo/dojox/lang/oo/rearrange.js b/js/dojo/dojox/lang/oo/rearrange.js new file mode 100644 index 0000000..5fbd78e --- /dev/null +++ b/js/dojo/dojox/lang/oo/rearrange.js @@ -0,0 +1,69 @@ +//>>built +// wrapped by build app +define("dojox/lang/oo/rearrange", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.oo.rearrange"); + +(function(){ + var extraNames = dojo._extraNames, extraLen = extraNames.length, + opts = Object.prototype.toString, empty = {}; + + dojox.lang.oo.rearrange = function(bag, map){ + // summary: + // Process properties in place by removing and renaming them. + // description: + // Properties of an object are to be renamed or removed specified + // by "map" argument. Only own properties of "map" are processed. + // example: + // | oo.rearrange(bag, { + // | abc: "def", // rename "abc" attribute to "def" + // | ghi: null // remove/hide "ghi" attribute + // | }); + // bag: Object: + // the object to be processed + // map: Object: + // the dictionary for renaming (false value indicates removal of the named property) + // returns: Object: + // the original object + + var name, newName, prop, i, t; + + for(name in map){ + newName = map[name]; + if(!newName || opts.call(newName) == "[object String]"){ + prop = bag[name]; + if(!(name in empty) || empty[name] !== prop){ + if(!(delete bag[name])){ + // can't delete => hide it + bag[name] = undefined; + } + if(newName){ + bag[newName] = prop; + } + } + } + } + if(extraLen){ + for(i = 0; i < extraLen; ++i){ + name = extraNames[i]; + // repeating the body above + newName = map[name]; + if(!newName || opts.call(newName) == "[object String]"){ + prop = bag[name]; + if(!(name in empty) || empty[name] !== prop){ + if(!(delete bag[name])){ + // can't delete => hide it + bag[name] = undefined; + } + if(newName){ + bag[newName] = prop; + } + } + } + } + } + + return bag; // Object + }; +})(); + +}); diff --git a/js/dojo/dojox/lang/typed.js b/js/dojo/dojox/lang/typed.js new file mode 100644 index 0000000..ec8a29e --- /dev/null +++ b/js/dojo/dojox/lang/typed.js @@ -0,0 +1,153 @@ +//>>built +// wrapped by build app +define("dojox/lang/typed", ["dijit","dojo","dojox","dojo/require!dojox/json/schema"], function(dijit,dojo,dojox){ +(function(){ + var jsonSchema, inDojo = typeof dojo != "undefined"; + if(inDojo){ + dojo.provide("dojox.lang.typed"); + dojo.require("dojox.json.schema"); + jsonSchema = dojox.json.schema; + }else{ + if(typeof JSONSchema == "undefined"){ + throw new Error("Dojo or JSON Schema library must be present"); + } + jsonSchema = JSONSchema; + } + function validatingFunction(func, getMethodDef){ + var validatingFunc = function(){ + var methodDef = getMethodDef(); + if(methodDef && methodDef.parameters){ + var params = methodDef.parameters; + for(var j = 0; j < params.length; j++){ + arguments[j] = validate(arguments[j], params[j], j.toString()); + } + if(methodDef.additionalParameters){ + for(;j < arguments.length; j++){ + arguments[j] = validate(arguments[j], methodDef.additionalParameters, j.toString()); + } + } + } + var returns = func.apply(this, arguments); + if(methodDef.returns){ + validate(returns, methodDef.returns); + } + return returns; + }; + validatingFunc.__typedFunction__ = true; + for(var i in func){ + validatingFunc[i] = func[i]; + } + return validatingFunc; + } + function identityFunc(obj){ + return function(){ + return obj; + } + } + function validate(instance, schema, property){ + // summary: + // This checks to ensure that the result is valid and will throw an appropriate error message if it is not + // result: the result returned from checkPropertyChange or validate + if(typeof instance == "function" && schema && !instance.__typedFunction__){ + instance = validatingFunction(instance, identityFunc(schema)); + } + var result = jsonSchema._validate(instance, schema, property); + if(!result.valid){ + var errorMessage = "" + var errors = result.errors; + for(var i = 0; i < errors.length; i++){ + errorMessage += errors[i].property + ' ' + errors[i].message + '\n'; + } + throw new TypeError(errorMessage); + } + return instance; + } + var hasGetters = jsonSchema.__defineGetter__; + var typedFunction = function(Class){ + // summary: + // Adds type checking to a class, returning a new class with typing enabled + if(Class.__typedClass__){ + // type checking has already been added + return Class; + } + var Wrapper = function(){ + var i, value, properties = Wrapper.properties; + var methods = Wrapper.methods; + Class.apply(this,arguments); + this.__props__ = {}; + for(i in methods){ + value = this[i]; + if(value){ + if(!value.__typedFunction__){ + // add typing checking to the method, going up the proto chain to find the right one + var proto = this; + while(!proto.hasOwnProperty(i) && proto.__proto__){ + proto = proto.__proto__; + } + (function(i){ + proto[i] = validatingFunction(value, function(){ + return methods[i]; + }); + })(i); + } + }else{ + (function(i){ + this[i] = function(){ + throw new TypeError("The method " + i + " is defined but not implemented"); + }; + })(i); + } + } + if(hasGetters){ + var self = this; + for(i in properties){ + // add type checking to each property + value = this[i]; + if(this.hasOwnProperty(i)){ + this.__props__[i] = value; + } + (function(i){ + delete self[i]; + self.__defineGetter__(i, function(){ + return i in this.__props__ ? this.__props__[i] : this.__proto__[i]; + }); + self.__defineSetter__(i, function(value){ + validate(value, properties[i], i); + return this.__props__[i] = value; + }); + })(i); + } + } + validate(this, Wrapper); + }; + Wrapper.prototype = Class.prototype; + for(var i in Class){ + Wrapper[i] = Class[i]; + } + if(Class.prototype.declaredClass && inDojo){ + dojo.setObject(Class.prototype.declaredClass, Wrapper); + } + Wrapper.__typedClass__ = true; + return Wrapper; + }; + if(inDojo){ + dojox.lang.typed = typedFunction; + if(dojo.config.typeCheckAllClasses){ + // This will add type checking to all classes that will be declared via dojo.declare + // (only ones to be declared in the future) + + // hook into all declared classes + var defaultDeclare = dojo.declare; + dojo.declare = function(name){ + var clazz = defaultDeclare.apply(this, arguments); + clazz = typedFunction(clazz); + return clazz; + }; + dojo.mixin(dojo.declare, defaultDeclare); + } + }else{ + typed = typedFunction; + } +})(); + +}); diff --git a/js/dojo/dojox/lang/utils.js b/js/dojo/dojox/lang/utils.js new file mode 100644 index 0000000..4523247 --- /dev/null +++ b/js/dojo/dojox/lang/utils.js @@ -0,0 +1,107 @@ +//>>built +define("dojox/lang/utils", ["..", "dojo/_base/lang"], + function(dojox, lang){ + var du = lang.getObject("lang.utils", true, dojox); + + var empty = {}, opts = Object.prototype.toString; + + var clone = function(o){ + if(o){ + switch(opts.call(o)){ + case "[object Array]": + return o.slice(0); + case "[object Object]": + return lang.delegate(o); + } + } + return o; + } + + lang.mixin(du, { + coerceType: function(target, source){ + // summary: Coerces one object to the type of another. + // target: Object: object, which typeof result is used to coerce "source" object. + // source: Object: object, which will be forced to change type. + switch(typeof target){ + case "number": return Number(eval("(" + source + ")")); + case "string": return String(source); + case "boolean": return Boolean(eval("(" + source + ")")); + } + return eval("(" + source + ")"); + }, + + updateWithObject: function(target, source, conv){ + // summary: Updates an existing object in place with properties from an "source" object. + // target: Object: the "target" object to be updated + // source: Object: the "source" object, whose properties will be used to source the existed object. + // conv: Boolean?: force conversion to the original type + if(!source){ return target; } + for(var x in target){ + if(x in source && !(x in empty)){ + var t = target[x]; + if(t && typeof t == "object"){ + du.updateWithObject(t, source[x], conv); + }else{ + target[x] = conv ? du.coerceType(t, source[x]) : clone(source[x]); + } + } + } + return target; // Object + }, + + updateWithPattern: function(target, source, pattern, conv){ + // summary: Updates an existing object in place with properties from an "source" object. + // target: Object: the "target" object to be updated + // source: Object: the "source" object, whose properties will be used to source the existed object. + // pattern: Object: object, whose properties will be used to pull values from the "source" + // conv: Boolean?: force conversion to the original type + if(!source || !pattern){ return target; } + for(var x in pattern){ + if(x in source && !(x in empty)){ + target[x] = conv ? du.coerceType(pattern[x], source[x]) : clone(source[x]); + } + } + return target; // Object + }, + + merge: function(object, mixin){ + // summary: Merge two objects structurally, mixin properties will override object's properties. + // object: Object: original object. + // mixin: Object: additional object, which properties will override object's properties. + if(mixin){ + var otype = opts.call(object), mtype = opts.call(mixin), t, i, l, m; + switch(mtype){ + case "[object Array]": + if(mtype == otype){ + t = new Array(Math.max(object.length, mixin.length)); + for(i = 0, l = t.length; i < l; ++i){ + t[i] = du.merge(object[i], mixin[i]); + } + return t; + } + return mixin.slice(0); + case "[object Object]": + if(mtype == otype && object){ + t = lang.delegate(object); + for(i in mixin){ + if(i in object){ + l = object[i]; + m = mixin[i]; + if(m !== l){ + t[i] = du.merge(l, m); + } + }else{ + t[i] = lang.clone(mixin[i]); + } + } + return t; + } + return lang.clone(mixin); + } + } + return mixin; + } + }); + + return du; +}); |
