text: AUI.add('aui-nested-list', function(A) {
  /**
   * The NestedList Utility - Full documentation coming soon.
   *
   * @module aui-nested-list
   */
  
  var L = A.Lang,
  	isString = L.isString,
  	isFunction = L.isFunction,
  
  	BLOCK = 'block',
  	BODY = 'body',
  	DD = 'dd',
  	DISPLAY = 'display',
  	DOWN = 'down',
  	DRAG_NODE = 'dragNode',
  	DROP_CONDITION = 'dropCondition',
  	DROP_CONTAINER = 'dropContainer',
  	DROP_ON = 'dropOn',
  	FLOAT = 'float',
  	HEIGHT = 'height',
  	HELPER = 'helper',
  	HIDDEN = 'hidden',
  	LEFT = 'left',
  	NESTED_LIST = 'nested-list',
  	NODE = 'node',
  	NODES = 'nodes',
  	NONE = 'none',
  	OFFSET_HEIGHT = 'offsetHeight',
  	PLACEHOLDER = 'placeholder',
  	PROXY = 'proxy',
  	PX = 'px',
  	RIGHT = 'right',
  	SORT_CONDITION = 'sortCondition',
  	UP = 'up',
  	VISIBILITY = 'visibility',
  	VISIBLE = 'visible',
  
  	DDM = A.DD.DDM,
  
  	isNodeList = function(v) {
  		return (v instanceof A.NodeList);
  	};
  
  /**
   * A base class for NestedList, providing:
   * <ul>
   *    <li>Widget Lifecycle (initializer, renderUI, bindUI, syncUI, destructor)</li>
   *    <li>Nested sortable utility</li>
   * </ul>
   *
   * Quick Example:<br/>
   *
   * <pre><code>var nl = new A.NestedList({
   *  	nodes: '#demo li',
   *  	dropContainer: 'ul.droppable'
   *  });
   * </code></pre>
   *
   * Check the list of <a href="NestedList.html#configattributes">Configuration Attributes</a> available for
   * NestedList.
   *
   * @param config {Object} Object literal specifying widget configuration properties.
   *
   * @class NestedList
   * @constructor
   * @extends Base
   */
  var NestedList = A.Component.create(
  	{
  		/**
  		 * Static property provides a string to identify the class.
  		 *
  		 * @property NestedList.NAME
  		 * @type String
  		 * @static
  		 */
  		NAME: NESTED_LIST,
  
  		/**
  		 * Static property used to define the default attribute
  		 * configuration for the NestedList.
  		 *
  		 * @property NestedList.ATTRS
  		 * @type Object
  		 * @static
  		 */
  		ATTRS: {
  			dd: {
  				value: null
  			},
  
  			dropCondition: {
  				value: function() {
  					return true;
  				},
  				setter: function(v) {
  					return A.bind(v, this);
  				},
  				validator: isFunction
  			},
  
  			dropContainer: {
  				value: function(event) {
  					var instance = this;
  					var drop = event.drop;
  					var dropNode = drop.get(NODE);
  					var dropOn = instance.get(DROP_ON);
  
  					return dropNode.one(dropOn);
  				},
  				validator: isFunction
  			},
  
  			dropOn: {
  				validator: isString
  			},
  
  			helper: {
  				value: null
  			},
  
  			nodes: {
  				setter: function(v) {
  					return this._setNodes(v);
  				}
  			},
  
  			placeholder: {
  				value: null
  			},
  
  			proxy: {
  				value: null,
  				setter: function(val) {
  					return A.merge(
  						{
  							moveOnEnd: false,
  							positionProxy: false
  						},
  						val || {}
  					);
  				}
  			},
  
  			sortCondition: {
  				value: function() {
  					return true;
  				},
  				setter: function(v) {
  					return A.bind(v, this);
  				},
  				validator: isFunction
  			}
  		},
  
  		EXTENDS: A.Base,
  
  		prototype: {
  			/**
  			 * Construction logic executed during NestedList instantiation. Lifecycle.
  			 *
  			 * @method initializer
  			 * @protected
  			 */
  			initializer: function() {
  				var instance = this;
  				var nodes = instance.get(NODES);
  
  				// drag & drop listeners
  				instance.on('drag:align', instance._onDragAlign);
  				instance.on('drag:end', instance._onDragEnd);
  				instance.on('drag:exit', instance._onDragExit);
  				instance.on('drag:mouseDown', instance._onDragMouseDown);
  				instance.on('drag:over', instance._onDragOver);
  				instance.on('drag:start', instance._onDragStart);
  
  				instance._createHelper();
  
  				if (nodes) {
  					instance.addAll(nodes);
  				}
  			},
  
  			/*
  			* Methods
  			*/
  			add: function(node) {
  				var instance = this;
  
  				instance._createDrag(node);
  			},
  
  			addAll: function(nodes) {
  				var instance = this;
  
  				nodes.each(function(node) {
  					instance.add(node);
  				});
  			},
  
  			_createDrag: function(node) {
  				var instance = this;
  				var helper = instance.get(HELPER);
  
  				if (!DDM.getDrag(node)) {
  					var dragOptions = {
  						bubbleTargets: instance,
  						node: node,
  						target: true
  					};
  
  					var proxyOptions = instance.get(PROXY);
  
  					if (helper) {
  						proxyOptions.borderStyle = null;
  					}
  
  					// creating delayed drag instance
  					var dd = new A.DD.Drag(
  						A.mix(dragOptions, instance.get(DD))
  					)
  					.plug(A.Plugin.DDProxy, proxyOptions);
  				}
  			},
  
  			_createHelper: function() {
  				var instance = this;
  				var helper = instance.get(HELPER);
  
  				if (helper) {
  					// append helper to the body
  					A.one(BODY).append( helper.hide() );
  
  					instance.set(HELPER, helper);
  				}
  			},
  
  			_updatePlaceholder: function(event, cancelAppend) {
  				var instance = this;
  				var drag = event.target;
  				var drop = event.drop;
  				var dragNode = drag.get(NODE);
  				var dropNode = drop.get(NODE);
  				var dropOn = instance.get(DROP_ON);
  
  				var dropContainer = instance.get(DROP_CONTAINER);
  
  				if (dropContainer) {
  					var container = dropContainer.apply(instance, arguments);
  				}
  
  				var floating = false;
  				var xDirection = instance.XDirection;
  				var yDirection = instance.YDirection;
  
  				if (dropNode.getStyle(FLOAT) != NONE) {
  					floating = true;
  				}
  
  				var placeholder = instance.get(PLACEHOLDER);
  
  				if (!placeholder) {
  					// if no placeholder use the dragNode instead
  					placeholder = dragNode;
  				}
  
  				if (!placeholder.contains(dropNode)) {
  					// check for the user dropCondition
  					var dropCondition = instance.get(DROP_CONDITION);
  
  					// if there is a container waiting for nodes to be appended it's priority
  					if (container && !cancelAppend && dropCondition(event)) {
  						// this checking avoid the parent bubbling drag:over
  						if (!container.contains(placeholder) &&
  							!placeholder.contains(container)) {
  								// append placeholder on the found container
  								container.append(placeholder);
  						}
  					}
  					// otherwise, check if it's floating and the xDirection
  					// or if it's not floating and the yDirection
  					else {
  						if (floating && (xDirection == LEFT) || !floating && (yDirection == UP)) {
  							// LEFT or UP directions means to place the placeholder before
  							dropNode.placeBefore(placeholder);
  						}
  						else {
  							// RIGHT or DOWN directions means to place the placeholder after
  							dropNode.placeAfter(placeholder);
  						}
  					}
  				}
  			},
  
  			/*
  			* Listeners
  			*/
  			_onDragAlign: function(event) {
  				var instance = this;
  				var lastX = instance.lastX;
  				var lastY = instance.lastY;
  				var xy = event.target.lastXY;
  
  				var x = xy[0];
  				var y = xy[1];
  
  				// if the y change
  				if (y != lastY) {
  					// set the drag vertical direction
  					instance.YDirection = (y < lastY) ? UP : DOWN;
  				}
  
  				// if the x change
  				if (x != lastX) {
  					// set the drag horizontal direction
  					instance.XDirection = (x < lastX) ? LEFT : RIGHT;
  				}
  
  				instance.lastX = x;
  				instance.lastY = y;
  			},
  
  			_onDragEnd: function(event) {
  				var instance = this;
  				var drag = event.target;
  				var dragNode = drag.get(NODE);
  				var placeholder = instance.get(PLACEHOLDER);
  
  				if (placeholder) {
  					dragNode.show();
  					placeholder.hide();
  
  					if (!dragNode.contains(placeholder)) {
  						// position dragNode after the placeholder
  						placeholder.placeAfter(dragNode);
  					}
  				}
  			},
  
  			_onDragExit: function(event) {
  				var instance = this;
  				var sortCondition = instance.get(SORT_CONDITION);
  
  				if (sortCondition(event)) {
  					instance._updatePlaceholder(event, true);
  				}
  			},
  
  			_onDragMouseDown: function(event) {
  				var instance = this;
  				var drag = event.target;
  				var helper = instance.get(HELPER);
  
  				if (helper) {
  					// update the DRAG_NODE with the new helper
  					drag.set(DRAG_NODE, helper);
  				}
  			},
  
  			_onDragStart: function(event) {
  		 		var instance = this;
  				var drag = event.target;
  				var node = drag.get(NODE);
  				var helper = instance.get(HELPER);
  				var placeholder = instance.get(PLACEHOLDER);
  
  				if (placeholder) {
  					// update placeholder height
  					placeholder.setStyle(
  						HEIGHT,
  						node.get(OFFSET_HEIGHT) + PX
  					);
  
  					node.hide();
  					placeholder.show();
  					// position placeholder after the node
  					node.placeAfter(placeholder);
  
  					if (helper) {
  						// show helper, we need display block here, yui dd hide it with display none
  						helper.setStyles({
  							display: BLOCK,
  							visibility: VISIBLE
  						}).show();
  					}
  				}
  		 	},
  
  			_onDragOver: function(event) {
  				var instance = this;
  				var sortCondition = instance.get(SORT_CONDITION);
  
  				if (sortCondition(event)) {
  					instance._updatePlaceholder(event);
  				}
  			},
  
  			/*
  			* Setters
  			*/
  			_setNodes: function(v) {
  				var instance = this;
  
  				if (isNodeList(v)) {
  					return v;
  				}
  				else if (isString(v)) {
  					return A.all(v);
  				}
  
  				return new A.NodeList([v]);
  			}
  		}
  	}
  );
  
  A.NestedList = NestedList;
  
  }, '@VERSION@' ,{skinnable:false, requires:['aui-base','dd-drag','dd-drop','dd-proxy']});
  