Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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