PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/assets/javascripts/vendor/backbone.paginator.js

https://github.com/wagnerpv/iugu-ux
JavaScript | 835 lines | 541 code | 152 blank | 142 comment | 113 complexity | 6e0f23a355b634508f7d4285e7af2603 MD5 | raw file
  1. /*! backbone.paginator - v0.1.54 - 8/18/2012
  2. * http://github.com/addyosmani/backbone.paginator
  3. * Copyright (c) 2012 Addy Osmani; Licensed MIT */
  4. Backbone.Paginator = (function ( Backbone, _, $ ) {
  5. "use strict";
  6. var Paginator = {};
  7. Paginator.version = "0.15";
  8. // @name: clientPager
  9. //
  10. // @tagline: Paginator for client-side data
  11. //
  12. // @description:
  13. // This paginator is responsible for providing pagination
  14. // and sort capabilities for a single payload of data
  15. // we wish to paginate by the UI for easier browsering.
  16. //
  17. Paginator.clientPager = Backbone.Collection.extend({
  18. // Default values used when sorting and/or filtering.
  19. initialize: function(){
  20. this.useDiacriticsPlugin = true; // use diacritics plugin if available
  21. this.useLevenshteinPlugin = true; // use levenshtein plugin if available
  22. this.sortColumn = "";
  23. this.sortDirection = "desc";
  24. this.lastSortColumn = "";
  25. this.fieldFilterRules = [];
  26. this.lastFieldFilterRules = [];
  27. this.filterFields = "";
  28. this.filterExpression = "";
  29. this.lastFilterExpression = "";
  30. },
  31. sync: function ( method, model, options ) {
  32. var self = this;
  33. // Create default values if no others are specified
  34. _.defaults(self.paginator_ui, {
  35. firstPage: 0,
  36. currentPage: 1,
  37. perPage: 5,
  38. totalPages: 10
  39. });
  40. // Change scope of 'paginator_ui' object values
  41. _.each(self.paginator_ui, function(value, key) {
  42. if( _.isUndefined(self[key]) ) {
  43. self[key] = self.paginator_ui[key];
  44. }
  45. });
  46. // Some values could be functions, let's make sure
  47. // to change their scope too and run them
  48. var queryAttributes = {};
  49. _.each(self.server_api, function(value, key){
  50. if( _.isFunction(value) ) {
  51. value = _.bind(value, self);
  52. value = value();
  53. }
  54. queryAttributes[key] = value;
  55. });
  56. var queryOptions = _.clone(self.paginator_core);
  57. _.each(queryOptions, function(value, key){
  58. if( _.isFunction(value) ) {
  59. value = _.bind(value, self);
  60. value = value();
  61. }
  62. queryOptions[key] = value;
  63. });
  64. // Create default values if no others are specified
  65. queryOptions = _.defaults(queryOptions, {
  66. timeout: 25000,
  67. cache: false,
  68. type: 'GET',
  69. dataType: 'jsonp'
  70. });
  71. queryOptions = _.extend(queryOptions, {
  72. jsonpCallback: 'callback',
  73. data: decodeURIComponent($.param(queryAttributes)),
  74. processData: false,
  75. url: _.result(queryOptions, 'url')
  76. }, options);
  77. return Backbone.ajax( queryOptions );
  78. },
  79. nextPage: function () {
  80. this.currentPage = ++this.currentPage;
  81. this.pager();
  82. },
  83. previousPage: function () {
  84. this.currentPage = --this.currentPage || 1;
  85. this.pager();
  86. },
  87. goTo: function ( page ) {
  88. if(page !== undefined){
  89. this.currentPage = parseInt(page, 10);
  90. this.pager();
  91. }
  92. },
  93. howManyPer: function ( perPage ) {
  94. if(perPage !== undefined){
  95. var lastPerPage = this.perPage;
  96. this.perPage = parseInt(perPage, 10);
  97. this.currentPage = Math.ceil( ( lastPerPage * ( this.currentPage - 1 ) + 1 ) / perPage);
  98. this.pager();
  99. }
  100. },
  101. // setSort is used to sort the current model. After
  102. // passing 'column', which is the model's field you want
  103. // to filter and 'direction', which is the direction
  104. // desired for the ordering ('asc' or 'desc'), pager()
  105. // and info() will be called automatically.
  106. setSort: function ( column, direction ) {
  107. if(column !== undefined && direction !== undefined){
  108. this.lastSortColumn = this.sortColumn;
  109. this.sortColumn = column;
  110. this.sortDirection = direction;
  111. this.pager();
  112. this.info();
  113. }
  114. },
  115. // setFieldFilter is used to filter each value of each model
  116. // according to `rules` that you pass as argument.
  117. // Example: You have a collection of books with 'release year' and 'author'.
  118. // You can filter only the books that were released between 1999 and 2003
  119. // And then you can add another `rule` that will filter those books only to
  120. // authors who's name start with 'A'.
  121. setFieldFilter: function ( fieldFilterRules ) {
  122. if( !_.isEmpty( fieldFilterRules ) ) {
  123. this.lastFieldFilterRules = this.fieldFilterRules;
  124. this.fieldFilterRules = fieldFilterRules;
  125. this.pager();
  126. this.info();
  127. }
  128. },
  129. // doFakeFieldFilter can be used to get the number of models that will remain
  130. // after calling setFieldFilter with a filter rule(s)
  131. doFakeFieldFilter: function ( fieldFilterRules ) {
  132. if( !_.isEmpty( fieldFilterRules ) ) {
  133. var bkp_lastFieldFilterRules = this.lastFieldFilterRules;
  134. var bkp_fieldFilterRules = this.fieldFilterRules;
  135. this.lastFieldFilterRules = this.fieldFilterRules;
  136. this.fieldFilterRules = fieldFilterRules;
  137. this.pager();
  138. this.info();
  139. var cmodels = this.models.length;
  140. this.lastFieldFilterRules = bkp_lastFieldFilterRules;
  141. this.fieldFilterRules = bkp_fieldFilterRules;
  142. this.pager();
  143. this.info();
  144. // Return size
  145. return cmodels;
  146. }
  147. },
  148. // setFilter is used to filter the current model. After
  149. // passing 'fields', which can be a string referring to
  150. // the model's field, an array of strings representing
  151. // each of the model's fields or an object with the name
  152. // of the model's field(s) and comparing options (see docs)
  153. // you wish to filter by and
  154. // 'filter', which is the word or words you wish to
  155. // filter by, pager() and info() will be called automatically.
  156. setFilter: function ( fields, filter ) {
  157. if( fields !== undefined && filter !== undefined ){
  158. this.filterFields = fields;
  159. this.lastFilterExpression = this.filterExpression;
  160. this.filterExpression = filter;
  161. this.pager();
  162. this.info();
  163. }
  164. },
  165. // doFakeFilter can be used to get the number of models that will
  166. // remain after calling setFilter with a `fields` and `filter` args.
  167. doFakeFilter: function ( fields, filter ) {
  168. if( fields !== undefined && filter !== undefined ){
  169. var bkp_filterFields = this.filterFields;
  170. var bkp_lastFilterExpression = this.lastFilterExpression;
  171. var bkp_filterExpression = this.filterExpression;
  172. this.filterFields = fields;
  173. this.lastFilterExpression = this.filterExpression;
  174. this.filterExpression = filter;
  175. this.pager();
  176. this.info();
  177. var cmodels = this.models.length;
  178. this.filterFields = bkp_filterFields;
  179. this.lastFilterExpression = bkp_lastFilterExpression;
  180. this.filterExpression = bkp_filterExpression;
  181. this.pager();
  182. this.info();
  183. // Return size
  184. return cmodels;
  185. }
  186. },
  187. // pager is used to sort, filter and show the data
  188. // you expect the library to display.
  189. pager: function () {
  190. var self = this,
  191. disp = this.perPage,
  192. start = (self.currentPage - 1) * disp,
  193. stop = start + disp;
  194. // Saving the original models collection is important
  195. // as we could need to sort or filter, and we don't want
  196. // to loose the data we fetched from the server.
  197. if (self.origModels === undefined) {
  198. self.origModels = self.models;
  199. }
  200. self.models = self.origModels;
  201. // Check if sorting was set using setSort.
  202. if ( this.sortColumn !== "" ) {
  203. self.models = self._sort(self.models, this.sortColumn, this.sortDirection);
  204. }
  205. // Check if field-filtering was set using setFieldFilter
  206. if ( !_.isEmpty( this.fieldFilterRules ) ) {
  207. self.models = self._fieldFilter(self.models, this.fieldFilterRules);
  208. }
  209. // Check if filtering was set using setFilter.
  210. if ( this.filterExpression !== "" ) {
  211. self.models = self._filter(self.models, this.filterFields, this.filterExpression);
  212. }
  213. // If the sorting or the filtering was changed go to the first page
  214. if ( this.lastSortColumn !== this.sortColumn || this.lastFilterExpression !== this.filterExpression || !_.isEqual(this.fieldFilterRules, this.lastFieldFilterRules) ) {
  215. start = 0;
  216. stop = start + disp;
  217. self.currentPage = 1;
  218. this.lastSortColumn = this.sortColumn;
  219. this.lastFieldFilterRules = this.fieldFilterRules;
  220. this.lastFilterExpression = this.filterExpression;
  221. }
  222. // We need to save the sorted and filtered models collection
  223. // because we'll use that sorted and filtered collection in info().
  224. self.sortedAndFilteredModels = self.models;
  225. self.reset(self.models.slice(start, stop));
  226. },
  227. // The actual place where the collection is sorted.
  228. // Check setSort for arguments explicacion.
  229. _sort: function ( models, sort, direction ) {
  230. models = models.sort(function (a, b) {
  231. var ac = a.get(sort),
  232. bc = b.get(sort);
  233. if ( !ac || !bc ) {
  234. return 0;
  235. } else {
  236. /* Make sure that both ac and bc are lowercase strings.
  237. * .toString() first so we don't have to worry if ac or bc
  238. * have other String-only methods.
  239. */
  240. ac = ac.toString().toLowerCase();
  241. bc = bc.toString().toLowerCase();
  242. }
  243. if (direction === 'desc') {
  244. // We need to know if there aren't any non-number characters
  245. // and that there are numbers-only characters and maybe a dot
  246. // if we have a float.
  247. if((!ac.match(/[^\d\.]/) && ac.match(/[\d\.]*/)) &&
  248. (!bc.match(/[^\d\.]/) && bc.match(/[\d\.]*/))
  249. ){
  250. if( (ac - 0) < (bc - 0) ) {
  251. return 1;
  252. }
  253. if( (ac - 0) > (bc - 0) ) {
  254. return -1;
  255. }
  256. } else {
  257. if (ac < bc) {
  258. return 1;
  259. }
  260. if (ac > bc) {
  261. return -1;
  262. }
  263. }
  264. } else {
  265. //Same as the regexp check in the 'if' part.
  266. if((!ac.match(/[^\d\.]/) && ac.match(/[\d\.]*/)) &&
  267. (!bc.match(/[^\d\.]/) && bc.match(/[\d\.]*/))
  268. ){
  269. if( (ac - 0) < (bc - 0) ) {
  270. return -1;
  271. }
  272. if( (ac - 0) > (bc - 0) ) {
  273. return 1;
  274. }
  275. } else {
  276. if (ac < bc) {
  277. return -1;
  278. }
  279. if (ac > bc) {
  280. return 1;
  281. }
  282. }
  283. }
  284. return 0;
  285. });
  286. return models;
  287. },
  288. // The actual place where the collection is field-filtered.
  289. // Check setFieldFilter for arguments explicacion.
  290. _fieldFilter: function( models, rules ) {
  291. // Check if there are any rules
  292. if ( _.isEmpty(rules) ) {
  293. return models;
  294. }
  295. var filteredModels = [];
  296. // Iterate over each rule
  297. _.each(models, function(model){
  298. var should_push = true;
  299. // Apply each rule to each model in the collection
  300. _.each(rules, function(rule){
  301. // Don't go inside the switch if we're already sure that the model won't be included in the results
  302. if( !should_push ){
  303. return false;
  304. }
  305. should_push = false;
  306. // The field's value will be passed to a custom function, which should
  307. // return true (if model should be included) or false (model should be ignored)
  308. if(rule.type === "function"){
  309. var f = _.wrap(rule.value, function(func){
  310. return func( model.get(rule.field) );
  311. });
  312. if( f() ){
  313. should_push = true;
  314. }
  315. // The field's value is required to be non-empty
  316. }else if(rule.type === "required"){
  317. if( !_.isEmpty( model.get(rule.field).toString() ) ) {
  318. should_push = true;
  319. }
  320. // The field's value is required to be greater tan N (numbers only)
  321. }else if(rule.type === "min"){
  322. if( !_.isNaN( Number( model.get(rule.field) ) ) &&
  323. !_.isNaN( Number( rule.value ) ) &&
  324. Number( model.get(rule.field) ) >= Number( rule.value ) ) {
  325. should_push = true;
  326. }
  327. // The field's value is required to be smaller tan N (numbers only)
  328. }else if(rule.type === "max"){
  329. if( !_.isNaN( Number( model.get(rule.field) ) ) &&
  330. !_.isNaN( Number( rule.value ) ) &&
  331. Number( model.get(rule.field) ) <= Number( rule.value ) ) {
  332. should_push = true;
  333. }
  334. // The field's value is required to be between N and M (numbers only)
  335. }else if(rule.type === "range"){
  336. if( !_.isNaN( Number( model.get(rule.field) ) ) &&
  337. _.isObject( rule.value ) &&
  338. !_.isNaN( Number( rule.value.min ) ) &&
  339. !_.isNaN( Number( rule.value.max ) ) &&
  340. Number( model.get(rule.field) ) >= Number( rule.value.min ) &&
  341. Number( model.get(rule.field) ) <= Number( rule.value.max ) ) {
  342. should_push = true;
  343. }
  344. // The field's value is required to be more than N chars long
  345. }else if(rule.type === "minLength"){
  346. if( model.get(rule.field).toString().length >= rule.value ) {
  347. should_push = true;
  348. }
  349. // The field's value is required to be no more than N chars long
  350. }else if(rule.type === "maxLength"){
  351. if( model.get(rule.field).toString().length <= rule.value ) {
  352. should_push = true;
  353. }
  354. // The field's value is required to be more than N chars long and no more than M chars long
  355. }else if(rule.type === "rangeLength"){
  356. if( _.isObject( rule.value ) &&
  357. !_.isNaN( Number( rule.value.min ) ) &&
  358. !_.isNaN( Number( rule.value.max ) ) &&
  359. model.get(rule.field).toString().length >= rule.value.min &&
  360. model.get(rule.field).toString().length <= rule.value.max ) {
  361. should_push = true;
  362. }
  363. // The field's value is required to be equal to one of the values in rules.value
  364. }else if(rule.type === "oneOf"){
  365. if( _.isArray( rule.value ) &&
  366. _.include( rule.value, model.get(rule.field) ) ) {
  367. should_push = true;
  368. }
  369. // The field's value is required to be equal to the value in rules.value
  370. }else if(rule.type === "equalTo"){
  371. if( rule.value === model.get(rule.field) ) {
  372. should_push = true;
  373. }
  374. // The field's value is required to match the regular expression
  375. }else if(rule.type === "pattern"){
  376. if( model.get(rule.field).toString().match(rule.value) ) {
  377. should_push = true;
  378. }
  379. //Unknown type
  380. }else{
  381. should_push = false;
  382. }
  383. });
  384. if( should_push ){
  385. filteredModels.push(model);
  386. }
  387. });
  388. return filteredModels;
  389. },
  390. // The actual place where the collection is filtered.
  391. // Check setFilter for arguments explicacion.
  392. _filter: function ( models, fields, filter ) {
  393. // For example, if you had a data model containing cars like { color: '', description: '', hp: '' },
  394. // your fields was set to ['color', 'description', 'hp'] and your filter was set
  395. // to "Black Mustang 300", the word "Black" will match all the cars that have black color, then
  396. // "Mustang" in the description and then the HP in the 'hp' field.
  397. // NOTE: "Black Musta 300" will return the same as "Black Mustang 300"
  398. // We accept fields to be a string, an array or an object
  399. // but if string or array is passed we need to convert it
  400. // to an object.
  401. var self = this;
  402. var obj_fields = {};
  403. if( _.isString( fields ) ) {
  404. obj_fields[fields] = {cmp_method: 'regexp'};
  405. }else if( _.isArray( fields ) ) {
  406. _.each(fields, function(field){
  407. obj_fields[field] = {cmp_method: 'regexp'};
  408. });
  409. }else{
  410. _.each(fields, function( cmp_opts, field ) {
  411. obj_fields[field] = _.defaults(cmp_opts, { cmp_method: 'regexp' });
  412. });
  413. }
  414. fields = obj_fields;
  415. //Remove diacritic characters if diacritic plugin is loaded
  416. if( _.has(Backbone.Paginator, 'removeDiacritics') && self.useDiacriticsPlugin ){
  417. filter = Backbone.Paginator.removeDiacritics(filter);
  418. }
  419. // 'filter' can be only a string.
  420. // If 'filter' is string we need to convert it to
  421. // a regular expression.
  422. // For example, if 'filter' is 'black dog' we need
  423. // to find every single word, remove duplicated ones (if any)
  424. // and transform the result to '(black|dog)'
  425. if( filter === '' || !_.isString(filter) ) {
  426. return models;
  427. } else {
  428. var words = filter.match(/\w+/ig);
  429. var pattern = "(" + _.uniq(words).join("|") + ")";
  430. var regexp = new RegExp(pattern, "igm");
  431. }
  432. var filteredModels = [];
  433. // We need to iterate over each model
  434. _.each( models, function( model ) {
  435. var matchesPerModel = [];
  436. // and over each field of each model
  437. _.each( fields, function( cmp_opts, field ) {
  438. var value = model.get( field );
  439. if( value ) {
  440. // The regular expression we created earlier let's us detect if a
  441. // given string contains each and all of the words in the regular expression
  442. // or not, but in both cases match() will return an array containing all
  443. // the words it matched.
  444. var matchesPerField = [];
  445. if( _.has(Backbone.Paginator, 'removeDiacritics') && self.useDiacriticsPlugin ){
  446. value = Backbone.Paginator.removeDiacritics(value.toString());
  447. }else{
  448. value = value.toString();
  449. }
  450. // Levenshtein cmp
  451. if( cmp_opts.cmp_method === 'levenshtein' && _.has(Backbone.Paginator, 'levenshtein') && self.useLevenshteinPlugin ) {
  452. var distance = Backbone.Paginator.levenshtein(value, filter);
  453. _.defaults(cmp_opts, { max_distance: 0 });
  454. if( distance <= cmp_opts.max_distance ) {
  455. matchesPerField = _.uniq(words);
  456. }
  457. // Default (RegExp) cmp
  458. }else{
  459. matchesPerField = value.match( regexp );
  460. }
  461. matchesPerField = _.map(matchesPerField, function(match) {
  462. return match.toString().toLowerCase();
  463. });
  464. _.each(matchesPerField, function(match){
  465. matchesPerModel.push(match);
  466. });
  467. }
  468. });
  469. // We just need to check if the returned array contains all the words in our
  470. // regex, and if it does, it means that we have a match, so we should save it.
  471. matchesPerModel = _.uniq( _.without(matchesPerModel, "") );
  472. if( _.isEmpty( _.difference(words, matchesPerModel) ) ) {
  473. filteredModels.push(model);
  474. }
  475. });
  476. return filteredModels;
  477. },
  478. // You shouldn't need to call info() as this method is used to
  479. // calculate internal data as first/prev/next/last page...
  480. info: function () {
  481. var self = this,
  482. info = {},
  483. totalRecords = (self.sortedAndFilteredModels) ? self.sortedAndFilteredModels.length : self.length,
  484. totalPages = Math.ceil(totalRecords / self.perPage);
  485. info = {
  486. totalUnfilteredRecords: self.origModels.length,
  487. totalRecords: totalRecords,
  488. currentPage: self.currentPage,
  489. perPage: this.perPage,
  490. totalPages: totalPages,
  491. lastPage: totalPages,
  492. previous: false,
  493. next: false,
  494. startRecord: totalRecords === 0 ? 0 : (self.currentPage - 1) * this.perPage + 1,
  495. endRecord: Math.min(totalRecords, self.currentPage * this.perPage)
  496. };
  497. if (self.currentPage > 1) {
  498. info.previous = self.currentPage - 1;
  499. }
  500. if (self.currentPage < info.totalPages) {
  501. info.next = self.currentPage + 1;
  502. }
  503. info.pageSet = self.setPagination(info);
  504. self.information = info;
  505. return info;
  506. },
  507. // setPagination also is an internal function that shouldn't be called directly.
  508. // It will create an array containing the pages right before and right after the
  509. // actual page.
  510. setPagination: function ( info ) {
  511. var pages = [], i = 0, l = 0;
  512. // How many adjacent pages should be shown on each side?
  513. var ADJACENT = 3,
  514. ADJACENTx2 = ADJACENT * 2,
  515. LASTPAGE = Math.ceil(info.totalRecords / info.perPage),
  516. LPM1 = -1;
  517. if (LASTPAGE > 1) {
  518. // not enough pages to bother breaking it up
  519. if (LASTPAGE < (7 + ADJACENTx2)) {
  520. for (i = 1, l = LASTPAGE; i <= l; i++) {
  521. pages.push(i);
  522. }
  523. }
  524. // enough pages to hide some
  525. else if (LASTPAGE > (5 + ADJACENTx2)) {
  526. //close to beginning; only hide later pages
  527. if (info.currentPage < (1 + ADJACENTx2)) {
  528. for (i = 1, l = 4 + ADJACENTx2; i < l; i++) {
  529. pages.push(i);
  530. }
  531. }
  532. // in middle; hide some front and some back
  533. else if (LASTPAGE - ADJACENTx2 > info.currentPage && info.currentPage > ADJACENTx2) {
  534. for (i = info.currentPage - ADJACENT; i <= info.currentPage + ADJACENT; i++) {
  535. pages.push(i);
  536. }
  537. }
  538. // close to end; only hide early pages
  539. else {
  540. for (i = LASTPAGE - (2 + ADJACENTx2); i <= LASTPAGE; i++) {
  541. pages.push(i);
  542. }
  543. }
  544. }
  545. }
  546. return pages;
  547. }
  548. });
  549. // @name: requestPager
  550. //
  551. // Paginator for server-side data being requested from a backend/API
  552. //
  553. // @description:
  554. // This paginator is responsible for providing pagination
  555. // and sort capabilities for requests to a server-side
  556. // data service (e.g an API)
  557. //
  558. Paginator.requestPager = Backbone.Collection.extend({
  559. sync: function ( method, model, options ) {
  560. var self = this;
  561. // Create default values if no others are specified
  562. _.defaults(self.paginator_ui, {
  563. firstPage: 0,
  564. currentPage: 1,
  565. perPage: 5,
  566. totalPages: 10
  567. });
  568. // Change scope of 'paginator_ui' object values
  569. _.each(self.paginator_ui, function(value, key) {
  570. if( _.isUndefined(self[key]) ) {
  571. self[key] = self.paginator_ui[key];
  572. }
  573. });
  574. // Some values could be functions, let's make sure
  575. // to change their scope too and run them
  576. var queryAttributes = {};
  577. _.each(self.server_api, function(value, key){
  578. if( _.isFunction(value) ) {
  579. value = _.bind(value, self);
  580. value = value();
  581. }
  582. queryAttributes[key] = value;
  583. });
  584. var queryOptions = _.clone(self.paginator_core);
  585. _.each(queryOptions, function(value, key){
  586. if( _.isFunction(value) ) {
  587. value = _.bind(value, self);
  588. value = value();
  589. }
  590. queryOptions[key] = value;
  591. });
  592. // Create default values if no others are specified
  593. queryOptions = _.defaults(queryOptions, {
  594. timeout: 25000,
  595. cache: false,
  596. type: 'GET',
  597. dataType: 'jsonp'
  598. });
  599. // Allows the passing in of {data: {foo: 'bar'}} at request time to overwrite server_api defaults
  600. if( options.data ){
  601. options.data = decodeURIComponent($.param(_.extend(queryAttributes,options.data)));
  602. // options.data = _.extend(queryAttributes,options.data);
  603. }else{
  604. // options.data = queryAttributes;
  605. options.data = decodeURIComponent($.param(queryAttributes));
  606. }
  607. queryOptions = _.extend(queryOptions, {
  608. jsonpCallback: 'callback',
  609. processData: true,
  610. url: _.result(queryOptions, 'url')
  611. }, options);
  612. return Backbone.ajax( queryOptions );
  613. },
  614. requestNextPage: function ( options ) {
  615. if ( this.currentPage !== undefined ) {
  616. this.currentPage += 1;
  617. return this.pager( options );
  618. } else {
  619. var response = new $.Deferred();
  620. response.reject();
  621. return response.promise();
  622. }
  623. },
  624. requestPreviousPage: function ( options ) {
  625. if ( this.currentPage !== undefined ) {
  626. this.currentPage -= 1;
  627. return this.pager( options );
  628. } else {
  629. var response = new $.Deferred();
  630. response.reject();
  631. return response.promise();
  632. }
  633. },
  634. updateOrder: function ( column ) {
  635. if (column !== undefined) {
  636. this.sortField = column;
  637. this.pager();
  638. }
  639. },
  640. goTo: function ( page, options ) {
  641. if ( page !== undefined ) {
  642. this.currentPage = parseInt(page, 10);
  643. return this.pager( options );
  644. } else {
  645. var response = new $.Deferred();
  646. response.reject();
  647. return response.promise();
  648. }
  649. },
  650. howManyPer: function ( count ) {
  651. if( count !== undefined ){
  652. this.currentPage = this.firstPage;
  653. this.perPage = count;
  654. this.pager();
  655. }
  656. },
  657. sort: function () {
  658. //assign to as needed.
  659. },
  660. info: function () {
  661. var info = {
  662. // If parse() method is implemented and totalRecords is set to the length
  663. // of the records returned, make it available. Else, default it to 0
  664. totalRecords: this.totalRecords || 0,
  665. currentPage: this.currentPage,
  666. firstPage: this.firstPage,
  667. totalPages: this.totalPages,
  668. lastPage: this.totalPages,
  669. perPage: this.perPage
  670. };
  671. this.information = info;
  672. return info;
  673. },
  674. // fetches the latest results from the server
  675. pager: function ( options ) {
  676. if ( !_.isObject(options) ) {
  677. options = {};
  678. }
  679. return this.fetch( options );
  680. }
  681. });
  682. return Paginator;
  683. }( Backbone, _, jQuery ));