From: dsc Date: Wed, 17 Nov 2010 09:36:28 +0000 (-0800) Subject: Adds support for class body constructor functions. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=755c5718bc79aefdcc6d52d4d092a34da2f6b277;p=tanks.git Adds support for class body constructor functions. --- diff --git a/src/Y/alias.js b/src/Y/alias.js index cf46872..a36af68 100644 --- a/src/Y/alias.js +++ b/src/Y/alias.js @@ -6,8 +6,9 @@ var globals = this , _Number = globals.Number , slice = _Array.prototype.slice +, isArray = _Array.isArray , toString = _Object.prototype.toString , hasOwn = _Object.prototype.hasOwnProperty -, isArray = _Array.isArray +, getProto = _Object.getPrototypeOf ; diff --git a/src/Y/y-array.js b/src/Y/y-array.js index 7e9b140..8d421cd 100644 --- a/src/Y/y-array.js +++ b/src/Y/y-array.js @@ -8,37 +8,43 @@ YCollection.subclass('YArray', { }); YArray.prototype.merge = -YArray.prototype.concat = concat; +YArray.prototype.concat = function concat( donor ){ var A = this._o; new Y(arguments).forEach(function( donor ){ A = A.concat(donor); }); return Y(A); -} +}; -YArray.prototype.remove = array_remove; -function array_remove(v){ +YArray.prototype.remove = +function remove(v){ var idx = this.indexOf(v); if ( idx != -1 ) this.splice(idx, 1); return this; -} +}; + +YArray.prototype.last = +function last(){ + var A = this._o; + return A[ A.length-1 ]; +}; -YArray.prototype.unique = array_unique; -function array_unique(){ +YArray.prototype.unique = +function unique(){ return this.filter(function(v, i){ // Executes in the context of the new array, so // `this.indexOf` is checking what we've already // collected. return (this.indexOf(v) === -1); }); -} +}; // Like map, but produces a dict // Y(["Foo?", "R&D"]).generate(encodeURIComponent) // -> Y({"Foo?":"Foo%3F", "R&D":"R%26D"}) -YArray.prototype.generate = generate; +YArray.prototype.generate = function generate(fn, acc){ var args = Y(arguments), fn = args.shift(), @@ -46,26 +52,29 @@ function generate(fn, acc){ return this.reduce(function(acc, v, i){ return acc.attr(v, fn.apply( this, [v].concat(args) )); }, Y(acc || {})); -} +}; // Y([ ["foo", 1], ["bar", 2] ]).toDict() // -> Y({foo:1, bar:2}) -YArray.prototype.toDict = array_toDict; -function array_toDict(){ -} +YArray.prototype.toDict = +function toDict(){ + +}; -YArray.prototype.clone = array_clone; -function array_clone(){ +YArray.prototype.clone = +function clone(){ return Y(this._o.slice(0)); -} +}; -YArray.prototype.size = function(){ +YArray.prototype.size = +function size(){ return this._o.length; }; -YArray.prototype.toString = function(){ +YArray.prototype.toString = +function toString(){ return this.className + "(" + (this._o || "") + ")"; }; diff --git a/src/Y/y-class.js b/src/Y/y-class.js index ee07b8c..5f810b7 100644 --- a/src/Y/y-class.js +++ b/src/Y/y-class.js @@ -1,6 +1,8 @@ // Inspired by John Resig's "Simple Class Inheritence" -- http://ejohn.org/blog/simple-javascript-inheritance/ -var KNOWN_CLASSES = {}; +var KNOWN_CLASSES = {} +, classToString = function toString(){ return this.className+"()"; } +; // Private delegating constructor -- must be defined for every // new class to prevent shared state. All construction is @@ -49,14 +51,14 @@ function Class(className, Parent, members) { Parent = null; } members = members || {}; - Parent = Parent || Object.getPrototypeOf(members).constructor || ClassFactory; + Parent = Parent || getProto(members).constructor || ClassFactory; if (Parent == ClassFactory) Parent = Object; // Parent is the prototype if ( !isFunction(Parent) ) { - SuperClass = Object.getPrototypeOf(Parent).constructor || Object; + SuperClass = getProto(Parent).constructor || Object; prototype = Parent; // Parent is a constructor: check ClassFactory @@ -66,7 +68,7 @@ function Class(className, Parent, members) { // Recurse so `this` is an instance of ClassFactory } else if ( !(prototype instanceof ClassFactory) ) { - return new ClassFactory(Parent, members); + return new ClassFactory(className, Parent, members); } else { parentMembers = Parent.prototype || {}; @@ -77,11 +79,10 @@ function Class(className, Parent, members) { var NewClass, constructor = [ 'var '+className, - (''+_Class).replace('_Class', className), + (''+_Class).replace(_Class.name, className), 'NewClass = '+className, '' ].join(';\n'); eval(constructor); - NewClass.prototype.toString = function(){ return this.className+"()"; }; // Copy Class statics for (var k in ClassFactory) @@ -90,22 +91,40 @@ function Class(className, Parent, members) { // Copy parent methods, then add new instance methods for (var k in parentMembers) prototype[k] = parentMembers[k]; - for (var k in members) - prototype[k] = members[k]; + + if ( prototype.toString === toString ) + prototype.toString = classToString; // Fix Constructors, prototypes - NewClass.prototype = prototype; - prototype.constructor = NewClass; - NewClass.constructor = SuperClass; + NewClass.prototype = NewClass.fn = prototype; + prototype.constructor = prototype.__class__ = NewClass; + NewClass.__super__ = SuperClass; // don't override NewClass.constructor -- it should be Function + NewClass.className = prototype.className = className; + + // Either invoke body constructor... + if ( isFunction(members) ) { + members.call(prototype, NewClass); + + // Or add new instance methods + } else for (var k in members) { + if ( hasOwn.call(members, k) ) + prototype[k] = members[k]; + } + if (prototype.init) NewClass.init = prototype.init; - // Finalize - NewClass.className = prototype.className = className; KNOWN_CLASSES[className] = NewClass; return NewClass; } +// Add metaprogramming data to Class object +Class.__super__ = Object; +Class.fn = Class.prototype; +Class.fn.__class__ = Class; +Class.className = Class.fn.className = "Class"; + + Y.Class = Y.subclass = Class; @@ -116,7 +135,7 @@ Y.subclass = Class; * Unlike the keyword `new`, instantiate can be applied. */ Class.instantiate = - function(){ + function instantiate(){ var instance = this.fabricate(); if ( instance.init ) { var r = instance.init.apply(instance, arguments); @@ -128,17 +147,17 @@ Class.instantiate = /** * Create new instance, but do not run the delegate constructor. */ -Class.fabricate = - function(){ - var Cls = this; - return new Cls(); +Class.fabricate = + function fabricate(){ + return new this(); }; /** * Class method of Classes, not to be confused with Y.subclass, which is a static method. */ -Class.subclass = - function(className, members){ +Class.subclass = +Class.fn.subclass = + function subclass(className, members){ return new Class(className, this, members); }; diff --git a/src/Y/y-function.js b/src/Y/y-function.js index 5428275..87aa5a9 100644 --- a/src/Y/y-function.js +++ b/src/Y/y-function.js @@ -1,16 +1,16 @@ -var WR_P = "__wraps__"; - -var _ = globals._ = YFunction._ = {}; +var _ = globals._ = Y._ = YFunction._ = {} +, WRAPS = "__wraps__" +; function YFunction(fn){ if (!fn) fn = function(){}; + if (fn.__y__) return fn; - // fn._o = fn; fn.__y__ = true; - return install(fn); + return Y.YFunction.install(fn); } Y.YFunction = YFunction; @@ -20,26 +20,24 @@ Y.extend(YFunction.prototype, { extend : methodize(Y.extend), end : function end(){ return this; } }); -YFunction.prototype.attr = methodize(Y.attr); -// YFunction.prototype.extend = methodize(Y.extend); -// YFunction.prototype.reduce = methodize(Y.reduce); +YFunction.prototype.attr = methodize(Y.attr); -YFunction.install = install; +YFunction.install = function install(target){ target = target || Function.prototype; - var proto = YFunction.prototype; + var proto = Y.YFunction.prototype; for (var k in proto) { - if ( isFunction(proto[k]) && (k == 'bind' || !target[k]) ) + if ( Y.isFunction(proto[k]) && (k == 'bind' || !target[k]) ) target[k] = proto[k]; } return target; -} +}; function unwrap(fn){ - return ( fn && isFunction(fn) ) ? unwrap(fn[WR_P]) || fn : fn; + return ( fn && isFunction(fn) ) ? unwrap(fn[WRAPS]) || fn : fn; } Y.curry = curry; @@ -63,7 +61,7 @@ function curry(fn){ return curry.apply(this, [fn].concat(_args)); } - curried.__wraps__ = fn; + curried[WRAPS] = fn; curried.__curried__ = args; return curried; } @@ -72,17 +70,39 @@ function curry(fn){ Y.methodize = methodize; YFunction.prototype.methodize = methodize(methodize); function methodize(fn) { - if ( fn.__methodized__ ) - return fn.__methodized__; + fn = fn.toFunction(); + var g = fn.__genericized__ + , m = fn.__methodized__ ; + if (m) return m; + if (g && g[WRAPS]) return g[WRAPS]; - var m = fn.__methodized__ = - function(){ + m = fn.__methodized__ = + function methodized(){ return fn.apply(this, [this].concat( Y(arguments) )); }; - m[WR_P] = fn; + m[WRAPS] = fn; return m; } +Y.genericize = genericize; +YFunction.prototype.genericize = methodize(genericize); // heh +function genericize( fn ) { + fn = fn.toFunction(); + var g = fn.__genericized__ + , m = fn.__methodized__ ; + if (g) return g; + if (m && m[WRAPS]) return m[WRAPS]; + + g = fn.__genericized__ = + function genericized(){ + var args = Y(arguments); + return fn.apply(args.shift(), args); + }; + g[WRAPS] = fn; + return g; +}; + + Y.compose = compose; YFunction.prototype.compose = methodize(compose); @@ -134,7 +154,7 @@ function chain(f,g){ // return arguments.callee; // } // ; -// lazied.__wraps__ = fn; +// lazied[WRAPS] = fn; // lazied.__args = args; // return lazied(); // } @@ -148,7 +168,7 @@ var _bind = _Function.prototype.bind; YFunction.prototype.bind = function bind(context, args){ var bound = _bind.apply(this, arguments); - bound.__wraps__ = this; + bound[WRAPS] = this; return Y(bound); }; @@ -161,26 +181,11 @@ function partial(){ , partially = function(){ return fn.apply( this, args.concat(Y(arguments)) ); }; - partially.__wraps__ = fn; + partially[WRAPS] = fn; return Y(partially); }; -Y.genericize = genericize; -YFunction.prototype.genericize = methodize(genericize); // heh -function genericize( fn ) { - if (fn.__genericized__) - return fn.__genericized__; - - var g = fn.__genericized__ = - function genericized(){ - var args = Y(arguments), self = args.shift(); - return fn.apply(self, args); - }; - g.__wraps__ = fn; - return Y(g); -}; - // Only works for arguments whose toString is unique and stateless (for example, primitives, but not closures). // XXX: hashCode() Y.memoize = memoize; @@ -206,7 +211,7 @@ function memoize(fn){ return cache[key]; }; - m.__wraps__ = fn; + m[WRAPS] = fn; m.purge = function purge(){ var cache = this.cache; this.cache = {}; diff --git a/src/evt/evt.class.js b/src/evt/evt.class.js index c2596af..fc8a0d6 100644 --- a/src/evt/evt.class.js +++ b/src/evt/evt.class.js @@ -1,6 +1,4 @@ (function(undefined){ -var Evt = this.Evt = (this.Evt || {}); - // Inspired by John Resig's "Simple Class Inheritence" -- http://ejohn.org/blog/simple-javascript-inheritance/ /* Metaprogramming API @@ -8,7 +6,7 @@ Metaprogramming API Events - "create" -- Fired when a new class instance is created. Data: self - "destroy" -- Fired when an instance is destoryed. Data: self -- "init" -- Fired when the initialiser is invoked. Note that subclasses of the listened class will fire this event. Data: self +- "init" -- Fired when the initialiser is invoked. Note that subclasses of the listened class may fire this event. Data: self - "subclass" -- Fired when a new subclass is created. Data: parent, self, body Customization Protocols @@ -28,12 +26,20 @@ Note: Metaprogramming events cannot depend on Class. -var KNOWN_CLASSES = Class.KNOWN_CLASSES = {}; +var Evt = this.Evt = (this.Evt || {}) +, KNOWN_CLASSES = Class.KNOWN_CLASSES = {} +, isFunction = Y.isFunction +, getProto = Object.getPrototypeOf +, hasOwn = Object.prototype.hasOwnProperty +, objToString = Object.prototype.toString +, classToString = function toString(){ return this.className+"()"; } +; // Private delegating constructor -- must be defined for every // new class to prevent shared state. All construction is // actually done in the init method. -function _Class() { +Evt.ConstructorTemplate = ConstructorTemplate; +function ConstructorTemplate() { var cls = arguments.callee , instance = this; @@ -46,14 +52,17 @@ function _Class() { 'args' : Y(arguments) }); - if ( instance.init ) - return instance.init.apply(instance, arguments); + if ( instance.initialise ) + return instance.initialise.apply(instance, arguments); } return instance; } + + + /** * Creates a new class. All classes inherit from Evt.Class, and therefore support * metaprogramming hooks and mixins. @@ -74,98 +83,129 @@ function Class(className, Parent, members){ var ClassFactory = arguments.callee , SuperClass = ClassFactory , prototype = this + , parentMembers = {} ; - if ( !members && !Y.isFunction(Parent) ) { + if ( !members && !isFunction(Parent) ) { members = Parent; Parent = null; } members = members || {}; - Parent = Parent || Object.getPrototypeOf(members).constructor; - if (Parent == ClassFactory) Parent = Object; - var parentMembers = Parent.prototype; + Parent = Parent || getProto(members).constructor || ClassFactory; + + if (Parent == ClassFactory) + Parent = Object; - if (Parent.prototype instanceof ClassFactory) { + // Parent is the prototype + if ( !isFunction(Parent) ) { + SuperClass = getProto(Parent).constructor || Object; + prototype = Parent; + + // Parent is a constructor: check ClassFactory + } else if (Parent.prototype instanceof ClassFactory) { SuperClass = Parent; prototype = Parent.fabricate(); - parentMembers = {}; + + // Recurse so `this` is an instance of ClassFactory + } else if ( !(prototype instanceof ClassFactory) ) { + return new ClassFactory(className, Parent, members); + + } else { + parentMembers = Parent.prototype || {}; } - if ( !(prototype instanceof ClassFactory) ) - return new ClassFactory(Parent, members); // Creates a new function with the appropriate name // based on the className. - var NewClass, + var NewClass, constructor = [ 'var '+className, - (''+_Class).replace('_Class', className), + (''+ConstructorTemplate).replace(ConstructorTemplate.name, className), 'NewClass = '+className, '' ].join(';\n'); + eval(constructor); - NewClass.prototype.toString = function(){ return this.className+"()"; }; // Copy Class statics for (var k in ClassFactory) { var v = ClassFactory[k]; - if ( Y.isFunction(v) && !(k in Y.event.Emitter.methods) ) + if ( isFunction(v) && !(k in Y.event.Emitter.methods) ) NewClass[k] = v; } - // Copy parent methods, then add new instance methods + // Copy parent methods for (var k in parentMembers) prototype[k] = parentMembers[k]; - for (var k in members) - prototype[k] = members[k]; - prototype.constructor = NewClass; - NewClass.prototype = prototype; + if ( prototype.toString === objToString ) + prototype.toString = classToString; + + NewClass.className = prototype.className = className; + NewClass.prototype = NewClass.fn = prototype; // Fix Constructors - NewClass.constructor = SuperClass; - var init = prototype.init; - function initWrapper(){ - var instance = this; - - if (init) { - var result = init.apply(instance, arguments); - if (result) instance = result; - } - - NewClass.fire('init', instance, { - 'instance' : instance, - 'cls' : NewClass, - 'args' : Y(arguments) - }); - - return instance; + NewClass.__super__ = SuperClass; // don't override NewClass.constructor -- it should be Function + prototype.constructor = prototype.__class__ = NewClass; + + NewClass.init = // XXX: This means subclasses will fire events. + prototype.initialise = + function initialise(){ + var instance = this + , init = NewClass.prototype.init + ; + + if (init) { + var result = init.apply(instance, arguments); + if (result) instance = result; + } + + NewClass.fire('init', instance, { + 'instance' : instance, + 'cls' : NewClass, + 'args' : Y(arguments) + }); + + return instance; + }; + + // Add class emitter + var ParentEmitter = (Parent.__emitter__ ? Parent : ClassFactory) + , ClassEmitter = NewClass.__emitter__ = new Y.event.Emitter(NewClass, ParentEmitter) + ; + + // Either invoke body constructor... + if ( isFunction(members) ) { + members.call(prototype, NewClass); + + // Or add new instance methods + } else for (var k in members) { + if ( hasOwn.call(members, k) ) + prototype[k] = members[k]; } - initWrapper.__wraps__ = init; - NewClass.init = prototype.init = initWrapper; - // Finalize - NewClass.className = prototype.className = className; + // Record for metaprogramming KNOWN_CLASSES[className] = NewClass; - var ParentEmitter = (Parent.__emitter__ ? Parent : ClassFactory); - NewClass.__emitter__ = new Y.event.Emitter(NewClass, ParentEmitter); - - ParentEmitter.fire('subclass', + // Notify parent of the subclass + ParentEmitter.fire('subclass', NewClass, { 'className' : className, 'parent' : Parent, 'child' : NewClass, - 'members' : members + 'members' : members, + 'prototype' : prototype }); return NewClass; } -// Decorate with emitter methods +// Decorate with emitter methods and +// add metaprogramming data to Class object +Class.__super__ = Object; Class.__emitter__ = new Y.event.Emitter(Class); - -Evt.Class = -Evt.subclass = Class; +Class.fn = Class.prototype; +Class.fn.__class__ = Class; +Class.className = Class.fn.className = "Class"; /* Class Methods */ @@ -173,30 +213,36 @@ Evt.subclass = Class; * Create a new instance and run delegate constructor if it exists. * Unlike the keyword `new`, instantiate can be applied. */ -Class.instantiate = -function instantiate(){ - var instance = this.fabricate(); - if ( instance.init ) - instance.init.apply(instance, arguments); - return instance; -}; - +Class.instantiate = + function instantiate(){ + var instance = this.fabricate(); + if ( instance.initialise ) + instance.initialise.apply(instance, arguments); + return instance; + }; + /** * Create new instance, but do not run the delegate constructor. */ -Class.fabricate = -function fabricate(){ - var Cls = this; - return new Cls(); -}; +Class.fabricate = + function fabricate(){ + return new this(); + }; /** - * Class method of Classes, not to be confused with Evt.subclass, which is a static method. + * Class/Instance method of Classes, not to be confused with Evt.subclass, which is a static method. */ -Class.subclass = -function subclass(className, members){ - return new Class(className, this, members); -}; +Class.subclass = +Class.fn.subclass = + function subclass(className, members){ + return new Class(className, this, members); + }; + + +// Expose +Evt.Class = +Evt.subclass = Class; + })(); \ No newline at end of file diff --git a/src/lessly/future.js b/src/lessly/future.js index c95d7a8..5531d95 100644 --- a/src/lessly/future.js +++ b/src/lessly/future.js @@ -1,14 +1,14 @@ -(function(){ +(function(_Object, _Array, _Function){ -var _Object = Object -, _Array = Array -, _Function = Function -, AP = _Array.prototype -, FP = _Function.prototype -, slice = AP.slice; +var P = "prototype" +, AP = _Array[P] +, FP = _Function[P] +, slice = AP.slice +, objToString = _Object[P].toString +; if ( !_Array.slice ) { - _Array.slice = function(a){ + _Array.slice = function slice(a){ return slice.apply(a, slice.call(arguments, 1)); }; } @@ -16,7 +16,7 @@ if ( !_Array.slice ) { // JavaScript 1.6 & 1.7 if ( !AP.indexOf ) { - AP.indexOf = function( value ){ + AP.indexOf = function indexOf( value ){ for ( var A = this, i = 0, l = A.length; i < l; ++i ) if ( A[i] === value ) return i; @@ -25,7 +25,7 @@ if ( !AP.indexOf ) { } if ( !AP.lastIndexOf ) { - AP.indexOf = function( value ){ + AP.lastIndexOf = function lastIndexOf( value ){ for ( var A = this, i = A.length-1; i >= 0; --i ) if ( A[i] === value ) return i; @@ -35,25 +35,25 @@ if ( !AP.lastIndexOf ) { if ( !AP.map ) { - AP.forEach = function( fn, context ){ + AP.forEach = function forEach( fn, context ){ for ( var A = this, context = context||A, i = 0, l = A.length; i < l; ++i ) fn.call( context, A[i], i, A ); }; - AP.map = function( fn, context ){ + AP.map = function map( fn, context ){ for ( var A = this, context = context||A, i = 0, l = A.length, r = new _Array(l); i < l; ++i ) r[i] = fn.call( context, A[i], i, A ); return r; }; - AP.filter = function( fn, context ){ + AP.filter = function filter( fn, context ){ for ( var A = this, context = context||A, i = 0, l = A.length, r = [], v = A[0]; i < l; v = A[++i] ) if ( fn.call( context, v, i, A ) ) r.push(v); return r; }; - AP.every = function( fn, context ){ + AP.every = function every( fn, context ){ var A = this, context = context||A; for (var i=0, l = A.length; i