PageSourceSearch

https://tapetfabrikken.no/cdn/shop/t/11/assets/tingle.min.js?v=88069265682991643661774336097

js tapetfabrikken.no collected 2026-10-06 12:54:10 UTC 13,992 bytes, 495 lines download raw bytes

1/**
2 * tingle.js - A simple modal plugin written in pure JavaScript
3 * @version v0.16.0
4 * @link https://github.com/robinparisi/tingle#readme
5 * @license MIT
6 */
7
8/* global define, module */
9(function (root, factory) {
10  if (typeof define === "function" && define.amd) {
11    define(factory);
12  } else if (typeof exports === "object") {
13    module.exports = factory();
14  } else {
15    root.tingle = factory();
16  }
17})(this, function () {
18  /* ----------------------------------------------------------- */
19  /* == modal */
20  /* ----------------------------------------------------------- */
21
22  var isBusy = false;
23
24  function Modal(options) {
25    var defaults = {
26      onClose: null,
27      onOpen: null,
28      beforeOpen: null,
29      beforeClose: null,
30      stickyFooter: false,
31      footer: false,
32      cssClass: [],
33      closeLabel: "Close",
34      closeMethods: ["overlay", "button", "escape"],
35    };
36
37    // extends config
38    this.opts = extend({}, defaults, options);
39
40    // init modal
41    this.init();
42  }
43
44  Modal.prototype.init = function () {
45    if (this.modal) {
46      return;
47    }
48
49    _build.call(this);
50    _bindEvents.call(this);
51
52    // insert modal in dom
53    document.body.appendChild(this.modal, document.body.firstChild);
54
55    if (this.opts.footer) {
56      this.addFooter();
57    }
58
59    return this;
60  };
61
62  Modal.prototype._busy = function (state) {
63    isBusy = state;
64  };
65
66  Modal.prototype._isBusy = function () {
67    return isBusy;
68  };
69
70  Modal.prototype.destroy = function () {
71    if (this.modal === null) {
72      return;
73    }
74
75    // restore scrolling
76    if (this.isOpen()) {
77      this.close(true);
78    }
79
80    // unbind all events
81    _unbindEvents.call(this);
82
83    // remove modal from dom
84    this.modal.parentNode.removeChild(this.modal);
85
86    this.modal = null;
87  };
88
89  Modal.prototype.isOpen = function () {
90    return !!this.modal.classList.contains("tingle-modal--visible");
91  };
92
93  Modal.prototype.open = function () {
94    if (this._isBusy()) return;
95    this._busy(true);
96
97    var self = this;
98
99    // before open callback
100    if (typeof self.opts.beforeOpen === "function") {
101      self.opts.beforeOpen();
102    }
103
104    if (this.modal.style.removeProperty) {
105      this.modal.style.removeProperty("display");
106    } else {
107      this.modal.style.removeAttribute("display");
108    }
109
110    // prevent text selection when opening multiple times
111    document.getSelection().removeAllRanges();
112
113    // prevent double scroll
114    this._scrollPosition = window.pageYOffset;
115    document.body.classList.add("tingle-enabled");
116    document.body.style.top = -this._scrollPosition + "px";
117    // sticky footer
118    this.setStickyFooter(this.opts.stickyFooter);
119
120    // show modal
121    this.modal.classList.add("tingle-modal--visible");
122
123    // onOpen callback
124    if (typeof self.opts.onOpen === "function") {
125      self.opts.onOpen.call(self);
126    }
127
128    self._busy(false);
129
130    // check if modal is bigger than screen height
131    this.checkOverflow();
132
133    return this;
134  };
135
136  Modal.prototype.close = function (force) {
137    if (this._isBusy()) return;
138    this._busy(true);
139    force = force || false;
140
141    //  before close
142    if (typeof this.opts.beforeClose === "function") {
143      var close = this.opts.beforeClose.call(this);
144      if (!close) {
145        this._busy(false);
146        return;
147      }
148    }
149
150    document.body.classList.remove("tingle-enabled");
151    document.body.style.top = null;
152    window.scrollTo({
153      top: this._scrollPosition,
154      behavior: "instant",
155    });
156    this.modal.classList.add("remove-loading");
157
158    // using similar setup as onOpen
159    var self = this;
160
161    if (this.isOpen()) {
162      this.close(true);
163    }
164
165    // unbind all events
166    _unbindEvents.call(this);
167
168    // remove modal from dom
169    setTimeout(function () {
170      self.modal.parentNode.removeChild(self.modal);
171      self.modal = null;
172    }, 600);
173
174    // release modal
175    self._busy(false);
176  };
177
178  Modal.prototype.setContent = function (content) {
179    // check type of content : String or Node
180    if (typeof content === "string") {
181      this.modalBoxContent.innerHTML = content;
182    } else {
183      this.modalBoxContent.innerHTML = "";
184      this.modalBoxContent.appendChild(content);
185    }
186
187    if (this.isOpen()) {
188      // check if modal is bigger than screen height
189      this.checkOverflow();
190    }
191
192    return this;
193  };
194
195  Modal.prototype.getContent = function () {
196    return this.modalBoxContent;
197  };
198
199  Modal.prototype.addFooter = function () {
200    // add footer to modal
201    _buildFooter.call(this);
202
203    return this;
204  };
205
206  Modal.prototype.setFooterContent = function (content) {
207    // set footer content
208    this.modalBoxFooter.innerHTML = content;
209
210    return this;
211  };
212
213  Modal.prototype.getFooterContent = function () {
214    return this.modalBoxFooter;
215  };
216
217  Modal.prototype.setStickyFooter = function (isSticky) {
218    // if the modal is smaller than the viewport height, we don't need sticky
219    if (!this.isOverflow()) {
220      isSticky = false;
221    }
222
223    if (isSticky) {
224      if (this.modalBox.contains(this.modalBoxFooter)) {
225        this.modalBox.removeChild(this.modalBoxFooter);
226        this.modal.appendChild(this.modalBoxFooter);
227        this.modalBoxFooter.classList.add("tingle-modal-box__footer--sticky");
228        _recalculateFooterPosition.call(this);
229      }
230      this.modalBoxContent.style["padding-bottom"] =
231        this.modalBoxFooter.clientHeight + 20 + "px";
232    } else if (this.modalBoxFooter) {
233      if (!this.modalBox.contains(this.modalBoxFooter)) {
234        this.modal.removeChild(this.modalBoxFooter);
235        this.modalBox.appendChild(this.modalBoxFooter);
236        this.modalBoxFooter.style.width = "auto";
237        this.modalBoxFooter.style.left = "";
238        this.modalBoxContent.style["padding-bottom"] = "";
239        this.modalBoxFooter.classList.remove(
240          "tingle-modal-box__footer--sticky"
241        );
242      }
243    }
244
245    return this;
246  };
247
248  Modal.prototype.addFooterBtn = function (label, cssClass, callback) {
249    var btn = document.createElement("button");
250
251    // set label
252    btn.innerHTML = label;
253
254    // bind callback
255    btn.addEventListener("click", callback);
256
257    if (typeof cssClass === "string" && cssClass.length) {
258      // add classes to btn
259      cssClass.split(" ").forEach(function (item) {
260        btn.classList.add(item);
261      });
262    }
263
264    this.modalBoxFooter.appendChild(btn);
265
266    return btn;
267  };
268
269  Modal.prototype.resize = function () {
270    // eslint-disable-next-line no-console
271    console.warn("Resize is deprecated and will be removed in version 1.0");
272  };
273
274  Modal.prototype.isOverflow = function () {
275    var viewportHeight = window.innerHeight;
276    var modalHeight = this.modalBox.clientHeight;
277
278    return modalHeight >= viewportHeight;
279  };
280
281  Modal.prototype.checkOverflow = function () {
282    // only if the modal is currently shown
283    if (this.modal.classList.contains("tingle-modal--visible")) {
284      if (this.isOverflow()) {
285        this.modal.classList.add("tingle-modal--overflow");
286      } else {
287        this.modal.classList.remove("tingle-modal--overflow");
288      }
289
290      if (!this.isOverflow() && this.opts.stickyFooter) {
291        this.setStickyFooter(false);
292      } else if (this.isOverflow() && this.opts.stickyFooter) {
293        _recalculateFooterPosition.call(this);
294        this.setStickyFooter(true);
295      }
296    }
297  };
298
299  /* ----------------------------------------------------------- */
300  /* == private methods */
301  /* ----------------------------------------------------------- */
302
303  function closeIcon() {
304    return '<svg width="13" height="13" viewBox="0 0 13 13" fill="none" class="transition"><use href="#icon-close"></use></svg>';
305  }
306
307  function _scrollbarWidth() {
308    var scrollDiv = document.createElement("div");
309    scrollDiv.style.width = "100px";
310    scrollDiv.style.height = "100px";
311    scrollDiv.style.overflow = "scroll";
312    scrollDiv.style.position = "absolute";
313    scrollDiv.style.top = "-9999px";
314    document.body.appendChild(scrollDiv);
315    var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
316    document.body.removeChild(scrollDiv);
317    return scrollbarWidth;
318  }
319
320  function _recalculateFooterPosition() {
321    if (!this.modalBoxFooter) {
322      return;
323    }
324    this.modalBoxFooter.style.width = this.modalBox.clientWidth + "px";
325    this.modalBoxFooter.style.left = this.modalBox.offsetLeft + "px";
326  }
327
328  function _build() {
329    // wrapper
330    this.modal = document.createElement("div");
331    this.modal.classList.add("tingle-modal");
332
333    // remove cusor if no overlay close method
334    if (
335      this.opts.closeMethods.length === 0 ||
336      this.opts.closeMethods.indexOf("overlay") === -1
337    ) {
338      this.modal.classList.add("tingle-modal--noOverlayClose");
339    }
340
341    this.modal.style.display = "none";
342
343    // custom class
344    this.opts.cssClass.forEach(function (item) {
345      if (typeof item === "string") {
346        this.modal.classList.add(item);
347      }
348    }, this);
349
350    // close btn
351    if (this.opts.closeMethods.indexOf("button") !== -1) {
352      this.modalCloseBtn = document.createElement("button");
353      this.modalCloseBtn.type = "button";
354      this.modalCloseBtn.classList.add(
355        "tingle-modal__close",
356        "btn-reset",
357        "hover-svg-zoom",
358        "pointer",
359        "absolute",
360        "right-10",
361        "top-10",
362        "z-1",
363        "w-44",
364        "h-44",
365        "inline-flex",
366        "content-center"
367      );
368
369      this.modalCloseBtnIcon = document.createElement("span");
370      this.modalCloseBtnIcon.classList.add("tingle-modal__closeIcon");
371      this.modalCloseBtnIcon.innerHTML = closeIcon();
372
373      this.modalCloseBtnLabel = document.createElement("span");
374      this.modalCloseBtnLabel.classList.add("tingle-modal__closeLabel");
375      this.modalCloseBtnLabel.innerHTML = this.opts.closeLabel;
376
377      this.modalCloseBtn.appendChild(this.modalCloseBtnIcon);
378      this.modalCloseBtn.appendChild(this.modalCloseBtnLabel);
379    }
380
381    // modal
382    this.modalBox = document.createElement("div");
383    this.modalBox.classList.add("tingle-modal-box");
384
385    // modal box content
386    this.modalBoxContent = document.createElement("div");
387    this.modalBoxContent.classList.add("tingle-modal-box__content");
388
389    if (this.opts.closeMethods.indexOf("button") !== -1) {
390      this.modalBox.appendChild(this.modalCloseBtn);
391    }
392
393    this.modalBox.appendChild(this.modalBoxContent);
394
395    this.modal.appendChild(this.modalBox);
396  }
397
398  function _buildFooter() {
399    this.modalBoxFooter = document.createElement("div");
400    this.modalBoxFooter.classList.add("tingle-modal-box__footer");
401    this.modalBox.appendChild(this.modalBoxFooter);
402  }
403
404  function _bindEvents() {
405    this._events = {
406      clickCloseBtn: this.close.bind(this),
407      clickOverlay: _handleClickOutside.bind(this),
408      resize: this.checkOverflow.bind(this),
409      keyboardNav: _handleKeyboardNav.bind(this),
410    };
411
412    if (this.opts.closeMethods.indexOf("button") !== -1) {
413      this.modalCloseBtn.addEventListener("click", this._events.clickCloseBtn);
414    }
415
416    this.modal.addEventListener("mousedown", this._events.clickOverlay);
417    window.addEventListener("resize", this._events.resize);
418    document.addEventListener("keydown", this._events.keyboardNav);
419  }
420
421  function _handleKeyboardNav(event) {
422    // escape key
423    if (
424      this.opts.closeMethods.indexOf("escape") !== -1 &&
425      event.which === 27 &&
426      this.isOpen()
427    ) {
428      this.close();
429    }
430  }
431
432  function _handleClickOutside(event) {
433    // on macOS, click on scrollbar (hidden mode) will trigger close event so we need to bypass this behavior b
433y detecting scrollbar mode
434    var scrollbarWidth = this.modal.offsetWidth - this.modal.clientWidth;
435    var clickedOnScrollbar = event.clientX >= this.modal.offsetWidth - 15; // 15px is macOS scrollbar default width
436    var isScrollable = this.modal.scrollHeight !== this.modal.offsetHeight;
437    if (
438      navigator.platform === "MacIntel" &&
439      scrollbarWidth === 0 &&
440      clickedOnScrollbar &&
441      isScrollable
442    ) {
443      return;
444    }
445
446    // if click is outside the modal
447    if (
448      this.opts.closeMethods.indexOf("overlay") !== -1 &&
449      !_findAncestor(event.target, "tingle-modal") &&
450      event.clientX < this.modal.clientWidth
451    ) {
452      this.close();
453    }
454  }
455
456  function _findAncestor(el, cls) {
457    while ((el = el.parentElement) && !el.classList.contains(cls));
458    return el;
459  }
460
461  function _unbindEvents() {
462    if (this.opts.closeMethods.indexOf("button") !== -1) {
463      this.modalCloseBtn.removeEventListener(
464        "click",
465        this._events.clickCloseBtn
466      );
467    }
468    this.modal.removeEventListener("mousedown", this._events.clickOverlay);
469    window.removeEventListener("resize", this._events.resize);
470    document.removeEventListener("keydown", this._events.keyboardNav);
471  }
472
473  /* ----------------------------------------------------------- */
474  /* == helpers */
475  /* ----------------------------------------------------------- */
476
477  function extend() {
478    for (var i = 1; i < arguments.length; i++) {
479      for (var key in arguments[i]) {
480        if (arguments[i].hasOwnProperty(key)) {
481          arguments[0][key] = arguments[i][key];
482        }
483      }
484    }
485    return arguments[0];
486  }
487
488  /* ----------------------------------------------------------- */
489  /* == return */
490  /* ----------------------------------------------------------- */
491
492  return {
493    modal: Modal,
494  };
495});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.