[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/2in3/2.9.0/build/yui2-paginator/ -> yui2-paginator.js (source)

   1  YUI.add('yui2-paginator', function(Y) {
   2      var YAHOO    = Y.YUI2;
   3      /*
   4  Copyright (c) 2011, Yahoo! Inc. All rights reserved.
   5  Code licensed under the BSD License:
   6  http://developer.yahoo.com/yui/license.html
   7  version: 2.9.0
   8  */
   9  (function () {
  10  /**
  11   * The Paginator widget provides a set of controls to navigate through paged
  12   * data.
  13   *
  14   * @module paginator
  15   * @uses YAHOO.util.EventProvider
  16   * @uses YAHOO.util.AttributeProvider
  17   */
  18  
  19  var Dom        = YAHOO.util.Dom,
  20      lang       = YAHOO.lang,
  21      isObject   = lang.isObject,
  22      isFunction = lang.isFunction,
  23      isArray    = lang.isArray,
  24      isString   = lang.isString;
  25  
  26  /**
  27   * Instantiate a Paginator, passing a configuration object to the contructor.
  28   * The configuration object should contain the following properties:
  29   * <ul>
  30   *   <li>rowsPerPage : <em>n</em> (int)</li>
  31   *   <li>totalRecords : <em>n</em> (int or Paginator.VALUE_UNLIMITED)</li>
  32   *   <li>containers : <em>id | el | arr</em> (HTMLElement reference, its id, or an array of either)</li>
  33   * </ul>
  34   *
  35   * @namespace YAHOO.widget
  36   * @class Paginator
  37   * @constructor
  38   * @param config {Object} Object literal to set instance and ui component
  39   * configuration.
  40   */
  41  function Paginator(config) {
  42      var UNLIMITED = Paginator.VALUE_UNLIMITED,
  43          attrib, initialPage, records, perPage, startIndex;
  44  
  45      config = isObject(config) ? config : {};
  46  
  47      this.initConfig();
  48  
  49      this.initEvents();
  50  
  51      // Set the basic config keys first
  52      this.set('rowsPerPage',config.rowsPerPage,true);
  53      if (Paginator.isNumeric(config.totalRecords)) {
  54          this.set('totalRecords',config.totalRecords,true);
  55      }
  56      
  57      this.initUIComponents();
  58  
  59      // Update the other config values
  60      for (attrib in config) {
  61          if (config.hasOwnProperty(attrib)) {
  62              this.set(attrib,config[attrib],true);
  63          }
  64      }
  65  
  66      // Calculate the initial record offset
  67      initialPage = this.get('initialPage');
  68      records     = this.get('totalRecords');
  69      perPage     = this.get('rowsPerPage');
  70      if (initialPage > 1 && perPage !== UNLIMITED) {
  71          startIndex = (initialPage - 1) * perPage;
  72          if (records === UNLIMITED || startIndex < records) {
  73              this.set('recordOffset',startIndex,true);
  74          }
  75      }
  76  }
  77  
  78  
  79  // Static members
  80  lang.augmentObject(Paginator, {
  81      /**
  82       * Incrementing index used to give instances unique ids.
  83       * @static
  84       * @property Paginator.id
  85       * @type number
  86       * @private
  87       */
  88      id : 0,
  89  
  90      /**
  91       * Base of id strings used for ui components.
  92       * @static
  93       * @property Paginator.ID_BASE
  94       * @type string
  95       * @private
  96       */
  97      ID_BASE : 'yui-pg',
  98  
  99      /**
 100       * Used to identify unset, optional configurations, or used explicitly in
 101       * the case of totalRecords to indicate unlimited pagination.
 102       * @static
 103       * @property Paginator.VALUE_UNLIMITED
 104       * @type number
 105       * @final
 106       */
 107      VALUE_UNLIMITED : -1,
 108  
 109      /**
 110       * Default template used by Paginator instances.  Update this if you want
 111       * all new Paginators to use a different default template.
 112       * @static
 113       * @property Paginator.TEMPLATE_DEFAULT
 114       * @type string
 115       */
 116      TEMPLATE_DEFAULT : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink}",
 117  
 118      /**
 119       * Common alternate pagination format, including page links, links for
 120       * previous, next, first and last pages as well as a rows-per-page
 121       * dropdown.  Offered as a convenience.
 122       * @static
 123       * @property Paginator.TEMPLATE_ROWS_PER_PAGE
 124       * @type string
 125       */
 126      TEMPLATE_ROWS_PER_PAGE : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}",
 127  
 128      /**
 129       * Storage object for UI Components
 130       * @static
 131       * @property Paginator.ui
 132       */
 133      ui : {},
 134  
 135      /**
 136       * Similar to YAHOO.lang.isNumber, but allows numeric strings.  This is
 137       * is used for attribute validation in conjunction with getters that return
 138       * numbers.
 139       *
 140       * @method Paginator.isNumeric
 141       * @param v {Number|String} value to be checked for number or numeric string
 142       * @returns {Boolean} true if the input is coercable into a finite number
 143       * @static
 144       */
 145      isNumeric : function (v) {
 146          return isFinite(+v);
 147      },
 148  
 149      /**
 150       * Return a number or null from input
 151       *
 152       * @method Paginator.toNumber
 153       * @param n {Number|String} a number or numeric string
 154       * @return Number
 155       * @static
 156       */
 157      toNumber : function (n) {
 158          return isFinite(+n) ? +n : null;
 159      }
 160  
 161  },true);
 162  
 163  
 164  // Instance members and methods
 165  Paginator.prototype = {
 166  
 167      // Instance members
 168  
 169      /**
 170       * Array of nodes in which to render pagination controls.  This is set via
 171       * the &quot;containers&quot; attribute.
 172       * @property _containers
 173       * @type Array(HTMLElement)
 174       * @private
 175       */
 176      _containers : [],
 177  
 178      /**
 179       * Flag used to indicate multiple attributes are being updated via setState
 180       * @property _batch
 181       * @type boolean
 182       * @protected
 183       */
 184      _batch : false,
 185  
 186      /**
 187       * Used by setState to indicate when a page change has occurred
 188       * @property _pageChanged
 189       * @type boolean
 190       * @protected
 191       */
 192      _pageChanged : false,
 193  
 194      /**
 195       * Temporary state cache used by setState to keep track of the previous
 196       * state for eventual pageChange event firing
 197       * @property _state
 198       * @type Object
 199       * @protected
 200       */
 201      _state : null,
 202  
 203  
 204      // Instance methods
 205  
 206      /**
 207       * Initialize the Paginator's attributes (see YAHOO.util.Element class
 208       * AttributeProvider).
 209       * @method initConfig
 210       * @private
 211       */
 212      initConfig : function () {
 213  
 214          var UNLIMITED = Paginator.VALUE_UNLIMITED;
 215  
 216          /**
 217           * REQUIRED. Number of records constituting a &quot;page&quot;
 218           * @attribute rowsPerPage
 219           * @type integer
 220           */
 221          this.setAttributeConfig('rowsPerPage', {
 222              value     : 0,
 223              validator : Paginator.isNumeric,
 224              setter    : Paginator.toNumber
 225          });
 226  
 227          /**
 228           * REQUIRED. Node references or ids of nodes in which to render the
 229           * pagination controls.
 230           * @attribute containers
 231           * @type {string|HTMLElement|Array(string|HTMLElement)}
 232           */
 233          this.setAttributeConfig('containers', {
 234              value     : null,
 235              validator : function (val) {
 236                  if (!isArray(val)) {
 237                      val = [val];
 238                  }
 239                  for (var i = 0, len = val.length; i < len; ++i) {
 240                      if (isString(val[i]) || 
 241                          (isObject(val[i]) && val[i].nodeType === 1)) {
 242                          continue;
 243                      }
 244                      return false;
 245                  }
 246                  return true;
 247              },
 248              method : function (val) {
 249                  val = Dom.get(val);
 250                  if (!isArray(val)) {
 251                      val = [val];
 252                  }
 253                  this._containers = val;
 254              }
 255          });
 256  
 257          /**
 258           * Total number of records to paginate through
 259           * @attribute totalRecords
 260           * @type integer
 261           * @default 0
 262           */
 263          this.setAttributeConfig('totalRecords', {
 264              value     : 0,
 265              validator : Paginator.isNumeric,
 266              setter    : Paginator.toNumber
 267          });
 268  
 269          /**
 270           * Zero based index of the record considered first on the current page.
 271           * For page based interactions, don't modify this attribute directly;
 272           * use setPage(n).
 273           * @attribute recordOffset
 274           * @type integer
 275           * @default 0
 276           */
 277          this.setAttributeConfig('recordOffset', {
 278              value     : 0,
 279              validator : function (val) {
 280                  var total = this.get('totalRecords');
 281                  if (Paginator.isNumeric(val)) {
 282                      val = +val;
 283                      return total === UNLIMITED || total > val ||
 284                             (total === 0 && val === 0);
 285                  }
 286  
 287                  return false;
 288              },
 289              setter    : Paginator.toNumber
 290          });
 291  
 292          /**
 293           * Page to display on initial paint
 294           * @attribute initialPage
 295           * @type integer
 296           * @default 1
 297           */
 298          this.setAttributeConfig('initialPage', {
 299              value     : 1,
 300              validator : Paginator.isNumeric,
 301              setter    : Paginator.toNumber
 302          });
 303  
 304          /**
 305           * Template used to render controls.  The string will be used as
 306           * innerHTML on all specified container nodes.  Bracketed keys
 307           * (e.g. {pageLinks}) in the string will be replaced with an instance
 308           * of the so named ui component.
 309           * @see Paginator.TEMPLATE_DEFAULT
 310           * @see Paginator.TEMPLATE_ROWS_PER_PAGE
 311           * @attribute template
 312           * @type string
 313           */
 314          this.setAttributeConfig('template', {
 315              value : Paginator.TEMPLATE_DEFAULT,
 316              validator : isString
 317          });
 318  
 319          /**
 320           * Class assigned to the element(s) containing pagination controls.
 321           * @attribute containerClass
 322           * @type string
 323           * @default 'yui-pg-container'
 324           */
 325          this.setAttributeConfig('containerClass', {
 326              value : 'yui-pg-container',
 327              validator : isString
 328          });
 329  
 330          /**
 331           * Display pagination controls even when there is only one page.  Set
 332           * to false to forgo rendering and/or hide the containers when there
 333           * is only one page of data.  Note if you are using the rowsPerPage
 334           * dropdown ui component, visibility will be maintained as long as the
 335           * number of records exceeds the smallest page size.
 336           * @attribute alwaysVisible
 337           * @type boolean
 338           * @default true
 339           */
 340          this.setAttributeConfig('alwaysVisible', {
 341              value : true,
 342              validator : lang.isBoolean
 343          });
 344  
 345          /**
 346           * Update the UI immediately upon interaction.  If false, changeRequest
 347           * subscribers or other external code will need to explicitly set the
 348           * new values in the paginator to trigger repaint.
 349           * @attribute updateOnChange
 350           * @type boolean
 351           * @default false
 352           * @deprecated use changeRequest listener that calls setState
 353           */
 354          this.setAttributeConfig('updateOnChange', {
 355              value     : false,
 356              validator : lang.isBoolean
 357          });
 358  
 359  
 360  
 361          // Read only attributes
 362  
 363          /**
 364           * Unique id assigned to this instance
 365           * @attribute id
 366           * @type integer
 367           * @final
 368           */
 369          this.setAttributeConfig('id', {
 370              value    : Paginator.id++,
 371              readOnly : true
 372          });
 373  
 374          /**
 375           * Indicator of whether the DOM nodes have been initially created
 376           * @attribute rendered
 377           * @type boolean
 378           * @final
 379           */
 380          this.setAttributeConfig('rendered', {
 381              value    : false,
 382              readOnly : true
 383          });
 384  
 385      },
 386  
 387      /**
 388       * Initialize registered ui components onto this instance.
 389       * @method initUIComponents
 390       * @private
 391       */
 392      initUIComponents : function () {
 393          var ui = Paginator.ui,
 394              name,UIComp;
 395          for (name in ui) {
 396              if (ui.hasOwnProperty(name)) {
 397                  UIComp = ui[name];
 398                  if (isObject(UIComp) && isFunction(UIComp.init)) {
 399                      UIComp.init(this);
 400                  }
 401              }
 402          }
 403      },
 404  
 405      /**
 406       * Initialize this instance's CustomEvents.
 407       * @method initEvents
 408       * @private
 409       */
 410      initEvents : function () {
 411          /**
 412           * Event fired when the Paginator is initially rendered
 413           * @event render
 414           */
 415          this.createEvent('render');
 416  
 417          /**
 418           * Event fired when the Paginator is initially rendered
 419           * @event rendered
 420           * @deprecated use render event
 421           */
 422          this.createEvent('rendered'); // backward compatibility
 423  
 424          /**
 425           * Event fired when a change in pagination values is requested,
 426           * either by interacting with the various ui components or via the
 427           * setStartIndex(n) etc APIs.
 428           * Subscribers will receive the proposed state as the first parameter.
 429           * The proposed state object will contain the following keys:
 430           * <ul>
 431           *   <li>paginator - the Paginator instance</li>
 432           *   <li>page</li>
 433           *   <li>totalRecords</li>
 434           *   <li>recordOffset - index of the first record on the new page</li>
 435           *   <li>rowsPerPage</li>
 436           *   <li>records - array containing [start index, end index] for the records on the new page</li>
 437           *   <li>before - object literal with all these keys for the current state</li>
 438           * </ul>
 439           * @event changeRequest
 440           */
 441          this.createEvent('changeRequest');
 442  
 443          /**
 444           * Event fired when attribute changes have resulted in the calculated
 445           * current page changing.
 446           * @event pageChange
 447           */
 448          this.createEvent('pageChange');
 449  
 450          /**
 451           * Event that fires before the destroy event.
 452           * @event beforeDestroy
 453           */
 454          this.createEvent('beforeDestroy');
 455  
 456          /**
 457           * Event used to trigger cleanup of ui components
 458           * @event destroy
 459           */
 460          this.createEvent('destroy');
 461  
 462          this._selfSubscribe();
 463      },
 464  
 465      /**
 466       * Subscribes to instance attribute change events to automate certain
 467       * behaviors.
 468       * @method _selfSubscribe
 469       * @protected
 470       */
 471      _selfSubscribe : function () {
 472          // Listen for changes to totalRecords and alwaysVisible 
 473          this.subscribe('totalRecordsChange',this.updateVisibility,this,true);
 474          this.subscribe('alwaysVisibleChange',this.updateVisibility,this,true);
 475  
 476          // Fire the pageChange event when appropriate
 477          this.subscribe('totalRecordsChange',this._handleStateChange,this,true);
 478          this.subscribe('recordOffsetChange',this._handleStateChange,this,true);
 479          this.subscribe('rowsPerPageChange',this._handleStateChange,this,true);
 480  
 481          // Update recordOffset when totalRecords is reduced below
 482          this.subscribe('totalRecordsChange',this._syncRecordOffset,this,true);
 483      },
 484  
 485      /**
 486       * Sets recordOffset to the starting index of the previous page when
 487       * totalRecords is reduced below the current recordOffset.
 488       * @method _syncRecordOffset
 489       * @param e {Event} totalRecordsChange event
 490       * @protected
 491       */
 492      _syncRecordOffset : function (e) {
 493          var v = e.newValue,rpp,state;
 494          if (e.prevValue !== v) {
 495              if (v !== Paginator.VALUE_UNLIMITED) {
 496                  rpp = this.get('rowsPerPage');
 497  
 498                  if (rpp && this.get('recordOffset') >= v) {
 499                      state = this.getState({
 500                          totalRecords : e.prevValue,
 501                          recordOffset : this.get('recordOffset')
 502                      });
 503  
 504                      this.set('recordOffset', state.before.recordOffset);
 505                      this._firePageChange(state);
 506                  }
 507              }
 508          }
 509      },
 510  
 511      /**
 512       * Fires the pageChange event when the state attributes have changed in
 513       * such a way as to locate the current recordOffset on a new page.
 514       * @method _handleStateChange
 515       * @param e {Event} the attribute change event
 516       * @protected
 517       */
 518      _handleStateChange : function (e) {
 519          if (e.prevValue !== e.newValue) {
 520              var change = this._state || {},
 521                  state;
 522  
 523              change[e.type.replace(/Change$/,'')] = e.prevValue;
 524              state = this.getState(change);
 525  
 526              if (state.page !== state.before.page) {
 527                  if (this._batch) {
 528                      this._pageChanged = true;
 529                  } else {
 530                      this._firePageChange(state);
 531                  }
 532              }
 533          }
 534      },
 535  
 536      /**
 537       * Fires a pageChange event in the form of a standard attribute change
 538       * event with additional properties prevState and newState.
 539       * @method _firePageChange
 540       * @param state {Object} the result of getState(oldState)
 541       * @protected
 542       */
 543      _firePageChange : function (state) {
 544          if (isObject(state)) {
 545              var current = state.before;
 546              delete state.before;
 547              this.fireEvent('pageChange',{
 548                  type      : 'pageChange',
 549                  prevValue : state.page,
 550                  newValue  : current.page,
 551                  prevState : state,
 552                  newState  : current
 553              });
 554          }
 555      },
 556  
 557      /**
 558       * Render the pagination controls per the format attribute into the
 559       * specified container nodes.
 560       * @method render
 561       * @return the Paginator instance
 562       * @chainable
 563       */
 564      render : function () {
 565          if (this.get('rendered')) {
 566              return this;
 567          }
 568  
 569          var template = this.get('template'),
 570              state    = this.getState(),
 571              // ex. yui-pg0-1 (first paginator, second container)
 572              id_base  = Paginator.ID_BASE + this.get('id') + '-',
 573              i, len;
 574  
 575          // Assemble the containers, keeping them hidden
 576          for (i = 0, len = this._containers.length; i < len; ++i) {
 577              this._renderTemplate(this._containers[i],template,id_base+i,true);
 578          }
 579  
 580          // Show the containers if appropriate
 581          this.updateVisibility();
 582  
 583          // Set render attribute manually to support its readOnly contract
 584          if (this._containers.length) {
 585              this.setAttributeConfig('rendered', { value: true });
 586  
 587              this.fireEvent('render', state);
 588              // For backward compatibility
 589              this.fireEvent('rendered', state);
 590          }
 591  
 592          return this;
 593      },
 594  
 595      /**
 596       * Creates the individual ui components and renders them into a container.
 597       *
 598       * @method _renderTemplate
 599       * @param container {HTMLElement} where to add the ui components
 600       * @param template {String} the template to use as a guide for rendering
 601       * @param id_base {String} id base for the container's ui components
 602       * @param hide {Boolean} leave the container hidden after assembly
 603       * @protected
 604       */
 605      _renderTemplate : function (container, template, id_base, hide) {
 606          var containerClass = this.get('containerClass'),
 607              markers, i, len;
 608  
 609          if (!container) {
 610              return;
 611          }
 612  
 613          // Hide the container while its contents are rendered
 614          Dom.setStyle(container,'display','none');
 615  
 616          Dom.addClass(container, containerClass);
 617  
 618          // Place the template innerHTML, adding marker spans to the template
 619          // html to indicate drop zones for ui components
 620          container.innerHTML = template.replace(/\{([a-z0-9_ \-]+)\}/gi,
 621              '<span class="yui-pg-ui yui-pg-ui-$1"></span>');
 622  
 623          // Replace each marker with the ui component's render() output
 624          markers = Dom.getElementsByClassName('yui-pg-ui','span',container);
 625  
 626          for (i = 0, len = markers.length; i < len; ++i) {
 627              this.renderUIComponent(markers[i], id_base);
 628          }
 629  
 630          if (!hide) {
 631              // Show the container allowing page reflow
 632              Dom.setStyle(container,'display','');
 633          }
 634      },
 635  
 636      /**
 637       * Replaces a marker node with a rendered UI component, determined by the
 638       * yui-pg-ui-(UI component class name) in the marker's className. e.g.
 639       * yui-pg-ui-PageLinks => new YAHOO.widget.Paginator.ui.PageLinks(this)
 640       *
 641       * @method renderUIComponent
 642       * @param marker {HTMLElement} the marker node to replace
 643       * @param id_base {String} string base the component's generated id
 644       * @return the Paginator instance
 645       * @chainable
 646       */
 647      renderUIComponent : function (marker, id_base) {
 648          var par    = marker.parentNode,
 649              name   = /yui-pg-ui-(\w+)/.exec(marker.className),
 650              UIComp = name && Paginator.ui[name[1]],
 651              comp;
 652  
 653          if (isFunction(UIComp)) {
 654              comp = new UIComp(this);
 655              if (isFunction(comp.render)) {
 656                  par.replaceChild(comp.render(id_base),marker);
 657              }
 658          }
 659  
 660          return this;
 661      },
 662  
 663      /**
 664       * Removes controls from the page and unhooks events.
 665       * @method destroy
 666       */
 667      destroy : function () {
 668          this.fireEvent('beforeDestroy');
 669          this.fireEvent('destroy');
 670  
 671          this.setAttributeConfig('rendered',{value:false});
 672          this.unsubscribeAll();
 673      },
 674  
 675      /**
 676       * Hides the containers if there is only one page of data and attribute
 677       * alwaysVisible is false.  Conversely, it displays the containers if either
 678       * there is more than one page worth of data or alwaysVisible is turned on.
 679       * @method updateVisibility
 680       */
 681      updateVisibility : function (e) {
 682          var alwaysVisible = this.get('alwaysVisible'),
 683              totalRecords, visible, rpp, rppOptions, i, len, opt;
 684  
 685          if (!e || e.type === 'alwaysVisibleChange' || !alwaysVisible) {
 686              totalRecords = this.get('totalRecords');
 687              visible      = true;
 688              rpp          = this.get('rowsPerPage');
 689              rppOptions   = this.get('rowsPerPageOptions');
 690  
 691              if (isArray(rppOptions)) {
 692                  for (i = 0, len = rppOptions.length; i < len; ++i) {
 693                      opt = rppOptions[i];
 694                      // account for value 'all'
 695                      if (lang.isNumber(opt || opt.value)) {
 696                          rpp = Math.min(rpp, (opt.value || opt));
 697                      }
 698                  }
 699              }
 700  
 701              if (totalRecords !== Paginator.VALUE_UNLIMITED &&
 702                  totalRecords <= rpp) {
 703                  visible = false;
 704              }
 705  
 706              visible = visible || alwaysVisible;
 707  
 708              for (i = 0, len = this._containers.length; i < len; ++i) {
 709                  Dom.setStyle(this._containers[i],'display',
 710                      visible ? '' : 'none');
 711              }
 712          }
 713      },
 714  
 715  
 716  
 717  
 718      /**
 719       * Get the configured container nodes
 720       * @method getContainerNodes
 721       * @return {Array} array of HTMLElement nodes
 722       */
 723      getContainerNodes : function () {
 724          return this._containers;
 725      },
 726  
 727      /**
 728       * Get the total number of pages in the data set according to the current
 729       * rowsPerPage and totalRecords values.  If totalRecords is not set, or
 730       * set to YAHOO.widget.Paginator.VALUE_UNLIMITED, returns
 731       * YAHOO.widget.Paginator.VALUE_UNLIMITED.
 732       * @method getTotalPages
 733       * @return {number}
 734       */
 735      getTotalPages : function () {
 736          var records = this.get('totalRecords'),
 737              perPage = this.get('rowsPerPage');
 738  
 739          // rowsPerPage not set.  Can't calculate
 740          if (!perPage) {
 741              return null;
 742          }
 743  
 744          if (records === Paginator.VALUE_UNLIMITED) {
 745              return Paginator.VALUE_UNLIMITED;
 746          }
 747  
 748          return Math.ceil(records/perPage);
 749      },
 750  
 751      /**
 752       * Does the requested page have any records?
 753       * @method hasPage
 754       * @param page {number} the page in question
 755       * @return {boolean}
 756       */
 757      hasPage : function (page) {
 758          if (!lang.isNumber(page) || page < 1) {
 759              return false;
 760          }
 761  
 762          var totalPages = this.getTotalPages();
 763  
 764          return (totalPages === Paginator.VALUE_UNLIMITED || totalPages >= page);
 765      },
 766  
 767      /**
 768       * Get the page number corresponding to the current record offset.
 769       * @method getCurrentPage
 770       * @return {number}
 771       */
 772      getCurrentPage : function () {
 773          var perPage = this.get('rowsPerPage');
 774          if (!perPage || !this.get('totalRecords')) {
 775              return 0;
 776          }
 777          return Math.floor(this.get('recordOffset') / perPage) + 1;
 778      },
 779  
 780      /**
 781       * Are there records on the next page?
 782       * @method hasNextPage
 783       * @return {boolean}
 784       */
 785      hasNextPage : function () {
 786          var currentPage = this.getCurrentPage(),
 787              totalPages  = this.getTotalPages();
 788  
 789          return currentPage && (totalPages === Paginator.VALUE_UNLIMITED || currentPage < totalPages);
 790      },
 791  
 792      /**
 793       * Get the page number of the next page, or null if the current page is the
 794       * last page.
 795       * @method getNextPage
 796       * @return {number}
 797       */
 798      getNextPage : function () {
 799          return this.hasNextPage() ? this.getCurrentPage() + 1 : null;
 800      },
 801  
 802      /**
 803       * Is there a page before the current page?
 804       * @method hasPreviousPage
 805       * @return {boolean}
 806       */
 807      hasPreviousPage : function () {
 808          return (this.getCurrentPage() > 1);
 809      },
 810  
 811      /**
 812       * Get the page number of the previous page, or null if the current page
 813       * is the first page.
 814       * @method getPreviousPage
 815       * @return {number}
 816       */
 817      getPreviousPage : function () {
 818          return (this.hasPreviousPage() ? this.getCurrentPage() - 1 : 1);
 819      },
 820  
 821      /**
 822       * Get the start and end record indexes of the specified page.
 823       * @method getPageRecords
 824       * @param page {number} (optional) The page (current page if not specified)
 825       * @return {Array} [start_index, end_index]
 826       */
 827      getPageRecords : function (page) {
 828          if (!lang.isNumber(page)) {
 829              page = this.getCurrentPage();
 830          }
 831  
 832          var perPage = this.get('rowsPerPage'),
 833              records = this.get('totalRecords'),
 834              start, end;
 835  
 836          if (!page || !perPage) {
 837              return null;
 838          }
 839  
 840          start = (page - 1) * perPage;
 841          if (records !== Paginator.VALUE_UNLIMITED) {
 842              if (start >= records) {
 843                  return null;
 844              }
 845              end = Math.min(start + perPage, records) - 1;
 846          } else {
 847              end = start + perPage - 1;
 848          }
 849  
 850          return [start,end];
 851      },
 852  
 853      /**
 854       * Set the current page to the provided page number if possible.
 855       * @method setPage
 856       * @param newPage {number} the new page number
 857       * @param silent {boolean} whether to forcibly avoid firing the
 858       * changeRequest event
 859       */
 860      setPage : function (page,silent) {
 861          if (this.hasPage(page) && page !== this.getCurrentPage()) {
 862              if (this.get('updateOnChange') || silent) {
 863                  this.set('recordOffset', (page - 1) * this.get('rowsPerPage'));
 864              } else {
 865                  this.fireEvent('changeRequest',this.getState({'page':page}));
 866              }
 867          }
 868      },
 869  
 870      /**
 871       * Get the number of rows per page.
 872       * @method getRowsPerPage
 873       * @return {number} the current setting of the rowsPerPage attribute
 874       */
 875      getRowsPerPage : function () {
 876          return this.get('rowsPerPage');
 877      },
 878  
 879      /**
 880       * Set the number of rows per page.
 881       * @method setRowsPerPage
 882       * @param rpp {number} the new number of rows per page
 883       * @param silent {boolean} whether to forcibly avoid firing the
 884       * changeRequest event
 885       */
 886      setRowsPerPage : function (rpp,silent) {
 887          if (Paginator.isNumeric(rpp) && +rpp > 0 &&
 888              +rpp !== this.get('rowsPerPage')) {
 889              if (this.get('updateOnChange') || silent) {
 890                  this.set('rowsPerPage',rpp);
 891              } else {
 892                  this.fireEvent('changeRequest',
 893                      this.getState({'rowsPerPage':+rpp}));
 894              }
 895          }
 896      },
 897  
 898      /**
 899       * Get the total number of records.
 900       * @method getTotalRecords
 901       * @return {number} the current setting of totalRecords attribute
 902       */
 903      getTotalRecords : function () {
 904          return this.get('totalRecords');
 905      },
 906  
 907      /**
 908       * Set the total number of records.
 909       * @method setTotalRecords
 910       * @param total {number} the new total number of records
 911       * @param silent {boolean} whether to forcibly avoid firing the changeRequest event
 912       */
 913      setTotalRecords : function (total,silent) {
 914          if (Paginator.isNumeric(total) && +total >= 0 &&
 915              +total !== this.get('totalRecords')) {
 916              if (this.get('updateOnChange') || silent) {
 917                  this.set('totalRecords',total);
 918              } else {
 919                  this.fireEvent('changeRequest',
 920                      this.getState({'totalRecords':+total}));
 921              }
 922          }
 923      },
 924  
 925      /**
 926       * Get the index of the first record on the current page
 927       * @method getStartIndex
 928       * @return {number} the index of the first record on the current page
 929       */
 930      getStartIndex : function () {
 931          return this.get('recordOffset');
 932      },
 933  
 934      /**
 935       * Move the record offset to a new starting index.  This will likely cause
 936       * the calculated current page to change.  You should probably use setPage.
 937       * @method setStartIndex
 938       * @param offset {number} the new record offset
 939       * @param silent {boolean} whether to forcibly avoid firing the changeRequest event
 940       */
 941      setStartIndex : function (offset,silent) {
 942          if (Paginator.isNumeric(offset) && +offset >= 0 &&
 943              +offset !== this.get('recordOffset')) {
 944              if (this.get('updateOnChange') || silent) {
 945                  this.set('recordOffset',offset);
 946              } else {
 947                  this.fireEvent('changeRequest',
 948                      this.getState({'recordOffset':+offset}));
 949              }
 950          }
 951      },
 952  
 953      /**
 954       * Get an object literal describing the current state of the paginator.  If
 955       * an object literal of proposed values is passed, the proposed state will
 956       * be returned as an object literal with the following keys:
 957       * <ul>
 958       * <li>paginator - instance of the Paginator</li>
 959       * <li>page - number</li>
 960       * <li>totalRecords - number</li>
 961       * <li>recordOffset - number</li>
 962       * <li>rowsPerPage - number</li>
 963       * <li>records - [ start_index, end_index ]</li>
 964       * <li>before - (OPTIONAL) { state object literal for current state }</li>
 965       * </ul>
 966       * @method getState
 967       * @return {object}
 968       * @param changes {object} OPTIONAL object literal with proposed values
 969       * Supported change keys include:
 970       * <ul>
 971       * <li>rowsPerPage</li>
 972       * <li>totalRecords</li>
 973       * <li>recordOffset OR</li>
 974       * <li>page</li>
 975       * </ul>
 976       */
 977      getState : function (changes) {
 978          var UNLIMITED = Paginator.VALUE_UNLIMITED,
 979              M = Math, max = M.max, ceil = M.ceil,
 980              currentState, state, offset;
 981  
 982          function normalizeOffset(offset,total,rpp) {
 983              if (offset <= 0 || total === 0) {
 984                  return 0;
 985              }
 986              if (total === UNLIMITED || total > offset) {
 987                  return offset - (offset % rpp);
 988              }
 989              return total - (total % rpp || rpp);
 990          }
 991  
 992          currentState = {
 993              paginator    : this,
 994              totalRecords : this.get('totalRecords'),
 995              rowsPerPage  : this.get('rowsPerPage'),
 996              records      : this.getPageRecords()
 997          };
 998          currentState.recordOffset = normalizeOffset(
 999                                          this.get('recordOffset'),
1000                                          currentState.totalRecords,
1001                                          currentState.rowsPerPage);
1002          currentState.page = ceil(currentState.recordOffset /
1003                                   currentState.rowsPerPage) + 1;
1004  
1005          if (!changes) {
1006              return currentState;
1007          }
1008  
1009          state = {
1010              paginator    : this,
1011              before       : currentState,
1012  
1013              rowsPerPage  : changes.rowsPerPage || currentState.rowsPerPage,
1014              totalRecords : (Paginator.isNumeric(changes.totalRecords) ?
1015                                  max(changes.totalRecords,UNLIMITED) :
1016                                  +currentState.totalRecords)
1017          };
1018  
1019          if (state.totalRecords === 0) {
1020              state.recordOffset =
1021              state.page         = 0;
1022          } else {
1023              offset = Paginator.isNumeric(changes.page) ?
1024                          (changes.page - 1) * state.rowsPerPage :
1025                          Paginator.isNumeric(changes.recordOffset) ?
1026                              +changes.recordOffset :
1027                              currentState.recordOffset;
1028  
1029              state.recordOffset = normalizeOffset(offset,
1030                                      state.totalRecords,
1031                                      state.rowsPerPage);
1032  
1033              state.page = ceil(state.recordOffset / state.rowsPerPage) + 1;
1034          }
1035  
1036          state.records = [ state.recordOffset,
1037                            state.recordOffset + state.rowsPerPage - 1 ];
1038  
1039          // limit upper index to totalRecords - 1
1040          if (state.totalRecords !== UNLIMITED &&
1041              state.recordOffset < state.totalRecords && state.records &&
1042              state.records[1] > state.totalRecords - 1) {
1043              state.records[1] = state.totalRecords - 1;
1044          }
1045  
1046          return state;
1047      },
1048  
1049      /**
1050       * Convenience method to facilitate setting state attributes rowsPerPage,
1051       * totalRecords, recordOffset in batch.  Also supports calculating
1052       * recordOffset from state.page if state.recordOffset is not provided.
1053       * Fires only a single pageChange event, if appropriate.
1054       * This will not fire a changeRequest event.
1055       * @method setState
1056       * @param state {Object} Object literal of attribute:value pairs to set
1057       */
1058      setState : function (state) {
1059          if (isObject(state)) {
1060              // get flux state based on current state with before state as well
1061              this._state = this.getState({});
1062  
1063              // use just the state props from the input obj
1064              state = {
1065                  page         : state.page,
1066                  rowsPerPage  : state.rowsPerPage,
1067                  totalRecords : state.totalRecords,
1068                  recordOffset : state.recordOffset
1069              };
1070  
1071              // calculate recordOffset from page if recordOffset not specified.
1072              // not using lang.isNumber for support of numeric strings
1073              if (state.page && state.recordOffset === undefined) {
1074                  state.recordOffset = (state.page - 1) *
1075                      (state.rowsPerPage || this.get('rowsPerPage'));
1076              }
1077  
1078              this._batch = true;
1079              this._pageChanged = false;
1080  
1081              for (var k in state) {
1082                  if (state.hasOwnProperty(k) && this._configs.hasOwnProperty(k)) {
1083                      this.set(k,state[k]);
1084                  }
1085              }
1086  
1087              this._batch = false;
1088              
1089              if (this._pageChanged) {
1090                  this._pageChanged = false;
1091  
1092                  this._firePageChange(this.getState(this._state));
1093              }
1094          }
1095      }
1096  };
1097  
1098  lang.augmentProto(Paginator, YAHOO.util.AttributeProvider);
1099  
1100  YAHOO.widget.Paginator = Paginator;
1101  })();
1102  (function () {
1103  
1104  var Paginator = YAHOO.widget.Paginator,
1105      l         = YAHOO.lang,
1106      setId     = YAHOO.util.Dom.generateId;
1107  
1108  /**
1109   * ui Component to generate the textual report of current pagination status.
1110   * E.g. "Now viewing page 1 of 13".
1111   *
1112   * @namespace YAHOO.widget.Paginator.ui
1113   * @class CurrentPageReport
1114   * @for YAHOO.widget.Paginator
1115   *
1116   * @constructor
1117   * @param p {Pagintor} Paginator instance to attach to
1118   */
1119  Paginator.ui.CurrentPageReport = function (p) {
1120      this.paginator = p;
1121  
1122      p.subscribe('recordOffsetChange', this.update,this,true);
1123      p.subscribe('rowsPerPageChange', this.update,this,true);
1124      p.subscribe('totalRecordsChange',this.update,this,true);
1125      p.subscribe('pageReportTemplateChange', this.update,this,true);
1126      p.subscribe('destroy',this.destroy,this,true);
1127  
1128      //TODO: make this work
1129      p.subscribe('pageReportClassChange', this.update,this,true);
1130  };
1131  
1132  /**
1133   * Decorates Paginator instances with new attributes. Called during
1134   * Paginator instantiation.
1135   * @method init
1136   * @param p {Paginator} Paginator instance to decorate
1137   * @static
1138   */
1139  Paginator.ui.CurrentPageReport.init = function (p) {
1140  
1141      /**
1142       * CSS class assigned to the span containing the info.
1143       * @attribute pageReportClass
1144       * @default 'yui-pg-current'
1145       */
1146      p.setAttributeConfig('pageReportClass', {
1147          value : 'yui-pg-current',
1148          validator : l.isString
1149      });
1150  
1151      /**
1152       * Used as innerHTML for the span.  Place holders in the form of {name}
1153       * will be replaced with the so named value from the key:value map
1154       * generated by the function held in the pageReportValueGenerator attribute.
1155       * @attribute pageReportTemplate
1156       * @default '({currentPage} of {totalPages})'
1157       * @see pageReportValueGenerator attribute
1158       */
1159      p.setAttributeConfig('pageReportTemplate', {
1160          value : '({currentPage} of {totalPages})',
1161          validator : l.isString
1162      });
1163  
1164      /**
1165       * Function to generate the value map used to populate the
1166       * pageReportTemplate.  The function is passed the Paginator instance as a
1167       * parameter.  The default function returns a map with the following keys:
1168       * <ul>
1169       * <li>currentPage</li>
1170       * <li>totalPages</li>
1171       * <li>startIndex</li>
1172       * <li>endIndex</li>
1173       * <li>startRecord</li>
1174       * <li>endRecord</li>
1175       * <li>totalRecords</li>
1176       * </ul>
1177       * @attribute pageReportValueGenarator
1178       */
1179      p.setAttributeConfig('pageReportValueGenerator', {
1180          value : function (paginator) {
1181              var curPage = paginator.getCurrentPage(),
1182                  records = paginator.getPageRecords();
1183  
1184              return {
1185                  'currentPage' : records ? curPage : 0,
1186                  'totalPages'  : paginator.getTotalPages(),
1187                  'startIndex'  : records ? records[0] : 0,
1188                  'endIndex'    : records ? records[1] : 0,
1189                  'startRecord' : records ? records[0] + 1 : 0,
1190                  'endRecord'   : records ? records[1] + 1 : 0,
1191                  'totalRecords': paginator.get('totalRecords')
1192              };
1193          },
1194          validator : l.isFunction
1195      });
1196  };
1197  
1198  /**
1199   * Replace place holders in a string with the named values found in an
1200   * object literal.
1201   * @static
1202   * @method sprintf
1203   * @param template {string} The content string containing place holders
1204   * @param values {object} The key:value pairs used to replace the place holders
1205   * @return {string}
1206   */
1207  Paginator.ui.CurrentPageReport.sprintf = function (template, values) {
1208      return template.replace(/\{([\w\s\-]+)\}/g, function (x,key) {
1209              return (key in values) ? values[key] : '';
1210          });
1211  };
1212  
1213  Paginator.ui.CurrentPageReport.prototype = {
1214  
1215      /**
1216       * Span node containing the formatted info
1217       * @property span
1218       * @type HTMLElement
1219       * @private
1220       */
1221      span : null,
1222  
1223  
1224      /**
1225       * Generate the span containing info formatted per the pageReportTemplate
1226       * attribute.
1227       * @method render
1228       * @param id_base {string} used to create unique ids for generated nodes
1229       * @return {HTMLElement}
1230       */
1231      render : function (id_base) {
1232          this.span = document.createElement('span');
1233          this.span.className = this.paginator.get('pageReportClass');
1234          setId(this.span, id_base + '-page-report');
1235          this.update();
1236          
1237          return this.span;
1238      },
1239      
1240      /**
1241       * Regenerate the content of the span if appropriate. Calls
1242       * CurrentPageReport.sprintf with the value of the pageReportTemplate
1243       * attribute and the value map returned from pageReportValueGenerator
1244       * function.
1245       * @method update
1246       * @param e {CustomEvent} The calling change event
1247       */
1248      update : function (e) {
1249          if (e && e.prevValue === e.newValue) {
1250              return;
1251          }
1252  
1253          this.span.innerHTML = Paginator.ui.CurrentPageReport.sprintf(
1254              this.paginator.get('pageReportTemplate'),
1255              this.paginator.get('pageReportValueGenerator')(this.paginator));
1256      },
1257  
1258      /**
1259       * Removes the link/span node and clears event listeners
1260       * removal.
1261       * @method destroy
1262       * @private
1263       */
1264      destroy : function () {
1265          this.span.parentNode.removeChild(this.span);
1266          this.span = null;
1267      }
1268  
1269  };
1270  
1271  })();
1272  (function () {
1273  
1274  var Paginator = YAHOO.widget.Paginator,
1275      l         = YAHOO.lang,
1276      setId     = YAHOO.util.Dom.generateId;
1277  
1278  /**
1279   * ui Component to generate the page links
1280   *
1281   * @namespace YAHOO.widget.Paginator.ui
1282   * @class PageLinks
1283   * @for YAHOO.widget.Paginator
1284   *
1285   * @constructor
1286   * @param p {Pagintor} Paginator instance to attach to
1287   */
1288  Paginator.ui.PageLinks = function (p) {
1289      this.paginator = p;
1290  
1291      p.subscribe('recordOffsetChange',this.update,this,true);
1292      p.subscribe('rowsPerPageChange',this.update,this,true);
1293      p.subscribe('totalRecordsChange',this.update,this,true);
1294      p.subscribe('pageLinksChange',   this.rebuild,this,true);
1295      p.subscribe('pageLinkClassChange', this.rebuild,this,true);
1296      p.subscribe('currentPageClassChange', this.rebuild,this,true);
1297      p.subscribe('destroy',this.destroy,this,true);
1298  
1299      //TODO: Make this work
1300      p.subscribe('pageLinksContainerClassChange', this.rebuild,this,true);
1301  };
1302  
1303  /**
1304   * Decorates Paginator instances with new attributes. Called during
1305   * Paginator instantiation.
1306   * @method init
1307   * @param p {Paginator} Paginator instance to decorate
1308   * @static
1309   */
1310  Paginator.ui.PageLinks.init = function (p) {
1311  
1312      /**
1313       * CSS class assigned to each page link/span.
1314       * @attribute pageLinkClass
1315       * @default 'yui-pg-page'
1316       */
1317      p.setAttributeConfig('pageLinkClass', {
1318          value : 'yui-pg-page',
1319          validator : l.isString
1320      });
1321  
1322      /**
1323       * CSS class assigned to the current page span.
1324       * @attribute currentPageClass
1325       * @default 'yui-pg-current-page'
1326       */
1327      p.setAttributeConfig('currentPageClass', {
1328          value : 'yui-pg-current-page',
1329          validator : l.isString
1330      });
1331  
1332      /**
1333       * CSS class assigned to the span containing the page links.
1334       * @attribute pageLinksContainerClass
1335       * @default 'yui-pg-pages'
1336       */
1337      p.setAttributeConfig('pageLinksContainerClass', {
1338          value : 'yui-pg-pages',
1339          validator : l.isString
1340      });
1341  
1342      /**
1343       * Maximum number of page links to display at one time.
1344       * @attribute pageLinks
1345       * @default 10
1346       */
1347      p.setAttributeConfig('pageLinks', {
1348          value : 10,
1349          validator : Paginator.isNumeric
1350      });
1351  
1352      /**
1353       * Function used generate the innerHTML for each page link/span.  The
1354       * function receives as parameters the page number and a reference to the
1355       * paginator object.
1356       * @attribute pageLabelBuilder
1357       * @default function (page, paginator) { return page; }
1358       */
1359      p.setAttributeConfig('pageLabelBuilder', {
1360          value : function (page, paginator) { return page; },
1361          validator : l.isFunction
1362      });
1363  
1364      /**
1365       * Function used generate the title for each page link.  The
1366       * function receives as parameters the page number and a reference to the
1367       * paginator object.
1368       * @attribute pageTitleBuilder
1369       * @default function (page, paginator) { return page; }
1370       */
1371      p.setAttributeConfig('pageTitleBuilder', {
1372          value : function (page, paginator) { return "Page " + page; },
1373          validator : l.isFunction
1374      });
1375  };
1376  
1377  /**
1378   * Calculates start and end page numbers given a current page, attempting
1379   * to keep the current page in the middle
1380   * @static
1381   * @method calculateRange
1382   * @param {int} currentPage  The current page
1383   * @param {int} totalPages   (optional) Maximum number of pages
1384   * @param {int} numPages     (optional) Preferred number of pages in range
1385   * @return {Array} [start_page_number, end_page_number]
1386   */
1387  Paginator.ui.PageLinks.calculateRange = function (currentPage,totalPages,numPages) {
1388      var UNLIMITED = Paginator.VALUE_UNLIMITED,
1389          start, end, delta;
1390  
1391      // Either has no pages, or unlimited pages.  Show none.
1392      if (!currentPage || numPages === 0 || totalPages === 0 ||
1393          (totalPages === UNLIMITED && numPages === UNLIMITED)) {
1394          return [0,-1];
1395      }
1396  
1397      // Limit requested pageLinks if there are fewer totalPages
1398      if (totalPages !== UNLIMITED) {
1399          numPages = numPages === UNLIMITED ?
1400                      totalPages :
1401                      Math.min(numPages,totalPages);
1402      }
1403  
1404      // Determine start and end, trying to keep current in the middle
1405      start = Math.max(1,Math.ceil(currentPage - (numPages/2)));
1406      if (totalPages === UNLIMITED) {
1407          end = start + numPages - 1;
1408      } else {
1409          end = Math.min(totalPages, start + numPages - 1);
1410      }
1411  
1412      // Adjust the start index when approaching the last page
1413      delta = numPages - (end - start + 1);
1414      start = Math.max(1, start - delta);
1415  
1416      return [start,end];
1417  };
1418  
1419  
1420  Paginator.ui.PageLinks.prototype = {
1421  
1422      /**
1423       * Current page
1424       * @property current
1425       * @type number
1426       * @private
1427       */
1428      current     : 0,
1429  
1430      /**
1431       * Span node containing the page links
1432       * @property container
1433       * @type HTMLElement
1434       * @private
1435       */
1436      container   : null,
1437  
1438  
1439      /**
1440       * Generate the nodes and return the container node containing page links
1441       * appropriate to the current pagination state.
1442       * @method render
1443       * @param id_base {string} used to create unique ids for generated nodes
1444       * @return {HTMLElement}
1445       */
1446      render : function (id_base) {
1447          var p = this.paginator;
1448  
1449          // Set up container
1450          this.container = document.createElement('span');
1451          setId(this.container, id_base + '-pages');
1452          this.container.className = p.get('pageLinksContainerClass');
1453          YAHOO.util.Event.on(this.container,'click',this.onClick,this,true);
1454  
1455          // Call update, flagging a need to rebuild
1456          this.update({newValue : null, rebuild : true});
1457  
1458          return this.container;
1459      },
1460  
1461      /**
1462       * Update the links if appropriate
1463       * @method update
1464       * @param e {CustomEvent} The calling change event
1465       */
1466      update : function (e) {
1467          if (e && e.prevValue === e.newValue) {
1468              return;
1469          }
1470  
1471          var p           = this.paginator,
1472              currentPage = p.getCurrentPage();
1473  
1474          // Replace content if there's been a change
1475          if (this.current !== currentPage || !currentPage || e.rebuild) {
1476              var labelBuilder = p.get('pageLabelBuilder'),
1477                  titleBuilder = p.get('pageTitleBuilder'),
1478                  range        = Paginator.ui.PageLinks.calculateRange(
1479                                  currentPage,
1480                                  p.getTotalPages(),
1481                                  p.get('pageLinks')),
1482                  start        = range[0],
1483                  end          = range[1],
1484                  content      = '',
1485                  linkTemplate,i,spanTemplate;
1486  
1487              linkTemplate = '<a href="#" class="{class}" page="{page}" title="{title}">{label}</a>';
1488              spanTemplate = '<span class="{class}">{label}</span>';
1489              for (i = start; i <= end; ++i) {
1490  
1491                  if (i === currentPage) {
1492                      content += l.substitute(spanTemplate, {
1493                          'class' : p.get('currentPageClass') + ' ' + p.get('pageLinkClass'),
1494                          'label' : labelBuilder(i,p)
1495                      });
1496  
1497                  } else {
1498                      content += l.substitute(linkTemplate, {
1499                          'class' : p.get('pageLinkClass'),
1500                          'page'  : i,
1501                          'label' : labelBuilder(i,p),
1502                          'title' : titleBuilder(i,p)
1503                      });
1504                  }
1505              }
1506  
1507              this.container.innerHTML = content;
1508          }
1509      },
1510  
1511      /**
1512       * Force a rebuild of the page links.
1513       * @method rebuild
1514       * @param e {CustomEvent} The calling change event
1515       */
1516      rebuild     : function (e) {
1517          e.rebuild = true;
1518          this.update(e);
1519      },
1520  
1521      /**
1522       * Removes the page links container node and clears event listeners
1523       * @method destroy
1524       * @private
1525       */
1526      destroy : function () {
1527          YAHOO.util.Event.purgeElement(this.container,true);
1528          this.container.parentNode.removeChild(this.container);
1529          this.container = null;
1530      },
1531  
1532      /**
1533       * Listener for the container's onclick event.  Looks for qualifying link
1534       * clicks, and pulls the page number from the link's page attribute.
1535       * Sends link's page attribute to the Paginator's setPage method.
1536       * @method onClick
1537       * @param e {DOMEvent} The click event
1538       */
1539      onClick : function (e) {
1540          var t = YAHOO.util.Event.getTarget(e);
1541          if (t && YAHOO.util.Dom.hasClass(t,
1542                          this.paginator.get('pageLinkClass'))) {
1543  
1544              YAHOO.util.Event.stopEvent(e);
1545  
1546              this.paginator.setPage(parseInt(t.getAttribute('page'),10));
1547          }
1548      }
1549  
1550  };
1551  
1552  })();
1553  (function () {
1554  
1555  var Paginator = YAHOO.widget.Paginator,
1556      l         = YAHOO.lang,
1557      setId     = YAHOO.util.Dom.generateId;
1558  
1559  /**
1560   * ui Component to generate the link to jump to the first page.
1561   *
1562   * @namespace YAHOO.widget.Paginator.ui
1563   * @class FirstPageLink
1564   * @for YAHOO.widget.Paginator
1565   *
1566   * @constructor
1567   * @param p {Pagintor} Paginator instance to attach to
1568   */
1569  Paginator.ui.FirstPageLink = function (p) {
1570      this.paginator = p;
1571  
1572      p.subscribe('recordOffsetChange',this.update,this,true);
1573      p.subscribe('rowsPerPageChange',this.update,this,true);
1574      p.subscribe('totalRecordsChange',this.update,this,true);
1575      p.subscribe('destroy',this.destroy,this,true);
1576  
1577      // TODO: make this work
1578      p.subscribe('firstPageLinkLabelChange',this.update,this,true);
1579      p.subscribe('firstPageLinkClassChange',this.update,this,true);
1580  };
1581  
1582  /**
1583   * Decorates Paginator instances with new attributes. Called during
1584   * Paginator instantiation.
1585   * @method init
1586   * @param p {Paginator} Paginator instance to decorate
1587   * @static
1588   */
1589  Paginator.ui.FirstPageLink.init = function (p) {
1590  
1591      /**
1592       * Used as innerHTML for the first page link/span.
1593       * @attribute firstPageLinkLabel
1594       * @default '&lt;&lt; first'
1595       */
1596      p.setAttributeConfig('firstPageLinkLabel', {
1597          value : '&lt;&lt; first',
1598          validator : l.isString
1599      });
1600  
1601      /**
1602       * CSS class assigned to the link/span
1603       * @attribute firstPageLinkClass
1604       * @default 'yui-pg-first'
1605       */
1606      p.setAttributeConfig('firstPageLinkClass', {
1607          value : 'yui-pg-first',
1608          validator : l.isString
1609      });
1610  
1611      /**
1612       * Used as title for the first page link.
1613       * @attribute firstPageLinkTitle
1614       * @default 'First Page'
1615       */
1616      p.setAttributeConfig('firstPageLinkTitle', {
1617          value : 'First Page',
1618          validator : l.isString
1619      });
1620  };
1621  
1622  // Instance members and methods
1623  Paginator.ui.FirstPageLink.prototype = {
1624  
1625      /**
1626       * The currently placed HTMLElement node
1627       * @property current
1628       * @type HTMLElement
1629       * @private
1630       */
1631      current   : null,
1632  
1633      /**
1634       * Link node
1635       * @property link
1636       * @type HTMLElement
1637       * @private
1638       */
1639      link      : null,
1640  
1641      /**
1642       * Span node (inactive link)
1643       * @property span
1644       * @type HTMLElement
1645       * @private
1646       */
1647      span      : null,
1648  
1649      /**
1650       * Generate the nodes and return the appropriate node given the current
1651       * pagination state.
1652       * @method render
1653       * @param id_base {string} used to create unique ids for generated nodes
1654       * @return {HTMLElement}
1655       */
1656      render : function (id_base) {
1657          var p     = this.paginator,
1658              c     = p.get('firstPageLinkClass'),
1659              label = p.get('firstPageLinkLabel'),
1660              title = p.get('firstPageLinkTitle');
1661  
1662          this.link     = document.createElement('a');
1663          this.span     = document.createElement('span');
1664  
1665          setId(this.link, id_base + '-first-link');
1666          this.link.href      = '#';
1667          this.link.className = c;
1668          this.link.innerHTML = label;
1669          this.link.title     = title;
1670          YAHOO.util.Event.on(this.link,'click',this.onClick,this,true);
1671  
1672          setId(this.span, id_base + '-first-span');
1673          this.span.className = c;
1674          this.span.innerHTML = label;
1675  
1676          this.current = p.getCurrentPage() > 1 ? this.link : this.span;
1677          return this.current;
1678      },
1679  
1680      /**
1681       * Swap the link and span nodes if appropriate.
1682       * @method update
1683       * @param e {CustomEvent} The calling change event
1684       */
1685      update : function (e) {
1686          if (e && e.prevValue === e.newValue) {
1687              return;
1688          }
1689  
1690          var par = this.current ? this.current.parentNode : null;
1691          if (this.paginator.getCurrentPage() > 1) {
1692              if (par && this.current === this.span) {
1693                  par.replaceChild(this.link,this.current);
1694                  this.current = this.link;
1695              }
1696          } else {
1697              if (par && this.current === this.link) {
1698                  par.replaceChild(this.span,this.current);
1699                  this.current = this.span;
1700              }
1701          }
1702      },
1703  
1704      /**
1705       * Removes the link/span node and clears event listeners
1706       * removal.
1707       * @method destroy
1708       * @private
1709       */
1710      destroy : function () {
1711          YAHOO.util.Event.purgeElement(this.link);
1712          this.current.parentNode.removeChild(this.current);
1713          this.link = this.span = null;
1714      },
1715  
1716      /**
1717       * Listener for the link's onclick event.  Pass new value to setPage method.
1718       * @method onClick
1719       * @param e {DOMEvent} The click event
1720       */
1721      onClick : function (e) {
1722          YAHOO.util.Event.stopEvent(e);
1723          this.paginator.setPage(1);
1724      }
1725  };
1726  
1727  })();
1728  (function () {
1729  
1730  var Paginator = YAHOO.widget.Paginator,
1731      l         = YAHOO.lang,
1732      setId     = YAHOO.util.Dom.generateId;
1733  
1734  /**
1735   * ui Component to generate the link to jump to the last page.
1736   *
1737   * @namespace YAHOO.widget.Paginator.ui
1738   * @class LastPageLink
1739   * @for YAHOO.widget.Paginator
1740   *
1741   * @constructor
1742   * @param p {Pagintor} Paginator instance to attach to
1743   */
1744  Paginator.ui.LastPageLink = function (p) {
1745      this.paginator = p;
1746  
1747      p.subscribe('recordOffsetChange',this.update,this,true);
1748      p.subscribe('rowsPerPageChange',this.update,this,true);
1749      p.subscribe('totalRecordsChange',this.update,this,true);
1750      p.subscribe('destroy',this.destroy,this,true);
1751  
1752      // TODO: make this work
1753      p.subscribe('lastPageLinkLabelChange',this.update,this,true);
1754      p.subscribe('lastPageLinkClassChange', this.update,this,true);
1755  };
1756  
1757  /**
1758   * Decorates Paginator instances with new attributes. Called during
1759   * Paginator instantiation.
1760   * @method init
1761   * @param paginator {Paginator} Paginator instance to decorate
1762   * @static
1763   */
1764  Paginator.ui.LastPageLink.init = function (p) {
1765  
1766      /**
1767       * Used as innerHTML for the last page link/span.
1768       * @attribute lastPageLinkLabel
1769       * @default 'last &gt;&gt;'
1770       */
1771      p.setAttributeConfig('lastPageLinkLabel', {
1772          value : 'last &gt;&gt;',
1773          validator : l.isString
1774      });
1775  
1776      /**
1777       * CSS class assigned to the link/span
1778       * @attribute lastPageLinkClass
1779       * @default 'yui-pg-last'
1780       */
1781      p.setAttributeConfig('lastPageLinkClass', {
1782          value : 'yui-pg-last',
1783          validator : l.isString
1784      });
1785  
1786     /**
1787       * Used as title for the last page link.
1788       * @attribute lastPageLinkTitle
1789       * @default 'Last Page'
1790       */
1791      p.setAttributeConfig('lastPageLinkTitle', {
1792          value : 'Last Page',
1793          validator : l.isString
1794      });
1795  
1796  };
1797  
1798  Paginator.ui.LastPageLink.prototype = {
1799  
1800      /**
1801       * Currently placed HTMLElement node
1802       * @property current
1803       * @type HTMLElement
1804       * @private
1805       */
1806      current   : null,
1807  
1808      /**
1809       * Link HTMLElement node
1810       * @property link
1811       * @type HTMLElement
1812       * @private
1813       */
1814      link      : null,
1815  
1816      /**
1817       * Span node (inactive link)
1818       * @property span
1819       * @type HTMLElement
1820       * @private
1821       */
1822      span      : null,
1823  
1824      /**
1825       * Empty place holder node for when the last page link is inappropriate to
1826       * display in any form (unlimited paging).
1827       * @property na
1828       * @type HTMLElement
1829       * @private
1830       */
1831      na        : null,
1832  
1833  
1834      /**
1835       * Generate the nodes and return the appropriate node given the current
1836       * pagination state.
1837       * @method render
1838       * @param id_base {string} used to create unique ids for generated nodes
1839       * @return {HTMLElement}
1840       */
1841      render : function (id_base) {
1842          var p     = this.paginator,
1843              c     = p.get('lastPageLinkClass'),
1844              label = p.get('lastPageLinkLabel'),
1845              last  = p.getTotalPages(),
1846              title = p.get('lastPageLinkTitle');
1847  
1848          this.link = document.createElement('a');
1849          this.span = document.createElement('span');
1850          this.na   = this.span.cloneNode(false);
1851  
1852          setId(this.link, id_base + '-last-link');
1853          this.link.href      = '#';
1854          this.link.className = c;
1855          this.link.innerHTML = label;
1856          this.link.title     = title;
1857          YAHOO.util.Event.on(this.link,'click',this.onClick,this,true);
1858  
1859          setId(this.span, id_base + '-last-span');
1860          this.span.className = c;
1861          this.span.innerHTML = label;
1862  
1863          setId(this.na, id_base + '-last-na');
1864  
1865          switch (last) {
1866              case Paginator.VALUE_UNLIMITED :
1867                      this.current = this.na; break;
1868              case p.getCurrentPage() :
1869                      this.current = this.span; break;
1870              default :
1871                      this.current = this.link;
1872          }
1873  
1874          return this.current;
1875      },
1876  
1877      /**
1878       * Swap the link, span, and na nodes if appropriate.
1879       * @method update
1880       * @param e {CustomEvent} The calling change event (ignored)
1881       */
1882      update : function (e) {
1883          if (e && e.prevValue === e.newValue) {
1884              return;
1885          }
1886  
1887          var par   = this.current ? this.current.parentNode : null,
1888              after = this.link;
1889  
1890          if (par) {
1891              switch (this.paginator.getTotalPages()) {
1892                  case Paginator.VALUE_UNLIMITED :
1893                          after = this.na; break;
1894                  case this.paginator.getCurrentPage() :
1895                          after = this.span; break;
1896              }
1897  
1898              if (this.current !== after) {
1899                  par.replaceChild(after,this.current);
1900                  this.current = after;
1901              }
1902          }
1903      },
1904  
1905      /**
1906       * Removes the link/span node and clears event listeners
1907       * @method destroy
1908       * @private
1909       */
1910      destroy : function () {
1911          YAHOO.util.Event.purgeElement(this.link);
1912          this.current.parentNode.removeChild(this.current);
1913          this.link = this.span = null;
1914      },
1915  
1916      /**
1917       * Listener for the link's onclick event.  Passes to setPage method.
1918       * @method onClick
1919       * @param e {DOMEvent} The click event
1920       */
1921      onClick : function (e) {
1922          YAHOO.util.Event.stopEvent(e);
1923          this.paginator.setPage(this.paginator.getTotalPages());
1924      }
1925  };
1926  
1927  })();
1928  (function () {
1929  
1930  var Paginator = YAHOO.widget.Paginator,
1931      l         = YAHOO.lang,
1932      setId     = YAHOO.util.Dom.generateId;
1933  
1934  /**
1935   * ui Component to generate the link to jump to the next page.
1936   *
1937   * @namespace YAHOO.widget.Paginator.ui
1938   * @class NextPageLink
1939   * @for YAHOO.widget.Paginator
1940   *
1941   * @constructor
1942   * @param p {Pagintor} Paginator instance to attach to
1943   */
1944  Paginator.ui.NextPageLink = function (p) {
1945      this.paginator = p;
1946  
1947      p.subscribe('recordOffsetChange', this.update,this,true);
1948      p.subscribe('rowsPerPageChange', this.update,this,true);
1949      p.subscribe('totalRecordsChange', this.update,this,true);
1950      p.subscribe('destroy',this.destroy,this,true);
1951  
1952      // TODO: make this work
1953      p.subscribe('nextPageLinkLabelChange', this.update,this,true);
1954      p.subscribe('nextPageLinkClassChange', this.update,this,true);
1955  };
1956  
1957  /**
1958   * Decorates Paginator instances with new attributes. Called during
1959   * Paginator instantiation.
1960   * @method init
1961   * @param p {Paginator} Paginator instance to decorate
1962   * @static
1963   */
1964  Paginator.ui.NextPageLink.init = function (p) {
1965  
1966      /**
1967       * Used as innerHTML for the next page link/span.
1968       * @attribute nextPageLinkLabel
1969       * @default 'next &gt;'
1970       */
1971      p.setAttributeConfig('nextPageLinkLabel', {
1972          value : 'next &gt;',
1973          validator : l.isString
1974      });
1975  
1976      /**
1977       * CSS class assigned to the link/span
1978       * @attribute nextPageLinkClass
1979       * @default 'yui-pg-next'
1980       */
1981      p.setAttributeConfig('nextPageLinkClass', {
1982          value : 'yui-pg-next',
1983          validator : l.isString
1984      });
1985  
1986      /**
1987       * Used as title for the next page link.
1988       * @attribute nextPageLinkTitle
1989       * @default 'Next Page'
1990       */
1991      p.setAttributeConfig('nextPageLinkTitle', {
1992          value : 'Next Page',
1993          validator : l.isString
1994      });
1995  
1996  };
1997  
1998  Paginator.ui.NextPageLink.prototype = {
1999  
2000      /**
2001       * Currently placed HTMLElement node
2002       * @property current
2003       * @type HTMLElement
2004       * @private
2005       */
2006      current   : null,
2007  
2008      /**
2009       * Link node
2010       * @property link
2011       * @type HTMLElement
2012       * @private
2013       */
2014      link      : null,
2015  
2016      /**
2017       * Span node (inactive link)
2018       * @property span
2019       * @type HTMLElement
2020       * @private
2021       */
2022      span      : null,
2023  
2024  
2025      /**
2026       * Generate the nodes and return the appropriate node given the current
2027       * pagination state.
2028       * @method render
2029       * @param id_base {string} used to create unique ids for generated nodes
2030       * @return {HTMLElement}
2031       */
2032      render : function (id_base) {
2033          var p     = this.paginator,
2034              c     = p.get('nextPageLinkClass'),
2035              label = p.get('nextPageLinkLabel'),
2036              last  = p.getTotalPages(),
2037              title = p.get('nextPageLinkTitle');
2038  
2039          this.link     = document.createElement('a');
2040          this.span     = document.createElement('span');
2041  
2042          setId(this.link, id_base + '-next-link');
2043          this.link.href      = '#';
2044          this.link.className = c;
2045          this.link.innerHTML = label;
2046          this.link.title     = title;
2047          YAHOO.util.Event.on(this.link,'click',this.onClick,this,true);
2048  
2049          setId(this.span, id_base + '-next-span');
2050          this.span.className = c;
2051          this.span.innerHTML = label;
2052  
2053          this.current = p.getCurrentPage() === last ? this.span : this.link;
2054  
2055          return this.current;
2056      },
2057  
2058      /**
2059       * Swap the link and span nodes if appropriate.
2060       * @method update
2061       * @param e {CustomEvent} The calling change event
2062       */
2063      update : function (e) {
2064          if (e && e.prevValue === e.newValue) {
2065              return;
2066          }
2067  
2068          var last = this.paginator.getTotalPages(),
2069              par  = this.current ? this.current.parentNode : null;
2070  
2071          if (this.paginator.getCurrentPage() !== last) {
2072              if (par && this.current === this.span) {
2073                  par.replaceChild(this.link,this.current);
2074                  this.current = this.link;
2075              }
2076          } else if (this.current === this.link) {
2077              if (par) {
2078                  par.replaceChild(this.span,this.current);
2079                  this.current = this.span;
2080              }
2081          }
2082      },
2083  
2084      /**
2085       * Removes the link/span node and clears event listeners
2086       * @method destroy
2087       * @private
2088       */
2089      destroy : function () {
2090          YAHOO.util.Event.purgeElement(this.link);
2091          this.current.parentNode.removeChild(this.current);
2092          this.link = this.span = null;
2093      },
2094  
2095      /**
2096       * Listener for the link's onclick event.  Passes to setPage method.
2097       * @method onClick
2098       * @param e {DOMEvent} The click event
2099       */
2100      onClick : function (e) {
2101          YAHOO.util.Event.stopEvent(e);
2102          this.paginator.setPage(this.paginator.getNextPage());
2103      }
2104  };
2105  
2106  })();
2107  (function () {
2108  
2109  var Paginator = YAHOO.widget.Paginator,
2110      l         = YAHOO.lang,
2111      setId     = YAHOO.util.Dom.generateId;
2112  
2113  /**
2114   * ui Component to generate the link to jump to the previous page.
2115   *
2116   * @namespace YAHOO.widget.Paginator.ui
2117   * @class PreviousPageLink
2118   * @for YAHOO.widget.Paginator
2119   *
2120   * @constructor
2121   * @param p {Pagintor} Paginator instance to attach to
2122   */
2123  Paginator.ui.PreviousPageLink = function (p) {
2124      this.paginator = p;
2125  
2126      p.subscribe('recordOffsetChange',this.update,this,true);
2127      p.subscribe('rowsPerPageChange',this.update,this,true);
2128      p.subscribe('totalRecordsChange',this.update,this,true);
2129      p.subscribe('destroy',this.destroy,this,true);
2130  
2131      // TODO: make this work
2132      p.subscribe('previousPageLinkLabelChange',this.update,this,true);
2133      p.subscribe('previousPageLinkClassChange',this.update,this,true);
2134  };
2135  
2136  /**
2137   * Decorates Paginator instances with new attributes. Called during
2138   * Paginator instantiation.
2139   * @method init
2140   * @param p {Paginator} Paginator instance to decorate
2141   * @static
2142   */
2143  Paginator.ui.PreviousPageLink.init = function (p) {
2144  
2145      /**
2146       * Used as innerHTML for the previous page link/span.
2147       * @attribute previousPageLinkLabel
2148       * @default '&lt; prev'
2149       */
2150      p.setAttributeConfig('previousPageLinkLabel', {
2151          value : '&lt; prev',
2152          validator : l.isString
2153      });
2154  
2155      /**
2156       * CSS class assigned to the link/span
2157       * @attribute previousPageLinkClass
2158       * @default 'yui-pg-previous'
2159       */
2160      p.setAttributeConfig('previousPageLinkClass', {
2161          value : 'yui-pg-previous',
2162          validator : l.isString
2163      });
2164  
2165      /**
2166       * Used as title for the previous page link.
2167       * @attribute previousPageLinkTitle
2168       * @default 'Previous Page'
2169       */
2170      p.setAttributeConfig('previousPageLinkTitle', {
2171          value : 'Previous Page',
2172          validator : l.isString
2173      });
2174  
2175  };
2176  
2177  Paginator.ui.PreviousPageLink.prototype = {
2178  
2179      /**
2180       * Currently placed HTMLElement node
2181       * @property current
2182       * @type HTMLElement
2183       * @private
2184       */
2185      current   : null,
2186  
2187      /**
2188       * Link node
2189       * @property link
2190       * @type HTMLElement
2191       * @private
2192       */
2193      link      : null,
2194  
2195      /**
2196       * Span node (inactive link)
2197       * @property span
2198       * @type HTMLElement
2199       * @private
2200       */
2201      span      : null,
2202  
2203  
2204      /**
2205       * Generate the nodes and return the appropriate node given the current
2206       * pagination state.
2207       * @method render
2208       * @param id_base {string} used to create unique ids for generated nodes
2209       * @return {HTMLElement}
2210       */
2211      render : function (id_base) {
2212          var p     = this.paginator,
2213              c     = p.get('previousPageLinkClass'),
2214              label = p.get('previousPageLinkLabel'),
2215              title = p.get('previousPageLinkTitle');
2216  
2217          this.link     = document.createElement('a');
2218          this.span     = document.createElement('span');
2219  
2220          setId(this.link, id_base + '-prev-link');
2221          this.link.href      = '#';
2222          this.link.className = c;
2223          this.link.innerHTML = label;
2224          this.link.title     = title;
2225          YAHOO.util.Event.on(this.link,'click',this.onClick,this,true);
2226  
2227          setId(this.span, id_base + '-prev-span');
2228          this.span.className = c;
2229          this.span.innerHTML = label;
2230  
2231          this.current = p.getCurrentPage() > 1 ? this.link : this.span;
2232          return this.current;
2233      },
2234  
2235      /**
2236       * Swap the link and span nodes if appropriate.
2237       * @method update
2238       * @param e {CustomEvent} The calling change event
2239       */
2240      update : function (e) {
2241          if (e && e.prevValue === e.newValue) {
2242              return;
2243          }
2244  
2245          var par = this.current ? this.current.parentNode : null;
2246          if (this.paginator.getCurrentPage() > 1) {
2247              if (par && this.current === this.span) {
2248                  par.replaceChild(this.link,this.current);
2249                  this.current = this.link;
2250              }
2251          } else {
2252              if (par && this.current === this.link) {
2253                  par.replaceChild(this.span,this.current);
2254                  this.current = this.span;
2255              }
2256          }
2257      },
2258  
2259      /**
2260       * Removes the link/span node and clears event listeners
2261       * @method destroy
2262       * @private
2263       */
2264      destroy : function () {
2265          YAHOO.util.Event.purgeElement(this.link);
2266          this.current.parentNode.removeChild(this.current);
2267          this.link = this.span = null;
2268      },
2269  
2270      /**
2271       * Listener for the link's onclick event.  Passes to setPage method.
2272       * @method onClick
2273       * @param e {DOMEvent} The click event
2274       */
2275      onClick : function (e) {
2276          YAHOO.util.Event.stopEvent(e);
2277          this.paginator.setPage(this.paginator.getPreviousPage());
2278      }
2279  };
2280  
2281  })();
2282  (function () {
2283  
2284  var Paginator = YAHOO.widget.Paginator,
2285      l         = YAHOO.lang,
2286      setId     = YAHOO.util.Dom.generateId;
2287  
2288  /**
2289   * ui Component to generate the rows-per-page dropdown
2290   *
2291   * @namespace YAHOO.widget.Paginator.ui
2292   * @class RowsPerPageDropdown
2293   * @for YAHOO.widget.Paginator
2294   *
2295   * @constructor
2296   * @param p {Pagintor} Paginator instance to attach to
2297   */
2298  Paginator.ui.RowsPerPageDropdown = function (p) {
2299      this.paginator = p;
2300  
2301      p.subscribe('rowsPerPageChange',this.update,this,true);
2302      p.subscribe('rowsPerPageOptionsChange',this.rebuild,this,true);
2303      p.subscribe('totalRecordsChange',this._handleTotalRecordsChange,this,true);
2304      p.subscribe('destroy',this.destroy,this,true);
2305  
2306      // TODO: make this work
2307      p.subscribe('rowsPerPageDropdownClassChange',this.rebuild,this,true);
2308  };
2309  
2310  /**
2311   * Decorates Paginator instances with new attributes. Called during
2312   * Paginator instantiation.
2313   * @method init
2314   * @param p {Paginator} Paginator instance to decorate
2315   * @static
2316   */
2317  Paginator.ui.RowsPerPageDropdown.init = function (p) {
2318  
2319      /**
2320       * Array of available rows-per-page sizes.  Converted into select options.
2321       * Array values may be positive integers or object literals in the form<br>
2322       * { value : NUMBER, text : STRING }
2323       * @attribute rowsPerPageOptions
2324       * @default []
2325       */
2326      p.setAttributeConfig('rowsPerPageOptions', {
2327          value : [],
2328          validator : l.isArray
2329      });
2330  
2331      /**
2332       * CSS class assigned to the select node
2333       * @attribute rowsPerPageDropdownClass
2334       * @default 'yui-pg-rpp-options'
2335       */
2336      p.setAttributeConfig('rowsPerPageDropdownClass', {
2337          value : 'yui-pg-rpp-options',
2338          validator : l.isString
2339      });
2340  };
2341  
2342  Paginator.ui.RowsPerPageDropdown.prototype = {
2343  
2344      /**
2345       * select node
2346       * @property select
2347       * @type HTMLElement
2348       * @private
2349       */
2350      select  : null,
2351  
2352  
2353      /**
2354       * option node for the optional All value
2355       *
2356       * @property all
2357       * @type HTMLElement
2358       * @protected
2359       */
2360      all : null,
2361  
2362      /**
2363       * Generate the select and option nodes and returns the select node.
2364       * @method render
2365       * @param id_base {string} used to create unique ids for generated nodes
2366       * @return {HTMLElement}
2367       */
2368      render : function (id_base) {
2369          this.select = document.createElement('select');
2370          setId(this.select, id_base + '-rpp');
2371          this.select.className = this.paginator.get('rowsPerPageDropdownClass');
2372          this.select.title = 'Rows per page';
2373  
2374          YAHOO.util.Event.on(this.select,'change',this.onChange,this,true);
2375  
2376          this.rebuild();
2377  
2378          return this.select;
2379      },
2380  
2381      /**
2382       * (Re)generate the select options.
2383       * @method rebuild
2384       */
2385      rebuild : function (e) {
2386          var p       = this.paginator,
2387              sel     = this.select,
2388              options = p.get('rowsPerPageOptions'),
2389              opt,cfg,val,i,len;
2390  
2391          this.all = null;
2392  
2393          for (i = 0, len = options.length; i < len; ++i) {
2394              cfg = options[i];
2395              opt = sel.options[i] ||
2396                    sel.appendChild(document.createElement('option'));
2397              val = l.isValue(cfg.value) ? cfg.value : cfg;
2398              opt.text = l.isValue(cfg.text) ? cfg.text : cfg;
2399  
2400              if (l.isString(val) && val.toLowerCase() === 'all') {
2401                  this.all  = opt;
2402                  opt.value = p.get('totalRecords');
2403              } else{
2404                  opt.value = val;
2405              }
2406  
2407          }
2408  
2409          while (sel.options.length > options.length) {
2410              sel.removeChild(sel.firstChild);
2411          }
2412  
2413          this.update();
2414      },
2415  
2416      /**
2417       * Select the appropriate option if changed.
2418       * @method update
2419       * @param e {CustomEvent} The calling change event
2420       */
2421      update : function (e) {
2422          if (e && e.prevValue === e.newValue) {
2423              return;
2424          }
2425  
2426          var rpp     = this.paginator.get('rowsPerPage')+'',
2427              options = this.select.options,
2428              i,len;
2429  
2430          for (i = 0, len = options.length; i < len; ++i) {
2431              if (options[i].value === rpp) {
2432                  options[i].selected = true;
2433                  break;
2434              }
2435          }
2436      },
2437  
2438      /**
2439       * Listener for the select's onchange event.  Sent to setRowsPerPage method.
2440       * @method onChange
2441       * @param e {DOMEvent} The change event
2442       */
2443      onChange : function (e) {
2444          this.paginator.setRowsPerPage(
2445                  parseInt(this.select.options[this.select.selectedIndex].value,10));
2446      },
2447  
2448      /**
2449       * Updates the all option value (and Paginator's rowsPerPage attribute if
2450       * necessary) in response to a change in the Paginator's totalRecords.
2451       *
2452       * @method _handleTotalRecordsChange
2453       * @param e {Event} attribute change event
2454       * @protected
2455       */
2456      _handleTotalRecordsChange : function (e) {
2457          if (!this.all || (e && e.prevValue === e.newValue)) {
2458              return;
2459          }
2460  
2461          this.all.value = e.newValue;
2462          if (this.all.selected) {
2463              this.paginator.set('rowsPerPage',e.newValue);
2464          }
2465      },
2466  
2467      /**
2468       * Removes the select node and clears event listeners
2469       * @method destroy
2470       * @private
2471       */
2472      destroy : function () {
2473          YAHOO.util.Event.purgeElement(this.select);
2474          this.select.parentNode.removeChild(this.select);
2475          this.select = null;
2476      }
2477  };
2478  
2479  })();
2480  (function () {
2481  
2482  var Paginator = YAHOO.widget.Paginator,
2483      l         = YAHOO.lang,
2484      setId     = YAHOO.util.Dom.generateId;
2485  
2486  /**
2487   * ui Component to generate the jump-to-page dropdown
2488   *
2489   * @namespace YAHOO.widget.Paginator.ui
2490   * @class JumpToPageDropdown
2491   * @for YAHOO.widget.Paginator
2492   *
2493   * @constructor
2494   * @param p {Pagintor} Paginator instance to attach to
2495   */
2496  Paginator.ui.JumpToPageDropdown = function (p) {
2497      this.paginator = p;
2498  
2499      p.subscribe('rowsPerPageChange',this.rebuild,this,true);
2500      p.subscribe('rowsPerPageOptionsChange',this.rebuild,this,true);
2501      p.subscribe('pageChange',this.update,this,true);
2502      p.subscribe('totalRecordsChange',this.rebuild,this,true);
2503      p.subscribe('destroy',this.destroy,this,true);
2504  
2505  };
2506  
2507  /**
2508   * Decorates Paginator instances with new attributes. Called during
2509   * Paginator instantiation.
2510   * @method init
2511   * @param p {Paginator} Paginator instance to decorate
2512   * @static
2513   */
2514  Paginator.ui.JumpToPageDropdown.init = function (p) {
2515  
2516  
2517  
2518      /**
2519       * CSS class assigned to the select node
2520       * @attribute jumpToPageDropdownClass
2521       * @default 'yui-pg-jtp-options'
2522       */
2523      p.setAttributeConfig('jumpToPageDropdownClass', {
2524          value : 'yui-pg-jtp-options',
2525          validator : l.isString
2526      });
2527  };
2528  
2529  Paginator.ui.JumpToPageDropdown.prototype = {
2530  
2531      /**
2532       * select node
2533       * @property select
2534       * @type HTMLElement
2535       * @private
2536       */
2537      select  : null,
2538  
2539  
2540  
2541      /**
2542       * Generate the select and option nodes and returns the select node.
2543       * @method render
2544       * @param id_base {string} used to create unique ids for generated nodes
2545       * @return {HTMLElement}
2546       */
2547      render : function (id_base) {
2548          this.select = document.createElement('select');
2549          setId(this.select, id_base + '-jtp');
2550          this.select.className = this.paginator.get('jumpToPageDropdownClass');
2551          this.select.title = 'Jump to page';
2552  
2553          YAHOO.util.Event.on(this.select,'change',this.onChange,this,true);
2554  
2555          this.rebuild();
2556  
2557          return this.select;
2558      },
2559  
2560      /**
2561       * (Re)generate the select options.
2562       * @method rebuild
2563       */
2564      rebuild : function (e) {
2565          var p       = this.paginator,
2566              sel     = this.select,
2567              numPages = p.getTotalPages(),
2568              opt,i,len;
2569  
2570          this.all = null;
2571  
2572          for (i = 0, len = numPages; i < len; ++i ) {
2573              opt = sel.options[i] ||
2574                    sel.appendChild(document.createElement('option'));
2575  
2576              opt.innerHTML = i + 1;
2577  
2578              opt.value = i + 1;
2579  
2580  
2581          }
2582  
2583          for ( i = numPages, len = sel.options.length ; i < len ; i++ ) {
2584              sel.removeChild(sel.lastChild);
2585          }
2586  
2587          this.update();
2588      },
2589  
2590      /**
2591       * Select the appropriate option if changed.
2592       * @method update
2593       * @param e {CustomEvent} The calling change event
2594       */
2595      update : function (e) {
2596  
2597          if (e && e.prevValue === e.newValue) {
2598              return;
2599          }
2600  
2601          var cp      = this.paginator.getCurrentPage()+'',
2602              options = this.select.options,
2603              i,len;
2604  
2605          for (i = 0, len = options.length; i < len; ++i) {
2606              if (options[i].value === cp) {
2607                  options[i].selected = true;
2608                  break;
2609              }
2610          }
2611      },
2612  
2613      /**
2614       * Listener for the select's onchange event.  Sent to setPage method.
2615       * @method onChange
2616       * @param e {DOMEvent} The change event
2617       */
2618      onChange : function (e) {
2619          this.paginator.setPage(
2620                  parseInt(this.select.options[this.select.selectedIndex].value,false));
2621      },
2622  
2623  
2624  
2625      /**
2626       * Removes the select node and clears event listeners
2627       * @method destroy
2628       * @private
2629       */
2630      destroy : function () {
2631          YAHOO.util.Event.purgeElement(this.select);
2632          this.select.parentNode.removeChild(this.select);
2633          this.select = null;
2634      }
2635  };
2636  
2637  })();
2638  YAHOO.register("paginator", YAHOO.widget.Paginator, {version: "2.9.0", build: "2800"});
2639  
2640  }, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom", "yui2-event", "yui2-element", "yui2-skin-sam-paginator"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1