, _String = globals.String
, _Number = globals.Number
-, hasOwn = _Object.prototype.hasOwnProperty
+, P = 'prototype'
+, hasOwn = _Object[P].hasOwnProperty
, getProto = _Object.getPrototypeOf
+, getDesc = _Object.getOwnPropertyDescriptor
+, setDesc = _Object.defineProperty
, KNOWN_CLASSES = type.type.KNOWN_CLASSES
, classToString = function toString(){ return this.className+"()"; }
* @param {Object} [members] Instance members to add to the new class's prototype; a class
* constructor can be supplied as `init`.
*
- * @returns {Class} A new Class.
+ * @return {Class} A new Class.
*/
function Class(className, Parent, members) {
var ClassFactory = arguments.callee
prototype = Parent;
// Parent is a constructor: check ClassFactory
- } else if (Parent.prototype instanceof ClassFactory) {
+ } else if (Parent[P] instanceof ClassFactory) {
SuperClass = Parent;
prototype = Parent.fabricate();
return new ClassFactory(className, Parent, members);
} else {
- parentMembers = Parent.prototype || {};
+ parentMembers = Parent[P] || {};
}
// Creates a new function with the appropriate name
// Copy parent methods, then add new instance methods
for (var k in parentMembers)
- prototype[k] = parentMembers[k];
+ if ( hasOwn.call(parentMembers,k) )
+ setDesc(prototype, k, getDesc(parentMembers,k));
+ else
+ prototype[k] = parentMembers[k];
if ( prototype.toString === toString )
prototype.toString = classToString;
// Fix Constructors, prototypes
- NewClass.prototype = NewClass.fn = prototype;
+ NewClass[P] = 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;
// Or add new instance methods
} else for (var k in members) {
if ( hasOwn.call(members, k) )
- prototype[k] = members[k];
+ setDesc(prototype, k, getDesc(members,k));
}
if (prototype.init) NewClass.init = YFunction(prototype.init);
// Add metaprogramming data to Class object
Class.__super__ = Object;
-Class.fn = Class.prototype;
+Class.fn = Class[P];
Class.fn.__class__ = Class;
Class.className = Class.fn.className = "Class";
// Generic Collection Functions
var undefined
+
, globals = (function(){ return this; })()
+, _Object = globals.Object
, _Function = globals.Function
, _Array = globals.Array
-, slice = _Array.prototype.slice
+
+, getProto = _Object.getPrototypeOf
+, getDesc = _Object.getOwnPropertyDescriptor
+, setDesc = _Object.defineProperty
+
+, P = 'prototype'
+, hasOwn = _Object[P].hasOwnProperty
+, slice = _Array[P].slice
, type = require('Y/type')
;
function extendall(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
+ */
+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;
+
+ return acc;
+ }, {});
+}
+
-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['slice'] = slice;
+exports['accessors'] = accessors;
+exports['slice'] = slice;
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 {};
+
+ if ( notWrapped(o.accessors) )
+ return o.accessors.apply(o, slice.call(arguments,1));
+
+ return core.accessors(o);
+}
exports['reduce'] = reduce;
exports['map'] = map;
exports['set'] = set;
exports['attr'] = attr;
exports['extend'] = extend;
+
+exports['accessors'] = accessors;
+
* @param {Object} [members] Instance members to add to the new class's prototype; a class
* constructor can be supplied as `init`.
*
- * @returns {Class} A new Class.
+ * @return {Class} A new Class.
*/
function Class(className, Parent, members){
var ClassFactory = arguments.callee
else
return obj;
};
- }
+ },
+
+ end : function end(o){ return ((o && o.__y__) ? o.end() : o); }
};
var Y = require('Y/y').Y // I *think* this is safe
, del = require('Y/delegate')
, type = require('Y/type')
+, op = require('Y/op')
, mixin = require('Y/utils').mixin
, YCollection = require('Y/types/collection').YCollection
function concat( donor ){
var A = this._o;
return new YArray(A.concat.apply( A, slice.call(arguments,0).map(unwrapY) ));
- // return new YArray( slice.call(arguments).reduce(function(A, donor ){
- // return A.concat(donor instanceof YArray ? donor.end() : donor);
- // }, this._o) );
};
this['extend'] =
acc.push(v);
return acc;
}, new YArray(), this );
-
- // 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);
- // });
};
- // this.zip =
- // function zip(){
- // var sequences = new YArray(slice.call(arguments)).map( Y.limit(1) ).unshift(this);
- // return this.map(function(_, k){
- // return sequences.invoke('attr', k);
- // }).invoke('end');
- // };
+ this['zip'] =
+ function zip(){
+ var sequences = new YArray(slice.call(arguments)).unshift(this._o)
+ , kget = op.curried.kget ;
+ return this.map(function(_, k){
+ return sequences.map(function(seq){
+ return del.attr(seq,k);
+ });
+ }); //.invoke('end');
+ };
/**
* Like map, but produces a YObject:
, isFunction = Y.isFunction
, KNOWN_CLASSES = Class.KNOWN_CLASSES = exports['KNOWN_CLASSES'] = {}
-, getProto = Object.getPrototypeOf
-, hasOwn = Object.prototype.hasOwnProperty
-, objToString = Object.prototype.toString
+
+, P = 'prototype'
+, _Object = Object
+, hasOwn = _Object[P].hasOwnProperty
+, getProto = _Object.getPrototypeOf
+, getDesc = _Object.getOwnPropertyDescriptor
+, setDesc = _Object.defineProperty
+
, classToString = function toString(){ return this.className+"()"; }
;
* @param {Object} [members] Instance members to add to the new class's prototype; a class
* constructor can be supplied as `init`.
*
- * @returns {Class} A new Class.
+ * @return {Class} A new Class.
*/
function Class(className, Parent, members){
var ClassFactory = arguments.callee
NewClass[k] = v;
}
- // Copy parent methods
+ // Copy parent methods, then add new instance methods
for (var k in parentMembers)
- prototype[k] = parentMembers[k];
+ if ( hasOwn.call(parentMembers,k) )
+ setDesc(prototype, k, getDesc(parentMembers,k));
+ else
+ prototype[k] = parentMembers[k];
if ( prototype.toString === objToString )
prototype.toString = classToString;
NewClass.init = // XXX: This means subclasses will fire events.
prototype.initialise =
- function initialise(){
+ Y(function initialise(){
var instance = this
, init = NewClass.prototype.init
;
});
return instance;
- };
+ });
// Add class emitter
var ParentEmitter = (Parent.__emitter__ ? Parent : ClassFactory)
// Or add new instance methods
} else for (var k in members) {
if ( hasOwn.call(members, k) )
- prototype[k] = members[k];
+ setDesc(prototype, k, getDesc(members,k));
}
// Record for metaprogramming
* Create a new instance and run delegate constructor if it exists.
* Unlike the keyword `new`, instantiate can be applied.
*/
-function instantiate(){
- var instance = this.fabricate();
+function instantiate(cls){
+ var instance = cls.fabricate();
if ( instance.initialise )
instance.initialise.apply(instance, arguments);
return instance;
ctx : null,
dirty : true,
- canvasWidth : 0, layerWidth : 0,
- canvasHeight : 0, layerHeight : 0,
+
+ layerWidth : 0, canvasWidth : 0,
+ layerHeight : 0, canvasHeight : 0,
x: 0, y: 0, loc : null, // Position relative to parent
posBleed : null, // Loc
// Transforms
+ _origin : null, // rotational origin
transform : null, // Object
useCanvasScaling : false, // default to CSS3 scaling
this.boundingBox = new BoundingBox(0,0, 0,0);
+ this._origin = new Loc('50%','50%');
this.transform = {
- origin : new Loc('50%','50%'), // rotational origin
rotate : 0,
scale : new Loc(1.0,1.0),
translate : new Loc(0,0) // translates canvas
},
/**
- * @returns The root of the scene graph.
+ * @return The root of the scene graph.
*/
root : function root(){
if (this.parent)
/// Attributes ///
-
- attr : function attr(key, value, def){
- if (!key) return this;
-
- if ( Y.isPlainObject(key) ) {
- for (var k in key)
- this.attr(k, key[k]);
- return this;
- } else
- return Y.op.attr(this, key, value, def);
- },
+ attr : Y.attr.methodize(),
/**
* Changes the layer's width and then updates the canvas.
return this.layerWidth;
this.layerWidth = w;
+
+ var origin = this._origin
+ , nb = this.negBleed
+ , v = this.canvasWidth = Math.ceil(w + nb.x + this.posBleed.x); // HTMLCanvas.width is a long
+
this.boundingBox = this.boundingBox.resize(w, this.layerHeight);
+ this.layer.width(w).css('margin-left', (-nb.x)+'px')
- var nb = this.negBleed.x
- , v = this.canvasWidth = Math.ceil(w + nb + this.posBleed.x);
- this.layer.width(w).css('margin-left', (-nb)+'px')
- this.canvas.width(v);
- // this.canvas.css({
- // 'width' : v+'px',
- // 'margin-left' : (-nb)+'px'
- // });
+ // this.canvas.width(v);
+ this.canvas.css({
+ 'width' : v+'px',
+ 'margin-left' : (-nb)+'px'
+ });
this.canvas[0].width = v;
return this.layerHeight;
this.layerHeight = h;
- this.boundingBox = this.boundingBox.resize(this.layerWidth, h);
+ this.boundingBox = this.boundingBox.resize(this.layerWidth, h);
var nb = this.negBleed.y
- , v = this.canvasHeight = Math.ceil(h + nb + this.posBleed.y);
+ , v = this.canvasHeight = Math.ceil(h + nb + this.posBleed.y); // HTMLCanvas.height is a long
this.layer.height(h).css('margin-top', (-nb)+'px')
this.canvas.height(v);
// this.canvas.css({
},
/**
- * position() -> object
+ * position() -> {Loc}
* Gets position of the layer relative to the parent.
+ * @return {Loc}
*/
/**
- * position(top, left) -> this
+ * position(x,y) -> {this}
* Sets the position of this node, and then returns it.
- * @param {Number|String|undefined} top If omitted, this method must be invoked with `undefined` as the first argument.
- * @param {Number|String|undefined} left
+ * @param {Number|String|undefined} x
+ * @param {Number|String|undefined} y If omitted, this method must be invoked with `undefined` as the first argument.
+ * @return {this}
*/
/**
- * position(pos) -> this
+ * position(pos) -> {this}
* Sets the position of this node, and then returns it.
- * @param {Object} pos An object with "top" and/or "left" properties as in `position(top, left)`.
+ * @param {Object} pos An object with "x" and/or "y" properties as in `position(x,y)`.
+ * @return {this}
*/
- position : function position(left, top){
- if (top === undefined && left === undefined)
+ position : function position(x,y){
+ if (x === undefined && y === undefined)
return this.layer.position();
- if (top && Y.isPlainObject(top))
- var pos = top;
- else
- var pos = { 'top': top, 'left':left };
+ if ( Y.isPlainObject(x) ){
+ y = x.y;
+ x = x.x;
+ }
+
+ // if (pos.x !== undefined) pos.x -= this.offsetX;
+ // if (pos.y !== undefined) pos.y -= this.offsetY;
- // if (pos.left !== undefined) pos.left -= this.offsetX;
- // if (pos.top !== undefined) pos.top -= this.offsetY;
+ this.boundingBox = this.boundingBox.relocate(x,y);
+ this.loc = this.boundingBox.midpoint();
- this.boundingBox = this.boundingBox.add(pos.left,pos.top);
- this.loc = this.boundingBox.p1;
- this.css(pos);
+ var origin = this.origin();
+ this.css({
+ 'left' : x,
+ 'top' : y,
+ 'margin-left' : -origin.x,
+ 'margin-top' : -origin.y
+ });
return this;
},
/// Transformations ///
/**
- * Gets and sets the transformation origin.
+ * Gets and sets the origin-location for this shape, used for
+ * position and rotation. Defaults to the midpoint.
*/
origin : function origin(x,y){
- var o = this.transform.origin;
+ var o = this._origin;
if (arguments.length === 0)
return o.absolute(this.layerWidth, this.layerHeight);
tfns.push('scale('+t.scale.x+','+t.scale.y+')');
var trans = (tfns.length ? tfns.join(' ') : 'none')
- , o = t.origin.toUnits()
+ , o = this._origin.toUnits('px')
, origin = o.x+' '+o.y ;
- this.layer.css(
- ['', '-moz-', '-webkit-'].reduce(
+ this.layer.css(['', '-moz-', '-webkit-']
+ .reduce(
function(values, prefix){
values[prefix+'transform'] = trans;
values[prefix+'transform-origin'] = origin;
return values;
- }, {}) );
+ },
+ {}) );
return this;
},
/// Misc ///
toString : function toString(){
- var pos = (this.layer ? this.position() : {top:NaN, left:NaN});
- return this.className+'['+pos.left+','+pos.top+']( children='+this.children.size()+' )';
+ var pos = (this.layer ? this.position() : {y:NaN, x:NaN});
+ return this.className+'['+pos.x+','+pos.y+']( children='+this.children.size()+' )';
}
});
BoundingBox =
exports['BoundingBox'] =
Rect.subclass('BoundingBox', {
- init : function initBoundingBox(x1,y1, x2,y2){
- Rect.init.call(this, x1,y1, x2,y2);
-
- this.sides = {
- top : new Line(x1,y1, x2,y1),
- bottom : new Line(x1,y2, x2,y2),
- left : new Line(x1,y1, x1,y2),
- right : new Line(x2,y1, x2,y2)
- };
- },
-
- set : function set(k, v, def){
- v = (v !== undefined ? v : def);
-
- switch (k) {
- case 'x1': case 0: case 'x': this.x1 = this[0] = this.x = v; break;
- case 'y1': case 1: case 'y': this.y1 = this[1] = this.y = v; break;
- case 'x2': case 2: this.x2 = this[2] = v; break;
- case 'y1': case 3: this.y2 = this[3] = v; break;
-
- default: this[k] = v;
- }
-
- this.width = this.x2 - this.x1;
- this.height = this.y2 - this.y1;
-
- return this._updateSides();
- },
-
- _updateSides : function _updateSides(){
- var s = this.sides
- , x1 = this.x1, y1 = this.y1
- , x2 = this.x2, y2 = this.y2
- , dx1 = x1 !== s.top.x1, dy1 = y1 !== s.top.y1
- , dx2 = x2 !== s.top.x2, dy2 = y2 !== s.left.y2
- ;
-
- if ( dx1 || dy1 ) this.p1 = new Vec(x1,y1);
- if ( dx2 || dy2 ) this.p2 = new Vec(x2,y2);
-
- if ( dx1 || dy1 || dx2 ) s.top = new Line(x1,y1, x2,y1);
- if ( dx1 || dx2 || dy2 ) s.bottom = new Line(x1,y2, x2,y2);
- if ( dx1 || dy1 || dy2 ) s.left = new Line(x1,y1, x1,y2);
- if ( dy1 || dy2 || dx2 ) s.right = new Line(x2,y1, x2,y2);
-
- return this;
- },
attr : Y.attr.methodize(),
- add : function add(x,y){
+ re