neyric/wireit

View on GitHub
build/wireit-all/wireit-all-debug.js

Summary

Maintainability
F
3 mos
Test Coverage
/* This file is auto-generated by src/loader/scripts/meta_join.js */

YUI().use(function(Y) {
   var CONFIG = {
      groups: {
         'wireit': {
            base: 'wireit/src/',
            combine: false,
            modules: {
    "arrow-wire": {
        "requires": [
            "wire-base"
        ]
    },
    "bezier-wire": {
        "requires": [
            "wire-base"
        ]
    },
    "bidirectional-arrow-wire": {
        "requires": [
            "arrow-wire"
        ]
    },
    "container": {
        "requires": [
            "widget-stdmod",
            "widget-stack",
            "widget-parent",
            "widget-child",
            "dd",
            "resize",
            "wires-delegate",
            "widget-terminals",
            "widget-icons"
        ],
        "skinnable": true
    },
    "image-container": {
        "requires": [
            "container"
        ],
        "skinnable": true
    },
    "inout-container": {
        "requires": [
            "container",
            "terminal-input",
            "terminal-output"
        ],
        "skinnable": true
    },
    "layer": {
        "requires": [
            "widget-parent",
            "container",
            "wires-delegate"
        ],
        "skinnable": true
    },
    "straight-wire": {
        "requires": [
            "wire-base"
        ]
    },
    "terminal": {
        "requires": [
            "widget",
            "widget-child",
            "widget-position",
            "widget-position-align",
            "wire-base",
            "wires-delegate",
            "terminal-dragedit",
            "terminal-scissors",
            "terminal-ddgroups"
        ],
        "skinnable": true
    },
    "terminal-ddgroups": {
        "requires": [
            "terminal-dragedit"
        ]
    },
    "terminal-dragedit": {
        "requires": [
            "bezier-wire",
            "dd-drop",
            "dd-drag",
            "dd-proxy"
        ]
    },
    "terminal-input": {
        "requires": [
            "terminal"
        ]
    },
    "terminal-output": {
        "requires": [
            "terminal"
        ]
    },
    "terminal-scissors": {
        "requires": [
            "overlay"
        ],
        "skinnable": true
    },
    "textarea-container": {
        "requires": [
            "container"
        ]
    },
    "widget-icons": {
        "requires": [],
        "skinnable": true
    },
    "widget-terminals": {
        "requires": [
            "terminal"
        ]
    },
    "wire-base": {
        "requires": [
            "graphics"
        ],
        "skinnable": true
    },
    "wireit-app": {
        "requires": [
            "app",
            "handlebars",
            "model",
            "model-list",
            "json",
            "view",
            "layer",
            "bezier-wire",
            "anim"
        ]
    },
    "wires-delegate": {
        "requires": [
            "wire-base"
        ]
    }
}
         }
      }
   };

   if(typeof YUI_config === 'undefined') {
      YUI_config = {groups: {}};
   }

   Y.mix(YUI_config.groups, CONFIG.groups);

});
YUI.add('arrow-wire', function (Y, NAME) {

'use strict';

/**
 * @module arrow-wire
 */

/**
 * Extend CanvasWire to draw an arrow wire
 * @class ArrowWire
 * @extends WireBase
 * @constructor
 * @param {Object} config the configuration for the ArrowWire attributes
 */
Y.ArrowWire = function (config) {
   Y.ArrowWire.superclass.constructor.apply(this, arguments);
};

Y.ArrowWire.NAME = 'arrowwire';

Y.extend(Y.ArrowWire, Y.WireBase, {

   _drawArrow: function(src, tgt) {


      var d = 7, // arrow width/2
          distance = Math.sqrt(Math.pow(src[0]-tgt[0],2) + Math.pow(src[1]-tgt[1],2)),
          dlug = 20, //arrow length
          t = (distance === 0) ? 0 : 1 - (dlug/distance),

          //point on the wire with constant distance (dlug) from terminal2
          z = [
            Math.abs( src[0] + t * (tgt[0] - src[0]) ),
            Math.abs( src[1] + t * (tgt[1] - src[1]) )
          ],

      // start drawing arrows

      // line which connects the terminals: y=ax+b
          W = src[0] - tgt[0],
          Wa = src[1] - tgt[1],
          Wb = src[0] * tgt[1] - src[1] * tgt[0],
          a, b, aProst, bProst,
          A,B,C,
          delta,
          x1,x2,y1,y2,
          o;

      if (W !== 0) {
         a = Wa / W;
         b = Wb / W;
      }
      else {
         a = 0;
      }
      
      // line perpendicular to the main line: y = aProst*x + b
      if (a === 0) {
         aProst = 0;
      }
      else {
         aProst = -1 / a;
      }
      bProst = z[1] - aProst * z[0]; //point z lays on this line
        
      // we have to calculate coordinates of 2 points, which lay on perpendicular line and have the same distance (d) from point z
      A = 1 + Math.pow(aProst, 2);
      B = 2 * aProst * bProst - 2 * z[0] - 2 * z[1] * aProst;
      C = -2 * z[1] * bProst + Math.pow(z[0], 2) + Math.pow(z[1], 2) - Math.pow(d, 2) + Math.pow(bProst, 2);

      delta = Math.pow(B, 2) - 4 * A * C;
      if (delta < 0) { return; }
        
      x1 = (-B + Math.sqrt(delta)) / (2 * A);
      x2 = (-B - Math.sqrt(delta)) / (2 * A);
      y1 = aProst * x1 + bProst;
      y2 = aProst * x2 + bProst;
        
      if (src[1] === tgt[1]) {
         o = (src[0] > tgt[0]) ? 1 : -1;
         x1 = tgt[0] + o * dlug;
         x2 = x1;
         y1 -= d;
         y2 += d;
      }

      // triangle border
      this.moveTo(tgt[0] + 6, tgt[1] + 6);
      this.lineTo(x1 + 6, y1 + 6);
      this.moveTo(tgt[0] + 6, tgt[1] + 6);
      this.lineTo(x2 + 6, y2 + 6);

   },

   /**
    * @method _draw
    * @private
    */
   _draw: function () {

      this.clear();

      var src = this.get('src').getXY(),
          tgt = this.get('tgt').getXY();


      this.moveTo((src[0] + 6), (src[1] + 6));
      this.lineTo((tgt[0] + 6), (tgt[1] + 6));

      this._drawArrow(src, tgt);

      this.end();
   }
});

Y.ArrowWire.ATTRS = Y.merge(Y.WireBase.ATTRS, {});


}, '@VERSION@', {"requires": ["wire-base"]});
YUI.add('bezier-wire', function (Y, NAME) {

/**
 * @module bezier-wire
 */

/**
 * Extend WireBase to draw a bezier curve
 * @class BezierWire
 * @extends WireBase
 * @constructor
 * @param {Object} config the configuration for the BezierWire attributes
 */
Y.BezierWire = function (cfg) {
   Y.BezierWire.superclass.constructor.apply(this, arguments);
};

Y.BezierWire.NAME = "bezierwire";

Y.extend(Y.BezierWire, Y.WireBase, {
   
   /**
    * Draw the bezier curve.
    * The canvas is made bigger to contain the curls
    * @method _draw
    * @method private
    */
    _draw: function () {
        
        this.clear();
        
        var src = this.get('src').getXY(),
            tgt = this.get('tgt').getXY(),
      
            srcDir = this.get('srcDir'),
            tgtDir = this.get('tgtDir'),
            bezierTangentNorm = this.get('bezierTangentNorm'),
         
            terminalSize = 14/2;
         
        this.moveTo(src[0]+terminalSize,src[1]+terminalSize);
        
        this.curveTo(src[0]+terminalSize+srcDir[0]*bezierTangentNorm,
                     src[1]+terminalSize+srcDir[1]*bezierTangentNorm, 
                     
                     tgt[0]+terminalSize+tgtDir[0]*bezierTangentNorm,
                     tgt[1]+terminalSize+tgtDir[1]*bezierTangentNorm, 
                     
                     tgt[0]+terminalSize,
                     tgt[1]+terminalSize);
        
        this.end();
     },
   
   
   
   SERIALIZABLE_ATTRS: function() {
      return ["color","width","bezierTangentNorm"];
   }
   
});

Y.BezierWire.ATTRS = Y.merge(Y.WireBase.ATTRS, {
   
   /** 
    * Norm of the tangeant vector at the endpoints.
    * @attribute bezierTangentNorm
    * @default 100
    * @type Integer
    */
   bezierTangentNorm: {
      setter: function (val) {
         return parseInt(val, 10);
      },
      value: 100
   }

});



}, '@VERSION@', {"requires": ["wire-base"]});
YUI.add('bidirectional-arrow-wire', function (Y, NAME) {

/**
 * @module bidirectional-arrow-wire
 */

/**
 * BidirectionalArrowWire
 * @class BidirectionalArrowWire
 * @extends ArrowWire
 * @constructor
 * @param {Object} config the configuration for the BezierWire attributes
 */
Y.BidirectionalArrowWire = function (config) {
   Y.BidirectionalArrowWire.superclass.constructor.apply(this, arguments);
};

Y.BidirectionalArrowWire.NAME = 'bidirectionalarrowwire';

Y.extend(Y.BidirectionalArrowWire, Y.ArrowWire, {
   
   _draw: function () {

      this.clear();

      var src = this.get('src').getXY(),
          tgt = this.get('tgt').getXY();

      this.moveTo((src[0] + 6), (src[1] + 6));
      this.lineTo((tgt[0] + 6), (tgt[1] + 6));

      this._drawArrow(src, tgt);
      this._drawArrow(tgt, src);

      this.end();
   }
   
});

Y.BidirectionalArrowWire.ATTRS = Y.merge(Y.ArrowWire.ATTRS, {});


}, '@VERSION@', {"requires": ["arrow-wire"]});
YUI.add('container', function (Y, NAME) {

'use strict';

/**
 * @module container
 */

/**
 * Container is an Overlay (XY positioning)
 * It is a WidgetChild (belongs to Layer)
 * It is also a WidgetParent (has many terminals)
 * @class Container
 * @extends Widget
 * @uses WidgetStdMod
 * @uses WidgetStack
 * @uses WidgetParent
 * @uses WidgetChild
 * @uses WiresDelegate
 * @uses WidgetTerminals
 * @uses WidgetIcons
 * @constructor
 */
Y.Container = Y.Base.create("container", Y.Widget, [
   Y.WidgetStdMod,
   Y.WidgetStack,
   Y.WidgetParent,
   Y.WidgetChild,
   Y.WiresDelegate,
   Y.WidgetTerminals,
   Y.WidgetIcons
], {

   /**
    * @method renderUI
    */
   renderUI: function () {
      this._renderDrag();
      this._renderResize();
   },

   bindUI: function() {

      if(this.resize) {
         this.resize.on('resize:resize', this._onResize, this);
      }

      this.drag.on('drag:drag', function () {
         this.redrawAllWires();
      }, this);

   },

   syncUI: function() {
      // waiting for the next tick to align the terminals
      Y.later(0, this, function() {
         this.alignTerminals();
      });
   },

   _renderDrag: function() {
      // make the overlay draggable
      this.drag = new Y.DD.Drag({
         node: this.get('boundingBox'),
         handles : [ this._findStdModSection(Y.WidgetStdMod.HEADER) ]
      });
   },

   _renderResize: function() {

      // Make the overlay resizable
      if(!this.get('resizable')) {
         return;
      }

      this.resize = new Y.Resize({
         node: this.get('contentBox'),
         handles: 'br' // bottom-right
      });
       
   },

   _onResize: function(e) {
      // On resize, fillHeight, & align terminals & wires
      this._fillHeight();
      this.alignTerminals();

      // Set width & height
      var region = this.get('boundingBox').get('region');
      this.set('width', e.details[0].info.offsetWidth);
      this.set('height', e.details[0].info.offsetHeight);
   },


   /**
    * Click handler for the close icon
    * @method _onCloseClick
    * @private
    */
   _onCloseClick: function () {
      this.destroy();
   },
   
   
   SERIALIZABLE_ATTRS: function() {
      var attrs = ['x', 'y'];
      if(this.get('resizable')) {
         attrs.push('width');
         attrs.push('height');
      }
      return attrs;
   },
   
   toJSON: function () {
      var o = {},
          a = this;
      Y.Array.each(this.SERIALIZABLE_ATTRS(), function (attr) {
         o[attr] = a.get(attr);
      });
      return o;
   },
   
   destructor: function () {

      this.drag.destroy();
      
      if(this.resize) {
         this.resize.destroy();
      }
   }

}, {

   ATTRS: {

      /**
       * Relative left position (in the layer referential)
       * @attribute x
       */
      x: {
         lazyAdd: false,
         getter: function() {
            return parseInt(this.get('boundingBox').getStyle('left'),10);
         },
         setter: function(val) {
            this.get('boundingBox').setStyle('left', val);
         },
         validator: function(val) {
            return Y.Lang.isNumber(val);
         }
      },

      /**
       * Relative top position (in the layer referential)
       * @attribute y
       */
      y: {
         lazyAdd: false,
         getter: function() {
            return parseInt(this.get('boundingBox').getStyle('top'),10);
         },
         setter: function(val) {
            this.get('boundingBox').setStyle('top', val);
         },
         validator: function(val) {
            return Y.Lang.isNumber(val);
         }
      },

      /**
       * @attribute zIndex
       */
      zIndex: {
         value: 5
      },
      
      /**
       * @attribute resizable
       */
      resizable: {
         value: true
      },
      
      /**
       * @attribute fillHeight
       */
      fillHeight: {
         value: true
      },
      
      preventSelfWiring: {
         value: true
      },

      /**
       * Override the default value of WidgetIcons to add the close button
       * @attribute icons
       */
      icons: {
         value: [
            {title: 'close', click: '_onCloseClick', className: 'ui-silk ui-silk-cancel'}
         ]
      }
   }
   
});



}, '@VERSION@', {
    "requires": [
        "widget-stdmod",
        "widget-stack",
        "widget-parent",
        "widget-child",
        "dd",
        "resize",
        "wires-delegate",
        "widget-terminals",
        "widget-icons"
    ],
    "skinnable": true
});
YUI.add('image-container', function (Y, NAME) {

/**
 * @module image-container
 */

/**
 * ImageContainer is an Overlay (XY positioning)
 * It is a WidgetChild (belongs to Layer)
 * It is also a WidgetParent (has many terminals)
 * @class ImageContainer
 * @extends Container
 * @constructor
 */
Y.ImageContainer = Y.Base.create("image-container", Y.Container, [], {
   
   /**
    * @method renderUI
    */
   renderUI: function () {

      this.image = Y.Node.create('<img src="'+this.get('imageUrl')+'" width="'+this.get('width')+'"  height="'+this.get('height')+'"/>');
      this.image.appendTo( this.get('contentBox') );
      
      Y.ImageContainer.superclass.renderUI.apply(this);

      if(this.resize && this.get('resizePreserveRatio') ) {
         this.resize.plug(Y.Plugin.ResizeConstrained, {
            preserveRatio: true
         });
      }

   },

   /**
    * @method bindUI
    */
   bindUI: function() {

      this.image.after('load', this.alignTerminals, this);

      if(this.resize) {
         this.resize.after('resize:resize', this._onResizeImage, this);
      }

      this.drag.set('handles', [this.image]);

      Y.ImageContainer.superclass.bindUI.apply(this);
   },

   _onResizeImage: function(e) {
      var p = e.details[0].info;
      this.image.set('width',  p.right-p.left);
      this.image.set('height', p.bottom-p.top);
   }
   
}, {

   ATTRS: {

      /**
       * Url of the image you want to render (relative to the script's page)
       * @attribute imageUrl
       */
      imageUrl: {
         value: '',
         setter: function(url) {
            if(this.image) {
              this.image.set('src', url);
            }
         }
      },

      /**
       * Preserve ratio when resized (only if resizable)
       * @attribute resizePreserveRatio
       */
      resizePreserveRatio: {
         value: true
      }

   }
   
});


}, '@VERSION@', {"requires": ["container"], "skinnable": true});
YUI.add('inout-container', function (Y, NAME) {

/**
 * @module inout-container
 */

/**
 * Container with left inputs and right outputs
 * @class InOutContainer
 * @extends Container
 * @constructor
 * @param {Object} options
 */

Y.InOutContainer = Y.Base.create("inout-container", Y.Container, [], {

   /**
    * @method renderUI
    */
   renderUI: function () {
      Y.InOutContainer.superclass.renderUI.call(this);
      this._renderInputsOutputs();
   },
   
   /**
    * @method _renderInputsOutputs
    */
   _renderInputsOutputs: function () {

      this.setStdModContent(Y.WidgetStdMod.BODY, "<ul class='inputs'></ul><ul class='outputs'></ul>");

      var bb = this.get('boundingBox'),
          inputsUl = bb.one('ul.inputs'),
          outputsUl = bb.one('ul.outputs'),
          inputs = this.get('inputs'),
          outputs = this.get('outputs'),
          i, n;


      for(i = 0, n = inputs.length ; i < n ; i++) {

         Y.Node.create('<li>'+inputs[i].label+'</li>').appendTo(inputsUl);

         this.add({
            type: 'TerminalInput',
            name: inputs[i].name,
            dir: [-0.3, 0]
         });
      }

      for(i = 0, n = outputs.length; i < n ; i++) {

         Y.Node.create('<li>'+outputs[i].label+'</li>').appendTo(outputsUl);

         this.add({
            type: 'TerminalOutput',
            name: outputs[i].name,
            dir: [0.3, 0]
         });
      }

      Y.later(100, this, function() {

         var i, term;

         for(i = 0 ; i < inputs.length ; i++) {
            this.item(i).align( inputsUl.all('li').item(i) , [Y.WidgetPositionAlign.TC, Y.WidgetPositionAlign.LC] );
         }
         for(i = 0 ; i < outputs.length ; i++) {
            term = this.item(inputs.length + i);
            term.align( outputsUl.all('li').item(i) , [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.RC] );
            term.set('x', term.get('x')+11);
         }
      });
      
   }
   
}, {

   ATTRS: {
      
      resizable: {
         value: false
      },
      
      /**
       * @attribute inputs
       * @description Array of strings for which an Input terminal will be created.
       * @default []
       * @type Array
       */
      inputs: [],

      /**
       * @attribute outputs
       * @description Array of strings for which an Output terminal will be created.
       * @default []
       * @type Array
       */
      outputs: []
   }
   
});


}, '@VERSION@', {"requires": ["container", "terminal-input", "terminal-output"], "skinnable": true});
YUI.add('layer', function (Y, NAME) {

/**
 * @module layer
 */

/**
 * Layer : Widget to manage collections of wires (through WiresDelegate) and containers (trough WidgetParent)
 * @class Layer
 * @extends Widget
 * @uses WidgetParent
 * @uses WiresDelegate
 */
Y.Layer = Y.Base.create("layer", Y.Widget, [Y.WidgetParent, Y.WiresDelegate], {
   
   initializer: function () {
      
      this.graphic = new Y.Graphic({render: this.get('contentBox') });
      
   },
   
   
   /**
    * Alias method for WidgetParent.removeAll
    * @method clear
    */
   clear: function () {
      this.removeAll();
   }
   
}, {
   
   ATTRS: {
      
      defaultChildType: {
         value: 'Container'
      }
      
   }
   
});



}, '@VERSION@', {"requires": ["widget-parent", "container", "wires-delegate"], "skinnable": true});
YUI.add('straight-wire', function (Y, NAME) {

/**
 * @module straight-wire
 */

/**
 * Straight Wire
 * @class StraightWire
 * @extends WireBase
 * @constructor
 * @param {Object} cfg the configuration for the StraightWire attributes
 */
Y.StraightWire = function (cfg) {
   Y.StraightWire.superclass.constructor.apply(this, arguments);
};

Y.StraightWire.NAME = "straightwire";

Y.extend(Y.StraightWire, Y.WireBase, {
  
   /**
    * @method _draw
    * @private
    */
   _draw: function () {
      
      this.clear();
      
      var src = this.get('src').getXY(),
          tgt = this.get('tgt').getXY();
      
      this.moveTo((src[0]+6), (src[1]+6));
      this.lineTo((tgt[0]+6), (tgt[1]+6));
      this.end();
   }
   
});

Y.StraightWire.ATTRS = Y.merge(Y.WireBase.ATTRS, {});


}, '@VERSION@', {"requires": ["wire-base"]});
YUI.add('terminal', function (Y, NAME) {

/**
 * @module terminal
 */

'use strict';

/**
 * Terminal is responsible for wire edition
 *
 * @class Terminal
 * @extends Widget
 * @uses WidgetChild
 * @uses WidgetPosition
 * @uses WidgetPositionAlign
 * @uses WiresDelegate
 * @uses TerminalDragEdit
 * @uses TerminalScissors
 * @uses TerminalDDGroups
 * @constructor
 * @param {Object} oConfigs The user configuration for the instance.
 */
Y.Terminal = Y.Base.create("terminal", Y.Widget, [
   Y.WidgetChild,
   Y.WidgetPosition,
   Y.WidgetPositionAlign,
   Y.WiresDelegate,
   Y.TerminalDragEdit,
   Y.TerminalScissors,
   Y.TerminalDDGroups
], {


   syncUI: function () {
      this._syncOffset();
   },

   _syncOffset: function() {
      var offset = this.get('offset');
      if(offset) {
         this._posNode.setStyle('left', offset[0]);
         this._posNode.setStyle('top',  offset[1]);
         this.syncXY();
      }
   },

   bindUI: function() {
      var bb = this.get('boundingBox');
      bb.on('mouseover', this._onMouseOver, this);
      bb.on('mouseout', this._onMouseOut, this);
   },

   _onMouseOver: function() {
      Y.later(300, this, this._showOverlay);
   },

   _showOverlay: function() {
      this.get('boundingBox').addClass( this.getClassName("show-overlay") );
   },

   _onMouseOut: function() {
      Y.later(300, this, this._hideOverlay);
   },

   _hideOverlay: function() {
      var bb = this.get('boundingBox');
      // because of the timer, the widget may have been destroyed
      if(bb) {
         bb.removeClass( this.getClassName("show-overlay") );
      }
   },
   
   // override the WiresDelegate behavior which re-fires the event
   // add the connected class
   _onAddWire: function (e) {
      this.get('boundingBox').addClass(  this.getClassName("connected") );
   },
   
   // override the WiresDelegate behavior which re-fires the event
   // Remove the connected class if it has no more wires:
   _onRemoveWire: function (e) {
      if(this._wires.length === 0) {
         this.get('boundingBox').removeClass(  this.getClassName("connected") );
      }
   },
   
   /**
    * This function is a temporary test. I added the border width while traversing the DOM and
    * I calculated the offset to center the wire in the terminal just after its creation
    * @method getXY
    */
   getXY: function () {
      var container = this.get('parent'),
          layer = container.get('parent'),
          layerXY = layer.get('boundingBox').getXY(),
          absXY = this.get('contentBox').getXY();

      return [absXY[0]-layerXY[0] + 15/2 , absXY[1]-layerXY[1] + 15/2];
   }

}, {
   
   ATTRS: {
      
      /**
       * @attribute name
       */
      name: {
         value: null
      },
      
      /**
       * Vector direction at the terminal
       * (used by BezierWire ou Scissors)
       * @attribute dir
       */
      dir: {
         value: [0,1]
      },
      
      alignNode: {
         value: null
      },

      /**
       * @attribute offset
       */
      offset: {
         value: null,
         validator: function(val) {
            return this._validateXY(val);
         }
      }
      
   }
   
});


}, '@VERSION@', {
    "requires": [
        "widget",
        "widget-child",
        "widget-position",
        "widget-position-align",
        "wire-base",
        "wires-delegate",
        "terminal-dragedit",
        "terminal-scissors",
        "terminal-ddgroups"
    ],
    "skinnable": true
});
YUI.add('terminal-ddgroups', function (Y, NAME) {

/**
 * @module terminal-ddgroups
 */

/**
 * Extension to add "groups" labels when hovering the terminal
 * @class TerminalDDGroups
 * @constructor
 * @param {Object} config configuration object
 */
Y.TerminalDDGroups = function (config) {
   Y.after(this._renderUIgroups, this, "renderUI");
   Y.after(this._showOverlayDDGroups, this, "_showOverlay");
};

Y.TerminalDDGroups.ATTRS = {
   
   showGroups: {
      value: true
   }
   
};

Y.TerminalDDGroups.prototype = {
   
   _renderUIgroups: function () {
      if( this.get('editable') ) {
         this._renderTooltip();
      }
   },
   
   /**
    * create a persisting tooltip with the dd-groups class
    * @method _renderTooltip
    */
   _renderTooltip: function () {
      
      if(this.get('showGroups')) {
         
         this._ddGroupsOverlay = new Y.Overlay({
            render: this.get('boundingBox'),
            bodyContent: this.get('ddGroupsDrag').join(',')
         });

         this._ddGroupsOverlay.get('contentBox').addClass( this.getClassName("dd-groups") );

      }
      
   },

   _showOverlayDDGroups: function() {
      this._ddGroupsOverlay.align( this.get('contentBox'), [Y.WidgetPositionAlign.TC, Y.WidgetPositionAlign.BC] );
   }
   
};



}, '@VERSION@', {"requires": ["terminal-dragedit"]});
YUI.add('terminal-dragedit', function (Y, NAME) {

/**
 * @module terminal-dragedit
 */

/**
 * Extension which makes the wires editable
 * @class TerminalDragEdit
 * @constructor
 * @param {Object} config configuration object
 */
Y.TerminalDragEdit = function (config) {

   Y.after(this._renderUIdragedit, this, "renderUI");
   Y.after(this._bindUIdragedit, this, "bindUI");
   var attrs = {
      "color":{value:"rgb(173,216,230)"},
      "weight":{value:4},
      "opacity":{value:1},
      "dashstyle":{value:"none"},
      "fill":{value:"rgb(255,255,255)"},
      "editwire-class": {value: Y.BezierWire}
   };
   this.addAttrs(attrs, config);   
};

Y.TerminalDragEdit.ATTRS = {

   /**
    * Sets the terminal editable
    * @attribute editable
    */
   editable: {
      value: true
   },
   
   /**
    * @attribute graphic
    */
   graphic: {
      value: null
   },
   
   /**
    * @attribute alwaysSrc
    */
   alwaysSrc: {
      value: false
   },


   ddGroupsDrag: {
      value: ['terminal']
   },

   ddGroupsDrop: {
    value: ['terminal']
   }
   
};

Y.TerminalDragEdit.prototype = {
   
   /**
    * @method _renderUIdragedit
    */
   _renderUIdragedit: function () {
      
      if( this.get('editable') ) {
         this.get('contentBox').addClass(  this.getClassName("editable") );
         
         // Make the contentBox draggable with a DDProxy
         var drag = new Y.DD.Drag({ 
            node: this.get('contentBox'),
            groups: this.get('ddGroupsDrag') //this.get('groups')
         }).plug(Y.Plugin.DDProxy, {
            cloneNode: true,
            moveOnEnd: false
         });
         
         this.drag = drag;
         
         // Create the Drop object
         var drop = new Y.DD.Drop({
            node: this.get('contentBox'),
            groups: this.get('ddGroupsDrop') //this.get('groups')
         });
         drop.terminal = this;
         this.drop = drop;
         
      }
      
   },
   
   /**
    * @method _bindUIdragedit
    */
   _bindUIdragedit: function () {
      var drag = this.drag;
      if(drag) {
         drag.on('drag:start',    this._onDragEditStart, this);
         drag.on('drag:drag',     this._onDragEditDrag, this);
         drag.on('drag:drophit',  this._onDragEditDrophit, this);
         drag.on('drag:dropmiss', this._onDragEditDropmiss, this);
         drag.on('drag:enter',    this._onDragEditEnter, this);
         drag.on('drag:exit',     this._onDragEditExit, this);
      }
   },
   
   /**
    * on drag start, create the wire between 2 fake terminals
    * @method _onDragEditStart
    */
   _onDragEditStart: function (ev) {
      // save the position
      this._editwireX = ev.pageX;
      this._editwireY = ev.pageY;
      
      var dir = this.get('dir');
      var that = this;
      
      if(!this.get('graphic')) {
         this.set('graphic', this.get('root').graphic);
      }


      var container = this.get('parent');
      var layer = container.get('parent');
      var offset = layer.get('boundingBox').getXY();
      
      this.drag.wire = this.get('graphic').addShape({
         
         type: this.get('editwire-class'),
         
         // TODO: customizable
         stroke: {
            weight: this.get('weight'),
           color: this.get('color'),
           opacity:this.get('opacity'),
           dashstyle:this.get('dashstyle'),
           fill:this.get('fill')
           },
           
           src: { 
              getXY: function () { return [ev.pageX - offset[0]  + 15 / 2, ev.pageY - offset[1] + 15 / 2]; }
           },
           tgt: { 
              getXY: function () { return [that._magnetX || (that._editwireX - offset[0] + 15 / 2),
                                           that._magnetY || (that._editwireY - offset[1] + 15 / 2)]; } 
           },

           srcDir: dir,
           tgtDir: [-dir[0],-dir[1]]

        });
      
      
   },
   
   /**
    * Update the position of the fake target and redraw the wire
    * @method _onDragEditDrag
    * @private
    */
   _onDragEditDrag: function (ev) {
      this._editwireX = ev.pageX;
      this._editwireY = ev.pageY;
      this.drag.wire._draw();
   },
   
   /**
    * on drop hit, set the wire src and tgt terminals
    * @method _onDragEditDrophit
    * @private
    */
   _onDragEditDrophit: function (ev) {

      if( this.isValidWireTerminal(ev.drop.terminal) ) {
         if(ev.drop.terminal.alwaysSrc) {
            this.drag.wire.set('src', ev.drop.terminal);
            this.drag.wire.set('tgt', this);
         } else {
            this.drag.wire.set('src', this);
            this.drag.wire.set('tgt', ev.drop.terminal);
         }
         
         // Remove the reference to this wire
         this.drag.wire = null;
         
         // Reset the magnet position
         this._magnetX = null;
         this._magnetY = null;
      } else {
         this.drag.wire.destroy();
      }
   },
   
   /**
    * on drop miss, destroy the wire
    * @method _onDragEditDropmiss
    */
   _onDragEditDropmiss: function (ev) {
      this.drag.wire.destroy();
      this.drag.wire = null;
   },
   
   /**
    * @method _onDragEditEnter
    */
   _onDragEditEnter: function (ev) {
      
         var pos = ev.drop.terminal.getXY();
         this._magnetX = pos[0];
         this._magnetY = pos[1];
         
         // TODO: this only works for Bezier...
         this.drag.wire.set('tgtDir', ev.drop.terminal.get('dir'));
      
   },
   
   /**
    * @method _onDragEditExit
    */
   _onDragEditExit: function (ev) {
      this._magnetX = null;
      this._magnetY = null;
   },
   
   /**
    * @method isValidWireTerminal
    */
   isValidWireTerminal: function (DDterminal) {
     if(this.get('parent') !== undefined && (this.get('parent').get('preventSelfWiring'))){
        if (DDterminal._parentNode._node == this._parentNode._node) {
         return false;
        } 
     }
      return true;
   },
   
   /**
    * @method destructor
    */
   destructor: function () {
      
      if(this.drag) {
         this.drag.destroy();
      }
      if(this.drop) {
         this.drop.destroy();
      }
   }
   
};



}, '@VERSION@', {"requires": ["bezier-wire", "dd-drop", "dd-drag", "dd-proxy"]});
YUI.add('terminal-input', function (Y, NAME) {

/**
 * @module terminal-input
 */

'use strict';

/**
 * Class that extends Terminal to differenciate Input/Output terminals
 * @class TerminalInput
 * @extends Terminal
 * @constructor
 * @param {Object} oConfigs The user configuration for the instance.
 */
Y.TerminalInput = Y.Base.create("terminal-input", Y.Terminal, [], {

  getClassName: function(n) {
    return "yui3-terminal-"+n;
  }

}, {
  ATTRS: {

    dir: {
      value: [-0.3, 0]
    },

    ddGroupsDrag: {
      value: ['input']
    },

    ddGroupsDrop: {
      value: ['output']
    }

    // TODO
    // nMaxWires: 1,

  }
});


}, '@VERSION@', {"requires": ["terminal"]});
YUI.add('terminal-output', function (Y, NAME) {

/**
 * @module terminal-output
 */

'use strict';

/**
 * Class that extends Terminal to differenciate Input/Output terminals
 * @class TerminalOutput
 * @extends Terminal
 * @constructor
 * @param {Object} oConfigs The user configuration for the instance.
 */
Y.TerminalOutput = Y.Base.create("terminal-output", Y.Terminal, [], {

  getClassName: function(n) {
    return "yui3-terminal-"+n;
  }

}, {
  ATTRS: {

    dir: {
      value: [0.3, 0]
    },

    ddGroupsDrag: {
      value: ['output']
    },

    ddGroupsDrop: {
      value: ['input']
    }

    // TODO
    // alwaysSrc: true

  }
});

}, '@VERSION@', {"requires": ["terminal"]});
YUI.add('terminal-scissors', function (Y, NAME) {

/**
 * @module terminal-scissors
 */

/**
 * @class TerminalScissors
 * @constructor
 * @param {Object} config configuration object
 */
Y.TerminalScissors = function (config) {
   
   Y.after(this._renderUIScissors, this, "renderUI");
   Y.after(this._bindUIScissors, this, "bindUI");
   
};

Y.TerminalScissors.ATTRS = {

   /**
    * @attribute dirNormed
    */
   dirNormed: {
      getter: function() {
         var dir = this.get('dir'),
             a = dir[0],
             b = dir[1],
             norm = Math.sqrt(a*a+b*b);
         return [dir[0]/norm, dir[1]/norm];
      }
   },

   /**
    * @attribute scissorsDistance
    */
   scissorsDistance: {
      value: 30
   }

};

Y.TerminalScissors.prototype = {
   
   /**
    * @method _renderUIScissors
    * @private
    */
   _renderUIScissors: function () {
      if( this.get('editable') ) {
         this._renderScissors();
      }
   },
   
   /**
    * @method _bindUIScissors
    * @private
    */
   _bindUIScissors: function () {
      if( this.get('editable') ) {
         this._scissorsOverlay.get('boundingBox').on('click', this.destroyWires, this);
      }
   },
   
   /**
    * @method _renderScissors
    * @private
    */
   _renderScissors: function () {
      this._scissorsOverlay = new Y.Overlay({});
      
      this._scissorsOverlay.get('contentBox').addClass( this.getClassName("scissors") );
      
      var refXY = this.get('xy'),
          normed_dir = this.get('dirNormed'),
          distance = this.get('scissorsDistance');

      this._scissorsOverlay.set('x', refXY[0]+normed_dir[0]*distance-8);
      this._scissorsOverlay.set('y', refXY[1]+normed_dir[1]*distance-8);
      
      this._scissorsOverlay.render( this.get('boundingBox') );
   }
   
};




}, '@VERSION@', {"requires": ["overlay"], "skinnable": true});
YUI.add('textarea-container', function (Y, NAME) {

/**
 * @module textarea-container
 */

/**
 * Form container for a single textarea field which is resizeable.
 * You still need to specify the "fields".
 * @class TextareaContainer
 * @extends Container
 * @constructor
 * @param {Object}   options  Configuration object (see properties)
 */

Y.TextareaContainer = Y.Base.create("textarea-container", Y.Container, [], {
   

   SERIALIZABLE_ATTRS: function() {
      return Y.TextareaContainer.superclass.SERIALIZABLE_ATTRS.call(this).concat(['value']);
   },
   
   renderUI: function() {
      Y.TextareaContainer.superclass.renderUI.call(this);

      this.setStdModContent(Y.WidgetStdMod.BODY, "<textarea></textarea>");

      this._bodyNode = this.getStdModNode(Y.WidgetStdMod.BODY);
      this._textarea = this._bodyNode.one('textarea');
   },

   bindUI: function() {

      Y.TextareaContainer.superclass.bindUI.call(this);

      if(this.resize) {
         this.resize.after('resize:resize', this._afterResizeTextarea, this);
      }
   },

   _fillTextareaSize: function() {
      this.fillHeight(this._bodyNode);

      var region = this._bodyNode.get('region');

      this._textarea.setStyle('height', region.height);
      this._textarea.setStyle('width', region.width);
   },

   _afterResizeTextarea: function(e) {
      this._fillTextareaSize();
   },

   syncUI: function() {
      Y.TextareaContainer.superclass.syncUI.call(this);

      this.getStdModNode(Y.WidgetStdMod.BODY).one('textarea').set( this.get('value') );

      Y.later(0, this, function() {
         this._fillTextareaSize();
      });
   }
   
}, {
   
   ATTRS: {
      
      /**
       * Value of the textarea
       * @attribute value
       */
      value: {

         getter: function () {
            return this.getStdModNode(Y.WidgetStdMod.BODY).one('textarea').get('value');
         },
         
         setter: function (value) {
            this.getStdModNode(Y.WidgetStdMod.BODY).one('textarea').set('value', value);
         }
      }
      
   }
   
});



}, '@VERSION@', {"requires": ["container"]});
YUI.add('widget-icons', function (Y, NAME) {

/**
 * @module widget-icons
 */

/**
 * @class WidgetIcons
 * @constructor
 * @param {Object} config configuration object
 */
Y.WidgetIcons = function (config) {
   Y.after(this._renderUIicons, this, "renderUI");
};

Y.WidgetIcons.ATTRS = {
   
   /**
    * Set of icons
    * @attribute icons
    */
   icons: {
      value: []
   }
   
};

Y.WidgetIcons.prototype = {
   
   _renderUIicons: function () {
      
      /*var p = this.get('contentBox'),
          that = this;*/
          
      Y.Array.each( this.get('icons'), Y.bind(this._renderUIicon, this));
      
   },

   _renderUIicon: function(icon) {
      var i = Y.Node.create('<span class="yui3-widget-icons-icon '+this.getClassName('icon')+' '+icon.className+'" title="'+icon.title+'"></span>');
      i.on('click', Y.bind(this[icon.click], this) );
      i.appendTo( this.get('contentBox') );
   }
   
};



}, '@VERSION@', {"requires": [], "skinnable": true});
YUI.add('wire-base', function (Y, NAME) {

/**
 * @module wire-base
 */

/**
 * The wire widget
 * The wire is drawn between "src" and "tgt" (so they might be directional).
 *
 * "src" and "tgt" MUST have a "getXY" function
 *
 * "src" and "tgt" MAY additionnaly have the "addWire", "removeWire" methods.
 * Those methods are designed to be used through the Y.WiringsDelegate extension,
 * which provide basic list-handling on wires.
 *
 * @class WireBase
 * @extends Path
 * @param {Object} oConfigs The user configuration for the instance.
 */
Y.WireBase = function (config) {
   Y.WireBase.superclass.constructor.apply(this, arguments);
};

Y.WireBase.NAME = "wirebase";

Y.extend(Y.WireBase, Y.Path, {
   
   /**
    * Notify the WiresDeletates through addWire
    * @method initializer
    */
   initializer: function () {
      
      Y.WireBase.superclass.initializer.apply(this, arguments);
      
      var src = this.get('src'), tgt = this.get('tgt');
      
      if(src && src.get) {
         this.set('srcDir', src.get('dir') );
      }
      
      if(tgt && tgt.get) {
         this.set('tgtDir', tgt.get('dir') );
      }
      
      if(src && Y.Lang.isFunction (src.addWire) ) {
         src.addWire(this);
      }
      if(tgt && Y.Lang.isFunction (tgt.addWire) ) {
         tgt.addWire(this);
      }
      
   },
   
   
   /**
    * @method bindUI
    */
   bindUI: function () {
      Y.ArrowWire.superclass.bindUI.call(this);
      
      //this.after("bezierTangentNormChange", this._afterChangeRedraw, this);
      
      this.on('srcChange', function (e) {
         this.set('srcDir', e.newVal.get('dir') );
      }, this);
      
      this.on('tgtChange', function (e) {
         this.set('tgtDir', e.newVal.get('dir') );
      }, this);
      
   },
   
   
   /**
    * call removeWire on WiringsDelegate
    * @method destroy
    */
   destroy: function () {
      
      Y.WireBase.superclass.destroy.apply(this, arguments);
      
      var src = this.get('src'), tgt = this.get('tgt');
      
      if(src && Y.Lang.isFunction (src.removeWire) ) {
         src.removeWire(this);
      }
      if(tgt && Y.Lang.isFunction (tgt.removeWire) ) {
         tgt.removeWire(this);
      }
   },
   
   /**
    * Drawing method. Meant to be overriden by a plugin
    * @method _draw
    * @private
    */
   _draw: function () {
      //throw new Error("Y.Wire has no draw method. Consider using a plugin such as 'bezier-wire' in your YUI.use statement");
   },
   
   getOtherTerminal: function (term) {
      return (term === this.get('src')) ? this.get('tgt') : this.get('src');
   },
   
   // TODO:
   //SERIALIZABLE_ATTRS: ["src","tgt"],

   toJSON: function () {
      return {};
   }
   
});


Y.WireBase.ATTRS = Y.merge(Y.Path.ATTRS, {
   
   /**
    * @attribute src
    */
   src: {
      value: null,
      setter: function (val) {
         //console.log("src setter", val, this);
         
         // remove this wire from the list of the previous src/tgt item
         // TODO: prev value
         /*if(e.prevVal && Y.Lang.isFunction (e.prevVal.removeWire) ) {
            e.prevVal.removeWire(this);
         }*/
         
         if(val && Y.Lang.isFunction (val.addWire) ) {
            val.addWire(this);
         }
         
         return val;
      }
   },
   
   /**
    * @attribute tgt
    */
   tgt: {
      value: null,
      setter: function (val) {
         //console.log("tgt setter", val, this);
         
         
         // remove this wire from the list of the previous src/tgt item
         // TODO: prev value
         /*if(e.prevVal && Y.Lang.isFunction (e.prevVal.removeWire) ) {
            e.prevVal.removeWire(this);
         }*/
         
         
         if(val && Y.Lang.isFunction (val.addWire) ) {
            val.addWire(this);
         }
         
         return val;
      }
   },
   
   /**
    * @attribute srcDir
    * @type Array
    * @default [1,0]
    */
   srcDir: {
      validator: Y.Lang.isArray,
      value: [1,0]
   },
   
   /**
    * @attribute tgtDir
    * @type Array
    * @default -srcDir
    */
   tgtDir: {
      validator: Y.Lang.isArray,
      valueFn: function () {
         var d = this.get('srcDir');
         return [-d[0],-d[1]];
      }
   }
   
});


}, '@VERSION@', {"requires": ["graphics"], "skinnable": true});
YUI.add('wireit-app', function (Y, NAME) {


// -- LocalStorageSync ---------------------------------------------------------------------
// Saves WiringModel
function LocalStorageSync(key) {
    var localStorage;

    if (!key) {
        Y.error('No storage key specified.');
    }

    if (Y.config.win.localStorage) {
        localStorage = Y.config.win.localStorage;
    }

    // Try to retrieve existing data from localStorage, if there is any.
    // Otherwise, initialize `data` to an empty object.
    var data = Y.JSON.parse((localStorage && localStorage.getItem(key)) || '{}');

    // Delete a model with the specified id.
    function destroy(id) {
        var modelHash;

        if ((modelHash = data[id])) {
            delete data[id];
            save();
        }

        return modelHash;
    }

    // Generate a unique id to assign to a newly-created model.
    function generateId() {
        var id = '',
            i  = 4;

        while (i--) {
            id += (((1 + Math.random()) * 0x10000) | 0)
                    .toString(16).substring(1);
        }

        return id;
    }

    // Loads a model with the specified id. This method is a little tricky,
    // since it handles loading for both individual models and for an entire
    // model list.
    //
    // If an id is specified, then it loads a single model. If no id is
    // specified then it loads an array of all models. This allows the same sync
    // layer to be used for both the TodoModel and TodoList classes.
    function get(id) {
        return id ? data[id] : Y.Object.values(data);
    }

    // Saves the entire `data` object to localStorage.
    function save() {
        localStorage && localStorage.setItem(key, Y.JSON.stringify(data));
    }

    // Sets the id attribute of the specified model (generating a new id if
    // necessary), then saves it to localStorage.
    function set(model) {
        var hash        = model.toJSON(),
            idAttribute = model.idAttribute;

        if (!Y.Lang.isValue(hash[idAttribute])) {
            hash[idAttribute] = generateId();
        }

        data[hash[idAttribute]] = hash;
        save();

        return hash;
    }

    // Returns a `sync()` function that can be used with either a Model or a
    // ModelList instance.
    return function (action, options, callback) {
        // `this` refers to the Model or ModelList instance to which this sync
        // method is attached.
        var isModel = Y.Model && this instanceof Y.Model;

        switch (action) {
        case 'create': // intentional fallthru
        case 'update':
            callback(null, set(this));
            return;

        case 'read':
            callback(null, get(isModel && this.get('id')));
            return;

        case 'delete':
            callback(null, destroy(isModel && this.get('id')));
            return;
        }
    };
}


// -- WiringModel ---------------------------------------------------------------------
Y.WiringModel = Y.Base.create('wiringModel', Y.Model, [], {
   sync: LocalStorageSync('wireit-app')
}, {
   ATTRS: {
      id: {value: null},
      name       : {value: ''},
      containers   : {value: []},
      description: {value: ''},
      wires   : {value: []}
   }
});


// -- WiringModelList ---------------------------------------------------------------------

Y.WiringModelList = Y.Base.create('wiringModelList', Y.ModelList, [], {
   sync: LocalStorageSync('wireit-app'),
    model    : Y.WiringModel
});

Y.WiringListView = Y.Base.create('wiringListView', Y.View, [], {
   
   template: Y.Handlebars.compile(Y.one('#t-wiring-list').getContent()),
   
   /*initializer: function () {
   },*/
   
   render: function () {
      var content = this.template({wirings: this.get('modelList').toJSON() });
      this.get('container').setContent(content);
      return this;
   }
});



// -- ContainerType ---------------------------------------------------------------------
Y.ContainerType = Y.Base.create('containerModel', Y.Model, [], {
   // The `id` attribute for this Model will be an alias for `name`.
   idAttribute: 'name'
}, {
   ATTRS: {
      name       : {value: null},
      description: {value: null},
      config   : {value: null}
   }
});

// -- ContainerTypeList -----------------------------------------------------------------
Y.ContainerTypeList = Y.Base.create('containerTypeList', Y.ModelList, [], {
   model: Y.ContainerType
});

// -- Editor View ------------------------------------------------------------
Y.EditorView = Y.Base.create('editorView', Y.View, [], {
   
   template: Y.Handlebars.compile(Y.one('#t-editor').getContent()),
   
   events: {
      '#wiring-save-btn': {click: 'saveWiring'}
   },
   
   render: function () {
      
      var content = this.template({
         containerTypes: this.get('containerTypes').toJSON()
      });
      this.get('container').setContent(content);
      
      
      // Make items draggable to the layer
      var that = this;
      this.get('container').all('.containerType-name').each(function (node) {
         
         var drag = new Y.DD.Drag({ 
            node: node,
            groups: ['containerType']
         }).plug(Y.Plugin.DDProxy, {
            cloneNode: true,
            moveOnEnd: false
         });
         drag._containerTypeName = node._node.attributes["app-container-name"].value; //node._node.innerHTML;
         
         // On drom, add it to the layer
         drag.on('drag:drophit',  function (ev) {
            var p = that.layer.get('boundingBox').getXY();
            that._addContainerFromName(ev.drag._containerTypeName, {
               x: ev.drag.lastXY[0] - p[0],
               y: ev.drag.lastXY[1] - p[1]
            });
         }, this);
         
         
      });
      
      this._renderLayer();
      
      return this;
   },
   
   _renderLayer: function () {
      
      this.layer = new Y.Layer({
         height: 500
      });
      
      // Create the Drop object
      var drop = new Y.DD.Drop({
         node: this.layer.get('contentBox'),
         groups: ['containerType']
      });
      
      this.layer.render( this.get('container').one('#layer-container') );

      var wiring = this.get('model');
      if(wiring) {
         this.setWiring( wiring );
      }
      
   },
   
   saveWiring: function (e) {
      var o = {
         name: Y.one('#wiring-name').get('value') || 'Unnamed'
      };
      
      // Children are containers
      o.containers = [];
      Y.Array.each(this.layer._items, function (item) {
         o.containers.push({
            containerType: item.containerTypeName,
            config: item.toJSON()
         });
      });

      // Wires:
      o.wires = [];
      var layer = this.layer;
      Y.Array.each(this.layer._wires, function (wire) {
         
         var src = wire.get('src');
         var tgt = wire.get('tgt');
         
         o.wires.push( {
            src: { container: layer._items.indexOf( src.get('parent') ), terminal: src.get('name') },
            tgt: { container: layer._items.indexOf( tgt.get('parent') ), terminal: tgt.get('name') },
            config: wire.toJSON()
         });
      });
      
      
      if( this.get('model') ) {
         this.get('model').setAttrs(o);
      }
      else {
         this.set('model', new Y.WiringModel(o) );
      }
      
      this.get('model').save();
      
      // TODO: add only one message
      var s = Y.Node.create('<div class="alert-message bg-warning" style="width: 300px; z-index: 10001;"><p>Saved !</p></div>').appendTo(document.body);
      var anim = new Y.Anim({
          node: s,
          duration: 0.5,
          easing: Y.Easing.easeOut,
         from: { xy: [400, -50] },
         to: { xy: [400, 2] }
      });
      anim.on('end', function () {
         Y.later(1000, this, function () {
            (new Y.Anim({
                node: s,
                duration: 0.5,
                easing: Y.Easing.easeOut,
               to: { xy: [400, -50] }
            })).run();
         });
      });
      anim.run();
      
      
   },
   
   setWiring: function (wiring) {
      
      var that = this,
          layer = this.layer;

      Y.Array.each( wiring.get('containers'), function (container) {
         
         that._addContainerFromName(container.containerType,  container.config);
         
         Y.on('available', function (el) {
            Y.one('#wiring-name').set('value', wiring.get('name') );
         }, '#wiring-name');
         
      });


      Y.Array.each( wiring.get('wires'), function (wire) {

         // prevent bad configs...
         if(!wire.src || !wire.tgt) return;
         
         var srcContainer = layer.item(wire.src.container),
             srcTerminal = srcContainer.getTerminal(wire.src.terminal),
         
             tgtContainer = layer.item(wire.tgt.container),
             tgtTerminal = tgtContainer.getTerminal(wire.tgt.terminal);
         
         // TODO: wire.config;
         var w = layer.graphic.addShape({
            type: Y.BezierWire,
            stroke: {
                weight: 4,
                color: "rgb(173,216,230)" 
            },

            src: srcTerminal,
            tgt: tgtTerminal

         });
         
      });
      
      // TODO: this is awful ! But we need to wait for everything to render & position
      Y.later(200, this, function () {
         layer.redrawAllWires();
      });
      
   },
   
   _addContainerFromName: function (containerTypeName, containerConfig) {
      var containerType = this.get('containerTypes').getById(containerTypeName);
      var containerConf = Y.mix({}, containerType.get('config'));
      containerConf = Y.mix(containerConf, containerConfig);
      this.layer.add(containerConf);
      var container =  this.layer.item(this.layer.size()-1);
      container.containerTypeName = containerTypeName;
   }
   
}, {
   ATTRS: {
      containerTypes: {
         value: null
      }
   }
});
/**
 * @module wireit-app
 */



// -- WireIt App ---------------------------------------------------------
Y.WireItApp = new Y.Base.create('contributorsApp', Y.App, [], {
   
   views: {
      editorPage: {
         type: Y.EditorView
      },
      wiringListPage: {
         type: Y.WiringListView
      }
   },
   
   initializer: function () {
      
      // show indication that the app is busy loading data.
      this.on('navigate', this.indicateLoading);
      
      this.once('ready', function (e) {
         if (this.hasRoute(this.getPath())) {
            this.dispatch();
         } else {
            this.showWiringListPage();
         }
      });
   },
   
   // -- Event Handlers -------------------------------------------------------
   
   indicateLoading: function (e) {
      this.get('activeView').get('container').addClass('loading');
   },
   
   // -- Route Handlers -------------------------------------------------------
   
   handleWiring: function (req, res, next) {
      var wiringId = req.params.wiring,
         wirings = this.get('modelList'),
         wiring = wirings.getById(wiringId);
      
      this.set('wiring', wiring);
      
      next();
   },

   showEditorPage: function () {
      this.showView('editorPage', {
         containerTypes: this.get('containerTypes'),
         wirings: this.get('modelList'),
         model: this.get('wiring')
      });
   },
   
   blankEditorPage: function () {
      this.showView('editorPage', {
         containerTypes: this.get('containerTypes'),
         wirings: this.get('modelList'),
         model: null
      });
   },
   
   showWiringListPage: function () {
      
      //this.get('modelList').load();
      
      var wirings = new Y.WiringModelList();
      wirings.load();
      this.set('modelList', wirings);
      
      this.showView('wiringListPage', {
         modelList: this.get('modelList')
      });
   }

}, {
   ATTRS: {
      
      containerTypes: {
         value: new Y.ContainerTypeList()
      },
      
      modelList: {
         value: new Y.WiringModelList()
      },
      
      wiring: {
         value: null
      },
      
      routes: {
         value: [
            {path: '/', callback: 'showWiringListPage'},
            {path: '/wirings/:wiring/*', callback: 'handleWiring'},
            {path: '/wirings/:wiring/edit', callback: 'showEditorPage'},
            {path: '/wirings/new', callback: 'blankEditorPage'}
         ]
      }
   }
});



}, '@VERSION@', {"requires": ["app", "handlebars", "model", "model-list", "json", "view", "layer", "bezier-wire", "anim"]});
YUI.add('wires-delegate', function (Y, NAME) {

/**
 * @module wires-delegate
 */

/**
 * WiresDelegate is an extension for Widgets to manipulate a list of wires.
 *
 * The WidgetParent/WidgetChild relationship isn't sufficient
 * because wires have 2 parents, so we use this extension instead of WidgetParent
 *
 * @class WiresDelegate
 * @constructor
 * @param {Object} config configuration object
 */
Y.WiresDelegate = function () {
   
   this._wires = [];
   
   this.publish('addWire');
   
   this.publish('removeWire');
   
   // Bubble events from terminals
   this.on('*:addWire', this._onAddWire, this);
   this.on('*:removeWire', this._onRemoveWire, this);
   
};

Y.WiresDelegate.ATTRS = {};

Y.WiresDelegate.prototype = {
   
   _wireFromEvent: function(e) {
      var w = e;
      while(!!w._event) { w = w.details[0]; }
      return w;
   },

   _onAddWire: function (e) {
      this.addWire( this._wireFromEvent(e) );
   },
   
   _onRemoveWire: function (e) {
      this.removeWire( this._wireFromEvent(e) );
   },
   
   /**
    * Add a wire to this terminal.
    * @method addWire
    * @param {Wire} wire Wire instance to add
    */
   addWire: function (wire) {
      var index = Y.Array.indexOf(this._wires, wire);
      if(index === -1) {
         this._wires.push(wire);
         this.fire('addWire', wire);
      }
   },
   
   /**
    * When a wire is destroyed
    * @method removeWire
    */
   removeWire: function (wire) {
      
      var index = Y.Array.indexOf(this._wires, wire),
          w, v;
      
      if( index !== -1 ) {
         
         // Compact the array
         w = this._wires;
         this._wires = [];
         v = this._wires;
         Y.Array.each(w,function (i) { if(i !== wire){ v.push(i); } });
         
         // Fire the event
         this.fire('removeWire', wire);
      }
      
   },
   
   /**
    * Remove all wires
    * @method destroyWires
    */
   destroyWires: function () {
      
      if(this._wires) {
         Y.Array.each(this._wires, function (w) {
            w.destroy();
         });
      }
   
   },
   
   /**
    * Returns a list of all the terminals connected to this terminal through its wires.
    * @method getConnected
    * @return  {Array}  List of all connected terminals
    */
   getConnected: function () {
      var list = [], i, n;
      if(this._wires) {
         for(i = 0, n = this._wires.length ; i < n ; i++) {
            list.push(this._wires[i].getOtherTerminal(this));
         }
      }
      return list;
   },
   
   /**
    * Redraw all the wires connected to this terminal
    * @method redrawAllWires
    */
   redrawAllWires: function () {
      
      if(this._wires) {
         Y.Array.each(this._wires, function (w) {
            w._draw();
         });
      }
   },

   destructor: function () {
      this.destroyWires();
   }
   
};



}, '@VERSION@', {"requires": ["wire-base"]});
YUI.add('wireit-all', function (Y, NAME) {}, '@VERSION@');