|
|
/*
|
|
|
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
|
|
|
Code licensed under the BSD License:
|
|
|
http://developer.yahoo.net/yui/license.txt
|
|
|
version: 2.8.0r4
|
|
|
*/
|
|
|
(function () {
|
|
|
/**
|
|
|
* The Paginator widget provides a set of controls to navigate through paged
|
|
|
* data.
|
|
|
*
|
|
|
* @module paginator
|
|
|
* @uses YAHOO.util.EventProvider
|
|
|
* @uses YAHOO.util.AttributeProvider
|
|
|
*/
|
|
|
|
|
|
var Dom = YAHOO.util.Dom,
|
|
|
lang = YAHOO.lang,
|
|
|
isObject = lang.isObject,
|
|
|
isFunction = lang.isFunction,
|
|
|
isArray = lang.isArray,
|
|
|
isString = lang.isString;
|
|
|
|
|
|
/**
|
|
|
* Instantiate a Paginator, passing a configuration object to the contructor.
|
|
|
* The configuration object should contain the following properties:
|
|
|
* <ul>
|
|
|
* <li>rowsPerPage : <em>n</em> (int)</li>
|
|
|
* <li>totalRecords : <em>n</em> (int or Paginator.VALUE_UNLIMITED)</li>
|
|
|
* <li>containers : <em>id | el | arr</em> (HTMLElement reference, its id, or an array of either)</li>
|
|
|
* </ul>
|
|
|
*
|
|
|
* @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,
|
|
|
attrib, initialPage, records, perPage, startIndex;
|
|
|
|
|
|
config = 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 (config.hasOwnProperty(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
|
|
|
lang.augmentObject(Paginator, {
|
|
|
/**
|
|
|
* Incrementing index used to give instances unique ids.
|
|
|
* @static
|
|
|
* @property Paginator.id
|
|
|
* @type number
|
|
|
* @private
|
|
|
*/
|
|
|
id : 0,
|
|
|
|
|
|
/**
|
|
|
* Base of id strings used for ui components.
|
|
|
* @static
|
|
|
* @property Paginator.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 Paginator.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 Paginator.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 Paginator.TEMPLATE_ROWS_PER_PAGE
|
|
|
* @type string
|
|
|
*/
|
|
|
TEMPLATE_ROWS_PER_PAGE : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}",
|
|
|
|
|
|
/**
|
|
|
* Storage object for UI Components
|
|
|
* @static
|
|
|
* @property Paginator.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 Paginator.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 Paginator.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;
|
|
|
|
|
|
/**
|
|
|
* 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 (!isArray(val)) {
|
|
|
val = [val];
|
|
|
}
|
|
|
for (var i = 0, len = val.length; i < len; ++i) {
|
|
|
if (isString(val[i]) ||
|
|
|
(isObject(val[i]) && val[i].nodeType === 1)) {
|
|
|
continue;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
return true;
|
|
|
},
|
|
|
method : function (val) {
|
|
|
val = Dom.get(val);
|
|
|
if (!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 : 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 : 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 : lang.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
|
|
|
* @deprecated use changeRequest listener that calls setState
|
|
|
*/
|
|
|
this.setAttributeConfig('updateOnChange', {
|
|
|
value : false,
|
|
|
validator : lang.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 (ui.hasOwnProperty(name)) {
|
|
|
UIComp = ui[name];
|
|
|
if (isObject(UIComp) && 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:
|
|
|
* <ul>
|
|
|
* <li>paginator - the Paginator instance</li>
|
|
|
* <li>page</li>
|
|
|
* <li>totalRecords</li>
|
|
|
* <li>recordOffset - index of the first record on the new page</li>
|
|
|
* <li>rowsPerPage</li>
|
|
|
* <li>records - array containing [start index, end index] for the records on the new page</li>
|
|
|
* <li>before - object literal with all these keys for the current state</li>
|
|
|
* </ul>
|
|
|
* @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 (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
|
|
|
* @return the Paginator instance
|
|
|
* @chainable
|
|
|
*/
|
|
|
render : function () {
|
|
|
if (this.get('rendered')) {
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
var template = this.get('template'),
|
|
|
state = this.getState(),
|
|
|
// ex. yui-pg0-1 (first paginator, second container)
|
|
|
id_base = Paginator.ID_BASE + this.get('id') + '-',
|
|
|
i, len;
|
|
|
|
|
|
// Assemble the containers, keeping them hidden
|
|
|
for (i = 0, len = this._containers.length; i < len; ++i) {
|
|
|
this._renderTemplate(this._containers[i],template,id_base+i,true);
|
|
|
}
|
|
|
|
|
|
// Show the containers if appropriate
|
|
|
this.updateVisibility();
|
|
|
|
|
|
// Set render attribute manually to support its readOnly contract
|
|
|
if (this._containers.length) {
|
|
|
this.setAttributeConfig('rendered', { value: true });
|
|
|
|
|
|
this.fireEvent('render', state);
|
|
|
// For backward compatibility
|
|
|
this.fireEvent('rendered', state);
|
|
|
}
|
|
|
|
|
|
return this;
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* Creates the individual ui components and renders them into a container.
|
|
|
*
|
|
|
* @method _renderTemplate
|
|
|
* @param container {HTMLElement} where to add the ui components
|
|
|
* @param template {String} the template to use as a guide for rendering
|
|
|
* @param id_base {String} id base for the container's ui components
|
|
|
* @param hide {Boolean} leave the container hidden after assembly
|
|
|
* @protected
|
|
|
*/
|
|
|
_renderTemplate : function (container, template, id_base, hide) {
|
|
|
var containerClass = this.get('containerClass'),
|
|
|
markers, i, len;
|
|
|
|
|
|
if (!container) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// Hide the container while its contents are rendered
|
|
|
Dom.setStyle(container,'display','none');
|
|
|
|
|
|
Dom.addClass(container, containerClass);
|
|
|
|
|
|
// Place the template innerHTML, adding marker spans to the template
|
|
|
// html to indicate drop zones for ui components
|
|
|
container.innerHTML = template.replace(/\{([a-z0-9_ \-]+)\}/gi,
|
|
|
'<span class="yui-pg-ui yui-pg-ui-$1"></span>');
|
|
|
|
|
|
// Replace each marker with the ui component's render() output
|
|
|
markers = Dom.getElementsByClassName('yui-pg-ui','span',container);
|
|
|
|
|
|
for (i = 0, len = markers.length; i < len; ++i) {
|
|
|
this.renderUIComponent(markers[i], id_base);
|
|
|
}
|
|
|
|
|
|
if (!hide) {
|
|
|
// Show the container allowing page reflow
|
|
|
Dom.setStyle(container,'display','');
|
|
|
}
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* Replaces a marker node with a rendered UI component, determined by the
|
|
|
* yui-pg-ui-(UI component class name) in the marker's className. e.g.
|
|
|
* yui-pg-ui-PageLinks => new YAHOO.widget.Paginator.ui.PageLinks(this)
|
|
|
*
|
|
|
* @method renderUIComponent
|
|
|
* @param marker {HTMLElement} the marker node to replace
|
|
|
* @param id_base {String} string base the component's generated id
|
|
|
*/
|
|
|
renderUIComponent : function (marker, id_base) {
|
|
|
var par = marker.parentNode,
|
|
|
name = /yui-pg-ui-(\w+)/.exec(marker.className),
|
|
|
UIComp = name && Paginator.ui[name[1]],
|
|
|
comp;
|
|
|
|
|
|
if (isFunction(UIComp)) {
|
|
|
comp = new UIComp(this);
|
|
|
if (isFunction(comp.render)) {
|
|
|
par.replaceChild(comp.render(id_base),marker);
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* Removes controls from the page and unhooks events.
|
|
|
* @method destroy
|
|
|
*/
|
|
|
destroy : function () {
|
|
|
this.fireEvent('beforeDestroy');
|
|
|
this.fireEvent('destroy');
|
|
|
|
|
|
this.setAttributeConfig('rendered',{value:false});
|
|
|
this.unsubscribeAll();
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 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 || e.type === 'alwaysVisibleChange' || !alwaysVisible) {
|
|
|
totalRecords = this.get('totalRecords');
|
|
|
visible = true;
|
|
|
rpp = this.get('rowsPerPage');
|
|
|
rppOptions = this.get('rowsPerPageOptions');
|
|
|
|
|
|
if (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) {
|
|
|
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 (!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 (!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:
|
|
|
* <ul>
|
|
|
* <li>paginator - instance of the Paginator</li>
|
|
|
* <li>page - number</li>
|
|
|
* <li>totalRecords - number</li>
|
|
|
* <li>recordOffset - number</li>
|
|
|
* <li>rowsPerPage - number</li>
|
|
|
* <li>records - [ start_index, end_index ]</li>
|
|
|
* <li>before - (OPTIONAL) { state object literal for current state }</li>
|
|
|
* </ul>
|
|
|
* @method getState
|
|
|
* @return {object}
|
|
|
* @param changes {object} OPTIONAL object literal with proposed values
|
|
|
* Supported change keys include:
|
|
|
* <ul>
|
|
|
* <li>rowsPerPage</li>
|
|
|
* <li>totalRecords</li>
|
|
|
* <li>recordOffset OR</li>
|
|
|
* <li>page</li>
|
|
|
* </ul>
|
|
|
*/
|
|
|
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 (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._configs.hasOwnProperty(k)) {
|
|
|
this.set(k,state[k]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
this._batch = false;
|
|
|
|
|
|
if (this._pageChanged) {
|
|
|
this._pageChanged = false;
|
|
|
|
|
|
this._firePageChange(this.getState(this._state));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
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:
|
|
|
* <ul>
|
|
|
* <li>currentPage</li>
|
|
|
* <li>totalPages</li>
|
|
|
* <li>startIndex</li>
|
|
|
* <li>endIndex</li>
|
|
|
* <li>startRecord</li>
|
|
|
* <li>endRecord</li>
|
|
|
* <li>totalRecords</li>
|
|
|
* </ul>
|
|
|
* @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 = '<a href="#" class="' + p.get('pageLinkClass') +
|
|
|
'" page="';
|
|
|
for (i = start; i <= end; ++i) {
|
|
|
if (i === currentPage) {
|
|
|
content +=
|
|
|
'<span class="' + p.get('currentPageClass') + ' ' +
|
|
|
p.get('pageLinkClass') + '">' +
|
|
|
labelBuilder(i,p) + '</span>';
|
|
|
} else {
|
|
|
content +=
|
|
|
linkTemplate + i + '">' + labelBuilder(i,p) + '</a>';
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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<br>
|
|
|
* { 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.8.0r4", build: "2449"});
|
|
|
|