/**
 * Copyright (c) 2014, 2015, Oracle and/or its affiliates.
 * All rights reserved.
 */
"use strict";
define(['ojs/ojcore', 'jquery', 'ojs/ojcomponentcore','ojs/ojbutton','ojs/ojmenu','ojs/ojlistview'], 
       function(oj, $)
{

/**
 * Handler for Expanded Navigation List
 * @constructor
 * @ignore
 */
oj.DefaultNavListHandler = function (widget, root, component)
{
    this.m_widget = widget;
    this.m_root = root;
    this.m_component = component;
};
/**
 * Restore Listview element
 * @private
 */
oj.DefaultNavListHandler.prototype.Destroy = function ()
{
    this.m_root.removeClass('oj-navigationlist-expanded oj-navigationlist-vertical');
};
/**
 * Expand group Item
 * @param {jQuery} groupItem  Group Item
 * @param {boolean} animate true if animate the expand operation, false otherwise
 * @private
 */
oj.DefaultNavListHandler.prototype.Expand = function (groupItem, animate) {
    // Do nothing
};

/**
 * Collapse group Item
 * @param {jQuery} item  Group Item
 * @param {boolean} animate true if animate the collapse operation, false otherwise
 * @private
 */
oj.DefaultNavListHandler.prototype.Collapse = function (item, animate) {
    // Do nothing
};

/**
 * Handle keyboard events for expand and collapse
 * @private
 */
oj.DefaultNavListHandler.prototype.HandleExpandAndCollapseKeys = function (event, keyCode, current, currentItemKey) {
    return false;
};

/**
 * Mofify List item
 * @private
 */
oj.DefaultNavListHandler.prototype.ModifyListItem = function ($item, itemContent) {
   //Do nothing
};

/**
 * Update aria properties when an item is selected.
 * @private
 */
oj.DefaultNavListHandler.prototype.UpdateAriaPropertiesOnSelectedItem = function (elem, highlight) {
    elem.attr("aria-selected", highlight ? 'true' : 'false');
};

/**
 * Placeholder to override or update generated dom after completing rendering
 * @private
 */
oj.DefaultNavListHandler.prototype.BeforeRenderComplete = function () {

};
/**
 * Called by content handler once the content of an item is rendered triggered by an insert event
 * @param {Element} elem the item element
 * @param {Object} context the context object used for the item
 * @private
 */
oj.DefaultNavListHandler.prototype.ItemInsertComplete = function(elem, context)
{

};

/**
 * Called by content handler once the content of an item is removed triggered by an remove event
 * @param {Element} elem the item element
 * @private
 */
oj.DefaultNavListHandler.prototype.ItemRemoveComplete = function(elem)
{

};
/**
 * Handles arrow keys navigation on item
 * @param {number} keyCode description
 * @param {boolean} isExtend
 * @param {Event} event the DOM event causing the arrow keys
 * @private
 */
oj.DefaultNavListHandler.prototype.HandleArrowKeys = function (keyCode, isExtend, event) {
    return _ojNavigationListView.superclass.HandleArrowKeys.apply(this.m_widget, arguments);
};

/**
 * Determine whether the key code is an arrow key
 * @param {number} keyCode
 * @return {boolean} true if it's an arrow key, false otherwise
 * @private
 */
oj.DefaultNavListHandler.prototype.IsArrowKey = function (keyCode) {
    return _ojNavigationListView.superclass.IsArrowKey.apply(this.m_widget, arguments);
};
/**
 * Determines whether the specified item is expanded
 * @param {jQuery} item the item element
 * @return {number} 0 if item is expanded, 1 if item is collapsed, 2 if item cannot be expand or collapse.
 * @private
 */
oj.DefaultNavListHandler.prototype.GetState = function (item) {
    return _ojNavigationListView.superclass.GetState.apply(this.m_widget, arguments);
};
/**
 * Sets the disclosed state of the item
 * @param {jQuery} item the item element
 * @param {number} state 0 if item is expanded, 1 if item is collapsed, 2 if item cannot be expand or collapse.
 * @private
 */
oj.DefaultNavListHandler.prototype.SetState = function (item, state) {
    _ojNavigationListView.superclass.SetState.apply(this.m_widget, arguments);
};

oj.DefaultNavListHandler.prototype.Init = function (opts) {
    this.m_root.addClass('oj-navigationlist-expanded oj-navigationlist-vertical');
};

/**
 * Check whether the item is selectable
 * @param {Element} item the item element
 * @return {boolean} true if item is selectable
 * @private
 */
oj.DefaultNavListHandler.prototype.IsSelectable = function (item)
{
    return _ojNavigationListView.superclass.IsSelectable.apply(this.m_widget, arguments);
};

/**
 * Restore Item
 * @param {jQuery} item, List item
 * @param {jQuery} itemContent, Item contentelement
 * @param {jQuery} sublist, sub list when item is a group item.
 * @private
 */
oj.DefaultNavListHandler.prototype.RestoreItem = function (item, itemContent, sublist) {

};

/**
 * In case of sliding navigation List, Collapses the current visible list if it is not the top level list
 * @private
 */
oj.DefaultNavListHandler.prototype.CollapseCurrentList = function () {
    //DO Nothing
};

/**
 * Invoked when an option was updated after component initialization.
 * Note: This will be invoked only for specifc options. see setOptions method in NavigationList.js.
 * @private
 */
oj.DefaultNavListHandler.prototype.setOptions = function (options) {
    //DO Nothing
};

/**
 * Handler for Horizontal Navigation List
 * @constructor
 * @extends oj.DefaultNavListHandler
 * @ignore
 */

oj.HorizontalNavListHandler = function (widget, root, component)
{
    oj.HorizontalNavListHandler.superclass.constructor.call(this, widget, root, component);
};

// Subclass from oj.Object
oj.Object.createSubclass(oj.HorizontalNavListHandler, oj.DefaultNavListHandler, "oj.HorizontalNavListHandler");

oj.HorizontalNavListHandler.prototype.Destroy = function ()
{
    this.m_root.removeClass('oj-navigationlist-expanded oj-navigationlist-horizontal');
    this.m_root.find('.'+this.m_widget.DIVIDER_STYLE_CLASS).remove();
};

oj.HorizontalNavListHandler.prototype.UpdateAriaPropertiesOnSelectedItem = function (elem, highlight) {
    elem.attr('aria-pressed', highlight ? 'true' : 'false');
};

/**
 * Handles arrow keys navigation on item
 * @param {number} keyCode description
 * @param {boolean} isExtend
 * @param {Event} event the DOM event causing the arrow keys
 * @private
 */
oj.HorizontalNavListHandler.prototype.HandleArrowKeys = function (keyCode, isExtend, event) {
    //Change keyCode to reverse left/right arrow keys for rtl case.
    if (keyCode === $.ui.keyCode.LEFT)
        keyCode = this.m_widget.isRtl() ? $.ui.keyCode.DOWN : $.ui.keyCode.UP;
    else if (keyCode === $.ui.keyCode.RIGHT)
        keyCode = this.m_widget.isRtl() ? $.ui.keyCode.UP : $.ui.keyCode.DOWN;

    var processed = _ojNavigationListView.superclass.HandleArrowKeys.call(this.m_widget, keyCode, isExtend, event);
    if (this.m_widget.ojContext.options['edge'] === 'top') {
        var current = this.m_widget.m_active['elem'];
        current[0].scrollIntoView(false);
    }
    return processed;
};

/**
 *
 * @param {number} keyCode
 * @return {boolean}
 * @private
 */
oj.HorizontalNavListHandler.prototype.IsArrowKey = function (keyCode)
{
    return (keyCode === this.m_widget.UP_KEY || keyCode === this.m_widget.DOWN_KEY || keyCode === this.m_widget.LEFT_KEY || keyCode === this.m_widget.RIGHT_KEY);
};

oj.HorizontalNavListHandler.prototype.ModifyListItem = function ($item, itemContent) {
    var focusableElement = this.m_widget.getSingleFocusableElement($item);
    focusableElement.attr('role', 'button');
    if(focusableElement[0].hasAttribute('aria-selected')){
        focusableElement.attr('aria-pressed', 'false');
        focusableElement.removeAttr('aria-selected');
    }
};

oj.HorizontalNavListHandler.prototype.BeforeRenderComplete = function () {
    var self = this;
    this.m_widget.element.attr('role', 'toolbar');
    this.m_widget.element.find('.' + this.m_widget.getItemElementStyleClass()+':visible').each(function (index) {
        if (index > 0) {
            self._addSeparator(this, index);
        }
    });
};

oj.HorizontalNavListHandler.prototype._addSeparator = function(elem, index) {
    var $elem = $(elem);
    var previousElement = $elem.prev();
    if(index > 0
        && $elem.is(':visible')
        && previousElement.length
        && !previousElement.is("li."+ this.m_widget.DIVIDER_STYLE_CLASS))
    {
            $elem.before('<li role="separator" class="' + this.m_widget.DIVIDER_STYLE_CLASS + '"></li>');//@HTMLUpdateOK
    }

};

oj.HorizontalNavListHandler.prototype.ItemInsertComplete = function(elem, context)
{
    this._addSeparator(elem, context.index);
};

oj.HorizontalNavListHandler.prototype.ItemRemoveComplete = function(elem)
{
    var $elem = $(elem), previousElement = $elem.prev();
    if( previousElement.length && previousElement.is("li."+this.m_widget.DIVIDER_STYLE_CLASS)){
        previousElement.remove();
    }else{
        // Means,this could be the first element,
        //so ensure that divider is not present on next element which is going to be the first one.
        //This is needed because when complete observable array is updated with new set of records, first new records will be added before removing old ones.
        var nextElement = $elem.next();
        if(nextElement.is("li."+ this.m_widget.DIVIDER_STYLE_CLASS)) {
            nextElement.remove();
        }
    }
};

oj.HorizontalNavListHandler.prototype.IsSelectable = function (item)
{
    return  this.m_widget.getFocusItem($(item))[0].hasAttribute("aria-pressed");
};

oj.HorizontalNavListHandler.prototype.Init = function (opts) {
    this.m_root.addClass('oj-navigationlist-expanded oj-navigationlist-horizontal');
};

oj.HorizontalNavListHandler.prototype.RestoreItem = function (item, itemContent, sublist) {

};

/**
 * Handler for Collapsible Navigation List
 * @constructor
 * @extends oj.DefaultNavListHandler
 * @ignore
 */
oj.CollapsibleNavListHandler = function (widget, root, component)
{
    oj.CollapsibleNavListHandler.superclass.constructor.call(this, widget, root, component);
};
// Subclass from oj.Object 
oj.Object.createSubclass(oj.CollapsibleNavListHandler, oj.DefaultNavListHandler, "oj.CollapsibleNavListHandler");

oj.CollapsibleNavListHandler.prototype.Destroy = function ()
{
    this.m_root.removeClass('oj-navigationlist-collapsible oj-navigationlist-vertical');
};

oj.CollapsibleNavListHandler.prototype.Init = function (opts) {
    this.m_root.addClass('oj-navigationlist-collapsible oj-navigationlist-vertical');
};

oj.CollapsibleNavListHandler.prototype.Expand = function (groupItem, animate) {
    _ojNavigationListView.superclass.AnimateExpand.apply(this.m_widget, arguments);
};

oj.CollapsibleNavListHandler.prototype.Collapse = function (item, animate) {
    _ojNavigationListView.superclass.AnimateCollapse.apply(this.m_widget, arguments);
};

oj.CollapsibleNavListHandler.prototype.HandleExpandAndCollapseKeys = function (event, keyCode, current, currentItemKey) {
    var isGroupItem = current.children('.' + this.m_widget.getGroupStyleClass()).length > 0;
    if (keyCode === this.m_widget.LEFT_KEY || keyCode === this.m_widget.RIGHT_KEY)
    {
        //Need to reverse left/right functionality  in rtl case
        if ((keyCode === this.m_widget.LEFT_KEY && !this.m_widget.isRtl()) || (keyCode === this.m_widget.RIGHT_KEY && this.m_widget.isRtl()))
        {
            if (this.m_widget.GetState(current) === this.m_widget.STATE_EXPANDED)
            {
                this.m_widget.CollapseItem(current, event, true, currentItemKey, true, true);
            }
        }
        else
        {
            if (this.m_widget.GetState(current) === this.m_widget.STATE_COLLAPSED)
            {
                this.m_widget.ExpandItem(current, event, true, currentItemKey, true, true, true);
            }
        }
        return true;
    } else if (isGroupItem && (keyCode === $.ui.keyCode.ENTER || keyCode === $.ui.keyCode.SPACE)) {
        if (current.length <= 0)
            return;
        if (this.m_widget.GetState(current) === this.m_widget.STATE_COLLAPSED) {
            this.m_widget.ExpandItem(current, null, true, currentItemKey, true, true, true);
        } else if (this.m_widget.GetState(current) === this.m_widget.STATE_EXPANDED) {
            this.m_widget.CollapseItem(current, null, true, currentItemKey, true, true);
        }
        return true;
    }
    return false;
};

oj.CollapsibleNavListHandler.prototype.RestoreItem = function (item, itemContent, sublist) {

};
/**
 * Handler for Sliding Navigation List
 * @constructor
 * @extends oj.CollapsibleNavListHandler
 * @ignore
 */
oj.SlidingNavListHandler = function (widget, root, component)
{
    oj.SlidingNavListHandler.superclass.constructor.call(this, widget, root, component);
    this.m_expanded = [];
};

// Subclass from oj.Object
oj.Object.createSubclass(oj.SlidingNavListHandler, oj.CollapsibleNavListHandler, "oj.SlidingNavListHandler");

oj.SlidingNavListHandler.prototype.Destroy = function ()
{
    this.m_root.removeClass('oj-navigationlist-slider oj-navigationlist-vertical');
    this._toolbar.remove();
};
/**
 * Intiate Expand/collapse animation.
 *
 * @param {Object} item, item which is going to be expanded.
 * @param {boolean} isMovingNext true for expand animation and false for collapse animation.
 * @param {Object} focusableElement, focusable element after animation.
 */
oj.SlidingNavListHandler.prototype._slideAnimation = function (item, isMovingNext, focusableElement) {
    var self = this;
    var list_root = this.m_widget.getRootElement();
    var contentWidth = list_root.outerWidth() / 2;
    var isRtl = this.m_widget.isRtl();
    if (!isMovingNext) {
        contentWidth = contentWidth * -1;
    }
    if (isRtl) {
        list_root.css({
            "margin-right": contentWidth
        });
        list_root.animate({
            "margin-right": "0px"
        }, 400, 'swing', function () {
            self._slideAnimationComplete(item, isMovingNext, focusableElement);
        });
    } else {
        list_root.css({
            "margin-left": contentWidth
        });
        list_root.animate({
            "margin-left": "0px"
        }, 400, 'swing', function () {
            self._slideAnimationComplete(item, isMovingNext, focusableElement);
        });
    }
};

/**
 * Update ui after animation.
 *
 * @param {Object} item, item which is going to be expanded.
 * @param {boolean} isMovingNext true for expand animation and false for collapse animation
 * @param {Object|null} focusableElement, focusable element after animation.
 * @private
 */
oj.SlidingNavListHandler.prototype._slideAnimationComplete = function (item, isMovingNext, focusableElement) {
    if (this.m_widget.m_contentHandler === null)
        return;
    if(focusableElement)
    {
        var key = this.m_widget.GetKey(focusableElement[0]);
        this.m_widget.SetOption('currentItem', key);//Set current option on listview
    }
    if (!isMovingNext) {
        this.m_widget.AnimateCollapseComplete(item.children('.' + this.m_widget.getGroupStyleClass()));
    } else {
        this.m_widget.AnimateExpandComplete(item.children('.' + this.m_widget.getGroupStyleClass()));
    }
};

/**
 * Expand item in sliding navigation list
 * @param {Object} groupItem group item to be expanded
 * @param {boolean} animate flag to trigger animation or not
 * @private
 */
oj.SlidingNavListHandler.prototype.Expand = function (groupItem, animate) {
    var target = $(groupItem).parents('.oj-navigationlist-item-element:first');
    var sublist = target.children('.' + this.m_widget.getGroupStyleClass()),
            parentLabel,
            nextFocusableItem = null;

    var currentListRoot = target.closest('.' + this.m_widget.SLIDING_NAVLIST_CURRENT_STYLE_CLASS);
    if (currentListRoot.length > 0) {
        //Move marker style class to expanded node only when it is child of an already expanded node.
        //It is possible that child is expanded before parent, in which we need should not remove marker class from child.
        //Ref , During initialization, listview expands child before parent which is causing the issue.
        currentListRoot.removeClass(this.m_widget.SLIDING_NAVLIST_CURRENT_STYLE_CLASS);
        target.addClass(this.m_widget.SLIDING_NAVLIST_CURRENT_STYLE_CLASS);
        nextFocusableItem = sublist.find('.' + this.m_widget.getItemElementStyleClass() + ':eq(0)');
        var listOfParents = target.parentsUntil(currentListRoot, 'ul');
        if (currentListRoot.is(this.m_widget.element)){
            listOfParents = listOfParents.get().concat(currentListRoot.get());
        }else{
            listOfParents = listOfParents.get();
        }

        var self = this, itemNode;
        listOfParents = $(listOfParents.reverse());
        listOfParents.each(function (i, parentList) {
            if ($(parentList).is(self.m_widget.element)) {
                parentLabel = self.m_widget.getRootLabel();
            } else {
                parentLabel = self.m_widget.getItemLabel($(parentList).parent());
            }
            if (i === listOfParents.length - 1) {
                itemNode = target;
            } else {
                itemNode = $(listOfParents.get(i + 1)).parent();
            }
            self._addItemToHviewMenu(self.m_widget.GetKey(itemNode[0]), self.m_widget.getItemLabel(itemNode), parentLabel);
            // this var keeps a stack of expanded items
            self.m_expanded.push(itemNode);
        });
    }

    if (animate)
        this._slideAnimation(target, true, nextFocusableItem);
    else
        this._slideAnimationComplete(target, true, nextFocusableItem);

    this.m_widget.element.focus();
    //For ios, it is needed to set aria-hidden.
    target.siblings().attr('aria-hidden', 'true');
    target.children('.' + this.m_widget.getGroupItemStyleClass())
            .children('.' + this.m_widget.ITEM_CONTENT_STYLE_CLASS)
            .attr('aria-hidden', 'true');
    sublist.removeAttr('aria-hidden');

    // undo any display set by ListView
    groupItem.css("display", "");
    //Skip focus for group element
    target.addClass('oj-skipfocus');
};

/**
 * Collapse item in sliding navigation list
 * @param {Object} target target list root
 * @param {boolean} animate flag to trigger animation or not
 * @private
 */
oj.SlidingNavListHandler.prototype.Collapse = function (target, animate) {
    var currentList = target.children('.' + this.m_widget.getGroupStyleClass()),
            parentlist = target.parent();

    target.children('.' + this.m_widget.getGroupItemStyleClass())
            .children('.' + this.m_widget.ITEM_CONTENT_STYLE_CLASS)
            .removeAttr('aria-hidden');
    currentList.attr('aria-hidden', 'true');
    target.siblings().removeAttr('aria-hidden');

    //Enable focus for group element
    target.removeClass('oj-skipfocus');

    target.removeClass(this.m_widget.SLIDING_NAVLIST_CURRENT_STYLE_CLASS);
    //While collapsing node, if any of parent is already having current style class then don't add again
    if(target.closest('.'+this.m_widget.SLIDING_NAVLIST_CURRENT_STYLE_CLASS).length === 0){
            parentlist.closest('.' + this.m_widget.getItemElementStyleClass()).addClass(this.m_widget.SLIDING_NAVLIST_CURRENT_STYLE_CLASS);
            if (this.m_widget.element.is(parentlist)) {
                    this.m_widget.element.addClass(this.m_widget.SLIDING_NAVLIST_CURRENT_STYLE_CLASS);
            }
    }

    if (animate)
        this._slideAnimation(target, false, currentList.parent());
    else
        this._slideAnimationComplete(target, false, currentList.parent());

    this._removeItemFromHviewMenu();
    this.m_widget.element.focus();
};

oj.SlidingNavListHandler.prototype.UpdateAriaPropertiesOnSelectedItem = function (elem, highlight) {
    if (highlight)
        elem.attr('aria-describedby', 'selectedLabel');
    else
        elem.removeAttr('aria-describedby');
};
/**
 * Determines whether the specified item is expanded
 * @param {jQuery} item the item element
 * @return {number} 0 if item is expanded, 1 if item is collapsed, 2 if item cannot be expand or collapse.
 * @private
 */
oj.SlidingNavListHandler.prototype.GetState = function (item) {
    var expanded = this.m_widget.getFocusItem(item).attr("aria-expanded");
    if (expanded === "true")
    {
        return this.m_widget.STATE_EXPANDED;
    }
    else if (expanded === "false")
    {
        return this.m_widget.STATE_COLLAPSED;
    }
    else
    {
        return this.m_widget.STATE_NONE;
    }
};
/**
 * Sets the disclosed state of the item
 * @param {jQuery} item the item element
 * @param {number} state 0 if item is expanded, 1 if item is collapsed, 2 if item cannot be expand or collapse.
 * @private
 */
oj.SlidingNavListHandler.prototype.SetState = function (item, state)
{
    if (state === this.m_widget.STATE_EXPANDED)
    {
        this.m_widget.getFocusItem(item)
                .attr("aria-expanded", "true");
        item.removeClass(this.m_widget.COLLAPSED_STYLE_CLASS)
                .addClass(this.m_widget.EXPANDED_STYLE_CLASS);

    }
    else if (state === this.m_widget.STATE_COLLAPSED)
    {
        this.m_widget.getFocusItem(item)
                .attr("aria-expanded", "false");
        item.removeClass(this.m_widget.EXPANDED_STYLE_CLASS)
                .addClass(this.m_widget.COLLAPSED_STYLE_CLASS);
    }
};

oj.SlidingNavListHandler.prototype.ModifyListItem = function ($item, itemContent) {
    var focusableElement = this.m_widget.getFocusItem($item);

    focusableElement.attr('role', 'menuitem');
    if (!itemContent.attr('id'))
        itemContent.uniqueId();
    // Ensure that it collapses all nodes.
    var groupItems = $item.children('.' + this.m_widget.getGroupStyleClass());
    if (groupItems.length > 0) {
        focusableElement.attr('aria-haspopup', 'true');
        groupItems.attr('role', 'menu');
        groupItems.css("display", "");
        $item.removeAttr('aria-expanded');
        focusableElement.attr('aria-expanded', 'false');
    }
};

oj.SlidingNavListHandler.prototype.BeforeRenderComplete = function () {
    this.m_widget.element.attr('role', 'menu');
};

oj.SlidingNavListHandler.prototype.Init = function (opts) {
    this.m_root.addClass('oj-navigationlist-slider oj-navigationlist-vertical');
    opts.element.addClass('oj-navigationlist-current');
    this._buildSlidingNavListHeader(opts);
    this._initializeHierarchicalView();
};
/**
 * Initialize sliding navigation list header .
 * @private
 * @memberof! oj.ojNavigationList
 */
oj.SlidingNavListHandler.prototype._buildSlidingNavListHeader = function (opts)
{
    var self = this, vSeparator, selectedLabel;
    this._toolbar = $(document.createElement('div'));
    this._toolbar.addClass('oj-navigationlist-toolbar');
    this._previousLink = $(document.createElement('a'));
    this._prevIcon = $(document.createElement('span'));
    this._prevIcon.addClass('oj-navigationlist-previous-icon oj-component-icon oj-clickable-icon-nocontext')
            .attr('role', 'img')
            .attr('alt', this.m_component.getTranslatedString('previousIcon'));
    this._prevIcon.hide();
    this._previousLink.addClass('oj-navigationlist-previous-link')
            .attr('tabindex', '-1')
            .append(this._prevIcon);// @HTMLUpdateOK
    this._headerLabel = $(document.createElement('label'));
    this._headerLabel.addClass('oj-navigationlist-current-header')
            .text(this.m_widget.getRootLabel());
    this._vSeparator = $(document.createElement('span'));
    this._vSeparator.attr('role', 'separator')
            .attr('aria-orientation', 'vertical')
            .addClass('oj-navigationlist-toolbar-separator');
    this._hviewBtn = $(document.createElement('button'));
    this._hviewBtn.addClass('oj-navigationlist-hierarchical-button');
    this._hviewMenu = $(document.createElement('ul'));
    this._hviewMenu.addClass('oj-navigationlist-hierarchical-menu').hide();
    selectedLabel = $(document.createElement('label'));
    selectedLabel.addClass('oj-helper-hidden-accessible')
            .attr('aria-hidden', 'true')
            .attr('id', 'selectedLabel');
    selectedLabel.text(this.m_component.getTranslatedString('selectedLabel'));
    this._previousLink.append(this._headerLabel);// @HTMLUpdateOK
    this._toolbar.append(this._previousLink)// @HTMLUpdateOK
            .append(this._vSeparator)// @HTMLUpdateOK
            .append(this._hviewBtn)// @HTMLUpdateOK
            .append(this._hviewMenu)// @HTMLUpdateOK
            .append(selectedLabel);// @HTMLUpdateOK
    this.m_root.prepend(this._toolbar);// @HTMLUpdateOK
    this._showOrHideHierarchyMenu(opts.hierarchyMenuDisplayThresholdLevel);
};

/**
 * In case of sliding navigation List, Collapses the current visible list if it is not the top level list
 * @private
 */
oj.SlidingNavListHandler.prototype.CollapseCurrentList = function () {
    // pop the expanded item stack
    var current = this.m_expanded.pop();
    if(current) {
        this.m_widget.CollapseItem(current, null, true, null, true, true);
    }
};



/**
 * Initialize hierarchical view for sliding navlist.
 * @private
 * @memberof! oj.ojNavigationList
 */
oj.SlidingNavListHandler.prototype._initializeHierarchicalView = function () {
    var self = this,
            menuid = this._hviewMenu.uniqueId().attr('id');
    this._hviewMenu.ojMenu({
        openOptions: {
            position: {
                "my": "end top",
                "at": "end bottom"
            }
        },
        select: function (event, ui) {
            var itemsToRemove = ui.item.nextAll(),
                    currentKey, item,
                    expandedItems = self.m_expanded,
                    targetItemKey = ui.item.data('key');
            //collapse all child lists untill target list is visible
            while (expandedItems.length > 0) {
                item = expandedItems.pop();// remove item from list
                currentKey = self.m_widget.GetKey(item[0]);
                self.m_widget.CollapseItem($(item), null, true, currentKey, true, true);
                if (targetItemKey === currentKey) {
                    break;
                }
            }
            itemsToRemove.remove();
            ui.item.remove();
            self._hviewMenu.ojMenu('refresh');
        }
    });
    this._hviewBtn.ojButton({
        'label': this.m_component.getTranslatedString('hierMenuBtnLabel'),
        'display': 'icons',
        'icons': {
            start: 'oj-fwk-icon oj-hier-icon'
        },
        'menu': '#' + menuid,
        'disabled': true,
        'chroming': 'half'
    });
};

/**
 * Add current visible group item to hierarchical menu and update header text with new list's label.
 * @param {string} itemKey key of the group item whose list will be displayed
 * @param {string} label current list's label
 * @param {string} parentLabel parent list's label
 * @private
 */
oj.SlidingNavListHandler.prototype._addItemToHviewMenu = function (itemKey, label, parentLabel) {
    if (this._hviewBtn) {
        var itemsinTree = this._hviewMenu.find('li').length;
        var menuListItem = $(document.createElement('li'));
        var menuItem = $(document.createElement('a'));
        menuItem.attr('href', '#');
        menuListItem.append(menuItem);// @HTMLUpdateOK
        if (itemsinTree > 0) {
            for (var i = 0; i < itemsinTree; i++) {
                if (i > 0) {
                    menuItem.append($(document.createElement('span'))// @HTMLUpdateOK
                            .addClass('oj-navigationlist-hvitem-space'));
                }
            }
            menuItem.append($(document.createElement('span'))// @HTMLUpdateOK
                    .addClass('oj-menu-item-icon oj-icon oj-navigationlist-level-indicator'));
        }
        var menuItemLabel = $(document.createElement('span'))
                .addClass('oj-navigationlist-hierarchical-menu-label');
        menuItemLabel.text(parentLabel);
        menuItem.append(menuItemLabel);// @HTMLUpdateOK

        //Store item key reference to refresh the list when user clicks on  hiearchical menu item.
        menuListItem.data('key', itemKey);
        this._hviewMenu.append(menuListItem);// @HTMLUpdateOK

        this._hviewMenu.ojMenu('refresh');
        this._showOrHideHierarchyMenu(this.m_widget.GetOption('hierarchyMenuDisplayThresholdLevel'));
        this._hviewBtn.ojButton("option", "disabled", false);
        this._prevIcon.show();
        this._previousLink.attr('tabindex', '0');
        this._headerLabel.text(label);
    }
};
oj.SlidingNavListHandler.prototype.setOptions = function (options) {
    var currentHMenuDisplayThreshold =  this.m_widget.GetOption('hierarchyMenuDisplayThresholdLevel');
    if (currentHMenuDisplayThreshold !== options['hierarchyMenuDisplayThresholdLevel']) {
        this._showOrHideHierarchyMenu(options.hierarchyMenuDisplayThresholdLevel);
    }
};
oj.SlidingNavListHandler.prototype._showOrHideHierarchyMenu =  function (hierarchyMenuDisplayThresholdLevel) {
    var itemsinTree = this._hviewMenu.find('li').length;
    if(hierarchyMenuDisplayThresholdLevel === -1 || itemsinTree < hierarchyMenuDisplayThresholdLevel ){
        this._vSeparator.hide();
        this._hviewBtn.hide();
    }else if(itemsinTree >= hierarchyMenuDisplayThresholdLevel ) {
        this._vSeparator.show();
        this._hviewBtn.show();
    }
};
/**
 * Removes parent item of current list from hierarchical menu
 * otherwise return the emptyText set in the options
 * @private
 */
oj.SlidingNavListHandler.prototype._removeItemFromHviewMenu = function () {
    if (this._hviewBtn) {
        var removed = this._hviewMenu.find('li').last().remove();
        this._hviewMenu.ojMenu('refresh');
        this._showOrHideHierarchyMenu(this.m_widget.GetOption('hierarchyMenuDisplayThresholdLevel'));
        if (this._hviewMenu.children('li').length === 0) {
            this._hviewBtn.ojButton("option", "disabled", true);
            this._prevIcon.hide();
            this._previousLink.attr('tabindex', '-1');
            this._headerLabel.text(this.m_widget.getRootLabel());
        } else {
            this._headerLabel.text(removed.children('a').text());
        }
    }
};

oj.SlidingNavListHandler.prototype.RestoreItem = function (item, itemContent, sublist) {
    item.removeClass(this.m_widget.SLIDING_NAVLIST_CURRENT_STYLE_CLASS);
    itemContent.removeAttr('role')
        .removeAttr('aria-expanded')
        .removeAttr('aria-describedby')
        .removeAttr('aria-hidden');
};

/**
 * todo: create common utility class between combobox, listview and navlist
 * @private
 */
var _NavigationListUtils = {
    clazz: function (SuperClass, methods)
    {
        var constructor = function () {
        };
        oj.Object.createSubclass(constructor, SuperClass, '');
        $.extend(constructor.prototype, methods);
        return constructor;
    }
};
/**
 * @export
 * @class _ojNavigationListView
 * @classdesc NavigationList
 * @constructor
 * @ignore
 */
var _ojNavigationListView = _NavigationListUtils.clazz(oj._ojListView,
/** @lends _ojNavigationListView.prototype */
{

    ITEM_CONTENT_STYLE_CLASS: 'oj-navigationlist-item-content',
    EXPANDED_STYLE_CLASS: 'oj-expanded',
    COLLAPSED_STYLE_CLASS: 'oj-collapsed',
    SLIDING_NAVLIST_CURRENT_STYLE_CLASS: 'oj-navigationlist-current',
    DIVIDER_STYLE_CLASS: 'oj-navigationlist-divider',
    _CATEGORY_DIVIDER_STYLE_CLASS: 'oj-navigationlist-category-divider',
    _ITEM_LABEL_STYLE_CLASS: 'oj-navigationlist-item-label',
    _ICON_ONLY_STYLE_CLASS: 'oj-navigationlist-icon-only',
    _ITEM_ICON_STYLE_CLASS: 'oj-navigationlist-item-icon',
    _HORIZONTAL_NAVLIST_STYLE_CLASS: 'oj-navigationlist-horizontal',
    _NAVLIST_HAS_ICONS: 'oj-navigationlist-has-icons',
    _NAVLIST_ITEM_HAS_NO_ICON: 'oj-navigationlist-item-no-icon',
    _NAVLIST_ITEM_TITLE: 'oj-navigationlist-item-title',
    _NAVLIST_STYLE_CLASS: 'oj-navigationlist',
    _NAVLIST_TOUCH_STYLE_CLASS: 'oj-navigationlist-touch',
    _NAVLIST_LISTVIEW_CONTAINER_STYLE_CLASS: 'oj-navigationlist-listview-container',
    _IS_TITLE_ATTR_ADDED_DUE_TO_TRUNCATION: 'IsTitleAttrDueToTruncation',
    _APPLICATION_LEVEL_NAV_STYLE_CLASS: 'oj-navigationlist-app-level',
    _PAGE_LEVEL_NAV_STYLE_CLASS: 'oj-navigationlist-page-level',
    /**
     * Returns Item label text
     * @returns {string|null} Item label
     */
    getItemLabel: function (target) {
        var itemContent = this.getItemContentElement(target);
        if (itemContent.is('a')) { // When using simple <a> markup
            return $.trim(itemContent.text());
        } else {
            //When using arbitrary content, extract item title from element having marker class .oj-navigationlist-item-title.
            return $.trim(itemContent.find('.'+this._NAVLIST_ITEM_TITLE).text());
        }
    },
    /**
     * Returns Item content root element.
     * @returns {Object} Returns jquery object for item content root
     */
    getItemContentElement: function (item) {
        var itemContent = item.children('.' + this.ITEM_CONTENT_STYLE_CLASS);
        if (itemContent.length === 0) {
            itemContent = item.children('.' + this.getGroupItemStyleClass()).children('.' + this.ITEM_CONTENT_STYLE_CLASS);
            if (itemContent.length === 0) { //This case is when item label is invoked before complete initialization of listview
                itemContent = item.children('.' + this.getGroupItemStyleClass()).children(':not(.' + this.getExpandIconStyleClass() + '):not(.' + this.getCollapseIconStyleClass() + ')');
            }
        }
        return itemContent;
    },
    /**
     * Return true when page is in rtl mode otherwise false.
     * @returns {boolean} true if page is in rtl mode
     */
    isRtl: function () {
        return this.ojContext._GetReadingDirection() === "rtl";
    },
    /**
     * Called by content handler once content of all items are rendered
     * @override
     */
    renderComplete: function () {
        this.m_listHandler.BeforeRenderComplete();
        _ojNavigationListView.superclass.renderComplete.apply(this, arguments);
    },
    /**
     * Called by content handler once the content of an item is rendered triggered by an insert event
     * @param {Element} elem the item element
     * @param {Object} context the context object used for the item
     */
    itemInsertComplete: function(elem, context)
    {
        this.m_listHandler.ItemInsertComplete(elem, context);
    },

    /**
     * Called by content handler once the content of an item is removed triggered by an remove event
     * @param {Element} elem the item element
     */
    itemRemoveComplete: function(elem)
    {
        this.m_listHandler.ItemRemoveComplete(elem);
        _ojNavigationListView.superclass.itemRemoveComplete.apply(this, arguments);
    },
    /**
     * Restore content while restroying list
     * @private
     */
    _restoreContent: function (list) {
        var items = list.children();
        for (var i = 0; i < items.length; i++)
        {
            var item = items[i];
            item = $(item);
            if (item.hasClass(this.DIVIDER_STYLE_CLASS)) {
                item.remove();
                continue;
            }
            var itemContent = this.getItemContentElement(item);
            itemContent.removeClass(this.ITEM_CONTENT_STYLE_CLASS)
                       .removeClass(this._NAVLIST_ITEM_HAS_NO_ICON)
                       .removeAttr('aria-haspopup');
            item.removeClass('oj-default')
                    .removeAttr('role')
                    .removeAttr('aria-disabled')
                    .removeAttr('aria-expanded')
                    .removeAttr('aria-hidden')
                    .removeAttr('aria-pressed');
            var focusableItem = this.getFocusItem(item);
            focusableItem.removeAttr('role')
                    .removeAttr('aria-hidden')
                    .removeAttr('aria-pressed');
            itemContent.children('.' + this._ITEM_LABEL_STYLE_CLASS)
                    .contents().unwrap();
            if(focusableItem.data(this._IS_TITLE_ATTR_ADDED_DUE_TO_TRUNCATION)){
                focusableItem.removeAttr('title');
            }
            var sublist = item.children('ul');
            if (sublist.length > 0) {

                sublist.removeAttr('style').removeClass(this._NAVLIST_HAS_ICONS)
                        .removeAttr('aria-hidden');
                this.m_listHandler.RestoreItem(item, itemContent, sublist);
                this._restoreContent(sublist);
            } else {
                this.m_listHandler.RestoreItem(item, itemContent);
            }
        }
    },
    /**
     * Overriding to exclude oj-navigationlist-item-content from clickable components.
     * @override
     * @protected
     */
    IsNodeEditableOrClickable: function (node) {
        if (node.hasClass(this.ITEM_CONTENT_STYLE_CLASS) || node.hasClass(this.getExpandIconStyleClass()) || node.hasClass(this.getCollapseIconStyleClass()))
            return false;
        return _ojNavigationListView.superclass.IsNodeEditableOrClickable.apply(this, arguments);
    },
    /**
    * checks for whether a node is editable or clickable.
    * @param {jQuery} node  Node
    * @return {boolean} true or false
    * @override
    * @protected
    */
    IsElementEditableOrClickable: function(node)
    {
        var nodeName = node.prop("nodeName");
        //Exclude navlist item content <a> tag from below list and also checking for oj-component class as it is needed for toggle button.
        return (nodeName.match(/^INPUT|SELECT|OPTION|BUTTON|^A\b|TEXTAREA/) && !node.hasClass(this.ITEM_CONTENT_STYLE_CLASS)) || node.hasClass('oj-component');
    },
    /**
     * Prepare options to initialize List
     * @private
     */
    _prepareListViewOptions: function (navlistOptions) {

        var opts = {
            drillMode: navlistOptions['drillMode'] !== 'none' ? 'collapsible' : 'none',
            selection: navlistOptions['selection'] !== null ? [navlistOptions['selection']] : [],
            item: navlistOptions['item'],
            data: navlistOptions['data'],
            selectionMode: 'single'
        };
        opts.element = this._list;
        opts.item.focusable = function (context) {
            if ($(context['data']).is('li')) {
                return !$(context['data']).hasClass('oj-disabled');
            }
            return !$(context['parentElement']).hasClass('oj-disabled');
        };
        opts = $.extend($.extend({}, navlistOptions), opts);
        return opts;
    },
    /**
     * Returns root label for navigation list.
     * otherwise return the emptyText set in the options
     * @return {string} Label for navigation list
     */
    getRootLabel: function () {
        if (this.ojContext.options['rootLabel'])
            return this.ojContext.options['rootLabel'];
        else
            return  this.ojContext.getTranslatedString('defaultRootLabel');
    },

    /**
     * Sets wai-aria properties on root element
     * @override
     * @protected
     */
    SetAriaProperties: function ()
    {
        //Set aria-labelledby or aria-label
        var ariaLabelId = this.ojContext.element.attr('aria-labelledby'), opts;
        if (ariaLabelId) {
            this.element.attr('aria-labelledby', ariaLabelId);
            this.ojContext.element.removeAttr('aria-labelledby');
        }
        else {
            this.element.attr('aria-label', this.getRootLabel());
        }
        _ojNavigationListView.superclass.SetAriaProperties.apply(this, arguments);
    },
    /**
     * Removes wai-aria properties on root element
     * @override
     * @protected
     */
    UnsetAriaProperties: function ()
    {
        //restore aria-labelledby on root element
        if (this.element.attr('aria-labelledby')) {
            this.ojContext.element.attr('aria-labelledby', this.element.attr('aria-labelledby'));
            this.element.removeAttr('aria-labelledby');
        }

        if (this.element.attr('aria-label')) {
            this.element.removeAttr('aria-label');
        }
        _ojNavigationListView.superclass.UnsetAriaProperties.apply(this, arguments);
    },
    /**
     * Overriding init to initialize list with respective List handler.
     * @override
     */
    init: function (navlistopts)
    {
        var opts,
            self = this,
            element = navlistopts.ojContext.element;
        element.addClass(this._NAVLIST_STYLE_CLASS);

        if(oj.DomUtils.isTouchSupported()){
            element.addClass(this._NAVLIST_TOUCH_STYLE_CLASS);
        }

        this._list = element.children('ul:first');
        if (this._list.length === 0) {
            this._list = $(document.createElement("ul"))
            element.append(this._list);//@HTMLUpdateOK
        }
        var listViewContainer = $(document.createElement("div"));
        listViewContainer.addClass(this._NAVLIST_LISTVIEW_CONTAINER_STYLE_CLASS);
        this._list.wrap(listViewContainer);// @HTMLUpdateOK
        opts = this._prepareListViewOptions(navlistopts);

        _ojNavigationListView.superclass.init.call(this, opts);
        this.element.removeClass('oj-component-initnode');
        this.ojContext._on(this.ojContext.element, {
            "touchstart": function(event){
                var item = self.FindItem($(event.target));
                if(item && !item.hasClass("oj-disabled"))
                    self.HighlightUnhighlightElem(item, "oj-active", true);
            },
            "touchend": function(event){
                var item = self.FindItem($(event.target));
                self.HighlightUnhighlightElem(item, "oj-active", false);
            },
            "mousedown": function(event){
                var item = self.FindItem($(event.target));
                if(item && !item.hasClass("oj-disabled"))
                    self.HighlightUnhighlightElem(item, "oj-active", true);
            },
            "mouseup": function(event){
                var item = self.FindItem($(event.target));
                self.HighlightUnhighlightElem(item, "oj-active", false);
            },
            "click .oj-navigationlist-previous-link": function (event) {
                self._collapseCurrentList();
            },
            "keydown .oj-navigationlist-previous-link": function (event) {
                if (event.keyCode === $.ui.keyCode.ENTER) {
                    self._collapseCurrentList();
                }
            },
            "keydown .oj-navigationlist-element": function (event) {
                //check for default prevented as it might have already processed for quiting f2 mode
                if (event.keyCode === $.ui.keyCode.ESCAPE && !event.isDefaultPrevented())
                {
                    self._collapseCurrentList();
                }
            },
            "mouseenter a.oj-navigationlist-item-content": function(event){
                var $itemLink = $(event.currentTarget);
                var $label = $itemLink.find('.'+ self._ITEM_LABEL_STYLE_CLASS);
                //Add title attribute only when the text is truncated.
                if($label[0].offsetWidth < $label[0].scrollWidth && !$itemLink.attr('title')){
                    $itemLink.attr('title', $label.text().trim());
                    $itemLink.data(self._IS_TITLE_ATTR_ADDED_DUE_TO_TRUNCATION,'true');
                }
            },
            "mouseleave a.oj-navigationlist-item-content": function(event){
                //Remove title attribute on mouseleave.
                var $itemLink = $(event.currentTarget);
                if($itemLink.data(self._IS_TITLE_ATTR_ADDED_DUE_TO_TRUNCATION)) {
                  $itemLink.removeData(self._IS_TITLE_ATTR_ADDED_DUE_TO_TRUNCATION);
                  $itemLink.removeAttr('title');
                }
            }
        });
    },
    _collapseCurrentList: function(){
        this.m_listHandler.CollapseCurrentList();
    },
    _initListHandler: function () {
        var drillMode = this.ojContext.options['drillMode'];
        var edge = this.ojContext.options['edge'];
        if (drillMode === 'sliding') {
            this.m_listHandler = new oj.SlidingNavListHandler(this, this.ojContext.element, this.ojContext);
        } else if (drillMode === 'collapsible') {
            this.m_listHandler = new oj.CollapsibleNavListHandler(this, this.ojContext.element, this.ojContext);
        } else if (drillMode === 'none' && edge === 'top') {
            this.m_listHandler = new oj.HorizontalNavListHandler(this, this.ojContext.element, this.ojContext);
        } else {
            this.m_listHandler = new oj.DefaultNavListHandler(this, this.ojContext.element, this.ojContext);
        }
        this.m_listHandler.Init(this.options);
        var navigationLevel = this.ojContext.options['navigationLevel'];
        this._setNavigationLevel(navigationLevel);
    },
    _setNavigationLevel: function(navigationLevel){
        var drillMode = this.ojContext.options['drillMode'];
        if(drillMode === 'none' ){
            if(navigationLevel === 'application') {
                this.ojContext.element.addClass(this._APPLICATION_LEVEL_NAV_STYLE_CLASS);
                this.ojContext.element.removeClass(this._PAGE_LEVEL_NAV_STYLE_CLASS);
            }else if(navigationLevel === 'page') {
                this.ojContext.element.addClass(this._PAGE_LEVEL_NAV_STYLE_CLASS);
                this.ojContext.element.removeClass(this._APPLICATION_LEVEL_NAV_STYLE_CLASS);
            }
        }
    },
    /**
     * Initialize the listview after creation
     * @override
     */
    afterCreate: function ()
    {
        this._initListHandler();
        _ojNavigationListView.superclass.afterCreate.apply(this, arguments);
    },
    /**
     * Returns Navlist specific container style class
     * @override
     * @protected
     */
    GetContainerStyleClass: function () {
        return "oj-navigationlist-listview";
    },
    /**
     * Returns Navlist specific root element style class
     * @override
     * @protected
     */
    GetStyleClass: function () {
        return "oj-navigationlist-element";
    },
    /**
     * Returns Navlist specific Item style class
     * @override
     */
    getItemStyleClass: function () {
        return "oj-navigationlist-item";
    },
    getFocusedElementStyleClass: function()
    {
        return "oj-navigationlist-focused-element";
    },
    /**
     * Returns Navlist specific Item element style class
     * @override
     */
    getItemElementStyleClass: function ()
    {
        return "oj-navigationlist-item-element";
    },
    /**
     * Returns Navlist specific collapse icon style class
     * @override
     */
    getCollapseIconStyleClass: function () {
        return "oj-navigationlist-collapse-icon";
    },
    /**
     * Returns Navlist specific expand icon style class
     * @override
     */
    getExpandIconStyleClass: function () {
        return "oj-navigationlist-expand-icon";
    },
    /**
     * Returns Navlist specific depth style class
     * @override
     */
    getDepthStyleClass: function (depth) {
        return "oj-navigationlist-depth-" + depth;
    },
    /**
     * Returns Navlist specific Group Item style class
     * @override
     */
    getGroupItemStyleClass: function ()
    {
        return "oj-navigationlist-group-item";
    },
    /**
     * Returns Navlist specific Group style class
     * @override
     */
    getGroupStyleClass: function () {
        return "oj-navigationlist-group";
    },
    /**
     * Returns Navlist specific group expand style class
     * @override
     */
    getGroupExpandStyleClass: function ()
    {
        return "oj-navigationlist-collapsible-transition";
    },
    /**
     * Returns Navlist specific group collapse style class
     * @override
     */
    getGroupCollapseStyleClass: function ()
    {
        return this.getGroupExpandStyleClass();
    },
    /**
     * Override expand animation behaviour to support differnet types of navlists.
     * @override
     * @protected
     */
    AnimateExpand: function (groupItem, animate) {
        this.m_listHandler.Expand(groupItem, animate);
    },
    /**
     * Override collapse animation behaviour to support differnet types of navlists.
     * @override
     * @protected
     */
    AnimateCollapse: function (item, animate) {
        this.m_listHandler.Collapse(item, animate);
    },
    /**
     * Handles arrow keys navigation on item
     * @param {number} keyCode description
     * @param {boolean} isExtend
     * @param {Event} event the DOM event causing the arrow keys
     * @override
     * @protected
     */
    HandleArrowKeys: function (keyCode, isExtend, event)
    {
        return this.m_listHandler.HandleArrowKeys(keyCode, isExtend, event);
    },
    /**
     * Determine whether the key code is an arrow key
     * @param {number} keyCode
     * @return {boolean} true if it's an arrow key, false otherwise
     * @override
     * @protected
     */
    IsArrowKey: function (keyCode)
    {
        return this.m_listHandler.IsArrowKey(keyCode);
    },
    /**
     * Determines whether the specified item is expanded
     * @param {jQuery} item the item element
     * @return {number} 0 if item is expanded, 1 if item is collapsed, 2 if item cannot be expand or collapse.
     * @override
     * @protected
     */
    GetState: function (item)
    {
        return this.m_listHandler.GetState(item);
    },
    /**
     * Sets the disclosed state of the item
     * @param {jQuery} item the item element
     * @param {number} state 0 if item is expanded, 1 if item is collapsed, 2 if item cannot be expand or collapse.
     * @override
     * @protected
     */
    SetState: function (item, state)
    {
        this.m_listHandler.SetState(item, state);
    },

    /**
     * Override to expand and collapse events on clicking item instead of icon. Also avoids expand/collapse when user click on arbitrary clickable elements.
     * @override
     * @protected
     */
    HandleMouseClick: function (event) {
        var item = this.FindItem($(event.target)), itemKey;
        if (item == null || item.length === 0)
        {
            // can't find item or if item cannot be focus
            return;
        }
        if(this.SkipFocus(item)){
            event.preventDefault();
            return;
        }
        itemKey = this.GetKey(item[0]);

        //TODO , once it is fixd in listview probably don't need to override HandleMouseClick

        if (this.IsNodeEditableOrClickable($(event.target))) {
            return;
        }

        var itemContent = this.getItemContentElement(item);
        var url = itemContent.attr('href');
        if (url && url !== "#") {
            if(event.button === 0  && (event.shiftKey || event.ctrlKey)) {
                return; // Do nothing, as Browser will launch  target it in new tab or browser
            }
        }

        _ojNavigationListView.superclass.HandleMouseClick.apply(this, arguments);
        event.preventDefault();
    },
    /**
     * Override to support flip left/right arrows for expand and collapse actions.
     * @override
     * @protected
     */
    HandleKeyDown: function (event) {
        var keyCode, current, currentItemKey;

        keyCode = event.keyCode;
        current = this.m_active['elem'];
        currentItemKey = this.m_active['key'];

        if (keyCode === $.ui.keyCode.HOME || keyCode === $.ui.keyCode.END) {
            var item, key;
            if(keyCode === $.ui.keyCode.HOME)
            {
                item = this.element.find('.' + this.ITEM_CONTENT_STYLE_CLASS + ':visible').first().closest('.' + this.getItemElementStyleClass());
            }else{
                item = this.element.find('.' + this.ITEM_CONTENT_STYLE_CLASS + ':visible').last().closest('.' + this.getItemElementStyleClass());
            }
            key = this.GetKey(item[0]);
            this.SetOption('currentItem', key); //Set current option on listview
            event.preventDefault();
        } else {
            var processed = this.HandleSelectionOrActiveKeyDown(event);
            var processExpansion = this.m_listHandler.HandleExpandAndCollapseKeys(event, keyCode, current, currentItemKey);
            if (processed === true || processExpansion === true)
            {
                event.preventDefault();
            }
        }
    },
    /**
     * Override to trigger beforeSelect event when user selecets an item.
     * @override
     * @protected
     */
    ToggleSelection: function (event, keepCurrentSelection, skipIfNotSelected) {
        var item = this.m_active['elem'];

        if (!this.IsSelectable(item[0])) {
            return;
        }
        if (this._isSelected(item)) {
            return;
        }
        var shouldSelect = this._fireBeforeSelectEvent(event, item);
        if (shouldSelect){
            _ojNavigationListView.superclass.ToggleSelection.apply(this, arguments);
            this._initiateNavigation(item);
        }
    },
    /**
     * Highlight or unhighlight an element
     * @param {jQuery|Element} elem the element the highlight or unhighlight
     * @param {string} style the style to add or remove
     * @param {boolean} highlight true if it's to highlight, false if it's to unhighlight
     * @override
     * @protected
     */
    HighlightUnhighlightElem: function (elem, style, highlight)
    {
        var group;

        elem = $(elem);

        if (style === "oj-selected") {
            this.m_listHandler.UpdateAriaPropertiesOnSelectedItem(this.getFocusItem(elem), highlight);
        }

        // if item is a group, the highlight should be apply to the group item element
        group = elem.children("." + this.getGroupItemStyleClass());
        if (group.length > 0)
        {
            elem = $(group[0]);
        }

        if (highlight)
        {
            elem.addClass(style);
        }
        else
        {
            elem.removeClass(style);
        }

        if (elem.hasClass('oj-selected') || elem.hasClass('oj-hover') || elem.hasClass('oj-active') || elem.hasClass('oj-disabled')) {
            elem.removeClass('oj-default');
        }
        else {
            elem.addClass('oj-default');
        }
    },
    /**
     * In Navlist,it is not gaurenteed that item will have aria-selected. So overriding it to make all items selectable.
     * @override
     * @protected
     */
    IsSelectable: function (item)
    {
        return !$(item).hasClass('oj-disabled') && this.m_listHandler.IsSelectable($(item));
    },
    /**
     * Override to skip firing events like ready which are not needed for navlist.
     * @override
     * @protected
     */
    Trigger: function (type, event, ui)
    {
        if (type !== 'ready')
            return this.ojContext._trigger(type, event, ui);
        else
            return true;
    },
    /**
     * Override to convert selection option value from array to string before setting on component.
     * @override
     * @protected
     */
    SetOption: function (key, value, flags)
    {
        if (key === 'selection') { // selection Data type is different for listview and navlist
            //change items to item as navlist does't allow multiple selection.
            var context = flags['_context'],
                selectedItem = context && context.extraData && context.extraData['items'];
            if(selectedItem) {
                //extraData['items'] is a jquery object(not an array) and in case of navlist it will always have single item.
                context.extraData['item'] = selectedItem;
                context.extraData['items'] = undefined;
            }
            if (value && value.length > 0)
                this.ojContext.option(key, value[0], flags);
            else
                this.ojContext.option(key, null, flags);

        } else {// Common options
            this.ojContext.option(key, value, flags);
        }
        this.options[key] = value;
    },
    /**
     * Whether ListView should refresh if certain option is updated
     * @param {Object} options the options to check
     * @return {boolean} true if should refresh, false otherwise
     * @override
     * @protected
     */
    ShouldRefresh: function (options)
    {
        return (options['data'] != null ||
                options['drillMode'] != null ||
                options['item'] != null ||
                options['display'] != null ||
                options['edge'] != null);
    },
    /**
     * Sets multiple options
     * @param {Object} options the options object
     * @param {Object} flags additional flags for option
     * @return {Object} result result object holding skipUpdate and needRefresh flags.
     * @override
     */
    setOptions: function (options, flags)
    {
        var result = { skipOptions: [] }, newSelectionValue;

        if( options['hierarchyMenuDisplayThresholdLevel'] !== undefined) {
            this.m_listHandler.setOptions(options);
        }

        if(options['navigationLevel'] !== undefined){
            this._setNavigationLevel(options['navigationLevel']);
        }

        if (options['selection'] !== undefined) {
            newSelectionValue = options['selection'];
            if (!!newSelectionValue) {
                var selection = this.GetOption('selection');
                if (!selection || selection.length === 0 || selection[0] !== newSelectionValue) {
                    var item = this.FindElementByKey(newSelectionValue);
                    if (item && this.IsSelectable(item)) {
                        var shouldSelect = this._fireBeforeSelectEvent(null, $(item));
                        if (shouldSelect) {
                           options['selection'] = [newSelectionValue];
                           this._initiateNavigation($(item));
                        }else{
                          delete options['selection'];
                          result.skipOptions.push('selection');
                        }
                    }else{
                      delete options['selection'];
                      result.skipOptions.push('selection');
                    }
                } else {
                  //When new value is same as existing one, no need to update it on listview. 
                  delete options['selection'];
                }
            } else {
              options['selection'] = [];
            }
        }

        result.needRefresh = _ojNavigationListView.superclass.setOptions.call(this, options, flags);
        //Restore original user provided selection value
        if(newSelectionValue !== undefined){
          options['selection'] = newSelectionValue;
        }
        return result;
    },
    /**
     * Override to convert selection option value from string to array before setting on component.
     * @override
     * @protected
     */
    GetOption: function (key)
    {
        var optionValue = this.ojContext.option(key);
        if (key === 'selection') { // selection Data type is different for listview and navlist
            if (optionValue) {
                return [optionValue];
            } else {
                return [];
            }
        }
        if (optionValue === null) { // if the option is only applicable to listview
            return this.options[key];
        }
        return optionValue;
    },
    /**
     * Override to return widget constructor
     * @override
     */
    getWidgetConstructor: function () {
        return oj.Components.getWidgetConstructor(this.ojContext.element);
    },
    /**
     * Override to skip intermediate nodes(only in case of collapsible) and disabled items from selection.
     * @override
     * @protected
     */
    SelectAndFocus: function (item, event) {
        //Disabled items are not selectable and focuable.
        if (item.hasClass('oj-disabled')) {
            return;
        }

        //Selectable items should gain focus on click
        if (this.IsSelectable(item[0])) {
            var key = this.GetKey(item[0]);
            this.SetOption('currentItem', key);
        } else {
            return;
        }

        //If item is already selected then return
        if (this._isSelected(item)) {
            return;
        }

        var shouldSelect = this._fireBeforeSelectEvent(event, item);
        if (shouldSelect) {
            _ojNavigationListView.superclass.SelectAndFocus.apply(this, arguments);
            this._initiateNavigation(item);
        }
    },
    _fireBeforeSelectEvent: function (event, item) {
        var key = this.GetKey(item[0]);
        return this.Trigger("beforeSelect", event, {item: item, key: key});
    },
    _initiateNavigation: function (item) {
        var itemContent = this.getItemContentElement(item);
        var url = itemContent.attr('href');
        if (url && url !== "#") {
            //In case of javascript uri, javascript will get executed on assigning it to href.
            //Ideally user can use beforeSelect/optionChange events to do this,Will there be any issue in supporting this?
            window.location.href = url;
            return true;
        }
        return false;
    },
    _isSelected: function (item) {
        var selection, key, index;

        selection = this.GetOption("selection");
        key = this.GetKey(item[0]);

        index = selection.indexOf(key);
        if (index > -1)
        {
            return true;
        }
        return false;
    },
    /**
     * Called by content handler once the content of an item is rendered
     * @param {Element} elem the item element
     * @param {Object} context the context object used for the item
     */
    itemRenderComplete: function(elem, context)
    {
        var $item = $(elem);
        if ($item.hasClass(this._CATEGORY_DIVIDER_STYLE_CLASS)) {
            $item.removeClass(this.getItemElementStyleClass());
            $item.removeClass(this.getFocusedElementStyleClass());
            $item.removeClass(this.getItemStyleClass());
            $item.removeAttr('aria-selected');
            $item.children().remove();
            $item.attr('role', 'separator');
            return;
        }
        var groupItemClass = this.getGroupItemStyleClass();
        var collapseIconClass = this.getCollapseIconStyleClass();
        var expandIconClass = this.getExpandIconStyleClass();
        var itemIconClass = this._ITEM_ICON_STYLE_CLASS;
        var groupItem = $item.children('.' + groupItemClass);
        var itemContent;
        if (groupItem.length > 0) {
            //Adding oj-navigationlist-item class on group node. Listview does't add this any more.
            groupItem.addClass(this.getItemStyleClass());
            itemContent = groupItem.children(':not(.' + expandIconClass + '):not(.' + collapseIconClass + ')');
            var groupIcon = groupItem.children('.' + expandIconClass);
            if (groupIcon.length === 0) {
                groupIcon = groupItem.children('.' + collapseIconClass);
            }
            groupIcon.attr('role', 'presentation');
            groupIcon.removeAttr('aria-labelledby');
            if ($item.hasClass('oj-disabled')) {
                groupItem.addClass('oj-disabled');
            }
        } else {
            itemContent = $item.children().first();
        }
        itemContent.addClass(this.ITEM_CONTENT_STYLE_CLASS);
        var icon = itemContent.wrapInner('<span class="' + this._ITEM_LABEL_STYLE_CLASS + '"></span>').find('.' + itemIconClass);//@HTMLUpdateOK
        if (icon.length > 0) {
            icon.insertBefore(icon.parent());// @HTMLUpdateOK
            if (this.ojContext.options['display'] === 'icons') {
                this.ojContext.element.addClass(this._ICON_ONLY_STYLE_CLASS);
                icon.attr('aria-label', this.getItemLabel($item));
                icon.attr('role','img');
            }
            $item.closest('ul').addClass(this._NAVLIST_HAS_ICONS);
        } else {
            itemContent.addClass(this._NAVLIST_ITEM_HAS_NO_ICON);
        }

        if ($item.hasClass('oj-disabled')) {
            this.getFocusItem($item).attr('aria-disabled', 'true');
        } else {
            if (groupItem.length > 0) {
                groupItem.addClass('oj-default');
            } else {
                $item.addClass('oj-default');
            }
        }
        this.m_listHandler.ModifyListItem($item, itemContent);
        _ojNavigationListView.superclass.itemRenderComplete.apply(this, arguments);

    },

    /**
     * Return node for given locator
     *
     * @override
     */
    getNodeBySubId: function (locator)
    {
        var subId, key, item;

        if (locator === null)
        {
            return this.element ? this.element[0] : null;
        }

        subId = locator['subId'];
        if (subId === 'oj-navigationlist-item')
        {
            key = locator['key'];
            item = this.FindElementByKey(key);
            return item;
        } else if (subId === 'oj-navigationlist-previous-link') {
            return this.m_listHandler._previousLink ? this.m_listHandler._previousLink[0] : null;
        } else if (subId === 'oj-navigationlist-hierarchical-button') {
            return this.m_listHandler._hviewBtn ? this.m_listHandler._hviewBtn[0] : null;
        } else if (subId === 'oj-navigationlist-hierarchical-menu') {
            return this.m_listHandler._hviewMenu ? this.m_listHandler._hviewMenu[0] : null;
        }

        // Non-null locators have to be handled by the component subclasses
        return null;
    },
    /**
     * Returns the subId locator for the given child DOM node.
     * Invoked by widget
     * @param {!Element} node - child DOM node
     * @return {Object|null} The locator for the DOM node, or <code class="prettyprint">null</code> when none is found.
     */
    getSubIdByNode: function(node)
    {
          var item, key;
          if(this.m_listHandler._previousLink && this.m_listHandler._previousLink[0] === node) {
            return {'subId': 'oj-navigationlist-previous-link'};
          } else if(this.m_listHandler._hviewBtn && this.m_listHandler._hviewBtn[0] === node) {
            return {'subId': 'oj-navigationlist-hierarchical-button'};
          } else if(this.m_listHandler._hviewMenu && this.m_listHandler._hviewMenu[0] === node) {
            return {'subId': 'oj-navigationlist-hierarchical-menu'};
          } else {
              item = this.FindItem(node);
              if (item != null && item.length > 0)
              {
                  key = this.GetKey(item[0]);
                  if (key != null)
                  {
                      return {'subId': 'oj-navigationlist-item', 'key': key};
                  }
              }
          }
          return null;
    },
    /**
     * Refresh navlist
     * @override
     */
    refresh: function () {
        this._resetNavlist();
        this._initListHandler();
        _ojNavigationListView.superclass.refresh.apply(this, arguments);
    },
    /**
     * Uninitialize dom.
     * @private
     */
    _resetNavlist: function () {
        this.ojContext.element.removeClass(this._ICON_ONLY_STYLE_CLASS);
        this.ojContext.element.removeClass(this._APPLICATION_LEVEL_NAV_STYLE_CLASS);
        this.ojContext.element.removeClass(this._PAGE_LEVEL_NAV_STYLE_CLASS);
        this._restoreContent(this.element);
        this.m_listHandler.Destroy();
    },
    /**
     * Destroy the Navigation List
     * @override
     */
    destroy: function () {
        this._resetNavlist();
        _ojNavigationListView.superclass.destroy.apply(this, arguments);
        this._list.unwrap();
        //for data source, this will be empty.
        if (this._list.is(':empty')) {
            this._list.remove();
        }
        this.ojContext.element.removeClass(this._NAVLIST_STYLE_CLASS+' '+this._NAVLIST_TOUCH_STYLE_CLASS);
        this.ojContext._off(this.element, "click");
        this.ojContext._off(this.element, "focus");
        this.ojContext._off(this.element, "blur");
        this.ojContext._off(this.element, "mouseover");
        this.ojContext._off(this.element, "mousein");
        this.ojContext._off(this.element, "mouseout");
        this.ojContext._off(this.element, "keydown");
    }

});

/**
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved.
 */
(function () {
    /**
     * @ojcomponent oj.ojNavigationList
     * @augments oj.baseComponent
     * @since 1.1.0
     *
     * @classdesc
     * <h3 id="navlistOverview-section">
     *   JET Navigation List Component
     *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#navlistOverview-section"></a>
     * </h3>
     *
     * <p>Description:  The JET Navigation List enhances a HTML list element into a themable, WAI-ARIA compliant, mobile friendly component with advance interactive features.
     *
     * <p>The JET Navigation List gets its data in following ways. </p>
     * <ul>
     * <li><b>Using table datasource</b>. This is typically used in case of flat list.There are several types of TableDataSource that
     * are available out of the box like oj.ArrayTableDataSource,oj.CollectionTableDataSource.NOTE: oj.PagingTableDataSource is not supported by navigation list.
     * For large amount of data, It is recommended to use hierarhcial navigation list with tree data source.</li>
     * <li><b>Using TreeDataSource</b>.  This is typically used to display hierarchical navigation list.  There are several types
     * of TreeDataSource that are available out of the box like oj.JsonTreeDataSource, oj.CollectionTreeDataSource </li>
     * <li><b>Using static content </b>. The structure of the content can be either flat or hierarhical.</li>
     * </ul>
     * <p>Example of flat static content</p>
     * <pre class="prettyprint">
     * <code>
     * &lt;div id="navigationlist">
     *  &lt;ul>
     *   &lt;li>&lt;a href="#">Item 1&lt;/a>&lt;/li>
     *   &lt;li>&lt;a href="#">Item 2&lt;/a>&lt;/li>
     *   &lt;li>&lt;a href="#">Item 3&lt;/a>&lt;/li>
     *  &lt;/ul>
     * &lt;/div>
     * </code></pre>
     *
     * <p>Example of hierarchical static content</p>
     * <pre class="prettyprint">
     * <code>
     * &lt;div id="navigationlist">
     *  &lt;ul >
     *   &lt;li>&lt;a href="#">Item 1&lt;/a>&lt;/li>
     *   &lt;li>&lt;a href="#">Item 2&lt;/a>&lt;/li>
     *   &lt;li>&lt;a href="#">Item 3&lt;/a>
     *     &lt;ul>
     *       &lt;li>&lt;a href="#">Item 3-1&lt;/a>&lt;/li>
     *       &lt;li>&lt;a href="#">Item 3-2&lt;/a>&lt;/li>
     *       &lt;li>&lt;a href="#">Item 3-3&lt;/a>&lt;/li>
     *       &lt;li>&lt;a href="#">Item 3-4&lt;/a>&lt;/li>
     *       &lt;li>&lt;a href="#">Item 3-5&lt;/a>&lt;/li>
     *     &lt;/ul>
     *   &lt;/li>
     *   &lt;li>&lt;a href="#">Item 4&lt;/a>&lt;/li>
     *   &lt;li>&lt;a href="#">Item 5&lt;/a>&lt;/li>
     *  &lt;/ul>
     * &lt;/div>
     * </code></pre>
     * <h4 id="treeJSON-section"> JSON Tree Node Format</h4>
     * </br>
     * Each node object typically have an
     * <code class="prettyprint">attr</code> property. This attr value will be provided as <code class="prettyprint">itemContext.data</code> to renderer function. See <a href="#itemContext">itemContext</a> section. Any node can be defined as a parent by supplying
     * a <code class="prettyprint">children</code> property, which is an array of one or more node definitions.
     * (Note: lazy-loading is not supported in navigation list so <code class="prettyprint">children</code> property should be a non empty array)
     * <p>Example: Basic JSON Tree definition
     * <pre class="prettyprint">
     * <code>
     *[
     *   {
     *     "attr": {
     *               "id": "home",
     *               "title": "Home"
     *             },
     *   },
     *   {
     *     "attr": {
     *               "id": "news",
     *               "title": "News"
     *             }
     *   },
     *   {
     *      "attr": {
     *                "id": "blogs",
     *                "title": "Blogs"
     *              },
     *      "children": [ {
     *
     *                       "attr": {
     *                                   "id": "today",
     *                                   "title": "Today"
     *                               }
     *                    },
     *                    {
     *                       "attr": {
     *                                   "id": "yesterday",
     *                                   "title": "Yesterday"
     *                               }
     *                    }
     *                  ]
     *   }
     *]
     *</code></pre>
     *</p></br>
     * <p>Any list item can be disabled by adding the <code class="prettyprint">oj-disabled</code> class to that element.  As with any DOM change, doing so post-init
     * requires a <code class="prettyprint">refresh()</code> of the component.
     *
     *
     * <h3 id="icons-section">
     *   Icons
     *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#icons-section"></a>
     * </h3>
     *
     * <p>Sublist icons are inserted automatically.  To add other icons to list items, include them in the markup and include the <code class="prettyprint">oj-navigationlist-item-icon</code> class, as follows:
     *
     * <pre class="prettyprint">
     * <code>&lt;ul id="navigationlist">
     *   &lt;li id="foo">&lt;a href="#">&lt;span class="oj-navigationlist-item-icon demo-icon-font-24 demo-palette-icon-24">&lt;/span>Foo&lt;/a>&lt;/li>
     * &lt;/ul>
     * </code></pre>
     *
     * <h3 id="styling-section">
     *   Styling
     *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#styling-section"></a>
     * </h3>
     *
     * <table class="generic-table styling-table">
     *   <thead>
     *     <tr>
     *       <th>Class(es)</th>
     *       <th>Description</th>
     *     </tr>
     *   </thead>
     *   <tbody>
     *     <tr>
     *       <td>oj-navigationlist-stack-icon-label</td>
     *       <td>Add this class to display Navigation List with Stacked icon and label. Applicable only when edge is top.</td>
     *     </tr>
     *     <tr>
     *       <td>oj-navigationlist-category-divider</td>
     *       <td>Use this class to add horizontal divider line between two categories of items.</td>
     *     </tr>
     *     <tr>
     *       <td>oj-navigationlist-item-icon</td>
     *       <td>Use this class to add icon to list item.</td>
     *     </tr>
     *     <tr>
     *       <td>oj-navigationlist-item-title</td>
     *       <td>When arbitrary content is placed in side item, mark item title using this marker class.
     *       <p>Example of item markup with arbitrary content.
     *        <pre class="prettyprint">
     *        <code>
     *&lt;li>
     *  &lt;div>
     *      &lt;span class="oj-navigationlist-item-title">Play&lt;/span>
     *      &lt;button>Button&lt;/button>
     *  &lt;/div>
     *&lt;/li>
     *        </code>
     *        </pre>
     *        </p>
     *       </td>
     *     </tr>
     *     <tr>
     *       <td>oj-navigationlist-item-text-wrap</td>
     *       <td>Use this class to wrap item label text.</td>
     *     </tr>
     *     <tr>
     *       <td>oj-navigationlist-item-dividers</td>
     *       <td>Use this class to show dividers between horizontal navigation list items.</td>
     *     </tr>
     *     <tr>
     *       <td>oj-sm-condense</td>
     *       <td>Use this class to condense horizontal navigation list items on small and up screens.
     *       </td>
     *     </tr>
     *     <tr>
     *       <td>oj-md-condense</td>
     *       <td>Use this class to condense horizontal navigation list items on medium and up screens.
     *       </td>
     *     </tr>
     *     <tr>
     *       <td>oj-lg-condense</td>
     *       <td>Use this class to condense horizontal navigation list items on large and up screens.
     *       </td>
     *     </tr>
     *     <tr>
     *       <td>oj-xl-condense</td>
     *       <td>Use this class to condense horizontal navigation list items on extra large and up screens.
     *       </td>
     *     </tr>
     *   </tbody>
     * </table>
     * <h3 id="touch-section">
     *   Touch End User Information
     *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#touch-section"></a>
     * </h3>
     *
     * {@ojinclude "name":"touchDoc"}
     *
     * <h3 id="keyboard-section">
     *   Keyboard End User Information
     *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#keyboard-section"></a>
     * </h3>
     * {@ojinclude "name":"keyboardDoc"}
     *
     * <p>Disabled items will not receive keyboard focus and do not allow any interaction.
     *
     *<h3 id="context-section">
     *   Item Context
     *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#context-section"></a>
     * </h3>
     *
     * <p>For all item options, developers can specify a function as the return value.  The function takes a single argument, which is an object that contains contextual information about the particular item.  This gives developers the flexibility to return different value depending on the context.</p>
     *
     * <p>The context paramter contains the following keys:</p>
     * <table class="keyboard-table">
     *   <thead>
     *     <tr>
     *       <th>Key</th>
     *       <th>Description</th>
     *     </tr>
     *   </thead>
     *   <tbody>
     *     <tr>
     *       <td><kbd>component</kbd></td>
     *       <td>A reference to the NavigationList widget constructor.</td>
     *     </tr>
     *     <tr>
     *       <td><kbd>datasource</kbd></td>
     *       <td>A reference to the data source object. (Not available for static content)</td>
     *     </tr>
     *     <tr>
     *       <td><kbd>index</kbd></td>
     *       <td>The index of the item, where 0 is the index of the first item.  In the hierarchical case the index is relative to its parent.</td>
     *     </tr>
     *     <tr>
     *       <td><kbd>key</kbd></td>
     *       <td>The key of the item.</td>
     *     </tr>
     *     <tr>
     *       <td><kbd>data</kbd></td>
     *       <td>The data object for the item.</td>
     *     </tr>
     *     <tr>
     *       <td><kbd>parentElement</kbd></td>
     *       <td>The list item element.  The renderer can use this to directly append content.</td>
     *     </tr>
     *   </tbody>
     * </table>
     *
     * <p></p>
     * <p>If the data is hierarchical, the following additional contextual information are available:</p>
     * <table class="keyboard-table">
     *   <thead>
     *     <tr>
     *       <th>Key</th>
     *       <th>Description</th>
     *     </tr>
     *   </thead>
     *   <tbody>
     *     <tr>
     *       <td><kbd>depth</kbd></td>
     *       <td>The depth of the item.  The depth of the first level children under the invisible root is 1.</td>
     *     </tr>
     *     <tr>
     *       <td><kbd>parentKey</kbd></td>
     *       <td>The key of the parent item.  The parent key is null for root node.</td>
     *     </tr>
     *     <tr>
     *       <td><kbd>leaf</kbd></td>
     *       <td>Whether the item is a leaf or a group item.</td>
     *     </tr>
     *   </tbody>
     * </table>
     * <h3 id="perf-section">
     *   Performance
     *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#perf-section"></a>
     * </h3>
     *
     * <h4>Data Set Size</h4>
     * <p>As a rule of thumb, it's recommended that applications limit the amount of data to display.  Displaying large
     * number of items in Navigation List makes it hard for user to find what they are looking for, but affects the load time.
     * If displaying large number of items is neccessary, it is recommended to group the items in to hierarchial navigation list.
     *
     * <h4>Item Content</h4>
     * <p>Navigation List allows developers to specify arbitrary content inside its item. In order to minimize any negative effect on
     * performance, you should avoid putting a large number of heavy-weight components inside because as you add more complexity
     * to the structure, the effect will be multiplied because there can be many items in the Navigation List.</p>
     *
     * <h4>Expand All</h4>
     * <p>While Navigation List provides a convenient way to initially expand all group items, it might have an impact
     * on the initial rendering performance since expanding each group item might cause a fetch from the server depending on
     * the TreeDataSource.  Other factors that could impact performance includes the depth of the tree, and the number of children
     * in each level.</p>
     *
     * <h3 id="a11y-section">
     *   Accessibility
     *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#a11y-section"></a>
     * </h3>
     *
     * <p>Disabled content: JET supports an accessible luminosity contrast ratio,
     * as specified in <a href="http://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast">WCAG 2.0 - Section 1.4.3 "Contrast"</a>,
     * in the themes that are accessible.  (See the "Theming" chapter of the JET Developer Guide for more information on which
     * themes are accessible.)  Note that Section 1.4.3 says that text or images of text that are part of an inactive user
     * interface component have no contrast requirement.  Because disabled content may not meet the minimum contrast ratio
     * required of enabled content, it cannot be used to convey meaningful information.<p>
     *
     *
     * <h3 id="rtl-section">
     *   Reading direction
     *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#rtl-section"></a>
     * </h3>
     *
     * <p>The only supported way to set the reading direction (LTR or RTL) is to set the <code class="prettyprint">"dir"</code> attribute on the
     * <code class="prettyprint">&lt;html></code> element of the page.  As with any JET component, in the unusual case that the reading direction
     * is changed post-init, the navigation list must be <code class="prettyprint">refresh()</code>ed, or the page must be reloaded.
     *
     *
     * <h3 id="pseudos-section">
     *   Pseudo-selectors
     *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#pseudos-section"></a>
     * </h3>
     *
     * <p>The <code class="prettyprint">:oj-navigationlist</code> pseudo-selector can be used in jQuery expressions to select JET Navigation Lists.  For example:
     *
     * <pre class="prettyprint">
     * <code>$( ":oj-navigationlist" ) // selects all JET Navigation lists on the page
     * $myEventTarget.closest( ":oj-navigationlist" ) // selects the closest ancestor that is a JET Navigation lists
     * </code></pre>
     *
     * <!-- - - - - Above this point, the tags are for the class.
     *              Below this point, the tags are for the constructor (initializer). - - - - - - -->
     *
     *
     * @desc Creates a JET Navigation List.
     *
     * @param {Object=} options a map of option-value pairs to set on the component
     *
     * @example <caption>Initialize the Navigation list with no options specified:</caption>
     * $( ".selector" ).ojNavigationList();
     *
     * @example <caption>Initialize the Navigation list with some options and callbacks specified:</caption>
     * $( ".selector" ).ojNavigationList( { "drillMode": "none", "beforeSelect": function( event, ui ) {} } );
     *
     * @example <caption>Initialize the Navigation list via the JET <code class="prettyprint">ojComponent</code> binding:</caption>
     * &lt;div id="navigationlist" data-bind="ojComponent: { component: 'ojNavigationList',
     *                                         drillMode: 'none',
     *                                         beforeSelect: beforeSelectItem }">
     */
    oj.__registerWidget("oj.ojNavigationList", $['oj']['baseComponent'], {
        widgetEventPrefix: 'oj',
        options: {
            /**
             * Key of the current item.  Current item is the list item which is having active focus.  Note that if currentItem
             * is set to an item that is currently not available (not fetched or
             * inside a collapsed parent node), then the value is ignored.
             *
             * @expose
             * @public
             * @instance
             * @memberof! oj.ojNavigationList
             * @type {Object}
             * @default <code class="prettyprint">null</code>
             *
             * @example <caption>Get the current item:</caption>
             * $( ".selector" ).ojNavigationList("option", "currentItem");
             *
             * @example <caption>Set the current item on the NavigationList during initialization:</caption>
             * $(".selector").ojNavigationList({"currentItem", "item2"});
             */
            currentItem: null,
            /**
             * Defines style of navigation used to drill down hierarchical list items.
             * <ul>
             * <li>none</li>
             * <li>collapsible</li>
             * <li>sliding</li>
             * </ul>
             *
             * @expose
             * @memberof oj.ojNavigationList
             * @instance
             * @type {string}
             * @default <code class="prettyprint">"none"</code>
             * @ojvalue {string} "none" All group items are expanded by default and user not allowed to collapse them.
             * @ojvalue {string} "collapsible" Allows user to expand and collapse group items. If there are more than two levels in hierarchy, <code class="prettyprint">sliding</code> is preferered drill mode.
             * @ojvalue {string} "sliding" This is typically used for hierarchical lists. This allows user to view one level at a time.
             *
             * @example <caption>Initialize the menu with the <code class="prettyprint">drillMode</code> option specified:</caption>
             * $( ".selector" ).ojNavigationList({ drillMode: "collapsible" });
             *
             * @example <caption>Get the <code class="prettyprint">drillMode</code> option, after initialization:</caption>
             * // getter
             * var drillMode = $( ".selector" ).ojNavigationList( "option", "drillMode" );
             */
            drillMode: "none",
            /**
             * The position of the Navigation List. Valid Values: top and start.
             * <p> NOTE: when value is <code class="prettyprint">top</code>,<code class="prettyprint">"none"</code> is the only supported drillMode and it also does't support hierarchical items.
             * @expose
             * @name edge
             * @memberof oj.ojNavigationList
             * @instance
             * @type {string|null}
             * @ojvalue {string} "top" This renders list items horizontally.
             * @ojvalue {string} "start" This renders list items vertically.
             * @default <code class="prettyprint">"start"</code>
             */
            edge: "start",
            /**
            * Specifies the level at which user can see hiearchical menu button.This is only valid when <code class="prettyprint">drillMode</code> set to <code class="prettyprint">sliding</code>.
            * Default value is 0, shows hiearchical menu always. If value is -1, then it will never be shown.
            *
            * <p>The default value for hierarchyMenuDisplayThresholdLevel varies by theme.  Each theme can set its default by setting
            * <code class="prettyprint">$navigationListHierarchyMenuDisplayThresholdLevelOptionDefault</code> as seen in the example below.
            *
            * @expose
            * @name hierarchyMenuDisplayThresholdLevel
            * @memberof oj.ojNavigationList
            * @instance
            * @type {number|null}
            * @default Varies by theme. <code class="prettyprint">"0"</code> if not specified in theme.
            *
            * @example <caption>Initialize the navigation list with the <code class="prettyprint">hierarchyMenuDisplayThresholdLevel</code> option specified:</caption>
            * $( ".selector" ).ojNavigationList( { "hierarchyMenuDisplayThresholdLevel": "4" } );
            *
            * @example <caption>Get or set the <code class="prettyprint">hierarchyMenuDisplayThresholdLevel</code> option, after initialization:</caption>
            * // getter
            * var display = $( ".selector" ).ojNavigationList( "option", "hierarchyMenuDisplayThresholdLevel" );
            *
            * // setter
            * $( ".selector" ).ojNavigationList( "option", "hierarchyMenuDisplayThresholdLevel", "4" );
            *
            * @example <caption>Set the default in the theme (SCSS) :</caption>
            * $navigationListHierarchyMenuDisplayThresholdLevelOptionDefault: -1 !default;
            */
            hierarchyMenuDisplayThresholdLevel: 0,
            /**
             * Label for top level list items.
             * <p>NOTE: This is needed only for sliding navigation list where
             * this will be used as title for the top level list elements.
             * @type {?string}
             * @default <code class="prettyprint">Navigation List</code>
             * @expose
             * @instance
             * @memberof oj.ojNavigationList
             */
            rootLabel: null,
            /**
             * Item key of currently selected list item. If the value is modified
             * by an application, navigation list UI is modified to match the new value.
             * @type {string|null}
             * @default <code class="prettyprint">null</code>
             * @expose
             * @instance
             * @memberof oj.ojNavigationList
             */
            selection: null,
            /**
             * Specifies which items in NavigationList should be expanded. Specifies "all" value to expand all items.  Specifies
             * an array of keys to expand specific items.
             *
             * The default value is "auto", which means that NavigationList will determine which items are expanded by default.
             * Specifically, if drillMode is set to "none", then all items are expanded, any other values for
             * drillMode will not cause any items to expand by default.
             *
             * Note that expanded does not return the currently expanded items.  This only returns what is specified
             * by default.  To retrieve the keys of currently expanded items, use the <code class="prettyprint">getExpanded</code>
             * method.
             * @type {Array|string}
             * @default <code class="prettyprint">[]</code>
             * @expose
             * @instance
             * @memberof oj.ojNavigationList
             */
            expanded: "auto",
            /**
             * The data source for the NavigationList accepts either a oj.TableDataSource or oj.TreeDataSource.
             * See the data source section in the introduction for out of the box data source types.
             * If the data attribute is not specified, the child elements are used as content.  If there's no
             * content specified, then an empty list is rendered.
             *
             * @expose
             * @memberof! oj.ojNavigationList
             * @instance
             * @type {oj.TableDataSource|oj.TreeDataSource}
             * @default <code class="prettyprint">null</code>
             *
             * @example <caption>Initialize the NavigationList with a one-dimensional array:</caption>
             * $( ".selector" ).ojNavigationList({ "data": new oj.ArrayTableDataSource([1,2,3])});
             *
             * @example <caption>Initialize the NavigationList with an oj.Collection:</caption>
             * $( ".selector" ).ojNavigationList({ "data": new oj.CollectionTableDataSource(collection)});
             */
            data: null,
            /**
             * Whether to display both the label and icons (<code class="prettyprint">"all"</code>) or just the icons (<code class="prettyprint">"icons"</code>).
             * In the latter case, the label is displayed in a tooltip instead, unless a tooltip was already supplied at create time.
             * Note: <code class="prettyprint">display="icons"</code> is valid only when <code class="prettyprint">drillMode=none</code> and navigation list is a flat list.
             * It is also mandatory to provide icons for each item as stated in <a href="#icons-section">icons section</a>.
             *
             * @expose
             * @memberof oj.ojNavigationList
             * @instance
             * @type {string}
             * @ojvalue {string} "all" Display both the label and icons.
             * @ojvalue {string} "icons" Display only the icons.This option
             * @default <code class="prettyprint">"all"</code>
             *
             * @example <caption>Initialize the NavigationList with the <code class="prettyprint">display</code> option specified:</caption>
             * $( ".selector" ).ojNavigationList( { "display": "icons" } );
             *
             * @example <caption>Get or set the <code class="prettyprint">display</code> option, after initialization:</caption>
             * // getter
             * var display = $( ".selector" ).ojNavigationList( "option", "display" );
             *
             * // setter
             * $( ".selector" ).ojNavigationList( "option", "display", "icons" );
             */
            display: "all",
            /**
             * Navigation List supports two different look-and-feel depends on its usage level, one is <code class="prettyprint">application</code> level and other is <code class="prettyprint">page</code> level.
             *
             * @expose
             * @memberof oj.ojNavigationList
             * @instance
             * @type {string}
             * @ojvalue {string} "application" Render Navigation List for application level navigation.
             * @ojvalue {string} "page" Render Navigation List for page level navigation.
             * @default <code class="prettyprint">"page"</code>
             *
             * @example <caption>Initialize the NavigationList with the <code class="prettyprint">navigationLevel</code> option specified:</caption>
             * $( ".selector" ).ojNavigationList( { "navigationLevel": "application" } );
             *
             * @example <caption>Get or set the <code class="prettyprint">navigationLevel</code> option, after initialization:</caption>
             * // getter
             * var display = $( ".selector" ).ojNavigationList( "option", "navigationLevel" );
             *
             * // setter
             * $( ".selector" ).ojNavigationList( "option", "navigationLevel", "application" );
             */
            navigationLevel: "page",
            /**
             * The item option contains a subset of options for items.
             *
             * @expose
             * @memberof! oj.ojNavigationList
             * @instance
             */
            item: {
                /**
                 * The renderer function that renders the content of the item. See <a href="#context-section">itemContext</a>
                 * in the introduction to see the object passed into the renderer function.
                 * The function returns either a String or a DOM element of the content inside the item.
                 * If the developer chooses to manipulate the list element directly, the function should return
                 * nothing. If no renderer is specified, Navigation List will treat the data as a String.
                 *
                 * @expose
                 * @alias item.renderer
                 * @memberof! oj.ojNavigationList
                 * @instance
                 * @type {function(Object)|null}
                 * @default <code class="prettyprint">null</code>
                 *
                 * @example <caption>Initialize the NavigationList with a renderer:</caption>
                 * $( ".selector" ).ojNavigationList({ "data":data, "item": { "renderer": function(itemContext) {
                 *                                            return itemContext['data'].get('FIRST_NAME');}}});
                 *
                 * @example <caption>Get or set the <code class="prettyprint">renderer</code> option, after initialization:</caption>
                 * // set the renderer function
                 * $( ".selector" ).ojNavigationList( "option", "item.renderer", myFunction});
                 */
                renderer: null,
                /**
                 * Whether the item is selectable.
                 * See <a href="#context-section">itemContext</a> in the introduction to see the object passed into the selectable function.
                 *
                 * @expose
                 * @alias item.selectable
                 * @memberof! oj.ojNavigationList
                 * @instance
                 * @type {function(Object)|boolean}
                 * @default <code class="prettyprint">true</code>
                 *
                 * @example <caption>Initialize the NavigationList such that the first 3 items are not selectable:</caption>
                 * $( ".selector" ).ojNavigationList({ "data":data, "item": { "selectable": function(itemContext) {
                 *                                            return itemContext['index'] > 3;}}});
                 */
                selectable: true
                        /**
                         * The knockout template used to render the content of the item.
                         *
                         * This attribute is only exposed via the <code class="prettyprint">ojComponent</code> binding, and is not a
                         * component option.
                         *
                         * @ojbindingonly
                         * @name item.template
                         * @memberof! oj.ojNavigationList
                         * @instance
                         * @type {string|null}
                         * @default <code class="prettyprint">null</code>
                         *
                         * @example <caption>Specify the <code class="prettyprint">template</code> when initializing NavigationList:</caption>
                         * // set the template
                         * &lt;div id="navlist" data-bind="ojComponent: {component: 'ojNavigationList', data: dataSource, template: 'my_template'}"&gt;&lt;/div&gt;
                         */
            },
            //Events
            /**
             * <p>Triggered before this list item is selected.
             * To prevent the item selection, return <code class="prettyprint">false</code> from event handler or invoke <code class="prettyprint">event.preventDefault()</code>.
             * <p>The <code class="prettyprint">ui.key</code> contains item key which uniquely identifies the item.
             * <code class="prettyprint">ui.item</code> payload field contains item element being selected.
             *
             * @expose
             * @event
             * @memberof! oj.ojNavigationList
             * @instance
             * @property {Event} event <code class="prettyprint">jQuery</code> event object
             * @property {Object} ui Parameters
             * @property {Object} ui.key Selected list item key.
             * @property {jQuery} ui.item Selected list item.
             *
             * @example <caption>Initialize the Navigation List with the <code class="prettyprint">beforeSelect</code> callback specified:</caption>
             * $( ".selector" ).ojNavigationList({
             *     "beforeSelect": function( event, ui ) {}
             * });
             *
             * @example <caption>Bind an event listener to the <code class="prettyprint">ojbeforeselect</code> event:</caption>
             * $( ".selector" ).on( "ojbeforeselect", function( event, ui ) {} );
             */
            beforeSelect: null,
            /**
             * Triggered before an item is collapse via the <code class="prettyprint">expanded</code> option,
             * the <code class="prettyprint">collapse</code> method, or via the UI.
             * To prevent the item being collapsed, return <code class="prettyprint">false</code> from event handler or invoke <code class="prettyprint">event.preventDefault()</code>.
             *
             * @expose
             * @event
             * @memberof! oj.ojNavigationList
             * @instance
             * @property {Event} event <code class="prettyprint">jQuery</code> event object
             * @property {Object} ui Parameters
             * @property {Object} ui.key the key of the item to be collapse
             * @property {jQuery} ui.item the item to be collapse
             *
             * @example <caption>Initialize the NavigationList with the <code class="prettyprint">beforeCollapse</code> callback specified:</caption>
             * $( ".selector" ).ojNavigationList({
             *     "beforeCollapse": function( event, ui ) {
             *         // return false to veto the event, which prevents the item to collapse
             *     }
             * });
             *
             * @example <caption>Bind an event listener to the <code class="prettyprint">ojbeforecollapse</code> event:</caption>
             * $( ".selector" ).on( "ojbeforecollapse", function( event, ui ) {} );
             */
            beforeCollapse: null,
            /**
             * Triggered before the current item is changed via the <code class="prettyprint">currentItem</code> option or via the UI.
             * To prevent the item being focused, return <code class="prettyprint">false</code> from event handler or invoke <code class="prettyprint">event.preventDefault()</code>.
             * @expose
             * @event
             * @memberof! oj.ojNavigationList
             * @instance
             * @property {Event} event <code class="prettyprint">jQuery</code> event object
             * @property {Object} ui Parameters
             * @property {Object} ui.previousKey the key of the previous item
             * @property {jQuery} ui.previousItem the previous item
             * @property {Object} ui.key the key of the new current item
             * @property {jQuery} ui.item the new current item
             *
             * @example <caption>Initialize the NavigationList with the <code class="prettyprint">beforeCurrentItem</code> callback specified:</caption>
             * $( ".selector" ).ojNavigationList({
             *     "beforeCurrentItem": function( event, ui ) {
             *         // return false to veto the event, which prevents the item to become focus
             *     }
             * });
             *
             * @example <caption>Bind an event listener to the <code class="prettyprint">ojbeforecurrentitem</code> event:</caption>
             * $( ".selector" ).on( "ojbeforecurrentitem", function( event, ui ) {} );
             */
            beforeCurrentItem: null,
            /**
             * Triggered after an item has been collapsed via the <code class="prettyprint">expanded</code> option,
             * the <code class="prettyprint">collapse</code> method, or via the UI.
             *
             * @expose
             * @event
             * @memberof! oj.ojNavigationList
             * @instance
             * @property {Event} event <code class="prettyprint">jQuery</code> event object
             * @property {Object} ui Parameters
             * @property {Object} ui.key The key of the item that was just collapsed.
             * @property {jQuery} ui.item The list item that was just collapsed.
             *
             * @example <caption>Initialize the NavigationList with the <code class="prettyprint">expand</code> callback specified:</caption>
             * $( ".selector" ).ojNavigationList({
             *     "collapse": function( event, ui ) {}
             * });
             *
             * @example <caption>Bind an event listener to the <code class="prettyprint">ojcollapse</code> event:</caption>
             * $( ".selector" ).on( "ojcollapse", function( event, ui ) {} );
             */
            collapse: null,
            /**
             * Triggered before an item is expand via the <code class="prettyprint">expanded</code> option,
             * the <code class="prettyprint">expand</code> method, or via the UI.
             * To prevent the item being expanded, return <code class="prettyprint">false</code> from event handler or invoke <code class="prettyprint">event.preventDefault()</code>.
             * @expose
             * @event
             * @memberof! oj.ojNavigationList
             * @instance
             * @property {Event} event <code class="prettyprint">jQuery</code> event object
             * @property {Object} ui Parameters
             * @property {Object} ui.key the key of the item to be expand
             * @property {jQuery} ui.item the item to be expand
             *
             * @example <caption>Initialize the NavigationList with the <code class="prettyprint">beforeExpand</code> callback specified:</caption>
             * $( ".selector" ).ojNavigationList({
             *     "beforeExpand": function( event, ui ) {
             *         // return false to veto the event, which prevents the item to expand
             *     }
             * });
             *
             * @example <caption>Bind an event listener to the <code class="prettyprint">ojbeforeexpand</code> event:</caption>
             * $( ".selector" ).on( "ojbeforeexpand", function( event, ui ) {} );
             */
            beforeExpand: null,
            /**
             * Triggered after an item has been expanded via the <code class="prettyprint">expanded</code> option,
             * the <code class="prettyprint">expand</code> method, or via the UI.
             *
             * @expose
             * @event
             * @memberof! oj.ojNavigationList
             * @instance
             * @property {Event} event <code class="prettyprint">jQuery</code> event object
             * @property {Object} ui Parameters
             * @property {Object} ui.key The key of the item that was just expanded.
             * @property {jQuery} ui.item The list item that was just expanded.
             *
             * @example <caption>Initialize the NavigationList with the <code class="prettyprint">expand</code> callback specified:</caption>
             * $( ".selector" ).ojNavigationList({
             *     "expand": function( event, ui ) {}
             * });
             *
             * @example <caption>Bind an event listener to the <code class="prettyprint">ojexpand</code> event:</caption>
             * $( ".selector" ).on( "ojexpand", function( event, ui ) {} );
             */
            expand: null,
            /**
             * Fired whenever a supported component option changes, whether due to user interaction or programmatic
             * intervention.  If the new value is the same as the previous value, no event will be fired.
             *
             * @expose
             * @event
             * @memberof! oj.ojNavigationList
             * @instance
             * @property {Event} event <code class="prettyprint">jQuery</code> event object
             * @property {Object} ui Parameters
             * @property {string} ui.option the name of the option that is changing
             * @property {boolean} ui.previousValue the previous value of the option
             * @property {boolean} ui.value the current value of the option
             * @property {Object} ui.optionMetadata information about the option that is changing
             * @property {string} ui.optionMetadata.writeback <code class="prettyprint">"shouldWrite"</code> or
             *           <code class="prettyprint">"shouldNotWrite"</code>.  For use by the JET writeback mechanism.
             * @property {jQuery} ui.item For <code class="prettyprint">selection</code> option change, this will be the selected list item. Otherwise it is <code class="prettyprint">undefined</code>.
             *
             * @example <caption>Initialize component with the <code class="prettyprint">optionChange</code> callback</caption>
             * $(".selector").ojNavigationList({
             *   'optionChange': function (event, data) {
             *        if (data['option'] === 'selection') { // handle selection change }
             *    }
             * });
             * @example <caption>Bind an event listener to the ojoptionchange event</caption>
             * $(".selector").on({
             *   'ojoptionchange': function (event, data) {
             *       window.console.log("option that changed is: " + data['option']);
             *   };
             * });
             */
            optionChange: null
        },
        /**
         * Create the Navigation List
         * @override
         * @memberof! oj.ojNavigationList
         * @protected
         */
        _ComponentCreate: function () {
            this._super();
            this._setup();
        },
        /**
         * Initialize the NavigationList after creation
         * @protected
         * @override
         * @memberof! oj.ojNavigationList
         */
        _AfterCreate: function ()
        {
            this._super();
            this.navlist.afterCreate();
        },
        /**
         * When the <a href="#contextMenu">contextMenu</a> option is set, this method is called when the user invokes the context menu via
         * the default gestures: right-click, pressHold, and <kbd>Shift-F10</kbd>.  Components should not call this method directly.
         *
         * @param {!Object} menu The JET Menu to open as a context menu.  Always non-<code class="prettyprint">null</code>.
         * @param {!Event} event What triggered the menu launch.  Always non-<code class="prettyprint">null</code>.
         * @param {string} eventType "mouse", "touch", or "keyboard".  Never <code class="prettyprint">null</code>.
         * @private
         */
        _NotifyContextMenuGesture: function (menu, event, eventType)
        {
            this.navlist.notifyContextMenuGesture(menu, event, eventType);
        },
        /**
         * Initialize DOM structure for navigation list.
         * @private
         * @memberof! oj.ojNavigationList
         */
        _setup: function () {
            for (var key in this.options) {
                this._validateOptionValues(key, this.options[key]);
            }
            this._validateOptionsForIconsOnlyAndHorizontalList(this.options['drillMode'], this.options['display'], this.options['edge']);

            this.navlist = new _ojNavigationListView();

            var opts = {};
            opts.ojContext = this;
            opts = $.extend(this.options, opts);
            this.navlist.init(opts);
        },

        getNodeBySubId: function (locator)
        {
            return this.navlist.getNodeBySubId(locator);
        },
        getSubIdByNode: function(node)
        {
            return this.navlist.getSubIdByNode(node);
        },
        /**
         * Expand an item.<p>
         * Note when vetoable is set to false, beforeExpand event will still be fired but the event cannot be veto.<p>
         *
         * @expose
         * @memberof oj.ojNavigationList
         * @instance
         * @param {Object} key the key of the item to expand
         * @param {boolean} vetoable if event is vetoable
         */
        expand: function (key, vetoable)
        {
            this.navlist.expandKey(key, vetoable, true, true);
        },
        /**
         * Collapse an item.<p>
         * Note when vetoable is set to false, beforeCollapse event will still be fired but the event cannot be veto.<p>
         *
         * @expose
         * @memberof oj.ojNavigationList
         * @instance
         * @param {Object} key the key of the item to collapse
         * @param {boolean} vetoable if event is vetoable
         */
        collapse: function (key, vetoable)
        {
            this.navlist.collapseKey(key, vetoable, true);
        },
        /**
         * Gets the key of currently expanded items.
         *
         * @expose
         * @memberof oj.ojNavigationList
         * @instance
         * @return {Array} array of keys of currently expanded items
         */
        getExpanded: function ()
        {
            return this.navlist.getExpanded();
        },
        _validateOptionsForIconsOnlyAndHorizontalList: function (drillMode, display, edge) {
            if (drillMode !== 'none') {
                if (display === 'icons') {
                    throw "Icon only navigation list should have drillMode set to 'none'.";
                }
                if (edge === 'top') {
                    throw "Horizontal navigation list should have drillMode set to 'none'.";
                }
            }
        },
        _validateOptionValues: function (key, value) {
            var valid = true;
            if (key === "drillMode") {
                valid = (value === 'none' || value === 'collapsible' || value === 'sliding');
            } else if (key === "display") {
                valid = (value === 'all' || value === 'icons');
            } else if (key === "edge") {
                valid = (value === 'top' || value === 'start');
            }

            if (!valid) {
                throw "Invalid value: " + value + " for key: " + key;
            }

        },
        /**
         * Set single option
         * @param {string} key the option key
         * @param {Object} value value for option
         * @override
         * @memberof oj.ojNavigationList
         * @instance
         * @private
         */
        _setOption: function (key, value) {
            this._validateOptionValues(key, value);
            switch (key) {
                case "drillMode":
                    this._validateOptionsForIconsOnlyAndHorizontalList(value, this.options['display'], this.options['edge']);
                    break;
                case "display":
                    this._validateOptionsForIconsOnlyAndHorizontalList(this.options['drillMode'], value, this.options['edge']);
                    break;
                case "edge":
                    this._validateOptionsForIconsOnlyAndHorizontalList(this.options['drillMode'], this.options['display'], value);
                    break;
            }
            return this._super(key, value);
        },
        /**
         * Sets multiple options
         * @param {Object} options the options object
         * @param {Object} flags additional flags for option
         * @override
         * @private
         */
        _setOptions: function (options, flags)
        {
            var result = this.navlist.setOptions(options, flags);

            for (var key in options)
            {
              if( result.skipOptions.indexOf(key) < 0 ) {
                this._setOption(key, options[key], flags);
              }
            }

            if (result.needRefresh)
            {
                this.navlist.refresh();
            }
            return this;
        },
        /**
         * In browsers [Chrome v35, Firefox v24.5, IE9, Safari v6.1.4], blur and mouseleave events are generated for hidden content but not detached content,
         * so for detached content only, we must use this hook to remove the focus and hover classes.
         * @override
         * @private
         */
        _NotifyDetached: function ()
        {
            this.navlist.notifyDetached();
        },
        /**
         * Invoked when application calls oj.Components.subtreeShown.
         * @override
         * @private
         */
        _NotifyShown: function()
        {
             this.navlist.notifyShown();
        },
        /**
         * Refreshes the visual state of the Navigation List. JET components require a <code class="prettyprint">refresh()</code> after the DOM is
         * programmatically changed underneath the component.
         * <p>This method does not accept any arguments.
         *
         * @expose
         * @memberof oj.ojNavigationList
         * @instance
         *
         * @example <caption>Invoke the <code class="prettyprint">refresh</code> method:</caption>
         * $( ".selector" ).ojNavigationList( "refresh" );
         */
        refresh: function () {
            this._super();
            this.navlist.refresh();

        },
        /**
         * Destroy the Navigation List
         * @memberof! oj.ojNavigationList
         * @private
         */
        _destroy: function ()
        {
            this.navlist.destroy();
            this._super();
            delete this.navlist;
        }

        // Fragments:

        /**
         * <table class="keyboard-table">
         *   <thead>
         *     <tr>
         *       <th>Target</th>
         *       <th>Gesture</th>
         *       <th>Action</th>
         *     </tr>
         *   </thead>
         *   <tbody>
         *     <tr>
         *       <td rowspan="2">List Item</td>
         *       <td><kbd>Tap</kbd></td>
         *       <td>Selects the item.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>Press & Hold</kbd></td>
         *       <td>Display context menu</td>
         *     </tr>
         *     <tr>
         *       <td rowspan="2">Group Item</td>
         *       <td><kbd>Tap</kbd></td>
         *       <td>Expand or collapse the group item if <code class="prettyprint">drillMode</code> is set to collapsible.
         *       When <code class="prettyprint">drillMode</code> is set to sliding, sublist will silde in.
         *       When <code class="prettyprint">drillMode</code> is set to none, group item will be selecetd.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>Press & Hold</kbd></td>
         *       <td>Display context menu</td>
         *     </tr>
         *     <tr>
         *       <td>Hierarchical Menu button</td>
         *       <td><kbd>Tap</kbd></td>
         *       <td>Open menu. Refer <a href="oj.ojButton.html#touch-section">menu button</a> touch documentation. Note: This is applicable only for Sliding Navigation List. </td>
         *     </tr>
         *     <tr>
         *       <td>Previous Icon or List Header</td>
         *       <td><kbd>Tap</kbd></td>
         *       <td>Collapses the sublist and slides to parent list. Note: This is applicable only for Sliding Navigation List. </td>
         *     </tr>
         *   </tbody>
         * </table>
         *
         * @ojfragment touchDoc - Used in touch gesture section of classdesc, and standalone gesture doc
         * @memberof oj.ojNavigationList
         */

        /**
         * <table class="keyboard-table">
         *   <thead>
         *     <tr>
         *       <th>Target</th>
         *       <th>Key</th>
         *       <th>Action</th>
         *     </tr>
         *   </thead>
         *   <tbody>
         *     <tr>
         *       <td rowspan="10">List Item</td>
         *       <td><kbd>Enter</kbd> or <kbd>Space</kbd></td>
         *       <td>Selects list item.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>UpArrow</kbd></td>
         *       <td>Moves focus to the previous visible list item.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>DownArrow</kbd></td>
         *       <td>Moves focus to the next  visible list item</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>RightArrow</kbd> (<kbd>LeftArrow</kbd> in RTL)</td>
         *       <td>For horizontal navigation list,focus will be moved to next visible item.
         *       </td>
         *     </tr>
         *     <tr>
         *       <td><kbd>LeftArrow</kbd> (<kbd>RightArrow</kbd> in RTL)</td>
         *       <td>For horizontal navigation list,focus will be moved to previous visible item.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>Home</kbd></td>
         *       <td>Moves focus to the first visible list item.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>End</kbd></td>
         *       <td>Moves focus to the last visible list item.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>F2</kbd></td>
         *       <td>If focus is on a list item, pressing F2 will make its contents accessible using TAB.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>Esc</kbd></td>
         *       <td>When F2 mode is enabled, press Esc to exit F2 mode.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>Shift+Tab</kbd></td>
         *       <td>Move fous to hierarchical menu button.Only applicable for sliding navigation list and when hierarchial menu button is enabled.</td>
         *     </tr>
         *     <tr>
         *       <td rowspan="2">Group Item</td>
         *       <td><kbd>RightArrow</kbd> (<kbd>LeftArrow</kbd> in RTL)</td>
         *       <td>If focus is on collapsed node, expands the sub list.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>LeftArrow</kbd> (<kbd>RightArrow</kbd> in RTL)</td>
         *       <td>If focus is on expanded node, collapses the sub list.</td>
         *     </tr>
         *     <tr>
         *       <td>List Item in sublist</td>
         *       <td><kbd>Esc</kbd></td>
         *       <td>Applicable only for sliding navigation list. If focus is in a sub list, closes the sublist and moves focus to the parent list item.</td>
         *     </tr>
         *     <tr>
         *       <td rowspan="3">Hierarchical Menu button</td>
         *       <td><kbd>Enter</kbd></td>
         *       <td>Open menu. Refer <a href="oj.ojButton.html#keyboard-section">menu button</a> keyboard documentation. Note: This target is visible only for Sliding Navigation List.</td>
         *     </tr>
         *     <tr>
         *       <td><kbd>Tab</kbd></td>
         *       <td>Moves focus to current list item. Note: This target is visible only for Sliding Navigation List. </td>
         *     </tr>
         *     <tr>
         *       <td><kbd>Shift + Tab</kbd></td>
         *       <td>Moves focus to Previous Icon. Note: This target is visible only for Sliding Navigation List.</td>
         *     </tr>
         *     <tr>
         *       <td rowspan="2">Previous Icon or List Header</td>
         *       <td><kbd>Enter</kbd></td>
         *       <td>Collapses the sublist and slides to parent list.Note: This target is visible only for Sliding Navigation List. </td>
         *     </tr>
         *     <tr>
         *       <td><kbd>Tab</kbd></td>
         *       <td>Moves focus to Hierarchical Menu button. Note: This target is visible only for Sliding Navigation List. </td>
         *     </tr>
         *
         *   </tbody>
         * </table>
         *
         *
         * @ojfragment keyboardDoc - Used in keyboard section of classdesc, and standalone gesture doc
         * @memberof oj.ojNavigationList
         */

         // SubId Locators *****************************************************

         /**
          * <p>Sub-ID for the ojNavigationList component's list item element.</p>
          *
          * <p>
          * To lookup the list items the locator object should have the following:
          * <ul>
          * <li><b>subId</b>: 'oj-navigationlist-item'</li>
          * <li><b>key</b>: the key of the item</li>
          * </ul>
          *
          * @ojsubid oj-navigationlist-item
          * @memberof oj.ojNavigationList
          *
          * @example <caption>Get the list item element with key 'foo':</caption>
          * var node = $( ".selector" ).ojNavigationList( "getNodeBySubId", {'subId': 'oj-navigationlist-item', 'key': 'foo'} );
          */
          /**
           * <p>Sub-ID for the ojNavigationList component's previous link element.</p>
           *
           * To lookup the previous link the locator object should have the following:
           * <ul>
           * <li><b>subId</b>: 'oj-navigationlist-previous-link'</li>
           * </ul>
           * @ojsubid
           * @memberof oj.ojNavigationList
           *
           * @example <caption>Get the previous link element:</caption>
           * var node = $( ".selector" ).ojNavigationList( "getNodeBySubId", {'subId': 'oj-navigationlist-previous-link'} );
           */
           /**
            * <p>Sub-ID for the ojNavigationList component's hierarchical menu button element.
            * See the <a href="#getNodeBySubId">getNodeBySubId</a> method for details.</p>
            *
            * To lookup the hierarchical menu button the locator object should have the following:
            * <ul>
            * <li><b>subId</b>: 'oj-navigationlist-hierarchical-button'</li>
            * </ul>
            * @ojsubid
            * @name oj-navigationlist-hierarchical-button
            * @memberof oj.ojNavigationList
            *
            * @example <caption>Get the hierarchical menu button element:</caption>
            * var node = $( ".selector" ).ojNavigationList( "getNodeBySubId", {'subId': 'oj-navigationlist-hierarchical-button'} );
            */
            /**
             * <p>Sub-ID for the ojNavigationList component's hierarchical menu element.</p>
             *
             * To lookup the hierarchical menu the locator object should have the following:
             * <ul>
             * <li><b>subId</b>: 'oj-navigationlist-hierarchical-menu'</li>
             * </ul>
             * @ojsubid
             * @name oj-navigationlist-hierarchical-menu
             * @memberof oj.ojNavigationList
             *
             * @example <caption>Get the hierarchical menu element:</caption>
             * var node = $( ".selector" ).ojNavigationList( "getNodeBySubId", {'subId': 'oj-navigationlist-hierarchical-menu'} );
             */

           // Node Context Objects *********************************************
    });

    // Add custom getters for properties from theming file
    oj.Components.setDefaultOptions(
      {
        'ojNavigationList':
        {
          'hierarchyMenuDisplayThresholdLevel': oj.Components.createDynamicPropertyGetter(
            function()
            {
               return oj.ThemeUtils.getOptionDefaultMap("navigationList")["hierarchyMenuDisplayThresholdLevel"];
            })
        }
      }
    );
}());

});
