You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@roller.apache.org by gm...@apache.org on 2014/07/11 18:23:29 UTC

svn commit: r1609737 [13/18] - in /roller/trunk/app/src/main/webapp: WEB-INF/jsps/editor/ WEB-INF/jsps/tiles/ roller-ui/yui3/arraylist/ roller-ui/yui3/assets/ roller-ui/yui3/assets/skins/ roller-ui/yui3/assets/skins/sam/ roller-ui/yui3/async-queue/ rol...

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/node-focusmanager/node-focusmanager.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/node-focusmanager/node-focusmanager.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/node-focusmanager/node-focusmanager.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/node-focusmanager/node-focusmanager.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,1076 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('node-focusmanager', function (Y, NAME) {
+
+/**
+* <p>The Focus Manager Node Plugin makes it easy to manage focus among
+* a Node's descendants.  Primarily intended to help with widget development,
+* the Focus Manager Node Plugin can be used to improve the keyboard
+* accessibility of widgets.</p>
+*
+* <p>
+* When designing widgets that manage a set of descendant controls (i.e. buttons
+* in a toolbar, tabs in a tablist, menuitems in a menu, etc.) it is important to
+* limit the number of descendants in the browser's default tab flow.  The fewer
+* number of descendants in the default tab flow, the easier it is for keyboard
+* users to navigate between widgets by pressing the tab key.  When a widget has
+* focus it should provide a set of shortcut keys (typically the arrow keys)
+* to move focus among its descendants.
+* </p>
+*
+* <p>
+* To this end, the Focus Manager Node Plugin makes it easy to define a Node's
+* focusable descendants, define which descendant should be in the default tab
+* flow, and define the keys that move focus among each descendant.
+* Additionally, as the CSS
+* <a href="http://www.w3.org/TR/CSS21/selector.html#x38"><code>:focus</code></a>
+* pseudo class is not supported on all elements in all
+* <a href="http://developer.yahoo.com/yui/articles/gbs/">A-Grade browsers</a>,
+* the Focus Manager Node Plugin provides an easy, cross-browser means of
+* styling focus.
+* </p>
+*
+
+DEPRECATED: The FocusManager Node Plugin has been deprecated as of YUI 3.9.0. This module will be removed from the library in a future version. If you require functionality similar to the one provided by this  module, consider taking a look at the various modules in the YUI Gallery <http://yuilibrary.com/gallery/>.
+
+* @module node-focusmanager
+* @deprecated 3.9.0
+*/
+
+	//	Frequently used strings
+
+var ACTIVE_DESCENDANT = "activeDescendant",
+	ID = "id",
+	DISABLED = "disabled",
+	TAB_INDEX = "tabIndex",
+	FOCUSED = "focused",
+	FOCUS_CLASS = "focusClass",
+	CIRCULAR = "circular",
+	UI = "UI",
+	KEY = "key",
+	ACTIVE_DESCENDANT_CHANGE = ACTIVE_DESCENDANT + "Change",
+	HOST = "host",
+
+	//	Collection of keys that, when pressed, cause the browser viewport
+	//	to scroll.
+	scrollKeys = {
+		37: true,
+		38: true,
+		39: true,
+		40: true
+	},
+
+	clickableElements = {
+		"a": true,
+		"button": true,
+		"input": true,
+		"object": true
+	},
+
+	//	Library shortcuts
+
+	Lang = Y.Lang,
+ 	UA = Y.UA,
+
+	/**
+	* The NodeFocusManager class is a plugin for a Node instance.  The class is used
+	* via the <a href="Node.html#method_plug"><code>plug</code></a> method of Node
+	* and should not be instantiated directly.
+	* @namespace plugin
+	* @class NodeFocusManager
+	*/
+	NodeFocusManager = function () {
+
+		NodeFocusManager.superclass.constructor.apply(this, arguments);
+
+	};
+
+
+NodeFocusManager.ATTRS = {
+
+	/**
+	* Boolean indicating that one of the descendants is focused.
+	*
+	* @attribute focused
+	* @readOnly
+	* @default false
+	* @type boolean
+	*/
+	focused: {
+
+		value: false,
+		readOnly: true
+
+	},
+
+
+	/**
+	* String representing the CSS selector used to define the descendant Nodes
+	* whose focus should be managed.
+	*
+	* @attribute descendants
+	* @type Y.NodeList
+	*/
+	descendants: {
+
+		getter: function (value) {
+
+			return this.get(HOST).all(value);
+
+		}
+
+	},
+
+
+	/**
+	* <p>Node, or index of the Node, representing the descendant that is either
+	* focused or is focusable (<code>tabIndex</code> attribute is set to 0).
+	* The value cannot represent a disabled descendant Node.  Use a value of -1
+	* to remove all descendant Nodes from the default tab flow.
+	* If no value is specified, the active descendant will be inferred using
+	* the following criteria:</p>
+	* <ol>
+	* <li>Examining the <code>tabIndex</code> attribute of each descendant and
+	* using the first descendant whose <code>tabIndex</code> attribute is set
+	* to 0</li>
+	* <li>If no default can be inferred then the value is set to either 0 or
+	* the index of the first enabled descendant.</li>
+	* </ol>
+	*
+	* @attribute activeDescendant
+	* @type Number
+	*/
+	activeDescendant: {
+
+		setter: function (value) {
+
+			var isNumber = Lang.isNumber,
+				INVALID_VALUE = Y.Attribute.INVALID_VALUE,
+				descendantsMap = this._descendantsMap,
+				descendants = this._descendants,
+				nodeIndex,
+				returnValue,
+				oNode;
+
+
+			if (isNumber(value)) {
+				nodeIndex = value;
+				returnValue = nodeIndex;
+			}
+			else if ((value instanceof Y.Node) && descendantsMap) {
+
+				nodeIndex = descendantsMap[value.get(ID)];
+
+				if (isNumber(nodeIndex)) {
+					returnValue = nodeIndex;
+				}
+				else {
+
+					//	The user passed a reference to a Node that wasn't one
+					//	of the descendants.
+					returnValue = INVALID_VALUE;
+
+				}
+
+			}
+			else {
+				returnValue = INVALID_VALUE;
+			}
+
+
+			if (descendants) {
+
+				oNode = descendants.item(nodeIndex);
+
+				if (oNode && oNode.get("disabled")) {
+
+					//	Setting the "activeDescendant" attribute to the index
+					//	of a disabled descendant is invalid.
+					returnValue = INVALID_VALUE;
+
+				}
+
+			}
+
+
+			return returnValue;
+
+		}
+
+	},
+
+
+	/**
+	* Object literal representing the keys to be used to navigate between the
+	* next/previous descendant.  The format for the attribute's value is
+	* <code>{ next: "down:40", previous: "down:38" }</code>.  The value for the
+	* "next" and "previous" properties are used to attach
+	* <a href="event/#keylistener"><code>key</code></a> event listeners. See
+	* the <a href="event/#keylistener">Using the key Event</a> section of
+	* the Event documentation for more information on "key" event listeners.
+	*
+	* @attribute keys
+	* @type Object
+	*/
+	keys: {
+
+		value: {
+
+			next: null,
+			previous: null
+
+		}
+
+
+	},
+
+
+	/**
+	* String representing the name of class applied to the focused active
+	* descendant Node.  Can also be an object literal used to define both the
+	* class name, and the Node to which the class should be applied.  If using
+	* an object literal, the format is:
+	* <code>{ className: "focus", fn: myFunction }</code>.  The function
+	* referenced by the <code>fn</code> property in the object literal will be
+	* passed a reference to the currently focused active descendant Node.
+	*
+	* @attribute focusClass
+	* @type String|Object
+	*/
+	focusClass: { },
+
+
+	/**
+	* Boolean indicating if focus should be set to the first/last descendant
+	* when the end or beginning of the descendants has been reached.
+	*
+	* @attribute circular
+	* @type Boolean
+	* @default true
+	*/
+	circular: {
+		value: true
+	}
+
+};
+
+Y.extend(NodeFocusManager, Y.Plugin.Base, {
+
+	//	Protected properties
+
+	//	Boolean indicating if the NodeFocusManager is active.
+	_stopped: true,
+
+	//	NodeList representing the descendants selected via the
+	//	"descendants" attribute.
+	_descendants: null,
+
+	//	Object literal mapping the IDs of each descendant to its index in the
+	//	"_descendants" NodeList.
+	_descendantsMap: null,
+
+	//	Reference to the Node instance to which the focused class (defined
+	//	by the "focusClass" attribute) is currently applied.
+	_focusedNode: null,
+
+	//	Number representing the index of the last descendant Node.
+	_lastNodeIndex: 0,
+
+	//	Array of handles for event handlers used for a NodeFocusManager instance.
+	_eventHandlers: null,
+
+
+
+	//	Protected methods
+
+	/**
+	* @method _initDescendants
+	* @description Sets the <code>tabIndex</code> attribute of all of the
+	* descendants to -1, except the active descendant, whose
+	* <code>tabIndex</code> attribute is set to 0.
+	* @protected
+	*/
+	_initDescendants: function () {
+
+		var descendants = this.get("descendants"),
+			descendantsMap = {},
+			nFirstEnabled = -1,
+			nDescendants,
+			nActiveDescendant = this.get(ACTIVE_DESCENDANT),
+			oNode,
+			sID,
+			i = 0;
+
+
+
+		if (Lang.isUndefined(nActiveDescendant)) {
+			nActiveDescendant = -1;
+		}
+
+
+		if (descendants) {
+
+			nDescendants = descendants.size();
+
+
+            for (i = 0; i < nDescendants; i++) {
+
+                oNode = descendants.item(i);
+
+                if (nFirstEnabled === -1 && !oNode.get(DISABLED)) {
+                    nFirstEnabled = i;
+                }
+
+
+                //	If the user didn't specify a value for the
+                //	"activeDescendant" attribute try to infer it from
+                //	the markup.
+
+                //	Need to pass "2" when using "getAttribute" for IE to get
+                //	the attribute value as it is set in the markup.
+                //	Need to use "parseInt" because IE always returns the
+                //	value as a number, whereas all other browsers return
+                //	the attribute as a string when accessed
+                //	via "getAttribute".
+
+                if (nActiveDescendant < 0 &&
+                        parseInt(oNode.getAttribute(TAB_INDEX, 2), 10) === 0) {
+
+                    nActiveDescendant = i;
+
+                }
+
+                if (oNode) {
+                    oNode.set(TAB_INDEX, -1);
+                }
+
+                sID = oNode.get(ID);
+
+                if (!sID) {
+                    sID = Y.guid();
+                    oNode.set(ID, sID);
+                }
+
+                descendantsMap[sID] = i;
+
+            }
+
+
+            //	If the user didn't specify a value for the
+            //	"activeDescendant" attribute and no default value could be
+            //	determined from the markup, then default to 0.
+
+            if (nActiveDescendant < 0) {
+                nActiveDescendant = 0;
+            }
+
+
+            oNode = descendants.item(nActiveDescendant);
+
+            //	Check to make sure the active descendant isn't disabled,
+            //	and fall back to the first enabled descendant if it is.
+
+            if (!oNode || oNode.get(DISABLED)) {
+                oNode = descendants.item(nFirstEnabled);
+                nActiveDescendant = nFirstEnabled;
+            }
+
+            this._lastNodeIndex = nDescendants - 1;
+            this._descendants = descendants;
+            this._descendantsMap = descendantsMap;
+
+            this.set(ACTIVE_DESCENDANT, nActiveDescendant);
+
+            //	Need to set the "tabIndex" attribute here, since the
+            //	"activeDescendantChange" event handler used to manage
+            //	the setting of the "tabIndex" attribute isn't wired up yet.
+
+            if (oNode) {
+                oNode.set(TAB_INDEX, 0);
+            }
+
+		}
+
+	},
+
+
+	/**
+	* @method _isDescendant
+	* @description Determines if the specified Node instance is a descendant
+	* managed by the Focus Manager.
+	* @param node {Node} Node instance to be checked.
+	* @return {Boolean} Boolean indicating if the specified Node instance is a
+	* descendant managed by the Focus Manager.
+	* @protected
+	*/
+	_isDescendant: function (node) {
+
+		return (node.get(ID) in this._descendantsMap);
+
+	},
+
+
+	/**
+	* @method _removeFocusClass
+	* @description Removes the class name representing focus (as specified by
+	* the "focusClass" attribute) from the Node instance to which it is
+	* currently applied.
+	* @protected
+	*/
+	_removeFocusClass: function () {
+
+		var oFocusedNode = this._focusedNode,
+			focusClass = this.get(FOCUS_CLASS),
+			sClassName;
+
+		if (focusClass) {
+			sClassName = Lang.isString(focusClass) ?
+				focusClass : focusClass.className;
+		}
+
+		if (oFocusedNode && sClassName) {
+			oFocusedNode.removeClass(sClassName);
+		}
+
+	},
+
+
+	/**
+	* @method _detachKeyHandler
+	* @description Detaches the "key" event handlers used to support the "keys"
+	* attribute.
+	* @protected
+	*/
+	_detachKeyHandler: function () {
+
+		var prevKeyHandler = this._prevKeyHandler,
+			nextKeyHandler = this._nextKeyHandler;
+
+		if (prevKeyHandler) {
+			prevKeyHandler.detach();
+		}
+
+		if (nextKeyHandler) {
+			nextKeyHandler.detach();
+		}
+
+	},
+
+
+	/**
+	* @method _preventScroll
+	* @description Prevents the viewport from scolling when the user presses
+	* the up, down, left, or right key.
+	* @protected
+	*/
+	_preventScroll: function (event) {
+
+		if (scrollKeys[event.keyCode] && this._isDescendant(event.target)) {
+			event.preventDefault();
+		}
+
+	},
+
+
+	/**
+	* @method _fireClick
+	* @description Fires the click event if the enter key is pressed while
+	* focused on an HTML element that is not natively clickable.
+	* @protected
+	*/
+	_fireClick: function (event) {
+
+		var oTarget = event.target,
+			sNodeName = oTarget.get("nodeName").toLowerCase();
+
+		if (event.keyCode === 13 && (!clickableElements[sNodeName] ||
+				(sNodeName === "a" && !oTarget.getAttribute("href")))) {
+
+
+			oTarget.simulate("click");
+
+		}
+
+	},
+
+
+	/**
+	* @method _attachKeyHandler
+	* @description Attaches the "key" event handlers used to support the "keys"
+	* attribute.
+	* @protected
+	*/
+	_attachKeyHandler: function () {
+
+		this._detachKeyHandler();
+
+		var sNextKey = this.get("keys.next"),
+			sPrevKey = this.get("keys.previous"),
+			oNode = this.get(HOST),
+			aHandlers = this._eventHandlers;
+
+		if (sPrevKey) {
+ 			this._prevKeyHandler =
+				Y.on(KEY, Y.bind(this._focusPrevious, this), oNode, sPrevKey);
+		}
+
+		if (sNextKey) {
+ 			this._nextKeyHandler =
+				Y.on(KEY, Y.bind(this._focusNext, this), oNode, sNextKey);
+		}
+
+
+		//	In Opera it is necessary to call the "preventDefault" method in
+		//	response to the user pressing the arrow keys in order to prevent
+		//	the viewport from scrolling when the user is moving focus among
+		//	the focusable descendants.
+
+		if (UA.opera) {
+			aHandlers.push(oNode.on("keypress", this._preventScroll, this));
+		}
+
+
+		//	For all browsers except Opera: HTML elements that are not natively
+		//	focusable but made focusable via the tabIndex attribute don't
+		//	fire a click event when the user presses the enter key.  It is
+		//	possible to work around this problem by simplying dispatching a
+		//	click event in response to the user pressing the enter key.
+
+		if (!UA.opera) {
+			aHandlers.push(oNode.on("keypress", this._fireClick, this));
+		}
+
+	},
+
+
+	/**
+	* @method _detachEventHandlers
+	* @description Detaches all event handlers used by the Focus Manager.
+	* @protected
+	*/
+	_detachEventHandlers: function () {
+
+		this._detachKeyHandler();
+
+		var aHandlers = this._eventHandlers;
+
+		if (aHandlers) {
+
+			Y.Array.each(aHandlers, function (handle) {
+				handle.detach();
+			});
+
+			this._eventHandlers = null;
+
+		}
+
+	},
+
+
+	/**
+	* @method _detachEventHandlers
+	* @description Attaches all event handlers used by the Focus Manager.
+	* @protected
+	*/
+	_attachEventHandlers: function () {
+
+		var descendants = this._descendants,
+			aHandlers,
+			oDocument,
+			handle;
+
+		if (descendants && descendants.size()) {
+
+			aHandlers = this._eventHandlers || [];
+			oDocument = this.get(HOST).get("ownerDocument");
+
+
+			if (aHandlers.length === 0) {
+
+
+				aHandlers.push(oDocument.on("focus", this._onDocFocus, this));
+
+				aHandlers.push(oDocument.on("mousedown",
+					this._onDocMouseDown, this));
+
+				aHandlers.push(
+						this.after("keysChange", this._attachKeyHandler));
+
+				aHandlers.push(
+						this.after("descendantsChange", this._initDescendants));
+
+				aHandlers.push(
+						this.after(ACTIVE_DESCENDANT_CHANGE,
+								this._afterActiveDescendantChange));
+
+
+				//	For performance: defer attaching all key-related event
+				//	handlers until the first time one of the specified
+				//	descendants receives focus.
+
+				handle = this.after("focusedChange", Y.bind(function (event) {
+
+					if (event.newVal) {
+
+
+						this._attachKeyHandler();
+
+						//	Detach this "focusedChange" handler so that the
+						//	key-related handlers only get attached once.
+
+						handle.detach();
+
+					}
+
+				}, this));
+
+				aHandlers.push(handle);
+
+			}
+
+
+			this._eventHandlers = aHandlers;
+
+		}
+
+	},
+
+
+	//	Protected event handlers
+
+	/**
+	* @method _onDocMouseDown
+	* @description "mousedown" event handler for the owner document of the
+	* Focus Manager's Node.
+	* @protected
+	* @param event {Object} Object representing the DOM event.
+	*/
+	_onDocMouseDown: function (event) {
+
+		var oHost = this.get(HOST),
+			oTarget = event.target,
+			bChildNode = oHost.contains(oTarget),
+			node,
+
+			getFocusable = function (node) {
+
+				var returnVal = false;
+
+				if (!node.compareTo(oHost)) {
+
+					returnVal = this._isDescendant(node) ? node :
+									getFocusable.call(this, node.get("parentNode"));
+
+				}
+
+				return returnVal;
+
+			};
+
+
+		if (bChildNode) {
+
+			//	Check to make sure that the target isn't a child node of one
+			//	of the focusable descendants.
+
+			node = getFocusable.call(this, oTarget);
+
+			if (node) {
+				oTarget = node;
+			}
+			else if (!node && this.get(FOCUSED)) {
+
+				//	The target was a non-focusable descendant of the root
+				//	node, so the "focused" attribute should be set to false.
+
+	 			this._set(FOCUSED, false);
+	 			this._onDocFocus(event);
+
+			}
+
+		}
+
+
+		if (bChildNode && this._isDescendant(oTarget)) {
+
+			//	Fix general problem in Webkit: mousing down on a button or an
+			//	anchor element doesn't focus it.
+
+			//	For all browsers: makes sure that the descendant that
+			//	was the target of the mousedown event is now considered the
+			//	active descendant.
+
+			this.focus(oTarget);
+		}
+		else if (UA.webkit && this.get(FOCUSED) &&
+			(!bChildNode || (bChildNode && !this._isDescendant(oTarget)))) {
+
+			//	Fix for Webkit:
+
+			//	Document doesn't receive focus in Webkit when the user mouses
+			//	down on it, so the "focused" attribute won't get set to the
+			//	correct value.
+
+			//	The goal is to force a blur if the user moused down on
+			//	either: 1) A descendant node, but not one that managed by
+			//	the FocusManager, or 2) an element outside of the
+			//	FocusManager
+
+ 			this._set(FOCUSED, false);
+ 			this._onDocFocus(event);
+
+		}
+
+	},
+
+
+	/**
+	* @method _onDocFocus
+	* @description "focus" event handler for the owner document of the
+	* Focus Manager's Node.
+	* @protected
+	* @param event {Object} Object representing the DOM event.
+	*/
+	_onDocFocus: function (event) {
+
+		var oTarget = this._focusTarget || event.target,
+			bFocused = this.get(FOCUSED),
+			focusClass = this.get(FOCUS_CLASS),
+			oFocusedNode = this._focusedNode,
+			bInCollection;
+
+		if (this._focusTarget) {
+			this._focusTarget = null;
+		}
+
+
+		if (this.get(HOST).contains(oTarget)) {
+
+			//	The target is a descendant of the root Node.
+
+			bInCollection = this._isDescendant(oTarget);
+
+			if (!bFocused && bInCollection) {
+
+				//	The user has focused a focusable descendant.
+
+				bFocused = true;
+
+			}
+			else if (bFocused && !bInCollection) {
+
+				//	The user has focused a child of the root Node that is
+				//	not one of the descendants managed by this Focus Manager
+				//	so clear the currently focused descendant.
+
+				bFocused = false;
+
+			}
+
+		}
+		else {
+
+			// The target is some other node in the document.
+
+			bFocused = false;
+
+		}
+
+
+		if (focusClass) {
+
+			if (oFocusedNode && (!oFocusedNode.compareTo(oTarget) || !bFocused)) {
+				this._removeFocusClass();
+			}
+
+			if (bInCollection && bFocused) {
+
+				if (focusClass.fn) {
+					oTarget = focusClass.fn(oTarget);
+					oTarget.addClass(focusClass.className);
+				}
+				else {
+					oTarget.addClass(focusClass);
+				}
+
+				this._focusedNode = oTarget;
+
+			}
+
+		}
+
+
+		this._set(FOCUSED, bFocused);
+
+	},
+
+
+	/**
+	* @method _focusNext
+	* @description Keydown event handler that moves focus to the next
+	* enabled descendant.
+	* @protected
+	* @param event {Object} Object representing the DOM event.
+	* @param activeDescendant {Number} Number representing the index of the
+	* next descendant to be focused
+	*/
+	_focusNext: function (event, activeDescendant) {
+
+		var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT),
+			oNode;
+
+
+		if (this._isDescendant(event.target) &&
+			(nActiveDescendant <= this._lastNodeIndex)) {
+
+			nActiveDescendant = nActiveDescendant + 1;
+
+			if (nActiveDescendant === (this._lastNodeIndex + 1) &&
+				this.get(CIRCULAR)) {
+
+				nActiveDescendant = 0;
+
+			}
+
+			oNode = this._descendants.item(nActiveDescendant);
+
+            if (oNode) {
+
+                if (oNode.get("disabled")) {
+                    this._focusNext(event, nActiveDescendant);
+                }
+                else {
+                    this.focus(nActiveDescendant);
+                }
+
+            }
+
+		}
+
+		this._preventScroll(event);
+
+	},
+
+
+	/**
+	* @method _focusPrevious
+	* @description Keydown event handler that moves focus to the previous
+	* enabled descendant.
+	* @protected
+	* @param event {Object} Object representing the DOM event.
+	* @param activeDescendant {Number} Number representing the index of the
+	* next descendant to be focused.
+	*/
+	_focusPrevious: function (event, activeDescendant) {
+
+		var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT),
+			oNode;
+
+		if (this._isDescendant(event.target) && nActiveDescendant >= 0) {
+
+			nActiveDescendant = nActiveDescendant - 1;
+
+			if (nActiveDescendant === -1 && this.get(CIRCULAR)) {
+				nActiveDescendant = this._lastNodeIndex;
+			}
+
+            oNode = this._descendants.item(nActiveDescendant);
+
+            if (oNode) {
+
+                if (oNode.get("disabled")) {
+                    this._focusPrevious(event, nActiveDescendant);
+                }
+                else {
+                    this.focus(nActiveDescendant);
+                }
+
+            }
+
+		}
+
+		this._preventScroll(event);
+
+	},
+
+
+	/**
+	* @method _afterActiveDescendantChange
+	* @description afterChange event handler for the
+	* "activeDescendant" attribute.
+	* @protected
+	* @param event {Object} Object representing the change event.
+	*/
+	_afterActiveDescendantChange: function (event) {
+
+		var oNode = this._descendants.item(event.prevVal);
+
+		if (oNode) {
+			oNode.set(TAB_INDEX, -1);
+		}
+
+		oNode = this._descendants.item(event.newVal);
+
+		if (oNode) {
+			oNode.set(TAB_INDEX, 0);
+		}
+
+	},
+
+
+
+	//	Public methods
+
+    initializer: function (config) {
+		this.start();
+
+    },
+
+	destructor: function () {
+
+		this.stop();
+		this.get(HOST).focusManager = null;
+
+    },
+
+
+	/**
+	* @method focus
+	* @description Focuses the active descendant and sets the
+	* <code>focused</code> attribute to true.
+	* @param index {Number|Node} Optional. Number representing the index of the
+	* descendant to be set as the active descendant or Node instance
+	* representing the descendant to be set as the active descendant.
+	*/
+	focus: function (index) {
+
+		if (Lang.isUndefined(index)) {
+			index = this.get(ACTIVE_DESCENDANT);
+		}
+
+		this.set(ACTIVE_DESCENDANT, index, { src: UI });
+
+		var oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT));
+
+		if (oNode) {
+
+			oNode.focus();
+
+			//	In Opera focusing a <BUTTON> element programmatically
+			//	will result in the document-level focus event handler
+			//	"_onDocFocus" being called, resulting in the handler
+			//	incorrectly setting the "focused" Attribute to false.  To fix
+			//	this, set a flag ("_focusTarget") that the "_onDocFocus" method
+			//	can look for to properly handle this edge case.
+
+			if (UA.opera && oNode.get("nodeName").toLowerCase() === "button") {
+				this._focusTarget = oNode;
+			}
+
+		}
+
+	},
+
+
+	/**
+	* @method blur
+	* @description Blurs the current active descendant and sets the
+	* <code>focused</code> attribute to false.
+	*/
+	blur: function () {
+
+		var oNode;
+
+		if (this.get(FOCUSED)) {
+
+			oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT));
+
+			if (oNode) {
+
+				oNode.blur();
+
+				//	For Opera and Webkit:  Blurring an element in either browser
+				//	doesn't result in another element (such as the document)
+				//	being focused.  Therefore, the "_onDocFocus" method
+				//	responsible for managing the application and removal of the
+				//	focus indicator class name is never called.
+
+				this._removeFocusClass();
+
+			}
+
+			this._set(FOCUSED, false, { src: UI });
+		}
+
+	},
+
+
+	/**
+	* @method start
+	* @description Enables the Focus Manager.
+	*/
+	start: function () {
+
+		if (this._stopped) {
+
+			this._initDescendants();
+			this._attachEventHandlers();
+
+			this._stopped = false;
+
+		}
+
+	},
+
+
+	/**
+	* @method stop
+	* @description Disables the Focus Manager by detaching all event handlers.
+	*/
+	stop: function () {
+
+		if (!this._stopped) {
+
+			this._detachEventHandlers();
+
+			this._descendants = null;
+			this._focusedNode = null;
+			this._lastNodeIndex = 0;
+			this._stopped = true;
+
+		}
+
+	},
+
+
+	/**
+	* @method refresh
+	* @description Refreshes the Focus Manager's descendants by re-executing the
+	* CSS selector query specified by the <code>descendants</code> attribute.
+	*/
+	refresh: function () {
+
+		this._initDescendants();
+
+		if (!this._eventHandlers) {
+			this._attachEventHandlers();
+		}
+
+	}
+
+});
+
+
+NodeFocusManager.NAME = "nodeFocusManager";
+NodeFocusManager.NS = "focusManager";
+
+Y.namespace("Plugin");
+Y.Plugin.NodeFocusManager = NodeFocusManager;
+
+
+}, '3.17.2', {"requires": ["attribute", "node", "plugin", "node-event-simulate", "event-key", "event-focus"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/node-pluginhost/node-pluginhost-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/node-pluginhost/node-pluginhost-min.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/node-pluginhost/node-pluginhost-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/node-pluginhost/node-pluginhost-min.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,8 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add("node-pluginhost",function(e,t){e.Node.plug=function(){var t=e.Array(arguments);return t.unshift(e.Node),e.Plugin.Host.plug.apply(e.Base,t),e.Node},e.Node.unplug=function(){var t=e.Array(arguments);return t.unshift(e.Node),e.Plugin.Host.unplug.apply(e.Base,t),e.Node},e.mix(e.Node,e.Plugin.Host,!1,null,1),e.Object.each(e.Node._instances,function(t){e.Plugin.Host.apply(t)}),e.NodeList.prototype.plug=function(){var t=arguments;return e.NodeList.each(this,function(n){e.Node.prototype.plug.apply(e.one(n),t)}),this},e.NodeList.prototype.unplug=function(){var t=arguments;return e.NodeList.each(this,function(n){e.Node.prototype.unplug.apply(e.one(n),t)}),this}},"3.17.2",{requires:["node-base","pluginhost"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/node-pluginhost/node-pluginhost.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/node-pluginhost/node-pluginhost.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/node-pluginhost/node-pluginhost.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/node-pluginhost/node-pluginhost.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,98 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('node-pluginhost', function (Y, NAME) {
+
+/**
+ * @module node
+ * @submodule node-pluginhost
+ */
+
+/**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of Node by default).
+ *
+ * @method plug
+ * @static
+ * @for Node
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+Y.Node.plug = function() {
+    var args = Y.Array(arguments);
+    args.unshift(Y.Node);
+    Y.Plugin.Host.plug.apply(Y.Base, args);
+    return Y.Node;
+};
+
+/**
+ * Unregisters any class level plugins which have been registered by the Node
+ *
+ * @method unplug
+ * @static
+ *
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+Y.Node.unplug = function() {
+    var args = Y.Array(arguments);
+    args.unshift(Y.Node);
+    Y.Plugin.Host.unplug.apply(Y.Base, args);
+    return Y.Node;
+};
+
+Y.mix(Y.Node, Y.Plugin.Host, false, null, 1);
+
+// run PluginHost constructor on cached Node instances
+Y.Object.each(Y.Node._instances, function (node) {
+    Y.Plugin.Host.apply(node);
+});
+
+// allow batching of plug/unplug via NodeList
+// doesn't use NodeList.importMethod because we need real Nodes (not tmpNode)
+/**
+ * Adds a plugin to each node in the NodeList.
+ * This will instantiate the plugin and attach it to the configured namespace on each node
+ * @method plug
+ * @for NodeList
+ * @param P {Function | Object |Array} Accepts the plugin class, or an
+ * object with a "fn" property specifying the plugin class and
+ * a "cfg" property specifying the configuration for the Plugin.
+ * <p>
+ * Additionally an Array can also be passed in, with the above function or
+ * object values, allowing the user to add multiple plugins in a single call.
+ * </p>
+ * @param config (Optional) If the first argument is the plugin class, the second argument
+ * can be the configuration for the plugin.
+ * @chainable
+ */
+Y.NodeList.prototype.plug = function() {
+    var args = arguments;
+    Y.NodeList.each(this, function(node) {
+        Y.Node.prototype.plug.apply(Y.one(node), args);
+    });
+    return this;
+};
+
+/**
+ * Removes a plugin from all nodes in the NodeList. This will destroy the
+ * plugin instance and delete the namespace each node.
+ * @method unplug
+ * @for NodeList
+ * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided,
+ * all registered plugins are unplugged.
+ * @chainable
+ */
+Y.NodeList.prototype.unplug = function() {
+    var args = arguments;
+    Y.NodeList.each(this, function(node) {
+        Y.Node.prototype.unplug.apply(Y.one(node), args);
+    });
+    return this;
+};
+
+
+}, '3.17.2', {"requires": ["node-base", "pluginhost"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/node-screen/node-screen-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/node-screen/node-screen-min.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/node-screen/node-screen-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/node-screen/node-screen-min.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,8 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add("node-screen",function(e,t){e.each(["winWidth","winHeight","docWidth","docHeight","docScrollX","docScrollY"],function(t){e.Node.ATTRS[t]={getter:function(){var n=Array.prototype.slice.call(arguments);return n.unshift(e.Node.getDOMNode(this)),e.DOM[t].apply(this,n)}}}),e.Node.ATTRS.scrollLeft={getter:function(){var t=e.Node.getDOMNode(this);return"scrollLeft"in t?t.scrollLeft:e.DOM.docScrollX(t)},setter:function(t){var n=e.Node.getDOMNode(this);n&&("scrollLeft"in n?n.scrollLeft=t:(n.document||n.nodeType===9)&&e.DOM._getWin(n).scrollTo(t,e.DOM.docScrollY(n)))}},e.Node.ATTRS.scrollTop={getter:function(){var t=e.Node.getDOMNode(this);return"scrollTop"in t?t.scrollTop:e.DOM.docScrollY(t)},setter:function(t){var n=e.Node.getDOMNode(this);n&&("scrollTop"in n?n.scrollTop=t:(n.document||n.nodeType===9)&&e.DOM._getWin(n).scrollTo(e.DOM.docScrollX(n),t))}},e.Node.importMethod(e.DOM,["getXY","setXY","getX","setX","getY","setY","swapXY"]),e.Node.ATTRS.region={getter:function(){var t=this
 .getDOMNode(),n;return t&&!t.tagName&&t.nodeType===9&&(t=t.documentElement),e.DOM.isWindow(t)?n=e.DOM.viewportRegion(t):n=e.DOM.region(t),n}},e.Node.ATTRS.viewportRegion={getter:function(){return e.DOM.viewportRegion(e.Node.getDOMNode(this))}},e.Node.importMethod(e.DOM,"inViewportRegion"),e.Node.prototype.intersect=function(t,n){var r=e.Node.getDOMNode(this);return e.instanceOf(t,e.Node)&&(t=e.Node.getDOMNode(t)),e.DOM.intersect(r,t,n)},e.Node.prototype.inRegion=function(t,n,r){var i=e.Node.getDOMNode(this);return e.instanceOf(t,e.Node)&&(t=e.Node.getDOMNode(t)),e.DOM.inRegion(i,t,n,r)}},"3.17.2",{requires:["dom-screen","node-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/node-screen/node-screen.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/node-screen/node-screen.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/node-screen/node-screen.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/node-screen/node-screen.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,245 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('node-screen', function (Y, NAME) {
+
+/**
+ * Extended Node interface for managing regions and screen positioning.
+ * Adds support for positioning elements and normalizes window size and scroll detection.
+ * @module node
+ * @submodule node-screen
+ */
+
+// these are all "safe" returns, no wrapping required
+Y.each([
+    /**
+     * Returns the inner width of the viewport (exludes scrollbar).
+     * @config winWidth
+     * @for Node
+     * @type {Number}
+     */
+    'winWidth',
+
+    /**
+     * Returns the inner height of the viewport (exludes scrollbar).
+     * @config winHeight
+     * @type {Number}
+     */
+    'winHeight',
+
+    /**
+     * Document width
+     * @config docWidth
+     * @type {Number}
+     */
+    'docWidth',
+
+    /**
+     * Document height
+     * @config docHeight
+     * @type {Number}
+     */
+    'docHeight',
+
+    /**
+     * Pixel distance the page has been scrolled horizontally
+     * @config docScrollX
+     * @type {Number}
+     */
+    'docScrollX',
+
+    /**
+     * Pixel distance the page has been scrolled vertically
+     * @config docScrollY
+     * @type {Number}
+     */
+    'docScrollY'
+    ],
+    function(name) {
+        Y.Node.ATTRS[name] = {
+            getter: function() {
+                var args = Array.prototype.slice.call(arguments);
+                args.unshift(Y.Node.getDOMNode(this));
+
+                return Y.DOM[name].apply(this, args);
+            }
+        };
+    }
+);
+
+Y.Node.ATTRS.scrollLeft = {
+    getter: function() {
+        var node = Y.Node.getDOMNode(this);
+        return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node);
+    },
+
+    setter: function(val) {
+        var node = Y.Node.getDOMNode(this);
+        if (node) {
+            if ('scrollLeft' in node) {
+                node.scrollLeft = val;
+            } else if (node.document || node.nodeType === 9) {
+                Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc
+            }
+        } else {
+        }
+    }
+};
+
+Y.Node.ATTRS.scrollTop = {
+    getter: function() {
+        var node = Y.Node.getDOMNode(this);
+        return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node);
+    },
+
+    setter: function(val) {
+        var node = Y.Node.getDOMNode(this);
+        if (node) {
+            if ('scrollTop' in node) {
+                node.scrollTop = val;
+            } else if (node.document || node.nodeType === 9) {
+                Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc
+            }
+        } else {
+        }
+    }
+};
+
+Y.Node.importMethod(Y.DOM, [
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getXY
+ * @for Node
+ * @return {Array} The XY position of the node
+*/
+    'getXY',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setXY
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @chainable
+ */
+    'setXY',
+
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getX
+ * @return {Number} The X position of the node
+*/
+    'getX',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setX
+ * @param {Number} x X value for new position (coordinates are page-based)
+ * @chainable
+ */
+    'setX',
+
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getY
+ * @return {Number} The Y position of the node
+*/
+    'getY',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setY
+ * @param {Number} y Y value for new position (coordinates are page-based)
+ * @chainable
+ */
+    'setY',
+
+/**
+ * Swaps the XY position of this node with another node.
+ * @method swapXY
+ * @param {Node | HTMLElement} otherNode The node to swap with.
+ * @chainable
+ */
+    'swapXY'
+]);
+
+/**
+ * @module node
+ * @submodule node-screen
+ */
+
+/**
+ * Returns a region object for the node
+ * @config region
+ * @for Node
+ * @type Node
+ */
+Y.Node.ATTRS.region = {
+    getter: function() {
+        var node = this.getDOMNode(),
+            region;
+
+        if (node && !node.tagName) {
+            if (node.nodeType === 9) { // document
+                node = node.documentElement;
+            }
+        }
+        if (Y.DOM.isWindow(node)) {
+            region = Y.DOM.viewportRegion(node);
+        } else {
+            region = Y.DOM.region(node);
+        }
+        return region;
+    }
+};
+
+/**
+ * Returns a region object for the node's viewport
+ * @config viewportRegion
+ * @type Node
+ */
+Y.Node.ATTRS.viewportRegion = {
+    getter: function() {
+        return Y.DOM.viewportRegion(Y.Node.getDOMNode(this));
+    }
+};
+
+Y.Node.importMethod(Y.DOM, 'inViewportRegion');
+
+// these need special treatment to extract 2nd node arg
+/**
+ * Compares the intersection of the node with another node or region
+ * @method intersect
+ * @for Node
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.intersect = function(node2, altRegion) {
+    var node1 = Y.Node.getDOMNode(this);
+    if (Y.instanceOf(node2, Y.Node)) { // might be a region object
+        node2 = Y.Node.getDOMNode(node2);
+    }
+    return Y.DOM.intersect(node1, node2, altRegion);
+};
+
+/**
+ * Determines whether or not the node is within the given region.
+ * @method inRegion
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Boolean} all Whether or not all of the node must be in the region.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Boolean} True if in region, false if not.
+ */
+Y.Node.prototype.inRegion = function(node2, all, altRegion) {
+    var node1 = Y.Node.getDOMNode(this);
+    if (Y.instanceOf(node2, Y.Node)) { // might be a region object
+        node2 = Y.Node.getDOMNode(node2);
+    }
+    return Y.DOM.inRegion(node1, node2, all, altRegion);
+};
+
+
+}, '3.17.2', {"requires": ["dom-screen", "node-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/node-style/node-style-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/node-style/node-style-min.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/node-style/node-style-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/node-style/node-style-min.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,8 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add("node-style",function(e,t){(function(e){e.mix(e.Node.prototype,{setStyle:function(t,n){return e.DOM.setStyle(this._node,t,n),this},setStyles:function(t){return e.DOM.setStyles(this._node,t),this},getStyle:function(t){return e.DOM.getStyle(this._node,t)},getComputedStyle:function(t){return e.DOM.getComputedStyle(this._node,t)}}),e.NodeList.importMethod(e.Node.prototype,["getStyle","getComputedStyle","setStyle","setStyles"])})(e);var n=e.Node;e.mix(n.prototype,{show:function(e){return e=arguments[arguments.length-1],this.toggleView(!0,e),this},_show:function(){this.removeAttribute("hidden"),this.setStyle("display","")},_isHidden:function(){return this.hasAttribute("hidden")||e.DOM.getComputedStyle(this._node,"display")==="none"},toggleView:function(e,t){return this._toggleView.apply(this,arguments),this},_toggleView:function(e,t){return t=arguments[arguments.length-1],typeof e!="boolean"&&(e=this._isHidden()?1:0),e?this._show():this._hide(),typeof t=="function"&&t.call(this),t
 his},hide:function(e){return e=arguments[arguments.length-1],this.toggleView(!1,e),this},_hide:function(){this.setAttribute("hidden","hidden"),this.setStyle("display","none")}}),e.NodeList.importMethod(e.Node.prototype,["show","hide","toggleView"])},"3.17.2",{requires:["dom-style","node-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/node-style/node-style.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/node-style/node-style.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/node-style/node-style.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/node-style/node-style.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,278 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('node-style', function (Y, NAME) {
+
+(function(Y) {
+/**
+ * Extended Node interface for managing node styles.
+ * @module node
+ * @submodule node-style
+ */
+
+Y.mix(Y.Node.prototype, {
+    /**
+     * Sets a style property of the node.
+     * Use camelCase (e.g. 'backgroundColor') for multi-word properties.
+     * @method setStyle
+     * @param {String} attr The style attribute to set.
+     * @param {String|Number} val The value.
+     * @chainable
+     */
+    setStyle: function(attr, val) {
+        Y.DOM.setStyle(this._node, attr, val);
+        return this;
+    },
+
+    /**
+     * Sets multiple style properties on the node.
+     * Use camelCase (e.g. 'backgroundColor') for multi-word properties.
+     * @method setStyles
+     * @param {Object} hash An object literal of property:value pairs.
+     * @chainable
+     */
+    setStyles: function(hash) {
+        Y.DOM.setStyles(this._node, hash);
+        return this;
+    },
+
+    /**
+     * Returns the style's current value.
+     * Use camelCase (e.g. 'backgroundColor') for multi-word properties.
+     * @method getStyle
+     * @for Node
+     * @param {String} attr The style attribute to retrieve.
+     * @return {String} The current value of the style property for the element.
+     */
+
+     getStyle: function(attr) {
+        return Y.DOM.getStyle(this._node, attr);
+     },
+
+    /**
+     * Returns the computed value for the given style property.
+     * Use camelCase (e.g. 'backgroundColor') for multi-word properties.
+     * @method getComputedStyle
+     * @param {String} attr The style attribute to retrieve.
+     * @return {String} The computed value of the style property for the element.
+     */
+     getComputedStyle: function(attr) {
+        return Y.DOM.getComputedStyle(this._node, attr);
+     }
+});
+
+/**
+ * Returns an array of values for each node.
+ * Use camelCase (e.g. 'backgroundColor') for multi-word properties.
+ * @method getStyle
+ * @for NodeList
+ * @see Node.getStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The current values of the style property for the element.
+ */
+
+/**
+ * Returns an array of the computed value for each node.
+ * Use camelCase (e.g. 'backgroundColor') for multi-word properties.
+ * @method getComputedStyle
+ * @see Node.getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The computed values for each node.
+ */
+
+/**
+ * Sets a style property on each node.
+ * Use camelCase (e.g. 'backgroundColor') for multi-word properties.
+ * @method setStyle
+ * @see Node.setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+
+/**
+ * Sets multiple style properties on each node.
+ * Use camelCase (e.g. 'backgroundColor') for multi-word properties.
+ * @method setStyles
+ * @see Node.setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+
+// These are broken out to handle undefined return (avoid false positive for
+// chainable)
+
+Y.NodeList.importMethod(Y.Node.prototype, ['getStyle', 'getComputedStyle', 'setStyle', 'setStyles']);
+})(Y);
+/**
+ * @module node
+ * @submodule node-base
+ */
+
+var Y_Node = Y.Node;
+
+Y.mix(Y_Node.prototype, {
+    /**
+     * Makes the node visible.
+     * If the "transition" module is loaded, show optionally
+     * animates the showing of the node using either the default
+     * transition effect ('fadeIn'), or the given named effect.
+     * @method show
+     * @for Node
+     * @param {String} name A named Transition effect to use as the show effect.
+     * @param {Object} config Options to use with the transition.
+     * @param {Function} callback An optional function to run after the transition completes.
+     * @chainable
+     */
+    show: function(callback) {
+        callback = arguments[arguments.length - 1];
+        this.toggleView(true, callback);
+        return this;
+    },
+
+    /**
+     * The implementation for showing nodes.
+     * Default is to remove the hidden attribute and reset the CSS style.display property.
+     * @method _show
+     * @protected
+     * @chainable
+     */
+    _show: function() {
+        this.removeAttribute('hidden');
+
+        // For back-compat we need to leave this in for browsers that
+        // do not visually hide a node via the hidden attribute
+        // and for users that check visibility based on style display.
+        this.setStyle('display', '');
+
+    },
+
+    /**
+    Returns whether the node is hidden by YUI or not. The hidden status is
+    determined by the 'hidden' attribute and the value of the 'display' CSS
+    property.
+
+    @method _isHidden
+    @return {Boolean} `true` if the node is hidden.
+    @private
+    **/
+    _isHidden: function() {
+        return  this.hasAttribute('hidden') || Y.DOM.getComputedStyle(this._node, 'display') === 'none';
+    },
+
+    /**
+     * Displays or hides the node.
+     * If the "transition" module is loaded, toggleView optionally
+     * animates the toggling of the node using given named effect.
+     * @method toggleView
+     * @for Node
+     * @param {Boolean} [on] An optional boolean value to force the node to be shown or hidden
+     * @param {Function} [callback] An optional function to run after the transition completes.
+     * @chainable
+     */
+    toggleView: function(on, callback) {
+        this._toggleView.apply(this, arguments);
+        return this;
+    },
+
+    _toggleView: function(on, callback) {
+        callback = arguments[arguments.length - 1];
+
+        // base on current state if not forcing
+        if (typeof on != 'boolean') {
+            on = (this._isHidden()) ? 1 : 0;
+        }
+
+        if (on) {
+            this._show();
+        }  else {
+            this._hide();
+        }
+
+        if (typeof callback == 'function') {
+            callback.call(this);
+        }
+
+        return this;
+    },
+
+    /**
+     * Hides the node.
+     * If the "transition" module is loaded, hide optionally
+     * animates the hiding of the node using either the default
+     * transition effect ('fadeOut'), or the given named effect.
+     * @method hide
+     * @param {String} name A named Transition effect to use as the show effect.
+     * @param {Object} config Options to use with the transition.
+     * @param {Function} callback An optional function to run after the transition completes.
+     * @chainable
+     */
+    hide: function(callback) {
+        callback = arguments[arguments.length - 1];
+        this.toggleView(false, callback);
+        return this;
+    },
+
+    /**
+     * The implementation for hiding nodes.
+     * Default is to set the hidden attribute to true and set the CSS style.display to 'none'.
+     * @method _hide
+     * @protected
+     * @chainable
+     */
+    _hide: function() {
+        this.setAttribute('hidden', 'hidden');
+
+        // For back-compat we need to leave this in for browsers that
+        // do not visually hide a node via the hidden attribute
+        // and for users that check visibility based on style display.
+        this.setStyle('display', 'none');
+    }
+});
+
+Y.NodeList.importMethod(Y.Node.prototype, [
+    /**
+     * Makes each node visible.
+     * If the "transition" module is loaded, show optionally
+     * animates the showing of the node using either the default
+     * transition effect ('fadeIn'), or the given named effect.
+     * @method show
+     * @param {String} name A named Transition effect to use as the show effect.
+     * @param {Object} config Options to use with the transition.
+     * @param {Function} callback An optional function to run after the transition completes.
+     * @for NodeList
+     * @chainable
+     */
+    'show',
+
+    /**
+     * Hides each node.
+     * If the "transition" module is loaded, hide optionally
+     * animates the hiding of the node using either the default
+     * transition effect ('fadeOut'), or the given named effect.
+     * @method hide
+     * @param {String} name A named Transition effect to use as the show effect.
+     * @param {Object} config Options to use with the transition.
+     * @param {Function} callback An optional function to run after the transition completes.
+     * @chainable
+     */
+    'hide',
+
+    /**
+     * Displays or hides each node.
+     * If the "transition" module is loaded, toggleView optionally
+     * animates the toggling of the nodes using given named effect.
+     * @method toggleView
+     * @param {Boolean} [on] An optional boolean value to force the nodes to be shown or hidden
+     * @param {Function} [callback] An optional function to run after the transition completes.
+     * @chainable
+     */
+    'toggleView'
+]);
+
+
+}, '3.17.2', {"requires": ["dom-style", "node-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/oop/oop-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/oop/oop-min.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/oop/oop-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/oop/oop-min.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,8 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add("oop",function(e,t){function a(t,n,i,s,o){if(t&&t[o]&&t!==e)return t[o].call(t,n,i);switch(r.test(t)){case 1:return r[o](t,n,i);case 2:return r[o](e.Array(t,0,!0),n,i);default:return e.Object[o](t,n,i,s)}}var n=e.Lang,r=e.Array,i=Object.prototype,s="_~yuim~_",o=i.hasOwnProperty,u=i.toString;e.augment=function(t,n,r,i,s){var a=t.prototype,f=a&&n,l=n.prototype,c=a||t,h,p,d,v,m;return s=s?e.Array(s):[],f&&(p={},d={},v={},h=function(e,t){if(r||!(t in a))u.call(e)==="[object Function]"?(v[t]=e,p[t]=d[t]=function(){return m(this,e,arguments)}):p[t]=e},m=function(e,t,r){for(var i in v)o.call(v,i)&&e[i]===d[i]&&(e[i]=v[i]);return n.apply(e,s),t.apply(e,r)},i?e.Array.each(i,function(e){e in l&&h(l[e],e)}):e.Object.each(l,h,null,!0)),e.mix(c,p||l,r,i),f||n.apply(c,s),t},e.aggregate=function(t,n,r,i){return e.mix(t,n,r,i,0,!0)},e.extend=function(t,n,r,s){(!n||!t)&&e.error("extend failed, verify dependencies");var o=n.prototype,u=e.Object(o);return t.prototype=u,u.constructor=t,t.superc
 lass=o,n!=Object&&o.constructor==i.constructor&&(o.constructor=n),r&&e.mix(u,r,!0),s&&e.mix(t,s,!0),t},e.each=function(e,t,n,r){return a(e,t,n,r,"each")},e.some=function(e,t,n,r){return a(e,t,n,r,"some")},e.clone=function(t,r,i,o,u,a){var f,l,c;if(!n.isObject(t)||e.instanceOf(t,YUI)||t.addEventListener||t.attachEvent)return t;l=a||{};switch(n.type(t)){case"date":return new Date(t);case"regexp":return t;case"function":return t;case"array":f=[];break;default:if(t[s])return l[t[s]];c=e.guid(),f=r?{}:e.Object(t),t[s]=c,l[c]=t}return e.each(t,function(n,a){(a||a===0)&&(!i||i.call(o||this,n,a,this,t)!==!1)&&a!==s&&a!="prototype"&&(this[a]=e.clone(n,r,i,o,u||t,l))},f),a||(e.Object.each(l,function(e,t){if(e[s])try{delete e[s]}catch(n){e[s]=null}},this),l=null),f},e.bind=function(t,r){var i=arguments.length>2?e.Array(arguments,2,!0):null;return function(){var s=n.isString(t)?r[t]:t,o=i?i.concat(e.Array(arguments,0,!0)):arguments;return s.apply(r||s,o)}},e.rbind=function(t,r){var i=arguments.
 length>2?e.Array(arguments,2,!0):null;return function(){var s=n.isString(t)?r[t]:t,o=i?e.Array(arguments,0,!0).concat(i):arguments;return s.apply(r||s,o)}}},"3.17.2",{requires:["yui-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/oop/oop.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/oop/oop.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/oop/oop.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/oop/oop.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,439 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('oop', function (Y, NAME) {
+
+/**
+Adds object inheritance and manipulation utilities to the YUI instance. This
+module is required by most YUI components.
+
+@module oop
+**/
+
+var L            = Y.Lang,
+    A            = Y.Array,
+    OP           = Object.prototype,
+    CLONE_MARKER = '_~yuim~_',
+
+    hasOwn   = OP.hasOwnProperty,
+    toString = OP.toString;
+
+/**
+Calls the specified _action_ method on _o_ if it exists. Otherwise, if _o_ is an
+array, calls the _action_ method on `Y.Array`, or if _o_ is an object, calls the
+_action_ method on `Y.Object`.
+
+If _o_ is an array-like object, it will be coerced to an array.
+
+This is intended to be used with array/object iteration methods that share
+signatures, such as `each()`, `some()`, etc.
+
+@method dispatch
+@param {Object} o Array or object to dispatch to.
+@param {Function} f Iteration callback.
+    @param {Mixed} f.value Value being iterated.
+    @param {Mixed} f.key Current object key or array index.
+    @param {Mixed} f.object Object or array being iterated.
+@param {Object} c `this` object to bind the iteration callback to.
+@param {Boolean} proto If `true`, prototype properties of objects will be
+    iterated.
+@param {String} action Function name to be dispatched on _o_. For example:
+    'some', 'each', etc.
+@private
+@return {Mixed} Returns the value returned by the chosen iteration action, which
+    varies.
+**/
+function dispatch(o, f, c, proto, action) {
+    if (o && o[action] && o !== Y) {
+        return o[action].call(o, f, c);
+    } else {
+        switch (A.test(o)) {
+            case 1:
+                return A[action](o, f, c);
+            case 2:
+                return A[action](Y.Array(o, 0, true), f, c);
+            default:
+                return Y.Object[action](o, f, c, proto);
+        }
+    }
+}
+
+/**
+Augments the _receiver_ with prototype properties from the _supplier_. The
+receiver may be a constructor function or an object. The supplier must be a
+constructor function.
+
+If the _receiver_ is an object, then the _supplier_ constructor will be called
+immediately after _receiver_ is augmented, with _receiver_ as the `this` object.
+
+If the _receiver_ is a constructor function, then all prototype methods of
+_supplier_ that are copied to _receiver_ will be sequestered, and the
+_supplier_ constructor will not be called immediately. The first time any
+sequestered method is called on the _receiver_'s prototype, all sequestered
+methods will be immediately copied to the _receiver_'s prototype, the
+_supplier_'s constructor will be executed, and finally the newly unsequestered
+method that was called will be executed.
+
+This sequestering logic sounds like a bunch of complicated voodoo, but it makes
+it cheap to perform frequent augmentation by ensuring that suppliers'
+constructors are only called if a supplied method is actually used. If none of
+the supplied methods is ever used, then there's no need to take the performance
+hit of calling the _supplier_'s constructor.
+
+@method augment
+@param {Function|Object} receiver Object or function to be augmented.
+@param {Function} supplier Function that supplies the prototype properties with
+  which to augment the _receiver_.
+@param {Boolean} [overwrite=false] If `true`, properties already on the receiver
+  will be overwritten if found on the supplier's prototype.
+@param {String[]} [whitelist] An array of property names. If specified,
+  only the whitelisted prototype properties will be applied to the receiver, and
+  all others will be ignored.
+@param {Array|any} [args] Argument or array of arguments to pass to the
+  supplier's constructor when initializing.
+@return {Function} Augmented object.
+@for YUI
+**/
+Y.augment = function (receiver, supplier, overwrite, whitelist, args) {
+    var rProto    = receiver.prototype,
+        sequester = rProto && supplier,
+        sProto    = supplier.prototype,
+        to        = rProto || receiver,
+
+        copy,
+        newPrototype,
+        replacements,
+        sequestered,
+        unsequester;
+
+    args = args ? Y.Array(args) : [];
+
+    if (sequester) {
+        newPrototype = {};
+        replacements = {};
+        sequestered  = {};
+
+        copy = function (value, key) {
+            if (overwrite || !(key in rProto)) {
+                if (toString.call(value) === '[object Function]') {
+                    sequestered[key] = value;
+
+                    newPrototype[key] = replacements[key] = function () {
+                        return unsequester(this, value, arguments);
+                    };
+                } else {
+                    newPrototype[key] = value;
+                }
+            }
+        };
+
+        unsequester = function (instance, fn, fnArgs) {
+            // Unsequester all sequestered functions.
+            for (var key in sequestered) {
+                if (hasOwn.call(sequestered, key)
+                        && instance[key] === replacements[key]) {
+
+                    instance[key] = sequestered[key];
+                }
+            }
+
+            // Execute the supplier constructor.
+            supplier.apply(instance, args);
+
+            // Finally, execute the original sequestered function.
+            return fn.apply(instance, fnArgs);
+        };
+
+        if (whitelist) {
+            Y.Array.each(whitelist, function (name) {
+                if (name in sProto) {
+                    copy(sProto[name], name);
+                }
+            });
+        } else {
+            Y.Object.each(sProto, copy, null, true);
+        }
+    }
+
+    Y.mix(to, newPrototype || sProto, overwrite, whitelist);
+
+    if (!sequester) {
+        supplier.apply(to, args);
+    }
+
+    return receiver;
+};
+
+/**
+ * Copies object properties from the supplier to the receiver. If the target has
+ * the property, and the property is an object, the target object will be
+ * augmented with the supplier's value.
+ *
+ * @method aggregate
+ * @param {Object} receiver Object to receive the augmentation.
+ * @param {Object} supplier Object that supplies the properties with which to
+ *     augment the receiver.
+ * @param {Boolean} [overwrite=false] If `true`, properties already on the receiver
+ *     will be overwritten if found on the supplier.
+ * @param {String[]} [whitelist] Whitelist. If supplied, only properties in this
+ *     list will be applied to the receiver.
+ * @return {Object} Augmented object.
+ */
+Y.aggregate = function(r, s, ov, wl) {
+    return Y.mix(r, s, ov, wl, 0, true);
+};
+
+/**
+ * Utility to set up the prototype, constructor and superclass properties to
+ * support an inheritance strategy that can chain constructors and methods.
+ * Static members will not be inherited.
+ *
+ * @method extend
+ * @param {function} r   the object to modify.
+ * @param {function} s the object to inherit.
+ * @param {object} px prototype properties to add/override.
+ * @param {object} sx static properties to add/override.
+ * @return {object} the extended object.
+ */
+Y.extend = function(r, s, px, sx) {
+    if (!s || !r) {
+        Y.error('extend failed, verify dependencies');
+    }
+
+    var sp = s.prototype, rp = Y.Object(sp);
+    r.prototype = rp;
+
+    rp.constructor = r;
+    r.superclass = sp;
+
+    // assign constructor property
+    if (s != Object && sp.constructor == OP.constructor) {
+        sp.constructor = s;
+    }
+
+    // add prototype overrides
+    if (px) {
+        Y.mix(rp, px, true);
+    }
+
+    // add object overrides
+    if (sx) {
+        Y.mix(r, sx, true);
+    }
+
+    return r;
+};
+
+/**
+ * Executes the supplied function for each item in
+ * a collection.  Supports arrays, objects, and
+ * NodeLists
+ * @method each
+ * @param {object} o the object to iterate.
+ * @param {function} f the function to execute.  This function
+ * receives the value, key, and object as parameters.
+ * @param {object} c the execution context for the function.
+ * @param {boolean} proto if true, prototype properties are
+ * iterated on objects.
+ * @return {YUI} the YUI instance.
+ */
+Y.each = function(o, f, c, proto) {
+    return dispatch(o, f, c, proto, 'each');
+};
+
+/**
+ * Executes the supplied function for each item in
+ * a collection.  The operation stops if the function
+ * returns true. Supports arrays, objects, and
+ * NodeLists.
+ * @method some
+ * @param {object} o the object to iterate.
+ * @param {function} f the function to execute.  This function
+ * receives the value, key, and object as parameters.
+ * @param {object} c the execution context for the function.
+ * @param {boolean} proto if true, prototype properties are
+ * iterated on objects.
+ * @return {boolean} true if the function ever returns true,
+ * false otherwise.
+ */
+Y.some = function(o, f, c, proto) {
+    return dispatch(o, f, c, proto, 'some');
+};
+
+/**
+Deep object/array copy. Function clones are actually wrappers around the
+original function. Array-like objects are treated as arrays. Primitives are
+returned untouched. Optionally, a function can be provided to handle other data
+types, filter keys, validate values, etc.
+
+**Note:** Cloning a non-trivial object is a reasonably heavy operation, due to
+the need to recursively iterate down non-primitive properties. Clone should be
+used only when a deep clone down to leaf level properties is explicitly
+required. This method will also
+
+In many cases (for example, when trying to isolate objects used as hashes for
+configuration properties), a shallow copy, using `Y.merge()` is normally
+sufficient. If more than one level of isolation is required, `Y.merge()` can be
+used selectively at each level which needs to be isolated from the original
+without going all the way to leaf properties.
+
+@method clone
+@param {object} o what to clone.
+@param {boolean} safe if true, objects will not have prototype items from the
+    source. If false, they will. In this case, the original is initially
+    protected, but the clone is not completely immune from changes to the source
+    object prototype. Also, cloned prototype items that are deleted from the
+    clone will result in the value of the source prototype being exposed. If
+    operating on a non-safe clone, items should be nulled out rather than
+    deleted.
+@param {function} f optional function to apply to each item in a collection; it
+    will be executed prior to applying the value to the new object.
+    Return false to prevent the copy.
+@param {object} c optional execution context for f.
+@param {object} owner Owner object passed when clone is iterating an object.
+    Used to set up context for cloned functions.
+@param {object} cloned hash of previously cloned objects to avoid multiple
+    clones.
+@return {Array|Object} the cloned object.
+**/
+Y.clone = function(o, safe, f, c, owner, cloned) {
+    var o2, marked, stamp;
+
+    // Does not attempt to clone:
+    //
+    // * Non-typeof-object values, "primitive" values don't need cloning.
+    //
+    // * YUI instances, cloning complex object like YUI instances is not
+    //   advised, this is like cloning the world.
+    //
+    // * DOM nodes (#2528250), common host objects like DOM nodes cannot be
+    //   "subclassed" in Firefox and old versions of IE. Trying to use
+    //   `Object.create()` or `Y.extend()` on a DOM node will throw an error in
+    //   these browsers.
+    //
+    // Instad, the passed-in `o` will be return as-is when it matches one of the
+    // above criteria.
+    if (!L.isObject(o) ||
+            Y.instanceOf(o, YUI) ||
+            (o.addEventListener || o.attachEvent)) {
+
+        return o;
+    }
+
+    marked = cloned || {};
+
+    switch (L.type(o)) {
+        case 'date':
+            return new Date(o);
+        case 'regexp':
+            // if we do this we need to set the flags too
+            // return new RegExp(o.source);
+            return o;
+        case 'function':
+            // o2 = Y.bind(o, owner);
+            // break;
+            return o;
+        case 'array':
+            o2 = [];
+            break;
+        default:
+
+            // #2528250 only one clone of a given object should be created.
+            if (o[CLONE_MARKER]) {
+                return marked[o[CLONE_MARKER]];
+            }
+
+            stamp = Y.guid();
+
+            o2 = (safe) ? {} : Y.Object(o);
+
+            o[CLONE_MARKER] = stamp;
+            marked[stamp] = o;
+    }
+
+    Y.each(o, function(v, k) {
+        if ((k || k === 0) && (!f || (f.call(c || this, v, k, this, o) !== false))) {
+            if (k !== CLONE_MARKER) {
+                if (k == 'prototype') {
+                    // skip the prototype
+                // } else if (o[k] === o) {
+                //     this[k] = this;
+                } else {
+                    this[k] =
+                        Y.clone(v, safe, f, c, owner || o, marked);
+                }
+            }
+        }
+    }, o2);
+
+    if (!cloned) {
+        Y.Object.each(marked, function(v, k) {
+            if (v[CLONE_MARKER]) {
+                try {
+                    delete v[CLONE_MARKER];
+                } catch (e) {
+                    v[CLONE_MARKER] = null;
+                }
+            }
+        }, this);
+        marked = null;
+    }
+
+    return o2;
+};
+
+/**
+ * Returns a function that will execute the supplied function in the
+ * supplied object's context, optionally adding any additional
+ * supplied parameters to the beginning of the arguments collection the
+ * supplied to the function.
+ *
+ * @method bind
+ * @param {Function|String} f the function to bind, or a function name
+ * to execute on the context object.
+ * @param {object} c the execution context.
+ * @param {any} args* 0..n arguments to include before the arguments the
+ * function is executed with.
+ * @return {function} the wrapped function.
+ */
+Y.bind = function(f, c) {
+    var xargs = arguments.length > 2 ?
+            Y.Array(arguments, 2, true) : null;
+    return function() {
+        var fn = L.isString(f) ? c[f] : f,
+            args = (xargs) ?
+                xargs.concat(Y.Array(arguments, 0, true)) : arguments;
+        return fn.apply(c || fn, args);
+    };
+};
+
+/**
+ * Returns a function that will execute the supplied function in the
+ * supplied object's context, optionally adding any additional
+ * supplied parameters to the end of the arguments the function
+ * is executed with.
+ *
+ * @method rbind
+ * @param {Function|String} f the function to bind, or a function name
+ * to execute on the context object.
+ * @param {object} c the execution context.
+ * @param {any} args* 0..n arguments to append to the end of
+ * arguments collection supplied to the function.
+ * @return {function} the wrapped function.
+ */
+Y.rbind = function(f, c) {
+    var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
+    return function() {
+        var fn = L.isString(f) ? c[f] : f,
+            args = (xargs) ?
+                Y.Array(arguments, 0, true).concat(xargs) : arguments;
+        return fn.apply(c || fn, args);
+    };
+};
+
+
+}, '3.17.2', {"requires": ["yui-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/plugin/plugin-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/plugin/plugin-min.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/plugin/plugin-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/plugin/plugin-min.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,8 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add("plugin",function(e,t){function n(t){!this.hasImpl||!this.hasImpl(e.Plugin.Base)?n.superclass.constructor.apply(this,arguments):n.prototype.initializer.apply(this,arguments)}n.ATTRS={host:{writeOnce:!0}},n.NAME="plugin",n.NS="plugin",e.extend(n,e.Base,{_handles:null,initializer:function(e){this._handles=[]},destructor:function(){if(this._handles)for(var e=0,t=this._handles.length;e<t;e++)this._handles[e].detach()},doBefore:function(e,t,n){var r=this.get("host"),i;return e in r?i=this.beforeHostMethod(e,t,n):r.on&&(i=this.onHostEvent(e,t,n)),i},doAfter:function(e,t,n){var r=this.get("host"),i;return e in r?i=this.afterHostMethod(e,t,n):r.after&&(i=this.afterHostEvent(e,t,n)),i},onHostEvent:function(e,t,n){var r=this.get("host").on(e,t,n||this);return this._handles.push(r),r},onceHostEvent:function(e,t,n){var r=this.get("host").once(e,t,n||this);return this._handles.push(r),r},afterHostEvent:function(e,t,n){var r=this.get("host").after(e,t,n||this);return this._handles.push(r)
 ,r},onceAfterHostEvent:function(e,t,n){var r=this.get("host").onceAfter(e,t,n||this);return this._handles.push(r),r},beforeHostMethod:function(t,n,r){var i=e.Do.before(n,this.get("host"),t,r||this);return this._handles.push(i),i},afterHostMethod:function(t,n,r){var i=e.Do.after(n,this.get("host"),t,r||this);return this._handles.push(i),i},toString:function(){return this.constructor.NAME+"["+this.constructor.NS+"]"}}),e.namespace("Plugin").Base=n},"3.17.2",{requires:["base-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/plugin/plugin.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/plugin/plugin.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/plugin/plugin.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/plugin/plugin.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,270 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('plugin', function (Y, NAME) {
+
+    /**
+     * Provides the base Plugin class, which plugin developers should extend, when creating custom plugins
+     *
+     * @module plugin
+     */
+
+    /**
+     * The base class for all Plugin instances.
+     *
+     * @class Plugin.Base
+     * @extends Base
+     * @param {Object} config Configuration object with property name/value pairs.
+     */
+    function Plugin(config) {
+        if (! (this.hasImpl && this.hasImpl(Y.Plugin.Base)) ) {
+            Plugin.superclass.constructor.apply(this, arguments);
+        } else {
+            Plugin.prototype.initializer.apply(this, arguments);
+        }
+    }
+
+    /**
+     * Object defining the set of attributes supported by the Plugin.Base class
+     *
+     * @property ATTRS
+     * @type Object
+     * @static
+     */
+    Plugin.ATTRS = {
+
+        /**
+         * The plugin's host object.
+         *
+         * @attribute host
+         * @writeonce
+         * @type Plugin.Host
+         */
+        host : {
+            writeOnce: true
+        }
+    };
+
+    /**
+     * The string identifying the Plugin.Base class. Plugins extending
+     * Plugin.Base should set their own NAME value.
+     *
+     * @property NAME
+     * @type String
+     * @static
+     */
+    Plugin.NAME = 'plugin';
+
+    /**
+     * The name of the property the the plugin will be attached to
+     * when plugged into a Plugin Host. Plugins extending Plugin.Base,
+     * should set their own NS value.
+     *
+     * @property NS
+     * @type String
+     * @static
+     */
+    Plugin.NS = 'plugin';
+
+    Y.extend(Plugin, Y.Base, {
+
+        /**
+         * The list of event handles for event listeners or AOP injected methods
+         * applied by the plugin to the host object.
+         *
+         * @property _handles
+         * @private
+         * @type Array
+         * @value null
+         */
+        _handles: null,
+
+        /**
+         * Initializer lifecycle implementation.
+         *
+         * @method initializer
+         * @param {Object} config Configuration object with property name/value pairs.
+         */
+        initializer : function(config) {
+            this._handles = [];
+        },
+
+        /**
+         * Destructor lifecycle implementation.
+         *
+         * Removes any event listeners or injected methods applied by the Plugin
+         *
+         * @method destructor
+         */
+        destructor: function() {
+            // remove all handles
+            if (this._handles) {
+                for (var i = 0, l = this._handles.length; i < l; i++) {
+                   this._handles[i].detach();
+                }
+            }
+        },
+
+        /**
+         * Listens for the "on" moment of events fired by the host,
+         * or injects code "before" a given method on the host.
+         *
+         * @method doBefore
+         *
+         * @param strMethod {String} The event to listen for, or method to inject logic before.
+         * @param fn {Function} The handler function. For events, the "on" moment listener. For methods, the function to execute before the given method is executed.
+         * @param context {Object} An optional context to call the handler with. The default context is the plugin instance.
+         * @return handle {EventHandle} The detach handle for the handler.
+         */
+        doBefore: function(strMethod, fn, context) {
+            var host = this.get("host"), handle;
+
+            if (strMethod in host) { // method
+                handle = this.beforeHostMethod(strMethod, fn, context);
+            } else if (host.on) { // event
+                handle = this.onHostEvent(strMethod, fn, context);
+            }
+
+            return handle;
+        },
+
+        /**
+         * Listens for the "after" moment of events fired by the host,
+         * or injects code "after" a given method on the host.
+         *
+         * @method doAfter
+         *
+         * @param strMethod {String} The event to listen for, or method to inject logic after.
+         * @param fn {Function} The handler function. For events, the "after" moment listener. For methods, the function to execute after the given method is executed.
+         * @param context {Object} An optional context to call the handler with. The default context is the plugin instance.
+         * @return handle {EventHandle} The detach handle for the listener.
+         */
+        doAfter: function(strMethod, fn, context) {
+            var host = this.get("host"), handle;
+
+            if (strMethod in host) { // method
+                handle = this.afterHostMethod(strMethod, fn, context);
+            } else if (host.after) { // event
+                handle = this.afterHostEvent(strMethod, fn, context);
+            }
+
+            return handle;
+        },
+
+        /**
+         * Listens for the "on" moment of events fired by the host object.
+         *
+         * Listeners attached through this method will be detached when the plugin is unplugged.
+         *
+         * @method onHostEvent
+         * @param {String | Object} type The event type.
+         * @param {Function} fn The listener.
+         * @param {Object} context The execution context. Defaults to the plugin instance.
+         * @return handle {EventHandle} The detach handle for the listener.
+         */
+        onHostEvent : function(type, fn, context) {
+            var handle = this.get("host").on(type, fn, context || this);
+            this._handles.push(handle);
+            return handle;
+        },
+
+        /**
+         * Listens for the "on" moment of events fired by the host object one time only.
+         * The listener is immediately detached when it is executed.
+         *
+         * Listeners attached through this method will be detached when the plugin is unplugged.
+         *
+         * @method onceHostEvent
+         * @param {String | Object} type The event type.
+         * @param {Function} fn The listener.
+         * @param {Object} context The execution context. Defaults to the plugin instance.
+         * @return handle {EventHandle} The detach handle for the listener.
+         */
+        onceHostEvent : function(type, fn, context) {
+            var handle = this.get("host").once(type, fn, context || this);
+            this._handles.push(handle);
+            return handle;
+        },
+
+        /**
+         * Listens for the "after" moment of events fired by the host object.
+         *
+         * Listeners attached through this method will be detached when the plugin is unplugged.
+         *
+         * @method afterHostEvent
+         * @param {String | Object} type The event type.
+         * @param {Function} fn The listener.
+         * @param {Object} context The execution context. Defaults to the plugin instance.
+         * @return handle {EventHandle} The detach handle for the listener.
+         */
+        afterHostEvent : function(type, fn, context) {
+            var handle = this.get("host").after(type, fn, context || this);
+            this._handles.push(handle);
+            return handle;
+        },
+
+        /**
+         * Listens for the "after" moment of events fired by the host object one time only.
+         * The listener is immediately detached when it is executed.
+         *
+         * Listeners attached through this method will be detached when the plugin is unplugged.
+         *
+         * @method onceAfterHostEvent
+         * @param {String | Object} type The event type.
+         * @param {Function} fn The listener.
+         * @param {Object} context The execution context. Defaults to the plugin instance.
+         * @return handle {EventHandle} The detach handle for the listener.
+         */
+        onceAfterHostEvent : function(type, fn, context) {
+            var handle = this.get("host").onceAfter(type, fn, context || this);
+            this._handles.push(handle);
+            return handle;
+        },
+
+        /**
+         * Injects a function to be executed before a given method on host object.
+         *
+         * The function will be detached when the plugin is unplugged.
+         *
+         * @method beforeHostMethod
+         * @param {String} method The name of the method to inject the function before.
+         * @param {Function} fn The function to inject.
+         * @param {Object} context The execution context. Defaults to the plugin instance.
+         * @return handle {EventHandle} The detach handle for the injected function.
+         */
+        beforeHostMethod : function(strMethod, fn, context) {
+            var handle = Y.Do.before(fn, this.get("host"), strMethod, context || this);
+            this._handles.push(handle);
+            return handle;
+        },
+
+        /**
+         * Injects a function to be executed after a given method on host object.
+         *
+         * The function will be detached when the plugin is unplugged.
+         *
+         * @method afterHostMethod
+         * @param {String} method The name of the method to inject the function after.
+         * @param {Function} fn The function to inject.
+         * @param {Object} context The execution context. Defaults to the plugin instance.
+         * @return handle {EventHandle} The detach handle for the injected function.
+         */
+        afterHostMethod : function(strMethod, fn, context) {
+            var handle = Y.Do.after(fn, this.get("host"), strMethod, context || this);
+            this._handles.push(handle);
+            return handle;
+        },
+
+        toString: function() {
+            return this.constructor.NAME + '[' + this.constructor.NS + ']';
+        }
+    });
+
+    Y.namespace("Plugin").Base = Plugin;
+
+
+}, '3.17.2', {"requires": ["base-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/pluginhost-base/pluginhost-base-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/pluginhost-base/pluginhost-base-min.js?rev=1609737&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/pluginhost-base/pluginhost-base-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/pluginhost-base/pluginhost-base-min.js Fri Jul 11 16:23:25 2014
@@ -0,0 +1,8 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add("pluginhost-base",function(e,t){function r(){this._plugins={}}var n=e.Lang;r.prototype={plug:function(e,t){var r,i,s;if(n.isArray(e))for(r=0,i=e.length;r<i;r++)this.plug(e[r]);else e&&!n.isFunction(e)&&(t=e.cfg,e=e.fn),e&&e.NS&&(s=e.NS,t=t||{},t.host=this,this.hasPlugin(s)?this[s].setAttrs&&this[s].setAttrs(t):(this[s]=new e(t),this._plugins[s]=e));return this},unplug:function(e){var t=e,r=this._plugins;if(e)n.isFunction(e)&&(t=e.NS,t&&(!r[t]||r[t]!==e)&&(t=null)),t&&(this[t]&&(this[t].destroy&&this[t].destroy(),delete this[t]),r[t]&&delete r[t]);else for(t in this._plugins)this._plugins.hasOwnProperty(t)&&this.unplug(t);return this},hasPlugin:function(e){return this._plugins[e]&&this[e]},_initPlugins:function(e){this._plugins=this._plugins||{},this._initConfigPlugins&&this._initConfigPlugins(e)},_destroyPlugins:function(){this.unplug()}},e.namespace("Plugin").Host=r},"3.17.2",{requires:["yui-base"]});