X-Git-Url: https://git.toastfreeware.priv.at/philipp/winterrodeln/wradmin.git/blobdiff_plain/c2d2b07d134a64271aede4886b8c1d0f6960cb4d..582150b643140e3e670d66f244812e314c7aa0c1:/wradmin/static/yui/paginator/paginator-debug.js diff --git a/wradmin/static/yui/paginator/paginator-debug.js b/wradmin/static/yui/paginator/paginator-debug.js new file mode 100644 index 0000000..ebbb9b6 --- /dev/null +++ b/wradmin/static/yui/paginator/paginator-debug.js @@ -0,0 +1,2359 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 2.7.0 +*/ +(function () { +/** + * The Paginator widget provides a set of controls to navigate through paged + * data. + * + * @module paginator + * @uses YAHOO.util.EventProvider + * @uses YAHOO.util.AttributeProvider + */ + +/** + * Instantiate a Paginator, passing a configuration object to the contructor. + * The configuration object should contain the following properties: + * + * + * @namespace YAHOO.widget + * @class Paginator + * @constructor + * @param config {Object} Object literal to set instance and ui component + * configuration. + */ +function Paginator(config) { + var UNLIMITED = Paginator.VALUE_UNLIMITED, + lang = YAHOO.lang, + attrib, initialPage, records, perPage, startIndex; + + config = lang.isObject(config) ? config : {}; + + this.initConfig(); + + this.initEvents(); + + // Set the basic config keys first + this.set('rowsPerPage',config.rowsPerPage,true); + if (Paginator.isNumeric(config.totalRecords)) { + this.set('totalRecords',config.totalRecords,true); + } + + this.initUIComponents(); + + // Update the other config values + for (attrib in config) { + if (lang.hasOwnProperty(config,attrib)) { + this.set(attrib,config[attrib],true); + } + } + + // Calculate the initial record offset + initialPage = this.get('initialPage'); + records = this.get('totalRecords'); + perPage = this.get('rowsPerPage'); + if (initialPage > 1 && perPage !== UNLIMITED) { + startIndex = (initialPage - 1) * perPage; + if (records === UNLIMITED || startIndex < records) { + this.set('recordOffset',startIndex,true); + } + } +} + + +// Static members +YAHOO.lang.augmentObject(Paginator, { + /** + * Incrementing index used to give instances unique ids. + * @static + * @property id + * @type number + * @private + */ + id : 0, + + /** + * Base of id strings used for ui components. + * @static + * @property ID_BASE + * @type string + * @private + */ + ID_BASE : 'yui-pg', + + /** + * Used to identify unset, optional configurations, or used explicitly in + * the case of totalRecords to indicate unlimited pagination. + * @static + * @property VALUE_UNLIMITED + * @type number + * @final + */ + VALUE_UNLIMITED : -1, + + /** + * Default template used by Paginator instances. Update this if you want + * all new Paginators to use a different default template. + * @static + * @property TEMPLATE_DEFAULT + * @type string + */ + TEMPLATE_DEFAULT : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink}", + + /** + * Common alternate pagination format, including page links, links for + * previous, next, first and last pages as well as a rows-per-page + * dropdown. Offered as a convenience. + * @static + * @property TEMPLATE_ROWS_PER_PAGE + * @type string + */ + TEMPLATE_ROWS_PER_PAGE : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}", + + /** + * Storage object for UI Components + * @static + * @property ui + */ + ui : {}, + + /** + * Similar to YAHOO.lang.isNumber, but allows numeric strings. This is + * is used for attribute validation in conjunction with getters that return + * numbers. + * + * @method isNumeric + * @param v {Number|String} value to be checked for number or numeric string + * @returns {Boolean} true if the input is coercable into a finite number + * @static + */ + isNumeric : function (v) { + return isFinite(+v); + }, + + /** + * Return a number or null from input + * + * @method toNumber + * @param n {Number|String} a number or numeric string + * @return Number + * @static + */ + toNumber : function (n) { + return isFinite(+n) ? +n : null; + } + +},true); + + +// Instance members and methods +Paginator.prototype = { + + // Instance members + + /** + * Array of nodes in which to render pagination controls. This is set via + * the "containers" attribute. + * @property _containers + * @type Array(HTMLElement) + * @private + */ + _containers : [], + + /** + * Flag used to indicate multiple attributes are being updated via setState + * @property _batch + * @type boolean + * @protected + */ + _batch : false, + + /** + * Used by setState to indicate when a page change has occurred + * @property _pageChanged + * @type boolean + * @protected + */ + _pageChanged : false, + + /** + * Temporary state cache used by setState to keep track of the previous + * state for eventual pageChange event firing + * @property _state + * @type Object + * @protected + */ + _state : null, + + + // Instance methods + + /** + * Initialize the Paginator's attributes (see YAHOO.util.Element class + * AttributeProvider). + * @method initConfig + * @private + */ + initConfig : function () { + + var UNLIMITED = Paginator.VALUE_UNLIMITED, + l = YAHOO.lang; + + /** + * REQUIRED. Number of records constituting a "page" + * @attribute rowsPerPage + * @type integer + */ + this.setAttributeConfig('rowsPerPage', { + value : 0, + validator : Paginator.isNumeric, + setter : Paginator.toNumber + }); + + /** + * REQUIRED. Node references or ids of nodes in which to render the + * pagination controls. + * @attribute containers + * @type {string|HTMLElement|Array(string|HTMLElement)} + */ + this.setAttributeConfig('containers', { + value : null, + validator : function (val) { + if (!l.isArray(val)) { + val = [val]; + } + for (var i = 0, len = val.length; i < len; ++i) { + if (l.isString(val[i]) || + (l.isObject(val[i]) && val[i].nodeType === 1)) { + continue; + } + return false; + } + return true; + }, + method : function (val) { + val = YAHOO.util.Dom.get(val); + if (!l.isArray(val)) { + val = [val]; + } + this._containers = val; + } + }); + + /** + * Total number of records to paginate through + * @attribute totalRecords + * @type integer + * @default 0 + */ + this.setAttributeConfig('totalRecords', { + value : 0, + validator : Paginator.isNumeric, + setter : Paginator.toNumber + }); + + /** + * Zero based index of the record considered first on the current page. + * For page based interactions, don't modify this attribute directly; + * use setPage(n). + * @attribute recordOffset + * @type integer + * @default 0 + */ + this.setAttributeConfig('recordOffset', { + value : 0, + validator : function (val) { + var total = this.get('totalRecords'); + if (Paginator.isNumeric(val)) { + val = +val; + return total === UNLIMITED || total > val || + (total === 0 && val === 0); + } + + return false; + }, + setter : Paginator.toNumber + }); + + /** + * Page to display on initial paint + * @attribute initialPage + * @type integer + * @default 1 + */ + this.setAttributeConfig('initialPage', { + value : 1, + validator : Paginator.isNumeric, + setter : Paginator.toNumber + }); + + /** + * Template used to render controls. The string will be used as + * innerHTML on all specified container nodes. Bracketed keys + * (e.g. {pageLinks}) in the string will be replaced with an instance + * of the so named ui component. + * @see Paginator.TEMPLATE_DEFAULT + * @see Paginator.TEMPLATE_ROWS_PER_PAGE + * @attribute template + * @type string + */ + this.setAttributeConfig('template', { + value : Paginator.TEMPLATE_DEFAULT, + validator : l.isString + }); + + /** + * Class assigned to the element(s) containing pagination controls. + * @attribute containerClass + * @type string + * @default 'yui-pg-container' + */ + this.setAttributeConfig('containerClass', { + value : 'yui-pg-container', + validator : l.isString + }); + + /** + * Display pagination controls even when there is only one page. Set + * to false to forgo rendering and/or hide the containers when there + * is only one page of data. Note if you are using the rowsPerPage + * dropdown ui component, visibility will be maintained as long as the + * number of records exceeds the smallest page size. + * @attribute alwaysVisible + * @type boolean + * @default true + */ + this.setAttributeConfig('alwaysVisible', { + value : true, + validator : l.isBoolean + }); + + /** + * Update the UI immediately upon interaction. If false, changeRequest + * subscribers or other external code will need to explicitly set the + * new values in the paginator to trigger repaint. + * @attribute updateOnChange + * @type boolean + * @default false + */ + this.setAttributeConfig('updateOnChange', { + value : false, + validator : l.isBoolean + }); + + + + // Read only attributes + + /** + * Unique id assigned to this instance + * @attribute id + * @type integer + * @final + */ + this.setAttributeConfig('id', { + value : Paginator.id++, + readOnly : true + }); + + /** + * Indicator of whether the DOM nodes have been initially created + * @attribute rendered + * @type boolean + * @final + */ + this.setAttributeConfig('rendered', { + value : false, + readOnly : true + }); + + }, + + /** + * Initialize registered ui components onto this instance. + * @method initUIComponents + * @private + */ + initUIComponents : function () { + var ui = Paginator.ui, + name,UIComp; + for (name in ui) { + if (YAHOO.lang.hasOwnProperty(ui,name)) { + UIComp = ui[name]; + if (YAHOO.lang.isObject(UIComp) && + YAHOO.lang.isFunction(UIComp.init)) { + UIComp.init(this); + } + } + } + }, + + /** + * Initialize this instance's CustomEvents. + * @method initEvents + * @private + */ + initEvents : function () { + /** + * Event fired when the Paginator is initially rendered + * @event render + */ + this.createEvent('render'); + + /** + * Event fired when the Paginator is initially rendered + * @event rendered + * @deprecated use render event + */ + this.createEvent('rendered'); // backward compatibility + + /** + * Event fired when a change in pagination values is requested, + * either by interacting with the various ui components or via the + * setStartIndex(n) etc APIs. + * Subscribers will receive the proposed state as the first parameter. + * The proposed state object will contain the following keys: + * + * @event changeRequest + */ + this.createEvent('changeRequest'); + + /** + * Event fired when attribute changes have resulted in the calculated + * current page changing. + * @event pageChange + */ + this.createEvent('pageChange'); + + /** + * Event that fires before the destroy event. + * @event beforeDestroy + */ + this.createEvent('beforeDestroy'); + + /** + * Event used to trigger cleanup of ui components + * @event destroy + */ + this.createEvent('destroy'); + + this._selfSubscribe(); + }, + + /** + * Subscribes to instance attribute change events to automate certain + * behaviors. + * @method _selfSubscribe + * @protected + */ + _selfSubscribe : function () { + // Listen for changes to totalRecords and alwaysVisible + this.subscribe('totalRecordsChange',this.updateVisibility,this,true); + this.subscribe('alwaysVisibleChange',this.updateVisibility,this,true); + + // Fire the pageChange event when appropriate + this.subscribe('totalRecordsChange',this._handleStateChange,this,true); + this.subscribe('recordOffsetChange',this._handleStateChange,this,true); + this.subscribe('rowsPerPageChange',this._handleStateChange,this,true); + + // Update recordOffset when totalRecords is reduced below + this.subscribe('totalRecordsChange',this._syncRecordOffset,this,true); + }, + + /** + * Sets recordOffset to the starting index of the previous page when + * totalRecords is reduced below the current recordOffset. + * @method _syncRecordOffset + * @param e {Event} totalRecordsChange event + * @protected + */ + _syncRecordOffset : function (e) { + var v = e.newValue,rpp,state; + if (e.prevValue !== v) { + if (v !== Paginator.VALUE_UNLIMITED) { + rpp = this.get('rowsPerPage'); + + if (rpp && this.get('recordOffset') >= v) { + state = this.getState({ + totalRecords : e.prevValue, + recordOffset : this.get('recordOffset') + }); + + this.set('recordOffset', state.before.recordOffset); + this._firePageChange(state); + } + } + } + }, + + /** + * Fires the pageChange event when the state attributes have changed in + * such a way as to locate the current recordOffset on a new page. + * @method _handleStateChange + * @param e {Event} the attribute change event + * @protected + */ + _handleStateChange : function (e) { + if (e.prevValue !== e.newValue) { + var change = this._state || {}, + state; + + change[e.type.replace(/Change$/,'')] = e.prevValue; + state = this.getState(change); + + if (state.page !== state.before.page) { + if (this._batch) { + this._pageChanged = true; + } else { + this._firePageChange(state); + } + } + } + }, + + /** + * Fires a pageChange event in the form of a standard attribute change + * event with additional properties prevState and newState. + * @method _firePageChange + * @param state {Object} the result of getState(oldState) + * @protected + */ + _firePageChange : function (state) { + if (YAHOO.lang.isObject(state)) { + var current = state.before; + delete state.before; + this.fireEvent('pageChange',{ + type : 'pageChange', + prevValue : state.page, + newValue : current.page, + prevState : state, + newState : current + }); + } + }, + + /** + * Render the pagination controls per the format attribute into the + * specified container nodes. + * @method render + */ + render : function () { + if (this.get('rendered')) { + return; + } + + // Forgo rendering if only one page and alwaysVisible is off + var totalRecords = this.get('totalRecords'), + Dom = YAHOO.util.Dom, + template = this.get('template'), + containerClass = this.get('containerClass'), + i,len,c,id_base,markers,j,jlen,m,mp,name,UIComp,comp; + + if (totalRecords !== Paginator.VALUE_UNLIMITED && + totalRecords < this.get('rowsPerPage') && + !this.get('alwaysVisible')) { + return; + } + + // add marker spans to the template html to indicate drop zones + // for ui components + template = template.replace(/\{([a-z0-9_ \-]+)\}/gi, + ''); + for (i = 0, len = this._containers.length; i < len; ++i) { + c = this._containers[i]; + // ex. yui-pg0-1 (first paginator, second container) + id_base = Paginator.ID_BASE + this.get('id') + '-' + i; + + if (!c) { + continue; + } + // Hide the container while its contents are rendered + c.style.display = 'none'; + + Dom.addClass(c,containerClass); + + // Place the template innerHTML + c.innerHTML = template; + + // Replace each marker with the ui component's render() output + markers = Dom.getElementsByClassName('yui-pg-ui','span',c); + + for (j = 0, jlen = markers.length; j < jlen; ++j) { + m = markers[j]; + mp = m.parentNode; + name = m.className.replace(/\s*yui-pg-ui\s+/g,''); + UIComp = Paginator.ui[name]; + + if (YAHOO.lang.isFunction(UIComp)) { + comp = new UIComp(this); + if (YAHOO.lang.isFunction(comp.render)) { + mp.replaceChild(comp.render(id_base),m); + } + } + } + + // Show the container allowing page reflow + c.style.display = ''; + } + + // Set render attribute manually to support its readOnly contract + if (this._containers.length) { + this.setAttributeConfig('rendered',{value:true}); + + this.fireEvent('render',this.getState()); + // For backward compatibility + this.fireEvent('rendered',this.getState()); + } + }, + + /** + * Removes controls from the page and unhooks events. + * @method destroy + */ + destroy : function () { + this.fireEvent('beforeDestroy'); + this.fireEvent('destroy'); + + this.setAttributeConfig('rendered',{value:false}); + }, + + /** + * Hides the containers if there is only one page of data and attribute + * alwaysVisible is false. Conversely, it displays the containers if either + * there is more than one page worth of data or alwaysVisible is turned on. + * @method updateVisibility + */ + updateVisibility : function (e) { + var alwaysVisible = this.get('alwaysVisible'), + totalRecords,visible,rpp,rppOptions,i,len; + + if (e.type === 'alwaysVisibleChange' || !alwaysVisible) { + totalRecords = this.get('totalRecords'); + visible = true; + rpp = this.get('rowsPerPage'); + rppOptions = this.get('rowsPerPageOptions'); + + if (YAHOO.lang.isArray(rppOptions)) { + for (i = 0, len = rppOptions.length; i < len; ++i) { + rpp = Math.min(rpp,rppOptions[i]); + } + } + + if (totalRecords !== Paginator.VALUE_UNLIMITED && + totalRecords <= rpp) { + visible = false; + } + + visible = visible || alwaysVisible; + + for (i = 0, len = this._containers.length; i < len; ++i) { + YAHOO.util.Dom.setStyle(this._containers[i],'display', + visible ? '' : 'none'); + } + } + }, + + + + + /** + * Get the configured container nodes + * @method getContainerNodes + * @return {Array} array of HTMLElement nodes + */ + getContainerNodes : function () { + return this._containers; + }, + + /** + * Get the total number of pages in the data set according to the current + * rowsPerPage and totalRecords values. If totalRecords is not set, or + * set to YAHOO.widget.Paginator.VALUE_UNLIMITED, returns + * YAHOO.widget.Paginator.VALUE_UNLIMITED. + * @method getTotalPages + * @return {number} + */ + getTotalPages : function () { + var records = this.get('totalRecords'), + perPage = this.get('rowsPerPage'); + + // rowsPerPage not set. Can't calculate + if (!perPage) { + return null; + } + + if (records === Paginator.VALUE_UNLIMITED) { + return Paginator.VALUE_UNLIMITED; + } + + return Math.ceil(records/perPage); + }, + + /** + * Does the requested page have any records? + * @method hasPage + * @param page {number} the page in question + * @return {boolean} + */ + hasPage : function (page) { + if (!YAHOO.lang.isNumber(page) || page < 1) { + return false; + } + + var totalPages = this.getTotalPages(); + + return (totalPages === Paginator.VALUE_UNLIMITED || totalPages >= page); + }, + + /** + * Get the page number corresponding to the current record offset. + * @method getCurrentPage + * @return {number} + */ + getCurrentPage : function () { + var perPage = this.get('rowsPerPage'); + if (!perPage || !this.get('totalRecords')) { + return 0; + } + return Math.floor(this.get('recordOffset') / perPage) + 1; + }, + + /** + * Are there records on the next page? + * @method hasNextPage + * @return {boolean} + */ + hasNextPage : function () { + var currentPage = this.getCurrentPage(), + totalPages = this.getTotalPages(); + + return currentPage && (totalPages === Paginator.VALUE_UNLIMITED || currentPage < totalPages); + }, + + /** + * Get the page number of the next page, or null if the current page is the + * last page. + * @method getNextPage + * @return {number} + */ + getNextPage : function () { + return this.hasNextPage() ? this.getCurrentPage() + 1 : null; + }, + + /** + * Is there a page before the current page? + * @method hasPreviousPage + * @return {boolean} + */ + hasPreviousPage : function () { + return (this.getCurrentPage() > 1); + }, + + /** + * Get the page number of the previous page, or null if the current page + * is the first page. + * @method getPreviousPage + * @return {number} + */ + getPreviousPage : function () { + return (this.hasPreviousPage() ? this.getCurrentPage() - 1 : 1); + }, + + /** + * Get the start and end record indexes of the specified page. + * @method getPageRecords + * @param page {number} (optional) The page (current page if not specified) + * @return {Array} [start_index, end_index] + */ + getPageRecords : function (page) { + if (!YAHOO.lang.isNumber(page)) { + page = this.getCurrentPage(); + } + + var perPage = this.get('rowsPerPage'), + records = this.get('totalRecords'), + start, end; + + if (!page || !perPage) { + return null; + } + + start = (page - 1) * perPage; + if (records !== Paginator.VALUE_UNLIMITED) { + if (start >= records) { + return null; + } + end = Math.min(start + perPage, records) - 1; + } else { + end = start + perPage - 1; + } + + return [start,end]; + }, + + /** + * Set the current page to the provided page number if possible. + * @method setPage + * @param newPage {number} the new page number + * @param silent {boolean} whether to forcibly avoid firing the + * changeRequest event + */ + setPage : function (page,silent) { + if (this.hasPage(page) && page !== this.getCurrentPage()) { + if (this.get('updateOnChange') || silent) { + this.set('recordOffset', (page - 1) * this.get('rowsPerPage')); + } else { + this.fireEvent('changeRequest',this.getState({'page':page})); + } + } + }, + + /** + * Get the number of rows per page. + * @method getRowsPerPage + * @return {number} the current setting of the rowsPerPage attribute + */ + getRowsPerPage : function () { + return this.get('rowsPerPage'); + }, + + /** + * Set the number of rows per page. + * @method setRowsPerPage + * @param rpp {number} the new number of rows per page + * @param silent {boolean} whether to forcibly avoid firing the + * changeRequest event + */ + setRowsPerPage : function (rpp,silent) { + if (Paginator.isNumeric(rpp) && +rpp > 0 && + +rpp !== this.get('rowsPerPage')) { + if (this.get('updateOnChange') || silent) { + this.set('rowsPerPage',rpp); + } else { + this.fireEvent('changeRequest', + this.getState({'rowsPerPage':+rpp})); + } + } + }, + + /** + * Get the total number of records. + * @method getTotalRecords + * @return {number} the current setting of totalRecords attribute + */ + getTotalRecords : function () { + return this.get('totalRecords'); + }, + + /** + * Set the total number of records. + * @method setTotalRecords + * @param total {number} the new total number of records + * @param silent {boolean} whether to forcibly avoid firing the changeRequest event + */ + setTotalRecords : function (total,silent) { + if (Paginator.isNumeric(total) && +total >= 0 && + +total !== this.get('totalRecords')) { + if (this.get('updateOnChange') || silent) { + this.set('totalRecords',total); + } else { + this.fireEvent('changeRequest', + this.getState({'totalRecords':+total})); + } + } + }, + + /** + * Get the index of the first record on the current page + * @method getStartIndex + * @return {number} the index of the first record on the current page + */ + getStartIndex : function () { + return this.get('recordOffset'); + }, + + /** + * Move the record offset to a new starting index. This will likely cause + * the calculated current page to change. You should probably use setPage. + * @method setStartIndex + * @param offset {number} the new record offset + * @param silent {boolean} whether to forcibly avoid firing the changeRequest event + */ + setStartIndex : function (offset,silent) { + if (Paginator.isNumeric(offset) && +offset >= 0 && + +offset !== this.get('recordOffset')) { + if (this.get('updateOnChange') || silent) { + this.set('recordOffset',offset); + } else { + this.fireEvent('changeRequest', + this.getState({'recordOffset':+offset})); + } + } + }, + + /** + * Get an object literal describing the current state of the paginator. If + * an object literal of proposed values is passed, the proposed state will + * be returned as an object literal with the following keys: + * + * @method getState + * @return {object} + * @param changes {object} OPTIONAL object literal with proposed values + * Supported change keys include: + * + */ + getState : function (changes) { + var UNLIMITED = Paginator.VALUE_UNLIMITED, + M = Math, max = M.max, ceil = M.ceil, + currentState, state, offset; + + function normalizeOffset(offset,total,rpp) { + if (offset <= 0 || total === 0) { + return 0; + } + if (total === UNLIMITED || total > offset) { + return offset - (offset % rpp); + } + return total - (total % rpp || rpp); + } + + currentState = { + paginator : this, + totalRecords : this.get('totalRecords'), + rowsPerPage : this.get('rowsPerPage'), + records : this.getPageRecords() + }; + currentState.recordOffset = normalizeOffset( + this.get('recordOffset'), + currentState.totalRecords, + currentState.rowsPerPage); + currentState.page = ceil(currentState.recordOffset / + currentState.rowsPerPage) + 1; + + if (!changes) { + return currentState; + } + + state = { + paginator : this, + before : currentState, + + rowsPerPage : changes.rowsPerPage || currentState.rowsPerPage, + totalRecords : (Paginator.isNumeric(changes.totalRecords) ? + max(changes.totalRecords,UNLIMITED) : + +currentState.totalRecords) + }; + + if (state.totalRecords === 0) { + state.recordOffset = + state.page = 0; + } else { + offset = Paginator.isNumeric(changes.page) ? + (changes.page - 1) * state.rowsPerPage : + Paginator.isNumeric(changes.recordOffset) ? + +changes.recordOffset : + currentState.recordOffset; + + state.recordOffset = normalizeOffset(offset, + state.totalRecords, + state.rowsPerPage); + + state.page = ceil(state.recordOffset / state.rowsPerPage) + 1; + } + + state.records = [ state.recordOffset, + state.recordOffset + state.rowsPerPage - 1 ]; + + // limit upper index to totalRecords - 1 + if (state.totalRecords !== UNLIMITED && + state.recordOffset < state.totalRecords && state.records && + state.records[1] > state.totalRecords - 1) { + state.records[1] = state.totalRecords - 1; + } + + return state; + }, + + /** + * Convenience method to facilitate setting state attributes rowsPerPage, + * totalRecords, recordOffset in batch. Also supports calculating + * recordOffset from state.page if state.recordOffset is not provided. + * Fires only a single pageChange event, if appropriate. + * This will not fire a changeRequest event. + * @method setState + * @param state {Object} Object literal of attribute:value pairs to set + */ + setState : function (state) { + if (YAHOO.lang.isObject(state)) { + // get flux state based on current state with before state as well + this._state = this.getState({}); + + // use just the state props from the input obj + state = { + page : state.page, + rowsPerPage : state.rowsPerPage, + totalRecords : state.totalRecords, + recordOffset : state.recordOffset + }; + + // calculate recordOffset from page if recordOffset not specified. + // not using lang.isNumber for support of numeric strings + if (state.page && state.recordOffset === undefined) { + state.recordOffset = (state.page - 1) * + (state.rowsPerPage || this.get('rowsPerPage')); + } + + this._batch = true; + this._pageChanged = false; + + for (var k in state) { + if (state.hasOwnProperty(k)) { + this.set(k,state[k]); + } + } + + this._batch = false; + + if (this._pageChanged) { + this._pageChanged = false; + + this._firePageChange(this.getState(this._state)); + } + } + } +}; + +YAHOO.lang.augmentProto(Paginator, YAHOO.util.AttributeProvider); + +YAHOO.widget.Paginator = Paginator; +})(); +(function () { + +var Paginator = YAHOO.widget.Paginator, + l = YAHOO.lang; + +/** + * ui Component to generate the textual report of current pagination status. + * E.g. "Now viewing page 1 of 13". + * + * @namespace YAHOO.widget.Paginator.ui + * @class CurrentPageReport + * @for YAHOO.widget.Paginator + * + * @constructor + * @param p {Pagintor} Paginator instance to attach to + */ +Paginator.ui.CurrentPageReport = function (p) { + this.paginator = p; + + p.subscribe('recordOffsetChange', this.update,this,true); + p.subscribe('rowsPerPageChange', this.update,this,true); + p.subscribe('totalRecordsChange',this.update,this,true); + p.subscribe('pageReportTemplateChange', this.update,this,true); + p.subscribe('destroy',this.destroy,this,true); + + //TODO: make this work + p.subscribe('pageReportClassChange', this.update,this,true); +}; + +/** + * Decorates Paginator instances with new attributes. Called during + * Paginator instantiation. + * @method init + * @param p {Paginator} Paginator instance to decorate + * @static + */ +Paginator.ui.CurrentPageReport.init = function (p) { + + /** + * CSS class assigned to the span containing the info. + * @attribute pageReportClass + * @default 'yui-pg-current' + */ + p.setAttributeConfig('pageReportClass', { + value : 'yui-pg-current', + validator : l.isString + }); + + /** + * Used as innerHTML for the span. Place holders in the form of {name} + * will be replaced with the so named value from the key:value map + * generated by the function held in the pageReportValueGenerator attribute. + * @attribute pageReportTemplate + * @default '({currentPage} of {totalPages})' + * @see pageReportValueGenerator attribute + */ + p.setAttributeConfig('pageReportTemplate', { + value : '({currentPage} of {totalPages})', + validator : l.isString + }); + + /** + * Function to generate the value map used to populate the + * pageReportTemplate. The function is passed the Paginator instance as a + * parameter. The default function returns a map with the following keys: + * + * @attribute pageReportValueGenarator + */ + p.setAttributeConfig('pageReportValueGenerator', { + value : function (paginator) { + var curPage = paginator.getCurrentPage(), + records = paginator.getPageRecords(); + + return { + 'currentPage' : records ? curPage : 0, + 'totalPages' : paginator.getTotalPages(), + 'startIndex' : records ? records[0] : 0, + 'endIndex' : records ? records[1] : 0, + 'startRecord' : records ? records[0] + 1 : 0, + 'endRecord' : records ? records[1] + 1 : 0, + 'totalRecords': paginator.get('totalRecords') + }; + }, + validator : l.isFunction + }); +}; + +/** + * Replace place holders in a string with the named values found in an + * object literal. + * @static + * @method sprintf + * @param template {string} The content string containing place holders + * @param values {object} The key:value pairs used to replace the place holders + * @return {string} + */ +Paginator.ui.CurrentPageReport.sprintf = function (template, values) { + return template.replace(/\{([\w\s\-]+)\}/g, function (x,key) { + return (key in values) ? values[key] : ''; + }); +}; + +Paginator.ui.CurrentPageReport.prototype = { + + /** + * Span node containing the formatted info + * @property span + * @type HTMLElement + * @private + */ + span : null, + + + /** + * Generate the span containing info formatted per the pageReportTemplate + * attribute. + * @method render + * @param id_base {string} used to create unique ids for generated nodes + * @return {HTMLElement} + */ + render : function (id_base) { + this.span = document.createElement('span'); + this.span.id = id_base + '-page-report'; + this.span.className = this.paginator.get('pageReportClass'); + this.update(); + + return this.span; + }, + + /** + * Regenerate the content of the span if appropriate. Calls + * CurrentPageReport.sprintf with the value of the pageReportTemplate + * attribute and the value map returned from pageReportValueGenerator + * function. + * @method update + * @param e {CustomEvent} The calling change event + */ + update : function (e) { + if (e && e.prevValue === e.newValue) { + return; + } + + this.span.innerHTML = Paginator.ui.CurrentPageReport.sprintf( + this.paginator.get('pageReportTemplate'), + this.paginator.get('pageReportValueGenerator')(this.paginator)); + }, + + /** + * Removes the link/span node and clears event listeners + * removal. + * @method destroy + * @private + */ + destroy : function () { + this.span.parentNode.removeChild(this.span); + this.span = null; + } + +}; + +})(); +(function () { + +var Paginator = YAHOO.widget.Paginator, + l = YAHOO.lang; + +/** + * ui Component to generate the page links + * + * @namespace YAHOO.widget.Paginator.ui + * @class PageLinks + * @for YAHOO.widget.Paginator + * + * @constructor + * @param p {Pagintor} Paginator instance to attach to + */ +Paginator.ui.PageLinks = function (p) { + this.paginator = p; + + p.subscribe('recordOffsetChange',this.update,this,true); + p.subscribe('rowsPerPageChange',this.update,this,true); + p.subscribe('totalRecordsChange',this.update,this,true); + p.subscribe('pageLinksChange', this.rebuild,this,true); + p.subscribe('pageLinkClassChange', this.rebuild,this,true); + p.subscribe('currentPageClassChange', this.rebuild,this,true); + p.subscribe('destroy',this.destroy,this,true); + + //TODO: Make this work + p.subscribe('pageLinksContainerClassChange', this.rebuild,this,true); +}; + +/** + * Decorates Paginator instances with new attributes. Called during + * Paginator instantiation. + * @method init + * @param p {Paginator} Paginator instance to decorate + * @static + */ +Paginator.ui.PageLinks.init = function (p) { + + /** + * CSS class assigned to each page link/span. + * @attribute pageLinkClass + * @default 'yui-pg-page' + */ + p.setAttributeConfig('pageLinkClass', { + value : 'yui-pg-page', + validator : l.isString + }); + + /** + * CSS class assigned to the current page span. + * @attribute currentPageClass + * @default 'yui-pg-current-page' + */ + p.setAttributeConfig('currentPageClass', { + value : 'yui-pg-current-page', + validator : l.isString + }); + + /** + * CSS class assigned to the span containing the page links. + * @attribute pageLinksContainerClass + * @default 'yui-pg-pages' + */ + p.setAttributeConfig('pageLinksContainerClass', { + value : 'yui-pg-pages', + validator : l.isString + }); + + /** + * Maximum number of page links to display at one time. + * @attribute pageLinks + * @default 10 + */ + p.setAttributeConfig('pageLinks', { + value : 10, + validator : Paginator.isNumeric + }); + + /** + * Function used generate the innerHTML for each page link/span. The + * function receives as parameters the page number and a reference to the + * paginator object. + * @attribute pageLabelBuilder + * @default function (page, paginator) { return page; } + */ + p.setAttributeConfig('pageLabelBuilder', { + value : function (page, paginator) { return page; }, + validator : l.isFunction + }); +}; + +/** + * Calculates start and end page numbers given a current page, attempting + * to keep the current page in the middle + * @static + * @method calculateRange + * @param {int} currentPage The current page + * @param {int} totalPages (optional) Maximum number of pages + * @param {int} numPages (optional) Preferred number of pages in range + * @return {Array} [start_page_number, end_page_number] + */ +Paginator.ui.PageLinks.calculateRange = function (currentPage,totalPages,numPages) { + var UNLIMITED = Paginator.VALUE_UNLIMITED, + start, end, delta; + + // Either has no pages, or unlimited pages. Show none. + if (!currentPage || numPages === 0 || totalPages === 0 || + (totalPages === UNLIMITED && numPages === UNLIMITED)) { + return [0,-1]; + } + + // Limit requested pageLinks if there are fewer totalPages + if (totalPages !== UNLIMITED) { + numPages = numPages === UNLIMITED ? + totalPages : + Math.min(numPages,totalPages); + } + + // Determine start and end, trying to keep current in the middle + start = Math.max(1,Math.ceil(currentPage - (numPages/2))); + if (totalPages === UNLIMITED) { + end = start + numPages - 1; + } else { + end = Math.min(totalPages, start + numPages - 1); + } + + // Adjust the start index when approaching the last page + delta = numPages - (end - start + 1); + start = Math.max(1, start - delta); + + return [start,end]; +}; + + +Paginator.ui.PageLinks.prototype = { + + /** + * Current page + * @property current + * @type number + * @private + */ + current : 0, + + /** + * Span node containing the page links + * @property container + * @type HTMLElement + * @private + */ + container : null, + + + /** + * Generate the nodes and return the container node containing page links + * appropriate to the current pagination state. + * @method render + * @param id_base {string} used to create unique ids for generated nodes + * @return {HTMLElement} + */ + render : function (id_base) { + var p = this.paginator; + + // Set up container + this.container = document.createElement('span'); + this.container.id = id_base + '-pages'; + this.container.className = p.get('pageLinksContainerClass'); + YAHOO.util.Event.on(this.container,'click',this.onClick,this,true); + + // Call update, flagging a need to rebuild + this.update({newValue : null, rebuild : true}); + + return this.container; + }, + + /** + * Update the links if appropriate + * @method update + * @param e {CustomEvent} The calling change event + */ + update : function (e) { + if (e && e.prevValue === e.newValue) { + return; + } + + var p = this.paginator, + currentPage = p.getCurrentPage(); + + // Replace content if there's been a change + if (this.current !== currentPage || !currentPage || e.rebuild) { + var labelBuilder = p.get('pageLabelBuilder'), + range = Paginator.ui.PageLinks.calculateRange( + currentPage, + p.getTotalPages(), + p.get('pageLinks')), + start = range[0], + end = range[1], + content = '', + linkTemplate,i; + + linkTemplate = '' + + labelBuilder(i,p) + ''; + } else { + content += + linkTemplate + i + '">' + labelBuilder(i,p) + ''; + } + } + + this.container.innerHTML = content; + } + }, + + /** + * Force a rebuild of the page links. + * @method rebuild + * @param e {CustomEvent} The calling change event + */ + rebuild : function (e) { + e.rebuild = true; + this.update(e); + }, + + /** + * Removes the page links container node and clears event listeners + * @method destroy + * @private + */ + destroy : function () { + YAHOO.util.Event.purgeElement(this.container,true); + this.container.parentNode.removeChild(this.container); + this.container = null; + }, + + /** + * Listener for the container's onclick event. Looks for qualifying link + * clicks, and pulls the page number from the link's page attribute. + * Sends link's page attribute to the Paginator's setPage method. + * @method onClick + * @param e {DOMEvent} The click event + */ + onClick : function (e) { + var t = YAHOO.util.Event.getTarget(e); + if (t && YAHOO.util.Dom.hasClass(t, + this.paginator.get('pageLinkClass'))) { + + YAHOO.util.Event.stopEvent(e); + + this.paginator.setPage(parseInt(t.getAttribute('page'),10)); + } + } + +}; + +})(); +(function () { + +var Paginator = YAHOO.widget.Paginator, + l = YAHOO.lang; + +/** + * ui Component to generate the link to jump to the first page. + * + * @namespace YAHOO.widget.Paginator.ui + * @class FirstPageLink + * @for YAHOO.widget.Paginator + * + * @constructor + * @param p {Pagintor} Paginator instance to attach to + */ +Paginator.ui.FirstPageLink = function (p) { + this.paginator = p; + + p.subscribe('recordOffsetChange',this.update,this,true); + p.subscribe('rowsPerPageChange',this.update,this,true); + p.subscribe('totalRecordsChange',this.update,this,true); + p.subscribe('destroy',this.destroy,this,true); + + // TODO: make this work + p.subscribe('firstPageLinkLabelChange',this.update,this,true); + p.subscribe('firstPageLinkClassChange',this.update,this,true); +}; + +/** + * Decorates Paginator instances with new attributes. Called during + * Paginator instantiation. + * @method init + * @param p {Paginator} Paginator instance to decorate + * @static + */ +Paginator.ui.FirstPageLink.init = function (p) { + + /** + * Used as innerHTML for the first page link/span. + * @attribute firstPageLinkLabel + * @default '<< first' + */ + p.setAttributeConfig('firstPageLinkLabel', { + value : '<< first', + validator : l.isString + }); + + /** + * CSS class assigned to the link/span + * @attribute firstPageLinkClass + * @default 'yui-pg-first' + */ + p.setAttributeConfig('firstPageLinkClass', { + value : 'yui-pg-first', + validator : l.isString + }); +}; + +// Instance members and methods +Paginator.ui.FirstPageLink.prototype = { + + /** + * The currently placed HTMLElement node + * @property current + * @type HTMLElement + * @private + */ + current : null, + + /** + * Link node + * @property link + * @type HTMLElement + * @private + */ + link : null, + + /** + * Span node (inactive link) + * @property span + * @type HTMLElement + * @private + */ + span : null, + + /** + * Generate the nodes and return the appropriate node given the current + * pagination state. + * @method render + * @param id_base {string} used to create unique ids for generated nodes + * @return {HTMLElement} + */ + render : function (id_base) { + var p = this.paginator, + c = p.get('firstPageLinkClass'), + label = p.get('firstPageLinkLabel'); + + this.link = document.createElement('a'); + this.span = document.createElement('span'); + + this.link.id = id_base + '-first-link'; + this.link.href = '#'; + this.link.className = c; + this.link.innerHTML = label; + YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); + + this.span.id = id_base + '-first-span'; + this.span.className = c; + this.span.innerHTML = label; + + this.current = p.getCurrentPage() > 1 ? this.link : this.span; + return this.current; + }, + + /** + * Swap the link and span nodes if appropriate. + * @method update + * @param e {CustomEvent} The calling change event + */ + update : function (e) { + if (e && e.prevValue === e.newValue) { + return; + } + + var par = this.current ? this.current.parentNode : null; + if (this.paginator.getCurrentPage() > 1) { + if (par && this.current === this.span) { + par.replaceChild(this.link,this.current); + this.current = this.link; + } + } else { + if (par && this.current === this.link) { + par.replaceChild(this.span,this.current); + this.current = this.span; + } + } + }, + + /** + * Removes the link/span node and clears event listeners + * removal. + * @method destroy + * @private + */ + destroy : function () { + YAHOO.util.Event.purgeElement(this.link); + this.current.parentNode.removeChild(this.current); + this.link = this.span = null; + }, + + /** + * Listener for the link's onclick event. Pass new value to setPage method. + * @method onClick + * @param e {DOMEvent} The click event + */ + onClick : function (e) { + YAHOO.util.Event.stopEvent(e); + this.paginator.setPage(1); + } +}; + +})(); +(function () { + +var Paginator = YAHOO.widget.Paginator, + l = YAHOO.lang; + +/** + * ui Component to generate the link to jump to the last page. + * + * @namespace YAHOO.widget.Paginator.ui + * @class LastPageLink + * @for YAHOO.widget.Paginator + * + * @constructor + * @param p {Pagintor} Paginator instance to attach to + */ +Paginator.ui.LastPageLink = function (p) { + this.paginator = p; + + p.subscribe('recordOffsetChange',this.update,this,true); + p.subscribe('rowsPerPageChange',this.update,this,true); + p.subscribe('totalRecordsChange',this.update,this,true); + p.subscribe('destroy',this.destroy,this,true); + + // TODO: make this work + p.subscribe('lastPageLinkLabelChange',this.update,this,true); + p.subscribe('lastPageLinkClassChange', this.update,this,true); +}; + +/** + * Decorates Paginator instances with new attributes. Called during + * Paginator instantiation. + * @method init + * @param paginator {Paginator} Paginator instance to decorate + * @static + */ +Paginator.ui.LastPageLink.init = function (p) { + + /** + * Used as innerHTML for the last page link/span. + * @attribute lastPageLinkLabel + * @default 'last >>' + */ + p.setAttributeConfig('lastPageLinkLabel', { + value : 'last >>', + validator : l.isString + }); + + /** + * CSS class assigned to the link/span + * @attribute lastPageLinkClass + * @default 'yui-pg-last' + */ + p.setAttributeConfig('lastPageLinkClass', { + value : 'yui-pg-last', + validator : l.isString + }); +}; + +Paginator.ui.LastPageLink.prototype = { + + /** + * Currently placed HTMLElement node + * @property current + * @type HTMLElement + * @private + */ + current : null, + + /** + * Link HTMLElement node + * @property link + * @type HTMLElement + * @private + */ + link : null, + + /** + * Span node (inactive link) + * @property span + * @type HTMLElement + * @private + */ + span : null, + + /** + * Empty place holder node for when the last page link is inappropriate to + * display in any form (unlimited paging). + * @property na + * @type HTMLElement + * @private + */ + na : null, + + + /** + * Generate the nodes and return the appropriate node given the current + * pagination state. + * @method render + * @param id_base {string} used to create unique ids for generated nodes + * @return {HTMLElement} + */ + render : function (id_base) { + var p = this.paginator, + c = p.get('lastPageLinkClass'), + label = p.get('lastPageLinkLabel'), + last = p.getTotalPages(); + + this.link = document.createElement('a'); + this.span = document.createElement('span'); + this.na = this.span.cloneNode(false); + + this.link.id = id_base + '-last-link'; + this.link.href = '#'; + this.link.className = c; + this.link.innerHTML = label; + YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); + + this.span.id = id_base + '-last-span'; + this.span.className = c; + this.span.innerHTML = label; + + this.na.id = id_base + '-last-na'; + + switch (last) { + case Paginator.VALUE_UNLIMITED : + this.current = this.na; break; + case p.getCurrentPage() : + this.current = this.span; break; + default : + this.current = this.link; + } + + return this.current; + }, + + /** + * Swap the link, span, and na nodes if appropriate. + * @method update + * @param e {CustomEvent} The calling change event (ignored) + */ + update : function (e) { + if (e && e.prevValue === e.newValue) { + return; + } + + var par = this.current ? this.current.parentNode : null, + after = this.link; + + if (par) { + switch (this.paginator.getTotalPages()) { + case Paginator.VALUE_UNLIMITED : + after = this.na; break; + case this.paginator.getCurrentPage() : + after = this.span; break; + } + + if (this.current !== after) { + par.replaceChild(after,this.current); + this.current = after; + } + } + }, + + /** + * Removes the link/span node and clears event listeners + * @method destroy + * @private + */ + destroy : function () { + YAHOO.util.Event.purgeElement(this.link); + this.current.parentNode.removeChild(this.current); + this.link = this.span = null; + }, + + /** + * Listener for the link's onclick event. Passes to setPage method. + * @method onClick + * @param e {DOMEvent} The click event + */ + onClick : function (e) { + YAHOO.util.Event.stopEvent(e); + this.paginator.setPage(this.paginator.getTotalPages()); + } +}; + +})(); +(function () { + +var Paginator = YAHOO.widget.Paginator, + l = YAHOO.lang; + +/** + * ui Component to generate the link to jump to the next page. + * + * @namespace YAHOO.widget.Paginator.ui + * @class NextPageLink + * @for YAHOO.widget.Paginator + * + * @constructor + * @param p {Pagintor} Paginator instance to attach to + */ +Paginator.ui.NextPageLink = function (p) { + this.paginator = p; + + p.subscribe('recordOffsetChange', this.update,this,true); + p.subscribe('rowsPerPageChange', this.update,this,true); + p.subscribe('totalRecordsChange', this.update,this,true); + p.subscribe('destroy',this.destroy,this,true); + + // TODO: make this work + p.subscribe('nextPageLinkLabelChange', this.update,this,true); + p.subscribe('nextPageLinkClassChange', this.update,this,true); +}; + +/** + * Decorates Paginator instances with new attributes. Called during + * Paginator instantiation. + * @method init + * @param p {Paginator} Paginator instance to decorate + * @static + */ +Paginator.ui.NextPageLink.init = function (p) { + + /** + * Used as innerHTML for the next page link/span. + * @attribute nextPageLinkLabel + * @default 'next >' + */ + p.setAttributeConfig('nextPageLinkLabel', { + value : 'next >', + validator : l.isString + }); + + /** + * CSS class assigned to the link/span + * @attribute nextPageLinkClass + * @default 'yui-pg-next' + */ + p.setAttributeConfig('nextPageLinkClass', { + value : 'yui-pg-next', + validator : l.isString + }); +}; + +Paginator.ui.NextPageLink.prototype = { + + /** + * Currently placed HTMLElement node + * @property current + * @type HTMLElement + * @private + */ + current : null, + + /** + * Link node + * @property link + * @type HTMLElement + * @private + */ + link : null, + + /** + * Span node (inactive link) + * @property span + * @type HTMLElement + * @private + */ + span : null, + + + /** + * Generate the nodes and return the appropriate node given the current + * pagination state. + * @method render + * @param id_base {string} used to create unique ids for generated nodes + * @return {HTMLElement} + */ + render : function (id_base) { + var p = this.paginator, + c = p.get('nextPageLinkClass'), + label = p.get('nextPageLinkLabel'), + last = p.getTotalPages(); + + this.link = document.createElement('a'); + this.span = document.createElement('span'); + + this.link.id = id_base + '-next-link'; + this.link.href = '#'; + this.link.className = c; + this.link.innerHTML = label; + YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); + + this.span.id = id_base + '-next-span'; + this.span.className = c; + this.span.innerHTML = label; + + this.current = p.getCurrentPage() === last ? this.span : this.link; + + return this.current; + }, + + /** + * Swap the link and span nodes if appropriate. + * @method update + * @param e {CustomEvent} The calling change event + */ + update : function (e) { + if (e && e.prevValue === e.newValue) { + return; + } + + var last = this.paginator.getTotalPages(), + par = this.current ? this.current.parentNode : null; + + if (this.paginator.getCurrentPage() !== last) { + if (par && this.current === this.span) { + par.replaceChild(this.link,this.current); + this.current = this.link; + } + } else if (this.current === this.link) { + if (par) { + par.replaceChild(this.span,this.current); + this.current = this.span; + } + } + }, + + /** + * Removes the link/span node and clears event listeners + * @method destroy + * @private + */ + destroy : function () { + YAHOO.util.Event.purgeElement(this.link); + this.current.parentNode.removeChild(this.current); + this.link = this.span = null; + }, + + /** + * Listener for the link's onclick event. Passes to setPage method. + * @method onClick + * @param e {DOMEvent} The click event + */ + onClick : function (e) { + YAHOO.util.Event.stopEvent(e); + this.paginator.setPage(this.paginator.getNextPage()); + } +}; + +})(); +(function () { + +var Paginator = YAHOO.widget.Paginator, + l = YAHOO.lang; + +/** + * ui Component to generate the link to jump to the previous page. + * + * @namespace YAHOO.widget.Paginator.ui + * @class PreviousPageLink + * @for YAHOO.widget.Paginator + * + * @constructor + * @param p {Pagintor} Paginator instance to attach to + */ +Paginator.ui.PreviousPageLink = function (p) { + this.paginator = p; + + p.subscribe('recordOffsetChange',this.update,this,true); + p.subscribe('rowsPerPageChange',this.update,this,true); + p.subscribe('totalRecordsChange',this.update,this,true); + p.subscribe('destroy',this.destroy,this,true); + + // TODO: make this work + p.subscribe('previousPageLinkLabelChange',this.update,this,true); + p.subscribe('previousPageLinkClassChange',this.update,this,true); +}; + +/** + * Decorates Paginator instances with new attributes. Called during + * Paginator instantiation. + * @method init + * @param p {Paginator} Paginator instance to decorate + * @static + */ +Paginator.ui.PreviousPageLink.init = function (p) { + + /** + * Used as innerHTML for the previous page link/span. + * @attribute previousPageLinkLabel + * @default '< prev' + */ + p.setAttributeConfig('previousPageLinkLabel', { + value : '< prev', + validator : l.isString + }); + + /** + * CSS class assigned to the link/span + * @attribute previousPageLinkClass + * @default 'yui-pg-previous' + */ + p.setAttributeConfig('previousPageLinkClass', { + value : 'yui-pg-previous', + validator : l.isString + }); +}; + +Paginator.ui.PreviousPageLink.prototype = { + + /** + * Currently placed HTMLElement node + * @property current + * @type HTMLElement + * @private + */ + current : null, + + /** + * Link node + * @property link + * @type HTMLElement + * @private + */ + link : null, + + /** + * Span node (inactive link) + * @property span + * @type HTMLElement + * @private + */ + span : null, + + + /** + * Generate the nodes and return the appropriate node given the current + * pagination state. + * @method render + * @param id_base {string} used to create unique ids for generated nodes + * @return {HTMLElement} + */ + render : function (id_base) { + var p = this.paginator, + c = p.get('previousPageLinkClass'), + label = p.get('previousPageLinkLabel'); + + this.link = document.createElement('a'); + this.span = document.createElement('span'); + + this.link.id = id_base + '-prev-link'; + this.link.href = '#'; + this.link.className = c; + this.link.innerHTML = label; + YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); + + this.span.id = id_base + '-prev-span'; + this.span.className = c; + this.span.innerHTML = label; + + this.current = p.getCurrentPage() > 1 ? this.link : this.span; + return this.current; + }, + + /** + * Swap the link and span nodes if appropriate. + * @method update + * @param e {CustomEvent} The calling change event + */ + update : function (e) { + if (e && e.prevValue === e.newValue) { + return; + } + + var par = this.current ? this.current.parentNode : null; + if (this.paginator.getCurrentPage() > 1) { + if (par && this.current === this.span) { + par.replaceChild(this.link,this.current); + this.current = this.link; + } + } else { + if (par && this.current === this.link) { + par.replaceChild(this.span,this.current); + this.current = this.span; + } + } + }, + + /** + * Removes the link/span node and clears event listeners + * @method destroy + * @private + */ + destroy : function () { + YAHOO.util.Event.purgeElement(this.link); + this.current.parentNode.removeChild(this.current); + this.link = this.span = null; + }, + + /** + * Listener for the link's onclick event. Passes to setPage method. + * @method onClick + * @param e {DOMEvent} The click event + */ + onClick : function (e) { + YAHOO.util.Event.stopEvent(e); + this.paginator.setPage(this.paginator.getPreviousPage()); + } +}; + +})(); +(function () { + +var Paginator = YAHOO.widget.Paginator, + l = YAHOO.lang; + +/** + * ui Component to generate the rows-per-page dropdown + * + * @namespace YAHOO.widget.Paginator.ui + * @class RowsPerPageDropdown + * @for YAHOO.widget.Paginator + * + * @constructor + * @param p {Pagintor} Paginator instance to attach to + */ +Paginator.ui.RowsPerPageDropdown = function (p) { + this.paginator = p; + + p.subscribe('rowsPerPageChange',this.update,this,true); + p.subscribe('rowsPerPageOptionsChange',this.rebuild,this,true); + p.subscribe('totalRecordsChange',this._handleTotalRecordsChange,this,true); + p.subscribe('destroy',this.destroy,this,true); + + // TODO: make this work + p.subscribe('rowsPerPageDropdownClassChange',this.rebuild,this,true); +}; + +/** + * Decorates Paginator instances with new attributes. Called during + * Paginator instantiation. + * @method init + * @param p {Paginator} Paginator instance to decorate + * @static + */ +Paginator.ui.RowsPerPageDropdown.init = function (p) { + + /** + * Array of available rows-per-page sizes. Converted into select options. + * Array values may be positive integers or object literals in the form
+ * { value : NUMBER, text : STRING } + * @attribute rowsPerPageOptions + * @default [] + */ + p.setAttributeConfig('rowsPerPageOptions', { + value : [], + validator : l.isArray + }); + + /** + * CSS class assigned to the select node + * @attribute rowsPerPageDropdownClass + * @default 'yui-pg-rpp-options' + */ + p.setAttributeConfig('rowsPerPageDropdownClass', { + value : 'yui-pg-rpp-options', + validator : l.isString + }); +}; + +Paginator.ui.RowsPerPageDropdown.prototype = { + + /** + * select node + * @property select + * @type HTMLElement + * @private + */ + select : null, + + + /** + * option node for the optional All value + * + * @property all + * @type HTMLElement + * @protected + */ + all : null, + + /** + * Generate the select and option nodes and returns the select node. + * @method render + * @param id_base {string} used to create unique ids for generated nodes + * @return {HTMLElement} + */ + render : function (id_base) { + this.select = document.createElement('select'); + this.select.id = id_base + '-rpp'; + this.select.className = this.paginator.get('rowsPerPageDropdownClass'); + this.select.title = 'Rows per page'; + + YAHOO.util.Event.on(this.select,'change',this.onChange,this,true); + + this.rebuild(); + + return this.select; + }, + + /** + * (Re)generate the select options. + * @method rebuild + */ + rebuild : function (e) { + var p = this.paginator, + sel = this.select, + options = p.get('rowsPerPageOptions'), + opt,cfg,val,i,len; + + this.all = null; + + for (i = 0, len = options.length; i < len; ++i) { + cfg = options[i]; + opt = sel.options[i] || + sel.appendChild(document.createElement('option')); + val = l.isValue(cfg.value) ? cfg.value : cfg; + opt.innerHTML = l.isValue(cfg.text) ? cfg.text : cfg; + + if (l.isString(val) && val.toLowerCase() === 'all') { + this.all = opt; + opt.value = p.get('totalRecords'); + } else{ + opt.value = val; + } + + } + + while (sel.options.length > options.length) { + sel.removeChild(sel.firstChild); + } + + this.update(); + }, + + /** + * Select the appropriate option if changed. + * @method update + * @param e {CustomEvent} The calling change event + */ + update : function (e) { + if (e && e.prevValue === e.newValue) { + return; + } + + var rpp = this.paginator.get('rowsPerPage')+'', + options = this.select.options, + i,len; + + for (i = 0, len = options.length; i < len; ++i) { + if (options[i].value === rpp) { + options[i].selected = true; + break; + } + } + }, + + /** + * Listener for the select's onchange event. Sent to setRowsPerPage method. + * @method onChange + * @param e {DOMEvent} The change event + */ + onChange : function (e) { + this.paginator.setRowsPerPage( + parseInt(this.select.options[this.select.selectedIndex].value,10)); + }, + + /** + * Updates the all option value (and Paginator's rowsPerPage attribute if + * necessary) in response to a change in the Paginator's totalRecords. + * + * @method _handleTotalRecordsChange + * @param e {Event} attribute change event + * @protected + */ + _handleTotalRecordsChange : function (e) { + if (!this.all || (e && e.prevValue === e.newValue)) { + return; + } + + this.all.value = e.newValue; + if (this.all.selected) { + this.paginator.set('rowsPerPage',e.newValue); + } + }, + + /** + * Removes the select node and clears event listeners + * @method destroy + * @private + */ + destroy : function () { + YAHOO.util.Event.purgeElement(this.select); + this.select.parentNode.removeChild(this.select); + this.select = null; + } +}; + +})(); +YAHOO.register("paginator", YAHOO.widget.Paginator, {version: "2.7.0", build: "1799"});