, type = require('Y/type')
;
+
+
function reduce(o, fn, acc){
if ( !o )
return acc;
return o[key];
}
+/**
+ * Copies all properties from all arguments after the first onto the first.
+ * Accepts any number of arguments greater than one.
+ *
+ * @param {Object} target Target object for the copied properties.
+ * @param {Object...} donors Donor object(s).
+ * @return {Object} A
+ *
+ * nb. "Properties" are only value descriptors. To copy accessor descriptors, use
+ * `core.accessors(A,B)`. To copy everything, use `core.descriptors(A,B)`.
+ *
+ */
function extend( A, B ){
var args = slice.call(arguments,1);
if ( type.isArray(A) )
return A.concat.apply(A, args);
else
- return args.reduce(extendall, A);
+ return args.reduce(_extend, A);
}
-function extendall(A, donor){ return reduce(donor, attrvk, A); }
-function attrvk(o, v, k){ return attr(o, k, v, o[k]); }
+function _extend(A, donor){ return reduce(donor, attrvk, A); }
+function attrvk(o, v, k){ return attr(o, k, v, o[k]); }
/**
* Fetches the descriptor for each property that is an accessor (getter/setter).
- * @param {Object} o Object for inspection.
- * @return {Object} A map of property to descriptor.
- * nb. An Array is safer, but:
- * 1) Object.defineProperties takes an Object
- * 2) That'd be much worse to work with
+ * @param {Object} target Object for inspection.
+ * @return {Object} Map from property to accessor descriptor.
*/
-function accessors(o){
- if ( !(o && typeof o === "object") )
- return {};
- return reduce(o, function(acc, v, k, obj){
-
- // Ignore inherited properties
- if ( !hasOwn.call(obj,k) )
- return acc;
-
- // Only yield accessors
- var desc = getDesc(obj,k);
- if ( !hasOwn.call(desc,'value') )
- acc[k] = desc;
-
+/**
+ * Copies descriptors from all arguments onto the first.
+ * Accepts any number of arguments greater than one.
+ *
+ * @param {Object} target Target object for the copied accessors.
+ * @param {Object...} donors Donor object(s).
+ * @return {Object} A.
+ */
+function accessors(A, B){
+ if ( !(A && typeof A === "object") )
+ return A;
+
+ if (arguments.length < 2)
+ return reduce(A, _getAccessors, {});
+
+ var desc = slice.call(arguments,1).reduce(_copyAccessors, {});
+ Object.defineProperties(A, desc);
+ return A;
+}
+function _getAccessors(acc, v, k, obj){
+
+ // Ignore inherited properties
+ if ( !hasOwn.call(obj,k) )
return acc;
- }, {});
+
+ // Only yield accessors
+ var desc = getDesc(obj,k);
+ if ( !hasOwn.call(desc,'value') )
+ acc[k] = desc;
+
+ return acc;
+}
+function _copyAccessors(desc, donor){
+ return reduce(donor, _getAccessors, desc);
}
+/**
+ * Fetches the descriptor for each own property of the object.
+ *
+ * @param {Object} target Object for inspection.
+ * @return {Object} Map from property to descriptor.
+ */
+/**
+ * Copies descriptors from all arguments onto the first.
+ * Accepts any number of arguments greater than one.
+ *
+ * @param {Object} target Target object for the copied accessors.
+ * @param {Object...} donors Donor object.
+ * @return {Object} A.
+ */
+function descriptors(A, B){
+ if ( !(A && typeof A === "object") )
+ return A;
+
+ if (arguments.length < 2)
+ return reduce(A, _getDescriptors, {});
+
+ var desc = slice.call(arguments,1).reduce(_copyDescriptors, {});
+ Object.defineProperties(A, desc);
+ return A;
+}
+function _getDescriptors(acc, v, k, obj){
+
+ // Ignore inherited properties
+ if ( !hasOwn.call(obj,k) )
+ return acc;
+
+ acc[k] = getDesc(obj,k);
+ return acc;
+}
+function _copyDescriptors(desc, donor){
+ return reduce(donor, _getDescriptors, desc);
+}
+
+
+
+exports['slice'] = slice;
+
+exports['reduce'] = reduce;
+exports['map'] = map;
+exports['forEach'] = forEach;
+exports['filter'] = filter;
-exports['reduce'] = reduce;
-exports['map'] = map;
-exports['forEach'] = forEach;
-exports['filter'] = filter;
+exports['set'] = set;
+exports['attr'] = attr;
+exports['extend'] = extend;
-exports['set'] = set;
-exports['attr'] = attr;
-exports['extend'] = extend;
+exports['accessors'] = accessors;
+exports['descriptors'] = descriptors;
-exports['accessors'] = accessors;
-exports['slice'] = slice;
\ No newline at end of file
function extendall(A, donor){ return reduce(donor, attrvk, A); }
function attrvk(o, v, k){ return attr(o, k, v, o[k]); }
-function accessors(o){
- if ( !(o && typeof o === "object") )
- return {};
+function accessors(A, B){
+ if ( !(A && typeof A === "object") )
+ return A;
- if ( notWrapped(o.accessors) )
- return o.accessors.apply(o, slice.call(arguments,1));
+ if ( notWrapped(A.accessors) )
+ return A.accessors.apply(A, slice.call(arguments,1));
- return core.accessors(o);
+ return core.accessors.apply(this, arguments);
}
-exports['reduce'] = reduce;
-exports['map'] = map;
-exports['forEach'] = forEach;
-exports['filter'] = filter;
+function descriptors(A,B){
+ if ( !(A && typeof A === "object") )
+ return A;
+
+ if ( notWrapped(A.descriptors) )
+ return A.descriptors.apply(A, slice.call(arguments,1));
+
+ return core.descriptors.apply(this, arguments);
+}
-exports['set'] = set;
-exports['attr'] = attr;
-exports['extend'] = extend;
+exports['reduce'] = reduce;
+exports['map'] = map;
+exports['forEach'] = forEach;
+exports['filter'] = filter;
-exports['accessors'] = accessors;
+exports['set'] = set;
+exports['attr'] = attr;
+exports['extend'] = extend;
+exports['accessors'] = accessors;
+exports['descriptors'] = descriptors;
--- /dev/null
+var
+Blank =
+exports['Blank'] =
+function Blank(v){ this.value = v; };
+
+exports['_'] = _ =
+exports['BLANK'] = BLANK = new Blank();
+
+exports['break_'] =
+function break_(v){ return new Blank(v); };
+
return ( L ? A[L-1] : undefined );
};
+ this['clear'] =
+ function clear(){
+ var A = this._o;
+ while (A.length) A.pop();
+ return this;
+ };
+
function unwrapY(o){
return (o instanceof YArray ? o.end() : o);
}
--- /dev/null
+var YCollection = require('Y/types/collection').YCollection
+, YFunction = require('Y/types/function').YFunction
+, core = require('Y/core')
+, del = require('Y/delegate')
+, op = require('Y/op')
+, type = require('Y/type')
+;
+
-/**
- * A generic collection (which also provides the majority of core functionality for YObject).
- * Specializations are provided by subclasses. All subclasses must implement reduce().
- */
-
var YBase = require('Y/class').YBase
// , type = require('Y/type')
, core = require('Y/core')
, del = require('Y/delegate')
, op = require('Y/op')
, slice = core.slice
+
+, internal = require('Y/internal')
;
return this;
}
+/**
+ * A generic collection (which also provides the majority of core functionality for YObject).
+ * Specializations are provided by subclasses.
+ *
+ * All subclasses must implement `reduce()`.
+ *
+ * There are several optional core methods for customization and optimization. By default:
+ * - `attr()` calls `Y.delegate.attr()` on the wrapped object, `this._o`. If the result is not the
+ * wrapped object, it is returned, otherwise `this` is returned.
+ * - `clone()` invokes the constructor with nothing, and then calls `extend()` on the new object passing itself.
+ * - `end()` returns this._o and does nothing else.
+ * - `remove()` must iterate the whole collection to ensure all values are removed.
+ * - `find()` is a variant of reduce that uses the break-wrapper `Y.internal.break_()`, as a sentinel to stop iteration.
+ */
+var YCollection =
exports['YCollection'] =
YBase.subclass('YCollection', {
- 'init' : function(o){
+ 'init' : function initYCollection(o){
this._o = o || {};
},
- 'attr' : function attr(k, v, def){
+ 'end' : function end(){ return this._o; },
+
+ 'clone' : function clone(){
+ return new this.constructor().extend(this);
+ },
+
+ 'attr' : function attr(k, v, def){
var r = del.attr(this._o, k, v, def);
if (r === this._o)
return this;
else
return r;
},
- 'extend' : extendY,
- 'merge' : extendY,
- 'concat' : extendY,
- 'end' : function end(){ return this._o; },
+ /**
+ * Removes all instances of the given values from the collection.
+ *
+ * @param {Any...} value Value(s) to be removed from the collection.
+ * @return {this}
+ */
+ 'remove' : function remove(v){
+ var o = this._o
+ , values = slice.call(arguments,0);
+ this.forEach(function(v, k){
+ if ( values.indexOf(v) !== -1 )
+ delete o[k];
+ });
+ return this;
+ },
+
'reduce' : function reduce( fn, acc, context ){
- var o = this._o || this,
- acc = acc || new o.constructor();
- for ( var name in o )
- acc = fn.call( context || this, acc, o[name], name, o );
- return acc;
+ throw new UnimplementedError("Reduce is unimplemented!");
},
'map' : function map( fn ){
- var o = this._o
- , cxt = arguments[1] || this
- , acc = new o.constructor()
- ;
- for ( var name in o )
- acc[name] = fn.call(cxt, o[name], name, o);
- return acc;
+ var cxt = arguments[1] || this;
+ return this.reduce(function(acc, v, k, o){
+ var _v = fn.call(cxt, v, k, o);
+ return acc.attr(k, _v);
+ }, new this.__class__() );
},
'forEach' : function forEach( fn, context ){
- var o = this._o, cxt = arguments[1] || this;
- for ( var name in o )
- fn.call(cxt, o[name], name, o);
+ this.reduce(function(cxt, v, k, o){
+ fn.call(cxt, v, k, o);
+ }, arguments[1] || this);
return this;
},
'filter' : function filter( fn, context ){
- var o = this._o, acc = new o.constructor();
- for ( var name in o )
- if ( fn.call( context || this, o[name], name, o ) )
- acc[name] = o[name];
- return acc;
+ var cxt = arguments[1] || this;
+ return this.reduce(function(acc, v, k, o){
+ if ( fn.call(cxt, v, k, o) )
+ acc.attr(k, v);
+ return acc;
+ }, new this.__class__() );
},
+ // 'find' : function find( fn ){
+ // var cxt = arguments[1] || this;
+ //
+ // },
+
+ /**
+ * FIXME
+ */
'indexOf' : function indexOf( value ){
var o = this._o;
for ( var name in o )
return ( this.indexOf(value) !== -1 );
},
- 'clone' : function clone(){
- return new this.constructor().extend(this);
- },
-
- // Dependence on YArray closes the loop
- // 'remove' : function remove(v){
- // var o = this._o
- // , toRemove = new Y(arguments);
- //
- // for (var k in o) {
- // var v = o[k];
- // if ( toRemove.has(v) ) {
- // delete o[k];
- // toRemove.remove(v);
- // }
- // }
- // return this;
- // },
-
- 'remove' : function remove(v){
- var o = this._o
- , toRemove = slice.call(arguments);
-
- for (var k in o) {
- var v = o[k], idx = toRemove.indexOf(v);
- if ( idx !== -1 ) {
- delete o[k];
- toRemove.splice(idx,1);
- }
- if (!toRemove.length)
- break;
- }
- return this;
- },
-
'every' : function every( fn ){
var self = this, fn = fn || op.bool;
return this.reduce(function(acc, v, k, o){
},
'zip' : function zip(){
- var sequences = slice.call(arguments);
+ var sequences = slice.call(arguments,0);
sequences.unshift(this);
return this.map(function(_, k){
return sequences.map(op.curried.kget(k));
'pluck' : function pluck(key){
return this.map(function(v){
return del.attr(v, key);
- // return v && (type.isFunction(v.attr) ? v.attr(key) : v[key]);
});
},
+ // FIXME: del.map
'invoke' : function invoke(name){
var args = slice.call(arguments,1);
return del.map(this, function(o){
});
},
+ // FIXME: this._o[name].apply
'apply' : function apply(name, args){
return this[name].apply(this, args);
}
, slice = core.slice
, YF = YFunction
-, _ = YF._ = {}
, YFP = YF.prototype
;
// Export these last to avoid methodizing them
exports['YFunction'] = YF(YF);
-exports['_'] = _;
exports['YObject'] =
YCollection.subclass('YObject', {
- init : function initYObject(o){
+ 'init' : function initYObject(o){
this._o = o || {};
},
- clone : function clone(){
+ 'clone' : function clone(){
return new YObject( deepcopy(this._o) );
+ },
+
+ 'remove' : function remove(v){
+ var o = this._o
+ , values = slice.call(arguments,0);
+
+ for (var k in o) {
+ var v = o[k];
+ if ( values.indexOf(v) !== -1 )
+ delete o[k];
+ }
+ return this;
+ },
+
+
+ 'reduce' : function reduce( fn, acc, context ){
+ var o = this._o || this,
+ acc = acc || new o.constructor();
+ for ( var name in o )
+ acc = fn.call( context || this, acc, o[name], name, o );
+ return acc;
+ },
+
+ 'map' : function map( fn ){
+ var o = this._o
+ , cxt = arguments[1] || this
+ , acc = new o.constructor()
+ ;
+ for ( var name in o )
+ acc[name] = fn.call(cxt, o[name], name, o);
+ return acc;
+ },
+
+ 'forEach' : function forEach( fn, context ){
+ var o = this._o, cxt = arguments[1] || this;
+ for ( var name in o )
+ fn.call(cxt, o[name], name, o);
+ return this;
+ },
+
+ 'filter' : function filter( fn, context ){
+ var o = this._o, acc = new o.constructor();
+ for ( var name in o )
+ if ( fn.call( context || this, o[name], name, o ) )
+ acc[name] = o[name];
+ return acc;
+ },
+
+ 'indexOf' : function indexOf( value ){
+ var o = this._o;
+ for ( var name in o )
+ if ( o[name] === value )
+ return name;
+ return -1;
}
+
});
},
'clone' : function clone(){
- return YString(this._o);
+ return YString(this._o); // strings are immutable
},
'size' : size,
, slice = core.slice
, isFunction = type.isFunction
, isArray = type.isArray
-;
+, UE =
+exports['UnimplementedError'] =
+function UnimplementedError(){ Error.call(this, arguments); }
+;
+UE.prototype = new Error('Unimplemented!');
+
function bindName(k){
var v = this[k];
if ( isFunction(v) )
return false;
},
- 'tick' : function(elapsed){
+ 'tick' : function(elapsed, now){
if (this.ready) return true;
this.elapsed += elapsed || 0;
framerate : 0, // Target framerate
frametime : 0, // 1000 / framerate
- now : 0, // Last tick time (ms)
+ clock : 0, // Subjective clock
+ now : 0, // Last tick time (real ms)
fps : 0, // Effective framerate
ticks : 0, // Number of ticks since start
this.stop();
this.elapsedAtStop = 0;
this.ticks = 0;
+ this.clock = 0;
this.times = [];
this.realtimes = [];
},
this.now = new Date().getTime();
this.elapsed = this.now - lastTick;
this.perceived = this.elapsed/this.timeDilation;
+ this.clock += this.perceived;
clearTimeout(this.timer);
this.timer = null;
this.fire('tick', this, {
- 'now' : this.now,
+ 'now' : this.clock,
'elapsed' : this.perceived,
'ticks' : ++this.ticks
});
+
+
var Y = require('Y').Y
, CAPACITY = 8
, REGION_ID = 0
,
-
Region =
exports['Region'] =
Y.subclass('Region', {
this.value = value;
},
- // Expects caller will have ordered x1 < x2, y1 < y2
- overlaps : function overlaps(x1,y1, x2,y2){
- return !( x1 > this.x2 || y1 > this.y2
- || x2 <= this.x1 || y2 <= this.y1 );
- },
+ /**
+ * Determines if the given Rect overlaps with this tree,
+ * including the top/left edges, but excluding the bottom/right.
+ */
+ overlaps : orderThen('_overlaps'),
+
+ /**
+ * As overlaps, but presumes input is sorted x1 < x2 and y1 < y2.
+ */
+ _overlaps : overlaps,
toString : function toString(){
return this.className+'(id='+this.id+', rect='+rectToString(this)+', value='+this.value+')';
this.clear();
},
- // Expects caller will have ordered x1 < x2, y1 < y2
- overlaps : function overlaps(x1,y1, x2,y2){
- return !( x1 > this.x2 || y1 > this.y2
- || x2 <= this.x1 || y2 <= this.y1 );
- },
+ /**
+ * Determines if the given Rect overlaps with this tree,
+ * including the top/left edges, but excluding the bottom/right.
+ */
+ overlaps : orderThen('_overlaps'),
+
+ /**
+ * As overlaps, but presumes input is sorted x1 < x2 and y1 < y2.
+ */
+ _overlaps : overlaps,
// TODO: Add optional filter
get : function get(x1,y1, x2,y2){
// , x1 = Math.min(_x1,_x2), x2 = Math.max(_x1,_x2)
// , y1 = Math.min(_y1,_y2), y2 = Math.max(_y1,_y2) ;
//
- // if ( !self.overlaps(x1,y1, x2,y2) )
+ // if ( !self._overlaps(x1,y1, x2,y2) )
// return acc;
//
// // Implies this is a leaf: check its values
});
},
- leaves : function leaves(x1,y1, x2,y2, fn, acc, context){
- var self = this
- , cxt = context || self
- , _x1 = Math.min(x1,x2), _x2 = Math.max(x1,x2)
- , _y1 = Math.min(y1,y2), _y2 = Math.max(y1,y2) ;
-
- if ( !self.overlaps(_x1,_y1, _x2,_y2) )
+ /**
+ * @protected
+ * Invokes iterator on each matching subtree (passing the accumulator, the tree's regions, the tree, and the Rect query)
+ * to acquire the new accumulator:
+ *
+ * acc = fn.call(context||tree, acc, tree.regions, tree, query);
+ *
+ * Where the Rect query is passed as an object with properties (x1,y1, x2,y2).
+ *
+ * For more friendly iterators, useful to public callers, @see this.collect(), @see this.reduce().
+ */
+ leaves : orderThen('_leaves'),
+
+ // leaves : function leaves(x1,y1, x2,y2, fn, acc, context){
+ // var _x1 = Math.min(x1,x2), _x2 = Math.max(x1,x2)
+ // , _y1 = Math.min(y1,y2), _y2 = Math.max(y1,y2);
+ // return this._leaves(_x1,_y1, _x2,_y2, fn, acc, context);
+ // },
+
+ _leaves : function _leaves(x1,y1, x2,y2, fn, acc, context){
+ if ( !this._overlaps(x1,y1, x2,y2) )
return acc;
// Implies this is a leaf: check its values
- if ( self.regions ) acc = fn.call(cxt, acc, self.regions, self, self);
+ if ( this.regions ) acc = fn.call(context || this, acc, this.regions, this);
// Recurse into any children -- Note we do not place this in an else-block,
// as mutation during the above call might cause the tree to split.
- if (self.nw) acc = self.nw.leaves(_x1,_y1, _x2,_y2, fn, acc, context);
- if (self.ne) acc = self.ne.leaves(_x1,_y1, _x2,_y2, fn, acc, context);
- if (self.sw) acc = self.sw.leaves(_x1,_y1, _x2,_y2, fn, acc, context);
- if (self.se) acc = self.se.leaves(_x1,_y1, _x2,_y2, fn, acc, context);