PluginProbe
Boxzilla – WordPress Popup Builder / 3.1.14
Boxzilla – WordPress Popup Builder v3.1.14
3.4.11 3.4.10 3.4.9 3.4.3 3.4.4 3.4.5 3.4.6 3.4.7 3.4.8 trunk 3.0 3.0.1 3.0.2 3.0.3 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 All 72 releases
boxzilla / assets / js / script.js

script.js in Boxzilla – WordPress Popup Builder 3.1.14, at assets/js/script.js

1,530 lines 48.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () { var require = undefined; var module = undefined; var exports = undefined; var define = undefined; (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2 'use strict';
3
4 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
5
6 (function () {
7 'use strict';
8
9 var Boxzilla = require('boxzilla');
10 var options = window.boxzilla_options;
11
12 // expose Boxzilla object to window
13 window.Boxzilla = Boxzilla;
14
15 // helper function for setting CSS styles
16 function css(element, styles) {
17 if (styles.background_color) {
18 element.style.background = styles.background_color;
19 }
20
21 if (styles.color) {
22 element.style.color = styles.color;
23 }
24
25 if (styles.border_color) {
26 element.style.borderColor = styles.border_color;
27 }
28
29 if (styles.border_width) {
30 element.style.borderWidth = parseInt(styles.border_width) + "px";
31 }
32
33 if (styles.border_style) {
34 element.style.borderStyle = styles.border_style;
35 }
36
37 if (styles.width) {
38 element.style.maxWidth = parseInt(styles.width) + "px";
39 }
40 }
41
42 function createBoxesFromConfig() {
43 var isLoggedIn = document.body.className.indexOf('logged-in') > -1;
44
45 // failsafe against including script twice.
46 if (options.inited) {
47 return;
48 }
49
50 // print message when test mode is enabled
51 if (isLoggedIn && options.testMode) {
52 console.log('Boxzilla: Test mode is enabled. Please disable test mode if you\'re done testing.');
53 }
54
55 // init boxzilla
56 Boxzilla.init();
57
58 // create boxes from options
59 for (var i = 0; i < options.boxes.length; i++) {
60 // get opts
61 var boxOpts = options.boxes[i];
62 boxOpts.testMode = isLoggedIn && options.testMode;
63
64 // fix http:// links in box content....
65 if (window.location.protocol === "https:" && window.location.host) {
66 var o = "http://" + window.location.host;
67 var n = o.replace('http://', 'https://');
68 boxOpts.content = boxOpts.content.replace(o, n);
69 }
70
71 // create box
72 var box = Boxzilla.create(boxOpts.id, boxOpts);
73
74 // add box slug to box element as classname
75 box.element.className = box.element.className + ' boxzilla-' + boxOpts.post.slug;
76
77 // add custom css to box
78 css(box.element, boxOpts.css);
79
80 box.element.firstChild.firstChild.className += " first-child";
81 box.element.firstChild.lastChild.className += " last-child";
82 }
83
84 // set flag to prevent initialising twice
85 options.inited = true;
86
87 // trigger "done" event.
88 Boxzilla.trigger('done');
89 }
90
91 function openMailChimpForWordPressBox() {
92 if (_typeof(window.mc4wp_forms_config) === "object" && window.mc4wp_forms_config.submitted_form) {
93 var selector = '#' + window.mc4wp_forms_config.submitted_form.element_id;
94 var boxes = Boxzilla.boxes;
95 for (var boxId in boxes) {
96 if (!boxes.hasOwnProperty(boxId)) {
97 continue;
98 }
99 var box = boxes[boxId];
100 if (box.element.querySelector(selector)) {
101 box.show();
102 return;
103 }
104 }
105 }
106 }
107
108 window.addEventListener('load', openMailChimpForWordPressBox);
109 createBoxesFromConfig();
110 })();
111
112 },{"boxzilla":4}],2:[function(require,module,exports){
113 'use strict';
114
115 var duration = 320;
116
117 function css(element, styles) {
118 for (var property in styles) {
119 element.style[property] = styles[property];
120 }
121 }
122
123 function initObjectProperties(properties, value) {
124 var newObject = {};
125 for (var i = 0; i < properties.length; i++) {
126 newObject[properties[i]] = value;
127 }
128 return newObject;
129 }
130
131 function copyObjectProperties(properties, object) {
132 var newObject = {};
133 for (var i = 0; i < properties.length; i++) {
134 newObject[properties[i]] = object[properties[i]];
135 }
136 return newObject;
137 }
138
139 /**
140 * Checks if the given element is currently being animated.
141 *
142 * @param element
143 * @returns {boolean}
144 */
145 function animated(element) {
146 return !!element.getAttribute('data-animated');
147 }
148
149 /**
150 * Toggles the element using the given animation.
151 *
152 * @param element
153 * @param animation Either "fade" or "slide"
154 */
155 function toggle(element, animation, callbackFn) {
156 var nowVisible = element.style.display != 'none' || element.offsetLeft > 0;
157
158 // create clone for reference
159 var clone = element.cloneNode(true);
160 var cleanup = function cleanup() {
161 element.removeAttribute('data-animated');
162 element.setAttribute('style', clone.getAttribute('style'));
163 element.style.display = nowVisible ? 'none' : '';
164 if (callbackFn) {
165 callbackFn();
166 }
167 };
168
169 // store attribute so everyone knows we're animating this element
170 element.setAttribute('data-animated', "true");
171
172 // toggle element visiblity right away if we're making something visible
173 if (!nowVisible) {
174 element.style.display = '';
175 }
176
177 var hiddenStyles, visibleStyles;
178
179 // animate properties
180 if (animation === 'slide') {
181 hiddenStyles = initObjectProperties(["height", "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"], 0);
182 visibleStyles = {};
183
184 if (!nowVisible) {
185 var computedStyles = window.getComputedStyle(element);
186 visibleStyles = copyObjectProperties(["height", "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"], computedStyles);
187
188 // in some browsers, getComputedStyle returns "auto" value. this falls back to getBoundingClientRect() in those browsers since we need an actual height.
189 if (!isFinite(visibleStyles.height)) {
190 var clientRect = element.getBoundingClientRect();
191 visibleStyles.height = clientRect.height;
192 }
193 css(element, hiddenStyles);
194 }
195
196 // don't show a scrollbar during animation
197 element.style.overflowY = 'hidden';
198 animate(element, nowVisible ? hiddenStyles : visibleStyles, cleanup);
199 } else {
200 hiddenStyles = { opacity: 0 };
201 visibleStyles = { opacity: 1 };
202 if (!nowVisible) {
203 css(element, hiddenStyles);
204 }
205
206 animate(element, nowVisible ? hiddenStyles : visibleStyles, cleanup);
207 }
208 }
209
210 function animate(element, targetStyles, fn) {
211 var last = +new Date();
212 var initialStyles = window.getComputedStyle(element);
213 var currentStyles = {};
214 var propSteps = {};
215
216 for (var property in targetStyles) {
217 // make sure we have an object filled with floats
218 targetStyles[property] = parseFloat(targetStyles[property]);
219
220 // calculate step size & current value
221 var to = targetStyles[property];
222 var current = parseFloat(initialStyles[property]);
223
224 // is there something to do?
225 if (current == to) {
226 delete targetStyles[property];
227 continue;
228 }
229
230 propSteps[property] = (to - current) / duration; // points per second
231 currentStyles[property] = current;
232 }
233
234 var tick = function tick() {
235 var now = +new Date();
236 var timeSinceLastTick = now - last;
237 var done = true;
238
239 var step, to, increment, newValue;
240 for (var property in targetStyles) {
241 step = propSteps[property];
242 to = targetStyles[property];
243 increment = step * timeSinceLastTick;
244 newValue = currentStyles[property] + increment;
245
246 if (step > 0 && newValue >= to || step < 0 && newValue <= to) {
247 newValue = to;
248 } else {
249 done = false;
250 }
251
252 // store new value
253 currentStyles[property] = newValue;
254
255 var suffix = property !== "opacity" ? "px" : "";
256 element.style[property] = newValue + suffix;
257 }
258
259 last = +new Date();
260
261 // keep going until we're done for all props
262 if (!done) {
263 window.requestAnimationFrame && requestAnimationFrame(tick) || setTimeout(tick, 32);
264 } else {
265 // call callback
266 fn && fn();
267 }
268 };
269
270 tick();
271 }
272
273 module.exports = {
274 'toggle': toggle,
275 'animate': animate,
276 'animated': animated
277 };
278
279 },{}],3:[function(require,module,exports){
280 'use strict';
281
282 var defaults = {
283 'animation': 'fade',
284 'rehide': false,
285 'content': '',
286 'cookie': null,
287 'icon': '&times',
288 'screenWidthCondition': null,
289 'position': 'center',
290 'testMode': false,
291 'trigger': false,
292 'closable': true
293 },
294 Boxzilla,
295 Animator = require('./animator.js');
296
297 /**
298 * Merge 2 objects, values of the latter overwriting the former.
299 *
300 * @param obj1
301 * @param obj2
302 * @returns {*}
303 */
304 function merge(obj1, obj2) {
305 var obj3 = {};
306 for (var attrname in obj1) {
307 obj3[attrname] = obj1[attrname];
308 }
309 for (var attrname in obj2) {
310 obj3[attrname] = obj2[attrname];
311 }
312 return obj3;
313 }
314
315 /**
316 * Get the real height of entire document.
317 * @returns {number}
318 */
319 function getDocumentHeight() {
320 var body = document.body,
321 html = document.documentElement;
322
323 var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
324
325 return height;
326 }
327
328 // Box Object
329 var Box = function Box(id, config) {
330 this.id = id;
331
332 // store config values
333 this.config = merge(defaults, config);
334
335 // store ref to overlay
336 this.overlay = document.getElementById('boxzilla-overlay');
337
338 // state
339 this.visible = false;
340 this.dismissed = false;
341 this.triggered = false;
342 this.triggerHeight = 0;
343 this.cookieSet = false;
344 this.element = null;
345 this.contentElement = null;
346 this.closeIcon = null;
347
348 // if a trigger was given, calculate values once and store
349 if (this.config.trigger) {
350 if (this.config.trigger.method === 'percentage' || this.config.trigger.method === 'element') {
351 this.triggerHeight = this.calculateTriggerHeight();
352 }
353
354 this.cookieSet = this.isCookieSet();
355 }
356
357 // create dom elements for this box
358 this.dom();
359
360 // further initialise the box
361 this.events();
362 };
363
364 // initialise the box
365 Box.prototype.events = function () {
366 var box = this;
367
368 // attach event to "close" icon inside box
369 if (this.closeIcon) {
370 this.closeIcon.addEventListener('click', this.dismiss.bind(this));
371 }
372
373 this.element.addEventListener('click', function (e) {
374 if (e.target.tagName === 'A') {
375 Boxzilla.trigger('box.interactions.link', [box, e.target]);
376 }
377 }, false);
378
379 this.element.addEventListener('submit', function (e) {
380 box.setCookie();
381 Boxzilla.trigger('box.interactions.form', [box, e.target]);
382 }, false);
383
384 // maybe show box right away
385 if (this.fits() && this.locationHashRefersBox()) {
386 window.addEventListener('load', this.show.bind(this));
387 }
388 };
389
390 // generate dom elements for this box
391 Box.prototype.dom = function () {
392 var wrapper = document.createElement('div');
393 wrapper.className = 'boxzilla-container boxzilla-' + this.config.position + '-container';
394
395 var box = document.createElement('div');
396 box.setAttribute('id', 'boxzilla-' + this.id);
397 box.className = 'boxzilla boxzilla-' + this.id + ' boxzilla-' + this.config.position;
398 box.style.display = 'none';
399 wrapper.appendChild(box);
400
401 var content = document.createElement('div');
402 content.className = 'boxzilla-content';
403 content.innerHTML = this.config.content;
404 box.appendChild(content);
405
406 // remove <script> from box content and append them to the document body
407 var scripts = content.querySelectorAll('script');
408 if (scripts.length) {
409 for (var i = 0; i < scripts.length; i++) {
410 var script = document.createElement('script');
411 if (scripts[i].src) {
412 script.src = scripts[i].src;
413 }
414 script.appendChild(document.createTextNode(scripts[i].text));
415 scripts[i].parentNode.removeChild(scripts[i]);
416 document.body.appendChild(script);
417 }
418 }
419
420 if (this.config.closable && this.config.icon) {
421 var closeIcon = document.createElement('span');
422 closeIcon.className = "boxzilla-close-icon";
423 closeIcon.innerHTML = this.config.icon;
424 box.appendChild(closeIcon);
425 this.closeIcon = closeIcon;
426 }
427
428 document.body.appendChild(wrapper);
429 this.contentElement = content;
430 this.element = box;
431 };
432
433 // set (calculate) custom box styling depending on box options
434 Box.prototype.setCustomBoxStyling = function () {
435
436 // reset element to its initial state
437 var origDisplay = this.element.style.display;
438 this.element.style.display = '';
439 this.element.style.overflowY = 'auto';
440 this.element.style.maxHeight = 'none';
441
442 // get new dimensions
443 var windowHeight = window.innerHeight;
444 var boxHeight = this.element.clientHeight;
445
446 // add scrollbar to box and limit height
447 if (boxHeight > windowHeight) {
448 this.element.style.maxHeight = windowHeight + "px";
449 this.element.style.overflowY = 'scroll';
450 }
451
452 // set new top margin for boxes which are centered
453 if (this.config.position === 'center') {
454 var newTopMargin = (windowHeight - boxHeight) / 2;
455 newTopMargin = newTopMargin >= 0 ? newTopMargin : 0;
456 this.element.style.marginTop = newTopMargin + "px";
457 }
458
459 this.element.style.display = origDisplay;
460 };
461
462 // toggle visibility of the box
463 Box.prototype.toggle = function (show) {
464
465 // revert visibility if no explicit argument is given
466 if (typeof show === "undefined") {
467 show = !this.visible;
468 }
469
470 // is box already at desired visibility?
471 if (show === this.visible) {
472 return false;
473 }
474
475 // is box being animated?
476 if (Animator.animated(this.element)) {
477 return false;
478 }
479
480 // if box should be hidden but is not closable, bail.
481 if (!show && !this.config.closable) {
482 return false;
483 }
484
485 // set new visibility status
486 this.visible = show;
487
488 // calculate new styling rules
489 this.setCustomBoxStyling();
490
491 // trigger event
492 Boxzilla.trigger('box.' + (show ? 'show' : 'hide'), [this]);
493
494 // show or hide box using selected animation
495 if (this.config.position === 'center') {
496 this.overlay.classList.toggle('boxzilla-' + this.id + '-overlay');
497 Animator.toggle(this.overlay, "fade");
498 }
499
500 Animator.toggle(this.element, this.config.animation, function () {
501 if (this.visible) {
502 return;
503 }
504 this.contentElement.innerHTML = this.contentElement.innerHTML;
505 }.bind(this));
506
507 return true;
508 };
509
510 // show the box
511 Box.prototype.show = function () {
512 return this.toggle(true);
513 };
514
515 // hide the box
516 Box.prototype.hide = function () {
517 return this.toggle(false);
518 };
519
520 // calculate trigger height
521 Box.prototype.calculateTriggerHeight = function () {
522 var triggerHeight = 0;
523
524 if (this.config.trigger.method === 'element') {
525 var triggerElement = document.body.querySelector(this.config.trigger.value);
526 if (triggerElement) {
527 var offset = triggerElement.getBoundingClientRect();
528 triggerHeight = offset.top;
529 }
530 } else if (this.config.trigger.method === 'percentage') {
531 triggerHeight = this.config.trigger.value / 100 * getDocumentHeight();
532 }
533
534 return triggerHeight;
535 };
536
537 // checks whether window.location.hash equals the box element ID or that of any element inside the box
538 Box.prototype.locationHashRefersBox = function () {
539
540 if (!window.location.hash || 0 === window.location.hash.length) {
541 return false;
542 }
543
544 var elementId = window.location.hash.substring(1);
545
546 // only attempt on strings looking like an ID or classname
547 var regex = /^[a-zA-Z\-\_0-9]{1,}$/;
548 if (regex.test(elementId)) {
549 return false;
550 }
551
552 if (elementId === this.element.id) {
553 return true;
554 } else if (this.element.querySelector('#' + elementId)) {
555 return true;
556 }
557
558 return false;
559 };
560
561 Box.prototype.fits = function () {
562 if (!this.config.screenWidthCondition || !this.config.screenWidthCondition.value) {
563 return true;
564 }
565
566 switch (this.config.screenWidthCondition.condition) {
567 case "larger":
568 return window.innerWidth > this.config.screenWidthCondition.value;
569 case "smaller":
570 return window.innerWidth < this.config.screenWidthCondition.value;
571 }
572
573 // meh.. condition should be "smaller" or "larger", just return true.
574 return true;
575 };
576
577 // is this box enabled?
578 Box.prototype.mayAutoShow = function () {
579
580 if (this.dismissed) {
581 return false;
582 }
583
584 // check if box fits on given minimum screen width
585 if (!this.fits()) {
586 return false;
587 }
588
589 // if trigger empty or error in calculating triggerHeight, return false
590 if (!this.config.trigger) {
591 return false;
592 }
593
594 // rely on cookie value (show if not set, don't show if set)
595 return !this.cookieSet;
596 };
597
598 Box.prototype.mayRehide = function () {
599 return this.config.rehide && this.triggered;
600 };
601
602 Box.prototype.isCookieSet = function () {
603 // always show on test mode
604 if (this.config.testMode) {
605 return false;
606 }
607
608 // if either cookie is null or trigger & dismiss are both falsey, don't bother checking.
609 if (!this.config.cookie || !this.config.cookie.triggered && !this.config.cookie.dismissed) {
610 return false;
611 }
612
613 var cookieSet = document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + 'boxzilla_box_' + this.id + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1") === "true";
614 return cookieSet;
615 };
616
617 // set cookie that disables automatically showing the box
618 Box.prototype.setCookie = function (hours) {
619 var expiryDate = new Date();
620 expiryDate.setHours(expiryDate.getHours() + hours);
621 document.cookie = 'boxzilla_box_' + this.id + '=true; expires=' + expiryDate.toUTCString() + '; path=/';
622 };
623
624 Box.prototype.trigger = function () {
625 var shown = this.show();
626 if (!shown) {
627 return;
628 }
629
630 this.triggered = true;
631 if (this.config.cookie && this.config.cookie.triggered) {
632 this.setCookie(this.config.cookie.triggered);
633 }
634 };
635
636 /**
637 * Dismisses the box and optionally sets a cookie.
638 *
639 * @param e The event that triggered this dismissal.
640 * @returns {boolean}
641 */
642 Box.prototype.dismiss = function (e) {
643 // prevent default action
644 e && e.preventDefault();
645
646 // only dismiss box if it's currently open.
647 if (!this.visible) {
648 return false;
649 }
650
651 // hide box element
652 this.hide();
653
654 // set cookie
655 if (this.config.cookie && this.config.cookie.dismissed) {
656 this.setCookie(this.config.cookie.dismissed);
657 }
658
659 this.dismissed = true;
660 Boxzilla.trigger('box.dismiss', [this]);
661 return true;
662 };
663
664 module.exports = function (_Boxzilla) {
665 Boxzilla = _Boxzilla;
666 return Box;
667 };
668
669 },{"./animator.js":2}],4:[function(require,module,exports){
670 'use strict';
671
672 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
673
674 var EventEmitter = require('wolfy87-eventemitter'),
675 Boxzilla = Object.create(EventEmitter.prototype),
676 Box = require('./box.js')(Boxzilla),
677 Timer = require('./timer.js'),
678 boxes = [],
679 overlay,
680 scrollElement = window,
681 exitIntentDelayTimer,
682 exitIntentTriggered,
683 siteTimer,
684 pageTimer,
685 pageViews;
686
687 function throttle(fn, threshhold, scope) {
688 threshhold || (threshhold = 250);
689 var last, deferTimer;
690 return function () {
691 var context = scope || this;
692
693 var now = +new Date(),
694 args = arguments;
695 if (last && now < last + threshhold) {
696 // hold on to it
697 clearTimeout(deferTimer);
698 deferTimer = setTimeout(function () {
699 last = now;
700 fn.apply(context, args);
701 }, threshhold);
702 } else {
703 last = now;
704 fn.apply(context, args);
705 }
706 };
707 }
708
709 // "keyup" listener
710 function onKeyUp(e) {
711 if (e.keyCode == 27) {
712 Boxzilla.dismiss();
713 }
714 }
715
716 // check "pageviews" criteria for each box
717 function checkPageViewsCriteria() {
718
719 // don't bother if another box is currently open
720 if (isAnyBoxVisible()) {
721 return;
722 }
723
724 boxes.forEach(function (box) {
725 if (!box.mayAutoShow()) {
726 return;
727 }
728
729 if (box.config.trigger.method === 'pageviews' && pageViews >= box.config.trigger.value) {
730 box.trigger();
731 }
732 });
733 }
734
735 // check time trigger criteria for each box
736 function checkTimeCriteria() {
737 // don't bother if another box is currently open
738 if (isAnyBoxVisible()) {
739 return;
740 }
741
742 boxes.forEach(function (box) {
743 if (!box.mayAutoShow()) {
744 return;
745 }
746
747 // check "time on site" trigger
748 if (box.config.trigger.method === 'time_on_site' && siteTimer.time >= box.config.trigger.value) {
749 box.trigger();
750 }
751
752 // check "time on page" trigger
753 if (box.config.trigger.method === 'time_on_page' && pageTimer.time >= box.config.trigger.value) {
754 box.trigger();
755 }
756 });
757 }
758
759 // check triggerHeight criteria for all boxes
760 function checkHeightCriteria() {
761
762 var scrollY = scrollElement.hasOwnProperty('scrollY') ? scrollElement.scrollY : scrollElement.scrollTop;
763 scrollY = scrollY + window.innerHeight * 0.9;
764
765 boxes.forEach(function (box) {
766 if (!box.mayAutoShow() || box.triggerHeight <= 0) {
767 return;
768 }
769
770 if (scrollY > box.triggerHeight) {
771 // don't bother if another box is currently open
772 if (isAnyBoxVisible()) {
773 return;
774 }
775
776 // trigger box
777 box.trigger();
778 } else if (box.mayRehide()) {
779 box.hide();
780 }
781 });
782 }
783
784 // recalculate heights and variables based on height
785 function recalculateHeights() {
786 boxes.forEach(function (box) {
787 box.setCustomBoxStyling();
788 });
789 }
790
791 function onOverlayClick(e) {
792 var x = e.offsetX;
793 var y = e.offsetY;
794
795 // calculate if click was less than 40px outside box to avoid closing it by accident
796 boxes.forEach(function (box) {
797 var rect = box.element.getBoundingClientRect();
798 var margin = 40;
799
800 // if click was not anywhere near box, dismiss it.
801 if (x < rect.left - margin || x > rect.right + margin || y < rect.top - margin || y > rect.bottom + margin) {
802 box.dismiss();
803 }
804 });
805 }
806
807 function triggerExitIntent() {
808 // do nothing if already triggered OR another box is visible.
809 if (exitIntentTriggered || isAnyBoxVisible()) {
810 return;
811 }
812
813 boxes.forEach(function (box) {
814 if (box.mayAutoShow() && box.config.trigger.method === 'exit_intent') {
815 box.trigger();
816 }
817 });
818
819 exitIntentTriggered = true;
820 }
821
822 function onMouseLeave(e) {
823 var delay = 400;
824
825 // did mouse leave at top of window?
826 if (e.clientY <= 0) {
827 exitIntentDelayTimer = window.setTimeout(triggerExitIntent, delay);
828 }
829 }
830
831 function isAnyBoxVisible() {
832
833 for (var i = 0; i < boxes.length; i++) {
834 var box = boxes[i];
835
836 if (box.visible) {
837 return true;
838 }
839 }
840
841 return false;
842 }
843
844 function onMouseEnter() {
845 if (exitIntentDelayTimer) {
846 window.clearInterval(exitIntentDelayTimer);
847 exitIntentDelayTimer = null;
848 }
849 }
850
851 function onElementClick(e) {
852 // find <a> element in up to 3 parent elements
853 var el = e.target || e.srcElement;
854 var depth = 3;
855 for (var i = 0; i <= depth; i++) {
856 if (!el || el.tagName === 'A') {
857 break;
858 }
859
860 el = el.parentElement;
861 }
862
863 if (el && el.tagName === 'A' && el.getAttribute('href').toLowerCase().indexOf('#boxzilla-') === 0) {
864 var boxId = el.getAttribute('href').toLowerCase().substring("#boxzilla-".length);
865 Boxzilla.toggle(boxId);
866 }
867 }
868
869 var timers = {
870 start: function start() {
871 try {
872 var sessionTime = sessionStorage.getItem('boxzilla_timer');
873 if (sessionTime) siteTimer.time = sessionTime;
874 } catch (e) {}
875 siteTimer.start();
876 pageTimer.start();
877 },
878 stop: function stop() {
879 sessionStorage.setItem('boxzilla_timer', siteTimer.time);
880 siteTimer.stop();
881 pageTimer.stop();
882 }
883 };
884
885 // initialise & add event listeners
886 Boxzilla.init = function () {
887 document.body.addEventListener('click', onElementClick, false);
888
889 try {
890 pageViews = sessionStorage.getItem('boxzilla_pageviews') || 0;
891 } catch (e) {
892 pageViews = 0;
893 }
894
895 siteTimer = new Timer(0);
896 pageTimer = new Timer(0);
897
898 // insert styles into DOM
899 var styles = require('./styles.js');
900 var styleElement = document.createElement('style');
901 styleElement.setAttribute("type", "text/css");
902 styleElement.innerHTML = styles;
903 document.head.appendChild(styleElement);
904
905 // add overlay element to dom
906 overlay = document.createElement('div');
907 overlay.style.display = 'none';
908 overlay.id = 'boxzilla-overlay';
909 document.body.appendChild(overlay);
910
911 // event binds
912 scrollElement.addEventListener('touchstart', throttle(checkHeightCriteria), true);
913 scrollElement.addEventListener('scroll', throttle(checkHeightCriteria), true);
914 window.addEventListener('resize', throttle(recalculateHeights));
915 window.addEventListener('load', recalculateHeights);
916 overlay.addEventListener('click', onOverlayClick);
917 window.setInterval(checkTimeCriteria, 1000);
918 window.setTimeout(checkPageViewsCriteria, 1000);
919 document.documentElement.addEventListener('mouseleave', onMouseLeave);
920 document.documentElement.addEventListener('mouseenter', onMouseEnter);
921 document.addEventListener('keyup', onKeyUp);
922
923 timers.start();
924 window.addEventListener('focus', timers.start);
925 window.addEventListener('beforeunload', function () {
926 timers.stop();
927 sessionStorage.setItem('boxzilla_pageviews', ++pageViews);
928 });
929 window.addEventListener('blur', timers.stop);
930
931 Boxzilla.trigger('ready');
932 };
933
934 /**
935 * Create a new Box
936 *
937 * @param string id
938 * @param object opts
939 *
940 * @returns Box
941 */
942 Boxzilla.create = function (id, opts) {
943
944 // preserve backwards compat for minimumScreenWidth option
945 if (typeof opts.minimumScreenWidth !== "undefined") {
946 opts.screenWidthCondition = {
947 condition: "larger",
948 value: opts.minimumScreenWidth
949 };
950 }
951
952 var box = new Box(id, opts);
953 boxes.push(box);
954 return box;
955 };
956
957 Boxzilla.get = function (id) {
958 for (var i = 0; i < boxes.length; i++) {
959 var box = boxes[i];
960 if (box.id == id) {
961 return box;
962 }
963 }
964
965 throw new Error("No box exists with ID " + id);
966 };
967
968 // dismiss a single box (or all by omitting id param)
969 Boxzilla.dismiss = function (id) {
970 // if no id given, dismiss all current open boxes
971 if (typeof id === "undefined") {
972 boxes.forEach(function (box) {
973 box.dismiss();
974 });
975 } else if (_typeof(boxes[id]) === "object") {
976 Boxzilla.get(id).dismiss();
977 }
978 };
979
980 Boxzilla.hide = function (id) {
981 if (typeof id === "undefined") {
982 boxes.forEach(function (box) {
983 box.hide();
984 });
985 } else {
986 Boxzilla.get(id).hide();
987 }
988 };
989
990 Boxzilla.show = function (id) {
991 if (typeof id === "undefined") {
992 boxes.forEach(function (box) {
993 box.show();
994 });
995 } else {
996 Boxzilla.get(id).show();
997 }
998 };
999
1000 Boxzilla.toggle = function (id) {
1001 if (typeof id === "undefined") {
1002 boxes.forEach(function (box) {
1003 box.toggle();
1004 });
1005 } else {
1006 Boxzilla.get(id).toggle();
1007 }
1008 };
1009
1010 // expose each individual box.
1011 Boxzilla.boxes = boxes;
1012
1013 // expose boxzilla object
1014 window.Boxzilla = Boxzilla;
1015
1016 if (typeof module !== 'undefined' && module.exports) {
1017 module.exports = Boxzilla;
1018 }
1019
1020 },{"./box.js":3,"./styles.js":5,"./timer.js":6,"wolfy87-eventemitter":7}],5:[function(require,module,exports){
1021 "use strict";
1022
1023 var styles = "#boxzilla-overlay{position:fixed;background:rgba(0,0,0,.65);width:100%;height:100%;left:0;top:0;z-index:99999}.boxzilla-center-container{position:fixed;top:0;left:0;right:0;height:0;text-align:center;z-index:999999;line-height:0}.boxzilla-center-container .boxzilla{display:inline-block;text-align:left;position:relative;line-height:normal}.boxzilla{position:fixed;z-index:999999;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;background:#fff;padding:25px}.boxzilla.boxzilla-top-left{top:0;left:0}.boxzilla.boxzilla-top-right{top:0;right:0}.boxzilla.boxzilla-bottom-left{bottom:0;left:0}.boxzilla.boxzilla-bottom-right{bottom:0;right:0}.boxzilla-content>:first-child{margin-top:0;padding-top:0}.boxzilla-content>:last-child{margin-bottom:0;padding-bottom:0}.boxzilla-close-icon{position:absolute;right:0;top:0;text-align:center;padding:6px;cursor:pointer;-webkit-appearance:none;font-size:28px;font-weight:700;line-height:20px;color:#000;opacity:.5}.boxzilla-close-icon:focus,.boxzilla-close-icon:hover{opacity:.8}";
1024 module.exports = styles;
1025
1026 },{}],6:[function(require,module,exports){
1027 'use strict';
1028
1029 var Timer = function Timer(start) {
1030 this.time = start;
1031 this.interval = 0;
1032 };
1033
1034 Timer.prototype.tick = function () {
1035 this.time++;
1036 };
1037
1038 Timer.prototype.start = function () {
1039 if (!this.interval) {
1040 this.interval = window.setInterval(this.tick.bind(this), 1000);
1041 }
1042 };
1043
1044 Timer.prototype.stop = function () {
1045 if (this.interval) {
1046 window.clearInterval(this.interval);
1047 this.interval = 0;
1048 }
1049 };
1050
1051 module.exports = Timer;
1052
1053 },{}],7:[function(require,module,exports){
1054 /*!
1055 * EventEmitter v4.2.11 - git.io/ee
1056 * Unlicense - http://unlicense.org/
1057 * Oliver Caldwell - http://oli.me.uk/
1058 * @preserve
1059 */
1060
1061 ;(function () {
1062 'use strict';
1063
1064 /**
1065 * Class for managing events.
1066 * Can be extended to provide event functionality in other classes.
1067 *
1068 * @class EventEmitter Manages event registering and emitting.
1069 */
1070 function EventEmitter() {}
1071
1072 // Shortcuts to improve speed and size
1073 var proto = EventEmitter.prototype;
1074 var exports = this;
1075 var originalGlobalValue = exports.EventEmitter;
1076
1077 /**
1078 * Finds the index of the listener for the event in its storage array.
1079 *
1080 * @param {Function[]} listeners Array of listeners to search through.
1081 * @param {Function} listener Method to look for.
1082 * @return {Number} Index of the specified listener, -1 if not found
1083 * @api private
1084 */
1085 function indexOfListener(listeners, listener) {
1086 var i = listeners.length;
1087 while (i--) {
1088 if (listeners[i].listener === listener) {
1089 return i;
1090 }
1091 }
1092
1093 return -1;
1094 }
1095
1096 /**
1097 * Alias a method while keeping the context correct, to allow for overwriting of target method.
1098 *
1099 * @param {String} name The name of the target method.
1100 * @return {Function} The aliased method
1101 * @api private
1102 */
1103 function alias(name) {
1104 return function aliasClosure() {
1105 return this[name].apply(this, arguments);
1106 };
1107 }
1108
1109 /**
1110 * Returns the listener array for the specified event.
1111 * Will initialise the event object and listener arrays if required.
1112 * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
1113 * Each property in the object response is an array of listener functions.
1114 *
1115 * @param {String|RegExp} evt Name of the event to return the listeners from.
1116 * @return {Function[]|Object} All listener functions for the event.
1117 */
1118 proto.getListeners = function getListeners(evt) {
1119 var events = this._getEvents();
1120 var response;
1121 var key;
1122
1123 // Return a concatenated array of all matching events if
1124 // the selector is a regular expression.
1125 if (evt instanceof RegExp) {
1126 response = {};
1127 for (key in events) {
1128 if (events.hasOwnProperty(key) && evt.test(key)) {
1129 response[key] = events[key];
1130 }
1131 }
1132 }
1133 else {
1134 response = events[evt] || (events[evt] = []);
1135 }
1136
1137 return response;
1138 };
1139
1140 /**
1141 * Takes a list of listener objects and flattens it into a list of listener functions.
1142 *
1143 * @param {Object[]} listeners Raw listener objects.
1144 * @return {Function[]} Just the listener functions.
1145 */
1146 proto.flattenListeners = function flattenListeners(listeners) {
1147 var flatListeners = [];
1148 var i;
1149
1150 for (i = 0; i < listeners.length; i += 1) {
1151 flatListeners.push(listeners[i].listener);
1152 }
1153
1154 return flatListeners;
1155 };
1156
1157 /**
1158 * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
1159 *
1160 * @param {String|RegExp} evt Name of the event to return the listeners from.
1161 * @return {Object} All listener functions for an event in an object.
1162 */
1163 proto.getListenersAsObject = function getListenersAsObject(evt) {
1164 var listeners = this.getListeners(evt);
1165 var response;
1166
1167 if (listeners instanceof Array) {
1168 response = {};
1169 response[evt] = listeners;
1170 }
1171
1172 return response || listeners;
1173 };
1174
1175 /**
1176 * Adds a listener function to the specified event.
1177 * The listener will not be added if it is a duplicate.
1178 * If the listener returns true then it will be removed after it is called.
1179 * If you pass a regular expression as the event name then the listener will be added to all events that match it.
1180 *
1181 * @param {String|RegExp} evt Name of the event to attach the listener to.
1182 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
1183 * @return {Object} Current instance of EventEmitter for chaining.
1184 */
1185 proto.addListener = function addListener(evt, listener) {
1186 var listeners = this.getListenersAsObject(evt);
1187 var listenerIsWrapped = typeof listener === 'object';
1188 var key;
1189
1190 for (key in listeners) {
1191 if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
1192 listeners[key].push(listenerIsWrapped ? listener : {
1193 listener: listener,
1194 once: false
1195 });
1196 }
1197 }
1198
1199 return this;
1200 };
1201
1202 /**
1203 * Alias of addListener
1204 */
1205 proto.on = alias('addListener');
1206
1207 /**
1208 * Semi-alias of addListener. It will add a listener that will be
1209 * automatically removed after its first execution.
1210 *
1211 * @param {String|RegExp} evt Name of the event to attach the listener to.
1212 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
1213 * @return {Object} Current instance of EventEmitter for chaining.
1214 */
1215 proto.addOnceListener = function addOnceListener(evt, listener) {
1216 return this.addListener(evt, {
1217 listener: listener,
1218 once: true
1219 });
1220 };
1221
1222 /**
1223 * Alias of addOnceListener.
1224 */
1225 proto.once = alias('addOnceListener');
1226
1227 /**
1228 * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
1229 * You need to tell it what event names should be matched by a regex.
1230 *
1231 * @param {String} evt Name of the event to create.
1232 * @return {Object} Current instance of EventEmitter for chaining.
1233 */
1234 proto.defineEvent = function defineEvent(evt) {
1235 this.getListeners(evt);
1236 return this;
1237 };
1238
1239 /**
1240 * Uses defineEvent to define multiple events.
1241 *
1242 * @param {String[]} evts An array of event names to define.
1243 * @return {Object} Current instance of EventEmitter for chaining.
1244 */
1245 proto.defineEvents = function defineEvents(evts) {
1246 for (var i = 0; i < evts.length; i += 1) {
1247 this.defineEvent(evts[i]);
1248 }
1249 return this;
1250 };
1251
1252 /**
1253 * Removes a listener function from the specified event.
1254 * When passed a regular expression as the event name, it will remove the listener from all events that match it.
1255 *
1256 * @param {String|RegExp} evt Name of the event to remove the listener from.
1257 * @param {Function} listener Method to remove from the event.
1258 * @return {Object} Current instance of EventEmitter for chaining.
1259 */
1260 proto.removeListener = function removeListener(evt, listener) {
1261 var listeners = this.getListenersAsObject(evt);
1262 var index;
1263 var key;
1264
1265 for (key in listeners) {
1266 if (listeners.hasOwnProperty(key)) {
1267 index = indexOfListener(listeners[key], listener);
1268
1269 if (index !== -1) {
1270 listeners[key].splice(index, 1);
1271 }
1272 }
1273 }
1274
1275 return this;
1276 };
1277
1278 /**
1279 * Alias of removeListener
1280 */
1281 proto.off = alias('removeListener');
1282
1283 /**
1284 * Adds listeners in bulk using the manipulateListeners method.
1285 * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
1286 * You can also pass it a regular expression to add the array of listeners to all events that match it.
1287 * Yeah, this function does quite a bit. That's probably a bad thing.
1288 *
1289 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
1290 * @param {Function[]} [listeners] An optional array of listener functions to add.
1291 * @return {Object} Current instance of EventEmitter for chaining.
1292 */
1293 proto.addListeners = function addListeners(evt, listeners) {
1294 // Pass through to manipulateListeners
1295 return this.manipulateListeners(false, evt, listeners);
1296 };
1297
1298 /**
1299 * Removes listeners in bulk using the manipulateListeners method.
1300 * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
1301 * You can also pass it an event name and an array of listeners to be removed.
1302 * You can also pass it a regular expression to remove the listeners from all events that match it.
1303 *
1304 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
1305 * @param {Function[]} [listeners] An optional array of listener functions to remove.
1306 * @return {Object} Current instance of EventEmitter for chaining.
1307 */
1308 proto.removeListeners = function removeListeners(evt, listeners) {
1309 // Pass through to manipulateListeners
1310 return this.manipulateListeners(true, evt, listeners);
1311 };
1312
1313 /**
1314 * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
1315 * The first argument will determine if the listeners are removed (true) or added (false).
1316 * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
1317 * You can also pass it an event name and an array of listeners to be added/removed.
1318 * You can also pass it a regular expression to manipulate the listeners of all events that match it.
1319 *
1320 * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
1321 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
1322 * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
1323 * @return {Object} Current instance of EventEmitter for chaining.
1324 */
1325 proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
1326 var i;
1327 var value;
1328 var single = remove ? this.removeListener : this.addListener;
1329 var multiple = remove ? this.removeListeners : this.addListeners;
1330
1331 // If evt is an object then pass each of its properties to this method
1332 if (typeof evt === 'object' && !(evt instanceof RegExp)) {
1333 for (i in evt) {
1334 if (evt.hasOwnProperty(i) && (value = evt[i])) {
1335 // Pass the single listener straight through to the singular method
1336 if (typeof value === 'function') {
1337 single.call(this, i, value);
1338 }
1339 else {
1340 // Otherwise pass back to the multiple function
1341 multiple.call(this, i, value);
1342 }
1343 }
1344 }
1345 }
1346 else {
1347 // So evt must be a string
1348 // And listeners must be an array of listeners
1349 // Loop over it and pass each one to the multiple method
1350 i = listeners.length;
1351 while (i--) {
1352 single.call(this, evt, listeners[i]);
1353 }
1354 }
1355
1356 return this;
1357 };
1358
1359 /**
1360 * Removes all listeners from a specified event.
1361 * If you do not specify an event then all listeners will be removed.
1362 * That means every event will be emptied.
1363 * You can also pass a regex to remove all events that match it.
1364 *
1365 * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
1366 * @return {Object} Current instance of EventEmitter for chaining.
1367 */
1368 proto.removeEvent = function removeEvent(evt) {
1369 var type = typeof evt;
1370 var events = this._getEvents();
1371 var key;
1372
1373 // Remove different things depending on the state of evt
1374 if (type === 'string') {
1375 // Remove all listeners for the specified event
1376 delete events[evt];
1377 }
1378 else if (evt instanceof RegExp) {
1379 // Remove all events matching the regex.
1380 for (key in events) {
1381 if (events.hasOwnProperty(key) && evt.test(key)) {
1382 delete events[key];
1383 }
1384 }
1385 }
1386 else {
1387 // Remove all listeners in all events
1388 delete this._events;
1389 }
1390
1391 return this;
1392 };
1393
1394 /**
1395 * Alias of removeEvent.
1396 *
1397 * Added to mirror the node API.
1398 */
1399 proto.removeAllListeners = alias('removeEvent');
1400
1401 /**
1402 * Emits an event of your choice.
1403 * When emitted, every listener attached to that event will be executed.
1404 * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
1405 * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
1406 * So they will not arrive within the array on the other side, they will be separate.
1407 * You can also pass a regular expression to emit to all events that match it.
1408 *
1409 * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
1410 * @param {Array} [args] Optional array of arguments to be passed to each listener.
1411 * @return {Object} Current instance of EventEmitter for chaining.
1412 */
1413 proto.emitEvent = function emitEvent(evt, args) {
1414 var listenersMap = this.getListenersAsObject(evt);
1415 var listeners;
1416 var listener;
1417 var i;
1418 var key;
1419 var response;
1420
1421 for (key in listenersMap) {
1422 if (listenersMap.hasOwnProperty(key)) {
1423 listeners = listenersMap[key].slice(0);
1424 i = listeners.length;
1425
1426 while (i--) {
1427 // If the listener returns true then it shall be removed from the event
1428 // The function is executed either with a basic call or an apply if there is an args array
1429 listener = listeners[i];
1430
1431 if (listener.once === true) {
1432 this.removeListener(evt, listener.listener);
1433 }
1434
1435 response = listener.listener.apply(this, args || []);
1436
1437 if (response === this._getOnceReturnValue()) {
1438 this.removeListener(evt, listener.listener);
1439 }
1440 }
1441 }
1442 }
1443
1444 return this;
1445 };
1446
1447 /**
1448 * Alias of emitEvent
1449 */
1450 proto.trigger = alias('emitEvent');
1451
1452 /**
1453 * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
1454 * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
1455 *
1456 * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
1457 * @param {...*} Optional additional arguments to be passed to each listener.
1458 * @return {Object} Current instance of EventEmitter for chaining.
1459 */
1460 proto.emit = function emit(evt) {
1461 var args = Array.prototype.slice.call(arguments, 1);
1462 return this.emitEvent(evt, args);
1463 };
1464
1465 /**
1466 * Sets the current value to check against when executing listeners. If a
1467 * listeners return value matches the one set here then it will be removed
1468 * after execution. This value defaults to true.
1469 *
1470 * @param {*} value The new value to check for when executing listeners.
1471 * @return {Object} Current instance of EventEmitter for chaining.
1472 */
1473 proto.setOnceReturnValue = function setOnceReturnValue(value) {
1474 this._onceReturnValue = value;
1475 return this;
1476 };
1477
1478 /**
1479 * Fetches the current value to check against when executing listeners. If
1480 * the listeners return value matches this one then it should be removed
1481 * automatically. It will return true by default.
1482 *
1483 * @return {*|Boolean} The current value to check for or the default, true.
1484 * @api private
1485 */
1486 proto._getOnceReturnValue = function _getOnceReturnValue() {
1487 if (this.hasOwnProperty('_onceReturnValue')) {
1488 return this._onceReturnValue;
1489 }
1490 else {
1491 return true;
1492 }
1493 };
1494
1495 /**
1496 * Fetches the events object and creates one if required.
1497 *
1498 * @return {Object} The events storage object.
1499 * @api private
1500 */
1501 proto._getEvents = function _getEvents() {
1502 return this._events || (this._events = {});
1503 };
1504
1505 /**
1506 * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
1507 *
1508 * @return {Function} Non conflicting EventEmitter class.
1509 */
1510 EventEmitter.noConflict = function noConflict() {
1511 exports.EventEmitter = originalGlobalValue;
1512 return EventEmitter;
1513 };
1514
1515 // Expose the class either via AMD, CommonJS or the global object
1516 if (typeof define === 'function' && define.amd) {
1517 define(function () {
1518 return EventEmitter;
1519 });
1520 }
1521 else if (typeof module === 'object' && module.exports){
1522 module.exports = EventEmitter;
1523 }
1524 else {
1525 exports.EventEmitter = EventEmitter;
1526 }
1527 }.call(this));
1528
1529 },{}]},{},[1]);
1530 ; })();