Added Shape.attr
authordsc <david.schoonover@gmail.com>
Sun, 31 Oct 2010 01:20:41 +0000 (18:20 -0700)
committerdsc <david.schoonover@gmail.com>
Sun, 31 Oct 2010 01:20:41 +0000 (18:20 -0700)
notes.md
src/Y/core.js
src/Y/y-collection.js
src/Y/y-core.js
src/Y/y-function.js
src/portal/layer.js
src/portal/shape.js
src/tanks/lttl.js

index e69de29..1d5c4a2 100644 (file)
--- a/notes.md
+++ b/notes.md
@@ -0,0 +1 @@
+- Clipping is going to suck
\ No newline at end of file
index d394536..d7c0080 100644 (file)
@@ -23,6 +23,11 @@ function attr(o, key, value, def){
     if ( o && notSelfOrWrapped(o.attr) )
         return o.attr.apply(o, slice.call(arguments,1));
     
+    if ( !o || key === undefined ) return o;
+    
+    if ( isPlainObject(key) )
+        return extend(o, key);
+    
     if ( value !== undefined || def !== undefined ){
         o[key] = (value !== undefined ? value : def);
         return o;
@@ -31,10 +36,12 @@ function attr(o, key, value, def){
 }
 
 function extend( A, B ){
-    return slice.call(arguments,1).reduce(function(A, donor){
-        return reduce(donor, function(o, v, k){
-            return attr(o, k, v, o[k]);
-        }, A);
-    }, A);
+    return slice.call(arguments,1).reduce(extend._extendall, A);
 }
+extend._extendall = function(A, donor){
+    return reduce(donor, extend._set, A);
+};
+extend._set = function(o, v, k){
+    return attr(o, k, v, o[k]);
+};
 
index 70d0db6..4de7996 100644 (file)
@@ -72,6 +72,10 @@ YBase.subclass('YCollection', {
         return -1;
     },
     
+    'has' : function( value ){
+        return ( this.indexOf(value) !== -1 );
+    },
+    
     'clone' : function(){
         return Y({}).extend(this);
     },
index e05b110..10a9b26 100644 (file)
@@ -11,160 +11,71 @@ Y.isPlainObject = isPlainObject;
 
 
 
-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);
-    }
-});
-
-
index f7654b4..91447cf 100644 (file)
@@ -202,3 +202,6 @@ function mixinNames(o, Donor, names, override, yWrap){
 }
 
 
+Y(Y.reduce);
+Y(Y.attr);
+Y(Y.extend);
index 53f1fe6..83613c2 100644 (file)
@@ -34,9 +34,6 @@ Layer = new Y.Class('Layer', {
     ctx      : null,
     dirty    : true,
     
-    x: 0,
-    y: 0,
-    
     
     /// Setup ///
     
index b2aef21..e0e3a63 100644 (file)
@@ -1,25 +1,33 @@
+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);
     }
     
 });
@@ -27,7 +35,6 @@ Shape = new Y.Class('Shape', Layer, {
 Rect = new Y.Class('Rect', Shape, {
     _cssClasses : 'portal layer shape rect',
     
-    _fill   : "#E73075",
     _width  : 0,
     _height : 0,
     
@@ -44,12 +51,66 @@ Rect = new Y.Class('Rect', Shape, {
     
     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, {
     
index 5dcafa1..fced4b3 100644 (file)
@@ -5,10 +5,19 @@ v = $('#viewport');
 L = new Layer(v)
     .width(v.width())
     .height(v.height())
-    .append( new Rect(50,50).position(50,50) )
     .append(
-        new Rect(250,250).position(50,150) )
-    .appendTo(v)
+        new Rect(50,50)
+            .position(50,50)
+            .attr({
+                fillStyle : '#E73075'
+            })
+    ).append(
+        R = new Rect(250,250)
+            .position(50,150)
+            .attr({
+                fillStyle : '#E2EEF5'
+            })
+    ).appendTo(v)
     .draw()
 ;