@ -5,122 +5,271 @@
Copyright ( c ) 2013 Jimmy Yuen Ho Wong and contributors
Licensed under the MIT @ license .
* /
( function ( $ , _ , Backbone , Backgrid ) {
( function ( factory ) {
// CommonJS
if ( typeof exports == "object" ) {
module . exports = factory ( require ( "underscore" ) ,
require ( "backbone" ) ,
require ( "backgrid" ) ,
require ( "backbone-pageable" ) ) ;
}
// Browser
else if ( typeof _ !== "undefined" &&
typeof Backbone !== "undefined" &&
typeof Backgrid !== "undefined" ) {
factory ( _ , Backbone , Backgrid ) ;
}
} ( function ( _ , Backbone , Backgrid ) {
"use strict" ;
/ * *
Paginator is a Backgrid extension that renders a series of configurable
pagination handles . This extension is best used for splitting a large data
set across multiple pages . If the number of pages is larger then a
threshold , which is set to 10 by default , the page handles are rendered
within a sliding window , plus the fast forward , fast backward , previous and
next page handles . The fast forward , fast backward , previous and next page
handles can be turned off .
@ class Backgrid . Extension . Paginator
PageHandle is a class that renders the actual page handles and reacts to
click events for pagination .
This class acts in two modes - control or discrete page handle modes . If
one of the ` is* ` flags is ` true ` , an instance of this class is under
control page handle mode . Setting a ` pageIndex ` to an instance of this
class under control mode has no effect and the correct page index will
always be inferred from the ` is* ` flag . Only one of the ` is* ` flags should
be set to ` true ` at a time . For example , an instance of this class cannot
simultaneously be a rewind control and a fast forward control . A ` label `
and a ` title ` template or a string are required to be passed to the
constuctor under this mode . If a ` title ` template is provided , it _ _MUST _ _
accept a parameter ` label ` . When the ` label ` is provided to the ` title `
template function , its result will be used to render the generated anchor ' s
title attribute .
If all of the ` is* ` flags is set to ` false ` , which is the default , an
instance of this class will be in discrete page handle mode . An instance
under this mode requires the ` pageIndex ` to be passed from the constructor
as an option and it _ _MUST _ _ be a 0 - based index of the list of page numbers
to render . The constuctor will normalize the base to the same base the
underlying PageableCollection collection instance uses . A ` label ` is not
required under this mode , which will default to the equivalent 1 - based page
index calculated from ` pageIndex ` and the underlying PageableCollection
instance . A provided ` label ` will still be honored however . The ` title `
parameter is also not required under this mode , in which case the default
` title ` template will be used . You are encouraged to provide your own
` title ` template however if you wish to localize the title strings .
If this page handle represents the current page , an ` active ` class will be
placed on the root list element .
if this page handle is at the border of the list of pages , a ` disabled `
class will be placed on the root list element .
Only page handles that are neither ` active ` nor ` disabled ` will respond to
click events and triggers pagination .
@ class Backgrid . Extension . PageHandle
* /
Backgrid . Extension . Paginator = Backbone . View . extend ( {
var PageHandle = Backgrid . Extension . Pag eHandle = Backbone . View . extend ( {
/** @property */
className : "backgrid-paginator" ,
tagName: "li ",
/** @property */
windowSize : 10 ,
events : {
"click a" : "changePage"
} ,
/ * *
@ property { Object } fastForwardHandleLabels You can disable specific
handles by setting its value to ` null ` .
@ property { string | function ( Object . < string , string > ) : string } title
The title to use for the ` title ` attribute of the generated page handle
anchor elements . It can be a string or an Underscore template function
that takes a mandatory ` label ` parameter .
* /
fastForwardHandleLabels : {
first : "《" ,
prev : "〈" ,
next : "〉" ,
last : "》"
} ,
title : _ . template ( 'Page <%- label %>' , null , { variable : null } ) ,
/** @property */
template : _ . template ( '<ul><% _.each(handles, function (handle) { %><li <% if (handle.className) { %>class="<%= handle.className %>"<% } %>><a href="#" <% if (handle.title) {%> title="<%= handle.title %>"<% } %>><%= handle.label %></a></li><% }); %></ul>' ) ,
/ * *
@ property { boolean } isRewind Whether this handle represents a rewind
control
* /
isRewind : false ,
/** @property */
events : {
"click a" : "changePage"
} ,
/ * *
@ property { boolean } isBack Whether this handle represents a back
control
* /
isBack : false ,
/ * *
@ property { boolean } isForward Whether this handle represents a forward
control
* /
isForward : false ,
/ * *
@ property { boolean } isFastForward Whether this handle represents a fast
forward control
* /
isFastForward : false ,
/ * *
Initializer .
@ param { Object } options
@ param { Backbone . Collection } options . collection
@ param { boolean } [ options . fastForwardHandleLabels ] Whether to render fast forward buttons .
@ param { number } pageIndex 0 - based index of the page number this handle
handles . This parameter will be normalized to the base the underlying
PageableCollection uses .
@ param { string } [ options . label ] If provided it is used to render the
anchor text , otherwise the normalized pageIndex will be used
instead . Required if any of the ` is* ` flags is set to ` true ` .
@ param { string } [ options . title ]
@ param { boolean } [ options . isRewind = false ]
@ param { boolean } [ options . isBack = false ]
@ param { boolean } [ options . isForward = false ]
@ param { boolean } [ options . isFastForward = false ]
* /
initialize : function ( options ) {
Backgrid . requireOptions ( options , [ "collection" ] ) ;
Back bone. View . prototype . initialize . apply ( this , arguments ) ;
var collection = this . collection ;
var fullCollection = collection . fullCollection ;
if ( fullCollection ) {
this . listenTo ( fullCollection , "add" , this . render ) ;
this . listenTo ( fullCollection , "remove" , this . render ) ;
this . listenTo ( fullCollection , "reset" , this . render ) ;
}
var state = collection . state ;
var currentPage = state . currentPage ;
var firstPage = state . firstPage ;
var lastPage = state . lastPage ;
_ . extend ( this , _ . pick ( options ,
[ "isRewind" , "isBack" , "isForward" , "isFastForward" ] ) ) ;
var pageIndex ;
if ( this . isRewind ) pageIndex = firstPage ;
else if ( this . isBack ) pageIndex = Math . max ( firstPage , currentPage - 1 ) ;
else if ( this . isForward ) pageIndex = Math . min ( lastPage , currentPage + 1 ) ;
else if ( this . isFastForward ) pageIndex = lastPage ;
else {
this . listenTo ( collection , "add" , this . render ) ;
this . listenTo ( collection , "remove" , this . render ) ;
this . listenTo ( collection , "reset" , this . render ) ;
pageIndex = + options . pageIndex ;
pageIndex = ( firstPage ? pageIndex + 1 : pageIndex ) ;
}
this . pageIndex = pageIndex ;
if ( ( ( this . isRewind || this . isBack ) && currentPage == firstPage ) ||
( ( this . isForward || this . isFastForward ) && currentPage == lastPage ) ) {
this . $el . addClass ( "disabled" ) ;
}
else if ( ! ( this . isRewind ||
this . isBack ||
this . isForward ||
this . isFastForward ) &&
currentPage == pageIndex ) {
this . $el . addClass ( "active" ) ;
}
this . label = ( options . label || ( firstPage ? pageIndex : pageIndex + 1 ) ) + '' ;
var title = options . title || this . title ;
this . title = _ . isFunction ( title ) ? title ( { label : this . label } ) : title ;
} ,
/ * *
jQuery event handler for the page handlers . Goes to the right page upon
clicking .
Renders a clickable anchor element under a list item .
* /
render : function ( ) {
this . $el . empty ( ) ;
var anchor = document . createElement ( "a" ) ;
anchor . href = '#' ;
if ( this . title ) anchor . title = this . title ;
anchor . innerHTML = this . label ;
this . el . appendChild ( anchor ) ;
this . delegateEvents ( ) ;
return this ;
} ,
@ param { Event } e
* /
/ * *
jQuery click event handler . Goes to the page this PageHandle instance
represents . No - op if this page handle is currently active or disabled .
* /
changePage : function ( e ) {
e . preventDefault ( ) ;
var $el = this . $el ;
if ( ! $el . hasClass ( "active" ) && ! $el . hasClass ( "disabled" ) ) {
this . collection . getPage ( this . pageIndex ) ;
}
return this ;
}
var $li = $ ( e . target ) . parent ( ) ;
if ( ! $li . hasClass ( "active" ) && ! $li . hasClass ( "disabled" ) ) {
var label = $ ( e . target ) . text ( ) ;
var ffLabels = this . fastForwardHandleLabels ;
var collection = this . collection ;
if ( ffLabels ) {
switch ( label ) {
case ffLabels . first :
collection . getFirstPage ( ) ;
return ;
case ffLabels . prev :
collection . getPreviousPage ( ) ;
return ;
case ffLabels . next :
collection . getNextPage ( ) ;
return ;
case ffLabels . last :
collection . getLastPage ( ) ;
return ;
}
}
} ) ;
/ * *
Paginator is a Backgrid extension that renders a series of configurable
pagination handles . This extension is best used for splitting a large data
set across multiple pages . If the number of pages is larger then a
threshold , which is set to 10 by default , the page handles are rendered
within a sliding window , plus the rewind , back , forward and fast forward
control handles . The individual control handles can be turned off .
@ class Backgrid . Extension . Paginator
* /
Backgrid . Extension . Paginator = Backbone . View . extend ( {
/** @property */
className : "backgrid-paginator" ,
/** @property */
windowSize : 10 ,
var state = collection . state ;
var pageIndex = + label ;
collection . getPage ( state . firstPage === 0 ? pageIndex - 1 : pageIndex ) ;
/ * *
@ property { Object . < string , Object . < string , string >> } controls You can
disable specific control handles by omitting certain keys .
* /
controls : {
rewind : {
label : "《" ,
title : "First"
} ,
back : {
label : "〈" ,
title : "Previous"
} ,
forward : {
label : "〉" ,
title : "Next"
} ,
fastForward : {
label : "》" ,
title : "Last"
}
} ,
/ * *
Internal method to create a list of page handle objects for the template
to render them .
@ property { Backgrid . Extension . PageHandle } pageHandle . The PageHandle
class to use for rendering individual handles
* /
pageHandle : PageHandle ,
@ return { Array . < Object > } an array of page handle objects hashes
* /
makeHandles : function ( ) {
/** @property */
goBackFirstOnSort : true ,
/ * *
Initializer .
@ param { Object } options
@ param { Backbone . Collection } options . collection
@ param { boolean } [ options . controls ]
@ param { boolean } [ options . pageHandle = Backgrid . Extension . PageHandle ]
@ param { boolean } [ options . goBackFirstOnSort = true ]
* /
initialize : function ( options ) {
this . controls = options . controls || this . controls ;
this . pageHandle = options . pageHandle || this . pageHandle ;
var handles = [ ] ;
var collection = this . collection ;
this . listenTo ( collection , "add" , this . render ) ;
this . listenTo ( collection , "remove" , this . render ) ;
this . listenTo ( collection , "reset" , this . render ) ;
if ( ( options . goBackFirstOnSort || this . goBackFirstOnSort ) &&
collection . fullCollection ) {
this . listenTo ( collection . fullCollection , "sort" , function ( ) {
collection . getFirstPage ( ) ;
} ) ;
}
} ,
_calculateWindow : function ( ) {
var collection = this . collection ;
var state = collection . state ;
@ -132,48 +281,44 @@
currentPage = firstPage ? currentPage - 1 : currentPage ;
var windowStart = Math . floor ( currentPage / this . windowSize ) * this . windowSize ;
var windowEnd = Math . min ( lastPage + 1 , windowStart + this . windowSize ) ;
return [ windowStart , windowEnd ] ;
} ,
if ( collection . mode !== "infinite" ) {
for ( var i = windowStart ; i < windowEnd ; i ++ ) {
handles . push ( {
label : i + 1 ,
title : "No. " + ( i + 1 ) ,
className : currentPage === i ? "active" : undefined
} ) ;
}
}
/ * *
Creates a list of page handle objects for rendering .
var ffLabels = this . fastForwardHandleLabels ;
if ( ffLabels ) {
@ return { Array . < Object > } an array of page handle objects hashes
* /
makeHandles : function ( ) {
if ( ffLabels . prev ) {
handles . unshift ( {
label : ffLabels . prev ,
className : collection . hasPrevious ( ) ? void 0 : "disabled"
} ) ;
}
var handles = [ ] ;
var collection = this . collection ;
if ( ffLabels . first ) {
handles . unshift ( {
label : ffLabels . first ,
className : collection . hasPrevious ( ) ? void 0 : "disabled"
} ) ;
}
var window = this . _calculateWindow ( ) ;
var winStart = window [ 0 ] , winEnd = window [ 1 ] ;
if ( ffLabels . next ) {
handles . push ( {
label : ffLabels . next ,
className : collection . hasNext ( ) ? void 0 : "disabled"
} ) ;
}
for ( var i = winStart ; i < winEnd ; i ++ ) {
handles . push ( new this . pageHandle ( {
collection : collection ,
pageIndex : i
} ) ) ;
}
if ( ffLabels . last ) {
handles . push ( {
label : ffLabels . last ,
className : collection . hasNext ( ) ? void 0 : "disabled"
} ) ;
var controls = this . controls ;
_ . each ( [ "back" , "rewind" , "forward" , "fastForward" ] , function ( key ) {
var value = controls [ key ] ;
if ( value ) {
var handleCtorOpts = {
collection : collection ,
title : value . title ,
label : value . label
} ;
handleCtorOpts [ "is" + key . slice ( 0 , 1 ) . toUpperCase ( ) + key . slice ( 1 ) ] = true ;
var handle = new this . pageHandle ( handleCtorOpts ) ;
if ( key == "rewind" || key == "back" ) handles . unshift ( handle ) ;
else handles . push ( handle ) ;
}
}
} , this ) ;
return handles ;
} ,
@ -184,15 +329,24 @@
render : function ( ) {
this . $el . empty ( ) ;
this . $el . append ( this . template ( {
handles : this . makeHandles ( )
} ) ) ;
if ( this . handles ) {
for ( var i = 0 , l = this . handles . length ; i < l ; i ++ ) {
this . handles [ i ] . remove ( ) ;
}
}
this . delegateEvents ( ) ;
var handles = this . handles = this . makeHandles ( ) ;
var ul = document . createElement ( "ul" ) ;
for ( var i = 0 ; i < handles . length ; i ++ ) {
ul . appendChild ( handles [ i ] . render ( ) . el ) ;
}
this . el . appendChild ( ul ) ;
return this ;
}
} ) ;
} ( jQuery , _ , Backbone , Backgrid ) ) ;
} )) ;