-function doPhase(phase, o, newY, args){
- var ps = Y.plugins.slice(0) // prevent mutation under iteration
- , pslen = ps.length
- , capture = (phase === 'capture')
- ;
-
- for ( var i=0, plugin=ps[i]; i<pslen; plugin=ps[++i] ) {
- if ( !(phase in plugin) )
- continue;
-
- // XXX: Should we clone args on each call to prevent mutation?
- var r = plugin[phase](o, newY, args);
-
- if (r) {
- // If this is the capture phase, we just matched a plugin,
- // so we should return without running the bubbling phase
- if (capture)
- return r;
- else
- o = r;
- }
- }
-
- // If this is the capture phase, we should bubble to call as
- // no plugins matched or we would have returned above.
- if (capture)
- return doPhase('call', o, newY, args);
- else
- return o;
-}
-
/**
* Creates a Y wrapper around its input.
*/
function Y(o){
var A = arguments
- , newY = (this instanceof Y);
+ , newY = (this instanceof Y)
+ , r, args
+ ;
// Passthrough nulls
if (o === undefined || o === null)
return newY ? this : o;
// Don't re-wrap Y-objects
- if ( o.__y__ || o instanceof YBase )
+ if ( o.__y__ )
return newY ? Y(o.end()) : o;
- // Invoke plugins
- var _o = o
- , args = slice.call(A, 0);
-
- // Capture phase, which will bubble to `call` if necessary
- o = doPhase('capture', o, newY, args);
+ // Y( arguments, start=0, stop=arguments.length )
+ // Cast `arguments` object to a real Array, optionally slicing at specified delimiters
+ if ( o.prototype === undefined
+ && isNumber(o.length)
+ && !isArray(o)
+ && o.constructor === _Object )
+ {
+ r = slice.call( o, A[1]||0, A[2]||o.length );
+ return (newY ? new Y.YArray(r) : r);
+ }
- // Post-processing phase
- o = doPhase('finally', o, newY, args);
+ // Convenience calls for non-object conversions to collections
+ args = slice.call(A, 0);
+ if ( A.length > 1 ) {
+
+ // Merge Arrays or Objects
+ // Y([0,1], [2,3], [4,5]) -> [0,1,2,3,4,5]
+ // Y({foo:1}, {bar:2}) -> { foo:1, bar:2 }
+ if ( args.every(isArray) || args.every(isPlainObject) ) {
+ r = extend.apply(this, args);
+
+ // Convenience of Y(Y.range())
+ } else if ( args.every(isNumber) ) {
+ r = Y.range.apply(this, A);
+
+ // We got random stuff: wrap an Array of it
+ } else
+ return new Y.YArray( args );
+
+ return (newY ? Y(r) : r);
+ }
- // Wrap as generic object if nothing matched
- if (o !== _o || o._o)
- return o;
- else
- return new Y.YObject(o);
-}
-
-Y.plugins = [];
-Y.plugins.byName = {};
-
-/**
- * @param {Object|Function} plugin should either be a function to be used as `call`, or an object
- * with some set of methods from:
- * - call: Invoked during bubbling to process input. Any non-false-y return value will be passed
- * along as the Y() return value. All plugins with a `call` method will be invoked during
- * bubbling, so non-matching plugins should not return a value.
- * - capture: Invoked before bubbling to process input. A non-false-y return value will be
- * returned as the Y() return value. All plugins with a `capture` method will be invoked during
- * capture, and any truth-y response will prevent bubbling. It is therefore critical non-
- * matching plugins do not return a value.
- * - finally: Invoked after capture and/or bubbling to post-process the result. Any non-false-y
- * return value will be passed along as the Y() return value.
- * Methods are invoked with:
- * - {Object} o: The current return value (or input)
- * - {Boolean} newY: Whether `Y()` was invoked with the `new` keyword.
- * - {Array} args: The original arguments, converted to an array.
- */
-Y.plugin = function(plugin){
- if ( isFunction(plugin) )
- plugin = { 'call':plugin, 'name':plugin.name };
+ // Do we have a type-specific wrapper?
+ var name = type_of(o)
+ , yname = 'Y' + name.charAt(0).toUpperCase() + name.slice(1)
+ , YType = Y[yname]
+ ;
- plugin.name = plugin.name || plugin.className;
+ // if ( YType && YType !== Y.YObject || YType !== Y.YBase )
+ // return new YType(o);
+ if ( YType )
+ return new YType(o);
- // Register plugin
- Y.plugins.push(plugin);
- if (plugin.name) Y.plugins.byName[plugin.name] = plugin;
+ // Add YFunction methods, since we can't subclass function
+ // if ( isFunction(o) )
+ // return new Y.YFunction(o);
- return plugin;
-};
-
-// Y( arguments, start=0, stop=arguments.length )
-// Cast `arguments` object to a real Array, optionally slicing at specified delimiters
-Y.plugin({
- 'capture': function(o, newY, args){
- if ( o.prototype === undefined
- && isNumber(o.length)
- && !isArray(o)
- && o.constructor === _Object )
- return slice.call(o, args[1] || 0, args[2] || o.length);
- }
-});
-
-Y.plugin({
- 'finally': function(o, newY, args){
- return (newY ? Y(o) : o);
- }
-});
-
-// Merge Arrays or Objects
-// Y([0,1], [2,3], [4,5]) -> [0,1,2,3,4,5]
-// Y({foo:1}, {bar:2}) -> { foo:1, bar:2 }
-Y.plugin({
- 'capture': function(o, newY, args){
- if ((args.length > 1) && ( args.every(isArray) || args.every(isPlainObject) ))
- return extend.apply(this, args);
- }
-});
-
-// Convenience of Y(Y.range())
-Y.plugin({
- 'capture': function(o, newY, args){
- if ( (args.length > 1) && args.every(isNumber) )
- return Y.range.apply(this, A);
- }
-});
-
-
-// We got random stuff: wrap an Array of it
-Y.plugin({
- 'capture': function(o, newY, args){
- if (args.length > 1)
- return new Y.YArray(args);
- }
-});
-
-function getYType(o){
- var name = type_of(o)
- , yname = 'Y' + name.charAt(0).toUpperCase() + name.slice(1);
- return Y[yname];
+ // Finally, Generic object wrapper
+ return new Y.YObject(o);
}
-// Builtin type-specific wrappers
-Y.plugin({
- 'call' : function(o, newY, args){
- var YType = getYType(o);
- if ( YType && YType !== Y.YObject || YType !== Y.YBase )
- return new YType(o);
- }
-});
-
-
+CONTEXT_ATTRS = Y([
+ 'globalAlpha', 'globalCompositeOperation', 'strokeStyle', 'fillStyle',
+ 'lineWidth', 'lineCap', 'lineJoin', 'miterLimit', 'shadowOffsetX',
+ 'shadowOffsetY', 'shadowBlur', 'shadowColor'
+]);
+
+
Shape = new Y.Class('Shape', Layer, {
_cssClasses : 'portal layer shape',
- _fill : false, // same as 'transparent'
- _line_width : 0, // will ignore strokeStyle
- _stroke : false, // same as 'transparent'
-
-
- _updateStyle : function(ctx, apply, force){
- ctx.lineWidth = this._line_width;
- ctx.strokeStyle = this._stroke || 'transparent';
- ctx.fillStyle = this._fill || 'transparent';
+ attr : function(key, value, def){
+ if (!key) return this;
- if (apply) {
- if (force || (this._line_width && this._stroke))
- ctx.stroke();
+ if ( Y.isPlainObject(key) ) {
+ for (var k in key)
+ this.attr(k, key[k]);
+ return this;
- if (force || this._fill)
- ctx.fill();
- }
-
- return this;
+ } else if ( CONTEXT_ATTRS.has(key) ) {
+ var r = Y.attr(this.ctx, key, value, def);
+
+ if (r === this.ctx) {
+ // This implies we set a property
+ this.dirty = true;
+ return this;
+ } else
+ return r;
+
+ } else
+ return Y.attr(this, key, value, def);
}
});
Rect = new Y.Class('Rect', Shape, {
_cssClasses : 'portal layer shape rect',
- _fill : "#E73075",
_width : 0,
_height : 0,
drawShape : function(ctx){
ctx.rect(0,0, this._width,this._height);
- this._updateStyle(ctx, true);
+ ctx.fill();
return this;
}
});
+Triangle = new Y.Class('Triangle', Shape, {
+ x1: 0, y1: 0,
+ x2: 0, y2: 0,
+ _offsetX : 0, // We need our triangle to be in the first quadrant
+ _offsetY : 0, // so we'll have to offset any negative shapes at the position call
+
+ init : function(x1,y1, x2,y2){
+ Layer.init.call(this);
+
+ var minX = Math.min(0, x1, x2);
+ if (minX < 0) {
+ var offX = this._offsetX = -minX;
+ x1 += offX; x2 += offX;
+ }
+
+ var minY = Math.min(0, y1, y2);
+ if (minY < 0) {
+ var offY = this._offsetY = -minY;
+ Y1 += offY; y2 += offY;
+ }
+
+ this.x1 = x1; this.y1 = y1;
+ this.x2 = x2; this.y2 = y2;
+ this.width( Math.max(0, x1, x2) );
+ this.height( Math.max(0, y1, y2) );
+ },
+
+ _calcOffset : function(which, values){
+ var min = Math.min(0, Math.min.apply(Math, values));
+
+ if (min < 0) {
+ var off = this['_offset'+which] = -min;
+ x1 += off; x2 += off;
+ }
+ return
+ },
+
+ position : function(left, top){
+ if (top === undefined && left === undefined)
+ return this.layer.position();
+
+ if (top && Y.isPlainObject(top))
+ var pos = top;
+ else
+ var pos = { 'top':top, 'left':left };
+
+ if (pos.top !== undefined) pos.top -= this.offsetX;
+ if (pos.left !== undefined) pos.left -= this.offsetY;
+
+ this.css(pos);
+ return this;
+ },
+
+});
Ellipse = new Y.Class('Ellipse', Shape, {