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 +467 -136 3.0 → 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,31 +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 - if( this.config.trigger.method === "time_on_page" && this.mayAutoShow() ) {
145 - window.setTimeout(this.trigger.bind(this), this.config.trigger.value * 1000 );
146 - // auto-show the box if box is referenced from URL
147 - } else if( this.fits() && this.locationHashRefersBox() ) {
148 - $(window).load(this.show.bind(this));
345 + if( this.fits() && this.locationHashRefersBox() ) {
346 + window.addEventListener('load', this.show.bind(this));
149 347 }
150 348
151 349 };
152 350
@@ -151,13 +349,13 @@
151 349 };
152 350
153 351 // generate dom elements for this box
154 352 Box.prototype.dom = function() {
155 -
156 353 var wrapper = document.createElement('div');
157 354 wrapper.className = 'boxzilla-container boxzilla-' + this.config.position + '-container';
158 355
159 356 var box = document.createElement('div');
357 + box.setAttribute('id', 'boxzilla-' + this.id);
160 358 box.className = 'boxzilla boxzilla-' + this.id + ' boxzilla-' + this.config.position;
161 359 box.style.display = 'none';
162 360 wrapper.appendChild(box);
163 361
@@ -165,18 +363,29 @@
165 363 content.className = 'boxzilla-content';
166 364 content.innerHTML = this.config.content;
167 365 box.appendChild(content);
168 366
367 + // remove <script> from box content and append them to the document body
368 + var scripts = content.querySelectorAll('script');
369 + if(scripts.length) {
370 + var script = document.createElement('script');
371 + for( var i=0; i<scripts.length; i++ ) {
372 + script.appendChild(document.createTextNode(scripts[i].text));
373 + scripts[i].parentNode.removeChild(scripts[i]);
374 + }
375 + document.body.appendChild(script);
376 + }
377 +
169 378 if( this.config.closable && this.config.icon ) {
170 - var icon = document.createElement('span');
171 - icon.className = "boxzilla-close-icon";
172 - icon.innerHTML = this.config.icon;
173 - 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;
174 384 }
175 385
176 386 document.body.appendChild(wrapper);
177 -
178 - return box;
387 + this.element = box;
179 388 };
180 389
181 390 // set (calculate) custom box styling depending on box options
182 391 Box.prototype.setCustomBoxStyling = function() {
@@ -181,14 +390,16 @@
181 390 // set (calculate) custom box styling depending on box options
182 391 Box.prototype.setCustomBoxStyling = function() {
183 392
184 393 // reset element to its initial state
394 + var origDisplay = this.element.style.display;
395 + this.element.style.display = '';
185 396 this.element.style.overflowY = 'auto';
186 397 this.element.style.maxHeight = 'none';
187 398
188 399 // get new dimensions
189 400 var windowHeight = window.innerHeight;
190 - var boxHeight = this.$element.outerHeight();
401 + var boxHeight = this.element.clientHeight;
191 402
192 403 // add scrollbar to box and limit height
193 404 if( boxHeight > windowHeight ) {
194 405 this.element.style.maxHeight = windowHeight + "px";
@@ -201,8 +412,9 @@
201 412 newTopMargin = newTopMargin >= 0 ? newTopMargin : 0;
202 413 this.element.style.marginTop = newTopMargin + "px";
203 414 }
204 415
416 + this.element.style.display = origDisplay;
205 417 };
206 418
207 419 // toggle visibility of the box
208 420 Box.prototype.toggle = function(show) {
@@ -211,15 +423,15 @@
211 423 if( typeof( show ) === "undefined" ) {
212 424 show = ! this.visible;
213 425 }
214 426
215 - // do nothing if element is being animated
216 - if( this.$element.is(':animated') ) {
427 + // is box already at desired visibility?
428 + if( show === this.visible ) {
217 429 return false;
218 430 }
219 431
220 - // is box already at desired visibility?
221 - if( show === this.visible ) {
432 + // is box being animated?
433 + if( Animator.animated(this.element) ) {
222 434 return false;
223 435 }
224 436
225 437 // if box should be hidden but is not closable, bail.
@@ -229,29 +441,27 @@
229 441
230 442 // set new visibility status
231 443 this.visible = show;
232 444
233 - // calculate custom styling for which CSS is "too stupid"
445 + // calculate new styling rules
234 446 this.setCustomBoxStyling();
235 447
236 - // fadein / fadeout the overlay if position is "center"
237 - if( this.config.position === 'center' ) {
238 - $(this.overlay).fadeToggle('slow');
239 - }
240 -
241 448 // trigger event
242 449 Boxzilla.trigger('box.' + ( show ? 'show' : 'hide' ), [ this ] );
243 450
244 451 // show or hide box using selected animation
245 - if( this.config.animation === 'fade' ) {
246 - this.$element.fadeToggle( 'slow' );
247 - } else {
248 - this.$element.slideToggle( 'slow' );
452 + if( this.config.position === 'center' ) {
453 + Animator.toggle(this.overlay, "fade");
249 454 }
250 455
251 - // // focus on first input field in box
252 - // this.$element.find('input').first().focus();
456 + Animator.toggle(this.element, this.config.animation);
253 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 +
254 464 return true;
255 465 };
256 466
257 467 // show the box
@@ -268,29 +478,20 @@
268 478 Box.prototype.calculateTriggerHeight = function() {
269 479 var triggerHeight = 0;
270 480
271 481 if( this.config.trigger.method === 'element' ) {
272 - var $triggerElement = $(this.config.trigger.value).first();
273 - 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 + }
274 487 } else if( this.config.trigger.method === 'percentage' ) {
275 - triggerHeight = ( this.config.trigger.value / 100 * $(document).height() );
488 + triggerHeight = ( this.config.trigger.value / 100 * getDocumentHeight() );
276 489 }
277 490
278 491 return triggerHeight;
279 492 };
280 493
281 -// set cookie that disables automatically showing the box
282 -Box.prototype.setCookie = function() {
283 - // do nothing if cookieTime evaluates to false
284 - if(! this.config.cookieTime) {
285 - return;
286 - }
287 -
288 - var expiryDate = new Date();
289 - expiryDate.setDate( expiryDate.getDate() + this.config.cookieTime );
290 - document.cookie = 'boxzilla_box_'+ this.id + '=true; expires='+ expiryDate.toUTCString() +'; path=/';
291 -};
292 -
293 494 // checks whether window.location.hash equals the box element ID or that of any element inside the box
294 495 Box.prototype.locationHashRefersBox = function() {
295 496
296 497 if( ! window.location.hash || 0 === window.location.hash.length ) {
@@ -317,10 +518,9 @@
317 518
318 519 // is this box enabled?
319 520 Box.prototype.mayAutoShow = function() {
320 521
321 - // don't show if box was closed (dismissed) before
322 - if( this.closed ) {
522 + if( this.dismissed ) {
323 523 return false;
324 524 }
325 525
326 526 // check if box fits on given minimum screen width
@@ -342,33 +542,48 @@
342 542 };
343 543
344 544 Box.prototype.isCookieSet = function() {
345 545 // always show on test mode
346 - if( this.config.testMode ) {
546 + if(this.config.testMode) {
347 547 return false;
348 548 }
349 549
350 - // check for cookie
351 - 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 ) ) {
352 552 return false;
353 553 }
354 554
355 555 var cookieSet = document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + 'boxzilla_box_' + this.id + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1") === "true";
356 556 return cookieSet;
557 +};
357 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=/';
358 564 };
359 565
360 566 Box.prototype.trigger = function() {
361 567 var shown = this.show();
362 - if( shown ) {
363 - this.triggered = true;
568 + if( ! shown ) {
569 + return;
364 570 }
571 +
572 + this.triggered = true;
573 + if(this.config.cookie && this.config.cookie.triggered) {
574 + this.setCookie(this.config.cookie.triggered);
575 + }
365 576 };
366 577
367 578 Box.prototype.dismiss = function() {
368 579 this.hide();
369 - this.setCookie();
370 - 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;
371 586 Boxzilla.trigger('box.dismiss', [ this ]);
372 587 };
373 588
374 589 module.exports = function(_Boxzilla) {
@@ -374,28 +589,20 @@
374 589 module.exports = function(_Boxzilla) {
375 590 Boxzilla = _Boxzilla;
376 591 return Box;
377 592 };
378 -},{}],3:[function(require,module,exports){
593 +},{"./animator.js":2}],4:[function(require,module,exports){
379 594 'use strict';
380 595
381 -var $ = window.jQuery,
382 - EventEmitter = require('wolfy87-eventemitter'),
596 +var EventEmitter = require('wolfy87-eventemitter'),
383 597 Boxzilla = Object.create(EventEmitter.prototype),
384 - Box = require('./Box.js')(Boxzilla),
385 - boxes = {},
386 - windowHeight = window.innerHeight,
387 - overlay = document.createElement('div'),
388 - exitIntentDelayTimer,
389 - exitIntentTriggered;
598 + Box = require('./box.js')(Boxzilla),
599 + Timer = require('./timer.js'),
600 + boxes = [],
601 + overlay,
602 + exitIntentDelayTimer, exitIntentTriggered,
603 + siteTimer, pageTimer, pageViews;
390 604
391 -function each( obj, callback ) {
392 - for( var key in obj ) {
393 - if(! obj.hasOwnProperty(key)) continue;
394 - callback(obj[key]);
395 - }
396 -}
397 -
398 605 function throttle(fn, threshhold, scope) {
399 606 threshhold || (threshhold = 250);
400 607 var last,
401 608 deferTimer;
@@ -424,35 +631,67 @@
424 631 Boxzilla.dismiss();
425 632 }
426 633 }
427 634
635 +// check "pageviews" criteria for each box
636 +function checkPageViewsCriteria() {
637 +
638 + // don't bother if another box is currently open
639 + if( isAnyBoxVisible() ) {
640 + return;
641 + }
642 +
643 + boxes.forEach(function(box) {
644 + if( ! box.mayAutoShow() ) {
645 + return;
646 + }
647 +
648 + if( box.config.trigger.method === 'pageviews' && pageViews >= box.config.trigger.value ) {
649 + box.trigger();
650 + }
651 + });
652 +}
653 +
654 +// check time trigger criteria for each box
428 655 function checkTimeCriteria() {
429 - var start = sessionStorage.getItem('boxzilla_start_time');
430 - var now = Date.now();
431 - var timeOnSite = ( now - start ) / 1000;
656 + // don't bother if another box is currently open
657 + if( isAnyBoxVisible() ) {
658 + return;
659 + }
432 660
433 - each(boxes, function(box) {
434 - if( ! box.mayAutoShow() || box.config.trigger.method !== 'time_on_site' ) {
661 + boxes.forEach(function(box) {
662 + if( ! box.mayAutoShow() ) {
435 663 return;
436 664 }
437 665
438 - if( timeOnSite > box.config.trigger.value ) {
666 + // check "time on site" trigger
667 + if (box.config.trigger.method === 'time_on_site' && siteTimer.time >= box.config.trigger.value) {
439 668 box.trigger();
440 669 }
670 +
671 + // check "time on page" trigger
672 + if (box.config.trigger.method === 'time_on_page' && pageTimer.time >= box.config.trigger.value) {
673 + box.trigger();
674 + }
441 675 });
442 676 }
443 677
444 678 // check triggerHeight criteria for all boxes
445 679 function checkHeightCriteria() {
446 - var scrollY = window.scrollY;
447 - var scrollHeight = scrollY + ( windowHeight * 0.9 );
680 + var scrollY = ( window.scrollY || window.pageYOffset ) + window.innerHeight * 0.75;
448 681
449 - 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 +
450 689 if( ! box.mayAutoShow() || box.triggerHeight <= 0 ) {
451 690 return;
452 691 }
453 692
454 - if( scrollHeight > box.triggerHeight ) {
693 + if( scrollY > box.triggerHeight ) {
455 694 box.trigger();
456 695 } else if( box.mayRehide() ) {
457 696 box.hide();
458 697 }
@@ -460,11 +699,9 @@
460 699 }
461 700
462 701 // recalculate heights and variables based on height
463 702 function recalculateHeights() {
464 - windowHeight = window.innerHeight;
465 -
466 - each(boxes, function(box) {
703 + boxes.forEach(function(box) {
467 704 box.setCustomBoxStyling();
468 705 });
469 706 }
470 707
@@ -472,9 +709,9 @@
472 709 var x = e.offsetX;
473 710 var y = e.offsetY;
474 711
475 712 // calculate if click was near a box to avoid closing it (click error margin)
476 - each(boxes, function(box) {
713 + boxes.forEach(function(box) {
477 714 var rect = box.element.getBoundingClientRect();
478 715 var margin = 100 + ( window.innerWidth * 0.05 );
479 716
480 717 // if click was not anywhere near box, dismiss it.
@@ -484,11 +721,14 @@
484 721 });
485 722 }
486 723
487 724 function triggerExitIntent() {
488 - if(exitIntentTriggered) return;
725 + // do nothing if already triggered OR another box is visible.
726 + if(exitIntentTriggered || isAnyBoxVisible() ) {
727 + return;
728 + }
489 729
490 - each(boxes, function(box) {
730 + boxes.forEach(function(box) {
491 731 if(box.mayAutoShow() && box.config.trigger.method === 'exit_intent' ) {
492 732 box.trigger();
493 733 }
494 734 });
@@ -496,14 +736,29 @@
496 736 exitIntentTriggered = true;
497 737 }
498 738
499 739 function onMouseLeave(e) {
740 + var delay = 400;
741 +
500 742 // did mouse leave at top of window?
501 - if( e.clientY < 0 ) {
502 - exitIntentDelayTimer = window.setTimeout(triggerExitIntent, 1000);
743 + if( e.clientY <= 0 ) {
744 + exitIntentDelayTimer = window.setTimeout(triggerExitIntent, delay);
503 745 }
504 746 }
505 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 +
506 761 function onMouseEnter() {
507 762 if( exitIntentDelayTimer ) {
508 763 window.clearInterval(exitIntentDelayTimer);
509 764 exitIntentDelayTimer = null;
@@ -509,27 +764,59 @@
509 764 exitIntentDelayTimer = null;
510 765 }
511 766 }
512 767
768 +var timers = {
769 + start: function() {
770 + var sessionTime = sessionStorage.getItem('boxzilla_timer');
771 + if( sessionTime ) siteTimer.time = sessionTime;
772 + siteTimer.start();
773 + pageTimer.start();
774 + },
775 + stop: function() {
776 + sessionStorage.setItem('boxzilla_timer', siteTimer.time);
777 + siteTimer.stop();
778 + pageTimer.stop();
779 + }
780 +};
781 +
513 782 // initialise & add event listeners
514 783 Boxzilla.init = function() {
784 + siteTimer = new Timer(sessionStorage.getItem('boxzilla_timer') || 0);
785 + pageTimer = new Timer(0);
786 + pageViews = sessionStorage.getItem('boxzilla_pageviews') || 0;
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 +
515 795 // add overlay element to dom
796 + overlay = document.createElement('div');
797 + overlay.style.display = 'none';
516 798 overlay.id = 'boxzilla-overlay';
517 799 document.body.appendChild(overlay);
518 800
519 801 // event binds
520 - $(window).on('scroll', throttle(checkHeightCriteria));
521 - $(window).on('resize', throttle(recalculateHeights));
522 - $(window).on('load', recalculateHeights );
523 - $(document).on('mouseleave', onMouseLeave);
524 - $(document).on('mouseenter', onMouseEnter);
525 - $(document).on('keyup', onKeyUp);
526 - $(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);
527 806 window.setInterval(checkTimeCriteria, 1000);
807 + window.setTimeout(checkPageViewsCriteria, 1000 );
808 + document.documentElement.addEventListener('mouseleave', onMouseLeave);
809 + document.documentElement.addEventListener('mouseenter', onMouseEnter);
810 + document.addEventListener('keyup', onKeyUp);
528 811
529 - if(! sessionStorage.getItem('boxzilla_start_time')) {
530 - sessionStorage.setItem('boxzilla_start_time', Date.now());
531 - }
812 + timers.start();
813 + window.addEventListener('focus', timers.start);
814 + window.addEventListener('beforeunload', function() {
815 + timers.stop();
816 + sessionStorage.setItem('boxzilla_pageviews', ++pageViews);
817 + });
818 + window.addEventListener('blur', timers.stop);
532 819
533 820 Boxzilla.trigger('ready');
534 821 };
535 822
@@ -541,52 +828,96 @@
541 828 *
542 829 * @returns Box
543 830 */
544 831 Boxzilla.create = function(id, opts) {
545 - boxes[id] = new Box(id, opts);
546 - return boxes[id];
832 + var box = new Box(id, opts);
833 + boxes.push(box);
834 + return box;
547 835 };
548 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 +
549 848 // dismiss a single box (or all by omitting id param)
550 849 Boxzilla.dismiss = function(id) {
551 850 // if no id given, dismiss all current open boxes
552 851 if( typeof(id) === "undefined" ) {
553 - each(boxes, function(box) { box.dismiss(); });
852 + boxes.forEach(function(box) { box.dismiss(); });
554 853 } else if( typeof( boxes[id] ) === "object" ) {
555 - boxes[id].dismiss();
854 + Boxzilla.get(id).dismiss();
556 855 }
557 856 };
558 857
559 858 Boxzilla.hide = function(id) {
560 859 if( typeof(id) === "undefined" ) {
561 - each(boxes, function(box) { box.hide(); });
562 - } else if( typeof( boxes[id] ) === "object" ) {
563 - boxes[id].hide();
860 + boxes.forEach(function(box) { box.hide(); });
861 + } else {
862 + Boxzilla.get(id).hide();
564 863 }
565 864 };
566 865
567 866 Boxzilla.show = function(id) {
568 867 if( typeof(id) === "undefined" ) {
569 - each(boxes, function(box) { box.show(); });
570 - } else if( typeof( boxes[id] ) === "object" ) {
571 - boxes[id].show();
868 + boxes.forEach(function(box) { box.show(); });
869 + } else {
870 + Boxzilla.get(id).show();
572 871 }
573 872 };
574 873
575 874 Boxzilla.toggle = function(id) {
576 875 if( typeof(id) === "undefined" ) {
577 - each(boxes, function(box) { box.toggle(); });
578 - } else if( typeof( boxes[id] ) === "object" ) {
579 - boxes[id].toggle();
876 + boxes.forEach(function(box) { box.toggle(); });
877 + } else {
878 + Boxzilla.get(id).toggle();
580 879 }
581 880 };
582 881
882 +// expose each individual box.
883 +Boxzilla.boxes = boxes;
884 +
885 +window.Boxzilla = Boxzilla;
886 +
583 887 if ( typeof module !== 'undefined' && module.exports ) {
584 888 module.exports = Boxzilla;
585 -} else {
586 - this.Boxzilla = Boxzilla;
587 889 }
588 -},{"./Box.js":2,"wolfy87-eventemitter":4}],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){
894 +'use strict';
895 +
896 +var Timer = function(start) {
897 + this.time = start;
898 + this.interval = 0;
899 +};
900 +
901 +Timer.prototype.tick = function() {
902 + this.time++;
903 +};
904 +
905 +Timer.prototype.start = function() {
906 + if( ! this.interval ) {
907 + this.interval = window.setInterval(this.tick.bind(this), 1000);
908 + }
909 +};
910 +
911 +Timer.prototype.stop = function() {
912 + if( this.interval ) {
913 + window.clearInterval(this.interval);
914 + this.interval = 0;
915 + }
916 +};
917 +
918 +module.exports = Timer;
919 +},{}],7:[function(require,module,exports){
589 920 /*!
590 921 * EventEmitter v4.2.11 - git.io/ee
591 922 * Unlicense - http://unlicense.org/
592 923 * Oliver Caldwell - http://oli.me.uk/
@@ -1061,5 +1392,5 @@
1061 1392 }
1062 1393 }.call(this));
1063 1394
1064 1395 },{}]},{},[1]);
1065 - })();
1396 +; })();