shufflejs-rails 0.1.0

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: 3ba99c78a87ad6fcf3bf25d308aec9f6c7263d6f
4
+ data.tar.gz: 8a08da53e41ac20b3852b358036f51f425c8a748
5
+ SHA512:
6
+ metadata.gz: 90e778544266afa67fae3062eab62d8ee7f8daf91f610184563c46253f6b2c4a3415ea3316cc8cabff23932cfc470f889d833fe0f464710e3371370ef3f855f4
7
+ data.tar.gz: 60547897b52a06d655934d840679b5e0e886dd3ee7ad2c237718945dab16974da1ba08bb7c172e740c6ec5ac36d1648bc94b4d99bcaa2d336db95b296b3189d9
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gem "rake"
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Kazuho Yamaguchi
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,32 @@
1
+ # shufflejs for Rails
2
+
3
+ Integrate [shufflejs](https://github.com/Vestride/Shuffle) into Rails
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'shufflejs-rails'
11
+ ```
12
+
13
+ And then execute:
14
+ ```console
15
+ $ bundle
16
+ ```
17
+
18
+ Or install it yourself as:
19
+
20
+ ```console
21
+ $ gem install shufflejs-rails
22
+ ```
23
+
24
+ Add this to your application.js file:
25
+
26
+ ```javascript
27
+ //= require shuffle
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ Visit: https://vestride.github.io/Shuffle/adding-removing
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,8 @@
1
+ require "shufflejs-rails/version"
2
+
3
+ module Shufflejs
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module Shufflejs
2
+ module Rails
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ require File.expand_path('../lib/shufflejs-rails/version', __FILE__)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "shufflejs-rails"
6
+ spec.version = Shufflejs::Rails::VERSION
7
+ spec.authors = ["Kazuho Yamaguchi"]
8
+ spec.email = ["kzh.yap@gmail.com"]
9
+
10
+ spec.summary = %q{shufflejs for Rails}
11
+ spec.description = %q{shufflejs for Ruby on Rails}
12
+ spec.homepage = "https://github.com/kyamaguchi/shufflejs-rails"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "railties", "> 3.1"
22
+ end
@@ -0,0 +1,2245 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
+ typeof define === 'function' && define.amd ? define(factory) :
4
+ (global.Shuffle = factory());
5
+ }(this, (function () { 'use strict';
6
+
7
+ function E () {
8
+ // Keep this empty so it's easier to inherit from
9
+ // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
10
+ }
11
+
12
+ E.prototype = {
13
+ on: function (name, callback, ctx) {
14
+ var e = this.e || (this.e = {});
15
+
16
+ (e[name] || (e[name] = [])).push({
17
+ fn: callback,
18
+ ctx: ctx
19
+ });
20
+
21
+ return this;
22
+ },
23
+
24
+ once: function (name, callback, ctx) {
25
+ var self = this;
26
+ function listener () {
27
+ self.off(name, listener);
28
+ callback.apply(ctx, arguments);
29
+ }
30
+
31
+ listener._ = callback;
32
+ return this.on(name, listener, ctx);
33
+ },
34
+
35
+ emit: function (name) {
36
+ var data = [].slice.call(arguments, 1);
37
+ var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
38
+ var i = 0;
39
+ var len = evtArr.length;
40
+
41
+ for (i; i < len; i++) {
42
+ evtArr[i].fn.apply(evtArr[i].ctx, data);
43
+ }
44
+
45
+ return this;
46
+ },
47
+
48
+ off: function (name, callback) {
49
+ var e = this.e || (this.e = {});
50
+ var evts = e[name];
51
+ var liveEvents = [];
52
+
53
+ if (evts && callback) {
54
+ for (var i = 0, len = evts.length; i < len; i++) {
55
+ if (evts[i].fn !== callback && evts[i].fn._ !== callback)
56
+ liveEvents.push(evts[i]);
57
+ }
58
+ }
59
+
60
+ // Remove event from queue to prevent memory leak
61
+ // Suggested by https://github.com/lazd
62
+ // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
63
+
64
+ (liveEvents.length)
65
+ ? e[name] = liveEvents
66
+ : delete e[name];
67
+
68
+ return this;
69
+ }
70
+ };
71
+
72
+ var index = E;
73
+
74
+ var proto = typeof Element !== 'undefined' ? Element.prototype : {};
75
+ var vendor = proto.matches
76
+ || proto.matchesSelector
77
+ || proto.webkitMatchesSelector
78
+ || proto.mozMatchesSelector
79
+ || proto.msMatchesSelector
80
+ || proto.oMatchesSelector;
81
+
82
+ var index$1 = match;
83
+
84
+ /**
85
+ * Match `el` to `selector`.
86
+ *
87
+ * @param {Element} el
88
+ * @param {String} selector
89
+ * @return {Boolean}
90
+ * @api public
91
+ */
92
+
93
+ function match(el, selector) {
94
+ if (!el || el.nodeType !== 1) return false;
95
+ if (vendor) return vendor.call(el, selector);
96
+ var nodes = el.parentNode.querySelectorAll(selector);
97
+ for (var i = 0; i < nodes.length; i++) {
98
+ if (nodes[i] == el) return true;
99
+ }
100
+ return false;
101
+ }
102
+
103
+ var index$2 = throttle;
104
+
105
+ /**
106
+ * Returns a new function that, when invoked, invokes `func` at most once per `wait` milliseconds.
107
+ *
108
+ * @param {Function} func Function to wrap.
109
+ * @param {Number} wait Number of milliseconds that must elapse between `func` invocations.
110
+ * @return {Function} A new function that wraps the `func` function passed in.
111
+ */
112
+
113
+ function throttle (func, wait) {
114
+ var ctx, args, rtn, timeoutID; // caching
115
+ var last = 0;
116
+
117
+ return function throttled () {
118
+ ctx = this;
119
+ args = arguments;
120
+ var delta = new Date() - last;
121
+ if (!timeoutID)
122
+ if (delta >= wait) call();
123
+ else timeoutID = setTimeout(call, wait - delta);
124
+ return rtn;
125
+ };
126
+
127
+ function call () {
128
+ timeoutID = 0;
129
+ last = +new Date();
130
+ rtn = func.apply(ctx, args);
131
+ ctx = null;
132
+ args = null;
133
+ }
134
+ }
135
+
136
+ var index$3 = function parallel(fns, context, callback) {
137
+ if (!callback) {
138
+ if (typeof context === 'function') {
139
+ callback = context;
140
+ context = null;
141
+ } else {
142
+ callback = noop;
143
+ }
144
+ }
145
+
146
+ var pending = fns && fns.length;
147
+ if (!pending) return callback(null, []);
148
+
149
+ var finished = false;
150
+ var results = new Array(pending);
151
+
152
+ fns.forEach(context ? function (fn, i) {
153
+ fn.call(context, maybeDone(i));
154
+ } : function (fn, i) {
155
+ fn(maybeDone(i));
156
+ });
157
+
158
+ function maybeDone(i) {
159
+ return function (err, result) {
160
+ if (finished) return;
161
+
162
+ if (err) {
163
+ callback(err, results);
164
+ finished = true;
165
+ return
166
+ }
167
+
168
+ results[i] = result;
169
+
170
+ if (!--pending) callback(null, results);
171
+ }
172
+ }
173
+ };
174
+
175
+ function noop() {}
176
+
177
+ /**
178
+ * Always returns a numeric value, given a value. Logic from jQuery's `isNumeric`.
179
+ * @param {*} value Possibly numeric value.
180
+ * @return {number} `value` or zero if `value` isn't numeric.
181
+ */
182
+ function getNumber(value) {
183
+ return parseFloat(value) || 0;
184
+ }
185
+
186
+ var classCallCheck = function (instance, Constructor) {
187
+ if (!(instance instanceof Constructor)) {
188
+ throw new TypeError("Cannot call a class as a function");
189
+ }
190
+ };
191
+
192
+ var createClass = function () {
193
+ function defineProperties(target, props) {
194
+ for (var i = 0; i < props.length; i++) {
195
+ var descriptor = props[i];
196
+ descriptor.enumerable = descriptor.enumerable || false;
197
+ descriptor.configurable = true;
198
+ if ("value" in descriptor) descriptor.writable = true;
199
+ Object.defineProperty(target, descriptor.key, descriptor);
200
+ }
201
+ }
202
+
203
+ return function (Constructor, protoProps, staticProps) {
204
+ if (protoProps) defineProperties(Constructor.prototype, protoProps);
205
+ if (staticProps) defineProperties(Constructor, staticProps);
206
+ return Constructor;
207
+ };
208
+ }();
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+ var inherits = function (subClass, superClass) {
219
+ if (typeof superClass !== "function" && superClass !== null) {
220
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
221
+ }
222
+
223
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
224
+ constructor: {
225
+ value: subClass,
226
+ enumerable: false,
227
+ writable: true,
228
+ configurable: true
229
+ }
230
+ });
231
+ if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
232
+ };
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+ var possibleConstructorReturn = function (self, call) {
245
+ if (!self) {
246
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
247
+ }
248
+
249
+ return call && (typeof call === "object" || typeof call === "function") ? call : self;
250
+ };
251
+
252
+ var Point = function () {
253
+ /**
254
+ * Represents a coordinate pair.
255
+ * @param {number} [x=0] X.
256
+ * @param {number} [y=0] Y.
257
+ */
258
+ function Point(x, y) {
259
+ classCallCheck(this, Point);
260
+
261
+ this.x = getNumber(x);
262
+ this.y = getNumber(y);
263
+ }
264
+
265
+ /**
266
+ * Whether two points are equal.
267
+ * @param {Point} a Point A.
268
+ * @param {Point} b Point B.
269
+ * @return {boolean}
270
+ */
271
+
272
+
273
+ createClass(Point, null, [{
274
+ key: 'equals',
275
+ value: function equals(a, b) {
276
+ return a.x === b.x && a.y === b.y;
277
+ }
278
+ }]);
279
+ return Point;
280
+ }();
281
+
282
+ var Rect = function () {
283
+ /**
284
+ * Class for representing rectangular regions.
285
+ * https://github.com/google/closure-library/blob/master/closure/goog/math/rect.js
286
+ * @param {number} x Left.
287
+ * @param {number} y Top.
288
+ * @param {number} w Width.
289
+ * @param {number} h Height.
290
+ * @param {number} id Identifier
291
+ * @constructor
292
+ */
293
+ function Rect(x, y, w, h, id) {
294
+ classCallCheck(this, Rect);
295
+
296
+ this.id = id;
297
+
298
+ /** @type {number} */
299
+ this.left = x;
300
+
301
+ /** @type {number} */
302
+ this.top = y;
303
+
304
+ /** @type {number} */
305
+ this.width = w;
306
+
307
+ /** @type {number} */
308
+ this.height = h;
309
+ }
310
+
311
+ /**
312
+ * Returns whether two rectangles intersect.
313
+ * @param {Rect} a A Rectangle.
314
+ * @param {Rect} b A Rectangle.
315
+ * @return {boolean} Whether a and b intersect.
316
+ */
317
+
318
+
319
+ createClass(Rect, null, [{
320
+ key: "intersects",
321
+ value: function intersects(a, b) {
322
+ return a.left < b.left + b.width && b.left < a.left + a.width && a.top < b.top + b.height && b.top < a.top + a.height;
323
+ }
324
+ }]);
325
+ return Rect;
326
+ }();
327
+
328
+ var Classes = {
329
+ BASE: 'shuffle',
330
+ SHUFFLE_ITEM: 'shuffle-item',
331
+ VISIBLE: 'shuffle-item--visible',
332
+ HIDDEN: 'shuffle-item--hidden'
333
+ };
334
+
335
+ var id$1 = 0;
336
+
337
+ var ShuffleItem = function () {
338
+ function ShuffleItem(element) {
339
+ classCallCheck(this, ShuffleItem);
340
+
341
+ id$1 += 1;
342
+ this.id = id$1;
343
+ this.element = element;
344
+
345
+ /**
346
+ * Used to separate items for layout and shrink.
347
+ */
348
+ this.isVisible = true;
349
+
350
+ /**
351
+ * Used to determine if a transition will happen. By the time the _layout
352
+ * and _shrink methods get the ShuffleItem instances, the `isVisible` value
353
+ * has already been changed by the separation methods, so this property is
354
+ * needed to know if the item was visible/hidden before the shrink/layout.
355
+ */
356
+ this.isHidden = false;
357
+ }
358
+
359
+ createClass(ShuffleItem, [{
360
+ key: 'show',
361
+ value: function show() {
362
+ this.isVisible = true;
363
+ this.element.classList.remove(Classes.HIDDEN);
364
+ this.element.classList.add(Classes.VISIBLE);
365
+ this.element.removeAttribute('aria-hidden');
366
+ }
367
+ }, {
368
+ key: 'hide',
369
+ value: function hide() {
370
+ this.isVisible = false;
371
+ this.element.classList.remove(Classes.VISIBLE);
372
+ this.element.classList.add(Classes.HIDDEN);
373
+ this.element.setAttribute('aria-hidden', true);
374
+ }
375
+ }, {
376
+ key: 'init',
377
+ value: function init() {
378
+ this.addClasses([Classes.SHUFFLE_ITEM, Classes.VISIBLE]);
379
+ this.applyCss(ShuffleItem.Css.INITIAL);
380
+ this.scale = ShuffleItem.Scale.VISIBLE;
381
+ this.point = new Point();
382
+ }
383
+ }, {
384
+ key: 'addClasses',
385
+ value: function addClasses(classes) {
386
+ var _this = this;
387
+
388
+ classes.forEach(function (className) {
389
+ _this.element.classList.add(className);
390
+ });
391
+ }
392
+ }, {
393
+ key: 'removeClasses',
394
+ value: function removeClasses(classes) {
395
+ var _this2 = this;
396
+
397
+ classes.forEach(function (className) {
398
+ _this2.element.classList.remove(className);
399
+ });
400
+ }
401
+ }, {
402
+ key: 'applyCss',
403
+ value: function applyCss(obj) {
404
+ var _this3 = this;
405
+
406
+ Object.keys(obj).forEach(function (key) {
407
+ _this3.element.style[key] = obj[key];
408
+ });
409
+ }
410
+ }, {
411
+ key: 'dispose',
412
+ value: function dispose() {
413
+ this.removeClasses([Classes.HIDDEN, Classes.VISIBLE, Classes.SHUFFLE_ITEM]);
414
+
415
+ this.element.removeAttribute('style');
416
+ this.element = null;
417
+ }
418
+ }]);
419
+ return ShuffleItem;
420
+ }();
421
+
422
+ ShuffleItem.Css = {
423
+ INITIAL: {
424
+ position: 'absolute',
425
+ top: 0,
426
+ left: 0,
427
+ visibility: 'visible',
428
+ 'will-change': 'transform'
429
+ },
430
+ VISIBLE: {
431
+ before: {
432
+ opacity: 1,
433
+ visibility: 'visible'
434
+ },
435
+ after: {
436
+ transitionDelay: ''
437
+ }
438
+ },
439
+ HIDDEN: {
440
+ before: {
441
+ opacity: 0
442
+ },
443
+ after: {
444
+ visibility: 'hidden',
445
+ transitionDelay: ''
446
+ }
447
+ }
448
+ };
449
+
450
+ ShuffleItem.Scale = {
451
+ VISIBLE: 1,
452
+ HIDDEN: 0.001
453
+ };
454
+
455
+ var element = document.body || document.documentElement;
456
+ var e = document.createElement('div');
457
+ e.style.cssText = 'width:10px;padding:2px;box-sizing:border-box;';
458
+ element.appendChild(e);
459
+
460
+ var width = window.getComputedStyle(e, null).width;
461
+ var ret = width === '10px';
462
+
463
+ element.removeChild(e);
464
+
465
+ /**
466
+ * Retrieve the computed style for an element, parsed as a float.
467
+ * @param {Element} element Element to get style for.
468
+ * @param {string} style Style property.
469
+ * @param {CSSStyleDeclaration} [styles] Optionally include clean styles to
470
+ * use instead of asking for them again.
471
+ * @return {number} The parsed computed value or zero if that fails because IE
472
+ * will return 'auto' when the element doesn't have margins instead of
473
+ * the computed style.
474
+ */
475
+ function getNumberStyle(element, style) {
476
+ var styles = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : window.getComputedStyle(element, null);
477
+
478
+ var value = getNumber(styles[style]);
479
+
480
+ // Support IE<=11 and W3C spec.
481
+ if (!ret && style === 'width') {
482
+ value += getNumber(styles.paddingLeft) + getNumber(styles.paddingRight) + getNumber(styles.borderLeftWidth) + getNumber(styles.borderRightWidth);
483
+ } else if (!ret && style === 'height') {
484
+ value += getNumber(styles.paddingTop) + getNumber(styles.paddingBottom) + getNumber(styles.borderTopWidth) + getNumber(styles.borderBottomWidth);
485
+ }
486
+
487
+ return value;
488
+ }
489
+
490
+ /**
491
+ * Fisher-Yates shuffle.
492
+ * http://stackoverflow.com/a/962890/373422
493
+ * https://bost.ocks.org/mike/shuffle/
494
+ * @param {Array} array Array to shuffle.
495
+ * @return {Array} Randomly sorted array.
496
+ */
497
+ function randomize(array) {
498
+ var n = array.length;
499
+
500
+ while (n) {
501
+ n -= 1;
502
+ var i = Math.floor(Math.random() * (n + 1));
503
+ var temp = array[i];
504
+ array[i] = array[n];
505
+ array[n] = temp;
506
+ }
507
+
508
+ return array;
509
+ }
510
+
511
+ var defaults$1 = {
512
+ // Use array.reverse() to reverse the results
513
+ reverse: false,
514
+
515
+ // Sorting function
516
+ by: null,
517
+
518
+ // If true, this will skip the sorting and return a randomized order in the array
519
+ randomize: false,
520
+
521
+ // Determines which property of each item in the array is passed to the
522
+ // sorting method.
523
+ key: 'element'
524
+ };
525
+
526
+ // You can return `undefined` from the `by` function to revert to DOM order.
527
+ function sorter(arr, options) {
528
+ var opts = Object.assign({}, defaults$1, options);
529
+ var original = Array.from(arr);
530
+ var revert = false;
531
+
532
+ if (!arr.length) {
533
+ return [];
534
+ }
535
+
536
+ if (opts.randomize) {
537
+ return randomize(arr);
538
+ }
539
+
540
+ // Sort the elements by the opts.by function.
541
+ // If we don't have opts.by, default to DOM order
542
+ if (typeof opts.by === 'function') {
543
+ arr.sort(function (a, b) {
544
+ // Exit early if we already know we want to revert
545
+ if (revert) {
546
+ return 0;
547
+ }
548
+
549
+ var valA = opts.by(a[opts.key]);
550
+ var valB = opts.by(b[opts.key]);
551
+
552
+ // If both values are undefined, use the DOM order
553
+ if (valA === undefined && valB === undefined) {
554
+ revert = true;
555
+ return 0;
556
+ }
557
+
558
+ if (valA < valB || valA === 'sortFirst' || valB === 'sortLast') {
559
+ return -1;
560
+ }
561
+
562
+ if (valA > valB || valA === 'sortLast' || valB === 'sortFirst') {
563
+ return 1;
564
+ }
565
+
566
+ return 0;
567
+ });
568
+ }
569
+
570
+ // Revert to the original array if necessary
571
+ if (revert) {
572
+ return original;
573
+ }
574
+
575
+ if (opts.reverse) {
576
+ arr.reverse();
577
+ }
578
+
579
+ return arr;
580
+ }
581
+
582
+ var transitions = {};
583
+ var eventName = 'transitionend';
584
+ var count = 0;
585
+
586
+ function uniqueId() {
587
+ count += 1;
588
+ return eventName + count;
589
+ }
590
+
591
+ function cancelTransitionEnd(id) {
592
+ if (transitions[id]) {
593
+ transitions[id].element.removeEventListener(eventName, transitions[id].listener);
594
+ transitions[id] = null;
595
+ return true;
596
+ }
597
+
598
+ return false;
599
+ }
600
+
601
+ function onTransitionEnd(element, callback) {
602
+ var id = uniqueId();
603
+ var listener = function listener(evt) {
604
+ if (evt.currentTarget === evt.target) {
605
+ cancelTransitionEnd(id);
606
+ callback(evt);
607
+ }
608
+ };
609
+
610
+ element.addEventListener(eventName, listener);
611
+
612
+ transitions[id] = { element: element, listener: listener };
613
+
614
+ return id;
615
+ }
616
+
617
+ function arrayMax(array) {
618
+ return Math.max.apply(Math, array); // eslint-disable-line prefer-spread
619
+ }
620
+
621
+ function arrayMin(array) {
622
+ return Math.min.apply(Math, array); // eslint-disable-line prefer-spread
623
+ }
624
+
625
+ /**
626
+ * Determine the number of columns an items spans.
627
+ * @param {number} itemWidth Width of the item.
628
+ * @param {number} columnWidth Width of the column (includes gutter).
629
+ * @param {number} columns Total number of columns
630
+ * @param {number} threshold A buffer value for the size of the column to fit.
631
+ * @return {number}
632
+ */
633
+ function getColumnSpan(itemWidth, columnWidth, columns, threshold) {
634
+ var columnSpan = itemWidth / columnWidth;
635
+
636
+ // If the difference between the rounded column span number and the
637
+ // calculated column span number is really small, round the number to
638
+ // make it fit.
639
+ if (Math.abs(Math.round(columnSpan) - columnSpan) < threshold) {
640
+ // e.g. columnSpan = 4.0089945390298745
641
+ columnSpan = Math.round(columnSpan);
642
+ }
643
+
644
+ // Ensure the column span is not more than the amount of columns in the whole layout.
645
+ return Math.min(Math.ceil(columnSpan), columns);
646
+ }
647
+
648
+ /**
649
+ * Retrieves the column set to use for placement.
650
+ * @param {number} columnSpan The number of columns this current item spans.
651
+ * @param {number} columns The total columns in the grid.
652
+ * @return {Array.<number>} An array of numbers represeting the column set.
653
+ */
654
+ function getAvailablePositions(positions, columnSpan, columns) {
655
+ // The item spans only one column.
656
+ if (columnSpan === 1) {
657
+ return positions;
658
+ }
659
+
660
+ // The item spans more than one column, figure out how many different
661
+ // places it could fit horizontally.
662
+ // The group count is the number of places within the positions this block
663
+ // could fit, ignoring the current positions of items.
664
+ // Imagine a 2 column brick as the second item in a 4 column grid with
665
+ // 10px height each. Find the places it would fit:
666
+ // [20, 10, 10, 0]
667
+ // | | |
668
+ // * * *
669
+ //
670
+ // Then take the places which fit and get the bigger of the two:
671
+ // max([20, 10]), max([10, 10]), max([10, 0]) = [20, 10, 0]
672
+ //
673
+ // Next, find the first smallest number (the short column).
674
+ // [20, 10, 0]
675
+ // |
676
+ // *
677
+ //
678
+ // And that's where it should be placed!
679
+ //
680
+ // Another example where the second column's item extends past the first:
681
+ // [10, 20, 10, 0] => [20, 20, 10] => 10
682
+ var available = [];
683
+
684
+ // For how many possible positions for this item there are.
685
+ for (var i = 0; i <= columns - columnSpan; i++) {
686
+ // Find the bigger value for each place it could fit.
687
+ available.push(arrayMax(positions.slice(i, i + columnSpan)));
688
+ }
689
+
690
+ return available;
691
+ }
692
+
693
+ /**
694
+ * Find index of short column, the first from the left where this item will go.
695
+ *
696
+ * @param {Array.<number>} positions The array to search for the smallest number.
697
+ * @param {number} buffer Optional buffer which is very useful when the height
698
+ * is a percentage of the width.
699
+ * @return {number} Index of the short column.
700
+ */
701
+ function getShortColumn(positions, buffer) {
702
+ var minPosition = arrayMin(positions);
703
+ for (var i = 0, len = positions.length; i < len; i++) {
704
+ if (positions[i] >= minPosition - buffer && positions[i] <= minPosition + buffer) {
705
+ return i;
706
+ }
707
+ }
708
+
709
+ return 0;
710
+ }
711
+
712
+ /**
713
+ * Determine the location of the next item, based on its size.
714
+ * @param {Object} itemSize Object with width and height.
715
+ * @param {Array.<number>} positions Positions of the other current items.
716
+ * @param {number} gridSize The column width or row height.
717
+ * @param {number} total The total number of columns or rows.
718
+ * @param {number} threshold Buffer value for the column to fit.
719
+ * @param {number} buffer Vertical buffer for the height of items.
720
+ * @return {Point}
721
+ */
722
+ function getItemPosition(_ref) {
723
+ var itemSize = _ref.itemSize,
724
+ positions = _ref.positions,
725
+ gridSize = _ref.gridSize,
726
+ total = _ref.total,
727
+ threshold = _ref.threshold,
728
+ buffer = _ref.buffer;
729
+
730
+ var span = getColumnSpan(itemSize.width, gridSize, total, threshold);
731
+ var setY = getAvailablePositions(positions, span, total);
732
+ var shortColumnIndex = getShortColumn(setY, buffer);
733
+
734
+ // Position the item
735
+ var point = new Point(gridSize * shortColumnIndex, setY[shortColumnIndex]);
736
+
737
+ // Update the columns array with the new values for each column.
738
+ // e.g. before the update the columns could be [250, 0, 0, 0] for an item
739
+ // which spans 2 columns. After it would be [250, itemHeight, itemHeight, 0].
740
+ var setHeight = setY[shortColumnIndex] + itemSize.height;
741
+ for (var i = 0; i < span; i++) {
742
+ positions[shortColumnIndex + i] = setHeight;
743
+ }
744
+
745
+ return point;
746
+ }
747
+
748
+ /**
749
+ * This method attempts to center items. This method could potentially be slow
750
+ * with a large number of items because it must place items, then check every
751
+ * previous item to ensure there is no overlap.
752
+ * @param {Array.<Rect>} itemRects Item data objects.
753
+ * @param {number} containerWidth Width of the containing element.
754
+ * @return {Array.<Point>}
755
+ */
756
+ function getCenteredPositions(itemRects, containerWidth) {
757
+ var rowMap = {};
758
+
759
+ // Populate rows by their offset because items could jump between rows like:
760
+ // a c
761
+ // bbb
762
+ itemRects.forEach(function (itemRect) {
763
+ if (rowMap[itemRect.top]) {
764
+ // Push the point to the last row array.
765
+ rowMap[itemRect.top].push(itemRect);
766
+ } else {
767
+ // Start of a new row.
768
+ rowMap[itemRect.top] = [itemRect];
769
+ }
770
+ });
771
+
772
+ // For each row, find the end of the last item, then calculate
773
+ // the remaining space by dividing it by 2. Then add that
774
+ // offset to the x position of each point.
775
+ var rects = [];
776
+ var rows = [];
777
+ var centeredRows = [];
778
+ Object.keys(rowMap).forEach(function (key) {
779
+ var itemRects = rowMap[key];
780
+ rows.push(itemRects);
781
+ var lastItem = itemRects[itemRects.length - 1];
782
+ var end = lastItem.left + lastItem.width;
783
+ var offset = Math.round((containerWidth - end) / 2);
784
+
785
+ var finalRects = itemRects;
786
+ var canMove = false;
787
+ if (offset > 0) {
788
+ var newRects = [];
789
+ canMove = itemRects.every(function (r) {
790
+ var newRect = new Rect(r.left + offset, r.top, r.width, r.height, r.id);
791
+
792
+ // Check all current rects to make sure none overlap.
793
+ var noOverlap = !rects.some(function (r) {
794
+ return Rect.intersects(newRect, r);
795
+ });
796
+
797
+ newRects.push(newRect);
798
+ return noOverlap;
799
+ });
800
+
801
+ // If none of the rectangles overlapped, the whole group can be centered.
802
+ if (canMove) {
803
+ finalRects = newRects;
804
+ }
805
+ }
806
+
807
+ // If the items are not going to be offset, ensure that the original
808
+ // placement for this row will not overlap previous rows (row-spanning
809
+ // elements could be in the way).
810
+ if (!canMove) {
811
+ var intersectingRect = void 0;
812
+ var hasOverlap = itemRects.some(function (itemRect) {
813
+ return rects.some(function (r) {
814
+ var intersects = Rect.intersects(itemRect, r);
815
+ if (intersects) {
816
+ intersectingRect = r;
817
+ }
818
+ return intersects;
819
+ });
820
+ });
821
+
822
+ // If there is any overlap, replace the overlapping row with the original.
823
+ if (hasOverlap) {
824
+ var rowIndex = centeredRows.findIndex(function (items) {
825
+ return items.includes(intersectingRect);
826
+ });
827
+ centeredRows.splice(rowIndex, 1, rows[rowIndex]);
828
+ }
829
+ }
830
+
831
+ rects = rects.concat(finalRects);
832
+ centeredRows.push(finalRects);
833
+ });
834
+
835
+ // Reduce array of arrays to a single array of points.
836
+ // https://stackoverflow.com/a/10865042/373422
837
+ // Then reset sort back to how the items were passed to this method.
838
+ // Remove the wrapper object with index, map to a Point.
839
+ return [].concat.apply([], centeredRows) // eslint-disable-line prefer-spread
840
+ .sort(function (a, b) {
841
+ return a.id - b.id;
842
+ }).map(function (itemRect) {
843
+ return new Point(itemRect.left, itemRect.top);
844
+ });
845
+ }
846
+
847
+ /**
848
+ * Hyphenates a javascript style string to a css one. For example:
849
+ * MozBoxSizing -> -moz-box-sizing.
850
+ * @param {string} str The string to hyphenate.
851
+ * @return {string} The hyphenated string.
852
+ */
853
+ function hyphenate(str) {
854
+ return str.replace(/([A-Z])/g, function (str, m1) {
855
+ return "-" + m1.toLowerCase();
856
+ });
857
+ }
858
+
859
+ function arrayUnique(x) {
860
+ return Array.from(new Set(x));
861
+ }
862
+
863
+ // Used for unique instance variables
864
+ var id = 0;
865
+
866
+ var Shuffle = function (_TinyEmitter) {
867
+ inherits(Shuffle, _TinyEmitter);
868
+
869
+ /**
870
+ * Categorize, sort, and filter a responsive grid of items.
871
+ *
872
+ * @param {Element} element An element which is the parent container for the grid items.
873
+ * @param {Object} [options=Shuffle.options] Options object.
874
+ * @constructor
875
+ */
876
+ function Shuffle(element) {
877
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
878
+ classCallCheck(this, Shuffle);
879
+
880
+ var _this = possibleConstructorReturn(this, (Shuffle.__proto__ || Object.getPrototypeOf(Shuffle)).call(this));
881
+
882
+ _this.options = Object.assign({}, Shuffle.options, options);
883
+
884
+ _this.lastSort = {};
885
+ _this.group = Shuffle.ALL_ITEMS;
886
+ _this.lastFilter = Shuffle.ALL_ITEMS;
887
+ _this.isEnabled = true;
888
+ _this.isDestroyed = false;
889
+ _this.isInitialized = false;
890
+ _this._transitions = [];
891
+ _this.isTransitioning = false;
892
+ _this._queue = [];
893
+
894
+ var el = _this._getElementOption(element);
895
+
896
+ if (!el) {
897
+ throw new TypeError('Shuffle needs to be initialized with an element.');
898
+ }
899
+
900
+ _this.element = el;
901
+ _this.id = 'shuffle_' + id;
902
+ id += 1;
903
+
904
+ _this._init();
905
+ _this.isInitialized = true;
906
+ return _this;
907
+ }
908
+
909
+ createClass(Shuffle, [{
910
+ key: '_init',
911
+ value: function _init() {
912
+ this.items = this._getItems();
913
+
914
+ this.options.sizer = this._getElementOption(this.options.sizer);
915
+
916
+ // Add class and invalidate styles
917
+ this.element.classList.add(Shuffle.Classes.BASE);
918
+
919
+ // Set initial css for each item
920
+ this._initItems(this.items);
921
+
922
+ // Bind resize events
923
+ this._onResize = this._getResizeFunction();
924
+ window.addEventListener('resize', this._onResize);
925
+
926
+ // If the page has not already emitted the `load` event, call layout on load.
927
+ // This avoids layout issues caused by images and fonts loading after the
928
+ // instance has been initialized.
929
+ if (document.readyState !== 'complete') {
930
+ var layout = this.layout.bind(this);
931
+ window.addEventListener('load', function onLoad() {
932
+ window.removeEventListener('load', onLoad);
933
+ layout();
934
+ });
935
+ }
936
+
937
+ // Get container css all in one request. Causes reflow
938
+ var containerCss = window.getComputedStyle(this.element, null);
939
+ var containerWidth = Shuffle.getSize(this.element).width;
940
+
941
+ // Add styles to the container if it doesn't have them.
942
+ this._validateStyles(containerCss);
943
+
944
+ // We already got the container's width above, no need to cause another
945
+ // reflow getting it again... Calculate the number of columns there will be
946
+ this._setColumns(containerWidth);
947
+
948
+ // Kick off!
949
+ this.filter(this.options.group, this.options.initialSort);
950
+
951
+ // The shuffle items haven't had transitions set on them yet so the user
952
+ // doesn't see the first layout. Set them now that the first layout is done.
953
+ // First, however, a synchronous layout must be caused for the previous
954
+ // styles to be applied without transitions.
955
+ this.element.offsetWidth; // eslint-disable-line no-unused-expressions
956
+ this.setItemTransitions(this.items);
957
+ this.element.style.transition = 'height ' + this.options.speed + 'ms ' + this.options.easing;
958
+ }
959
+
960
+ /**
961
+ * Returns a throttled and proxied function for the resize handler.
962
+ * @return {function}
963
+ * @private
964
+ */
965
+
966
+ }, {
967
+ key: '_getResizeFunction',
968
+ value: function _getResizeFunction() {
969
+ var resizeFunction = this._handleResize.bind(this);
970
+ return this.options.throttle ? this.options.throttle(resizeFunction, this.options.throttleTime) : resizeFunction;
971
+ }
972
+
973
+ /**
974
+ * Retrieve an element from an option.
975
+ * @param {string|jQuery|Element} option The option to check.
976
+ * @return {?Element} The plain element or null.
977
+ * @private
978
+ */
979
+
980
+ }, {
981
+ key: '_getElementOption',
982
+ value: function _getElementOption(option) {
983
+ // If column width is a string, treat is as a selector and search for the
984
+ // sizer element within the outermost container
985
+ if (typeof option === 'string') {
986
+ return this.element.querySelector(option);
987
+
988
+ // Check for an element
989
+ } else if (option && option.nodeType && option.nodeType === 1) {
990
+ return option;
991
+
992
+ // Check for jQuery object
993
+ } else if (option && option.jquery) {
994
+ return option[0];
995
+ }
996
+
997
+ return null;
998
+ }
999
+
1000
+ /**
1001
+ * Ensures the shuffle container has the css styles it needs applied to it.
1002
+ * @param {Object} styles Key value pairs for position and overflow.
1003
+ * @private
1004
+ */
1005
+
1006
+ }, {
1007
+ key: '_validateStyles',
1008
+ value: function _validateStyles(styles) {
1009
+ // Position cannot be static.
1010
+ if (styles.position === 'static') {
1011
+ this.element.style.position = 'relative';
1012
+ }
1013
+
1014
+ // Overflow has to be hidden.
1015
+ if (styles.overflow !== 'hidden') {
1016
+ this.element.style.overflow = 'hidden';
1017
+ }
1018
+ }
1019
+
1020
+ /**
1021
+ * Filter the elements by a category.
1022
+ * @param {string|string[]|function(Element):boolean} [category] Category to
1023
+ * filter by. If it's given, the last category will be used to filter the items.
1024
+ * @param {Array} [collection] Optionally filter a collection. Defaults to
1025
+ * all the items.
1026
+ * @return {{visible: ShuffleItem[], hidden: ShuffleItem[]}}
1027
+ * @private
1028
+ */
1029
+
1030
+ }, {
1031
+ key: '_filter',
1032
+ value: function _filter() {
1033
+ var category = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.lastFilter;
1034
+ var collection = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.items;
1035
+
1036
+ var set$$1 = this._getFilteredSets(category, collection);
1037
+
1038
+ // Individually add/remove hidden/visible classes
1039
+ this._toggleFilterClasses(set$$1);
1040
+
1041
+ // Save the last filter in case elements are appended.
1042
+ this.lastFilter = category;
1043
+
1044
+ // This is saved mainly because providing a filter function (like searching)
1045
+ // will overwrite the `lastFilter` property every time its called.
1046
+ if (typeof category === 'string') {
1047
+ this.group = category;
1048
+ }
1049
+
1050
+ return set$$1;
1051
+ }
1052
+
1053
+ /**
1054
+ * Returns an object containing the visible and hidden elements.
1055
+ * @param {string|string[]|function(Element):boolean} category Category or function to filter by.
1056
+ * @param {ShuffleItem[]} items A collection of items to filter.
1057
+ * @return {{visible: ShuffleItem[], hidden: ShuffleItem[]}}
1058
+ * @private
1059
+ */
1060
+
1061
+ }, {
1062
+ key: '_getFilteredSets',
1063
+ value: function _getFilteredSets(category, items) {
1064
+ var _this2 = this;
1065
+
1066
+ var visible = [];
1067
+ var hidden = [];
1068
+
1069
+ // category === 'all', add visible class to everything
1070
+ if (category === Shuffle.ALL_ITEMS) {
1071
+ visible = items;
1072
+
1073
+ // Loop through each item and use provided function to determine
1074
+ // whether to hide it or not.
1075
+ } else {
1076
+ items.forEach(function (item) {
1077
+ if (_this2._doesPassFilter(category, item.element)) {
1078
+ visible.push(item);
1079
+ } else {
1080
+ hidden.push(item);
1081
+ }
1082
+ });
1083
+ }
1084
+
1085
+ return {
1086
+ visible: visible,
1087
+ hidden: hidden
1088
+ };
1089
+ }
1090
+
1091
+ /**
1092
+ * Test an item to see if it passes a category.
1093
+ * @param {string|string[]|function():boolean} category Category or function to filter by.
1094
+ * @param {Element} element An element to test.
1095
+ * @return {boolean} Whether it passes the category/filter.
1096
+ * @private
1097
+ */
1098
+
1099
+ }, {
1100
+ key: '_doesPassFilter',
1101
+ value: function _doesPassFilter(category, element) {
1102
+ if (typeof category === 'function') {
1103
+ return category.call(element, element, this);
1104
+ }
1105
+
1106
+ // Check each element's data-groups attribute against the given category.
1107
+ var attr = element.getAttribute('data-' + Shuffle.FILTER_ATTRIBUTE_KEY);
1108
+ var keys = this.options.delimeter ? attr.split(this.options.delimeter) : JSON.parse(attr);
1109
+
1110
+ function testCategory(category) {
1111
+ return keys.includes(category);
1112
+ }
1113
+
1114
+ if (Array.isArray(category)) {
1115
+ if (this.options.filterMode === Shuffle.FilterMode.ANY) {
1116
+ return category.some(testCategory);
1117
+ }
1118
+ return category.every(testCategory);
1119
+ }
1120
+
1121
+ return keys.includes(category);
1122
+ }
1123
+
1124
+ /**
1125
+ * Toggles the visible and hidden class names.
1126
+ * @param {{visible, hidden}} Object with visible and hidden arrays.
1127
+ * @private
1128
+ */
1129
+
1130
+ }, {
1131
+ key: '_toggleFilterClasses',
1132
+ value: function _toggleFilterClasses(_ref) {
1133
+ var visible = _ref.visible,
1134
+ hidden = _ref.hidden;
1135
+
1136
+ visible.forEach(function (item) {
1137
+ item.show();
1138
+ });
1139
+
1140
+ hidden.forEach(function (item) {
1141
+ item.hide();
1142
+ });
1143
+ }
1144
+
1145
+ /**
1146
+ * Set the initial css for each item
1147
+ * @param {ShuffleItem[]} items Set to initialize.
1148
+ * @private
1149
+ */
1150
+
1151
+ }, {
1152
+ key: '_initItems',
1153
+ value: function _initItems(items) {
1154
+ items.forEach(function (item) {
1155
+ item.init();
1156
+ });
1157
+ }
1158
+
1159
+ /**
1160
+ * Remove element reference and styles.
1161
+ * @param {ShuffleItem[]} items Set to dispose.
1162
+ * @private
1163
+ */
1164
+
1165
+ }, {
1166
+ key: '_disposeItems',
1167
+ value: function _disposeItems(items) {
1168
+ items.forEach(function (item) {
1169
+ item.dispose();
1170
+ });
1171
+ }
1172
+
1173
+ /**
1174
+ * Updates the visible item count.
1175
+ * @private
1176
+ */
1177
+
1178
+ }, {
1179
+ key: '_updateItemCount',
1180
+ value: function _updateItemCount() {
1181
+ this.visibleItems = this._getFilteredItems().length;
1182
+ }
1183
+
1184
+ /**
1185
+ * Sets css transform transition on a group of elements. This is not executed
1186
+ * at the same time as `item.init` so that transitions don't occur upon
1187
+ * initialization of a new Shuffle instance.
1188
+ * @param {ShuffleItem[]} items Shuffle items to set transitions on.
1189
+ * @protected
1190
+ */
1191
+
1192
+ }, {
1193
+ key: 'setItemTransitions',
1194
+ value: function setItemTransitions(items) {
1195
+ var speed = this.options.speed;
1196
+ var easing = this.options.easing;
1197
+ var positionProps = this.options.useTransforms ? ['transform'] : ['top', 'left'];
1198
+
1199
+ // Allow users to transtion other properties if they exist in the `before`
1200
+ // css mapping of the shuffle item.
1201
+ var properties = positionProps.concat(Object.keys(ShuffleItem.Css.HIDDEN.before).map(function (k) {
1202
+ return hyphenate(k);
1203
+ })).join();
1204
+
1205
+ items.forEach(function (item) {
1206
+ item.element.style.transitionDuration = speed + 'ms';
1207
+ item.element.style.transitionTimingFunction = easing;
1208
+ item.element.style.transitionProperty = properties;
1209
+ });
1210
+ }
1211
+ }, {
1212
+ key: '_getItems',
1213
+ value: function _getItems() {
1214
+ var _this3 = this;
1215
+
1216
+ return Array.from(this.element.children).filter(function (el) {
1217
+ return index$1(el, _this3.options.itemSelector);
1218
+ }).map(function (el) {
1219
+ return new ShuffleItem(el);
1220
+ });
1221
+ }
1222
+
1223
+ /**
1224
+ * When new elements are added to the shuffle container, update the array of
1225
+ * items because that is the order `_layout` calls them.
1226
+ * @param {ShuffleItem[]} items Items to track.
1227
+ * @return {Shuffle[]}
1228
+ */
1229
+
1230
+ }, {
1231
+ key: '_mergeNewItems',
1232
+ value: function _mergeNewItems(items) {
1233
+ var children = Array.from(this.element.children);
1234
+ return sorter(this.items.concat(items), {
1235
+ by: function by(element) {
1236
+ return children.indexOf(element);
1237
+ }
1238
+ });
1239
+ }
1240
+ }, {
1241
+ key: '_getFilteredItems',
1242
+ value: function _getFilteredItems() {
1243
+ return this.items.filter(function (item) {
1244
+ return item.isVisible;
1245
+ });
1246
+ }
1247
+ }, {
1248
+ key: '_getConcealedItems',
1249
+ value: function _getConcealedItems() {
1250
+ return this.items.filter(function (item) {
1251
+ return !item.isVisible;
1252
+ });
1253
+ }
1254
+
1255
+ /**
1256
+ * Returns the column size, based on column width and sizer options.
1257
+ * @param {number} containerWidth Size of the parent container.
1258
+ * @param {number} gutterSize Size of the gutters.
1259
+ * @return {number}
1260
+ * @private
1261
+ */
1262
+
1263
+ }, {
1264
+ key: '_getColumnSize',
1265
+ value: function _getColumnSize(containerWidth, gutterSize) {
1266
+ var size = void 0;
1267
+
1268
+ // If the columnWidth property is a function, then the grid is fluid
1269
+ if (typeof this.options.columnWidth === 'function') {
1270
+ size = this.options.columnWidth(containerWidth);
1271
+
1272
+ // columnWidth option isn't a function, are they using a sizing element?
1273
+ } else if (this.options.sizer) {
1274
+ size = Shuffle.getSize(this.options.sizer).width;
1275
+
1276
+ // if not, how about the explicitly set option?
1277
+ } else if (this.options.columnWidth) {
1278
+ size = this.options.columnWidth;
1279
+
1280
+ // or use the size of the first item
1281
+ } else if (this.items.length > 0) {
1282
+ size = Shuffle.getSize(this.items[0].element, true).width;
1283
+
1284
+ // if there's no items, use size of container
1285
+ } else {
1286
+ size = containerWidth;
1287
+ }
1288
+
1289
+ // Don't let them set a column width of zero.
1290
+ if (size === 0) {
1291
+ size = containerWidth;
1292
+ }
1293
+
1294
+ return size + gutterSize;
1295
+ }
1296
+
1297
+ /**
1298
+ * Returns the gutter size, based on gutter width and sizer options.
1299
+ * @param {number} containerWidth Size of the parent container.
1300
+ * @return {number}
1301
+ * @private
1302
+ */
1303
+
1304
+ }, {
1305
+ key: '_getGutterSize',
1306
+ value: function _getGutterSize(containerWidth) {
1307
+ var size = void 0;
1308
+ if (typeof this.options.gutterWidth === 'function') {
1309
+ size = this.options.gutterWidth(containerWidth);
1310
+ } else if (this.options.sizer) {
1311
+ size = getNumberStyle(this.options.sizer, 'marginLeft');
1312
+ } else {
1313
+ size = this.options.gutterWidth;
1314
+ }
1315
+
1316
+ return size;
1317
+ }
1318
+
1319
+ /**
1320
+ * Calculate the number of columns to be used. Gets css if using sizer element.
1321
+ * @param {number} [containerWidth] Optionally specify a container width if
1322
+ * it's already available.
1323
+ */
1324
+
1325
+ }, {
1326
+ key: '_setColumns',
1327
+ value: function _setColumns() {
1328
+ var containerWidth = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Shuffle.getSize(this.element).width;
1329
+
1330
+ var gutter = this._getGutterSize(containerWidth);
1331
+ var columnWidth = this._getColumnSize(containerWidth, gutter);
1332
+ var calculatedColumns = (containerWidth + gutter) / columnWidth;
1333
+
1334
+ // Widths given from getStyles are not precise enough...
1335
+ if (Math.abs(Math.round(calculatedColumns) - calculatedColumns) < this.options.columnThreshold) {
1336
+ // e.g. calculatedColumns = 11.998876
1337
+ calculatedColumns = Math.round(calculatedColumns);
1338
+ }
1339
+
1340
+ this.cols = Math.max(Math.floor(calculatedColumns), 1);
1341
+ this.containerWidth = containerWidth;
1342
+ this.colWidth = columnWidth;
1343
+ }
1344
+
1345
+ /**
1346
+ * Adjust the height of the grid
1347
+ */
1348
+
1349
+ }, {
1350
+ key: '_setContainerSize',
1351
+ value: function _setContainerSize() {
1352
+ this.element.style.height = this._getContainerSize() + 'px';
1353
+ }
1354
+
1355
+ /**
1356
+ * Based on the column heights, it returns the biggest one.
1357
+ * @return {number}
1358
+ * @private
1359
+ */
1360
+
1361
+ }, {
1362
+ key: '_getContainerSize',
1363
+ value: function _getContainerSize() {
1364
+ return arrayMax(this.positions);
1365
+ }
1366
+
1367
+ /**
1368
+ * Get the clamped stagger amount.
1369
+ * @param {number} index Index of the item to be staggered.
1370
+ * @return {number}
1371
+ */
1372
+
1373
+ }, {
1374
+ key: '_getStaggerAmount',
1375
+ value: function _getStaggerAmount(index$$1) {
1376
+ return Math.min(index$$1 * this.options.staggerAmount, this.options.staggerAmountMax);
1377
+ }
1378
+
1379
+ /**
1380
+ * Emit an event from this instance.
1381
+ * @param {string} name Event name.
1382
+ * @param {Object} [data={}] Optional object data.
1383
+ */
1384
+
1385
+ }, {
1386
+ key: '_dispatch',
1387
+ value: function _dispatch(name) {
1388
+ var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1389
+
1390
+ if (this.isDestroyed) {
1391
+ return;
1392
+ }
1393
+
1394
+ data.shuffle = this;
1395
+ this.emit(name, data);
1396
+ }
1397
+
1398
+ /**
1399
+ * Zeros out the y columns array, which is used to determine item placement.
1400
+ * @private
1401
+ */
1402
+
1403
+ }, {
1404
+ key: '_resetCols',
1405
+ value: function _resetCols() {
1406
+ var i = this.cols;
1407
+ this.positions = [];
1408
+ while (i) {
1409
+ i -= 1;
1410
+ this.positions.push(0);
1411
+ }
1412
+ }
1413
+
1414
+ /**
1415
+ * Loops through each item that should be shown and calculates the x, y position.
1416
+ * @param {ShuffleItem[]} items Array of items that will be shown/layed
1417
+ * out in order in their array.
1418
+ */
1419
+
1420
+ }, {
1421
+ key: '_layout',
1422
+ value: function _layout(items) {
1423
+ var _this4 = this;
1424
+
1425
+ var itemPositions = this._getNextPositions(items);
1426
+
1427
+ var count = 0;
1428
+ items.forEach(function (item, i) {
1429
+ function callback() {
1430
+ item.applyCss(ShuffleItem.Css.VISIBLE.after);
1431
+ }
1432
+
1433
+ // If the item will not change its position, do not add it to the render
1434
+ // queue. Transitions don't fire when setting a property to the same value.
1435
+ if (Point.equals(item.point, itemPositions[i]) && !item.isHidden) {
1436
+ item.applyCss(ShuffleItem.Css.VISIBLE.before);
1437
+ callback();
1438
+ return;
1439
+ }
1440
+
1441
+ item.point = itemPositions[i];
1442
+ item.scale = ShuffleItem.Scale.VISIBLE;
1443
+ item.isHidden = false;
1444
+
1445
+ // Clone the object so that the `before` object isn't modified when the
1446
+ // transition delay is added.
1447
+ var styles = _this4.getStylesForTransition(item, ShuffleItem.Css.VISIBLE.before);
1448
+ styles.transitionDelay = _this4._getStaggerAmount(count) + 'ms';
1449
+
1450
+ _this4._queue.push({
1451
+ item: item,
1452
+ styles: styles,
1453
+ callback: callback
1454
+ });
1455
+
1456
+ count += 1;
1457
+ });
1458
+ }
1459
+
1460
+ /**
1461
+ * Return an array of Point instances representing the future positions of
1462
+ * each item.
1463
+ * @param {ShuffleItem[]} items Array of sorted shuffle items.
1464
+ * @return {Point[]}
1465
+ * @private
1466
+ */
1467
+
1468
+ }, {
1469
+ key: '_getNextPositions',
1470
+ value: function _getNextPositions(items) {
1471
+ var _this5 = this;
1472
+
1473
+ // If position data is going to be changed, add the item's size to the
1474
+ // transformer to allow for calculations.
1475
+ if (this.options.isCentered) {
1476
+ var itemsData = items.map(function (item, i) {
1477
+ var itemSize = Shuffle.getSize(item.element, true);
1478
+ var point = _this5._getItemPosition(itemSize);
1479
+ return new Rect(point.x, point.y, itemSize.width, itemSize.height, i);
1480
+ });
1481
+
1482
+ return this.getTransformedPositions(itemsData, this.containerWidth);
1483
+ }
1484
+
1485
+ // If no transforms are going to happen, simply return an array of the
1486
+ // future points of each item.
1487
+ return items.map(function (item) {
1488
+ return _this5._getItemPosition(Shuffle.getSize(item.element, true));
1489
+ });
1490
+ }
1491
+
1492
+ /**
1493
+ * Determine the location of the next item, based on its size.
1494
+ * @param {{width: number, height: number}} itemSize Object with width and height.
1495
+ * @return {Point}
1496
+ * @private
1497
+ */
1498
+
1499
+ }, {
1500
+ key: '_getItemPosition',
1501
+ value: function _getItemPosition(itemSize) {
1502
+ return getItemPosition({
1503
+ itemSize: itemSize,
1504
+ positions: this.positions,
1505
+ gridSize: this.colWidth,
1506
+ total: this.cols,
1507
+ threshold: this.options.columnThreshold,
1508
+ buffer: this.options.buffer
1509
+ });
1510
+ }
1511
+
1512
+ /**
1513
+ * Mutate positions before they're applied.
1514
+ * @param {Rect[]} itemRects Item data objects.
1515
+ * @param {number} containerWidth Width of the containing element.
1516
+ * @return {Point[]}
1517
+ * @protected
1518
+ */
1519
+
1520
+ }, {
1521
+ key: 'getTransformedPositions',
1522
+ value: function getTransformedPositions(itemRects, containerWidth) {
1523
+ return getCenteredPositions(itemRects, containerWidth);
1524
+ }
1525
+
1526
+ /**
1527
+ * Hides the elements that don't match our filter.
1528
+ * @param {ShuffleItem[]} collection Collection to shrink.
1529
+ * @private
1530
+ */
1531
+
1532
+ }, {
1533
+ key: '_shrink',
1534
+ value: function _shrink() {
1535
+ var _this6 = this;
1536
+
1537
+ var collection = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._getConcealedItems();
1538
+
1539
+ var count = 0;
1540
+ collection.forEach(function (item) {
1541
+ function callback() {
1542
+ item.applyCss(ShuffleItem.Css.HIDDEN.after);
1543
+ }
1544
+
1545
+ // Continuing would add a transitionend event listener to the element, but
1546
+ // that listener would not execute because the transform and opacity would
1547
+ // stay the same.
1548
+ // The callback is executed here because it is not guaranteed to be called
1549
+ // after the transitionend event because the transitionend could be
1550
+ // canceled if another animation starts.
1551
+ if (item.isHidden) {
1552
+ item.applyCss(ShuffleItem.Css.HIDDEN.before);
1553
+ callback();
1554
+ return;
1555
+ }
1556
+
1557
+ item.scale = ShuffleItem.Scale.HIDDEN;
1558
+ item.isHidden = true;
1559
+
1560
+ var styles = _this6.getStylesForTransition(item, ShuffleItem.Css.HIDDEN.before);
1561
+ styles.transitionDelay = _this6._getStaggerAmount(count) + 'ms';
1562
+
1563
+ _this6._queue.push({
1564
+ item: item,
1565
+ styles: styles,
1566
+ callback: callback
1567
+ });
1568
+
1569
+ count += 1;
1570
+ });
1571
+ }
1572
+
1573
+ /**
1574
+ * Resize handler.
1575
+ * @private
1576
+ */
1577
+
1578
+ }, {
1579
+ key: '_handleResize',
1580
+ value: function _handleResize() {
1581
+ // If shuffle is disabled, destroyed, don't do anything
1582
+ if (!this.isEnabled || this.isDestroyed) {
1583
+ return;
1584
+ }
1585
+
1586
+ this.update();
1587
+ }
1588
+
1589
+ /**
1590
+ * Returns styles which will be applied to the an item for a transition.
1591
+ * @param {ShuffleItem} item Item to get styles for. Should have updated
1592
+ * scale and point properties.
1593
+ * @param {Object} styleObject Extra styles that will be used in the transition.
1594
+ * @return {!Object} Transforms for transitions, left/top for animate.
1595
+ * @protected
1596
+ */
1597
+
1598
+ }, {
1599
+ key: 'getStylesForTransition',
1600
+ value: function getStylesForTransition(item, styleObject) {
1601
+ // Clone the object to avoid mutating the original.
1602
+ var styles = Object.assign({}, styleObject);
1603
+
1604
+ if (this.options.useTransforms) {
1605
+ if (this.options.roundTransforms) {
1606
+ item.point = new Point(Math.round(item.point.x), Math.round(item.point.y));
1607
+ }
1608
+ styles.transform = 'translate(' + item.point.x + 'px, ' + item.point.y + 'px) scale(' + item.scale + ')';
1609
+ } else {
1610
+ styles.left = item.point.x + 'px';
1611
+ styles.top = item.point.y + 'px';
1612
+ }
1613
+
1614
+ return styles;
1615
+ }
1616
+
1617
+ /**
1618
+ * Listen for the transition end on an element and execute the itemCallback
1619
+ * when it finishes.
1620
+ * @param {Element} element Element to listen on.
1621
+ * @param {function} itemCallback Callback for the item.
1622
+ * @param {function} done Callback to notify `parallel` that this one is done.
1623
+ */
1624
+
1625
+ }, {
1626
+ key: '_whenTransitionDone',
1627
+ value: function _whenTransitionDone(element, itemCallback, done) {
1628
+ var id = onTransitionEnd(element, function (evt) {
1629
+ itemCallback();
1630
+ done(null, evt);
1631
+ });
1632
+
1633
+ this._transitions.push(id);
1634
+ }
1635
+
1636
+ /**
1637
+ * Return a function which will set CSS styles and call the `done` function
1638
+ * when (if) the transition finishes.
1639
+ * @param {Object} opts Transition object.
1640
+ * @return {function} A function to be called with a `done` function.
1641
+ */
1642
+
1643
+ }, {
1644
+ key: '_getTransitionFunction',
1645
+ value: function _getTransitionFunction(opts) {
1646
+ var _this7 = this;
1647
+
1648
+ return function (done) {
1649
+ opts.item.applyCss(opts.styles);
1650
+ _this7._whenTransitionDone(opts.item.element, opts.callback, done);
1651
+ };
1652
+ }
1653
+
1654
+ /**
1655
+ * Execute the styles gathered in the style queue. This applies styles to elements,
1656
+ * triggering transitions.
1657
+ * @private
1658
+ */
1659
+
1660
+ }, {
1661
+ key: '_processQueue',
1662
+ value: function _processQueue() {
1663
+ if (this.isTransitioning) {
1664
+ this._cancelMovement();
1665
+ }
1666
+
1667
+ var hasSpeed = this.options.speed > 0;
1668
+ var hasQueue = this._queue.length > 0;
1669
+
1670
+ if (hasQueue && hasSpeed && this.isInitialized) {
1671
+ this._startTransitions(this._queue);
1672
+ } else if (hasQueue) {
1673
+ this._styleImmediately(this._queue);
1674
+ this._dispatch(Shuffle.EventType.LAYOUT);
1675
+
1676
+ // A call to layout happened, but none of the newly visible items will
1677
+ // change position or the transition duration is zero, which will not trigger
1678
+ // the transitionend event.
1679
+ } else {
1680
+ this._dispatch(Shuffle.EventType.LAYOUT);
1681
+ }
1682
+
1683
+ // Remove everything in the style queue
1684
+ this._queue.length = 0;
1685
+ }
1686
+
1687
+ /**
1688
+ * Wait for each transition to finish, the emit the layout event.
1689
+ * @param {Object[]} transitions Array of transition objects.
1690
+ */
1691
+
1692
+ }, {
1693
+ key: '_startTransitions',
1694
+ value: function _startTransitions(transitions) {
1695
+ var _this8 = this;
1696
+
1697
+ // Set flag that shuffle is currently in motion.
1698
+ this.isTransitioning = true;
1699
+
1700
+ // Create an array of functions to be called.
1701
+ var callbacks = transitions.map(function (obj) {
1702
+ return _this8._getTransitionFunction(obj);
1703
+ });
1704
+
1705
+ index$3(callbacks, this._movementFinished.bind(this));
1706
+ }
1707
+ }, {
1708
+ key: '_cancelMovement',
1709
+ value: function _cancelMovement() {
1710
+ // Remove the transition end event for each listener.
1711
+ this._transitions.forEach(cancelTransitionEnd);
1712
+
1713
+ // Reset the array.
1714
+ this._transitions.length = 0;
1715
+
1716
+ // Show it's no longer active.
1717
+ this.isTransitioning = false;
1718
+ }
1719
+
1720
+ /**
1721
+ * Apply styles without a transition.
1722
+ * @param {Object[]} objects Array of transition objects.
1723
+ * @private
1724
+ */
1725
+
1726
+ }, {
1727
+ key: '_styleImmediately',
1728
+ value: function _styleImmediately(objects) {
1729
+ if (objects.length) {
1730
+ var elements = objects.map(function (obj) {
1731
+ return obj.item.element;
1732
+ });
1733
+
1734
+ Shuffle._skipTransitions(elements, function () {
1735
+ objects.forEach(function (obj) {
1736
+ obj.item.applyCss(obj.styles);
1737
+ obj.callback();
1738
+ });
1739
+ });
1740
+ }
1741
+ }
1742
+ }, {
1743
+ key: '_movementFinished',
1744
+ value: function _movementFinished() {
1745
+ this._transitions.length = 0;
1746
+ this.isTransitioning = false;
1747
+ this._dispatch(Shuffle.EventType.LAYOUT);
1748
+ }
1749
+
1750
+ /**
1751
+ * The magic. This is what makes the plugin 'shuffle'
1752
+ * @param {string|string[]|function(Element):boolean} [category] Category to filter by.
1753
+ * Can be a function, string, or array of strings.
1754
+ * @param {Object} [sortObj] A sort object which can sort the visible set
1755
+ */
1756
+
1757
+ }, {
1758
+ key: 'filter',
1759
+ value: function filter(category, sortObj) {
1760
+ if (!this.isEnabled) {
1761
+ return;
1762
+ }
1763
+
1764
+ if (!category || category && category.length === 0) {
1765
+ category = Shuffle.ALL_ITEMS; // eslint-disable-line no-param-reassign
1766
+ }
1767
+
1768
+ this._filter(category);
1769
+
1770
+ // Shrink each hidden item
1771
+ this._shrink();
1772
+
1773
+ // How many visible elements?
1774
+ this._updateItemCount();
1775
+
1776
+ // Update transforms on visible elements so they will animate to their new positions.
1777
+ this.sort(sortObj);
1778
+ }
1779
+
1780
+ /**
1781
+ * Gets the visible elements, sorts them, and passes them to layout.
1782
+ * @param {Object} [sortOptions] The options object to pass to `sorter`.
1783
+ */
1784
+
1785
+ }, {
1786
+ key: 'sort',
1787
+ value: function sort() {
1788
+ var sortOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.lastSort;
1789
+
1790
+ if (!this.isEnabled) {
1791
+ return;
1792
+ }
1793
+
1794
+ this._resetCols();
1795
+
1796
+ var items = sorter(this._getFilteredItems(), sortOptions);
1797
+
1798
+ this._layout(items);
1799
+
1800
+ // `_layout` always happens after `_shrink`, so it's safe to process the style
1801
+ // queue here with styles from the shrink method.
1802
+ this._processQueue();
1803
+
1804
+ // Adjust the height of the container.
1805
+ this._setContainerSize();
1806
+
1807
+ this.lastSort = sortOptions;
1808
+ }
1809
+
1810
+ /**
1811
+ * Reposition everything.
1812
+ * @param {boolean} [isOnlyLayout=false] If true, column and gutter widths won't be recalculated.
1813
+ */
1814
+
1815
+ }, {
1816
+ key: 'update',
1817
+ value: function update() {
1818
+ var isOnlyLayout = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
1819
+
1820
+ if (this.isEnabled) {
1821
+ if (!isOnlyLayout) {
1822
+ // Get updated colCount
1823
+ this._setColumns();
1824
+ }
1825
+
1826
+ // Layout items
1827
+ this.sort();
1828
+ }
1829
+ }
1830
+
1831
+ /**
1832
+ * Use this instead of `update()` if you don't need the columns and gutters updated
1833
+ * Maybe an image inside `shuffle` loaded (and now has a height), which means calculations
1834
+ * could be off.
1835
+ */
1836
+
1837
+ }, {
1838
+ key: 'layout',
1839
+ value: function layout() {
1840
+ this.update(true);
1841
+ }
1842
+
1843
+ /**
1844
+ * New items have been appended to shuffle. Mix them in with the current
1845
+ * filter or sort status.
1846
+ * @param {Element[]} newItems Collection of new items.
1847
+ */
1848
+
1849
+ }, {
1850
+ key: 'add',
1851
+ value: function add(newItems) {
1852
+ var _this9 = this;
1853
+
1854
+ var items = arrayUnique(newItems).map(function (el) {
1855
+ return new ShuffleItem(el);
1856
+ });
1857
+
1858
+ // Add classes and set initial positions.
1859
+ this._initItems(items);
1860
+
1861
+ // Determine which items will go with the current filter.
1862
+ this._resetCols();
1863
+ var newItemSet = this._filter(this.lastFilter, items);
1864
+ var willBeVisible = this._mergeNewItems(newItemSet.visible);
1865
+ var sortedVisibleItems = sorter(willBeVisible, this.lastSort);
1866
+
1867
+ // Layout all items again so that new items get positions.
1868
+ // Synchonously apply positions.
1869
+ var itemPositions = this._getNextPositions(sortedVisibleItems);
1870
+ sortedVisibleItems.forEach(function (item, i) {
1871
+ if (newItemSet.visible.includes(item)) {
1872
+ item.point = itemPositions[i];
1873
+ item.scale = ShuffleItem.Scale.HIDDEN;
1874
+ item.isHidden = true;
1875
+ item.applyCss(ShuffleItem.Css.HIDDEN.before);
1876
+ item.applyCss(ShuffleItem.Css.HIDDEN.after);
1877
+ item.applyCss(_this9.getStylesForTransition(item, {}));
1878
+ }
1879
+ });
1880
+
1881
+ // Cause layout so that the styles above are applied.
1882
+ this.element.offsetWidth; // eslint-disable-line no-unused-expressions
1883
+
1884
+ // Add transition to each item.
1885
+ this.setItemTransitions(items);
1886
+
1887
+ // Update the list of items.
1888
+ this.items = this._mergeNewItems(items);
1889
+
1890
+ // Update layout/visibility of new and old items.
1891
+ this.filter(this.lastFilter);
1892
+ }
1893
+
1894
+ /**
1895
+ * Disables shuffle from updating dimensions and layout on resize
1896
+ */
1897
+
1898
+ }, {
1899
+ key: 'disable',
1900
+ value: function disable() {
1901
+ this.isEnabled = false;
1902
+ }
1903
+
1904
+ /**
1905
+ * Enables shuffle again
1906
+ * @param {boolean} [isUpdateLayout=true] if undefined, shuffle will update columns and gutters
1907
+ */
1908
+
1909
+ }, {
1910
+ key: 'enable',
1911
+ value: function enable() {
1912
+ var isUpdateLayout = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
1913
+
1914
+ this.isEnabled = true;
1915
+ if (isUpdateLayout) {
1916
+ this.update();
1917
+ }
1918
+ }
1919
+
1920
+ /**
1921
+ * Remove 1 or more shuffle items.
1922
+ * @param {Element[]} elements An array containing one or more
1923
+ * elements in shuffle
1924
+ * @return {Shuffle} The shuffle instance.
1925
+ */
1926
+
1927
+ }, {
1928
+ key: 'remove',
1929
+ value: function remove(elements) {
1930
+ var _this10 = this;
1931
+
1932
+ if (!elements.length) {
1933
+ return;
1934
+ }
1935
+
1936
+ var collection = arrayUnique(elements);
1937
+
1938
+ var oldItems = collection.map(function (element) {
1939
+ return _this10.getItemByElement(element);
1940
+ }).filter(function (item) {
1941
+ return !!item;
1942
+ });
1943
+
1944
+ var handleLayout = function handleLayout() {
1945
+ _this10._disposeItems(oldItems);
1946
+
1947
+ // Remove the collection in the callback
1948
+ collection.forEach(function (element) {
1949
+ element.parentNode.removeChild(element);
1950
+ });
1951
+
1952
+ _this10._dispatch(Shuffle.EventType.REMOVED, { collection: collection });
1953
+ };
1954
+
1955
+ // Hide collection first.
1956
+ this._toggleFilterClasses({
1957
+ visible: [],
1958
+ hidden: oldItems
1959
+ });
1960
+
1961
+ this._shrink(oldItems);
1962
+
1963
+ this.sort();
1964
+
1965
+ // Update the list of items here because `remove` could be called again
1966
+ // with an item that is in the process of being removed.
1967
+ this.items = this.items.filter(function (item) {
1968
+ return !oldItems.includes(item);
1969
+ });
1970
+ this._updateItemCount();
1971
+
1972
+ this.once(Shuffle.EventType.LAYOUT, handleLayout);
1973
+ }
1974
+
1975
+ /**
1976
+ * Retrieve a shuffle item by its element.
1977
+ * @param {Element} element Element to look for.
1978
+ * @return {?ShuffleItem} A shuffle item or undefined if it's not found.
1979
+ */
1980
+
1981
+ }, {
1982
+ key: 'getItemByElement',
1983
+ value: function getItemByElement(element) {
1984
+ return this.items.find(function (item) {
1985
+ return item.element === element;
1986
+ });
1987
+ }
1988
+
1989
+ /**
1990
+ * Dump the elements currently stored and reinitialize all child elements which
1991
+ * match the `itemSelector`.
1992
+ */
1993
+
1994
+ }, {
1995
+ key: 'resetItems',
1996
+ value: function resetItems() {
1997
+ var _this11 = this;
1998
+
1999
+ // Remove refs to current items.
2000
+ this._disposeItems(this.items);
2001
+ this.isInitialized = false;
2002
+
2003
+ // Find new items in the DOM.
2004
+ this.items = this._getItems();
2005
+
2006
+ // Set initial styles on the new items.
2007
+ this._initItems(this.items);
2008
+
2009
+ this.once(Shuffle.EventType.LAYOUT, function () {
2010
+ // Add transition to each item.
2011
+ _this11.setItemTransitions(_this11.items);
2012
+ _this11.isInitialized = true;
2013
+ });
2014
+
2015
+ // Lay out all items.
2016
+ this.filter(this.lastFilter);
2017
+ }
2018
+
2019
+ /**
2020
+ * Destroys shuffle, removes events, styles, and classes
2021
+ */
2022
+
2023
+ }, {
2024
+ key: 'destroy',
2025
+ value: function destroy() {
2026
+ this._cancelMovement();
2027
+ window.removeEventListener('resize', this._onResize);
2028
+
2029
+ // Reset container styles
2030
+ this.element.classList.remove('shuffle');
2031
+ this.element.removeAttribute('style');
2032
+
2033
+ // Reset individual item styles
2034
+ this._disposeItems(this.items);
2035
+
2036
+ this.items.length = 0;
2037
+ this._transitions.length = 0;
2038
+
2039
+ // Null DOM references
2040
+ this.options.sizer = null;
2041
+ this.element = null;
2042
+
2043
+ // Set a flag so if a debounced resize has been triggered,
2044
+ // it can first check if it is actually isDestroyed and not doing anything
2045
+ this.isDestroyed = true;
2046
+ this.isEnabled = false;
2047
+ }
2048
+
2049
+ /**
2050
+ * Returns the outer width of an element, optionally including its margins.
2051
+ *
2052
+ * There are a few different methods for getting the width of an element, none of
2053
+ * which work perfectly for all Shuffle's use cases.
2054
+ *
2055
+ * 1. getBoundingClientRect() `left` and `right` properties.
2056
+ * - Accounts for transform scaled elements, making it useless for Shuffle
2057
+ * elements which have shrunk.
2058
+ * 2. The `offsetWidth` property.
2059
+ * - This value stays the same regardless of the elements transform property,
2060
+ * however, it does not return subpixel values.
2061
+ * 3. getComputedStyle()
2062
+ * - This works great Chrome, Firefox, Safari, but IE<=11 does not include
2063
+ * padding and border when box-sizing: border-box is set, requiring a feature
2064
+ * test and extra work to add the padding back for IE and other browsers which
2065
+ * follow the W3C spec here.
2066
+ *
2067
+ * @param {Element} element The element.
2068
+ * @param {boolean} [includeMargins=false] Whether to include margins.
2069
+ * @return {{width: number, height: number}} The width and height.
2070
+ */
2071
+
2072
+ }], [{
2073
+ key: 'getSize',
2074
+ value: function getSize(element) {
2075
+ var includeMargins = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2076
+
2077
+ // Store the styles so that they can be used by others without asking for it again.
2078
+ var styles = window.getComputedStyle(element, null);
2079
+ var width = getNumberStyle(element, 'width', styles);
2080
+ var height = getNumberStyle(element, 'height', styles);
2081
+
2082
+ if (includeMargins) {
2083
+ var marginLeft = getNumberStyle(element, 'marginLeft', styles);
2084
+ var marginRight = getNumberStyle(element, 'marginRight', styles);
2085
+ var marginTop = getNumberStyle(element, 'marginTop', styles);
2086
+ var marginBottom = getNumberStyle(element, 'marginBottom', styles);
2087
+ width += marginLeft + marginRight;
2088
+ height += marginTop + marginBottom;
2089
+ }
2090
+
2091
+ return {
2092
+ width: width,
2093
+ height: height
2094
+ };
2095
+ }
2096
+
2097
+ /**
2098
+ * Change a property or execute a function which will not have a transition
2099
+ * @param {Element[]} elements DOM elements that won't be transitioned.
2100
+ * @param {function} callback A function which will be called while transition
2101
+ * is set to 0ms.
2102
+ * @private
2103
+ */
2104
+
2105
+ }, {
2106
+ key: '_skipTransitions',
2107
+ value: function _skipTransitions(elements, callback) {
2108
+ var zero = '0ms';
2109
+
2110
+ // Save current duration and delay.
2111
+ var data = elements.map(function (element) {
2112
+ var style = element.style;
2113
+ var duration = style.transitionDuration;
2114
+ var delay = style.transitionDelay;
2115
+
2116
+ // Set the duration to zero so it happens immediately
2117
+ style.transitionDuration = zero;
2118
+ style.transitionDelay = zero;
2119
+
2120
+ return {
2121
+ duration: duration,
2122
+ delay: delay
2123
+ };
2124
+ });
2125
+
2126
+ callback();
2127
+
2128
+ // Cause forced synchronous layout.
2129
+ elements[0].offsetWidth; // eslint-disable-line no-unused-expressions
2130
+
2131
+ // Put the duration back
2132
+ elements.forEach(function (element, i) {
2133
+ element.style.transitionDuration = data[i].duration;
2134
+ element.style.transitionDelay = data[i].delay;
2135
+ });
2136
+ }
2137
+ }]);
2138
+ return Shuffle;
2139
+ }(index);
2140
+
2141
+ Shuffle.ShuffleItem = ShuffleItem;
2142
+
2143
+ Shuffle.ALL_ITEMS = 'all';
2144
+ Shuffle.FILTER_ATTRIBUTE_KEY = 'groups';
2145
+
2146
+ /** @enum {string} */
2147
+ Shuffle.EventType = {
2148
+ LAYOUT: 'shuffle:layout',
2149
+ REMOVED: 'shuffle:removed'
2150
+ };
2151
+
2152
+ /** @enum {string} */
2153
+ Shuffle.Classes = Classes;
2154
+
2155
+ /** @enum {string} */
2156
+ Shuffle.FilterMode = {
2157
+ ANY: 'any',
2158
+ ALL: 'all'
2159
+ };
2160
+
2161
+ // Overrideable options
2162
+ Shuffle.options = {
2163
+ // Initial filter group.
2164
+ group: Shuffle.ALL_ITEMS,
2165
+
2166
+ // Transition/animation speed (milliseconds).
2167
+ speed: 250,
2168
+
2169
+ // CSS easing function to use.
2170
+ easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
2171
+
2172
+ // e.g. '.picture-item'.
2173
+ itemSelector: '*',
2174
+
2175
+ // Element or selector string. Use an element to determine the size of columns
2176
+ // and gutters.
2177
+ sizer: null,
2178
+
2179
+ // A static number or function that tells the plugin how wide the gutters
2180
+ // between columns are (in pixels).
2181
+ gutterWidth: 0,
2182
+
2183
+ // A static number or function that returns a number which tells the plugin
2184
+ // how wide the columns are (in pixels).
2185
+ columnWidth: 0,
2186
+
2187
+ // If your group is not json, and is comma delimeted, you could set delimeter
2188
+ // to ','.
2189
+ delimeter: null,
2190
+
2191
+ // Useful for percentage based heights when they might not always be exactly
2192
+ // the same (in pixels).
2193
+ buffer: 0,
2194
+
2195
+ // Reading the width of elements isn't precise enough and can cause columns to
2196
+ // jump between values.
2197
+ columnThreshold: 0.01,
2198
+
2199
+ // Shuffle can be isInitialized with a sort object. It is the same object
2200
+ // given to the sort method.
2201
+ initialSort: null,
2202
+
2203
+ // By default, shuffle will throttle resize events. This can be changed or
2204
+ // removed.
2205
+ throttle: index$2,
2206
+
2207
+ // How often shuffle can be called on resize (in milliseconds).
2208
+ throttleTime: 300,
2209
+
2210
+ // Transition delay offset for each item in milliseconds.
2211
+ staggerAmount: 15,
2212
+
2213
+ // Maximum stagger delay in milliseconds.
2214
+ staggerAmountMax: 150,
2215
+
2216
+ // Whether to use transforms or absolute positioning.
2217
+ useTransforms: true,
2218
+
2219
+ // Affects using an array with filter. e.g. `filter(['one', 'two'])`. With "any",
2220
+ // the element passes the test if any of its groups are in the array. With "all",
2221
+ // the element only passes if all groups are in the array.
2222
+ filterMode: Shuffle.FilterMode.ANY,
2223
+
2224
+ // Attempt to center grid items in each row.
2225
+ isCentered: false,
2226
+
2227
+ // Whether to round pixel values used in translate(x, y). This usually avoids
2228
+ // blurriness.
2229
+ roundTransforms: true
2230
+ };
2231
+
2232
+ Shuffle.Point = Point;
2233
+ Shuffle.Rect = Rect;
2234
+
2235
+ // Expose for testing. Hack at your own risk.
2236
+ Shuffle.__sorter = sorter;
2237
+ Shuffle.__getColumnSpan = getColumnSpan;
2238
+ Shuffle.__getAvailablePositions = getAvailablePositions;
2239
+ Shuffle.__getShortColumn = getShortColumn;
2240
+ Shuffle.__getCenteredPositions = getCenteredPositions;
2241
+
2242
+ return Shuffle;
2243
+
2244
+ })));
2245
+ //# sourceMappingURL=shuffle.js.map