PluginProbe
Booking Calendar / 11.1
Booking Calendar v11.1
11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 10.11.2 10.11.3 All 202 releases
booking / vendors / sortablejs / src / utils.js

utils.js in Booking Calendar 11.1, at vendors/sortablejs/src/utils.js

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