shufflejs-rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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