roller-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gma...@apache.org
Subject svn commit: r1609870 [3/6] - in /roller/trunk/app/src/main/webapp: WEB-INF/jsps/editor/ roller-ui/yui3/array-extras/ roller-ui/yui3/autocomplete-base/ roller-ui/yui3/autocomplete-filters/ roller-ui/yui3/autocomplete-list-keys/ roller-ui/yui3/autocomple...
Date Sat, 12 Jul 2014 00:34:04 GMT
Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/autocomplete-list.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/autocomplete-list.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/autocomplete-list.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/autocomplete-list.js Sat Jul 12 00:34:02 2014
@@ -0,0 +1,913 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('autocomplete-list', function (Y, NAME) {
+
+/**
+Traditional autocomplete dropdown list widget, just like Mom used to make.
+
+@module autocomplete
+@submodule autocomplete-list
+**/
+
+/**
+Traditional autocomplete dropdown list widget, just like Mom used to make.
+
+@class AutoCompleteList
+@extends Widget
+@uses AutoCompleteBase
+@uses WidgetPosition
+@uses WidgetPositionAlign
+@constructor
+@param {Object} config Configuration object.
+**/
+
+var Lang   = Y.Lang,
+    Node   = Y.Node,
+    YArray = Y.Array,
+
+    // Whether or not we need an iframe shim.
+    useShim = Y.UA.ie && Y.UA.ie < 7,
+
+    // keyCode constants.
+    KEY_TAB = 9,
+
+    // String shorthand.
+    _CLASS_ITEM        = '_CLASS_ITEM',
+    _CLASS_ITEM_ACTIVE = '_CLASS_ITEM_ACTIVE',
+    _CLASS_ITEM_HOVER  = '_CLASS_ITEM_HOVER',
+    _SELECTOR_ITEM     = '_SELECTOR_ITEM',
+
+    ACTIVE_ITEM      = 'activeItem',
+    ALWAYS_SHOW_LIST = 'alwaysShowList',
+    CIRCULAR         = 'circular',
+    HOVERED_ITEM     = 'hoveredItem',
+    ID               = 'id',
+    ITEM             = 'item',
+    LIST             = 'list',
+    RESULT           = 'result',
+    RESULTS          = 'results',
+    VISIBLE          = 'visible',
+    WIDTH            = 'width',
+
+    // Event names.
+    EVT_SELECT = 'select',
+
+List = Y.Base.create('autocompleteList', Y.Widget, [
+    Y.AutoCompleteBase,
+    Y.WidgetPosition,
+    Y.WidgetPositionAlign
+], {
+    // -- Prototype Properties -------------------------------------------------
+    ARIA_TEMPLATE: '<div/>',
+    ITEM_TEMPLATE: '<li/>',
+    LIST_TEMPLATE: '<ul/>',
+
+    // Widget automatically attaches delegated event handlers to everything in
+    // Y.Node.DOM_EVENTS, including synthetic events. Since Widget's event
+    // delegation won't work for the synthetic valuechange event, and since
+    // it creates a name collision between the backcompat "valueChange" synth
+    // event alias and AutoCompleteList's "valueChange" event for the "value"
+    // attr, this hack is necessary in order to prevent Widget from attaching
+    // valuechange handlers.
+    UI_EVENTS: (function () {
+        var uiEvents = Y.merge(Y.Node.DOM_EVENTS);
+
+        delete uiEvents.valuechange;
+        delete uiEvents.valueChange;
+
+        return uiEvents;
+    }()),
+
+    // -- Lifecycle Prototype Methods ------------------------------------------
+    initializer: function () {
+        var inputNode = this.get('inputNode');
+
+        if (!inputNode) {
+            Y.error('No inputNode specified.');
+            return;
+        }
+
+        this._inputNode  = inputNode;
+        this._listEvents = [];
+
+        // This ensures that the list is rendered inside the same parent as the
+        // input node by default, which is necessary for proper ARIA support.
+        this.DEF_PARENT_NODE = inputNode.get('parentNode');
+
+        // Cache commonly used classnames and selectors for performance.
+        this[_CLASS_ITEM]        = this.getClassName(ITEM);
+        this[_CLASS_ITEM_ACTIVE] = this.getClassName(ITEM, 'active');
+        this[_CLASS_ITEM_HOVER]  = this.getClassName(ITEM, 'hover');
+        this[_SELECTOR_ITEM]     = '.' + this[_CLASS_ITEM];
+
+        /**
+        Fires when an autocomplete suggestion is selected from the list,
+        typically via a keyboard action or mouse click.
+
+        @event select
+        @param {Node} itemNode List item node that was selected.
+        @param {Object} result AutoComplete result object.
+        @preventable _defSelectFn
+        **/
+        this.publish(EVT_SELECT, {
+            defaultFn: this._defSelectFn
+        });
+    },
+
+    destructor: function () {
+        while (this._listEvents.length) {
+            this._listEvents.pop().detach();
+        }
+
+        if (this._ariaNode) {
+            this._ariaNode.remove().destroy(true);
+        }
+    },
+
+    bindUI: function () {
+        this._bindInput();
+        this._bindList();
+    },
+
+    renderUI: function () {
+        var ariaNode    = this._createAriaNode(),
+            boundingBox = this.get('boundingBox'),
+            contentBox  = this.get('contentBox'),
+            inputNode   = this._inputNode,
+            listNode    = this._createListNode(),
+            parentNode  = inputNode.get('parentNode');
+
+        inputNode.addClass(this.getClassName('input')).setAttrs({
+            'aria-autocomplete': LIST,
+            'aria-expanded'    : false,
+            'aria-owns'        : listNode.get('id')
+        });
+
+        // ARIA node must be outside the widget or announcements won't be made
+        // when the widget is hidden.
+        parentNode.append(ariaNode);
+
+        // Add an iframe shim for IE6.
+        if (useShim) {
+            boundingBox.plug(Y.Plugin.Shim);
+        }
+
+        this._ariaNode    = ariaNode;
+        this._boundingBox = boundingBox;
+        this._contentBox  = contentBox;
+        this._listNode    = listNode;
+        this._parentNode  = parentNode;
+    },
+
+    syncUI: function () {
+        // No need to call _syncPosition() here; the other _sync methods will
+        // call it when necessary.
+        this._syncResults();
+        this._syncVisibility();
+    },
+
+    // -- Public Prototype Methods ---------------------------------------------
+
+    /**
+    Hides the list, unless the `alwaysShowList` attribute is `true`.
+
+    @method hide
+    @see show
+    @chainable
+    **/
+    hide: function () {
+        return this.get(ALWAYS_SHOW_LIST) ? this : this.set(VISIBLE, false);
+    },
+
+    /**
+    Selects the specified _itemNode_, or the current `activeItem` if _itemNode_
+    is not specified.
+
+    @method selectItem
+    @param {Node} [itemNode] Item node to select.
+    @param {EventFacade} [originEvent] Event that triggered the selection, if
+        any.
+    @chainable
+    **/
+    selectItem: function (itemNode, originEvent) {
+        if (itemNode) {
+            if (!itemNode.hasClass(this[_CLASS_ITEM])) {
+                return this;
+            }
+        } else {
+            itemNode = this.get(ACTIVE_ITEM);
+
+            if (!itemNode) {
+                return this;
+            }
+        }
+
+        this.fire(EVT_SELECT, {
+            itemNode   : itemNode,
+            originEvent: originEvent || null,
+            result     : itemNode.getData(RESULT)
+        });
+
+        return this;
+    },
+
+    // -- Protected Prototype Methods ------------------------------------------
+
+    /**
+    Activates the next item after the currently active item. If there is no next
+    item and the `circular` attribute is `true`, focus will wrap back to the
+    input node.
+
+    @method _activateNextItem
+    @chainable
+    @protected
+    **/
+    _activateNextItem: function () {
+        var item = this.get(ACTIVE_ITEM),
+            nextItem;
+
+        if (item) {
+            nextItem = item.next(this[_SELECTOR_ITEM]) ||
+                    (this.get(CIRCULAR) ? null : item);
+        } else {
+            nextItem = this._getFirstItemNode();
+        }
+
+        this.set(ACTIVE_ITEM, nextItem);
+
+        return this;
+    },
+
+    /**
+    Activates the item previous to the currently active item. If there is no
+    previous item and the `circular` attribute is `true`, focus will wrap back
+    to the input node.
+
+    @method _activatePrevItem
+    @chainable
+    @protected
+    **/
+    _activatePrevItem: function () {
+        var item     = this.get(ACTIVE_ITEM),
+            prevItem = item ? item.previous(this[_SELECTOR_ITEM]) :
+                    this.get(CIRCULAR) && this._getLastItemNode();
+
+        this.set(ACTIVE_ITEM, prevItem || null);
+
+        return this;
+    },
+
+    /**
+    Appends the specified result _items_ to the list inside a new item node.
+
+    @method _add
+    @param {Array|Node|HTMLElement|String} items Result item or array of
+        result items.
+    @return {NodeList} Added nodes.
+    @protected
+    **/
+    _add: function (items) {
+        var itemNodes = [];
+
+        YArray.each(Lang.isArray(items) ? items : [items], function (item) {
+            itemNodes.push(this._createItemNode(item).setData(RESULT, item));
+        }, this);
+
+        itemNodes = Y.all(itemNodes);
+        this._listNode.append(itemNodes.toFrag());
+
+        return itemNodes;
+    },
+
+    /**
+    Updates the ARIA live region with the specified message.
+
+    @method _ariaSay
+    @param {String} stringId String id (from the `strings` attribute) of the
+        message to speak.
+    @param {Object} [subs] Substitutions for placeholders in the string.
+    @protected
+    **/
+    _ariaSay: function (stringId, subs) {
+        var message = this.get('strings.' + stringId);
+        this._ariaNode.set('text', subs ? Lang.sub(message, subs) : message);
+    },
+
+    /**
+    Binds `inputNode` events and behavior.
+
+    @method _bindInput
+    @protected
+    **/
+    _bindInput: function () {
+        var inputNode = this._inputNode,
+            alignNode, alignWidth, tokenInput;
+
+        // Null align means we can auto-align. Set align to false to prevent
+        // auto-alignment, or a valid alignment config to customize the
+        // alignment.
+        if (this.get('align') === null) {
+            // If this is a tokenInput, align with its bounding box.
+            // Otherwise, align with the inputNode. Bit of a cheat.
+            tokenInput = this.get('tokenInput');
+            alignNode  = (tokenInput && tokenInput.get('boundingBox')) || inputNode;
+
+            this.set('align', {
+                node  : alignNode,
+                points: ['tl', 'bl']
+            });
+
+            // If no width config is set, attempt to set the list's width to the
+            // width of the alignment node. If the alignment node's width is
+            // falsy, do nothing.
+            if (!this.get(WIDTH) && (alignWidth = alignNode.get('offsetWidth'))) {
+                this.set(WIDTH, alignWidth);
+            }
+        }
+
+        // Attach inputNode events.
+        this._listEvents = this._listEvents.concat([
+            inputNode.after('blur',  this._afterListInputBlur, this),
+            inputNode.after('focus', this._afterListInputFocus, this)
+        ]);
+    },
+
+    /**
+    Binds list events.
+
+    @method _bindList
+    @protected
+    **/
+    _bindList: function () {
+        this._listEvents = this._listEvents.concat([
+            Y.one('doc').after('click', this._afterDocClick, this),
+            Y.one('win').after('windowresize', this._syncPosition, this),
+
+            this.after({
+                mouseover: this._afterMouseOver,
+                mouseout : this._afterMouseOut,
+
+                activeItemChange    : this._afterActiveItemChange,
+                alwaysShowListChange: this._afterAlwaysShowListChange,
+                hoveredItemChange   : this._afterHoveredItemChange,
+                resultsChange       : this._afterResultsChange,
+                visibleChange       : this._afterVisibleChange
+            }),
+
+            this._listNode.delegate('click', this._onItemClick,
+                    this[_SELECTOR_ITEM], this)
+        ]);
+    },
+
+    /**
+    Clears the contents of the tray.
+
+    @method _clear
+    @protected
+    **/
+    _clear: function () {
+        this.set(ACTIVE_ITEM, null);
+        this._set(HOVERED_ITEM, null);
+
+        this._listNode.get('children').remove(true);
+    },
+
+    /**
+    Creates and returns an ARIA live region node.
+
+    @method _createAriaNode
+    @return {Node} ARIA node.
+    @protected
+    **/
+    _createAriaNode: function () {
+        var ariaNode = Node.create(this.ARIA_TEMPLATE);
+
+        return ariaNode.addClass(this.getClassName('aria')).setAttrs({
+            'aria-live': 'polite',
+            role       : 'status'
+        });
+    },
+
+    /**
+    Creates and returns an item node with the specified _content_.
+
+    @method _createItemNode
+    @param {Object} result Result object.
+    @return {Node} Item node.
+    @protected
+    **/
+    _createItemNode: function (result) {
+        var itemNode = Node.create(this.ITEM_TEMPLATE);
+
+        return itemNode.addClass(this[_CLASS_ITEM]).setAttrs({
+            id  : Y.stamp(itemNode),
+            role: 'option'
+        }).setAttribute('data-text', result.text).append(result.display);
+    },
+
+    /**
+    Creates and returns a list node. If the `listNode` attribute is already set
+    to an existing node, that node will be used.
+
+    @method _createListNode
+    @return {Node} List node.
+    @protected
+    **/
+    _createListNode: function () {
+        var listNode = this.get('listNode') || Node.create(this.LIST_TEMPLATE);
+
+        listNode.addClass(this.getClassName(LIST)).setAttrs({
+            id  : Y.stamp(listNode),
+            role: 'listbox'
+        });
+
+        this._set('listNode', listNode);
+        this.get('contentBox').append(listNode);
+
+        return listNode;
+    },
+
+    /**
+    Gets the first item node in the list, or `null` if the list is empty.
+
+    @method _getFirstItemNode
+    @return {Node|null}
+    @protected
+    **/
+    _getFirstItemNode: function () {
+        return this._listNode.one(this[_SELECTOR_ITEM]);
+    },
+
+    /**
+    Gets the last item node in the list, or `null` if the list is empty.
+
+    @method _getLastItemNode
+    @return {Node|null}
+    @protected
+    **/
+    _getLastItemNode: function () {
+        return this._listNode.one(this[_SELECTOR_ITEM] + ':last-child');
+    },
+
+    /**
+    Synchronizes the result list's position and alignment.
+
+    @method _syncPosition
+    @protected
+    **/
+    _syncPosition: function () {
+        // Force WidgetPositionAlign to refresh its alignment.
+        this._syncUIPosAlign();
+
+        // Resize the IE6 iframe shim to match the list's dimensions.
+        this._syncShim();
+    },
+
+    /**
+    Synchronizes the results displayed in the list with those in the _results_
+    argument, or with the `results` attribute if an argument is not provided.
+
+    @method _syncResults
+    @param {Array} [results] Results.
+    @protected
+    **/
+    _syncResults: function (results) {
+        if (!results) {
+            results = this.get(RESULTS);
+        }
+
+        this._clear();
+
+        if (results.length) {
+            this._add(results);
+            this._ariaSay('items_available');
+        }
+
+        this._syncPosition();
+
+        if (this.get('activateFirstItem') && !this.get(ACTIVE_ITEM)) {
+            this.set(ACTIVE_ITEM, this._getFirstItemNode());
+        }
+    },
+
+    /**
+    Synchronizes the size of the iframe shim used for IE6 and lower. In other
+    browsers, this method is a noop.
+
+    @method _syncShim
+    @protected
+    **/
+    _syncShim: useShim ? function () {
+        var shim = this._boundingBox.shim;
+
+        if (shim) {
+            shim.sync();
+        }
+    } : function () {},
+
+    /**
+    Synchronizes the visibility of the tray with the _visible_ argument, or with
+    the `visible` attribute if an argument is not provided.
+
+    @method _syncVisibility
+    @param {Boolean} [visible] Visibility.
+    @protected
+    **/
+    _syncVisibility: function (visible) {
+        if (this.get(ALWAYS_SHOW_LIST)) {
+            visible = true;
+            this.set(VISIBLE, visible);
+        }
+
+        if (typeof visible === 'undefined') {
+            visible = this.get(VISIBLE);
+        }
+
+        this._inputNode.set('aria-expanded', visible);
+        this._boundingBox.set('aria-hidden', !visible);
+
+        if (visible) {
+            this._syncPosition();
+        } else {
+            this.set(ACTIVE_ITEM, null);
+            this._set(HOVERED_ITEM, null);
+
+            // Force a reflow to work around a glitch in IE6 and 7 where some of
+            // the contents of the list will sometimes remain visible after the
+            // container is hidden.
+            this._boundingBox.get('offsetWidth');
+        }
+
+        // In some pages, IE7 fails to repaint the contents of the list after it
+        // becomes visible. Toggling a bogus class on the body forces a repaint
+        // that fixes the issue.
+        if (Y.UA.ie === 7) {
+            // Note: We don't actually need to use ClassNameManager here. This
+            // class isn't applying any actual styles; it's just frobbing the
+            // body element to force a repaint. The actual class name doesn't
+            // really matter.
+            Y.one('body')
+                .addClass('yui3-ie7-sucks')
+                .removeClass('yui3-ie7-sucks');
+        }
+    },
+
+    // -- Protected Event Handlers ---------------------------------------------
+
+    /**
+    Handles `activeItemChange` events.
+
+    @method _afterActiveItemChange
+    @param {EventFacade} e
+    @protected
+    **/
+    _afterActiveItemChange: function (e) {
+        var inputNode = this._inputNode,
+            newVal    = e.newVal,
+            prevVal   = e.prevVal,
+            node;
+
+        // The previous item may have disappeared by the time this handler runs,
+        // so we need to be careful.
+        if (prevVal && prevVal._node) {
+            prevVal.removeClass(this[_CLASS_ITEM_ACTIVE]);
+        }
+
+        if (newVal) {
+            newVal.addClass(this[_CLASS_ITEM_ACTIVE]);
+            inputNode.set('aria-activedescendant', newVal.get(ID));
+        } else {
+            inputNode.removeAttribute('aria-activedescendant');
+        }
+
+        if (this.get('scrollIntoView')) {
+            node = newVal || inputNode;
+
+            if (!node.inRegion(Y.DOM.viewportRegion(), true)
+                    || !node.inRegion(this._contentBox, true)) {
+
+                node.scrollIntoView();
+            }
+        }
+    },
+
+    /**
+    Handles `alwaysShowListChange` events.
+
+    @method _afterAlwaysShowListChange
+    @param {EventFacade} e
+    @protected
+    **/
+    _afterAlwaysShowListChange: function (e) {
+        this.set(VISIBLE, e.newVal || this.get(RESULTS).length > 0);
+    },
+
+    /**
+    Handles click events on the document. If the click is outside both the
+    input node and the bounding box, the list will be hidden.
+
+    @method _afterDocClick
+    @param {EventFacade} e
+    @protected
+    @since 3.5.0
+    **/
+    _afterDocClick: function (e) {
+        var boundingBox = this._boundingBox,
+            target      = e.target;
+
+        if (target !== this._inputNode && target !== boundingBox &&
+                !target.ancestor('#' + boundingBox.get('id'), true)){
+            this.hide();
+        }
+    },
+
+    /**
+    Handles `hoveredItemChange` events.
+
+    @method _afterHoveredItemChange
+    @param {EventFacade} e
+    @protected
+    **/
+    _afterHoveredItemChange: function (e) {
+        var newVal  = e.newVal,
+            prevVal = e.prevVal;
+
+        if (prevVal) {
+            prevVal.removeClass(this[_CLASS_ITEM_HOVER]);
+        }
+
+        if (newVal) {
+            newVal.addClass(this[_CLASS_ITEM_HOVER]);
+        }
+    },
+
+    /**
+    Handles `inputNode` blur events.
+
+    @method _afterListInputBlur
+    @protected
+    **/
+    _afterListInputBlur: function () {
+        this._listInputFocused = false;
+
+        if (this.get(VISIBLE) &&
+                !this._mouseOverList &&
+                (this._lastInputKey !== KEY_TAB ||
+                    !this.get('tabSelect') ||
+                    !this.get(ACTIVE_ITEM))) {
+            this.hide();
+        }
+    },
+
+    /**
+    Handles `inputNode` focus events.
+
+    @method _afterListInputFocus
+    @protected
+    **/
+    _afterListInputFocus: function () {
+        this._listInputFocused = true;
+    },
+
+    /**
+    Handles `mouseover` events.
+
+    @method _afterMouseOver
+    @param {EventFacade} e
+    @protected
+    **/
+    _afterMouseOver: function (e) {
+        var itemNode = e.domEvent.target.ancestor(this[_SELECTOR_ITEM], true);
+
+        this._mouseOverList = true;
+
+        if (itemNode) {
+            this._set(HOVERED_ITEM, itemNode);
+        }
+    },
+
+    /**
+    Handles `mouseout` events.
+
+    @method _afterMouseOut
+    @param {EventFacade} e
+    @protected
+    **/
+    _afterMouseOut: function () {
+        this._mouseOverList = false;
+        this._set(HOVERED_ITEM, null);
+    },
+
+    /**
+    Handles `resultsChange` events.
+
+    @method _afterResultsChange
+    @param {EventFacade} e
+    @protected
+    **/
+    _afterResultsChange: function (e) {
+        this._syncResults(e.newVal);
+
+        if (!this.get(ALWAYS_SHOW_LIST)) {
+            this.set(VISIBLE, !!e.newVal.length);
+        }
+    },
+
+    /**
+    Handles `visibleChange` events.
+
+    @method _afterVisibleChange
+    @param {EventFacade} e
+    @protected
+    **/
+    _afterVisibleChange: function (e) {
+        this._syncVisibility(!!e.newVal);
+    },
+
+    /**
+    Delegated event handler for item `click` events.
+
+    @method _onItemClick
+    @param {EventFacade} e
+    @protected
+    **/
+    _onItemClick: function (e) {
+        var itemNode = e.currentTarget;
+
+        this.set(ACTIVE_ITEM, itemNode);
+        this.selectItem(itemNode, e);
+    },
+
+    // -- Protected Default Event Handlers -------------------------------------
+
+    /**
+    Default `select` event handler.
+
+    @method _defSelectFn
+    @param {EventFacade} e
+    @protected
+    **/
+    _defSelectFn: function (e) {
+        var text = e.result.text;
+
+        // TODO: support typeahead completion, etc.
+        this._inputNode.focus();
+        this._updateValue(text);
+        this._ariaSay('item_selected', {item: text});
+        this.hide();
+    }
+}, {
+    ATTRS: {
+        /**
+        If `true`, the first item in the list will be activated by default when
+        the list is initially displayed and when results change.
+
+        @attribute activateFirstItem
+        @type Boolean
+        @default false
+        **/
+        activateFirstItem: {
+            value: false
+        },
+
+        /**
+        Item that's currently active, if any. When the user presses enter, this
+        is the item that will be selected.
+
+        @attribute activeItem
+        @type Node
+        **/
+        activeItem: {
+            setter: Y.one,
+            value: null
+        },
+
+        /**
+        If `true`, the list will remain visible even when there are no results
+        to display.
+
+        @attribute alwaysShowList
+        @type Boolean
+        @default false
+        **/
+        alwaysShowList: {
+            value: false
+        },
+
+        /**
+        If `true`, keyboard navigation will wrap around to the opposite end of
+        the list when navigating past the first or last item.
+
+        @attribute circular
+        @type Boolean
+        @default true
+        **/
+        circular: {
+            value: true
+        },
+
+        /**
+        Item currently being hovered over by the mouse, if any.
+
+        @attribute hoveredItem
+        @type Node|null
+        @readOnly
+        **/
+        hoveredItem: {
+            readOnly: true,
+            value: null
+        },
+
+        /**
+        Node that will contain result items.
+
+        @attribute listNode
+        @type Node|null
+        @initOnly
+        **/
+        listNode: {
+            writeOnce: 'initOnly',
+            value: null
+        },
+
+        /**
+        If `true`, the viewport will be scrolled to ensure that the active list
+        item is visible when necessary.
+
+        @attribute scrollIntoView
+        @type Boolean
+        @default false
+        **/
+        scrollIntoView: {
+            value: false
+        },
+
+        /**
+        Translatable strings used by the AutoCompleteList widget.
+
+        @attribute strings
+        @type Object
+        **/
+        strings: {
+            valueFn: function () {
+                return Y.Intl.get('autocomplete-list');
+            }
+        },
+
+        /**
+        If `true`, pressing the tab key while the list is visible will select
+        the active item, if any.
+
+        @attribute tabSelect
+        @type Boolean
+        @default true
+        **/
+        tabSelect: {
+            value: true
+        },
+
+        // The "visible" attribute is documented in Widget.
+        visible: {
+            value: false
+        }
+    },
+
+    CSS_PREFIX: Y.ClassNameManager.getClassName('aclist')
+});
+
+Y.AutoCompleteList = List;
+
+/**
+Alias for <a href="AutoCompleteList.html">`AutoCompleteList`</a>. See that class
+for API docs.
+
+@class AutoComplete
+**/
+
+Y.AutoComplete = List;
+
+
+}, '3.17.2', {
+    "lang": [
+        "en",
+        "es",
+        "hu",
+        "it"
+    ],
+    "requires": [
+        "autocomplete-base",
+        "event-resize",
+        "node-screen",
+        "selector-css3",
+        "shim-plugin",
+        "widget",
+        "widget-position",
+        "widget-position-align"
+    ],
+    "skinnable": true
+});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list.js Sat Jul 12 00:34:02 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("lang/autocomplete-list",function(e){e.Intl.add("autocomplete-list","",{item_selected:"{item} selected.",items_available:"Suggestions are available. Use the up and down arrow keys to select suggestions."})},"3.17.2");

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_en.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_en.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_en.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_en.js Sat Jul 12 00:34:02 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("lang/autocomplete-list_en",function(e){e.Intl.add("autocomplete-list","en",{item_selected:"{item} selected.",items_available:"Suggestions are available. Use up and down arrows to select."})},"3.17.2");

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_es.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_es.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_es.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_es.js Sat Jul 12 00:34:02 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("lang/autocomplete-list_es",function(e){e.Intl.add("autocomplete-list","es",{item_selected:"{item} seleccionado.",items_available:"Hay sugerencias disponibles.  Use flecha arriba y abajo para seleccionar."})},"3.17.2");

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_hu.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_hu.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_hu.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_hu.js Sat Jul 12 00:34:02 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("lang/autocomplete-list_hu",function(e){e.Intl.add("autocomplete-list","hu",{item_selected:"{item} kiv\u00e1lasztva.",items_available:"Javaslatok \u00e1llnak rendelkez\u00e9sre. Haszn\u00e1lja a fel \u00e9s le nyilakat a v\u00e1laszt\u00e1shoz."})},"3.17.2");

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_it.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_it.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_it.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-list/lang/autocomplete-list_it.js Sat Jul 12 00:34:02 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("lang/autocomplete-list_it",function(e){e.Intl.add("autocomplete-list","it",{item_selected:"{item} selezionato.",items_available:"Sono disponibili suggerimenti. Usa la freccia su e gi\u00f9 per selezionare."})},"3.17.2");

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-plugin/autocomplete-plugin-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-plugin/autocomplete-plugin-min.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-plugin/autocomplete-plugin-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-plugin/autocomplete-plugin-min.js Sat Jul 12 00:34:02 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("autocomplete-plugin",function(e,t){function r(e){e.inputNode=e.host,!e.render&&e.render!==!1&&(e.render=!0),r.superclass.constructor.apply(this,arguments)}var n=e.Plugin;e.extend(r,e.AutoCompleteList,{},{NAME:"autocompleteListPlugin",NS:"ac",CSS_PREFIX:e.ClassNameManager.getClassName("aclist")}),n.AutoComplete=r,n.AutoCompleteList=r},"3.17.2",{requires:["autocomplete-list","node-pluginhost"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-plugin/autocomplete-plugin.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-plugin/autocomplete-plugin.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-plugin/autocomplete-plugin.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-plugin/autocomplete-plugin.js Sat Jul 12 00:34:02 2014
@@ -0,0 +1,55 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('autocomplete-plugin', function (Y, NAME) {
+
+/**
+Binds an AutoCompleteList instance to a Node instance.
+
+@module autocomplete
+@submodule autocomplete-plugin
+**/
+
+/**
+Binds an AutoCompleteList instance to a Node instance.
+
+@example
+
+    Y.one('#my-input').plug(Y.Plugin.AutoComplete, {
+        source: 'select * from search.suggest where query="{query}"'
+    });
+
+    // You can now access the AutoCompleteList instance at Y.one('#my-input').ac
+
+@class Plugin.AutoComplete
+@extends AutoCompleteList
+**/
+
+var Plugin = Y.Plugin;
+
+function ACListPlugin(config) {
+    config.inputNode = config.host;
+
+    // Render by default.
+    if (!config.render && config.render !== false) {
+      config.render = true;
+    }
+
+    ACListPlugin.superclass.constructor.apply(this, arguments);
+}
+
+Y.extend(ACListPlugin, Y.AutoCompleteList, {}, {
+    NAME      : 'autocompleteListPlugin',
+    NS        : 'ac',
+    CSS_PREFIX: Y.ClassNameManager.getClassName('aclist')
+});
+
+Plugin.AutoComplete     = ACListPlugin;
+Plugin.AutoCompleteList = ACListPlugin;
+
+
+}, '3.17.2', {"requires": ["autocomplete-list", "node-pluginhost"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-sources/autocomplete-sources-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-sources/autocomplete-sources-min.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-sources/autocomplete-sources-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-sources/autocomplete-sources-min.js Sat Jul 12 00:34:02 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("autocomplete-sources",function(e,t){var n=e.AutoCompleteBase,r=e.Lang,i="_sourceSuccess",s="maxResults",o="requestTemplate",u="resultListLocator";e.mix(n.prototype,{_YQL_SOURCE_REGEX:/^(?:select|set|use)\s+/i,_beforeCreateObjectSource:function(t){return t instanceof e.Node&&t.get("nodeName").toLowerCase()==="select"?this._createSelectSource(t):e.JSONPRequest&&t instanceof e.JSONPRequest?this._createJSONPSource(t):this._createObjectSource(t)},_createIOSource:function(t){function a(n){var o=n.request;if(r._cache&&o in r._cache){r[i](r._cache[o],n);return}s&&s.isInProgress()&&s.abort(),s=e.io(r._getXHRUrl(t,n),{on:{success:function(t,s){var u;try{u=e.JSON.parse(s.responseText)}catch(a){e.error("JSON parse error",a)}u&&(r._cache&&(r._cache[o]=u),r[i](u,n))}}})}var n={type:"io"},r=this,s,o,u;return n.sendRequest=function(t){o=t;if(u)return;u=!0,e.use("io-base","json-parse",function(){n.sendRequest=a,a(o)})},n},_createJSONPSource:function(t){function u(e){var n=e.request,s=e.quer
 y;if(r._cache&&n in r._cache){r[i](r._cache[n],e);return}t._config.on.success=function(t){r._cache&&(r._cache[n]=t),r[i](t,e)},t.send(s)}var n={type:"jsonp"},r=this,s,o;return n.sendRequest=function(i){s=i;if(o)return;o=!0,e.use("jsonp",function(){t instanceof e.JSONPRequest||(t=new e.JSONPRequest(t,{format:e.bind(r._jsonpFormatter,r)})),n.sendRequest=u,u(s)})},n},_createSelectSource:function(e){var t=this;return{type:"select",sendRequest:function(n){var r=[];e.get("options").each(function(e){r.push({html:e.get("innerHTML"),index:e.get("index"),node:e,selected:e.get("selected"),text:e.get("text"),value:e.get("value")})}),t[i](r,n)}}},_createStringSource:function(e){return this._YQL_SOURCE_REGEX.test(e)?this._createYQLSource(e):e.indexOf("{callback}")!==-1?this._createJSONPSource(e):this._createIOSource(e)},_createYQLSource:function(t){function c(o){var u=o.query,a=n.get("yqlEnv"),f=n.get(s),c,h,p;p=r.sub(t,{maxResults:f>0?f:1e3,request:o.request,query:u});if(n._cache&&p in n._cache)
 {n[i](n._cache[p],o);return}c=function(e){n._cache&&(n._cache[p]=e),n[i](e,o)},h={proto:n.get("yqlProtocol")},l?(l._callback=c,l._opts=h,l._params.q=p,a&&(l._params.env=a)):l=new e.YQLRequest(p,{on:{success:c},allowCache:!1},a?{env:a}:null,h),l.send()}var n=this,o={type:"yql"},a,f,l;return n.get(u)||n.set(u,n._defaultYQLLocator),o.sendRequest=function(t){a=t,f||(f=!0,e.use("yql",function(){o.sendRequest=c,c(a)}))},o},_defaultYQLLocator:function(t){var n=t&&t.query&&t.query.results,i;return n&&r.isObject(n)?(i=e.Object.values(n)||[],n=i.length===1?i[0]:i,r.isArray(n)||(n=[n])):n=[],n},_getXHRUrl:function(e,t){var n=this.get(s);return t.query!==t.request&&(e+=t.request),r.sub(e,{maxResults:n>0?n:1e3,query:encodeURIComponent(t.query)})},_jsonpFormatter:function(e,t,n){var i=this.get(s),u=this.get(o);return u&&(e+=u(n)),r.sub(e,{callback:t,maxResults:i>0?i:1e3,query:encodeURIComponent(n)})}}),e.mix(n.ATTRS,{yqlEnv:{value:null},yqlProtocol:{value:"http"}}),e.mix(n.SOURCE_TYPES,{io:"_crea
 teIOSource",jsonp:"_createJSONPSource",object:"_beforeCreateObjectSource",select:"_createSelectSource",string:"_createStringSource",yql:"_createYQLSource"},!0)},"3.17.2",{optional:["io-base","json-parse","jsonp","yql"],requires:["autocomplete-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-sources/autocomplete-sources.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-sources/autocomplete-sources.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-sources/autocomplete-sources.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/autocomplete-sources/autocomplete-sources.js Sat Jul 12 00:34:02 2014
@@ -0,0 +1,482 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('autocomplete-sources', function (Y, NAME) {
+
+/**
+Mixes support for JSONP and YQL result sources into AutoCompleteBase.
+
+@module autocomplete
+@submodule autocomplete-sources
+**/
+
+var ACBase = Y.AutoCompleteBase,
+    Lang   = Y.Lang,
+
+    _SOURCE_SUCCESS = '_sourceSuccess',
+
+    MAX_RESULTS         = 'maxResults',
+    REQUEST_TEMPLATE    = 'requestTemplate',
+    RESULT_LIST_LOCATOR = 'resultListLocator';
+
+// Add prototype properties and methods to AutoCompleteBase.
+Y.mix(ACBase.prototype, {
+    /**
+    Regular expression used to determine whether a String source is a YQL query.
+
+    @property _YQL_SOURCE_REGEX
+    @type RegExp
+    @protected
+    @for AutoCompleteBase
+    **/
+    _YQL_SOURCE_REGEX: /^(?:select|set|use)\s+/i,
+
+    /**
+    Runs before AutoCompleteBase's `_createObjectSource()` method and augments
+    it to support additional object-based source types.
+
+    @method _beforeCreateObjectSource
+    @param {String} source
+    @protected
+    @for AutoCompleteBase
+    **/
+    _beforeCreateObjectSource: function (source) {
+        // If the object is a <select> node, use the options as the result
+        // source.
+        if (source instanceof Y.Node &&
+                source.get('nodeName').toLowerCase() === 'select') {
+
+            return this._createSelectSource(source);
+        }
+
+        // If the object is a JSONPRequest instance, try to use it as a JSONP
+        // source.
+        if (Y.JSONPRequest && source instanceof Y.JSONPRequest) {
+            return this._createJSONPSource(source);
+        }
+
+        // Fall back to a basic object source.
+        return this._createObjectSource(source);
+    },
+
+    /**
+    Creates a DataSource-like object that uses `Y.io` as a source. See the
+    `source` attribute for more details.
+
+    @method _createIOSource
+    @param {String} source URL.
+    @return {Object} DataSource-like object.
+    @protected
+    @for AutoCompleteBase
+    **/
+    _createIOSource: function (source) {
+        var ioSource = {type: 'io'},
+            that     = this,
+            ioRequest, lastRequest, loading;
+
+        // Private internal _sendRequest method that will be assigned to
+        // ioSource.sendRequest once io-base and json-parse are available.
+        function _sendRequest(request) {
+            var cacheKey = request.request;
+
+            // Return immediately on a cached response.
+            if (that._cache && cacheKey in that._cache) {
+                that[_SOURCE_SUCCESS](that._cache[cacheKey], request);
+                return;
+            }
+
+            // Cancel any outstanding requests.
+            if (ioRequest && ioRequest.isInProgress()) {
+                ioRequest.abort();
+            }
+
+            ioRequest = Y.io(that._getXHRUrl(source, request), {
+                on: {
+                    success: function (tid, response) {
+                        var data;
+
+                        try {
+                            data = Y.JSON.parse(response.responseText);
+                        } catch (ex) {
+                            Y.error('JSON parse error', ex);
+                        }
+
+                        if (data) {
+                            that._cache && (that._cache[cacheKey] = data);
+                            that[_SOURCE_SUCCESS](data, request);
+                        }
+                    }
+                }
+            });
+        }
+
+        ioSource.sendRequest = function (request) {
+            // Keep track of the most recent request in case there are multiple
+            // requests while we're waiting for the IO module to load. Only the
+            // most recent request will be sent.
+            lastRequest = request;
+
+            if (loading) { return; }
+
+            loading = true;
+
+            // Lazy-load the io-base and json-parse modules if necessary,
+            // then overwrite the sendRequest method to bypass this check in
+            // the future.
+            Y.use('io-base', 'json-parse', function () {
+                ioSource.sendRequest = _sendRequest;
+                _sendRequest(lastRequest);
+            });
+        };
+
+        return ioSource;
+    },
+
+    /**
+    Creates a DataSource-like object that uses the specified JSONPRequest
+    instance as a source. See the `source` attribute for more details.
+
+    @method _createJSONPSource
+    @param {JSONPRequest|String} source URL string or JSONPRequest instance.
+    @return {Object} DataSource-like object.
+    @protected
+    @for AutoCompleteBase
+    **/
+    _createJSONPSource: function (source) {
+        var jsonpSource = {type: 'jsonp'},
+            that        = this,
+            lastRequest, loading;
+
+        function _sendRequest(request) {
+            var cacheKey = request.request,
+                query    = request.query;
+
+            if (that._cache && cacheKey in that._cache) {
+                that[_SOURCE_SUCCESS](that._cache[cacheKey], request);
+                return;
+            }
+
+            // Hack alert: JSONPRequest currently doesn't support
+            // per-request callbacks, so we're reaching into the protected
+            // _config object to make it happen.
+            //
+            // This limitation is mentioned in the following JSONP
+            // enhancement ticket:
+            //
+            // http://yuilibrary.com/projects/yui3/ticket/2529371
+            source._config.on.success = function (data) {
+                that._cache && (that._cache[cacheKey] = data);
+                that[_SOURCE_SUCCESS](data, request);
+            };
+
+            source.send(query);
+        }
+
+        jsonpSource.sendRequest = function (request) {
+            // Keep track of the most recent request in case there are multiple
+            // requests while we're waiting for the JSONP module to load. Only
+            // the most recent request will be sent.
+            lastRequest = request;
+
+            if (loading) { return; }
+
+            loading = true;
+
+            // Lazy-load the JSONP module if necessary, then overwrite the
+            // sendRequest method to bypass this check in the future.
+            Y.use('jsonp', function () {
+                // Turn the source into a JSONPRequest instance if it isn't
+                // one already.
+                if (!(source instanceof Y.JSONPRequest)) {
+                    source = new Y.JSONPRequest(source, {
+                        format: Y.bind(that._jsonpFormatter, that)
+                    });
+                }
+
+                jsonpSource.sendRequest = _sendRequest;
+                _sendRequest(lastRequest);
+            });
+        };
+
+        return jsonpSource;
+    },
+
+    /**
+    Creates a DataSource-like object that uses the specified `<select>` node as
+    a source.
+
+    @method _createSelectSource
+    @param {Node} source YUI Node instance wrapping a `<select>` node.
+    @return {Object} DataSource-like object.
+    @protected
+    @for AutoCompleteBase
+    **/
+    _createSelectSource: function (source) {
+        var that = this;
+
+        return {
+            type: 'select',
+            sendRequest: function (request) {
+                var options = [];
+
+                source.get('options').each(function (option) {
+                    options.push({
+                        html    : option.get('innerHTML'),
+                        index   : option.get('index'),
+                        node    : option,
+                        selected: option.get('selected'),
+                        text    : option.get('text'),
+                        value   : option.get('value')
+                    });
+                });
+
+                that[_SOURCE_SUCCESS](options, request);
+            }
+        };
+    },
+
+    /**
+    Creates a DataSource-like object that calls the specified  URL or executes
+    the specified YQL query for results. If the string starts with "select ",
+    "use ", or "set " (case-insensitive), it's assumed to be a YQL query;
+    otherwise, it's assumed to be a URL (which may be absolute or relative).
+    URLs containing a "{callback}" placeholder are assumed to be JSONP URLs; all
+    others will use XHR. See the `source` attribute for more details.
+
+    @method _createStringSource
+    @param {String} source URL or YQL query.
+    @return {Object} DataSource-like object.
+    @protected
+    @for AutoCompleteBase
+    **/
+    _createStringSource: function (source) {
+        if (this._YQL_SOURCE_REGEX.test(source)) {
+            // Looks like a YQL query.
+            return this._createYQLSource(source);
+        } else if (source.indexOf('{callback}') !== -1) {
+            // Contains a {callback} param and isn't a YQL query, so it must be
+            // JSONP.
+            return this._createJSONPSource(source);
+        } else {
+            // Not a YQL query or JSONP, so we'll assume it's an XHR URL.
+            return this._createIOSource(source);
+        }
+    },
+
+    /**
+    Creates a DataSource-like object that uses the specified YQL query string to
+    create a YQL-based source. See the `source` attribute for details. If no
+    `resultListLocator` is defined, this method will set a best-guess locator
+    that might work for many typical YQL queries.
+
+    @method _createYQLSource
+    @param {String} source YQL query.
+    @return {Object} DataSource-like object.
+    @protected
+    @for AutoCompleteBase
+    **/
+    _createYQLSource: function (source) {
+        var that      = this,
+            yqlSource = {type: 'yql'},
+            lastRequest, loading, yqlRequest;
+
+        if (!that.get(RESULT_LIST_LOCATOR)) {
+            that.set(RESULT_LIST_LOCATOR, that._defaultYQLLocator);
+        }
+
+        function _sendRequest(request) {
+            var query      = request.query,
+                env        = that.get('yqlEnv'),
+                maxResults = that.get(MAX_RESULTS),
+                callback, opts, yqlQuery;
+
+            yqlQuery = Lang.sub(source, {
+                maxResults: maxResults > 0 ? maxResults : 1000,
+                request   : request.request,
+                query     : query
+            });
+
+            if (that._cache && yqlQuery in that._cache) {
+                that[_SOURCE_SUCCESS](that._cache[yqlQuery], request);
+                return;
+            }
+
+            callback = function (data) {
+                that._cache && (that._cache[yqlQuery] = data);
+                that[_SOURCE_SUCCESS](data, request);
+            };
+
+            opts = {proto: that.get('yqlProtocol')};
+
+            // Only create a new YQLRequest instance if this is the
+            // first request. For subsequent requests, we'll reuse the
+            // original instance.
+            if (yqlRequest) {
+                yqlRequest._callback   = callback;
+                yqlRequest._opts       = opts;
+                yqlRequest._params.q   = yqlQuery;
+
+                if (env) {
+                    yqlRequest._params.env = env;
+                }
+            } else {
+                yqlRequest = new Y.YQLRequest(yqlQuery, {
+                    on: {success: callback},
+                    allowCache: false // temp workaround until JSONP has per-URL callback proxies
+                }, env ? {env: env} : null, opts);
+            }
+
+            yqlRequest.send();
+        }
+
+        yqlSource.sendRequest = function (request) {
+            // Keep track of the most recent request in case there are multiple
+            // requests while we're waiting for the YQL module to load. Only the
+            // most recent request will be sent.
+            lastRequest = request;
+
+            if (!loading) {
+                // Lazy-load the YQL module if necessary, then overwrite the
+                // sendRequest method to bypass this check in the future.
+                loading = true;
+
+                Y.use('yql', function () {
+                    yqlSource.sendRequest = _sendRequest;
+                    _sendRequest(lastRequest);
+                });
+            }
+        };
+
+        return yqlSource;
+    },
+
+    /**
+    Default resultListLocator used when a string-based YQL source is set and the
+    implementer hasn't already specified one.
+
+    @method _defaultYQLLocator
+    @param {Object} response YQL response object.
+    @return {Array}
+    @protected
+    @for AutoCompleteBase
+    **/
+    _defaultYQLLocator: function (response) {
+        var results = response && response.query && response.query.results,
+            values;
+
+        if (results && Lang.isObject(results)) {
+            // If there's only a single value on YQL's results object, that
+            // value almost certainly contains the array of results we want. If
+            // there are 0 or 2+ values, then the values themselves are most
+            // likely the results we want.
+            values  = Y.Object.values(results) || [];
+            results = values.length === 1 ? values[0] : values;
+
+            if (!Lang.isArray(results)) {
+                results = [results];
+            }
+        } else {
+            results = [];
+        }
+
+        return results;
+    },
+
+    /**
+    Returns a formatted XHR URL based on the specified base _url_, _query_, and
+    the current _requestTemplate_ if any.
+
+    @method _getXHRUrl
+    @param {String} url Base URL.
+    @param {Object} request Request object containing `query` and `request`
+      properties.
+    @return {String} Formatted URL.
+    @protected
+    @for AutoCompleteBase
+    **/
+    _getXHRUrl: function (url, request) {
+        var maxResults = this.get(MAX_RESULTS);
+
+        if (request.query !== request.request) {
+            // Append the request template to the URL.
+            url += request.request;
+        }
+
+        return Lang.sub(url, {
+            maxResults: maxResults > 0 ? maxResults : 1000,
+            query     : encodeURIComponent(request.query)
+        });
+    },
+
+    /**
+    URL formatter passed to `JSONPRequest` instances.
+
+    @method _jsonpFormatter
+    @param {String} url
+    @param {String} proxy
+    @param {String} query
+    @return {String} Formatted URL
+    @protected
+    @for AutoCompleteBase
+    **/
+    _jsonpFormatter: function (url, proxy, query) {
+        var maxResults      = this.get(MAX_RESULTS),
+            requestTemplate = this.get(REQUEST_TEMPLATE);
+
+        if (requestTemplate) {
+            url += requestTemplate(query);
+        }
+
+        return Lang.sub(url, {
+            callback  : proxy,
+            maxResults: maxResults > 0 ? maxResults : 1000,
+            query     : encodeURIComponent(query)
+        });
+    }
+});
+
+// Add attributes to AutoCompleteBase.
+Y.mix(ACBase.ATTRS, {
+    /**
+    YQL environment file URL to load when the `source` is set to a YQL query.
+    Set this to `null` to use the default Open Data Tables environment file
+    (http://datatables.org/alltables.env).
+
+    @attribute yqlEnv
+    @type String
+    @default null
+    @for AutoCompleteBase
+    **/
+    yqlEnv: {
+        value: null
+    },
+
+    /**
+    URL protocol to use when the `source` is set to a YQL query.
+
+    @attribute yqlProtocol
+    @type String
+    @default 'http'
+    @for AutoCompleteBase
+    **/
+    yqlProtocol: {
+        value: 'http'
+    }
+});
+
+// Tell AutoCompleteBase about the new source types it can now support.
+Y.mix(ACBase.SOURCE_TYPES, {
+    io    : '_createIOSource',
+    jsonp : '_createJSONPSource',
+    object: '_beforeCreateObjectSource', // Run our version before the base version.
+    select: '_createSelectSource',
+    string: '_createStringSource',
+    yql   : '_createYQLSource'
+}, true);
+
+
+}, '3.17.2', {"optional": ["io-base", "json-parse", "jsonp", "yql"], "requires": ["autocomplete-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-base/dataschema-base-debug.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-base/dataschema-base-debug.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-base/dataschema-base-debug.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-base/dataschema-base-debug.js Sat Jul 12 00:34:02 2014
@@ -0,0 +1,74 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('dataschema-base', function (Y, NAME) {
+
+/**
+ * The DataSchema utility provides a common configurable interface for widgets to
+ * apply a given schema to a variety of data.
+ *
+ * @module dataschema
+ * @main dataschema
+ */
+
+/**
+ * Provides the base DataSchema implementation, which can be extended to
+ * create DataSchemas for specific data formats, such XML, JSON, text and
+ * arrays.
+ *
+ * @module dataschema
+ * @submodule dataschema-base
+ */
+
+var LANG = Y.Lang,
+/**
+ * Base class for the YUI DataSchema Utility.
+ * @class DataSchema.Base
+ * @static
+ */
+    SchemaBase = {
+    /**
+     * Overridable method returns data as-is.
+     *
+     * @method apply
+     * @param schema {Object} Schema to apply.
+     * @param data {Object} Data.
+     * @return {Object} Schema-parsed data.
+     * @static
+     */
+    apply: function(schema, data) {
+        return data;
+    },
+
+    /**
+     * Applies field parser, if defined
+     *
+     * @method parse
+     * @param value {Object} Original value.
+     * @param field {Object} Field.
+     * @return {Object} Type-converted value.
+     */
+    parse: function(value, field) {
+        if(field.parser) {
+            var parser = (LANG.isFunction(field.parser)) ?
+            field.parser : Y.Parsers[field.parser+''];
+            if(parser) {
+                value = parser.call(this, value);
+            }
+            else {
+                Y.log("Could not find parser for field " + Y.dump(field), "warn", "dataschema-json");
+            }
+        }
+        return value;
+    }
+};
+
+Y.namespace("DataSchema").Base = SchemaBase;
+Y.namespace("Parsers");
+
+
+}, '3.17.2', {"requires": ["base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-base/dataschema-base-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-base/dataschema-base-min.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-base/dataschema-base-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-base/dataschema-base-min.js Sat Jul 12 00:34:02 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("dataschema-base",function(e,t){var n=e.Lang,r={apply:function(e,t){return t},parse:function(t,r){if(r.parser){var i=n.isFunction(r.parser)?r.parser:e.Parsers[r.parser+""];i&&(t=i.call(this,t))}return t}};e.namespace("DataSchema").Base=r,e.namespace("Parsers")},"3.17.2",{requires:["base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-json/dataschema-json-debug.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-json/dataschema-json-debug.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-json/dataschema-json-debug.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-json/dataschema-json-debug.js Sat Jul 12 00:34:02 2014
@@ -0,0 +1,450 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('dataschema-json', function (Y, NAME) {
+
+/**
+Provides a DataSchema implementation which can be used to work with JSON data.
+
+@module dataschema
+@submodule dataschema-json
+**/
+
+/**
+Provides a DataSchema implementation which can be used to work with JSON data.
+
+See the `apply` method for usage.
+
+@class DataSchema.JSON
+@extends DataSchema.Base
+@static
+**/
+var LANG = Y.Lang,
+    isFunction = LANG.isFunction,
+    isObject   = LANG.isObject,
+    isArray    = LANG.isArray,
+    // TODO: I don't think the calls to Base.* need to be done via Base since
+    // Base is mixed into SchemaJSON.  Investigate for later.
+    Base       = Y.DataSchema.Base,
+
+    SchemaJSON;
+
+SchemaJSON = {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DataSchema.JSON static methods
+//
+/////////////////////////////////////////////////////////////////////////////
+    /**
+     * Utility function converts JSON locator strings into walkable paths
+     *
+     * @method getPath
+     * @param locator {String} JSON value locator.
+     * @return {String[]} Walkable path to data value.
+     * @static
+     */
+    getPath: function(locator) {
+        var path = null,
+            keys = [],
+            i = 0;
+
+        if (locator) {
+            // Strip the ["string keys"] and [1] array indexes
+            // TODO: the first two steps can probably be reduced to one with
+            // /\[\s*(['"])?(.*?)\1\s*\]/g, but the array indices would be
+            // stored as strings.  This is not likely an issue.
+            locator = locator.
+                replace(/\[\s*(['"])(.*?)\1\s*\]/g,
+                function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
+                replace(/\[(\d+)\]/g,
+                function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
+                replace(/^\./,''); // remove leading dot
+
+            // Validate against problematic characters.
+            // commented out because the path isn't sent to eval, so it
+            // should be safe. I'm not sure what makes a locator invalid.
+            //if (!/[^\w\.\$@]/.test(locator)) {
+            path = locator.split('.');
+            for (i=path.length-1; i >= 0; --i) {
+                if (path[i].charAt(0) === '@') {
+                    path[i] = keys[parseInt(path[i].substr(1),10)];
+                }
+            }
+            /*}
+            else {
+                Y.log("Invalid locator: " + locator, "error", "dataschema-json");
+            }
+            */
+        }
+        return path;
+    },
+
+    /**
+     * Utility function to walk a path and return the value located there.
+     *
+     * @method getLocationValue
+     * @param path {String[]} Locator path.
+     * @param data {String} Data to traverse.
+     * @return {Object} Data value at location.
+     * @static
+     */
+    getLocationValue: function (path, data) {
+        var i = 0,
+            len = path.length;
+        for (;i<len;i++) {
+            if (isObject(data) && (path[i] in data)) {
+                data = data[path[i]];
+            } else {
+                data = undefined;
+                break;
+            }
+        }
+        return data;
+    },
+
+    /**
+    Applies a schema to an array of data located in a JSON structure, returning
+    a normalized object with results in the `results` property. Additional
+    information can be parsed out of the JSON for inclusion in the `meta`
+    property of the response object.  If an error is encountered during
+    processing, an `error` property will be added.
+
+    The input _data_ is expected to be an object or array.  If it is a string,
+    it will be passed through `Y.JSON.parse()`.
+
+    If _data_ contains an array of data records to normalize, specify the
+    _schema.resultListLocator_ as a dot separated path string just as you would
+    reference it in JavaScript.  So if your _data_ object has a record array at
+    _data.response.results_, use _schema.resultListLocator_ =
+    "response.results". Bracket notation can also be used for array indices or
+    object properties (e.g. "response['results']");  This is called a "path
+    locator"
+
+    Field data in the result list is extracted with field identifiers in
+    _schema.resultFields_.  Field identifiers are objects with the following
+    properties:
+
+      * `key`   : <strong>(required)</strong> The path locator (String)
+      * `parser`: A function or the name of a function on `Y.Parsers` used
+            to convert the input value into a normalized type.  Parser
+            functions are passed the value as input and are expected to
+            return a value.
+
+    If no value parsing is needed, you can use path locators (strings)
+    instead of field identifiers (objects) -- see example below.
+
+    If no processing of the result list array is needed, _schema.resultFields_
+    can be omitted; the `response.results` will point directly to the array.
+
+    If the result list contains arrays, `response.results` will contain an
+    array of objects with key:value pairs assuming the fields in
+    _schema.resultFields_ are ordered in accordance with the data array
+    values.
+
+    If the result list contains objects, the identified _schema.resultFields_
+    will be used to extract a value from those objects for the output result.
+
+    To extract additional information from the JSON, include an array of
+    path locators in _schema.metaFields_.  The collected values will be
+    stored in `response.meta`.
+
+
+    @example
+        // Process array of arrays
+        var schema = {
+                resultListLocator: 'produce.fruit',
+                resultFields: [ 'name', 'color' ]
+            },
+            data = {
+                produce: {
+                    fruit: [
+                        [ 'Banana', 'yellow' ],
+                        [ 'Orange', 'orange' ],
+                        [ 'Eggplant', 'purple' ]
+                    ]
+                }
+            };
+
+        var response = Y.DataSchema.JSON.apply(schema, data);
+
+        // response.results[0] is { name: "Banana", color: "yellow" }
+
+
+        // Process array of objects + some metadata
+        schema.metaFields = [ 'lastInventory' ];
+
+        data = {
+            produce: {
+                fruit: [
+                    { name: 'Banana', color: 'yellow', price: '1.96' },
+                    { name: 'Orange', color: 'orange', price: '2.04' },
+                    { name: 'Eggplant', color: 'purple', price: '4.31' }
+                ]
+            },
+            lastInventory: '2011-07-19'
+        };
+
+        response = Y.DataSchema.JSON.apply(schema, data);
+
+        // response.results[0] is { name: "Banana", color: "yellow" }
+        // response.meta.lastInventory is '2001-07-19'
+
+
+        // Use parsers
+        schema.resultFields = [
+            {
+                key: 'name',
+                parser: function (val) { return val.toUpperCase(); }
+            },
+            {
+                key: 'price',
+                parser: 'number' // Uses Y.Parsers.number
+            }
+        ];
+
+        response = Y.DataSchema.JSON.apply(schema, data);
+
+        // Note price was converted from a numeric string to a number
+        // response.results[0] looks like { fruit: "BANANA", price: 1.96 }
+
+    @method apply
+    @param {Object} [schema] Schema to apply.  Supported configuration
+        properties are:
+      @param {String} [schema.resultListLocator] Path locator for the
+          location of the array of records to flatten into `response.results`
+      @param {Array} [schema.resultFields] Field identifiers to
+          locate/assign values in the response records. See above for
+          details.
+      @param {Array} [schema.metaFields] Path locators to extract extra
+          non-record related information from the data object.
+    @param {Object|Array|String} data JSON data or its string serialization.
+    @return {Object} An Object with properties `results` and `meta`
+    @static
+    **/
+    apply: function(schema, data) {
+        var data_in = data,
+            data_out = { results: [], meta: {} };
+
+        // Convert incoming JSON strings
+        if (!isObject(data)) {
+            try {
+                data_in = Y.JSON.parse(data);
+            }
+            catch(e) {
+                data_out.error = e;
+                return data_out;
+            }
+        }
+
+        if (isObject(data_in) && schema) {
+            // Parse results data
+            data_out = SchemaJSON._parseResults.call(this, schema, data_in, data_out);
+
+            // Parse meta data
+            if (schema.metaFields !== undefined) {
+                data_out = SchemaJSON._parseMeta(schema.metaFields, data_in, data_out);
+            }
+        }
+        else {
+            Y.log("JSON data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-json");
+            data_out.error = new Error("JSON schema parse failure");
+        }
+
+        return data_out;
+    },
+
+    /**
+     * Schema-parsed list of results from full data
+     *
+     * @method _parseResults
+     * @param schema {Object} Schema to parse against.
+     * @param json_in {Object} JSON to parse.
+     * @param data_out {Object} In-progress parsed data to update.
+     * @return {Object} Parsed data object.
+     * @static
+     * @protected
+     */
+    _parseResults: function(schema, json_in, data_out) {
+        var getPath  = SchemaJSON.getPath,
+            getValue = SchemaJSON.getLocationValue,
+            path     = getPath(schema.resultListLocator),
+            results  = path ?
+                        (getValue(path, json_in) ||
+                         // Fall back to treat resultListLocator as a simple key
+                            json_in[schema.resultListLocator]) :
+                        // Or if no resultListLocator is supplied, use the input
+                        json_in;
+
+        if (isArray(results)) {
+            // if no result fields are passed in, then just take
+            // the results array whole-hog Sometimes you're getting
+            // an array of strings, or want the whole object, so
+            // resultFields don't make sense.
+            if (isArray(schema.resultFields)) {
+                data_out = SchemaJSON._getFieldValues.call(this, schema.resultFields, results, data_out);
+            } else {
+                data_out.results = results;
+            }
+        } else if (schema.resultListLocator) {
+            data_out.results = [];
+            data_out.error = new Error("JSON results retrieval failure");
+            Y.log("JSON data could not be parsed: " + Y.dump(json_in), "error", "dataschema-json");
+        }
+
+        return data_out;
+    },
+
+    /**
+     * Get field data values out of list of full results
+     *
+     * @method _getFieldValues
+     * @param fields {Array} Fields to find.
+     * @param array_in {Array} Results to parse.
+     * @param data_out {Object} In-progress parsed data to update.
+     * @return {Object} Parsed data object.
+     * @static
+     * @protected
+     */
+    _getFieldValues: function(fields, array_in, data_out) {
+        var results = [],
+            len = fields.length,
+            i, j,
+            field, key, locator, path, parser, val,
+            simplePaths = [], complexPaths = [], fieldParsers = [],
+            result, record;
+
+        // First collect hashes of simple paths, complex paths, and parsers
+        for (i=0; i<len; i++) {
+            field = fields[i]; // A field can be a simple string or a hash
+            key = field.key || field; // Find the key
+            locator = field.locator || key; // Find the locator
+
+            // Validate and store locators for later
+            path = SchemaJSON.getPath(locator);
+            if (path) {
+                if (path.length === 1) {
+                    simplePaths.push({
+                        key : key,
+                        path: path[0]
+                    });
+                } else {
+                    complexPaths.push({
+                        key    : key,
+                        path   : path,
+                        locator: locator
+                    });
+                }
+            } else {
+                Y.log("Invalid key syntax: " + key, "warn", "dataschema-json");
+            }
+
+            // Validate and store parsers for later
+            //TODO: use Y.DataSchema.parse?
+            parser = (isFunction(field.parser)) ?
+                        field.parser :
+                        Y.Parsers[field.parser + ''];
+
+            if (parser) {
+                fieldParsers.push({
+                    key   : key,
+                    parser: parser
+                });
+            }
+        }
+
+        // Traverse list of array_in, creating records of simple fields,
+        // complex fields, and applying parsers as necessary
+        for (i=array_in.length-1; i>=0; --i) {
+            record = {};
+            result = array_in[i];
+            if(result) {
+                // Cycle through complexLocators
+                for (j=complexPaths.length - 1; j>=0; --j) {
+                    path = complexPaths[j];
+                    val = SchemaJSON.getLocationValue(path.path, result);
+                    if (val === undefined) {
+                        val = SchemaJSON.getLocationValue([path.locator], result);
+                        // Fail over keys like "foo.bar" from nested parsing
+                        // to single token parsing if a value is found in
+                        // results["foo.bar"]
+                        if (val !== undefined) {
+                            simplePaths.push({
+                                key:  path.key,
+                                path: path.locator
+                            });
+                            // Don't try to process the path as complex
+                            // for further results
+                            complexPaths.splice(i,1);
+                            continue;
+                        }
+                    }
+
+                    record[path.key] = Base.parse.call(this,
+                        (SchemaJSON.getLocationValue(path.path, result)), path);
+                }
+
+                // Cycle through simpleLocators
+                for (j = simplePaths.length - 1; j >= 0; --j) {
+                    path = simplePaths[j];
+                    // Bug 1777850: The result might be an array instead of object
+                    record[path.key] = Base.parse.call(this,
+                            ((result[path.path] === undefined) ?
+                            result[j] : result[path.path]), path);
+                }
+
+                // Cycle through fieldParsers
+                for (j=fieldParsers.length-1; j>=0; --j) {
+                    key = fieldParsers[j].key;
+                    record[key] = fieldParsers[j].parser.call(this, record[key]);
+                    // Safety net
+                    if (record[key] === undefined) {
+                        record[key] = null;
+                    }
+                }
+                results[i] = record;
+            }
+        }
+        data_out.results = results;
+        return data_out;
+    },
+
+    /**
+     * Parses results data according to schema
+     *
+     * @method _parseMeta
+     * @param metaFields {Object} Metafields definitions.
+     * @param json_in {Object} JSON to parse.
+     * @param data_out {Object} In-progress parsed data to update.
+     * @return {Object} Schema-parsed meta data.
+     * @static
+     * @protected
+     */
+    _parseMeta: function(metaFields, json_in, data_out) {
+        if (isObject(metaFields)) {
+            var key, path;
+            for(key in metaFields) {
+                if (metaFields.hasOwnProperty(key)) {
+                    path = SchemaJSON.getPath(metaFields[key]);
+                    if (path && json_in) {
+                        data_out.meta[key] = SchemaJSON.getLocationValue(path, json_in);
+                    }
+                }
+            }
+        }
+        else {
+            data_out.error = new Error("JSON meta data retrieval failure");
+        }
+        return data_out;
+    }
+};
+
+// TODO: Y.Object + mix() might be better here
+Y.DataSchema.JSON = Y.mix(SchemaJSON, Base);
+
+
+}, '3.17.2', {"requires": ["dataschema-base", "json"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-json/dataschema-json-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-json/dataschema-json-min.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-json/dataschema-json-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/dataschema-json/dataschema-json-min.js Sat Jul 12 00:34:02 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("dataschema-json",function(e,t){var n=e.Lang,r=n.isFunction,i=n.isObject,s=n.isArray,o=e.DataSchema.Base,u;u={getPath:function(e){var t=null,n=[],r=0;if(e){e=e.replace(/\[\s*(['"])(.*?)\1\s*\]/g,function(e,t,i){return n[r]=i,".@"+r++}).replace(/\[(\d+)\]/g,function(e,t){return n[r]=parseInt(t,10)|0,".@"+r++}).replace(/^\./,""),t=e.split(".");for(r=t.length-1;r>=0;--r)t[r].charAt(0)==="@"&&(t[r]=n[parseInt(t[r].substr(1),10)])}return t},getLocationValue:function(e,t){var n=0,r=e.length;for(;n<r;n++){if(!(i(t)&&e[n]in t)){t=undefined;break}t=t[e[n]]}return t},apply:function(t,n){var r=n,s={results:[],meta:{}};if(!i(n))try{r=e.JSON.parse(n)}catch(o){return s.error=o,s}return i(r)&&t?(s=u._parseResults.call(this,t,r,s),t.metaFields!==undefined&&(s=u._parseMeta(t.metaFields,r,s))):s.error=new Error("JSON schema parse failure"),s},_parseResults:function(e,t,n){var r=u.getPath,i=u.getLocationValue,o=r(e.resultListLocator),a=o?i(o,t)||t[e.resultListLocator]:t;return s(a)?s(e.resultF
 ields)?n=u._getFieldValues.call(this,e.resultFields,a,n):n.results=a:e.resultListLocator&&(n.results=[],n.error=new Error("JSON results retrieval failure")),n},_getFieldValues:function(t,n,i){var s=[],a=t.length,f,l,c,h,p,d,v,m,g=[],y=[],b=[],w,E;for(f=0;f<a;f++)c=t[f],h=c.key||c,p=c.locator||h,d=u.getPath(p),d&&(d.length===1?g.push({key:h,path:d[0]}):y.push({key:h,path:d,locator:p})),v=r(c.parser)?c.parser:e.Parsers[c.parser+""],v&&b.push({key:h,parser:v});for(f=n.length-1;f>=0;--f){E={},w=n[f];if(w){for(l=y.length-1;l>=0;--l){d=y[l],m=u.getLocationValue(d.path,w);if(m===undefined){m=u.getLocationValue([d.locator],w);if(m!==undefined){g.push({key:d.key,path:d.locator}),y.splice(f,1);continue}}E[d.key]=o.parse.call(this,u.getLocationValue(d.path,w),d)}for(l=g.length-1;l>=0;--l)d=g[l],E[d.key]=o.parse.call(this,w[d.path]===undefined?w[l]:w[d.path],d);for(l=b.length-1;l>=0;--l)h=b[l].key,E[h]=b[l].parser.call(this,E[h]),E[h]===undefined&&(E[h]=null);s[f]=E}}return i.results=s,i},_pars
 eMeta:function(e,t,n){if(i(e)){var r,s;for(r in e)e.hasOwnProperty(r)&&(s=u.getPath(e[r]),s&&t&&(n.meta[r]=u.getLocationValue(s,t)))}else n.error=new Error("JSON meta data retrieval failure");return n}},e.DataSchema.JSON=e.mix(u,o)},"3.17.2",{requires:["dataschema-base","json"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/datasource-io/datasource-io-debug.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/datasource-io/datasource-io-debug.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/datasource-io/datasource-io-debug.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/datasource-io/datasource-io-debug.js Sat Jul 12 00:34:02 2014
@@ -0,0 +1,211 @@
+/*
+YUI 3.17.2 (build 9c3c78e)
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('datasource-io', function (Y, NAME) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data via the IO Utility.
+ *
+ * @module datasource
+ * @submodule datasource-io
+ */
+
+/**
+ * IO subclass for the DataSource Utility.
+ * @class DataSource.IO
+ * @extends DataSource.Local
+ * @constructor
+ */
+var DSIO = function() {
+    DSIO.superclass.constructor.apply(this, arguments);
+};
+
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // DataSource.IO static properties
+    //
+    /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSIO, {
+    /**
+     * Class name.
+     *
+     * @property NAME
+     * @type String
+     * @static
+     * @final
+     * @value "dataSourceIO"
+     */
+    NAME: "dataSourceIO",
+
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // DataSource.IO Attributes
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    ATTRS: {
+        /**
+         * Pointer to IO Utility.
+         *
+         * @attribute io
+         * @type Y.io
+         * @default Y.io
+         */
+        io: {
+            value: Y.io,
+            cloneDefaultValue: false
+        },
+
+        /**
+         * Default IO Config.
+         *
+         * @attribute ioConfig
+         * @type Object
+         * @default null
+         */
+         ioConfig: {
+            value: null
+         }
+    }
+});
+
+Y.extend(DSIO, Y.DataSource.Local, {
+    /**
+    * Internal init() handler.
+    *
+    * @method initializer
+    * @param config {Object} Config object.
+    * @private
+    */
+    initializer: function(config) {
+        this._queue = {interval:null, conn:null, requests:[]};
+    },
+
+    /**
+    * IO success callback.
+    *
+    * @method successHandler
+    * @param id {String} Transaction ID.
+    * @param response {String} Response.
+    * @param e {EventFacade} Event facade.
+    * @private
+    */
+    successHandler: function (id, response, e) {
+        var defIOConfig = this.get("ioConfig"),
+            payload = e.details[0];
+
+        delete Y.DataSource.Local.transactions[e.tId];
+
+        payload.data = response;
+        this.fire("data", payload);
+
+        Y.log("Received IO data response for \"" + e.request + "\"", "info", "datasource-io");
+
+        if (defIOConfig && defIOConfig.on && defIOConfig.on.success) {
+            defIOConfig.on.success.apply(defIOConfig.context || Y, arguments);
+        }
+    },
+
+    /**
+    * IO failure callback.
+    *
+    * @method failureHandler
+    * @param id {String} Transaction ID.
+    * @param response {String} Response.
+    * @param e {EventFacade} Event facade.
+    * @private
+    */
+    failureHandler: function (id, response, e) {
+        var defIOConfig = this.get("ioConfig"),
+            payload = e.details[0];
+
+        delete Y.DataSource.Local.transactions[e.tId];
+
+        payload.error = new Error("IO data failure");
+        Y.log("IO data failure", "error", "datasource-io");
+
+        payload.data = response;
+        this.fire("data", payload);
+
+        Y.log("Received IO data failure for \"" + e.request + "\"", "info", "datasource-io");
+
+        if (defIOConfig && defIOConfig.on && defIOConfig.on.failure) {
+            defIOConfig.on.failure.apply(defIOConfig.context || Y, arguments);
+        }
+    },
+
+    /**
+    * @property _queue
+    * @description Object literal to manage asynchronous request/response
+    * cycles enabled if queue needs to be managed (asyncMode/ioConnMode):
+    * <dl>
+    *     <dt>interval {Number}</dt>
+    *         <dd>Interval ID of in-progress queue.</dd>
+    *     <dt>conn</dt>
+    *         <dd>In-progress connection identifier (if applicable).</dd>
+    *     <dt>requests {Object[]}</dt>
+    *         <dd>Array of queued request objects: {request:request, callback:callback}.</dd>
+    * </dl>
+    * @type Object
+    * @default {interval:null, conn:null, requests:[]}
+    * @private
+    */
+    _queue: null,
+
+    /**
+     * Passes query string to IO. Fires <code>response</code> event when
+     * response is received asynchronously.
+     *
+     * @method _defRequestFn
+     * @param e {EventFacade} Event Facade with the following properties:
+     * <dl>
+     * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+     * <dt>request (Object)</dt> <dd>The request.</dd>
+     * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+     *     <dl>
+     *         <dt>success (Function)</dt> <dd>Success handler.</dd>
+     *         <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+     *     </dl>
+     * </dd>
+     * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+     * </dl>
+     * @protected
+     */
+    _defRequestFn: function(e) {
+        var uri = this.get("source"),
+            io = this.get("io"),
+            defIOConfig = this.get("ioConfig"),
+            request = e.request,
+            cfg = Y.merge(defIOConfig, e.cfg, {
+                on: Y.merge(defIOConfig, {
+                    success: this.successHandler,
+                    failure: this.failureHandler
+                }),
+                context: this,
+                "arguments": e
+            });
+
+        // Support for POST transactions
+        if(Y.Lang.isString(request)) {
+            if(cfg.method && (cfg.method.toUpperCase() === "POST")) {
+                cfg.data = cfg.data ? cfg.data+request : request;
+            }
+            else {
+                uri += request;
+            }
+        }
+        Y.DataSource.Local.transactions[e.tId] = io(uri, cfg);
+        return e.tId;
+    }
+});
+
+Y.DataSource.IO = DSIO;
+
+
+}, '3.17.2', {"requires": ["datasource-local", "io-base"]});

Added: roller/trunk/app/src/main/webapp/roller-ui/yui3/datasource-io/datasource-io-min.js
URL: http://svn.apache.org/viewvc/roller/trunk/app/src/main/webapp/roller-ui/yui3/datasource-io/datasource-io-min.js?rev=1609870&view=auto
==============================================================================
--- roller/trunk/app/src/main/webapp/roller-ui/yui3/datasource-io/datasource-io-min.js (added)
+++ roller/trunk/app/src/main/webapp/roller-ui/yui3/datasource-io/datasource-io-min.js Sat Jul 12 00:34:02 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("datasource-io",function(e,t){var n=function(){n.superclass.constructor.apply(this,arguments)};e.mix(n,{NAME:"dataSourceIO",ATTRS:{io:{value:e.io,cloneDefaultValue:!1},ioConfig:{value:null}}}),e.extend(n,e.DataSource.Local,{initializer:function(e){this._queue={interval:null,conn:null,requests:[]}},successHandler:function(t,n,r){var i=this.get("ioConfig"),s=r.details[0];delete e.DataSource.Local.transactions[r.tId],s.data=n,this.fire("data",s),i&&i.on&&i.on.success&&i.on.success.apply(i.context||e,arguments)},failureHandler:function(t,n,r){var i=this.get("ioConfig"),s=r.details[0];delete e.DataSource.Local.transactions[r.tId],s.error=new Error("IO data failure"),s.data=n,this.fire("data",s),i&&i.on&&i.on.failure&&i.on.failure.apply(i.context||e,arguments)},_queue:null,_defRequestFn:function(t){var n=this.get("source"),r=this.get("io"),i=this.get("ioConfig"),s=t.request,o=e.merge(i,t.cfg,{on:e.merge(i,{success:this.successHandler,failure:this.failureHandler}),context:this,argu
 ments:t});return e.Lang.isString(s)&&(o.method&&o.method.toUpperCase()==="POST"?o.data=o.data?o.data+s:s:n+=s),e.DataSource.Local.transactions[t.tId]=r(n,o),t.tId}}),e.DataSource.IO=n},"3.17.2",{requires:["datasource-local","io-base"]});



Mime
View raw message