PluginProbe
Boxzilla – WordPress Popup Builder / 3.1.4
Boxzilla – WordPress Popup Builder v3.1.4
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
← All changes | assets/js/script.js +391 -134 3.0.2 → 3.1.4 View file →
@@ -1,5 +1,5 @@
1 -(function () { var require = 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){
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 2 'use strict';
3 3
4 4 var Boxzilla = require('boxzilla');
5 5 var options = window.boxzilla_options;
@@ -18,15 +18,21 @@
18 18 // get opts
19 19 var boxOpts = options.boxes[i];
20 20 boxOpts.testMode = isLoggedIn && options.testMode;
21 21
22 + // fix http:// links in box content....
23 + if( window.location.origin.substring(0, 5) === "https" ) {
24 + boxOpts.content = boxOpts.content.replace(window.location.origin.replace("https", "http"), window.location.origin);
25 + }
26 +
22 27 // create box
23 28 var box = Boxzilla.create( boxOpts.id, boxOpts);
24 -
29 +
25 30 // add custom css to box
26 31 css(box.element, boxOpts.css);
27 32 }
28 33
34 +// helper function for setting CSS styles
29 35 function css(element, styles) {
30 36 if( styles.background_color ) {
31 37 element.style.background = styles.background_color;
32 38 }
@@ -51,18 +57,193 @@
51 57 element.style.maxWidth = parseInt(styles.width) + "px";
52 58 }
53 59 }
54 60
61 +/**
62 + * If a MailChimp for WordPress form was submitted, open the box containing that form (if any)
63 + *
64 + * TODO: Just set location hash from MailChimp for WP?
65 + */
66 +window.addEventListener('load', function() {
67 + if( typeof(window.mc4wp_forms_config) === "object" && window.mc4wp_forms_config.submitted_form ) {
68 + var selector = '#' + window.mc4wp_forms_config.submitted_form.element_id;
69 + var boxes = Boxzilla.boxes;
70 + for( var boxId in boxes ) {
71 + if(!boxes.hasOwnProperty(boxId)) { continue; }
72 + var box = boxes[boxId];
73 + if( box.element.querySelector(selector)) {
74 + box.show();
75 + return;
76 + }
77 + }
78 + }
79 +});
80 +
55 81 window.Boxzilla = Boxzilla;
56 -},{"boxzilla":3}],2:[function(require,module,exports){
82 +},{"boxzilla":4}],2:[function(require,module,exports){
83 +var duration = 320;
84 +
85 +function css(element, styles) {
86 + for(var property in styles) {
87 + element.style[property] = styles[property];
88 + }
89 +}
90 +
91 +function initObjectProperties(properties, value) {
92 + var newObject = {};
93 + for(var i=0; i<properties.length; i++) {
94 + newObject[properties[i]] = value;
95 + }
96 + return newObject;
97 +}
98 +
99 +function copyObjectProperties(properties, object) {
100 + var newObject = {}
101 + for(var i=0; i<properties.length; i++) {
102 + newObject[properties[i]] = object[properties[i]];
103 + }
104 + return newObject;
105 +}
106 +
107 +/**
108 + * Checks if the given element is currently being animated.
109 + *
110 + * @param element
111 + * @returns {boolean}
112 + */
113 +function animated(element) {
114 + return !! element.getAttribute('data-animated');
115 +}
116 +
117 +/**
118 + * Toggles the element using the given animation.
119 + *
120 + * @param element
121 + * @param animation Either "fade" or "slide"
122 + */
123 +function toggle(element, animation) {
124 + var nowVisible = element.style.display != 'none' || element.offsetLeft > 0;
125 +
126 + // create clone for reference
127 + var clone = element.cloneNode(true);
128 + var cleanup = function() {
129 + element.removeAttribute('data-animated');
130 + element.setAttribute('style', clone.getAttribute('style'));
131 + element.style.display = nowVisible ? 'none' : '';
132 + };
133 +
134 + // store attribute so everyone knows we're animating this element
135 + element.setAttribute('data-animated', "true");
136 +
137 + // toggle element visiblity right away if we're making something visible
138 + if( ! nowVisible ) {
139 + element.style.display = '';
140 + }
141 +
142 + var hiddenStyles, visibleStyles;
143 +
144 + // animate properties
145 + if( animation === 'slide' ) {
146 + hiddenStyles = initObjectProperties(["height", "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"], 0);
147 + visibleStyles = {};
148 +
149 + if( ! nowVisible ) {
150 + var computedStyles = window.getComputedStyle(element);
151 + visibleStyles = copyObjectProperties(["height", "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"], computedStyles);
152 + css(element, hiddenStyles);
153 + }
154 +
155 + // don't show a scrollbar during animation
156 + element.style.overflowY = 'hidden';
157 + animate(element, nowVisible ? hiddenStyles : visibleStyles, cleanup);
158 + } else {
159 + hiddenStyles = { opacity: 0 };
160 + visibleStyles = { opacity: 1 };
161 + if( ! nowVisible ) {
162 + css(element, hiddenStyles);
163 + }
164 +
165 + animate(element, nowVisible ? hiddenStyles : visibleStyles, cleanup);
166 + }
167 +}
168 +
169 +function animate(element, targetStyles, fn) {
170 + var last = +new Date();
171 + var initialStyles = window.getComputedStyle(element);
172 + var currentStyles = {};
173 + var propSteps = {};
174 +
175 + for(var property in targetStyles) {
176 + // make sure we have an object filled with floats
177 + targetStyles[property] = parseFloat(targetStyles[property]);
178 +
179 + // calculate step size & current value
180 + var to = targetStyles[property];
181 + var current = parseFloat(initialStyles[property]);
182 +
183 + // is there something to do?
184 + if( current == to ) {
185 + delete targetStyles[property];
186 + continue;
187 + }
188 +
189 + propSteps[property] = ( to - current ) / duration; // points per second
190 + currentStyles[property] = current;
191 + }
192 +
193 + var tick = function() {
194 + var now = +new Date();
195 + var timeSinceLastTick = now - last;
196 + var done = true;
197 +
198 + var step, to, increment, newValue;
199 + for(var property in targetStyles ) {
200 + step = propSteps[property];
201 + to = targetStyles[property];
202 + increment = step * timeSinceLastTick;
203 + newValue = currentStyles[property] + increment;
204 +
205 + if( step > 0 && newValue >= to || step < 0 && newValue <= to ) {
206 + newValue = to;
207 + } else {
208 + done = false;
209 + }
210 +
211 + // store new value
212 + currentStyles[property] = newValue;
213 +
214 + var suffix = property !== "opacity" ? "px" : "";
215 + element.style[property] = newValue + suffix;
216 + }
217 +
218 + last = +new Date();
219 +
220 + // keep going until we're done for all props
221 + if(!done) {
222 + (window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 32);
223 + } else {
224 + // call callback
225 + fn && fn();
226 + }
227 + };
228 +
229 + tick();
230 +}
231 +
232 +
233 +module.exports = {
234 + 'toggle': toggle,
235 + 'animate': animate,
236 + 'animated': animated
237 +};
238 +},{}],3:[function(require,module,exports){
57 239 'use strict';
58 240
59 -var $ = window.jQuery,
60 - defaults = {
241 +var defaults = {
61 242 'animation': 'fade',
62 243 'rehide': false,
63 244 'content': '',
64 - 'cookieTime': 0,
245 + 'cookie': null,
65 246 'icon': '&times',
66 247 'minimumScreenWidth': 0,
67 248 'position': 'center',
68 249 'testMode': false,
@@ -67,9 +248,11 @@
67 248 'position': 'center',
68 249 'testMode': false,
69 250 'trigger': false,
70 251 'closable': true
71 - }, Boxzilla;
252 + },
253 + Boxzilla,
254 + Animator = require('./animator.js');
72 255
73 256 /**
74 257 * Merge 2 objects, values of the latter overwriting the former.
75 258 *
@@ -83,8 +266,22 @@
83 266 for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
84 267 return obj3;
85 268 }
86 269
270 +/**
271 + * Get the real height of entire document.
272 + * @returns {number}
273 + */
274 +function getDocumentHeight() {
275 + var body = document.body,
276 + html = document.documentElement;
277 +
278 + var height = Math.max( body.scrollHeight, body.offsetHeight,
279 + html.clientHeight, html.scrollHeight, html.offsetHeight );
280 +
281 + return height;
282 +}
283 +
87 284 // Box Object
88 285 var Box = function( id, config ) {
89 286 this.id = id;
90 287
@@ -95,12 +292,14 @@
95 292 this.overlay = document.getElementById('boxzilla-overlay');
96 293
97 294 // state
98 295 this.visible = false;
99 - this.closed = false;
296 + this.dismissed = false;
100 297 this.triggered = false;
101 298 this.triggerHeight = 0;
102 299 this.cookieSet = false;
300 + this.element = null;
301 + this.closeIcon = null;
103 302
104 303 // if a trigger was given, calculate values once and store
105 304 if( this.config.trigger ) {
106 305 if( this.config.trigger.method === 'percentage' || this.config.trigger.method === 'element' ) {
@@ -109,11 +308,10 @@
109 308
110 309 this.cookieSet = this.isCookieSet();
111 310 }
112 311
113 - // create dom element for this box
114 - this.element = this.dom();
115 - this.$element = $(this.element);
312 + // create dom elements for this box
313 + this.dom();
116 314
117 315 // further initialise the box
118 316 this.events();
119 317 };
@@ -122,28 +320,31 @@
122 320 Box.prototype.events = function() {
123 321 var box = this;
124 322
125 323 // attach event to "close" icon inside box
126 - this.$element.find('.boxzilla-close-icon').click(box.dismiss.bind(this));
324 + this.closeIcon && this.closeIcon.addEventListener('click', box.dismiss.bind(this));
127 325
128 - this.$element.on('click', 'a', function(e) {
129 - Boxzilla.trigger('box.interactions.link', [ box, e.target ] );
130 - });
326 + this.element.addEventListener('click', function(e) {
327 + if( e.target.tagName === 'A' ) {
328 + Boxzilla.trigger('box.interactions.link', [ box, e.target ] );
329 + }
330 + }, false);
131 331
132 - this.$element.on('submit', 'form', function(e) {
332 + this.element.addEventListener('submit', function(e) {
133 333 box.setCookie();
134 334 Boxzilla.trigger('box.interactions.form', [ box, e.target ]);
335 + }, false);
336 +
337 + window.addEventListener("hashchange", function() {
338 + var needle = "#boxzilla-" + box.id;
339 + if( location.hash === needle ) {
340 + box.toggle();
341 + }
135 342 });
136 343
137 - // attach event to all links referring #boxzilla-{box_id}
138 - $(document.body).on('click', 'a[href="#boxzilla-' + box.id + '"]', function() {
139 - box.toggle();
140 - return false;
141 - });
142 -
143 344 // maybe show box right away
144 345 if( this.fits() && this.locationHashRefersBox() ) {
145 - $(window).load(this.show.bind(this));
346 + window.addEventListener('load', this.show.bind(this));
146 347 }
147 348
148 349 };
149 350
@@ -152,8 +353,9 @@
152 353 var wrapper = document.createElement('div');
153 354 wrapper.className = 'boxzilla-container boxzilla-' + this.config.position + '-container';
154 355
155 356 var box = document.createElement('div');
357 + box.setAttribute('id', 'boxzilla-' + this.id);
156 358 box.className = 'boxzilla boxzilla-' + this.id + ' boxzilla-' + this.config.position;
157 359 box.style.display = 'none';
158 360 wrapper.appendChild(box);
159 361
@@ -161,9 +363,9 @@
161 363 content.className = 'boxzilla-content';
162 364 content.innerHTML = this.config.content;
163 365 box.appendChild(content);
164 366
165 - // remove <script> from box content and append them to head
367 + // remove <script> from box content and append them to the document body
166 368 var scripts = content.querySelectorAll('script');
167 369 if(scripts.length) {
168 370 var script = document.createElement('script');
169 371 for( var i=0; i<scripts.length; i++ ) {
@@ -169,21 +371,21 @@
169 371 for( var i=0; i<scripts.length; i++ ) {
170 372 script.appendChild(document.createTextNode(scripts[i].text));
171 373 scripts[i].parentNode.removeChild(scripts[i]);
172 374 }
173 - document.head.appendChild(script);
375 + document.body.appendChild(script);
174 376 }
175 -
377 +
176 378 if( this.config.closable && this.config.icon ) {
177 - var icon = document.createElement('span');
178 - icon.className = "boxzilla-close-icon";
179 - icon.innerHTML = this.config.icon;
180 - box.appendChild(icon);
379 + var closeIcon = document.createElement('span');
380 + closeIcon.className = "boxzilla-close-icon";
381 + closeIcon.innerHTML = this.config.icon;
382 + box.appendChild(closeIcon);
383 + this.closeIcon = closeIcon;
181 384 }
182 385
183 386 document.body.appendChild(wrapper);
184 -
185 - return box;
387 + this.element = box;
186 388 };
187 389
188 390 // set (calculate) custom box styling depending on box options
189 391 Box.prototype.setCustomBoxStyling = function() {
@@ -188,14 +390,16 @@
188 390 // set (calculate) custom box styling depending on box options
189 391 Box.prototype.setCustomBoxStyling = function() {
190 392
191 393 // reset element to its initial state
394 + var origDisplay = this.element.style.display;
395 + this.element.style.display = '';
192 396 this.element.style.overflowY = 'auto';
193 397 this.element.style.maxHeight = 'none';
194 398
195 399 // get new dimensions
196 400 var windowHeight = window.innerHeight;
197 - var boxHeight = this.$element.outerHeight();
401 + var boxHeight = this.element.clientHeight;
198 402
199 403 // add scrollbar to box and limit height
200 404 if( boxHeight > windowHeight ) {
201 405 this.element.style.maxHeight = windowHeight + "px";
@@ -208,8 +412,9 @@
208 412 newTopMargin = newTopMargin >= 0 ? newTopMargin : 0;
209 413 this.element.style.marginTop = newTopMargin + "px";
210 414 }
211 415
416 + this.element.style.display = origDisplay;
212 417 };
213 418
214 419 // toggle visibility of the box
215 420 Box.prototype.toggle = function(show) {
@@ -218,15 +423,15 @@
218 423 if( typeof( show ) === "undefined" ) {
219 424 show = ! this.visible;
220 425 }
221 426
222 - // do nothing if element is being animated
223 - if( this.$element.is(':animated') ) {
427 + // is box already at desired visibility?
428 + if( show === this.visible ) {
224 429 return false;
225 430 }
226 431
227 - // is box already at desired visibility?
228 - if( show === this.visible ) {
432 + // is box being animated?
433 + if( Animator.animated(this.element) ) {
229 434 return false;
230 435 }
231 436
232 437 // if box should be hidden but is not closable, bail.
@@ -236,29 +441,27 @@
236 441
237 442 // set new visibility status
238 443 this.visible = show;
239 444
240 - // calculate custom styling for which CSS is "too stupid"
445 + // calculate new styling rules
241 446 this.setCustomBoxStyling();
242 447
243 - // fadein / fadeout the overlay if position is "center"
244 - if( this.config.position === 'center' ) {
245 - $(this.overlay).fadeToggle('slow');
246 - }
247 -
248 448 // trigger event
249 449 Boxzilla.trigger('box.' + ( show ? 'show' : 'hide' ), [ this ] );
250 450
251 451 // show or hide box using selected animation
252 - if( this.config.animation === 'fade' ) {
253 - this.$element.fadeToggle( 'slow' );
254 - } else {
255 - this.$element.slideToggle( 'slow' );
452 + if( this.config.position === 'center' ) {
453 + Animator.toggle(this.overlay, "fade");
256 454 }
257 455
258 - // // focus on first input field in box
259 - // this.$element.find('input').first().focus();
456 + Animator.toggle(this.element, this.config.animation);
260 457
458 + // focus on first input field in box
459 + var firstInput = this.element.querySelector('input, textarea');
460 + if(firstInput) {
461 + firstInput.focus();
462 + }
463 +
261 464 return true;
262 465 };
263 466
264 467 // show the box
@@ -275,29 +478,20 @@
275 478 Box.prototype.calculateTriggerHeight = function() {
276 479 var triggerHeight = 0;
277 480
278 481 if( this.config.trigger.method === 'element' ) {
279 - var $triggerElement = $(this.config.trigger.value).first();
280 - triggerHeight = ( $triggerElement.length > 0 ) ? $triggerElement.offset().top : 0;
482 + var triggerElement = document.body.querySelector(this.config.trigger.value);
483 + if( triggerElement ) {
484 + var offset = triggerElement.getBoundingClientRect();
485 + triggerHeight = offset.top;
486 + }
281 487 } else if( this.config.trigger.method === 'percentage' ) {
282 - triggerHeight = ( this.config.trigger.value / 100 * $(document).height() );
488 + triggerHeight = ( this.config.trigger.value / 100 * getDocumentHeight() );
283 489 }
284 490
285 491 return triggerHeight;
286 492 };
287 493
288 -// set cookie that disables automatically showing the box
289 -Box.prototype.setCookie = function() {
290 - // do nothing if cookieTime evaluates to false
291 - if(! this.config.cookieTime) {
292 - return;
293 - }
294 -
295 - var expiryDate = new Date();
296 - expiryDate.setDate( expiryDate.getDate() + this.config.cookieTime );
297 - document.cookie = 'boxzilla_box_'+ this.id + '=true; expires='+ expiryDate.toUTCString() +'; path=/';
298 -};
299 -
300 494 // checks whether window.location.hash equals the box element ID or that of any element inside the box
301 495 Box.prototype.locationHashRefersBox = function() {
302 496
303 497 if( ! window.location.hash || 0 === window.location.hash.length ) {
@@ -324,10 +518,9 @@
324 518
325 519 // is this box enabled?
326 520 Box.prototype.mayAutoShow = function() {
327 521
328 - // don't show if box was closed (dismissed) before
329 - if( this.closed ) {
522 + if( this.dismissed ) {
330 523 return false;
331 524 }
332 525
333 526 // check if box fits on given minimum screen width
@@ -349,33 +542,48 @@
349 542 };
350 543
351 544 Box.prototype.isCookieSet = function() {
352 545 // always show on test mode
353 - if( this.config.testMode ) {
546 + if(this.config.testMode) {
354 547 return false;
355 548 }
356 549
357 - // check for cookie
358 - if( ! this.config.cookieTime ) {
550 + // if either cookie is null or trigger & dismiss are both falsey, don't bother checking.
551 + if(!this.config.cookie || ( ! this.config.cookie.triggered && ! this.config.cookie.dismissed ) ) {
359 552 return false;
360 553 }
361 554
362 555 var cookieSet = document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + 'boxzilla_box_' + this.id + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1") === "true";
363 556 return cookieSet;
557 +};
364 558
559 +// set cookie that disables automatically showing the box
560 +Box.prototype.setCookie = function(hours) {
561 + var expiryDate = new Date();
562 + expiryDate.setHours( expiryDate.getHours() + hours);
563 + document.cookie = 'boxzilla_box_'+ this.id + '=true; expires='+ expiryDate.toUTCString() +'; path=/';
365 564 };
366 565
367 566 Box.prototype.trigger = function() {
368 567 var shown = this.show();
369 - if( shown ) {
370 - this.triggered = true;
568 + if( ! shown ) {
569 + return;
371 570 }
571 +
572 + this.triggered = true;
573 + if(this.config.cookie && this.config.cookie.triggered) {
574 + this.setCookie(this.config.cookie.triggered);
575 + }
372 576 };
373 577
374 578 Box.prototype.dismiss = function() {
375 579 this.hide();
376 - this.setCookie();
377 - this.closed = true;
580 +
581 + if(this.config.cookie && this.config.cookie.dismissed) {
582 + this.setCookie(this.config.cookie.dismissed);
583 + }
584 +
585 + this.dismissed = true;
378 586 Boxzilla.trigger('box.dismiss', [ this ]);
379 587 };
380 588
381 589 module.exports = function(_Boxzilla) {
@@ -381,32 +589,20 @@
381 589 module.exports = function(_Boxzilla) {
382 590 Boxzilla = _Boxzilla;
383 591 return Box;
384 592 };
385 -},{}],3:[function(require,module,exports){
593 +},{"./animator.js":2}],4:[function(require,module,exports){
386 594 'use strict';
387 595
388 -var $ = window.jQuery,
389 - EventEmitter = require('wolfy87-eventemitter'),
596 +var EventEmitter = require('wolfy87-eventemitter'),
390 597 Boxzilla = Object.create(EventEmitter.prototype),
391 - Box = require('./Box.js')(Boxzilla),
392 - Timer = require('./Timer.js'),
393 - boxes = {},
394 - windowHeight = window.innerHeight,
395 - overlay = document.createElement('div'),
396 - exitIntentDelayTimer,
397 - exitIntentTriggered,
398 - siteTimer = new Timer(sessionStorage.getItem('boxzilla_timer') || 0),
399 - pageTimer = new Timer(0),
400 - pageViews = sessionStorage.getItem('boxzilla_pageviews') || 0;
598 + Box = require('./box.js')(Boxzilla),
599 + Timer = require('./timer.js'),
600 + boxes = [],
601 + overlay,
602 + exitIntentDelayTimer, exitIntentTriggered,
603 + siteTimer, pageTimer, pageViews;
401 604
402 -function each( obj, callback ) {
403 - for( var key in obj ) {
404 - if(! obj.hasOwnProperty(key)) continue;
405 - callback(obj[key]);
406 - }
407 -}
408 -
409 605 function throttle(fn, threshhold, scope) {
410 606 threshhold || (threshhold = 250);
411 607 var last,
412 608 deferTimer;
@@ -437,9 +633,15 @@
437 633 }
438 634
439 635 // check "pageviews" criteria for each box
440 636 function checkPageViewsCriteria() {
441 - each(boxes, function(box) {
637 +
638 + // don't bother if another box is currently open
639 + if( isAnyBoxVisible() ) {
640 + return;
641 + }
642 +
643 + boxes.forEach(function(box) {
442 644 if( ! box.mayAutoShow() ) {
443 645 return;
444 646 }
445 647
@@ -450,9 +652,14 @@
450 652 }
451 653
452 654 // check time trigger criteria for each box
453 655 function checkTimeCriteria() {
454 - each(boxes, function(box) {
656 + // don't bother if another box is currently open
657 + if( isAnyBoxVisible() ) {
658 + return;
659 + }
660 +
661 + boxes.forEach(function(box) {
455 662 if( ! box.mayAutoShow() ) {
456 663 return;
457 664 }
458 665
@@ -469,17 +676,22 @@
469 676 }
470 677
471 678 // check triggerHeight criteria for all boxes
472 679 function checkHeightCriteria() {
473 - var scrollY = window.scrollY;
474 - var scrollHeight = scrollY + ( windowHeight * 0.667 );
680 + var scrollY = ( window.scrollY || window.pageYOffset ) + window.innerHeight * 0.75;
475 681
476 - each(boxes, function(box) {
682 + // don't bother if another box is currently open
683 + if( isAnyBoxVisible() ) {
684 + return;
685 + }
686 +
687 + boxes.forEach(function(box) {
688 +
477 689 if( ! box.mayAutoShow() || box.triggerHeight <= 0 ) {
478 690 return;
479 691 }
480 692
481 - if( scrollHeight > box.triggerHeight ) {
693 + if( scrollY > box.triggerHeight ) {
482 694 box.trigger();
483 695 } else if( box.mayRehide() ) {
484 696 box.hide();
485 697 }
@@ -487,11 +699,9 @@
487 699 }
488 700
489 701 // recalculate heights and variables based on height
490 702 function recalculateHeights() {
491 - windowHeight = window.innerHeight;
492 -
493 - each(boxes, function(box) {
703 + boxes.forEach(function(box) {
494 704 box.setCustomBoxStyling();
495 705 });
496 706 }
497 707
@@ -499,9 +709,9 @@
499 709 var x = e.offsetX;
500 710 var y = e.offsetY;
501 711
502 712 // calculate if click was near a box to avoid closing it (click error margin)
503 - each(boxes, function(box) {
713 + boxes.forEach(function(box) {
504 714 var rect = box.element.getBoundingClientRect();
505 715 var margin = 100 + ( window.innerWidth * 0.05 );
506 716
507 717 // if click was not anywhere near box, dismiss it.
@@ -511,11 +721,14 @@
511 721 });
512 722 }
513 723
514 724 function triggerExitIntent() {
515 - if(exitIntentTriggered) return;
725 + // do nothing if already triggered OR another box is visible.
726 + if(exitIntentTriggered || isAnyBoxVisible() ) {
727 + return;
728 + }
516 729
517 - each(boxes, function(box) {
730 + boxes.forEach(function(box) {
518 731 if(box.mayAutoShow() && box.config.trigger.method === 'exit_intent' ) {
519 732 box.trigger();
520 733 }
521 734 });
@@ -531,8 +744,21 @@
531 744 exitIntentDelayTimer = window.setTimeout(triggerExitIntent, delay);
532 745 }
533 746 }
534 747
748 +function isAnyBoxVisible() {
749 +
750 + for( var i=0; i<boxes.length; i++ ) {
751 + var box = boxes[i];
752 +
753 + if( box.visible ) {
754 + return true;
755 + }
756 + }
757 +
758 + return false;
759 +}
760 +
535 761 function onMouseEnter() {
536 762 if( exitIntentDelayTimer ) {
537 763 window.clearInterval(exitIntentDelayTimer);
538 764 exitIntentDelayTimer = null;
@@ -554,32 +780,43 @@
554 780 };
555 781
556 782 // initialise & add event listeners
557 783 Boxzilla.init = function() {
558 - var html = document.documentElement;
784 + siteTimer = new Timer(sessionStorage.getItem('boxzilla_timer') || 0);
785 + pageTimer = new Timer(0);
786 + pageViews = sessionStorage.getItem('boxzilla_pageviews') || 0;
559 787
788 + // insert styles into DOM
789 + var styles = require('./styles.js');
790 + var styleElement = document.createElement('style');
791 + styleElement.setAttribute("type", "text/css");
792 + styleElement.innerHTML = styles;
793 + document.head.appendChild(styleElement);
794 +
560 795 // add overlay element to dom
796 + overlay = document.createElement('div');
797 + overlay.style.display = 'none';
561 798 overlay.id = 'boxzilla-overlay';
562 799 document.body.appendChild(overlay);
563 800
564 801 // event binds
565 - $(window).on('scroll', throttle(checkHeightCriteria));
566 - $(window).on('resize', throttle(recalculateHeights));
567 - $(window).on('load', recalculateHeights );
568 - $(html).on('mouseleave', onMouseLeave);
569 - $(html).on('mouseenter', onMouseEnter);
570 - $(html).on('keyup', onKeyUp);
571 - $(overlay).click(onOverlayClick);
802 + window.addEventListener('scroll', throttle(checkHeightCriteria));
803 + window.addEventListener('resize', throttle(recalculateHeights));
804 + window.addEventListener('load', recalculateHeights );
805 + overlay.addEventListener('click', onOverlayClick);
572 806 window.setInterval(checkTimeCriteria, 1000);
573 807 window.setTimeout(checkPageViewsCriteria, 1000 );
808 + document.documentElement.addEventListener('mouseleave', onMouseLeave);
809 + document.documentElement.addEventListener('mouseenter', onMouseEnter);
810 + document.addEventListener('keyup', onKeyUp);
574 811
575 812 timers.start();
576 - $(window).on('focus', timers.start);
577 - $(window).on('beforeunload', function() {
813 + window.addEventListener('focus', timers.start);
814 + window.addEventListener('beforeunload', function() {
578 815 timers.stop();
579 816 sessionStorage.setItem('boxzilla_pageviews', ++pageViews);
580 817 });
581 - $(window).on('blur', timers.stop);
818 + window.addEventListener('blur', timers.stop);
582 819
583 820 Boxzilla.trigger('ready');
584 821 };
585 822
@@ -591,52 +828,70 @@
591 828 *
592 829 * @returns Box
593 830 */
594 831 Boxzilla.create = function(id, opts) {
595 - boxes[id] = new Box(id, opts);
596 - return boxes[id];
832 + var box = new Box(id, opts);
833 + boxes.push(box);
834 + return box;
597 835 };
598 836
837 +Boxzilla.get = function(id) {
838 + for( var i=0; i<boxes.length; i++) {
839 + var box = boxes[i];
840 + if( box.id == id ) {
841 + return box;
842 + }
843 + }
844 +
845 + throw new Error("No box exists with ID " + id);
846 +}
847 +
599 848 // dismiss a single box (or all by omitting id param)
600 849 Boxzilla.dismiss = function(id) {
601 850 // if no id given, dismiss all current open boxes
602 851 if( typeof(id) === "undefined" ) {
603 - each(boxes, function(box) { box.dismiss(); });
852 + boxes.forEach(function(box) { box.dismiss(); });
604 853 } else if( typeof( boxes[id] ) === "object" ) {
605 - boxes[id].dismiss();
854 + Boxzilla.get(id).dismiss();
606 855 }
607 856 };
608 857
609 858 Boxzilla.hide = function(id) {
610 859 if( typeof(id) === "undefined" ) {
611 - each(boxes, function(box) { box.hide(); });
612 - } else if( typeof( boxes[id] ) === "object" ) {
613 - boxes[id].hide();
860 + boxes.forEach(function(box) { box.hide(); });
861 + } else {
862 + Boxzilla.get(id).hide();
614 863 }
615 864 };
616 865
617 866 Boxzilla.show = function(id) {
618 867 if( typeof(id) === "undefined" ) {
619 - each(boxes, function(box) { box.show(); });
620 - } else if( typeof( boxes[id] ) === "object" ) {
621 - boxes[id].show();
868 + boxes.forEach(function(box) { box.show(); });
869 + } else {
870 + Boxzilla.get(id).show();
622 871 }
623 872 };
624 873
625 874 Boxzilla.toggle = function(id) {
626 875 if( typeof(id) === "undefined" ) {
627 - each(boxes, function(box) { box.toggle(); });
628 - } else if( typeof( boxes[id] ) === "object" ) {
629 - boxes[id].toggle();
876 + boxes.forEach(function(box) { box.toggle(); });
877 + } else {
878 + Boxzilla.get(id).toggle();
630 879 }
631 880 };
632 881
882 +// expose each individual box.
883 +Boxzilla.boxes = boxes;
884 +
633 885 window.Boxzilla = Boxzilla;
634 886
635 887 if ( typeof module !== 'undefined' && module.exports ) {
636 888 module.exports = Boxzilla;
637 889 }
638 -},{"./Box.js":2,"./Timer.js":4,"wolfy87-eventemitter":5}],4:[function(require,module,exports){
890 +},{"./box.js":3,"./styles.js":5,"./timer.js":6,"wolfy87-eventemitter":7}],5:[function(require,module,exports){
891 +const 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}`;
892 +module.exports = styles;
893 +},{}],6:[function(require,module,exports){
639 894 'use strict';
640 895
641 896 var Timer = function(start) {
642 897 this.time = start;
@@ -653,14 +908,16 @@
653 908 }
654 909 };
655 910
656 911 Timer.prototype.stop = function() {
657 - window.clearInterval(this.interval);
658 - this.interval = 0;
912 + if( this.interval ) {
913 + window.clearInterval(this.interval);
914 + this.interval = 0;
915 + }
659 916 };
660 917
661 918 module.exports = Timer;
662 -},{}],5:[function(require,module,exports){
919 +},{}],7:[function(require,module,exports){
663 920 /*!
664 921 * EventEmitter v4.2.11 - git.io/ee
665 922 * Unlicense - http://unlicense.org/
666 923 * Oliver Caldwell - http://oli.me.uk/
@@ -1135,5 +1392,5 @@
1135 1392 }
1136 1393 }.call(this));
1137 1394
1138 1395 },{}]},{},[1]);
1139 - })();
1396 +; })();