Source: src/main/javascript/oracle/oj/ojpagingcontrol/ojpagingcontrol.js

Oracle® JavaScript Extension Toolkit (JET)
1.1.2

E65298-01

/**
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved.
 */

/**
 * @preserve Copyright 2013 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

/**
 * The ojPagingControl component provides paging functionality.
 * 
 * <h3 id="keyboard-section">
 *   Keyboard End User Information
 *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#keyboard-section"></a>
 * </h3>
 * 
 * {@ojinclude "name":"keyboardDoc"}
 * 
 * <h3 id="touch-section">
 *   Touch End User Information
 *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#touch-section"></a>
 * </h3>
 *
 * {@ojinclude "name":"touchDoc"}
 *  
 * @example  <caption>Initialize the paging control via the JET <code class="prettyprint">ojComponent</code> binding:</caption>
 * <div id="paging" data-bind="ojComponent: {component: 'ojPagingControl', data: pagingModel, pageSize: 10}">
 *     
 * 
 * @ojcomponent oj.ojPagingControl
 * @augments oj.baseComponent
 */
(function() {
  oj.__registerWidget("oj.ojPagingControl", $['oj']['baseComponent'],
    {
      version: '1.0.0',
      defaultElement: '<div>',
      widgetEventPrefix: 'oj',
      options:
        {
          /** 
           * The data to bind to the component.
           * <p>
           * Must implement the oj.PagingModel interface {@link oj.PagingModel} 
           * @expose 
           * @public 
           * @instance
           * @memberof! oj.ojPagingControl
           * @type {oj.PagingModel}
           * @default <code class="prettyprint">null</code>
           */
          data: null,
          /** 
           * Options for when the component width is too narrow to accommodate the controls in the paging control
           * <p>
           * Valid values are:
           * <ul>
           *   <li>fit: Display as many controls as can fit in the component width</li>
           *   <li>none: Display all controls. Controls which cannot fit will be truncated.</li>
           * </ul>
           * <p>
           * @expose 
           * @public 
           * @instance
           * @memberof! oj.ojPagingControl
           * @type {string}
           * @default <code class="prettyprint">fit</code>
           */
          overflow: 'fit',
          /** 
           * Page size.
           * <p>
           * @expose 
           * @public 
           * @instance
           * @memberof! oj.ojPagingControl
           * @type {number}
           * @default <code class="prettyprint">25</code>
           */
          pageSize: 25,
          /** 
           * Options for page mode. 
           * <p>
           * Supported options are:
           * <ul>
           *   <li>layout: Array of paging navigation controls to be displayed (only applicable for numbers type).
           *   <ul>Valid array values are:
           *     <li>auto: Component decides which controls to display</li>
           *     <li>all: Display all controls</li>
           *     <li>input: Display the page input control</li>
           *     <li>rangeText: Display the page range text control</li>
           *     <li>pages: Display the page links</li>
           *     <li>nav: Display the navigation arrows</li>
           *   </ul>
           *   </li>
           *   <li>type: The type of page links. Specifying 'numbers' will render
           *   numeric page links whereas specifying 'dots' will render dots.</li>
           *   <li>orientation: The orientation of the page links. Can be either horizontal
           *   or vertical.</li>
           *   <li>maxPageLinks: The maximum number of page links to display (only applicable for numbers type). 
           *   An ellipsis '...' will be displayed for pages which exceed the maximum.
           *   maxPageLinks must be greater than 4.</li>
           * </ul>
           * @expose 
           * @public 
           * @instance
           * @memberof! oj.ojPagingControl
           * @type {Object.<string, Array|number>}
           * @property {Array} layout Array of paging navigation controls to be displayed (only applicable for numbers type)
           * @property {string} orientation Horizontal or vertical
           * @property {string} type Numbers or dots
           * @property {number} maxPageLinks The maximum number of page links to display (only applicable for numbers type)
           * @default <code class="prettyprint">{layout: ['auto'], 'type': 'numbers', 'orientation': 'horizontal', maxPageLinks: 6}</code>
           * @example <caption>Initialize the paging control with the <code class="prettyprint">pageOptions</code> option specified:</caption>
           * <div id="paging" data-bind="ojComponent: {component: 'ojPagingControl', data: pagingModel, pageSize: 10, pageOptions: {layout: ['auto', 'input', 'rangeText'], maxPageLinks: 8}}">
           */
          pageOptions: {'layout': ['auto'], 'type': 'numbers', 'maxPageLinks': 6, 'orientation': 'horizontal'},
          /** 
           * Options for loadMore mode. 
           * <p>
           * Supported options are:
           * <ul>
           *   <li>maxCount: Integer</li>
           * </ul> 
           * @expose 
           * @public 
           * @instance
           * @memberof! oj.ojPagingControl
           * @type {Object.<string, number>}
           * @property {number} maxCount The maximum number items to display
           * @default <code class="prettyprint">{maxCount: 500}</code>
           */
          loadMoreOptions: {'maxCount': 500},
          /** 
           * Paging mode. 
           * <p>
           * Valid values are:
           * <ul>
           *   <li>page: Display paging control in pagination mode</li>
           *   <li>loadMore: Display paging control in high watermark mode</li>
           * </ul>
           * @expose 
           * @public 
           * @instance
           * @memberof! oj.ojPagingControl
           * @type {string}
           * @default <code class="prettyprint">page</code>
           */
          mode: 'page',
          /**
            * Triggered when the paging control has finished rendering
            *
            * @expose 
            * @event 
            * @memberof! oj.ojPagingControl
            * @instance
            * 
            * @example <caption>Initialize the paging control with the <code class="prettyprint">ready</code> callback specified:</caption>
            * $( ".selector" ).ojPagingControl({
            *     "ready": function() {}
            * });
            *
            * @example <caption>Bind an event listener to the <code class="prettyprint">ojready</code> event:</caption>
            * $( ".selector" ).on( "ojready", function() {} );
            */
          ready: null,
          /** 
           * Translations for the component
           * @expose 
           * @public 
           * @instance
           * @memberof! oj.ojPagingControl
           * @type {Object.<string, string>}
           * @property {string} labelAccPaging Paging control label for screen readers
           * @property {string} labelAccNavFirstPage First page label for screen readers
           * @property {string} labelAccNavLastPage Last page label for screen readers
           * @property {string} labelAccNavNextPage Next page label for screen readers
           * @property {string} labelAccNavPreviousPage Previous page label for screen readers
           * @property {string} labelAccNavPage Current page label for screen readers
           * @property {string} labelLoadMore Load more link text
           * @property {string} labelNavInputPage Current page label
           * @property {string} labelNavInputPageMax Maximum page label
           * @property {string} labelNavInputPageSummary Page item summary
           * @property {string} msgItemRange Item range text for known row count
           * @property {string} msgItemRangeUnknown Item range text for unknown row count
           * @property {string} tipNavInputPage Current page tip
           * @property {string} tipNavPageLink Current page link tip 
           * @property {string} tipNavNextPage Next page button tip
           * @property {string} tipNavPreviousPage Previous page button tip
           * @property {string} tipNavFirstPage First page button tip
           * @property {string} tipNavLastPage Last page button tip
           */
          translations: {}
        },
      /**
       * @private
       * @const
       */
      _BUNDLE_KEY:
        {
          _LABEL_ACC_PAGING:                              'labelAccPaging',
          _LABEL_ACC_NAV_FIRST_PAGE:                      'labelAccNavFirstPage',
          _LABEL_ACC_NAV_LAST_PAGE:                       'labelAccNavLastPage',
          _LABEL_ACC_NAV_NEXT_PAGE:                       'labelAccNavNextPage',
          _LABEL_ACC_NAV_PREVIOUS_PAGE:                   'labelAccNavPreviousPage',
          _LABEL_ACC_NAV_PAGE:                            'labelAccNavPage',
          _LABEL_LOAD_MORE:                               'labelLoadMore',
          _LABEL_LOAD_MORE_MAX_ROWS:                      'labelLoadMoreMaxRows',
          _LABEL_NAV_INPUT_PAGE:                          'labelNavInputPage',
          _LABEL_NAV_INPUT_PAGE_MAX:                      'labelNavInputPageMax',
          _LABEL_NAV_INPUT_PAGE_SUMMARY:                  'labelNavInputPageSummary',
          _MSG_ITEM_RANGE_CURRENT:                        'msgItemRangeCurrent',
          _MSG_ITEM_RANGE_ITEMS:                          'msgItemRangeItems',
          _MSG_ITEM_RANGE_OF:                             'msgItemRangeOf',
          _TIP_NAV_INPUT_PAGE:                            'tipNavInputPage',
          _TIP_NAV_PAGE_LINK:                             'tipNavPageLink',
          _TIP_NAV_NEXT_PAGE:                             'tipNavNextPage',
          _TIP_NAV_PREVIOUS_PAGE:                         'tipNavPreviousPage',
          _TIP_NAV_FIRST_PAGE:                            'tipNavFirstPage',
          _TIP_NAV_LAST_PAGE:                             'tipNavLastPage',
          _ERR_PAGE_INVALID_SUMMARY:                      'pageInvalid.summary',
          _ERR_PAGE_INVALID_DETAIL:                       'pageInvalid.detail',
          _ERR_DATA_INVALID_TYPE_SUMMARY:                 'dataInvalidType.summary',
          _ERR_DATA_INVALID_TYPE_DETAIL:                  'dataInvalidType.detail',
          _ERR_MAXPAGELINKS_INVALID_SUMMARY:              'maxPageLinksInvalid.summary',
          _ERR_MAXPAGELINKS_INVALID_DETAIL:               'maxPageLinksInvalid.detail'
        },
      /**
       * @private
       * @const
       */
      _MARKER_STYLE_CLASSES:
        {
          _WIDGET:                                        'oj-component',
          _ACTIVE:                                        'oj-active',
          _CLICKABLE_ICON:                                'oj-clickable-icon-nocontext',
          _DISABLED:                                      'oj-disabled',
          _ENABLED:                                       'oj-enabled',
          _FOCUS:                                         'oj-focus',
          _HOVER:                                         'oj-hover',
          _SELECTED:                                      'oj-selected'
        },
      /**
       * @private
       * @const
       */
      _CSS_CLASSES:
        {
          _PAGING_CONTROL_CLASS:                          'oj-pagingcontrol',
          _PAGING_CONTROL_ACC_LABEL_CLASS:                'oj-pagingcontrol-acc-label',
          _PAGING_CONTROL_CONTENT_CLASS:                  'oj-pagingcontrol-content',
          _PAGING_CONTROL_LOAD_MORE_CLASS:                'oj-pagingcontrol-loadmore',
          _PAGING_CONTROL_LOAD_MORE_LINK_CLASS:           'oj-pagingcontrol-loadmore-link',
          _PAGING_CONTROL_LOAD_MORE_MAX_ROWS_CLASS:       'oj-pagingcontrol-loadmore-max-rows',
          _PAGING_CONTROL_LOAD_MORE_RANGE_CLASS:          'oj-pagingcontrol-loadmore-range',
          _PAGING_CONTROL_LOAD_MORE_RANGE_CURRENT_CLASS:  'oj-pagingcontrol-loadmore-range-current',
          _PAGING_CONTROL_LOAD_MORE_RANGE_MAX_CLASS:      'oj-pagingcontrol-loadmore-range-max',
          _PAGING_CONTROL_NAV_CLASS:                      'oj-pagingcontrol-nav',
          _PAGING_CONTROL_NAV_ARROW_CLASS:                'oj-pagingcontrol-nav-arrow',
          _PAGING_CONTROL_NAV_ARROW_SECTION_CLASS:        'oj-pagingcontrol-nav-arrow-section',
          _PAGING_CONTROL_NAV_PAGE_CLASS:                 'oj-pagingcontrol-nav-page',
          _PAGING_CONTROL_NAV_PAGE_ELLIPSIS_CLASS:        'oj-pagingcontrol-nav-page-ellipsis',
          _PAGING_CONTROL_NAV_DOT_CLASS:                  'oj-pagingcontrol-nav-dot',
          _PAGING_CONTROL_NAV_DOT_BULLET_CLASS:           'oj-pagingcontrol-nav-dot-bullet',
          _PAGING_CONTROL_NAV_PAGE_ACC_LABEL_CLASS:       'oj-pagingcontrol-nav-page-acc-label',
          _PAGING_CONTROL_NAV_LABEL_CLASS:                'oj-pagingcontrol-nav-label',
          _PAGING_CONTROL_NAV_INPUT_SECTION_CLASS:        'oj-pagingcontrol-nav-input-section',
          _PAGING_CONTROL_NAV_INPUT_CLASS:                'oj-pagingcontrol-nav-input',
          _PAGING_CONTROL_NAV_INPUT_MAX_CLASS:            'oj-pagingcontrol-nav-input-max',
          _PAGING_CONTROL_NAV_INPUT_SUMMARY_CLASS:        'oj-pagingcontrol-nav-input-summary',
          _PAGING_CONTROL_NAV_INPUT_SUMMARY_CURRENT_CLASS:'oj-pagingcontrol-nav-input-summary-current',
          _PAGING_CONTROL_NAV_INPUT_SUMMARY_MAX_CLASS:    'oj-pagingcontrol-nav-input-summary-max',
          _PAGING_CONTROL_NAV_PAGES_SECTION_CLASS:        'oj-pagingcontrol-nav-pages-section',
          _PAGING_CONTROL_NAV_PAGES_LINKS_CLASS:          'oj-pagingcontrol-nav-pages-links',
          _PAGING_CONTROL_NAV_FIRST_CLASS:                'oj-pagingcontrol-nav-first',
          _PAGING_CONTROL_NAV_FIRST_ACC_LABEL_CLASS:      'oj-pagingcontrol-nav-first-acc-label',
          _PAGING_CONTROL_NAV_PREVIOUS_CLASS:             'oj-pagingcontrol-nav-previous',
          _PAGING_CONTROL_NAV_PREVIOUS_ACC_LABEL_CLASS:   'oj-pagingcontrol-nav-previous-acc-label',
          _PAGING_CONTROL_NAV_NEXT_CLASS:                 'oj-pagingcontrol-nav-next',
          _PAGING_CONTROL_NAV_NEXT_ACC_LABEL_CLASS:       'oj-pagingcontrol-nav-next-acc-label',
          _PAGING_CONTROL_NAV_LAST_CLASS:                 'oj-pagingcontrol-nav-last',
          _PAGING_CONTROL_NAV_LAST_ACC_LABEL_CLASS:       'oj-pagingcontrol-nav-last-acc-label',
          _PAGING_CONTROL_NAV_FIRST_ICON_CLASS:           'oj-pagingcontrol-nav-first-icon',
          _PAGING_CONTROL_NAV_PREVIOUS_ICON_CLASS:        'oj-pagingcontrol-nav-previous-icon',
          _PAGING_CONTROL_NAV_NEXT_ICON_CLASS:            'oj-pagingcontrol-nav-next-icon',
          _PAGING_CONTROL_NAV_LAST_ICON_CLASS:            'oj-pagingcontrol-nav-last-icon',
          _PAGING_CONTROL_NAV_FIRST_VERTICAL_ICON_CLASS:  'oj-pagingcontrol-nav-first-vertical-icon',
          _PAGING_CONTROL_NAV_PREVIOUS_VERTICAL_ICON_CLASS:'oj-pagingcontrol-nav-previous-vertical-icon',
          _PAGING_CONTROL_NAV_NEXT_VERTICAL_ICON_CLASS:   'oj-pagingcontrol-nav-next-vertical-icon',
          _PAGING_CONTROL_NAV_LAST_VERTICAL_ICON_CLASS:   'oj-pagingcontrol-nav-last-vertical-icon',
          _WIDGET_ICON_CLASS:                             'oj-component-icon',
          _HIDDEN_CONTENT_ACC_CLASS:                      'oj-helper-hidden-accessible'
        },
      /**
       * @private
       * @const
       * @type {string}
       */
      _DATA_ATTR_PAGE_NUM: 'data-oj-pagenum',
      /**
       * @private
       * @const
       * @type {string}
       */
      _OPTION_ENABLED: 'enabled',
      /**
       * @private
       * @const
       * @type {string}
       */
      _OPTION_DISABLED: 'disabled',
      /**
       * @private
       * @const
       * @type {string}
       */
      _TAB_INDEX: 'tabindex',
      /**
       * @private
       * @const
       */
      _MODE:
        {
          _LOAD_MORE:   'loadMore',
          _PAGE:        'page'
        },
      /**
       * @private
       * @const
       */
      _PAGE_OPTION_LAYOUT:
        {
          _AUTO:  'auto',
          _ALL: 'all',
          _INPUT: 'input',
          _RANGE_TEXT: 'rangeText',
          _PAGES: 'pages',
          _NAV:   'nav'
        },
      /**
       * @private
       * @const
       */
      _PAGE_OPTION_DEFAULT_MAX_PAGE_LINKS: 6,
      /**
       * @private
       * @const
       */
      _TYPE:
        {
          _NUMBERS:   'numbers',
          _DOTS:      'dots'
        },
      /**** start Public APIs ****/

      /**
       * Load the first page of data
       * @expose
       * @memberof! oj.ojPagingControl
       * @instance
       * @return {Promise} promise object triggering done when complete.
       * @throws {Error}
       * @export
       * @example <caption>Invoke the <code class="prettyprint">firstPage</code> method:</caption>
       * $( ".selector" ).ojPagingControl( "firstPage" );
       */
      'firstPage': function()
      {
        var data = this._getData();
        if (data != null)
        {
          return this._invokeDataPage(0, false);
        }
        return this._getRejectPromise();
      },
      /**
       * Load the previous page of data
       * @expose
       * @memberof! oj.ojPagingControl
       * @instance
       * @return {Promise} promise object triggering done when complete.
       * @throws {Error}
       * @export
       * @example <caption>Invoke the <code class="prettyprint">previousPage</code> method:</caption>
       * $( ".selector" ).ojPagingControl( "previousPage" );
       */
      'previousPage': function()
      {
        var data = this._getData();
        if (data != null)
        {
          var page = this._getCurrentPage();
          // can only go to previous page if on 2nd page or greater
          if (page > 0)
          {
            return this._invokeDataPage(page - 1, false);
          }
        }
        return this._getRejectPromise();
      },
      /**
       * Load the next page of data
       * @expose
       * @memberof! oj.ojPagingControl
       * @instance
       * @return {Promise} promise object triggering done when complete.
       * @throws {Error}
       * @export
       * @example <caption>Invoke the <code class="prettyprint">nextPage</code> method:</caption>
       * $( ".selector" ).ojPagingControl( "nextPage" );
       */
      'nextPage': function()
      {
        var data = this._getData();
        if (data != null)
        {
          var page = this._getCurrentPage();
          if (page + 1 <= this._getTotalPages() - 1|| this._getTotalPages() < 0) 
          {
            return this._invokeDataPage(page + 1, false);
          }
        }
        return this._getRejectPromise();
      },
      /**
       * Load the last page of data
       * @expose
       * @memberof! oj.ojPagingControl
       * @instance
       * @return {Promise} promise object triggering done when complete.
       * @throws {Error}
       * @export
       * @example <caption>Invoke the <code class="prettyprint">lastPage</code> method:</caption>
       * $( ".selector" ).ojPagingControl( "lastPage" );
       */
      'lastPage': function()
      {
        var data = this._getData();
        if (data != null)
        {
          if (this._getTotalPages() > 0)
          {
            return this._invokeDataPage(this._getTotalPages() - 1, false);
          }
        }
        return this._getRejectPromise();
      },
      /**
       * Load the specified page of data
       * @expose
       * @memberof! oj.ojPagingControl
       * @instance
       * @param {number} page  Page number. 
       * @return {Promise} promise object triggering done when complete.
       * @throws {Error}
       * @export
       * @example <caption>Invoke the <code class="prettyprint">page</code> method:</caption>
       * $( ".selector" ).ojPagingControl( "page", 5 );
       */
      'page': function(page)
      {
        var data = this._getData();
        if (data != null)
        {
          if (page <= this._getTotalPages() || this._getTotalPages() < 0) 
          {
            return this._invokeDataPage(page, false);
          }
        }
        return this._getRejectPromise();
      },
      /**
       * Load the next set of data
       * @expose
       * @memberof! oj.ojPagingControl
       * @instance
       * @return {Promise} promise object triggering done when complete.
       * @throws {Error}
       * @export
       * @example <caption>Invoke the <code class="prettyprint">loadNext</code> method:</caption>
       * $( ".selector" ).ojPagingControl( "loadNext" );
       */
      'loadNext': function()
      {
        var data = this._getData();
        if (data != null)
        {
          return this._invokeDataFetchNext();
        }
        return this._getRejectPromise();
      },
      /**
       * Refresh the paging control.
       * @expose
       * @memberof! oj.ojPagingControl
       * @instance
       * @export
       * @example <caption>Invoke the <code class="prettyprint">refresh</code> method:</caption>
       * $( ".selector" ).ojPagingControl( "refresh" );
       */
      'refresh': function()
      {
        this._super();
        this._refresh();
      },
      /**
       * Return the subcomponent node represented by the documented locator attribute values.
       * Test authors should target sub elements using the following <b>subIds</b>:
       * <ul>
       * <li><b>oj-pagingcontrol-nav-input</b>: page number navigation input text </li>
       * <li><b>oj-pagingcontrol-nav-input-max</b>: current maximum page text </li>
       * <li><b>oj-pagingcontrol-nav-input-summary</b>: summary items text </li>
       * <li><b>oj-pagingcontrol-nav-input-summary-current</b>: summary current items text </li>
       * <li><b>oj-pagingcontrol-nav-input-summary-max</b>: summary max items text </li>
       * <li><b>oj-pagingcontrol-nav-first</b>: first button </li>
       * <li><b>oj-pagingcontrol-nav-next</b>: next button </li>
       * <li><b>oj-pagingcontrol-nav-previous</b>: previous button </li>
       * <li><b>oj-pagingcontrol-nav-last</b>: last button </li>
       * <li><b>oj-pagingcontrol-nav-page</b>: page buttons
       * <ul><li> <b>index</b>: zero-based index of page number node</li></ul></li>
       * <li><b>oj-pagingcontrol-load-more-link</b>: load more link </li>
       * <li><b>oj-pagingcontrol-load-more-range</b>: load more range </li>
       * <li><b>oj-pagingcontrol-load-more-range-current</b>: load more range current items text </li>
       * <li><b>oj-pagingcontrol-load-more-range-max</b>: load more range max items text </li>
       * <li><b>oj-pagingcontrol-load-more-max-rows</b>: load more max message </li>
       * </ul>
       *          
       * @override
       * @param {Object} locator An Object containing at minimum a subId property whose value is a string 
       * @param {string=} locator.subId - One of the subIds list above.
       * @param {number=} locator.index - A zero-based index as documented above.
       * @return {Element|null} the subcomponent located by the subId string passed in locator, if found.
       * @export
       * @expose
       * @memberof! oj.ojPagingControl
       * @instance
       * 
       */
      'getNodeBySubId': function(locator)
      {
        if (locator == null)
        {
          return this.element ? this.element[0] : null;
        }

        var subId = locator['subId'];
        var retval = null;

        if (subId === 'oj-pagingcontrol-nav-input')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-nav-input-max')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_MAX_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-nav-input-summary')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_SUMMARY_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-nav-input-summary-current')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_SUMMARY_CURRENT_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-nav-input-summary-max')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_SUMMARY_MAX_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-nav-first')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_FIRST_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-nav-next')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_NEXT_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-nav-previous')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_PREVIOUS_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-nav-last')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_LAST_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-nav-page')
        {
          var index = locator['index'];
          retval = this._getPagingControlContainer().find("[" + this._DATA_ATTR_PAGE_NUM + "=" + index +"]")[0];
        }
        else if (subId === 'oj-pagingcontrol-load-more-link')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_LINK_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-load-more-range')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_RANGE_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-load-more-range-current')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_RANGE_CURRENT_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-load-more-range-max')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_RANGE_MAX_CLASS)[0];
        }
        else if (subId === 'oj-pagingcontrol-load-more-max-rows')
        {
          retval = this._getPagingControlContainer().find('.' + this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_MAX_ROWS_CLASS)[0];
        }

        // Non-null locators have to be handled by the component subclasses
        if (retval === undefined)
          retval = null;

        return retval;
      },              
      
      /**** end Public APIs ****/

      /**** start internal widget functions ****/
      
      /**
       * @override
       * @protected
       * @instance
       * @memberof! oj.ojPagingControl
       */
      _ComponentCreate : function ()
      {
        this._super();
        this._registerDataSourceEventListeners();
        this._draw();
        this._registerResizeListener(this._getPagingControlContainer());
        this._on(this._events);
      },
      /**
       * Initialize the paging control after creation
       * @protected
       * @override
       * @memberof! oj.ojPagingControl
       */        
      _AfterCreate: function () 
      {  
        this._super();
        this._registerSwipeHandler();
        this._invokeDataPage(0, true);
      },
      /**
       * @override
       * @private
       */
      _destroy: function()
      {
        this._unregisterDataSourceEventListeners();
        this._unregisterSwipeHandler();
      },
      /**
       * @override
       * @private
       */
      _draw: function()
      {
        var options = this.options;
        // add main css class to element
        this.element.addClass(this._CSS_CLASSES._PAGING_CONTROL_CLASS);
        this.element.addClass(this._MARKER_STYLE_CLASSES._WIDGET);

        this._createPagingControlAccLabel();
        this._createPagingControlContent();
        this._mode = options['mode'];
        
        if (options['mode'] == this._MODE._LOAD_MORE)
        {
          this._createPagingControlLoadMore();
          this._createPagingControlLoadMoreLink();
          this._createPagingControlLoadMoreRange();
        }
        else
        {
          this._createPagingControlNav();
        }
      },
      /**
       * @override
       * @private
       */
      _events:
        {
          /**
           * invoke loading next page of data
           */
          'click .oj-pagingcontrol-loadmore-link': function(event)
          {
            this['loadNext']();
            event.preventDefault();
          },
          /**
           * invoke loading page of data
           */
          'click .oj-pagingcontrol-nav-dot': function(event)
          {
            var pageNum = $(event.currentTarget).attr('data-oj-pagenum');
            this['page'](pageNum);  
            event.preventDefault();
          },
          /**
           * invoke loading page of data
           */
          'click .oj-pagingcontrol-nav-page': function(event)
          {
            var pageNum = $(event.currentTarget).attr('data-oj-pagenum');
            this['page'](pageNum);  
            event.preventDefault();
          },
          /**
           * invoke loading first page of data
           */
          'click .oj-pagingcontrol-nav-first': function(event)
          {
            if (!$(event.currentTarget).hasClass(this._MARKER_STYLE_CLASSES._DISABLED))
            {
              this['firstPage']();
            }
            event.preventDefault();
          },
          /**
           * invoke loading previous page of data
           */
          'click .oj-pagingcontrol-nav-previous': function(event)
          {
            if (!$(event.currentTarget).hasClass(this._MARKER_STYLE_CLASSES._DISABLED))
            {
              this['previousPage']();
            }
            event.preventDefault();
          },
          /**
           * invoke loading next page of data
           */
          'click .oj-pagingcontrol-nav-next': function(event)
          {
            if (!$(event.currentTarget).hasClass(this._MARKER_STYLE_CLASSES._DISABLED))
            {
              this['nextPage']();
            }
            event.preventDefault();
          },
          /**
           * invoke loading last page of data
           */
          'click .oj-pagingcontrol-nav-last': function(event)
          {
            if (!$(event.currentTarget).hasClass(this._MARKER_STYLE_CLASSES._DISABLED))
            {
              this['lastPage']();
            }
            event.preventDefault();
          },
          /**
           * prevent submission of form on enter
           */
          'keypress .oj-pagingcontrol-nav-input': function(event)
          {
            var keyCode = event.which;
            
            if (keyCode == 13)
            {
              event.preventDefault();
            }
          },
          /**
           * Add oj-active
           */
          'mousedown .oj-pagingcontrol-nav-first': function(event)
          {
            if (!$(event.currentTarget).hasClass(this._MARKER_STYLE_CLASSES._DISABLED))
            {
              $(event.target).addClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            }
            event.preventDefault();
          },
          /**
           * Add oj-active
           */
          'mousedown .oj-pagingcontrol-nav-previous': function(event)
          {
            if (!$(event.currentTarget).hasClass(this._MARKER_STYLE_CLASSES._DISABLED))
            {
              $(event.target).addClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            }
            event.preventDefault();
          },
          /**
           * Add oj-active
           */
          'mousedown .oj-pagingcontrol-nav-next': function(event)
          {
            if (!$(event.currentTarget).hasClass(this._MARKER_STYLE_CLASSES._DISABLED))
            {
              $(event.target).addClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            }
            event.preventDefault();
          },
          /**
           * Add oj-active
           */
          'mousedown .oj-pagingcontrol-nav-last': function(event)
          {
            if (!$(event.currentTarget).hasClass(this._MARKER_STYLE_CLASSES._DISABLED))
            {
              $(event.target).addClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            }
            event.preventDefault();
          },
          /**
           * Remove oj-active
           */
          'mouseup .oj-pagingcontrol-nav-first': function(event)
          {
            $(event.target).removeClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            event.preventDefault();
          },
          /**
           * Remove oj-active
           */
          'mouseup .oj-pagingcontrol-nav-previous': function(event)
          {
            $(event.target).removeClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            event.preventDefault();
          },
          /**
           * Remove oj-active
           */
          'mouseup .oj-pagingcontrol-nav-next': function(event)
          {
            $(event.target).removeClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            event.preventDefault();
          },
          /**
           * Remove oj-active
           */
          'mouseup .oj-pagingcontrol-nav-last': function(event)
          {
            $(event.target).removeClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            event.preventDefault();
          },
          /**
           * Remove oj-active
           */
          'mouseleave .oj-pagingcontrol-nav-first': function(event)
          {
            $(event.target).removeClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            event.preventDefault();
          },
          /**
           * Remove oj-active
           */
          'mouseleave .oj-pagingcontrol-nav-previous': function(event)
          {
            $(event.target).removeClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            event.preventDefault();
          },
          /**
           * Remove oj-active
           */
          'mouseleave .oj-pagingcontrol-nav-next': function(event)
          {
            $(event.target).removeClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            event.preventDefault();
          },
          /**
           * Remove oj-active
           */
          'mouseleave .oj-pagingcontrol-nav-last': function(event)
          {
            $(event.target).removeClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            event.preventDefault();
          }
        },
      /**
       * @private
       */
      _refresh: function()
      {
        if (this._data != this.options['data'])
        {
          this._clearCachedDataMetadata();
          this._invokeDataPage(0, true);
        }
        
        var size = 0;
        var startIndex = 0;
        
        if (this._data != null)
        {
          startIndex = this._data.getStartItemIndex();
        }

        // if totalSize == 0 then size is just 0. If totalSize < 0 then
        // we have unknown row count, if totalSize > 0 then we have a known row count.
        // For non-zero unknown row count and non-zero known row count
        // we need to calculate the rows on the page.
        if (this._data != null && this._data.totalSize() != 0)
        {
          // startIndex = 0 for loadMore mode
          size = this._data.getEndItemIndex() - startIndex + 1;
        }

        if (this._mode != this.options['mode'])
        {
          this._mode = this.options['mode'];
          
          if (this.options['mode'] == this._MODE._LOAD_MORE)
          {
            this._refreshPagingControlLoadMore(size, startIndex);
          }
          else
          {
            this._refreshPagingControlNav(size, startIndex);
          }
        }
        else if (this.options['mode'] == this._MODE._LOAD_MORE)
        {
          // hide loadMore if there are no more rows to fetch
          var data = this._getData();
          var pagingControlLoadMore = this._getPagingControlLoadMore();
          var rowCount = startIndex + size;
          if (data != null && rowCount == data.totalSize())
          {
            pagingControlLoadMore.css('display', 'none');
          }
          else
          {
            this._refreshPagingControlLoadMore(size, startIndex);
          }
        }
        else
        {
          this._refreshPagingControlNav(size, startIndex);
        }
      },
      /**
       * @override
       * @private
       */
      _setOption: function(key, value)
      {
        this._superApply(arguments);
        this._invokeDataPage(0, true);
        this._refresh();
      },
      /**** end internal widget functions ****/

      /**** start internal functions ****/
      /**
       * Clear any cached data metadata
       * @private
       */
      _clearCachedDataMetadata: function()
      {
        if (this._data != null)
        {
          this._unregisterDataSourceEventListeners();
        }
        this._data = null;
      },
      /**
       * Clear cached range text DOM
       * @private
       */
      _clearCachedDomLoadMore: function()
      {
        this._cachedDomPagingControlLoadMore = null;
        this._cachedDomPagingControlLoadMoreLink = null;
        this._cachedDomPagingControlLoadMoreRange = null;
      },
      /**
       * Clear any cached DOM nav elements
       * @private
       */
      _clearCachedDomPagingControlNav: function()
      {
        this._cachedDomPagingControlNav = null;
        this._cachedDomPagingControlNavInput = null;
        this._cachedDomPagingControlNavInputSummary = null;
      },
      /**
       * Return the current page
       * @return {number} Current page.
       * @throws {Error}
       * @private
       */
      _getCurrentPage: function()
      {
        var data = this._getData();
        var page = 0;
        if (data != null)
        {
          page = data.getPage();
        }
        return page;
      },
      /**
       * Return the datasource object defined for this paging control
       * @return {Object} Datasource object.
       * @throws {Error}
       * @private
       */
      _getData: function()
      {
        if (!this._data && this.options['data'] != null)
        {
          this._data = this.options['data'];
          this._dataMetadata = this.options['data'];
          // In case we get a delayed setting of the data property--check to rebind the listeners
          this._registerDataSourceEventListeners();
        }
        return this._data;
      },
      /**
       * Return the item range text span
       * @param {number} size Number of rows
       * @param {number} startIndex Start index
       * @return {Object} Item range text span element.
       * @throws {Error}
       * @private
       */
      _getItemRange: function(size, startIndex)
      {
        var pageFrom = startIndex;
        var itemRangeSpan = $(document.createElement('span'));
        var itemRangeCurrentSpan = $(document.createElement('span'));
        if (this.options['mode'] == this._MODE._LOAD_MORE)
        {
          itemRangeCurrentSpan.addClass(this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_RANGE_CURRENT_CLASS); 
        }
        else
        {
          itemRangeCurrentSpan.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_SUMMARY_CURRENT_CLASS); 
        }
        var itemRangeCurrentText = this.getTranslatedString(this._BUNDLE_KEY._MSG_ITEM_RANGE_CURRENT, {'pageFrom': pageFrom, 'pageTo': 0});
        itemRangeSpan.append(itemRangeCurrentSpan); //@HTMLUpdateOK
        
        if (size != null)
        {
          var pageTo = parseInt(startIndex, 10) + parseInt(size, 10);
          pageFrom = pageTo > 0 ? pageFrom + 1 : 0;
          var data = this._getData();
          if (data != null && data.totalSize() != -1)
          {
            pageTo = pageTo > data.totalSize() ? data.totalSize() : pageTo;
            itemRangeCurrentText = this.getTranslatedString(this._BUNDLE_KEY._MSG_ITEM_RANGE_CURRENT, {'pageFrom': pageFrom, 'pageTo': pageTo});
            var itemRangeOfText = this.getTranslatedString(this._BUNDLE_KEY._MSG_ITEM_RANGE_OF);
            var itemRangeOfSpan = $(document.createElement('span'));
            itemRangeOfSpan.text(" " + itemRangeOfText + " ");
            itemRangeSpan.append(itemRangeOfSpan); //@HTMLUpdateOK
            var itemRangeMaxSpan = $(document.createElement('span'));
            if (this.options['mode'] == this._MODE._LOAD_MORE)
            {
              itemRangeMaxSpan.addClass(this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_RANGE_MAX_CLASS); 
            }
            else
            {
              itemRangeMaxSpan.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_SUMMARY_MAX_CLASS); 
            }
            itemRangeMaxSpan.text(data.totalSize());
            itemRangeSpan.append(itemRangeMaxSpan); //@HTMLUpdateOK
            var itemRangeItemsText = this.getTranslatedString(this._BUNDLE_KEY._MSG_ITEM_RANGE_ITEMS);
            var itemRangeItemsSpan = $(document.createElement('span'));
            itemRangeItemsSpan.text(" " + itemRangeItemsText);
            itemRangeSpan.append(itemRangeItemsSpan); //@HTMLUpdateOK
          }
          else
          {
            itemRangeCurrentText = this.getTranslatedString(this._BUNDLE_KEY._MSG_ITEM_RANGE_CURRENT, {'pageFrom': pageFrom, 'pageTo': pageTo});
            var itemRangeItemsText = this.getTranslatedString(this._BUNDLE_KEY._MSG_ITEM_RANGE_ITEMS);
            var itemRangeItemsSpan = $(document.createElement('span'));
            itemRangeItemsSpan.text(" " + itemRangeItemsText);
            itemRangeSpan.append(itemRangeItemsSpan); //@HTMLUpdateOK
          }
        }
        itemRangeCurrentSpan.text(itemRangeCurrentText);
        return itemRangeSpan;
      },
      /**
       * Return maximum number of page links
       * @return {number} Max page links.
       * @private
       */
      _getMaxPageLinks: function()
      {
        var maxPageLinks = this.options['pageOptions']['maxPageLinks'];
        
        if (this.options['pageOptions']['type'] == this._TYPE._DOTS)
        {
          maxPageLinks = Number.MAX_VALUE;
        }
        else if (!maxPageLinks)
        {
          maxPageLinks = this._PAGE_OPTION_DEFAULT_MAX_PAGE_LINKS;
        }
        
        return maxPageLinks;
      },
      /**
       * Return reject Promise
       * @return {Promise} promise object reject.
       * @private
       */
      _getRejectPromise: function()
      {
        return Promise.reject();
      },
      /**
       * Return the total number of pages
       * @return {number} Total pages.
       * @throws {Error}
       * @private
       */
      _getTotalPages: function()
      {
        var data = this._getData();
        var pageCount = 0;
        if (data != null)
        {
          pageCount = data.getPageCount();
        }
        return pageCount;
      },
      /**
       * Callback handler for fetch completed in the datasource.
       * status message.
       * @param {Object} event 
       * @private
       */
      _handleDataFetchEnd: function(event)
      {
        // restore focus
        var activeElement = $(document.activeElement);

        if (activeElement.hasClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_PAGE_CLASS))
        {
          var pageNum = activeElement.attr('data-oj-pagenum');
          var self = this;
          setTimeout(function() 
          {
            if (pageNum >= 0)
            {
              var navPage = self._getPagingControlContent().find('div[data-oj-pagenum=' + pageNum + ']');
              navPage.focus();
            }
          }, 100);
        }
        else if (activeElement.hasClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_FIRST_CLASS))
        {
          var self = this;
          setTimeout(function() 
          {
            var firstArrow = self._getPagingControlContent().find('.' + self._CSS_CLASSES._PAGING_CONTROL_NAV_FIRST_CLASS);
            firstArrow.focus();
          }, 100);
        }
        else if (activeElement.hasClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_PREVIOUS_CLASS))
        {
          var self = this;
          setTimeout(function() 
          {
            var previousArrow = self._getPagingControlContent().find('.' + self._CSS_CLASSES._PAGING_CONTROL_NAV_PREVIOUS_CLASS);
            previousArrow.focus();
          }, 100);
        }
        else if (activeElement.hasClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_NEXT_CLASS))
        {
          var self = this;
          setTimeout(function() 
          {
            var nextArrow = self._getPagingControlContent().find('.' + self._CSS_CLASSES._PAGING_CONTROL_NAV_NEXT_CLASS);
            nextArrow.focus();
          }, 100);
        }
        else if (activeElement.hasClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_LAST_CLASS))
        {
          var self = this;
          setTimeout(function() 
          {
            var lastArrow = self._getPagingControlContent().find('.' + self._CSS_CLASSES._PAGING_CONTROL_NAV_LAST_CLASS);
            lastArrow.focus();
          }, 100);
        }
        else if (activeElement.hasClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_CLASS))
        {
          var self = this;
          setTimeout(function() 
          {
            var navInput = self._getPagingControlContent().find('.' + self._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_CLASS);
            navInput.focus();
          }, 100);
        }
        this._queueRefresh();
      },
      /**
       * Callback handler for page event in the datasource.
       * status message.
       * @param {Object} event 
       * @private
       */
      _handleDataPage: function(event)
      {
        var page = event['page'];
        var previousPage = event['previousPage'];
        
        if (page != previousPage)
        {
          // only refresh if page has changed
          this._queueRefresh();
        }
      },
      /**
       * Callback handler for reset in the datasource.
       * status message.
       * @param {Object} event 
       * @private
       */
      _handleDataReset: function(event)
      {
        this._invokeDataPage(0, false);    
      },
      /**
       * Callback handler for refresh in the datasource.
       * status message.
       * @param {Object} event 
       * @private
       */
      _handleDataRefresh: function(event)
      {
        this._queueRefresh();       
      },
      /**
       * Callback handler for sort in the datasource.
       * status message.
       * @param {Object} event 
       * @private
       */
      _handleDataSort: function(event)
      {
        // Do a reset if the paging mode is loadmore, otherwise just refresh
        if (this.options['mode'] == this._MODE._LOAD_MORE) {
            this._handleDataReset(event);
        }
      },
      /**
       * Callback handler for row added into the datasource.
       * status message.
       * @param {Object} event
       * @private
       */
      _handleDataRowAdd: function(event)
      {
        if (this.options['mode'] == this._MODE._PAGE)
        {
          if (this._isOperationOnCurrentPage(event))
          {
            // this means that the add caused the pages to shift or
            // the row was added to the current page and the first page is full already
            // so we need to re-fetch the current page
            this._invokeDataPage(this._getCurrentPage(), true);
            return;
          }
        }
        this._queueRefresh();
      },
      /**
       * Callback handler for row removed in the datasource.
       * status message.
       * @param {Object} event
       * @private
       */
      _handleDataRowRemove: function(event)
      {
        if (this._getTotalPages() > 0 && this._getCurrentPage() > this._getTotalPages() - 1)
        {
          // if the number of pages decreased due to the removal, then
          // reset the page
          this._invokeDataPage(this._getTotalPages() - 1, true);
        }
        else if (this._isOperationOnCurrentPage(event))
        {
          // this means that the remove caused the pages to shift or
          // the row was deleted from the current page
          // so we need to re-fetch the current page
          this._invokeDataPage(this._getCurrentPage(), true);
          return;
        }
        else
        {
          this._queueRefresh();
        }
      },
      /**
       * Callback handler for page change.
       * @param {Object} event
       * @private
       */
      _handlePageChange: function(event, data)
      {
        try
        {
          var page = data.value;
          if (page != this._getCurrentPage() + 1 && !isNaN(page) && page > 0)
          {
            page = Math.round(page);
            this['page'](page - 1);
          }
        }
        catch (err)
        {
          var errSummary = this.getTranslatedString(this._BUNDLE_KEY._ERR_PAGE_INVALID_SUMMARY);
          var errDetail = this.getTranslatedString(this._BUNDLE_KEY._ERR_PAGE_INVALID_DETAIL);
          oj.Logger.error(errSummary + '\n' + errDetail);
        }
      },
      /**
       * Set page
       * @param {number} page Page
       * @param {boolean} async Asynchronous
       * @return (Promise} Page Promise 
       * @private
       */
      _invokeDataPage: function(page, async)
      {
        try
        {
          page = parseInt(page, 10);
        }
        catch (e)
        {
          return Promise.reject(e);
        }
        
        this._currentStartIndex = 0;
        
        if (async)
        {
          this._queuePageFetch(page);
          return Promise.resolve();
        }
        else
        {
          var data = this._getData();
          var self = this;
          return new Promise(function(resolve, reject)
          {
            if (data != null)
            {
              data.setPage(page, {'pageSize' : self.options['pageSize']}).then(function(result)
              {
                resolve(null);
              }, function(error)
              {
                reject(error);
              });
            }
            else
            {
              resolve(null);
            }
          });
        }
      },
      /**
       * Fetch the next set of rows
       * @return (Promise} Promise 
       * @private
       */
      _invokeDataFetchNext: function()
      {
        var data = this._getData();
        var pageSize = this.options['pageSize'];
        
        if (!this._currentStartIndex)
        {
          this._currentStartIndex = pageSize;
        }
        else
        {
          this._currentStartIndex = this._currentStartIndex + pageSize;
        }

        if (data.totalSize() == -1 || data.totalSize() > this._currentStartIndex)
        {
          var self = this;
          return new Promise(function(resolve, reject)
          {
            data.fetch({'startIndex': self._currentStartIndex, 'pageSize': pageSize}).then(function(result)
            {
              resolve(result);
            }, function (error)
            {
              reject(null);  
            });
          });
        }
        return Promise.resolve();
      },
      /**
       * Check if the rowIdx is for the current page
       * @return {boolean} true or false
       * @private
       */
      _isOperationOnCurrentPage: function(event)
      {
        var data = this._getData();
        var pageSize = this.options['pageSize'];
        var i;
        for (i = 0; i < event['indexes'].length; i++)
        {
          var rowIdx = event['indexes'][i];
          var startIndex = data.getStartItemIndex();

          if (rowIdx >= startIndex && rowIdx < startIndex + pageSize)
          {
            return true;
          }
        }
        return false;
      },
      _queuePageFetch: function(page)
      {
        var self = this;
        if (!this._pendingPageFetch)
        {
          this._pageFetchCount = 0;
          this._pendingPageFetch = Promise.resolve();
        }
        this._pageFetchCount++;
        // keep track of the latest page. We only do a fetch on the latest page.
        this._pageFetchLatestPage = page;
        this._pendingPageFetch = this._pendingPageFetch
        .then(function()
        {
          self._pageFetchCount--;
          if (self._pageFetchCount == 0)
          {
            self._pendingPageFetch = undefined;
            var data = self._getData();
            if (data != null)
            {
              data.setPage(self._pageFetchLatestPage, {'pageSize' : self.options['pageSize']});
            }
          }
        },
        function(error)
        {
          self._pageFetchCount--;
          if (self._pageFetchCount == 0)
          {
            self._pendingPageFetch = undefined;
            oj.Logger.error(error);
          }
        });
      },
      _queueRefresh: function()
      {
        var self = this;
        if (!this._pendingRefreshes)
        {
          this._refreshCount = 0;
          this._pendingRefreshes = Promise.resolve();
        }
        this._refreshCount++;
        this._pendingRefreshes = this._pendingRefreshes
        .then(function()
        {
          self._refreshCount--;
          if (self._refreshCount == 0)
          {
            self._pendingRefreshes = undefined;
            self._refresh();
            self._trigger('ready');
          }
        },
        function(error)
        {
          self._refreshCount--;
          if (self._refreshCount == 0)
          {
            self._pendingRefreshes = undefined;
            oj.Logger.error(error);
          }
        });
      },
      /**
       * @param {number} size Number of rows
       * @param {number} startIndex Start index
       * @private
       */
      _refreshPagingControlLoadMore: function(size, startIndex)
      {
        var pagingControlContent = this._getPagingControlContent();
        if (pagingControlContent != null)
        {
          pagingControlContent.empty();
        }
        this._clearCachedDomLoadMore();
        this._createPagingControlLoadMore();
        var rowCount = -1;
        
        if (size != null)
        {
          rowCount = startIndex + size;
        }
        
        if (rowCount < 0 || rowCount < this.options['loadMoreOptions']['maxCount'])
        {
          this._createPagingControlLoadMoreLink();
          this._createPagingControlLoadMoreRange(size, startIndex);
        }
        else
        {
          this._createPagingControlLoadMoreMaxRows();
        }
      },
      /**
       * @param {number} size Number of rows
       * @param {number} startIndex Start index
       * @private
       */
      _refreshPagingControlNav: function(size, startIndex)
      {
        var overflowOption = this.options['overflow'];
        var pagingControlContent = this._getPagingControlContent();
        if (pagingControlContent != null)
        {
          this._unregisterChildStateListeners(pagingControlContent);
          this._unregisterSwipeHandler();
          pagingControlContent.empty();
        }
        this._clearCachedDomPagingControlNav();
        this._createPagingControlNav(size, startIndex);
        this._registerSwipeHandler();
        this._refreshPagingControlNavArrows(size, startIndex);
        
        if (overflowOption == 'fit')
        {
          // dynamically hide controls based on available width
          var elementWidth = this.element.width();
          var pagingControlNavArrowSection = this._getPagingControlNavArrowSection();
          var pagingControlNavInputSection = this._getPagingControlNavInputSection();
          var pagingControlNavPageLinks = this._getPagingControlNavPageLinks();
          var pagingControlNavInputSummary = this._getPagingControlNavInputSummary();
          var pagingControlNavArrowSectionWidth = pagingControlNavArrowSection != null ? pagingControlNavArrowSection[0].offsetWidth:0;
          var pagingControlNavInputSectionWidth = pagingControlNavInputSection != null ? pagingControlNavInputSection[0].offsetWidth:0;
          var pagingControlNavPageLinksWidth = pagingControlNavPageLinks != null ? pagingControlNavPageLinks.width():0;
          var pagingControlNavInputSummaryWidth = pagingControlNavInputSummary != null ? pagingControlNavInputSummary.width():0;
          var pagingControlNavWidth = pagingControlNavArrowSectionWidth + pagingControlNavInputSectionWidth + pagingControlNavInputSummaryWidth;
          
          if (pagingControlNavWidth > elementWidth)
          {
            if (pagingControlNavWidth 
                - pagingControlNavPageLinksWidth <= elementWidth)
            {
              // hide only the page links
              pagingControlNavPageLinks.css('display', 'none'); 
            }
            else if (pagingControlNavWidth 
                     - pagingControlNavPageLinksWidth 
                     - pagingControlNavInputSummaryWidth <= elementWidth)
            {
              // hide the range text too
              if (pagingControlNavPageLinks != null)
              {
                pagingControlNavPageLinks.css('display', 'none');
              }
              if (pagingControlNavInputSummary != null)
              {
                pagingControlNavInputSummary.css('display', 'none');
              }
            }
            else
            {
              // hide the arrows too
              if (pagingControlNavPageLinks != null)
              {
                pagingControlNavPageLinks.css('display', 'none');
              }
              if (pagingControlNavInputSummary != null)
              {
                pagingControlNavInputSummary.css('display', 'none');
              }
              if (pagingControlNavArrowSection != null)
              {
                pagingControlNavArrowSection.css('display', 'none'); 
              }
            }
          }
          else if (pagingControlNavWidth > 0)
          {
            if (pagingControlNavPageLinks != null)
            {
              pagingControlNavPageLinks.css('display', '');
            }
            if (pagingControlNavInputSummary != null)
            {
              pagingControlNavInputSummary.css('display', '');
            }
            if (pagingControlNavArrowSection != null)
            {
              pagingControlNavArrowSection.css('display', '');
            }
          }
        }
      },
      /**
       * @param {number} size Number of rows
       * @param {number} startIndex Start index
       * @private
       */
      _refreshPagingControlNavArrows: function(size, startIndex)
      {
        var pagingControlNavArrowSection = this._getPagingControlNavArrowSection();
        var pagingControlNavFirst = pagingControlNavArrowSection.children('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_FIRST_CLASS);
        if (pagingControlNavFirst && pagingControlNavFirst.length > 0)
        {
          pagingControlNavFirst = $(pagingControlNavFirst[0]);
          
          if (this._getCurrentPage() == 0)
          {
            pagingControlNavFirst.addClass(this._MARKER_STYLE_CLASSES._DISABLED);
            pagingControlNavFirst.removeClass(this._MARKER_STYLE_CLASSES._ENABLED);
            pagingControlNavFirst.attr('tabindex', '-1');
          }
          else
          {
            pagingControlNavFirst.addClass(this._MARKER_STYLE_CLASSES._ENABLED);
            pagingControlNavFirst.removeClass(this._MARKER_STYLE_CLASSES._DISABLED);
            pagingControlNavFirst.attr(this._TAB_INDEX, '0');
          }
        }
        var pagingControlNavPrevious = pagingControlNavArrowSection.children('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_PREVIOUS_CLASS);
        if (pagingControlNavPrevious && pagingControlNavPrevious.length > 0)
        {
          pagingControlNavPrevious = $(pagingControlNavPrevious[0]);
          
          if (this._getCurrentPage() == 0)
          {
            pagingControlNavPrevious.addClass(this._MARKER_STYLE_CLASSES._DISABLED);
            pagingControlNavPrevious.removeClass(this._MARKER_STYLE_CLASSES._ENABLED);
            pagingControlNavPrevious.attr(this._TAB_INDEX, '-1');
          }
          else
          {
            pagingControlNavPrevious.addClass(this._MARKER_STYLE_CLASSES._ENABLED);
            pagingControlNavPrevious.removeClass(this._MARKER_STYLE_CLASSES._DISABLED);
            pagingControlNavPrevious.attr(this._TAB_INDEX, '0');
          }
        }
        var pagingControlNavLast = pagingControlNavArrowSection.children('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_LAST_CLASS);
        if (pagingControlNavLast && pagingControlNavLast.length > 0)
        {
          pagingControlNavLast = $(pagingControlNavLast[0]);
          
          if (this._getCurrentPage() == this._getTotalPages() - 1 || this._getTotalPages() <= 0)
          {
            pagingControlNavLast.addClass(this._MARKER_STYLE_CLASSES._DISABLED);
            pagingControlNavLast.removeClass(this._MARKER_STYLE_CLASSES._ENABLED);
            pagingControlNavLast.attr(this._TAB_INDEX, '-1');
          }
          else
          {
            pagingControlNavLast.addClass(this._MARKER_STYLE_CLASSES._ENABLED);
            pagingControlNavLast.removeClass(this._MARKER_STYLE_CLASSES._DISABLED);
            pagingControlNavLast.attr(this._TAB_INDEX, '0');
          }
        }
        var pagingControlNavNext = pagingControlNavArrowSection.children('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_NEXT_CLASS);
        if (pagingControlNavNext && pagingControlNavNext.length > 0)
        {
          pagingControlNavNext = $(pagingControlNavNext[0]);
          
          if (this._getCurrentPage() == this._getTotalPages() - 1 || this._getTotalPages() === 0 ||
              (this._getTotalPages() < 0 && size === 0))
          {
            pagingControlNavNext.addClass(this._MARKER_STYLE_CLASSES._DISABLED);
            pagingControlNavNext.removeClass(this._MARKER_STYLE_CLASSES._ENABLED);
            pagingControlNavNext.attr(this._TAB_INDEX, '-1');
          }
          else
          {
            pagingControlNavNext.addClass(this._MARKER_STYLE_CLASSES._ENABLED);
            pagingControlNavNext.removeClass(this._MARKER_STYLE_CLASSES._DISABLED);
            pagingControlNavNext.attr(this._TAB_INDEX, '0');
          }
        }
      },
      /**
       * Register event listeners which need to be registered datasource. 
       * @private
       */
      _registerDataSourceEventListeners: function()
      {
        // register the listeners on the datasource
        var data = this._getData();
        if (data != null)
        {
          this._unregisterDataSourceEventListeners();
          
          this._dataSourceEventHandlers = [];
          this._dataSourceEventHandlers.push({'eventType': oj.PagingModel.EventType['PAGE'], 'eventHandler': this._handleDataPage.bind(this)});
          this._dataSourceEventHandlers.push({'eventType': oj.PagingModel.EventType['PAGECOUNT'], 'eventHandler': this._handleDataRefresh.bind(this)});
          this._dataSourceEventHandlers.push({'eventType': oj.PagingTableDataSource.EventType['ADD'], 'eventHandler': this._handleDataRowAdd.bind(this)});
          this._dataSourceEventHandlers.push({'eventType': oj.PagingTableDataSource.EventType['REMOVE'], 'eventHandler': this._handleDataRowRemove.bind(this)});
          this._dataSourceEventHandlers.push({'eventType': oj.PagingTableDataSource.EventType['RESET'], 'eventHandler': this._handleDataReset.bind(this)});
          this._dataSourceEventHandlers.push({'eventType': oj.PagingTableDataSource.EventType['REFRESH'], 'eventHandler': this._handleDataRefresh.bind(this)});
          this._dataSourceEventHandlers.push({'eventType': oj.PagingTableDataSource.EventType['SYNC'], 'eventHandler': this._handleDataFetchEnd.bind(this)});
          this._dataSourceEventHandlers.push({'eventType': oj.PagingTableDataSource.EventType['SORT'], 'eventHandler': this._handleDataSort.bind(this)});

          var i, ev;
          for (i = 0; i < this._dataSourceEventHandlers.length; i++) {
            ev = data.on(this._dataSourceEventHandlers[i]['eventType'], this._dataSourceEventHandlers[i]['eventHandler']);
            if (ev) {
                this._dataSourceEventHandlers[i]['eventHandler'] = ev;
            }
            
        }
        }
      },
      /**
       * Register event listeners for resize the container DOM element.
       * @param {jQuery} element  DOM element
       * @private
       */
      _registerResizeListener: function(element)
      {         
        if (!this._isResizeListenerAdded)
        {
          var self = this;
          oj.DomUtils.addResizeListener(element[0], function(width, height)
                                                    {
                                                      self._queueRefresh();
                                                    }, 50);
          this._isResizeListenerAdded = true;
        }
      },
      /**
       * Register swipe handler for DOM element.
       * @private
       */
      _registerSwipeHandler: function()
      {
        if (oj.DomUtils.isTouchSupported())
        {
          if (this.options['mode'] == this._MODE._PAGE)
          {
            var pagingControlNavArrowSection = this._getPagingControlNavArrowSection();

            if (pagingControlNavArrowSection != null)
            {
              var isVertical = this.options['pageOptions']['orientation'] == 'vertical' ? true : false;
              var self = this;

              if (isVertical)
              {
                var options = {
                  'recognizers': [
                    [Hammer.Swipe, {'direction': Hammer['DIRECTION_VERTICAL']}]
                ]};

                this._hammerSwipeUp = pagingControlNavArrowSection.ojHammer(options).on('swipeup', function(event)
                {
                  event.preventDefault();
                  self['nextPage']();
                });

                this._hammerSwipeDown = pagingControlNavArrowSection.ojHammer(options).on('swipedown', function(event)
                {
                  event.preventDefault();
                  self['previousPage']();
                });
              }
              else
              {
                var options = {
                  'recognizers': [
                    [Hammer.Swipe, {'direction': Hammer['DIRECTION_HORIZONTAL']}]
                ]};

                this._hammerSwipeLeft = pagingControlNavArrowSection.ojHammer(options).on('swipeleft', function(event)
                {
                  event.preventDefault();
                  self['nextPage']();
                });

                this._hammerSwipeRight = pagingControlNavArrowSection.ojHammer(options).on('swiperight', function(event)
                {
                  event.preventDefault();
                  self['previousPage']();
                });
              }
            }
          }
        }
      },
      /**
       * Unregister event listeners which are registered on datasource. 
       * @private
       */
      _unregisterDataSourceEventListeners: function()
      {
        var data = this._getData();
        // unregister the listeners on the datasource
        if (this._dataSourceEventHandlers != null && data != null)
        {
          var i;
          for (i = 0; i < this._dataSourceEventHandlers.length; i++)
            data.off(this._dataSourceEventHandlers[i]['eventType'], this._dataSourceEventHandlers[i]['eventHandler']);
        }
      },
      /**
       * Unregister _focusable(), etc, which were added to the child elements
       * @param {jQuery} parent jQuery div DOM element
       * @private
       */
      _unregisterChildStateListeners: function(parent)
      {
        var self = this;
        parent.find('*').each(function()
        {
          self._UnregisterChildNode(this);
        });
      },
      /**
       * Unregister swipe handler for DOM element.
       * @private
       */
      _unregisterSwipeHandler: function()
      {
        if (oj.DomUtils.isTouchSupported())
        {
          var isVertical = this.options['pageOptions']['orientation'] == 'vertical' ? true : false;
 
          if (isVertical)
          {
            if (this._hammerSwipeUp != null)
            {
              this._hammerSwipeUp.off('swipeup');
              this._hammerSwipeUp = null;
            }
            if (this._hammerSwipeDown != null)
            {
              this._hammerSwipeDown.off('swipedown');
              this._hammerSwipeDown = null;
            }
          }
          else
          {
            if (this._hammerSwipeLeft != null)
            {
              this._hammerSwipeLeft.off('swipeleft');
              this._hammerSwipeLeft = null;
            }
            if (this._hammerSwipeRight != null)
            {
              this._hammerSwipeRight.off('swiperight');
              this._hammerSwipeRight = null;
            }
          }
        }
      },
      /**** end internal functions ****/
      /**
       * Create a span element for acc purposes
       * @param {string} text span text
       * @param {string} className css class
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createAccLabelSpan: function(text, className)
      {
        var accLabel = $(document.createElement('span'));
        accLabel.addClass(className);
        accLabel.addClass(this._CSS_CLASSES._HIDDEN_CONTENT_ACC_CLASS);
        accLabel.text(text);

        return accLabel;
      },
      /**** start internal DOM functions ****/
      /**
       * Create the acc paging control label
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlAccLabel: function()
      {
        var pagingControlContainer = this._getPagingControlContainer();
        var pagingControlAccLabelText = this.getTranslatedString(this._BUNDLE_KEY._LABEL_ACC_PAGING);
        var pagingControlAccLabel = this._createAccLabelSpan(pagingControlAccLabelText, this._CSS_CLASSES._PAGING_CONTROL_ACC_LABEL_CLASS);
        var pagingControlAccLabelId = this.element.attr('id') + '_oj_pgCtrl_acc_label';
        pagingControlAccLabel.attr('id', pagingControlAccLabelId);
        pagingControlContainer.append(pagingControlAccLabel); //@HTMLUpdateOK

        return pagingControlAccLabel;
      },
      /**
       * Create the acc page link label
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlAccNavPageLabel: function()
      {
        var pagingControlAccNavPageLabelText = this.getTranslatedString(this._BUNDLE_KEY._LABEL_ACC_NAV_PAGE);
        var pagingControlAccNavPageLabel = this._createAccLabelSpan(pagingControlAccNavPageLabelText, this._CSS_CLASSES._PAGING_CONTROL_NAV_PAGE_ACC_LABEL_CLASS);

        return pagingControlAccNavPageLabel;
      },
      /**
       * Create the acc first page label
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlAccNavFirstLabel: function()
      {
        var pagingControlAccNavFirstLabelText = this.getTranslatedString(this._BUNDLE_KEY._LABEL_ACC_NAV_FIRST_PAGE);
        var pagingControlAccNavFirstLabel = this._createAccLabelSpan(pagingControlAccNavFirstLabelText, this._CSS_CLASSES._PAGING_CONTROL_NAV_FIRST_ACC_LABEL_CLASS);

        return pagingControlAccNavFirstLabel;
      },
      /**
       * Create the acc last page label
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlAccNavLastLabel: function()
      {
        var pagingControlAccNavLastLabelText = this.getTranslatedString(this._BUNDLE_KEY._LABEL_ACC_NAV_LAST_PAGE);
        var pagingControlAccNavLastLabel = this._createAccLabelSpan(pagingControlAccNavLastLabelText, this._CSS_CLASSES._PAGING_CONTROL_NAV_LAST_ACC_LABEL_CLASS);

        return pagingControlAccNavLastLabel;
      },
      /**
       * Create the acc next page label
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlAccNavNextLabel: function()
      {
        var pagingControlAccNavNextLabelText = this.getTranslatedString(this._BUNDLE_KEY._LABEL_ACC_NAV_NEXT_PAGE);
        var pagingControlAccNavNextLabel = this._createAccLabelSpan(pagingControlAccNavNextLabelText, this._CSS_CLASSES._PAGING_CONTROL_NAV_NEXT_ACC_LABEL_CLASS);

        return pagingControlAccNavNextLabel;
      },
      /**
       * Create the acc previous page label
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlAccNavPreviousLabel: function()
      {
        var pagingControlAccNavPreviousLabelText = this.getTranslatedString(this._BUNDLE_KEY._LABEL_ACC_NAV_PREVIOUS_PAGE);
        var pagingControlAccNavPreviousLabel = this._createAccLabelSpan(pagingControlAccNavPreviousLabelText, this._CSS_CLASSES._PAGING_CONTROL_NAV_PREVIOUS_ACC_LABEL_CLASS);

        return pagingControlAccNavPreviousLabel;
      },
      /**
       * Create an paging content div
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlContent: function()
      {
        var pagingControlContainer = this._getPagingControlContainer();
        var pagingControlContent = $(document.createElement('div'));
        pagingControlContent.addClass(this._CSS_CLASSES._PAGING_CONTROL_CONTENT_CLASS);
        var pagingControlAccLabelId = this._getPagingControlAccLabel().attr('id');
        pagingControlContent.attr('role', 'navigation');
        pagingControlContent.attr('aria-labelledby', pagingControlAccLabelId);
        pagingControlContainer.append(pagingControlContent); //@HTMLUpdateOK

        return pagingControlContent;
      },
      /**
       * Create an paging load more div
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlLoadMore: function()
      {
        var pagingControlContent = this._getPagingControlContent();
        var pagingControlLoadMore = $(document.createElement('div'));
        pagingControlLoadMore.addClass(this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_CLASS);
        pagingControlContent.append(pagingControlLoadMore); //@HTMLUpdateOK

        return pagingControlLoadMore;
      },
      /**
       * Create an paging load more link
       * @return {jQuery} jQuery a DOM element
       * @private
       */
      _createPagingControlLoadMoreLink: function()
      {
        var pagingControlLoadMore = this._getPagingControlLoadMore();
        var pagingControlLoadMoreLink = $(document.createElement('a'));
        pagingControlLoadMoreLink.addClass(this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_LINK_CLASS);
        var loadMoreText = this.getTranslatedString(this._BUNDLE_KEY._LABEL_LOAD_MORE);
        pagingControlLoadMoreLink.text(loadMoreText);
        pagingControlLoadMoreLink.attr(this._TAB_INDEX, '0');
        pagingControlLoadMoreLink.attr('href', '#');
        pagingControlLoadMore.append(pagingControlLoadMoreLink); //@HTMLUpdateOK

        return pagingControlLoadMoreLink;
      },
      /**
       * Create an paging load more max page message
       * @return {jQuery} jQuery a DOM element
       * @private
       */
      _createPagingControlLoadMoreMaxRows: function()
      {
        var pagingControlLoadMore = this._getPagingControlLoadMore();
        var pagingControlLoadMoreMaxRows = $(document.createElement('span'));
        pagingControlLoadMoreMaxRows.addClass(this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_MAX_ROWS_CLASS);
        var maxRowsText = this.getTranslatedString(this._BUNDLE_KEY._LABEL_LOAD_MORE_MAX_ROWS, {'maxRows': this.options['loadMoreOptions']['maxCount']});
        pagingControlLoadMoreMaxRows.text(maxRowsText);
        pagingControlLoadMore.append(pagingControlLoadMoreMaxRows); //@HTMLUpdateOK

        return pagingControlLoadMoreMaxRows;
      },
      /**
       * Create an paging load more link
       * @param {number} size Number of rows
       * @param {number} startIndex Start index
       * @return {jQuery} jQuery a DOM element
       * @private
       */
      _createPagingControlLoadMoreRange: function(size, startIndex)
      {
        var pagingControlLoadMore = this._getPagingControlLoadMore();
        var pagingControlLoadMoreRange = $(document.createElement('span'));
        pagingControlLoadMoreRange.addClass(this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_RANGE_CLASS);
        var loadMoreRange = this._getItemRange(size, startIndex);
        pagingControlLoadMoreRange.append(loadMoreRange); //@HTMLUpdateOK
        pagingControlLoadMore.append(pagingControlLoadMoreRange); //@HTMLUpdateOK

        return pagingControlLoadMoreRange;
      },
      /**
       * Create the paging nav bar div
       * @param {number} size Number of rows
       * @param {number} startIndex Start index
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlNav: function(size, startIndex)
      {
        var options = this.options;
        var isVertical = this.options['pageOptions']['orientation'] == 'vertical' ? true : false;
        var isDot = this.options['pageOptions']['type'] == 'dots' ? true : false;
        var pageOptionLayout = options['pageOptions']['layout'];
        if (pageOptionLayout == null)
        {
          pageOptionLayout = [this._PAGE_OPTION_LAYOUT._AUTO];
        }
        var pagingControlContent = this._getPagingControlContent();
        var pagingControlNav = $(document.createElement('div'));
        pagingControlNav.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_CLASS);
        pagingControlContent.append(pagingControlNav); //@HTMLUpdateOK

        // page input section
        if (($.inArray(this._PAGE_OPTION_LAYOUT._AUTO, pageOptionLayout) != -1 && !isDot)||
            $.inArray(this._PAGE_OPTION_LAYOUT._ALL, pageOptionLayout) != -1 ||
            $.inArray(this._PAGE_OPTION_LAYOUT._INPUT, pageOptionLayout) != -1)
        {
          var pagingControlNavInputSection = $(document.createElement('div'));
          pagingControlNavInputSection.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_SECTION_CLASS);
          pagingControlNav.append(pagingControlNavInputSection); //@HTMLUpdateOK
          var pagingControlNavLabel = $(document.createElement('label'));
          pagingControlNavLabel.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_LABEL_CLASS);
          var navInputPageLabel = this.getTranslatedString(this._BUNDLE_KEY._LABEL_NAV_INPUT_PAGE);
          pagingControlNavLabel.text(navInputPageLabel);
          pagingControlNavInputSection.append(pagingControlNavLabel); //@HTMLUpdateOK

          var pagingControlNavInput = $(document.createElement('input'));
          pagingControlNavInput.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_CLASS);
          var navInputPageTip = this.getTranslatedString(this._BUNDLE_KEY._TIP_NAV_INPUT_PAGE);
          this._focusable(this.element);
          pagingControlNavInput.attr('title', navInputPageTip);
          pagingControlNavInput.attr(this._TAB_INDEX, '0');
          pagingControlNavInput.val(this._getCurrentPage() + 1);
          pagingControlNavLabel.append(pagingControlNavInput); //@HTMLUpdateOK
          var maxPageVal = 0;
          
          if (this._getTotalPages() > 0)
          {
            maxPageVal = this._getTotalPages();
            var pagingControlNavMaxLabel = $(document.createElement('span'));
            pagingControlNavMaxLabel.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_MAX_CLASS);
            var navInputPageMaxLabel = this.getTranslatedString(this._BUNDLE_KEY._LABEL_NAV_INPUT_PAGE_MAX, {'pageMax': maxPageVal});
            pagingControlNavMaxLabel.text(navInputPageMaxLabel);
            pagingControlNavInputSection.append(pagingControlNavMaxLabel); //@HTMLUpdateOK
          }
          else
          {
            if (size > 0)
            {
              maxPageVal = this._getCurrentPage() + 2;
            }
            else
            {
              maxPageVal = this._getCurrentPage() + 1;
            }
          }
          pagingControlNavInput.ojInputText({'rootAttributes': {'style':"width: auto; min-width: 0;"}, 'optionChange': this._handlePageChange.bind(this), 'validators': [{'type': 'numberRange', 'options': {'min': 1, max: maxPageVal}}]});
        }
        
        if (($.inArray(this._PAGE_OPTION_LAYOUT._AUTO, pageOptionLayout) != -1 && !isDot) ||
            $.inArray(this._PAGE_OPTION_LAYOUT._ALL, pageOptionLayout) != -1 ||
            $.inArray(this._PAGE_OPTION_LAYOUT._RANGE_TEXT, pageOptionLayout) != -1)
        {
          var pagingControlNavSummaryLabel = $(document.createElement('span'));
          pagingControlNavSummaryLabel.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_SUMMARY_CLASS);
          var itemRange = this._getItemRange(size, startIndex);
          pagingControlNavSummaryLabel.append("("); //@HTMLUpdateOK
          pagingControlNavSummaryLabel.append(itemRange); //@HTMLUpdateOK
          pagingControlNavSummaryLabel.append(")"); //@HTMLUpdateOK
          pagingControlNav.append(pagingControlNavSummaryLabel); //@HTMLUpdateOK
        }

        // nav arrow section
        var pagingControlNavArrowSection = $(document.createElement('div'));
        pagingControlNavArrowSection.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_ARROW_SECTION_CLASS);
        pagingControlNav.append(pagingControlNavArrowSection); //@HTMLUpdateOK
        
        if (($.inArray(this._PAGE_OPTION_LAYOUT._AUTO, pageOptionLayout) != -1 && !isDot) ||
            $.inArray(this._PAGE_OPTION_LAYOUT._ALL, pageOptionLayout) != -1 ||
            $.inArray(this._PAGE_OPTION_LAYOUT._NAV, pageOptionLayout) != -1)
        {
          var pagingControlNavFirst = $(document.createElement('a'));
          pagingControlNavFirst.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_ARROW_CLASS);
          pagingControlNavFirst.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_FIRST_CLASS);
          if (!isVertical)
          {
            pagingControlNavFirst.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_FIRST_ICON_CLASS);
          }
          else
          {
            pagingControlNavFirst.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_FIRST_VERTICAL_ICON_CLASS);
          }
          pagingControlNavFirst.addClass(this._CSS_CLASSES._WIDGET_ICON_CLASS);
          pagingControlNavFirst.addClass(this._MARKER_STYLE_CLASSES._CLICKABLE_ICON);
          pagingControlNavFirst.addClass(this._MARKER_STYLE_CLASSES._DISABLED);
          var navFirstPageTip = this.getTranslatedString(this._BUNDLE_KEY._TIP_NAV_FIRST_PAGE);
          this._hoverable(pagingControlNavFirst);
          this._focusable(pagingControlNavFirst);
          pagingControlNavFirst.attr('title', navFirstPageTip);
          pagingControlNavFirst.attr(this._TAB_INDEX, '0');
          pagingControlNavFirst.attr('href', '#');
          pagingControlNavFirst.attr('oncontextmenu', 'return false;');
          var pagingControlNavFirstAccLabel = this._createPagingControlAccNavFirstLabel();
          pagingControlNavFirst.append(pagingControlNavFirstAccLabel); //@HTMLUpdateOK
          if (isVertical)
          {
            pagingControlNavFirst.css('display', 'block');
          }
          pagingControlNavArrowSection.append(pagingControlNavFirst); //@HTMLUpdateOK

          var pagingControlNavPrevious = $(document.createElement('a'));
          pagingControlNavPrevious.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_ARROW_CLASS);
          pagingControlNavPrevious.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_PREVIOUS_CLASS);
          if (!isVertical)
          {
            pagingControlNavPrevious.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_PREVIOUS_ICON_CLASS);
          }
          else
          {
            pagingControlNavPrevious.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_PREVIOUS_VERTICAL_ICON_CLASS);
          }
          pagingControlNavPrevious.addClass(this._CSS_CLASSES._WIDGET_ICON_CLASS);
          pagingControlNavPrevious.addClass(this._MARKER_STYLE_CLASSES._CLICKABLE_ICON);
          pagingControlNavPrevious.addClass(this._MARKER_STYLE_CLASSES._DISABLED);
          var navPreviousPageTip= this.getTranslatedString(this._BUNDLE_KEY._TIP_NAV_PREVIOUS_PAGE);
          this._hoverable(pagingControlNavPrevious);
          this._focusable(pagingControlNavPrevious);
          pagingControlNavPrevious.attr('title', navPreviousPageTip);
          pagingControlNavPrevious.attr(this._TAB_INDEX, '0');
          pagingControlNavPrevious.attr('href', '#');
          pagingControlNavPrevious.attr('oncontextmenu', 'return false;');
          var pagingControlNavPreviousAccLabel = this._createPagingControlAccNavPreviousLabel();
          pagingControlNavPrevious.append(pagingControlNavPreviousAccLabel); //@HTMLUpdateOK
          if (isVertical)
          {
            pagingControlNavPrevious.css('display', 'block');
          }
          pagingControlNavArrowSection.append(pagingControlNavPrevious); //@HTMLUpdateOK
        }

        // nav pages section
        if ($.inArray(this._PAGE_OPTION_LAYOUT._AUTO, pageOptionLayout) != -1 ||
            $.inArray(this._PAGE_OPTION_LAYOUT._ALL, pageOptionLayout) != -1 ||
            $.inArray(this._PAGE_OPTION_LAYOUT._PAGES, pageOptionLayout) != -1)
        {
          var pagingControlNavPagesSection = $(document.createElement('div'));
          pagingControlNavPagesSection.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_PAGES_SECTION_CLASS);
          pagingControlNavArrowSection.append(pagingControlNavPagesSection); //@HTMLUpdateOK
          this._createPagingControlNavPages(pagingControlNavPagesSection, this._getMaxPageLinks(), size, startIndex);
        }

        if (($.inArray(this._PAGE_OPTION_LAYOUT._AUTO, pageOptionLayout) != -1 && !isDot) ||
            $.inArray(this._PAGE_OPTION_LAYOUT._ALL, pageOptionLayout) != -1 ||
            $.inArray(this._PAGE_OPTION_LAYOUT._NAV, pageOptionLayout) != -1)
        {
          var pagingControlNavNext = $(document.createElement('a'));
          pagingControlNavNext.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_ARROW_CLASS);
          pagingControlNavNext.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_NEXT_CLASS);
          if (!isVertical)
          {
            pagingControlNavNext.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_NEXT_ICON_CLASS);
          }
          else
          {
            pagingControlNavNext.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_NEXT_VERTICAL_ICON_CLASS);
          }
          pagingControlNavNext.addClass(this._CSS_CLASSES._WIDGET_ICON_CLASS);
          pagingControlNavNext.addClass(this._MARKER_STYLE_CLASSES._CLICKABLE_ICON);
          pagingControlNavNext.addClass(this._MARKER_STYLE_CLASSES._DISABLED);
          var navNextPageTip = this.getTranslatedString(this._BUNDLE_KEY._TIP_NAV_NEXT_PAGE);
          this._hoverable(pagingControlNavNext);
          this._focusable(pagingControlNavNext);
          pagingControlNavNext.attr('title', navNextPageTip);
          pagingControlNavNext.attr(this._TAB_INDEX, '0');
          pagingControlNavNext.attr('href', '#');
          pagingControlNavNext.attr('oncontextmenu', 'return false;');
          var pagingControlNavNextAccLabel = this._createPagingControlAccNavNextLabel();
          pagingControlNavNext.append(pagingControlNavNextAccLabel); //@HTMLUpdateOK
          if (isVertical)
          {
            pagingControlNavNext.css('display', 'block');
          }
          pagingControlNavArrowSection.append(pagingControlNavNext); //@HTMLUpdateOK

          var pagingControlNavLast = $(document.createElement('a'));
          pagingControlNavLast.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_ARROW_CLASS);
          pagingControlNavLast.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_LAST_CLASS);
          if (!isVertical)
          {
            pagingControlNavLast.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_LAST_ICON_CLASS);
          }
          else
          {
            pagingControlNavLast.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_LAST_VERTICAL_ICON_CLASS); 
          }
          pagingControlNavLast.addClass(this._CSS_CLASSES._WIDGET_ICON_CLASS);
          pagingControlNavLast.addClass(this._MARKER_STYLE_CLASSES._CLICKABLE_ICON);
          pagingControlNavLast.addClass(this._MARKER_STYLE_CLASSES._DISABLED);
          var navLastPageTip = this.getTranslatedString(this._BUNDLE_KEY._TIP_NAV_LAST_PAGE);
          this._hoverable(pagingControlNavLast);
          this._focusable(pagingControlNavLast);
          pagingControlNavLast.attr('title', navLastPageTip);
          pagingControlNavLast.attr(this._TAB_INDEX, '0');
          pagingControlNavLast.attr('href', '#');
          pagingControlNavLast.attr('oncontextmenu', 'return false;');
          var pagingControlNavLastAccLabel = this._createPagingControlAccNavLastLabel();
          pagingControlNavLast.append(pagingControlNavLastAccLabel); //@HTMLUpdateOK
          if (isVertical)
          {
            pagingControlNavLast.css('display', 'block');
          }
          pagingControlNavArrowSection.append(pagingControlNavLast); //@HTMLUpdateOK
        }

        return pagingControlNav;
      },
      /**
       * Create the page links
       * @param {jQuery} parentDiv parent element
       * @param {number} numLinks number of page links
       * @param {number} size Number of rows
       * @param {number} startIndex Start index
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlNavPages: function(parentDiv, numLinks, size, startIndex)
      {
        if (numLinks < 5)
        {
          var errSummary = this.getTranslatedString(this._BUNDLE_KEY._ERR_MAXPAGELINKS_INVALID_SUMMARY);
          var errDetail = this.getTranslatedString(this._BUNDLE_KEY._ERR_MAXPAGELINKS_INVALID_DETAIL);
          throw new Error(errSummary + '\n' + errDetail);
        }
        var pagingControlNavPagesLinks = $(document.createElement('div'));
        pagingControlNavPagesLinks.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_PAGES_LINKS_CLASS);
        parentDiv.append(pagingControlNavPagesLinks); //@HTMLUpdateOK
        var totalPages = this._getTotalPages();
        var currentPage = this._getCurrentPage();
        
        var numPagesToAdd = numLinks;
        // this will hold our page list
        var pageList = [];

        if (currentPage >= 0)
        {
          var i;
          if (totalPages != -1 && totalPages <= numPagesToAdd)
          {
            // always add the first page
            pageList[0] = 0;
            
            // just enumerate the pages
            for (i = 1; i < totalPages; i++)
            {
              pageList[i] = i;
            }
          }
          else
          {
            // add the first, current, and last page
            pageList.push(0);
            if (currentPage != 0)
            {
              pageList.push(currentPage);
            }
            // add last page if known row count
            if (currentPage != totalPages - 1 && totalPages != -1)
            {
              pageList.push(totalPages - 1);
            }
            numPagesToAdd = numPagesToAdd - pageList.length;
            // keep adding before the current page till we get to the
            // first page or we've added numPagesToAdd - 1.
            // If the last page or 2nd to last page then add until numPagesToAdd
            var pageBeforeCurrent = currentPage - 1;
            // number of pages to add after current
            var numPagesAfterCurrent = 1;
            // if at last page or second to last page then don't add any pages
            // after current
            if (currentPage == totalPages - 1 || currentPage == totalPages - 2)
            {
              numPagesAfterCurrent = 0;
            }
            while (numPagesToAdd > numPagesAfterCurrent && pageBeforeCurrent > 1)
            {
              pageList.push(pageBeforeCurrent);
              pageBeforeCurrent--;
              numPagesToAdd--;
            }
            // keep adding after the current page
            var pageAfterCurrent = currentPage + 1;
            // if unknown row count, only add one page after current if there is data
            if (totalPages == -1)
            {
              if (size > 0)
              {
                numPagesToAdd = 1;
              }
              else
              {
                numPagesToAdd = 0;
              }
            }
            while (numPagesToAdd > 0 && (pageAfterCurrent <= totalPages || totalPages == -1))
            {
              pageList.push(pageAfterCurrent);
              pageAfterCurrent++;
              numPagesToAdd--;
            }
          }

          // sort the pageList array
          var compareNumbers = function (a, b) {
            return a - b;
          };
          
          pageList.sort(compareNumbers);

          for (i = 0; i < pageList.length; i++)
          {
            var pageNum = pageList[i];
            this._createPagingControlNavPage(pagingControlNavPagesLinks, pageNum);
            // check if we have a gap
            if (i != pageList.length - 1)
            {
              if (pageNum != pageList[i + 1] - 1)
              {
                this._createPagingControlNavPage(pagingControlNavPagesLinks, -1);
              }
            }
          }
          if (totalPages == -1)
          {
            this._createPagingControlNavPage(pagingControlNavPagesLinks, -1);
          }
        }
        return pagingControlNavPagesLinks;
      },
      /**
       * Create the page link or page gap
       * @param {jQuery} parentDiv parent element
       * @param {number} pageNum page number
       * @return {jQuery} jQuery div DOM element
       * @private
       */
      _createPagingControlNavPage: function(parentDiv, pageNum)
      {
        var currentPage = this._getCurrentPage();
        var pagingControlNavPage = null;
        var isRTL = (this._GetReadingDirection() === "rtl");
        var isVertical = this.options['pageOptions']['orientation'] == 'vertical' ? true : false;
        var isDot = this.options['pageOptions']['type'] == 'dots' ? true : false;
        if (pageNum == -1)
        {
          pagingControlNavPage = $(document.createElement('span'));
          pagingControlNavPage.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_PAGE_ELLIPSIS_CLASS);
          pagingControlNavPage.text("...");
          parentDiv.append(pagingControlNavPage); //@HTMLUpdateOK
        }
        else
        {          
          if (currentPage == pageNum)
          {
             pagingControlNavPage = $(document.createElement('div'));
             pagingControlNavPage.addClass(this._MARKER_STYLE_CLASSES._SELECTED);
             pagingControlNavPage.addClass(this._MARKER_STYLE_CLASSES._ACTIVE);
             pagingControlNavPage.addClass(this._MARKER_STYLE_CLASSES._DISABLED);
             pagingControlNavPage.removeClass(this._MARKER_STYLE_CLASSES._ENABLED);
          }
          else
          {
            pagingControlNavPage = $(document.createElement('a'));
            pagingControlNavPage.removeClass(this._MARKER_STYLE_CLASSES._SELECTED);
            pagingControlNavPage.removeClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            pagingControlNavPage.removeClass(this._MARKER_STYLE_CLASSES._DISABLED);
            pagingControlNavPage.addClass(this._MARKER_STYLE_CLASSES._ENABLED);
          }
          pagingControlNavPage.attr('data-oj-pagenum', pageNum);
          if (!isDot)
          {
            pagingControlNavPage.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_PAGE_CLASS);
          }
          else
          {
            pagingControlNavPage.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_DOT_CLASS);
          }
          var pageTitle = this.getTranslatedString(this._BUNDLE_KEY._TIP_NAV_PAGE_LINK, {'pageNum': (pageNum + 1).toString()});
          this._hoverable(pagingControlNavPage);
          this._focusable(pagingControlNavPage);
          pagingControlNavPage.attr('title', pageTitle);
          pagingControlNavPage.attr(this._TAB_INDEX, '0');
          pagingControlNavPage.attr('href', '#');
          pagingControlNavPage.attr('oncontextmenu', 'return false;');
          // create the acc label for the page link
          var accPageLabel = this._createPagingControlAccNavPageLabel();
          pagingControlNavPage.append(accPageLabel); //@HTMLUpdateOK
          var pagingControlNavPageSpan = $(document.createElement('span'));
          pagingControlNavPageSpan.append((pageNum + 1).toString()); //@HTMLUpdateOK
          
          if (isDot)
          {
            pagingControlNavPageSpan.addClass(this._CSS_CLASSES._PAGING_CONTROL_NAV_DOT_BULLET_CLASS);
            this._hoverable(pagingControlNavPageSpan);
            
            if (currentPage == pageNum)
            {
               pagingControlNavPageSpan.addClass(this._MARKER_STYLE_CLASSES._SELECTED);
               pagingControlNavPageSpan.addClass(this._MARKER_STYLE_CLASSES._ACTIVE);
            }
          }
          var dirAttrVal = isRTL ? 'rtl' : 'ltr';
          pagingControlNavPageSpan.attr('dir', dirAttrVal);
          pagingControlNavPage.append(pagingControlNavPageSpan); //@HTMLUpdateOK
          this._hoverable(pagingControlNavPage);
          if (isVertical)
          {
            pagingControlNavPage.css('display', 'block');
          }
          parentDiv.append(pagingControlNavPage); //@HTMLUpdateOK
        }
        return pagingControlNavPage;
      },
      /**
       * Return the paging content acc label
       * @return {jQuery|null} jQuery div DOM element
       * @private
       */
      _getPagingControlAccLabel: function()
      {
        var pagingControlContainer = this._getPagingControlContainer();
        var pagingControlContentAccLabel = null;
        
        if (pagingControlContainer)
        {
          pagingControlContentAccLabel = pagingControlContainer.find('.' + this._CSS_CLASSES._PAGING_CONTROL_ACC_LABEL_CLASS);
          if (pagingControlContentAccLabel && pagingControlContentAccLabel.length > 0)
          {
            pagingControlContentAccLabel = $(pagingControlContentAccLabel.get(0));
          }
        }
        
        return pagingControlContentAccLabel;
      },
      /**
       * Return the paging container
       * @return {jQuery|null} jQuery div DOM element
       * @private
       */
      _getPagingControlContainer: function()
      {
        return $(this.element);
      },
      /**
       * Return the paging content
       * @return {jQuery|null} jQuery div DOM element
       * @private
       */
      _getPagingControlContent: function()
      {
        if (!this._cachedDomPagingControlContent)
        {
          var pagingControlContainer = this._getPagingControlContainer();
          var pagingControlContent = null;
          if (pagingControlContainer)
          {
            pagingControlContent = pagingControlContainer.find('.' + this._CSS_CLASSES._PAGING_CONTROL_CONTENT_CLASS);
            if (pagingControlContent && pagingControlContent.length > 0)
            {
              this._cachedDomPagingControlContent = $(pagingControlContent.get(0));
            }
          }
        }

        return this._cachedDomPagingControlContent;
      },
      /**
       * Return the Load More div
       * @return {jQuery|null} jQuery a DOM element
       * @private
       */
      _getPagingControlLoadMore: function()
      {
        if (!this._cachedDomPagingControlLoadMore)
        {
          var pagingControlContent = this._getPagingControlContent();
          var pagingControlLoadMore = null;
          if (pagingControlContent)
          {
            pagingControlLoadMore = pagingControlContent.children('.' + this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_CLASS);
            if (pagingControlLoadMore && pagingControlLoadMore.length > 0)
            {
              this._cachedDomPagingControlLoadMore = $(pagingControlLoadMore.get(0));
            }
          }
        }

        return this._cachedDomPagingControlLoadMore;
      },
      /**
       * Return the Load More link
       * @return {jQuery|null} jQuery a DOM element
       * @private
       */
      _getPagingControlLoadMoreLink: function()
      {
        if (!this._cachedDomPagingControlLoadMoreLink)
        {
          var pagingControlLoadMore = this._getPagingControlLoadMore();
          var pagingControlLoadMoreLink = null;
          if (pagingControlLoadMore)
          {
            pagingControlLoadMoreLink = pagingControlLoadMore.children('.' + this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_LINK_CLASS);
            if (pagingControlLoadMoreLink && pagingControlLoadMoreLink.length > 0)
            {
              this._cachedDomPagingControlLoadMoreLink = $(pagingControlLoadMoreLink.get(0));
            }
          }
        }

        return this._cachedDomPagingControlLoadMoreLink;
      },
      /**
       * Return the Load More Range
       * @return {jQuery|null} jQuery span DOM element
       * @private
       */
      _getPagingControlLoadMoreRange: function()
      {
        if (!this._cachedDomPagingControlLoadMoreRange)
        {
          var pagingControlLoadMore = this._getPagingControlLoadMore();
          var pagingControlLoadMoreRange = null;
          if (pagingControlLoadMore)
          {
            pagingControlLoadMoreRange = pagingControlLoadMore.children('.' + this._CSS_CLASSES._PAGING_CONTROL_LOAD_MORE_RANGE_CLASS);
            if (pagingControlLoadMoreRange && pagingControlLoadMoreRange.length > 0)
            {
              this._cachedDomPagingControlLoadMoreRange = $(pagingControlLoadMoreRange.get(0));
            }
          }
        }

        return this._cachedDomPagingControlLoadMoreRange;
      },
      /**
       * Return the paging nav bar
       * @return {jQuery|null} jQuery a DOM element
       * @private
       */
      _getPagingControlNav: function()
      {
        if (!this._cachedDomPagingControlNav)
        {
          var pagingControlContent = this._getPagingControlContent();
          var pagingControlNav = null;
          if (pagingControlContent)
          {
            pagingControlNav = pagingControlContent.children('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_CLASS);
            if (pagingControlNav && pagingControlNav.length > 0)
            {
              this._cachedDomPagingControlNav = $(pagingControlNav.get(0));
            }
          }
        }

        return this._cachedDomPagingControlNav;
      },
      /**
       * Return the paging nav input
       * @return {jQuery|null} jQuery input DOM element
       * @private
       */
      _getPagingControlNavInput: function()
      {
        if (!this._cachedDomPagingControlNavInput)
        {
          var pagingControlNav = this._getPagingControlNav();
          var pagingControlNavInput = null;
          if (pagingControlNav)
          {
            pagingControlNavInput = pagingControlNav.find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_CLASS);
            if (pagingControlNavInput && pagingControlNavInput.length > 0)
            {
              this._cachedDomPagingControlNavInput = $(pagingControlNavInput.get(0));
            }
          }
        }

        return this._cachedDomPagingControlNavInput;
      },
      /**
       * Return the paging nav input summary
       * @return {jQuery|null} jQuery input DOM element
       * @private
       */
      _getPagingControlNavInputSummary: function()
      {
        if (!this._cachedDomPagingControlNavInputSummary)
        {
          var pagingControlNav = this._getPagingControlNav();
          var pagingControlNavInputSummary = null;
          if (pagingControlNav)
          {
            pagingControlNavInputSummary = pagingControlNav.find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_SUMMARY_CLASS);
            if (pagingControlNavInputSummary && pagingControlNavInputSummary.length > 0)
            {
              this._cachedDomPagingControlNavInputSummary = $(pagingControlNavInputSummary.get(0));
            }
          }
        }

        return this._cachedDomPagingControlNavInputSummary;
      },
      /**
       * Return the page links
       * @private
       */
      _getPagingControlNavPageLinks: function()
      {
        var pagingControlNav = this._getPagingControlNav();
        var pagingControlNavPageLinks = null;
        if (pagingControlNav)
        {
          pagingControlNavPageLinks = pagingControlNav.find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_PAGES_LINKS_CLASS);
          if (pagingControlNavPageLinks && pagingControlNavPageLinks.length > 0)
          {
            pagingControlNavPageLinks = $(pagingControlNavPageLinks.get(0));
          }
        }

        return pagingControlNavPageLinks;
      },
      /**
       * Return the nav arrows
       * @private
       */
      _getPagingControlNavArrowSection: function()
      {
        var pagingControlNav = this._getPagingControlNav();
        var pagingControlNavArrowSection = null;
        if (pagingControlNav)
        {
          pagingControlNavArrowSection = pagingControlNav.find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_ARROW_SECTION_CLASS);
          if (pagingControlNavArrowSection && pagingControlNavArrowSection.length > 0)
          {
            pagingControlNavArrowSection = $(pagingControlNavArrowSection.get(0));
          }
          else
          {
            return null;
          }
        }

        return pagingControlNavArrowSection;
      }
      ,
      /**
       * Return the nav input section
       * @private
       */
      _getPagingControlNavInputSection: function()
      {
        var pagingControlNav = this._getPagingControlNav();
        var pagingControlNavInputSection = null;
        if (pagingControlNav)
        {
          pagingControlNavInputSection = pagingControlNav.find('.' + this._CSS_CLASSES._PAGING_CONTROL_NAV_INPUT_SECTION_CLASS);
          if (pagingControlNavInputSection && pagingControlNavInputSection.length > 0)
          {
            pagingControlNavInputSection = $(pagingControlNavInputSection.get(0));
          }
          else
          {
            return null;
          }
        }

        return pagingControlNavInputSection;
      }
      /**** end internal DOM functions ****/
    })
    //////////////////     FRAGMENTS    //////////////////
    /**
     * <table class="keyboard-table">
     *   <thead>
     *     <tr>
     *       <th>Target</th>
     *       <th>Gesture</th>
     *       <th>Action</th>
     *     </tr>
     *   </thead>
     *   <tbody>
     *     <tr>
     *       <td rowspan="2">Page Navigation Bar</td>
     *       <td><kbd>Swipe</kbd></td>
     *       <td>When mode='page', swiping left or right on the page navigation bar will either increment or decrement the page respectively.</td>
     *     </tr>
     *   </tbody>
     * </table>
     *
     * @ojfragment touchDoc - Used in touch section of classdesc, and standalone gesture doc
     * @memberof oj.ojPagingControl
     */  
    
    /**
     * <p>This component has no keyboard interaction.  
     * @ojfragment keyboardDoc - Used in keyboard section of classdesc, and standalone gesture doc
     * @memberof oj.ojPagingControl
     */
}());