@pantograph/sortable 1.15.6

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.
package/src/utils.js ADDED
@@ -0,0 +1,600 @@
1
+ import { IE11OrLess } from './BrowserInfo.js';
2
+ import Sortable from './Sortable.js';
3
+
4
+ const captureMode = {
5
+ capture: false,
6
+ passive: false
7
+ };
8
+
9
+ function on(el, event, fn) {
10
+ el.addEventListener(event, fn, !IE11OrLess && captureMode);
11
+ }
12
+
13
+
14
+ function off(el, event, fn) {
15
+ el.removeEventListener(event, fn, !IE11OrLess && captureMode);
16
+ }
17
+
18
+ function matches(/**HTMLElement*/el, /**String*/selector) {
19
+ if (!selector) return;
20
+
21
+ selector[0] === '>' && (selector = selector.substring(1));
22
+
23
+ if (el) {
24
+ try {
25
+ if (el.matches) {
26
+ return el.matches(selector);
27
+ } else if (el.msMatchesSelector) {
28
+ return el.msMatchesSelector(selector);
29
+ } else if (el.webkitMatchesSelector) {
30
+ return el.webkitMatchesSelector(selector);
31
+ }
32
+ } catch(_) {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ return false;
38
+ }
39
+
40
+ function getParentOrHost(el) {
41
+ return (el.host && el !== document && el.host.nodeType && el.host !== el)
42
+ ? el.host
43
+ : el.parentNode;
44
+ }
45
+
46
+ function closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx, includeCTX, /**HTMLElement*/ignoreEl) {
47
+ if (el) {
48
+ ctx = ctx || document;
49
+
50
+ do {
51
+ if (
52
+ el !== ignoreEl &&
53
+ ( selector != null &&
54
+ (
55
+ selector[0] === '>' ?
56
+ el.parentNode === ctx && matches(el, selector) :
57
+ matches(el, selector)
58
+ ) ||
59
+ includeCTX && el === ctx )
60
+ ) {
61
+ return el;
62
+ }
63
+
64
+ if (el === ctx) break;
65
+ /* jshint boss:true */
66
+ } while (el = getParentOrHost(el));
67
+ }
68
+
69
+ return null;
70
+ }
71
+
72
+ const R_SPACE = /\s+/g;
73
+
74
+ function toggleClass(el, name, state) {
75
+ if (el && name) {
76
+ if (el.classList) {
77
+ el.classList[state ? 'add' : 'remove'](name);
78
+ }
79
+ else {
80
+ let className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' ');
81
+ el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' ');
82
+ }
83
+ }
84
+ }
85
+
86
+
87
+ function css(el, prop, val) {
88
+ let style = el && el.style;
89
+
90
+ if (style) {
91
+ if (val === void 0) {
92
+ if (document.defaultView && document.defaultView.getComputedStyle) {
93
+ val = document.defaultView.getComputedStyle(el, '');
94
+ }
95
+ else if (el.currentStyle) {
96
+ val = el.currentStyle;
97
+ }
98
+
99
+ return prop === void 0 ? val : val[prop];
100
+ }
101
+ else {
102
+ if (!(prop in style) && prop.indexOf('webkit') === -1) {
103
+ prop = '-webkit-' + prop;
104
+ }
105
+
106
+ style[prop] = val + (typeof val === 'string' ? '' : 'px');
107
+ }
108
+ }
109
+ }
110
+
111
+ function matrix(el, selfOnly) {
112
+ let appliedTransforms = '';
113
+ if (typeof(el) === 'string') {
114
+ appliedTransforms = el;
115
+ } else {
116
+ do {
117
+ let transform = css(el, 'transform');
118
+
119
+ if (transform && transform !== 'none') {
120
+ appliedTransforms = transform + ' ' + appliedTransforms;
121
+ }
122
+ /* jshint boss:true */
123
+ } while (!selfOnly && (el = el.parentNode));
124
+ }
125
+
126
+ const matrixFn = window.DOMMatrix || window.WebKitCSSMatrix || window.CSSMatrix || window.MSCSSMatrix;
127
+ /*jshint -W056 */
128
+ return matrixFn && (new matrixFn(appliedTransforms));
129
+ }
130
+
131
+
132
+ function find(ctx, tagName, iterator) {
133
+ if (ctx) {
134
+ let list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
135
+
136
+ if (iterator) {
137
+ for (; i < n; i++) {
138
+ iterator(list[i], i);
139
+ }
140
+ }
141
+
142
+ return list;
143
+ }
144
+
145
+ return [];
146
+ }
147
+
148
+
149
+
150
+ function getWindowScrollingElement() {
151
+ let scrollingElement = document.scrollingElement;
152
+
153
+ if (scrollingElement) {
154
+ return scrollingElement
155
+ } else {
156
+ return document.documentElement
157
+ }
158
+ }
159
+
160
+
161
+ /**
162
+ * Returns the "bounding client rect" of given element
163
+ * @param {HTMLElement} el The element whose boundingClientRect is wanted
164
+ * @param {[Boolean]} relativeToContainingBlock Whether the rect should be relative to the containing block of (including) the container
165
+ * @param {[Boolean]} relativeToNonStaticParent Whether the rect should be relative to the relative parent of (including) the contaienr
166
+ * @param {[Boolean]} undoScale Whether the container's scale() should be undone
167
+ * @param {[HTMLElement]} container The parent the element will be placed in
168
+ * @return {Object} The boundingClientRect of el, with specified adjustments
169
+ */
170
+ function getRect(el, relativeToContainingBlock, relativeToNonStaticParent, undoScale, container) {
171
+ if (!el.getBoundingClientRect && el !== window) return;
172
+
173
+ let elRect,
174
+ top,
175
+ left,
176
+ bottom,
177
+ right,
178
+ height,
179
+ width;
180
+
181
+ if (el !== window && el.parentNode && el !== getWindowScrollingElement()) {
182
+ elRect = el.getBoundingClientRect();
183
+ top = elRect.top;
184
+ left = elRect.left;
185
+ bottom = elRect.bottom;
186
+ right = elRect.right;
187
+ height = elRect.height;
188
+ width = elRect.width;
189
+ } else {
190
+ top = 0;
191
+ left = 0;
192
+ bottom = window.innerHeight;
193
+ right = window.innerWidth;
194
+ height = window.innerHeight;
195
+ width = window.innerWidth;
196
+ }
197
+
198
+ if ((relativeToContainingBlock || relativeToNonStaticParent) && el !== window) {
199
+ // Adjust for translate()
200
+ container = container || el.parentNode;
201
+
202
+ // solves #1123 (see: https://stackoverflow.com/a/37953806/6088312)
203
+ // Not needed on <= IE11
204
+ if (!IE11OrLess) {
205
+ do {
206
+ if (
207
+ container &&
208
+ container.getBoundingClientRect &&
209
+ (
210
+ css(container, 'transform') !== 'none' ||
211
+ relativeToNonStaticParent &&
212
+ css(container, 'position') !== 'static'
213
+ )
214
+ ) {
215
+ let containerRect = container.getBoundingClientRect();
216
+
217
+ // Set relative to edges of padding box of container
218
+ top -= containerRect.top + parseInt(css(container, 'border-top-width'));
219
+ left -= containerRect.left + parseInt(css(container, 'border-left-width'));
220
+ bottom = top + elRect.height;
221
+ right = left + elRect.width;
222
+
223
+ break;
224
+ }
225
+ /* jshint boss:true */
226
+ } while (container = container.parentNode);
227
+ }
228
+ }
229
+
230
+ if (undoScale && el !== window) {
231
+ // Adjust for scale()
232
+ let elMatrix = matrix(container || el),
233
+ scaleX = elMatrix && elMatrix.a,
234
+ scaleY = elMatrix && elMatrix.d;
235
+
236
+ if (elMatrix) {
237
+ top /= scaleY;
238
+ left /= scaleX;
239
+
240
+ width /= scaleX;
241
+ height /= scaleY;
242
+
243
+ bottom = top + height;
244
+ right = left + width;
245
+ }
246
+ }
247
+
248
+ return {
249
+ top: top,
250
+ left: left,
251
+ bottom: bottom,
252
+ right: right,
253
+ width: width,
254
+ height: height
255
+ };
256
+ }
257
+
258
+ /**
259
+ * Returns the content rect of the element (bounding rect minus border and padding)
260
+ * @param {HTMLElement} el
261
+ */
262
+ function getContentRect(el) {
263
+ let rect = getRect(el);
264
+ const paddingLeft = parseInt(css(el, 'padding-left')),
265
+ paddingTop = parseInt(css(el, 'padding-top')),
266
+ paddingRight = parseInt(css(el, 'padding-right')),
267
+ paddingBottom = parseInt(css(el, 'padding-bottom'));
268
+ rect.top += paddingTop + parseInt(css(el, 'border-top-width'));
269
+ rect.left += paddingLeft + parseInt(css(el, 'border-left-width'));
270
+ // Client Width/Height includes padding only
271
+ rect.width = el.clientWidth - paddingLeft - paddingRight;
272
+ rect.height = el.clientHeight - paddingTop - paddingBottom;
273
+ rect.bottom = rect.top + rect.height;
274
+ rect.right = rect.left + rect.width;
275
+ return rect;
276
+ }
277
+
278
+ /**
279
+ * Checks if a side of an element is scrolled past a side of its parents
280
+ * @param {HTMLElement} el The element who's side being scrolled out of view is in question
281
+ * @param {String} elSide Side of the element in question ('top', 'left', 'right', 'bottom')
282
+ * @param {String} parentSide Side of the parent in question ('top', 'left', 'right', 'bottom')
283
+ * @return {HTMLElement} The parent scroll element that the el's side is scrolled past, or null if there is no such element
284
+ */
285
+ function isScrolledPast(el, elSide, parentSide) {
286
+ let parent = getParentAutoScrollElement(el, true),
287
+ elSideVal = getRect(el)[elSide];
288
+
289
+ /* jshint boss:true */
290
+ while (parent) {
291
+ let parentSideVal = getRect(parent)[parentSide],
292
+ visible;
293
+
294
+ if (parentSide === 'top' || parentSide === 'left') {
295
+ visible = elSideVal >= parentSideVal;
296
+ } else {
297
+ visible = elSideVal <= parentSideVal;
298
+ }
299
+
300
+ if (!visible) return parent;
301
+
302
+ if (parent === getWindowScrollingElement()) break;
303
+
304
+ parent = getParentAutoScrollElement(parent, false);
305
+ }
306
+
307
+ return false;
308
+ }
309
+
310
+
311
+
312
+ /**
313
+ * Gets nth child of el, ignoring hidden children, sortable's elements (does not ignore clone if it's visible)
314
+ * and non-draggable elements
315
+ * @param {HTMLElement} el The parent element
316
+ * @param {Number} childNum The index of the child
317
+ * @param {Object} options Parent Sortable's options
318
+ * @param {boolean} includeDragEl include dragging item
319
+ * @param {HTMLElement} ignoreEl exclude item
320
+ * @return {HTMLElement} The child at index childNum, or null if not found
321
+ */
322
+ function getChild(el, childNum, options, includeDragEl, ignoreEl) {
323
+ let currentChild = 0,
324
+ i = 0,
325
+ children = el.children;
326
+
327
+ while (i < children.length) {
328
+ if (
329
+ children[i] !== ignoreEl &&
330
+ children[i].style.display !== 'none' &&
331
+ children[i] !== Sortable.ghost &&
332
+ (includeDragEl || children[i] !== Sortable.dragged) &&
333
+ closest(children[i], options.draggable, el, false)
334
+ ) {
335
+ if (currentChild === childNum) {
336
+ return children[i];
337
+ }
338
+ currentChild++;
339
+ }
340
+
341
+ i++;
342
+ }
343
+ return null;
344
+ }
345
+
346
+ /**
347
+ * Gets the last child in the el, ignoring ghostEl or invisible elements (clones)
348
+ * @param {HTMLElement} el Parent element
349
+ * @param {selector} selector Any other elements that should be ignored
350
+ * @return {HTMLElement} The last child, ignoring ghostEl
351
+ */
352
+ function lastChild(el, selector) {
353
+ let last = el.lastElementChild;
354
+
355
+ while (
356
+ last &&
357
+ (
358
+ last === Sortable.ghost ||
359
+ css(last, 'display') === 'none' ||
360
+ selector && !matches(last, selector)
361
+ )
362
+ ) {
363
+ last = last.previousElementSibling;
364
+ }
365
+
366
+ return last || null;
367
+ }
368
+
369
+
370
+ /**
371
+ * Returns the index of an element within its parent for a selected set of
372
+ * elements
373
+ * @param {HTMLElement} el
374
+ * @param {selector} selector
375
+ * @param {HTMLElement} ignoreEl
376
+ * @return {number}
377
+ */
378
+ function index(el, selector, ignoreEl) {
379
+ let index = 0;
380
+
381
+ if (!el || !el.parentNode) {
382
+ return -1;
383
+ }
384
+
385
+ /* jshint boss:true */
386
+ while (el = el.previousElementSibling) {
387
+ if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && el !== Sortable.clone && (!selector || matches(el, selector)) && (el !== ignoreEl)) {
388
+ index++;
389
+ }
390
+ }
391
+
392
+ return index;
393
+ }
394
+
395
+ /**
396
+ * Returns the scroll offset of the given element, added with all the scroll offsets of parent elements.
397
+ * The value is returned in real pixels.
398
+ * @param {HTMLElement} el
399
+ * @return {Array} Offsets in the format of [left, top]
400
+ */
401
+ function getRelativeScrollOffset(el) {
402
+ let offsetLeft = 0,
403
+ offsetTop = 0,
404
+ winScroller = getWindowScrollingElement();
405
+
406
+ if (el) {
407
+ do {
408
+ let elMatrix = matrix(el),
409
+ scaleX = elMatrix.a,
410
+ scaleY = elMatrix.d;
411
+
412
+ offsetLeft += el.scrollLeft * scaleX;
413
+ offsetTop += el.scrollTop * scaleY;
414
+ } while (el !== winScroller && (el = el.parentNode));
415
+ }
416
+
417
+ return [offsetLeft, offsetTop];
418
+ }
419
+
420
+ /**
421
+ * Returns the index of the object within the given array
422
+ * @param {Array} arr Array that may or may not hold the object
423
+ * @param {Object} obj An object that has a key-value pair unique to and identical to a key-value pair in the object you want to find
424
+ * @return {Number} The index of the object in the array, or -1
425
+ */
426
+ function indexOfObject(arr, obj) {
427
+ for (let i in arr) {
428
+ if (!arr.hasOwnProperty(i)) continue;
429
+ for (let key in obj) {
430
+ if (obj.hasOwnProperty(key) && obj[key] === arr[i][key]) return Number(i);
431
+ }
432
+ }
433
+ return -1;
434
+ }
435
+
436
+
437
+ function getParentAutoScrollElement(el, includeSelf) {
438
+ // skip to window
439
+ if (!el || !el.getBoundingClientRect) return getWindowScrollingElement();
440
+
441
+ let elem = el;
442
+ let gotSelf = false;
443
+ do {
444
+ // we don't need to get elem css if it isn't even overflowing in the first place (performance)
445
+ if (elem.clientWidth < elem.scrollWidth || elem.clientHeight < elem.scrollHeight) {
446
+ let elemCSS = css(elem);
447
+ if (
448
+ elem.clientWidth < elem.scrollWidth && (elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll') ||
449
+ elem.clientHeight < elem.scrollHeight && (elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll')
450
+ ) {
451
+ if (!elem.getBoundingClientRect || elem === document.body) return getWindowScrollingElement();
452
+
453
+ if (gotSelf || includeSelf) return elem;
454
+ gotSelf = true;
455
+ }
456
+ }
457
+ /* jshint boss:true */
458
+ } while (elem = elem.parentNode);
459
+
460
+ return getWindowScrollingElement();
461
+ }
462
+
463
+ function extend(dst, src) {
464
+ if (dst && src) {
465
+ for (let key in src) {
466
+ if (src.hasOwnProperty(key)) {
467
+ dst[key] = src[key];
468
+ }
469
+ }
470
+ }
471
+
472
+ return dst;
473
+ }
474
+
475
+
476
+ function isRectEqual(rect1, rect2) {
477
+ return Math.round(rect1.top) === Math.round(rect2.top) &&
478
+ Math.round(rect1.left) === Math.round(rect2.left) &&
479
+ Math.round(rect1.height) === Math.round(rect2.height) &&
480
+ Math.round(rect1.width) === Math.round(rect2.width);
481
+ }
482
+
483
+
484
+ let _throttleTimeout;
485
+ function throttle(callback, ms) {
486
+ return function () {
487
+ if (!_throttleTimeout) {
488
+ let args = arguments,
489
+ _this = this;
490
+
491
+ if (args.length === 1) {
492
+ callback.call(_this, args[0]);
493
+ } else {
494
+ callback.apply(_this, args);
495
+ }
496
+
497
+ _throttleTimeout = setTimeout(function () {
498
+ _throttleTimeout = void 0;
499
+ }, ms);
500
+ }
501
+ };
502
+ }
503
+
504
+
505
+ function cancelThrottle() {
506
+ clearTimeout(_throttleTimeout);
507
+ _throttleTimeout = void 0;
508
+ }
509
+
510
+
511
+ function scrollBy(el, x, y) {
512
+ el.scrollLeft += x;
513
+ el.scrollTop += y;
514
+ }
515
+
516
+
517
+ function clone(el) {
518
+ let Polymer = window.Polymer;
519
+ let $ = window.jQuery || window.Zepto;
520
+
521
+ if (Polymer && Polymer.dom) {
522
+ return Polymer.dom(el).cloneNode(true);
523
+ }
524
+ else if ($) {
525
+ return $(el).clone(true)[0];
526
+ }
527
+ else {
528
+ return el.cloneNode(true);
529
+ }
530
+ }
531
+
532
+
533
+ function setRect(el, rect) {
534
+ css(el, 'position', 'absolute');
535
+ css(el, 'top', rect.top);
536
+ css(el, 'left', rect.left);
537
+ css(el, 'width', rect.width);
538
+ css(el, 'height', rect.height);
539
+ }
540
+
541
+ function unsetRect(el) {
542
+ css(el, 'position', '');
543
+ css(el, 'top', '');
544
+ css(el, 'left', '');
545
+ css(el, 'width', '');
546
+ css(el, 'height', '');
547
+ }
548
+
549
+ function getChildContainingRectFromElement(container, options, ghostEl, ignoreEl) {
550
+ const rect = {};
551
+
552
+ Array.from(container.children).forEach(child => {
553
+ if (!closest(child, options.draggable, container, false, ignoreEl) || child.animated || child === ghostEl) return;
554
+ const childRect = getRect(child);
555
+ rect.left = Math.min(rect.left ?? Infinity, childRect.left);
556
+ rect.top = Math.min(rect.top ?? Infinity, childRect.top);
557
+ rect.right = Math.max(rect.right ?? -Infinity, childRect.right);
558
+ rect.bottom = Math.max(rect.bottom ?? -Infinity, childRect.bottom);
559
+ });
560
+ rect.width = rect.right - rect.left;
561
+ rect.height = rect.bottom - rect.top;
562
+ rect.x = rect.left;
563
+ rect.y = rect.top;
564
+ return rect;
565
+ }
566
+
567
+ const expando = 'Sortable' + (new Date).getTime();
568
+
569
+
570
+ export {
571
+ on,
572
+ off,
573
+ matches,
574
+ getParentOrHost,
575
+ closest,
576
+ toggleClass,
577
+ css,
578
+ matrix,
579
+ find,
580
+ getWindowScrollingElement,
581
+ getRect,
582
+ isScrolledPast,
583
+ getChild,
584
+ lastChild,
585
+ index,
586
+ getRelativeScrollOffset,
587
+ indexOfObject,
588
+ getParentAutoScrollElement,
589
+ extend,
590
+ isRectEqual,
591
+ throttle,
592
+ cancelThrottle,
593
+ scrollBy,
594
+ clone,
595
+ setRect,
596
+ unsetRect,
597
+ getContentRect,
598
+ getChildContainingRectFromElement,
599
+ expando
600
+ };