isotope-rails 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d4189d78779914932c65e891609a5286827066ad
4
+ data.tar.gz: 5c988799a9e47fe38ffce9778e5bb62ec8c241e1
5
+ SHA512:
6
+ metadata.gz: 937d189157d14dee7fec61c632ce3c70f6715a4fda1416023025a5e0ae1129fb0992b49518486e4bf25fb0e8288659c51cff75146bc07304dd54dad27d39008b
7
+ data.tar.gz: 59593c7fced9627e746166cb0af646c008cfb19e71f03e0fba29095c0df7278390bf6b835b5ed960466c8bbd88ae7c8de9347cbfa3e59ee37b18208bbdb7ea02
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in isotope-rails.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Dominik Janković
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,36 @@
1
+ # isotope-rails
2
+
3
+ isotope-rails wraps the [Isotope](http://isotope.metafizzy.co/) library for use in Rails 4.0 and above. Assets should be automatically minified during production (unless you have your project configured in a different manner).
4
+
5
+ #### Versioning
6
+ The gem version matches the version of the used JavaScript file. Currently, the version of Isotope used is `2.2.2`.
7
+
8
+ ## Dependencies
9
+
10
+ This gem depends on
11
+
12
+ 1. `rails`, `>= 4.0`
13
+ 2. `jquery-rails`
14
+
15
+ ## Usage
16
+
17
+ 1. Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'isotope-rails'
21
+ ```
22
+
23
+ 2. Add the following directive to your JavaScript manifest file (application.js):
24
+
25
+ ```
26
+ //= require isotope
27
+ ```
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at https://github.com/aaio/isotope-rails.
32
+
33
+
34
+ ## License
35
+
36
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "isotope/rails"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'isotope/rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "isotope-rails"
8
+ spec.version = Isotope::Rails::VERSION
9
+ spec.authors = ["Dominik Janković"]
10
+ spec.email = ["aaio@outlook.com"]
11
+ spec.summary = %q{Isotope wrapper for Rails asset pipeline.}
12
+ spec.homepage = "https://github.com/aaio/isotope-rails"
13
+ spec.license = "MIT"
14
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
15
+ spec.require_paths = ["lib"]
16
+ spec.add_development_dependency "bundler", [">= 1.10.6"]
17
+ spec.add_runtime_dependency "rails", [">= 4.0"]
18
+ spec.add_runtime_dependency "jquery-rails"
19
+ end
@@ -0,0 +1,7 @@
1
+ require "isotope/rails/version"
2
+
3
+ module Isotope
4
+ module Rails
5
+ require "isotope/rails/engine"
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module Isotope
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Isotope
2
+ module Rails
3
+ VERSION = "2.2.2"
4
+ end
5
+ end
@@ -0,0 +1,4257 @@
1
+ /*!
2
+ * Isotope PACKAGED v2.2.2
3
+ *
4
+ * Licensed GPLv3 for open source use
5
+ * or Isotope Commercial License for commercial use
6
+ *
7
+ * http://isotope.metafizzy.co
8
+ * Copyright 2015 Metafizzy
9
+ */
10
+
11
+ /**
12
+ * Bridget makes jQuery widgets
13
+ * v1.1.0
14
+ * MIT license
15
+ */
16
+
17
+ ( function( window ) {
18
+
19
+
20
+
21
+ // -------------------------- utils -------------------------- //
22
+
23
+ var slice = Array.prototype.slice;
24
+
25
+ function noop() {}
26
+
27
+ // -------------------------- definition -------------------------- //
28
+
29
+ function defineBridget( $ ) {
30
+
31
+ // bail if no jQuery
32
+ if ( !$ ) {
33
+ return;
34
+ }
35
+
36
+ // -------------------------- addOptionMethod -------------------------- //
37
+
38
+ /**
39
+ * adds option method -> $().plugin('option', {...})
40
+ * @param {Function} PluginClass - constructor class
41
+ */
42
+ function addOptionMethod( PluginClass ) {
43
+ // don't overwrite original option method
44
+ if ( PluginClass.prototype.option ) {
45
+ return;
46
+ }
47
+
48
+ // option setter
49
+ PluginClass.prototype.option = function( opts ) {
50
+ // bail out if not an object
51
+ if ( !$.isPlainObject( opts ) ){
52
+ return;
53
+ }
54
+ this.options = $.extend( true, this.options, opts );
55
+ };
56
+ }
57
+
58
+ // -------------------------- plugin bridge -------------------------- //
59
+
60
+ // helper function for logging errors
61
+ // $.error breaks jQuery chaining
62
+ var logError = typeof console === 'undefined' ? noop :
63
+ function( message ) {
64
+ console.error( message );
65
+ };
66
+
67
+ /**
68
+ * jQuery plugin bridge, access methods like $elem.plugin('method')
69
+ * @param {String} namespace - plugin name
70
+ * @param {Function} PluginClass - constructor class
71
+ */
72
+ function bridge( namespace, PluginClass ) {
73
+ // add to jQuery fn namespace
74
+ $.fn[ namespace ] = function( options ) {
75
+ if ( typeof options === 'string' ) {
76
+ // call plugin method when first argument is a string
77
+ // get arguments for method
78
+ var args = slice.call( arguments, 1 );
79
+
80
+ for ( var i=0, len = this.length; i < len; i++ ) {
81
+ var elem = this[i];
82
+ var instance = $.data( elem, namespace );
83
+ if ( !instance ) {
84
+ logError( "cannot call methods on " + namespace + " prior to initialization; " +
85
+ "attempted to call '" + options + "'" );
86
+ continue;
87
+ }
88
+ if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) {
89
+ logError( "no such method '" + options + "' for " + namespace + " instance" );
90
+ continue;
91
+ }
92
+
93
+ // trigger method with arguments
94
+ var returnValue = instance[ options ].apply( instance, args );
95
+
96
+ // break look and return first value if provided
97
+ if ( returnValue !== undefined ) {
98
+ return returnValue;
99
+ }
100
+ }
101
+ // return this if no return value
102
+ return this;
103
+ } else {
104
+ return this.each( function() {
105
+ var instance = $.data( this, namespace );
106
+ if ( instance ) {
107
+ // apply options & init
108
+ instance.option( options );
109
+ instance._init();
110
+ } else {
111
+ // initialize new instance
112
+ instance = new PluginClass( this, options );
113
+ $.data( this, namespace, instance );
114
+ }
115
+ });
116
+ }
117
+ };
118
+
119
+ }
120
+
121
+ // -------------------------- bridget -------------------------- //
122
+
123
+ /**
124
+ * converts a Prototypical class into a proper jQuery plugin
125
+ * the class must have a ._init method
126
+ * @param {String} namespace - plugin name, used in $().pluginName
127
+ * @param {Function} PluginClass - constructor class
128
+ */
129
+ $.bridget = function( namespace, PluginClass ) {
130
+ addOptionMethod( PluginClass );
131
+ bridge( namespace, PluginClass );
132
+ };
133
+
134
+ return $.bridget;
135
+
136
+ }
137
+
138
+ // transport
139
+ if ( typeof define === 'function' && define.amd ) {
140
+ // AMD
141
+ define( 'jquery-bridget/jquery.bridget',[ 'jquery' ], defineBridget );
142
+ } else if ( typeof exports === 'object' ) {
143
+ defineBridget( require('jquery') );
144
+ } else {
145
+ // get jquery from browser global
146
+ defineBridget( window.jQuery );
147
+ }
148
+
149
+ })( window );
150
+
151
+ /*!
152
+ * eventie v1.0.6
153
+ * event binding helper
154
+ * eventie.bind( elem, 'click', myFn )
155
+ * eventie.unbind( elem, 'click', myFn )
156
+ * MIT license
157
+ */
158
+
159
+ /*jshint browser: true, undef: true, unused: true */
160
+ /*global define: false, module: false */
161
+
162
+ ( function( window ) {
163
+
164
+
165
+
166
+ var docElem = document.documentElement;
167
+
168
+ var bind = function() {};
169
+
170
+ function getIEEvent( obj ) {
171
+ var event = window.event;
172
+ // add event.target
173
+ event.target = event.target || event.srcElement || obj;
174
+ return event;
175
+ }
176
+
177
+ if ( docElem.addEventListener ) {
178
+ bind = function( obj, type, fn ) {
179
+ obj.addEventListener( type, fn, false );
180
+ };
181
+ } else if ( docElem.attachEvent ) {
182
+ bind = function( obj, type, fn ) {
183
+ obj[ type + fn ] = fn.handleEvent ?
184
+ function() {
185
+ var event = getIEEvent( obj );
186
+ fn.handleEvent.call( fn, event );
187
+ } :
188
+ function() {
189
+ var event = getIEEvent( obj );
190
+ fn.call( obj, event );
191
+ };
192
+ obj.attachEvent( "on" + type, obj[ type + fn ] );
193
+ };
194
+ }
195
+
196
+ var unbind = function() {};
197
+
198
+ if ( docElem.removeEventListener ) {
199
+ unbind = function( obj, type, fn ) {
200
+ obj.removeEventListener( type, fn, false );
201
+ };
202
+ } else if ( docElem.detachEvent ) {
203
+ unbind = function( obj, type, fn ) {
204
+ obj.detachEvent( "on" + type, obj[ type + fn ] );
205
+ try {
206
+ delete obj[ type + fn ];
207
+ } catch ( err ) {
208
+ // can't delete window object properties
209
+ obj[ type + fn ] = undefined;
210
+ }
211
+ };
212
+ }
213
+
214
+ var eventie = {
215
+ bind: bind,
216
+ unbind: unbind
217
+ };
218
+
219
+ // ----- module definition ----- //
220
+
221
+ if ( typeof define === 'function' && define.amd ) {
222
+ // AMD
223
+ define( 'eventie/eventie',eventie );
224
+ } else if ( typeof exports === 'object' ) {
225
+ // CommonJS
226
+ module.exports = eventie;
227
+ } else {
228
+ // browser global
229
+ window.eventie = eventie;
230
+ }
231
+
232
+ })( window );
233
+
234
+ /*!
235
+ * EventEmitter v4.2.11 - git.io/ee
236
+ * Unlicense - http://unlicense.org/
237
+ * Oliver Caldwell - http://oli.me.uk/
238
+ * @preserve
239
+ */
240
+
241
+ ;(function () {
242
+ 'use strict';
243
+
244
+ /**
245
+ * Class for managing events.
246
+ * Can be extended to provide event functionality in other classes.
247
+ *
248
+ * @class EventEmitter Manages event registering and emitting.
249
+ */
250
+ function EventEmitter() {}
251
+
252
+ // Shortcuts to improve speed and size
253
+ var proto = EventEmitter.prototype;
254
+ var exports = this;
255
+ var originalGlobalValue = exports.EventEmitter;
256
+
257
+ /**
258
+ * Finds the index of the listener for the event in its storage array.
259
+ *
260
+ * @param {Function[]} listeners Array of listeners to search through.
261
+ * @param {Function} listener Method to look for.
262
+ * @return {Number} Index of the specified listener, -1 if not found
263
+ * @api private
264
+ */
265
+ function indexOfListener(listeners, listener) {
266
+ var i = listeners.length;
267
+ while (i--) {
268
+ if (listeners[i].listener === listener) {
269
+ return i;
270
+ }
271
+ }
272
+
273
+ return -1;
274
+ }
275
+
276
+ /**
277
+ * Alias a method while keeping the context correct, to allow for overwriting of target method.
278
+ *
279
+ * @param {String} name The name of the target method.
280
+ * @return {Function} The aliased method
281
+ * @api private
282
+ */
283
+ function alias(name) {
284
+ return function aliasClosure() {
285
+ return this[name].apply(this, arguments);
286
+ };
287
+ }
288
+
289
+ /**
290
+ * Returns the listener array for the specified event.
291
+ * Will initialise the event object and listener arrays if required.
292
+ * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
293
+ * Each property in the object response is an array of listener functions.
294
+ *
295
+ * @param {String|RegExp} evt Name of the event to return the listeners from.
296
+ * @return {Function[]|Object} All listener functions for the event.
297
+ */
298
+ proto.getListeners = function getListeners(evt) {
299
+ var events = this._getEvents();
300
+ var response;
301
+ var key;
302
+
303
+ // Return a concatenated array of all matching events if
304
+ // the selector is a regular expression.
305
+ if (evt instanceof RegExp) {
306
+ response = {};
307
+ for (key in events) {
308
+ if (events.hasOwnProperty(key) && evt.test(key)) {
309
+ response[key] = events[key];
310
+ }
311
+ }
312
+ }
313
+ else {
314
+ response = events[evt] || (events[evt] = []);
315
+ }
316
+
317
+ return response;
318
+ };
319
+
320
+ /**
321
+ * Takes a list of listener objects and flattens it into a list of listener functions.
322
+ *
323
+ * @param {Object[]} listeners Raw listener objects.
324
+ * @return {Function[]} Just the listener functions.
325
+ */
326
+ proto.flattenListeners = function flattenListeners(listeners) {
327
+ var flatListeners = [];
328
+ var i;
329
+
330
+ for (i = 0; i < listeners.length; i += 1) {
331
+ flatListeners.push(listeners[i].listener);
332
+ }
333
+
334
+ return flatListeners;
335
+ };
336
+
337
+ /**
338
+ * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
339
+ *
340
+ * @param {String|RegExp} evt Name of the event to return the listeners from.
341
+ * @return {Object} All listener functions for an event in an object.
342
+ */
343
+ proto.getListenersAsObject = function getListenersAsObject(evt) {
344
+ var listeners = this.getListeners(evt);
345
+ var response;
346
+
347
+ if (listeners instanceof Array) {
348
+ response = {};
349
+ response[evt] = listeners;
350
+ }
351
+
352
+ return response || listeners;
353
+ };
354
+
355
+ /**
356
+ * Adds a listener function to the specified event.
357
+ * The listener will not be added if it is a duplicate.
358
+ * If the listener returns true then it will be removed after it is called.
359
+ * If you pass a regular expression as the event name then the listener will be added to all events that match it.
360
+ *
361
+ * @param {String|RegExp} evt Name of the event to attach the listener to.
362
+ * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
363
+ * @return {Object} Current instance of EventEmitter for chaining.
364
+ */
365
+ proto.addListener = function addListener(evt, listener) {
366
+ var listeners = this.getListenersAsObject(evt);
367
+ var listenerIsWrapped = typeof listener === 'object';
368
+ var key;
369
+
370
+ for (key in listeners) {
371
+ if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
372
+ listeners[key].push(listenerIsWrapped ? listener : {
373
+ listener: listener,
374
+ once: false
375
+ });
376
+ }
377
+ }
378
+
379
+ return this;
380
+ };
381
+
382
+ /**
383
+ * Alias of addListener
384
+ */
385
+ proto.on = alias('addListener');
386
+
387
+ /**
388
+ * Semi-alias of addListener. It will add a listener that will be
389
+ * automatically removed after its first execution.
390
+ *
391
+ * @param {String|RegExp} evt Name of the event to attach the listener to.
392
+ * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
393
+ * @return {Object} Current instance of EventEmitter for chaining.
394
+ */
395
+ proto.addOnceListener = function addOnceListener(evt, listener) {
396
+ return this.addListener(evt, {
397
+ listener: listener,
398
+ once: true
399
+ });
400
+ };
401
+
402
+ /**
403
+ * Alias of addOnceListener.
404
+ */
405
+ proto.once = alias('addOnceListener');
406
+
407
+ /**
408
+ * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
409
+ * You need to tell it what event names should be matched by a regex.
410
+ *
411
+ * @param {String} evt Name of the event to create.
412
+ * @return {Object} Current instance of EventEmitter for chaining.
413
+ */
414
+ proto.defineEvent = function defineEvent(evt) {
415
+ this.getListeners(evt);
416
+ return this;
417
+ };
418
+
419
+ /**
420
+ * Uses defineEvent to define multiple events.
421
+ *
422
+ * @param {String[]} evts An array of event names to define.
423
+ * @return {Object} Current instance of EventEmitter for chaining.
424
+ */
425
+ proto.defineEvents = function defineEvents(evts) {
426
+ for (var i = 0; i < evts.length; i += 1) {
427
+ this.defineEvent(evts[i]);
428
+ }
429
+ return this;
430
+ };
431
+
432
+ /**
433
+ * Removes a listener function from the specified event.
434
+ * When passed a regular expression as the event name, it will remove the listener from all events that match it.
435
+ *
436
+ * @param {String|RegExp} evt Name of the event to remove the listener from.
437
+ * @param {Function} listener Method to remove from the event.
438
+ * @return {Object} Current instance of EventEmitter for chaining.
439
+ */
440
+ proto.removeListener = function removeListener(evt, listener) {
441
+ var listeners = this.getListenersAsObject(evt);
442
+ var index;
443
+ var key;
444
+
445
+ for (key in listeners) {
446
+ if (listeners.hasOwnProperty(key)) {
447
+ index = indexOfListener(listeners[key], listener);
448
+
449
+ if (index !== -1) {
450
+ listeners[key].splice(index, 1);
451
+ }
452
+ }
453
+ }
454
+
455
+ return this;
456
+ };
457
+
458
+ /**
459
+ * Alias of removeListener
460
+ */
461
+ proto.off = alias('removeListener');
462
+
463
+ /**
464
+ * Adds listeners in bulk using the manipulateListeners method.
465
+ * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
466
+ * You can also pass it a regular expression to add the array of listeners to all events that match it.
467
+ * Yeah, this function does quite a bit. That's probably a bad thing.
468
+ *
469
+ * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
470
+ * @param {Function[]} [listeners] An optional array of listener functions to add.
471
+ * @return {Object} Current instance of EventEmitter for chaining.
472
+ */
473
+ proto.addListeners = function addListeners(evt, listeners) {
474
+ // Pass through to manipulateListeners
475
+ return this.manipulateListeners(false, evt, listeners);
476
+ };
477
+
478
+ /**
479
+ * Removes listeners in bulk using the manipulateListeners method.
480
+ * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
481
+ * You can also pass it an event name and an array of listeners to be removed.
482
+ * You can also pass it a regular expression to remove the listeners from all events that match it.
483
+ *
484
+ * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
485
+ * @param {Function[]} [listeners] An optional array of listener functions to remove.
486
+ * @return {Object} Current instance of EventEmitter for chaining.
487
+ */
488
+ proto.removeListeners = function removeListeners(evt, listeners) {
489
+ // Pass through to manipulateListeners
490
+ return this.manipulateListeners(true, evt, listeners);
491
+ };
492
+
493
+ /**
494
+ * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
495
+ * The first argument will determine if the listeners are removed (true) or added (false).
496
+ * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
497
+ * You can also pass it an event name and an array of listeners to be added/removed.
498
+ * You can also pass it a regular expression to manipulate the listeners of all events that match it.
499
+ *
500
+ * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
501
+ * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
502
+ * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
503
+ * @return {Object} Current instance of EventEmitter for chaining.
504
+ */
505
+ proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
506
+ var i;
507
+ var value;
508
+ var single = remove ? this.removeListener : this.addListener;
509
+ var multiple = remove ? this.removeListeners : this.addListeners;
510
+
511
+ // If evt is an object then pass each of its properties to this method
512
+ if (typeof evt === 'object' && !(evt instanceof RegExp)) {
513
+ for (i in evt) {
514
+ if (evt.hasOwnProperty(i) && (value = evt[i])) {
515
+ // Pass the single listener straight through to the singular method
516
+ if (typeof value === 'function') {
517
+ single.call(this, i, value);
518
+ }
519
+ else {
520
+ // Otherwise pass back to the multiple function
521
+ multiple.call(this, i, value);
522
+ }
523
+ }
524
+ }
525
+ }
526
+ else {
527
+ // So evt must be a string
528
+ // And listeners must be an array of listeners
529
+ // Loop over it and pass each one to the multiple method
530
+ i = listeners.length;
531
+ while (i--) {
532
+ single.call(this, evt, listeners[i]);
533
+ }
534
+ }
535
+
536
+ return this;
537
+ };
538
+
539
+ /**
540
+ * Removes all listeners from a specified event.
541
+ * If you do not specify an event then all listeners will be removed.
542
+ * That means every event will be emptied.
543
+ * You can also pass a regex to remove all events that match it.
544
+ *
545
+ * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
546
+ * @return {Object} Current instance of EventEmitter for chaining.
547
+ */
548
+ proto.removeEvent = function removeEvent(evt) {
549
+ var type = typeof evt;
550
+ var events = this._getEvents();
551
+ var key;
552
+
553
+ // Remove different things depending on the state of evt
554
+ if (type === 'string') {
555
+ // Remove all listeners for the specified event
556
+ delete events[evt];
557
+ }
558
+ else if (evt instanceof RegExp) {
559
+ // Remove all events matching the regex.
560
+ for (key in events) {
561
+ if (events.hasOwnProperty(key) && evt.test(key)) {
562
+ delete events[key];
563
+ }
564
+ }
565
+ }
566
+ else {
567
+ // Remove all listeners in all events
568
+ delete this._events;
569
+ }
570
+
571
+ return this;
572
+ };
573
+
574
+ /**
575
+ * Alias of removeEvent.
576
+ *
577
+ * Added to mirror the node API.
578
+ */
579
+ proto.removeAllListeners = alias('removeEvent');
580
+
581
+ /**
582
+ * Emits an event of your choice.
583
+ * When emitted, every listener attached to that event will be executed.
584
+ * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
585
+ * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
586
+ * So they will not arrive within the array on the other side, they will be separate.
587
+ * You can also pass a regular expression to emit to all events that match it.
588
+ *
589
+ * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
590
+ * @param {Array} [args] Optional array of arguments to be passed to each listener.
591
+ * @return {Object} Current instance of EventEmitter for chaining.
592
+ */
593
+ proto.emitEvent = function emitEvent(evt, args) {
594
+ var listeners = this.getListenersAsObject(evt);
595
+ var listener;
596
+ var i;
597
+ var key;
598
+ var response;
599
+
600
+ for (key in listeners) {
601
+ if (listeners.hasOwnProperty(key)) {
602
+ i = listeners[key].length;
603
+
604
+ while (i--) {
605
+ // If the listener returns true then it shall be removed from the event
606
+ // The function is executed either with a basic call or an apply if there is an args array
607
+ listener = listeners[key][i];
608
+
609
+ if (listener.once === true) {
610
+ this.removeListener(evt, listener.listener);
611
+ }
612
+
613
+ response = listener.listener.apply(this, args || []);
614
+
615
+ if (response === this._getOnceReturnValue()) {
616
+ this.removeListener(evt, listener.listener);
617
+ }
618
+ }
619
+ }
620
+ }
621
+
622
+ return this;
623
+ };
624
+
625
+ /**
626
+ * Alias of emitEvent
627
+ */
628
+ proto.trigger = alias('emitEvent');
629
+
630
+ /**
631
+ * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
632
+ * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
633
+ *
634
+ * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
635
+ * @param {...*} Optional additional arguments to be passed to each listener.
636
+ * @return {Object} Current instance of EventEmitter for chaining.
637
+ */
638
+ proto.emit = function emit(evt) {
639
+ var args = Array.prototype.slice.call(arguments, 1);
640
+ return this.emitEvent(evt, args);
641
+ };
642
+
643
+ /**
644
+ * Sets the current value to check against when executing listeners. If a
645
+ * listeners return value matches the one set here then it will be removed
646
+ * after execution. This value defaults to true.
647
+ *
648
+ * @param {*} value The new value to check for when executing listeners.
649
+ * @return {Object} Current instance of EventEmitter for chaining.
650
+ */
651
+ proto.setOnceReturnValue = function setOnceReturnValue(value) {
652
+ this._onceReturnValue = value;
653
+ return this;
654
+ };
655
+
656
+ /**
657
+ * Fetches the current value to check against when executing listeners. If
658
+ * the listeners return value matches this one then it should be removed
659
+ * automatically. It will return true by default.
660
+ *
661
+ * @return {*|Boolean} The current value to check for or the default, true.
662
+ * @api private
663
+ */
664
+ proto._getOnceReturnValue = function _getOnceReturnValue() {
665
+ if (this.hasOwnProperty('_onceReturnValue')) {
666
+ return this._onceReturnValue;
667
+ }
668
+ else {
669
+ return true;
670
+ }
671
+ };
672
+
673
+ /**
674
+ * Fetches the events object and creates one if required.
675
+ *
676
+ * @return {Object} The events storage object.
677
+ * @api private
678
+ */
679
+ proto._getEvents = function _getEvents() {
680
+ return this._events || (this._events = {});
681
+ };
682
+
683
+ /**
684
+ * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
685
+ *
686
+ * @return {Function} Non conflicting EventEmitter class.
687
+ */
688
+ EventEmitter.noConflict = function noConflict() {
689
+ exports.EventEmitter = originalGlobalValue;
690
+ return EventEmitter;
691
+ };
692
+
693
+ // Expose the class either via AMD, CommonJS or the global object
694
+ if (typeof define === 'function' && define.amd) {
695
+ define('eventEmitter/EventEmitter',[],function () {
696
+ return EventEmitter;
697
+ });
698
+ }
699
+ else if (typeof module === 'object' && module.exports){
700
+ module.exports = EventEmitter;
701
+ }
702
+ else {
703
+ exports.EventEmitter = EventEmitter;
704
+ }
705
+ }.call(this));
706
+
707
+ /*!
708
+ * getStyleProperty v1.0.4
709
+ * original by kangax
710
+ * http://perfectionkills.com/feature-testing-css-properties/
711
+ * MIT license
712
+ */
713
+
714
+ /*jshint browser: true, strict: true, undef: true */
715
+ /*global define: false, exports: false, module: false */
716
+
717
+ ( function( window ) {
718
+
719
+
720
+
721
+ var prefixes = 'Webkit Moz ms Ms O'.split(' ');
722
+ var docElemStyle = document.documentElement.style;
723
+
724
+ function getStyleProperty( propName ) {
725
+ if ( !propName ) {
726
+ return;
727
+ }
728
+
729
+ // test standard property first
730
+ if ( typeof docElemStyle[ propName ] === 'string' ) {
731
+ return propName;
732
+ }
733
+
734
+ // capitalize
735
+ propName = propName.charAt(0).toUpperCase() + propName.slice(1);
736
+
737
+ // test vendor specific properties
738
+ var prefixed;
739
+ for ( var i=0, len = prefixes.length; i < len; i++ ) {
740
+ prefixed = prefixes[i] + propName;
741
+ if ( typeof docElemStyle[ prefixed ] === 'string' ) {
742
+ return prefixed;
743
+ }
744
+ }
745
+ }
746
+
747
+ // transport
748
+ if ( typeof define === 'function' && define.amd ) {
749
+ // AMD
750
+ define( 'get-style-property/get-style-property',[],function() {
751
+ return getStyleProperty;
752
+ });
753
+ } else if ( typeof exports === 'object' ) {
754
+ // CommonJS for Component
755
+ module.exports = getStyleProperty;
756
+ } else {
757
+ // browser global
758
+ window.getStyleProperty = getStyleProperty;
759
+ }
760
+
761
+ })( window );
762
+
763
+ /*!
764
+ * getSize v1.2.2
765
+ * measure size of elements
766
+ * MIT license
767
+ */
768
+
769
+ /*jshint browser: true, strict: true, undef: true, unused: true */
770
+ /*global define: false, exports: false, require: false, module: false, console: false */
771
+
772
+ ( function( window, undefined ) {
773
+
774
+
775
+
776
+ // -------------------------- helpers -------------------------- //
777
+
778
+ // get a number from a string, not a percentage
779
+ function getStyleSize( value ) {
780
+ var num = parseFloat( value );
781
+ // not a percent like '100%', and a number
782
+ var isValid = value.indexOf('%') === -1 && !isNaN( num );
783
+ return isValid && num;
784
+ }
785
+
786
+ function noop() {}
787
+
788
+ var logError = typeof console === 'undefined' ? noop :
789
+ function( message ) {
790
+ console.error( message );
791
+ };
792
+
793
+ // -------------------------- measurements -------------------------- //
794
+
795
+ var measurements = [
796
+ 'paddingLeft',
797
+ 'paddingRight',
798
+ 'paddingTop',
799
+ 'paddingBottom',
800
+ 'marginLeft',
801
+ 'marginRight',
802
+ 'marginTop',
803
+ 'marginBottom',
804
+ 'borderLeftWidth',
805
+ 'borderRightWidth',
806
+ 'borderTopWidth',
807
+ 'borderBottomWidth'
808
+ ];
809
+
810
+ function getZeroSize() {
811
+ var size = {
812
+ width: 0,
813
+ height: 0,
814
+ innerWidth: 0,
815
+ innerHeight: 0,
816
+ outerWidth: 0,
817
+ outerHeight: 0
818
+ };
819
+ for ( var i=0, len = measurements.length; i < len; i++ ) {
820
+ var measurement = measurements[i];
821
+ size[ measurement ] = 0;
822
+ }
823
+ return size;
824
+ }
825
+
826
+
827
+
828
+ function defineGetSize( getStyleProperty ) {
829
+
830
+ // -------------------------- setup -------------------------- //
831
+
832
+ var isSetup = false;
833
+
834
+ var getStyle, boxSizingProp, isBoxSizeOuter;
835
+
836
+ /**
837
+ * setup vars and functions
838
+ * do it on initial getSize(), rather than on script load
839
+ * For Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=548397
840
+ */
841
+ function setup() {
842
+ // setup once
843
+ if ( isSetup ) {
844
+ return;
845
+ }
846
+ isSetup = true;
847
+
848
+ var getComputedStyle = window.getComputedStyle;
849
+ getStyle = ( function() {
850
+ var getStyleFn = getComputedStyle ?
851
+ function( elem ) {
852
+ return getComputedStyle( elem, null );
853
+ } :
854
+ function( elem ) {
855
+ return elem.currentStyle;
856
+ };
857
+
858
+ return function getStyle( elem ) {
859
+ var style = getStyleFn( elem );
860
+ if ( !style ) {
861
+ logError( 'Style returned ' + style +
862
+ '. Are you running this code in a hidden iframe on Firefox? ' +
863
+ 'See http://bit.ly/getsizebug1' );
864
+ }
865
+ return style;
866
+ };
867
+ })();
868
+
869
+ // -------------------------- box sizing -------------------------- //
870
+
871
+ boxSizingProp = getStyleProperty('boxSizing');
872
+
873
+ /**
874
+ * WebKit measures the outer-width on style.width on border-box elems
875
+ * IE & Firefox measures the inner-width
876
+ */
877
+ if ( boxSizingProp ) {
878
+ var div = document.createElement('div');
879
+ div.style.width = '200px';
880
+ div.style.padding = '1px 2px 3px 4px';
881
+ div.style.borderStyle = 'solid';
882
+ div.style.borderWidth = '1px 2px 3px 4px';
883
+ div.style[ boxSizingProp ] = 'border-box';
884
+
885
+ var body = document.body || document.documentElement;
886
+ body.appendChild( div );
887
+ var style = getStyle( div );
888
+
889
+ isBoxSizeOuter = getStyleSize( style.width ) === 200;
890
+ body.removeChild( div );
891
+ }
892
+
893
+ }
894
+
895
+ // -------------------------- getSize -------------------------- //
896
+
897
+ function getSize( elem ) {
898
+ setup();
899
+
900
+ // use querySeletor if elem is string
901
+ if ( typeof elem === 'string' ) {
902
+ elem = document.querySelector( elem );
903
+ }
904
+
905
+ // do not proceed on non-objects
906
+ if ( !elem || typeof elem !== 'object' || !elem.nodeType ) {
907
+ return;
908
+ }
909
+
910
+ var style = getStyle( elem );
911
+
912
+ // if hidden, everything is 0
913
+ if ( style.display === 'none' ) {
914
+ return getZeroSize();
915
+ }
916
+
917
+ var size = {};
918
+ size.width = elem.offsetWidth;
919
+ size.height = elem.offsetHeight;
920
+
921
+ var isBorderBox = size.isBorderBox = !!( boxSizingProp &&
922
+ style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' );
923
+
924
+ // get all measurements
925
+ for ( var i=0, len = measurements.length; i < len; i++ ) {
926
+ var measurement = measurements[i];
927
+ var value = style[ measurement ];
928
+ value = mungeNonPixel( elem, value );
929
+ var num = parseFloat( value );
930
+ // any 'auto', 'medium' value will be 0
931
+ size[ measurement ] = !isNaN( num ) ? num : 0;
932
+ }
933
+
934
+ var paddingWidth = size.paddingLeft + size.paddingRight;
935
+ var paddingHeight = size.paddingTop + size.paddingBottom;
936
+ var marginWidth = size.marginLeft + size.marginRight;
937
+ var marginHeight = size.marginTop + size.marginBottom;
938
+ var borderWidth = size.borderLeftWidth + size.borderRightWidth;
939
+ var borderHeight = size.borderTopWidth + size.borderBottomWidth;
940
+
941
+ var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
942
+
943
+ // overwrite width and height if we can get it from style
944
+ var styleWidth = getStyleSize( style.width );
945
+ if ( styleWidth !== false ) {
946
+ size.width = styleWidth +
947
+ // add padding and border unless it's already including it
948
+ ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
949
+ }
950
+
951
+ var styleHeight = getStyleSize( style.height );
952
+ if ( styleHeight !== false ) {
953
+ size.height = styleHeight +
954
+ // add padding and border unless it's already including it
955
+ ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
956
+ }
957
+
958
+ size.innerWidth = size.width - ( paddingWidth + borderWidth );
959
+ size.innerHeight = size.height - ( paddingHeight + borderHeight );
960
+
961
+ size.outerWidth = size.width + marginWidth;
962
+ size.outerHeight = size.height + marginHeight;
963
+
964
+ return size;
965
+ }
966
+
967
+ // IE8 returns percent values, not pixels
968
+ // taken from jQuery's curCSS
969
+ function mungeNonPixel( elem, value ) {
970
+ // IE8 and has percent value
971
+ if ( window.getComputedStyle || value.indexOf('%') === -1 ) {
972
+ return value;
973
+ }
974
+ var style = elem.style;
975
+ // Remember the original values
976
+ var left = style.left;
977
+ var rs = elem.runtimeStyle;
978
+ var rsLeft = rs && rs.left;
979
+
980
+ // Put in the new values to get a computed value out
981
+ if ( rsLeft ) {
982
+ rs.left = elem.currentStyle.left;
983
+ }
984
+ style.left = value;
985
+ value = style.pixelLeft;
986
+
987
+ // Revert the changed values
988
+ style.left = left;
989
+ if ( rsLeft ) {
990
+ rs.left = rsLeft;
991
+ }
992
+
993
+ return value;
994
+ }
995
+
996
+ return getSize;
997
+
998
+ }
999
+
1000
+ // transport
1001
+ if ( typeof define === 'function' && define.amd ) {
1002
+ // AMD for RequireJS
1003
+ define( 'get-size/get-size',[ 'get-style-property/get-style-property' ], defineGetSize );
1004
+ } else if ( typeof exports === 'object' ) {
1005
+ // CommonJS for Component
1006
+ module.exports = defineGetSize( require('desandro-get-style-property') );
1007
+ } else {
1008
+ // browser global
1009
+ window.getSize = defineGetSize( window.getStyleProperty );
1010
+ }
1011
+
1012
+ })( window );
1013
+
1014
+ /*!
1015
+ * docReady v1.0.4
1016
+ * Cross browser DOMContentLoaded event emitter
1017
+ * MIT license
1018
+ */
1019
+
1020
+ /*jshint browser: true, strict: true, undef: true, unused: true*/
1021
+ /*global define: false, require: false, module: false */
1022
+
1023
+ ( function( window ) {
1024
+
1025
+
1026
+
1027
+ var document = window.document;
1028
+ // collection of functions to be triggered on ready
1029
+ var queue = [];
1030
+
1031
+ function docReady( fn ) {
1032
+ // throw out non-functions
1033
+ if ( typeof fn !== 'function' ) {
1034
+ return;
1035
+ }
1036
+
1037
+ if ( docReady.isReady ) {
1038
+ // ready now, hit it
1039
+ fn();
1040
+ } else {
1041
+ // queue function when ready
1042
+ queue.push( fn );
1043
+ }
1044
+ }
1045
+
1046
+ docReady.isReady = false;
1047
+
1048
+ // triggered on various doc ready events
1049
+ function onReady( event ) {
1050
+ // bail if already triggered or IE8 document is not ready just yet
1051
+ var isIE8NotReady = event.type === 'readystatechange' && document.readyState !== 'complete';
1052
+ if ( docReady.isReady || isIE8NotReady ) {
1053
+ return;
1054
+ }
1055
+
1056
+ trigger();
1057
+ }
1058
+
1059
+ function trigger() {
1060
+ docReady.isReady = true;
1061
+ // process queue
1062
+ for ( var i=0, len = queue.length; i < len; i++ ) {
1063
+ var fn = queue[i];
1064
+ fn();
1065
+ }
1066
+ }
1067
+
1068
+ function defineDocReady( eventie ) {
1069
+ // trigger ready if page is ready
1070
+ if ( document.readyState === 'complete' ) {
1071
+ trigger();
1072
+ } else {
1073
+ // listen for events
1074
+ eventie.bind( document, 'DOMContentLoaded', onReady );
1075
+ eventie.bind( document, 'readystatechange', onReady );
1076
+ eventie.bind( window, 'load', onReady );
1077
+ }
1078
+
1079
+ return docReady;
1080
+ }
1081
+
1082
+ // transport
1083
+ if ( typeof define === 'function' && define.amd ) {
1084
+ // AMD
1085
+ define( 'doc-ready/doc-ready',[ 'eventie/eventie' ], defineDocReady );
1086
+ } else if ( typeof exports === 'object' ) {
1087
+ module.exports = defineDocReady( require('eventie') );
1088
+ } else {
1089
+ // browser global
1090
+ window.docReady = defineDocReady( window.eventie );
1091
+ }
1092
+
1093
+ })( window );
1094
+
1095
+ /**
1096
+ * matchesSelector v1.0.3
1097
+ * matchesSelector( element, '.selector' )
1098
+ * MIT license
1099
+ */
1100
+
1101
+ /*jshint browser: true, strict: true, undef: true, unused: true */
1102
+ /*global define: false, module: false */
1103
+
1104
+ ( function( ElemProto ) {
1105
+
1106
+ 'use strict';
1107
+
1108
+ var matchesMethod = ( function() {
1109
+ // check for the standard method name first
1110
+ if ( ElemProto.matches ) {
1111
+ return 'matches';
1112
+ }
1113
+ // check un-prefixed
1114
+ if ( ElemProto.matchesSelector ) {
1115
+ return 'matchesSelector';
1116
+ }
1117
+ // check vendor prefixes
1118
+ var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
1119
+
1120
+ for ( var i=0, len = prefixes.length; i < len; i++ ) {
1121
+ var prefix = prefixes[i];
1122
+ var method = prefix + 'MatchesSelector';
1123
+ if ( ElemProto[ method ] ) {
1124
+ return method;
1125
+ }
1126
+ }
1127
+ })();
1128
+
1129
+ // ----- match ----- //
1130
+
1131
+ function match( elem, selector ) {
1132
+ return elem[ matchesMethod ]( selector );
1133
+ }
1134
+
1135
+ // ----- appendToFragment ----- //
1136
+
1137
+ function checkParent( elem ) {
1138
+ // not needed if already has parent
1139
+ if ( elem.parentNode ) {
1140
+ return;
1141
+ }
1142
+ var fragment = document.createDocumentFragment();
1143
+ fragment.appendChild( elem );
1144
+ }
1145
+
1146
+ // ----- query ----- //
1147
+
1148
+ // fall back to using QSA
1149
+ // thx @jonathantneal https://gist.github.com/3062955
1150
+ function query( elem, selector ) {
1151
+ // append to fragment if no parent
1152
+ checkParent( elem );
1153
+
1154
+ // match elem with all selected elems of parent
1155
+ var elems = elem.parentNode.querySelectorAll( selector );
1156
+ for ( var i=0, len = elems.length; i < len; i++ ) {
1157
+ // return true if match
1158
+ if ( elems[i] === elem ) {
1159
+ return true;
1160
+ }
1161
+ }
1162
+ // otherwise return false
1163
+ return false;
1164
+ }
1165
+
1166
+ // ----- matchChild ----- //
1167
+
1168
+ function matchChild( elem, selector ) {
1169
+ checkParent( elem );
1170
+ return match( elem, selector );
1171
+ }
1172
+
1173
+ // ----- matchesSelector ----- //
1174
+
1175
+ var matchesSelector;
1176
+
1177
+ if ( matchesMethod ) {
1178
+ // IE9 supports matchesSelector, but doesn't work on orphaned elems
1179
+ // check for that
1180
+ var div = document.createElement('div');
1181
+ var supportsOrphans = match( div, 'div' );
1182
+ matchesSelector = supportsOrphans ? match : matchChild;
1183
+ } else {
1184
+ matchesSelector = query;
1185
+ }
1186
+
1187
+ // transport
1188
+ if ( typeof define === 'function' && define.amd ) {
1189
+ // AMD
1190
+ define( 'matches-selector/matches-selector',[],function() {
1191
+ return matchesSelector;
1192
+ });
1193
+ } else if ( typeof exports === 'object' ) {
1194
+ module.exports = matchesSelector;
1195
+ }
1196
+ else {
1197
+ // browser global
1198
+ window.matchesSelector = matchesSelector;
1199
+ }
1200
+
1201
+ })( Element.prototype );
1202
+
1203
+ /**
1204
+ * Fizzy UI utils v1.0.1
1205
+ * MIT license
1206
+ */
1207
+
1208
+ /*jshint browser: true, undef: true, unused: true, strict: true */
1209
+
1210
+ ( function( window, factory ) {
1211
+ /*global define: false, module: false, require: false */
1212
+ 'use strict';
1213
+ // universal module definition
1214
+
1215
+ if ( typeof define == 'function' && define.amd ) {
1216
+ // AMD
1217
+ define( 'fizzy-ui-utils/utils',[
1218
+ 'doc-ready/doc-ready',
1219
+ 'matches-selector/matches-selector'
1220
+ ], function( docReady, matchesSelector ) {
1221
+ return factory( window, docReady, matchesSelector );
1222
+ });
1223
+ } else if ( typeof exports == 'object' ) {
1224
+ // CommonJS
1225
+ module.exports = factory(
1226
+ window,
1227
+ require('doc-ready'),
1228
+ require('desandro-matches-selector')
1229
+ );
1230
+ } else {
1231
+ // browser global
1232
+ window.fizzyUIUtils = factory(
1233
+ window,
1234
+ window.docReady,
1235
+ window.matchesSelector
1236
+ );
1237
+ }
1238
+
1239
+ }( window, function factory( window, docReady, matchesSelector ) {
1240
+
1241
+
1242
+
1243
+ var utils = {};
1244
+
1245
+ // ----- extend ----- //
1246
+
1247
+ // extends objects
1248
+ utils.extend = function( a, b ) {
1249
+ for ( var prop in b ) {
1250
+ a[ prop ] = b[ prop ];
1251
+ }
1252
+ return a;
1253
+ };
1254
+
1255
+ // ----- modulo ----- //
1256
+
1257
+ utils.modulo = function( num, div ) {
1258
+ return ( ( num % div ) + div ) % div;
1259
+ };
1260
+
1261
+ // ----- isArray ----- //
1262
+
1263
+ var objToString = Object.prototype.toString;
1264
+ utils.isArray = function( obj ) {
1265
+ return objToString.call( obj ) == '[object Array]';
1266
+ };
1267
+
1268
+ // ----- makeArray ----- //
1269
+
1270
+ // turn element or nodeList into an array
1271
+ utils.makeArray = function( obj ) {
1272
+ var ary = [];
1273
+ if ( utils.isArray( obj ) ) {
1274
+ // use object if already an array
1275
+ ary = obj;
1276
+ } else if ( obj && typeof obj.length == 'number' ) {
1277
+ // convert nodeList to array
1278
+ for ( var i=0, len = obj.length; i < len; i++ ) {
1279
+ ary.push( obj[i] );
1280
+ }
1281
+ } else {
1282
+ // array of single index
1283
+ ary.push( obj );
1284
+ }
1285
+ return ary;
1286
+ };
1287
+
1288
+ // ----- indexOf ----- //
1289
+
1290
+ // index of helper cause IE8
1291
+ utils.indexOf = Array.prototype.indexOf ? function( ary, obj ) {
1292
+ return ary.indexOf( obj );
1293
+ } : function( ary, obj ) {
1294
+ for ( var i=0, len = ary.length; i < len; i++ ) {
1295
+ if ( ary[i] === obj ) {
1296
+ return i;
1297
+ }
1298
+ }
1299
+ return -1;
1300
+ };
1301
+
1302
+ // ----- removeFrom ----- //
1303
+
1304
+ utils.removeFrom = function( ary, obj ) {
1305
+ var index = utils.indexOf( ary, obj );
1306
+ if ( index != -1 ) {
1307
+ ary.splice( index, 1 );
1308
+ }
1309
+ };
1310
+
1311
+ // ----- isElement ----- //
1312
+
1313
+ // http://stackoverflow.com/a/384380/182183
1314
+ utils.isElement = ( typeof HTMLElement == 'function' || typeof HTMLElement == 'object' ) ?
1315
+ function isElementDOM2( obj ) {
1316
+ return obj instanceof HTMLElement;
1317
+ } :
1318
+ function isElementQuirky( obj ) {
1319
+ return obj && typeof obj == 'object' &&
1320
+ obj.nodeType == 1 && typeof obj.nodeName == 'string';
1321
+ };
1322
+
1323
+ // ----- setText ----- //
1324
+
1325
+ utils.setText = ( function() {
1326
+ var setTextProperty;
1327
+ function setText( elem, text ) {
1328
+ // only check setTextProperty once
1329
+ setTextProperty = setTextProperty || ( document.documentElement.textContent !== undefined ? 'textContent' : 'innerText' );
1330
+ elem[ setTextProperty ] = text;
1331
+ }
1332
+ return setText;
1333
+ })();
1334
+
1335
+ // ----- getParent ----- //
1336
+
1337
+ utils.getParent = function( elem, selector ) {
1338
+ while ( elem != document.body ) {
1339
+ elem = elem.parentNode;
1340
+ if ( matchesSelector( elem, selector ) ) {
1341
+ return elem;
1342
+ }
1343
+ }
1344
+ };
1345
+
1346
+ // ----- getQueryElement ----- //
1347
+
1348
+ // use element as selector string
1349
+ utils.getQueryElement = function( elem ) {
1350
+ if ( typeof elem == 'string' ) {
1351
+ return document.querySelector( elem );
1352
+ }
1353
+ return elem;
1354
+ };
1355
+
1356
+ // ----- handleEvent ----- //
1357
+
1358
+ // enable .ontype to trigger from .addEventListener( elem, 'type' )
1359
+ utils.handleEvent = function( event ) {
1360
+ var method = 'on' + event.type;
1361
+ if ( this[ method ] ) {
1362
+ this[ method ]( event );
1363
+ }
1364
+ };
1365
+
1366
+ // ----- filterFindElements ----- //
1367
+
1368
+ utils.filterFindElements = function( elems, selector ) {
1369
+ // make array of elems
1370
+ elems = utils.makeArray( elems );
1371
+ var ffElems = [];
1372
+
1373
+ for ( var i=0, len = elems.length; i < len; i++ ) {
1374
+ var elem = elems[i];
1375
+ // check that elem is an actual element
1376
+ if ( !utils.isElement( elem ) ) {
1377
+ continue;
1378
+ }
1379
+ // filter & find items if we have a selector
1380
+ if ( selector ) {
1381
+ // filter siblings
1382
+ if ( matchesSelector( elem, selector ) ) {
1383
+ ffElems.push( elem );
1384
+ }
1385
+ // find children
1386
+ var childElems = elem.querySelectorAll( selector );
1387
+ // concat childElems to filterFound array
1388
+ for ( var j=0, jLen = childElems.length; j < jLen; j++ ) {
1389
+ ffElems.push( childElems[j] );
1390
+ }
1391
+ } else {
1392
+ ffElems.push( elem );
1393
+ }
1394
+ }
1395
+
1396
+ return ffElems;
1397
+ };
1398
+
1399
+ // ----- debounceMethod ----- //
1400
+
1401
+ utils.debounceMethod = function( _class, methodName, threshold ) {
1402
+ // original method
1403
+ var method = _class.prototype[ methodName ];
1404
+ var timeoutName = methodName + 'Timeout';
1405
+
1406
+ _class.prototype[ methodName ] = function() {
1407
+ var timeout = this[ timeoutName ];
1408
+ if ( timeout ) {
1409
+ clearTimeout( timeout );
1410
+ }
1411
+ var args = arguments;
1412
+
1413
+ var _this = this;
1414
+ this[ timeoutName ] = setTimeout( function() {
1415
+ method.apply( _this, args );
1416
+ delete _this[ timeoutName ];
1417
+ }, threshold || 100 );
1418
+ };
1419
+ };
1420
+
1421
+ // ----- htmlInit ----- //
1422
+
1423
+ // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
1424
+ utils.toDashed = function( str ) {
1425
+ return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
1426
+ return $1 + '-' + $2;
1427
+ }).toLowerCase();
1428
+ };
1429
+
1430
+ var console = window.console;
1431
+ /**
1432
+ * allow user to initialize classes via .js-namespace class
1433
+ * htmlInit( Widget, 'widgetName' )
1434
+ * options are parsed from data-namespace-option attribute
1435
+ */
1436
+ utils.htmlInit = function( WidgetClass, namespace ) {
1437
+ docReady( function() {
1438
+ var dashedNamespace = utils.toDashed( namespace );
1439
+ var elems = document.querySelectorAll( '.js-' + dashedNamespace );
1440
+ var dataAttr = 'data-' + dashedNamespace + '-options';
1441
+
1442
+ for ( var i=0, len = elems.length; i < len; i++ ) {
1443
+ var elem = elems[i];
1444
+ var attr = elem.getAttribute( dataAttr );
1445
+ var options;
1446
+ try {
1447
+ options = attr && JSON.parse( attr );
1448
+ } catch ( error ) {
1449
+ // log error, do not initialize
1450
+ if ( console ) {
1451
+ console.error( 'Error parsing ' + dataAttr + ' on ' +
1452
+ elem.nodeName.toLowerCase() + ( elem.id ? '#' + elem.id : '' ) + ': ' +
1453
+ error );
1454
+ }
1455
+ continue;
1456
+ }
1457
+ // initialize
1458
+ var instance = new WidgetClass( elem, options );
1459
+ // make available via $().data('layoutname')
1460
+ var jQuery = window.jQuery;
1461
+ if ( jQuery ) {
1462
+ jQuery.data( elem, namespace, instance );
1463
+ }
1464
+ }
1465
+ });
1466
+ };
1467
+
1468
+ // ----- ----- //
1469
+
1470
+ return utils;
1471
+
1472
+ }));
1473
+
1474
+ /**
1475
+ * Outlayer Item
1476
+ */
1477
+
1478
+ ( function( window, factory ) {
1479
+ 'use strict';
1480
+ // universal module definition
1481
+ if ( typeof define === 'function' && define.amd ) {
1482
+ // AMD
1483
+ define( 'outlayer/item',[
1484
+ 'eventEmitter/EventEmitter',
1485
+ 'get-size/get-size',
1486
+ 'get-style-property/get-style-property',
1487
+ 'fizzy-ui-utils/utils'
1488
+ ],
1489
+ function( EventEmitter, getSize, getStyleProperty, utils ) {
1490
+ return factory( window, EventEmitter, getSize, getStyleProperty, utils );
1491
+ }
1492
+ );
1493
+ } else if (typeof exports === 'object') {
1494
+ // CommonJS
1495
+ module.exports = factory(
1496
+ window,
1497
+ require('wolfy87-eventemitter'),
1498
+ require('get-size'),
1499
+ require('desandro-get-style-property'),
1500
+ require('fizzy-ui-utils')
1501
+ );
1502
+ } else {
1503
+ // browser global
1504
+ window.Outlayer = {};
1505
+ window.Outlayer.Item = factory(
1506
+ window,
1507
+ window.EventEmitter,
1508
+ window.getSize,
1509
+ window.getStyleProperty,
1510
+ window.fizzyUIUtils
1511
+ );
1512
+ }
1513
+
1514
+ }( window, function factory( window, EventEmitter, getSize, getStyleProperty, utils ) {
1515
+ 'use strict';
1516
+
1517
+ // ----- helpers ----- //
1518
+
1519
+ var getComputedStyle = window.getComputedStyle;
1520
+ var getStyle = getComputedStyle ?
1521
+ function( elem ) {
1522
+ return getComputedStyle( elem, null );
1523
+ } :
1524
+ function( elem ) {
1525
+ return elem.currentStyle;
1526
+ };
1527
+
1528
+
1529
+ function isEmptyObj( obj ) {
1530
+ for ( var prop in obj ) {
1531
+ return false;
1532
+ }
1533
+ prop = null;
1534
+ return true;
1535
+ }
1536
+
1537
+ // -------------------------- CSS3 support -------------------------- //
1538
+
1539
+ var transitionProperty = getStyleProperty('transition');
1540
+ var transformProperty = getStyleProperty('transform');
1541
+ var supportsCSS3 = transitionProperty && transformProperty;
1542
+ var is3d = !!getStyleProperty('perspective');
1543
+
1544
+ var transitionEndEvent = {
1545
+ WebkitTransition: 'webkitTransitionEnd',
1546
+ MozTransition: 'transitionend',
1547
+ OTransition: 'otransitionend',
1548
+ transition: 'transitionend'
1549
+ }[ transitionProperty ];
1550
+
1551
+ // properties that could have vendor prefix
1552
+ var prefixableProperties = [
1553
+ 'transform',
1554
+ 'transition',
1555
+ 'transitionDuration',
1556
+ 'transitionProperty'
1557
+ ];
1558
+
1559
+ // cache all vendor properties
1560
+ var vendorProperties = ( function() {
1561
+ var cache = {};
1562
+ for ( var i=0, len = prefixableProperties.length; i < len; i++ ) {
1563
+ var prop = prefixableProperties[i];
1564
+ var supportedProp = getStyleProperty( prop );
1565
+ if ( supportedProp && supportedProp !== prop ) {
1566
+ cache[ prop ] = supportedProp;
1567
+ }
1568
+ }
1569
+ return cache;
1570
+ })();
1571
+
1572
+ // -------------------------- Item -------------------------- //
1573
+
1574
+ function Item( element, layout ) {
1575
+ if ( !element ) {
1576
+ return;
1577
+ }
1578
+
1579
+ this.element = element;
1580
+ // parent layout class, i.e. Masonry, Isotope, or Packery
1581
+ this.layout = layout;
1582
+ this.position = {
1583
+ x: 0,
1584
+ y: 0
1585
+ };
1586
+
1587
+ this._create();
1588
+ }
1589
+
1590
+ // inherit EventEmitter
1591
+ utils.extend( Item.prototype, EventEmitter.prototype );
1592
+
1593
+ Item.prototype._create = function() {
1594
+ // transition objects
1595
+ this._transn = {
1596
+ ingProperties: {},
1597
+ clean: {},
1598
+ onEnd: {}
1599
+ };
1600
+
1601
+ this.css({
1602
+ position: 'absolute'
1603
+ });
1604
+ };
1605
+
1606
+ // trigger specified handler for event type
1607
+ Item.prototype.handleEvent = function( event ) {
1608
+ var method = 'on' + event.type;
1609
+ if ( this[ method ] ) {
1610
+ this[ method ]( event );
1611
+ }
1612
+ };
1613
+
1614
+ Item.prototype.getSize = function() {
1615
+ this.size = getSize( this.element );
1616
+ };
1617
+
1618
+ /**
1619
+ * apply CSS styles to element
1620
+ * @param {Object} style
1621
+ */
1622
+ Item.prototype.css = function( style ) {
1623
+ var elemStyle = this.element.style;
1624
+
1625
+ for ( var prop in style ) {
1626
+ // use vendor property if available
1627
+ var supportedProp = vendorProperties[ prop ] || prop;
1628
+ elemStyle[ supportedProp ] = style[ prop ];
1629
+ }
1630
+ };
1631
+
1632
+ // measure position, and sets it
1633
+ Item.prototype.getPosition = function() {
1634
+ var style = getStyle( this.element );
1635
+ var layoutOptions = this.layout.options;
1636
+ var isOriginLeft = layoutOptions.isOriginLeft;
1637
+ var isOriginTop = layoutOptions.isOriginTop;
1638
+ var xValue = style[ isOriginLeft ? 'left' : 'right' ];
1639
+ var yValue = style[ isOriginTop ? 'top' : 'bottom' ];
1640
+ // convert percent to pixels
1641
+ var layoutSize = this.layout.size;
1642
+ var x = xValue.indexOf('%') != -1 ?
1643
+ ( parseFloat( xValue ) / 100 ) * layoutSize.width : parseInt( xValue, 10 );
1644
+ var y = yValue.indexOf('%') != -1 ?
1645
+ ( parseFloat( yValue ) / 100 ) * layoutSize.height : parseInt( yValue, 10 );
1646
+
1647
+ // clean up 'auto' or other non-integer values
1648
+ x = isNaN( x ) ? 0 : x;
1649
+ y = isNaN( y ) ? 0 : y;
1650
+ // remove padding from measurement
1651
+ x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
1652
+ y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
1653
+
1654
+ this.position.x = x;
1655
+ this.position.y = y;
1656
+ };
1657
+
1658
+ // set settled position, apply padding
1659
+ Item.prototype.layoutPosition = function() {
1660
+ var layoutSize = this.layout.size;
1661
+ var layoutOptions = this.layout.options;
1662
+ var style = {};
1663
+
1664
+ // x
1665
+ var xPadding = layoutOptions.isOriginLeft ? 'paddingLeft' : 'paddingRight';
1666
+ var xProperty = layoutOptions.isOriginLeft ? 'left' : 'right';
1667
+ var xResetProperty = layoutOptions.isOriginLeft ? 'right' : 'left';
1668
+
1669
+ var x = this.position.x + layoutSize[ xPadding ];
1670
+ // set in percentage or pixels
1671
+ style[ xProperty ] = this.getXValue( x );
1672
+ // reset other property
1673
+ style[ xResetProperty ] = '';
1674
+
1675
+ // y
1676
+ var yPadding = layoutOptions.isOriginTop ? 'paddingTop' : 'paddingBottom';
1677
+ var yProperty = layoutOptions.isOriginTop ? 'top' : 'bottom';
1678
+ var yResetProperty = layoutOptions.isOriginTop ? 'bottom' : 'top';
1679
+
1680
+ var y = this.position.y + layoutSize[ yPadding ];
1681
+ // set in percentage or pixels
1682
+ style[ yProperty ] = this.getYValue( y );
1683
+ // reset other property
1684
+ style[ yResetProperty ] = '';
1685
+
1686
+ this.css( style );
1687
+ this.emitEvent( 'layout', [ this ] );
1688
+ };
1689
+
1690
+ Item.prototype.getXValue = function( x ) {
1691
+ var layoutOptions = this.layout.options;
1692
+ return layoutOptions.percentPosition && !layoutOptions.isHorizontal ?
1693
+ ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px';
1694
+ };
1695
+
1696
+ Item.prototype.getYValue = function( y ) {
1697
+ var layoutOptions = this.layout.options;
1698
+ return layoutOptions.percentPosition && layoutOptions.isHorizontal ?
1699
+ ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px';
1700
+ };
1701
+
1702
+
1703
+ Item.prototype._transitionTo = function( x, y ) {
1704
+ this.getPosition();
1705
+ // get current x & y from top/left
1706
+ var curX = this.position.x;
1707
+ var curY = this.position.y;
1708
+
1709
+ var compareX = parseInt( x, 10 );
1710
+ var compareY = parseInt( y, 10 );
1711
+ var didNotMove = compareX === this.position.x && compareY === this.position.y;
1712
+
1713
+ // save end position
1714
+ this.setPosition( x, y );
1715
+
1716
+ // if did not move and not transitioning, just go to layout
1717
+ if ( didNotMove && !this.isTransitioning ) {
1718
+ this.layoutPosition();
1719
+ return;
1720
+ }
1721
+
1722
+ var transX = x - curX;
1723
+ var transY = y - curY;
1724
+ var transitionStyle = {};
1725
+ transitionStyle.transform = this.getTranslate( transX, transY );
1726
+
1727
+ this.transition({
1728
+ to: transitionStyle,
1729
+ onTransitionEnd: {
1730
+ transform: this.layoutPosition
1731
+ },
1732
+ isCleaning: true
1733
+ });
1734
+ };
1735
+
1736
+ Item.prototype.getTranslate = function( x, y ) {
1737
+ // flip cooridinates if origin on right or bottom
1738
+ var layoutOptions = this.layout.options;
1739
+ x = layoutOptions.isOriginLeft ? x : -x;
1740
+ y = layoutOptions.isOriginTop ? y : -y;
1741
+
1742
+ if ( is3d ) {
1743
+ return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
1744
+ }
1745
+
1746
+ return 'translate(' + x + 'px, ' + y + 'px)';
1747
+ };
1748
+
1749
+ // non transition + transform support
1750
+ Item.prototype.goTo = function( x, y ) {
1751
+ this.setPosition( x, y );
1752
+ this.layoutPosition();
1753
+ };
1754
+
1755
+ // use transition and transforms if supported
1756
+ Item.prototype.moveTo = supportsCSS3 ?
1757
+ Item.prototype._transitionTo : Item.prototype.goTo;
1758
+
1759
+ Item.prototype.setPosition = function( x, y ) {
1760
+ this.position.x = parseInt( x, 10 );
1761
+ this.position.y = parseInt( y, 10 );
1762
+ };
1763
+
1764
+ // ----- transition ----- //
1765
+
1766
+ /**
1767
+ * @param {Object} style - CSS
1768
+ * @param {Function} onTransitionEnd
1769
+ */
1770
+
1771
+ // non transition, just trigger callback
1772
+ Item.prototype._nonTransition = function( args ) {
1773
+ this.css( args.to );
1774
+ if ( args.isCleaning ) {
1775
+ this._removeStyles( args.to );
1776
+ }
1777
+ for ( var prop in args.onTransitionEnd ) {
1778
+ args.onTransitionEnd[ prop ].call( this );
1779
+ }
1780
+ };
1781
+
1782
+ /**
1783
+ * proper transition
1784
+ * @param {Object} args - arguments
1785
+ * @param {Object} to - style to transition to
1786
+ * @param {Object} from - style to start transition from
1787
+ * @param {Boolean} isCleaning - removes transition styles after transition
1788
+ * @param {Function} onTransitionEnd - callback
1789
+ */
1790
+ Item.prototype._transition = function( args ) {
1791
+ // redirect to nonTransition if no transition duration
1792
+ if ( !parseFloat( this.layout.options.transitionDuration ) ) {
1793
+ this._nonTransition( args );
1794
+ return;
1795
+ }
1796
+
1797
+ var _transition = this._transn;
1798
+ // keep track of onTransitionEnd callback by css property
1799
+ for ( var prop in args.onTransitionEnd ) {
1800
+ _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
1801
+ }
1802
+ // keep track of properties that are transitioning
1803
+ for ( prop in args.to ) {
1804
+ _transition.ingProperties[ prop ] = true;
1805
+ // keep track of properties to clean up when transition is done
1806
+ if ( args.isCleaning ) {
1807
+ _transition.clean[ prop ] = true;
1808
+ }
1809
+ }
1810
+
1811
+ // set from styles
1812
+ if ( args.from ) {
1813
+ this.css( args.from );
1814
+ // force redraw. http://blog.alexmaccaw.com/css-transitions
1815
+ var h = this.element.offsetHeight;
1816
+ // hack for JSHint to hush about unused var
1817
+ h = null;
1818
+ }
1819
+ // enable transition
1820
+ this.enableTransition( args.to );
1821
+ // set styles that are transitioning
1822
+ this.css( args.to );
1823
+
1824
+ this.isTransitioning = true;
1825
+
1826
+ };
1827
+
1828
+ // dash before all cap letters, including first for
1829
+ // WebkitTransform => -webkit-transform
1830
+ function toDashedAll( str ) {
1831
+ return str.replace( /([A-Z])/g, function( $1 ) {
1832
+ return '-' + $1.toLowerCase();
1833
+ });
1834
+ }
1835
+
1836
+ var transitionProps = 'opacity,' +
1837
+ toDashedAll( vendorProperties.transform || 'transform' );
1838
+
1839
+ Item.prototype.enableTransition = function(/* style */) {
1840
+ // HACK changing transitionProperty during a transition
1841
+ // will cause transition to jump
1842
+ if ( this.isTransitioning ) {
1843
+ return;
1844
+ }
1845
+
1846
+ // make `transition: foo, bar, baz` from style object
1847
+ // HACK un-comment this when enableTransition can work
1848
+ // while a transition is happening
1849
+ // var transitionValues = [];
1850
+ // for ( var prop in style ) {
1851
+ // // dash-ify camelCased properties like WebkitTransition
1852
+ // prop = vendorProperties[ prop ] || prop;
1853
+ // transitionValues.push( toDashedAll( prop ) );
1854
+ // }
1855
+ // enable transition styles
1856
+ this.css({
1857
+ transitionProperty: transitionProps,
1858
+ transitionDuration: this.layout.options.transitionDuration
1859
+ });
1860
+ // listen for transition end event
1861
+ this.element.addEventListener( transitionEndEvent, this, false );
1862
+ };
1863
+
1864
+ Item.prototype.transition = Item.prototype[ transitionProperty ? '_transition' : '_nonTransition' ];
1865
+
1866
+ // ----- events ----- //
1867
+
1868
+ Item.prototype.onwebkitTransitionEnd = function( event ) {
1869
+ this.ontransitionend( event );
1870
+ };
1871
+
1872
+ Item.prototype.onotransitionend = function( event ) {
1873
+ this.ontransitionend( event );
1874
+ };
1875
+
1876
+ // properties that I munge to make my life easier
1877
+ var dashedVendorProperties = {
1878
+ '-webkit-transform': 'transform',
1879
+ '-moz-transform': 'transform',
1880
+ '-o-transform': 'transform'
1881
+ };
1882
+
1883
+ Item.prototype.ontransitionend = function( event ) {
1884
+ // disregard bubbled events from children
1885
+ if ( event.target !== this.element ) {
1886
+ return;
1887
+ }
1888
+ var _transition = this._transn;
1889
+ // get property name of transitioned property, convert to prefix-free
1890
+ var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
1891
+
1892
+ // remove property that has completed transitioning
1893
+ delete _transition.ingProperties[ propertyName ];
1894
+ // check if any properties are still transitioning
1895
+ if ( isEmptyObj( _transition.ingProperties ) ) {
1896
+ // all properties have completed transitioning
1897
+ this.disableTransition();
1898
+ }
1899
+ // clean style
1900
+ if ( propertyName in _transition.clean ) {
1901
+ // clean up style
1902
+ this.element.style[ event.propertyName ] = '';
1903
+ delete _transition.clean[ propertyName ];
1904
+ }
1905
+ // trigger onTransitionEnd callback
1906
+ if ( propertyName in _transition.onEnd ) {
1907
+ var onTransitionEnd = _transition.onEnd[ propertyName ];
1908
+ onTransitionEnd.call( this );
1909
+ delete _transition.onEnd[ propertyName ];
1910
+ }
1911
+
1912
+ this.emitEvent( 'transitionEnd', [ this ] );
1913
+ };
1914
+
1915
+ Item.prototype.disableTransition = function() {
1916
+ this.removeTransitionStyles();
1917
+ this.element.removeEventListener( transitionEndEvent, this, false );
1918
+ this.isTransitioning = false;
1919
+ };
1920
+
1921
+ /**
1922
+ * removes style property from element
1923
+ * @param {Object} style
1924
+ **/
1925
+ Item.prototype._removeStyles = function( style ) {
1926
+ // clean up transition styles
1927
+ var cleanStyle = {};
1928
+ for ( var prop in style ) {
1929
+ cleanStyle[ prop ] = '';
1930
+ }
1931
+ this.css( cleanStyle );
1932
+ };
1933
+
1934
+ var cleanTransitionStyle = {
1935
+ transitionProperty: '',
1936
+ transitionDuration: ''
1937
+ };
1938
+
1939
+ Item.prototype.removeTransitionStyles = function() {
1940
+ // remove transition
1941
+ this.css( cleanTransitionStyle );
1942
+ };
1943
+
1944
+ // ----- show/hide/remove ----- //
1945
+
1946
+ // remove element from DOM
1947
+ Item.prototype.removeElem = function() {
1948
+ this.element.parentNode.removeChild( this.element );
1949
+ // remove display: none
1950
+ this.css({ display: '' });
1951
+ this.emitEvent( 'remove', [ this ] );
1952
+ };
1953
+
1954
+ Item.prototype.remove = function() {
1955
+ // just remove element if no transition support or no transition
1956
+ if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
1957
+ this.removeElem();
1958
+ return;
1959
+ }
1960
+
1961
+ // start transition
1962
+ var _this = this;
1963
+ this.once( 'transitionEnd', function() {
1964
+ _this.removeElem();
1965
+ });
1966
+ this.hide();
1967
+ };
1968
+
1969
+ Item.prototype.reveal = function() {
1970
+ delete this.isHidden;
1971
+ // remove display: none
1972
+ this.css({ display: '' });
1973
+
1974
+ var options = this.layout.options;
1975
+
1976
+ var onTransitionEnd = {};
1977
+ var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle');
1978
+ onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd;
1979
+
1980
+ this.transition({
1981
+ from: options.hiddenStyle,
1982
+ to: options.visibleStyle,
1983
+ isCleaning: true,
1984
+ onTransitionEnd: onTransitionEnd
1985
+ });
1986
+ };
1987
+
1988
+ Item.prototype.onRevealTransitionEnd = function() {
1989
+ // check if still visible
1990
+ // during transition, item may have been hidden
1991
+ if ( !this.isHidden ) {
1992
+ this.emitEvent('reveal');
1993
+ }
1994
+ };
1995
+
1996
+ /**
1997
+ * get style property use for hide/reveal transition end
1998
+ * @param {String} styleProperty - hiddenStyle/visibleStyle
1999
+ * @returns {String}
2000
+ */
2001
+ Item.prototype.getHideRevealTransitionEndProperty = function( styleProperty ) {
2002
+ var optionStyle = this.layout.options[ styleProperty ];
2003
+ // use opacity
2004
+ if ( optionStyle.opacity ) {
2005
+ return 'opacity';
2006
+ }
2007
+ // get first property
2008
+ for ( var prop in optionStyle ) {
2009
+ return prop;
2010
+ }
2011
+ };
2012
+
2013
+ Item.prototype.hide = function() {
2014
+ // set flag
2015
+ this.isHidden = true;
2016
+ // remove display: none
2017
+ this.css({ display: '' });
2018
+
2019
+ var options = this.layout.options;
2020
+
2021
+ var onTransitionEnd = {};
2022
+ var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle');
2023
+ onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd;
2024
+
2025
+ this.transition({
2026
+ from: options.visibleStyle,
2027
+ to: options.hiddenStyle,
2028
+ // keep hidden stuff hidden
2029
+ isCleaning: true,
2030
+ onTransitionEnd: onTransitionEnd
2031
+ });
2032
+ };
2033
+
2034
+ Item.prototype.onHideTransitionEnd = function() {
2035
+ // check if still hidden
2036
+ // during transition, item may have been un-hidden
2037
+ if ( this.isHidden ) {
2038
+ this.css({ display: 'none' });
2039
+ this.emitEvent('hide');
2040
+ }
2041
+ };
2042
+
2043
+ Item.prototype.destroy = function() {
2044
+ this.css({
2045
+ position: '',
2046
+ left: '',
2047
+ right: '',
2048
+ top: '',
2049
+ bottom: '',
2050
+ transition: '',
2051
+ transform: ''
2052
+ });
2053
+ };
2054
+
2055
+ return Item;
2056
+
2057
+ }));
2058
+
2059
+ /*!
2060
+ * Outlayer v1.4.2
2061
+ * the brains and guts of a layout library
2062
+ * MIT license
2063
+ */
2064
+
2065
+ ( function( window, factory ) {
2066
+ 'use strict';
2067
+ // universal module definition
2068
+
2069
+ if ( typeof define == 'function' && define.amd ) {
2070
+ // AMD
2071
+ define( 'outlayer/outlayer',[
2072
+ 'eventie/eventie',
2073
+ 'eventEmitter/EventEmitter',
2074
+ 'get-size/get-size',
2075
+ 'fizzy-ui-utils/utils',
2076
+ './item'
2077
+ ],
2078
+ function( eventie, EventEmitter, getSize, utils, Item ) {
2079
+ return factory( window, eventie, EventEmitter, getSize, utils, Item);
2080
+ }
2081
+ );
2082
+ } else if ( typeof exports == 'object' ) {
2083
+ // CommonJS
2084
+ module.exports = factory(
2085
+ window,
2086
+ require('eventie'),
2087
+ require('wolfy87-eventemitter'),
2088
+ require('get-size'),
2089
+ require('fizzy-ui-utils'),
2090
+ require('./item')
2091
+ );
2092
+ } else {
2093
+ // browser global
2094
+ window.Outlayer = factory(
2095
+ window,
2096
+ window.eventie,
2097
+ window.EventEmitter,
2098
+ window.getSize,
2099
+ window.fizzyUIUtils,
2100
+ window.Outlayer.Item
2101
+ );
2102
+ }
2103
+
2104
+ }( window, function factory( window, eventie, EventEmitter, getSize, utils, Item ) {
2105
+ 'use strict';
2106
+
2107
+ // ----- vars ----- //
2108
+
2109
+ var console = window.console;
2110
+ var jQuery = window.jQuery;
2111
+ var noop = function() {};
2112
+
2113
+ // -------------------------- Outlayer -------------------------- //
2114
+
2115
+ // globally unique identifiers
2116
+ var GUID = 0;
2117
+ // internal store of all Outlayer intances
2118
+ var instances = {};
2119
+
2120
+
2121
+ /**
2122
+ * @param {Element, String} element
2123
+ * @param {Object} options
2124
+ * @constructor
2125
+ */
2126
+ function Outlayer( element, options ) {
2127
+ var queryElement = utils.getQueryElement( element );
2128
+ if ( !queryElement ) {
2129
+ if ( console ) {
2130
+ console.error( 'Bad element for ' + this.constructor.namespace +
2131
+ ': ' + ( queryElement || element ) );
2132
+ }
2133
+ return;
2134
+ }
2135
+ this.element = queryElement;
2136
+ // add jQuery
2137
+ if ( jQuery ) {
2138
+ this.$element = jQuery( this.element );
2139
+ }
2140
+
2141
+ // options
2142
+ this.options = utils.extend( {}, this.constructor.defaults );
2143
+ this.option( options );
2144
+
2145
+ // add id for Outlayer.getFromElement
2146
+ var id = ++GUID;
2147
+ this.element.outlayerGUID = id; // expando
2148
+ instances[ id ] = this; // associate via id
2149
+
2150
+ // kick it off
2151
+ this._create();
2152
+
2153
+ if ( this.options.isInitLayout ) {
2154
+ this.layout();
2155
+ }
2156
+ }
2157
+
2158
+ // settings are for internal use only
2159
+ Outlayer.namespace = 'outlayer';
2160
+ Outlayer.Item = Item;
2161
+
2162
+ // default options
2163
+ Outlayer.defaults = {
2164
+ containerStyle: {
2165
+ position: 'relative'
2166
+ },
2167
+ isInitLayout: true,
2168
+ isOriginLeft: true,
2169
+ isOriginTop: true,
2170
+ isResizeBound: true,
2171
+ isResizingContainer: true,
2172
+ // item options
2173
+ transitionDuration: '0.4s',
2174
+ hiddenStyle: {
2175
+ opacity: 0,
2176
+ transform: 'scale(0.001)'
2177
+ },
2178
+ visibleStyle: {
2179
+ opacity: 1,
2180
+ transform: 'scale(1)'
2181
+ }
2182
+ };
2183
+
2184
+ // inherit EventEmitter
2185
+ utils.extend( Outlayer.prototype, EventEmitter.prototype );
2186
+
2187
+ /**
2188
+ * set options
2189
+ * @param {Object} opts
2190
+ */
2191
+ Outlayer.prototype.option = function( opts ) {
2192
+ utils.extend( this.options, opts );
2193
+ };
2194
+
2195
+ Outlayer.prototype._create = function() {
2196
+ // get items from children
2197
+ this.reloadItems();
2198
+ // elements that affect layout, but are not laid out
2199
+ this.stamps = [];
2200
+ this.stamp( this.options.stamp );
2201
+ // set container style
2202
+ utils.extend( this.element.style, this.options.containerStyle );
2203
+
2204
+ // bind resize method
2205
+ if ( this.options.isResizeBound ) {
2206
+ this.bindResize();
2207
+ }
2208
+ };
2209
+
2210
+ // goes through all children again and gets bricks in proper order
2211
+ Outlayer.prototype.reloadItems = function() {
2212
+ // collection of item elements
2213
+ this.items = this._itemize( this.element.children );
2214
+ };
2215
+
2216
+
2217
+ /**
2218
+ * turn elements into Outlayer.Items to be used in layout
2219
+ * @param {Array or NodeList or HTMLElement} elems
2220
+ * @returns {Array} items - collection of new Outlayer Items
2221
+ */
2222
+ Outlayer.prototype._itemize = function( elems ) {
2223
+
2224
+ var itemElems = this._filterFindItemElements( elems );
2225
+ var Item = this.constructor.Item;
2226
+
2227
+ // create new Outlayer Items for collection
2228
+ var items = [];
2229
+ for ( var i=0, len = itemElems.length; i < len; i++ ) {
2230
+ var elem = itemElems[i];
2231
+ var item = new Item( elem, this );
2232
+ items.push( item );
2233
+ }
2234
+
2235
+ return items;
2236
+ };
2237
+
2238
+ /**
2239
+ * get item elements to be used in layout
2240
+ * @param {Array or NodeList or HTMLElement} elems
2241
+ * @returns {Array} items - item elements
2242
+ */
2243
+ Outlayer.prototype._filterFindItemElements = function( elems ) {
2244
+ return utils.filterFindElements( elems, this.options.itemSelector );
2245
+ };
2246
+
2247
+ /**
2248
+ * getter method for getting item elements
2249
+ * @returns {Array} elems - collection of item elements
2250
+ */
2251
+ Outlayer.prototype.getItemElements = function() {
2252
+ var elems = [];
2253
+ for ( var i=0, len = this.items.length; i < len; i++ ) {
2254
+ elems.push( this.items[i].element );
2255
+ }
2256
+ return elems;
2257
+ };
2258
+
2259
+ // ----- init & layout ----- //
2260
+
2261
+ /**
2262
+ * lays out all items
2263
+ */
2264
+ Outlayer.prototype.layout = function() {
2265
+ this._resetLayout();
2266
+ this._manageStamps();
2267
+
2268
+ // don't animate first layout
2269
+ var isInstant = this.options.isLayoutInstant !== undefined ?
2270
+ this.options.isLayoutInstant : !this._isLayoutInited;
2271
+ this.layoutItems( this.items, isInstant );
2272
+
2273
+ // flag for initalized
2274
+ this._isLayoutInited = true;
2275
+ };
2276
+
2277
+ // _init is alias for layout
2278
+ Outlayer.prototype._init = Outlayer.prototype.layout;
2279
+
2280
+ /**
2281
+ * logic before any new layout
2282
+ */
2283
+ Outlayer.prototype._resetLayout = function() {
2284
+ this.getSize();
2285
+ };
2286
+
2287
+
2288
+ Outlayer.prototype.getSize = function() {
2289
+ this.size = getSize( this.element );
2290
+ };
2291
+
2292
+ /**
2293
+ * get measurement from option, for columnWidth, rowHeight, gutter
2294
+ * if option is String -> get element from selector string, & get size of element
2295
+ * if option is Element -> get size of element
2296
+ * else use option as a number
2297
+ *
2298
+ * @param {String} measurement
2299
+ * @param {String} size - width or height
2300
+ * @private
2301
+ */
2302
+ Outlayer.prototype._getMeasurement = function( measurement, size ) {
2303
+ var option = this.options[ measurement ];
2304
+ var elem;
2305
+ if ( !option ) {
2306
+ // default to 0
2307
+ this[ measurement ] = 0;
2308
+ } else {
2309
+ // use option as an element
2310
+ if ( typeof option === 'string' ) {
2311
+ elem = this.element.querySelector( option );
2312
+ } else if ( utils.isElement( option ) ) {
2313
+ elem = option;
2314
+ }
2315
+ // use size of element, if element
2316
+ this[ measurement ] = elem ? getSize( elem )[ size ] : option;
2317
+ }
2318
+ };
2319
+
2320
+ /**
2321
+ * layout a collection of item elements
2322
+ * @api public
2323
+ */
2324
+ Outlayer.prototype.layoutItems = function( items, isInstant ) {
2325
+ items = this._getItemsForLayout( items );
2326
+
2327
+ this._layoutItems( items, isInstant );
2328
+
2329
+ this._postLayout();
2330
+ };
2331
+
2332
+ /**
2333
+ * get the items to be laid out
2334
+ * you may want to skip over some items
2335
+ * @param {Array} items
2336
+ * @returns {Array} items
2337
+ */
2338
+ Outlayer.prototype._getItemsForLayout = function( items ) {
2339
+ var layoutItems = [];
2340
+ for ( var i=0, len = items.length; i < len; i++ ) {
2341
+ var item = items[i];
2342
+ if ( !item.isIgnored ) {
2343
+ layoutItems.push( item );
2344
+ }
2345
+ }
2346
+ return layoutItems;
2347
+ };
2348
+
2349
+ /**
2350
+ * layout items
2351
+ * @param {Array} items
2352
+ * @param {Boolean} isInstant
2353
+ */
2354
+ Outlayer.prototype._layoutItems = function( items, isInstant ) {
2355
+ this._emitCompleteOnItems( 'layout', items );
2356
+
2357
+ if ( !items || !items.length ) {
2358
+ // no items, emit event with empty array
2359
+ return;
2360
+ }
2361
+
2362
+ var queue = [];
2363
+
2364
+ for ( var i=0, len = items.length; i < len; i++ ) {
2365
+ var item = items[i];
2366
+ // get x/y object from method
2367
+ var position = this._getItemLayoutPosition( item );
2368
+ // enqueue
2369
+ position.item = item;
2370
+ position.isInstant = isInstant || item.isLayoutInstant;
2371
+ queue.push( position );
2372
+ }
2373
+
2374
+ this._processLayoutQueue( queue );
2375
+ };
2376
+
2377
+ /**
2378
+ * get item layout position
2379
+ * @param {Outlayer.Item} item
2380
+ * @returns {Object} x and y position
2381
+ */
2382
+ Outlayer.prototype._getItemLayoutPosition = function( /* item */ ) {
2383
+ return {
2384
+ x: 0,
2385
+ y: 0
2386
+ };
2387
+ };
2388
+
2389
+ /**
2390
+ * iterate over array and position each item
2391
+ * Reason being - separating this logic prevents 'layout invalidation'
2392
+ * thx @paul_irish
2393
+ * @param {Array} queue
2394
+ */
2395
+ Outlayer.prototype._processLayoutQueue = function( queue ) {
2396
+ for ( var i=0, len = queue.length; i < len; i++ ) {
2397
+ var obj = queue[i];
2398
+ this._positionItem( obj.item, obj.x, obj.y, obj.isInstant );
2399
+ }
2400
+ };
2401
+
2402
+ /**
2403
+ * Sets position of item in DOM
2404
+ * @param {Outlayer.Item} item
2405
+ * @param {Number} x - horizontal position
2406
+ * @param {Number} y - vertical position
2407
+ * @param {Boolean} isInstant - disables transitions
2408
+ */
2409
+ Outlayer.prototype._positionItem = function( item, x, y, isInstant ) {
2410
+ if ( isInstant ) {
2411
+ // if not transition, just set CSS
2412
+ item.goTo( x, y );
2413
+ } else {
2414
+ item.moveTo( x, y );
2415
+ }
2416
+ };
2417
+
2418
+ /**
2419
+ * Any logic you want to do after each layout,
2420
+ * i.e. size the container
2421
+ */
2422
+ Outlayer.prototype._postLayout = function() {
2423
+ this.resizeContainer();
2424
+ };
2425
+
2426
+ Outlayer.prototype.resizeContainer = function() {
2427
+ if ( !this.options.isResizingContainer ) {
2428
+ return;
2429
+ }
2430
+ var size = this._getContainerSize();
2431
+ if ( size ) {
2432
+ this._setContainerMeasure( size.width, true );
2433
+ this._setContainerMeasure( size.height, false );
2434
+ }
2435
+ };
2436
+
2437
+ /**
2438
+ * Sets width or height of container if returned
2439
+ * @returns {Object} size
2440
+ * @param {Number} width
2441
+ * @param {Number} height
2442
+ */
2443
+ Outlayer.prototype._getContainerSize = noop;
2444
+
2445
+ /**
2446
+ * @param {Number} measure - size of width or height
2447
+ * @param {Boolean} isWidth
2448
+ */
2449
+ Outlayer.prototype._setContainerMeasure = function( measure, isWidth ) {
2450
+ if ( measure === undefined ) {
2451
+ return;
2452
+ }
2453
+
2454
+ var elemSize = this.size;
2455
+ // add padding and border width if border box
2456
+ if ( elemSize.isBorderBox ) {
2457
+ measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
2458
+ elemSize.borderLeftWidth + elemSize.borderRightWidth :
2459
+ elemSize.paddingBottom + elemSize.paddingTop +
2460
+ elemSize.borderTopWidth + elemSize.borderBottomWidth;
2461
+ }
2462
+
2463
+ measure = Math.max( measure, 0 );
2464
+ this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
2465
+ };
2466
+
2467
+ /**
2468
+ * emit eventComplete on a collection of items events
2469
+ * @param {String} eventName
2470
+ * @param {Array} items - Outlayer.Items
2471
+ */
2472
+ Outlayer.prototype._emitCompleteOnItems = function( eventName, items ) {
2473
+ var _this = this;
2474
+ function onComplete() {
2475
+ _this.dispatchEvent( eventName + 'Complete', null, [ items ] );
2476
+ }
2477
+
2478
+ var count = items.length;
2479
+ if ( !items || !count ) {
2480
+ onComplete();
2481
+ return;
2482
+ }
2483
+
2484
+ var doneCount = 0;
2485
+ function tick() {
2486
+ doneCount++;
2487
+ if ( doneCount === count ) {
2488
+ onComplete();
2489
+ }
2490
+ }
2491
+
2492
+ // bind callback
2493
+ for ( var i=0, len = items.length; i < len; i++ ) {
2494
+ var item = items[i];
2495
+ item.once( eventName, tick );
2496
+ }
2497
+ };
2498
+
2499
+ /**
2500
+ * emits events via eventEmitter and jQuery events
2501
+ * @param {String} type - name of event
2502
+ * @param {Event} event - original event
2503
+ * @param {Array} args - extra arguments
2504
+ */
2505
+ Outlayer.prototype.dispatchEvent = function( type, event, args ) {
2506
+ // add original event to arguments
2507
+ var emitArgs = event ? [ event ].concat( args ) : args;
2508
+ this.emitEvent( type, emitArgs );
2509
+
2510
+ if ( jQuery ) {
2511
+ // set this.$element
2512
+ this.$element = this.$element || jQuery( this.element );
2513
+ if ( event ) {
2514
+ // create jQuery event
2515
+ var $event = jQuery.Event( event );
2516
+ $event.type = type;
2517
+ this.$element.trigger( $event, args );
2518
+ } else {
2519
+ // just trigger with type if no event available
2520
+ this.$element.trigger( type, args );
2521
+ }
2522
+ }
2523
+ };
2524
+
2525
+ // -------------------------- ignore & stamps -------------------------- //
2526
+
2527
+
2528
+ /**
2529
+ * keep item in collection, but do not lay it out
2530
+ * ignored items do not get skipped in layout
2531
+ * @param {Element} elem
2532
+ */
2533
+ Outlayer.prototype.ignore = function( elem ) {
2534
+ var item = this.getItem( elem );
2535
+ if ( item ) {
2536
+ item.isIgnored = true;
2537
+ }
2538
+ };
2539
+
2540
+ /**
2541
+ * return item to layout collection
2542
+ * @param {Element} elem
2543
+ */
2544
+ Outlayer.prototype.unignore = function( elem ) {
2545
+ var item = this.getItem( elem );
2546
+ if ( item ) {
2547
+ delete item.isIgnored;
2548
+ }
2549
+ };
2550
+
2551
+ /**
2552
+ * adds elements to stamps
2553
+ * @param {NodeList, Array, Element, or String} elems
2554
+ */
2555
+ Outlayer.prototype.stamp = function( elems ) {
2556
+ elems = this._find( elems );
2557
+ if ( !elems ) {
2558
+ return;
2559
+ }
2560
+
2561
+ this.stamps = this.stamps.concat( elems );
2562
+ // ignore
2563
+ for ( var i=0, len = elems.length; i < len; i++ ) {
2564
+ var elem = elems[i];
2565
+ this.ignore( elem );
2566
+ }
2567
+ };
2568
+
2569
+ /**
2570
+ * removes elements to stamps
2571
+ * @param {NodeList, Array, or Element} elems
2572
+ */
2573
+ Outlayer.prototype.unstamp = function( elems ) {
2574
+ elems = this._find( elems );
2575
+ if ( !elems ){
2576
+ return;
2577
+ }
2578
+
2579
+ for ( var i=0, len = elems.length; i < len; i++ ) {
2580
+ var elem = elems[i];
2581
+ // filter out removed stamp elements
2582
+ utils.removeFrom( this.stamps, elem );
2583
+ this.unignore( elem );
2584
+ }
2585
+
2586
+ };
2587
+
2588
+ /**
2589
+ * finds child elements
2590
+ * @param {NodeList, Array, Element, or String} elems
2591
+ * @returns {Array} elems
2592
+ */
2593
+ Outlayer.prototype._find = function( elems ) {
2594
+ if ( !elems ) {
2595
+ return;
2596
+ }
2597
+ // if string, use argument as selector string
2598
+ if ( typeof elems === 'string' ) {
2599
+ elems = this.element.querySelectorAll( elems );
2600
+ }
2601
+ elems = utils.makeArray( elems );
2602
+ return elems;
2603
+ };
2604
+
2605
+ Outlayer.prototype._manageStamps = function() {
2606
+ if ( !this.stamps || !this.stamps.length ) {
2607
+ return;
2608
+ }
2609
+
2610
+ this._getBoundingRect();
2611
+
2612
+ for ( var i=0, len = this.stamps.length; i < len; i++ ) {
2613
+ var stamp = this.stamps[i];
2614
+ this._manageStamp( stamp );
2615
+ }
2616
+ };
2617
+
2618
+ // update boundingLeft / Top
2619
+ Outlayer.prototype._getBoundingRect = function() {
2620
+ // get bounding rect for container element
2621
+ var boundingRect = this.element.getBoundingClientRect();
2622
+ var size = this.size;
2623
+ this._boundingRect = {
2624
+ left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
2625
+ top: boundingRect.top + size.paddingTop + size.borderTopWidth,
2626
+ right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
2627
+ bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
2628
+ };
2629
+ };
2630
+
2631
+ /**
2632
+ * @param {Element} stamp
2633
+ **/
2634
+ Outlayer.prototype._manageStamp = noop;
2635
+
2636
+ /**
2637
+ * get x/y position of element relative to container element
2638
+ * @param {Element} elem
2639
+ * @returns {Object} offset - has left, top, right, bottom
2640
+ */
2641
+ Outlayer.prototype._getElementOffset = function( elem ) {
2642
+ var boundingRect = elem.getBoundingClientRect();
2643
+ var thisRect = this._boundingRect;
2644
+ var size = getSize( elem );
2645
+ var offset = {
2646
+ left: boundingRect.left - thisRect.left - size.marginLeft,
2647
+ top: boundingRect.top - thisRect.top - size.marginTop,
2648
+ right: thisRect.right - boundingRect.right - size.marginRight,
2649
+ bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
2650
+ };
2651
+ return offset;
2652
+ };
2653
+
2654
+ // -------------------------- resize -------------------------- //
2655
+
2656
+ // enable event handlers for listeners
2657
+ // i.e. resize -> onresize
2658
+ Outlayer.prototype.handleEvent = function( event ) {
2659
+ var method = 'on' + event.type;
2660
+ if ( this[ method ] ) {
2661
+ this[ method ]( event );
2662
+ }
2663
+ };
2664
+
2665
+ /**
2666
+ * Bind layout to window resizing
2667
+ */
2668
+ Outlayer.prototype.bindResize = function() {
2669
+ // bind just one listener
2670
+ if ( this.isResizeBound ) {
2671
+ return;
2672
+ }
2673
+ eventie.bind( window, 'resize', this );
2674
+ this.isResizeBound = true;
2675
+ };
2676
+
2677
+ /**
2678
+ * Unbind layout to window resizing
2679
+ */
2680
+ Outlayer.prototype.unbindResize = function() {
2681
+ if ( this.isResizeBound ) {
2682
+ eventie.unbind( window, 'resize', this );
2683
+ }
2684
+ this.isResizeBound = false;
2685
+ };
2686
+
2687
+ // original debounce by John Hann
2688
+ // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
2689
+
2690
+ // this fires every resize
2691
+ Outlayer.prototype.onresize = function() {
2692
+ if ( this.resizeTimeout ) {
2693
+ clearTimeout( this.resizeTimeout );
2694
+ }
2695
+
2696
+ var _this = this;
2697
+ function delayed() {
2698
+ _this.resize();
2699
+ delete _this.resizeTimeout;
2700
+ }
2701
+
2702
+ this.resizeTimeout = setTimeout( delayed, 100 );
2703
+ };
2704
+
2705
+ // debounced, layout on resize
2706
+ Outlayer.prototype.resize = function() {
2707
+ // don't trigger if size did not change
2708
+ // or if resize was unbound. See #9
2709
+ if ( !this.isResizeBound || !this.needsResizeLayout() ) {
2710
+ return;
2711
+ }
2712
+
2713
+ this.layout();
2714
+ };
2715
+
2716
+ /**
2717
+ * check if layout is needed post layout
2718
+ * @returns Boolean
2719
+ */
2720
+ Outlayer.prototype.needsResizeLayout = function() {
2721
+ var size = getSize( this.element );
2722
+ // check that this.size and size are there
2723
+ // IE8 triggers resize on body size change, so they might not be
2724
+ var hasSizes = this.size && size;
2725
+ return hasSizes && size.innerWidth !== this.size.innerWidth;
2726
+ };
2727
+
2728
+ // -------------------------- methods -------------------------- //
2729
+
2730
+ /**
2731
+ * add items to Outlayer instance
2732
+ * @param {Array or NodeList or Element} elems
2733
+ * @returns {Array} items - Outlayer.Items
2734
+ **/
2735
+ Outlayer.prototype.addItems = function( elems ) {
2736
+ var items = this._itemize( elems );
2737
+ // add items to collection
2738
+ if ( items.length ) {
2739
+ this.items = this.items.concat( items );
2740
+ }
2741
+ return items;
2742
+ };
2743
+
2744
+ /**
2745
+ * Layout newly-appended item elements
2746
+ * @param {Array or NodeList or Element} elems
2747
+ */
2748
+ Outlayer.prototype.appended = function( elems ) {
2749
+ var items = this.addItems( elems );
2750
+ if ( !items.length ) {
2751
+ return;
2752
+ }
2753
+ // layout and reveal just the new items
2754
+ this.layoutItems( items, true );
2755
+ this.reveal( items );
2756
+ };
2757
+
2758
+ /**
2759
+ * Layout prepended elements
2760
+ * @param {Array or NodeList or Element} elems
2761
+ */
2762
+ Outlayer.prototype.prepended = function( elems ) {
2763
+ var items = this._itemize( elems );
2764
+ if ( !items.length ) {
2765
+ return;
2766
+ }
2767
+ // add items to beginning of collection
2768
+ var previousItems = this.items.slice(0);
2769
+ this.items = items.concat( previousItems );
2770
+ // start new layout
2771
+ this._resetLayout();
2772
+ this._manageStamps();
2773
+ // layout new stuff without transition
2774
+ this.layoutItems( items, true );
2775
+ this.reveal( items );
2776
+ // layout previous items
2777
+ this.layoutItems( previousItems );
2778
+ };
2779
+
2780
+ /**
2781
+ * reveal a collection of items
2782
+ * @param {Array of Outlayer.Items} items
2783
+ */
2784
+ Outlayer.prototype.reveal = function( items ) {
2785
+ this._emitCompleteOnItems( 'reveal', items );
2786
+
2787
+ var len = items && items.length;
2788
+ for ( var i=0; len && i < len; i++ ) {
2789
+ var item = items[i];
2790
+ item.reveal();
2791
+ }
2792
+ };
2793
+
2794
+ /**
2795
+ * hide a collection of items
2796
+ * @param {Array of Outlayer.Items} items
2797
+ */
2798
+ Outlayer.prototype.hide = function( items ) {
2799
+ this._emitCompleteOnItems( 'hide', items );
2800
+
2801
+ var len = items && items.length;
2802
+ for ( var i=0; len && i < len; i++ ) {
2803
+ var item = items[i];
2804
+ item.hide();
2805
+ }
2806
+ };
2807
+
2808
+ /**
2809
+ * reveal item elements
2810
+ * @param {Array}, {Element}, {NodeList} items
2811
+ */
2812
+ Outlayer.prototype.revealItemElements = function( elems ) {
2813
+ var items = this.getItems( elems );
2814
+ this.reveal( items );
2815
+ };
2816
+
2817
+ /**
2818
+ * hide item elements
2819
+ * @param {Array}, {Element}, {NodeList} items
2820
+ */
2821
+ Outlayer.prototype.hideItemElements = function( elems ) {
2822
+ var items = this.getItems( elems );
2823
+ this.hide( items );
2824
+ };
2825
+
2826
+ /**
2827
+ * get Outlayer.Item, given an Element
2828
+ * @param {Element} elem
2829
+ * @param {Function} callback
2830
+ * @returns {Outlayer.Item} item
2831
+ */
2832
+ Outlayer.prototype.getItem = function( elem ) {
2833
+ // loop through items to get the one that matches
2834
+ for ( var i=0, len = this.items.length; i < len; i++ ) {
2835
+ var item = this.items[i];
2836
+ if ( item.element === elem ) {
2837
+ // return item
2838
+ return item;
2839
+ }
2840
+ }
2841
+ };
2842
+
2843
+ /**
2844
+ * get collection of Outlayer.Items, given Elements
2845
+ * @param {Array} elems
2846
+ * @returns {Array} items - Outlayer.Items
2847
+ */
2848
+ Outlayer.prototype.getItems = function( elems ) {
2849
+ elems = utils.makeArray( elems );
2850
+ var items = [];
2851
+ for ( var i=0, len = elems.length; i < len; i++ ) {
2852
+ var elem = elems[i];
2853
+ var item = this.getItem( elem );
2854
+ if ( item ) {
2855
+ items.push( item );
2856
+ }
2857
+ }
2858
+
2859
+ return items;
2860
+ };
2861
+
2862
+ /**
2863
+ * remove element(s) from instance and DOM
2864
+ * @param {Array or NodeList or Element} elems
2865
+ */
2866
+ Outlayer.prototype.remove = function( elems ) {
2867
+ var removeItems = this.getItems( elems );
2868
+
2869
+ this._emitCompleteOnItems( 'remove', removeItems );
2870
+
2871
+ // bail if no items to remove
2872
+ if ( !removeItems || !removeItems.length ) {
2873
+ return;
2874
+ }
2875
+
2876
+ for ( var i=0, len = removeItems.length; i < len; i++ ) {
2877
+ var item = removeItems[i];
2878
+ item.remove();
2879
+ // remove item from collection
2880
+ utils.removeFrom( this.items, item );
2881
+ }
2882
+ };
2883
+
2884
+ // ----- destroy ----- //
2885
+
2886
+ // remove and disable Outlayer instance
2887
+ Outlayer.prototype.destroy = function() {
2888
+ // clean up dynamic styles
2889
+ var style = this.element.style;
2890
+ style.height = '';
2891
+ style.position = '';
2892
+ style.width = '';
2893
+ // destroy items
2894
+ for ( var i=0, len = this.items.length; i < len; i++ ) {
2895
+ var item = this.items[i];
2896
+ item.destroy();
2897
+ }
2898
+
2899
+ this.unbindResize();
2900
+
2901
+ var id = this.element.outlayerGUID;
2902
+ delete instances[ id ]; // remove reference to instance by id
2903
+ delete this.element.outlayerGUID;
2904
+ // remove data for jQuery
2905
+ if ( jQuery ) {
2906
+ jQuery.removeData( this.element, this.constructor.namespace );
2907
+ }
2908
+
2909
+ };
2910
+
2911
+ // -------------------------- data -------------------------- //
2912
+
2913
+ /**
2914
+ * get Outlayer instance from element
2915
+ * @param {Element} elem
2916
+ * @returns {Outlayer}
2917
+ */
2918
+ Outlayer.data = function( elem ) {
2919
+ elem = utils.getQueryElement( elem );
2920
+ var id = elem && elem.outlayerGUID;
2921
+ return id && instances[ id ];
2922
+ };
2923
+
2924
+
2925
+ // -------------------------- create Outlayer class -------------------------- //
2926
+
2927
+ /**
2928
+ * create a layout class
2929
+ * @param {String} namespace
2930
+ */
2931
+ Outlayer.create = function( namespace, options ) {
2932
+ // sub-class Outlayer
2933
+ function Layout() {
2934
+ Outlayer.apply( this, arguments );
2935
+ }
2936
+ // inherit Outlayer prototype, use Object.create if there
2937
+ if ( Object.create ) {
2938
+ Layout.prototype = Object.create( Outlayer.prototype );
2939
+ } else {
2940
+ utils.extend( Layout.prototype, Outlayer.prototype );
2941
+ }
2942
+ // set contructor, used for namespace and Item
2943
+ Layout.prototype.constructor = Layout;
2944
+
2945
+ Layout.defaults = utils.extend( {}, Outlayer.defaults );
2946
+ // apply new options
2947
+ utils.extend( Layout.defaults, options );
2948
+ // keep prototype.settings for backwards compatibility (Packery v1.2.0)
2949
+ Layout.prototype.settings = {};
2950
+
2951
+ Layout.namespace = namespace;
2952
+
2953
+ Layout.data = Outlayer.data;
2954
+
2955
+ // sub-class Item
2956
+ Layout.Item = function LayoutItem() {
2957
+ Item.apply( this, arguments );
2958
+ };
2959
+
2960
+ Layout.Item.prototype = new Item();
2961
+
2962
+ // -------------------------- declarative -------------------------- //
2963
+
2964
+ utils.htmlInit( Layout, namespace );
2965
+
2966
+ // -------------------------- jQuery bridge -------------------------- //
2967
+
2968
+ // make into jQuery plugin
2969
+ if ( jQuery && jQuery.bridget ) {
2970
+ jQuery.bridget( namespace, Layout );
2971
+ }
2972
+
2973
+ return Layout;
2974
+ };
2975
+
2976
+ // ----- fin ----- //
2977
+
2978
+ // back in global
2979
+ Outlayer.Item = Item;
2980
+
2981
+ return Outlayer;
2982
+
2983
+ }));
2984
+
2985
+
2986
+ /**
2987
+ * Isotope Item
2988
+ **/
2989
+
2990
+ ( function( window, factory ) {
2991
+ 'use strict';
2992
+ // universal module definition
2993
+ if ( typeof define == 'function' && define.amd ) {
2994
+ // AMD
2995
+ define( 'isotope/js/item',[
2996
+ 'outlayer/outlayer'
2997
+ ],
2998
+ factory );
2999
+ } else if ( typeof exports == 'object' ) {
3000
+ // CommonJS
3001
+ module.exports = factory(
3002
+ require('outlayer')
3003
+ );
3004
+ } else {
3005
+ // browser global
3006
+ window.Isotope = window.Isotope || {};
3007
+ window.Isotope.Item = factory(
3008
+ window.Outlayer
3009
+ );
3010
+ }
3011
+
3012
+ }( window, function factory( Outlayer ) {
3013
+ 'use strict';
3014
+
3015
+ // -------------------------- Item -------------------------- //
3016
+
3017
+ // sub-class Outlayer Item
3018
+ function Item() {
3019
+ Outlayer.Item.apply( this, arguments );
3020
+ }
3021
+
3022
+ Item.prototype = new Outlayer.Item();
3023
+
3024
+ Item.prototype._create = function() {
3025
+ // assign id, used for original-order sorting
3026
+ this.id = this.layout.itemGUID++;
3027
+ Outlayer.Item.prototype._create.call( this );
3028
+ this.sortData = {};
3029
+ };
3030
+
3031
+ Item.prototype.updateSortData = function() {
3032
+ if ( this.isIgnored ) {
3033
+ return;
3034
+ }
3035
+ // default sorters
3036
+ this.sortData.id = this.id;
3037
+ // for backward compatibility
3038
+ this.sortData['original-order'] = this.id;
3039
+ this.sortData.random = Math.random();
3040
+ // go thru getSortData obj and apply the sorters
3041
+ var getSortData = this.layout.options.getSortData;
3042
+ var sorters = this.layout._sorters;
3043
+ for ( var key in getSortData ) {
3044
+ var sorter = sorters[ key ];
3045
+ this.sortData[ key ] = sorter( this.element, this );
3046
+ }
3047
+ };
3048
+
3049
+ var _destroy = Item.prototype.destroy;
3050
+ Item.prototype.destroy = function() {
3051
+ // call super
3052
+ _destroy.apply( this, arguments );
3053
+ // reset display, #741
3054
+ this.css({
3055
+ display: ''
3056
+ });
3057
+ };
3058
+
3059
+ return Item;
3060
+
3061
+ }));
3062
+
3063
+ /**
3064
+ * Isotope LayoutMode
3065
+ */
3066
+
3067
+ ( function( window, factory ) {
3068
+ 'use strict';
3069
+ // universal module definition
3070
+
3071
+ if ( typeof define == 'function' && define.amd ) {
3072
+ // AMD
3073
+ define( 'isotope/js/layout-mode',[
3074
+ 'get-size/get-size',
3075
+ 'outlayer/outlayer'
3076
+ ],
3077
+ factory );
3078
+ } else if ( typeof exports == 'object' ) {
3079
+ // CommonJS
3080
+ module.exports = factory(
3081
+ require('get-size'),
3082
+ require('outlayer')
3083
+ );
3084
+ } else {
3085
+ // browser global
3086
+ window.Isotope = window.Isotope || {};
3087
+ window.Isotope.LayoutMode = factory(
3088
+ window.getSize,
3089
+ window.Outlayer
3090
+ );
3091
+ }
3092
+
3093
+ }( window, function factory( getSize, Outlayer ) {
3094
+ 'use strict';
3095
+
3096
+ // layout mode class
3097
+ function LayoutMode( isotope ) {
3098
+ this.isotope = isotope;
3099
+ // link properties
3100
+ if ( isotope ) {
3101
+ this.options = isotope.options[ this.namespace ];
3102
+ this.element = isotope.element;
3103
+ this.items = isotope.filteredItems;
3104
+ this.size = isotope.size;
3105
+ }
3106
+ }
3107
+
3108
+ /**
3109
+ * some methods should just defer to default Outlayer method
3110
+ * and reference the Isotope instance as `this`
3111
+ **/
3112
+ ( function() {
3113
+ var facadeMethods = [
3114
+ '_resetLayout',
3115
+ '_getItemLayoutPosition',
3116
+ '_manageStamp',
3117
+ '_getContainerSize',
3118
+ '_getElementOffset',
3119
+ 'needsResizeLayout'
3120
+ ];
3121
+
3122
+ for ( var i=0, len = facadeMethods.length; i < len; i++ ) {
3123
+ var methodName = facadeMethods[i];
3124
+ LayoutMode.prototype[ methodName ] = getOutlayerMethod( methodName );
3125
+ }
3126
+
3127
+ function getOutlayerMethod( methodName ) {
3128
+ return function() {
3129
+ return Outlayer.prototype[ methodName ].apply( this.isotope, arguments );
3130
+ };
3131
+ }
3132
+ })();
3133
+
3134
+ // ----- ----- //
3135
+
3136
+ // for horizontal layout modes, check vertical size
3137
+ LayoutMode.prototype.needsVerticalResizeLayout = function() {
3138
+ // don't trigger if size did not change
3139
+ var size = getSize( this.isotope.element );
3140
+ // check that this.size and size are there
3141
+ // IE8 triggers resize on body size change, so they might not be
3142
+ var hasSizes = this.isotope.size && size;
3143
+ return hasSizes && size.innerHeight != this.isotope.size.innerHeight;
3144
+ };
3145
+
3146
+ // ----- measurements ----- //
3147
+
3148
+ LayoutMode.prototype._getMeasurement = function() {
3149
+ this.isotope._getMeasurement.apply( this, arguments );
3150
+ };
3151
+
3152
+ LayoutMode.prototype.getColumnWidth = function() {
3153
+ this.getSegmentSize( 'column', 'Width' );
3154
+ };
3155
+
3156
+ LayoutMode.prototype.getRowHeight = function() {
3157
+ this.getSegmentSize( 'row', 'Height' );
3158
+ };
3159
+
3160
+ /**
3161
+ * get columnWidth or rowHeight
3162
+ * segment: 'column' or 'row'
3163
+ * size 'Width' or 'Height'
3164
+ **/
3165
+ LayoutMode.prototype.getSegmentSize = function( segment, size ) {
3166
+ var segmentName = segment + size;
3167
+ var outerSize = 'outer' + size;
3168
+ // columnWidth / outerWidth // rowHeight / outerHeight
3169
+ this._getMeasurement( segmentName, outerSize );
3170
+ // got rowHeight or columnWidth, we can chill
3171
+ if ( this[ segmentName ] ) {
3172
+ return;
3173
+ }
3174
+ // fall back to item of first element
3175
+ var firstItemSize = this.getFirstItemSize();
3176
+ this[ segmentName ] = firstItemSize && firstItemSize[ outerSize ] ||
3177
+ // or size of container
3178
+ this.isotope.size[ 'inner' + size ];
3179
+ };
3180
+
3181
+ LayoutMode.prototype.getFirstItemSize = function() {
3182
+ var firstItem = this.isotope.filteredItems[0];
3183
+ return firstItem && firstItem.element && getSize( firstItem.element );
3184
+ };
3185
+
3186
+ // ----- methods that should reference isotope ----- //
3187
+
3188
+ LayoutMode.prototype.layout = function() {
3189
+ this.isotope.layout.apply( this.isotope, arguments );
3190
+ };
3191
+
3192
+ LayoutMode.prototype.getSize = function() {
3193
+ this.isotope.getSize();
3194
+ this.size = this.isotope.size;
3195
+ };
3196
+
3197
+ // -------------------------- create -------------------------- //
3198
+
3199
+ LayoutMode.modes = {};
3200
+
3201
+ LayoutMode.create = function( namespace, options ) {
3202
+
3203
+ function Mode() {
3204
+ LayoutMode.apply( this, arguments );
3205
+ }
3206
+
3207
+ Mode.prototype = new LayoutMode();
3208
+
3209
+ // default options
3210
+ if ( options ) {
3211
+ Mode.options = options;
3212
+ }
3213
+
3214
+ Mode.prototype.namespace = namespace;
3215
+ // register in Isotope
3216
+ LayoutMode.modes[ namespace ] = Mode;
3217
+
3218
+ return Mode;
3219
+ };
3220
+
3221
+ return LayoutMode;
3222
+
3223
+ }));
3224
+
3225
+ /*!
3226
+ * Masonry v3.3.1
3227
+ * Cascading grid layout library
3228
+ * http://masonry.desandro.com
3229
+ * MIT License
3230
+ * by David DeSandro
3231
+ */
3232
+
3233
+ ( function( window, factory ) {
3234
+ 'use strict';
3235
+ // universal module definition
3236
+ if ( typeof define === 'function' && define.amd ) {
3237
+ // AMD
3238
+ define( 'masonry/masonry',[
3239
+ 'outlayer/outlayer',
3240
+ 'get-size/get-size',
3241
+ 'fizzy-ui-utils/utils'
3242
+ ],
3243
+ factory );
3244
+ } else if ( typeof exports === 'object' ) {
3245
+ // CommonJS
3246
+ module.exports = factory(
3247
+ require('outlayer'),
3248
+ require('get-size'),
3249
+ require('fizzy-ui-utils')
3250
+ );
3251
+ } else {
3252
+ // browser global
3253
+ window.Masonry = factory(
3254
+ window.Outlayer,
3255
+ window.getSize,
3256
+ window.fizzyUIUtils
3257
+ );
3258
+ }
3259
+
3260
+ }( window, function factory( Outlayer, getSize, utils ) {
3261
+
3262
+
3263
+
3264
+ // -------------------------- masonryDefinition -------------------------- //
3265
+
3266
+ // create an Outlayer layout class
3267
+ var Masonry = Outlayer.create('masonry');
3268
+
3269
+ Masonry.prototype._resetLayout = function() {
3270
+ this.getSize();
3271
+ this._getMeasurement( 'columnWidth', 'outerWidth' );
3272
+ this._getMeasurement( 'gutter', 'outerWidth' );
3273
+ this.measureColumns();
3274
+
3275
+ // reset column Y
3276
+ var i = this.cols;
3277
+ this.colYs = [];
3278
+ while (i--) {
3279
+ this.colYs.push( 0 );
3280
+ }
3281
+
3282
+ this.maxY = 0;
3283
+ };
3284
+
3285
+ Masonry.prototype.measureColumns = function() {
3286
+ this.getContainerWidth();
3287
+ // if columnWidth is 0, default to outerWidth of first item
3288
+ if ( !this.columnWidth ) {
3289
+ var firstItem = this.items[0];
3290
+ var firstItemElem = firstItem && firstItem.element;
3291
+ // columnWidth fall back to item of first element
3292
+ this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth ||
3293
+ // if first elem has no width, default to size of container
3294
+ this.containerWidth;
3295
+ }
3296
+
3297
+ var columnWidth = this.columnWidth += this.gutter;
3298
+
3299
+ // calculate columns
3300
+ var containerWidth = this.containerWidth + this.gutter;
3301
+ var cols = containerWidth / columnWidth;
3302
+ // fix rounding errors, typically with gutters
3303
+ var excess = columnWidth - containerWidth % columnWidth;
3304
+ // if overshoot is less than a pixel, round up, otherwise floor it
3305
+ var mathMethod = excess && excess < 1 ? 'round' : 'floor';
3306
+ cols = Math[ mathMethod ]( cols );
3307
+ this.cols = Math.max( cols, 1 );
3308
+ };
3309
+
3310
+ Masonry.prototype.getContainerWidth = function() {
3311
+ // container is parent if fit width
3312
+ var container = this.options.isFitWidth ? this.element.parentNode : this.element;
3313
+ // check that this.size and size are there
3314
+ // IE8 triggers resize on body size change, so they might not be
3315
+ var size = getSize( container );
3316
+ this.containerWidth = size && size.innerWidth;
3317
+ };
3318
+
3319
+ Masonry.prototype._getItemLayoutPosition = function( item ) {
3320
+ item.getSize();
3321
+ // how many columns does this brick span
3322
+ var remainder = item.size.outerWidth % this.columnWidth;
3323
+ var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
3324
+ // round if off by 1 pixel, otherwise use ceil
3325
+ var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
3326
+ colSpan = Math.min( colSpan, this.cols );
3327
+
3328
+ var colGroup = this._getColGroup( colSpan );
3329
+ // get the minimum Y value from the columns
3330
+ var minimumY = Math.min.apply( Math, colGroup );
3331
+ var shortColIndex = utils.indexOf( colGroup, minimumY );
3332
+
3333
+ // position the brick
3334
+ var position = {
3335
+ x: this.columnWidth * shortColIndex,
3336
+ y: minimumY
3337
+ };
3338
+
3339
+ // apply setHeight to necessary columns
3340
+ var setHeight = minimumY + item.size.outerHeight;
3341
+ var setSpan = this.cols + 1 - colGroup.length;
3342
+ for ( var i = 0; i < setSpan; i++ ) {
3343
+ this.colYs[ shortColIndex + i ] = setHeight;
3344
+ }
3345
+
3346
+ return position;
3347
+ };
3348
+
3349
+ /**
3350
+ * @param {Number} colSpan - number of columns the element spans
3351
+ * @returns {Array} colGroup
3352
+ */
3353
+ Masonry.prototype._getColGroup = function( colSpan ) {
3354
+ if ( colSpan < 2 ) {
3355
+ // if brick spans only one column, use all the column Ys
3356
+ return this.colYs;
3357
+ }
3358
+
3359
+ var colGroup = [];
3360
+ // how many different places could this brick fit horizontally
3361
+ var groupCount = this.cols + 1 - colSpan;
3362
+ // for each group potential horizontal position
3363
+ for ( var i = 0; i < groupCount; i++ ) {
3364
+ // make an array of colY values for that one group
3365
+ var groupColYs = this.colYs.slice( i, i + colSpan );
3366
+ // and get the max value of the array
3367
+ colGroup[i] = Math.max.apply( Math, groupColYs );
3368
+ }
3369
+ return colGroup;
3370
+ };
3371
+
3372
+ Masonry.prototype._manageStamp = function( stamp ) {
3373
+ var stampSize = getSize( stamp );
3374
+ var offset = this._getElementOffset( stamp );
3375
+ // get the columns that this stamp affects
3376
+ var firstX = this.options.isOriginLeft ? offset.left : offset.right;
3377
+ var lastX = firstX + stampSize.outerWidth;
3378
+ var firstCol = Math.floor( firstX / this.columnWidth );
3379
+ firstCol = Math.max( 0, firstCol );
3380
+ var lastCol = Math.floor( lastX / this.columnWidth );
3381
+ // lastCol should not go over if multiple of columnWidth #425
3382
+ lastCol -= lastX % this.columnWidth ? 0 : 1;
3383
+ lastCol = Math.min( this.cols - 1, lastCol );
3384
+ // set colYs to bottom of the stamp
3385
+ var stampMaxY = ( this.options.isOriginTop ? offset.top : offset.bottom ) +
3386
+ stampSize.outerHeight;
3387
+ for ( var i = firstCol; i <= lastCol; i++ ) {
3388
+ this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
3389
+ }
3390
+ };
3391
+
3392
+ Masonry.prototype._getContainerSize = function() {
3393
+ this.maxY = Math.max.apply( Math, this.colYs );
3394
+ var size = {
3395
+ height: this.maxY
3396
+ };
3397
+
3398
+ if ( this.options.isFitWidth ) {
3399
+ size.width = this._getContainerFitWidth();
3400
+ }
3401
+
3402
+ return size;
3403
+ };
3404
+
3405
+ Masonry.prototype._getContainerFitWidth = function() {
3406
+ var unusedCols = 0;
3407
+ // count unused columns
3408
+ var i = this.cols;
3409
+ while ( --i ) {
3410
+ if ( this.colYs[i] !== 0 ) {
3411
+ break;
3412
+ }
3413
+ unusedCols++;
3414
+ }
3415
+ // fit container to columns that have been used
3416
+ return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
3417
+ };
3418
+
3419
+ Masonry.prototype.needsResizeLayout = function() {
3420
+ var previousWidth = this.containerWidth;
3421
+ this.getContainerWidth();
3422
+ return previousWidth !== this.containerWidth;
3423
+ };
3424
+
3425
+ return Masonry;
3426
+
3427
+ }));
3428
+
3429
+ /*!
3430
+ * Masonry layout mode
3431
+ * sub-classes Masonry
3432
+ * http://masonry.desandro.com
3433
+ */
3434
+
3435
+ ( function( window, factory ) {
3436
+ 'use strict';
3437
+ // universal module definition
3438
+ if ( typeof define == 'function' && define.amd ) {
3439
+ // AMD
3440
+ define( 'isotope/js/layout-modes/masonry',[
3441
+ '../layout-mode',
3442
+ 'masonry/masonry'
3443
+ ],
3444
+ factory );
3445
+ } else if ( typeof exports == 'object' ) {
3446
+ // CommonJS
3447
+ module.exports = factory(
3448
+ require('../layout-mode'),
3449
+ require('masonry-layout')
3450
+ );
3451
+ } else {
3452
+ // browser global
3453
+ factory(
3454
+ window.Isotope.LayoutMode,
3455
+ window.Masonry
3456
+ );
3457
+ }
3458
+
3459
+ }( window, function factory( LayoutMode, Masonry ) {
3460
+ 'use strict';
3461
+
3462
+ // -------------------------- helpers -------------------------- //
3463
+
3464
+ // extend objects
3465
+ function extend( a, b ) {
3466
+ for ( var prop in b ) {
3467
+ a[ prop ] = b[ prop ];
3468
+ }
3469
+ return a;
3470
+ }
3471
+
3472
+ // -------------------------- masonryDefinition -------------------------- //
3473
+
3474
+ // create an Outlayer layout class
3475
+ var MasonryMode = LayoutMode.create('masonry');
3476
+
3477
+ // save on to these methods
3478
+ var _getElementOffset = MasonryMode.prototype._getElementOffset;
3479
+ var layout = MasonryMode.prototype.layout;
3480
+ var _getMeasurement = MasonryMode.prototype._getMeasurement;
3481
+
3482
+ // sub-class Masonry
3483
+ extend( MasonryMode.prototype, Masonry.prototype );
3484
+
3485
+ // set back, as it was overwritten by Masonry
3486
+ MasonryMode.prototype._getElementOffset = _getElementOffset;
3487
+ MasonryMode.prototype.layout = layout;
3488
+ MasonryMode.prototype._getMeasurement = _getMeasurement;
3489
+
3490
+ var measureColumns = MasonryMode.prototype.measureColumns;
3491
+ MasonryMode.prototype.measureColumns = function() {
3492
+ // set items, used if measuring first item
3493
+ this.items = this.isotope.filteredItems;
3494
+ measureColumns.call( this );
3495
+ };
3496
+
3497
+ // HACK copy over isOriginLeft/Top options
3498
+ var _manageStamp = MasonryMode.prototype._manageStamp;
3499
+ MasonryMode.prototype._manageStamp = function() {
3500
+ this.options.isOriginLeft = this.isotope.options.isOriginLeft;
3501
+ this.options.isOriginTop = this.isotope.options.isOriginTop;
3502
+ _manageStamp.apply( this, arguments );
3503
+ };
3504
+
3505
+ return MasonryMode;
3506
+
3507
+ }));
3508
+
3509
+ /**
3510
+ * fitRows layout mode
3511
+ */
3512
+
3513
+ ( function( window, factory ) {
3514
+ 'use strict';
3515
+ // universal module definition
3516
+ if ( typeof define == 'function' && define.amd ) {
3517
+ // AMD
3518
+ define( 'isotope/js/layout-modes/fit-rows',[
3519
+ '../layout-mode'
3520
+ ],
3521
+ factory );
3522
+ } else if ( typeof exports == 'object' ) {
3523
+ // CommonJS
3524
+ module.exports = factory(
3525
+ require('../layout-mode')
3526
+ );
3527
+ } else {
3528
+ // browser global
3529
+ factory(
3530
+ window.Isotope.LayoutMode
3531
+ );
3532
+ }
3533
+
3534
+ }( window, function factory( LayoutMode ) {
3535
+ 'use strict';
3536
+
3537
+ var FitRows = LayoutMode.create('fitRows');
3538
+
3539
+ FitRows.prototype._resetLayout = function() {
3540
+ this.x = 0;
3541
+ this.y = 0;
3542
+ this.maxY = 0;
3543
+ this._getMeasurement( 'gutter', 'outerWidth' );
3544
+ };
3545
+
3546
+ FitRows.prototype._getItemLayoutPosition = function( item ) {
3547
+ item.getSize();
3548
+
3549
+ var itemWidth = item.size.outerWidth + this.gutter;
3550
+ // if this element cannot fit in the current row
3551
+ var containerWidth = this.isotope.size.innerWidth + this.gutter;
3552
+ if ( this.x !== 0 && itemWidth + this.x > containerWidth ) {
3553
+ this.x = 0;
3554
+ this.y = this.maxY;
3555
+ }
3556
+
3557
+ var position = {
3558
+ x: this.x,
3559
+ y: this.y
3560
+ };
3561
+
3562
+ this.maxY = Math.max( this.maxY, this.y + item.size.outerHeight );
3563
+ this.x += itemWidth;
3564
+
3565
+ return position;
3566
+ };
3567
+
3568
+ FitRows.prototype._getContainerSize = function() {
3569
+ return { height: this.maxY };
3570
+ };
3571
+
3572
+ return FitRows;
3573
+
3574
+ }));
3575
+
3576
+ /**
3577
+ * vertical layout mode
3578
+ */
3579
+
3580
+ ( function( window, factory ) {
3581
+ 'use strict';
3582
+ // universal module definition
3583
+ if ( typeof define == 'function' && define.amd ) {
3584
+ // AMD
3585
+ define( 'isotope/js/layout-modes/vertical',[
3586
+ '../layout-mode'
3587
+ ],
3588
+ factory );
3589
+ } else if ( typeof exports == 'object' ) {
3590
+ // CommonJS
3591
+ module.exports = factory(
3592
+ require('../layout-mode')
3593
+ );
3594
+ } else {
3595
+ // browser global
3596
+ factory(
3597
+ window.Isotope.LayoutMode
3598
+ );
3599
+ }
3600
+
3601
+ }( window, function factory( LayoutMode ) {
3602
+ 'use strict';
3603
+
3604
+ var Vertical = LayoutMode.create( 'vertical', {
3605
+ horizontalAlignment: 0
3606
+ });
3607
+
3608
+ Vertical.prototype._resetLayout = function() {
3609
+ this.y = 0;
3610
+ };
3611
+
3612
+ Vertical.prototype._getItemLayoutPosition = function( item ) {
3613
+ item.getSize();
3614
+ var x = ( this.isotope.size.innerWidth - item.size.outerWidth ) *
3615
+ this.options.horizontalAlignment;
3616
+ var y = this.y;
3617
+ this.y += item.size.outerHeight;
3618
+ return { x: x, y: y };
3619
+ };
3620
+
3621
+ Vertical.prototype._getContainerSize = function() {
3622
+ return { height: this.y };
3623
+ };
3624
+
3625
+ return Vertical;
3626
+
3627
+ }));
3628
+
3629
+ /*!
3630
+ * Isotope v2.2.2
3631
+ *
3632
+ * Licensed GPLv3 for open source use
3633
+ * or Isotope Commercial License for commercial use
3634
+ *
3635
+ * http://isotope.metafizzy.co
3636
+ * Copyright 2015 Metafizzy
3637
+ */
3638
+
3639
+ ( function( window, factory ) {
3640
+ 'use strict';
3641
+ // universal module definition
3642
+
3643
+ if ( typeof define == 'function' && define.amd ) {
3644
+ // AMD
3645
+ define( [
3646
+ 'outlayer/outlayer',
3647
+ 'get-size/get-size',
3648
+ 'matches-selector/matches-selector',
3649
+ 'fizzy-ui-utils/utils',
3650
+ 'isotope/js/item',
3651
+ 'isotope/js/layout-mode',
3652
+ // include default layout modes
3653
+ 'isotope/js/layout-modes/masonry',
3654
+ 'isotope/js/layout-modes/fit-rows',
3655
+ 'isotope/js/layout-modes/vertical'
3656
+ ],
3657
+ function( Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ) {
3658
+ return factory( window, Outlayer, getSize, matchesSelector, utils, Item, LayoutMode );
3659
+ });
3660
+ } else if ( typeof exports == 'object' ) {
3661
+ // CommonJS
3662
+ module.exports = factory(
3663
+ window,
3664
+ require('outlayer'),
3665
+ require('get-size'),
3666
+ require('desandro-matches-selector'),
3667
+ require('fizzy-ui-utils'),
3668
+ require('./item'),
3669
+ require('./layout-mode'),
3670
+ // include default layout modes
3671
+ require('./layout-modes/masonry'),
3672
+ require('./layout-modes/fit-rows'),
3673
+ require('./layout-modes/vertical')
3674
+ );
3675
+ } else {
3676
+ // browser global
3677
+ window.Isotope = factory(
3678
+ window,
3679
+ window.Outlayer,
3680
+ window.getSize,
3681
+ window.matchesSelector,
3682
+ window.fizzyUIUtils,
3683
+ window.Isotope.Item,
3684
+ window.Isotope.LayoutMode
3685
+ );
3686
+ }
3687
+
3688
+ }( window, function factory( window, Outlayer, getSize, matchesSelector, utils,
3689
+ Item, LayoutMode ) {
3690
+
3691
+
3692
+
3693
+ // -------------------------- vars -------------------------- //
3694
+
3695
+ var jQuery = window.jQuery;
3696
+
3697
+ // -------------------------- helpers -------------------------- //
3698
+
3699
+ var trim = String.prototype.trim ?
3700
+ function( str ) {
3701
+ return str.trim();
3702
+ } :
3703
+ function( str ) {
3704
+ return str.replace( /^\s+|\s+$/g, '' );
3705
+ };
3706
+
3707
+ var docElem = document.documentElement;
3708
+
3709
+ var getText = docElem.textContent ?
3710
+ function( elem ) {
3711
+ return elem.textContent;
3712
+ } :
3713
+ function( elem ) {
3714
+ return elem.innerText;
3715
+ };
3716
+
3717
+ // -------------------------- isotopeDefinition -------------------------- //
3718
+
3719
+ // create an Outlayer layout class
3720
+ var Isotope = Outlayer.create( 'isotope', {
3721
+ layoutMode: "masonry",
3722
+ isJQueryFiltering: true,
3723
+ sortAscending: true
3724
+ });
3725
+
3726
+ Isotope.Item = Item;
3727
+ Isotope.LayoutMode = LayoutMode;
3728
+
3729
+ Isotope.prototype._create = function() {
3730
+ this.itemGUID = 0;
3731
+ // functions that sort items
3732
+ this._sorters = {};
3733
+ this._getSorters();
3734
+ // call super
3735
+ Outlayer.prototype._create.call( this );
3736
+
3737
+ // create layout modes
3738
+ this.modes = {};
3739
+ // start filteredItems with all items
3740
+ this.filteredItems = this.items;
3741
+ // keep of track of sortBys
3742
+ this.sortHistory = [ 'original-order' ];
3743
+ // create from registered layout modes
3744
+ for ( var name in LayoutMode.modes ) {
3745
+ this._initLayoutMode( name );
3746
+ }
3747
+ };
3748
+
3749
+ Isotope.prototype.reloadItems = function() {
3750
+ // reset item ID counter
3751
+ this.itemGUID = 0;
3752
+ // call super
3753
+ Outlayer.prototype.reloadItems.call( this );
3754
+ };
3755
+
3756
+ Isotope.prototype._itemize = function() {
3757
+ var items = Outlayer.prototype._itemize.apply( this, arguments );
3758
+ // assign ID for original-order
3759
+ for ( var i=0, len = items.length; i < len; i++ ) {
3760
+ var item = items[i];
3761
+ item.id = this.itemGUID++;
3762
+ }
3763
+ this._updateItemsSortData( items );
3764
+ return items;
3765
+ };
3766
+
3767
+
3768
+ // -------------------------- layout -------------------------- //
3769
+
3770
+ Isotope.prototype._initLayoutMode = function( name ) {
3771
+ var Mode = LayoutMode.modes[ name ];
3772
+ // set mode options
3773
+ // HACK extend initial options, back-fill in default options
3774
+ var initialOpts = this.options[ name ] || {};
3775
+ this.options[ name ] = Mode.options ?
3776
+ utils.extend( Mode.options, initialOpts ) : initialOpts;
3777
+ // init layout mode instance
3778
+ this.modes[ name ] = new Mode( this );
3779
+ };
3780
+
3781
+
3782
+ Isotope.prototype.layout = function() {
3783
+ // if first time doing layout, do all magic
3784
+ if ( !this._isLayoutInited && this.options.isInitLayout ) {
3785
+ this.arrange();
3786
+ return;
3787
+ }
3788
+ this._layout();
3789
+ };
3790
+
3791
+ // private method to be used in layout() & magic()
3792
+ Isotope.prototype._layout = function() {
3793
+ // don't animate first layout
3794
+ var isInstant = this._getIsInstant();
3795
+ // layout flow
3796
+ this._resetLayout();
3797
+ this._manageStamps();
3798
+ this.layoutItems( this.filteredItems, isInstant );
3799
+
3800
+ // flag for initalized
3801
+ this._isLayoutInited = true;
3802
+ };
3803
+
3804
+ // filter + sort + layout
3805
+ Isotope.prototype.arrange = function( opts ) {
3806
+ // set any options pass
3807
+ this.option( opts );
3808
+ this._getIsInstant();
3809
+ // filter, sort, and layout
3810
+
3811
+ // filter
3812
+ var filtered = this._filter( this.items );
3813
+ this.filteredItems = filtered.matches;
3814
+
3815
+ var _this = this;
3816
+ function hideReveal() {
3817
+ _this.reveal( filtered.needReveal );
3818
+ _this.hide( filtered.needHide );
3819
+ }
3820
+
3821
+ this._bindArrangeComplete();
3822
+
3823
+ if ( this._isInstant ) {
3824
+ this._noTransition( hideReveal );
3825
+ } else {
3826
+ hideReveal();
3827
+ }
3828
+
3829
+ this._sort();
3830
+ this._layout();
3831
+ };
3832
+ // alias to _init for main plugin method
3833
+ Isotope.prototype._init = Isotope.prototype.arrange;
3834
+
3835
+ // HACK
3836
+ // Don't animate/transition first layout
3837
+ // Or don't animate/transition other layouts
3838
+ Isotope.prototype._getIsInstant = function() {
3839
+ var isInstant = this.options.isLayoutInstant !== undefined ?
3840
+ this.options.isLayoutInstant : !this._isLayoutInited;
3841
+ this._isInstant = isInstant;
3842
+ return isInstant;
3843
+ };
3844
+
3845
+ // listen for layoutComplete, hideComplete and revealComplete
3846
+ // to trigger arrangeComplete
3847
+ Isotope.prototype._bindArrangeComplete = function() {
3848
+ // listen for 3 events to trigger arrangeComplete
3849
+ var isLayoutComplete, isHideComplete, isRevealComplete;
3850
+ var _this = this;
3851
+ function arrangeParallelCallback() {
3852
+ if ( isLayoutComplete && isHideComplete && isRevealComplete ) {
3853
+ _this.dispatchEvent( 'arrangeComplete', null, [ _this.filteredItems ] );
3854
+ }
3855
+ }
3856
+ this.once( 'layoutComplete', function() {
3857
+ isLayoutComplete = true;
3858
+ arrangeParallelCallback();
3859
+ });
3860
+ this.once( 'hideComplete', function() {
3861
+ isHideComplete = true;
3862
+ arrangeParallelCallback();
3863
+ });
3864
+ this.once( 'revealComplete', function() {
3865
+ isRevealComplete = true;
3866
+ arrangeParallelCallback();
3867
+ });
3868
+ };
3869
+
3870
+ // -------------------------- filter -------------------------- //
3871
+
3872
+ Isotope.prototype._filter = function( items ) {
3873
+ var filter = this.options.filter;
3874
+ filter = filter || '*';
3875
+ var matches = [];
3876
+ var hiddenMatched = [];
3877
+ var visibleUnmatched = [];
3878
+
3879
+ var test = this._getFilterTest( filter );
3880
+
3881
+ // test each item
3882
+ for ( var i=0, len = items.length; i < len; i++ ) {
3883
+ var item = items[i];
3884
+ if ( item.isIgnored ) {
3885
+ continue;
3886
+ }
3887
+ // add item to either matched or unmatched group
3888
+ var isMatched = test( item );
3889
+ // item.isFilterMatched = isMatched;
3890
+ // add to matches if its a match
3891
+ if ( isMatched ) {
3892
+ matches.push( item );
3893
+ }
3894
+ // add to additional group if item needs to be hidden or revealed
3895
+ if ( isMatched && item.isHidden ) {
3896
+ hiddenMatched.push( item );
3897
+ } else if ( !isMatched && !item.isHidden ) {
3898
+ visibleUnmatched.push( item );
3899
+ }
3900
+ }
3901
+
3902
+ // return collections of items to be manipulated
3903
+ return {
3904
+ matches: matches,
3905
+ needReveal: hiddenMatched,
3906
+ needHide: visibleUnmatched
3907
+ };
3908
+ };
3909
+
3910
+ // get a jQuery, function, or a matchesSelector test given the filter
3911
+ Isotope.prototype._getFilterTest = function( filter ) {
3912
+ if ( jQuery && this.options.isJQueryFiltering ) {
3913
+ // use jQuery
3914
+ return function( item ) {
3915
+ return jQuery( item.element ).is( filter );
3916
+ };
3917
+ }
3918
+ if ( typeof filter == 'function' ) {
3919
+ // use filter as function
3920
+ return function( item ) {
3921
+ return filter( item.element );
3922
+ };
3923
+ }
3924
+ // default, use filter as selector string
3925
+ return function( item ) {
3926
+ return matchesSelector( item.element, filter );
3927
+ };
3928
+ };
3929
+
3930
+ // -------------------------- sorting -------------------------- //
3931
+
3932
+ /**
3933
+ * @params {Array} elems
3934
+ * @public
3935
+ */
3936
+ Isotope.prototype.updateSortData = function( elems ) {
3937
+ // get items
3938
+ var items;
3939
+ if ( elems ) {
3940
+ elems = utils.makeArray( elems );
3941
+ items = this.getItems( elems );
3942
+ } else {
3943
+ // update all items if no elems provided
3944
+ items = this.items;
3945
+ }
3946
+
3947
+ this._getSorters();
3948
+ this._updateItemsSortData( items );
3949
+ };
3950
+
3951
+ Isotope.prototype._getSorters = function() {
3952
+ var getSortData = this.options.getSortData;
3953
+ for ( var key in getSortData ) {
3954
+ var sorter = getSortData[ key ];
3955
+ this._sorters[ key ] = mungeSorter( sorter );
3956
+ }
3957
+ };
3958
+
3959
+ /**
3960
+ * @params {Array} items - of Isotope.Items
3961
+ * @private
3962
+ */
3963
+ Isotope.prototype._updateItemsSortData = function( items ) {
3964
+ // do not update if no items
3965
+ var len = items && items.length;
3966
+
3967
+ for ( var i=0; len && i < len; i++ ) {
3968
+ var item = items[i];
3969
+ item.updateSortData();
3970
+ }
3971
+ };
3972
+
3973
+ // ----- munge sorter ----- //
3974
+
3975
+ // encapsulate this, as we just need mungeSorter
3976
+ // other functions in here are just for munging
3977
+ var mungeSorter = ( function() {
3978
+ // add a magic layer to sorters for convienent shorthands
3979
+ // `.foo-bar` will use the text of .foo-bar querySelector
3980
+ // `[foo-bar]` will use attribute
3981
+ // you can also add parser
3982
+ // `.foo-bar parseInt` will parse that as a number
3983
+ function mungeSorter( sorter ) {
3984
+ // if not a string, return function or whatever it is
3985
+ if ( typeof sorter != 'string' ) {
3986
+ return sorter;
3987
+ }
3988
+ // parse the sorter string
3989
+ var args = trim( sorter ).split(' ');
3990
+ var query = args[0];
3991
+ // check if query looks like [an-attribute]
3992
+ var attrMatch = query.match( /^\[(.+)\]$/ );
3993
+ var attr = attrMatch && attrMatch[1];
3994
+ var getValue = getValueGetter( attr, query );
3995
+ // use second argument as a parser
3996
+ var parser = Isotope.sortDataParsers[ args[1] ];
3997
+ // parse the value, if there was a parser
3998
+ sorter = parser ? function( elem ) {
3999
+ return elem && parser( getValue( elem ) );
4000
+ } :
4001
+ // otherwise just return value
4002
+ function( elem ) {
4003
+ return elem && getValue( elem );
4004
+ };
4005
+
4006
+ return sorter;
4007
+ }
4008
+
4009
+ // get an attribute getter, or get text of the querySelector
4010
+ function getValueGetter( attr, query ) {
4011
+ var getValue;
4012
+ // if query looks like [foo-bar], get attribute
4013
+ if ( attr ) {
4014
+ getValue = function( elem ) {
4015
+ return elem.getAttribute( attr );
4016
+ };
4017
+ } else {
4018
+ // otherwise, assume its a querySelector, and get its text
4019
+ getValue = function( elem ) {
4020
+ var child = elem.querySelector( query );
4021
+ return child && getText( child );
4022
+ };
4023
+ }
4024
+ return getValue;
4025
+ }
4026
+
4027
+ return mungeSorter;
4028
+ })();
4029
+
4030
+ // parsers used in getSortData shortcut strings
4031
+ Isotope.sortDataParsers = {
4032
+ 'parseInt': function( val ) {
4033
+ return parseInt( val, 10 );
4034
+ },
4035
+ 'parseFloat': function( val ) {
4036
+ return parseFloat( val );
4037
+ }
4038
+ };
4039
+
4040
+ // ----- sort method ----- //
4041
+
4042
+ // sort filteredItem order
4043
+ Isotope.prototype._sort = function() {
4044
+ var sortByOpt = this.options.sortBy;
4045
+ if ( !sortByOpt ) {
4046
+ return;
4047
+ }
4048
+ // concat all sortBy and sortHistory
4049
+ var sortBys = [].concat.apply( sortByOpt, this.sortHistory );
4050
+ // sort magic
4051
+ var itemSorter = getItemSorter( sortBys, this.options.sortAscending );
4052
+ this.filteredItems.sort( itemSorter );
4053
+ // keep track of sortBy History
4054
+ if ( sortByOpt != this.sortHistory[0] ) {
4055
+ // add to front, oldest goes in last
4056
+ this.sortHistory.unshift( sortByOpt );
4057
+ }
4058
+ };
4059
+
4060
+ // returns a function used for sorting
4061
+ function getItemSorter( sortBys, sortAsc ) {
4062
+ return function sorter( itemA, itemB ) {
4063
+ // cycle through all sortKeys
4064
+ for ( var i = 0, len = sortBys.length; i < len; i++ ) {
4065
+ var sortBy = sortBys[i];
4066
+ var a = itemA.sortData[ sortBy ];
4067
+ var b = itemB.sortData[ sortBy ];
4068
+ if ( a > b || a < b ) {
4069
+ // if sortAsc is an object, use the value given the sortBy key
4070
+ var isAscending = sortAsc[ sortBy ] !== undefined ? sortAsc[ sortBy ] : sortAsc;
4071
+ var direction = isAscending ? 1 : -1;
4072
+ return ( a > b ? 1 : -1 ) * direction;
4073
+ }
4074
+ }
4075
+ return 0;
4076
+ };
4077
+ }
4078
+
4079
+ // -------------------------- methods -------------------------- //
4080
+
4081
+ // get layout mode
4082
+ Isotope.prototype._mode = function() {
4083
+ var layoutMode = this.options.layoutMode;
4084
+ var mode = this.modes[ layoutMode ];
4085
+ if ( !mode ) {
4086
+ // TODO console.error
4087
+ throw new Error( 'No layout mode: ' + layoutMode );
4088
+ }
4089
+ // HACK sync mode's options
4090
+ // any options set after init for layout mode need to be synced
4091
+ mode.options = this.options[ layoutMode ];
4092
+ return mode;
4093
+ };
4094
+
4095
+ Isotope.prototype._resetLayout = function() {
4096
+ // trigger original reset layout
4097
+ Outlayer.prototype._resetLayout.call( this );
4098
+ this._mode()._resetLayout();
4099
+ };
4100
+
4101
+ Isotope.prototype._getItemLayoutPosition = function( item ) {
4102
+ return this._mode()._getItemLayoutPosition( item );
4103
+ };
4104
+
4105
+ Isotope.prototype._manageStamp = function( stamp ) {
4106
+ this._mode()._manageStamp( stamp );
4107
+ };
4108
+
4109
+ Isotope.prototype._getContainerSize = function() {
4110
+ return this._mode()._getContainerSize();
4111
+ };
4112
+
4113
+ Isotope.prototype.needsResizeLayout = function() {
4114
+ return this._mode().needsResizeLayout();
4115
+ };
4116
+
4117
+ // -------------------------- adding & removing -------------------------- //
4118
+
4119
+ // HEADS UP overwrites default Outlayer appended
4120
+ Isotope.prototype.appended = function( elems ) {
4121
+ var items = this.addItems( elems );
4122
+ if ( !items.length ) {
4123
+ return;
4124
+ }
4125
+ // filter, layout, reveal new items
4126
+ var filteredItems = this._filterRevealAdded( items );
4127
+ // add to filteredItems
4128
+ this.filteredItems = this.filteredItems.concat( filteredItems );
4129
+ };
4130
+
4131
+ // HEADS UP overwrites default Outlayer prepended
4132
+ Isotope.prototype.prepended = function( elems ) {
4133
+ var items = this._itemize( elems );
4134
+ if ( !items.length ) {
4135
+ return;
4136
+ }
4137
+ // start new layout
4138
+ this._resetLayout();
4139
+ this._manageStamps();
4140
+ // filter, layout, reveal new items
4141
+ var filteredItems = this._filterRevealAdded( items );
4142
+ // layout previous items
4143
+ this.layoutItems( this.filteredItems );
4144
+ // add to items and filteredItems
4145
+ this.filteredItems = filteredItems.concat( this.filteredItems );
4146
+ this.items = items.concat( this.items );
4147
+ };
4148
+
4149
+ Isotope.prototype._filterRevealAdded = function( items ) {
4150
+ var filtered = this._filter( items );
4151
+ this.hide( filtered.needHide );
4152
+ // reveal all new items
4153
+ this.reveal( filtered.matches );
4154
+ // layout new items, no transition
4155
+ this.layoutItems( filtered.matches, true );
4156
+ return filtered.matches;
4157
+ };
4158
+
4159
+ /**
4160
+ * Filter, sort, and layout newly-appended item elements
4161
+ * @param {Array or NodeList or Element} elems
4162
+ */
4163
+ Isotope.prototype.insert = function( elems ) {
4164
+ var items = this.addItems( elems );
4165
+ if ( !items.length ) {
4166
+ return;
4167
+ }
4168
+ // append item elements
4169
+ var i, item;
4170
+ var len = items.length;
4171
+ for ( i=0; i < len; i++ ) {
4172
+ item = items[i];
4173
+ this.element.appendChild( item.element );
4174
+ }
4175
+ // filter new stuff
4176
+ var filteredInsertItems = this._filter( items ).matches;
4177
+ // set flag
4178
+ for ( i=0; i < len; i++ ) {
4179
+ items[i].isLayoutInstant = true;
4180
+ }
4181
+ this.arrange();
4182
+ // reset flag
4183
+ for ( i=0; i < len; i++ ) {
4184
+ delete items[i].isLayoutInstant;
4185
+ }
4186
+ this.reveal( filteredInsertItems );
4187
+ };
4188
+
4189
+ var _remove = Isotope.prototype.remove;
4190
+ Isotope.prototype.remove = function( elems ) {
4191
+ elems = utils.makeArray( elems );
4192
+ var removeItems = this.getItems( elems );
4193
+ // do regular thing
4194
+ _remove.call( this, elems );
4195
+ // bail if no items to remove
4196
+ var len = removeItems && removeItems.length;
4197
+ if ( !len ) {
4198
+ return;
4199
+ }
4200
+ // remove elems from filteredItems
4201
+ for ( var i=0; i < len; i++ ) {
4202
+ var item = removeItems[i];
4203
+ // remove item from collection
4204
+ utils.removeFrom( this.filteredItems, item );
4205
+ }
4206
+ };
4207
+
4208
+ Isotope.prototype.shuffle = function() {
4209
+ // update random sortData
4210
+ for ( var i=0, len = this.items.length; i < len; i++ ) {
4211
+ var item = this.items[i];
4212
+ item.sortData.random = Math.random();
4213
+ }
4214
+ this.options.sortBy = 'random';
4215
+ this._sort();
4216
+ this._layout();
4217
+ };
4218
+
4219
+ /**
4220
+ * trigger fn without transition
4221
+ * kind of hacky to have this in the first place
4222
+ * @param {Function} fn
4223
+ * @returns ret
4224
+ * @private
4225
+ */
4226
+ Isotope.prototype._noTransition = function( fn ) {
4227
+ // save transitionDuration before disabling
4228
+ var transitionDuration = this.options.transitionDuration;
4229
+ // disable transition
4230
+ this.options.transitionDuration = 0;
4231
+ // do it
4232
+ var returnValue = fn.call( this );
4233
+ // re-enable transition for reveal
4234
+ this.options.transitionDuration = transitionDuration;
4235
+ return returnValue;
4236
+ };
4237
+
4238
+ // ----- helper methods ----- //
4239
+
4240
+ /**
4241
+ * getter method for getting filtered item elements
4242
+ * @returns {Array} elems - collection of item elements
4243
+ */
4244
+ Isotope.prototype.getFilteredItemElements = function() {
4245
+ var elems = [];
4246
+ for ( var i=0, len = this.filteredItems.length; i < len; i++ ) {
4247
+ elems.push( this.filteredItems[i].element );
4248
+ }
4249
+ return elems;
4250
+ };
4251
+
4252
+ // ----- ----- //
4253
+
4254
+ return Isotope;
4255
+
4256
+ }));
4257
+