paginator.js 69 KB

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