Initial version of files.
Showing
10 changed files
with
523 additions
and
0 deletions
.gitignore
0 → 100644
Gruntfile.js
0 → 100644
1 | /* global module */ | ||
2 | |||
3 | module.exports = function (grunt) { | ||
4 | /* global require */ | ||
5 | 'use strict'; | ||
6 | |||
7 | var config = {}; | ||
8 | config.base = 'src'; | ||
9 | config.jshint = { | ||
10 | options: { | ||
11 | }, | ||
12 | browserOptions: { | ||
13 | }, | ||
14 | }; | ||
15 | config.jscs = { | ||
16 | options: { | ||
17 | validateIndentation: 4, | ||
18 | reporter: 'console', | ||
19 | maxErrors: -1, | ||
20 | }, | ||
21 | }; | ||
22 | config.bower = { | ||
23 | directory: 'lib/bower', | ||
24 | }; | ||
25 | config.jasmine = { | ||
26 | withCoverage: true, | ||
27 | }; | ||
28 | var montyPython = require('grunt-monty-python')(grunt); | ||
29 | montyPython.createConfig(config); | ||
30 | }; |
bower.json
0 → 100644
1 | { | ||
2 | "name": "search-tools", | ||
3 | "version": "2016.07.26-0", | ||
4 | "authors": [ | ||
5 | "Adam Heath <doogie@brainfood.com>" | ||
6 | ], | ||
7 | "main": [ | ||
8 | "src/scripts/search-tools/*.js" | ||
9 | ], | ||
10 | "ignore": [ | ||
11 | "**/.*", | ||
12 | "node_modules", | ||
13 | "src/lib", | ||
14 | "src/scripts/**/*.spec.js", | ||
15 | "src/scripts/config.js", | ||
16 | "src/scripts/main.js" | ||
17 | ], | ||
18 | "dependencies": { | ||
19 | "backbone": "", | ||
20 | "jquery": "", | ||
21 | "requirejs": "", | ||
22 | "underscore": "" | ||
23 | } | ||
24 | } |
package.json
0 → 100644
1 | { | ||
2 | "name": "search-tools", | ||
3 | "version": "2016.07.26-0", | ||
4 | "dependencies": { | ||
5 | "requirejs": "" | ||
6 | }, | ||
7 | "devDependencies": { | ||
8 | "grunt": "~0", | ||
9 | "grunt-monty-python": "git+http://gitlab.brainfood.com/brainfood/grunt-monty-python.git", | ||
10 | "rivets": "" | ||
11 | }, | ||
12 | "engines": { | ||
13 | "node": ">=0.8.0" | ||
14 | } | ||
15 | } | ||
16 |
src/scripts/config.js
0 → 100644
1 | /* global require:true */ | ||
2 | var require; | ||
3 | require = (function() { | ||
4 | 'use strict'; | ||
5 | |||
6 | var require = { | ||
7 | baseUrl: 'scripts', | ||
8 | config: { | ||
9 | }, | ||
10 | shim: { | ||
11 | backbone: { | ||
12 | deps: ['underscore'], | ||
13 | exports: 'Backbone', | ||
14 | }, | ||
15 | underscore: { | ||
16 | exports: '_', | ||
17 | }, | ||
18 | }, | ||
19 | paths: { | ||
20 | backbone: '../lib/bower/backbone/backbone', | ||
21 | jquery: '../lib/bower/jquery/dist/jquery', | ||
22 | underscore: '../lib/bower/underscore/underscore', | ||
23 | }, | ||
24 | }; | ||
25 | |||
26 | return require; | ||
27 | })(); |
src/scripts/main.js
0 → 100644
src/scripts/search-tools/Paginator.js
0 → 100644
1 | define(function(require) { | ||
2 | 'use strict'; | ||
3 | var _ = require('underscore'); | ||
4 | var Backbone = require('backbone'); | ||
5 | var Util = require('./Util'); | ||
6 | |||
7 | var Paginator = Backbone.Model.extend({ | ||
8 | defaults: function defaults() { | ||
9 | return { | ||
10 | hasNext: false, | ||
11 | hasNextJump: undefined, | ||
12 | hasPrevious: false, | ||
13 | hasPreviousJump: undefined, | ||
14 | leadingPageCount: 4, | ||
15 | nextJumpPage: undefined, | ||
16 | nextPage: undefined, | ||
17 | pages: [], | ||
18 | pageJump: 5, | ||
19 | previousJumpPage: undefined, | ||
20 | previousPage: undefined, | ||
21 | totalPages: 0, | ||
22 | trailingPageCount: 5, | ||
23 | }; | ||
24 | }, | ||
25 | initialize: function(data, options) { | ||
26 | var buildPages = _.bind(function buildPages() { | ||
27 | var source = this.get('source'); | ||
28 | if (!source) { | ||
29 | return; | ||
30 | } | ||
31 | var currentPage = parseInt(source.get('currentPage')); | ||
32 | var pageSize = parseInt(source.get('pageSize')); | ||
33 | var totalCount = parseInt(source.get('totalCount')); | ||
34 | if (!totalCount) { | ||
35 | return; | ||
36 | } | ||
37 | var pages = []; | ||
38 | var totalPages = Math.floor((totalCount + pageSize - 1) / pageSize); | ||
39 | if (currentPage < 0) { | ||
40 | source.set('currentPage', 1); | ||
41 | return; | ||
42 | } else if (currentPage > totalPages) { | ||
43 | source.set('currentPage', totalPages); | ||
44 | return; | ||
45 | } | ||
46 | function addPage(self, i) { | ||
47 | pages.push({ | ||
48 | current: i === currentPage, | ||
49 | jump: _.bind(function() { | ||
50 | this.get('source').set('currentPage', i); | ||
51 | return true; | ||
52 | }, self), | ||
53 | number: i, | ||
54 | }); | ||
55 | } | ||
56 | var startAt = currentPage - this.get('leadingPageCount'), endAt = currentPage + this.get('trailingPageCount'); | ||
57 | if (startAt < 1) { | ||
58 | endAt += (1 - startAt); | ||
59 | } | ||
60 | if (endAt > totalPages) { | ||
61 | startAt -= endAt - totalPages - 1; | ||
62 | endAt = totalPages + 1; | ||
63 | } | ||
64 | if (startAt < 1) { | ||
65 | startAt = 1; | ||
66 | } | ||
67 | |||
68 | for (var i = startAt; i < endAt; i++) { | ||
69 | addPage(this, i); | ||
70 | } | ||
71 | var hasPrevious = currentPage > 1; | ||
72 | var hasNext = currentPage < totalPages; | ||
73 | var pageJump = this.get('pageJump'); | ||
74 | var nextJumpPage, previousJumpPage, hasNextJump, hasPreviousJump; | ||
75 | if (pageJump) { | ||
76 | nextJumpPage = currentPage + pageJump; | ||
77 | previousJumpPage = currentPage - pageJump; | ||
78 | hasNextJump = nextJumpPage < totalPages; | ||
79 | hasPreviousJump = previousJumpPage > 0; | ||
80 | } else { | ||
81 | hasNextJump = false; | ||
82 | hasPreviousJump = false; | ||
83 | } | ||
84 | this.set({ | ||
85 | hasNext: hasNext, | ||
86 | hasNextJump: hasNextJump, | ||
87 | hasPrevious: hasPrevious, | ||
88 | hasPreviousJump: hasPreviousJump, | ||
89 | nextJumpPage: hasNextJump ? nextJumpPage : undefined, | ||
90 | nextPage: hasNext ? currentPage + 1 : undefined, | ||
91 | pages: pages, | ||
92 | previousJumpPage: hasNextJump ? previousJumpPage : undefined, | ||
93 | previousPage: hasPrevious ? currentPage - 1 : undefined, | ||
94 | totalPages: totalPages, | ||
95 | }); | ||
96 | }, this); | ||
97 | this.on('change:leadingPageCount change:trailingPageCount', buildPages); | ||
98 | var installActions = _.bind(function installActions(eventName, nextName, actNextName, hasNextName, previousName, actPreviousName, hasPreviousName, pageCount) { | ||
99 | this.on(eventName, function() { | ||
100 | var hasNext = this.get(hasNextName); | ||
101 | var hasPrevious = this.get(hasPreviousName); | ||
102 | var next, previous; | ||
103 | if (hasNext) { | ||
104 | next = _.bind(function(e) { | ||
105 | Util.preventDefault(e); | ||
106 | var source = this.get('source'); | ||
107 | source.set('currentPage', source.get('currentPage') + pageCount); | ||
108 | return false; | ||
109 | }, this); | ||
110 | } else { | ||
111 | next = Util.eventFalse; | ||
112 | } | ||
113 | if (hasPrevious) { | ||
114 | previous = _.bind(function(e) { | ||
115 | Util.preventDefault(e); | ||
116 | var source = this.get('source'); | ||
117 | source.set('currentPage', source.get('currentPage') - pageCount); | ||
118 | return false; | ||
119 | }, this); | ||
120 | } else { | ||
121 | previous = Util.eventFalse; | ||
122 | } | ||
123 | this.set(nextName, next); | ||
124 | this.set(previousName, previous); | ||
125 | }, this); | ||
126 | this[actNextName] = _.bind(function() { | ||
127 | return this.get(nextName)(); | ||
128 | }, this); | ||
129 | this[actPreviousName] = _.bind(function() { | ||
130 | return this.get(previousName)(); | ||
131 | }, this); | ||
132 | this.trigger(eventName.split(' ')[0]); | ||
133 | }, this); | ||
134 | this.on('change:pageJump', function() { | ||
135 | var pageJump = this.get('pageJump'); | ||
136 | installActions('change:hasNextJump change:hasPreviousJump', 'nextJump', 'actNextJump', 'hasNextJump', 'previousJump', 'actPreviousJump', 'hasPreviousJump', pageJump); | ||
137 | }, this); | ||
138 | installActions('change:hasNext change:hasPrevious', 'next', 'actNext', 'hasNext', 'previous', 'actPrevious', 'hasPrevious', 1); | ||
139 | this.on('change:source', function() { | ||
140 | var oldSource = this.previous('source'); | ||
141 | if (oldSource) { | ||
142 | this.stopListening(oldSource); | ||
143 | } | ||
144 | this.listenTo(this.get('source'), 'change:pageSize change:currentPage change:totalCount', buildPages); | ||
145 | }); | ||
146 | var r = Paginator.__super__.initialize.apply(this, arguments); | ||
147 | buildPages(); | ||
148 | this.trigger('change:pageJump'); | ||
149 | this.trigger('change:source'); | ||
150 | return r; | ||
151 | } | ||
152 | }); | ||
153 | return Paginator; | ||
154 | }); |
src/scripts/search-tools/Paginator.spec.js
0 → 100644
1 | define(function(require) { | ||
2 | 'use strict'; | ||
3 | var _ = require('underscore'); | ||
4 | var Backbone = require('backbone'); | ||
5 | var Paginator = require('search-tools/Paginator'); | ||
6 | |||
7 | describe('Paginator', function() { | ||
8 | it('loads', function() { | ||
9 | expect(Paginator).toBeDefined(); | ||
10 | }); | ||
11 | describe(':pages', function() { | ||
12 | it('default value is not shared globally', function() { | ||
13 | var p1 = new Paginator(), p2 = new Paginator(); | ||
14 | expect(p1.get('pages')).not.toBe(p2.get('pages')); | ||
15 | }); | ||
16 | }); | ||
17 | describe('singleton', function() { | ||
18 | var p, source; | ||
19 | |||
20 | function checkHas(hasNext, hasNextJump, hasPrevious, hasPreviousJump) { | ||
21 | var wanted = { | ||
22 | hasNext: hasNext, | ||
23 | hasNextJump: hasNextJump, | ||
24 | hasPrevious: hasPrevious, | ||
25 | hasPreviousJump: hasPreviousJump, | ||
26 | }; | ||
27 | it('has', function() { | ||
28 | var got = { | ||
29 | hasNext: p.get('hasNext'), | ||
30 | hasNextJump: p.get('hasNextJump'), | ||
31 | hasPrevious: p.get('hasPrevious'), | ||
32 | hasPreviousJump: p.get('hasPreviousJump'), | ||
33 | }; | ||
34 | expect(got).toEqual(wanted); | ||
35 | }); | ||
36 | } | ||
37 | function checkPages(wantedLength, offset, currentPage) { | ||
38 | var pages; | ||
39 | beforeEach(function() { | ||
40 | pages = p.get('pages'); | ||
41 | }); | ||
42 | //console.log(JSON.stringify(pages)); | ||
43 | var wanted = { length: wantedLength, pages: [] }; | ||
44 | for (var i = 0; i < wantedLength; i++) { | ||
45 | wanted.pages[i] = { current: i + 1 === currentPage, number: i + 1 + offset }; | ||
46 | } | ||
47 | it('pages', function() { | ||
48 | pages = p.get('pages'); | ||
49 | var got = { length: pages.length, pages: [] }; | ||
50 | for (var i = 0; i < pages.length; i++) { | ||
51 | var page = pages[i]; | ||
52 | got.pages[i] = { current: page.current, number: page.number }; | ||
53 | } | ||
54 | expect(got).toEqual(wanted); | ||
55 | }); | ||
56 | } | ||
57 | |||
58 | beforeEach(function() { | ||
59 | source = new Backbone.Model({currentPage: 1, pageSize: 10, totalCount: 0}); | ||
60 | p = new Paginator({source: source}); | ||
61 | }); | ||
62 | describe('default settings', function() { | ||
63 | checkHas(false, undefined, false, undefined); | ||
64 | checkPages(0, 0, 1); | ||
65 | }); | ||
66 | describe('35 items', function() { | ||
67 | beforeEach(function() { | ||
68 | source.set({totalCount:35}); | ||
69 | }); | ||
70 | describe('initial settings', function() { | ||
71 | checkHas(true, false, false, false); | ||
72 | checkPages(4, 0, 1); | ||
73 | }); | ||
74 | describe('clamping', function() { | ||
75 | describe('set currentPage=5', function() { | ||
76 | beforeEach(function() { | ||
77 | source.set({currentPage: 5}); | ||
78 | }); | ||
79 | checkHas(false, false, true, false); | ||
80 | checkPages(4, 0, 4); | ||
81 | }); | ||
82 | describe('set currentPage=-1', function() { | ||
83 | beforeEach(function() { | ||
84 | source.set({currentPage: -1}); | ||
85 | }); | ||
86 | checkHas(true, false, false, false); | ||
87 | checkPages(4, 0, 1); | ||
88 | }); | ||
89 | }); | ||
90 | describe('next[1]', function() { | ||
91 | beforeEach(function() { | ||
92 | p.actNext(); | ||
93 | }); | ||
94 | checkPages(4, 0, 2); | ||
95 | describe('previous', function() { | ||
96 | beforeEach(function() { | ||
97 | p.actPrevious(); | ||
98 | }); | ||
99 | checkHas(true, false, false, false); | ||
100 | checkPages(4, 0, 1); | ||
101 | }); | ||
102 | describe('next[2]', function() { | ||
103 | beforeEach(function() { | ||
104 | p.actNext(); | ||
105 | p.actNext(); | ||
106 | }); | ||
107 | checkHas(false, false, true, false); | ||
108 | checkPages(4, 0, 4); | ||
109 | describe('next[1] - clamp', function() { | ||
110 | beforeEach(function() { | ||
111 | p.actNext(); | ||
112 | }); | ||
113 | checkHas(false, false, true, false); | ||
114 | checkPages(4, 0, 4); | ||
115 | }); | ||
116 | describe('page[0].jump', function() { | ||
117 | beforeEach(function() { | ||
118 | p.get('pages')[0].jump(); | ||
119 | }); | ||
120 | checkHas(true, false, false, false); | ||
121 | checkPages(4, 0, 1); | ||
122 | }); | ||
123 | describe('page[3].jump', function() { | ||
124 | beforeEach(function() { | ||
125 | p.get('pages')[3].jump(); | ||
126 | }); | ||
127 | checkHas(false, false, true, false); | ||
128 | checkPages(4, 0, 4); | ||
129 | }); | ||
130 | describe('decrease to 25 items', function() { | ||
131 | beforeEach(function() { | ||
132 | source.set({totalCount:25}); | ||
133 | }); | ||
134 | checkHas(false, false, true, false); | ||
135 | checkPages(3, 0, 3); | ||
136 | describe('increase to 150 items', function() { | ||
137 | beforeEach(function() { | ||
138 | source.set({totalCount:150}); | ||
139 | }); | ||
140 | checkHas(true, true, true, false); | ||
141 | checkPages(9, 0, 3); | ||
142 | }); | ||
143 | }); | ||
144 | }); | ||
145 | }); | ||
146 | describe('previous[1]', function() { | ||
147 | beforeEach(function() { | ||
148 | p.actPrevious(); | ||
149 | }); | ||
150 | checkHas(true, false, false, false); | ||
151 | checkPages(4, 0, 1); | ||
152 | }); | ||
153 | }); | ||
154 | describe('150 items', function() { | ||
155 | beforeEach(function() { | ||
156 | source.set({totalCount:150}); | ||
157 | }); | ||
158 | checkHas(true, true, false, false); | ||
159 | checkPages(9, 0, 1); | ||
160 | describe('page[7].jump', function() { | ||
161 | beforeEach(function() { | ||
162 | p.get('pages')[7].jump(); | ||
163 | }); | ||
164 | checkHas(true, true, true, true); | ||
165 | checkPages(9, 3, 5); | ||
166 | describe('previousJump', function() { | ||
167 | beforeEach(function() { | ||
168 | p.actPreviousJump(); | ||
169 | }); | ||
170 | checkHas(true, true, true, false); | ||
171 | checkPages(9, 0, 3); | ||
172 | }); | ||
173 | }); | ||
174 | }); | ||
175 | describe('no page jump', function() { | ||
176 | beforeEach(function() { | ||
177 | p.set({pageJump: false}); | ||
178 | source.set({totalCount:150}); | ||
179 | }); | ||
180 | checkHas(true, false, false, false); | ||
181 | checkPages(9, 0, 1); | ||
182 | }); | ||
183 | }); | ||
184 | }); | ||
185 | }); |
src/scripts/search-tools/Util.js
0 → 100644
src/scripts/search-tools/Util.spec.js
0 → 100644
1 | define(function(require) { | ||
2 | 'use strict'; | ||
3 | var _ = require('underscore'); | ||
4 | var Util = require('search-tools/Util'); | ||
5 | |||
6 | describe('Util', function() { | ||
7 | it('loads', function() { | ||
8 | expect(Util).toBeDefined(); | ||
9 | }); | ||
10 | |||
11 | it('exported items', function() { | ||
12 | var keys = {}; | ||
13 | _.each(Util, function(value, key) { | ||
14 | keys[key] = true; | ||
15 | }); | ||
16 | expect(keys).toEqual({ | ||
17 | preventDefault: true, | ||
18 | eventFalse: true, | ||
19 | }); | ||
20 | }); | ||
21 | describe('methods', function() { | ||
22 | var e; | ||
23 | beforeEach(function() { | ||
24 | e = {preventDefault: jasmine.createSpy('preventDefault')}; | ||
25 | }); | ||
26 | it('preventDefault(null)', function() { | ||
27 | var r = Util.preventDefault(null); | ||
28 | expect(r).toBe(undefined); | ||
29 | expect(e.preventDefault).not.toHaveBeenCalled(); | ||
30 | }); | ||
31 | it('preventDefault(undefined)', function() { | ||
32 | var r = Util.preventDefault(undefined); | ||
33 | expect(r).toBe(undefined); | ||
34 | expect(e.preventDefault).not.toHaveBeenCalled(); | ||
35 | }); | ||
36 | it('preventDefault(e)', function() { | ||
37 | var r = Util.preventDefault(e); | ||
38 | expect(r).toBe(undefined); | ||
39 | expect(e.preventDefault).toHaveBeenCalled(); | ||
40 | }); | ||
41 | it('eventFalse(null)', function() { | ||
42 | var r = Util.eventFalse(null); | ||
43 | expect(r).toBe(false); | ||
44 | expect(e.preventDefault).not.toHaveBeenCalled(); | ||
45 | }); | ||
46 | it('eventFalse(undefined)', function() { | ||
47 | var r = Util.eventFalse(undefined); | ||
48 | expect(r).toBe(false); | ||
49 | expect(e.preventDefault).not.toHaveBeenCalled(); | ||
50 | }); | ||
51 | it('preventDefault(e)', function() { | ||
52 | var r = Util.eventFalse(e); | ||
53 | expect(r).toBe(false); | ||
54 | expect(e.preventDefault).toHaveBeenCalled(); | ||
55 | }); | ||
56 | }); | ||
57 | }); | ||
58 | }); |
-
Please register or sign in to post a comment