3 | grid-it example
4 |
5 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
51 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Cast.js
2 |
3 | Cast helps you build beautiful, animated grid layouts. Supply an array of attributes, select a layout mode, supply a template, and get a grid-view. It's vanilla Javascript and it's inspired by [Isotope](https://github.com/desandro/isotope). It works in IE9+, and IE8 with `bind` and `indexOf` polyfills.
4 |
5 | Check out the [demonstration](http://cast.meteor.com) built with Meteor.
6 |
7 | If you're using React, use [react-cast](http://github.com/bmcmahen/react-cast) instead.
8 |
9 | ## Installation
10 |
11 | The easiest way to use Cast is to use the `cast.js` located in the `dist` folder, and require this script in your html page. Cast will then be available under the global variable `cast`'.
12 |
13 | Alternatively, Cast can be used as a [component](https://github.com/component/component).
14 |
15 | $ component install bmcmahen/cast
16 |
17 | ## Example
18 |
19 | ```javascript
20 | var Cast = require('cast');
21 |
22 | var docs = [{name: 'ben'}, {name: 'kit'}, {name: 'rick'}, {name: 'james'}];
23 | var container = document.getElementById('#wrapper');
24 |
25 | function render(attr, el, block){
26 | el.innerHTML = '
' + attr.name + '
';
27 | }
28 |
29 | // Create our cast
30 | var grid = new Cast(container)
31 | .render(render)
32 | .data(docs, 'name')
33 | .sortBy('name')
34 | .justify(50, 50, 10, 10)
35 | .draw();
36 | ```
37 |
38 | ## API
39 |
40 | ### new Cast(container)
41 |
42 | `container` should be the element that you want your cast to appear in.
43 |
44 | ### .render(fn)
45 |
46 | Render will be called whenever a new element is rendered in the cast layout. This should be called __before adding documents to your cast__. For example:
47 |
48 | ```javascript
49 | var layout = new Cast(container)
50 | .render(function(attr, el, block){
51 | el.innerHTML = template(attr);
52 | })
53 | .data(docs, 'name');
54 | ```
55 |
56 | ### .data(docs, Fn|String)
57 |
58 | Supply Cast with an array of documents. Use the callback function to supply a unique identifer for each field, which will allow Cast to update, remove, and add attributes on subsequent calls. Alternatively, supply the key of the unique identifer ('_id', 'id', etc.).
59 |
60 | cast.data(docs, function(attr){
61 | return attr._id;
62 | });
63 |
64 | cast.data(docs, '_id');
65 |
66 | ### .justify(width, height, paddingWidth, paddingHeight)
67 |
68 | Calculates grid positions to maintain a container left & right padding of zero. Grid item width and height remain constant, while grid-item-padding is dyanimc. If `#draw` has been called, `#justify` will automatically rerender your views.
69 |
70 | ### .center(width, height, paddingWidth, paddingHeight)
71 |
72 | Calculates the grid with dynamic width on the left and right side of the wrapper. Grid item width, height, paddingWidth, and paddingHeight are constant. If `#draw` has been called, `#center` will automatically rerender your views.
73 |
74 | ### .dynamic(width, height, paddingWidth, paddingHeight)
75 |
76 | Calculates the grid with a constant `paddingWidth` and `paddingHeight`, and a dynamic `boxWidth` and `boxHeight`. If `#draw` has been called, `#dynamic` will automatically rerender your views.
77 |
78 | ### .list(height, paddingHeight)
79 |
80 | Calculates the grid as a list, with one object per line.
81 |
82 | ### .toJSON()
83 |
84 | var json = cast.data(docs).justify(40, 40, 10, 10).toJSON();
85 |
86 | ### .reset(docs, fn|String)
87 |
88 | Resets the Cast object with the supplied array. Use the callback to provide a unique, constant value for the field.
89 |
90 | cast.reset(docs, function(attr){
91 | return attr._id;
92 | });
93 |
94 | cast.reset(docs, 'id');
95 |
96 | ### .add(docs, fn|String)
97 |
98 | Appends docs to the Cast object.
99 |
100 | ### .remove(uid)
101 |
102 | cast.remove('34');
103 |
104 | ### .sortBy(field, 1)
105 |
106 | Sorts the collection based on a `field`.
107 |
108 | cast.sortBy('name', -1).center(50, 50, 10, 10);
109 |
110 | ### .draw()
111 |
112 | Simple utility function to append the cast layout to the container element.
113 |
114 | ## Events
115 |
116 | ### enter(model)
117 |
118 | cast.on('enter', function(model){ });
119 |
120 | ### exit(model)
121 | ### view-created(view)
122 | ### view-destroyed(view)
123 |
124 |
125 |
126 | ## Meteor Usage
127 |
128 | See an example app [here](https://github.com/bmcmahen/meteor-cast-example). Include the standalone `cast.js` file in your client-side folder structure, and Meteor should automatically load it into your application. Create a template that will act as your cast wrapper.
129 |
130 | ```html
131 | {{#constant}}
132 |
133 | {{/constant}}
134 | ```
135 |
136 | In your code, you'll want to create a simple template function that will be used to render each cast item. Here's a simple example:
137 |
138 | ```javascript
139 | function renderTemplate(obj){
140 | return '
' + obj.title + '
';
141 | }
142 | ```
143 |
144 | Inside your templat rendered callback, instantiate a new `cast` and attach the data to the cast, specifying the layout that you want. Putting your `data` attachment function inside of an autorun will automatically update your cast layout any time the collection changes.
145 |
146 | ```javascript
147 | Template.cast.rendered = function(){
148 |
149 | var el = document.getElementById('#cast');
150 |
151 | var mycast = cast(el, renderTemplate);
152 | mycast.draw();
153 |
154 | this.handle = Meteor.autorun(function(){
155 | var videos = Videos.find().fetch();
156 | mycast
157 | .data(videos, '_id')
158 | .dynamic(150, 150, 10, 10);
159 | });
160 | }
161 | ```
162 | ## License
163 |
164 | MIT
165 |
--------------------------------------------------------------------------------
/test/cast.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var cast = require('cast');
3 | var $ = require('jquery');
4 |
5 | var testTemplate = function(attr){
6 | return '
'+attr.name+'
';
7 | };
8 |
9 | var container = document.createElement('div');
10 | container.id = 'cast';
11 | container.style.width = 500 + 'px';
12 | document.body.appendChild(container);
13 |
14 | describe('Cast', function(){
15 |
16 | beforeEach(function(){
17 | this.cast = cast(container);
18 | });
19 |
20 | afterEach(function(){
21 | delete this.cast;
22 | });
23 |
24 | it('should be constructed with a container', function(){
25 | assert(this.cast);
26 | assert(this.cast instanceof cast);
27 | assert(this.cast.wrapper);
28 | });
29 |
30 | it('should set the width', function(){
31 | assert(this.cast.wrapperWidth === 500);
32 | });
33 |
34 | describe('#data', function(){
35 | var docs = [{name: 'ben'}, {name: 'kit'}, {name: 'joe'}];
36 |
37 | it('should iterate through the docs array and create models', function(){
38 | this.cast.data(docs, 'name');
39 | assert(this.cast.collection.length() === 3);
40 | assert(this.cast.collection.at(0).get('name') === 'ben');
41 | assert(this.cast.collection.get('ben').get('name') === 'ben');
42 | });
43 |
44 | it('should support a fn callback to determine unique id', function(){
45 | this.cast.data(docs, function(attr){
46 | return attr.name;
47 | });
48 | assert(this.cast.collection.length() === 3);
49 | });
50 |
51 | it('should remove missing docs upon subsequent calls', function(){
52 | this.cast.data(docs, 'name');
53 | this.cast.data([{ name: 'ben' }], 'name');
54 | assert(this.cast.collection.length() === 1);
55 | assert(!this.cast.collection.get('kit'));
56 | });
57 |
58 | it('should fill in new docs upon subsquent calls', function(){
59 | this.cast.data(docs, 'name');
60 | var extended = docs;
61 | extended.push({ name: 'superman' });
62 | this.cast.data(extended, 'name');
63 | assert(this.cast.collection.get('superman'));
64 | assert(this.cast.toJSON().length === 4);
65 | });
66 |
67 | });
68 |
69 | describe('#toJSON', function(){
70 | var docs = [{name: 'ben', age: 28}, {name: 'kit'}, {name: 'joe'}];
71 |
72 | it('should return all of our docs', function(){
73 | this.cast.data(docs, 'name');
74 | var json = this.cast.toJSON();
75 | assert(json.length === 3);
76 | assert(json[0].name === 'ben');
77 | assert(json[0].age == 28);
78 | assert(json[2].name === 'joe');
79 | });
80 |
81 | });
82 |
83 | describe('#reset', function(){
84 | var docs = [{name: 'ben', age: 28}, {name: 'kit'}, {name: 'joe'}];
85 |
86 | it('should clear out our previous attributes, and add new ones', function(){
87 | this.cast.data(docs, 'name');
88 | this.cast.reset([{name: 'albert'}, {name: 'roger'}], 'name');
89 | var json = this.cast.toJSON();
90 | assert(json.length === 2);
91 | assert(!this.cast.collection.get('ben'));
92 | assert(json[0].name === 'albert');
93 | assert(json[1].name === 'roger');
94 | });
95 | });
96 |
97 | describe('#add', function(){
98 |
99 | it('should add a value to an empty cast collection', function(){
100 | this.cast.add({name: 'ben'}, 'name');
101 | assert(this.cast.toJSON()[0].name === 'ben');
102 | this.cast.add({name: 'kit'}, 'name');
103 | assert(this.cast.toJSON().length === 2);
104 | assert(this.cast.toJSON()[1].name === 'kit');
105 | });
106 |
107 | });
108 |
109 | // better tests here...
110 | describe('#justify', function(){
111 | var docs = [{name: 'ben', age: 28}, {name: 'kit'}, {name: 'joe'}];
112 |
113 | it('should set a left and top attribute', function(){
114 | this.cast.data(docs, 'name');
115 | this.cast.justify(50, 50, 20, 20);
116 | var json = this.cast.toJSON();
117 | assert(json);
118 | for (var i = 0; i < json.length; i++){
119 | assert(typeof json[i].left !== 'undefined');
120 | assert(json[i].top);
121 | }
122 | });
123 |
124 | it('should set the first to 0, 20; last to 450, 20', function(){
125 | this.cast.data(docs, 'name');
126 | this.cast.justify(50, 50, 20, 20);
127 | var json = this.cast.toJSON();
128 | assert(json[0].left === 0);
129 | assert(json[0].top === 20);
130 | });
131 | });
132 |
133 | // better tests here...
134 | describe('#center', function(){
135 | var docs = [{name: 'ben', age: 28}, {name: 'kit'}, {name: 'joe'}];
136 |
137 | it('should set a left and top attribute', function(){
138 | this.cast.data(docs, 'name');
139 | this.cast.center(50, 50, 20, 20);
140 | var json = this.cast.toJSON();
141 | assert(json);
142 | for (var i = 0; i < json.length; i++){
143 | assert(json[i].left);
144 | assert(json[i].top);
145 | }
146 |
147 | });
148 | });
149 |
150 | // better tests here...
151 | describe('#dynamic', function(){
152 | var docs = [{name: 'ben', age: 28}, {name: 'kit'}, {name: 'joe'}];
153 |
154 | it('should set a left, top, width, and height', function(){
155 | this.cast.data(docs, 'name');
156 | this.cast.dynamic(50, 50, 20, 20);
157 | var json = this.cast.toJSON();
158 | assert(json);
159 | for (var i = 0; i < json.length; i++){
160 | assert(json[i].left);
161 | assert(json[i].top);
162 | assert(json[i].width);
163 | assert(json[i].top);
164 | }
165 |
166 | });
167 |
168 |
169 | });
170 |
171 | describe('#draw', function(){
172 | var docs = [{name: 'ben', age: 28}, {name: 'kit'}, {name: 'joe'}];
173 |
174 | beforeEach(function(){
175 | this.cast = cast(container)
176 | .data(docs, 'name')
177 | .justify(50, 50, 20, 20)
178 | .draw(testTemplate);
179 | });
180 |
181 | afterEach(function(){
182 | delete this.cast;
183 | });
184 |
185 | it('should attach itself to the container', function(){
186 | assert(container.children[0].children.length === 3);
187 | });
188 |
189 | it('should render using the supplied template', function(){
190 | assert($(container.children[0].children[0]).find('p').text() === 'ben');
191 | assert($(container.children[0].children[1]).find('p').text() === 'kit');
192 | });
193 |
194 | it('should remove a view when associated doc is removed', function(done){
195 | this.cast.data([{name: 'ben'}], 'name');
196 |
197 | setTimeout(function(){
198 | assert($('.Cast-item').length === 1);
199 | assert($('.Cast-item').find('p').text() === 'ben');
200 | done();
201 | }, 500);
202 | });
203 |
204 | it('should add a view when new doc is added', function(){
205 | this.cast.data([{name: 'zoey'}], 'name');
206 | assert($('.Cast-item').length === 4);
207 | });
208 |
209 |
210 | });
211 |
212 | });
213 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Module dependencies
3 | */
4 |
5 | var Emitter = require('emitter');
6 | var Dict = require('ordered-dictionary');
7 | var fastdom = require('fastdom');
8 | var type = require('type');
9 | var Block = require('./block');
10 | var empty = require('empty');
11 |
12 | module.exports = Cast;
13 |
14 | /**
15 | * Cast
16 | * @param {Element} wrapper
17 | */
18 |
19 | function Cast(wrapper){
20 | if (!(this instanceof Cast)) return new Cast(wrapper);
21 | this.wrapper = wrapper;
22 | this.wrapperWidth = this.wrapper.clientWidth;
23 | this.el = document.createElement('div');
24 | this.el.className = 'Cast';
25 | this.collection = new Dict();
26 | }
27 |
28 | Emitter(Cast.prototype);
29 |
30 | /**
31 | * Supply our Cast with a collection of documents. If a unique identifier is
32 | * supplied, then we can efficiently update, add, and remove models on subsequent
33 | * .data() calls. If we don't have a uid, then reset collection.
34 | *
35 | * @param {Object} attr
36 | * @param {Function|String} fn unique id
37 | * @return {Cast}
38 | */
39 |
40 | Cast.prototype.data = function(docs, fn) {
41 | if (!fn) throw new Error('Unique id required');
42 |
43 | var len = this.collection.length();
44 | var keys = [];
45 | var isFn = (type(fn) === 'function');
46 |
47 | // Either update our model, or make a new one for each docsibute
48 | // that we have passed.
49 | for ( var i = 0, l = docs.length; i < l; i++ ){
50 | var key = isFn ? fn(docs[i]) : docs[i][fn];
51 | var model = this.collection.get(key);
52 | keys.push(key);
53 | if (model) {
54 | model.set(docs[i]);
55 | } else {
56 | var block = new Block(docs[i], this, this.template);
57 | this.renderNew(block);
58 | this.collection.set(key, block);
59 | }
60 | }
61 |
62 | // If running .data() multiple times, remove any attributes
63 | // that were not contained in subsequent calls. This is fugly. Yoiks.
64 | if (len) {
65 | var toRemove = [];
66 | this.collection.forEach(function(key, model, i){
67 | if (keys.indexOf(key) === -1 ) toRemove.push(key);
68 | });
69 | for (var x = 0, length = toRemove.length; x < length; x++){
70 | this.removeOld(toRemove[x]);
71 | this.collection.remove(toRemove[x]);
72 | }
73 | }
74 | return this;
75 | };
76 |
77 | /**
78 | * return the JSON of our Cast.
79 | *
80 | * @return {Array}
81 | */
82 |
83 | Cast.prototype.toJSON = function(){
84 | var json = [];
85 | this.collection.forEach(function(key, value){
86 | json.push(value.toJSON());
87 | });
88 | return json;
89 | };
90 |
91 |
92 | /**
93 | * Reset Cast with given docs.
94 | *
95 | * @param {Array|Object} attr
96 | * @param {Function|String} fn
97 | * @return {Cast}
98 | */
99 |
100 | Cast.prototype.reset = function(docs, fn){
101 | this.collection.clear();
102 | this.add(docs, fn);
103 | return this;
104 | };
105 |
106 | /**
107 | * Add item with optional uid.
108 | *
109 | * @param {Object|Array} attr
110 | * @param {Function|String} fn
111 | */
112 |
113 | Cast.prototype.add = function(docs, fn){
114 | if (type(docs) !== 'array') docs = [docs];
115 | if (!fn) throw new Error('Unique id required');
116 | var isFn = type(fn) === 'function';
117 | for (var i = 0, l = docs.length; i < l; i++){
118 | var key = isFn ? fn(docs[i]) : docs[i][fn];
119 | var val = new Block(docs[i], this);
120 | this.collection.set(key, val);
121 | }
122 | return this;
123 | };
124 |
125 | /**
126 | * Remove item given a unique id.
127 | *
128 | * @param {String} key
129 | * @return {Cast}
130 | */
131 |
132 | Cast.prototype.remove = function(uid){
133 | this.collection.remove(uid);
134 | return this;
135 | };
136 |
137 |
138 |
139 | /**
140 | * Remove any left/right padding from the container.
141 | *
142 | * @param {Number} w width
143 | * @param {Number} h height
144 | * @param {Number} pw padding-width
145 | * @param {Number} ph padding-height
146 | * @return {Cast}
147 | */
148 |
149 | Cast.prototype.justify = function(w, h, pw, ph){
150 | var cw = this.wrapperWidth;
151 |
152 | var bpr = Math.floor((cw - (pw * 2)) / (w + pw));
153 |
154 | var getLeft = function(c, r) {
155 | if (c === 0) return 0;
156 | if (c === bpr - 1) return cw - w;
157 | var remainingSpace = cw - (w * bpr);
158 | var padding = remainingSpace / (bpr - 1);
159 | return w + (c * padding) + ((c - 1) * w);
160 | };
161 |
162 | this.collection.forEach(function(key, block, i){
163 | var r = Math.floor(i / bpr);
164 | var c = i % bpr;
165 | var left = getLeft(c, r);
166 | var top = ((r * h) + (r + 1) * ph);
167 |
168 | block.position({
169 | 'left': left,
170 | 'top': top,
171 | 'width': w,
172 | 'height': h
173 | });
174 | });
175 |
176 | var t = this.collection.length();
177 | var wrapperHeight = Math.ceil(t / bpr) * (h + ph) + ph;
178 | this.setHeight(wrapperHeight);
179 | return this;
180 | };
181 |
182 |
183 | /**
184 | * The left and right container padding is the
185 | * dynamic property here.
186 | *
187 | * @param {Number} w width
188 | * @param {Number} h height
189 | * @param {Number} pw padding-width
190 | * @param {Number} ph padding-height
191 | * @return {Cast}
192 | */
193 |
194 | Cast.prototype.center = function(w, h, pw, ph){
195 | var cw = this.wrapperWidth;
196 | var bpr = Math.floor(cw/(w + pw));
197 | var mx = (cw - (bpr * w) - (bpr - 1) * pw) * 0.5;
198 |
199 | this.collection.forEach(function(key, block, i){
200 | var r = Math.floor(i / bpr);
201 | var c = i % bpr;
202 | var left = mx + (c * (w + pw));
203 | var top = (r * h) + (r + 1) * ph;
204 |
205 | block.position({
206 | 'left': left,
207 | 'top': top,
208 | 'width': w,
209 | 'height': h
210 | });
211 | });
212 |
213 | var t = this.collection.length();
214 | var wrapperHeight = Math.ceil(t / bpr) * (h + ph) + ph;
215 | this.setHeight(wrapperHeight);
216 | return this;
217 | };
218 |
219 |
220 | /**
221 | * Keep a constant padding-width & padding-height with
222 | * a dynamic cast-item width and height.
223 | *
224 | * @param {Number} w width
225 | * @param {Number} h height
226 | * @param {Number} pw padding-width
227 | * @param {Number} ph padding-height
228 | * @return {Cast}
229 | */
230 |
231 | Cast.prototype.dynamic = function(w, h, pw, ph){
232 | var cw = this.wrapperWidth;
233 | var bpr = Math.floor(cw / ( w + pw ));
234 | var newWidth = (cw - (bpr * pw)) / bpr;
235 | var newHeight = ( newWidth / w ) * h;
236 | var mx = (cw - (bpr * newWidth) - (bpr - 1) * pw) * 0.5;
237 |
238 | // XXX This logic is the same as center(). Should we make
239 | // this a shared function?
240 | this.collection.forEach(function(id, block, i){
241 | var r = Math.floor(i / bpr);
242 | var c = i % bpr;
243 | var left = mx + (c * (newWidth + pw));
244 | var top = (r * newHeight) + (r + 1) * ph;
245 |
246 | block.position({
247 | 'width': newWidth,
248 | 'left': left,
249 | 'top': top,
250 | 'height': newHeight
251 | });
252 | });
253 |
254 | var t = this.collection.length();
255 | var wrapperHeight = Math.ceil(t / bpr) * (newHeight + ph) + ph;
256 | this.setHeight(wrapperHeight);
257 | return this;
258 | };
259 |
260 | /**
261 | * List layout
262 | *
263 | * @param {Number} h height
264 | * @param {Number} ph padding-height
265 | * @return {Cast}
266 | */
267 |
268 | Cast.prototype.list = function(h, ph){
269 |
270 | this.collection.forEach(function(id, block, i){
271 | var top = (h + ph) * i;
272 | block.position({
273 | 'left': 0,
274 | 'top': top,
275 | 'height': h
276 | });
277 | });
278 |
279 | this.setHeight(this.collection.length() * (h + ph));
280 | return this;
281 | };
282 |
283 | /**
284 | * Sort data by field
285 | *
286 | * @param {String} field
287 | * @param {Number} invert
288 | * @return {Cast}
289 | */
290 |
291 | Cast.prototype.sortBy = function(field, invert){
292 | invert = invert || 1;
293 |
294 | this.collection.sort(function(left, right){
295 | var leftVal = left.attr[field];
296 | var rightVal = right.attr[field];
297 | if (leftVal < rightVal) return (-1 * invert);
298 | if (leftVal > rightVal) return (1 * invert);
299 | return 0;
300 | });
301 |
302 | return this;
303 | };
304 |
305 | /**
306 | * render new block
307 | * @param {Block} block
308 | */
309 |
310 | Cast.prototype.renderNew = function(block){
311 | this.emit('render', block)
312 | fastdom.write(function(){
313 | this.el.appendChild(block.el);
314 | }.bind(this));
315 | fastdom.defer(block.show.bind(block));
316 | };
317 |
318 | /**
319 | * render function
320 | * @param {Function} fn
321 | * @return {Cast}
322 | */
323 |
324 | Cast.prototype.render = function(fn){
325 | this.on('render', function(block){
326 | if (fn) fn(block.attr, block.el, block);
327 | }.bind(this));
328 | return this;
329 | };
330 |
331 | /**
332 | * remove old block
333 | * @param {Block} block
334 | */
335 |
336 | Cast.prototype.removeOld = function(id){
337 | var block = this.collection.get(id);
338 | fastdom.write(function(){
339 | block.hide(function(){ block.remove(); });
340 | });
341 | };
342 |
343 | /**
344 | * Set our cast.js height
345 | * @param {Number} height
346 | */
347 |
348 | Cast.prototype.setHeight = function(height){
349 | fastdom.write(function(){
350 | this.el.style.height = height + 'px';
351 | }.bind(this));
352 | };
353 |
354 |
355 | /**
356 | * Draw function
357 | * @param {Function} template
358 | * @return {Cast}
359 | */
360 |
361 | Cast.prototype.draw = function(template){
362 | this.template = template;
363 | fastdom.write(function(){
364 | empty(this.wrapper);
365 | this.wrapper.appendChild(this.el);
366 | }.bind(this));
367 | return this;
368 | };
369 |
--------------------------------------------------------------------------------
/dist/cast.js:
--------------------------------------------------------------------------------
1 | ;(function(){
2 |
3 | /**
4 | * Require the given path.
5 | *
6 | * @param {String} path
7 | * @return {Object} exports
8 | * @api public
9 | */
10 |
11 | function require(path, parent, orig) {
12 | var resolved = require.resolve(path);
13 |
14 | // lookup failed
15 | if (null == resolved) {
16 | orig = orig || path;
17 | parent = parent || 'root';
18 | var err = new Error('Failed to require "' + orig + '" from "' + parent + '"');
19 | err.path = orig;
20 | err.parent = parent;
21 | err.require = true;
22 | throw err;
23 | }
24 |
25 | var module = require.modules[resolved];
26 |
27 | // perform real require()
28 | // by invoking the module's
29 | // registered function
30 | if (!module._resolving && !module.exports) {
31 | var mod = {};
32 | mod.exports = {};
33 | mod.client = mod.component = true;
34 | module._resolving = true;
35 | module.call(this, mod.exports, require.relative(resolved), mod);
36 | delete module._resolving;
37 | module.exports = mod.exports;
38 | }
39 |
40 | return module.exports;
41 | }
42 |
43 | /**
44 | * Registered modules.
45 | */
46 |
47 | require.modules = {};
48 |
49 | /**
50 | * Registered aliases.
51 | */
52 |
53 | require.aliases = {};
54 |
55 | /**
56 | * Resolve `path`.
57 | *
58 | * Lookup:
59 | *
60 | * - PATH/index.js
61 | * - PATH.js
62 | * - PATH
63 | *
64 | * @param {String} path
65 | * @return {String} path or null
66 | * @api private
67 | */
68 |
69 | require.resolve = function(path) {
70 | if (path.charAt(0) === '/') path = path.slice(1);
71 |
72 | var paths = [
73 | path,
74 | path + '.js',
75 | path + '.json',
76 | path + '/index.js',
77 | path + '/index.json'
78 | ];
79 |
80 | for (var i = 0; i < paths.length; i++) {
81 | var path = paths[i];
82 | if (require.modules.hasOwnProperty(path)) return path;
83 | if (require.aliases.hasOwnProperty(path)) return require.aliases[path];
84 | }
85 | };
86 |
87 | /**
88 | * Normalize `path` relative to the current path.
89 | *
90 | * @param {String} curr
91 | * @param {String} path
92 | * @return {String}
93 | * @api private
94 | */
95 |
96 | require.normalize = function(curr, path) {
97 | var segs = [];
98 |
99 | if ('.' != path.charAt(0)) return path;
100 |
101 | curr = curr.split('/');
102 | path = path.split('/');
103 |
104 | for (var i = 0; i < path.length; ++i) {
105 | if ('..' == path[i]) {
106 | curr.pop();
107 | } else if ('.' != path[i] && '' != path[i]) {
108 | segs.push(path[i]);
109 | }
110 | }
111 |
112 | return curr.concat(segs).join('/');
113 | };
114 |
115 | /**
116 | * Register module at `path` with callback `definition`.
117 | *
118 | * @param {String} path
119 | * @param {Function} definition
120 | * @api private
121 | */
122 |
123 | require.register = function(path, definition) {
124 | require.modules[path] = definition;
125 | };
126 |
127 | /**
128 | * Alias a module definition.
129 | *
130 | * @param {String} from
131 | * @param {String} to
132 | * @api private
133 | */
134 |
135 | require.alias = function(from, to) {
136 | if (!require.modules.hasOwnProperty(from)) {
137 | throw new Error('Failed to alias "' + from + '", it does not exist');
138 | }
139 | require.aliases[to] = from;
140 | };
141 |
142 | /**
143 | * Return a require function relative to the `parent` path.
144 | *
145 | * @param {String} parent
146 | * @return {Function}
147 | * @api private
148 | */
149 |
150 | require.relative = function(parent) {
151 | var p = require.normalize(parent, '..');
152 |
153 | /**
154 | * lastIndexOf helper.
155 | */
156 |
157 | function lastIndexOf(arr, obj) {
158 | var i = arr.length;
159 | while (i--) {
160 | if (arr[i] === obj) return i;
161 | }
162 | return -1;
163 | }
164 |
165 | /**
166 | * The relative require() itself.
167 | */
168 |
169 | function localRequire(path) {
170 | var resolved = localRequire.resolve(path);
171 | return require(resolved, parent, path);
172 | }
173 |
174 | /**
175 | * Resolve relative to the parent.
176 | */
177 |
178 | localRequire.resolve = function(path) {
179 | var c = path.charAt(0);
180 | if ('/' == c) return path.slice(1);
181 | if ('.' == c) return require.normalize(p, path);
182 |
183 | // resolve deps by returning
184 | // the dep in the nearest "deps"
185 | // directory
186 | var segs = parent.split('/');
187 | var i = lastIndexOf(segs, 'deps') + 1;
188 | if (!i) i = 0;
189 | path = segs.slice(0, i + 1).join('/') + '/deps/' + path;
190 | return path;
191 | };
192 |
193 | /**
194 | * Check if module is defined at `path`.
195 | */
196 |
197 | localRequire.exists = function(path) {
198 | return require.modules.hasOwnProperty(localRequire.resolve(path));
199 | };
200 |
201 | return localRequire;
202 | };
203 | require.register("component-emitter/index.js", function(exports, require, module){
204 |
205 | /**
206 | * Expose `Emitter`.
207 | */
208 |
209 | module.exports = Emitter;
210 |
211 | /**
212 | * Initialize a new `Emitter`.
213 | *
214 | * @api public
215 | */
216 |
217 | function Emitter(obj) {
218 | if (obj) return mixin(obj);
219 | };
220 |
221 | /**
222 | * Mixin the emitter properties.
223 | *
224 | * @param {Object} obj
225 | * @return {Object}
226 | * @api private
227 | */
228 |
229 | function mixin(obj) {
230 | for (var key in Emitter.prototype) {
231 | obj[key] = Emitter.prototype[key];
232 | }
233 | return obj;
234 | }
235 |
236 | /**
237 | * Listen on the given `event` with `fn`.
238 | *
239 | * @param {String} event
240 | * @param {Function} fn
241 | * @return {Emitter}
242 | * @api public
243 | */
244 |
245 | Emitter.prototype.on =
246 | Emitter.prototype.addEventListener = function(event, fn){
247 | this._callbacks = this._callbacks || {};
248 | (this._callbacks[event] = this._callbacks[event] || [])
249 | .push(fn);
250 | return this;
251 | };
252 |
253 | /**
254 | * Adds an `event` listener that will be invoked a single
255 | * time then automatically removed.
256 | *
257 | * @param {String} event
258 | * @param {Function} fn
259 | * @return {Emitter}
260 | * @api public
261 | */
262 |
263 | Emitter.prototype.once = function(event, fn){
264 | var self = this;
265 | this._callbacks = this._callbacks || {};
266 |
267 | function on() {
268 | self.off(event, on);
269 | fn.apply(this, arguments);
270 | }
271 |
272 | on.fn = fn;
273 | this.on(event, on);
274 | return this;
275 | };
276 |
277 | /**
278 | * Remove the given callback for `event` or all
279 | * registered callbacks.
280 | *
281 | * @param {String} event
282 | * @param {Function} fn
283 | * @return {Emitter}
284 | * @api public
285 | */
286 |
287 | Emitter.prototype.off =
288 | Emitter.prototype.removeListener =
289 | Emitter.prototype.removeAllListeners =
290 | Emitter.prototype.removeEventListener = function(event, fn){
291 | this._callbacks = this._callbacks || {};
292 |
293 | // all
294 | if (0 == arguments.length) {
295 | this._callbacks = {};
296 | return this;
297 | }
298 |
299 | // specific event
300 | var callbacks = this._callbacks[event];
301 | if (!callbacks) return this;
302 |
303 | // remove all handlers
304 | if (1 == arguments.length) {
305 | delete this._callbacks[event];
306 | return this;
307 | }
308 |
309 | // remove specific handler
310 | var cb;
311 | for (var i = 0; i < callbacks.length; i++) {
312 | cb = callbacks[i];
313 | if (cb === fn || cb.fn === fn) {
314 | callbacks.splice(i, 1);
315 | break;
316 | }
317 | }
318 | return this;
319 | };
320 |
321 | /**
322 | * Emit `event` with the given args.
323 | *
324 | * @param {String} event
325 | * @param {Mixed} ...
326 | * @return {Emitter}
327 | */
328 |
329 | Emitter.prototype.emit = function(event){
330 | this._callbacks = this._callbacks || {};
331 | var args = [].slice.call(arguments, 1)
332 | , callbacks = this._callbacks[event];
333 |
334 | if (callbacks) {
335 | callbacks = callbacks.slice(0);
336 | for (var i = 0, len = callbacks.length; i < len; ++i) {
337 | callbacks[i].apply(this, args);
338 | }
339 | }
340 |
341 | return this;
342 | };
343 |
344 | /**
345 | * Return array of callbacks for `event`.
346 | *
347 | * @param {String} event
348 | * @return {Array}
349 | * @api public
350 | */
351 |
352 | Emitter.prototype.listeners = function(event){
353 | this._callbacks = this._callbacks || {};
354 | return this._callbacks[event] || [];
355 | };
356 |
357 | /**
358 | * Check if this emitter has `event` handlers.
359 | *
360 | * @param {String} event
361 | * @return {Boolean}
362 | * @api public
363 | */
364 |
365 | Emitter.prototype.hasListeners = function(event){
366 | return !! this.listeners(event).length;
367 | };
368 |
369 | });
370 | require.register("component-clone/index.js", function(exports, require, module){
371 |
372 | /**
373 | * Module dependencies.
374 | */
375 |
376 | var type;
377 |
378 | try {
379 | type = require('type');
380 | } catch(e){
381 | type = require('type-component');
382 | }
383 |
384 | /**
385 | * Module exports.
386 | */
387 |
388 | module.exports = clone;
389 |
390 | /**
391 | * Clones objects.
392 | *
393 | * @param {Mixed} any object
394 | * @api public
395 | */
396 |
397 | function clone(obj){
398 | switch (type(obj)) {
399 | case 'object':
400 | var copy = {};
401 | for (var key in obj) {
402 | if (obj.hasOwnProperty(key)) {
403 | copy[key] = clone(obj[key]);
404 | }
405 | }
406 | return copy;
407 |
408 | case 'array':
409 | var copy = new Array(obj.length);
410 | for (var i = 0, l = obj.length; i < l; i++) {
411 | copy[i] = clone(obj[i]);
412 | }
413 | return copy;
414 |
415 | case 'regexp':
416 | // from millermedeiros/amd-utils - MIT
417 | var flags = '';
418 | flags += obj.multiline ? 'm' : '';
419 | flags += obj.global ? 'g' : '';
420 | flags += obj.ignoreCase ? 'i' : '';
421 | return new RegExp(obj.source, flags);
422 |
423 | case 'date':
424 | return new Date(obj.getTime());
425 |
426 | default: // string, number, boolean, …
427 | return obj;
428 | }
429 | }
430 |
431 | });
432 | require.register("component-type/index.js", function(exports, require, module){
433 |
434 | /**
435 | * toString ref.
436 | */
437 |
438 | var toString = Object.prototype.toString;
439 |
440 | /**
441 | * Return the type of `val`.
442 | *
443 | * @param {Mixed} val
444 | * @return {String}
445 | * @api public
446 | */
447 |
448 | module.exports = function(val){
449 | switch (toString.call(val)) {
450 | case '[object Function]': return 'function';
451 | case '[object Date]': return 'date';
452 | case '[object RegExp]': return 'regexp';
453 | case '[object Arguments]': return 'arguments';
454 | case '[object Array]': return 'array';
455 | case '[object String]': return 'string';
456 | }
457 |
458 | if (val === null) return 'null';
459 | if (val === undefined) return 'undefined';
460 | if (val && val.nodeType === 1) return 'element';
461 | if (val === Object(val)) return 'object';
462 |
463 | return typeof val;
464 | };
465 |
466 | });
467 | require.register("component-indexof/index.js", function(exports, require, module){
468 | module.exports = function(arr, obj){
469 | if (arr.indexOf) return arr.indexOf(obj);
470 | for (var i = 0; i < arr.length; ++i) {
471 | if (arr[i] === obj) return i;
472 | }
473 | return -1;
474 | };
475 | });
476 | require.register("bmcmahen-ordered-dictionary/index.js", function(exports, require, module){
477 | // Modules
478 | var indexOf = require('indexof'),
479 | Emitter = require('emitter');
480 |
481 | var OrderedDictonary = function(attr){
482 | if (!(this instanceof OrderedDictonary))
483 | return new OrderedDictonary(attr);
484 |
485 | this.map = {};
486 | this.array = [];
487 |
488 | if (typeof attr === 'object')
489 | this.set(attr);
490 | };
491 |
492 | module.exports = OrderedDictonary;
493 |
494 | Emitter(OrderedDictonary.prototype);
495 |
496 | // Allow both 'key', 'value' and {key: value} style arguments.
497 | OrderedDictonary.prototype.set = function(key, val){
498 | var attr, attrs;
499 | if (typeof key === 'object') attrs = key;
500 | else (attrs = {})[key] = val;
501 | for (attr in attrs) {
502 | if (attr in this.map) this.map[attr] = attrs[attr];
503 | else {
504 | this.array.push(attr);
505 | this.map[attr] = attrs[attr];
506 | this.emit('enter', attrs[attr]);
507 | }
508 | }
509 | return this;
510 | };
511 |
512 | OrderedDictonary.prototype.remove = function(key) {
513 | var index = indexOf(this.array, key);
514 | if (index === -1) throw new Error('Key doesnt exist');
515 | this.emit('exit', this.map[key]);
516 | this.array.splice(index, 1);
517 | delete this.map[key];
518 | };
519 |
520 | OrderedDictonary.prototype.get = function(key){
521 | return this.map[key];
522 | };
523 |
524 | OrderedDictonary.prototype.at = function(index){
525 | return this.map[this.array[index]];
526 | };
527 |
528 | OrderedDictonary.prototype.length = function(){
529 | return this.array.length;
530 | };
531 |
532 | // Iterates through our array, providing the key, value,
533 | // and index of the field.
534 | OrderedDictonary.prototype.forEach = function(fn){
535 | var key, value;
536 | for (var i = 0, len = this.array.length; i < len; i++) {
537 | key = this.array[i];
538 | value = this.map[key];
539 | fn(key, value, i);
540 | }
541 | return this;
542 | };
543 |
544 | OrderedDictonary.prototype.sort = function(fn){
545 | var _this = this;
546 | this.array.sort(function(left, right){
547 | return fn(_this.map[left], _this.map[right]);
548 | });
549 | return this;
550 | };
551 |
552 | OrderedDictonary.prototype.clear = function(){
553 | this.map = {};
554 | this.array = [];
555 | return this;
556 | };
557 | });
558 | require.register("component-has-translate3d/index.js", function(exports, require, module){
559 |
560 | var prop = require('transform-property');
561 | // IE8<= doesn't have `getComputedStyle`
562 | if (!prop || !window.getComputedStyle) return module.exports = false;
563 |
564 | var map = {
565 | webkitTransform: '-webkit-transform',
566 | OTransform: '-o-transform',
567 | msTransform: '-ms-transform',
568 | MozTransform: '-moz-transform',
569 | transform: 'transform'
570 | };
571 |
572 | // from: https://gist.github.com/lorenzopolidori/3794226
573 | var el = document.createElement('div');
574 | el.style[prop] = 'translate3d(1px,1px,1px)';
575 | document.body.insertBefore(el, null);
576 | var val = getComputedStyle(el).getPropertyValue(map[prop]);
577 | document.body.removeChild(el);
578 | module.exports = null != val && val.length && 'none' != val;
579 |
580 | });
581 | require.register("component-transform-property/index.js", function(exports, require, module){
582 |
583 | var styles = [
584 | 'webkitTransform',
585 | 'MozTransform',
586 | 'msTransform',
587 | 'OTransform',
588 | 'transform'
589 | ];
590 |
591 | var el = document.createElement('p');
592 | var style;
593 |
594 | for (var i = 0; i < styles.length; i++) {
595 | style = styles[i];
596 | if (null != el.style[style]) {
597 | module.exports = style;
598 | break;
599 | }
600 | }
601 |
602 | });
603 | require.register("component-translate/index.js", function(exports, require, module){
604 |
605 | /**
606 | * Module dependencies.
607 | */
608 |
609 | var transform = require('transform-property');
610 | var has3d = require('has-translate3d');
611 |
612 | /**
613 | * Expose `translate`.
614 | */
615 |
616 | module.exports = translate;
617 |
618 | /**
619 | * Translate `el` by `(x, y)`.
620 | *
621 | * @param {Element} el
622 | * @param {Number} x
623 | * @param {Number} y
624 | * @api public
625 | */
626 |
627 | function translate(el, x, y){
628 | if (transform) {
629 | if (has3d) {
630 | el.style[transform] = 'translate3d(' + x + 'px,' + y + 'px, 0)';
631 | } else {
632 | el.style[transform] = 'translate(' + x + 'px,' + y + 'px)';
633 | }
634 | } else {
635 | el.style.left = x + 'px';
636 | el.style.top = y + 'px';
637 | }
638 | };
639 |
640 | });
641 | require.register("wilsonpage-fastdom/index.js", function(exports, require, module){
642 | /**
643 | * FastDom
644 | *
645 | * Eliminates layout thrashing
646 | * by batching DOM read/write
647 | * interactions.
648 | *
649 | * @author Wilson Page
650 | */
651 |
652 | ;(function(fastdom){
653 |
654 | 'use strict';
655 |
656 | // Normalize rAF
657 | var raf = window.requestAnimationFrame
658 | || window.webkitRequestAnimationFrame
659 | || window.mozRequestAnimationFrame
660 | || window.msRequestAnimationFrame
661 | || function(cb) { return window.setTimeout(cb, 1000 / 60); };
662 |
663 | // Normalize cAF
664 | var caf = window.cancelAnimationFrame
665 | || window.cancelRequestAnimationFrame
666 | || window.mozCancelAnimationFrame
667 | || window.mozCancelRequestAnimationFrame
668 | || window.webkitCancelAnimationFrame
669 | || window.webkitCancelRequestAnimationFrame
670 | || window.msCancelAnimationFrame
671 | || window.msCancelRequestAnimationFrame
672 | || function(id) { window.clearTimeout(id); };
673 |
674 | /**
675 | * Creates a fresh
676 | * FastDom instance.
677 | *
678 | * @constructor
679 | */
680 | function FastDom() {
681 | this.frames = [];
682 | this.lastId = 0;
683 |
684 | // Placing the rAF method
685 | // on the instance allows
686 | // us to replace it with
687 | // a stub for testing.
688 | this.raf = raf;
689 |
690 | this.batch = {
691 | hash: {},
692 | read: [],
693 | write: [],
694 | mode: null
695 | };
696 | }
697 |
698 | /**
699 | * Adds a job to the
700 | * read batch and schedules
701 | * a new frame if need be.
702 | *
703 | * @param {Function} fn
704 | * @api public
705 | */
706 | FastDom.prototype.read = function(fn, ctx) {
707 | var job = this.add('read', fn, ctx);
708 | var id = job.id;
709 |
710 | // Add this job to the read queue
711 | this.batch.read.push(job.id);
712 |
713 | // We should *not* schedule a new frame if:
714 | // 1. We're 'reading'
715 | // 2. A frame is already scheduled
716 | var doesntNeedFrame = this.batch.mode === 'reading'
717 | || this.batch.scheduled;
718 |
719 | // If a frame isn't needed, return
720 | if (doesntNeedFrame) return id;
721 |
722 | // Schedule a new
723 | // frame, then return
724 | this.scheduleBatch();
725 | return id;
726 | };
727 |
728 | /**
729 | * Adds a job to the
730 | * write batch and schedules
731 | * a new frame if need be.
732 | *
733 | * @param {Function} fn
734 | * @api public
735 | */
736 | FastDom.prototype.write = function(fn, ctx) {
737 | var job = this.add('write', fn, ctx);
738 | var mode = this.batch.mode;
739 | var id = job.id;
740 |
741 | // Push the job id into the queue
742 | this.batch.write.push(job.id);
743 |
744 | // We should *not* schedule a new frame if:
745 | // 1. We are 'writing'
746 | // 2. We are 'reading'
747 | // 3. A frame is already scheduled.
748 | var doesntNeedFrame = mode === 'writing'
749 | || mode === 'reading'
750 | || this.batch.scheduled;
751 |
752 | // If a frame isn't needed, return
753 | if (doesntNeedFrame) return id;
754 |
755 | // Schedule a new
756 | // frame, then return
757 | this.scheduleBatch();
758 | return id;
759 | };
760 |
761 | /**
762 | * Defers the given job
763 | * by the number of frames
764 | * specified.
765 | *
766 | * If no frames are given
767 | * then the job is run in
768 | * the next free frame.
769 | *
770 | * @param {Number} frame
771 | * @param {Function} fn
772 | * @api public
773 | */
774 | FastDom.prototype.defer = function(frame, fn, ctx) {
775 |
776 | // Accepts two arguments
777 | if (typeof frame === 'function') {
778 | ctx = fn;
779 | fn = frame;
780 | frame = 1;
781 | }
782 |
783 | var self = this;
784 | var index = frame - 1;
785 |
786 | return this.schedule(index, function() {
787 | self.run({
788 | fn: fn,
789 | ctx: ctx
790 | });
791 | });
792 | };
793 |
794 | /**
795 | * Clears a scheduled 'read',
796 | * 'write' or 'defer' job.
797 | *
798 | * @param {Number} id
799 | * @api public
800 | */
801 | FastDom.prototype.clear = function(id) {
802 |
803 | // Defer jobs are cleared differently
804 | if (typeof id === 'function') {
805 | return this.clearFrame(id);
806 | }
807 |
808 | var job = this.batch.hash[id];
809 | if (!job) return;
810 |
811 | var list = this.batch[job.type];
812 | var index = list.indexOf(id);
813 |
814 | // Clear references
815 | delete this.batch.hash[id];
816 | if (~index) list.splice(index, 1);
817 | };
818 |
819 | /**
820 | * Clears a scheduled frame.
821 | *
822 | * @param {Function} frame
823 | * @api private
824 | */
825 | FastDom.prototype.clearFrame = function(frame) {
826 | var index = this.frames.indexOf(frame);
827 | if (~index) this.frames.splice(index, 1);
828 | };
829 |
830 | /**
831 | * Schedules a new read/write
832 | * batch if one isn't pending.
833 | *
834 | * @api private
835 | */
836 | FastDom.prototype.scheduleBatch = function() {
837 | var self = this;
838 |
839 | // Schedule batch for next frame
840 | this.schedule(0, function() {
841 | self.batch.scheduled = false;
842 | self.runBatch();
843 | });
844 |
845 | // Set flag to indicate
846 | // a frame has been scheduled
847 | this.batch.scheduled = true;
848 | };
849 |
850 | /**
851 | * Generates a unique
852 | * id for a job.
853 | *
854 | * @return {Number}
855 | * @api private
856 | */
857 | FastDom.prototype.uniqueId = function() {
858 | return ++this.lastId;
859 | };
860 |
861 | /**
862 | * Calls each job in
863 | * the list passed.
864 | *
865 | * If a context has been
866 | * stored on the function
867 | * then it is used, else the
868 | * current `this` is used.
869 | *
870 | * @param {Array} list
871 | * @api private
872 | */
873 | FastDom.prototype.flush = function(list) {
874 | var id;
875 |
876 | while (id = list.shift()) {
877 | this.run(this.batch.hash[id]);
878 | }
879 | };
880 |
881 | /**
882 | * Runs any 'read' jobs followed
883 | * by any 'write' jobs.
884 | *
885 | * We run this inside a try catch
886 | * so that if any jobs error, we
887 | * are able to recover and continue
888 | * to flush the batch until it's empty.
889 | *
890 | * @api private
891 | */
892 | FastDom.prototype.runBatch = function() {
893 | try {
894 |
895 | // Set the mode to 'reading',
896 | // then empty all read jobs
897 | this.batch.mode = 'reading';
898 | this.flush(this.batch.read);
899 |
900 | // Set the mode to 'writing'
901 | // then empty all write jobs
902 | this.batch.mode = 'writing';
903 | this.flush(this.batch.write);
904 |
905 | this.batch.mode = null;
906 |
907 | } catch (e) {
908 | this.runBatch();
909 | throw e;
910 | }
911 | };
912 |
913 | /**
914 | * Adds a new job to
915 | * the given batch.
916 | *
917 | * @param {Array} list
918 | * @param {Function} fn
919 | * @param {Object} ctx
920 | * @returns {Number} id
921 | * @api private
922 | */
923 | FastDom.prototype.add = function(type, fn, ctx) {
924 | var id = this.uniqueId();
925 | return this.batch.hash[id] = {
926 | id: id,
927 | fn: fn,
928 | ctx: ctx,
929 | type: type
930 | };
931 | };
932 |
933 | /**
934 | * Runs a given job.
935 | *
936 | * Applications using FastDom
937 | * have the options of setting
938 | * `fastdom.onError`.
939 | *
940 | * This will catch any
941 | * errors that may throw
942 | * inside callbacks, which
943 | * is useful as often DOM
944 | * nodes have been removed
945 | * since a job was scheduled.
946 | *
947 | * Example:
948 | *
949 | * fastdom.onError = function(e) {
950 | * // Runs when jobs error
951 | * };
952 | *
953 | * @param {Object} job
954 | * @api private
955 | */
956 | FastDom.prototype.run = function(job){
957 | var ctx = job.ctx || this;
958 | var fn = job.fn;
959 |
960 | // Clear reference to the job
961 | delete this.batch.hash[job.id];
962 |
963 | // If no `onError` handler
964 | // has been registered, just
965 | // run the job normally.
966 | if (!this.onError) {
967 | return fn.call(ctx);
968 | }
969 |
970 | // If an `onError` handler
971 | // has been registered, catch
972 | // errors that throw inside
973 | // callbacks, and run the
974 | // handler instead.
975 | try { fn.call(ctx); } catch (e) {
976 | this.onError(e);
977 | }
978 | };
979 |
980 | /**
981 | * Starts a rAF loop
982 | * to empty the frame queue.
983 | *
984 | * @api private
985 | */
986 | FastDom.prototype.loop = function() {
987 | var self = this;
988 | var raf = this.raf;
989 |
990 | // Don't start more than one loop
991 | if (this.looping) return;
992 |
993 | raf(function frame() {
994 | var fn = self.frames.shift();
995 |
996 | // If no more frames,
997 | // stop looping
998 | if (!self.frames.length) {
999 | self.looping = false;
1000 |
1001 | // Otherwise, schedule the
1002 | // next frame
1003 | } else {
1004 | raf(frame);
1005 | }
1006 |
1007 | // Run the frame. Note that
1008 | // this may throw an error
1009 | // in user code, but all
1010 | // fastdom tasks are dealt
1011 | // with already so the code
1012 | // will continue to iterate
1013 | if (fn) fn();
1014 | });
1015 |
1016 | this.looping = true;
1017 | };
1018 |
1019 | /**
1020 | * Adds a function to
1021 | * a specified index
1022 | * of the frame queue.
1023 | *
1024 | * @param {Number} index
1025 | * @param {Function} fn
1026 | * @return {Function}
1027 | */
1028 | FastDom.prototype.schedule = function(index, fn) {
1029 |
1030 | // Make sure this slot
1031 | // hasn't already been
1032 | // taken. If it has, try
1033 | // re-scheduling for the next slot
1034 | if (this.frames[index]) {
1035 | return this.schedule(index + 1, fn);
1036 | }
1037 |
1038 | // Start the rAF
1039 | // loop to empty
1040 | // the frame queue
1041 | this.loop();
1042 |
1043 | // Insert this function into
1044 | // the frames queue and return
1045 | return this.frames[index] = fn;
1046 | };
1047 |
1048 | // We only ever want there to be
1049 | // one instance of FastDom in an app
1050 | fastdom = fastdom || new FastDom();
1051 |
1052 | /**
1053 | * Expose 'fastdom'
1054 | */
1055 |
1056 | if (typeof module !== 'undefined' && module.exports) {
1057 | module.exports = fastdom;
1058 | } else if (typeof define === 'function' && define.amd) {
1059 | define(function(){ return fastdom; });
1060 | } else {
1061 | window['fastdom'] = fastdom;
1062 | }
1063 |
1064 | })(window.fastdom);
1065 |
1066 | });
1067 | require.register("component-props/index.js", function(exports, require, module){
1068 | /**
1069 | * Global Names
1070 | */
1071 |
1072 | var globals = /\b(this|Array|Date|Object|Math|JSON)\b/g;
1073 |
1074 | /**
1075 | * Return immediate identifiers parsed from `str`.
1076 | *
1077 | * @param {String} str
1078 | * @param {String|Function} map function or prefix
1079 | * @return {Array}
1080 | * @api public
1081 | */
1082 |
1083 | module.exports = function(str, fn){
1084 | var p = unique(props(str));
1085 | if (fn && 'string' == typeof fn) fn = prefixed(fn);
1086 | if (fn) return map(str, p, fn);
1087 | return p;
1088 | };
1089 |
1090 | /**
1091 | * Return immediate identifiers in `str`.
1092 | *
1093 | * @param {String} str
1094 | * @return {Array}
1095 | * @api private
1096 | */
1097 |
1098 | function props(str) {
1099 | return str
1100 | .replace(/\.\w+|\w+ *\(|"[^"]*"|'[^']*'|\/([^/]+)\//g, '')
1101 | .replace(globals, '')
1102 | .match(/[$a-zA-Z_]\w*/g)
1103 | || [];
1104 | }
1105 |
1106 | /**
1107 | * Return `str` with `props` mapped with `fn`.
1108 | *
1109 | * @param {String} str
1110 | * @param {Array} props
1111 | * @param {Function} fn
1112 | * @return {String}
1113 | * @api private
1114 | */
1115 |
1116 | function map(str, props, fn) {
1117 | var re = /\.\w+|\w+ *\(|"[^"]*"|'[^']*'|\/([^/]+)\/|[a-zA-Z_]\w*/g;
1118 | return str.replace(re, function(_){
1119 | if ('(' == _[_.length - 1]) return fn(_);
1120 | if (!~props.indexOf(_)) return _;
1121 | return fn(_);
1122 | });
1123 | }
1124 |
1125 | /**
1126 | * Return unique array.
1127 | *
1128 | * @param {Array} arr
1129 | * @return {Array}
1130 | * @api private
1131 | */
1132 |
1133 | function unique(arr) {
1134 | var ret = [];
1135 |
1136 | for (var i = 0; i < arr.length; i++) {
1137 | if (~ret.indexOf(arr[i])) continue;
1138 | ret.push(arr[i]);
1139 | }
1140 |
1141 | return ret;
1142 | }
1143 |
1144 | /**
1145 | * Map with prefix `str`.
1146 | */
1147 |
1148 | function prefixed(str) {
1149 | return function(_){
1150 | return str + _;
1151 | };
1152 | }
1153 |
1154 | });
1155 | require.register("component-to-function/index.js", function(exports, require, module){
1156 | /**
1157 | * Module Dependencies
1158 | */
1159 |
1160 | var expr = require('props');
1161 |
1162 | /**
1163 | * Expose `toFunction()`.
1164 | */
1165 |
1166 | module.exports = toFunction;
1167 |
1168 | /**
1169 | * Convert `obj` to a `Function`.
1170 | *
1171 | * @param {Mixed} obj
1172 | * @return {Function}
1173 | * @api private
1174 | */
1175 |
1176 | function toFunction(obj) {
1177 | switch ({}.toString.call(obj)) {
1178 | case '[object Object]':
1179 | return objectToFunction(obj);
1180 | case '[object Function]':
1181 | return obj;
1182 | case '[object String]':
1183 | return stringToFunction(obj);
1184 | case '[object RegExp]':
1185 | return regexpToFunction(obj);
1186 | default:
1187 | return defaultToFunction(obj);
1188 | }
1189 | }
1190 |
1191 | /**
1192 | * Default to strict equality.
1193 | *
1194 | * @param {Mixed} val
1195 | * @return {Function}
1196 | * @api private
1197 | */
1198 |
1199 | function defaultToFunction(val) {
1200 | return function(obj){
1201 | return val === obj;
1202 | }
1203 | }
1204 |
1205 | /**
1206 | * Convert `re` to a function.
1207 | *
1208 | * @param {RegExp} re
1209 | * @return {Function}
1210 | * @api private
1211 | */
1212 |
1213 | function regexpToFunction(re) {
1214 | return function(obj){
1215 | return re.test(obj);
1216 | }
1217 | }
1218 |
1219 | /**
1220 | * Convert property `str` to a function.
1221 | *
1222 | * @param {String} str
1223 | * @return {Function}
1224 | * @api private
1225 | */
1226 |
1227 | function stringToFunction(str) {
1228 | // immediate such as "> 20"
1229 | if (/^ *\W+/.test(str)) return new Function('_', 'return _ ' + str);
1230 |
1231 | // properties such as "name.first" or "age > 18" or "age > 18 && age < 36"
1232 | return new Function('_', 'return ' + get(str));
1233 | }
1234 |
1235 | /**
1236 | * Convert `object` to a function.
1237 | *
1238 | * @param {Object} object
1239 | * @return {Function}
1240 | * @api private
1241 | */
1242 |
1243 | function objectToFunction(obj) {
1244 | var match = {}
1245 | for (var key in obj) {
1246 | match[key] = typeof obj[key] === 'string'
1247 | ? defaultToFunction(obj[key])
1248 | : toFunction(obj[key])
1249 | }
1250 | return function(val){
1251 | if (typeof val !== 'object') return false;
1252 | for (var key in match) {
1253 | if (!(key in val)) return false;
1254 | if (!match[key](val[key])) return false;
1255 | }
1256 | return true;
1257 | }
1258 | }
1259 |
1260 | /**
1261 | * Built the getter function. Supports getter style functions
1262 | *
1263 | * @param {String} str
1264 | * @return {String}
1265 | * @api private
1266 | */
1267 |
1268 | function get(str) {
1269 | var props = expr(str);
1270 | if (!props.length) return '_.' + str;
1271 |
1272 | var val;
1273 | for(var i = 0, prop; prop = props[i]; i++) {
1274 | val = '_.' + prop;
1275 | val = "('function' == typeof " + val + " ? " + val + "() : " + val + ")";
1276 | str = str.replace(new RegExp(prop, 'g'), val);
1277 | }
1278 |
1279 | return str;
1280 | }
1281 |
1282 | });
1283 | require.register("component-each/index.js", function(exports, require, module){
1284 |
1285 | /**
1286 | * Module dependencies.
1287 | */
1288 |
1289 | var type = require('type');
1290 | var toFunction = require('to-function');
1291 |
1292 | /**
1293 | * HOP reference.
1294 | */
1295 |
1296 | var has = Object.prototype.hasOwnProperty;
1297 |
1298 | /**
1299 | * Iterate the given `obj` and invoke `fn(val, i)`
1300 | * in optional context `ctx`.
1301 | *
1302 | * @param {String|Array|Object} obj
1303 | * @param {Function} fn
1304 | * @param {Object} [ctx]
1305 | * @api public
1306 | */
1307 |
1308 | module.exports = function(obj, fn, ctx){
1309 | fn = toFunction(fn);
1310 | ctx = ctx || this;
1311 | switch (type(obj)) {
1312 | case 'array':
1313 | return array(obj, fn, ctx);
1314 | case 'object':
1315 | if ('number' == typeof obj.length) return array(obj, fn, ctx);
1316 | return object(obj, fn, ctx);
1317 | case 'string':
1318 | return string(obj, fn, ctx);
1319 | }
1320 | };
1321 |
1322 | /**
1323 | * Iterate string chars.
1324 | *
1325 | * @param {String} obj
1326 | * @param {Function} fn
1327 | * @param {Object} ctx
1328 | * @api private
1329 | */
1330 |
1331 | function string(obj, fn, ctx) {
1332 | for (var i = 0; i < obj.length; ++i) {
1333 | fn.call(ctx, obj.charAt(i), i);
1334 | }
1335 | }
1336 |
1337 | /**
1338 | * Iterate object keys.
1339 | *
1340 | * @param {Object} obj
1341 | * @param {Function} fn
1342 | * @param {Object} ctx
1343 | * @api private
1344 | */
1345 |
1346 | function object(obj, fn, ctx) {
1347 | for (var key in obj) {
1348 | if (has.call(obj, key)) {
1349 | fn.call(ctx, key, obj[key]);
1350 | }
1351 | }
1352 | }
1353 |
1354 | /**
1355 | * Iterate array-ish.
1356 | *
1357 | * @param {Array|Object} obj
1358 | * @param {Function} fn
1359 | * @param {Object} ctx
1360 | * @api private
1361 | */
1362 |
1363 | function array(obj, fn, ctx) {
1364 | for (var i = 0; i < obj.length; ++i) {
1365 | fn.call(ctx, obj[i], i);
1366 | }
1367 | }
1368 |
1369 | });
1370 | require.register("component-classes/index.js", function(exports, require, module){
1371 | /**
1372 | * Module dependencies.
1373 | */
1374 |
1375 | var index = require('indexof');
1376 |
1377 | /**
1378 | * Whitespace regexp.
1379 | */
1380 |
1381 | var re = /\s+/;
1382 |
1383 | /**
1384 | * toString reference.
1385 | */
1386 |
1387 | var toString = Object.prototype.toString;
1388 |
1389 | /**
1390 | * Wrap `el` in a `ClassList`.
1391 | *
1392 | * @param {Element} el
1393 | * @return {ClassList}
1394 | * @api public
1395 | */
1396 |
1397 | module.exports = function(el){
1398 | return new ClassList(el);
1399 | };
1400 |
1401 | /**
1402 | * Initialize a new ClassList for `el`.
1403 | *
1404 | * @param {Element} el
1405 | * @api private
1406 | */
1407 |
1408 | function ClassList(el) {
1409 | if (!el) throw new Error('A DOM element reference is required');
1410 | this.el = el;
1411 | this.list = el.classList;
1412 | }
1413 |
1414 | /**
1415 | * Add class `name` if not already present.
1416 | *
1417 | * @param {String} name
1418 | * @return {ClassList}
1419 | * @api public
1420 | */
1421 |
1422 | ClassList.prototype.add = function(name){
1423 | // classList
1424 | if (this.list) {
1425 | this.list.add(name);
1426 | return this;
1427 | }
1428 |
1429 | // fallback
1430 | var arr = this.array();
1431 | var i = index(arr, name);
1432 | if (!~i) arr.push(name);
1433 | this.el.className = arr.join(' ');
1434 | return this;
1435 | };
1436 |
1437 | /**
1438 | * Remove class `name` when present, or
1439 | * pass a regular expression to remove
1440 | * any which match.
1441 | *
1442 | * @param {String|RegExp} name
1443 | * @return {ClassList}
1444 | * @api public
1445 | */
1446 |
1447 | ClassList.prototype.remove = function(name){
1448 | if ('[object RegExp]' == toString.call(name)) {
1449 | return this.removeMatching(name);
1450 | }
1451 |
1452 | // classList
1453 | if (this.list) {
1454 | this.list.remove(name);
1455 | return this;
1456 | }
1457 |
1458 | // fallback
1459 | var arr = this.array();
1460 | var i = index(arr, name);
1461 | if (~i) arr.splice(i, 1);
1462 | this.el.className = arr.join(' ');
1463 | return this;
1464 | };
1465 |
1466 | /**
1467 | * Remove all classes matching `re`.
1468 | *
1469 | * @param {RegExp} re
1470 | * @return {ClassList}
1471 | * @api private
1472 | */
1473 |
1474 | ClassList.prototype.removeMatching = function(re){
1475 | var arr = this.array();
1476 | for (var i = 0; i < arr.length; i++) {
1477 | if (re.test(arr[i])) {
1478 | this.remove(arr[i]);
1479 | }
1480 | }
1481 | return this;
1482 | };
1483 |
1484 | /**
1485 | * Toggle class `name`, can force state via `force`.
1486 | *
1487 | * For browsers that support classList, but do not support `force` yet,
1488 | * the mistake will be detected and corrected.
1489 | *
1490 | * @param {String} name
1491 | * @param {Boolean} force
1492 | * @return {ClassList}
1493 | * @api public
1494 | */
1495 |
1496 | ClassList.prototype.toggle = function(name, force){
1497 | // classList
1498 | if (this.list) {
1499 | if ("undefined" !== typeof force) {
1500 | if (force !== this.list.toggle(name, force)) {
1501 | this.list.toggle(name); // toggle again to correct
1502 | }
1503 | } else {
1504 | this.list.toggle(name);
1505 | }
1506 | return this;
1507 | }
1508 |
1509 | // fallback
1510 | if ("undefined" !== typeof force) {
1511 | if (!force) {
1512 | this.remove(name);
1513 | } else {
1514 | this.add(name);
1515 | }
1516 | } else {
1517 | if (this.has(name)) {
1518 | this.remove(name);
1519 | } else {
1520 | this.add(name);
1521 | }
1522 | }
1523 |
1524 | return this;
1525 | };
1526 |
1527 | /**
1528 | * Return an array of classes.
1529 | *
1530 | * @return {Array}
1531 | * @api public
1532 | */
1533 |
1534 | ClassList.prototype.array = function(){
1535 | var str = this.el.className.replace(/^\s+|\s+$/g, '');
1536 | var arr = str.split(re);
1537 | if ('' === arr[0]) arr.shift();
1538 | return arr;
1539 | };
1540 |
1541 | /**
1542 | * Check if class `name` is present.
1543 | *
1544 | * @param {String} name
1545 | * @return {ClassList}
1546 | * @api public
1547 | */
1548 |
1549 | ClassList.prototype.has =
1550 | ClassList.prototype.contains = function(name){
1551 | return this.list
1552 | ? this.list.contains(name)
1553 | : !! ~index(this.array(), name);
1554 | };
1555 |
1556 | });
1557 | require.register("anthonyshort-has-transitions/index.js", function(exports, require, module){
1558 | /**
1559 | * This will store the property that the current
1560 | * browser uses for transitionDuration
1561 | */
1562 | var property;
1563 |
1564 | /**
1565 | * The properties we'll check on an element
1566 | * to determine if it actually has transitions
1567 | * We use duration as this is the only property
1568 | * needed to technically have transitions
1569 | * @type {Array}
1570 | */
1571 | var types = [
1572 | "transitionDuration",
1573 | "MozTransitionDuration",
1574 | "webkitTransitionDuration"
1575 | ];
1576 |
1577 | /**
1578 | * Determine the correct property for this browser
1579 | * just once so we done need to check every time
1580 | */
1581 | while(types.length) {
1582 | var type = types.shift();
1583 | if(type in document.body.style) {
1584 | property = type;
1585 | }
1586 | }
1587 |
1588 | /**
1589 | * Determine if the browser supports transitions or
1590 | * if an element has transitions at all.
1591 | * @param {Element} el Optional. Returns browser support if not included
1592 | * @return {Boolean}
1593 | */
1594 | function hasTransitions(el){
1595 | if(!property) {
1596 | return false; // No browser support for transitions
1597 | }
1598 | if(!el) {
1599 | return property != null; // We just want to know if browsers support it
1600 | }
1601 | var duration = getComputedStyle(el)[property];
1602 | return duration !== "" && parseFloat(duration) !== 0; // Does this element have transitions?
1603 | }
1604 |
1605 | module.exports = hasTransitions;
1606 | });
1607 | require.register("component-event/index.js", function(exports, require, module){
1608 | var bind = window.addEventListener ? 'addEventListener' : 'attachEvent',
1609 | unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
1610 | prefix = bind !== 'addEventListener' ? 'on' : '';
1611 |
1612 | /**
1613 | * Bind `el` event `type` to `fn`.
1614 | *
1615 | * @param {Element} el
1616 | * @param {String} type
1617 | * @param {Function} fn
1618 | * @param {Boolean} capture
1619 | * @return {Function}
1620 | * @api public
1621 | */
1622 |
1623 | exports.bind = function(el, type, fn, capture){
1624 | el[bind](prefix + type, fn, capture || false);
1625 | return fn;
1626 | };
1627 |
1628 | /**
1629 | * Unbind `el` event `type`'s callback `fn`.
1630 | *
1631 | * @param {Element} el
1632 | * @param {String} type
1633 | * @param {Function} fn
1634 | * @param {Boolean} capture
1635 | * @return {Function}
1636 | * @api public
1637 | */
1638 |
1639 | exports.unbind = function(el, type, fn, capture){
1640 | el[unbind](prefix + type, fn, capture || false);
1641 | return fn;
1642 | };
1643 | });
1644 | require.register("anthonyshort-css-emitter/index.js", function(exports, require, module){
1645 | /**
1646 | * Module Dependencies
1647 | */
1648 |
1649 | var events = require('event');
1650 |
1651 | // CSS events
1652 |
1653 | var watch = [
1654 | 'transitionend'
1655 | , 'webkitTransitionEnd'
1656 | , 'oTransitionEnd'
1657 | , 'MSTransitionEnd'
1658 | , 'animationend'
1659 | , 'webkitAnimationEnd'
1660 | , 'oAnimationEnd'
1661 | , 'MSAnimationEnd'
1662 | ];
1663 |
1664 | /**
1665 | * Expose `CSSnext`
1666 | */
1667 |
1668 | module.exports = CssEmitter;
1669 |
1670 | /**
1671 | * Initialize a new `CssEmitter`
1672 | *
1673 | */
1674 |
1675 | function CssEmitter(element){
1676 | if (!(this instanceof CssEmitter)) return new CssEmitter(element);
1677 | this.el = element;
1678 | }
1679 |
1680 | /**
1681 | * Bind CSS events.
1682 | *
1683 | * @api public
1684 | */
1685 |
1686 | CssEmitter.prototype.bind = function(fn){
1687 | for (var i=0; i < watch.length; i++) {
1688 | events.bind(this.el, watch[i], fn);
1689 | }
1690 | return this;
1691 | };
1692 |
1693 | /**
1694 | * Unbind CSS events
1695 | *
1696 | * @api public
1697 | */
1698 |
1699 | CssEmitter.prototype.unbind = function(fn){
1700 | for (var i=0; i < watch.length; i++) {
1701 | events.unbind(this.el, watch[i], fn);
1702 | }
1703 | return this;
1704 | };
1705 |
1706 | /**
1707 | * Fire callback only once
1708 | *
1709 | * @api public
1710 | */
1711 |
1712 | CssEmitter.prototype.once = function(fn){
1713 | var self = this;
1714 | function on(){
1715 | self.unbind(on);
1716 | fn.apply(self.el, arguments);
1717 | }
1718 | self.bind(on);
1719 | return this;
1720 | };
1721 |
1722 |
1723 | });
1724 | require.register("anthonyshort-after-transition/index.js", function(exports, require, module){
1725 | var hasTransitions = require('has-transitions');
1726 | var emitter = require('css-emitter');
1727 |
1728 | function afterTransition(el, callback) {
1729 | if(hasTransitions(el)) {
1730 | return emitter(el).bind(callback);
1731 | }
1732 | return callback.apply(el);
1733 | };
1734 |
1735 | afterTransition.once = function(el, callback) {
1736 | afterTransition(el, function fn(){
1737 | callback.apply(el);
1738 | emitter(el).unbind(fn);
1739 | });
1740 | };
1741 |
1742 | module.exports = afterTransition;
1743 | });
1744 | require.register("yields-empty/index.js", function(exports, require, module){
1745 |
1746 | /**
1747 | * Empty the given `el`.
1748 | *
1749 | * @param {Element} el
1750 | * @return {Element}
1751 | */
1752 |
1753 | module.exports = function(el, node){
1754 | while (node = el.firstChild) el.removeChild(node);
1755 | return el;
1756 | };
1757 |
1758 | });
1759 | require.register("component-domify/index.js", function(exports, require, module){
1760 |
1761 | /**
1762 | * Expose `parse`.
1763 | */
1764 |
1765 | module.exports = parse;
1766 |
1767 | /**
1768 | * Wrap map from jquery.
1769 | */
1770 |
1771 | var map = {
1772 | legend: [1, ''],
1773 | tr: [2, '