2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
9 * The Paginator widget provides a set of controls to navigate through paged
13 * @uses YAHOO.util.EventProvider
14 * @uses YAHOO.util.AttributeProvider
18 * Instantiate a Paginator, passing a configuration object to the contructor.
19 * The configuration object should contain the following properties:
21 * <li>rowsPerPage : <em>n</em> (int)</li>
22 * <li>totalRecords : <em>n</em> (int or Paginator.VALUE_UNLIMITED)</li>
23 * <li>containers : <em>id | el | arr</em> (HTMLElement reference, its id, or an array of either)</li>
26 * @namespace YAHOO.widget
29 * @param config {Object} Object literal to set instance and ui component
32 function Paginator(config) {
33 var UNLIMITED = Paginator.VALUE_UNLIMITED,
35 attrib, initialPage, records, perPage, startIndex;
37 config = lang.isObject(config) ? config : {};
43 // Set the basic config keys first
44 this.set('rowsPerPage',config.rowsPerPage,true);
45 if (Paginator.isNumeric(config.totalRecords)) {
46 this.set('totalRecords',config.totalRecords,true);
49 this.initUIComponents();
51 // Update the other config values
52 for (attrib in config) {
53 if (lang.hasOwnProperty(config,attrib)) {
54 this.set(attrib,config[attrib],true);
58 // Calculate the initial record offset
59 initialPage = this.get('initialPage');
60 records = this.get('totalRecords');
61 perPage = this.get('rowsPerPage');
62 if (initialPage > 1 && perPage !== UNLIMITED) {
63 startIndex = (initialPage - 1) * perPage;
64 if (records === UNLIMITED || startIndex < records) {
65 this.set('recordOffset',startIndex,true);
72 YAHOO.lang.augmentObject(Paginator, {
74 * Incrementing index used to give instances unique ids.
83 * Base of id strings used for ui components.
92 * Used to identify unset, optional configurations, or used explicitly in
93 * the case of totalRecords to indicate unlimited pagination.
95 * @property VALUE_UNLIMITED
102 * Default template used by Paginator instances. Update this if you want
103 * all new Paginators to use a different default template.
105 * @property TEMPLATE_DEFAULT
108 TEMPLATE_DEFAULT : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink}",
111 * Common alternate pagination format, including page links, links for
112 * previous, next, first and last pages as well as a rows-per-page
113 * dropdown. Offered as a convenience.
115 * @property TEMPLATE_ROWS_PER_PAGE
118 TEMPLATE_ROWS_PER_PAGE : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}",
121 * Storage object for UI Components
128 * Similar to YAHOO.lang.isNumber, but allows numeric strings. This is
129 * is used for attribute validation in conjunction with getters that return
133 * @param v {Number|String} value to be checked for number or numeric string
134 * @returns {Boolean} true if the input is coercable into a finite number
137 isNumeric : function (v) {
142 * Return a number or null from input
145 * @param n {Number|String} a number or numeric string
149 toNumber : function (n) {
150 return isFinite(+n) ? +n : null;
156 // Instance members and methods
157 Paginator.prototype = {
162 * Array of nodes in which to render pagination controls. This is set via
163 * the "containers" attribute.
164 * @property _containers
165 * @type Array(HTMLElement)
171 * Flag used to indicate multiple attributes are being updated via setState
179 * Used by setState to indicate when a page change has occurred
180 * @property _pageChanged
184 _pageChanged : false,
187 * Temporary state cache used by setState to keep track of the previous
188 * state for eventual pageChange event firing
199 * Initialize the Paginator's attributes (see YAHOO.util.Element class
200 * AttributeProvider).
204 initConfig : function () {
206 var UNLIMITED = Paginator.VALUE_UNLIMITED,
210 * REQUIRED. Number of records constituting a "page"
211 * @attribute rowsPerPage
214 this.setAttributeConfig('rowsPerPage', {
216 validator : Paginator.isNumeric,
217 setter : Paginator.toNumber
221 * REQUIRED. Node references or ids of nodes in which to render the
222 * pagination controls.
223 * @attribute containers
224 * @type {string|HTMLElement|Array(string|HTMLElement)}
226 this.setAttributeConfig('containers', {
228 validator : function (val) {
229 if (!l.isArray(val)) {
232 for (var i = 0, len = val.length; i < len; ++i) {
233 if (l.isString(val[i]) ||
234 (l.isObject(val[i]) && val[i].nodeType === 1)) {
241 method : function (val) {
242 val = YAHOO.util.Dom.get(val);
243 if (!l.isArray(val)) {
246 this._containers = val;
251 * Total number of records to paginate through
252 * @attribute totalRecords
256 this.setAttributeConfig('totalRecords', {
258 validator : Paginator.isNumeric,
259 setter : Paginator.toNumber
263 * Zero based index of the record considered first on the current page.
264 * For page based interactions, don't modify this attribute directly;
266 * @attribute recordOffset
270 this.setAttributeConfig('recordOffset', {
272 validator : function (val) {
273 var total = this.get('totalRecords');
274 if (Paginator.isNumeric(val)) {
276 return total === UNLIMITED || total > val ||
277 (total === 0 && val === 0);
282 setter : Paginator.toNumber
286 * Page to display on initial paint
287 * @attribute initialPage
291 this.setAttributeConfig('initialPage', {
293 validator : Paginator.isNumeric,
294 setter : Paginator.toNumber
298 * Template used to render controls. The string will be used as
299 * innerHTML on all specified container nodes. Bracketed keys
300 * (e.g. {pageLinks}) in the string will be replaced with an instance
301 * of the so named ui component.
302 * @see Paginator.TEMPLATE_DEFAULT
303 * @see Paginator.TEMPLATE_ROWS_PER_PAGE
304 * @attribute template
307 this.setAttributeConfig('template', {
308 value : Paginator.TEMPLATE_DEFAULT,
309 validator : l.isString
313 * Class assigned to the element(s) containing pagination controls.
314 * @attribute containerClass
316 * @default 'yui-pg-container'
318 this.setAttributeConfig('containerClass', {
319 value : 'yui-pg-container',
320 validator : l.isString
324 * Display pagination controls even when there is only one page. Set
325 * to false to forgo rendering and/or hide the containers when there
326 * is only one page of data. Note if you are using the rowsPerPage
327 * dropdown ui component, visibility will be maintained as long as the
328 * number of records exceeds the smallest page size.
329 * @attribute alwaysVisible
333 this.setAttributeConfig('alwaysVisible', {
335 validator : l.isBoolean
339 * Update the UI immediately upon interaction. If false, changeRequest
340 * subscribers or other external code will need to explicitly set the
341 * new values in the paginator to trigger repaint.
342 * @attribute updateOnChange
346 this.setAttributeConfig('updateOnChange', {
348 validator : l.isBoolean
353 // Read only attributes
356 * Unique id assigned to this instance
361 this.setAttributeConfig('id', {
362 value : Paginator.id++,
367 * Indicator of whether the DOM nodes have been initially created
368 * @attribute rendered
372 this.setAttributeConfig('rendered', {
380 * Initialize registered ui components onto this instance.
381 * @method initUIComponents
384 initUIComponents : function () {
385 var ui = Paginator.ui,
388 if (YAHOO.lang.hasOwnProperty(ui,name)) {
390 if (YAHOO.lang.isObject(UIComp) &&
391 YAHOO.lang.isFunction(UIComp.init)) {
399 * Initialize this instance's CustomEvents.
403 initEvents : function () {
405 * Event fired when the Paginator is initially rendered
408 this.createEvent('render');
411 * Event fired when the Paginator is initially rendered
413 * @deprecated use render event
415 this.createEvent('rendered'); // backward compatibility
418 * Event fired when a change in pagination values is requested,
419 * either by interacting with the various ui components or via the
420 * setStartIndex(n) etc APIs.
421 * Subscribers will receive the proposed state as the first parameter.
422 * The proposed state object will contain the following keys:
424 * <li>paginator - the Paginator instance</li>
426 * <li>totalRecords</li>
427 * <li>recordOffset - index of the first record on the new page</li>
428 * <li>rowsPerPage</li>
429 * <li>records - array containing [start index, end index] for the records on the new page</li>
430 * <li>before - object literal with all these keys for the current state</li>
432 * @event changeRequest
434 this.createEvent('changeRequest');
437 * Event fired when attribute changes have resulted in the calculated
438 * current page changing.
441 this.createEvent('pageChange');
444 * Event that fires before the destroy event.
445 * @event beforeDestroy
447 this.createEvent('beforeDestroy');
450 * Event used to trigger cleanup of ui components
453 this.createEvent('destroy');
455 this._selfSubscribe();
459 * Subscribes to instance attribute change events to automate certain
461 * @method _selfSubscribe
464 _selfSubscribe : function () {
465 // Listen for changes to totalRecords and alwaysVisible
466 this.subscribe('totalRecordsChange',this.updateVisibility,this,true);
467 this.subscribe('alwaysVisibleChange',this.updateVisibility,this,true);
469 // Fire the pageChange event when appropriate
470 this.subscribe('totalRecordsChange',this._handleStateChange,this,true);
471 this.subscribe('recordOffsetChange',this._handleStateChange,this,true);
472 this.subscribe('rowsPerPageChange',this._handleStateChange,this,true);
474 // Update recordOffset when totalRecords is reduced below
475 this.subscribe('totalRecordsChange',this._syncRecordOffset,this,true);
479 * Sets recordOffset to the starting index of the previous page when
480 * totalRecords is reduced below the current recordOffset.
481 * @method _syncRecordOffset
482 * @param e {Event} totalRecordsChange event
485 _syncRecordOffset : function (e) {
486 var v = e.newValue,rpp,state;
487 if (e.prevValue !== v) {
488 if (v !== Paginator.VALUE_UNLIMITED) {
489 rpp = this.get('rowsPerPage');
491 if (rpp && this.get('recordOffset') >= v) {
492 state = this.getState({
493 totalRecords : e.prevValue,
494 recordOffset : this.get('recordOffset')
497 this.set('recordOffset', state.before.recordOffset);
498 this._firePageChange(state);
505 * Fires the pageChange event when the state attributes have changed in
506 * such a way as to locate the current recordOffset on a new page.
507 * @method _handleStateChange
508 * @param e {Event} the attribute change event
511 _handleStateChange : function (e) {
512 if (e.prevValue !== e.newValue) {
513 var change = this._state || {},
516 change[e.type.replace(/Change$/,'')] = e.prevValue;
517 state = this.getState(change);
519 if (state.page !== state.before.page) {
521 this._pageChanged = true;
523 this._firePageChange(state);
530 * Fires a pageChange event in the form of a standard attribute change
531 * event with additional properties prevState and newState.
532 * @method _firePageChange
533 * @param state {Object} the result of getState(oldState)
536 _firePageChange : function (state) {
537 if (YAHOO.lang.isObject(state)) {
538 var current = state.before;
540 this.fireEvent('pageChange',{
542 prevValue : state.page,
543 newValue : current.page,
551 * Render the pagination controls per the format attribute into the
552 * specified container nodes.
555 render : function () {
556 if (this.get('rendered')) {
560 // Forgo rendering if only one page and alwaysVisible is off
561 var totalRecords = this.get('totalRecords'),
562 Dom = YAHOO.util.Dom,
563 template = this.get('template'),
564 containerClass = this.get('containerClass'),
565 i,len,c,id_base,markers,j,jlen,m,mp,name,UIComp,comp;
567 if (totalRecords !== Paginator.VALUE_UNLIMITED &&
568 totalRecords < this.get('rowsPerPage') &&
569 !this.get('alwaysVisible')) {
573 // add marker spans to the template html to indicate drop zones
575 template = template.replace(/\{([a-z0-9_ \-]+)\}/gi,
576 '<span class="yui-pg-ui $1"></span>');
577 for (i = 0, len = this._containers.length; i < len; ++i) {
578 c = this._containers[i];
579 // ex. yui-pg0-1 (first paginator, second container)
580 id_base = Paginator.ID_BASE + this.get('id') + '-' + i;
585 // Hide the container while its contents are rendered
586 c.style.display = 'none';
588 Dom.addClass(c,containerClass);
590 // Place the template innerHTML
591 c.innerHTML = template;
593 // Replace each marker with the ui component's render() output
594 markers = Dom.getElementsByClassName('yui-pg-ui','span',c);
596 for (j = 0, jlen = markers.length; j < jlen; ++j) {
599 name = m.className.replace(/\s*yui-pg-ui\s+/g,'');
600 UIComp = Paginator.ui[name];
602 if (YAHOO.lang.isFunction(UIComp)) {
603 comp = new UIComp(this);
604 if (YAHOO.lang.isFunction(comp.render)) {
605 mp.replaceChild(comp.render(id_base),m);
610 // Show the container allowing page reflow
611 c.style.display = '';
614 // Set render attribute manually to support its readOnly contract
615 if (this._containers.length) {
616 this.setAttributeConfig('rendered',{value:true});
618 this.fireEvent('render',this.getState());
619 // For backward compatibility
620 this.fireEvent('rendered',this.getState());
625 * Removes controls from the page and unhooks events.
628 destroy : function () {
629 this.fireEvent('beforeDestroy');
630 this.fireEvent('destroy');
632 this.setAttributeConfig('rendered',{value:false});
636 * Hides the containers if there is only one page of data and attribute
637 * alwaysVisible is false. Conversely, it displays the containers if either
638 * there is more than one page worth of data or alwaysVisible is turned on.
639 * @method updateVisibility
641 updateVisibility : function (e) {
642 var alwaysVisible = this.get('alwaysVisible'),
643 totalRecords,visible,rpp,rppOptions,i,len;
645 if (e.type === 'alwaysVisibleChange' || !alwaysVisible) {
646 totalRecords = this.get('totalRecords');
648 rpp = this.get('rowsPerPage');
649 rppOptions = this.get('rowsPerPageOptions');
651 if (YAHOO.lang.isArray(rppOptions)) {
652 for (i = 0, len = rppOptions.length; i < len; ++i) {
653 rpp = Math.min(rpp,rppOptions[i]);
657 if (totalRecords !== Paginator.VALUE_UNLIMITED &&
658 totalRecords <= rpp) {
662 visible = visible || alwaysVisible;
664 for (i = 0, len = this._containers.length; i < len; ++i) {
665 YAHOO.util.Dom.setStyle(this._containers[i],'display',
666 visible ? '' : 'none');
675 * Get the configured container nodes
676 * @method getContainerNodes
677 * @return {Array} array of HTMLElement nodes
679 getContainerNodes : function () {
680 return this._containers;
684 * Get the total number of pages in the data set according to the current
685 * rowsPerPage and totalRecords values. If totalRecords is not set, or
686 * set to YAHOO.widget.Paginator.VALUE_UNLIMITED, returns
687 * YAHOO.widget.Paginator.VALUE_UNLIMITED.
688 * @method getTotalPages
691 getTotalPages : function () {
692 var records = this.get('totalRecords'),
693 perPage = this.get('rowsPerPage');
695 // rowsPerPage not set. Can't calculate
700 if (records === Paginator.VALUE_UNLIMITED) {
701 return Paginator.VALUE_UNLIMITED;
704 return Math.ceil(records/perPage);
708 * Does the requested page have any records?
710 * @param page {number} the page in question
713 hasPage : function (page) {
714 if (!YAHOO.lang.isNumber(page) || page < 1) {
718 var totalPages = this.getTotalPages();
720 return (totalPages === Paginator.VALUE_UNLIMITED || totalPages >= page);
724 * Get the page number corresponding to the current record offset.
725 * @method getCurrentPage
728 getCurrentPage : function () {
729 var perPage = this.get('rowsPerPage');
730 if (!perPage || !this.get('totalRecords')) {
733 return Math.floor(this.get('recordOffset') / perPage) + 1;
737 * Are there records on the next page?
738 * @method hasNextPage
741 hasNextPage : function () {
742 var currentPage = this.getCurrentPage(),
743 totalPages = this.getTotalPages();
745 return currentPage && (totalPages === Paginator.VALUE_UNLIMITED || currentPage < totalPages);
749 * Get the page number of the next page, or null if the current page is the
751 * @method getNextPage
754 getNextPage : function () {
755 return this.hasNextPage() ? this.getCurrentPage() + 1 : null;
759 * Is there a page before the current page?
760 * @method hasPreviousPage
763 hasPreviousPage : function () {
764 return (this.getCurrentPage() > 1);
768 * Get the page number of the previous page, or null if the current page
770 * @method getPreviousPage
773 getPreviousPage : function () {
774 return (this.hasPreviousPage() ? this.getCurrentPage() - 1 : 1);
778 * Get the start and end record indexes of the specified page.
779 * @method getPageRecords
780 * @param page {number} (optional) The page (current page if not specified)
781 * @return {Array} [start_index, end_index]
783 getPageRecords : function (page) {
784 if (!YAHOO.lang.isNumber(page)) {
785 page = this.getCurrentPage();
788 var perPage = this.get('rowsPerPage'),
789 records = this.get('totalRecords'),
792 if (!page || !perPage) {
796 start = (page - 1) * perPage;
797 if (records !== Paginator.VALUE_UNLIMITED) {
798 if (start >= records) {
801 end = Math.min(start + perPage, records) - 1;
803 end = start + perPage - 1;
810 * Set the current page to the provided page number if possible.
812 * @param newPage {number} the new page number
813 * @param silent {boolean} whether to forcibly avoid firing the
814 * changeRequest event
816 setPage : function (page,silent) {
817 if (this.hasPage(page) && page !== this.getCurrentPage()) {
818 if (this.get('updateOnChange') || silent) {
819 this.set('recordOffset', (page - 1) * this.get('rowsPerPage'));
821 this.fireEvent('changeRequest',this.getState({'page':page}));
827 * Get the number of rows per page.
828 * @method getRowsPerPage
829 * @return {number} the current setting of the rowsPerPage attribute
831 getRowsPerPage : function () {
832 return this.get('rowsPerPage');
836 * Set the number of rows per page.
837 * @method setRowsPerPage
838 * @param rpp {number} the new number of rows per page
839 * @param silent {boolean} whether to forcibly avoid firing the
840 * changeRequest event
842 setRowsPerPage : function (rpp,silent) {
843 if (Paginator.isNumeric(rpp) && +rpp > 0 &&
844 +rpp !== this.get('rowsPerPage')) {
845 if (this.get('updateOnChange') || silent) {
846 this.set('rowsPerPage',rpp);
848 this.fireEvent('changeRequest',
849 this.getState({'rowsPerPage':+rpp}));
855 * Get the total number of records.
856 * @method getTotalRecords
857 * @return {number} the current setting of totalRecords attribute
859 getTotalRecords : function () {
860 return this.get('totalRecords');
864 * Set the total number of records.
865 * @method setTotalRecords
866 * @param total {number} the new total number of records
867 * @param silent {boolean} whether to forcibly avoid firing the changeRequest event
869 setTotalRecords : function (total,silent) {
870 if (Paginator.isNumeric(total) && +total >= 0 &&
871 +total !== this.get('totalRecords')) {
872 if (this.get('updateOnChange') || silent) {
873 this.set('totalRecords',total);
875 this.fireEvent('changeRequest',
876 this.getState({'totalRecords':+total}));
882 * Get the index of the first record on the current page
883 * @method getStartIndex
884 * @return {number} the index of the first record on the current page
886 getStartIndex : function () {
887 return this.get('recordOffset');
891 * Move the record offset to a new starting index. This will likely cause
892 * the calculated current page to change. You should probably use setPage.
893 * @method setStartIndex
894 * @param offset {number} the new record offset
895 * @param silent {boolean} whether to forcibly avoid firing the changeRequest event
897 setStartIndex : function (offset,silent) {
898 if (Paginator.isNumeric(offset) && +offset >= 0 &&
899 +offset !== this.get('recordOffset')) {
900 if (this.get('updateOnChange') || silent) {
901 this.set('recordOffset',offset);
903 this.fireEvent('changeRequest',
904 this.getState({'recordOffset':+offset}));
910 * Get an object literal describing the current state of the paginator. If
911 * an object literal of proposed values is passed, the proposed state will
912 * be returned as an object literal with the following keys:
914 * <li>paginator - instance of the Paginator</li>
915 * <li>page - number</li>
916 * <li>totalRecords - number</li>
917 * <li>recordOffset - number</li>
918 * <li>rowsPerPage - number</li>
919 * <li>records - [ start_index, end_index ]</li>
920 * <li>before - (OPTIONAL) { state object literal for current state }</li>
924 * @param changes {object} OPTIONAL object literal with proposed values
925 * Supported change keys include:
927 * <li>rowsPerPage</li>
928 * <li>totalRecords</li>
929 * <li>recordOffset OR</li>
933 getState : function (changes) {
934 var UNLIMITED = Paginator.VALUE_UNLIMITED,
935 M = Math, max = M.max, ceil = M.ceil,
936 currentState, state, offset;
938 function normalizeOffset(offset,total,rpp) {
939 if (offset <= 0 || total === 0) {
942 if (total === UNLIMITED || total > offset) {
943 return offset - (offset % rpp);
945 return total - (total % rpp || rpp);
950 totalRecords : this.get('totalRecords'),
951 rowsPerPage : this.get('rowsPerPage'),
952 records : this.getPageRecords()
954 currentState.recordOffset = normalizeOffset(
955 this.get('recordOffset'),
956 currentState.totalRecords,
957 currentState.rowsPerPage);
958 currentState.page = ceil(currentState.recordOffset /
959 currentState.rowsPerPage) + 1;
967 before : currentState,
969 rowsPerPage : changes.rowsPerPage || currentState.rowsPerPage,
970 totalRecords : (Paginator.isNumeric(changes.totalRecords) ?
971 max(changes.totalRecords,UNLIMITED) :
972 +currentState.totalRecords)
975 if (state.totalRecords === 0) {
979 offset = Paginator.isNumeric(changes.page) ?
980 (changes.page - 1) * state.rowsPerPage :
981 Paginator.isNumeric(changes.recordOffset) ?
982 +changes.recordOffset :
983 currentState.recordOffset;
985 state.recordOffset = normalizeOffset(offset,
989 state.page = ceil(state.recordOffset / state.rowsPerPage) + 1;
992 state.records = [ state.recordOffset,
993 state.recordOffset + state.rowsPerPage - 1 ];
995 // limit upper index to totalRecords - 1
996 if (state.totalRecords !== UNLIMITED &&
997 state.recordOffset < state.totalRecords && state.records &&
998 state.records[1] > state.totalRecords - 1) {
999 state.records[1] = state.totalRecords - 1;
1006 * Convenience method to facilitate setting state attributes rowsPerPage,
1007 * totalRecords, recordOffset in batch. Also supports calculating
1008 * recordOffset from state.page if state.recordOffset is not provided.
1009 * Fires only a single pageChange event, if appropriate.
1010 * This will not fire a changeRequest event.
1012 * @param state {Object} Object literal of attribute:value pairs to set
1014 setState : function (state) {
1015 if (YAHOO.lang.isObject(state)) {
1016 // get flux state based on current state with before state as well
1017 this._state = this.getState({});
1019 // use just the state props from the input obj
1022 rowsPerPage : state.rowsPerPage,
1023 totalRecords : state.totalRecords,
1024 recordOffset : state.recordOffset
1027 // calculate recordOffset from page if recordOffset not specified.
1028 // not using lang.isNumber for support of numeric strings
1029 if (state.page && state.recordOffset === undefined) {
1030 state.recordOffset = (state.page - 1) *
1031 (state.rowsPerPage || this.get('rowsPerPage'));
1035 this._pageChanged = false;
1037 for (var k in state) {
1038 if (state.hasOwnProperty(k)) {
1039 this.set(k,state[k]);
1043 this._batch = false;
1045 if (this._pageChanged) {
1046 this._pageChanged = false;
1048 this._firePageChange(this.getState(this._state));
1054 YAHOO.lang.augmentProto(Paginator, YAHOO.util.AttributeProvider);
1056 YAHOO.widget.Paginator = Paginator;
1060 var Paginator = YAHOO.widget.Paginator,
1064 * ui Component to generate the textual report of current pagination status.
1065 * E.g. "Now viewing page 1 of 13".
1067 * @namespace YAHOO.widget.Paginator.ui
1068 * @class CurrentPageReport
1069 * @for YAHOO.widget.Paginator
1072 * @param p {Pagintor} Paginator instance to attach to
1074 Paginator.ui.CurrentPageReport = function (p) {
1077 p.subscribe('recordOffsetChange', this.update,this,true);
1078 p.subscribe('rowsPerPageChange', this.update,this,true);
1079 p.subscribe('totalRecordsChange',this.update,this,true);
1080 p.subscribe('pageReportTemplateChange', this.update,this,true);
1081 p.subscribe('destroy',this.destroy,this,true);
1083 //TODO: make this work
1084 p.subscribe('pageReportClassChange', this.update,this,true);
1088 * Decorates Paginator instances with new attributes. Called during
1089 * Paginator instantiation.
1091 * @param p {Paginator} Paginator instance to decorate
1094 Paginator.ui.CurrentPageReport.init = function (p) {
1097 * CSS class assigned to the span containing the info.
1098 * @attribute pageReportClass
1099 * @default 'yui-pg-current'
1101 p.setAttributeConfig('pageReportClass', {
1102 value : 'yui-pg-current',
1103 validator : l.isString
1107 * Used as innerHTML for the span. Place holders in the form of {name}
1108 * will be replaced with the so named value from the key:value map
1109 * generated by the function held in the pageReportValueGenerator attribute.
1110 * @attribute pageReportTemplate
1111 * @default '({currentPage} of {totalPages})'
1112 * @see pageReportValueGenerator attribute
1114 p.setAttributeConfig('pageReportTemplate', {
1115 value : '({currentPage} of {totalPages})',
1116 validator : l.isString
1120 * Function to generate the value map used to populate the
1121 * pageReportTemplate. The function is passed the Paginator instance as a
1122 * parameter. The default function returns a map with the following keys:
1124 * <li>currentPage</li>
1125 * <li>totalPages</li>
1126 * <li>startIndex</li>
1128 * <li>startRecord</li>
1129 * <li>endRecord</li>
1130 * <li>totalRecords</li>
1132 * @attribute pageReportValueGenarator
1134 p.setAttributeConfig('pageReportValueGenerator', {
1135 value : function (paginator) {
1136 var curPage = paginator.getCurrentPage(),
1137 records = paginator.getPageRecords();
1140 'currentPage' : records ? curPage : 0,
1141 'totalPages' : paginator.getTotalPages(),
1142 'startIndex' : records ? records[0] : 0,
1143 'endIndex' : records ? records[1] : 0,
1144 'startRecord' : records ? records[0] + 1 : 0,
1145 'endRecord' : records ? records[1] + 1 : 0,
1146 'totalRecords': paginator.get('totalRecords')
1149 validator : l.isFunction
1154 * Replace place holders in a string with the named values found in an
1158 * @param template {string} The content string containing place holders
1159 * @param values {object} The key:value pairs used to replace the place holders
1162 Paginator.ui.CurrentPageReport.sprintf = function (template, values) {
1163 return template.replace(/\{([\w\s\-]+)\}/g, function (x,key) {
1164 return (key in values) ? values[key] : '';
1168 Paginator.ui.CurrentPageReport.prototype = {
1171 * Span node containing the formatted info
1180 * Generate the span containing info formatted per the pageReportTemplate
1183 * @param id_base {string} used to create unique ids for generated nodes
1184 * @return {HTMLElement}
1186 render : function (id_base) {
1187 this.span = document.createElement('span');
1188 this.span.id = id_base + '-page-report';
1189 this.span.className = this.paginator.get('pageReportClass');
1196 * Regenerate the content of the span if appropriate. Calls
1197 * CurrentPageReport.sprintf with the value of the pageReportTemplate
1198 * attribute and the value map returned from pageReportValueGenerator
1201 * @param e {CustomEvent} The calling change event
1203 update : function (e) {
1204 if (e && e.prevValue === e.newValue) {
1208 this.span.innerHTML = Paginator.ui.CurrentPageReport.sprintf(
1209 this.paginator.get('pageReportTemplate'),
1210 this.paginator.get('pageReportValueGenerator')(this.paginator));
1214 * Removes the link/span node and clears event listeners
1219 destroy : function () {
1220 this.span.parentNode.removeChild(this.span);
1229 var Paginator = YAHOO.widget.Paginator,
1233 * ui Component to generate the page links
1235 * @namespace YAHOO.widget.Paginator.ui
1237 * @for YAHOO.widget.Paginator
1240 * @param p {Pagintor} Paginator instance to attach to
1242 Paginator.ui.PageLinks = function (p) {
1245 p.subscribe('recordOffsetChange',this.update,this,true);
1246 p.subscribe('rowsPerPageChange',this.update,this,true);
1247 p.subscribe('totalRecordsChange',this.update,this,true);
1248 p.subscribe('pageLinksChange', this.rebuild,this,true);
1249 p.subscribe('pageLinkClassChange', this.rebuild,this,true);
1250 p.subscribe('currentPageClassChange', this.rebuild,this,true);
1251 p.subscribe('destroy',this.destroy,this,true);
1253 //TODO: Make this work
1254 p.subscribe('pageLinksContainerClassChange', this.rebuild,this,true);
1258 * Decorates Paginator instances with new attributes. Called during
1259 * Paginator instantiation.
1261 * @param p {Paginator} Paginator instance to decorate
1264 Paginator.ui.PageLinks.init = function (p) {
1267 * CSS class assigned to each page link/span.
1268 * @attribute pageLinkClass
1269 * @default 'yui-pg-page'
1271 p.setAttributeConfig('pageLinkClass', {
1272 value : 'yui-pg-page',
1273 validator : l.isString
1277 * CSS class assigned to the current page span.
1278 * @attribute currentPageClass
1279 * @default 'yui-pg-current-page'
1281 p.setAttributeConfig('currentPageClass', {
1282 value : 'yui-pg-current-page',
1283 validator : l.isString
1287 * CSS class assigned to the span containing the page links.
1288 * @attribute pageLinksContainerClass
1289 * @default 'yui-pg-pages'
1291 p.setAttributeConfig('pageLinksContainerClass', {
1292 value : 'yui-pg-pages',
1293 validator : l.isString
1297 * Maximum number of page links to display at one time.
1298 * @attribute pageLinks
1301 p.setAttributeConfig('pageLinks', {
1303 validator : Paginator.isNumeric
1307 * Function used generate the innerHTML for each page link/span. The
1308 * function receives as parameters the page number and a reference to the
1310 * @attribute pageLabelBuilder
1311 * @default function (page, paginator) { return page; }
1313 p.setAttributeConfig('pageLabelBuilder', {
1314 value : function (page, paginator) { return page; },
1315 validator : l.isFunction
1320 * Calculates start and end page numbers given a current page, attempting
1321 * to keep the current page in the middle
1323 * @method calculateRange
1324 * @param {int} currentPage The current page
1325 * @param {int} totalPages (optional) Maximum number of pages
1326 * @param {int} numPages (optional) Preferred number of pages in range
1327 * @return {Array} [start_page_number, end_page_number]
1329 Paginator.ui.PageLinks.calculateRange = function (currentPage,totalPages,numPages) {
1330 var UNLIMITED = Paginator.VALUE_UNLIMITED,
1333 // Either has no pages, or unlimited pages. Show none.
1334 if (!currentPage || numPages === 0 || totalPages === 0 ||
1335 (totalPages === UNLIMITED && numPages === UNLIMITED)) {
1339 // Limit requested pageLinks if there are fewer totalPages
1340 if (totalPages !== UNLIMITED) {
1341 numPages = numPages === UNLIMITED ?
1343 Math.min(numPages,totalPages);
1346 // Determine start and end, trying to keep current in the middle
1347 start = Math.max(1,Math.ceil(currentPage - (numPages/2)));
1348 if (totalPages === UNLIMITED) {
1349 end = start + numPages - 1;
1351 end = Math.min(totalPages, start + numPages - 1);
1354 // Adjust the start index when approaching the last page
1355 delta = numPages - (end - start + 1);
1356 start = Math.max(1, start - delta);
1362 Paginator.ui.PageLinks.prototype = {
1373 * Span node containing the page links
1374 * @property container
1382 * Generate the nodes and return the container node containing page links
1383 * appropriate to the current pagination state.
1385 * @param id_base {string} used to create unique ids for generated nodes
1386 * @return {HTMLElement}
1388 render : function (id_base) {
1389 var p = this.paginator;
1392 this.container = document.createElement('span');
1393 this.container.id = id_base + '-pages';
1394 this.container.className = p.get('pageLinksContainerClass');
1395 YAHOO.util.Event.on(this.container,'click',this.onClick,this,true);
1397 // Call update, flagging a need to rebuild
1398 this.update({newValue : null, rebuild : true});
1400 return this.container;
1404 * Update the links if appropriate
1406 * @param e {CustomEvent} The calling change event
1408 update : function (e) {
1409 if (e && e.prevValue === e.newValue) {
1413 var p = this.paginator,
1414 currentPage = p.getCurrentPage();
1416 // Replace content if there's been a change
1417 if (this.current !== currentPage || !currentPage || e.rebuild) {
1418 var labelBuilder = p.get('pageLabelBuilder'),
1419 range = Paginator.ui.PageLinks.calculateRange(
1422 p.get('pageLinks')),
1428 linkTemplate = '<a href="#" class="' + p.get('pageLinkClass') +
1430 for (i = start; i <= end; ++i) {
1431 if (i === currentPage) {
1433 '<span class="' + p.get('currentPageClass') + ' ' +
1434 p.get('pageLinkClass') + '">' +
1435 labelBuilder(i,p) + '</span>';
1438 linkTemplate + i + '">' + labelBuilder(i,p) + '</a>';
1442 this.container.innerHTML = content;
1447 * Force a rebuild of the page links.
1449 * @param e {CustomEvent} The calling change event
1451 rebuild : function (e) {
1457 * Removes the page links container node and clears event listeners
1461 destroy : function () {
1462 YAHOO.util.Event.purgeElement(this.container,true);
1463 this.container.parentNode.removeChild(this.container);
1464 this.container = null;
1468 * Listener for the container's onclick event. Looks for qualifying link
1469 * clicks, and pulls the page number from the link's page attribute.
1470 * Sends link's page attribute to the Paginator's setPage method.
1472 * @param e {DOMEvent} The click event
1474 onClick : function (e) {
1475 var t = YAHOO.util.Event.getTarget(e);
1476 if (t && YAHOO.util.Dom.hasClass(t,
1477 this.paginator.get('pageLinkClass'))) {
1479 YAHOO.util.Event.stopEvent(e);
1481 this.paginator.setPage(parseInt(t.getAttribute('page'),10));
1490 var Paginator = YAHOO.widget.Paginator,
1494 * ui Component to generate the link to jump to the first page.
1496 * @namespace YAHOO.widget.Paginator.ui
1497 * @class FirstPageLink
1498 * @for YAHOO.widget.Paginator
1501 * @param p {Pagintor} Paginator instance to attach to
1503 Paginator.ui.FirstPageLink = function (p) {
1506 p.subscribe('recordOffsetChange',this.update,this,true);
1507 p.subscribe('rowsPerPageChange',this.update,this,true);
1508 p.subscribe('totalRecordsChange',this.update,this,true);
1509 p.subscribe('destroy',this.destroy,this,true);
1511 // TODO: make this work
1512 p.subscribe('firstPageLinkLabelChange',this.update,this,true);
1513 p.subscribe('firstPageLinkClassChange',this.update,this,true);
1517 * Decorates Paginator instances with new attributes. Called during
1518 * Paginator instantiation.
1520 * @param p {Paginator} Paginator instance to decorate
1523 Paginator.ui.FirstPageLink.init = function (p) {
1526 * Used as innerHTML for the first page link/span.
1527 * @attribute firstPageLinkLabel
1528 * @default '<< first'
1530 p.setAttributeConfig('firstPageLinkLabel', {
1531 value : '<< first',
1532 validator : l.isString
1536 * CSS class assigned to the link/span
1537 * @attribute firstPageLinkClass
1538 * @default 'yui-pg-first'
1540 p.setAttributeConfig('firstPageLinkClass', {
1541 value : 'yui-pg-first',
1542 validator : l.isString
1546 // Instance members and methods
1547 Paginator.ui.FirstPageLink.prototype = {
1550 * The currently placed HTMLElement node
1566 * Span node (inactive link)
1574 * Generate the nodes and return the appropriate node given the current
1577 * @param id_base {string} used to create unique ids for generated nodes
1578 * @return {HTMLElement}
1580 render : function (id_base) {
1581 var p = this.paginator,
1582 c = p.get('firstPageLinkClass'),
1583 label = p.get('firstPageLinkLabel');
1585 this.link = document.createElement('a');
1586 this.span = document.createElement('span');
1588 this.link.id = id_base + '-first-link';
1589 this.link.href = '#';
1590 this.link.className = c;
1591 this.link.innerHTML = label;
1592 YAHOO.util.Event.on(this.link,'click',this.onClick,this,true);
1594 this.span.id = id_base + '-first-span';
1595 this.span.className = c;
1596 this.span.innerHTML = label;
1598 this.current = p.getCurrentPage() > 1 ? this.link : this.span;
1599 return this.current;
1603 * Swap the link and span nodes if appropriate.
1605 * @param e {CustomEvent} The calling change event
1607 update : function (e) {
1608 if (e && e.prevValue === e.newValue) {
1612 var par = this.current ? this.current.parentNode : null;
1613 if (this.paginator.getCurrentPage() > 1) {
1614 if (par && this.current === this.span) {
1615 par.replaceChild(this.link,this.current);
1616 this.current = this.link;
1619 if (par && this.current === this.link) {
1620 par.replaceChild(this.span,this.current);
1621 this.current = this.span;
1627 * Removes the link/span node and clears event listeners
1632 destroy : function () {
1633 YAHOO.util.Event.purgeElement(this.link);
1634 this.current.parentNode.removeChild(this.current);
1635 this.link = this.span = null;
1639 * Listener for the link's onclick event. Pass new value to setPage method.
1641 * @param e {DOMEvent} The click event
1643 onClick : function (e) {
1644 YAHOO.util.Event.stopEvent(e);
1645 this.paginator.setPage(1);
1652 var Paginator = YAHOO.widget.Paginator,
1656 * ui Component to generate the link to jump to the last page.
1658 * @namespace YAHOO.widget.Paginator.ui
1659 * @class LastPageLink
1660 * @for YAHOO.widget.Paginator
1663 * @param p {Pagintor} Paginator instance to attach to
1665 Paginator.ui.LastPageLink = function (p) {
1668 p.subscribe('recordOffsetChange',this.update,this,true);
1669 p.subscribe('rowsPerPageChange',this.update,this,true);
1670 p.subscribe('totalRecordsChange',this.update,this,true);
1671 p.subscribe('destroy',this.destroy,this,true);
1673 // TODO: make this work
1674 p.subscribe('lastPageLinkLabelChange',this.update,this,true);
1675 p.subscribe('lastPageLinkClassChange', this.update,this,true);
1679 * Decorates Paginator instances with new attributes. Called during
1680 * Paginator instantiation.
1682 * @param paginator {Paginator} Paginator instance to decorate
1685 Paginator.ui.LastPageLink.init = function (p) {
1688 * Used as innerHTML for the last page link/span.
1689 * @attribute lastPageLinkLabel
1690 * @default 'last >>'
1692 p.setAttributeConfig('lastPageLinkLabel', {
1693 value : 'last >>',
1694 validator : l.isString
1698 * CSS class assigned to the link/span
1699 * @attribute lastPageLinkClass
1700 * @default 'yui-pg-last'
1702 p.setAttributeConfig('lastPageLinkClass', {
1703 value : 'yui-pg-last',
1704 validator : l.isString
1708 Paginator.ui.LastPageLink.prototype = {
1711 * Currently placed HTMLElement node
1719 * Link HTMLElement node
1727 * Span node (inactive link)
1735 * Empty place holder node for when the last page link is inappropriate to
1736 * display in any form (unlimited paging).
1745 * Generate the nodes and return the appropriate node given the current
1748 * @param id_base {string} used to create unique ids for generated nodes
1749 * @return {HTMLElement}
1751 render : function (id_base) {
1752 var p = this.paginator,
1753 c = p.get('lastPageLinkClass'),
1754 label = p.get('lastPageLinkLabel'),
1755 last = p.getTotalPages();
1757 this.link = document.createElement('a');
1758 this.span = document.createElement('span');
1759 this.na = this.span.cloneNode(false);
1761 this.link.id = id_base + '-last-link';
1762 this.link.href = '#';
1763 this.link.className = c;
1764 this.link.innerHTML = label;
1765 YAHOO.util.Event.on(this.link,'click',this.onClick,this,true);
1767 this.span.id = id_base + '-last-span';
1768 this.span.className = c;
1769 this.span.innerHTML = label;
1771 this.na.id = id_base + '-last-na';
1774 case Paginator.VALUE_UNLIMITED :
1775 this.current = this.na; break;
1776 case p.getCurrentPage() :
1777 this.current = this.span; break;
1779 this.current = this.link;
1782 return this.current;
1786 * Swap the link, span, and na nodes if appropriate.
1788 * @param e {CustomEvent} The calling change event (ignored)
1790 update : function (e) {
1791 if (e && e.prevValue === e.newValue) {
1795 var par = this.current ? this.current.parentNode : null,
1799 switch (this.paginator.getTotalPages()) {
1800 case Paginator.VALUE_UNLIMITED :
1801 after = this.na; break;
1802 case this.paginator.getCurrentPage() :
1803 after = this.span; break;
1806 if (this.current !== after) {
1807 par.replaceChild(after,this.current);
1808 this.current = after;
1814 * Removes the link/span node and clears event listeners
1818 destroy : function () {
1819 YAHOO.util.Event.purgeElement(this.link);
1820 this.current.parentNode.removeChild(this.current);
1821 this.link = this.span = null;
1825 * Listener for the link's onclick event. Passes to setPage method.
1827 * @param e {DOMEvent} The click event
1829 onClick : function (e) {
1830 YAHOO.util.Event.stopEvent(e);
1831 this.paginator.setPage(this.paginator.getTotalPages());
1838 var Paginator = YAHOO.widget.Paginator,
1842 * ui Component to generate the link to jump to the next page.
1844 * @namespace YAHOO.widget.Paginator.ui
1845 * @class NextPageLink
1846 * @for YAHOO.widget.Paginator
1849 * @param p {Pagintor} Paginator instance to attach to
1851 Paginator.ui.NextPageLink = function (p) {
1854 p.subscribe('recordOffsetChange', this.update,this,true);
1855 p.subscribe('rowsPerPageChange', this.update,this,true);
1856 p.subscribe('totalRecordsChange', this.update,this,true);
1857 p.subscribe('destroy',this.destroy,this,true);
1859 // TODO: make this work
1860 p.subscribe('nextPageLinkLabelChange', this.update,this,true);
1861 p.subscribe('nextPageLinkClassChange', this.update,this,true);
1865 * Decorates Paginator instances with new attributes. Called during
1866 * Paginator instantiation.
1868 * @param p {Paginator} Paginator instance to decorate
1871 Paginator.ui.NextPageLink.init = function (p) {
1874 * Used as innerHTML for the next page link/span.
1875 * @attribute nextPageLinkLabel
1876 * @default 'next >'
1878 p.setAttributeConfig('nextPageLinkLabel', {
1879 value : 'next >',
1880 validator : l.isString
1884 * CSS class assigned to the link/span
1885 * @attribute nextPageLinkClass
1886 * @default 'yui-pg-next'
1888 p.setAttributeConfig('nextPageLinkClass', {
1889 value : 'yui-pg-next',
1890 validator : l.isString
1894 Paginator.ui.NextPageLink.prototype = {
1897 * Currently placed HTMLElement node
1913 * Span node (inactive link)
1922 * Generate the nodes and return the appropriate node given the current
1925 * @param id_base {string} used to create unique ids for generated nodes
1926 * @return {HTMLElement}
1928 render : function (id_base) {
1929 var p = this.paginator,
1930 c = p.get('nextPageLinkClass'),
1931 label = p.get('nextPageLinkLabel'),
1932 last = p.getTotalPages();
1934 this.link = document.createElement('a');
1935 this.span = document.createElement('span');
1937 this.link.id = id_base + '-next-link';
1938 this.link.href = '#';
1939 this.link.className = c;
1940 this.link.innerHTML = label;
1941 YAHOO.util.Event.on(this.link,'click',this.onClick,this,true);
1943 this.span.id = id_base + '-next-span';
1944 this.span.className = c;
1945 this.span.innerHTML = label;
1947 this.current = p.getCurrentPage() === last ? this.span : this.link;
1949 return this.current;
1953 * Swap the link and span nodes if appropriate.
1955 * @param e {CustomEvent} The calling change event
1957 update : function (e) {
1958 if (e && e.prevValue === e.newValue) {
1962 var last = this.paginator.getTotalPages(),
1963 par = this.current ? this.current.parentNode : null;
1965 if (this.paginator.getCurrentPage() !== last) {
1966 if (par && this.current === this.span) {
1967 par.replaceChild(this.link,this.current);
1968 this.current = this.link;
1970 } else if (this.current === this.link) {
1972 par.replaceChild(this.span,this.current);
1973 this.current = this.span;
1979 * Removes the link/span node and clears event listeners
1983 destroy : function () {
1984 YAHOO.util.Event.purgeElement(this.link);
1985 this.current.parentNode.removeChild(this.current);
1986 this.link = this.span = null;
1990 * Listener for the link's onclick event. Passes to setPage method.
1992 * @param e {DOMEvent} The click event
1994 onClick : function (e) {
1995 YAHOO.util.Event.stopEvent(e);
1996 this.paginator.setPage(this.paginator.getNextPage());
2003 var Paginator = YAHOO.widget.Paginator,
2007 * ui Component to generate the link to jump to the previous page.
2009 * @namespace YAHOO.widget.Paginator.ui
2010 * @class PreviousPageLink
2011 * @for YAHOO.widget.Paginator
2014 * @param p {Pagintor} Paginator instance to attach to
2016 Paginator.ui.PreviousPageLink = function (p) {
2019 p.subscribe('recordOffsetChange',this.update,this,true);
2020 p.subscribe('rowsPerPageChange',this.update,this,true);
2021 p.subscribe('totalRecordsChange',this.update,this,true);
2022 p.subscribe('destroy',this.destroy,this,true);
2024 // TODO: make this work
2025 p.subscribe('previousPageLinkLabelChange',this.update,this,true);
2026 p.subscribe('previousPageLinkClassChange',this.update,this,true);
2030 * Decorates Paginator instances with new attributes. Called during
2031 * Paginator instantiation.
2033 * @param p {Paginator} Paginator instance to decorate
2036 Paginator.ui.PreviousPageLink.init = function (p) {
2039 * Used as innerHTML for the previous page link/span.
2040 * @attribute previousPageLinkLabel
2041 * @default '< prev'
2043 p.setAttributeConfig('previousPageLinkLabel', {
2044 value : '< prev',
2045 validator : l.isString
2049 * CSS class assigned to the link/span
2050 * @attribute previousPageLinkClass
2051 * @default 'yui-pg-previous'
2053 p.setAttributeConfig('previousPageLinkClass', {
2054 value : 'yui-pg-previous',
2055 validator : l.isString
2059 Paginator.ui.PreviousPageLink.prototype = {
2062 * Currently placed HTMLElement node
2078 * Span node (inactive link)
2087 * Generate the nodes and return the appropriate node given the current
2090 * @param id_base {string} used to create unique ids for generated nodes
2091 * @return {HTMLElement}
2093 render : function (id_base) {
2094 var p = this.paginator,
2095 c = p.get('previousPageLinkClass'),
2096 label = p.get('previousPageLinkLabel');
2098 this.link = document.createElement('a');
2099 this.span = document.createElement('span');
2101 this.link.id = id_base + '-prev-link';
2102 this.link.href = '#';
2103 this.link.className = c;
2104 this.link.innerHTML = label;
2105 YAHOO.util.Event.on(this.link,'click',this.onClick,this,true);
2107 this.span.id = id_base + '-prev-span';
2108 this.span.className = c;
2109 this.span.innerHTML = label;
2111 this.current = p.getCurrentPage() > 1 ? this.link : this.span;
2112 return this.current;
2116 * Swap the link and span nodes if appropriate.
2118 * @param e {CustomEvent} The calling change event
2120 update : function (e) {
2121 if (e && e.prevValue === e.newValue) {
2125 var par = this.current ? this.current.parentNode : null;
2126 if (this.paginator.getCurrentPage() > 1) {
2127 if (par && this.current === this.span) {
2128 par.replaceChild(this.link,this.current);
2129 this.current = this.link;
2132 if (par && this.current === this.link) {
2133 par.replaceChild(this.span,this.current);
2134 this.current = this.span;
2140 * Removes the link/span node and clears event listeners
2144 destroy : function () {
2145 YAHOO.util.Event.purgeElement(this.link);
2146 this.current.parentNode.removeChild(this.current);
2147 this.link = this.span = null;
2151 * Listener for the link's onclick event. Passes to setPage method.
2153 * @param e {DOMEvent} The click event
2155 onClick : function (e) {
2156 YAHOO.util.Event.stopEvent(e);
2157 this.paginator.setPage(this.paginator.getPreviousPage());
2164 var Paginator = YAHOO.widget.Paginator,
2168 * ui Component to generate the rows-per-page dropdown
2170 * @namespace YAHOO.widget.Paginator.ui
2171 * @class RowsPerPageDropdown
2172 * @for YAHOO.widget.Paginator
2175 * @param p {Pagintor} Paginator instance to attach to
2177 Paginator.ui.RowsPerPageDropdown = function (p) {
2180 p.subscribe('rowsPerPageChange',this.update,this,true);
2181 p.subscribe('rowsPerPageOptionsChange',this.rebuild,this,true);
2182 p.subscribe('totalRecordsChange',this._handleTotalRecordsChange,this,true);
2183 p.subscribe('destroy',this.destroy,this,true);
2185 // TODO: make this work
2186 p.subscribe('rowsPerPageDropdownClassChange',this.rebuild,this,true);
2190 * Decorates Paginator instances with new attributes. Called during
2191 * Paginator instantiation.
2193 * @param p {Paginator} Paginator instance to decorate
2196 Paginator.ui.RowsPerPageDropdown.init = function (p) {
2199 * Array of available rows-per-page sizes. Converted into select options.
2200 * Array values may be positive integers or object literals in the form<br>
2201 * { value : NUMBER, text : STRING }
2202 * @attribute rowsPerPageOptions
2205 p.setAttributeConfig('rowsPerPageOptions', {
2207 validator : l.isArray
2211 * CSS class assigned to the select node
2212 * @attribute rowsPerPageDropdownClass
2213 * @default 'yui-pg-rpp-options'
2215 p.setAttributeConfig('rowsPerPageDropdownClass', {
2216 value : 'yui-pg-rpp-options',
2217 validator : l.isString
2221 Paginator.ui.RowsPerPageDropdown.prototype = {
2233 * option node for the optional All value
2242 * Generate the select and option nodes and returns the select node.
2244 * @param id_base {string} used to create unique ids for generated nodes
2245 * @return {HTMLElement}
2247 render : function (id_base) {
2248 this.select = document.createElement('select');
2249 this.select.id = id_base + '-rpp';
2250 this.select.className = this.paginator.get('rowsPerPageDropdownClass');
2251 this.select.title = 'Rows per page';
2253 YAHOO.util.Event.on(this.select,'change',this.onChange,this,true);
2261 * (Re)generate the select options.
2264 rebuild : function (e) {
2265 var p = this.paginator,
2267 options = p.get('rowsPerPageOptions'),
2272 for (i = 0, len = options.length; i < len; ++i) {
2274 opt = sel.options[i] ||
2275 sel.appendChild(document.createElement('option'));
2276 val = l.isValue(cfg.value) ? cfg.value : cfg;
2277 opt.innerHTML = l.isValue(cfg.text) ? cfg.text : cfg;
2279 if (l.isString(val) && val.toLowerCase() === 'all') {
2281 opt.value = p.get('totalRecords');
2288 while (sel.options.length > options.length) {
2289 sel.removeChild(sel.firstChild);
2296 * Select the appropriate option if changed.
2298 * @param e {CustomEvent} The calling change event
2300 update : function (e) {
2301 if (e && e.prevValue === e.newValue) {
2305 var rpp = this.paginator.get('rowsPerPage')+'',
2306 options = this.select.options,
2309 for (i = 0, len = options.length; i < len; ++i) {
2310 if (options[i].value === rpp) {
2311 options[i].selected = true;
2318 * Listener for the select's onchange event. Sent to setRowsPerPage method.
2320 * @param e {DOMEvent} The change event
2322 onChange : function (e) {
2323 this.paginator.setRowsPerPage(
2324 parseInt(this.select.options[this.select.selectedIndex].value,10));
2328 * Updates the all option value (and Paginator's rowsPerPage attribute if
2329 * necessary) in response to a change in the Paginator's totalRecords.
2331 * @method _handleTotalRecordsChange
2332 * @param e {Event} attribute change event
2335 _handleTotalRecordsChange : function (e) {
2336 if (!this.all || (e && e.prevValue === e.newValue)) {
2340 this.all.value = e.newValue;
2341 if (this.all.selected) {
2342 this.paginator.set('rowsPerPage',e.newValue);
2347 * Removes the select node and clears event listeners
2351 destroy : function () {
2352 YAHOO.util.Event.purgeElement(this.select);
2353 this.select.parentNode.removeChild(this.select);
2359 YAHOO.register("paginator", YAHOO.widget.Paginator, {version: "2.7.0", build: "1799"});