Boxzilla - Version 3.2.10

Version Description

Download this release

Release Info

Developer DvanKooten
Plugin Icon 128x128 Boxzilla
Version 3.2.10
Comparing to
See all releases

Code changes from version 3.2.9 to 3.2.10

Files changed (40) hide show
  1. assets/js/script.js +274 -198
  2. assets/js/script.min.js +1 -1
  3. assets/js/script.min.js.map +1 -1
  4. bootstrap.php +1 -1
  5. boxzilla.php +3 -3
  6. languages/boxzilla.pot +72 -68
  7. readme.txt +13 -2
  8. src/admin/class-admin.php +812 -763
  9. src/admin/class-autocomplete.php +97 -91
  10. src/admin/class-installer.php +93 -88
  11. src/admin/class-migrations.php +21 -17
  12. src/admin/class-notices.php +36 -32
  13. src/admin/class-review-notice.php +29 -23
  14. src/admin/migrations/3.1-cookie-option.php +9 -9
  15. src/admin/migrations/3.1.11-screen-width-condition.php +11 -11
  16. src/admin/views/extensions.php +16 -14
  17. src/admin/views/metaboxes/box-appearance-controls.php +19 -19
  18. src/admin/views/metaboxes/box-option-controls.php +145 -142
  19. src/admin/views/metaboxes/email-optin.php +3 -3
  20. src/admin/views/metaboxes/need-help.php +2 -2
  21. src/admin/views/settings.php +12 -12
  22. src/class-bootstrapper.php +24 -22
  23. src/class-box.php +209 -203
  24. src/class-boxzilla-service-provider.php +59 -64
  25. src/class-boxzilla.php +3 -1
  26. src/class-loader.php +336 -332
  27. src/class-php-fallback.php +58 -56
  28. src/class-plugin.php +114 -105
  29. src/default-actions.php +26 -25
  30. src/default-filters.php +9 -9
  31. src/di/class-container-with-property-access.php +24 -19
  32. src/functions.php +4 -4
  33. src/licensing/class-api-exception.php +29 -27
  34. src/licensing/class-api.php +174 -168
  35. src/licensing/class-license-manager.php +181 -173
  36. src/licensing/class-license-service-provider.php +34 -34
  37. src/licensing/class-license.php +108 -101
  38. src/licensing/class-poller.php +16 -13
  39. src/licensing/class-update-manager.php +240 -232
  40. src/licensing/views/license-form.php +32 -23
assets/js/script.js CHANGED
@@ -1,151 +1,12 @@
1
  (function () { var require = undefined; var module = undefined; var exports = undefined; var define = undefined; (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2
- "use strict";
3
-
4
- function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
5
-
6
- (function () {
7
- 'use strict';
8
-
9
- var Boxzilla = require('boxzilla');
10
-
11
- var options = window.boxzilla_options; // expose Boxzilla object to window
12
-
13
- window.Boxzilla = Boxzilla; // helper function for setting CSS styles
14
-
15
- function css(element, styles) {
16
- if (styles.background_color) {
17
- element.style.background = styles.background_color;
18
- }
19
-
20
- if (styles.color) {
21
- element.style.color = styles.color;
22
- }
23
-
24
- if (styles.border_color) {
25
- element.style.borderColor = styles.border_color;
26
- }
27
-
28
- if (styles.border_width) {
29
- element.style.borderWidth = parseInt(styles.border_width) + "px";
30
- }
31
-
32
- if (styles.border_style) {
33
- element.style.borderStyle = styles.border_style;
34
- }
35
-
36
- if (styles.width) {
37
- element.style.maxWidth = parseInt(styles.width) + "px";
38
- }
39
- }
40
-
41
- function createBoxesFromConfig() {
42
- // failsafe against including script twice.
43
- if (options.inited) {
44
- return;
45
- } // create boxes from options
46
-
47
-
48
- for (var key in options.boxes) {
49
- // get opts
50
- var boxOpts = options.boxes[key];
51
- boxOpts.testMode = isLoggedIn && options.testMode; // find box content element, bail if not found
52
-
53
- var boxContentElement = document.getElementById('boxzilla-box-' + boxOpts.id + '-content');
54
-
55
- if (!boxContentElement) {
56
- continue;
57
- } // use element as content option
58
-
59
-
60
- boxOpts.content = boxContentElement; // create box
61
-
62
- var box = Boxzilla.create(boxOpts.id, boxOpts); // add box slug to box element as classname
63
-
64
- box.element.className = box.element.className + ' boxzilla-' + boxOpts.post.slug; // add custom css to box
65
-
66
- css(box.element, boxOpts.css);
67
- box.element.firstChild.firstChild.className += " first-child";
68
- box.element.firstChild.lastChild.className += " last-child"; // maybe show box right away
69
-
70
- if (box.fits() && locationHashRefersBox(box)) {
71
- box.show();
72
- }
73
- } // set flag to prevent initialising twice
74
-
75
-
76
- options.inited = true; // trigger "done" event.
77
-
78
- Boxzilla.trigger('done'); // maybe open box with MC4WP form in it
79
-
80
- maybeOpenMailChimpForWordPressBox();
81
- }
82
-
83
- function locationHashRefersBox(box) {
84
- if (!window.location.hash || 0 === window.location.hash.length) {
85
- return false;
86
- }
87
-
88
- var elementId = window.location.hash.substring(1); // only attempt on strings looking like an ID
89
-
90
- var regex = /^[a-zA-Z\-\_0-9]+$/;
91
-
92
- if (!regex.test(elementId)) {
93
- return false;
94
- }
95
-
96
- if (elementId === box.element.id) {
97
- return true;
98
- } else if (box.element.querySelector('#' + elementId)) {
99
- return true;
100
- }
101
-
102
- return false;
103
- }
104
-
105
- function maybeOpenMailChimpForWordPressBox() {
106
- if (_typeof(window.mc4wp_forms_config) !== "object" || !window.mc4wp_forms_config.submitted_form) {
107
- return;
108
- }
109
-
110
- var selector = '#' + window.mc4wp_forms_config.submitted_form.element_id;
111
- var boxes = Boxzilla.boxes;
112
-
113
- for (var boxId in boxes) {
114
- if (!boxes.hasOwnProperty(boxId)) {
115
- continue;
116
- }
117
-
118
- var box = boxes[boxId];
119
-
120
- if (box.element.querySelector(selector)) {
121
- box.show();
122
- return;
123
- }
124
- }
125
- } // print message when test mode is enabled
126
-
127
-
128
- var isLoggedIn = document.body.className.indexOf('logged-in') > -1;
129
-
130
- if (isLoggedIn && options.testMode) {
131
- console.log('Boxzilla: Test mode is enabled. Please disable test mode if you\'re done testing.');
132
- } // init boxzilla
133
-
134
-
135
- Boxzilla.init(); // on window.load, create DOM elements for boxes
136
-
137
- window.addEventListener('load', createBoxesFromConfig);
138
- })();
139
-
140
- },{"boxzilla":5}],2:[function(require,module,exports){
141
  /*!
142
- * EventEmitter v4.2.11 - git.io/ee
143
  * Unlicense - http://unlicense.org/
144
  * Oliver Caldwell - http://oli.me.uk/
145
  * @preserve
146
  */
147
 
148
- ;(function () {
149
  'use strict';
150
 
151
  /**
@@ -158,7 +19,6 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
158
 
159
  // Shortcuts to improve speed and size
160
  var proto = EventEmitter.prototype;
161
- var exports = this;
162
  var originalGlobalValue = exports.EventEmitter;
163
 
164
  /**
@@ -259,6 +119,16 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
259
  return response || listeners;
260
  };
261
 
 
 
 
 
 
 
 
 
 
 
262
  /**
263
  * Adds a listener function to the specified event.
264
  * The listener will not be added if it is a duplicate.
@@ -270,6 +140,10 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
270
  * @return {Object} Current instance of EventEmitter for chaining.
271
  */
272
  proto.addListener = function addListener(evt, listener) {
 
 
 
 
273
  var listeners = this.getListenersAsObject(evt);
274
  var listenerIsWrapped = typeof listener === 'object';
275
  var key;
@@ -369,7 +243,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
369
 
370
  /**
371
  * Adds listeners in bulk using the manipulateListeners method.
372
- * 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.
373
  * You can also pass it a regular expression to add the array of listeners to all events that match it.
374
  * Yeah, this function does quite a bit. That's probably a bad thing.
375
  *
@@ -384,7 +258,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
384
 
385
  /**
386
  * Removes listeners in bulk using the manipulateListeners method.
387
- * 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.
388
  * You can also pass it an event name and an array of listeners to be removed.
389
  * You can also pass it a regular expression to remove the listeners from all events that match it.
390
  *
@@ -508,9 +382,8 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
508
  for (key in listenersMap) {
509
  if (listenersMap.hasOwnProperty(key)) {
510
  listeners = listenersMap[key].slice(0);
511
- i = listeners.length;
512
 
513
- while (i--) {
514
  // If the listener returns true then it shall be removed from the event
515
  // The function is executed either with a basic call or an apply if there is an args array
516
  listener = listeners[i];
@@ -611,9 +484,9 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat
611
  else {
612
  exports.EventEmitter = EventEmitter;
613
  }
614
- }.call(this));
615
 
616
- },{}],3:[function(require,module,exports){
617
  "use strict";
618
 
619
  var duration = 320;
@@ -787,7 +660,7 @@ module.exports = {
787
  'animated': animated
788
  };
789
 
790
- },{}],4:[function(require,module,exports){
791
  'use strict';
792
 
793
  var defaults = {
@@ -1130,21 +1003,28 @@ module.exports = function (_Boxzilla) {
1130
  return Box;
1131
  };
1132
 
1133
- },{"./animator.js":3}],5:[function(require,module,exports){
1134
  'use strict';
1135
 
1136
- var EventEmitter = require('wolfy87-eventemitter'),
1137
- Boxzilla = Object.create(EventEmitter.prototype),
1138
- Box = require('./box.js')(Boxzilla),
1139
- Timer = require('./timer.js'),
1140
- boxes = [],
1141
- overlay,
1142
- scrollElement = window,
1143
- exitIntentDelayTimer,
1144
- exitIntentTriggered,
1145
- siteTimer,
1146
- pageTimer,
1147
- pageViews;
 
 
 
 
 
 
 
1148
 
1149
  function throttle(fn, threshhold, scope) {
1150
  threshhold || (threshhold = 250);
@@ -1170,7 +1050,7 @@ function throttle(fn, threshhold, scope) {
1170
 
1171
 
1172
  function onKeyUp(e) {
1173
- if (e.keyCode == 27) {
1174
  Boxzilla.dismiss();
1175
  }
1176
  } // check "pageviews" criteria for each box
@@ -1264,9 +1144,9 @@ function onOverlayClick(e) {
1264
  });
1265
  }
1266
 
1267
- function triggerExitIntent() {
1268
  // do nothing if already triggered OR another box is visible.
1269
- if (exitIntentTriggered || isAnyBoxVisible()) {
1270
  return;
1271
  }
1272
 
@@ -1275,34 +1155,12 @@ function triggerExitIntent() {
1275
  box.trigger();
1276
  }
1277
  });
1278
- exitIntentTriggered = true;
1279
- }
1280
-
1281
- function onMouseLeave(e) {
1282
- var delay = 400; // did mouse leave at top of window?
1283
-
1284
- if (e.clientY <= 0) {
1285
- exitIntentDelayTimer = window.setTimeout(triggerExitIntent, delay);
1286
- }
1287
  }
1288
 
1289
  function isAnyBoxVisible() {
1290
- for (var i = 0; i < boxes.length; i++) {
1291
- var box = boxes[i];
1292
-
1293
- if (box.visible) {
1294
- return true;
1295
- }
1296
- }
1297
-
1298
- return false;
1299
- }
1300
-
1301
- function onMouseEnter() {
1302
- if (exitIntentDelayTimer) {
1303
- window.clearInterval(exitIntentDelayTimer);
1304
- exitIntentDelayTimer = null;
1305
- }
1306
  }
1307
 
1308
  function onElementClick(e) {
@@ -1346,6 +1204,10 @@ var timers = {
1346
  }; // initialise & add event listeners
1347
 
1348
  Boxzilla.init = function () {
 
 
 
 
1349
  document.body.addEventListener('click', onElementClick, true);
1350
 
1351
  try {
@@ -1357,8 +1219,6 @@ Boxzilla.init = function () {
1357
  siteTimer = new Timer(0);
1358
  pageTimer = new Timer(0); // insert styles into DOM
1359
 
1360
- var styles = require('./styles.js');
1361
-
1362
  var styleElement = document.createElement('style');
1363
  styleElement.setAttribute("type", "text/css");
1364
  styleElement.innerHTML = styles;
@@ -1367,8 +1227,9 @@ Boxzilla.init = function () {
1367
  overlay = document.createElement('div');
1368
  overlay.style.display = 'none';
1369
  overlay.id = 'boxzilla-overlay';
1370
- document.body.appendChild(overlay); // event binds
1371
 
 
1372
  scrollElement.addEventListener('touchstart', throttle(checkHeightCriteria), true);
1373
  scrollElement.addEventListener('scroll', throttle(checkHeightCriteria), true);
1374
  window.addEventListener('resize', throttle(recalculateHeights));
@@ -1376,8 +1237,6 @@ Boxzilla.init = function () {
1376
  overlay.addEventListener('click', onOverlayClick);
1377
  window.setInterval(checkTimeCriteria, 1000);
1378
  window.setTimeout(checkPageViewsCriteria, 1000);
1379
- document.documentElement.addEventListener('mouseleave', onMouseLeave);
1380
- document.documentElement.addEventListener('mouseenter', onMouseEnter);
1381
  document.addEventListener('keyup', onKeyUp);
1382
  timers.start();
1383
  window.addEventListener('focus', timers.start);
@@ -1387,6 +1246,7 @@ Boxzilla.init = function () {
1387
  });
1388
  window.addEventListener('blur', timers.stop);
1389
  Boxzilla.trigger('ready');
 
1390
  };
1391
  /**
1392
  * Create a new Box
@@ -1475,13 +1335,13 @@ if (typeof module !== 'undefined' && module.exports) {
1475
  module.exports = Boxzilla;
1476
  }
1477
 
1478
- },{"./box.js":4,"./styles.js":6,"./timer.js":7,"wolfy87-eventemitter":2}],6:[function(require,module,exports){
1479
  "use strict";
1480
 
1481
  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}";
1482
  module.exports = styles;
1483
 
1484
- },{}],7:[function(require,module,exports){
1485
  'use strict';
1486
 
1487
  var Timer = function Timer(start) {
@@ -1508,5 +1368,221 @@ Timer.prototype.stop = function () {
1508
 
1509
  module.exports = Timer;
1510
 
1511
- },{}]},{},[1]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1512
  ; })();
1
  (function () { var require = undefined; var module = undefined; var exports = undefined; var define = undefined; (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  /*!
3
+ * EventEmitter v5.2.5 - git.io/ee
4
  * Unlicense - http://unlicense.org/
5
  * Oliver Caldwell - http://oli.me.uk/
6
  * @preserve
7
  */
8
 
9
+ ;(function (exports) {
10
  'use strict';
11
 
12
  /**
19
 
20
  // Shortcuts to improve speed and size
21
  var proto = EventEmitter.prototype;
 
22
  var originalGlobalValue = exports.EventEmitter;
23
 
24
  /**
119
  return response || listeners;
120
  };
121
 
122
+ function isValidListener (listener) {
123
+ if (typeof listener === 'function' || listener instanceof RegExp) {
124
+ return true
125
+ } else if (listener && typeof listener === 'object') {
126
+ return isValidListener(listener.listener)
127
+ } else {
128
+ return false
129
+ }
130
+ }
131
+
132
  /**
133
  * Adds a listener function to the specified event.
134
  * The listener will not be added if it is a duplicate.
140
  * @return {Object} Current instance of EventEmitter for chaining.
141
  */
142
  proto.addListener = function addListener(evt, listener) {
143
+ if (!isValidListener(listener)) {
144
+ throw new TypeError('listener must be a function');
145
+ }
146
+
147
  var listeners = this.getListenersAsObject(evt);
148
  var listenerIsWrapped = typeof listener === 'object';
149
  var key;
243
 
244
  /**
245
  * Adds listeners in bulk using the manipulateListeners method.
246
+ * If you pass an object as the first 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.
247
  * You can also pass it a regular expression to add the array of listeners to all events that match it.
248
  * Yeah, this function does quite a bit. That's probably a bad thing.
249
  *
258
 
259
  /**
260
  * Removes listeners in bulk using the manipulateListeners method.
261
+ * If you pass an object as the first argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
262
  * You can also pass it an event name and an array of listeners to be removed.
263
  * You can also pass it a regular expression to remove the listeners from all events that match it.
264
  *
382
  for (key in listenersMap) {
383
  if (listenersMap.hasOwnProperty(key)) {
384
  listeners = listenersMap[key].slice(0);
 
385
 
386
+ for (i = 0; i < listeners.length; i++) {
387
  // If the listener returns true then it shall be removed from the event
388
  // The function is executed either with a basic call or an apply if there is an args array
389
  listener = listeners[i];
484
  else {
485
  exports.EventEmitter = EventEmitter;
486
  }
487
+ }(typeof window !== 'undefined' ? window : this || {}));
488
 
489
+ },{}],2:[function(require,module,exports){
490
  "use strict";
491
 
492
  var duration = 320;
660
  'animated': animated
661
  };
662
 
663
+ },{}],3:[function(require,module,exports){
664
  'use strict';
665
 
666
  var defaults = {
1003
  return Box;
1004
  };
1005
 
1006
+ },{"./animator.js":2}],4:[function(require,module,exports){
1007
  'use strict';
1008
 
1009
+ var EventEmitter = require('wolfy87-eventemitter');
1010
+
1011
+ var Timer = require('./timer.js');
1012
+
1013
+ var Boxzilla = Object.create(EventEmitter.prototype);
1014
+
1015
+ var Box = require('./box.js')(Boxzilla);
1016
+
1017
+ var boxes = [];
1018
+ var overlay;
1019
+ var scrollElement = window;
1020
+ var siteTimer;
1021
+ var pageTimer;
1022
+ var pageViews;
1023
+ var initialised = false;
1024
+
1025
+ var styles = require('./styles.js');
1026
+
1027
+ var ExitIntent = require('./triggers/exit-intent.js');
1028
 
1029
  function throttle(fn, threshhold, scope) {
1030
  threshhold || (threshhold = 250);
1050
 
1051
 
1052
  function onKeyUp(e) {
1053
+ if (e.keyCode === 27) {
1054
  Boxzilla.dismiss();
1055
  }
1056
  } // check "pageviews" criteria for each box
1144
  });
1145
  }
1146
 
1147
+ function showBoxesWithExitIntentTrigger() {
1148
  // do nothing if already triggered OR another box is visible.
1149
+ if (isAnyBoxVisible()) {
1150
  return;
1151
  }
1152
 
1155
  box.trigger();
1156
  }
1157
  });
 
 
 
 
 
 
 
 
 
1158
  }
1159
 
1160
  function isAnyBoxVisible() {
1161
+ return boxes.filter(function (b) {
1162
+ return b.visible;
1163
+ }).length > 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
1164
  }
1165
 
1166
  function onElementClick(e) {
1204
  }; // initialise & add event listeners
1205
 
1206
  Boxzilla.init = function () {
1207
+ if (initialised) {
1208
+ return;
1209
+ }
1210
+
1211
  document.body.addEventListener('click', onElementClick, true);
1212
 
1213
  try {
1219
  siteTimer = new Timer(0);
1220
  pageTimer = new Timer(0); // insert styles into DOM
1221
 
 
 
1222
  var styleElement = document.createElement('style');
1223
  styleElement.setAttribute("type", "text/css");
1224
  styleElement.innerHTML = styles;
1227
  overlay = document.createElement('div');
1228
  overlay.style.display = 'none';
1229
  overlay.id = 'boxzilla-overlay';
1230
+ document.body.appendChild(overlay); // init exit intent trigger
1231
 
1232
+ new ExitIntent(showBoxesWithExitIntentTrigger);
1233
  scrollElement.addEventListener('touchstart', throttle(checkHeightCriteria), true);
1234
  scrollElement.addEventListener('scroll', throttle(checkHeightCriteria), true);
1235
  window.addEventListener('resize', throttle(recalculateHeights));
1237
  overlay.addEventListener('click', onOverlayClick);
1238
  window.setInterval(checkTimeCriteria, 1000);
1239
  window.setTimeout(checkPageViewsCriteria, 1000);
 
 
1240
  document.addEventListener('keyup', onKeyUp);
1241
  timers.start();
1242
  window.addEventListener('focus', timers.start);
1246
  });
1247
  window.addEventListener('blur', timers.stop);
1248
  Boxzilla.trigger('ready');
1249
+ initialised = true; // ensure this function doesn't run again
1250
  };
1251
  /**
1252
  * Create a new Box
1335
  module.exports = Boxzilla;
1336
  }
1337
 
1338
+ },{"./box.js":3,"./styles.js":5,"./timer.js":6,"./triggers/exit-intent.js":7,"wolfy87-eventemitter":1}],5:[function(require,module,exports){
1339
  "use strict";
1340
 
1341
  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}";
1342
  module.exports = styles;
1343
 
1344
+ },{}],6:[function(require,module,exports){
1345
  'use strict';
1346
 
1347
  var Timer = function Timer(start) {
1368
 
1369
  module.exports = Timer;
1370
 
1371
+ },{}],7:[function(require,module,exports){
1372
+ 'use strict';
1373
+
1374
+ module.exports = function (callback) {
1375
+ var timeout = null;
1376
+ var touchStart = {};
1377
+
1378
+ function triggerCallback() {
1379
+ document.documentElement.removeEventListener('mouseleave', onMouseLeave);
1380
+ document.documentElement.removeEventListener('mouseenter', onMouseEnter);
1381
+ document.documentElement.removeEventListener('click', clearTimeout);
1382
+ window.removeEventListener('touchstart', onTouchStart);
1383
+ window.removeEventListener('touchend', onTouchEnd);
1384
+ callback();
1385
+ }
1386
+
1387
+ function clearTimeout() {
1388
+ if (timeout === null) {
1389
+ return;
1390
+ }
1391
+
1392
+ window.clearTimeout(timeout);
1393
+ timeout = null;
1394
+ }
1395
+
1396
+ function onMouseEnter(evt) {
1397
+ clearTimeout();
1398
+ }
1399
+
1400
+ function onMouseLeave(evt) {
1401
+ clearTimeout(); // did mouse leave at top of window?
1402
+ // add small exception space in the top-right corner
1403
+
1404
+ if (evt.clientY <= 0 && evt.clientX < 0.80 * window.innerWidth) {
1405
+ timeout = window.setTimeout(triggerCallback, 400);
1406
+ }
1407
+ }
1408
+
1409
+ function onTouchStart(evt) {
1410
+ clearTimeout();
1411
+ touchStart = {
1412
+ timestamp: performance.now(),
1413
+ scrollY: window.scrollY,
1414
+ windowHeight: window.innerHeight
1415
+ };
1416
+ }
1417
+
1418
+ function onTouchEnd(evt) {
1419
+ clearTimeout(); // did address bar appear?
1420
+
1421
+ if (window.innerHeight > touchStart.windowHeight) {
1422
+ return;
1423
+ } // allow a tiny tiny margin for error, to not fire on clicks
1424
+
1425
+
1426
+ if (window.scrollY + 20 >= touchStart.scrollY) {
1427
+ return;
1428
+ }
1429
+
1430
+ if (performance.now() - touchStart.timestamp > 300) {
1431
+ return;
1432
+ }
1433
+
1434
+ if (['A', 'INPUT', 'BUTTON'].indexOf(evt.target.tagName) > -1) {
1435
+ return;
1436
+ }
1437
+
1438
+ timeout = window.setTimeout(triggerCallback, 800);
1439
+ }
1440
+
1441
+ window.addEventListener('touchstart', onTouchStart);
1442
+ window.addEventListener('touchend', onTouchEnd);
1443
+ document.documentElement.addEventListener('mouseenter', onMouseEnter);
1444
+ document.documentElement.addEventListener('mouseleave', onMouseLeave);
1445
+ document.documentElement.addEventListener('click', clearTimeout);
1446
+ };
1447
+
1448
+ },{}],8:[function(require,module,exports){
1449
+ "use strict";
1450
+
1451
+ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
1452
+
1453
+ (function () {
1454
+ 'use strict';
1455
+
1456
+ var Boxzilla = require('boxzilla');
1457
+
1458
+ var options = window.boxzilla_options; // expose Boxzilla object to window
1459
+
1460
+ window.Boxzilla = Boxzilla; // helper function for setting CSS styles
1461
+
1462
+ function css(element, styles) {
1463
+ if (styles.background_color) {
1464
+ element.style.background = styles.background_color;
1465
+ }
1466
+
1467
+ if (styles.color) {
1468
+ element.style.color = styles.color;
1469
+ }
1470
+
1471
+ if (styles.border_color) {
1472
+ element.style.borderColor = styles.border_color;
1473
+ }
1474
+
1475
+ if (styles.border_width) {
1476
+ element.style.borderWidth = parseInt(styles.border_width) + "px";
1477
+ }
1478
+
1479
+ if (styles.border_style) {
1480
+ element.style.borderStyle = styles.border_style;
1481
+ }
1482
+
1483
+ if (styles.width) {
1484
+ element.style.maxWidth = parseInt(styles.width) + "px";
1485
+ }
1486
+ }
1487
+
1488
+ function createBoxesFromConfig() {
1489
+ // failsafe against including script twice.
1490
+ if (options.inited) {
1491
+ return;
1492
+ } // create boxes from options
1493
+
1494
+
1495
+ for (var key in options.boxes) {
1496
+ // get opts
1497
+ var boxOpts = options.boxes[key];
1498
+ boxOpts.testMode = isLoggedIn && options.testMode; // find box content element, bail if not found
1499
+
1500
+ var boxContentElement = document.getElementById('boxzilla-box-' + boxOpts.id + '-content');
1501
+
1502
+ if (!boxContentElement) {
1503
+ continue;
1504
+ } // use element as content option
1505
+
1506
+
1507
+ boxOpts.content = boxContentElement; // create box
1508
+
1509
+ var box = Boxzilla.create(boxOpts.id, boxOpts); // add box slug to box element as classname
1510
+
1511
+ box.element.className = box.element.className + ' boxzilla-' + boxOpts.post.slug; // add custom css to box
1512
+
1513
+ css(box.element, boxOpts.css);
1514
+ box.element.firstChild.firstChild.className += " first-child";
1515
+ box.element.firstChild.lastChild.className += " last-child"; // maybe show box right away
1516
+
1517
+ if (box.fits() && locationHashRefersBox(box)) {
1518
+ box.show();
1519
+ }
1520
+ } // set flag to prevent initialising twice
1521
+
1522
+
1523
+ options.inited = true; // trigger "done" event.
1524
+
1525
+ Boxzilla.trigger('done'); // maybe open box with MC4WP form in it
1526
+
1527
+ maybeOpenMailChimpForWordPressBox();
1528
+ }
1529
+
1530
+ function locationHashRefersBox(box) {
1531
+ if (!window.location.hash || 0 === window.location.hash.length) {
1532
+ return false;
1533
+ }
1534
+
1535
+ var elementId = window.location.hash.substring(1); // only attempt on strings looking like an ID
1536
+
1537
+ var regex = /^[a-zA-Z\-\_0-9]+$/;
1538
+
1539
+ if (!regex.test(elementId)) {
1540
+ return false;
1541
+ }
1542
+
1543
+ if (elementId === box.element.id) {
1544
+ return true;
1545
+ } else if (box.element.querySelector('#' + elementId)) {
1546
+ return true;
1547
+ }
1548
+
1549
+ return false;
1550
+ }
1551
+
1552
+ function maybeOpenMailChimpForWordPressBox() {
1553
+ if (_typeof(window.mc4wp_forms_config) !== "object" || !window.mc4wp_forms_config.submitted_form) {
1554
+ return;
1555
+ }
1556
+
1557
+ var selector = '#' + window.mc4wp_forms_config.submitted_form.element_id;
1558
+ var boxes = Boxzilla.boxes;
1559
+
1560
+ for (var boxId in boxes) {
1561
+ if (!boxes.hasOwnProperty(boxId)) {
1562
+ continue;
1563
+ }
1564
+
1565
+ var box = boxes[boxId];
1566
+
1567
+ if (box.element.querySelector(selector)) {
1568
+ box.show();
1569
+ return;
1570
+ }
1571
+ }
1572
+ } // print message when test mode is enabled
1573
+
1574
+
1575
+ var isLoggedIn = document.body.className.indexOf('logged-in') > -1;
1576
+
1577
+ if (isLoggedIn && options.testMode) {
1578
+ console.log('Boxzilla: Test mode is enabled. Please disable test mode if you\'re done testing.');
1579
+ } // init boxzilla
1580
+
1581
+
1582
+ Boxzilla.init(); // on window.load, create DOM elements for boxes
1583
+
1584
+ window.addEventListener('load', createBoxesFromConfig);
1585
+ })();
1586
+
1587
+ },{"boxzilla":4}]},{},[8]);
1588
  ; })();
assets/js/script.min.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(){var l=void 0;!function r(s,l,a){function c(e,t){if(!l[e]){if(!s[e]){var i=!1;if(!t&&i)return i(e,!0);if(d)return d(e,!0);var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}var o=l[e]={exports:{}};s[e][0].call(o.exports,function(t){return c(s[e][1][t]||t)},o,o.exports,r,s,l,a)}return l[e].exports}for(var d=!1,t=0;t<a.length;t++)c(a[t]);return c}({1:[function(t,e,i){"use strict";function d(t){return(d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}!function(){var s=t("boxzilla"),l=window.boxzilla_options;function a(t){if(!window.location.hash||0===window.location.hash.length)return!1;var e=window.location.hash.substring(1);return!!/^[a-zA-Z\-\_0-9]+$/.test(e)&&(e===t.element.id||!!t.element.querySelector("#"+e))}window.Boxzilla=s;var c=-1<document.body.className.indexOf("logged-in");c&&l.testMode&&console.log("Boxzilla: Test mode is enabled. Please disable test mode if you're done testing."),s.init(),window.addEventListener("load",function(){if(!l.inited){for(var t in l.boxes){var e=l.boxes[t];e.testMode=c&&l.testMode;var i=document.getElementById("boxzilla-box-"+e.id+"-content");if(i){e.content=i;var n=s.create(e.id,e);n.element.className=n.element.className+" boxzilla-"+e.post.slug,o=n.element,(r=e.css).background_color&&(o.style.background=r.background_color),r.color&&(o.style.color=r.color),r.border_color&&(o.style.borderColor=r.border_color),r.border_width&&(o.style.borderWidth=parseInt(r.border_width)+"px"),r.border_style&&(o.style.borderStyle=r.border_style),r.width&&(o.style.maxWidth=parseInt(r.width)+"px"),n.element.firstChild.firstChild.className+=" first-child",n.element.firstChild.lastChild.className+=" last-child",n.fits()&&a(n)&&n.show()}}var o,r;l.inited=!0,s.trigger("done"),function(){if("object"===d(window.mc4wp_forms_config)&&window.mc4wp_forms_config.submitted_form){var t="#"+window.mc4wp_forms_config.submitted_form.element_id,e=s.boxes;for(var i in e)if(e.hasOwnProperty(i)){var n=e[i];if(n.element.querySelector(t))return n.show()}}}()}})}()},{boxzilla:5}],2:[function(t,s,e){(function(){"use strict";function t(){}var e=t.prototype,i=this,n=i.EventEmitter;function r(t,e){for(var i=t.length;i--;)if(t[i].listener===e)return i;return-1}function o(t){return function(){return this[t].apply(this,arguments)}}e.getListeners=function(t){var e,i,n=this._getEvents();if(t instanceof RegExp)for(i in e={},n)n.hasOwnProperty(i)&&t.test(i)&&(e[i]=n[i]);else e=n[t]||(n[t]=[]);return e},e.flattenListeners=function(t){var e,i=[];for(e=0;e<t.length;e+=1)i.push(t[e].listener);return i},e.getListenersAsObject=function(t){var e,i=this.getListeners(t);return i instanceof Array&&((e={})[t]=i),e||i},e.addListener=function(t,e){var i,n=this.getListenersAsObject(t),o="object"==typeof e;for(i in n)n.hasOwnProperty(i)&&-1===r(n[i],e)&&n[i].push(o?e:{listener:e,once:!1});return this},e.on=o("addListener"),e.addOnceListener=function(t,e){return this.addListener(t,{listener:e,once:!0})},e.once=o("addOnceListener"),e.defineEvent=function(t){return this.getListeners(t),this},e.defineEvents=function(t){for(var e=0;e<t.length;e+=1)this.defineEvent(t[e]);return this},e.removeListener=function(t,e){var i,n,o=this.getListenersAsObject(t);for(n in o)o.hasOwnProperty(n)&&-1!==(i=r(o[n],e))&&o[n].splice(i,1);return this},e.off=o("removeListener"),e.addListeners=function(t,e){return this.manipulateListeners(!1,t,e)},e.removeListeners=function(t,e){return this.manipulateListeners(!0,t,e)},e.manipulateListeners=function(t,e,i){var n,o,r=t?this.removeListener:this.addListener,s=t?this.removeListeners:this.addListeners;if("object"!=typeof e||e instanceof RegExp)for(n=i.length;n--;)r.call(this,e,i[n]);else for(n in e)e.hasOwnProperty(n)&&(o=e[n])&&("function"==typeof o?r.call(this,n,o):s.call(this,n,o));return this},e.removeEvent=function(t){var e,i=typeof t,n=this._getEvents();if("string"===i)delete n[t];else if(t instanceof RegExp)for(e in n)n.hasOwnProperty(e)&&t.test(e)&&delete n[e];else delete this._events;return this},e.removeAllListeners=o("removeEvent"),e.emitEvent=function(t,e){var i,n,o,r,s=this.getListenersAsObject(t);for(r in s)if(s.hasOwnProperty(r))for(o=(i=s[r].slice(0)).length;o--;)!0===(n=i[o]).once&&this.removeListener(t,n.listener),n.listener.apply(this,e||[])===this._getOnceReturnValue()&&this.removeListener(t,n.listener);return this},e.trigger=o("emitEvent"),e.emit=function(t){var e=Array.prototype.slice.call(arguments,1);return this.emitEvent(t,e)},e.setOnceReturnValue=function(t){return this._onceReturnValue=t,this},e._getOnceReturnValue=function(){return!this.hasOwnProperty("_onceReturnValue")||this._onceReturnValue},e._getEvents=function(){return this._events||(this._events={})},t.noConflict=function(){return i.EventEmitter=n,t},"function"==typeof l&&l.amd?l(function(){return t}):"object"==typeof s&&s.exports?s.exports=t:i.EventEmitter=t}).call(this)},{}],3:[function(t,e,i){"use strict";var o=320;function c(t,e){for(var i in e)t.style[i]=e[i]}function d(c,d,h){var f=+new Date,t=window.getComputedStyle(c),u={},g={};for(var e in d){d[e]=parseFloat(d[e]);var i=d[e],n=parseFloat(t[e]);n!=i?(g[e]=(i-n)/o,u[e]=n):delete d[e]}!function t(){var e,i,n,o,r=+new Date-f,s=!0;for(var l in d){e=g[l],i=d[l],n=e*r,o=u[l]+n,0<e&&i<=o||e<0&&o<=i?o=i:s=!1,u[l]=o;var a="opacity"!==l?"px":"";c.style[l]=o+a}f=+new Date,s?h&&h():window.requestAnimationFrame&&requestAnimationFrame(t)||setTimeout(t,32)}()}e.exports={toggle:function(t,e,i){var n,o,r="none"!=t.style.display||0<t.offsetLeft,s=t.cloneNode(!0),l=function(){t.removeAttribute("data-animated"),t.setAttribute("style",s.getAttribute("style")),t.style.display=r?"none":"",i&&i()};if(t.setAttribute("data-animated","true"),r||(t.style.display=""),"slide"===e){if(n=function(t,e){for(var i={},n=0;n<t.length;n++)i[t[n]]=e;return i}(["height","borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],0),o={},!r){if(o=function(t,e){for(var i={},n=0;n<t.length;n++)i[t[n]]=e[t[n]];return i}(["height","borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],window.getComputedStyle(t)),!isFinite(o.height)){var a=t.getBoundingClientRect();o.height=a.height}c(t,n)}t.style.overflowY="hidden",d(t,r?n:o,l)}else n={opacity:0},o={opacity:1},r||c(t,n),d(t,r?n:o,l)},animate:d,animated:function(t){return!!t.getAttribute("data-animated")}}},{}],4:[function(t,e,i){"use strict";var n,o={animation:"fade",rehide:!1,content:"",cookie:null,icon:"&times",screenWidthCondition:null,position:"center",testMode:!1,trigger:!1,closable:!0},r=t("./animator.js");var s=function(t,e){this.id=t,this.config=function(t,e){var i={};for(var n in t)i[n]=t[n];for(var n in e)i[n]=e[n];return i}(o,e),this.overlay=document.getElementById("boxzilla-overlay"),this.visible=!1,this.dismissed=!1,this.triggered=!1,this.triggerHeight=this.calculateTriggerHeight(),this.cookieSet=this.isCookieSet(),this.element=null,this.contentElement=null,this.closeIcon=null,this.dom(),this.events()};s.prototype.events=function(){var e=this;this.closeIcon&&this.closeIcon.addEventListener("click",this.dismiss.bind(this)),this.element.addEventListener("click",function(t){"A"===t.target.tagName&&n.trigger("box.interactions.link",[e,t.target])},!1),this.element.addEventListener("submit",function(t){e.setCookie(),n.trigger("box.interactions.form",[e,t.target])},!1)},s.prototype.dom=function(){var t=document.createElement("div");t.className="boxzilla-container boxzilla-"+this.config.position+"-container";var e,i=document.createElement("div");if(i.setAttribute("id","boxzilla-"+this.id),i.className="boxzilla boxzilla-"+this.id+" boxzilla-"+this.config.position,i.style.display="none",t.appendChild(i),"string"==typeof this.config.content?(e=document.createElement("div")).innerHTML=this.config.content:(e=this.config.content).style.display="",e.className="boxzilla-content",i.appendChild(e),this.config.closable&&this.config.icon){var n=document.createElement("span");n.className="boxzilla-close-icon",n.innerHTML=this.config.icon,i.appendChild(n),this.closeIcon=n}document.body.appendChild(t),this.contentElement=e,this.element=i},s.prototype.setCustomBoxStyling=function(){var t=this.element.style.display;this.element.style.display="",this.element.style.overflowY="auto",this.element.style.maxHeight="none";var e=window.innerHeight,i=this.element.clientHeight;if(e<i&&(this.element.style.maxHeight=e+"px",this.element.style.overflowY="scroll"),"center"===this.config.position){var n=(e-i)/2;n=0<=n?n:0,this.element.style.marginTop=n+"px"}this.element.style.display=t},s.prototype.toggle=function(t){return void 0===t&&(t=!this.visible),t!==this.visible&&(!r.animated(this.element)&&(!(!t&&!this.config.closable)&&(this.visible=t,this.setCustomBoxStyling(),n.trigger("box."+(t?"show":"hide"),[this]),"center"===this.config.position&&(this.overlay.classList.toggle("boxzilla-"+this.id+"-overlay"),r.toggle(this.overlay,"fade")),r.toggle(this.element,this.config.animation,function(){this.visible||(this.contentElement.innerHTML=this.contentElement.innerHTML)}.bind(this)),!0)))},s.prototype.show=function(){return this.toggle(!0)},s.prototype.hide=function(){return this.toggle(!1)},s.prototype.calculateTriggerHeight=function(){var t,e,i=0;if(this.config.trigger)if("element"===this.config.trigger.method){var n=document.body.querySelector(this.config.trigger.value);if(n)i=n.getBoundingClientRect().top}else"percentage"===this.config.trigger.method&&(i=this.config.trigger.value/100*(t=document.body,e=document.documentElement,Math.max(t.scrollHeight,t.offsetHeight,e.clientHeight,e.scrollHeight,e.offsetHeight)));return i},s.prototype.fits=function(){if(!this.config.screenWidthCondition||!this.config.screenWidthCondition.value)return!0;switch(this.config.screenWidthCondition.condition){case"larger":return window.innerWidth>this.config.screenWidthCondition.value;case"smaller":return window.innerWidth<this.config.screenWidthCondition.value}return!0},s.prototype.onResize=function(){this.triggerHeight=this.calculateTriggerHeight(),this.setCustomBoxStyling()},s.prototype.mayAutoShow=function(){return!this.dismissed&&(!!this.fits()&&(!!this.config.trigger&&!this.cookieSet))},s.prototype.mayRehide=function(){return this.config.rehide&&this.triggered},s.prototype.isCookieSet=function(){return!(this.config.testMode||!this.config.trigger)&&(!(!this.config.cookie||!this.config.cookie.triggered&&!this.config.cookie.dismissed)&&"true"===document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*boxzilla_box_"+this.id+"\\s*\\=\\s*([^;]*).*$)|^.*$"),"$1"))},s.prototype.setCookie=function(t){var e=new Date;e.setHours(e.getHours()+t),document.cookie="boxzilla_box_"+this.id+"=true; expires="+e.toUTCString()+"; path=/"},s.prototype.trigger=function(){this.show()&&(this.triggered=!0,this.config.cookie&&this.config.cookie.triggered&&this.setCookie(this.config.cookie.triggered))},s.prototype.dismiss=function(t){return t&&t.preventDefault(),!!this.visible&&(this.hide(),this.config.cookie&&this.config.cookie.dismissed&&this.setCookie(this.config.cookie.dismissed),this.dismissed=!0,n.trigger("box.dismiss",[this]),!0)},e.exports=function(t){return n=t,s}},{"./animator.js":3}],5:[function(i,t,e){"use strict";var n,o,r,s,l,a,c=i("wolfy87-eventemitter"),d=Object.create(c.prototype),h=i("./box.js")(d),f=i("./timer.js"),u=[],g=window;function m(n,o,r){var s,l;return o||(o=250),function(){var t=r||this,e=+new Date,i=arguments;s&&e<s+o?(clearTimeout(l),l=setTimeout(function(){s=e,n.apply(t,i)},o)):(s=e,n.apply(t,i))}}function p(t){27==t.keyCode&&d.dismiss()}function v(){L()||u.forEach(function(t){t.mayAutoShow()&&"pageviews"===t.config.trigger.method&&a>=t.config.trigger.value&&t.trigger()})}function b(){L()||u.forEach(function(t){t.mayAutoShow()&&("time_on_site"===t.config.trigger.method&&s.time>=t.config.trigger.value&&t.trigger(),"time_on_page"===t.config.trigger.method&&l.time>=t.config.trigger.value&&t.trigger())})}function y(){var e=g.hasOwnProperty("pageYOffset")?g.pageYOffset:g.scrollTop;e+=.9*window.innerHeight,u.forEach(function(t){if(t.mayAutoShow()&&!(t.triggerHeight<=0)){if(e>t.triggerHeight){if(L())return;t.trigger()}t.mayRehide()&&e<t.triggerHeight-5&&t.hide()}})}function w(){u.forEach(function(t){t.onResize()})}function x(t){var i=t.offsetX,n=t.offsetY;u.forEach(function(t){var e=t.element.getBoundingClientRect();(i<e.left-40||i>e.right+40||n<e.top-40||n>e.bottom+40)&&t.dismiss()})}function E(){r||L()||(u.forEach(function(t){t.mayAutoShow()&&"exit_intent"===t.config.trigger.method&&t.trigger()}),r=!0)}function z(t){t.clientY<=0&&(o=window.setTimeout(E,400))}function L(){for(var t=0;t<u.length;t++){if(u[t].visible)return!0}return!1}function _(){o&&(window.clearInterval(o),o=null)}function C(t){for(var e=t.target||t.srcElement,i=0;i<=3&&(e&&"A"!==e.tagName);i++)e=e.parentElement;if(e&&"A"===e.tagName&&e.getAttribute("href")&&0===e.getAttribute("href").toLowerCase().indexOf("#boxzilla-")){var n=e.getAttribute("href").toLowerCase().substring("#boxzilla-".length);d.toggle(n)}}var k=function(){try{var t=sessionStorage.getItem("boxzilla_timer");t&&(s.time=t)}catch(t){}s.start(),l.start()},S=function(){sessionStorage.setItem("boxzilla_timer",s.time),s.stop(),l.stop()};d.init=function(){document.body.addEventListener("click",C,!0);try{a=sessionStorage.getItem("boxzilla_pageviews")||0}catch(t){a=0}s=new f(0),l=new f(0);var t=i("./styles.js"),e=document.createElement("style");e.setAttribute("type","text/css"),e.innerHTML=t,document.head.appendChild(e),(n=document.createElement("div")).style.display="none",n.id="boxzilla-overlay",document.body.appendChild(n),g.addEventListener("touchstart",m(y),!0),g.addEventListener("scroll",m(y),!0),window.addEventListener("resize",m(w)),window.addEventListener("load",w),n.addEventListener("click",x),window.setInterval(b,1e3),window.setTimeout(v,1e3),document.documentElement.addEventListener("mouseleave",z),document.documentElement.addEventListener("mouseenter",_),document.addEventListener("keyup",p),k(),window.addEventListener("focus",k),window.addEventListener("beforeunload",function(){S(),sessionStorage.setItem("boxzilla_pageviews",++a)}),window.addEventListener("blur",S),d.trigger("ready")},d.create=function(t,e){void 0!==e.minimumScreenWidth&&(e.screenWidthCondition={condition:"larger",value:e.minimumScreenWidth});var i=new h(t,e);return u.push(i),i},d.get=function(t){for(var e=0;e<u.length;e++){var i=u[e];if(i.id==t)return i}throw new Error("No box exists with ID "+t)},d.dismiss=function(t){void 0===t?u.forEach(function(t){t.dismiss()}):d.get(t).dismiss()},d.hide=function(t){void 0===t?u.forEach(function(t){t.hide()}):d.get(t).hide()},d.show=function(t){void 0===t?u.forEach(function(t){t.show()}):d.get(t).show()},d.toggle=function(t){void 0===t?u.forEach(function(t){t.toggle()}):d.get(t).toggle()},d.boxes=u,window.Boxzilla=d,void 0!==t&&t.exports&&(t.exports=d)},{"./box.js":4,"./styles.js":6,"./timer.js":7,"wolfy87-eventemitter":2}],6:[function(t,e,i){"use strict";e.exports="#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}"},{}],7:[function(t,e,i){"use strict";var n=function(t){this.time=t,this.interval=0};n.prototype.tick=function(){this.time++},n.prototype.start=function(){this.interval||(this.interval=window.setInterval(this.tick.bind(this),1e3))},n.prototype.stop=function(){this.interval&&(window.clearInterval(this.interval),this.interval=0)},e.exports=n},{}]},{},[1])}();
2
  //# sourceMappingURL=script.min.js.map
1
+ !function(){var l=void 0;!function r(s,l,c){function a(e,t){if(!l[e]){if(!s[e]){var i=!1;if(!t&&i)return i(e,!0);if(d)return d(e,!0);var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}var o=l[e]={exports:{}};s[e][0].call(o.exports,function(t){return a(s[e][1][t]||t)},o,o.exports,r,s,l,c)}return l[e].exports}for(var d=!1,t=0;t<c.length;t++)a(c[t]);return a}({1:[function(t,s,e){!function(t){"use strict";function e(){}var i=e.prototype,n=t.EventEmitter;function r(t,e){for(var i=t.length;i--;)if(t[i].listener===e)return i;return-1}function o(t){return function(){return this[t].apply(this,arguments)}}i.getListeners=function(t){var e,i,n=this._getEvents();if(t instanceof RegExp)for(i in e={},n)n.hasOwnProperty(i)&&t.test(i)&&(e[i]=n[i]);else e=n[t]||(n[t]=[]);return e},i.flattenListeners=function(t){var e,i=[];for(e=0;e<t.length;e+=1)i.push(t[e].listener);return i},i.getListenersAsObject=function(t){var e,i=this.getListeners(t);return i instanceof Array&&((e={})[t]=i),e||i},i.addListener=function(t,e){if(!function t(e){return"function"==typeof e||e instanceof RegExp||!(!e||"object"!=typeof e)&&t(e.listener)}(e))throw new TypeError("listener must be a function");var i,n=this.getListenersAsObject(t),o="object"==typeof e;for(i in n)n.hasOwnProperty(i)&&-1===r(n[i],e)&&n[i].push(o?e:{listener:e,once:!1});return this},i.on=o("addListener"),i.addOnceListener=function(t,e){return this.addListener(t,{listener:e,once:!0})},i.once=o("addOnceListener"),i.defineEvent=function(t){return this.getListeners(t),this},i.defineEvents=function(t){for(var e=0;e<t.length;e+=1)this.defineEvent(t[e]);return this},i.removeListener=function(t,e){var i,n,o=this.getListenersAsObject(t);for(n in o)o.hasOwnProperty(n)&&-1!==(i=r(o[n],e))&&o[n].splice(i,1);return this},i.off=o("removeListener"),i.addListeners=function(t,e){return this.manipulateListeners(!1,t,e)},i.removeListeners=function(t,e){return this.manipulateListeners(!0,t,e)},i.manipulateListeners=function(t,e,i){var n,o,r=t?this.removeListener:this.addListener,s=t?this.removeListeners:this.addListeners;if("object"!=typeof e||e instanceof RegExp)for(n=i.length;n--;)r.call(this,e,i[n]);else for(n in e)e.hasOwnProperty(n)&&(o=e[n])&&("function"==typeof o?r.call(this,n,o):s.call(this,n,o));return this},i.removeEvent=function(t){var e,i=typeof t,n=this._getEvents();if("string"===i)delete n[t];else if(t instanceof RegExp)for(e in n)n.hasOwnProperty(e)&&t.test(e)&&delete n[e];else delete this._events;return this},i.removeAllListeners=o("removeEvent"),i.emitEvent=function(t,e){var i,n,o,r,s=this.getListenersAsObject(t);for(r in s)if(s.hasOwnProperty(r))for(i=s[r].slice(0),o=0;o<i.length;o++)!0===(n=i[o]).once&&this.removeListener(t,n.listener),n.listener.apply(this,e||[])===this._getOnceReturnValue()&&this.removeListener(t,n.listener);return this},i.trigger=o("emitEvent"),i.emit=function(t){var e=Array.prototype.slice.call(arguments,1);return this.emitEvent(t,e)},i.setOnceReturnValue=function(t){return this._onceReturnValue=t,this},i._getOnceReturnValue=function(){return!this.hasOwnProperty("_onceReturnValue")||this._onceReturnValue},i._getEvents=function(){return this._events||(this._events={})},e.noConflict=function(){return t.EventEmitter=n,e},"function"==typeof l&&l.amd?l(function(){return e}):"object"==typeof s&&s.exports?s.exports=e:t.EventEmitter=e}("undefined"!=typeof window?window:this||{})},{}],2:[function(t,e,i){"use strict";var o=320;function a(t,e){for(var i in e)t.style[i]=e[i]}function d(a,d,u){var h=+new Date,t=window.getComputedStyle(a),f={},g={};for(var e in d){d[e]=parseFloat(d[e]);var i=d[e],n=parseFloat(t[e]);n!=i?(g[e]=(i-n)/o,f[e]=n):delete d[e]}!function t(){var e,i,n,o,r=+new Date-h,s=!0;for(var l in d){e=g[l],i=d[l],n=e*r,o=f[l]+n,0<e&&i<=o||e<0&&o<=i?o=i:s=!1,f[l]=o;var c="opacity"!==l?"px":"";a.style[l]=o+c}h=+new Date,s?u&&u():window.requestAnimationFrame&&requestAnimationFrame(t)||setTimeout(t,32)}()}e.exports={toggle:function(t,e,i){var n,o,r="none"!=t.style.display||0<t.offsetLeft,s=t.cloneNode(!0),l=function(){t.removeAttribute("data-animated"),t.setAttribute("style",s.getAttribute("style")),t.style.display=r?"none":"",i&&i()};if(t.setAttribute("data-animated","true"),r||(t.style.display=""),"slide"===e){if(n=function(t,e){for(var i={},n=0;n<t.length;n++)i[t[n]]=e;return i}(["height","borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],0),o={},!r){if(o=function(t,e){for(var i={},n=0;n<t.length;n++)i[t[n]]=e[t[n]];return i}(["height","borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],window.getComputedStyle(t)),!isFinite(o.height)){var c=t.getBoundingClientRect();o.height=c.height}a(t,n)}t.style.overflowY="hidden",d(t,r?n:o,l)}else n={opacity:0},o={opacity:1},r||a(t,n),d(t,r?n:o,l)},animate:d,animated:function(t){return!!t.getAttribute("data-animated")}}},{}],3:[function(t,e,i){"use strict";var n,o={animation:"fade",rehide:!1,content:"",cookie:null,icon:"&times",screenWidthCondition:null,position:"center",testMode:!1,trigger:!1,closable:!0},r=t("./animator.js");var s=function(t,e){this.id=t,this.config=function(t,e){var i={};for(var n in t)i[n]=t[n];for(var n in e)i[n]=e[n];return i}(o,e),this.overlay=document.getElementById("boxzilla-overlay"),this.visible=!1,this.dismissed=!1,this.triggered=!1,this.triggerHeight=this.calculateTriggerHeight(),this.cookieSet=this.isCookieSet(),this.element=null,this.contentElement=null,this.closeIcon=null,this.dom(),this.events()};s.prototype.events=function(){var e=this;this.closeIcon&&this.closeIcon.addEventListener("click",this.dismiss.bind(this)),this.element.addEventListener("click",function(t){"A"===t.target.tagName&&n.trigger("box.interactions.link",[e,t.target])},!1),this.element.addEventListener("submit",function(t){e.setCookie(),n.trigger("box.interactions.form",[e,t.target])},!1)},s.prototype.dom=function(){var t=document.createElement("div");t.className="boxzilla-container boxzilla-"+this.config.position+"-container";var e,i=document.createElement("div");if(i.setAttribute("id","boxzilla-"+this.id),i.className="boxzilla boxzilla-"+this.id+" boxzilla-"+this.config.position,i.style.display="none",t.appendChild(i),"string"==typeof this.config.content?(e=document.createElement("div")).innerHTML=this.config.content:(e=this.config.content).style.display="",e.className="boxzilla-content",i.appendChild(e),this.config.closable&&this.config.icon){var n=document.createElement("span");n.className="boxzilla-close-icon",n.innerHTML=this.config.icon,i.appendChild(n),this.closeIcon=n}document.body.appendChild(t),this.contentElement=e,this.element=i},s.prototype.setCustomBoxStyling=function(){var t=this.element.style.display;this.element.style.display="",this.element.style.overflowY="auto",this.element.style.maxHeight="none";var e=window.innerHeight,i=this.element.clientHeight;if(e<i&&(this.element.style.maxHeight=e+"px",this.element.style.overflowY="scroll"),"center"===this.config.position){var n=(e-i)/2;n=0<=n?n:0,this.element.style.marginTop=n+"px"}this.element.style.display=t},s.prototype.toggle=function(t){return void 0===t&&(t=!this.visible),t!==this.visible&&(!r.animated(this.element)&&(!(!t&&!this.config.closable)&&(this.visible=t,this.setCustomBoxStyling(),n.trigger("box."+(t?"show":"hide"),[this]),"center"===this.config.position&&(this.overlay.classList.toggle("boxzilla-"+this.id+"-overlay"),r.toggle(this.overlay,"fade")),r.toggle(this.element,this.config.animation,function(){this.visible||(this.contentElement.innerHTML=this.contentElement.innerHTML)}.bind(this)),!0)))},s.prototype.show=function(){return this.toggle(!0)},s.prototype.hide=function(){return this.toggle(!1)},s.prototype.calculateTriggerHeight=function(){var t,e,i=0;if(this.config.trigger)if("element"===this.config.trigger.method){var n=document.body.querySelector(this.config.trigger.value);if(n)i=n.getBoundingClientRect().top}else"percentage"===this.config.trigger.method&&(i=this.config.trigger.value/100*(t=document.body,e=document.documentElement,Math.max(t.scrollHeight,t.offsetHeight,e.clientHeight,e.scrollHeight,e.offsetHeight)));return i},s.prototype.fits=function(){if(!this.config.screenWidthCondition||!this.config.screenWidthCondition.value)return!0;switch(this.config.screenWidthCondition.condition){case"larger":return window.innerWidth>this.config.screenWidthCondition.value;case"smaller":return window.innerWidth<this.config.screenWidthCondition.value}return!0},s.prototype.onResize=function(){this.triggerHeight=this.calculateTriggerHeight(),this.setCustomBoxStyling()},s.prototype.mayAutoShow=function(){return!this.dismissed&&(!!this.fits()&&(!!this.config.trigger&&!this.cookieSet))},s.prototype.mayRehide=function(){return this.config.rehide&&this.triggered},s.prototype.isCookieSet=function(){return!(this.config.testMode||!this.config.trigger)&&(!(!this.config.cookie||!this.config.cookie.triggered&&!this.config.cookie.dismissed)&&"true"===document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*boxzilla_box_"+this.id+"\\s*\\=\\s*([^;]*).*$)|^.*$"),"$1"))},s.prototype.setCookie=function(t){var e=new Date;e.setHours(e.getHours()+t),document.cookie="boxzilla_box_"+this.id+"=true; expires="+e.toUTCString()+"; path=/"},s.prototype.trigger=function(){this.show()&&(this.triggered=!0,this.config.cookie&&this.config.cookie.triggered&&this.setCookie(this.config.cookie.triggered))},s.prototype.dismiss=function(t){return t&&t.preventDefault(),!!this.visible&&(this.hide(),this.config.cookie&&this.config.cookie.dismissed&&this.setCookie(this.config.cookie.dismissed),this.dismissed=!0,n.trigger("box.dismiss",[this]),!0)},e.exports=function(t){return n=t,s}},{"./animator.js":2}],4:[function(t,e,i){"use strict";var n,o,r,s,l=t("wolfy87-eventemitter"),c=t("./timer.js"),a=Object.create(l.prototype),d=t("./box.js")(a),u=[],h=window,f=!1,g=t("./styles.js"),m=t("./triggers/exit-intent.js");function p(n,o,r){var s,l;return o||(o=250),function(){var t=r||this,e=+new Date,i=arguments;s&&e<s+o?(clearTimeout(l),l=setTimeout(function(){s=e,n.apply(t,i)},o)):(s=e,n.apply(t,i))}}function v(t){27===t.keyCode&&a.dismiss()}function b(){z()||u.forEach(function(t){t.mayAutoShow()&&"pageviews"===t.config.trigger.method&&s>=t.config.trigger.value&&t.trigger()})}function w(){z()||u.forEach(function(t){t.mayAutoShow()&&("time_on_site"===t.config.trigger.method&&o.time>=t.config.trigger.value&&t.trigger(),"time_on_page"===t.config.trigger.method&&r.time>=t.config.trigger.value&&t.trigger())})}function y(){var e=h.hasOwnProperty("pageYOffset")?h.pageYOffset:h.scrollTop;e+=.9*window.innerHeight,u.forEach(function(t){if(t.mayAutoShow()&&!(t.triggerHeight<=0)){if(e>t.triggerHeight){if(z())return;t.trigger()}t.mayRehide()&&e<t.triggerHeight-5&&t.hide()}})}function x(){u.forEach(function(t){t.onResize()})}function E(t){var i=t.offsetX,n=t.offsetY;u.forEach(function(t){var e=t.element.getBoundingClientRect();(i<e.left-40||i>e.right+40||n<e.top-40||n>e.bottom+40)&&t.dismiss()})}function L(){z()||u.forEach(function(t){t.mayAutoShow()&&"exit_intent"===t.config.trigger.method&&t.trigger()})}function z(){return 0<u.filter(function(t){return t.visible}).length}function _(t){for(var e=t.target||t.srcElement,i=0;i<=3&&(e&&"A"!==e.tagName);i++)e=e.parentElement;if(e&&"A"===e.tagName&&e.getAttribute("href")&&0===e.getAttribute("href").toLowerCase().indexOf("#boxzilla-")){var n=e.getAttribute("href").toLowerCase().substring("#boxzilla-".length);a.toggle(n)}}var k=function(){try{var t=sessionStorage.getItem("boxzilla_timer");t&&(o.time=t)}catch(t){}o.start(),r.start()},C=function(){sessionStorage.setItem("boxzilla_timer",o.time),o.stop(),r.stop()};a.init=function(){if(!f){document.body.addEventListener("click",_,!0);try{s=sessionStorage.getItem("boxzilla_pageviews")||0}catch(t){s=0}o=new c(0),r=new c(0);var t=document.createElement("style");t.setAttribute("type","text/css"),t.innerHTML=g,document.head.appendChild(t),(n=document.createElement("div")).style.display="none",n.id="boxzilla-overlay",document.body.appendChild(n),new m(L),h.addEventListener("touchstart",p(y),!0),h.addEventListener("scroll",p(y),!0),window.addEventListener("resize",p(x)),window.addEventListener("load",x),n.addEventListener("click",E),window.setInterval(w,1e3),window.setTimeout(b,1e3),document.addEventListener("keyup",v),k(),window.addEventListener("focus",k),window.addEventListener("beforeunload",function(){C(),sessionStorage.setItem("boxzilla_pageviews",++s)}),window.addEventListener("blur",C),a.trigger("ready"),f=!0}},a.create=function(t,e){void 0!==e.minimumScreenWidth&&(e.screenWidthCondition={condition:"larger",value:e.minimumScreenWidth});var i=new d(t,e);return u.push(i),i},a.get=function(t){for(var e=0;e<u.length;e++){var i=u[e];if(i.id==t)return i}throw new Error("No box exists with ID "+t)},a.dismiss=function(t){void 0===t?u.forEach(function(t){t.dismiss()}):a.get(t).dismiss()},a.hide=function(t){void 0===t?u.forEach(function(t){t.hide()}):a.get(t).hide()},a.show=function(t){void 0===t?u.forEach(function(t){t.show()}):a.get(t).show()},a.toggle=function(t){void 0===t?u.forEach(function(t){t.toggle()}):a.get(t).toggle()},a.boxes=u,window.Boxzilla=a,void 0!==e&&e.exports&&(e.exports=a)},{"./box.js":3,"./styles.js":5,"./timer.js":6,"./triggers/exit-intent.js":7,"wolfy87-eventemitter":1}],5:[function(t,e,i){"use strict";e.exports="#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}"},{}],6:[function(t,e,i){"use strict";var n=function(t){this.time=t,this.interval=0};n.prototype.tick=function(){this.time++},n.prototype.start=function(){this.interval||(this.interval=window.setInterval(this.tick.bind(this),1e3))},n.prototype.stop=function(){this.interval&&(window.clearInterval(this.interval),this.interval=0)},e.exports=n},{}],7:[function(t,e,i){"use strict";e.exports=function(t){var e=null,i={};function n(){document.documentElement.removeEventListener("mouseleave",s),document.documentElement.removeEventListener("mouseenter",r),document.documentElement.removeEventListener("click",o),window.removeEventListener("touchstart",l),window.removeEventListener("touchend",c),t()}function o(){null!==e&&(window.clearTimeout(e),e=null)}function r(t){o()}function s(t){o(),t.clientY<=0&&t.clientX<.8*window.innerWidth&&(e=window.setTimeout(n,400))}function l(t){o(),i={timestamp:performance.now(),scrollY:window.scrollY,windowHeight:window.innerHeight}}function c(t){o(),window.innerHeight>i.windowHeight||window.scrollY+20>=i.scrollY||300<performance.now()-i.timestamp||-1<["A","INPUT","BUTTON"].indexOf(t.target.tagName)||(e=window.setTimeout(n,800))}window.addEventListener("touchstart",l),window.addEventListener("touchend",c),document.documentElement.addEventListener("mouseenter",r),document.documentElement.addEventListener("mouseleave",s),document.documentElement.addEventListener("click",o)}},{}],8:[function(t,e,i){"use strict";function d(t){return(d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}!function(){var s=t("boxzilla"),l=window.boxzilla_options;function c(t){if(!window.location.hash||0===window.location.hash.length)return!1;var e=window.location.hash.substring(1);return!!/^[a-zA-Z\-\_0-9]+$/.test(e)&&(e===t.element.id||!!t.element.querySelector("#"+e))}window.Boxzilla=s;var a=-1<document.body.className.indexOf("logged-in");a&&l.testMode&&console.log("Boxzilla: Test mode is enabled. Please disable test mode if you're done testing."),s.init(),window.addEventListener("load",function(){if(!l.inited){for(var t in l.boxes){var e=l.boxes[t];e.testMode=a&&l.testMode;var i=document.getElementById("boxzilla-box-"+e.id+"-content");if(i){e.content=i;var n=s.create(e.id,e);n.element.className=n.element.className+" boxzilla-"+e.post.slug,o=n.element,(r=e.css).background_color&&(o.style.background=r.background_color),r.color&&(o.style.color=r.color),r.border_color&&(o.style.borderColor=r.border_color),r.border_width&&(o.style.borderWidth=parseInt(r.border_width)+"px"),r.border_style&&(o.style.borderStyle=r.border_style),r.width&&(o.style.maxWidth=parseInt(r.width)+"px"),n.element.firstChild.firstChild.className+=" first-child",n.element.firstChild.lastChild.className+=" last-child",n.fits()&&c(n)&&n.show()}}var o,r;l.inited=!0,s.trigger("done"),function(){if("object"===d(window.mc4wp_forms_config)&&window.mc4wp_forms_config.submitted_form){var t="#"+window.mc4wp_forms_config.submitted_form.element_id,e=s.boxes;for(var i in e)if(e.hasOwnProperty(i)){var n=e[i];if(n.element.querySelector(t))return n.show()}}}()}})}()},{boxzilla:4}]},{},[8])}();
2
  //# sourceMappingURL=script.min.js.map
assets/js/script.min.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["script.js"],"names":["define","undefined","r","e","n","t","o","i","f","c","u","a","Error","code","p","exports","call","length","1","require","module","_typeof","obj","Symbol","iterator","constructor","prototype","Boxzilla","options","window","boxzilla_options","locationHashRefersBox","box","location","hash","elementId","substring","test","element","id","querySelector","isLoggedIn","document","body","className","indexOf","testMode","console","log","init","addEventListener","inited","key","boxes","boxOpts","boxContentElement","getElementById","content","create","post","slug","styles","css","background_color","style","background","color","border_color","borderColor","border_width","borderWidth","parseInt","border_style","borderStyle","width","maxWidth","firstChild","lastChild","fits","show","trigger","mc4wp_forms_config","submitted_form","selector","element_id","boxId","hasOwnProperty","maybeOpenMailChimpForWordPressBox","boxzilla","2","EventEmitter","proto","this","originalGlobalValue","indexOfListener","listeners","listener","alias","name","apply","arguments","getListeners","evt","response","events","_getEvents","RegExp","flattenListeners","flatListeners","push","getListenersAsObject","Array","addListener","listenerIsWrapped","once","on","addOnceListener","defineEvent","defineEvents","evts","removeListener","index","splice","off","addListeners","manipulateListeners","removeListeners","remove","value","single","multiple","removeEvent","type","_events","removeAllListeners","emitEvent","args","listenersMap","slice","_getOnceReturnValue","emit","setOnceReturnValue","_onceReturnValue","noConflict","amd","3","duration","property","animate","targetStyles","fn","last","Date","initialStyles","getComputedStyle","currentStyles","propSteps","parseFloat","to","current","tick","step","increment","newValue","timeSinceLastTick","done","suffix","requestAnimationFrame","setTimeout","toggle","animation","callbackFn","hiddenStyles","visibleStyles","nowVisible","display","offsetLeft","clone","cloneNode","cleanup","removeAttribute","setAttribute","getAttribute","properties","newObject","initObjectProperties","object","copyObjectProperties","isFinite","height","clientRect","getBoundingClientRect","overflowY","opacity","animated","4","defaults","rehide","cookie","icon","screenWidthCondition","position","closable","Animator","Box","config","obj1","obj2","obj3","attrname","merge","overlay","visible","dismissed","triggered","triggerHeight","calculateTriggerHeight","cookieSet","isCookieSet","contentElement","closeIcon","dom","dismiss","bind","target","tagName","setCookie","wrapper","createElement","appendChild","innerHTML","setCustomBoxStyling","origDisplay","maxHeight","windowHeight","innerHeight","boxHeight","clientHeight","newTopMargin","marginTop","classList","hide","html","method","triggerElement","top","documentElement","Math","max","scrollHeight","offsetHeight","condition","innerWidth","onResize","mayAutoShow","mayRehide","replace","hours","expiryDate","setHours","getHours","toUTCString","preventDefault","_Boxzilla","./animator.js","5","exitIntentDelayTimer","exitIntentTriggered","siteTimer","pageTimer","pageViews","Object","Timer","scrollElement","throttle","threshhold","scope","deferTimer","context","now","clearTimeout","onKeyUp","keyCode","checkPageViewsCriteria","isAnyBoxVisible","forEach","checkTimeCriteria","time","checkHeightCriteria","scrollY","pageYOffset","scrollTop","recalculateHeights","onOverlayClick","x","offsetX","y","offsetY","rect","left","right","bottom","triggerExitIntent","onMouseLeave","clientY","onMouseEnter","clearInterval","onElementClick","el","srcElement","parentElement","toLowerCase","timers","sessionTime","sessionStorage","getItem","start","setItem","stop","styleElement","head","setInterval","opts","minimumScreenWidth","get","./box.js","./styles.js","./timer.js","wolfy87-eventemitter","6","7","interval"],"mappings":"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","file":"script.min.js","sourcesContent":["(function () { var require = undefined; var module = undefined; var exports = undefined; var define = undefined; (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){\n\"use strict\";\n\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n(function () {\n 'use strict';\n\n var Boxzilla = require('boxzilla');\n\n var options = window.boxzilla_options; // expose Boxzilla object to window\n\n window.Boxzilla = Boxzilla; // helper function for setting CSS styles\n\n function css(element, styles) {\n if (styles.background_color) {\n element.style.background = styles.background_color;\n }\n\n if (styles.color) {\n element.style.color = styles.color;\n }\n\n if (styles.border_color) {\n element.style.borderColor = styles.border_color;\n }\n\n if (styles.border_width) {\n element.style.borderWidth = parseInt(styles.border_width) + \"px\";\n }\n\n if (styles.border_style) {\n element.style.borderStyle = styles.border_style;\n }\n\n if (styles.width) {\n element.style.maxWidth = parseInt(styles.width) + \"px\";\n }\n }\n\n function createBoxesFromConfig() {\n // failsafe against including script twice.\n if (options.inited) {\n return;\n } // create boxes from options\n\n\n for (var key in options.boxes) {\n // get opts\n var boxOpts = options.boxes[key];\n boxOpts.testMode = isLoggedIn && options.testMode; // find box content element, bail if not found\n\n var boxContentElement = document.getElementById('boxzilla-box-' + boxOpts.id + '-content');\n\n if (!boxContentElement) {\n continue;\n } // use element as content option\n\n\n boxOpts.content = boxContentElement; // create box\n\n var box = Boxzilla.create(boxOpts.id, boxOpts); // add box slug to box element as classname\n\n box.element.className = box.element.className + ' boxzilla-' + boxOpts.post.slug; // add custom css to box\n\n css(box.element, boxOpts.css);\n box.element.firstChild.firstChild.className += \" first-child\";\n box.element.firstChild.lastChild.className += \" last-child\"; // maybe show box right away\n\n if (box.fits() && locationHashRefersBox(box)) {\n box.show();\n }\n } // set flag to prevent initialising twice\n\n\n options.inited = true; // trigger \"done\" event.\n\n Boxzilla.trigger('done'); // maybe open box with MC4WP form in it\n\n maybeOpenMailChimpForWordPressBox();\n }\n\n function locationHashRefersBox(box) {\n if (!window.location.hash || 0 === window.location.hash.length) {\n return false;\n }\n\n var elementId = window.location.hash.substring(1); // only attempt on strings looking like an ID \n\n var regex = /^[a-zA-Z\\-\\_0-9]+$/;\n\n if (!regex.test(elementId)) {\n return false;\n }\n\n if (elementId === box.element.id) {\n return true;\n } else if (box.element.querySelector('#' + elementId)) {\n return true;\n }\n\n return false;\n }\n\n function maybeOpenMailChimpForWordPressBox() {\n if (_typeof(window.mc4wp_forms_config) !== \"object\" || !window.mc4wp_forms_config.submitted_form) {\n return;\n }\n\n var selector = '#' + window.mc4wp_forms_config.submitted_form.element_id;\n var boxes = Boxzilla.boxes;\n\n for (var boxId in boxes) {\n if (!boxes.hasOwnProperty(boxId)) {\n continue;\n }\n\n var box = boxes[boxId];\n\n if (box.element.querySelector(selector)) {\n box.show();\n return;\n }\n }\n } // print message when test mode is enabled\n\n\n var isLoggedIn = document.body.className.indexOf('logged-in') > -1;\n\n if (isLoggedIn && options.testMode) {\n console.log('Boxzilla: Test mode is enabled. Please disable test mode if you\\'re done testing.');\n } // init boxzilla\n\n\n Boxzilla.init(); // on window.load, create DOM elements for boxes\n\n window.addEventListener('load', createBoxesFromConfig);\n})();\n\n},{\"boxzilla\":5}],2:[function(require,module,exports){\n/*!\n * EventEmitter v4.2.11 - git.io/ee\n * Unlicense - http://unlicense.org/\n * Oliver Caldwell - http://oli.me.uk/\n * @preserve\n */\n\n;(function () {\n 'use strict';\n\n /**\n * Class for managing events.\n * Can be extended to provide event functionality in other classes.\n *\n * @class EventEmitter Manages event registering and emitting.\n */\n function EventEmitter() {}\n\n // Shortcuts to improve speed and size\n var proto = EventEmitter.prototype;\n var exports = this;\n var originalGlobalValue = exports.EventEmitter;\n\n /**\n * Finds the index of the listener for the event in its storage array.\n *\n * @param {Function[]} listeners Array of listeners to search through.\n * @param {Function} listener Method to look for.\n * @return {Number} Index of the specified listener, -1 if not found\n * @api private\n */\n function indexOfListener(listeners, listener) {\n var i = listeners.length;\n while (i--) {\n if (listeners[i].listener === listener) {\n return i;\n }\n }\n\n return -1;\n }\n\n /**\n * Alias a method while keeping the context correct, to allow for overwriting of target method.\n *\n * @param {String} name The name of the target method.\n * @return {Function} The aliased method\n * @api private\n */\n function alias(name) {\n return function aliasClosure() {\n return this[name].apply(this, arguments);\n };\n }\n\n /**\n * Returns the listener array for the specified event.\n * Will initialise the event object and listener arrays if required.\n * 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.\n * Each property in the object response is an array of listener functions.\n *\n * @param {String|RegExp} evt Name of the event to return the listeners from.\n * @return {Function[]|Object} All listener functions for the event.\n */\n proto.getListeners = function getListeners(evt) {\n var events = this._getEvents();\n var response;\n var key;\n\n // Return a concatenated array of all matching events if\n // the selector is a regular expression.\n if (evt instanceof RegExp) {\n response = {};\n for (key in events) {\n if (events.hasOwnProperty(key) && evt.test(key)) {\n response[key] = events[key];\n }\n }\n }\n else {\n response = events[evt] || (events[evt] = []);\n }\n\n return response;\n };\n\n /**\n * Takes a list of listener objects and flattens it into a list of listener functions.\n *\n * @param {Object[]} listeners Raw listener objects.\n * @return {Function[]} Just the listener functions.\n */\n proto.flattenListeners = function flattenListeners(listeners) {\n var flatListeners = [];\n var i;\n\n for (i = 0; i < listeners.length; i += 1) {\n flatListeners.push(listeners[i].listener);\n }\n\n return flatListeners;\n };\n\n /**\n * 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.\n *\n * @param {String|RegExp} evt Name of the event to return the listeners from.\n * @return {Object} All listener functions for an event in an object.\n */\n proto.getListenersAsObject = function getListenersAsObject(evt) {\n var listeners = this.getListeners(evt);\n var response;\n\n if (listeners instanceof Array) {\n response = {};\n response[evt] = listeners;\n }\n\n return response || listeners;\n };\n\n /**\n * Adds a listener function to the specified event.\n * The listener will not be added if it is a duplicate.\n * If the listener returns true then it will be removed after it is called.\n * If you pass a regular expression as the event name then the listener will be added to all events that match it.\n *\n * @param {String|RegExp} evt Name of the event to attach the listener to.\n * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.addListener = function addListener(evt, listener) {\n var listeners = this.getListenersAsObject(evt);\n var listenerIsWrapped = typeof listener === 'object';\n var key;\n\n for (key in listeners) {\n if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {\n listeners[key].push(listenerIsWrapped ? listener : {\n listener: listener,\n once: false\n });\n }\n }\n\n return this;\n };\n\n /**\n * Alias of addListener\n */\n proto.on = alias('addListener');\n\n /**\n * Semi-alias of addListener. It will add a listener that will be\n * automatically removed after its first execution.\n *\n * @param {String|RegExp} evt Name of the event to attach the listener to.\n * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.addOnceListener = function addOnceListener(evt, listener) {\n return this.addListener(evt, {\n listener: listener,\n once: true\n });\n };\n\n /**\n * Alias of addOnceListener.\n */\n proto.once = alias('addOnceListener');\n\n /**\n * 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.\n * You need to tell it what event names should be matched by a regex.\n *\n * @param {String} evt Name of the event to create.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.defineEvent = function defineEvent(evt) {\n this.getListeners(evt);\n return this;\n };\n\n /**\n * Uses defineEvent to define multiple events.\n *\n * @param {String[]} evts An array of event names to define.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.defineEvents = function defineEvents(evts) {\n for (var i = 0; i < evts.length; i += 1) {\n this.defineEvent(evts[i]);\n }\n return this;\n };\n\n /**\n * Removes a listener function from the specified event.\n * When passed a regular expression as the event name, it will remove the listener from all events that match it.\n *\n * @param {String|RegExp} evt Name of the event to remove the listener from.\n * @param {Function} listener Method to remove from the event.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.removeListener = function removeListener(evt, listener) {\n var listeners = this.getListenersAsObject(evt);\n var index;\n var key;\n\n for (key in listeners) {\n if (listeners.hasOwnProperty(key)) {\n index = indexOfListener(listeners[key], listener);\n\n if (index !== -1) {\n listeners[key].splice(index, 1);\n }\n }\n }\n\n return this;\n };\n\n /**\n * Alias of removeListener\n */\n proto.off = alias('removeListener');\n\n /**\n * Adds listeners in bulk using the manipulateListeners method.\n * 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.\n * You can also pass it a regular expression to add the array of listeners to all events that match it.\n * Yeah, this function does quite a bit. That's probably a bad thing.\n *\n * @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.\n * @param {Function[]} [listeners] An optional array of listener functions to add.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.addListeners = function addListeners(evt, listeners) {\n // Pass through to manipulateListeners\n return this.manipulateListeners(false, evt, listeners);\n };\n\n /**\n * Removes listeners in bulk using the manipulateListeners method.\n * 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.\n * You can also pass it an event name and an array of listeners to be removed.\n * You can also pass it a regular expression to remove the listeners from all events that match it.\n *\n * @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.\n * @param {Function[]} [listeners] An optional array of listener functions to remove.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.removeListeners = function removeListeners(evt, listeners) {\n // Pass through to manipulateListeners\n return this.manipulateListeners(true, evt, listeners);\n };\n\n /**\n * 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.\n * The first argument will determine if the listeners are removed (true) or added (false).\n * 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.\n * You can also pass it an event name and an array of listeners to be added/removed.\n * You can also pass it a regular expression to manipulate the listeners of all events that match it.\n *\n * @param {Boolean} remove True if you want to remove listeners, false if you want to add.\n * @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.\n * @param {Function[]} [listeners] An optional array of listener functions to add/remove.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {\n var i;\n var value;\n var single = remove ? this.removeListener : this.addListener;\n var multiple = remove ? this.removeListeners : this.addListeners;\n\n // If evt is an object then pass each of its properties to this method\n if (typeof evt === 'object' && !(evt instanceof RegExp)) {\n for (i in evt) {\n if (evt.hasOwnProperty(i) && (value = evt[i])) {\n // Pass the single listener straight through to the singular method\n if (typeof value === 'function') {\n single.call(this, i, value);\n }\n else {\n // Otherwise pass back to the multiple function\n multiple.call(this, i, value);\n }\n }\n }\n }\n else {\n // So evt must be a string\n // And listeners must be an array of listeners\n // Loop over it and pass each one to the multiple method\n i = listeners.length;\n while (i--) {\n single.call(this, evt, listeners[i]);\n }\n }\n\n return this;\n };\n\n /**\n * Removes all listeners from a specified event.\n * If you do not specify an event then all listeners will be removed.\n * That means every event will be emptied.\n * You can also pass a regex to remove all events that match it.\n *\n * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.removeEvent = function removeEvent(evt) {\n var type = typeof evt;\n var events = this._getEvents();\n var key;\n\n // Remove different things depending on the state of evt\n if (type === 'string') {\n // Remove all listeners for the specified event\n delete events[evt];\n }\n else if (evt instanceof RegExp) {\n // Remove all events matching the regex.\n for (key in events) {\n if (events.hasOwnProperty(key) && evt.test(key)) {\n delete events[key];\n }\n }\n }\n else {\n // Remove all listeners in all events\n delete this._events;\n }\n\n return this;\n };\n\n /**\n * Alias of removeEvent.\n *\n * Added to mirror the node API.\n */\n proto.removeAllListeners = alias('removeEvent');\n\n /**\n * Emits an event of your choice.\n * When emitted, every listener attached to that event will be executed.\n * If you pass the optional argument array then those arguments will be passed to every listener upon execution.\n * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.\n * So they will not arrive within the array on the other side, they will be separate.\n * You can also pass a regular expression to emit to all events that match it.\n *\n * @param {String|RegExp} evt Name of the event to emit and execute listeners for.\n * @param {Array} [args] Optional array of arguments to be passed to each listener.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.emitEvent = function emitEvent(evt, args) {\n var listenersMap = this.getListenersAsObject(evt);\n var listeners;\n var listener;\n var i;\n var key;\n var response;\n\n for (key in listenersMap) {\n if (listenersMap.hasOwnProperty(key)) {\n listeners = listenersMap[key].slice(0);\n i = listeners.length;\n\n while (i--) {\n // If the listener returns true then it shall be removed from the event\n // The function is executed either with a basic call or an apply if there is an args array\n listener = listeners[i];\n\n if (listener.once === true) {\n this.removeListener(evt, listener.listener);\n }\n\n response = listener.listener.apply(this, args || []);\n\n if (response === this._getOnceReturnValue()) {\n this.removeListener(evt, listener.listener);\n }\n }\n }\n }\n\n return this;\n };\n\n /**\n * Alias of emitEvent\n */\n proto.trigger = alias('emitEvent');\n\n /**\n * 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.\n * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.\n *\n * @param {String|RegExp} evt Name of the event to emit and execute listeners for.\n * @param {...*} Optional additional arguments to be passed to each listener.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.emit = function emit(evt) {\n var args = Array.prototype.slice.call(arguments, 1);\n return this.emitEvent(evt, args);\n };\n\n /**\n * Sets the current value to check against when executing listeners. If a\n * listeners return value matches the one set here then it will be removed\n * after execution. This value defaults to true.\n *\n * @param {*} value The new value to check for when executing listeners.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.setOnceReturnValue = function setOnceReturnValue(value) {\n this._onceReturnValue = value;\n return this;\n };\n\n /**\n * Fetches the current value to check against when executing listeners. If\n * the listeners return value matches this one then it should be removed\n * automatically. It will return true by default.\n *\n * @return {*|Boolean} The current value to check for or the default, true.\n * @api private\n */\n proto._getOnceReturnValue = function _getOnceReturnValue() {\n if (this.hasOwnProperty('_onceReturnValue')) {\n return this._onceReturnValue;\n }\n else {\n return true;\n }\n };\n\n /**\n * Fetches the events object and creates one if required.\n *\n * @return {Object} The events storage object.\n * @api private\n */\n proto._getEvents = function _getEvents() {\n return this._events || (this._events = {});\n };\n\n /**\n * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.\n *\n * @return {Function} Non conflicting EventEmitter class.\n */\n EventEmitter.noConflict = function noConflict() {\n exports.EventEmitter = originalGlobalValue;\n return EventEmitter;\n };\n\n // Expose the class either via AMD, CommonJS or the global object\n if (typeof define === 'function' && define.amd) {\n define(function () {\n return EventEmitter;\n });\n }\n else if (typeof module === 'object' && module.exports){\n module.exports = EventEmitter;\n }\n else {\n exports.EventEmitter = EventEmitter;\n }\n}.call(this));\n\n},{}],3:[function(require,module,exports){\n\"use strict\";\n\nvar duration = 320;\n\nfunction css(element, styles) {\n for (var property in styles) {\n element.style[property] = styles[property];\n }\n}\n\nfunction initObjectProperties(properties, value) {\n var newObject = {};\n\n for (var i = 0; i < properties.length; i++) {\n newObject[properties[i]] = value;\n }\n\n return newObject;\n}\n\nfunction copyObjectProperties(properties, object) {\n var newObject = {};\n\n for (var i = 0; i < properties.length; i++) {\n newObject[properties[i]] = object[properties[i]];\n }\n\n return newObject;\n}\n/**\n * Checks if the given element is currently being animated.\n *\n * @param element\n * @returns {boolean}\n */\n\n\nfunction animated(element) {\n return !!element.getAttribute('data-animated');\n}\n/**\n * Toggles the element using the given animation.\n *\n * @param element\n * @param animation Either \"fade\" or \"slide\"\n */\n\n\nfunction toggle(element, animation, callbackFn) {\n var nowVisible = element.style.display != 'none' || element.offsetLeft > 0; // create clone for reference\n\n var clone = element.cloneNode(true);\n\n var cleanup = function cleanup() {\n element.removeAttribute('data-animated');\n element.setAttribute('style', clone.getAttribute('style'));\n element.style.display = nowVisible ? 'none' : '';\n\n if (callbackFn) {\n callbackFn();\n }\n }; // store attribute so everyone knows we're animating this element\n\n\n element.setAttribute('data-animated', \"true\"); // toggle element visiblity right away if we're making something visible\n\n if (!nowVisible) {\n element.style.display = '';\n }\n\n var hiddenStyles, visibleStyles; // animate properties\n\n if (animation === 'slide') {\n hiddenStyles = initObjectProperties([\"height\", \"borderTopWidth\", \"borderBottomWidth\", \"paddingTop\", \"paddingBottom\"], 0);\n visibleStyles = {};\n\n if (!nowVisible) {\n var computedStyles = window.getComputedStyle(element);\n visibleStyles = copyObjectProperties([\"height\", \"borderTopWidth\", \"borderBottomWidth\", \"paddingTop\", \"paddingBottom\"], computedStyles); // in some browsers, getComputedStyle returns \"auto\" value. this falls back to getBoundingClientRect() in those browsers since we need an actual height.\n\n if (!isFinite(visibleStyles.height)) {\n var clientRect = element.getBoundingClientRect();\n visibleStyles.height = clientRect.height;\n }\n\n css(element, hiddenStyles);\n } // don't show a scrollbar during animation\n\n\n element.style.overflowY = 'hidden';\n animate(element, nowVisible ? hiddenStyles : visibleStyles, cleanup);\n } else {\n hiddenStyles = {\n opacity: 0\n };\n visibleStyles = {\n opacity: 1\n };\n\n if (!nowVisible) {\n css(element, hiddenStyles);\n }\n\n animate(element, nowVisible ? hiddenStyles : visibleStyles, cleanup);\n }\n}\n\nfunction animate(element, targetStyles, fn) {\n var last = +new Date();\n var initialStyles = window.getComputedStyle(element);\n var currentStyles = {};\n var propSteps = {};\n\n for (var property in targetStyles) {\n // make sure we have an object filled with floats\n targetStyles[property] = parseFloat(targetStyles[property]); // calculate step size & current value\n\n var to = targetStyles[property];\n var current = parseFloat(initialStyles[property]); // is there something to do?\n\n if (current == to) {\n delete targetStyles[property];\n continue;\n }\n\n propSteps[property] = (to - current) / duration; // points per second\n\n currentStyles[property] = current;\n }\n\n var tick = function tick() {\n var now = +new Date();\n var timeSinceLastTick = now - last;\n var done = true;\n var step, to, increment, newValue;\n\n for (var property in targetStyles) {\n step = propSteps[property];\n to = targetStyles[property];\n increment = step * timeSinceLastTick;\n newValue = currentStyles[property] + increment;\n\n if (step > 0 && newValue >= to || step < 0 && newValue <= to) {\n newValue = to;\n } else {\n done = false;\n } // store new value\n\n\n currentStyles[property] = newValue;\n var suffix = property !== \"opacity\" ? \"px\" : \"\";\n element.style[property] = newValue + suffix;\n }\n\n last = +new Date(); // keep going until we're done for all props\n\n if (!done) {\n window.requestAnimationFrame && requestAnimationFrame(tick) || setTimeout(tick, 32);\n } else {\n // call callback\n fn && fn();\n }\n };\n\n tick();\n}\n\nmodule.exports = {\n 'toggle': toggle,\n 'animate': animate,\n 'animated': animated\n};\n\n},{}],4:[function(require,module,exports){\n'use strict';\n\nvar defaults = {\n 'animation': 'fade',\n 'rehide': false,\n 'content': '',\n 'cookie': null,\n 'icon': '&times',\n 'screenWidthCondition': null,\n 'position': 'center',\n 'testMode': false,\n 'trigger': false,\n 'closable': true\n},\n Boxzilla,\n Animator = require('./animator.js');\n/**\n* Merge 2 objects, values of the latter overwriting the former.\n*\n* @param obj1\n* @param obj2\n* @returns {*}\n*/\n\n\nfunction merge(obj1, obj2) {\n var obj3 = {};\n\n for (var attrname in obj1) {\n obj3[attrname] = obj1[attrname];\n }\n\n for (var attrname in obj2) {\n obj3[attrname] = obj2[attrname];\n }\n\n return obj3;\n}\n/**\n* Get the real height of entire document.\n* @returns {number}\n*/\n\n\nfunction getDocumentHeight() {\n var body = document.body,\n html = document.documentElement;\n var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);\n return height;\n} // Box Object\n\n\nvar Box = function Box(id, config) {\n this.id = id; // store config values\n\n this.config = merge(defaults, config); // store ref to overlay\n\n this.overlay = document.getElementById('boxzilla-overlay'); // state\n\n this.visible = false;\n this.dismissed = false;\n this.triggered = false;\n this.triggerHeight = this.calculateTriggerHeight();\n this.cookieSet = this.isCookieSet();\n this.element = null;\n this.contentElement = null;\n this.closeIcon = null; // create dom elements for this box\n\n this.dom(); // further initialise the box\n\n this.events();\n}; // initialise the box\n\n\nBox.prototype.events = function () {\n var box = this; // attach event to \"close\" icon inside box\n\n if (this.closeIcon) {\n this.closeIcon.addEventListener('click', this.dismiss.bind(this));\n }\n\n this.element.addEventListener('click', function (e) {\n if (e.target.tagName === 'A') {\n Boxzilla.trigger('box.interactions.link', [box, e.target]);\n }\n }, false);\n this.element.addEventListener('submit', function (e) {\n box.setCookie();\n Boxzilla.trigger('box.interactions.form', [box, e.target]);\n }, false);\n}; // generate dom elements for this box\n\n\nBox.prototype.dom = function () {\n var wrapper = document.createElement('div');\n wrapper.className = 'boxzilla-container boxzilla-' + this.config.position + '-container';\n var box = document.createElement('div');\n box.setAttribute('id', 'boxzilla-' + this.id);\n box.className = 'boxzilla boxzilla-' + this.id + ' boxzilla-' + this.config.position;\n box.style.display = 'none';\n wrapper.appendChild(box);\n var content;\n\n if (typeof this.config.content === \"string\") {\n content = document.createElement('div');\n content.innerHTML = this.config.content;\n } else {\n content = this.config.content; // make sure element is visible\n\n content.style.display = '';\n }\n\n content.className = 'boxzilla-content';\n box.appendChild(content);\n\n if (this.config.closable && this.config.icon) {\n var closeIcon = document.createElement('span');\n closeIcon.className = \"boxzilla-close-icon\";\n closeIcon.innerHTML = this.config.icon;\n box.appendChild(closeIcon);\n this.closeIcon = closeIcon;\n }\n\n document.body.appendChild(wrapper);\n this.contentElement = content;\n this.element = box;\n}; // set (calculate) custom box styling depending on box options\n\n\nBox.prototype.setCustomBoxStyling = function () {\n // reset element to its initial state\n var origDisplay = this.element.style.display;\n this.element.style.display = '';\n this.element.style.overflowY = 'auto';\n this.element.style.maxHeight = 'none'; // get new dimensions\n\n var windowHeight = window.innerHeight;\n var boxHeight = this.element.clientHeight; // add scrollbar to box and limit height\n\n if (boxHeight > windowHeight) {\n this.element.style.maxHeight = windowHeight + \"px\";\n this.element.style.overflowY = 'scroll';\n } // set new top margin for boxes which are centered\n\n\n if (this.config.position === 'center') {\n var newTopMargin = (windowHeight - boxHeight) / 2;\n newTopMargin = newTopMargin >= 0 ? newTopMargin : 0;\n this.element.style.marginTop = newTopMargin + \"px\";\n }\n\n this.element.style.display = origDisplay;\n}; // toggle visibility of the box\n\n\nBox.prototype.toggle = function (show) {\n // revert visibility if no explicit argument is given\n if (typeof show === \"undefined\") {\n show = !this.visible;\n } // is box already at desired visibility?\n\n\n if (show === this.visible) {\n return false;\n } // is box being animated?\n\n\n if (Animator.animated(this.element)) {\n return false;\n } // if box should be hidden but is not closable, bail.\n\n\n if (!show && !this.config.closable) {\n return false;\n } // set new visibility status\n\n\n this.visible = show; // calculate new styling rules\n\n this.setCustomBoxStyling(); // trigger event\n\n Boxzilla.trigger('box.' + (show ? 'show' : 'hide'), [this]); // show or hide box using selected animation\n\n if (this.config.position === 'center') {\n this.overlay.classList.toggle('boxzilla-' + this.id + '-overlay');\n Animator.toggle(this.overlay, \"fade\");\n }\n\n Animator.toggle(this.element, this.config.animation, function () {\n if (this.visible) {\n return;\n }\n\n this.contentElement.innerHTML = this.contentElement.innerHTML;\n }.bind(this));\n return true;\n}; // show the box\n\n\nBox.prototype.show = function () {\n return this.toggle(true);\n}; // hide the box\n\n\nBox.prototype.hide = function () {\n return this.toggle(false);\n}; // calculate trigger height\n\n\nBox.prototype.calculateTriggerHeight = function () {\n var triggerHeight = 0;\n\n if (this.config.trigger) {\n if (this.config.trigger.method === 'element') {\n var triggerElement = document.body.querySelector(this.config.trigger.value);\n\n if (triggerElement) {\n var offset = triggerElement.getBoundingClientRect();\n triggerHeight = offset.top;\n }\n } else if (this.config.trigger.method === 'percentage') {\n triggerHeight = this.config.trigger.value / 100 * getDocumentHeight();\n }\n }\n\n return triggerHeight;\n};\n\nBox.prototype.fits = function () {\n if (!this.config.screenWidthCondition || !this.config.screenWidthCondition.value) {\n return true;\n }\n\n switch (this.config.screenWidthCondition.condition) {\n case \"larger\":\n return window.innerWidth > this.config.screenWidthCondition.value;\n\n case \"smaller\":\n return window.innerWidth < this.config.screenWidthCondition.value;\n } // meh.. condition should be \"smaller\" or \"larger\", just return true.\n\n\n return true;\n};\n\nBox.prototype.onResize = function () {\n this.triggerHeight = this.calculateTriggerHeight();\n this.setCustomBoxStyling();\n}; // is this box enabled?\n\n\nBox.prototype.mayAutoShow = function () {\n if (this.dismissed) {\n return false;\n } // check if box fits on given minimum screen width\n\n\n if (!this.fits()) {\n return false;\n } // if trigger empty or error in calculating triggerHeight, return false\n\n\n if (!this.config.trigger) {\n return false;\n } // rely on cookie value (show if not set, don't show if set)\n\n\n return !this.cookieSet;\n};\n\nBox.prototype.mayRehide = function () {\n return this.config.rehide && this.triggered;\n};\n\nBox.prototype.isCookieSet = function () {\n // always show on test mode or when no auto-trigger is configured\n if (this.config.testMode || !this.config.trigger) {\n return false;\n } // if either cookie is null or trigger & dismiss are both falsey, don't bother checking.\n\n\n if (!this.config.cookie || !this.config.cookie.triggered && !this.config.cookie.dismissed) {\n return false;\n }\n\n var cookieSet = document.cookie.replace(new RegExp(\"(?:(?:^|.*;)\\\\s*\" + 'boxzilla_box_' + this.id + \"\\\\s*\\\\=\\\\s*([^;]*).*$)|^.*$\"), \"$1\") === \"true\";\n return cookieSet;\n}; // set cookie that disables automatically showing the box\n\n\nBox.prototype.setCookie = function (hours) {\n var expiryDate = new Date();\n expiryDate.setHours(expiryDate.getHours() + hours);\n document.cookie = 'boxzilla_box_' + this.id + '=true; expires=' + expiryDate.toUTCString() + '; path=/';\n};\n\nBox.prototype.trigger = function () {\n var shown = this.show();\n\n if (!shown) {\n return;\n }\n\n this.triggered = true;\n\n if (this.config.cookie && this.config.cookie.triggered) {\n this.setCookie(this.config.cookie.triggered);\n }\n};\n/**\n* Dismisses the box and optionally sets a cookie.\n*\n* @param e The event that triggered this dismissal.\n* @returns {boolean}\n*/\n\n\nBox.prototype.dismiss = function (e) {\n // prevent default action\n e && e.preventDefault(); // only dismiss box if it's currently open.\n\n if (!this.visible) {\n return false;\n } // hide box element\n\n\n this.hide(); // set cookie\n\n if (this.config.cookie && this.config.cookie.dismissed) {\n this.setCookie(this.config.cookie.dismissed);\n }\n\n this.dismissed = true;\n Boxzilla.trigger('box.dismiss', [this]);\n return true;\n};\n\nmodule.exports = function (_Boxzilla) {\n Boxzilla = _Boxzilla;\n return Box;\n};\n\n},{\"./animator.js\":3}],5:[function(require,module,exports){\n'use strict';\n\nvar EventEmitter = require('wolfy87-eventemitter'),\n Boxzilla = Object.create(EventEmitter.prototype),\n Box = require('./box.js')(Boxzilla),\n Timer = require('./timer.js'),\n boxes = [],\n overlay,\n scrollElement = window,\n exitIntentDelayTimer,\n exitIntentTriggered,\n siteTimer,\n pageTimer,\n pageViews;\n\nfunction throttle(fn, threshhold, scope) {\n threshhold || (threshhold = 250);\n var last, deferTimer;\n return function () {\n var context = scope || this;\n var now = +new Date(),\n args = arguments;\n\n if (last && now < last + threshhold) {\n // hold on to it\n clearTimeout(deferTimer);\n deferTimer = setTimeout(function () {\n last = now;\n fn.apply(context, args);\n }, threshhold);\n } else {\n last = now;\n fn.apply(context, args);\n }\n };\n} // \"keyup\" listener\n\n\nfunction onKeyUp(e) {\n if (e.keyCode == 27) {\n Boxzilla.dismiss();\n }\n} // check \"pageviews\" criteria for each box\n\n\nfunction checkPageViewsCriteria() {\n // don't bother if another box is currently open\n if (isAnyBoxVisible()) {\n return;\n }\n\n boxes.forEach(function (box) {\n if (!box.mayAutoShow()) {\n return;\n }\n\n if (box.config.trigger.method === 'pageviews' && pageViews >= box.config.trigger.value) {\n box.trigger();\n }\n });\n} // check time trigger criteria for each box\n\n\nfunction checkTimeCriteria() {\n // don't bother if another box is currently open\n if (isAnyBoxVisible()) {\n return;\n }\n\n boxes.forEach(function (box) {\n if (!box.mayAutoShow()) {\n return;\n } // check \"time on site\" trigger\n\n\n if (box.config.trigger.method === 'time_on_site' && siteTimer.time >= box.config.trigger.value) {\n box.trigger();\n } // check \"time on page\" trigger\n\n\n if (box.config.trigger.method === 'time_on_page' && pageTimer.time >= box.config.trigger.value) {\n box.trigger();\n }\n });\n} // check triggerHeight criteria for all boxes\n\n\nfunction checkHeightCriteria() {\n var scrollY = scrollElement.hasOwnProperty('pageYOffset') ? scrollElement.pageYOffset : scrollElement.scrollTop;\n scrollY = scrollY + window.innerHeight * 0.9;\n boxes.forEach(function (box) {\n if (!box.mayAutoShow() || box.triggerHeight <= 0) {\n return;\n }\n\n if (scrollY > box.triggerHeight) {\n // don't bother if another box is currently open\n if (isAnyBoxVisible()) {\n return;\n } // trigger box\n\n\n box.trigger();\n } // if box may auto-hide and scrollY is less than triggerHeight (with small margin of error), hide box\n\n\n if (box.mayRehide() && scrollY < box.triggerHeight - 5) {\n box.hide();\n }\n });\n} // recalculate heights and variables based on height\n\n\nfunction recalculateHeights() {\n boxes.forEach(function (box) {\n box.onResize();\n });\n}\n\nfunction onOverlayClick(e) {\n var x = e.offsetX;\n var y = e.offsetY; // calculate if click was less than 40px outside box to avoid closing it by accident\n\n boxes.forEach(function (box) {\n var rect = box.element.getBoundingClientRect();\n var margin = 40; // if click was not anywhere near box, dismiss it.\n\n if (x < rect.left - margin || x > rect.right + margin || y < rect.top - margin || y > rect.bottom + margin) {\n box.dismiss();\n }\n });\n}\n\nfunction triggerExitIntent() {\n // do nothing if already triggered OR another box is visible.\n if (exitIntentTriggered || isAnyBoxVisible()) {\n return;\n }\n\n boxes.forEach(function (box) {\n if (box.mayAutoShow() && box.config.trigger.method === 'exit_intent') {\n box.trigger();\n }\n });\n exitIntentTriggered = true;\n}\n\nfunction onMouseLeave(e) {\n var delay = 400; // did mouse leave at top of window?\n\n if (e.clientY <= 0) {\n exitIntentDelayTimer = window.setTimeout(triggerExitIntent, delay);\n }\n}\n\nfunction isAnyBoxVisible() {\n for (var i = 0; i < boxes.length; i++) {\n var box = boxes[i];\n\n if (box.visible) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction onMouseEnter() {\n if (exitIntentDelayTimer) {\n window.clearInterval(exitIntentDelayTimer);\n exitIntentDelayTimer = null;\n }\n}\n\nfunction onElementClick(e) {\n // find <a> element in up to 3 parent elements\n var el = e.target || e.srcElement;\n var depth = 3;\n\n for (var i = 0; i <= depth; i++) {\n if (!el || el.tagName === 'A') {\n break;\n }\n\n el = el.parentElement;\n }\n\n if (!el || el.tagName !== 'A' || !el.getAttribute('href')) {\n return;\n }\n\n if (el.getAttribute('href').toLowerCase().indexOf('#boxzilla-') === 0) {\n var boxId = el.getAttribute('href').toLowerCase().substring(\"#boxzilla-\".length);\n Boxzilla.toggle(boxId);\n }\n}\n\nvar timers = {\n start: function start() {\n try {\n var sessionTime = sessionStorage.getItem('boxzilla_timer');\n if (sessionTime) siteTimer.time = sessionTime;\n } catch (e) {}\n\n siteTimer.start();\n pageTimer.start();\n },\n stop: function stop() {\n sessionStorage.setItem('boxzilla_timer', siteTimer.time);\n siteTimer.stop();\n pageTimer.stop();\n }\n}; // initialise & add event listeners\n\nBoxzilla.init = function () {\n document.body.addEventListener('click', onElementClick, true);\n\n try {\n pageViews = sessionStorage.getItem('boxzilla_pageviews') || 0;\n } catch (e) {\n pageViews = 0;\n }\n\n siteTimer = new Timer(0);\n pageTimer = new Timer(0); // insert styles into DOM\n\n var styles = require('./styles.js');\n\n var styleElement = document.createElement('style');\n styleElement.setAttribute(\"type\", \"text/css\");\n styleElement.innerHTML = styles;\n document.head.appendChild(styleElement); // add overlay element to dom\n\n overlay = document.createElement('div');\n overlay.style.display = 'none';\n overlay.id = 'boxzilla-overlay';\n document.body.appendChild(overlay); // event binds\n\n scrollElement.addEventListener('touchstart', throttle(checkHeightCriteria), true);\n scrollElement.addEventListener('scroll', throttle(checkHeightCriteria), true);\n window.addEventListener('resize', throttle(recalculateHeights));\n window.addEventListener('load', recalculateHeights);\n overlay.addEventListener('click', onOverlayClick);\n window.setInterval(checkTimeCriteria, 1000);\n window.setTimeout(checkPageViewsCriteria, 1000);\n document.documentElement.addEventListener('mouseleave', onMouseLeave);\n document.documentElement.addEventListener('mouseenter', onMouseEnter);\n document.addEventListener('keyup', onKeyUp);\n timers.start();\n window.addEventListener('focus', timers.start);\n window.addEventListener('beforeunload', function () {\n timers.stop();\n sessionStorage.setItem('boxzilla_pageviews', ++pageViews);\n });\n window.addEventListener('blur', timers.stop);\n Boxzilla.trigger('ready');\n};\n/**\n * Create a new Box\n *\n * @param string id\n * @param object opts\n *\n * @returns Box\n */\n\n\nBoxzilla.create = function (id, opts) {\n // preserve backwards compat for minimumScreenWidth option\n if (typeof opts.minimumScreenWidth !== \"undefined\") {\n opts.screenWidthCondition = {\n condition: \"larger\",\n value: opts.minimumScreenWidth\n };\n }\n\n var box = new Box(id, opts);\n boxes.push(box);\n return box;\n};\n\nBoxzilla.get = function (id) {\n for (var i = 0; i < boxes.length; i++) {\n var box = boxes[i];\n\n if (box.id == id) {\n return box;\n }\n }\n\n throw new Error(\"No box exists with ID \" + id);\n}; // dismiss a single box (or all by omitting id param)\n\n\nBoxzilla.dismiss = function (id) {\n // if no id given, dismiss all current open boxes\n if (typeof id === \"undefined\") {\n boxes.forEach(function (box) {\n box.dismiss();\n });\n } else {\n Boxzilla.get(id).dismiss();\n }\n};\n\nBoxzilla.hide = function (id) {\n if (typeof id === \"undefined\") {\n boxes.forEach(function (box) {\n box.hide();\n });\n } else {\n Boxzilla.get(id).hide();\n }\n};\n\nBoxzilla.show = function (id) {\n if (typeof id === \"undefined\") {\n boxes.forEach(function (box) {\n box.show();\n });\n } else {\n Boxzilla.get(id).show();\n }\n};\n\nBoxzilla.toggle = function (id) {\n if (typeof id === \"undefined\") {\n boxes.forEach(function (box) {\n box.toggle();\n });\n } else {\n Boxzilla.get(id).toggle();\n }\n}; // expose each individual box.\n\n\nBoxzilla.boxes = boxes; // expose boxzilla object\n\nwindow.Boxzilla = Boxzilla;\n\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = Boxzilla;\n}\n\n},{\"./box.js\":4,\"./styles.js\":6,\"./timer.js\":7,\"wolfy87-eventemitter\":2}],6:[function(require,module,exports){\n\"use strict\";\n\nvar 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}\";\nmodule.exports = styles;\n\n},{}],7:[function(require,module,exports){\n'use strict';\n\nvar Timer = function Timer(start) {\n this.time = start;\n this.interval = 0;\n};\n\nTimer.prototype.tick = function () {\n this.time++;\n};\n\nTimer.prototype.start = function () {\n if (!this.interval) {\n this.interval = window.setInterval(this.tick.bind(this), 1000);\n }\n};\n\nTimer.prototype.stop = function () {\n if (this.interval) {\n window.clearInterval(this.interval);\n this.interval = 0;\n }\n};\n\nmodule.exports = Timer;\n\n},{}]},{},[1]);\n; })();"]}
1
+ {"version":3,"sources":["script.js"],"names":["define","undefined","r","e","n","t","o","i","f","c","u","a","Error","code","p","exports","call","length","1","require","module","EventEmitter","proto","prototype","originalGlobalValue","indexOfListener","listeners","listener","alias","name","this","apply","arguments","getListeners","evt","response","key","events","_getEvents","RegExp","hasOwnProperty","test","flattenListeners","flatListeners","push","getListenersAsObject","Array","addListener","isValidListener","TypeError","listenerIsWrapped","once","on","addOnceListener","defineEvent","defineEvents","evts","removeListener","index","splice","off","addListeners","manipulateListeners","removeListeners","remove","value","single","multiple","removeEvent","type","_events","removeAllListeners","emitEvent","args","listenersMap","slice","_getOnceReturnValue","trigger","emit","setOnceReturnValue","_onceReturnValue","noConflict","amd","window","2","duration","css","element","styles","property","style","animate","targetStyles","fn","last","Date","initialStyles","getComputedStyle","currentStyles","propSteps","parseFloat","to","current","tick","step","increment","newValue","timeSinceLastTick","done","suffix","requestAnimationFrame","setTimeout","toggle","animation","callbackFn","hiddenStyles","visibleStyles","nowVisible","display","offsetLeft","clone","cloneNode","cleanup","removeAttribute","setAttribute","getAttribute","properties","newObject","initObjectProperties","object","copyObjectProperties","isFinite","height","clientRect","getBoundingClientRect","overflowY","opacity","animated","3","Boxzilla","defaults","rehide","content","cookie","icon","screenWidthCondition","position","testMode","closable","Animator","Box","id","config","obj1","obj2","obj3","attrname","merge","overlay","document","getElementById","visible","dismissed","triggered","triggerHeight","calculateTriggerHeight","cookieSet","isCookieSet","contentElement","closeIcon","dom","box","addEventListener","dismiss","bind","target","tagName","setCookie","wrapper","createElement","className","appendChild","innerHTML","body","setCustomBoxStyling","origDisplay","maxHeight","windowHeight","innerHeight","boxHeight","clientHeight","newTopMargin","marginTop","show","classList","hide","html","method","triggerElement","querySelector","top","documentElement","Math","max","scrollHeight","offsetHeight","fits","condition","innerWidth","onResize","mayAutoShow","mayRehide","replace","hours","expiryDate","setHours","getHours","toUTCString","preventDefault","_Boxzilla","./animator.js","4","siteTimer","pageTimer","pageViews","Timer","Object","create","boxes","scrollElement","initialised","ExitIntent","throttle","threshhold","scope","deferTimer","context","now","clearTimeout","onKeyUp","keyCode","checkPageViewsCriteria","isAnyBoxVisible","forEach","checkTimeCriteria","time","checkHeightCriteria","scrollY","pageYOffset","scrollTop","recalculateHeights","onOverlayClick","x","offsetX","y","offsetY","rect","left","right","bottom","showBoxesWithExitIntentTrigger","filter","b","onElementClick","el","srcElement","parentElement","toLowerCase","indexOf","boxId","substring","timers","sessionTime","sessionStorage","getItem","start","setItem","stop","init","styleElement","head","setInterval","opts","minimumScreenWidth","get","./box.js","./styles.js","./timer.js","./triggers/exit-intent.js","wolfy87-eventemitter","5","6","interval","clearInterval","7","callback","timeout","touchStart","triggerCallback","removeEventListener","onMouseLeave","onMouseEnter","onTouchStart","onTouchEnd","clientY","clientX","timestamp","performance","8","_typeof","obj","Symbol","iterator","constructor","options","boxzilla_options","locationHashRefersBox","location","hash","elementId","isLoggedIn","console","log","inited","boxOpts","boxContentElement","post","slug","background_color","background","color","border_color","borderColor","border_width","borderWidth","parseInt","border_style","borderStyle","width","maxWidth","firstChild","lastChild","mc4wp_forms_config","submitted_form","selector","element_id","maybeOpenMailChimpForWordPressBox","boxzilla"],"mappings":"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","file":"script.min.js","sourcesContent":["(function () { var require = undefined; var module = undefined; var exports = undefined; var define = undefined; (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){\n/*!\n * EventEmitter v5.2.5 - git.io/ee\n * Unlicense - http://unlicense.org/\n * Oliver Caldwell - http://oli.me.uk/\n * @preserve\n */\n\n;(function (exports) {\n 'use strict';\n\n /**\n * Class for managing events.\n * Can be extended to provide event functionality in other classes.\n *\n * @class EventEmitter Manages event registering and emitting.\n */\n function EventEmitter() {}\n\n // Shortcuts to improve speed and size\n var proto = EventEmitter.prototype;\n var originalGlobalValue = exports.EventEmitter;\n\n /**\n * Finds the index of the listener for the event in its storage array.\n *\n * @param {Function[]} listeners Array of listeners to search through.\n * @param {Function} listener Method to look for.\n * @return {Number} Index of the specified listener, -1 if not found\n * @api private\n */\n function indexOfListener(listeners, listener) {\n var i = listeners.length;\n while (i--) {\n if (listeners[i].listener === listener) {\n return i;\n }\n }\n\n return -1;\n }\n\n /**\n * Alias a method while keeping the context correct, to allow for overwriting of target method.\n *\n * @param {String} name The name of the target method.\n * @return {Function} The aliased method\n * @api private\n */\n function alias(name) {\n return function aliasClosure() {\n return this[name].apply(this, arguments);\n };\n }\n\n /**\n * Returns the listener array for the specified event.\n * Will initialise the event object and listener arrays if required.\n * 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.\n * Each property in the object response is an array of listener functions.\n *\n * @param {String|RegExp} evt Name of the event to return the listeners from.\n * @return {Function[]|Object} All listener functions for the event.\n */\n proto.getListeners = function getListeners(evt) {\n var events = this._getEvents();\n var response;\n var key;\n\n // Return a concatenated array of all matching events if\n // the selector is a regular expression.\n if (evt instanceof RegExp) {\n response = {};\n for (key in events) {\n if (events.hasOwnProperty(key) && evt.test(key)) {\n response[key] = events[key];\n }\n }\n }\n else {\n response = events[evt] || (events[evt] = []);\n }\n\n return response;\n };\n\n /**\n * Takes a list of listener objects and flattens it into a list of listener functions.\n *\n * @param {Object[]} listeners Raw listener objects.\n * @return {Function[]} Just the listener functions.\n */\n proto.flattenListeners = function flattenListeners(listeners) {\n var flatListeners = [];\n var i;\n\n for (i = 0; i < listeners.length; i += 1) {\n flatListeners.push(listeners[i].listener);\n }\n\n return flatListeners;\n };\n\n /**\n * 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.\n *\n * @param {String|RegExp} evt Name of the event to return the listeners from.\n * @return {Object} All listener functions for an event in an object.\n */\n proto.getListenersAsObject = function getListenersAsObject(evt) {\n var listeners = this.getListeners(evt);\n var response;\n\n if (listeners instanceof Array) {\n response = {};\n response[evt] = listeners;\n }\n\n return response || listeners;\n };\n\n function isValidListener (listener) {\n if (typeof listener === 'function' || listener instanceof RegExp) {\n return true\n } else if (listener && typeof listener === 'object') {\n return isValidListener(listener.listener)\n } else {\n return false\n }\n }\n\n /**\n * Adds a listener function to the specified event.\n * The listener will not be added if it is a duplicate.\n * If the listener returns true then it will be removed after it is called.\n * If you pass a regular expression as the event name then the listener will be added to all events that match it.\n *\n * @param {String|RegExp} evt Name of the event to attach the listener to.\n * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.addListener = function addListener(evt, listener) {\n if (!isValidListener(listener)) {\n throw new TypeError('listener must be a function');\n }\n\n var listeners = this.getListenersAsObject(evt);\n var listenerIsWrapped = typeof listener === 'object';\n var key;\n\n for (key in listeners) {\n if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {\n listeners[key].push(listenerIsWrapped ? listener : {\n listener: listener,\n once: false\n });\n }\n }\n\n return this;\n };\n\n /**\n * Alias of addListener\n */\n proto.on = alias('addListener');\n\n /**\n * Semi-alias of addListener. It will add a listener that will be\n * automatically removed after its first execution.\n *\n * @param {String|RegExp} evt Name of the event to attach the listener to.\n * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.addOnceListener = function addOnceListener(evt, listener) {\n return this.addListener(evt, {\n listener: listener,\n once: true\n });\n };\n\n /**\n * Alias of addOnceListener.\n */\n proto.once = alias('addOnceListener');\n\n /**\n * 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.\n * You need to tell it what event names should be matched by a regex.\n *\n * @param {String} evt Name of the event to create.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.defineEvent = function defineEvent(evt) {\n this.getListeners(evt);\n return this;\n };\n\n /**\n * Uses defineEvent to define multiple events.\n *\n * @param {String[]} evts An array of event names to define.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.defineEvents = function defineEvents(evts) {\n for (var i = 0; i < evts.length; i += 1) {\n this.defineEvent(evts[i]);\n }\n return this;\n };\n\n /**\n * Removes a listener function from the specified event.\n * When passed a regular expression as the event name, it will remove the listener from all events that match it.\n *\n * @param {String|RegExp} evt Name of the event to remove the listener from.\n * @param {Function} listener Method to remove from the event.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.removeListener = function removeListener(evt, listener) {\n var listeners = this.getListenersAsObject(evt);\n var index;\n var key;\n\n for (key in listeners) {\n if (listeners.hasOwnProperty(key)) {\n index = indexOfListener(listeners[key], listener);\n\n if (index !== -1) {\n listeners[key].splice(index, 1);\n }\n }\n }\n\n return this;\n };\n\n /**\n * Alias of removeListener\n */\n proto.off = alias('removeListener');\n\n /**\n * Adds listeners in bulk using the manipulateListeners method.\n * If you pass an object as the first 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.\n * You can also pass it a regular expression to add the array of listeners to all events that match it.\n * Yeah, this function does quite a bit. That's probably a bad thing.\n *\n * @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.\n * @param {Function[]} [listeners] An optional array of listener functions to add.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.addListeners = function addListeners(evt, listeners) {\n // Pass through to manipulateListeners\n return this.manipulateListeners(false, evt, listeners);\n };\n\n /**\n * Removes listeners in bulk using the manipulateListeners method.\n * If you pass an object as the first argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.\n * You can also pass it an event name and an array of listeners to be removed.\n * You can also pass it a regular expression to remove the listeners from all events that match it.\n *\n * @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.\n * @param {Function[]} [listeners] An optional array of listener functions to remove.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.removeListeners = function removeListeners(evt, listeners) {\n // Pass through to manipulateListeners\n return this.manipulateListeners(true, evt, listeners);\n };\n\n /**\n * 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.\n * The first argument will determine if the listeners are removed (true) or added (false).\n * 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.\n * You can also pass it an event name and an array of listeners to be added/removed.\n * You can also pass it a regular expression to manipulate the listeners of all events that match it.\n *\n * @param {Boolean} remove True if you want to remove listeners, false if you want to add.\n * @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.\n * @param {Function[]} [listeners] An optional array of listener functions to add/remove.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {\n var i;\n var value;\n var single = remove ? this.removeListener : this.addListener;\n var multiple = remove ? this.removeListeners : this.addListeners;\n\n // If evt is an object then pass each of its properties to this method\n if (typeof evt === 'object' && !(evt instanceof RegExp)) {\n for (i in evt) {\n if (evt.hasOwnProperty(i) && (value = evt[i])) {\n // Pass the single listener straight through to the singular method\n if (typeof value === 'function') {\n single.call(this, i, value);\n }\n else {\n // Otherwise pass back to the multiple function\n multiple.call(this, i, value);\n }\n }\n }\n }\n else {\n // So evt must be a string\n // And listeners must be an array of listeners\n // Loop over it and pass each one to the multiple method\n i = listeners.length;\n while (i--) {\n single.call(this, evt, listeners[i]);\n }\n }\n\n return this;\n };\n\n /**\n * Removes all listeners from a specified event.\n * If you do not specify an event then all listeners will be removed.\n * That means every event will be emptied.\n * You can also pass a regex to remove all events that match it.\n *\n * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.removeEvent = function removeEvent(evt) {\n var type = typeof evt;\n var events = this._getEvents();\n var key;\n\n // Remove different things depending on the state of evt\n if (type === 'string') {\n // Remove all listeners for the specified event\n delete events[evt];\n }\n else if (evt instanceof RegExp) {\n // Remove all events matching the regex.\n for (key in events) {\n if (events.hasOwnProperty(key) && evt.test(key)) {\n delete events[key];\n }\n }\n }\n else {\n // Remove all listeners in all events\n delete this._events;\n }\n\n return this;\n };\n\n /**\n * Alias of removeEvent.\n *\n * Added to mirror the node API.\n */\n proto.removeAllListeners = alias('removeEvent');\n\n /**\n * Emits an event of your choice.\n * When emitted, every listener attached to that event will be executed.\n * If you pass the optional argument array then those arguments will be passed to every listener upon execution.\n * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.\n * So they will not arrive within the array on the other side, they will be separate.\n * You can also pass a regular expression to emit to all events that match it.\n *\n * @param {String|RegExp} evt Name of the event to emit and execute listeners for.\n * @param {Array} [args] Optional array of arguments to be passed to each listener.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.emitEvent = function emitEvent(evt, args) {\n var listenersMap = this.getListenersAsObject(evt);\n var listeners;\n var listener;\n var i;\n var key;\n var response;\n\n for (key in listenersMap) {\n if (listenersMap.hasOwnProperty(key)) {\n listeners = listenersMap[key].slice(0);\n\n for (i = 0; i < listeners.length; i++) {\n // If the listener returns true then it shall be removed from the event\n // The function is executed either with a basic call or an apply if there is an args array\n listener = listeners[i];\n\n if (listener.once === true) {\n this.removeListener(evt, listener.listener);\n }\n\n response = listener.listener.apply(this, args || []);\n\n if (response === this._getOnceReturnValue()) {\n this.removeListener(evt, listener.listener);\n }\n }\n }\n }\n\n return this;\n };\n\n /**\n * Alias of emitEvent\n */\n proto.trigger = alias('emitEvent');\n\n /**\n * 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.\n * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.\n *\n * @param {String|RegExp} evt Name of the event to emit and execute listeners for.\n * @param {...*} Optional additional arguments to be passed to each listener.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.emit = function emit(evt) {\n var args = Array.prototype.slice.call(arguments, 1);\n return this.emitEvent(evt, args);\n };\n\n /**\n * Sets the current value to check against when executing listeners. If a\n * listeners return value matches the one set here then it will be removed\n * after execution. This value defaults to true.\n *\n * @param {*} value The new value to check for when executing listeners.\n * @return {Object} Current instance of EventEmitter for chaining.\n */\n proto.setOnceReturnValue = function setOnceReturnValue(value) {\n this._onceReturnValue = value;\n return this;\n };\n\n /**\n * Fetches the current value to check against when executing listeners. If\n * the listeners return value matches this one then it should be removed\n * automatically. It will return true by default.\n *\n * @return {*|Boolean} The current value to check for or the default, true.\n * @api private\n */\n proto._getOnceReturnValue = function _getOnceReturnValue() {\n if (this.hasOwnProperty('_onceReturnValue')) {\n return this._onceReturnValue;\n }\n else {\n return true;\n }\n };\n\n /**\n * Fetches the events object and creates one if required.\n *\n * @return {Object} The events storage object.\n * @api private\n */\n proto._getEvents = function _getEvents() {\n return this._events || (this._events = {});\n };\n\n /**\n * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.\n *\n * @return {Function} Non conflicting EventEmitter class.\n */\n EventEmitter.noConflict = function noConflict() {\n exports.EventEmitter = originalGlobalValue;\n return EventEmitter;\n };\n\n // Expose the class either via AMD, CommonJS or the global object\n if (typeof define === 'function' && define.amd) {\n define(function () {\n return EventEmitter;\n });\n }\n else if (typeof module === 'object' && module.exports){\n module.exports = EventEmitter;\n }\n else {\n exports.EventEmitter = EventEmitter;\n }\n}(typeof window !== 'undefined' ? window : this || {}));\n\n},{}],2:[function(require,module,exports){\n\"use strict\";\n\nvar duration = 320;\n\nfunction css(element, styles) {\n for (var property in styles) {\n element.style[property] = styles[property];\n }\n}\n\nfunction initObjectProperties(properties, value) {\n var newObject = {};\n\n for (var i = 0; i < properties.length; i++) {\n newObject[properties[i]] = value;\n }\n\n return newObject;\n}\n\nfunction copyObjectProperties(properties, object) {\n var newObject = {};\n\n for (var i = 0; i < properties.length; i++) {\n newObject[properties[i]] = object[properties[i]];\n }\n\n return newObject;\n}\n/**\n * Checks if the given element is currently being animated.\n *\n * @param element\n * @returns {boolean}\n */\n\n\nfunction animated(element) {\n return !!element.getAttribute('data-animated');\n}\n/**\n * Toggles the element using the given animation.\n *\n * @param element\n * @param animation Either \"fade\" or \"slide\"\n */\n\n\nfunction toggle(element, animation, callbackFn) {\n var nowVisible = element.style.display != 'none' || element.offsetLeft > 0; // create clone for reference\n\n var clone = element.cloneNode(true);\n\n var cleanup = function cleanup() {\n element.removeAttribute('data-animated');\n element.setAttribute('style', clone.getAttribute('style'));\n element.style.display = nowVisible ? 'none' : '';\n\n if (callbackFn) {\n callbackFn();\n }\n }; // store attribute so everyone knows we're animating this element\n\n\n element.setAttribute('data-animated', \"true\"); // toggle element visiblity right away if we're making something visible\n\n if (!nowVisible) {\n element.style.display = '';\n }\n\n var hiddenStyles, visibleStyles; // animate properties\n\n if (animation === 'slide') {\n hiddenStyles = initObjectProperties([\"height\", \"borderTopWidth\", \"borderBottomWidth\", \"paddingTop\", \"paddingBottom\"], 0);\n visibleStyles = {};\n\n if (!nowVisible) {\n var computedStyles = window.getComputedStyle(element);\n visibleStyles = copyObjectProperties([\"height\", \"borderTopWidth\", \"borderBottomWidth\", \"paddingTop\", \"paddingBottom\"], computedStyles); // in some browsers, getComputedStyle returns \"auto\" value. this falls back to getBoundingClientRect() in those browsers since we need an actual height.\n\n if (!isFinite(visibleStyles.height)) {\n var clientRect = element.getBoundingClientRect();\n visibleStyles.height = clientRect.height;\n }\n\n css(element, hiddenStyles);\n } // don't show a scrollbar during animation\n\n\n element.style.overflowY = 'hidden';\n animate(element, nowVisible ? hiddenStyles : visibleStyles, cleanup);\n } else {\n hiddenStyles = {\n opacity: 0\n };\n visibleStyles = {\n opacity: 1\n };\n\n if (!nowVisible) {\n css(element, hiddenStyles);\n }\n\n animate(element, nowVisible ? hiddenStyles : visibleStyles, cleanup);\n }\n}\n\nfunction animate(element, targetStyles, fn) {\n var last = +new Date();\n var initialStyles = window.getComputedStyle(element);\n var currentStyles = {};\n var propSteps = {};\n\n for (var property in targetStyles) {\n // make sure we have an object filled with floats\n targetStyles[property] = parseFloat(targetStyles[property]); // calculate step size & current value\n\n var to = targetStyles[property];\n var current = parseFloat(initialStyles[property]); // is there something to do?\n\n if (current == to) {\n delete targetStyles[property];\n continue;\n }\n\n propSteps[property] = (to - current) / duration; // points per second\n\n currentStyles[property] = current;\n }\n\n var tick = function tick() {\n var now = +new Date();\n var timeSinceLastTick = now - last;\n var done = true;\n var step, to, increment, newValue;\n\n for (var property in targetStyles) {\n step = propSteps[property];\n to = targetStyles[property];\n increment = step * timeSinceLastTick;\n newValue = currentStyles[property] + increment;\n\n if (step > 0 && newValue >= to || step < 0 && newValue <= to) {\n newValue = to;\n } else {\n done = false;\n } // store new value\n\n\n currentStyles[property] = newValue;\n var suffix = property !== \"opacity\" ? \"px\" : \"\";\n element.style[property] = newValue + suffix;\n }\n\n last = +new Date(); // keep going until we're done for all props\n\n if (!done) {\n window.requestAnimationFrame && requestAnimationFrame(tick) || setTimeout(tick, 32);\n } else {\n // call callback\n fn && fn();\n }\n };\n\n tick();\n}\n\nmodule.exports = {\n 'toggle': toggle,\n 'animate': animate,\n 'animated': animated\n};\n\n},{}],3:[function(require,module,exports){\n'use strict';\n\nvar defaults = {\n 'animation': 'fade',\n 'rehide': false,\n 'content': '',\n 'cookie': null,\n 'icon': '&times',\n 'screenWidthCondition': null,\n 'position': 'center',\n 'testMode': false,\n 'trigger': false,\n 'closable': true\n},\n Boxzilla,\n Animator = require('./animator.js');\n/**\n* Merge 2 objects, values of the latter overwriting the former.\n*\n* @param obj1\n* @param obj2\n* @returns {*}\n*/\n\n\nfunction merge(obj1, obj2) {\n var obj3 = {};\n\n for (var attrname in obj1) {\n obj3[attrname] = obj1[attrname];\n }\n\n for (var attrname in obj2) {\n obj3[attrname] = obj2[attrname];\n }\n\n return obj3;\n}\n/**\n* Get the real height of entire document.\n* @returns {number}\n*/\n\n\nfunction getDocumentHeight() {\n var body = document.body,\n html = document.documentElement;\n var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);\n return height;\n} // Box Object\n\n\nvar Box = function Box(id, config) {\n this.id = id; // store config values\n\n this.config = merge(defaults, config); // store ref to overlay\n\n this.overlay = document.getElementById('boxzilla-overlay'); // state\n\n this.visible = false;\n this.dismissed = false;\n this.triggered = false;\n this.triggerHeight = this.calculateTriggerHeight();\n this.cookieSet = this.isCookieSet();\n this.element = null;\n this.contentElement = null;\n this.closeIcon = null; // create dom elements for this box\n\n this.dom(); // further initialise the box\n\n this.events();\n}; // initialise the box\n\n\nBox.prototype.events = function () {\n var box = this; // attach event to \"close\" icon inside box\n\n if (this.closeIcon) {\n this.closeIcon.addEventListener('click', this.dismiss.bind(this));\n }\n\n this.element.addEventListener('click', function (e) {\n if (e.target.tagName === 'A') {\n Boxzilla.trigger('box.interactions.link', [box, e.target]);\n }\n }, false);\n this.element.addEventListener('submit', function (e) {\n box.setCookie();\n Boxzilla.trigger('box.interactions.form', [box, e.target]);\n }, false);\n}; // generate dom elements for this box\n\n\nBox.prototype.dom = function () {\n var wrapper = document.createElement('div');\n wrapper.className = 'boxzilla-container boxzilla-' + this.config.position + '-container';\n var box = document.createElement('div');\n box.setAttribute('id', 'boxzilla-' + this.id);\n box.className = 'boxzilla boxzilla-' + this.id + ' boxzilla-' + this.config.position;\n box.style.display = 'none';\n wrapper.appendChild(box);\n var content;\n\n if (typeof this.config.content === \"string\") {\n content = document.createElement('div');\n content.innerHTML = this.config.content;\n } else {\n content = this.config.content; // make sure element is visible\n\n content.style.display = '';\n }\n\n content.className = 'boxzilla-content';\n box.appendChild(content);\n\n if (this.config.closable && this.config.icon) {\n var closeIcon = document.createElement('span');\n closeIcon.className = \"boxzilla-close-icon\";\n closeIcon.innerHTML = this.config.icon;\n box.appendChild(closeIcon);\n this.closeIcon = closeIcon;\n }\n\n document.body.appendChild(wrapper);\n this.contentElement = content;\n this.element = box;\n}; // set (calculate) custom box styling depending on box options\n\n\nBox.prototype.setCustomBoxStyling = function () {\n // reset element to its initial state\n var origDisplay = this.element.style.display;\n this.element.style.display = '';\n this.element.style.overflowY = 'auto';\n this.element.style.maxHeight = 'none'; // get new dimensions\n\n var windowHeight = window.innerHeight;\n var boxHeight = this.element.clientHeight; // add scrollbar to box and limit height\n\n if (boxHeight > windowHeight) {\n this.element.style.maxHeight = windowHeight + \"px\";\n this.element.style.overflowY = 'scroll';\n } // set new top margin for boxes which are centered\n\n\n if (this.config.position === 'center') {\n var newTopMargin = (windowHeight - boxHeight) / 2;\n newTopMargin = newTopMargin >= 0 ? newTopMargin : 0;\n this.element.style.marginTop = newTopMargin + \"px\";\n }\n\n this.element.style.display = origDisplay;\n}; // toggle visibility of the box\n\n\nBox.prototype.toggle = function (show) {\n // revert visibility if no explicit argument is given\n if (typeof show === \"undefined\") {\n show = !this.visible;\n } // is box already at desired visibility?\n\n\n if (show === this.visible) {\n return false;\n } // is box being animated?\n\n\n if (Animator.animated(this.element)) {\n return false;\n } // if box should be hidden but is not closable, bail.\n\n\n if (!show && !this.config.closable) {\n return false;\n } // set new visibility status\n\n\n this.visible = show; // calculate new styling rules\n\n this.setCustomBoxStyling(); // trigger event\n\n Boxzilla.trigger('box.' + (show ? 'show' : 'hide'), [this]); // show or hide box using selected animation\n\n if (this.config.position === 'center') {\n this.overlay.classList.toggle('boxzilla-' + this.id + '-overlay');\n Animator.toggle(this.overlay, \"fade\");\n }\n\n Animator.toggle(this.element, this.config.animation, function () {\n if (this.visible) {\n return;\n }\n\n this.contentElement.innerHTML = this.contentElement.innerHTML;\n }.bind(this));\n return true;\n}; // show the box\n\n\nBox.prototype.show = function () {\n return this.toggle(true);\n}; // hide the box\n\n\nBox.prototype.hide = function () {\n return this.toggle(false);\n}; // calculate trigger height\n\n\nBox.prototype.calculateTriggerHeight = function () {\n var triggerHeight = 0;\n\n if (this.config.trigger) {\n if (this.config.trigger.method === 'element') {\n var triggerElement = document.body.querySelector(this.config.trigger.value);\n\n if (triggerElement) {\n var offset = triggerElement.getBoundingClientRect();\n triggerHeight = offset.top;\n }\n } else if (this.config.trigger.method === 'percentage') {\n triggerHeight = this.config.trigger.value / 100 * getDocumentHeight();\n }\n }\n\n return triggerHeight;\n};\n\nBox.prototype.fits = function () {\n if (!this.config.screenWidthCondition || !this.config.screenWidthCondition.value) {\n return true;\n }\n\n switch (this.config.screenWidthCondition.condition) {\n case \"larger\":\n return window.innerWidth > this.config.screenWidthCondition.value;\n\n case \"smaller\":\n return window.innerWidth < this.config.screenWidthCondition.value;\n } // meh.. condition should be \"smaller\" or \"larger\", just return true.\n\n\n return true;\n};\n\nBox.prototype.onResize = function () {\n this.triggerHeight = this.calculateTriggerHeight();\n this.setCustomBoxStyling();\n}; // is this box enabled?\n\n\nBox.prototype.mayAutoShow = function () {\n if (this.dismissed) {\n return false;\n } // check if box fits on given minimum screen width\n\n\n if (!this.fits()) {\n return false;\n } // if trigger empty or error in calculating triggerHeight, return false\n\n\n if (!this.config.trigger) {\n return false;\n } // rely on cookie value (show if not set, don't show if set)\n\n\n return !this.cookieSet;\n};\n\nBox.prototype.mayRehide = function () {\n return this.config.rehide && this.triggered;\n};\n\nBox.prototype.isCookieSet = function () {\n // always show on test mode or when no auto-trigger is configured\n if (this.config.testMode || !this.config.trigger) {\n return false;\n } // if either cookie is null or trigger & dismiss are both falsey, don't bother checking.\n\n\n if (!this.config.cookie || !this.config.cookie.triggered && !this.config.cookie.dismissed) {\n return false;\n }\n\n var cookieSet = document.cookie.replace(new RegExp(\"(?:(?:^|.*;)\\\\s*\" + 'boxzilla_box_' + this.id + \"\\\\s*\\\\=\\\\s*([^;]*).*$)|^.*$\"), \"$1\") === \"true\";\n return cookieSet;\n}; // set cookie that disables automatically showing the box\n\n\nBox.prototype.setCookie = function (hours) {\n var expiryDate = new Date();\n expiryDate.setHours(expiryDate.getHours() + hours);\n document.cookie = 'boxzilla_box_' + this.id + '=true; expires=' + expiryDate.toUTCString() + '; path=/';\n};\n\nBox.prototype.trigger = function () {\n var shown = this.show();\n\n if (!shown) {\n return;\n }\n\n this.triggered = true;\n\n if (this.config.cookie && this.config.cookie.triggered) {\n this.setCookie(this.config.cookie.triggered);\n }\n};\n/**\n* Dismisses the box and optionally sets a cookie.\n*\n* @param e The event that triggered this dismissal.\n* @returns {boolean}\n*/\n\n\nBox.prototype.dismiss = function (e) {\n // prevent default action\n e && e.preventDefault(); // only dismiss box if it's currently open.\n\n if (!this.visible) {\n return false;\n } // hide box element\n\n\n this.hide(); // set cookie\n\n if (this.config.cookie && this.config.cookie.dismissed) {\n this.setCookie(this.config.cookie.dismissed);\n }\n\n this.dismissed = true;\n Boxzilla.trigger('box.dismiss', [this]);\n return true;\n};\n\nmodule.exports = function (_Boxzilla) {\n Boxzilla = _Boxzilla;\n return Box;\n};\n\n},{\"./animator.js\":2}],4:[function(require,module,exports){\n'use strict';\n\nvar EventEmitter = require('wolfy87-eventemitter');\n\nvar Timer = require('./timer.js');\n\nvar Boxzilla = Object.create(EventEmitter.prototype);\n\nvar Box = require('./box.js')(Boxzilla);\n\nvar boxes = [];\nvar overlay;\nvar scrollElement = window;\nvar siteTimer;\nvar pageTimer;\nvar pageViews;\nvar initialised = false;\n\nvar styles = require('./styles.js');\n\nvar ExitIntent = require('./triggers/exit-intent.js');\n\nfunction throttle(fn, threshhold, scope) {\n threshhold || (threshhold = 250);\n var last, deferTimer;\n return function () {\n var context = scope || this;\n var now = +new Date(),\n args = arguments;\n\n if (last && now < last + threshhold) {\n // hold on to it\n clearTimeout(deferTimer);\n deferTimer = setTimeout(function () {\n last = now;\n fn.apply(context, args);\n }, threshhold);\n } else {\n last = now;\n fn.apply(context, args);\n }\n };\n} // \"keyup\" listener\n\n\nfunction onKeyUp(e) {\n if (e.keyCode === 27) {\n Boxzilla.dismiss();\n }\n} // check \"pageviews\" criteria for each box\n\n\nfunction checkPageViewsCriteria() {\n // don't bother if another box is currently open\n if (isAnyBoxVisible()) {\n return;\n }\n\n boxes.forEach(function (box) {\n if (!box.mayAutoShow()) {\n return;\n }\n\n if (box.config.trigger.method === 'pageviews' && pageViews >= box.config.trigger.value) {\n box.trigger();\n }\n });\n} // check time trigger criteria for each box\n\n\nfunction checkTimeCriteria() {\n // don't bother if another box is currently open\n if (isAnyBoxVisible()) {\n return;\n }\n\n boxes.forEach(function (box) {\n if (!box.mayAutoShow()) {\n return;\n } // check \"time on site\" trigger\n\n\n if (box.config.trigger.method === 'time_on_site' && siteTimer.time >= box.config.trigger.value) {\n box.trigger();\n } // check \"time on page\" trigger\n\n\n if (box.config.trigger.method === 'time_on_page' && pageTimer.time >= box.config.trigger.value) {\n box.trigger();\n }\n });\n} // check triggerHeight criteria for all boxes\n\n\nfunction checkHeightCriteria() {\n var scrollY = scrollElement.hasOwnProperty('pageYOffset') ? scrollElement.pageYOffset : scrollElement.scrollTop;\n scrollY = scrollY + window.innerHeight * 0.9;\n boxes.forEach(function (box) {\n if (!box.mayAutoShow() || box.triggerHeight <= 0) {\n return;\n }\n\n if (scrollY > box.triggerHeight) {\n // don't bother if another box is currently open\n if (isAnyBoxVisible()) {\n return;\n } // trigger box\n\n\n box.trigger();\n } // if box may auto-hide and scrollY is less than triggerHeight (with small margin of error), hide box\n\n\n if (box.mayRehide() && scrollY < box.triggerHeight - 5) {\n box.hide();\n }\n });\n} // recalculate heights and variables based on height\n\n\nfunction recalculateHeights() {\n boxes.forEach(function (box) {\n box.onResize();\n });\n}\n\nfunction onOverlayClick(e) {\n var x = e.offsetX;\n var y = e.offsetY; // calculate if click was less than 40px outside box to avoid closing it by accident\n\n boxes.forEach(function (box) {\n var rect = box.element.getBoundingClientRect();\n var margin = 40; // if click was not anywhere near box, dismiss it.\n\n if (x < rect.left - margin || x > rect.right + margin || y < rect.top - margin || y > rect.bottom + margin) {\n box.dismiss();\n }\n });\n}\n\nfunction showBoxesWithExitIntentTrigger() {\n // do nothing if already triggered OR another box is visible.\n if (isAnyBoxVisible()) {\n return;\n }\n\n boxes.forEach(function (box) {\n if (box.mayAutoShow() && box.config.trigger.method === 'exit_intent') {\n box.trigger();\n }\n });\n}\n\nfunction isAnyBoxVisible() {\n return boxes.filter(function (b) {\n return b.visible;\n }).length > 0;\n}\n\nfunction onElementClick(e) {\n // find <a> element in up to 3 parent elements\n var el = e.target || e.srcElement;\n var depth = 3;\n\n for (var i = 0; i <= depth; i++) {\n if (!el || el.tagName === 'A') {\n break;\n }\n\n el = el.parentElement;\n }\n\n if (!el || el.tagName !== 'A' || !el.getAttribute('href')) {\n return;\n }\n\n if (el.getAttribute('href').toLowerCase().indexOf('#boxzilla-') === 0) {\n var boxId = el.getAttribute('href').toLowerCase().substring(\"#boxzilla-\".length);\n Boxzilla.toggle(boxId);\n }\n}\n\nvar timers = {\n start: function start() {\n try {\n var sessionTime = sessionStorage.getItem('boxzilla_timer');\n if (sessionTime) siteTimer.time = sessionTime;\n } catch (e) {}\n\n siteTimer.start();\n pageTimer.start();\n },\n stop: function stop() {\n sessionStorage.setItem('boxzilla_timer', siteTimer.time);\n siteTimer.stop();\n pageTimer.stop();\n }\n}; // initialise & add event listeners\n\nBoxzilla.init = function () {\n if (initialised) {\n return;\n }\n\n document.body.addEventListener('click', onElementClick, true);\n\n try {\n pageViews = sessionStorage.getItem('boxzilla_pageviews') || 0;\n } catch (e) {\n pageViews = 0;\n }\n\n siteTimer = new Timer(0);\n pageTimer = new Timer(0); // insert styles into DOM\n\n var styleElement = document.createElement('style');\n styleElement.setAttribute(\"type\", \"text/css\");\n styleElement.innerHTML = styles;\n document.head.appendChild(styleElement); // add overlay element to dom\n\n overlay = document.createElement('div');\n overlay.style.display = 'none';\n overlay.id = 'boxzilla-overlay';\n document.body.appendChild(overlay); // init exit intent trigger\n\n new ExitIntent(showBoxesWithExitIntentTrigger);\n scrollElement.addEventListener('touchstart', throttle(checkHeightCriteria), true);\n scrollElement.addEventListener('scroll', throttle(checkHeightCriteria), true);\n window.addEventListener('resize', throttle(recalculateHeights));\n window.addEventListener('load', recalculateHeights);\n overlay.addEventListener('click', onOverlayClick);\n window.setInterval(checkTimeCriteria, 1000);\n window.setTimeout(checkPageViewsCriteria, 1000);\n document.addEventListener('keyup', onKeyUp);\n timers.start();\n window.addEventListener('focus', timers.start);\n window.addEventListener('beforeunload', function () {\n timers.stop();\n sessionStorage.setItem('boxzilla_pageviews', ++pageViews);\n });\n window.addEventListener('blur', timers.stop);\n Boxzilla.trigger('ready');\n initialised = true; // ensure this function doesn't run again\n};\n/**\n * Create a new Box\n *\n * @param string id\n * @param object opts\n *\n * @returns Box\n */\n\n\nBoxzilla.create = function (id, opts) {\n // preserve backwards compat for minimumScreenWidth option\n if (typeof opts.minimumScreenWidth !== \"undefined\") {\n opts.screenWidthCondition = {\n condition: \"larger\",\n value: opts.minimumScreenWidth\n };\n }\n\n var box = new Box(id, opts);\n boxes.push(box);\n return box;\n};\n\nBoxzilla.get = function (id) {\n for (var i = 0; i < boxes.length; i++) {\n var box = boxes[i];\n\n if (box.id == id) {\n return box;\n }\n }\n\n throw new Error(\"No box exists with ID \" + id);\n}; // dismiss a single box (or all by omitting id param)\n\n\nBoxzilla.dismiss = function (id) {\n // if no id given, dismiss all current open boxes\n if (typeof id === \"undefined\") {\n boxes.forEach(function (box) {\n box.dismiss();\n });\n } else {\n Boxzilla.get(id).dismiss();\n }\n};\n\nBoxzilla.hide = function (id) {\n if (typeof id === \"undefined\") {\n boxes.forEach(function (box) {\n box.hide();\n });\n } else {\n Boxzilla.get(id).hide();\n }\n};\n\nBoxzilla.show = function (id) {\n if (typeof id === \"undefined\") {\n boxes.forEach(function (box) {\n box.show();\n });\n } else {\n Boxzilla.get(id).show();\n }\n};\n\nBoxzilla.toggle = function (id) {\n if (typeof id === \"undefined\") {\n boxes.forEach(function (box) {\n box.toggle();\n });\n } else {\n Boxzilla.get(id).toggle();\n }\n}; // expose each individual box.\n\n\nBoxzilla.boxes = boxes; // expose boxzilla object\n\nwindow.Boxzilla = Boxzilla;\n\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = Boxzilla;\n}\n\n},{\"./box.js\":3,\"./styles.js\":5,\"./timer.js\":6,\"./triggers/exit-intent.js\":7,\"wolfy87-eventemitter\":1}],5:[function(require,module,exports){\n\"use strict\";\n\nvar 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}\";\nmodule.exports = styles;\n\n},{}],6:[function(require,module,exports){\n'use strict';\n\nvar Timer = function Timer(start) {\n this.time = start;\n this.interval = 0;\n};\n\nTimer.prototype.tick = function () {\n this.time++;\n};\n\nTimer.prototype.start = function () {\n if (!this.interval) {\n this.interval = window.setInterval(this.tick.bind(this), 1000);\n }\n};\n\nTimer.prototype.stop = function () {\n if (this.interval) {\n window.clearInterval(this.interval);\n this.interval = 0;\n }\n};\n\nmodule.exports = Timer;\n\n},{}],7:[function(require,module,exports){\n'use strict';\n\nmodule.exports = function (callback) {\n var timeout = null;\n var touchStart = {};\n\n function triggerCallback() {\n document.documentElement.removeEventListener('mouseleave', onMouseLeave);\n document.documentElement.removeEventListener('mouseenter', onMouseEnter);\n document.documentElement.removeEventListener('click', clearTimeout);\n window.removeEventListener('touchstart', onTouchStart);\n window.removeEventListener('touchend', onTouchEnd);\n callback();\n }\n\n function clearTimeout() {\n if (timeout === null) {\n return;\n }\n\n window.clearTimeout(timeout);\n timeout = null;\n }\n\n function onMouseEnter(evt) {\n clearTimeout();\n }\n\n function onMouseLeave(evt) {\n clearTimeout(); // did mouse leave at top of window?\n // add small exception space in the top-right corner\n\n if (evt.clientY <= 0 && evt.clientX < 0.80 * window.innerWidth) {\n timeout = window.setTimeout(triggerCallback, 400);\n }\n }\n\n function onTouchStart(evt) {\n clearTimeout();\n touchStart = {\n timestamp: performance.now(),\n scrollY: window.scrollY,\n windowHeight: window.innerHeight\n };\n }\n\n function onTouchEnd(evt) {\n clearTimeout(); // did address bar appear?\n\n if (window.innerHeight > touchStart.windowHeight) {\n return;\n } // allow a tiny tiny margin for error, to not fire on clicks\n\n\n if (window.scrollY + 20 >= touchStart.scrollY) {\n return;\n }\n\n if (performance.now() - touchStart.timestamp > 300) {\n return;\n }\n\n if (['A', 'INPUT', 'BUTTON'].indexOf(evt.target.tagName) > -1) {\n return;\n }\n\n timeout = window.setTimeout(triggerCallback, 800);\n }\n\n window.addEventListener('touchstart', onTouchStart);\n window.addEventListener('touchend', onTouchEnd);\n document.documentElement.addEventListener('mouseenter', onMouseEnter);\n document.documentElement.addEventListener('mouseleave', onMouseLeave);\n document.documentElement.addEventListener('click', clearTimeout);\n};\n\n},{}],8:[function(require,module,exports){\n\"use strict\";\n\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n(function () {\n 'use strict';\n\n var Boxzilla = require('boxzilla');\n\n var options = window.boxzilla_options; // expose Boxzilla object to window\n\n window.Boxzilla = Boxzilla; // helper function for setting CSS styles\n\n function css(element, styles) {\n if (styles.background_color) {\n element.style.background = styles.background_color;\n }\n\n if (styles.color) {\n element.style.color = styles.color;\n }\n\n if (styles.border_color) {\n element.style.borderColor = styles.border_color;\n }\n\n if (styles.border_width) {\n element.style.borderWidth = parseInt(styles.border_width) + \"px\";\n }\n\n if (styles.border_style) {\n element.style.borderStyle = styles.border_style;\n }\n\n if (styles.width) {\n element.style.maxWidth = parseInt(styles.width) + \"px\";\n }\n }\n\n function createBoxesFromConfig() {\n // failsafe against including script twice.\n if (options.inited) {\n return;\n } // create boxes from options\n\n\n for (var key in options.boxes) {\n // get opts\n var boxOpts = options.boxes[key];\n boxOpts.testMode = isLoggedIn && options.testMode; // find box content element, bail if not found\n\n var boxContentElement = document.getElementById('boxzilla-box-' + boxOpts.id + '-content');\n\n if (!boxContentElement) {\n continue;\n } // use element as content option\n\n\n boxOpts.content = boxContentElement; // create box\n\n var box = Boxzilla.create(boxOpts.id, boxOpts); // add box slug to box element as classname\n\n box.element.className = box.element.className + ' boxzilla-' + boxOpts.post.slug; // add custom css to box\n\n css(box.element, boxOpts.css);\n box.element.firstChild.firstChild.className += \" first-child\";\n box.element.firstChild.lastChild.className += \" last-child\"; // maybe show box right away\n\n if (box.fits() && locationHashRefersBox(box)) {\n box.show();\n }\n } // set flag to prevent initialising twice\n\n\n options.inited = true; // trigger \"done\" event.\n\n Boxzilla.trigger('done'); // maybe open box with MC4WP form in it\n\n maybeOpenMailChimpForWordPressBox();\n }\n\n function locationHashRefersBox(box) {\n if (!window.location.hash || 0 === window.location.hash.length) {\n return false;\n }\n\n var elementId = window.location.hash.substring(1); // only attempt on strings looking like an ID \n\n var regex = /^[a-zA-Z\\-\\_0-9]+$/;\n\n if (!regex.test(elementId)) {\n return false;\n }\n\n if (elementId === box.element.id) {\n return true;\n } else if (box.element.querySelector('#' + elementId)) {\n return true;\n }\n\n return false;\n }\n\n function maybeOpenMailChimpForWordPressBox() {\n if (_typeof(window.mc4wp_forms_config) !== \"object\" || !window.mc4wp_forms_config.submitted_form) {\n return;\n }\n\n var selector = '#' + window.mc4wp_forms_config.submitted_form.element_id;\n var boxes = Boxzilla.boxes;\n\n for (var boxId in boxes) {\n if (!boxes.hasOwnProperty(boxId)) {\n continue;\n }\n\n var box = boxes[boxId];\n\n if (box.element.querySelector(selector)) {\n box.show();\n return;\n }\n }\n } // print message when test mode is enabled\n\n\n var isLoggedIn = document.body.className.indexOf('logged-in') > -1;\n\n if (isLoggedIn && options.testMode) {\n console.log('Boxzilla: Test mode is enabled. Please disable test mode if you\\'re done testing.');\n } // init boxzilla\n\n\n Boxzilla.init(); // on window.load, create DOM elements for boxes\n\n window.addEventListener('load', createBoxesFromConfig);\n})();\n\n},{\"boxzilla\":4}]},{},[8]);\n; })();"]}
bootstrap.php CHANGED
@@ -45,7 +45,7 @@ add_action( 'plugins_loaded', function() use( $boxzilla ) {
45
  // license manager
46
  if( is_admin() || ( defined( 'DOING_CRON' ) && DOING_CRON ) || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
47
  $boxzilla['license_manager']->hook();
48
-
49
  if( count( $boxzilla->plugins ) > 0 ) {
50
  $boxzilla['update_manager']->hook();
51
  }
45
  // license manager
46
  if( is_admin() || ( defined( 'DOING_CRON' ) && DOING_CRON ) || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
47
  $boxzilla['license_manager']->hook();
48
+
49
  if( count( $boxzilla->plugins ) > 0 ) {
50
  $boxzilla['update_manager']->hook();
51
  }
boxzilla.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /*
3
  Plugin Name: Boxzilla
4
- Version: 3.2.9
5
  Plugin URI: https://boxzillaplugin.com/#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=plugins-page
6
  Description: Call-To-Action Boxes that display after visitors scroll down far enough. Unobtrusive, but highly conversing!
7
  Author: ibericode
@@ -11,7 +11,7 @@ Domain Path: /languages/
11
  License: GPL v3
12
 
13
  Boxzilla Plugin
14
- Copyright (C) 2013-2018, Danny van Kooten, hi@dannyvankooten.com
15
 
16
  This program is free software: you can redistribute it and/or modify
17
  it under the terms of the GNU General Public License as published by
@@ -41,7 +41,7 @@ if ( ! defined( 'ABSPATH' ) ) {
41
  function _load_boxzilla() {
42
 
43
  define( 'BOXZILLA_FILE', __FILE__ );
44
- define( 'BOXZILLA_VERSION', '3.2.9' );
45
 
46
  require __DIR__ . '/bootstrap.php';
47
  }
1
  <?php
2
  /*
3
  Plugin Name: Boxzilla
4
+ Version: 3.2.10
5
  Plugin URI: https://boxzillaplugin.com/#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=plugins-page
6
  Description: Call-To-Action Boxes that display after visitors scroll down far enough. Unobtrusive, but highly conversing!
7
  Author: ibericode
11
  License: GPL v3
12
 
13
  Boxzilla Plugin
14
+ Copyright (C) 2013-2019, Danny van Kooten, hi@dannyvankooten.com
15
 
16
  This program is free software: you can redistribute it and/or modify
17
  it under the terms of the GNU General Public License as published by
41
  function _load_boxzilla() {
42
 
43
  define( 'BOXZILLA_FILE', __FILE__ );
44
+ define( 'BOXZILLA_VERSION', '3.2.10' );
45
 
46
  require __DIR__ . '/bootstrap.php';
47
  }
languages/boxzilla.pot CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2018 boxzilla
2
  # This file is distributed under the same license as the boxzilla package.
3
  msgid ""
4
  msgstr ""
@@ -57,83 +57,87 @@ msgstr ""
57
  msgid "No Boxes found in Trash"
58
  msgstr ""
59
 
60
- #: src/admin/class-admin.php:104
 
 
 
 
61
  msgid "Awesome, you are using Boxzilla! You can now safely <a href=\"%s\">deactivate the Scroll Triggered Boxes plugin</a>."
62
  msgstr ""
63
 
64
- #: src/admin/class-admin.php:172
65
  msgid "Box ID"
66
  msgstr ""
67
 
68
- #: src/admin/class-admin.php:175
69
  msgid "Box Title"
70
  msgstr ""
71
 
72
- #: src/admin/class-admin.php:209, src/admin/class-admin.php:210, src/admin/views/settings.php:7
73
  msgid "Settings"
74
  msgstr ""
75
 
76
- #: src/admin/class-admin.php:215, src/admin/class-admin.php:216
77
  msgid "Extensions"
78
  msgstr ""
79
 
80
- #: src/admin/class-admin.php:308, src/admin/views/metaboxes/box-option-controls.php:60
81
  msgid "and"
82
  msgstr ""
83
 
84
- #: src/admin/class-admin.php:309, src/admin/views/metaboxes/box-option-controls.php:59
85
  msgid "or"
86
  msgstr ""
87
 
88
- #: src/admin/class-admin.php:310
89
  msgid "Enter a comma-separated list of values."
90
  msgstr ""
91
 
92
- #: src/admin/class-admin.php:311
93
  msgid "Enter a comma-separated list of post slugs or post ID's.."
94
  msgstr ""
95
 
96
- #: src/admin/class-admin.php:312
97
  msgid "Enter a comma-separated list of page slugs or page ID's.."
98
  msgstr ""
99
 
100
- #: src/admin/class-admin.php:313
101
  msgid "Enter a comma-separated list of post types.."
102
  msgstr ""
103
 
104
- #: src/admin/class-admin.php:314
105
  msgid "Enter a comma-separated list of relative URL's, eg /contact/"
106
  msgstr ""
107
 
108
- #: src/admin/class-admin.php:346
109
  msgid "Box Appearance"
110
  msgstr ""
111
 
112
- #: src/admin/class-admin.php:355
113
  msgid "Box Options"
114
  msgstr ""
115
 
116
- #: src/admin/class-admin.php:364
117
  msgid "Looking for help?"
118
  msgstr ""
119
 
120
- #: src/admin/class-admin.php:372
121
  msgid "Subscribe to our newsletter"
122
  msgstr ""
123
 
124
- #: src/admin/class-review-notice.php:74
125
  msgid "You've been using Boxzilla for some time now; we hope you love it!"
126
  msgstr ""
127
 
128
- #: src/admin/class-review-notice.php:75
129
  msgid "If you do, please <a href=\"%s\">leave us a 5★ rating on WordPress.org</a>. It would be of great help to us."
130
  msgstr ""
131
 
132
- #: src/admin/class-review-notice.php:77
133
  msgid "Dismiss this notice."
134
  msgstr ""
135
 
136
- #: src/licensing/class-api.php:165
137
  msgid "The Boxzilla server returned an invalid response."
138
  msgstr ""
139
 
@@ -157,11 +161,11 @@ msgstr ""
157
  msgid "If not, please click here: %s."
158
  msgstr ""
159
 
160
- #: src/admin/views/settings.php:20, src/admin/views/metaboxes/box-option-controls.php:213
161
  msgid "Enable test mode?"
162
  msgstr ""
163
 
164
- #: src/admin/views/settings.php:24, src/admin/views/metaboxes/box-option-controls.php:217
165
  msgid "If test mode is enabled, all boxes will show up regardless of whether a cookie has been set."
166
  msgstr ""
167
 
@@ -169,39 +173,39 @@ msgstr ""
169
  msgid "License & Plugin Updates"
170
  msgstr ""
171
 
172
- #: src/licensing/views/license-form.php:13
173
  msgid "Warning! You are <u>not</u> receiving plugin updates for the following plugin(s):"
174
  msgstr ""
175
 
176
- #: src/licensing/views/license-form.php:37
177
  msgid "License Key"
178
  msgstr ""
179
 
180
- #: src/licensing/views/license-form.php:39
181
  msgid "Enter your license key.."
182
  msgstr ""
183
 
184
- #: src/licensing/views/license-form.php:42
185
  msgid "The license key received when purchasing your premium Boxzilla plan. <a href=\"%s\">You can find it here</a>."
186
  msgstr ""
187
 
188
- #: src/licensing/views/license-form.php:47
189
  msgid "License Status"
190
  msgstr ""
191
 
192
- #: src/licensing/views/license-form.php:51
193
  msgid "ACTIVE"
194
  msgstr ""
195
 
196
- #: src/licensing/views/license-form.php:51
197
  msgid "you are receiving plugin updates"
198
  msgstr ""
199
 
200
- #: src/licensing/views/license-form.php:53
201
  msgid "INACTIVE"
202
  msgstr ""
203
 
204
- #: src/licensing/views/license-form.php:53
205
  msgid "you are <strong>not</strong> receiving plugin updates"
206
  msgstr ""
207
 
@@ -297,147 +301,147 @@ msgstr ""
297
  msgid "of the following conditions."
298
  msgstr ""
299
 
300
- #: src/admin/views/metaboxes/box-option-controls.php:71, src/admin/views/metaboxes/box-option-controls.php:237
301
  msgid "Remove rule"
302
  msgstr ""
303
 
304
- #: src/admin/views/metaboxes/box-option-controls.php:81, src/admin/views/metaboxes/box-option-controls.php:246
305
  msgid "is"
306
  msgstr ""
307
 
308
- #: src/admin/views/metaboxes/box-option-controls.php:82, src/admin/views/metaboxes/box-option-controls.php:247
309
  msgid "is not"
310
  msgstr ""
311
 
312
- #: src/admin/views/metaboxes/box-option-controls.php:83, src/admin/views/metaboxes/box-option-controls.php:248
313
  msgid "contains"
314
  msgstr ""
315
 
316
- #: src/admin/views/metaboxes/box-option-controls.php:84, src/admin/views/metaboxes/box-option-controls.php:249
317
  msgid "does not contain"
318
  msgstr ""
319
 
320
- #: src/admin/views/metaboxes/box-option-controls.php:87, src/admin/views/metaboxes/box-option-controls.php:252
321
  msgid "Leave empty for any or enter (comma-separated) names or ID's"
322
  msgstr ""
323
 
324
- #: src/admin/views/metaboxes/box-option-controls.php:95
325
  msgid "Add another rule"
326
  msgstr ""
327
 
328
- #: src/admin/views/metaboxes/box-option-controls.php:98
329
  msgid "Box Position"
330
  msgstr ""
331
 
332
- #: src/admin/views/metaboxes/box-option-controls.php:104
333
  msgid "Top Left"
334
  msgstr ""
335
 
336
- #: src/admin/views/metaboxes/box-option-controls.php:111
337
  msgid "Top Right"
338
  msgstr ""
339
 
340
- #: src/admin/views/metaboxes/box-option-controls.php:120
341
  msgid "Center"
342
  msgstr ""
343
 
344
- #: src/admin/views/metaboxes/box-option-controls.php:129
345
  msgid "Bottom Left"
346
  msgstr ""
347
 
348
- #: src/admin/views/metaboxes/box-option-controls.php:136
349
  msgid "Bottom Right"
350
  msgstr ""
351
 
352
- #: src/admin/views/metaboxes/box-option-controls.php:145
353
  msgid "Animation"
354
  msgstr ""
355
 
356
- #: src/admin/views/metaboxes/box-option-controls.php:147
357
  msgid "Fade In"
358
  msgstr ""
359
 
360
- #: src/admin/views/metaboxes/box-option-controls.php:148
361
  msgid "Slide In"
362
  msgstr ""
363
 
364
- #: src/admin/views/metaboxes/box-option-controls.php:149
365
  msgid "Which animation type should be used to show the box when triggered?"
366
  msgstr ""
367
 
368
- #: src/admin/views/metaboxes/box-option-controls.php:153
369
  msgid "Auto-show box?"
370
  msgstr ""
371
 
372
- #: src/admin/views/metaboxes/box-option-controls.php:155
373
  msgid "Never"
374
  msgstr ""
375
 
376
- #: src/admin/views/metaboxes/box-option-controls.php:156
377
  msgid "Yes, after %s seconds on the page."
378
  msgstr ""
379
 
380
- #: src/admin/views/metaboxes/box-option-controls.php:157
381
  msgid "Yes, when at %s of page height"
382
  msgstr ""
383
 
384
- #: src/admin/views/metaboxes/box-option-controls.php:158
385
  msgid "Yes, when at element %s"
386
  msgstr ""
387
 
388
- #: src/admin/views/metaboxes/box-option-controls.php:158
389
  msgid "Example: #comments"
390
  msgstr ""
391
 
392
- #: src/admin/views/metaboxes/box-option-controls.php:164
393
  msgid "Cookie expiration"
394
  msgstr ""
395
 
396
- #: src/admin/views/metaboxes/box-option-controls.php:169, src/admin/views/metaboxes/box-option-controls.php:174
397
  msgid "hours"
398
  msgstr ""
399
 
400
- #: src/admin/views/metaboxes/box-option-controls.php:178
401
  msgid "After this box is triggered or dismissed, how many hours should it stay hidden?"
402
  msgstr ""
403
 
404
- #: src/admin/views/metaboxes/box-option-controls.php:182
405
  msgid "Screen width"
406
  msgstr ""
407
 
408
- #: src/admin/views/metaboxes/box-option-controls.php:187
409
  msgid "larger"
410
  msgstr ""
411
 
412
- #: src/admin/views/metaboxes/box-option-controls.php:187
413
  msgid "smaller"
414
  msgstr ""
415
 
416
- #: src/admin/views/metaboxes/box-option-controls.php:188
417
  msgid "Only show on screens %s than %s."
418
  msgstr ""
419
 
420
- #: src/admin/views/metaboxes/box-option-controls.php:189
421
  msgid "Leave empty if you want to show the box on all screen sizes."
422
  msgstr ""
423
 
424
- #: src/admin/views/metaboxes/box-option-controls.php:194
425
  msgid "Auto-hide?"
426
  msgstr ""
427
 
428
- #: src/admin/views/metaboxes/box-option-controls.php:198
429
  msgid "Hide box again when visitors scroll back up?"
430
  msgstr ""
431
 
432
- #: src/admin/views/metaboxes/box-option-controls.php:202
433
  msgid "Show close icon?"
434
  msgstr ""
435
 
436
- #: src/admin/views/metaboxes/box-option-controls.php:207
437
  msgid "If you decide to hide the close icon, make sure to offer an alternative way to close the box."
438
  msgstr ""
439
 
440
- #: src/admin/views/metaboxes/box-option-controls.php:208
441
  msgid "Example: "
442
  msgstr ""
443
 
1
+ # Copyright (C) 2019 boxzilla
2
  # This file is distributed under the same license as the boxzilla package.
3
  msgid ""
4
  msgstr ""
57
  msgid "No Boxes found in Trash"
58
  msgstr ""
59
 
60
+ #: src/admin/class-admin.php:97
61
+ msgid "Duplicate box"
62
+ msgstr ""
63
+
64
+ #: src/admin/class-admin.php:136
65
  msgid "Awesome, you are using Boxzilla! You can now safely <a href=\"%s\">deactivate the Scroll Triggered Boxes plugin</a>."
66
  msgstr ""
67
 
68
+ #: src/admin/class-admin.php:208
69
  msgid "Box ID"
70
  msgstr ""
71
 
72
+ #: src/admin/class-admin.php:211
73
  msgid "Box Title"
74
  msgstr ""
75
 
76
+ #: src/admin/class-admin.php:246, src/admin/class-admin.php:247, src/admin/views/settings.php:7
77
  msgid "Settings"
78
  msgstr ""
79
 
80
+ #: src/admin/class-admin.php:252, src/admin/class-admin.php:253
81
  msgid "Extensions"
82
  msgstr ""
83
 
84
+ #: src/admin/class-admin.php:349, src/admin/views/metaboxes/box-option-controls.php:62
85
  msgid "and"
86
  msgstr ""
87
 
88
+ #: src/admin/class-admin.php:350, src/admin/views/metaboxes/box-option-controls.php:61
89
  msgid "or"
90
  msgstr ""
91
 
92
+ #: src/admin/class-admin.php:351
93
  msgid "Enter a comma-separated list of values."
94
  msgstr ""
95
 
96
+ #: src/admin/class-admin.php:352
97
  msgid "Enter a comma-separated list of post slugs or post ID's.."
98
  msgstr ""
99
 
100
+ #: src/admin/class-admin.php:353
101
  msgid "Enter a comma-separated list of page slugs or page ID's.."
102
  msgstr ""
103
 
104
+ #: src/admin/class-admin.php:354
105
  msgid "Enter a comma-separated list of post types.."
106
  msgstr ""
107
 
108
+ #: src/admin/class-admin.php:355
109
  msgid "Enter a comma-separated list of relative URL's, eg /contact/"
110
  msgstr ""
111
 
112
+ #: src/admin/class-admin.php:386
113
  msgid "Box Appearance"
114
  msgstr ""
115
 
116
+ #: src/admin/class-admin.php:395
117
  msgid "Box Options"
118
  msgstr ""
119
 
120
+ #: src/admin/class-admin.php:404
121
  msgid "Looking for help?"
122
  msgstr ""
123
 
124
+ #: src/admin/class-admin.php:412
125
  msgid "Subscribe to our newsletter"
126
  msgstr ""
127
 
128
+ #: src/admin/class-review-notice.php:79
129
  msgid "You've been using Boxzilla for some time now; we hope you love it!"
130
  msgstr ""
131
 
132
+ #: src/admin/class-review-notice.php:80
133
  msgid "If you do, please <a href=\"%s\">leave us a 5★ rating on WordPress.org</a>. It would be of great help to us."
134
  msgstr ""
135
 
136
+ #: src/admin/class-review-notice.php:82
137
  msgid "Dismiss this notice."
138
  msgstr ""
139
 
140
+ #: src/licensing/class-api.php:172
141
  msgid "The Boxzilla server returned an invalid response."
142
  msgstr ""
143
 
161
  msgid "If not, please click here: %s."
162
  msgstr ""
163
 
164
+ #: src/admin/views/settings.php:20, src/admin/views/metaboxes/box-option-controls.php:216
165
  msgid "Enable test mode?"
166
  msgstr ""
167
 
168
+ #: src/admin/views/settings.php:24, src/admin/views/metaboxes/box-option-controls.php:220
169
  msgid "If test mode is enabled, all boxes will show up regardless of whether a cookie has been set."
170
  msgstr ""
171
 
173
  msgid "License & Plugin Updates"
174
  msgstr ""
175
 
176
+ #: src/licensing/views/license-form.php:14
177
  msgid "Warning! You are <u>not</u> receiving plugin updates for the following plugin(s):"
178
  msgstr ""
179
 
180
+ #: src/licensing/views/license-form.php:40
181
  msgid "License Key"
182
  msgstr ""
183
 
184
+ #: src/licensing/views/license-form.php:42
185
  msgid "Enter your license key.."
186
  msgstr ""
187
 
188
+ #: src/licensing/views/license-form.php:47
189
  msgid "The license key received when purchasing your premium Boxzilla plan. <a href=\"%s\">You can find it here</a>."
190
  msgstr ""
191
 
192
+ #: src/licensing/views/license-form.php:52
193
  msgid "License Status"
194
  msgstr ""
195
 
196
+ #: src/licensing/views/license-form.php:57
197
  msgid "ACTIVE"
198
  msgstr ""
199
 
200
+ #: src/licensing/views/license-form.php:57
201
  msgid "you are receiving plugin updates"
202
  msgstr ""
203
 
204
+ #: src/licensing/views/license-form.php:61
205
  msgid "INACTIVE"
206
  msgstr ""
207
 
208
+ #: src/licensing/views/license-form.php:61
209
  msgid "you are <strong>not</strong> receiving plugin updates"
210
  msgstr ""
211
 
301
  msgid "of the following conditions."
302
  msgstr ""
303
 
304
+ #: src/admin/views/metaboxes/box-option-controls.php:72, src/admin/views/metaboxes/box-option-controls.php:240
305
  msgid "Remove rule"
306
  msgstr ""
307
 
308
+ #: src/admin/views/metaboxes/box-option-controls.php:82, src/admin/views/metaboxes/box-option-controls.php:249
309
  msgid "is"
310
  msgstr ""
311
 
312
+ #: src/admin/views/metaboxes/box-option-controls.php:83, src/admin/views/metaboxes/box-option-controls.php:250
313
  msgid "is not"
314
  msgstr ""
315
 
316
+ #: src/admin/views/metaboxes/box-option-controls.php:84, src/admin/views/metaboxes/box-option-controls.php:251
317
  msgid "contains"
318
  msgstr ""
319
 
320
+ #: src/admin/views/metaboxes/box-option-controls.php:85, src/admin/views/metaboxes/box-option-controls.php:252
321
  msgid "does not contain"
322
  msgstr ""
323
 
324
+ #: src/admin/views/metaboxes/box-option-controls.php:88, src/admin/views/metaboxes/box-option-controls.php:255
325
  msgid "Leave empty for any or enter (comma-separated) names or ID's"
326
  msgstr ""
327
 
328
+ #: src/admin/views/metaboxes/box-option-controls.php:98
329
  msgid "Add another rule"
330
  msgstr ""
331
 
332
+ #: src/admin/views/metaboxes/box-option-controls.php:101
333
  msgid "Box Position"
334
  msgstr ""
335
 
336
+ #: src/admin/views/metaboxes/box-option-controls.php:107
337
  msgid "Top Left"
338
  msgstr ""
339
 
340
+ #: src/admin/views/metaboxes/box-option-controls.php:114
341
  msgid "Top Right"
342
  msgstr ""
343
 
344
+ #: src/admin/views/metaboxes/box-option-controls.php:123
345
  msgid "Center"
346
  msgstr ""
347
 
348
+ #: src/admin/views/metaboxes/box-option-controls.php:132
349
  msgid "Bottom Left"
350
  msgstr ""
351
 
352
+ #: src/admin/views/metaboxes/box-option-controls.php:139
353
  msgid "Bottom Right"
354
  msgstr ""
355
 
356
+ #: src/admin/views/metaboxes/box-option-controls.php:148
357
  msgid "Animation"
358
  msgstr ""
359
 
360
+ #: src/admin/views/metaboxes/box-option-controls.php:150
361
  msgid "Fade In"
362
  msgstr ""
363
 
364
+ #: src/admin/views/metaboxes/box-option-controls.php:151
365
  msgid "Slide In"
366
  msgstr ""
367
 
368
+ #: src/admin/views/metaboxes/box-option-controls.php:152
369
  msgid "Which animation type should be used to show the box when triggered?"
370
  msgstr ""
371
 
372
+ #: src/admin/views/metaboxes/box-option-controls.php:156
373
  msgid "Auto-show box?"
374
  msgstr ""
375
 
376
+ #: src/admin/views/metaboxes/box-option-controls.php:158
377
  msgid "Never"
378
  msgstr ""
379
 
380
+ #: src/admin/views/metaboxes/box-option-controls.php:159
381
  msgid "Yes, after %s seconds on the page."
382
  msgstr ""
383
 
384
+ #: src/admin/views/metaboxes/box-option-controls.php:160
385
  msgid "Yes, when at %s of page height"
386
  msgstr ""
387
 
388
+ #: src/admin/views/metaboxes/box-option-controls.php:161
389
  msgid "Yes, when at element %s"
390
  msgstr ""
391
 
392
+ #: src/admin/views/metaboxes/box-option-controls.php:161
393
  msgid "Example: #comments"
394
  msgstr ""
395
 
396
+ #: src/admin/views/metaboxes/box-option-controls.php:167
397
  msgid "Cookie expiration"
398
  msgstr ""
399
 
400
+ #: src/admin/views/metaboxes/box-option-controls.php:172, src/admin/views/metaboxes/box-option-controls.php:177
401
  msgid "hours"
402
  msgstr ""
403
 
404
+ #: src/admin/views/metaboxes/box-option-controls.php:181
405
  msgid "After this box is triggered or dismissed, how many hours should it stay hidden?"
406
  msgstr ""
407
 
408
+ #: src/admin/views/metaboxes/box-option-controls.php:185
409
  msgid "Screen width"
410
  msgstr ""
411
 
412
+ #: src/admin/views/metaboxes/box-option-controls.php:190
413
  msgid "larger"
414
  msgstr ""
415
 
416
+ #: src/admin/views/metaboxes/box-option-controls.php:190
417
  msgid "smaller"
418
  msgstr ""
419
 
420
+ #: src/admin/views/metaboxes/box-option-controls.php:191
421
  msgid "Only show on screens %s than %s."
422
  msgstr ""
423
 
424
+ #: src/admin/views/metaboxes/box-option-controls.php:192
425
  msgid "Leave empty if you want to show the box on all screen sizes."
426
  msgstr ""
427
 
428
+ #: src/admin/views/metaboxes/box-option-controls.php:197
429
  msgid "Auto-hide?"
430
  msgstr ""
431
 
432
+ #: src/admin/views/metaboxes/box-option-controls.php:201
433
  msgid "Hide box again when visitors scroll back up?"
434
  msgstr ""
435
 
436
+ #: src/admin/views/metaboxes/box-option-controls.php:205
437
  msgid "Show close icon?"
438
  msgstr ""
439
 
440
+ #: src/admin/views/metaboxes/box-option-controls.php:210
441
  msgid "If you decide to hide the close icon, make sure to offer an alternative way to close the box."
442
  msgstr ""
443
 
444
+ #: src/admin/views/metaboxes/box-option-controls.php:211
445
  msgid "Example: "
446
  msgstr ""
447
 
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: Ibericode, DvanKooten, hchouhan, lapzor
3
  Donate link: https://boxzillaplugin.com/#utm_source=wp-plugin-repo&utm_medium=boxzilla&utm_campaign=donate-link
4
  Tags: scroll triggered box, cta, social, pop-up, newsletter, call to action, mailchimp, contact form 7, social media, mc4wp, ibericode
5
  Requires at least: 4.1
6
- Tested up to: 5.0.2
7
- Stable tag: 3.2.9
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
  Requires PHP: 5.3
@@ -130,6 +130,17 @@ Have a look at the [frequently asked questions](https://wordpress.org/plugins/bo
130
  == Changelog ==
131
 
132
 
 
 
 
 
 
 
 
 
 
 
 
133
  #### 3.2.9 - December 5, 2018
134
 
135
  **Improvements**
3
  Donate link: https://boxzillaplugin.com/#utm_source=wp-plugin-repo&utm_medium=boxzilla&utm_campaign=donate-link
4
  Tags: scroll triggered box, cta, social, pop-up, newsletter, call to action, mailchimp, contact form 7, social media, mc4wp, ibericode
5
  Requires at least: 4.1
6
+ Tested up to: 5.1
7
+ Stable tag: 3.2.10
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
  Requires PHP: 5.3
130
  == Changelog ==
131
 
132
 
133
+ #### 3.2.10 - February 15, 2019
134
+
135
+ **Improvements**
136
+
137
+ - Better [exit intent detection](https://boxzillaplugin.com/add-ons/exit-intent/) for mobile devices.
138
+
139
+ **Additions**
140
+
141
+ - New bulk action to quickly duplicate a box with all of its settings.
142
+
143
+
144
  #### 3.2.9 - December 5, 2018
145
 
146
  **Improvements**
src/admin/class-admin.php CHANGED
@@ -1,763 +1,812 @@
1
- <?php
2
-
3
- namespace Boxzilla\Admin;
4
-
5
- use Boxzilla\Plugin,
6
- Boxzilla\Box,
7
- Boxzilla\Boxzilla;
8
- use WP_Post;
9
- use WP_Screen;
10
-
11
- class Admin {
12
-
13
- /**
14
- * @var Plugin $plugin
15
- */
16
- private $plugin;
17
-
18
- /**
19
- * @var Boxzilla
20
- */
21
- protected $boxzilla;
22
-
23
- /**
24
- * @var ReviewNotice
25
- */
26
- protected $review_notice;
27
-
28
- /**
29
- * @param Plugin $plugin
30
- * @param Boxzilla $boxzilla
31
- */
32
- public function __construct( Plugin $plugin, Boxzilla $boxzilla ) {
33
- $this->plugin = $plugin;
34
- $this->boxzilla = $boxzilla;
35
- $this->review_notice = new ReviewNotice();
36
- }
37
-
38
- /**
39
- * Initialise the all admin related stuff
40
- */
41
- public function init() {
42
-
43
- // Load the plugin textdomain
44
- load_plugin_textdomain( 'boxzilla', null, basename( $this->plugin->dir() ) . '/languages' );
45
-
46
- // action hooks
47
- $this->add_hooks();
48
- $this->run_migrations();
49
- }
50
-
51
- /**
52
- * Add necessary hooks
53
- */
54
- protected function add_hooks() {
55
- add_action( 'admin_init', array( $this, 'lazy_add_hooks' ) );
56
- add_action( 'admin_init', array( $this, 'register' ) );
57
- add_action( 'init', array( $this, 'listen_for_actions' ) );
58
- add_action( 'admin_menu', array( $this, 'menu' ) );
59
- add_action( 'admin_notices', array( $this, 'notices' ) );
60
- add_action( 'save_post_boxzilla-box', array( $this, 'save_box_options' ), 20, 2 );
61
- add_action( 'trashed_post', array( $this, 'flush_rules' ) );
62
- add_action( 'untrashed_post', array( $this, 'flush_rules' ) );
63
-
64
- $this->review_notice->add_hooks();
65
- }
66
-
67
- /**
68
- * Listen for admin actions.
69
- */
70
- public function listen_for_actions() {
71
-
72
- // triggered?
73
- $vars = array_merge( $_POST, $_GET );
74
- if( empty( $vars['_boxzilla_admin_action'] ) ) {
75
- return false;
76
- }
77
-
78
- // authorized?
79
- if( ! current_user_can( 'edit_posts' ) ) {
80
- return false;
81
- }
82
-
83
- // fire action
84
- $action = $vars['_boxzilla_admin_action'];
85
- do_action( 'boxzilla_admin_' . $action );
86
-
87
- return true;
88
- }
89
-
90
- /**
91
- * All logic for admin notices lives here.
92
- */
93
- public function notices() {
94
- global $pagenow,
95
- $current_screen;
96
-
97
- if( ( $pagenow === 'plugins.php' || ( $current_screen && $current_screen->post_type === 'boxzilla-box' ) )
98
- && current_user_can( 'install_plugins' )
99
- && is_plugin_active( 'scroll-triggered-boxes/index.php' ) ) {
100
-
101
- $url = wp_nonce_url( 'plugins.php?action=deactivate&plugin=scroll-triggered-boxes/index.php', 'deactivate-plugin_' . 'scroll-triggered-boxes/index.php' );
102
- ?>
103
- <div class="notice notice-info">
104
- <p><?php printf( __( 'Awesome, you are using Boxzilla! You can now safely <a href="%s">deactivate the Scroll Triggered Boxes plugin</a>.', 'boxzilla' ), $url ); ?></p>
105
- </div>
106
- <?php
107
- }
108
-
109
- }
110
-
111
- /**
112
- * Checks current version against stored version & runs necessary update routines.
113
- *
114
- * @return bool
115
- */
116
- protected function run_migrations() {
117
-
118
- // Only run if db option is at older version than code constant
119
- $previous_version = get_option( 'boxzilla_version', '0' );
120
- $current_version = $this->plugin->version();
121
-
122
- if( version_compare( $current_version, $previous_version, '<=' ) ) {
123
- return false;
124
- }
125
-
126
- $upgrade_routines = new Migrations( $previous_version, $current_version, __DIR__ . '/migrations' );
127
- $upgrade_routines->run();
128
- update_option( 'boxzilla_version', $current_version );
129
- }
130
-
131
- public function lazy_add_hooks() {
132
- global $pagenow;
133
-
134
- add_action( 'admin_enqueue_scripts', array( $this, 'load_assets' ) );
135
- add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
136
- add_filter( 'tiny_mce_before_init', array( $this, 'tinymce_init' ) );
137
- add_filter( 'manage_edit-boxzilla-box_columns', array( $this, 'post_type_column_titles' ) );
138
- add_action( 'manage_boxzilla-box_posts_custom_column', array( $this, 'post_type_column_content' ), 10, 2 );
139
- add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ) );
140
-
141
- if ( $pagenow === 'plugins.php' ) {
142
- add_filter( 'plugin_action_links', array( $this, 'add_plugin_settings_link' ), 10, 2 );
143
- add_filter( 'plugin_row_meta', array( $this, 'add_plugin_meta_links' ), 10, 2 );
144
- }
145
- }
146
-
147
- /**
148
- * @param $post_id
149
- */
150
- public function post_type_column_box_id_content( $post_id ) {
151
- echo $post_id;
152
- }
153
-
154
- /**
155
- * @param $column
156
- * @param $post_id
157
- */
158
- public function post_type_column_content( $column, $post_id ) {
159
- $method_name = 'post_type_column_' . $column . '_content';
160
- if ( method_exists( $this, $method_name ) ) {
161
- call_user_func( array( $this, $method_name ), $post_id );
162
- }
163
- }
164
-
165
- /**
166
- * @param $columns
167
- *
168
- * @return mixed
169
- */
170
- public function post_type_column_titles( $columns ) {
171
- $columns = self::array_insert( $columns, array(
172
- 'box_id' => __( 'Box ID', 'boxzilla' )
173
- ), 1 );
174
-
175
- $columns['title'] = __( 'Box Title', 'boxzilla' );
176
-
177
- return $columns;
178
- }
179
-
180
- /**
181
- * Register stuffs
182
- */
183
- public function register() {
184
-
185
- // register settings
186
- register_setting( 'boxzilla_settings', 'boxzilla_settings', array( $this, 'sanitize_settings' ) );
187
-
188
- // register scripts
189
- $pre_suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
190
-
191
- wp_register_script( 'boxzilla-admin', $this->plugin->url( '/assets/js/admin-script' . $pre_suffix . '.js' ), array(
192
- 'jquery',
193
- 'wp-util',
194
- 'wp-color-picker',
195
- 'suggest'
196
- ), $this->plugin->version(), true );
197
-
198
- // load stylesheets
199
- wp_register_style( 'boxzilla-admin', $this->plugin->url( '/assets/css/admin-styles' . $pre_suffix . '.css' ), array(), $this->plugin->version() );
200
- }
201
-
202
- /**
203
- * Renders the STB Menu items
204
- */
205
- public function menu() {
206
-
207
- $menu_items = array(
208
- array(
209
- __( 'Settings', 'boxzilla' ),
210
- __( 'Settings', 'boxzilla' ),
211
- 'boxzilla-settings',
212
- array( $this, 'show_settings_page' )
213
- ),
214
- array(
215
- __( 'Extensions', 'boxzilla' ),
216
- '<span style="color: orange">' . __( 'Extensions', 'boxzilla' ) . '</span>',
217
- 'boxzilla-extensions',
218
- array( $this, 'show_extensions_page' )
219
- )
220
- );
221
-
222
- $menu_items = apply_filters( 'boxzilla_admin_menu_items', $menu_items );
223
-
224
- foreach ( $menu_items as $item ) {
225
- add_submenu_page( 'edit.php?post_type=boxzilla-box', $item[0] . '- Boxzilla', $item[1], 'manage_options', $item[2], $item[3] );
226
- }
227
- }
228
-
229
- /**
230
- * Shows the settings page
231
- */
232
- public function show_settings_page() {
233
- $opts = $this->boxzilla->options;
234
- require __DIR__ . '/views/settings.php';
235
- }
236
-
237
- /**
238
- * Shows the extensions page
239
- */
240
- public function show_extensions_page() {
241
- $extensions = $this->fetch_extensions();
242
- require __DIR__ . '/views/extensions.php';
243
- }
244
-
245
- /**
246
- * Are we currently editing a box?
247
- *
248
- * @return bool
249
- */
250
- protected function on_edit_box_page() {
251
- global $pagenow;
252
-
253
- if ( ! in_array( $pagenow, array( 'post-new.php', 'post.php' ) ) ) {
254
- return false;
255
- }
256
-
257
- if ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'boxzilla-box' ) {
258
- return true;
259
- }
260
-
261
- if ( get_post_type() === 'boxzilla-box' ) {
262
- return true;
263
- }
264
-
265
- return false;
266
- }
267
-
268
- /**
269
- * @param $args
270
- *
271
- * @return mixed
272
- */
273
- public function tinymce_init( $args ) {
274
-
275
- // only act on our post type
276
- if ( get_post_type() !== 'boxzilla-box' ) {
277
- return $args;
278
- }
279
-
280
- $args['setup'] = 'function( editor ) { if(typeof(window.Boxzilla_Admin) === \'undefined\') { return; } editor.on("PreInit", window.Boxzilla_Admin.Designer.init ); }';
281
-
282
- return $args;
283
- }
284
-
285
- /**
286
- * Load plugin assets
287
- */
288
- public function load_assets() {
289
-
290
- $screen = get_current_screen();
291
-
292
- if ( ! $screen instanceof WP_Screen ) {
293
- return false;
294
- }
295
-
296
- if ( $screen->base === 'edit' && $screen->post_type === 'boxzilla-box' ) {
297
- wp_enqueue_style( 'boxzilla-admin' );
298
- }
299
-
300
- if ( $screen->base === 'post' && $screen->post_type === 'boxzilla-box' ) {
301
- // color picker
302
- wp_enqueue_style( 'wp-color-picker' );
303
-
304
- // load scripts
305
- wp_enqueue_script( 'boxzilla-admin' );
306
-
307
- $data = array(
308
- 'and' => __( 'and', 'boxzilla' ),
309
- 'or' => __( 'or', 'boxzilla' ),
310
- 'enterCommaSeparatedValues' => __( 'Enter a comma-separated list of values.', 'boxzilla' ),
311
- 'enterCommaSeparatedPosts' => __( "Enter a comma-separated list of post slugs or post ID's..", 'boxzilla' ),
312
- 'enterCommaSeparatedPages' => __( "Enter a comma-separated list of page slugs or page ID's..", 'boxzilla' ),
313
- 'enterCommaSeparatedPostTypes' => __( "Enter a comma-separated list of post types..", 'boxzilla' ),
314
- 'enterCommaSeparatedRelativeUrls' => __( "Enter a comma-separated list of relative URL's, eg /contact/", 'boxzilla' ),
315
- );
316
- wp_localize_script( 'boxzilla-admin' ,'boxzilla_i18n', $data );
317
-
318
- // load stylesheets
319
- wp_enqueue_style( 'boxzilla-admin' );
320
-
321
- // allow add-ons to easily load their own scripts or stylesheets
322
- do_action( 'boxzilla_load_admin_assets' );
323
- }
324
-
325
- if ( isset( $_GET['page'] ) && $_GET['page'] === 'boxzilla-settings' ) {
326
- wp_enqueue_style( 'boxzilla-admin' );
327
- }
328
-
329
- }
330
-
331
- /**
332
- * Register meta boxes
333
- *
334
- * @param string $post_type
335
- *
336
- * @return bool
337
- */
338
- public function add_meta_boxes( $post_type ) {
339
-
340
- if ( $post_type !== 'boxzilla-box' ) {
341
- return false;
342
- }
343
-
344
- add_meta_box(
345
- 'boxzilla-box-appearance-controls',
346
- __( 'Box Appearance', 'boxzilla' ),
347
- array( $this, 'metabox_box_appearance_controls' ),
348
- 'boxzilla-box',
349
- 'normal',
350
- 'core'
351
- );
352
-
353
- add_meta_box(
354
- 'boxzilla-box-options-controls',
355
- __( 'Box Options', 'boxzilla' ),
356
- array( $this, 'metabox_box_option_controls' ),
357
- 'boxzilla-box',
358
- 'normal',
359
- 'core'
360
- );
361
-
362
- add_meta_box(
363
- 'boxzilla-support',
364
- __( 'Looking for help?', 'boxzilla' ),
365
- array( $this, 'metabox_support' ),
366
- 'boxzilla-box',
367
- 'side'
368
- );
369
-
370
- add_meta_box(
371
- 'boxzilla-email-optin',
372
- __( 'Subscribe to our newsletter', 'boxzilla' ),
373
- array( $this, 'metabox_email_optin' ),
374
- 'boxzilla-box',
375
- 'side'
376
- );
377
-
378
- return true;
379
- }
380
-
381
- /**
382
- * @param \WP_Post $post
383
- * @param $metabox
384
- */
385
- public function metabox_box_appearance_controls( \WP_Post $post, $metabox ) {
386
-
387
- // get box options
388
- $box = new Box( $post );
389
- $opts = $box->get_options();
390
-
391
- // include view
392
- include __DIR__ . '/views/metaboxes/box-appearance-controls.php';
393
- }
394
-
395
- /**
396
- * @param \WP_Post $post
397
- * @param $metabox
398
- */
399
- public function metabox_box_option_controls( \WP_Post $post, $metabox ) {
400
-
401
- // get box options
402
- $box = new Box( $post );
403
- $opts = $box->get_options();
404
- $global_opts = $this->boxzilla->options;
405
-
406
- if ( empty( $opts['rules'] ) ) {
407
- $opts['rules'][] = array( 'condition' => '', 'qualifier' => 1, 'value' => '' );
408
- }
409
-
410
- // include view
411
- include __DIR__ . '/views/metaboxes/box-option-controls.php';
412
- }
413
-
414
- /**
415
- * @param \WP_Post $post
416
- * @param $metabox
417
- */
418
- public function metabox_email_optin( \WP_Post $post, $metabox ) {
419
- include __DIR__ . '/views/metaboxes/email-optin.php';
420
- }
421
-
422
- /**
423
- * @param \WP_Post $post
424
- * @param $metabox
425
- */
426
- public function metabox_support( \WP_Post $post, $metabox ) {
427
- include __DIR__ . '/views/metaboxes/need-help.php';
428
- }
429
-
430
-
431
- /**
432
- * Saves box options and rules
433
- *
434
- * @param int $box_id
435
- *
436
- * @return bool
437
- */
438
- public function save_box_options( $box_id, $post ) {
439
-
440
- // Only act on our own post type
441
- if ( $post->post_type !== 'boxzilla-box' ) {
442
- return false;
443
- }
444
-
445
- // is this a revision save?
446
- if ( wp_is_post_revision( $box_id ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
447
- return false;
448
- }
449
-
450
- // can user edit this post?
451
- if ( ! current_user_can( 'edit_post', $box_id ) ) {
452
- return false;
453
- }
454
-
455
- // make sure options array is set
456
- if ( ! isset( $_POST['boxzilla_box'] ) || ! is_array( $_POST['boxzilla_box'] ) ) {
457
- return false;
458
- }
459
-
460
- // get new options from $_POST
461
- $opts = $this->sanitize_box_options( $_POST['boxzilla_box'] );
462
-
463
- // allow extensions to filter the saved options
464
- $opts = apply_filters( 'boxzilla_saved_options', $opts, $box_id );
465
-
466
- // save individual box settings
467
- update_post_meta( $box_id, 'boxzilla_options', $opts );
468
-
469
- // update global settings if given
470
- if( ! empty( $_POST['boxzilla_global_settings'] ) ) {
471
- $global_settings = get_option( 'boxzilla_settings', array() );
472
- if( ! is_array( $global_settings ) ) { $global_settings = array(); }
473
- $global_settings = array_merge( $global_settings, $_POST['boxzilla_global_settings'] );
474
- update_option( 'boxzilla_settings', $global_settings );
475
- }
476
-
477
- $this->flush_rules( $box_id );
478
-
479
- return true;
480
- }
481
-
482
- /**
483
- * @param array $opts
484
- *
485
- * @return array
486
- */
487
- public function sanitize_settings( $opts ) {
488
- return $opts;
489
- }
490
-
491
- /**
492
- * @param string $url_string
493
- *
494
- * @return string
495
- */
496
- public function sanitize_url( $url_string ) {
497
-
498
- // if empty, just return a slash
499
- if( empty( $url_string ) ) {
500
- return '/';
501
- }
502
-
503
- // if string looks like an absolute URL, extract just the path
504
- if( preg_match( '/^((https|http)?\:\/\/)?(\w+\.)?\w+\.\w+\.*/i', $url_string ) ) {
505
-
506
- // make sure URL has scheme prepended, to make parse_url() understand..
507
- $url_string = 'https://' . str_replace( array( 'http://', 'https://' ), '', $url_string );
508
-
509
- // get just the path
510
- $url_string = parse_url( $url_string, PHP_URL_PATH );
511
- }
512
-
513
- // leading slash it
514
- return $url_string;
515
- }
516
-
517
- /**
518
- * @param array $rule
519
- * @return array The sanitized rule array
520
- */
521
- public function sanitize_box_rule( $rule) {
522
-
523
- $rule['value'] = trim( $rule['value'] );
524
-
525
- // convert to array
526
- $rule['value'] = explode( ',', trim( $rule['value'], ',' ) );
527
-
528
- // trim all whitespace in value field
529
- $rule['value'] = array_map( 'trim', $rule['value'] );
530
-
531
- // Make sure "is_url" values have a leading slash
532
- if ( $rule['condition'] === 'is_url' ) {
533
- $rule['value'] = array_map( array( $this, 'sanitize_url' ), $rule['value'] );
534
- }
535
-
536
- // (re)set value to 0 when condition is everywhere
537
- if ( $rule['condition'] === 'everywhere' ) {
538
- $rule['value'] = '';
539
- }
540
-
541
- // convert back to string before saving
542
- if ( is_array( $rule['value'] ) ) {
543
- $rule['value'] = join( ',', $rule['value'] );
544
- }
545
-
546
- return $rule;
547
- }
548
-
549
- /**
550
- * @param array $css
551
- * @return array
552
- */
553
- public function sanitize_box_css( $css ) {
554
-
555
- // sanitize settings
556
- if ( '' !== $css['width'] ) {
557
- $css['width'] = absint( $css['width'] );
558
- }
559
-
560
- if ( '' !== $css['border_width'] ) {
561
- $css['border_width'] = absint( $css['border_width'] );
562
- }
563
-
564
- // make sure colors start with `#`
565
- $color_keys = array( 'color', 'background_color', 'border_color' );
566
- foreach ( $color_keys as $key ) {
567
- $value = $css[ $key ];
568
- $color = sanitize_text_field( $value );
569
-
570
- // make sure color starts with `#`
571
- if ( '' !== $color && $color[0] !== '#' ) {
572
- $color = '#' . $color;
573
- }
574
- $css[ $key ] = $color;
575
- }
576
-
577
- return $css;
578
- }
579
-
580
- /**
581
- * Sanitize the options for this box.
582
- *
583
- * @param array $opts
584
- *
585
- * @return array
586
- */
587
- protected function sanitize_box_options( $opts ) {
588
-
589
- static $defaults = array(
590
- 'rules' => array(),
591
- 'css' => array()
592
- );
593
-
594
- $opts = array_replace_recursive( $defaults, $opts );
595
-
596
- $opts['rules'] = array_map( array( $this, 'sanitize_box_rule' ), $opts['rules'] );
597
- $opts['css'] = $this->sanitize_box_css( $opts['css'] );
598
- $opts['cookie']['triggered'] = absint( $opts['cookie']['triggered'] );
599
- $opts['cookie']['dismissed'] = absint( $opts['cookie']['dismissed'] );
600
- $opts['trigger'] = sanitize_text_field( $opts['trigger'] );
601
- $opts['trigger_percentage'] = absint( $opts['trigger_percentage'] );
602
- $opts['trigger_element'] = sanitize_text_field( $opts['trigger_element'] );
603
- $opts['screen_size_condition']['value'] = intval( $opts['screen_size_condition']['value'] );
604
-
605
- return $opts;
606
- }
607
-
608
- /**
609
- * Add the settings link to the Plugins overview
610
- *
611
- * @param array $links
612
- * @param string $slug
613
- *
614
- * @return array
615
- */
616
- public function add_plugin_settings_link( $links, $slug ) {
617
- if ( $slug !== $this->plugin->slug() || ! is_array( $links ) ) {
618
- return $links;
619
- }
620
-
621
- $settings_link = '<a href="' . admin_url( 'edit.php?post_type=boxzilla-box' ) . '">' . __( 'Boxes' ) . '</a>';
622
- array_unshift( $links, $settings_link );
623
-
624
- return $links;
625
- }
626
-
627
- /**
628
- * Adds meta links to the plugin in the WP Admin > Plugins screen
629
- *
630
- * @param array $links
631
- * @param string $slug
632
- *
633
- * @return array
634
- */
635
- public function add_plugin_meta_links( $links, $slug ) {
636
- if ( $slug !== $this->plugin->slug() ) {
637
- return $links;
638
- }
639
-
640
- $links[] = '<a href="https://kb.boxzillaplugin.com/#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=plugins-page">Documentation</a>';
641
- $links[] = '<a href="https://boxzillaplugin.com/add-ons/#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=plugins-page">Add-ons</a>';
642
-
643
- return $links;
644
- }
645
-
646
- /**
647
- * Flush all box rules
648
- *
649
- * Loops through all published boxes and fills the rules option
650
- *
651
- * @param int $post_id
652
- */
653
- public function flush_rules( $post_id ) {
654
-
655
- // only act on our own post type
656
- $post = get_post( $post_id );
657
- if ( ! $post instanceof WP_Post || $post->post_type !== 'boxzilla-box' ) {
658
- return;
659
- }
660
-
661
- // get all published boxes
662
- $boxes = get_posts(
663
- array(
664
- 'post_type' => 'boxzilla-box',
665
- 'post_status' => 'publish',
666
- 'numberposts' => - 1
667
- )
668
- );
669
-
670
- // setup empty array of rules
671
- $rules = array();
672
-
673
- // fill rules array
674
- if ( is_array( $boxes ) ) {
675
-
676
- foreach ( $boxes as $box ) {
677
- // get box meta data
678
- $box_meta = get_post_meta( $box->ID, 'boxzilla_options', true );
679
-
680
- // add box rules to all rules
681
- $rules[ $box->ID ] = $box_meta['rules'];
682
- $rules[ $box->ID ]['comparision'] = isset( $box_meta['rules_comparision'] ) ? $box_meta['rules_comparision'] : 'any';
683
-
684
- }
685
-
686
- }
687
-
688
- update_option( 'boxzilla_rules', $rules );
689
- }
690
-
691
- /**
692
- * Fetches a list of available add-on plugins
693
- *
694
- * @return array
695
- */
696
- protected function fetch_extensions() {
697
-
698
- $extensions = get_transient( 'boxzilla_remote_extensions' );
699
- if ( $extensions ) {
700
- return $extensions;
701
- }
702
- $request = wp_remote_get( 'https://api.boxzillaplugin.com/v1/plugins' );
703
-
704
- if ( is_wp_error( $request ) ) {
705
- return array();
706
- }
707
-
708
- $response = wp_remote_retrieve_body( $request );
709
- $response = json_decode( $response );
710
-
711
- if ( is_array( $response->data ) ) {
712
- set_transient( 'boxzilla_remote_extensions', $response->data, 24 * HOUR_IN_SECONDS );
713
-
714
- return $response->data;
715
- }
716
-
717
- return array();
718
- }
719
-
720
- /**
721
- * @param $arr
722
- * @param $insert
723
- * @param $position
724
- *
725
- * @return array
726
- */
727
- public static function array_insert( $arr, $insert, $position ) {
728
- $i = 0;
729
- $ret = array();
730
- foreach ( $arr as $key => $value ) {
731
- if ( $i == $position ) {
732
- foreach ( $insert as $ikey => $ivalue ) {
733
- $ret[ $ikey ] = $ivalue;
734
- }
735
- }
736
- $ret[ $key ] = $value;
737
- $i ++;
738
- }
739
-
740
- return $ret;
741
- }
742
-
743
- /**
744
- * @param string $text
745
- *
746
- * @return string
747
- */
748
- public function admin_footer_text( $text ) {
749
- $screen = get_current_screen();
750
-
751
- if ( ! $screen instanceof WP_Screen ) {
752
- return $text;
753
- }
754
-
755
- $on_edit_page = $screen->parent_base === 'edit' && $screen->post_type === 'boxzilla-box';
756
- if ( $on_edit_page ) {
757
- return sprintf( 'If you enjoy using <strong>Boxzilla</strong>, please <a href="%s" target="_blank">leave us a ★★★★★ rating</a>. A <strong style="text-decoration: underline;">huge</strong> thank you in advance!', 'https://wordpress.org/support/view/plugin-reviews/boxzilla?rate=5#postform' );
758
- }
759
-
760
- return $text;
761
- }
762
-
763
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Boxzilla\Admin;
4
+
5
+ use Boxzilla\Plugin;
6
+ use Boxzilla\Box;
7
+ use Boxzilla\Boxzilla;
8
+ use WP_Post;
9
+ use WP_Screen;
10
+
11
+ class Admin
12
+ {
13
+
14
+ /**
15
+ * @var Plugin $plugin
16
+ */
17
+ private $plugin;
18
+
19
+ /**
20
+ * @var Boxzilla
21
+ */
22
+ protected $boxzilla;
23
+
24
+ /**
25
+ * @var ReviewNotice
26
+ */
27
+ protected $review_notice;
28
+
29
+ /**
30
+ * @param Plugin $plugin
31
+ * @param Boxzilla $boxzilla
32
+ */
33
+ public function __construct(Plugin $plugin, Boxzilla $boxzilla)
34
+ {
35
+ $this->plugin = $plugin;
36
+ $this->boxzilla = $boxzilla;
37
+ $this->review_notice = new ReviewNotice();
38
+ }
39
+
40
+ /**
41
+ * Initialise the all admin related stuff
42
+ */
43
+ public function init()
44
+ {
45
+
46
+ // Load the plugin textdomain
47
+ load_plugin_textdomain('boxzilla', null, basename($this->plugin->dir()) . '/languages');
48
+
49
+ // action hooks
50
+ $this->add_hooks();
51
+ $this->run_migrations();
52
+ }
53
+
54
+ /**
55
+ * Add necessary hooks
56
+ */
57
+ protected function add_hooks()
58
+ {
59
+ add_action('admin_init', array( $this, 'lazy_add_hooks' ));
60
+ add_action('admin_init', array( $this, 'register' ));
61
+ add_action('init', array( $this, 'listen_for_actions' ));
62
+ add_action('admin_menu', array( $this, 'menu' ));
63
+ add_action('admin_notices', array( $this, 'notices' ));
64
+ add_action('save_post_boxzilla-box', array( $this, 'save_box_options' ), 20, 2);
65
+ add_action('trashed_post', array( $this, 'flush_rules' ));
66
+ add_action('untrashed_post', array( $this, 'flush_rules' ));
67
+ add_filter( 'bulk_actions-edit-boxzilla-box', array( $this, 'bulk_action_add') );
68
+ add_filter( 'handle_bulk_actions-edit-boxzilla-box', array( $this, 'bulk_action_handle' ), 10, 3 );
69
+
70
+ $this->review_notice->add_hooks();
71
+ }
72
+
73
+ /**
74
+ * Listen for admin actions.
75
+ */
76
+ public function listen_for_actions()
77
+ {
78
+ // triggered?
79
+ $vars = array_merge($_POST, $_GET);
80
+ if (empty($vars['_boxzilla_admin_action'])) {
81
+ return false;
82
+ }
83
+
84
+ // authorized?
85
+ if (! current_user_can('edit_posts')) {
86
+ return false;
87
+ }
88
+
89
+ // fire action
90
+ $action = $vars['_boxzilla_admin_action'];
91
+ do_action('boxzilla_admin_' . $action);
92
+
93
+ return true;
94
+ }
95
+
96
+ public function bulk_action_add( $bulk_actions ) {
97
+ $bulk_actions['boxzilla_duplicate_box'] = __( 'Duplicate box', 'boxzilla');
98
+ return $bulk_actions;
99
+ }
100
+
101
+ public function bulk_action_handle( $redirect_to, $doaction, $post_ids ) {
102
+ if ( $doaction !== 'boxzilla_duplicate_box' || empty( $post_ids )) {
103
+ return $redirect_to;
104
+ }
105
+
106
+ foreach ($post_ids as $post_id) {
107
+ $post = get_post($post_id);
108
+
109
+ $new_post_id = wp_insert_post(array(
110
+ 'post_type' => $post->post_type,
111
+ 'post_title' => $post->post_title,
112
+ 'post_content' => $post->post_content,
113
+ 'post_status' => 'draft',
114
+ ));
115
+
116
+ $options = get_post_meta($post_id, 'boxzilla_options', true);
117
+ add_post_meta($new_post_id, 'boxzilla_options', $options);
118
+ }
119
+
120
+ return $redirect_to;
121
+ }
122
+
123
+ /**
124
+ * All logic for admin notices lives here.
125
+ */
126
+ public function notices()
127
+ {
128
+ global $pagenow,
129
+ $current_screen;
130
+
131
+ if (($pagenow === 'plugins.php' || ($current_screen && $current_screen->post_type === 'boxzilla-box'))
132
+ && current_user_can('install_plugins')
133
+ && is_plugin_active('scroll-triggered-boxes/index.php')) {
134
+ $url = wp_nonce_url('plugins.php?action=deactivate&plugin=scroll-triggered-boxes/index.php', 'deactivate-plugin_' . 'scroll-triggered-boxes/index.php'); ?>
135
+ <div class="notice notice-info">
136
+ <p><?php printf(__('Awesome, you are using Boxzilla! You can now safely <a href="%s">deactivate the Scroll Triggered Boxes plugin</a>.', 'boxzilla'), $url); ?></p>
137
+ </div>
138
+ <?php
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Checks current version against stored version & runs necessary update routines.
144
+ *
145
+ * @return bool
146
+ */
147
+ protected function run_migrations()
148
+ {
149
+
150
+ // Only run if db option is at older version than code constant
151
+ $previous_version = get_option('boxzilla_version', '0');
152
+ $current_version = $this->plugin->version();
153
+
154
+ if (version_compare($current_version, $previous_version, '<=')) {
155
+ return false;
156
+ }
157
+
158
+ $upgrade_routines = new Migrations($previous_version, $current_version, __DIR__ . '/migrations');
159
+ $upgrade_routines->run();
160
+ update_option('boxzilla_version', $current_version);
161
+ }
162
+
163
+ public function lazy_add_hooks()
164
+ {
165
+ global $pagenow;
166
+
167
+ add_action('admin_enqueue_scripts', array( $this, 'load_assets' ));
168
+ add_action('add_meta_boxes', array( $this, 'add_meta_boxes' ));
169
+ add_filter('tiny_mce_before_init', array( $this, 'tinymce_init' ));
170
+ add_filter('manage_edit-boxzilla-box_columns', array( $this, 'post_type_column_titles' ));
171
+ add_action('manage_boxzilla-box_posts_custom_column', array( $this, 'post_type_column_content' ), 10, 2);
172
+ add_filter('admin_footer_text', array( $this, 'admin_footer_text' ));
173
+
174
+ if ($pagenow === 'plugins.php') {
175
+ add_filter('plugin_action_links', array( $this, 'add_plugin_settings_link' ), 10, 2);
176
+ add_filter('plugin_row_meta', array( $this, 'add_plugin_meta_links' ), 10, 2);
177
+ }
178
+ }
179
+
180
+ /**
181
+ * @param $post_id
182
+ */
183
+ public function post_type_column_box_id_content($post_id)
184
+ {
185
+ echo $post_id;
186
+ }
187
+
188
+ /**
189
+ * @param $column
190
+ * @param $post_id
191
+ */
192
+ public function post_type_column_content($column, $post_id)
193
+ {
194
+ $method_name = 'post_type_column_' . $column . '_content';
195
+ if (method_exists($this, $method_name)) {
196
+ call_user_func(array( $this, $method_name ), $post_id);
197
+ }
198
+ }
199
+
200
+ /**
201
+ * @param $columns
202
+ *
203
+ * @return mixed
204
+ */
205
+ public function post_type_column_titles($columns)
206
+ {
207
+ $columns = self::array_insert($columns, array(
208
+ 'box_id' => __('Box ID', 'boxzilla')
209
+ ), 1);
210
+
211
+ $columns['title'] = __('Box Title', 'boxzilla');
212
+
213
+ return $columns;
214
+ }
215
+
216
+ /**
217
+ * Register stuffs
218
+ */
219
+ public function register()
220
+ {
221
+
222
+ // register settings
223
+ register_setting('boxzilla_settings', 'boxzilla_settings', array( $this, 'sanitize_settings' ));
224
+
225
+ // register scripts
226
+ $pre_suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
227
+
228
+ wp_register_script('boxzilla-admin', $this->plugin->url('/assets/js/admin-script' . $pre_suffix . '.js'), array(
229
+ 'jquery',
230
+ 'wp-util',
231
+ 'wp-color-picker',
232
+ 'suggest'
233
+ ), $this->plugin->version(), true);
234
+
235
+ // load stylesheets
236
+ wp_register_style('boxzilla-admin', $this->plugin->url('/assets/css/admin-styles' . $pre_suffix . '.css'), array(), $this->plugin->version());
237
+ }
238
+
239
+ /**
240
+ * Renders the STB Menu items
241
+ */
242
+ public function menu()
243
+ {
244
+ $menu_items = array(
245
+ array(
246
+ __('Settings', 'boxzilla'),
247
+ __('Settings', 'boxzilla'),
248
+ 'boxzilla-settings',
249
+ array( $this, 'show_settings_page' )
250
+ ),
251
+ array(
252
+ __('Extensions', 'boxzilla'),
253
+ '<span style="color: orange">' . __('Extensions', 'boxzilla') . '</span>',
254
+ 'boxzilla-extensions',
255
+ array( $this, 'show_extensions_page' )
256
+ )
257
+ );
258
+
259
+ $menu_items = apply_filters('boxzilla_admin_menu_items', $menu_items);
260
+
261
+ foreach ($menu_items as $item) {
262
+ add_submenu_page('edit.php?post_type=boxzilla-box', $item[0] . '- Boxzilla', $item[1], 'manage_options', $item[2], $item[3]);
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Shows the settings page
268
+ */
269
+ public function show_settings_page()
270
+ {
271
+ $opts = $this->boxzilla->options;
272
+ require __DIR__ . '/views/settings.php';
273
+ }
274
+
275
+ /**
276
+ * Shows the extensions page
277
+ */
278
+ public function show_extensions_page()
279
+ {
280
+ $extensions = $this->fetch_extensions();
281
+ require __DIR__ . '/views/extensions.php';
282
+ }
283
+
284
+ /**
285
+ * Are we currently editing a box?
286
+ *
287
+ * @return bool
288
+ */
289
+ protected function on_edit_box_page()
290
+ {
291
+ global $pagenow;
292
+
293
+ if (! in_array($pagenow, array( 'post-new.php', 'post.php' ))) {
294
+ return false;
295
+ }
296
+
297
+ if (isset($_GET['post_type']) && $_GET['post_type'] === 'boxzilla-box') {
298
+ return true;
299
+ }
300
+
301
+ if (get_post_type() === 'boxzilla-box') {
302
+ return true;
303
+ }
304
+
305
+ return false;
306
+ }
307
+
308
+ /**
309
+ * @param $args
310
+ *
311
+ * @return mixed
312
+ */
313
+ public function tinymce_init($args)
314
+ {
315
+
316
+ // only act on our post type
317
+ if (get_post_type() !== 'boxzilla-box') {
318
+ return $args;
319
+ }
320
+
321
+ $args['setup'] = 'function( editor ) { if(typeof(window.Boxzilla_Admin) === \'undefined\') { return; } editor.on("PreInit", window.Boxzilla_Admin.Designer.init ); }';
322
+
323
+ return $args;
324
+ }
325
+
326
+ /**
327
+ * Load plugin assets
328
+ */
329
+ public function load_assets()
330
+ {
331
+ $screen = get_current_screen();
332
+
333
+ if (! $screen instanceof WP_Screen) {
334
+ return false;
335
+ }
336
+
337
+ if ($screen->base === 'edit' && $screen->post_type === 'boxzilla-box') {
338
+ wp_enqueue_style('boxzilla-admin');
339
+ }
340
+
341
+ if ($screen->base === 'post' && $screen->post_type === 'boxzilla-box') {
342
+ // color picker
343
+ wp_enqueue_style('wp-color-picker');
344
+
345
+ // load scripts
346
+ wp_enqueue_script('boxzilla-admin');
347
+
348
+ $data = array(
349
+ 'and' => __('and', 'boxzilla'),
350
+ 'or' => __('or', 'boxzilla'),
351
+ 'enterCommaSeparatedValues' => __('Enter a comma-separated list of values.', 'boxzilla'),
352
+ 'enterCommaSeparatedPosts' => __("Enter a comma-separated list of post slugs or post ID's..", 'boxzilla'),
353
+ 'enterCommaSeparatedPages' => __("Enter a comma-separated list of page slugs or page ID's..", 'boxzilla'),
354
+ 'enterCommaSeparatedPostTypes' => __("Enter a comma-separated list of post types..", 'boxzilla'),
355
+ 'enterCommaSeparatedRelativeUrls' => __("Enter a comma-separated list of relative URL's, eg /contact/", 'boxzilla'),
356
+ );
357
+ wp_localize_script('boxzilla-admin', 'boxzilla_i18n', $data);
358
+
359
+ // load stylesheets
360
+ wp_enqueue_style('boxzilla-admin');
361
+
362
+ // allow add-ons to easily load their own scripts or stylesheets
363
+ do_action('boxzilla_load_admin_assets');
364
+ }
365
+
366
+ if (isset($_GET['page']) && $_GET['page'] === 'boxzilla-settings') {
367
+ wp_enqueue_style('boxzilla-admin');
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Register meta boxes
373
+ *
374
+ * @param string $post_type
375
+ *
376
+ * @return bool
377
+ */
378
+ public function add_meta_boxes($post_type)
379
+ {
380
+ if ($post_type !== 'boxzilla-box') {
381
+ return false;
382
+ }
383
+
384
+ add_meta_box(
385
+ 'boxzilla-box-appearance-controls',
386
+ __('Box Appearance', 'boxzilla'),
387
+ array( $this, 'metabox_box_appearance_controls' ),
388
+ 'boxzilla-box',
389
+ 'normal',
390
+ 'core'
391
+ );
392
+
393
+ add_meta_box(
394
+ 'boxzilla-box-options-controls',
395
+ __('Box Options', 'boxzilla'),
396
+ array( $this, 'metabox_box_option_controls' ),
397
+ 'boxzilla-box',
398
+ 'normal',
399
+ 'core'
400
+ );
401
+
402
+ add_meta_box(
403
+ 'boxzilla-support',
404
+ __('Looking for help?', 'boxzilla'),
405
+ array( $this, 'metabox_support' ),
406
+ 'boxzilla-box',
407
+ 'side'
408
+ );
409
+
410
+ add_meta_box(
411
+ 'boxzilla-email-optin',
412
+ __('Subscribe to our newsletter', 'boxzilla'),
413
+ array( $this, 'metabox_email_optin' ),
414
+ 'boxzilla-box',
415
+ 'side'
416
+ );
417
+
418
+ return true;
419
+ }
420
+
421
+ /**
422
+ * @param \WP_Post $post
423
+ * @param $metabox
424
+ */
425
+ public function metabox_box_appearance_controls(\WP_Post $post, $metabox)
426
+ {
427
+
428
+ // get box options
429
+ $box = new Box($post);
430
+ $opts = $box->get_options();
431
+
432
+ // include view
433
+ include __DIR__ . '/views/metaboxes/box-appearance-controls.php';
434
+ }
435
+
436
+ /**
437
+ * @param \WP_Post $post
438
+ * @param $metabox
439
+ */
440
+ public function metabox_box_option_controls(\WP_Post $post, $metabox)
441
+ {
442
+
443
+ // get box options
444
+ $box = new Box($post);
445
+ $opts = $box->get_options();
446
+ $global_opts = $this->boxzilla->options;
447
+
448
+ if (empty($opts['rules'])) {
449
+ $opts['rules'][] = array( 'condition' => '', 'qualifier' => 1, 'value' => '' );
450
+ }
451
+
452
+ // include view
453
+ include __DIR__ . '/views/metaboxes/box-option-controls.php';
454
+ }
455
+
456
+ /**
457
+ * @param \WP_Post $post
458
+ * @param $metabox
459
+ */
460
+ public function metabox_email_optin(\WP_Post $post, $metabox)
461
+ {
462
+ include __DIR__ . '/views/metaboxes/email-optin.php';
463
+ }
464
+
465
+ /**
466
+ * @param \WP_Post $post
467
+ * @param $metabox
468
+ */
469
+ public function metabox_support(\WP_Post $post, $metabox)
470
+ {
471
+ include __DIR__ . '/views/metaboxes/need-help.php';
472
+ }
473
+
474
+
475
+ /**
476
+ * Saves box options and rules
477
+ *
478
+ * @param int $box_id
479
+ *
480
+ * @return bool
481
+ */
482
+ public function save_box_options($box_id, $post)
483
+ {
484
+
485
+ // Only act on our own post type
486
+ if ($post->post_type !== 'boxzilla-box') {
487
+ return false;
488
+ }
489
+
490
+ // is this a revision save?
491
+ if (wp_is_post_revision($box_id) || (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)) {
492
+ return false;
493
+ }
494
+
495
+ // can user edit this post?
496
+ if (! current_user_can('edit_post', $box_id)) {
497
+ return false;
498
+ }
499
+
500
+ // make sure options array is set
501
+ if (! isset($_POST['boxzilla_box']) || ! is_array($_POST['boxzilla_box'])) {
502
+ return false;
503
+ }
504
+
505
+ // get new options from $_POST
506
+ $opts = $this->sanitize_box_options($_POST['boxzilla_box']);
507
+
508
+ // allow extensions to filter the saved options
509
+ $opts = apply_filters('boxzilla_saved_options', $opts, $box_id);
510
+
511
+ // save individual box settings
512
+ update_post_meta($box_id, 'boxzilla_options', $opts);
513
+
514
+ // update global settings if given
515
+ if (! empty($_POST['boxzilla_global_settings'])) {
516
+ $global_settings = get_option('boxzilla_settings', array());
517
+ if (! is_array($global_settings)) {
518
+ $global_settings = array();
519
+ }
520
+ $global_settings = array_merge($global_settings, $_POST['boxzilla_global_settings']);
521
+ update_option('boxzilla_settings', $global_settings);
522
+ }
523
+
524
+ $this->flush_rules($box_id);
525
+
526
+ return true;
527
+ }
528
+
529
+ /**
530
+ * @param array $opts
531
+ *
532
+ * @return array
533
+ */
534
+ public function sanitize_settings($opts)
535
+ {
536
+ return $opts;
537
+ }
538
+
539
+ /**
540
+ * @param string $url_string
541
+ *
542
+ * @return string
543
+ */
544
+ public function sanitize_url($url_string)
545
+ {
546
+
547
+ // if empty, just return a slash
548
+ if (empty($url_string)) {
549
+ return '/';
550
+ }
551
+
552
+ // if string looks like an absolute URL, extract just the path
553
+ if (preg_match('/^((https|http)?\:\/\/)?(\w+\.)?\w+\.\w+\.*/i', $url_string)) {
554
+
555
+ // make sure URL has scheme prepended, to make parse_url() understand..
556
+ $url_string = 'https://' . str_replace(array( 'http://', 'https://' ), '', $url_string);
557
+
558
+ // get just the path
559
+ $url_string = parse_url($url_string, PHP_URL_PATH);
560
+ }
561
+
562
+ // leading slash it
563
+ return $url_string;
564
+ }
565
+
566
+ /**
567
+ * @param array $rule
568
+ * @return array The sanitized rule array
569
+ */
570
+ public function sanitize_box_rule($rule)
571
+ {
572
+ $rule['value'] = trim($rule['value']);
573
+
574
+ // convert to array
575
+ $rule['value'] = explode(',', trim($rule['value'], ','));
576
+
577
+ // trim all whitespace in value field
578
+ $rule['value'] = array_map('trim', $rule['value']);
579
+
580
+ // Make sure "is_url" values have a leading slash
581
+ if ($rule['condition'] === 'is_url') {
582
+ $rule['value'] = array_map(array( $this, 'sanitize_url' ), $rule['value']);
583
+ }
584
+
585
+ // (re)set value to 0 when condition is everywhere
586
+ if ($rule['condition'] === 'everywhere') {
587
+ $rule['value'] = '';
588
+ }
589
+
590
+ // convert back to string before saving
591
+ if (is_array($rule['value'])) {
592
+ $rule['value'] = join(',', $rule['value']);
593
+ }
594
+
595
+ return $rule;
596
+ }
597
+
598
+ /**
599
+ * @param array $css
600
+ * @return array
601
+ */
602
+ public function sanitize_box_css($css)
603
+ {
604
+
605
+ // sanitize settings
606
+ if ('' !== $css['width']) {
607
+ $css['width'] = absint($css['width']);
608
+ }
609
+
610
+ if ('' !== $css['border_width']) {
611
+ $css['border_width'] = absint($css['border_width']);
612
+ }
613
+
614
+ // make sure colors start with `#`
615
+ $color_keys = array( 'color', 'background_color', 'border_color' );
616
+ foreach ($color_keys as $key) {
617
+ $value = $css[ $key ];
618
+ $color = sanitize_text_field($value);
619
+
620
+ // make sure color starts with `#`
621
+ if ('' !== $color && $color[0] !== '#') {
622
+ $color = '#' . $color;
623
+ }
624
+ $css[ $key ] = $color;
625
+ }
626
+
627
+ return $css;
628
+ }
629
+
630
+ /**
631
+ * Sanitize the options for this box.
632
+ *
633
+ * @param array $opts
634
+ *
635
+ * @return array
636
+ */
637
+ protected function sanitize_box_options($opts)
638
+ {
639
+ $defaults = array(
640
+ 'rules' => array(),
641
+ 'css' => array()
642
+ );
643
+
644
+ $opts = array_replace_recursive($defaults, $opts);
645
+
646
+ $opts['rules'] = array_map(array( $this, 'sanitize_box_rule' ), $opts['rules']);
647
+ $opts['css'] = $this->sanitize_box_css($opts['css']);
648
+ $opts['cookie']['triggered'] = absint($opts['cookie']['triggered']);
649
+ $opts['cookie']['dismissed'] = absint($opts['cookie']['dismissed']);
650
+ $opts['trigger'] = sanitize_text_field($opts['trigger']);
651
+ $opts['trigger_percentage'] = absint($opts['trigger_percentage']);
652
+ $opts['trigger_element'] = sanitize_text_field($opts['trigger_element']);
653
+ $opts['screen_size_condition']['value'] = intval($opts['screen_size_condition']['value']);
654
+
655
+ return $opts;
656
+ }
657
+
658
+ /**
659
+ * Add the settings link to the Plugins overview
660
+ *
661
+ * @param array $links
662
+ * @param string $slug
663
+ *
664
+ * @return array
665
+ */
666
+ public function add_plugin_settings_link($links, $slug)
667
+ {
668
+ if ($slug !== $this->plugin->slug() || ! is_array($links)) {
669
+ return $links;
670
+ }
671
+
672
+ $settings_link = '<a href="' . admin_url('edit.php?post_type=boxzilla-box') . '">' . __('Boxes') . '</a>';
673
+ array_unshift($links, $settings_link);
674
+
675
+ return $links;
676
+ }
677
+
678
+ /**
679
+ * Adds meta links to the plugin in the WP Admin > Plugins screen
680
+ *
681
+ * @param array $links
682
+ * @param string $slug
683
+ *
684
+ * @return array
685
+ */
686
+ public function add_plugin_meta_links($links, $slug)
687
+ {
688
+ if ($slug !== $this->plugin->slug()) {
689
+ return $links;
690
+ }
691
+
692
+ $links[] = '<a href="https://kb.boxzillaplugin.com/#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=plugins-page">Documentation</a>';
693
+ $links[] = '<a href="https://boxzillaplugin.com/add-ons/#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=plugins-page">Add-ons</a>';
694
+
695
+ return $links;
696
+ }
697
+
698
+ /**
699
+ * Flush all box rules
700
+ *
701
+ * Loops through all published boxes and fills the rules option
702
+ *
703
+ * @param int $post_id
704
+ */
705
+ public function flush_rules($post_id)
706
+ {
707
+
708
+ // only act on our own post type
709
+ $post = get_post($post_id);
710
+ if (! $post instanceof WP_Post || $post->post_type !== 'boxzilla-box') {
711
+ return;
712
+ }
713
+
714
+ // get all published boxes
715
+ $boxes = get_posts(
716
+ array(
717
+ 'post_type' => 'boxzilla-box',
718
+ 'post_status' => 'publish',
719
+ 'numberposts' => - 1
720
+ )
721
+ );
722
+
723
+ // setup empty array of rules
724
+ $rules = array();
725
+
726
+ // fill rules array
727
+ if (is_array($boxes)) {
728
+ foreach ($boxes as $box) {
729
+ // get box meta data
730
+ $box_meta = get_post_meta($box->ID, 'boxzilla_options', true);
731
+
732
+ // add box rules to all rules
733
+ $rules[ $box->ID ] = $box_meta['rules'];
734
+ $rules[ $box->ID ]['comparision'] = isset($box_meta['rules_comparision']) ? $box_meta['rules_comparision'] : 'any';
735
+ }
736
+ }
737
+
738
+ update_option('boxzilla_rules', $rules);
739
+ }
740
+
741
+ /**
742
+ * Fetches a list of available add-on plugins
743
+ *
744
+ * @return array
745
+ */
746
+ protected function fetch_extensions()
747
+ {
748
+ $extensions = get_transient('boxzilla_remote_extensions');
749
+ if ($extensions) {
750
+ return $extensions;
751
+ }
752
+
753
+ $response = wp_remote_get('https://my.boxzillaplugin.com/api/v2/plugins');
754
+ if (is_wp_error($response) || wp_remote_retrieve_response_code($response) >= 400) {
755
+ return array();
756
+ }
757
+
758
+ $body = wp_remote_retrieve_body($response);
759
+ $data = json_decode($body);
760
+ if (is_array($data)) {
761
+ set_transient('boxzilla_remote_extensions', $data, 24 * HOUR_IN_SECONDS);
762
+ return $data;
763
+ }
764
+
765
+ return array();
766
+ }
767
+
768
+ /**
769
+ * @param $arr
770
+ * @param $insert
771
+ * @param $position
772
+ *
773
+ * @return array
774
+ */
775
+ public static function array_insert($arr, $insert, $position)
776
+ {
777
+ $i = 0;
778
+ $ret = array();
779
+ foreach ($arr as $key => $value) {
780
+ if ($i == $position) {
781
+ foreach ($insert as $ikey => $ivalue) {
782
+ $ret[ $ikey ] = $ivalue;
783
+ }
784
+ }
785
+ $ret[ $key ] = $value;
786
+ $i ++;
787
+ }
788
+
789
+ return $ret;
790
+ }
791
+
792
+ /**
793
+ * @param string $text
794
+ *
795
+ * @return string
796
+ */
797
+ public function admin_footer_text($text)
798
+ {
799
+ $screen = get_current_screen();
800
+
801
+ if (! $screen instanceof WP_Screen) {
802
+ return $text;
803
+ }
804
+
805
+ $on_edit_page = $screen->parent_base === 'edit' && $screen->post_type === 'boxzilla-box';
806
+ if ($on_edit_page) {
807
+ return sprintf('If you enjoy using <strong>Boxzilla</strong>, please <a href="%s" target="_blank">leave us a ★★★★★ rating</a>. A <strong style="text-decoration: underline;">huge</strong> thank you in advance!', 'https://wordpress.org/support/view/plugin-reviews/boxzilla?rate=5#postform');
808
+ }
809
+
810
+ return $text;
811
+ }
812
+ }
src/admin/class-autocomplete.php CHANGED
@@ -2,94 +2,100 @@
2
 
3
  namespace Boxzilla\Filter;
4
 
5
- class Autocomplete {
6
-
7
- public function add_hooks() {
8
- add_action( 'wp_ajax_boxzilla_autocomplete', array( $this, 'ajax' ) );
9
- }
10
-
11
- /**
12
- * AJAX listener for autocomplete
13
- */
14
- public function ajax() {
15
- $q = ( isset( $_GET['q'] ) ) ? sanitize_text_field( $_GET['q'] ) : '';
16
- $type = ( isset( $_GET['type'] ) && in_array( $_GET['type'], array( 'page', 'post', 'category', 'post_type', 'post_tag' ) ) ) ? $_GET['type'] : 'post';
17
-
18
- // do nothing if supplied 'q' parameter is omitted or empty
19
- // or less than 2 characters long
20
- if( empty( $q ) || strlen( $q ) < 2 ) {
21
- die();
22
- }
23
-
24
- switch( $type ) {
25
-
26
- default:
27
- case 'post':
28
- case 'page':
29
- echo $this->list_posts( $q, $type );
30
- break;
31
-
32
- case 'category':
33
- echo $this->list_categories( $q );
34
- break;
35
-
36
- case 'post_type':
37
- echo $this->list_post_types( $q );
38
- break;
39
-
40
- case 'post_tag':
41
- echo $this->list_tags( $q );
42
- break;
43
- }
44
-
45
- die();
46
- }
47
-
48
- /**
49
- * @param string $query
50
- * @param string $post_type
51
- *
52
- * @return string
53
- */
54
- protected function list_posts( $query, $post_type = 'post' ) {
55
- global $wpdb;
56
- $sql = $wpdb->prepare( "SELECT p.post_name FROM $wpdb->posts p WHERE p.post_type = '%s' AND p.post_status = 'publish' AND ( p.post_title LIKE '%s' OR p.post_name LIKE '%s' ) GROUP BY p.post_name", $post_type, $query . '%%', $query . '%%' );
57
- $post_slugs = $wpdb->get_col( $sql );
58
- return join( $post_slugs, PHP_EOL );
59
- }
60
-
61
- /**
62
- * @param string $query
63
- *
64
- * @return string
65
- */
66
- protected function list_categories( $query ) {
67
- $terms = get_terms( 'category', array( 'name__like' => $query, 'fields' => 'names', 'hide_empty' => false ) );
68
- return join( $terms, PHP_EOL );
69
- }
70
-
71
- /**
72
- * @param string $query
73
- *
74
- * @return string
75
- */
76
- protected function list_tags( $query ) {
77
- $terms = get_terms( 'post_tag', array( 'name__like' => $query, 'fields' => 'names', 'hide_empty' => false ) );
78
- return join( $terms, PHP_EOL );
79
- }
80
-
81
-
82
- /**
83
- * @param string $query
84
- *
85
- * @return string
86
- */
87
- protected function list_post_types( $query ) {
88
- $post_types = get_post_types( array( 'public' => true ), 'names' );
89
- $matched_post_types = array_filter( $post_types, function( $name ) use( $query ) {
90
- return strpos( $name, $query ) === 0;
91
- });
92
-
93
- return join( $matched_post_types, PHP_EOL );
94
- }
95
- }
 
 
 
 
 
 
2
 
3
  namespace Boxzilla\Filter;
4
 
5
+ class Autocomplete
6
+ {
7
+ public function add_hooks()
8
+ {
9
+ add_action('wp_ajax_boxzilla_autocomplete', array( $this, 'ajax' ));
10
+ }
11
+
12
+ /**
13
+ * AJAX listener for autocomplete
14
+ */
15
+ public function ajax()
16
+ {
17
+ $q = (isset($_GET['q'])) ? sanitize_text_field($_GET['q']) : '';
18
+ $type = (isset($_GET['type']) && in_array($_GET['type'], array( 'page', 'post', 'category', 'post_type', 'post_tag' ))) ? $_GET['type'] : 'post';
19
+
20
+ // do nothing if supplied 'q' parameter is omitted or empty
21
+ // or less than 2 characters long
22
+ if (empty($q) || strlen($q) < 2) {
23
+ die();
24
+ }
25
+
26
+ switch ($type) {
27
+
28
+ default:
29
+ case 'post':
30
+ case 'page':
31
+ echo $this->list_posts($q, $type);
32
+ break;
33
+
34
+ case 'category':
35
+ echo $this->list_categories($q);
36
+ break;
37
+
38
+ case 'post_type':
39
+ echo $this->list_post_types($q);
40
+ break;
41
+
42
+ case 'post_tag':
43
+ echo $this->list_tags($q);
44
+ break;
45
+ }
46
+
47
+ die();
48
+ }
49
+
50
+ /**
51
+ * @param string $query
52
+ * @param string $post_type
53
+ *
54
+ * @return string
55
+ */
56
+ protected function list_posts($query, $post_type = 'post')
57
+ {
58
+ global $wpdb;
59
+ $sql = $wpdb->prepare("SELECT p.post_name FROM $wpdb->posts p WHERE p.post_type = '%s' AND p.post_status = 'publish' AND ( p.post_title LIKE '%s' OR p.post_name LIKE '%s' ) GROUP BY p.post_name", $post_type, $query . '%%', $query . '%%');
60
+ $post_slugs = $wpdb->get_col($sql);
61
+ return join($post_slugs, PHP_EOL);
62
+ }
63
+
64
+ /**
65
+ * @param string $query
66
+ *
67
+ * @return string
68
+ */
69
+ protected function list_categories($query)
70
+ {
71
+ $terms = get_terms('category', array( 'name__like' => $query, 'fields' => 'names', 'hide_empty' => false ));
72
+ return join($terms, PHP_EOL);
73
+ }
74
+
75
+ /**
76
+ * @param string $query
77
+ *
78
+ * @return string
79
+ */
80
+ protected function list_tags($query)
81
+ {
82
+ $terms = get_terms('post_tag', array( 'name__like' => $query, 'fields' => 'names', 'hide_empty' => false ));
83
+ return join($terms, PHP_EOL);
84
+ }
85
+
86
+
87
+ /**
88
+ * @param string $query
89
+ *
90
+ * @return string
91
+ */
92
+ protected function list_post_types($query)
93
+ {
94
+ $post_types = get_post_types(array( 'public' => true ), 'names');
95
+ $matched_post_types = array_filter($post_types, function ($name) use ($query) {
96
+ return strpos($name, $query) === 0;
97
+ });
98
+
99
+ return join($matched_post_types, PHP_EOL);
100
+ }
101
+ }
src/admin/class-installer.php CHANGED
@@ -3,91 +3,96 @@
3
 
4
  namespace Boxzilla\Admin;
5
 
6
- class Installer {
7
-
8
- /**
9
- * Run the installer
10
- */
11
- public static function run() {
12
- $installer = new self;
13
- $installer->install();
14
- }
15
-
16
- /**
17
- * The main install method
18
- */
19
- public function install() {
20
-
21
- // don't install sample boxes on multisite
22
- if( is_multisite() ) {
23
- return;
24
- }
25
-
26
- $this->transfer_from_stb();
27
- $this->create_sample_box();
28
- }
29
-
30
- /**
31
- *
32
- */
33
- public function transfer_from_stb() {
34
- global $wpdb;
35
-
36
- // transfer post types
37
- $query = $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_type = %s WHERE post_type = %s", 'boxzilla-box', 'scroll-triggered-box' );
38
- $wpdb->query( $query );
39
-
40
- // transfer post meta
41
- $query = $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_key = %s WHERE meta_key = %s", 'boxzilla_options', 'stb_options' );
42
- $wpdb->query( $query );
43
-
44
- // transfer rules
45
- $query = $wpdb->prepare( "UPDATE {$wpdb->options} SET option_name = %s WHERE option_name = %s", 'boxzilla_rules', 'stb_rules' );
46
- $wpdb->query( $query );
47
- }
48
-
49
- /**
50
- * @return bool
51
- */
52
- protected function create_sample_box() {
53
-
54
- // only create sample box if no boxes were found
55
- $boxes = get_posts(
56
- array(
57
- 'post_type' => 'boxzilla-box',
58
- 'post_status' => array( 'publish', 'draft' )
59
- )
60
- );
61
-
62
- if( ! empty( $boxes ) ) {
63
- return false;
64
- }
65
-
66
- $box_id = wp_insert_post(
67
- array(
68
- 'post_type' => 'boxzilla-box',
69
- 'post_title' => "Sample Box",
70
- 'post_content' => "<h4>Hello world.</h4><p>This is a sample box, with some sample content in it.</p>",
71
- 'post_status' => 'draft',
72
- )
73
- );
74
-
75
- // set box settings
76
- $settings = array(
77
- 'css' => array(
78
- 'background_color' => '#edf9ff',
79
- 'color' => '',
80
- 'width' => '340',
81
- 'border_color' => '#dd7575',
82
- 'border_width' => '4',
83
- 'border_style' => 'dashed',
84
- 'position' => 'bottom-right',
85
- 'manual' => ''
86
- )
87
- );
88
-
89
- update_post_meta( $box_id, 'boxzilla_options', $settings );
90
-
91
- return true;
92
- }
93
- }
 
 
 
 
 
3
 
4
  namespace Boxzilla\Admin;
5
 
6
+ class Installer
7
+ {
8
+
9
+ /**
10
+ * Run the installer
11
+ */
12
+ public static function run()
13
+ {
14
+ $installer = new self;
15
+ $installer->install();
16
+ }
17
+
18
+ /**
19
+ * The main install method
20
+ */
21
+ public function install()
22
+ {
23
+
24
+ // don't install sample boxes on multisite
25
+ if (is_multisite()) {
26
+ return;
27
+ }
28
+
29
+ $this->transfer_from_stb();
30
+ $this->create_sample_box();
31
+ }
32
+
33
+ /**
34
+ *
35
+ */
36
+ public function transfer_from_stb()
37
+ {
38
+ global $wpdb;
39
+
40
+ // transfer post types
41
+ $query = $wpdb->prepare("UPDATE {$wpdb->posts} SET post_type = %s WHERE post_type = %s", 'boxzilla-box', 'scroll-triggered-box');
42
+ $wpdb->query($query);
43
+
44
+ // transfer post meta
45
+ $query = $wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_key = %s WHERE meta_key = %s", 'boxzilla_options', 'stb_options');
46
+ $wpdb->query($query);
47
+
48
+ // transfer rules
49
+ $query = $wpdb->prepare("UPDATE {$wpdb->options} SET option_name = %s WHERE option_name = %s", 'boxzilla_rules', 'stb_rules');
50
+ $wpdb->query($query);
51
+ }
52
+
53
+ /**
54
+ * @return bool
55
+ */
56
+ protected function create_sample_box()
57
+ {
58
+
59
+ // only create sample box if no boxes were found
60
+ $boxes = get_posts(
61
+ array(
62
+ 'post_type' => 'boxzilla-box',
63
+ 'post_status' => array( 'publish', 'draft' )
64
+ )
65
+ );
66
+
67
+ if (! empty($boxes)) {
68
+ return false;
69
+ }
70
+
71
+ $box_id = wp_insert_post(
72
+ array(
73
+ 'post_type' => 'boxzilla-box',
74
+ 'post_title' => "Sample Box",
75
+ 'post_content' => "<h4>Hello world.</h4><p>This is a sample box, with some sample content in it.</p>",
76
+ 'post_status' => 'draft',
77
+ )
78
+ );
79
+
80
+ // set box settings
81
+ $settings = array(
82
+ 'css' => array(
83
+ 'background_color' => '#edf9ff',
84
+ 'color' => '',
85
+ 'width' => '340',
86
+ 'border_color' => '#dd7575',
87
+ 'border_width' => '4',
88
+ 'border_style' => 'dashed',
89
+ 'position' => 'bottom-right',
90
+ 'manual' => ''
91
+ )
92
+ );
93
+
94
+ update_post_meta($box_id, 'boxzilla_options', $settings);
95
+
96
+ return true;
97
+ }
98
+ }
src/admin/class-migrations.php CHANGED
@@ -7,7 +7,8 @@ use Exception;
7
  /**
8
  *
9
  */
10
- class Migrations {
 
11
 
12
  /**
13
  * @var float
@@ -29,7 +30,8 @@ class Migrations {
29
  * @param float $to
30
  * @param string $migrations_dir
31
  */
32
- public function __construct( $from, $to, $migrations_dir ) {
 
33
  $this->version_from = $from;
34
  $this->version_to = $to;
35
  $this->migrations_dir = $migrations_dir;
@@ -38,36 +40,38 @@ class Migrations {
38
  /**
39
  * Run the various upgrade routines, all the way up to the latest version
40
  */
41
- public function run() {
 
42
  $migrations = $this->find_migrations();
43
  // run in sub-function for scope
44
- array_map( array( $this, 'run_migration' ), $migrations );
45
  }
46
 
47
  /**
48
  * @return array
49
  */
50
- public function find_migrations() {
51
- $files = glob( rtrim( $this->migrations_dir, '/' ) . '/*.php' );
 
52
  $migrations = array();
53
 
54
  // return empty array when glob returns non-array value.
55
- if( ! is_array( $files ) ) {
56
  return $migrations;
57
  }
58
 
59
- foreach( $files as $file ) {
60
- $migration = basename( $file );
61
- $parts = explode( '-', $migration );
62
  $version = $parts[0];
63
 
64
  // check if migration file is not for an even higher version
65
- if( version_compare( $version, $this->version_to, '>' ) ) {
66
  continue;
67
  }
68
 
69
  // check if we ran migration file before.
70
- if( version_compare( $this->version_from, $version, '>=' ) ) {
71
  continue;
72
  }
73
 
@@ -85,12 +89,12 @@ class Migrations {
85
  *
86
  * @throws Exception
87
  */
88
- protected function run_migration( $file ) {
89
-
90
- if( ! file_exists( $file ) ) {
91
- throw new Exception( "Migration file $file does not exist.");
92
  }
93
 
94
  include $file;
95
  }
96
- }
7
  /**
8
  *
9
  */
10
+ class Migrations
11
+ {
12
 
13
  /**
14
  * @var float
30
  * @param float $to
31
  * @param string $migrations_dir
32
  */
33
+ public function __construct($from, $to, $migrations_dir)
34
+ {
35
  $this->version_from = $from;
36
  $this->version_to = $to;
37
  $this->migrations_dir = $migrations_dir;
40
  /**
41
  * Run the various upgrade routines, all the way up to the latest version
42
  */
43
+ public function run()
44
+ {
45
  $migrations = $this->find_migrations();
46
  // run in sub-function for scope
47
+ array_map(array( $this, 'run_migration' ), $migrations);
48
  }
49
 
50
  /**
51
  * @return array
52
  */
53
+ public function find_migrations()
54
+ {
55
+ $files = glob(rtrim($this->migrations_dir, '/') . '/*.php');
56
  $migrations = array();
57
 
58
  // return empty array when glob returns non-array value.
59
+ if (! is_array($files)) {
60
  return $migrations;
61
  }
62
 
63
+ foreach ($files as $file) {
64
+ $migration = basename($file);
65
+ $parts = explode('-', $migration);
66
  $version = $parts[0];
67
 
68
  // check if migration file is not for an even higher version
69
+ if (version_compare($version, $this->version_to, '>')) {
70
  continue;
71
  }
72
 
73
  // check if we ran migration file before.
74
+ if (version_compare($this->version_from, $version, '>=')) {
75
  continue;
76
  }
77
 
89
  *
90
  * @throws Exception
91
  */
92
+ protected function run_migration($file)
93
+ {
94
+ if (! file_exists($file)) {
95
+ throw new Exception("Migration file $file does not exist.");
96
  }
97
 
98
  include $file;
99
  }
100
+ }
src/admin/class-notices.php CHANGED
@@ -2,41 +2,45 @@
2
 
3
  namespace Boxzilla\Admin;
4
 
5
- class Notices {
 
6
 
7
- /**
8
- * @var array
9
- */
10
- protected $notices = array();
11
 
12
- /**
13
- * Constructor
14
- */
15
- public function __construct() {
16
- add_action( 'admin_notices', array( $this, 'show' ) );
17
- }
 
18
 
19
- /**
20
- * @param $message
21
- * @param $type
22
- *
23
- * @return $this
24
- */
25
- public function add( $message, $type = 'updated' ) {
26
- $this->notices[] = array(
27
- 'message' => $message,
28
- 'type' => $type,
29
- );
 
30
 
31
- return $this;
32
- }
33
 
34
- /**
35
- * Output the registered notices
36
- */
37
- public function show() {
38
- foreach( $this->notices as $notice ) {
39
- echo sprintf( '<div class="notice notice-%s"><p>%s</p></div>', $notice['type'], $notice['message'] );
40
- }
41
- }
 
42
  }
2
 
3
  namespace Boxzilla\Admin;
4
 
5
+ class Notices
6
+ {
7
 
8
+ /**
9
+ * @var array
10
+ */
11
+ protected $notices = array();
12
 
13
+ /**
14
+ * Constructor
15
+ */
16
+ public function __construct()
17
+ {
18
+ add_action('admin_notices', array( $this, 'show' ));
19
+ }
20
 
21
+ /**
22
+ * @param $message
23
+ * @param $type
24
+ *
25
+ * @return $this
26
+ */
27
+ public function add($message, $type = 'updated')
28
+ {
29
+ $this->notices[] = array(
30
+ 'message' => $message,
31
+ 'type' => $type,
32
+ );
33
 
34
+ return $this;
35
+ }
36
 
37
+ /**
38
+ * Output the registered notices
39
+ */
40
+ public function show()
41
+ {
42
+ foreach ($this->notices as $notice) {
43
+ echo sprintf('<div class="notice notice-%s"><p>%s</p></div>', $notice['type'], $notice['message']);
44
+ }
45
+ }
46
  }
src/admin/class-review-notice.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
2
 
3
  namespace Boxzilla\Admin;
 
4
  use WP_Screen;
5
 
6
  /**
@@ -8,7 +9,8 @@ use WP_Screen;
8
  *
9
  * @ignore
10
  */
11
- class ReviewNotice {
 
12
 
13
  /**
14
  * @var string
@@ -18,63 +20,66 @@ class ReviewNotice {
18
  /**
19
  * Constructor.
20
  */
21
- public function __construct() {
22
-
23
  }
24
 
25
  /**
26
  * Add action & filter hooks.
27
  */
28
- public function add_hooks() {
29
- add_action( 'admin_notices', array( $this, 'show' ) );
30
- add_action( 'boxzilla_admin_dismiss_review_notice', array( $this, 'dismiss' ) );
 
31
  }
32
 
33
  /**
34
  * Set flag in user meta so notice won't be shown.
35
  */
36
- public function dismiss() {
 
37
  $user = wp_get_current_user();
38
- update_user_meta( $user->ID, $this->meta_key_dismissed, 1 );
39
  }
40
 
41
  /**
42
  * @return bool
43
  */
44
- public function show() {
 
45
  $screen = get_current_screen();
46
- if( ! $screen instanceof WP_Screen ) {
47
  return false;
48
  }
49
 
50
  // on some boxzilla screen?
51
- if( $screen->post_type !== 'boxzilla-box' ) {
52
  return false;
53
  }
54
 
55
  // authorized?
56
- if( ! current_user_can( 'edit_posts' ) ) {
57
  return false;
58
  }
59
 
60
  // only show if 2 weeks have passed since first use.
61
- $two_weeks_in_seconds = ( 60 * 60 * 24 * 14 );
62
- if( $this->time_since_first_use() <= $two_weeks_in_seconds ) {
63
  return false;
64
  }
65
 
66
  // only show if user did not dismiss before
67
  $user = wp_get_current_user();
68
- if( get_user_meta( $user->ID, $this->meta_key_dismissed, true ) ) {
69
  return false;
70
  }
71
 
72
  echo '<div class="notice notice-info boxzilla-is-dismissible">';
73
  echo '<p>';
74
- echo __( 'You\'ve been using Boxzilla for some time now; we hope you love it!', 'boxzilla' ) . ' <br />';
75
- echo sprintf( __( 'If you do, please <a href="%s">leave us a 5★ rating on WordPress.org</a>. It would be of great help to us.', 'boxzilla' ), 'https://wordpress.org/support/view/plugin-reviews/boxzilla?rate=5#new-post' );
76
  echo '</p>';
77
- echo '<form method="POST"><button type="submit" class="notice-dismiss"><span class="screen-reader-text">'. __( 'Dismiss this notice.', 'boxzilla' ) .'</span></button><input type="hidden" name="_boxzilla_admin_action" value="dismiss_review_notice"/></form>';
78
  echo '</div>';
79
  return true;
80
  }
@@ -82,15 +87,16 @@ class ReviewNotice {
82
  /**
83
  * @return int
84
  */
85
- private function time_since_first_use() {
86
- $options = get_option( 'boxzilla_settings' );
 
87
 
88
  // option was never added before, do it now.
89
- if( empty( $options['first_activated_on'] ) ) {
90
  $options['first_activated_on'] = time();
91
- update_option( 'boxzilla_settings', $options );
92
  }
93
 
94
  return time() - $options['first_activated_on'];
95
  }
96
- }
1
  <?php
2
 
3
  namespace Boxzilla\Admin;
4
+
5
  use WP_Screen;
6
 
7
  /**
9
  *
10
  * @ignore
11
  */
12
+ class ReviewNotice
13
+ {
14
 
15
  /**
16
  * @var string
20
  /**
21
  * Constructor.
22
  */
23
+ public function __construct()
24
+ {
25
  }
26
 
27
  /**
28
  * Add action & filter hooks.
29
  */
30
+ public function add_hooks()
31
+ {
32
+ add_action('admin_notices', array( $this, 'show' ));
33
+ add_action('boxzilla_admin_dismiss_review_notice', array( $this, 'dismiss' ));
34
  }
35
 
36
  /**
37
  * Set flag in user meta so notice won't be shown.
38
  */
39
+ public function dismiss()
40
+ {
41
  $user = wp_get_current_user();
42
+ update_user_meta($user->ID, $this->meta_key_dismissed, 1);
43
  }
44
 
45
  /**
46
  * @return bool
47
  */
48
+ public function show()
49
+ {
50
  $screen = get_current_screen();
51
+ if (! $screen instanceof WP_Screen) {
52
  return false;
53
  }
54
 
55
  // on some boxzilla screen?
56
+ if ($screen->post_type !== 'boxzilla-box') {
57
  return false;
58
  }
59
 
60
  // authorized?
61
+ if (! current_user_can('edit_posts')) {
62
  return false;
63
  }
64
 
65
  // only show if 2 weeks have passed since first use.
66
+ $two_weeks_in_seconds = (60 * 60 * 24 * 14);
67
+ if ($this->time_since_first_use() <= $two_weeks_in_seconds) {
68
  return false;
69
  }
70
 
71
  // only show if user did not dismiss before
72
  $user = wp_get_current_user();
73
+ if (get_user_meta($user->ID, $this->meta_key_dismissed, true)) {
74
  return false;
75
  }
76
 
77
  echo '<div class="notice notice-info boxzilla-is-dismissible">';
78
  echo '<p>';
79
+ echo __('You\'ve been using Boxzilla for some time now; we hope you love it!', 'boxzilla') . ' <br />';
80
+ echo sprintf(__('If you do, please <a href="%s">leave us a 5★ rating on WordPress.org</a>. It would be of great help to us.', 'boxzilla'), 'https://wordpress.org/support/view/plugin-reviews/boxzilla?rate=5#new-post');
81
  echo '</p>';
82
+ echo '<form method="POST"><button type="submit" class="notice-dismiss"><span class="screen-reader-text">'. __('Dismiss this notice.', 'boxzilla') .'</span></button><input type="hidden" name="_boxzilla_admin_action" value="dismiss_review_notice"/></form>';
83
  echo '</div>';
84
  return true;
85
  }
87
  /**
88
  * @return int
89
  */
90
+ private function time_since_first_use()
91
+ {
92
+ $options = get_option('boxzilla_settings');
93
 
94
  // option was never added before, do it now.
95
+ if (empty($options['first_activated_on'])) {
96
  $options['first_activated_on'] = time();
97
+ update_option('boxzilla_settings', $options);
98
  }
99
 
100
  return time() - $options['first_activated_on'];
101
  }
102
+ }
src/admin/migrations/3.1-cookie-option.php CHANGED
@@ -1,24 +1,24 @@
1
  <?php
2
 
3
- defined( 'ABSPATH' ) or exit;
4
 
5
- $posts = get_posts( array( 'post_type' => 'boxzilla-box' ) );
6
 
7
- if( ! empty( $posts ) ) {
8
- foreach( $posts as $post ) {
9
- $settings = get_post_meta( $post->ID, 'boxzilla_options', true );
10
 
11
- if( ! is_array( $settings ) ) {
12
  continue;
13
  }
14
 
15
  // translate from days to hours
16
- $new_value = intval( $settings['cookie'] ) * 24;
17
 
18
  // store in new location
19
  $settings['cookie'] = array(
20
  'dismissed' => $new_value
21
  );
22
- update_post_meta( $post->ID, 'boxzilla_options', $settings );
23
  }
24
- }
1
  <?php
2
 
3
+ defined('ABSPATH') or exit;
4
 
5
+ $posts = get_posts(array( 'post_type' => 'boxzilla-box' ));
6
 
7
+ if (! empty($posts)) {
8
+ foreach ($posts as $post) {
9
+ $settings = get_post_meta($post->ID, 'boxzilla_options', true);
10
 
11
+ if (! is_array($settings)) {
12
  continue;
13
  }
14
 
15
  // translate from days to hours
16
+ $new_value = intval($settings['cookie']) * 24;
17
 
18
  // store in new location
19
  $settings['cookie'] = array(
20
  'dismissed' => $new_value
21
  );
22
+ update_post_meta($post->ID, 'boxzilla_options', $settings);
23
  }
24
+ }
src/admin/migrations/3.1.11-screen-width-condition.php CHANGED
@@ -1,29 +1,29 @@
1
  <?php
2
 
3
- defined( 'ABSPATH' ) or exit;
4
 
5
- $posts = get_posts( array( 'post_type' => 'boxzilla-box' ) );
6
 
7
- if( ! empty( $posts ) ) {
8
- foreach( $posts as $post ) {
9
- $settings = get_post_meta( $post->ID, 'boxzilla_options', true );
10
 
11
- if( ! is_array( $settings ) ) {
12
  continue;
13
  }
14
 
15
- if( empty( $settings['hide_on_screen_size'] ) ) {
16
  continue;
17
  }
18
 
19
  // set updated option
20
  $settings['screen_size_condition'] = array(
21
  'condition' => 'larger',
22
- 'value' => intval( $settings['hide_on_screen_size'] ),
23
  );
24
 
25
- unset( $settings['hide_on_screen_size'] );
26
 
27
- update_post_meta( $post->ID, 'boxzilla_options', $settings );
28
  }
29
- }
1
  <?php
2
 
3
+ defined('ABSPATH') or exit;
4
 
5
+ $posts = get_posts(array( 'post_type' => 'boxzilla-box' ));
6
 
7
+ if (! empty($posts)) {
8
+ foreach ($posts as $post) {
9
+ $settings = get_post_meta($post->ID, 'boxzilla_options', true);
10
 
11
+ if (! is_array($settings)) {
12
  continue;
13
  }
14
 
15
+ if (empty($settings['hide_on_screen_size'])) {
16
  continue;
17
  }
18
 
19
  // set updated option
20
  $settings['screen_size_condition'] = array(
21
  'condition' => 'larger',
22
+ 'value' => intval($settings['hide_on_screen_size']),
23
  );
24
 
25
+ unset($settings['hide_on_screen_size']);
26
 
27
+ update_post_meta($post->ID, 'boxzilla_options', $settings);
28
  }
29
+ }
src/admin/views/extensions.php CHANGED
@@ -1,37 +1,39 @@
1
- <?php defined( 'ABSPATH' ) or exit; ?>
2
  <div class="wrap" id="boxzilla-admin" class="boxzilla-extensions">
3
 
4
- <h2><?php _e( 'Available Add-On Plugins', 'boxzilla' ); ?></h2>
5
  <p>
6
- <?php _e( "There are various add-ons available for Boxzilla which further enhance the functionality of the core plugin.", 'boxzilla' ); ?>
7
  </p>
8
  <p>
9
- <?php printf( __( 'To gain instant access the premium add-on plugins listed here, <a href="%s">have a look at our pricing</a>.', 'boxzilla' ), 'https://boxzillaplugin.com/pricing#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=extensions-page' ); ?>
10
  </p>
11
 
12
- <?php if( empty( $extensions ) ) : ?>
13
  <script>
14
  window.setTimeout( function() {
15
  window.location.href = 'https://boxzillaplugin.com/add-ons/#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=extensions-page';
16
  }, 2000 );
17
  </script>
18
- <p><?php _e( 'You will be redirected to the Boxzilla site in a few seconds..', 'boxzilla' ); ?></p>
19
- <p><?php printf( __( 'If not, please click here: %s.', 'boxzilla' ), '<a href="https://boxzillaplugin.com/add-ons/#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=extensions-page" target="_blank">View add-on plugins</a>' ); ?></p>
20
  <?php else : ?>
21
 
22
- <?php foreach( $extensions as $i => $plugin ) : ?>
23
  <div class="plugin">
24
- <a href="<?php echo esc_url( $plugin->page_url ); ?>" class="unstyled"><img src="<?php echo esc_url( $plugin->image_url ); ?>" alt="<?php echo $plugin->name; ?>" width="280" height="220"></a>
25
  <div class="caption">
26
- <h3><a href="<?php echo esc_url( $plugin->page_url ); ?>" class="unstyled"><?php echo $plugin->name; ?></a></h3>
27
- <p><?php echo esc_html( $plugin->short_description ); ?></p>
28
  <p>
29
- <a class="button" href="<?php echo esc_url( $plugin->page_url ); ?>" title="More about <?php echo esc_attr( $plugin->name ); ?>">Read More</a>
30
- <span class="type"><?php echo esc_html( $plugin->type ); ?></span>
31
  </p>
32
  </div>
33
  </div>
34
- <?php if(($i+1) % 4 === 0) { echo '<div style="clear: both;"></div>'; } ?>
 
 
35
  <?php endforeach; ?>
36
 
37
  <br style="clear: both;" />
1
+ <?php defined('ABSPATH') or exit; ?>
2
  <div class="wrap" id="boxzilla-admin" class="boxzilla-extensions">
3
 
4
+ <h2><?php _e('Available Add-On Plugins', 'boxzilla'); ?></h2>
5
  <p>
6
+ <?php _e("There are various add-ons available for Boxzilla which further enhance the functionality of the core plugin.", 'boxzilla'); ?>
7
  </p>
8
  <p>
9
+ <?php printf(__('To gain instant access the premium add-on plugins listed here, <a href="%s">have a look at our pricing</a>.', 'boxzilla'), 'https://boxzillaplugin.com/pricing#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=extensions-page'); ?>
10
  </p>
11
 
12
+ <?php if (empty($extensions)) : ?>
13
  <script>
14
  window.setTimeout( function() {
15
  window.location.href = 'https://boxzillaplugin.com/add-ons/#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=extensions-page';
16
  }, 2000 );
17
  </script>
18
+ <p><?php _e('You will be redirected to the Boxzilla site in a few seconds..', 'boxzilla'); ?></p>
19
+ <p><?php printf(__('If not, please click here: %s.', 'boxzilla'), '<a href="https://boxzillaplugin.com/add-ons/#utm_source=wp-plugin&utm_medium=boxzilla&utm_campaign=extensions-page" target="_blank">View add-on plugins</a>'); ?></p>
20
  <?php else : ?>
21
 
22
+ <?php foreach ($extensions as $i => $plugin) : ?>
23
  <div class="plugin">
24
+ <a href="<?php echo esc_url($plugin->page_url); ?>" class="unstyled"><img src="<?php echo esc_url($plugin->image_url); ?>" alt="<?php echo $plugin->name; ?>" width="280" height="220"></a>
25
  <div class="caption">
26
+ <h3><a href="<?php echo esc_url($plugin->page_url); ?>" class="unstyled"><?php echo esc_html($plugin->name); ?></a></h3>
27
+ <p><?php echo esc_html($plugin->description); ?></p>
28
  <p>
29
+ <a class="button" href="<?php echo esc_url($plugin->page_url); ?>" title="More about <?php echo esc_attr($plugin->name); ?>">Read More</a>
30
+ <span class="type">Premium</span>
31
  </p>
32
  </div>
33
  </div>
34
+ <?php if (($i+1) % 4 === 0) {
35
+ echo '<div style="clear: both;"></div>';
36
+ } ?>
37
  <?php endforeach; ?>
38
 
39
  <br style="clear: both;" />
src/admin/views/metaboxes/box-appearance-controls.php CHANGED
@@ -1,46 +1,46 @@
1
- <?php defined( 'ABSPATH' ) or exit; ?>
2
- <div id="notice-notinymce" class="error" style="display: none;"><p><?php _e( 'For the best experience when styling your box, please use the default WordPress visual editor.', 'boxzilla' ); ?></p></div>
3
 
4
  <table class="form-table">
5
- <?php do_action( 'boxzilla_before_box_appearance_controls', $box, $opts ); ?>
6
  <tr valign="top">
7
  <td>
8
- <label class="boxzilla-label" for="boxzilla-background-color"><?php _e( 'Background color', 'boxzilla' ); ?></label>
9
  <input id="boxzilla-background-color" name="boxzilla_box[css][background_color]" type="text" class="boxzilla-color-field" value="<?php echo esc_attr($opts['css']['background_color']); ?>" />
10
  </td>
11
  <td>
12
- <label class="boxzilla-label" for="boxzilla-color"><?php _e( 'Text color', 'boxzilla' ); ?></label>
13
  <input id="boxzilla-color" name="boxzilla_box[css][color]" type="text" class="boxzilla-color-field" value="<?php echo esc_attr($opts['css']['color']); ?>" />
14
  </td>
15
  <td>
16
- <label class="boxzilla-label" for="boxzilla-width"><?php _e( 'Box width', 'boxzilla' ); ?></label>
17
  <input id="boxzilla-width" name="boxzilla_box[css][width]" id="boxzilla-box-width" min="0" max="3200" type="number" step="1" value="<?php echo esc_attr($opts['css']['width']); ?>" />
18
- <p class="help"><?php _e( 'Width in px', 'boxzilla' ); ?></p>
19
  </td>
20
  </tr>
21
  <tr valign="top">
22
  <td>
23
- <label class="boxzilla-label" for="boxzilla-border-color"><?php _e( 'Border color', 'boxzilla' ); ?></label>
24
  <input name="boxzilla_box[css][border_color]" id="boxzilla-border-color" type="text" class="boxzilla-color-field" value="<?php echo esc_attr($opts['css']['border_color']); ?>" />
25
  </td>
26
  <td>
27
- <label class="boxzilla-label" for="boxzilla-border-width"><?php _e( 'Border width', 'boxzilla' ); ?></label>
28
  <input name="boxzilla_box[css][border_width]" id="boxzilla-border-width" type="number" min="0" max="25" step="1" value="<?php echo esc_attr($opts['css']['border_width']); ?>" />
29
- <p class="help"><?php _e( 'Width in px', 'boxzilla' ); ?></p>
30
  </td>
31
  <td>
32
- <label class="boxzilla-label" for="boxzilla-border-style"><?php _e( 'Border style', 'boxzilla' ); ?></label>
33
  <select name="boxzilla_box[css][border_style]" id="boxzilla-border-style">
34
- <option value="" <?php selected( $opts['css']['border_style'], '' ); ?>><?php _e( 'Default', 'boxzilla-theme-pack' ); ?></option>
35
- <option value="solid" <?php selected( $opts['css']['border_style'], 'solid' ); ?>><?php _e( 'Solid', 'boxzilla-theme-pack' ); ?></option>
36
- <option value="dashed" <?php selected( $opts['css']['border_style'], 'dashed' ); ?>><?php _e( 'Dashed', 'boxzilla-theme-pack' ); ?></option>
37
- <option value="dotted" <?php selected( $opts['css']['border_style'], 'dotted' ); ?>><?php _e( 'Dotted', 'boxzilla-theme-pack' ); ?></option>
38
- <option value="double" <?php selected( $opts['css']['border_style'], 'double' ); ?>><?php _e( 'Double', 'boxzilla-theme-pack' ); ?></option>
39
  </select>
40
- <p class="help"><?php _e( 'Border style', 'boxzilla' ); ?></p>
41
  </td>
42
  </tr>
43
- <?php do_action( 'boxzilla_after_box_appearance_controls', $box, $opts ); ?>
44
  </table>
45
 
46
- <p><?php printf( __( '<a href="%s">Click here to reset all styling settings</a>.', 'boxzilla' ), 'javascript:Boxzilla_Admin.Designer.resetStyles();' ); ?></p>
1
+ <?php defined('ABSPATH') or exit; ?>
2
+ <div id="notice-notinymce" class="error" style="display: none;"><p><?php _e('For the best experience when styling your box, please use the default WordPress visual editor.', 'boxzilla'); ?></p></div>
3
 
4
  <table class="form-table">
5
+ <?php do_action('boxzilla_before_box_appearance_controls', $box, $opts); ?>
6
  <tr valign="top">
7
  <td>
8
+ <label class="boxzilla-label" for="boxzilla-background-color"><?php _e('Background color', 'boxzilla'); ?></label>
9
  <input id="boxzilla-background-color" name="boxzilla_box[css][background_color]" type="text" class="boxzilla-color-field" value="<?php echo esc_attr($opts['css']['background_color']); ?>" />
10
  </td>
11
  <td>
12
+ <label class="boxzilla-label" for="boxzilla-color"><?php _e('Text color', 'boxzilla'); ?></label>
13
  <input id="boxzilla-color" name="boxzilla_box[css][color]" type="text" class="boxzilla-color-field" value="<?php echo esc_attr($opts['css']['color']); ?>" />
14
  </td>
15
  <td>
16
+ <label class="boxzilla-label" for="boxzilla-width"><?php _e('Box width', 'boxzilla'); ?></label>
17
  <input id="boxzilla-width" name="boxzilla_box[css][width]" id="boxzilla-box-width" min="0" max="3200" type="number" step="1" value="<?php echo esc_attr($opts['css']['width']); ?>" />
18
+ <p class="help"><?php _e('Width in px', 'boxzilla'); ?></p>
19
  </td>
20
  </tr>
21
  <tr valign="top">
22
  <td>
23
+ <label class="boxzilla-label" for="boxzilla-border-color"><?php _e('Border color', 'boxzilla'); ?></label>
24
  <input name="boxzilla_box[css][border_color]" id="boxzilla-border-color" type="text" class="boxzilla-color-field" value="<?php echo esc_attr($opts['css']['border_color']); ?>" />
25
  </td>
26
  <td>
27
+ <label class="boxzilla-label" for="boxzilla-border-width"><?php _e('Border width', 'boxzilla'); ?></label>
28
  <input name="boxzilla_box[css][border_width]" id="boxzilla-border-width" type="number" min="0" max="25" step="1" value="<?php echo esc_attr($opts['css']['border_width']); ?>" />
29
+ <p class="help"><?php _e('Width in px', 'boxzilla'); ?></p>
30
  </td>
31
  <td>
32
+ <label class="boxzilla-label" for="boxzilla-border-style"><?php _e('Border style', 'boxzilla'); ?></label>
33
  <select name="boxzilla_box[css][border_style]" id="boxzilla-border-style">
34
+ <option value="" <?php selected($opts['css']['border_style'], ''); ?>><?php _e('Default', 'boxzilla-theme-pack'); ?></option>
35
+ <option value="solid" <?php selected($opts['css']['border_style'], 'solid'); ?>><?php _e('Solid', 'boxzilla-theme-pack'); ?></option>
36
+ <option value="dashed" <?php selected($opts['css']['border_style'], 'dashed'); ?>><?php _e('Dashed', 'boxzilla-theme-pack'); ?></option>
37
+ <option value="dotted" <?php selected($opts['css']['border_style'], 'dotted'); ?>><?php _e('Dotted', 'boxzilla-theme-pack'); ?></option>
38
+ <option value="double" <?php selected($opts['css']['border_style'], 'double'); ?>><?php _e('Double', 'boxzilla-theme-pack'); ?></option>
39
  </select>
40
+ <p class="help"><?php _e('Border style', 'boxzilla'); ?></p>
41
  </td>
42
  </tr>
43
+ <?php do_action('boxzilla_after_box_appearance_controls', $box, $opts); ?>
44
  </table>
45
 
46
+ <p><?php printf(__('<a href="%s">Click here to reset all styling settings</a>.', 'boxzilla'), 'javascript:Boxzilla_Admin.Designer.resetStyles();'); ?></p>
src/admin/views/metaboxes/box-option-controls.php CHANGED
@@ -1,228 +1,231 @@
1
- <?php
2
-
3
- defined( 'ABSPATH' ) or exit;
4
-
5
- /** @var \Boxzilla\Box $box */
6
- /** @var array $opts */
7
- /** @var array $global_opts */
8
-
9
- /** @var array $rule_options */
10
- $rule_options = array(
11
- 'everywhere' => __( 'everywhere', 'boxzilla' ),
12
- 'is_page' => __( 'if page', 'boxzilla' ),
13
- 'is_single' => __( 'if post', 'boxzilla' ),
14
- 'is_post_with_tag' => __( 'if post tag', 'boxzilla' ),
15
- 'is_post_in_category' => __( 'if post category', 'boxzilla' ),
16
- 'is_post_type' => __( 'if post type', 'boxzilla' ),
17
- 'is_url' => __( 'if URL', 'boxzilla' ),
18
- 'is_referer' => __( 'if referer', 'boxzilla' ),
19
- 'is_user_logged_in' => __( 'if user', 'boxzilla' ),
20
- );
21
-
22
- /**
23
- * @ignore
24
- */
25
- $rule_options = apply_filters( 'boxzilla_rules_options', $rule_options );
26
-
27
  ?>
28
  <table class="form-table">
29
- <?php
30
-
31
- /**
32
- * @ignore
33
- */
34
- do_action( 'boxzilla_before_box_option_controls', $box, $opts );
35
-
36
- ?>
37
  <tr>
38
- <th><?php _e( 'Load this box if', 'boxzilla' ); ?></th>
39
  <td>
40
  <label>
41
- <?php _e( 'Request matches', 'boxzilla' ); ?>
42
  <select name="boxzilla_box[rules_comparision]" id="boxzilla-rule-comparison">
43
- <option value="any" <?php selected( $opts['rules_comparision'], 'any' ); ?>><?php _e( 'any', 'boxzilla' ); ?></option>
44
- <option value="all" <?php selected( $opts['rules_comparision'], 'all' ); ?>><?php _e( 'all', 'boxzilla' ); ?></option>
45
  </select>
46
- <?php _e( 'of the following conditions.', 'boxzilla' ); ?>
47
  </label>
48
  </td>
49
  </tr>
50
  <tbody id="boxzilla-box-rules">
51
- <?php
52
- $key = 0;
53
- foreach( $opts['rules'] as $rule ) {
54
- // skip invalid looking rules
55
- if( ! array_key_exists( 'condition', $rule ) ) { continue; }
56
-
57
- // output row showing "and" or "or" between rules
58
- if($key > 0) {
59
- $or = __( 'or', 'boxzilla' );
60
- $and = __( 'and', 'boxzilla' );
61
- $text = $opts['rules_comparision'] === 'any' ? $or : $and;
62
-
63
- echo '<tr>';
64
- echo '<th class="boxzilla-no-vpadding"></th>';
65
- echo '<td class="boxzilla-no-vpadding"><span class="boxzilla-andor boxzilla-muted">' . $text . '</span></td>';
66
- echo '</tr>';
67
- }
68
- ?>
 
69
  <tr valign="top" class="boxzilla-rule-row boxzilla-rule-row-<?php echo $key; ?>">
70
  <th style="text-align: right; font-weight: normal;">
71
- <span class="boxzilla-close boxzilla-remove-rule" title="<?php esc_attr_e( 'Remove rule', 'boxzilla' ); ?>"><span class="dashicons dashicons-dismiss"></span></span>
72
  </th>
73
  <td>
74
  <select class="boxzilla-rule-condition" name="boxzilla_box[rules][<?php echo $key; ?>][condition]">
75
- <?php foreach( $rule_options as $value => $label ) {
76
- printf( '<option value="%s" %s>%s</option>', $value, selected( $rule['condition'], $value ), $label );
77
- } ?>
78
  </select>
79
 
80
  <select class="boxzilla-rule-qualifier" name="boxzilla_box[rules][<?php echo $key; ?>][qualifier]" style="min-width: 135px;">
81
- <option value="1" <?php selected( ! isset( $rule['qualifier'] ) || $rule['qualifier'] ); ?>><?php _e( 'is', 'boxzilla' ); ?></option>
82
- <option value="0" <?php selected( isset( $rule['qualifier'] ) && !$rule['qualifier'] ); ?>><?php _e( 'is not', 'boxzilla' ); ?></option>
83
- <option value="contains" <?php selected( isset( $rule['qualifier'] ) && $rule['qualifier'] === 'contains' ); ?> style="display: none;"><?php _e( 'contains', 'boxzilla' ); ?>
84
- <option value="not_contains" <?php selected( isset( $rule['qualifier'] ) && $rule['qualifier'] === 'not_contains' ); ?> style="display: none;"><?php _e( 'does not contain', 'boxzilla' ); ?></option>
85
  </select>
86
 
87
- <input class="boxzilla-rule-value regular-text" name="boxzilla_box[rules][<?php echo $key; ?>][value]" type="text" value="<?php echo esc_attr( $rule['value'] ); ?>" placeholder="<?php _e( 'Leave empty for any or enter (comma-separated) names or ID\'s', 'boxzilla' ); ?>" style="<?php if( in_array( $rule['condition'], array( '', 'everywhere' ) ) ) { echo 'display: none;'; } ?>" />
 
 
88
  </td>
89
  </tr>
90
- <?php $key++;
91
- } ?>
92
  </tbody>
93
  <tr>
94
  <th></th>
95
- <td><button type="button" class="button boxzilla-add-rule"><?php _e( 'Add another rule', 'boxzilla' ); ?></button></td>
96
  </tr>
97
  <tr valign="top">
98
- <th><label for="boxzilla_position"><?php _e( 'Box Position', 'boxzilla' ); ?></label></th>
99
  <td>
100
  <table class="window-positions">
101
  <tr>
102
  <td>
103
- <?php
104
- $value = 'top-left'; $label = __( 'Top Left', 'boxzilla' );
105
- printf( '<label><input type="radio" name="boxzilla_box[css][position]" value="%s" %s> &nbsp; %s</label>', $value, checked( $opts['css']['position'], $value, false ), $label );
106
- ?>
107
  </td>
108
  <td></td>
109
  <td>
110
- <?php
111
- $value = 'top-right'; $label = __( 'Top Right', 'boxzilla' );
112
- printf( '<label><input type="radio" name="boxzilla_box[css][position]" value="%s" %s> &nbsp; %s</label>', $value, checked( $opts['css']['position'], $value, false ), $label );
113
- ?>
114
  </td>
115
  </tr>
116
  <tr>
117
  <td></td>
118
  <td>
119
- <?php
120
- $value = 'center'; $label = __( 'Center', 'boxzilla' );
121
- printf( '<label><input type="radio" name="boxzilla_box[css][position]" value="%s" %s> &nbsp; %s</label>', $value, checked( $opts['css']['position'], $value, false ), $label );
122
- ?>
123
  </td>
124
  <td></td>
125
  </tr>
126
  <tr>
127
  <td>
128
- <?php
129
- $value = 'bottom-left'; $label = __( 'Bottom Left', 'boxzilla' );
130
- printf( '<label><input type="radio" name="boxzilla_box[css][position]" value="%s" %s> &nbsp; %s</label>', $value, checked( $opts['css']['position'], $value, false ), $label );
131
- ?>
132
  </td>
133
  <td></td>
134
  <td>
135
- <?php
136
- $value = 'bottom-right'; $label = __( 'Bottom Right', 'boxzilla' );
137
- printf( '<label><input type="radio" name="boxzilla_box[css][position]" value="%s" %s> &nbsp; %s</label>', $value, checked( $opts['css']['position'], $value, false ), $label );
138
- ?>
139
  </td>
140
  </tr>
141
  </table>
142
  </td>
143
  </tr>
144
  <tr valign="top">
145
- <th><label><?php _e( 'Animation', 'boxzilla' ); ?></label></th>
146
  <td>
147
- <label><input type="radio" name="boxzilla_box[animation]" value="fade" <?php checked($opts['animation'], 'fade'); ?> /> <?php _e( 'Fade In', 'boxzilla' ); ?></label> &nbsp;
148
- <label><input type="radio" name="boxzilla_box[animation]" value="slide" <?php checked($opts['animation'], 'slide'); ?> /> <?php _e( 'Slide In', 'boxzilla' ); ?></label>
149
- <p class="help"><?php _e( 'Which animation type should be used to show the box when triggered?', 'boxzilla' ); ?></p>
150
  </td>
151
  </tr>
152
  <tr valign="top">
153
- <th><label for="boxzilla_trigger"><?php _e( 'Auto-show box?', 'boxzilla' ); ?></label></th>
154
  <td>
155
- <label><input type="radio" class="boxzilla-auto-show-trigger" name="boxzilla_box[trigger]" value="" <?php checked( $opts['trigger'], '' ); ?> /> <?php _e( 'Never', 'boxzilla' ); ?></label><br />
156
- <label><input type="radio" class="boxzilla-auto-show-trigger" name="boxzilla_box[trigger]" value="time_on_page" <?php checked( $opts['trigger'], 'time_on_page' ); ?> /> <?php printf( __( 'Yes, after %s seconds on the page.', 'boxzilla' ), '<input type="number" name="boxzilla_box[trigger_time_on_page]" min="0" value="' . esc_attr( $opts['trigger_time_on_page'] ) . '" />' ); ?></label><br />
157
- <label><input type="radio" class="boxzilla-auto-show-trigger" name="boxzilla_box[trigger]" value="percentage" <?php checked( $opts['trigger'], 'percentage' ); ?> /> <?php printf( __( 'Yes, when at %s of page height', 'boxzilla' ), '<input type="number" name="boxzilla_box[trigger_percentage]" min="0" max="100" value="' . esc_attr( $opts['trigger_percentage'] ) . '" />%' ); ?></label><br />
158
- <label><input type="radio" class="boxzilla-auto-show-trigger" name="boxzilla_box[trigger]" value="element" <?php checked( $opts['trigger'], 'element' ); ?> /> <?php printf( __( 'Yes, when at element %s', 'boxzilla' ), '<input type="text" name="boxzilla_box[trigger_element]" value="' . esc_attr( $opts['trigger_element'] ) . '" placeholder="' . __( 'Example: #comments', 'boxzilla') .'" />' ); ?></label><br />
159
- <?php do_action( 'boxzilla_output_auto_show_trigger_options', $opts ); ?>
160
  </td>
161
  </tr>
162
- <tbody class="boxzilla-trigger-options" style="display: <?php echo ( $opts['trigger'] === '' ) ? 'none' : 'table-row-group'; ?>;">
163
  <tr valign="top">
164
- <th><label for="boxzilla_cookie"><?php _e( 'Cookie expiration', 'boxzilla' ); ?></label></th>
165
  <td>
166
  <div style="display: inline-block; margin-right: 20px;">
167
- <label for="boxzilla_cookie_triggered" style="font-weight: bold; display: block;"><?php esc_html_e( 'Triggered', 'mailchimp-for-wp' ); ?></label>
168
  <input type="number" id="boxzilla_cookie_triggered" name="boxzilla_box[cookie][triggered]" min="0" step="1" value="<?php echo esc_attr($opts['cookie']['triggered']); ?>" />
169
- <small><?php _e( 'hours', 'boxzilla' ); ?></small>
170
  </div>
171
  <div style="display: inline-block;">
172
- <label for="boxzilla_cookie_dismissed" style="font-weight: bold; display: block;"><?php esc_html_e( 'Dismissed', 'mailchimp-for-wp' ); ?></label>
173
  <input type="number" id="boxzilla_cookie_dismissed" name="boxzilla_box[cookie][dismissed]" min="0" step="1" value="<?php echo esc_attr($opts['cookie']['dismissed']); ?>" />
174
- <small><?php _e( 'hours', 'boxzilla' ); ?></small>
175
  </div>
176
  <br />
177
 
178
- <p class="help"><?php _e( 'After this box is triggered or dismissed, how many hours should it stay hidden?', 'boxzilla' ); ?></p>
179
  </td>
180
  </tr>
181
  <tr valign="top">
182
- <th><label><?php _e( 'Screen width', 'boxzilla' ); ?></label></th>
183
  <td>
184
- <?php
185
- $condition_type = $opts['screen_size_condition']['condition'];
186
- $condition_value = $opts['screen_size_condition']['value'];
187
- $condition_select = '<select name="boxzilla_box[screen_size_condition][condition]"><option value="larger" '. ( $condition_type === 'larger' ? 'selected' : '' ) .'>' . __( 'larger', 'boxzilla' ) . '</option><option value="smaller" '. ( $condition_type === 'smaller' ? 'selected' : '' ) .'>' . __( 'smaller', 'boxzilla' ) . '</option></select>'; ?>
188
- <p><?php printf( __( 'Only show on screens %s than %s.', 'boxzilla' ), $condition_select, '<input type="number" min="0" name="boxzilla_box[screen_size_condition][value]" value="' . esc_attr( $condition_value ) . '" style="max-width: 70px;" />px' ); ?></p>
189
- <p class="help"><?php _e( 'Leave empty if you want to show the box on all screen sizes.', 'boxzilla' ); ?></p>
190
  </td>
191
 
192
  </tr>
193
  <tr valign="top">
194
- <th><label for="boxzilla_auto_hide"><?php _e( 'Auto-hide?', 'boxzilla' ); ?></label></th>
195
  <td>
196
- <label><input type="radio" name="boxzilla_box[auto_hide]" value="1" <?php checked( $opts['auto_hide'], 1 ); ?> /> <?php _e( 'Yes' ); ?></label> &nbsp;
197
- <label><input type="radio" name="boxzilla_box[auto_hide]" value="0" <?php checked( $opts['auto_hide'], 0 ); ?> /> <?php _e( 'No' ); ?></label> &nbsp;
198
- <p class="help"><?php _e( 'Hide box again when visitors scroll back up?', 'boxzilla' ); ?></p>
199
  </td>
200
  </tr>
201
  <tr valign="top">
202
- <th><label for="boxzilla_closable"><?php _e( 'Show close icon?', 'boxzilla' ); ?></label></th>
203
  <td>
204
- <label><input type="radio" id="boxzilla_closable_1" name="boxzilla_box[show_close_icon]" value="1" <?php checked( $opts['show_close_icon'], 1 ); ?> /> <?php _e( 'Yes' ); ?></label> &nbsp;
205
- <label><input type="radio" id="boxzilla_closable_0" name="boxzilla_box[show_close_icon]" value="0" <?php checked( $opts['show_close_icon'], 0 ); ?> /> <?php _e( 'No' ); ?></label> &nbsp;
206
  <p class="help">
207
- <?php _e( 'If you decide to hide the close icon, make sure to offer an alternative way to close the box.', 'boxzilla' ); ?><br />
208
- <?php _e ( 'Example: ', 'boxzilla' ); ?> <code>[boxzilla-close]No, thanks![/boxzilla-close]</code>
209
  </p>
210
  </td>
211
  </tr>
212
  <tr valign="top">
213
- <th><label for="boxzilla_test_mode"><?php _e( 'Enable test mode?', 'boxzilla' ); ?></label></th>
214
  <td>
215
- <label><input type="radio" id="boxzilla_test_mode_1" name="boxzilla_global_settings[test_mode]" value="1" <?php checked( $global_opts['test_mode'], 1 ); ?> /> <?php _e( 'Yes' ); ?></label> &nbsp;
216
- <label><input type="radio" id="boxzilla_test_mode_0" name="boxzilla_global_settings[test_mode]" value="0" <?php checked( $global_opts['test_mode'], 0 ); ?> /> <?php _e( 'No' ); ?></label> &nbsp;
217
- <p class="help"><?php _e( 'If test mode is enabled, all boxes will show up regardless of whether a cookie has been set.', 'boxzilla' ); ?></p>
218
  </td>
219
  </tr>
220
- <?php
221
-
222
- /**
223
- * @ignore
224
- */
225
- do_action( 'boxzilla_after_box_option_controls', $box, $opts ); ?>
226
  </tbody>
227
  </table>
228
 
@@ -234,22 +237,22 @@ $rule_options = apply_filters( 'boxzilla_rules_options', $rule_options );
234
  </tr>
235
  <tr valign="top" class="boxzilla-rule-row boxzilla-rule-row-{{{data.key}}}">
236
  <th style="text-align: right; font-weight: normal;">
237
- <span class="boxzilla-close boxzilla-remove-rule" title="<?php esc_attr_e( 'Remove rule', 'boxzilla' ); ?>"><span class="dashicons dashicons-dismiss"></span></span>
238
  </th>
239
  <td class="boxzilla-sm">
240
  <select class="boxzilla-rule-condition" name="boxzilla_box[rules][{{{data.key}}}][condition]">
241
- <?php foreach( $rule_options as $value => $label ) {
242
- printf( '<option value="%s" %s %s>%s</option>', $value, disabled( $value, '', false ), '', $label );
243
- } ?>
244
  </select>
245
  <select class="boxzilla-rule-qualifier" name="boxzilla_box[rules][{{{data.key}}}][qualifier]" style="display: none; min-width: 135px;" >
246
- <option value="1" selected><?php _e( 'is', 'boxzilla' ); ?></option>
247
- <option value="0"><?php _e( 'is not', 'boxzilla' ); ?></option>
248
- <option value="contains" style="display: none;"><?php _e( 'contains', 'boxzilla' ); ?></option>
249
- <option value="not_contains" style="display: none;"><?php _e( 'does not contain', 'boxzilla' ); ?></option>
250
  </select>
251
 
252
- <input class="boxzilla-rule-value regular-text" name="boxzilla_box[rules][{{{data.key}}}][value]" type="text" value="" placeholder="<?php _e( 'Leave empty for any or enter (comma-separated) names or ID\'s', 'boxzilla' ); ?>" style="display: none;" />
253
  </td>
254
  </tr>
255
  </script>
1
+ <?php
2
+
3
+ defined('ABSPATH') or exit;
4
+
5
+ /** @var \Boxzilla\Box $box */
6
+ /** @var array $opts */
7
+ /** @var array $global_opts */
8
+
9
+ /** @var array $rule_options */
10
+ $rule_options = array(
11
+ 'everywhere' => __('everywhere', 'boxzilla'),
12
+ 'is_page' => __('if page', 'boxzilla'),
13
+ 'is_single' => __('if post', 'boxzilla'),
14
+ 'is_post_with_tag' => __('if post tag', 'boxzilla'),
15
+ 'is_post_in_category' => __('if post category', 'boxzilla'),
16
+ 'is_post_type' => __('if post type', 'boxzilla'),
17
+ 'is_url' => __('if URL', 'boxzilla'),
18
+ 'is_referer' => __('if referer', 'boxzilla'),
19
+ 'is_user_logged_in' => __('if user', 'boxzilla'),
20
+ );
21
+
22
+ /**
23
+ * @ignore
24
+ */
25
+ $rule_options = apply_filters('boxzilla_rules_options', $rule_options);
26
+
27
  ?>
28
  <table class="form-table">
29
+ <?php
30
+
31
+ /**
32
+ * @ignore
33
+ */
34
+ do_action('boxzilla_before_box_option_controls', $box, $opts);
35
+
36
+ ?>
37
  <tr>
38
+ <th><?php _e('Load this box if', 'boxzilla'); ?></th>
39
  <td>
40
  <label>
41
+ <?php _e('Request matches', 'boxzilla'); ?>
42
  <select name="boxzilla_box[rules_comparision]" id="boxzilla-rule-comparison">
43
+ <option value="any" <?php selected($opts['rules_comparision'], 'any'); ?>><?php _e('any', 'boxzilla'); ?></option>
44
+ <option value="all" <?php selected($opts['rules_comparision'], 'all'); ?>><?php _e('all', 'boxzilla'); ?></option>
45
  </select>
46
+ <?php _e('of the following conditions.', 'boxzilla'); ?>
47
  </label>
48
  </td>
49
  </tr>
50
  <tbody id="boxzilla-box-rules">
51
+ <?php
52
+ $key = 0;
53
+ foreach ($opts['rules'] as $rule) {
54
+ // skip invalid looking rules
55
+ if (! array_key_exists('condition', $rule)) {
56
+ continue;
57
+ }
58
+
59
+ // output row showing "and" or "or" between rules
60
+ if ($key > 0) {
61
+ $or = __('or', 'boxzilla');
62
+ $and = __('and', 'boxzilla');
63
+ $text = $opts['rules_comparision'] === 'any' ? $or : $and;
64
+
65
+ echo '<tr>';
66
+ echo '<th class="boxzilla-no-vpadding"></th>';
67
+ echo '<td class="boxzilla-no-vpadding"><span class="boxzilla-andor boxzilla-muted">' . $text . '</span></td>';
68
+ echo '</tr>';
69
+ } ?>
70
  <tr valign="top" class="boxzilla-rule-row boxzilla-rule-row-<?php echo $key; ?>">
71
  <th style="text-align: right; font-weight: normal;">
72
+ <span class="boxzilla-close boxzilla-remove-rule" title="<?php esc_attr_e('Remove rule', 'boxzilla'); ?>"><span class="dashicons dashicons-dismiss"></span></span>
73
  </th>
74
  <td>
75
  <select class="boxzilla-rule-condition" name="boxzilla_box[rules][<?php echo $key; ?>][condition]">
76
+ <?php foreach ($rule_options as $value => $label) {
77
+ printf('<option value="%s" %s>%s</option>', $value, selected($rule['condition'], $value), $label);
78
+ } ?>
79
  </select>
80
 
81
  <select class="boxzilla-rule-qualifier" name="boxzilla_box[rules][<?php echo $key; ?>][qualifier]" style="min-width: 135px;">
82
+ <option value="1" <?php selected(! isset($rule['qualifier']) || $rule['qualifier']); ?>><?php _e('is', 'boxzilla'); ?></option>
83
+ <option value="0" <?php selected(isset($rule['qualifier']) && !$rule['qualifier']); ?>><?php _e('is not', 'boxzilla'); ?></option>
84
+ <option value="contains" <?php selected(isset($rule['qualifier']) && $rule['qualifier'] === 'contains'); ?> style="display: none;"><?php _e('contains', 'boxzilla'); ?>
85
+ <option value="not_contains" <?php selected(isset($rule['qualifier']) && $rule['qualifier'] === 'not_contains'); ?> style="display: none;"><?php _e('does not contain', 'boxzilla'); ?></option>
86
  </select>
87
 
88
+ <input class="boxzilla-rule-value regular-text" name="boxzilla_box[rules][<?php echo $key; ?>][value]" type="text" value="<?php echo esc_attr($rule['value']); ?>" placeholder="<?php _e('Leave empty for any or enter (comma-separated) names or ID\'s', 'boxzilla'); ?>" style="<?php if (in_array($rule['condition'], array( '', 'everywhere' ))) {
89
+ echo 'display: none;';
90
+ } ?>" />
91
  </td>
92
  </tr>
93
+ <?php $key++;
94
+ } ?>
95
  </tbody>
96
  <tr>
97
  <th></th>
98
+ <td><button type="button" class="button boxzilla-add-rule"><?php _e('Add another rule', 'boxzilla'); ?></button></td>
99
  </tr>
100
  <tr valign="top">
101
+ <th><label for="boxzilla_position"><?php _e('Box Position', 'boxzilla'); ?></label></th>
102
  <td>
103
  <table class="window-positions">
104
  <tr>
105
  <td>
106
+ <?php
107
+ $value = 'top-left'; $label = __('Top Left', 'boxzilla');
108
+ printf('<label><input type="radio" name="boxzilla_box[css][position]" value="%s" %s> &nbsp; %s</label>', $value, checked($opts['css']['position'], $value, false), $label);
109
+ ?>
110
  </td>
111
  <td></td>
112
  <td>
113
+ <?php
114
+ $value = 'top-right'; $label = __('Top Right', 'boxzilla');
115
+ printf('<label><input type="radio" name="boxzilla_box[css][position]" value="%s" %s> &nbsp; %s</label>', $value, checked($opts['css']['position'], $value, false), $label);
116
+ ?>
117
  </td>
118
  </tr>
119
  <tr>
120
  <td></td>
121
  <td>
122
+ <?php
123
+ $value = 'center'; $label = __('Center', 'boxzilla');
124
+ printf('<label><input type="radio" name="boxzilla_box[css][position]" value="%s" %s> &nbsp; %s</label>', $value, checked($opts['css']['position'], $value, false), $label);
125
+ ?>
126
  </td>
127
  <td></td>
128
  </tr>
129
  <tr>
130
  <td>
131
+ <?php
132
+ $value = 'bottom-left'; $label = __('Bottom Left', 'boxzilla');
133
+ printf('<label><input type="radio" name="boxzilla_box[css][position]" value="%s" %s> &nbsp; %s</label>', $value, checked($opts['css']['position'], $value, false), $label);
134
+ ?>
135
  </td>
136
  <td></td>
137
  <td>
138
+ <?php
139
+ $value = 'bottom-right'; $label = __('Bottom Right', 'boxzilla');
140
+ printf('<label><input type="radio" name="boxzilla_box[css][position]" value="%s" %s> &nbsp; %s</label>', $value, checked($opts['css']['position'], $value, false), $label);
141
+ ?>
142
  </td>
143
  </tr>
144
  </table>
145
  </td>
146
  </tr>
147
  <tr valign="top">
148
+ <th><label><?php _e('Animation', 'boxzilla'); ?></label></th>
149
  <td>
150
+ <label><input type="radio" name="boxzilla_box[animation]" value="fade" <?php checked($opts['animation'], 'fade'); ?> /> <?php _e('Fade In', 'boxzilla'); ?></label> &nbsp;
151
+ <label><input type="radio" name="boxzilla_box[animation]" value="slide" <?php checked($opts['animation'], 'slide'); ?> /> <?php _e('Slide In', 'boxzilla'); ?></label>
152
+ <p class="help"><?php _e('Which animation type should be used to show the box when triggered?', 'boxzilla'); ?></p>
153
  </td>
154
  </tr>
155
  <tr valign="top">
156
+ <th><label for="boxzilla_trigger"><?php _e('Auto-show box?', 'boxzilla'); ?></label></th>
157
  <td>
158
+ <label><input type="radio" class="boxzilla-auto-show-trigger" name="boxzilla_box[trigger]" value="" <?php checked($opts['trigger'], ''); ?> /> <?php _e('Never', 'boxzilla'); ?></label><br />
159
+ <label><input type="radio" class="boxzilla-auto-show-trigger" name="boxzilla_box[trigger]" value="time_on_page" <?php checked($opts['trigger'], 'time_on_page'); ?> /> <?php printf(__('Yes, after %s seconds on the page.', 'boxzilla'), '<input type="number" name="boxzilla_box[trigger_time_on_page]" min="0" value="' . esc_attr($opts['trigger_time_on_page']) . '" />'); ?></label><br />
160
+ <label><input type="radio" class="boxzilla-auto-show-trigger" name="boxzilla_box[trigger]" value="percentage" <?php checked($opts['trigger'], 'percentage'); ?> /> <?php printf(__('Yes, when at %s of page height', 'boxzilla'), '<input type="number" name="boxzilla_box[trigger_percentage]" min="0" max="100" value="' . esc_attr($opts['trigger_percentage']) . '" />%'); ?></label><br />
161
+ <label><input type="radio" class="boxzilla-auto-show-trigger" name="boxzilla_box[trigger]" value="element" <?php checked($opts['trigger'], 'element'); ?> /> <?php printf(__('Yes, when at element %s', 'boxzilla'), '<input type="text" name="boxzilla_box[trigger_element]" value="' . esc_attr($opts['trigger_element']) . '" placeholder="' . __('Example: #comments', 'boxzilla') .'" />'); ?></label><br />
162
+ <?php do_action('boxzilla_output_auto_show_trigger_options', $opts); ?>
163
  </td>
164
  </tr>
165
+ <tbody class="boxzilla-trigger-options" style="display: <?php echo ($opts['trigger'] === '') ? 'none' : 'table-row-group'; ?>;">
166
  <tr valign="top">
167
+ <th><label for="boxzilla_cookie"><?php _e('Cookie expiration', 'boxzilla'); ?></label></th>
168
  <td>
169
  <div style="display: inline-block; margin-right: 20px;">
170
+ <label for="boxzilla_cookie_triggered" style="font-weight: bold; display: block;"><?php esc_html_e('Triggered', 'mailchimp-for-wp'); ?></label>
171
  <input type="number" id="boxzilla_cookie_triggered" name="boxzilla_box[cookie][triggered]" min="0" step="1" value="<?php echo esc_attr($opts['cookie']['triggered']); ?>" />
172
+ <small><?php _e('hours', 'boxzilla'); ?></small>
173
  </div>
174
  <div style="display: inline-block;">
175
+ <label for="boxzilla_cookie_dismissed" style="font-weight: bold; display: block;"><?php esc_html_e('Dismissed', 'mailchimp-for-wp'); ?></label>
176
  <input type="number" id="boxzilla_cookie_dismissed" name="boxzilla_box[cookie][dismissed]" min="0" step="1" value="<?php echo esc_attr($opts['cookie']['dismissed']); ?>" />
177
+ <small><?php _e('hours', 'boxzilla'); ?></small>
178
  </div>
179
  <br />
180
 
181
+ <p class="help"><?php _e('After this box is triggered or dismissed, how many hours should it stay hidden?', 'boxzilla'); ?></p>
182
  </td>
183
  </tr>
184
  <tr valign="top">
185
+ <th><label><?php _e('Screen width', 'boxzilla'); ?></label></th>
186
  <td>
187
+ <?php
188
+ $condition_type = $opts['screen_size_condition']['condition'];
189
+ $condition_value = $opts['screen_size_condition']['value'];
190
+ $condition_select = '<select name="boxzilla_box[screen_size_condition][condition]"><option value="larger" '. ($condition_type === 'larger' ? 'selected' : '') .'>' . __('larger', 'boxzilla') . '</option><option value="smaller" '. ($condition_type === 'smaller' ? 'selected' : '') .'>' . __('smaller', 'boxzilla') . '</option></select>'; ?>
191
+ <p><?php printf(__('Only show on screens %s than %s.', 'boxzilla'), $condition_select, '<input type="number" min="0" name="boxzilla_box[screen_size_condition][value]" value="' . esc_attr($condition_value) . '" style="max-width: 70px;" />px'); ?></p>
192
+ <p class="help"><?php _e('Leave empty if you want to show the box on all screen sizes.', 'boxzilla'); ?></p>
193
  </td>
194
 
195
  </tr>
196
  <tr valign="top">
197
+ <th><label for="boxzilla_auto_hide"><?php _e('Auto-hide?', 'boxzilla'); ?></label></th>
198
  <td>
199
+ <label><input type="radio" name="boxzilla_box[auto_hide]" value="1" <?php checked($opts['auto_hide'], 1); ?> /> <?php _e('Yes'); ?></label> &nbsp;
200
+ <label><input type="radio" name="boxzilla_box[auto_hide]" value="0" <?php checked($opts['auto_hide'], 0); ?> /> <?php _e('No'); ?></label> &nbsp;
201
+ <p class="help"><?php _e('Hide box again when visitors scroll back up?', 'boxzilla'); ?></p>
202
  </td>
203
  </tr>
204
  <tr valign="top">
205
+ <th><label for="boxzilla_closable"><?php _e('Show close icon?', 'boxzilla'); ?></label></th>
206
  <td>
207
+ <label><input type="radio" id="boxzilla_closable_1" name="boxzilla_box[show_close_icon]" value="1" <?php checked($opts['show_close_icon'], 1); ?> /> <?php _e('Yes'); ?></label> &nbsp;
208
+ <label><input type="radio" id="boxzilla_closable_0" name="boxzilla_box[show_close_icon]" value="0" <?php checked($opts['show_close_icon'], 0); ?> /> <?php _e('No'); ?></label> &nbsp;
209
  <p class="help">
210
+ <?php _e('If you decide to hide the close icon, make sure to offer an alternative way to close the box.', 'boxzilla'); ?><br />
211
+ <?php _e('Example: ', 'boxzilla'); ?> <code>[boxzilla-close]No, thanks![/boxzilla-close]</code>
212
  </p>
213
  </td>
214
  </tr>
215
  <tr valign="top">
216
+ <th><label for="boxzilla_test_mode"><?php _e('Enable test mode?', 'boxzilla'); ?></label></th>
217
  <td>
218
+ <label><input type="radio" id="boxzilla_test_mode_1" name="boxzilla_global_settings[test_mode]" value="1" <?php checked($global_opts['test_mode'], 1); ?> /> <?php _e('Yes'); ?></label> &nbsp;
219
+ <label><input type="radio" id="boxzilla_test_mode_0" name="boxzilla_global_settings[test_mode]" value="0" <?php checked($global_opts['test_mode'], 0); ?> /> <?php _e('No'); ?></label> &nbsp;
220
+ <p class="help"><?php _e('If test mode is enabled, all boxes will show up regardless of whether a cookie has been set.', 'boxzilla'); ?></p>
221
  </td>
222
  </tr>
223
+ <?php
224
+
225
+ /**
226
+ * @ignore
227
+ */
228
+ do_action('boxzilla_after_box_option_controls', $box, $opts); ?>
229
  </tbody>
230
  </table>
231
 
237
  </tr>
238
  <tr valign="top" class="boxzilla-rule-row boxzilla-rule-row-{{{data.key}}}">
239
  <th style="text-align: right; font-weight: normal;">
240
+ <span class="boxzilla-close boxzilla-remove-rule" title="<?php esc_attr_e('Remove rule', 'boxzilla'); ?>"><span class="dashicons dashicons-dismiss"></span></span>
241
  </th>
242
  <td class="boxzilla-sm">
243
  <select class="boxzilla-rule-condition" name="boxzilla_box[rules][{{{data.key}}}][condition]">
244
+ <?php foreach ($rule_options as $value => $label) {
245
+ printf('<option value="%s" %s %s>%s</option>', $value, disabled($value, '', false), '', $label);
246
+ } ?>
247
  </select>
248
  <select class="boxzilla-rule-qualifier" name="boxzilla_box[rules][{{{data.key}}}][qualifier]" style="display: none; min-width: 135px;" >
249
+ <option value="1" selected><?php _e('is', 'boxzilla'); ?></option>
250
+ <option value="0"><?php _e('is not', 'boxzilla'); ?></option>
251
+ <option value="contains" style="display: none;"><?php _e('contains', 'boxzilla'); ?></option>
252
+ <option value="not_contains" style="display: none;"><?php _e('does not contain', 'boxzilla'); ?></option>
253
  </select>
254
 
255
+ <input class="boxzilla-rule-value regular-text" name="boxzilla_box[rules][{{{data.key}}}][value]" type="text" value="" placeholder="<?php _e('Leave empty for any or enter (comma-separated) names or ID\'s', 'boxzilla'); ?>" style="display: none;" />
256
  </td>
257
  </tr>
258
  </script>
src/admin/views/metaboxes/email-optin.php CHANGED
@@ -1,16 +1,16 @@
1
  <?php
2
- defined( 'ABSPATH' ) or exit;
3
  $user = wp_get_current_user(); ?>
4
  <form action="//dannyvankooten.us1.list-manage.com/subscribe/post?u=a2d08947dcd3683512ce174c5&amp;id=e3e1e0f8d8" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
5
  <p>Get the most out of the Boxzilla plugin by subscribing to our newsletter filled with tips & tricks.</p>
6
 
7
  <p class="mc-field-group">
8
  <label for="mce-EMAIL" style="margin-bottom: 0;">Email Address &nbsp;<span style="color: red;">*</span></label>
9
- <input type="email" value="<?php echo esc_attr( $user->user_email ); ?>" name="EMAIL" class="widefat" id="mce-EMAIL">
10
  </p>
11
  <p class="mc-field-group">
12
  <label for="mce-FNAME" style="margin-bottom: 0;">First Name &nbsp;<small style="font-weight: normal;">(optional)</small></label>
13
- <input type="text" value="<?php echo esc_attr( $user->first_name ); ?>" name="FNAME" class="widefat" id="mce-FNAME">
14
  </p>
15
  <div id="mce-responses" class="clear">
16
  <div class="response" id="mce-error-response" style="display:none"></div>
1
  <?php
2
+ defined('ABSPATH') or exit;
3
  $user = wp_get_current_user(); ?>
4
  <form action="//dannyvankooten.us1.list-manage.com/subscribe/post?u=a2d08947dcd3683512ce174c5&amp;id=e3e1e0f8d8" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
5
  <p>Get the most out of the Boxzilla plugin by subscribing to our newsletter filled with tips & tricks.</p>
6
 
7
  <p class="mc-field-group">
8
  <label for="mce-EMAIL" style="margin-bottom: 0;">Email Address &nbsp;<span style="color: red;">*</span></label>
9
+ <input type="email" value="<?php echo esc_attr($user->user_email); ?>" name="EMAIL" class="widefat" id="mce-EMAIL">
10
  </p>
11
  <p class="mc-field-group">
12
  <label for="mce-FNAME" style="margin-bottom: 0;">First Name &nbsp;<small style="font-weight: normal;">(optional)</small></label>
13
+ <input type="text" value="<?php echo esc_attr($user->first_name); ?>" name="FNAME" class="widefat" id="mce-FNAME">
14
  </p>
15
  <div id="mce-responses" class="clear">
16
  <div class="response" id="mce-error-response" style="display:none"></div>
src/admin/views/metaboxes/need-help.php CHANGED
@@ -1,5 +1,5 @@
1
- <?php defined( 'ABSPATH' ) or exit; ?>
2
- <p><?php _e( 'Please make sure to look at the following available resources.', 'boxzilla' ); ?></p>
3
  <ul class="ul-square">
4
  <li><a href="https://kb.boxzillaplugin.com/">Knowledge Base</a></li>
5
  <li><a href="https://wordpress.org/plugins/boxzilla/faq/">Frequently Asked Questions</a></li>
1
+ <?php defined('ABSPATH') or exit; ?>
2
+ <p><?php _e('Please make sure to look at the following available resources.', 'boxzilla'); ?></p>
3
  <ul class="ul-square">
4
  <li><a href="https://kb.boxzillaplugin.com/">Knowledge Base</a></li>
5
  <li><a href="https://wordpress.org/plugins/boxzilla/faq/">Frequently Asked Questions</a></li>
src/admin/views/settings.php CHANGED
@@ -1,37 +1,37 @@
1
- <?php defined( 'ABSPATH' ) or exit; ?>
2
  <div class="wrap" id="boxzilla-admin" class="boxzilla-settings">
3
 
4
  <div class="boxzilla-row">
5
  <div class="boxzilla-col-two-third">
6
 
7
- <h2><?php _e( 'Settings', 'boxzilla' ); ?></h2>
8
 
9
- <?php do_action( 'boxzilla_before_settings' ); ?>
10
 
11
- <form action="<?php echo admin_url( 'options.php' ); ?>" method="post">
12
 
13
- <?php settings_fields( 'boxzilla_settings' ); ?>
14
 
15
  <table class="form-table">
16
 
17
- <?php do_action( 'boxzilla_before_settings_rows' ); ?>
18
 
19
  <tr valign="top">
20
- <th><label for="boxzilla_test_mode"><?php _e( 'Enable test mode?', 'boxzilla' ); ?></label></th>
21
  <td>
22
- <label><input type="radio" id="boxzilla_test_mode_1" name="boxzilla_settings[test_mode]" value="1" <?php checked( $opts['test_mode'], 1 ); ?> /> <?php _e( 'Yes' ); ?></label> &nbsp;
23
- <label><input type="radio" id="boxzilla_test_mode_0" name="boxzilla_settings[test_mode]" value="0" <?php checked( $opts['test_mode'], 0 ); ?> /> <?php _e( 'No' ); ?></label> &nbsp;
24
- <p class="help"><?php _e( 'If test mode is enabled, all boxes will show up regardless of whether a cookie has been set.', 'boxzilla' ); ?></p>
25
  </td>
26
  </tr>
27
 
28
- <?php do_action( 'boxzilla_after_settings_rows' ); ?>
29
  </table>
30
 
31
  <?php submit_button(); ?>
32
  </form>
33
 
34
- <?php do_action( 'boxzilla_after_settings' ); ?>
35
  </div>
36
 
37
  <div class="boxzilla-sidebar boxzilla-col-one-third">
1
+ <?php defined('ABSPATH') or exit; ?>
2
  <div class="wrap" id="boxzilla-admin" class="boxzilla-settings">
3
 
4
  <div class="boxzilla-row">
5
  <div class="boxzilla-col-two-third">
6
 
7
+ <h2><?php _e('Settings', 'boxzilla'); ?></h2>
8
 
9
+ <?php do_action('boxzilla_before_settings'); ?>
10
 
11
+ <form action="<?php echo admin_url('options.php'); ?>" method="post">
12
 
13
+ <?php settings_fields('boxzilla_settings'); ?>
14
 
15
  <table class="form-table">
16
 
17
+ <?php do_action('boxzilla_before_settings_rows'); ?>
18
 
19
  <tr valign="top">
20
+ <th><label for="boxzilla_test_mode"><?php _e('Enable test mode?', 'boxzilla'); ?></label></th>
21
  <td>
22
+ <label><input type="radio" id="boxzilla_test_mode_1" name="boxzilla_settings[test_mode]" value="1" <?php checked($opts['test_mode'], 1); ?> /> <?php _e('Yes'); ?></label> &nbsp;
23
+ <label><input type="radio" id="boxzilla_test_mode_0" name="boxzilla_settings[test_mode]" value="0" <?php checked($opts['test_mode'], 0); ?> /> <?php _e('No'); ?></label> &nbsp;
24
+ <p class="help"><?php _e('If test mode is enabled, all boxes will show up regardless of whether a cookie has been set.', 'boxzilla'); ?></p>
25
  </td>
26
  </tr>
27
 
28
+ <?php do_action('boxzilla_after_settings_rows'); ?>
29
  </table>
30
 
31
  <?php submit_button(); ?>
32
  </form>
33
 
34
+ <?php do_action('boxzilla_after_settings'); ?>
35
  </div>
36
 
37
  <div class="boxzilla-sidebar boxzilla-col-one-third">
src/class-bootstrapper.php CHANGED
@@ -14,7 +14,8 @@ use InvalidArgumentException;
14
  * @method void ajax( callable $callback )
15
  * @method void cli( callable $callback )
16
  */
17
- class Bootstrapper {
 
18
 
19
  /**
20
  * @var array
@@ -32,14 +33,14 @@ class Bootstrapper {
32
  * @param string $section
33
  * @param callable $callable
34
  */
35
- public function register( $section, $callable ) {
36
-
37
- if( ! isset( $this->bootstrappers[ $section ] ) ) {
38
- throw new InvalidArgumentException( "Section $section is invalid." );
39
  }
40
 
41
- if( ! is_callable( $callable ) ) {
42
- throw new InvalidArgumentException( 'Callable argument is not callable.' );
43
  }
44
 
45
  $this->bootstrappers[ $section ][] = $callable;
@@ -49,9 +50,10 @@ class Bootstrapper {
49
  * @param string $name
50
  * @param array $arguments
51
  */
52
- public function __call( $name, $arguments ) {
53
- if( isset( $this->bootstrappers[ $name ] ) ) {
54
- $this->register( $name, $arguments[0] );
 
55
  }
56
  }
57
 
@@ -60,19 +62,19 @@ class Bootstrapper {
60
  *
61
  * @param string $section
62
  */
63
- public function run( $section = '' ) {
64
-
65
- if( ! $section ) {
66
  $section = $this->section();
67
  }
68
 
69
  // call all global callbacks
70
- foreach( $this->bootstrappers['global'] as $callback ) {
71
  $callback();
72
  }
73
 
74
  // call section specific callbacks
75
- foreach( $this->bootstrappers[ $section ] as $callback ) {
76
  $callback();
77
  }
78
  }
@@ -82,22 +84,22 @@ class Bootstrapper {
82
  *
83
  * @return string
84
  */
85
- public function section() {
86
- if( is_admin() ) {
87
- if( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
 
88
  return 'ajax';
89
  } else {
90
  return 'admin';
91
  }
92
  } else {
93
- if( defined( 'DOING_CRON' ) && DOING_CRON ) {
94
  return 'cron';
95
- } else if( defined( 'WP_CLI' ) && WP_CLI ) {
96
  return 'cli';
97
  } else {
98
  return 'front';
99
  }
100
  }
101
  }
102
-
103
- }
14
  * @method void ajax( callable $callback )
15
  * @method void cli( callable $callback )
16
  */
17
+ class Bootstrapper
18
+ {
19
 
20
  /**
21
  * @var array
33
  * @param string $section
34
  * @param callable $callable
35
  */
36
+ public function register($section, $callable)
37
+ {
38
+ if (! isset($this->bootstrappers[ $section ])) {
39
+ throw new InvalidArgumentException("Section $section is invalid.");
40
  }
41
 
42
+ if (! is_callable($callable)) {
43
+ throw new InvalidArgumentException('Callable argument is not callable.');
44
  }
45
 
46
  $this->bootstrappers[ $section ][] = $callable;
50
  * @param string $name
51
  * @param array $arguments
52
  */
53
+ public function __call($name, $arguments)
54
+ {
55
+ if (isset($this->bootstrappers[ $name ])) {
56
+ $this->register($name, $arguments[0]);
57
  }
58
  }
59
 
62
  *
63
  * @param string $section
64
  */
65
+ public function run($section = '')
66
+ {
67
+ if (! $section) {
68
  $section = $this->section();
69
  }
70
 
71
  // call all global callbacks
72
+ foreach ($this->bootstrappers['global'] as $callback) {
73
  $callback();
74
  }
75
 
76
  // call section specific callbacks
77
+ foreach ($this->bootstrappers[ $section ] as $callback) {
78
  $callback();
79
  }
80
  }
84
  *
85
  * @return string
86
  */
87
+ public function section()
88
+ {
89
+ if (is_admin()) {
90
+ if (defined('DOING_AJAX') && DOING_AJAX) {
91
  return 'ajax';
92
  } else {
93
  return 'admin';
94
  }
95
  } else {
96
+ if (defined('DOING_CRON') && DOING_CRON) {
97
  return 'cron';
98
+ } elseif (defined('WP_CLI') && WP_CLI) {
99
  return 'cli';
100
  } else {
101
  return 'front';
102
  }
103
  }
104
  }
105
+ }
 
src/class-box.php CHANGED
@@ -4,181 +4,187 @@ namespace Boxzilla;
4
 
5
  use WP_Post;
6
 
7
- class Box {
 
8
 
9
- /**
10
- * @var int
11
- */
12
- public $ID;
13
 
14
- /**
15
- * @var array
16
- */
17
- public $options = array();
18
 
19
- /**
20
- * @var string
21
- */
22
- public $title = '';
23
 
24
- /**
25
- * @var string
26
- */
27
- protected $content = '';
28
 
29
- /**
30
- * @var bool
31
- */
32
- public $enabled = false;
33
 
34
  /**
35
  * @var WP_Post
36
  */
37
  private $post;
38
 
39
- /**
40
- * @param WP_Post|int $post
41
- */
42
- public function __construct( $post ) {
43
-
44
- // fetch post if it hasn't been fetched yet
45
- if( ! $post instanceof WP_Post ) {
46
- $post = get_post( $post );
47
- }
48
-
49
- // store ref to post
50
- $this->post = $post;
51
-
52
- // store ID in property for quick access
53
- $this->ID = $post->ID;
54
-
55
- // store title in property
56
- $this->title = $post->post_title;
57
-
58
- // store content in property
59
- $this->content = $post->post_content;
60
-
61
- // is this box enabled?
62
- $this->enabled = ( $post->post_status === 'publish' );
63
-
64
- // load and store options in property
65
- $this->options = $this->load_options();
66
- }
67
-
68
- /**
69
- * Get the options for this box.
70
- **
71
- * @return array Array of box options
72
- */
73
- protected function load_options() {
74
- $defaults = array(
75
- 'css' => array(
76
- 'background_color' => '',
77
- 'color' => '',
78
- 'width' => '',
79
- 'border_color' => '',
80
- 'border_width' => '',
81
- 'border_style' => '',
82
- 'position' => 'bottom-right',
83
- ),
84
- 'rules' => array(
85
- 0 => array(
86
- 'condition' => '',
87
- 'value' => '',
88
- ),
89
- ),
90
- 'rules_comparision' => 'any',
91
- 'cookie' => array(
92
- 'triggered' => 0,
93
- 'dismissed' => 0,
94
- ),
95
- 'trigger' => 'percentage',
96
- 'trigger_percentage' => 65,
97
- 'trigger_element' => '',
98
- 'trigger_time_on_site' => 0,
99
- 'trigger_time_on_page' => 0,
100
- 'animation' => 'fade',
101
- 'auto_hide' => 0,
 
 
102
  'screen_size_condition' => array(
103
  'condition' => 'larger',
104
  'value' => 0,
105
  ),
106
- 'closable' => 1,
107
  'show_close_icon' => 1,
108
- );
109
- $box = $this;
110
-
111
- $options = get_post_meta( $this->ID, 'boxzilla_options', true );
112
- $options = is_array( $options ) ? $options : array();
113
-
114
-
115
- // merge options with default options
116
- $options = array_replace_recursive( $defaults, $options );
117
-
118
- // allow others to filter the final array of options
119
- /**
120
- * Filter the options for a given box
121
- *
122
- * @param array $options
123
- * @param Box $box
124
- */
125
- $options = apply_filters( 'boxzilla_box_options', $options, $box );
126
-
127
- return $options;
128
- }
129
-
130
- /**
131
- * @return bool
132
- */
133
- public function is_enabled() {
134
- return $this->enabled;
135
- }
136
-
137
- /**
138
- * Get the options for this box
139
- *
140
- * @return array
141
- */
142
- public function get_options() {
143
- return $this->options;
144
- }
145
-
146
- /**
147
- * Get the close / hide icon for this box
148
- *
149
- * @return string
150
- */
151
- public function get_close_icon() {
152
-
153
- if( ! $this->options['show_close_icon'] ) {
154
- return '';
 
 
155
  }
156
 
157
- $box = $this;
158
- $html = '&times;';
159
 
160
- /**
161
- * Filters the HTML for the close icon.
162
- *
163
- * @param string $html
164
- * @param Box $box
165
- */
166
- $close_icon = (string) apply_filters( 'boxzilla_box_close_icon', $html, $box );
167
 
168
- return $close_icon;
169
- }
170
 
171
- /**
172
- * Get the content of this box
173
- *
174
- * @return string
175
- */
176
- public function get_content() {
177
- $content = $this->content;
178
- $box = $this;
 
179
 
180
- // replace boxzilla specific shortcodes
181
- $close_link = sprintf('<a href="javascript:Boxzilla.dismiss(%d);">', $this->ID );
182
 
183
  $replacements = array(
184
  '[boxzilla_close]' => $close_link, // accept underscore and dash here for consistency with other shortcode
@@ -187,77 +193,77 @@ class Box {
187
  '[/boxzilla-close]' => '</a>',
188
  );
189
 
190
- $content = str_replace( array_keys( $replacements ), array_values( $replacements ), $content );
191
 
192
  /**
193
- * Filters the HTML for the box content
194
- *
195
- * @param string $content
196
- * @param Box $box
197
- */
198
- $content = apply_filters( 'boxzilla_box_content', $content, $box );
199
- return $content;
200
- }
201
 
202
  /**
203
  * Get options object for JS script.
204
  *
205
  * @return array
206
  */
207
- public function get_client_options() {
208
- $box = $this;
209
-
210
- $trigger = false;
211
- if( $box->options['trigger'] ) {
212
- $trigger = array(
213
- 'method' => $this->options['trigger']
 
214
  );
215
 
216
- if( isset( $this->options[ 'trigger_' . $this->options['trigger'] ] ) ) {
217
- $trigger['value'] = $this->options[ 'trigger_' . $this->options['trigger'] ];
218
- }
219
- }
220
 
221
- // build screenWidthCondition object (or null)
222
- $screen_width_condition = null;
223
- if( $box->options['screen_size_condition']['value'] > 0 ) {
224
  $screen_width_condition = array(
225
  'condition' => $box->options['screen_size_condition']['condition'],
226
- 'value' => intval( $box->options['screen_size_condition']['value'] ),
227
  );
228
  }
229
 
230
- $client_options = array(
231
- 'id' => $box->ID,
232
- 'icon' => $box->get_close_icon(),
233
- 'content' => '', // we grab this later from an HTML element
234
- 'css' => array_filter( $box->options['css'] ),
235
- 'trigger' => $trigger,
236
- 'animation' => $box->options['animation'],
237
- 'cookie' => array(
238
- 'triggered' => absint( $box->options['cookie']['triggered'] ),
239
- 'dismissed' => absint( $box->options['cookie']['dismissed'] ),
240
- ),
241
- 'rehide' => (bool) $box->options['auto_hide'],
242
- 'position' => $box->options['css']['position'],
243
  'screenWidthCondition' => $screen_width_condition,
244
- 'closable' => !!$box->options['closable'],
245
  'post' => array(
246
  'id' => $this->post->ID,
247
  'title' => $this->post->post_title,
248
  'slug' => $this->post->post_name,
249
  ),
250
- );
251
-
252
- /**
253
- * Filter the final options for the JS Boxzilla client.
254
- *
255
- * @param array $client_options
256
- * @param Box $box
257
- */
258
- $client_options = apply_filters( 'boxzilla_box_client_options', $client_options, $box );
259
-
260
- return $client_options;
261
- }
262
 
 
 
 
 
 
 
 
 
 
 
263
  }
4
 
5
  use WP_Post;
6
 
7
+ class Box
8
+ {
9
 
10
+ /**
11
+ * @var int
12
+ */
13
+ public $ID;
14
 
15
+ /**
16
+ * @var array
17
+ */
18
+ public $options = array();
19
 
20
+ /**
21
+ * @var string
22
+ */
23
+ public $title = '';
24
 
25
+ /**
26
+ * @var string
27
+ */
28
+ protected $content = '';
29
 
30
+ /**
31
+ * @var bool
32
+ */
33
+ public $enabled = false;
34
 
35
  /**
36
  * @var WP_Post
37
  */
38
  private $post;
39
 
40
+ /**
41
+ * @param WP_Post|int $post
42
+ */
43
+ public function __construct($post)
44
+ {
45
+
46
+ // fetch post if it hasn't been fetched yet
47
+ if (! $post instanceof WP_Post) {
48
+ $post = get_post($post);
49
+ }
50
+
51
+ // store ref to post
52
+ $this->post = $post;
53
+
54
+ // store ID in property for quick access
55
+ $this->ID = $post->ID;
56
+
57
+ // store title in property
58
+ $this->title = $post->post_title;
59
+
60
+ // store content in property
61
+ $this->content = $post->post_content;
62
+
63
+ // is this box enabled?
64
+ $this->enabled = ($post->post_status === 'publish');
65
+
66
+ // load and store options in property
67
+ $this->options = $this->load_options();
68
+ }
69
+
70
+ /**
71
+ * Get the options for this box.
72
+ **
73
+ * @return array Array of box options
74
+ */
75
+ protected function load_options()
76
+ {
77
+ $defaults = array(
78
+ 'css' => array(
79
+ 'background_color' => '',
80
+ 'color' => '',
81
+ 'width' => '',
82
+ 'border_color' => '',
83
+ 'border_width' => '',
84
+ 'border_style' => '',
85
+ 'position' => 'bottom-right',
86
+ ),
87
+ 'rules' => array(
88
+ 0 => array(
89
+ 'condition' => '',
90
+ 'value' => '',
91
+ ),
92
+ ),
93
+ 'rules_comparision' => 'any',
94
+ 'cookie' => array(
95
+ 'triggered' => 0,
96
+ 'dismissed' => 0,
97
+ ),
98
+ 'trigger' => 'percentage',
99
+ 'trigger_percentage' => 65,
100
+ 'trigger_element' => '',
101
+ 'trigger_time_on_site' => 0,
102
+ 'trigger_time_on_page' => 0,
103
+ 'animation' => 'fade',
104
+ 'auto_hide' => 0,
105
  'screen_size_condition' => array(
106
  'condition' => 'larger',
107
  'value' => 0,
108
  ),
109
+ 'closable' => 1,
110
  'show_close_icon' => 1,
111
+ );
112
+ $box = $this;
113
+
114
+ $options = get_post_meta($this->ID, 'boxzilla_options', true);
115
+ $options = is_array($options) ? $options : array();
116
+
117
+
118
+ // merge options with default options
119
+ $options = array_replace_recursive($defaults, $options);
120
+
121
+ // allow others to filter the final array of options
122
+ /**
123
+ * Filter the options for a given box
124
+ *
125
+ * @param array $options
126
+ * @param Box $box
127
+ */
128
+ $options = apply_filters('boxzilla_box_options', $options, $box);
129
+
130
+ return $options;
131
+ }
132
+
133
+ /**
134
+ * @return bool
135
+ */
136
+ public function is_enabled()
137
+ {
138
+ return $this->enabled;
139
+ }
140
+
141
+ /**
142
+ * Get the options for this box
143
+ *
144
+ * @return array
145
+ */
146
+ public function get_options()
147
+ {
148
+ return $this->options;
149
+ }
150
+
151
+ /**
152
+ * Get the close / hide icon for this box
153
+ *
154
+ * @return string
155
+ */
156
+ public function get_close_icon()
157
+ {
158
+ if (! $this->options['show_close_icon']) {
159
+ return '';
160
  }
161
 
162
+ $box = $this;
163
+ $html = '&times;';
164
 
165
+ /**
166
+ * Filters the HTML for the close icon.
167
+ *
168
+ * @param string $html
169
+ * @param Box $box
170
+ */
171
+ $close_icon = (string) apply_filters('boxzilla_box_close_icon', $html, $box);
172
 
173
+ return $close_icon;
174
+ }
175
 
176
+ /**
177
+ * Get the content of this box
178
+ *
179
+ * @return string
180
+ */
181
+ public function get_content()
182
+ {
183
+ $content = $this->content;
184
+ $box = $this;
185
 
186
+ // replace boxzilla specific shortcodes
187
+ $close_link = sprintf('<a href="javascript:Boxzilla.dismiss(%d);">', $this->ID);
188
 
189
  $replacements = array(
190
  '[boxzilla_close]' => $close_link, // accept underscore and dash here for consistency with other shortcode
193
  '[/boxzilla-close]' => '</a>',
194
  );
195
 
196
+ $content = str_replace(array_keys($replacements), array_values($replacements), $content);
197
 
198
  /**
199
+ * Filters the HTML for the box content
200
+ *
201
+ * @param string $content
202
+ * @param Box $box
203
+ */
204
+ $content = apply_filters('boxzilla_box_content', $content, $box);
205
+ return $content;
206
+ }
207
 
208
  /**
209
  * Get options object for JS script.
210
  *
211
  * @return array
212
  */
213
+ public function get_client_options()
214
+ {
215
+ $box = $this;
216
+
217
+ $trigger = false;
218
+ if ($box->options['trigger']) {
219
+ $trigger = array(
220
+ 'method' => $this->options['trigger']
221
  );
222
 
223
+ if (isset($this->options[ 'trigger_' . $this->options['trigger'] ])) {
224
+ $trigger['value'] = $this->options[ 'trigger_' . $this->options['trigger'] ];
225
+ }
226
+ }
227
 
228
+ // build screenWidthCondition object (or null)
229
+ $screen_width_condition = null;
230
+ if ($box->options['screen_size_condition']['value'] > 0) {
231
  $screen_width_condition = array(
232
  'condition' => $box->options['screen_size_condition']['condition'],
233
+ 'value' => intval($box->options['screen_size_condition']['value']),
234
  );
235
  }
236
 
237
+ $client_options = array(
238
+ 'id' => $box->ID,
239
+ 'icon' => $box->get_close_icon(),
240
+ 'content' => '', // we grab this later from an HTML element
241
+ 'css' => array_filter($box->options['css']),
242
+ 'trigger' => $trigger,
243
+ 'animation' => $box->options['animation'],
244
+ 'cookie' => array(
245
+ 'triggered' => absint($box->options['cookie']['triggered']),
246
+ 'dismissed' => absint($box->options['cookie']['dismissed']),
247
+ ),
248
+ 'rehide' => (bool) $box->options['auto_hide'],
249
+ 'position' => $box->options['css']['position'],
250
  'screenWidthCondition' => $screen_width_condition,
251
+ 'closable' => !!$box->options['closable'],
252
  'post' => array(
253
  'id' => $this->post->ID,
254
  'title' => $this->post->post_title,
255
  'slug' => $this->post->post_name,
256
  ),
257
+ );
 
 
 
 
 
 
 
 
 
 
 
258
 
259
+ /**
260
+ * Filter the final options for the JS Boxzilla client.
261
+ *
262
+ * @param array $client_options
263
+ * @param Box $box
264
+ */
265
+ $client_options = apply_filters('boxzilla_box_client_options', $client_options, $box);
266
+
267
+ return $client_options;
268
+ }
269
  }
src/class-boxzilla-service-provider.php CHANGED
@@ -7,68 +7,63 @@ use Boxzilla\Admin\Notices;
7
  use Boxzilla\DI\Container;
8
  use Boxzilla\DI\ServiceProviderInterface;
9
 
10
- class BoxzillaServiceProvider implements ServiceProviderInterface {
11
-
12
- /**
13
- * Registers services on the given container.
14
- *
15
- * This method should only be used to configure services and parameters.
16
- * It should not get services.
17
- *
18
- * @param Container $container An Container instance
19
- */
20
- public function register( Container $container ) {
21
-
22
- $container['admin'] = function( $container ) {
23
- return new Admin( $container->plugin, $container );
24
- };
25
-
26
- $container['bootstrapper'] = new Bootstrapper();
27
-
28
- $container['box_loader'] = function( $container ) {
29
- return new BoxLoader( $container->plugin, $container->options );
30
- };
31
-
32
- $container['filter.autocomplete'] = function( $container ) {
33
- return new Filter\Autocomplete();
34
- };
35
-
36
- $container['notices'] = function( $container ) {
37
- return new Notices();
38
- };
39
-
40
- $container['options'] = function( $container ) {
41
- $defaults = array(
42
- 'test_mode' => 0
43
- );
44
-
45
- $options = (array) get_option( 'boxzilla_settings', $defaults );
46
- $options = array_merge( $defaults, $options );
47
- return $options;
48
- };
49
-
50
- $container['plugin'] = new Plugin(
51
- 'boxzilla',
52
- 'Boxzilla',
53
- BOXZILLA_VERSION,
54
- BOXZILLA_FILE,
55
- dirname( BOXZILLA_FILE )
56
- );
57
-
58
- $container['plugins'] = function( $container ) {
59
- $raw = (array) apply_filters( 'boxzilla_extensions', array() );
60
-
61
- $plugins = array();
62
- foreach( $raw as $p ) {
63
- $plugins[ $p->id() ] = $p;
64
- }
65
- return $plugins;
66
- };
67
-
68
-
69
-
70
-
71
-
72
-
73
- }
74
  }
7
  use Boxzilla\DI\Container;
8
  use Boxzilla\DI\ServiceProviderInterface;
9
 
10
+ class BoxzillaServiceProvider implements ServiceProviderInterface
11
+ {
12
+
13
+ /**
14
+ * Registers services on the given container.
15
+ *
16
+ * This method should only be used to configure services and parameters.
17
+ * It should not get services.
18
+ *
19
+ * @param Container $container An Container instance
20
+ */
21
+ public function register(Container $container)
22
+ {
23
+ $container['admin'] = function ($container) {
24
+ return new Admin($container->plugin, $container);
25
+ };
26
+
27
+ $container['bootstrapper'] = new Bootstrapper();
28
+
29
+ $container['box_loader'] = function ($container) {
30
+ return new BoxLoader($container->plugin, $container->options);
31
+ };
32
+
33
+ $container['filter.autocomplete'] = function ($container) {
34
+ return new Filter\Autocomplete();
35
+ };
36
+
37
+ $container['notices'] = function ($container) {
38
+ return new Notices();
39
+ };
40
+
41
+ $container['options'] = function ($container) {
42
+ $defaults = array(
43
+ 'test_mode' => 0
44
+ );
45
+
46
+ $options = (array) get_option('boxzilla_settings', $defaults);
47
+ $options = array_merge($defaults, $options);
48
+ return $options;
49
+ };
50
+
51
+ $container['plugin'] = new Plugin(
52
+ 'boxzilla',
53
+ 'Boxzilla',
54
+ BOXZILLA_VERSION,
55
+ BOXZILLA_FILE,
56
+ dirname(BOXZILLA_FILE)
57
+ );
58
+
59
+ $container['plugins'] = function ($container) {
60
+ $raw = (array) apply_filters('boxzilla_extensions', array());
61
+
62
+ $plugins = array();
63
+ foreach ($raw as $p) {
64
+ $plugins[ $p->id() ] = $p;
65
+ }
66
+ return $plugins;
67
+ };
68
+ }
 
 
 
 
 
69
  }
src/class-boxzilla.php CHANGED
@@ -17,4 +17,6 @@ use Boxzilla\Licensing\License;
17
  * @property Bootstrapper $bootstrapper
18
  *
19
  */
20
- class Boxzilla extends ContainerWithPropertyAccess {}
 
 
17
  * @property Bootstrapper $bootstrapper
18
  *
19
  */
20
+ class Boxzilla extends ContainerWithPropertyAccess
21
+ {
22
+ }
src/class-loader.php CHANGED
@@ -1,332 +1,336 @@
1
- <?php
2
-
3
- namespace Boxzilla;
4
-
5
- class BoxLoader {
6
-
7
- /**
8
- * @var Plugin
9
- */
10
- private $plugin;
11
-
12
- /**
13
- * @var array
14
- */
15
- private $box_ids_to_load = array();
16
-
17
- /**
18
- * @var array
19
- */
20
- protected $options;
21
-
22
- /**
23
- * Constructor
24
- *
25
- * @param Plugin $plugin
26
- * @param array $options
27
- */
28
- public function __construct( Plugin $plugin, array $options ) {
29
- $this->plugin = $plugin;
30
- $this->options = $options;
31
- }
32
-
33
- /**
34
- * Initializes the plugin, runs on `wp` hook.
35
- */
36
- public function init() {
37
-
38
- $this->box_ids_to_load = $this->filter_boxes();
39
-
40
- // Only add other hooks if necessary
41
- if( count( $this->box_ids_to_load ) > 0 ) {
42
- add_action( 'wp_footer', array( $this, 'print_boxes_content' ), 1 );
43
- add_action( 'wp_enqueue_scripts', array( $this, 'load_assets' ), 90 );
44
- }
45
- }
46
-
47
- /**
48
- * Get global rules for all boxes
49
- *
50
- * @return array
51
- */
52
- protected function get_filter_rules() {
53
- $rules = get_option( 'boxzilla_rules', array() );
54
-
55
- if( ! is_array( $rules ) ) {
56
- return array();
57
- }
58
-
59
- return $rules;
60
- }
61
-
62
-
63
- /**
64
- * Match a string against an array of patterns, glob-style.
65
- *
66
- * @param string $string
67
- * @param array $patterns
68
- *
69
- * @return boolean
70
- */
71
- protected function match_patterns( $string, $patterns, $contains = false ) {
72
- $string = strtolower( $string );
73
-
74
- foreach( $patterns as $pattern ) {
75
- $pattern = rtrim( $pattern, '/' );
76
- $pattern = strtolower( $pattern );
77
-
78
- // contains means we should do a simple occurrence check
79
- // does not support wildcards
80
- if( $contains ) {
81
- return strpos( $string, $pattern ) !== false;
82
- }
83
-
84
- if( function_exists( 'fnmatch' ) ) {
85
- $match = fnmatch( $pattern, $string );
86
- } else {
87
- $match = ( $pattern === $string );
88
- }
89
-
90
- if( $match ) {
91
- return true;
92
- }
93
- }
94
-
95
- return false;
96
- }
97
-
98
- /**
99
- * @return string
100
- */
101
- protected function get_request_url() {
102
- // strip trailing slashes
103
- $request_uri = rtrim( $_SERVER['REQUEST_URI'], '/' );
104
- return $request_uri;
105
- }
106
-
107
- /**
108
- * Check if this rule passes (conditional matches expected value)
109
- *
110
- * @param string $condition
111
- * @param string $value
112
- * @param boolean $qualifier
113
- *
114
- * @return bool
115
- */
116
- protected function match_rule( $condition, $value, $qualifier = true ) {
117
-
118
- $matched = false;
119
-
120
- // cast value to array & trim whitespace or excess comma's
121
- $value = array_map( 'trim', explode( ',', rtrim( trim( $value ), ',' ) ) );
122
-
123
- switch ( $condition ) {
124
- case 'everywhere';
125
- $matched = true;
126
- break;
127
-
128
- case 'is_url':
129
- $url = $this->get_request_url();
130
- $matched = $this->match_patterns( $url, $value, $qualifier === 'contains' || $qualifier === 'not_contains' );
131
- break;
132
-
133
- case 'is_referer':
134
- if( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
135
- $referer = $_SERVER['HTTP_REFERER'];
136
- $matched = $this->match_patterns( $referer, $value, $qualifier === 'contains' || $qualifier === 'not_contains' );
137
- }
138
- break;
139
-
140
- case 'is_post_type':
141
- $post_type = (string) get_post_type();
142
- $matched = in_array( $post_type, (array) $value );
143
- break;
144
-
145
- case 'is_single':
146
- case 'is_post':
147
- // convert to empty string if array with just empty string in it
148
- $value = ( $value === array( '' ) ) ? '' : $value;
149
- $matched = is_single( $value );
150
- break;
151
-
152
- case 'is_post_in_category':
153
- $matched = is_singular( 'post' ) && has_category( $value );
154
- break;
155
-
156
-
157
- case 'is_page':
158
- $matched = is_page( $value );
159
- break;
160
-
161
- case 'is_post_with_tag':
162
- $matched = is_singular( 'post' ) && has_tag( $value );
163
- break;
164
-
165
- case 'is_user_logged_in':
166
- $matched = is_user_logged_in();
167
- break;
168
- }
169
-
170
- /**
171
- * Filters whether a given box rule matches the condition and expected value.
172
- *
173
- * The dynamic portion of the hook, `$condition`, refers to the condition being matched.
174
- *
175
- * @param boolean $matched
176
- * @param array $value
177
- */
178
- $matched = apply_filters( 'boxzilla_box_rule_matches_' . $condition, $matched, $value );
179
-
180
- // if qualifier is set to false, we need to reverse this value here.
181
- if( ! $qualifier || $qualifier === 'not_contains' ) {
182
- $matched = ! $matched;
183
- }
184
-
185
- return $matched;
186
- }
187
-
188
- /**
189
- * Checks which boxes should be loaded for this request.
190
- *
191
- * @return array
192
- */
193
- private function filter_boxes() {
194
-
195
- $box_ids_to_load = array();
196
- $rules = $this->get_filter_rules();
197
-
198
- foreach( $rules as $box_id => $box_rules ) {
199
-
200
- $matched = false;
201
- $comparision = isset( $box_rules['comparision'] ) ? $box_rules['comparision'] : 'any';
202
-
203
- // loop through all rules for all boxes
204
- foreach ( $box_rules as $rule ) {
205
-
206
- // skip faulty values (and comparision rule)
207
- if( empty( $rule['condition'] ) ) {
208
- continue;
209
- }
210
-
211
- $qualifier = isset( $rule['qualifier'] ) ? $rule['qualifier'] : true;
212
- $matched = $this->match_rule( $rule['condition'], $rule['value'], $qualifier );
213
-
214
- // break out of loop if we've already matched
215
- if( $comparision === 'any' && $matched ) {
216
- break;
217
- }
218
-
219
- // no need to continue if this rule didn't match
220
- if( $comparision === 'all' && ! $matched ) {
221
- break;
222
- }
223
- }
224
-
225
- // value of $matched at this point determines whether box should be loaded
226
- $load_box = $matched;
227
-
228
- /**
229
- * Filters whether a box should be loaded into the page HTML.
230
- *
231
- * The dynamic portion of the hook, `$box_id`, refers to the ID of the box. Return true if you want to output the box.
232
- *
233
- * @param boolean $load_box
234
- */
235
- $load_box = apply_filters( 'boxzilla_load_box_' . $box_id, $load_box );
236
-
237
- /**
238
- * Filters whether a box should be loaded into the page HTML.
239
- *
240
- * @param boolean $load_box
241
- * @param int $box_id
242
- */
243
- $load_box = apply_filters( 'boxzilla_load_box', $load_box, $box_id );
244
-
245
- // if matched, box should be loaded on this page
246
- if ( $load_box ) {
247
- $box_ids_to_load[] = $box_id;
248
- }
249
-
250
- }
251
-
252
- return $box_ids_to_load;
253
- }
254
-
255
- /**
256
- * Load plugin styles
257
- */
258
- public function load_assets() {
259
- $pre_suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
260
- wp_enqueue_script('boxzilla', $this->plugin->url('/assets/js/script' . $pre_suffix . '.js' ), array(), $this->plugin->version(), true );
261
-
262
- // create boxzilla_Global_Options object
263
- $plugin_options = $this->options;
264
- $boxes = $this->get_matched_boxes();
265
-
266
- $data = array(
267
- 'testMode' => (bool) $plugin_options['test_mode'],
268
- 'boxes' => (array) array_map( function(Box $box) { return $box->get_client_options(); }, $boxes ),
269
- );
270
-
271
- wp_localize_script( 'boxzilla', 'boxzilla_options', $data );
272
-
273
- do_action( 'boxzilla_load_assets', $this );
274
- }
275
-
276
- public function print_boxes_content() {
277
- $boxes = $this->get_matched_boxes();
278
- if( empty( $boxes ) ) {
279
- return;
280
- }
281
-
282
- echo '<div style="display: none;">';
283
- foreach( $boxes as $box ) {
284
- echo sprintf( '<div id="boxzilla-box-%d-content">', $box->ID ) . $box->get_content() . '</div>';
285
- }
286
- echo '</div>';
287
- }
288
-
289
- /**
290
- * Get an array of Box objects. These are the boxes that will be loaded for the current request.
291
- *
292
- * @return Box[]
293
- */
294
- public function get_matched_boxes() {
295
- static $boxes;
296
-
297
- if( is_null( $boxes ) ) {
298
-
299
- if( count( $this->box_ids_to_load ) === 0 ) {
300
- $boxes = array();
301
- return $boxes;
302
- }
303
-
304
- // query Box posts
305
- $posts = get_posts(
306
- array(
307
- 'post_type' => 'boxzilla-box',
308
- 'post_status' => 'publish',
309
- 'post__in' => $this->box_ids_to_load,
310
- 'numberposts' => -1
311
- )
312
- );
313
-
314
- // create `Box` instances out of \WP_Post instances
315
- $boxes = array();
316
- foreach ( $posts as $key => $post ) {
317
- // skip posts with no content
318
- $post_content = trim( $post->post_content );
319
- if( empty( $post_content ) ) {
320
- continue;
321
- }
322
-
323
- $boxes[ $key ] = new Box( $post );
324
- }
325
- }
326
-
327
- return $boxes;
328
- }
329
-
330
- }
331
-
332
-
 
 
 
 
1
+ <?php
2
+
3
+ namespace Boxzilla;
4
+
5
+ class BoxLoader
6
+ {
7
+
8
+ /**
9
+ * @var Plugin
10
+ */
11
+ private $plugin;
12
+
13
+ /**
14
+ * @var array
15
+ */
16
+ private $box_ids_to_load = array();
17
+
18
+ /**
19
+ * @var array
20
+ */
21
+ protected $options;
22
+
23
+ /**
24
+ * Constructor
25
+ *
26
+ * @param Plugin $plugin
27
+ * @param array $options
28
+ */
29
+ public function __construct(Plugin $plugin, array $options)
30
+ {
31
+ $this->plugin = $plugin;
32
+ $this->options = $options;
33
+ }
34
+
35
+ /**
36
+ * Initializes the plugin, runs on `wp` hook.
37
+ */
38
+ public function init()
39
+ {
40
+ $this->box_ids_to_load = $this->filter_boxes();
41
+
42
+ // Only add other hooks if necessary
43
+ if (count($this->box_ids_to_load) > 0) {
44
+ add_action('wp_footer', array( $this, 'print_boxes_content' ), 1);
45
+ add_action('wp_enqueue_scripts', array( $this, 'load_assets' ), 90);
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Get global rules for all boxes
51
+ *
52
+ * @return array
53
+ */
54
+ protected function get_filter_rules()
55
+ {
56
+ $rules = get_option('boxzilla_rules', array());
57
+
58
+ if (! is_array($rules)) {
59
+ return array();
60
+ }
61
+
62
+ return $rules;
63
+ }
64
+
65
+
66
+ /**
67
+ * Match a string against an array of patterns, glob-style.
68
+ *
69
+ * @param string $string
70
+ * @param array $patterns
71
+ *
72
+ * @return boolean
73
+ */
74
+ protected function match_patterns($string, $patterns, $contains = false)
75
+ {
76
+ $string = strtolower($string);
77
+
78
+ foreach ($patterns as $pattern) {
79
+ $pattern = rtrim($pattern, '/');
80
+ $pattern = strtolower($pattern);
81
+
82
+ // contains means we should do a simple occurrence check
83
+ // does not support wildcards
84
+ if ($contains) {
85
+ return strpos($string, $pattern) !== false;
86
+ }
87
+
88
+ if (function_exists('fnmatch')) {
89
+ $match = fnmatch($pattern, $string);
90
+ } else {
91
+ $match = ($pattern === $string);
92
+ }
93
+
94
+ if ($match) {
95
+ return true;
96
+ }
97
+ }
98
+
99
+ return false;
100
+ }
101
+
102
+ /**
103
+ * @return string
104
+ */
105
+ protected function get_request_url()
106
+ {
107
+ // strip trailing slashes
108
+ $request_uri = rtrim($_SERVER['REQUEST_URI'], '/');
109
+ return $request_uri;
110
+ }
111
+
112
+ /**
113
+ * Check if this rule passes (conditional matches expected value)
114
+ *
115
+ * @param string $condition
116
+ * @param string $value
117
+ * @param boolean $qualifier
118
+ *
119
+ * @return bool
120
+ */
121
+ protected function match_rule($condition, $value, $qualifier = true)
122
+ {
123
+ $matched = false;
124
+
125
+ // cast value to array & trim whitespace or excess comma's
126
+ $value = array_map('trim', explode(',', rtrim(trim($value), ',')));
127
+
128
+ switch ($condition) {
129
+ case 'everywhere':
130
+ $matched = true;
131
+ break;
132
+
133
+ case 'is_url':
134
+ $url = $this->get_request_url();
135
+ $matched = $this->match_patterns($url, $value, $qualifier === 'contains' || $qualifier === 'not_contains');
136
+ break;
137
+
138
+ case 'is_referer':
139
+ if (! empty($_SERVER['HTTP_REFERER'])) {
140
+ $referer = $_SERVER['HTTP_REFERER'];
141
+ $matched = $this->match_patterns($referer, $value, $qualifier === 'contains' || $qualifier === 'not_contains');
142
+ }
143
+ break;
144
+
145
+ case 'is_post_type':
146
+ $post_type = (string) get_post_type();
147
+ $matched = in_array($post_type, (array) $value);
148
+ break;
149
+
150
+ case 'is_single':
151
+ case 'is_post':
152
+ // convert to empty string if array with just empty string in it
153
+ $value = ($value === array( '' )) ? '' : $value;
154
+ $matched = is_single($value);
155
+ break;
156
+
157
+ case 'is_post_in_category':
158
+ $matched = is_singular('post') && has_category($value);
159
+ break;
160
+
161
+
162
+ case 'is_page':
163
+ $matched = is_page($value);
164
+ break;
165
+
166
+ case 'is_post_with_tag':
167
+ $matched = is_singular('post') && has_tag($value);
168
+ break;
169
+
170
+ case 'is_user_logged_in':
171
+ $matched = is_user_logged_in();
172
+ break;
173
+ }
174
+
175
+ /**
176
+ * Filters whether a given box rule matches the condition and expected value.
177
+ *
178
+ * The dynamic portion of the hook, `$condition`, refers to the condition being matched.
179
+ *
180
+ * @param boolean $matched
181
+ * @param array $value
182
+ */
183
+ $matched = apply_filters('boxzilla_box_rule_matches_' . $condition, $matched, $value);
184
+
185
+ // if qualifier is set to false, we need to reverse this value here.
186
+ if (! $qualifier || $qualifier === 'not_contains') {
187
+ $matched = ! $matched;
188
+ }
189
+
190
+ return $matched;
191
+ }
192
+
193
+ /**
194
+ * Checks which boxes should be loaded for this request.
195
+ *
196
+ * @return array
197
+ */
198
+ private function filter_boxes()
199
+ {
200
+ $box_ids_to_load = array();
201
+ $rules = $this->get_filter_rules();
202
+
203
+ foreach ($rules as $box_id => $box_rules) {
204
+ $matched = false;
205
+ $comparision = isset($box_rules['comparision']) ? $box_rules['comparision'] : 'any';
206
+
207
+ // loop through all rules for all boxes
208
+ foreach ($box_rules as $rule) {
209
+
210
+ // skip faulty values (and comparision rule)
211
+ if (empty($rule['condition'])) {
212
+ continue;
213
+ }
214
+
215
+ $qualifier = isset($rule['qualifier']) ? $rule['qualifier'] : true;
216
+ $matched = $this->match_rule($rule['condition'], $rule['value'], $qualifier);
217
+
218
+ // break out of loop if we've already matched
219
+ if ($comparision === 'any' && $matched) {
220
+ break;
221
+ }
222
+
223
+ // no need to continue if this rule didn't match
224
+ if ($comparision === 'all' && ! $matched) {
225
+ break;
226
+ }
227
+ }
228
+
229
+ // value of $matched at this point determines whether box should be loaded
230
+ $load_box = $matched;
231
+
232
+ /**
233
+ * Filters whether a box should be loaded into the page HTML.
234
+ *
235
+ * The dynamic portion of the hook, `$box_id`, refers to the ID of the box. Return true if you want to output the box.
236
+ *
237
+ * @param boolean $load_box
238
+ */
239
+ $load_box = apply_filters('boxzilla_load_box_' . $box_id, $load_box);
240
+
241
+ /**
242
+ * Filters whether a box should be loaded into the page HTML.
243
+ *
244
+ * @param boolean $load_box
245
+ * @param int $box_id
246
+ */
247
+ $load_box = apply_filters('boxzilla_load_box', $load_box, $box_id);
248
+
249
+ // if matched, box should be loaded on this page
250
+ if ($load_box) {
251
+ $box_ids_to_load[] = $box_id;
252
+ }
253
+ }
254
+
255
+ return $box_ids_to_load;
256
+ }
257
+
258
+ /**
259
+ * Load plugin styles
260
+ */
261
+ public function load_assets()
262
+ {
263
+ $pre_suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
264
+ wp_enqueue_script('boxzilla', $this->plugin->url('/assets/js/script' . $pre_suffix . '.js'), array(), $this->plugin->version(), true);
265
+
266
+ // create boxzilla_Global_Options object
267
+ $plugin_options = $this->options;
268
+ $boxes = $this->get_matched_boxes();
269
+
270
+ $data = array(
271
+ 'testMode' => (bool) $plugin_options['test_mode'],
272
+ 'boxes' => (array) array_map(function (Box $box) {
273
+ return $box->get_client_options();
274
+ }, $boxes),
275
+ );
276
+
277
+ wp_localize_script('boxzilla', 'boxzilla_options', $data);
278
+
279
+ do_action('boxzilla_load_assets', $this);
280
+ }
281
+
282
+ public function print_boxes_content()
283
+ {
284
+ $boxes = $this->get_matched_boxes();
285
+ if (empty($boxes)) {
286
+ return;
287
+ }
288
+
289
+ echo '<div style="display: none;">';
290
+ foreach ($boxes as $box) {
291
+ echo sprintf('<div id="boxzilla-box-%d-content">', $box->ID) . $box->get_content() . '</div>';
292
+ }
293
+ echo '</div>';
294
+ }
295
+
296
+ /**
297
+ * Get an array of Box objects. These are the boxes that will be loaded for the current request.
298
+ *
299
+ * @return Box[]
300
+ */
301
+ public function get_matched_boxes()
302
+ {
303
+ static $boxes;
304
+
305
+ if (is_null($boxes)) {
306
+ if (count($this->box_ids_to_load) === 0) {
307
+ $boxes = array();
308
+ return $boxes;
309
+ }
310
+
311
+ // query Box posts
312
+ $posts = get_posts(
313
+ array(
314
+ 'post_type' => 'boxzilla-box',
315
+ 'post_status' => 'publish',
316
+ 'post__in' => $this->box_ids_to_load,
317
+ 'numberposts' => -1
318
+ )
319
+ );
320
+
321
+ // create `Box` instances out of \WP_Post instances
322
+ $boxes = array();
323
+ foreach ($posts as $key => $post) {
324
+ // skip posts with no content
325
+ $post_content = trim($post->post_content);
326
+ if (empty($post_content)) {
327
+ continue;
328
+ }
329
+
330
+ $boxes[ $key ] = new Box($post);
331
+ }
332
+ }
333
+
334
+ return $boxes;
335
+ }
336
+ }
src/class-php-fallback.php CHANGED
@@ -1,62 +1,64 @@
1
  <?php
2
 
3
- class Boxzilla_PHP_Fallback {
4
-
5
- /**
6
- * @var string
7
- */
8
- private $plugin_name = '';
9
-
10
- /**
11
- * @var string
12
- */
13
- private $plugin_file = '';
14
-
15
- /**
16
- * @param $plugin_name
17
- * @param $plugin_file
18
- */
19
- public function __construct( $plugin_name, $plugin_file ) {
20
-
21
- $this->plugin_name = $plugin_name;
22
- $this->plugin_file = $plugin_file;
23
-
24
- // deactivate plugin straight away
25
- add_action( 'admin_init', array( $this, 'deactivate_self' ) );
26
- }
27
-
28
- /**
29
- * @return bool
30
- */
31
- public function deactivate_self() {
32
- if( ! current_user_can( 'activate_plugins' ) ) {
33
- return false;
34
- }
35
-
36
- // deactivate self
37
- deactivate_plugins( $this->plugin_file );
38
-
39
- // get rid of "Plugin activated" notice
40
- if( isset( $_GET['activate'] ) ) {
41
- unset( $_GET['activate'] );
42
- }
43
-
44
- // show notice to user
45
- add_action( 'admin_notices', array( $this, 'show_notice' ) );
46
-
47
- return true;
48
- }
49
-
50
- /**
51
- * @return void
52
- */
53
- public function show_notice() {
54
- ?>
 
 
 
55
  <div class="updated">
56
- <p><?php printf( '<strong>%s</strong> did not activate because it requires <strong>PHP v5.3</strong> or higher, while your server is running <strong>PHP v%s</strong>.', $this->plugin_name, PHP_VERSION ); ?>
57
- <p><?php printf( '<a href="%s">Updating your PHP version</a> makes your site faster, more secure and should be easy for your host.', 'http://www.wpupdatephp.com/update/#utm_source=wp-plugin&utm_medium=boxzillas&utm_campaign=activation-notice' ); ?></p>
58
  </div>
59
  <?php
60
- }
61
-
62
  }
1
  <?php
2
 
3
+ class Boxzilla_PHP_Fallback
4
+ {
5
+
6
+ /**
7
+ * @var string
8
+ */
9
+ private $plugin_name = '';
10
+
11
+ /**
12
+ * @var string
13
+ */
14
+ private $plugin_file = '';
15
+
16
+ /**
17
+ * @param $plugin_name
18
+ * @param $plugin_file
19
+ */
20
+ public function __construct($plugin_name, $plugin_file)
21
+ {
22
+ $this->plugin_name = $plugin_name;
23
+ $this->plugin_file = $plugin_file;
24
+
25
+ // deactivate plugin straight away
26
+ add_action('admin_init', array( $this, 'deactivate_self' ));
27
+ }
28
+
29
+ /**
30
+ * @return bool
31
+ */
32
+ public function deactivate_self()
33
+ {
34
+ if (! current_user_can('activate_plugins')) {
35
+ return false;
36
+ }
37
+
38
+ // deactivate self
39
+ deactivate_plugins($this->plugin_file);
40
+
41
+ // get rid of "Plugin activated" notice
42
+ if (isset($_GET['activate'])) {
43
+ unset($_GET['activate']);
44
+ }
45
+
46
+ // show notice to user
47
+ add_action('admin_notices', array( $this, 'show_notice' ));
48
+
49
+ return true;
50
+ }
51
+
52
+ /**
53
+ * @return void
54
+ */
55
+ public function show_notice()
56
+ {
57
+ ?>
58
  <div class="updated">
59
+ <p><?php printf('<strong>%s</strong> did not activate because it requires <strong>PHP v5.3</strong> or higher, while your server is running <strong>PHP v%s</strong>.', $this->plugin_name, PHP_VERSION); ?>
60
+ <p><?php printf('<a href="%s">Updating your PHP version</a> makes your site faster, more secure and should be easy for your host.', 'http://www.wpupdatephp.com/update/#utm_source=wp-plugin&utm_medium=boxzillas&utm_campaign=activation-notice'); ?></p>
61
  </div>
62
  <?php
63
+ }
 
64
  }
src/class-plugin.php CHANGED
@@ -2,108 +2,117 @@
2
 
3
  namespace Boxzilla;
4
 
5
- class Plugin {
6
-
7
- /**
8
- * @var string The current version of the plugin
9
- */
10
- protected $version = '1.0';
11
-
12
- /**
13
- * @var string
14
- */
15
- protected $file = '';
16
-
17
- /**
18
- * @var string
19
- */
20
- protected $dir = '';
21
-
22
- /**
23
- * @var string
24
- */
25
- protected $name = '';
26
-
27
- /**
28
- * @var string
29
- */
30
- protected $slug = '';
31
-
32
- /**
33
- * @var int
34
- */
35
- protected $id = 0;
36
-
37
- /**
38
- * Constructor
39
- *
40
- * @param int $id
41
- * @param string $name
42
- * @param string $version
43
- * @param string $file
44
- * @param string $dir (optional)
45
- */
46
- public function __construct( $id, $name, $version, $file, $dir = '' ) {
47
- $this->id = $id;
48
- $this->name = $name;
49
- $this->version = $version;
50
- $this->file = $file;
51
- $this->dir = $dir;
52
- $this->slug = plugin_basename( $file );
53
-
54
- if( empty( $dir ) ) {
55
- $this->dir = dirname( $file );
56
- }
57
- }
58
-
59
- /**
60
- * @return int
61
- */
62
- public function id() {
63
- return $this->id;
64
- }
65
-
66
- /**
67
- * @return string
68
- */
69
- public function slug() {
70
- return $this->slug;
71
- }
72
-
73
- /**
74
- * @return string
75
- */
76
- public function name() {
77
- return $this->name;
78
- }
79
-
80
- /**
81
- * @return string
82
- */
83
- public function version() {
84
- return $this->version;
85
- }
86
-
87
- /**
88
- * @return string
89
- */
90
- public function file() {
91
- return $this->file;
92
- }
93
-
94
- /**
95
- * @return string
96
- */
97
- public function dir() {
98
- return $this->dir;
99
- }
100
-
101
- /**
102
- * @param string $path
103
- *
104
- * @return mixed
105
- */
106
- public function url( $path = '' ) {
107
- return plugins_url( $path, $this->file() );
108
- }
109
- }
 
 
 
 
 
 
 
 
 
2
 
3
  namespace Boxzilla;
4
 
5
+ class Plugin
6
+ {
7
+
8
+ /**
9
+ * @var string The current version of the plugin
10
+ */
11
+ protected $version = '1.0';
12
+
13
+ /**
14
+ * @var string
15
+ */
16
+ protected $file = '';
17
+
18
+ /**
19
+ * @var string
20
+ */
21
+ protected $dir = '';
22
+
23
+ /**
24
+ * @var string
25
+ */
26
+ protected $name = '';
27
+
28
+ /**
29
+ * @var string
30
+ */
31
+ protected $slug = '';
32
+
33
+ /**
34
+ * @var int
35
+ */
36
+ protected $id = 0;
37
+
38
+ /**
39
+ * Constructor
40
+ *
41
+ * @param int $id
42
+ * @param string $name
43
+ * @param string $version
44
+ * @param string $file
45
+ * @param string $dir (optional)
46
+ */
47
+ public function __construct($id, $name, $version, $file, $dir = '')
48
+ {
49
+ $this->id = $id;
50
+ $this->name = $name;
51
+ $this->version = $version;
52
+ $this->file = $file;
53
+ $this->dir = $dir;
54
+ $this->slug = plugin_basename($file);
55
+
56
+ if (empty($dir)) {
57
+ $this->dir = dirname($file);
58
+ }
59
+ }
60
+
61
+ /**
62
+ * @return int
63
+ */
64
+ public function id()
65
+ {
66
+ return $this->id;
67
+ }
68
+
69
+ /**
70
+ * @return string
71
+ */
72
+ public function slug()
73
+ {
74
+ return $this->slug;
75
+ }
76
+
77
+ /**
78
+ * @return string
79
+ */
80
+ public function name()
81
+ {
82
+ return $this->name;
83
+ }
84
+
85
+ /**
86
+ * @return string
87
+ */
88
+ public function version()
89
+ {
90
+ return $this->version;
91
+ }
92
+
93
+ /**
94
+ * @return string
95
+ */
96
+ public function file()
97
+ {
98
+ return $this->file;
99
+ }
100
+
101
+ /**
102
+ * @return string
103
+ */
104
+ public function dir()
105
+ {
106
+ return $this->dir;
107
+ }
108
+
109
+ /**
110
+ * @param string $path
111
+ *
112
+ * @return mixed
113
+ */
114
+ public function url($path = '')
115
+ {
116
+ return plugins_url($path, $this->file());
117
+ }
118
+ }
src/default-actions.php CHANGED
@@ -1,31 +1,31 @@
1
  <?php
2
 
3
- defined( 'ABSPATH' ) or exit;
4
 
5
  $boxzilla = boxzilla();
6
 
7
  // Register custom post type
8
- add_action( 'init', function() use( $boxzilla ){
9
  $args = array(
10
  'public' => false,
11
  'labels' => array(
12
- 'name' => __( 'Boxzilla', 'boxzilla' ),
13
- 'singular_name' => __( 'Box', 'boxzilla' ),
14
- 'add_new' => __( 'Add New', 'boxzilla' ),
15
- 'add_new_item' => __( 'Add New Box', 'boxzilla' ),
16
- 'edit_item' => __( 'Edit Box', 'boxzilla' ),
17
- 'new_item' => __( 'New Box', 'boxzilla' ),
18
- 'all_items' => __( 'All Boxes', 'boxzilla' ),
19
- 'view_item' => __( 'View Box', 'boxzilla' ),
20
- 'search_items' => __( 'Search Boxes', 'boxzilla' ),
21
- 'not_found' => __( 'No Boxes found', 'boxzilla' ),
22
- 'not_found_in_trash' => __( 'No Boxes found in Trash', 'boxzilla' ),
23
  'parent_item_colon' => '',
24
- 'menu_name' => __( 'Boxzilla', 'boxzilla' )
25
  ),
26
  'show_ui' => true,
27
  'menu_position' => '108.1337133',
28
- 'menu_icon' => 'data:image/svg+xml;base64,' . base64_encode( '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16" width="16" height="16"><defs><path d="M0 0L16 0L16 16L0 16L0 16L0 0ZM6.72 13.59L6.72 12.47L7.71 13.28L8.94 13.59L10.17 13.28L11.17 12.46L11.85 11.25L12.1 9.76L11.85 8.27L11.17 7.05L10.17 6.23L8.94 5.93L7.71 6.23L6.72 7.04L6.72 2.27L4.16 2.27L4.16 13.59L4.16 13.59L6.72 13.59ZM9.93 9.03L10.06 9.76L9.93 10.49L9.56 11.08L9.01 11.48L8.35 11.63L7.68 11.48L7.13 11.08L6.76 10.49L6.72 10.26L6.72 9.25L6.76 9.03L7.13 8.43L7.68 8.03L8.35 7.88L9.01 8.03L9.56 8.43L9.56 8.43L9.93 9.03Z" id="c1kAiZqIlD"></path></defs><g><g><g><use xlink:href="#c1kAiZqIlD" opacity="1" fill="black" fill-opacity="1"></use></g></g></g></svg>' ),
29
  'query_var' => false,
30
  'capability_type' => 'box',
31
  'capabilities' => array(
@@ -39,15 +39,15 @@ add_action( 'init', function() use( $boxzilla ){
39
  ),
40
  );
41
 
42
- register_post_type( 'boxzilla-box', $args );
43
 
44
- add_shortcode( 'boxzilla_link', 'boxzilla_get_link_html' );
45
  });
46
 
47
- add_action( 'admin_init', function() {
48
- $admins = get_role( 'administrator' );
49
 
50
- if( ! $admins->has_cap( 'edit_box' ) ) {
51
  $admins->add_cap('edit_box');
52
  $admins->add_cap('edit_boxes');
53
  $admins->add_cap('edit_other_boxes');
@@ -58,15 +58,16 @@ add_action( 'admin_init', function() {
58
  }
59
  });
60
 
61
- function boxzilla_get_link_html( $args = array(), $content = '' ) {
 
62
  $valid_actions = array(
63
  'show',
64
  'toggle',
65
  'hide',
66
  'dismiss'
67
  );
68
- $box_id = empty( $args['box'] ) ? '' : absint( $args['box'] );
69
- $class_attr = empty( $args['class'] ) ? '' : esc_attr( $args['class'] );
70
- $action = empty( $args['action'] ) || ! in_array( $args['action'], $valid_actions ) ? 'show' : $args['action'];
71
- return sprintf( '<a href="javascript:Boxzilla.%s(%s)" class="%s">', $action, $box_id, $class_attr ) . $content . '</a>';
72
  }
1
  <?php
2
 
3
+ defined('ABSPATH') or exit;
4
 
5
  $boxzilla = boxzilla();
6
 
7
  // Register custom post type
8
+ add_action('init', function () use ($boxzilla) {
9
  $args = array(
10
  'public' => false,
11
  'labels' => array(
12
+ 'name' => __('Boxzilla', 'boxzilla'),
13
+ 'singular_name' => __('Box', 'boxzilla'),
14
+ 'add_new' => __('Add New', 'boxzilla'),
15
+ 'add_new_item' => __('Add New Box', 'boxzilla'),
16
+ 'edit_item' => __('Edit Box', 'boxzilla'),
17
+ 'new_item' => __('New Box', 'boxzilla'),
18
+ 'all_items' => __('All Boxes', 'boxzilla'),
19
+ 'view_item' => __('View Box', 'boxzilla'),
20
+ 'search_items' => __('Search Boxes', 'boxzilla'),
21
+ 'not_found' => __('No Boxes found', 'boxzilla'),
22
+ 'not_found_in_trash' => __('No Boxes found in Trash', 'boxzilla'),
23
  'parent_item_colon' => '',
24
+ 'menu_name' => __('Boxzilla', 'boxzilla')
25
  ),
26
  'show_ui' => true,
27
  'menu_position' => '108.1337133',
28
+ 'menu_icon' => 'data:image/svg+xml;base64,' . base64_encode('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16" width="16" height="16"><defs><path d="M0 0L16 0L16 16L0 16L0 16L0 0ZM6.72 13.59L6.72 12.47L7.71 13.28L8.94 13.59L10.17 13.28L11.17 12.46L11.85 11.25L12.1 9.76L11.85 8.27L11.17 7.05L10.17 6.23L8.94 5.93L7.71 6.23L6.72 7.04L6.72 2.27L4.16 2.27L4.16 13.59L4.16 13.59L6.72 13.59ZM9.93 9.03L10.06 9.76L9.93 10.49L9.56 11.08L9.01 11.48L8.35 11.63L7.68 11.48L7.13 11.08L6.76 10.49L6.72 10.26L6.72 9.25L6.76 9.03L7.13 8.43L7.68 8.03L8.35 7.88L9.01 8.03L9.56 8.43L9.56 8.43L9.93 9.03Z" id="c1kAiZqIlD"></path></defs><g><g><g><use xlink:href="#c1kAiZqIlD" opacity="1" fill="black" fill-opacity="1"></use></g></g></g></svg>'),
29
  'query_var' => false,
30
  'capability_type' => 'box',
31
  'capabilities' => array(
39
  ),
40
  );
41
 
42
+ register_post_type('boxzilla-box', $args);
43
 
44
+ add_shortcode('boxzilla_link', 'boxzilla_get_link_html');
45
  });
46
 
47
+ add_action('admin_init', function () {
48
+ $admins = get_role('administrator');
49
 
50
+ if (! $admins->has_cap('edit_box')) {
51
  $admins->add_cap('edit_box');
52
  $admins->add_cap('edit_boxes');
53
  $admins->add_cap('edit_other_boxes');
58
  }
59
  });
60
 
61
+ function boxzilla_get_link_html($args = array(), $content = '')
62
+ {
63
  $valid_actions = array(
64
  'show',
65
  'toggle',
66
  'hide',
67
  'dismiss'
68
  );
69
+ $box_id = empty($args['box']) ? '' : absint($args['box']);
70
+ $class_attr = empty($args['class']) ? '' : esc_attr($args['class']);
71
+ $action = empty($args['action']) || ! in_array($args['action'], $valid_actions) ? 'show' : $args['action'];
72
+ return sprintf('<a href="javascript:Boxzilla.%s(%s)" class="%s">', $action, $box_id, $class_attr) . $content . '</a>';
73
  }
src/default-filters.php CHANGED
@@ -1,17 +1,17 @@
1
  <?php
2
 
3
- defined( 'ABSPATH' ) or exit;
4
 
5
- add_filter( 'boxzilla_box_content', 'wptexturize') ;
6
- add_filter( 'boxzilla_box_content', 'convert_smilies' );
7
- add_filter( 'boxzilla_box_content', 'convert_chars' );
8
- add_filter( 'boxzilla_box_content', 'wpautop' );
9
- add_filter( 'boxzilla_box_content', 'shortcode_unautop' );
10
- add_filter( 'boxzilla_box_content', 'do_shortcode', 11 );
11
 
12
  /**
13
  * Allow Jetpack Photon to filter on Boxzilla box content.
14
  */
15
- if ( class_exists( 'Jetpack' ) && class_exists( 'Jetpack_Photon' ) && Jetpack::is_module_active( 'photon' ) ) {
16
- add_filter( 'boxzilla_box_content', array( 'Jetpack_Photon', 'filter_the_content' ), 999999 );
17
  }
1
  <?php
2
 
3
+ defined('ABSPATH') or exit;
4
 
5
+ add_filter('boxzilla_box_content', 'wptexturize') ;
6
+ add_filter('boxzilla_box_content', 'convert_smilies');
7
+ add_filter('boxzilla_box_content', 'convert_chars');
8
+ add_filter('boxzilla_box_content', 'wpautop');
9
+ add_filter('boxzilla_box_content', 'shortcode_unautop');
10
+ add_filter('boxzilla_box_content', 'do_shortcode', 11);
11
 
12
  /**
13
  * Allow Jetpack Photon to filter on Boxzilla box content.
14
  */
15
+ if (class_exists('Jetpack') && class_exists('Jetpack_Photon') && Jetpack::is_module_active('photon')) {
16
+ add_filter('boxzilla_box_content', array( 'Jetpack_Photon', 'filter_the_content' ), 999999);
17
  }
src/di/class-container-with-property-access.php CHANGED
@@ -9,46 +9,51 @@ namespace Boxzilla\DI;
9
  *
10
  * @package Boxzilla\DI
11
  */
12
- class ContainerWithPropertyAccess extends Container {
 
13
 
14
  /**
15
  * @param string $name
16
  * @return mixed
17
  */
18
- public function __get( $name ) {
19
- return $this->offsetGet( $name );
20
- }
 
21
 
22
- /**
23
- * @param string $name
24
- * @param mixed $value
25
- */
26
- public function __set( $name, $value ) {
27
- $this[ $name ] = $value;
28
- }
 
29
 
30
  /**
31
  * @param string $name
32
  * @return bool
33
  */
34
- public function __isset( $name ) {
35
- return $this->offsetExists( $name );
 
36
  }
37
 
38
  /**
39
  * @param string $name
40
  * @return bool
41
  */
42
- public function has( $name ) {
43
- return $this->offsetExists( $name );
 
44
  }
45
 
46
  /**
47
  * @param string $name
48
  * @return mixed
49
  */
50
- public function get( $name ) {
51
- return $this->offsetGet( $name );
 
52
  }
53
-
54
- }
9
  *
10
  * @package Boxzilla\DI
11
  */
12
+ class ContainerWithPropertyAccess extends Container
13
+ {
14
 
15
  /**
16
  * @param string $name
17
  * @return mixed
18
  */
19
+ public function __get($name)
20
+ {
21
+ return $this->offsetGet($name);
22
+ }
23
 
24
+ /**
25
+ * @param string $name
26
+ * @param mixed $value
27
+ */
28
+ public function __set($name, $value)
29
+ {
30
+ $this[ $name ] = $value;
31
+ }
32
 
33
  /**
34
  * @param string $name
35
  * @return bool
36
  */
37
+ public function __isset($name)
38
+ {
39
+ return $this->offsetExists($name);
40
  }
41
 
42
  /**
43
  * @param string $name
44
  * @return bool
45
  */
46
+ public function has($name)
47
+ {
48
+ return $this->offsetExists($name);
49
  }
50
 
51
  /**
52
  * @param string $name
53
  * @return mixed
54
  */
55
+ public function get($name)
56
+ {
57
+ return $this->offsetGet($name);
58
  }
59
+ }
 
src/functions.php CHANGED
@@ -5,13 +5,13 @@ use Boxzilla\Boxzilla;
5
  /**
6
  * @return Boxzilla
7
  */
8
- function boxzilla() {
 
9
  static $instance;
10
 
11
- if( is_null( $instance ) ) {
12
- $instance = new Boxzilla();
13
  }
14
 
15
  return $instance;
16
  }
17
-
5
  /**
6
  * @return Boxzilla
7
  */
8
+ function boxzilla()
9
+ {
10
  static $instance;
11
 
12
+ if (is_null($instance)) {
13
+ $instance = new Boxzilla();
14
  }
15
 
16
  return $instance;
17
  }
 
src/licensing/class-api-exception.php CHANGED
@@ -4,30 +4,32 @@ namespace Boxzilla\Licensing;
4
 
5
  use Exception;
6
 
7
- class API_Exception extends Exception {
8
-
9
- /**
10
- * @var string
11
- */
12
- protected $apiCode;
13
-
14
- /**
15
- * API_Exception constructor.
16
- *
17
- * @param string $message
18
- * @param string $apiCode (optional)
19
- */
20
- public function __construct( $message, $apiCode = '' ) {
21
- parent::__construct( $message );
22
-
23
- $this->apiCode = $apiCode;
24
- }
25
-
26
- /**
27
- * @return string
28
- */
29
- public function getApiCode() {
30
- return $this->apiCode;
31
- }
32
-
33
- }
 
 
4
 
5
  use Exception;
6
 
7
+ class API_Exception extends Exception
8
+ {
9
+
10
+ /**
11
+ * @var string
12
+ */
13
+ protected $apiCode;
14
+
15
+ /**
16
+ * API_Exception constructor.
17
+ *
18
+ * @param string $message
19
+ * @param string $apiCode (optional)
20
+ */
21
+ public function __construct($message, $apiCode = '')
22
+ {
23
+ parent::__construct($message);
24
+
25
+ $this->apiCode = $apiCode;
26
+ }
27
+
28
+ /**
29
+ * @return string
30
+ */
31
+ public function getApiCode()
32
+ {
33
+ return $this->apiCode;
34
+ }
35
+ }
src/licensing/class-api.php CHANGED
@@ -6,172 +6,178 @@ use Boxzilla\Plugin;
6
  use Exception;
7
  use WP_Error;
8
 
9
- class API {
10
-
11
- /**
12
- * @var License
13
- */
14
- protected $license;
15
-
16
- /**
17
- * The API url
18
- *
19
- * @var string
20
- */
21
- public $url = '';
22
-
23
- /**
24
- * @var int
25
- */
26
- protected $error_code = 0;
27
-
28
- /**
29
- * @var string
30
- */
31
- protected $error_message = '';
32
-
33
- /**
34
- * @var
35
- */
36
- protected $last_response;
37
-
38
- /**
39
- * @param string $url
40
- * @param License $license
41
- */
42
- public function __construct( $url, License $license ) {
43
- $this->url = $url;
44
- $this->license = $license;
45
- }
46
-
47
- /**
48
- * Gets license status
49
- *
50
- * @return object
51
- */
52
- public function get_license() {
53
- $endpoint = '/license';
54
- $response = $this->request( 'GET', $endpoint );
55
- return $response;
56
- }
57
-
58
-
59
- /**
60
- * Logs the current site in to the remote API
61
- *
62
- * @return object
63
- */
64
- public function activate_license() {
65
- $endpoint = '/license/activations';
66
- $args = array(
67
- 'site_url' => get_option( 'siteurl' )
68
- );
69
- $response = $this->request( 'POST', $endpoint, $args );
70
- return $response;
71
- }
72
-
73
- /**
74
- * Logs the current site out of the remote API
75
- *
76
- * @return object
77
- */
78
- public function deactivate_license() {
79
- $endpoint = sprintf( '/license/activations/%s', $this->license->activation_key );
80
- $response = $this->request( 'DELETE', $endpoint );
81
- return $response;
82
- }
83
-
84
- /**
85
- * @param Plugin $plugin
86
- * @return object
87
- */
88
- public function get_plugin( Plugin $plugin ) {
89
- $endpoint = sprintf( '/plugins/%s?format=wp', $plugin->id() );
90
- $response = $this->request( 'GET', $endpoint );
91
- return $response;
92
- }
93
-
94
- /**
95
- * @param Plugin[] $plugins (optional)
96
- * @return object
97
- */
98
- public function get_plugins( $plugins = null ) {
99
-
100
- $args = array(
101
- 'format' => 'wp',
102
- );
103
-
104
- $endpoint = add_query_arg( $args, '/plugins' );
105
- $response = $this->request( 'GET', $endpoint );
106
- return $response;
107
- }
108
-
109
- /**
110
- * @param string $method
111
- * @param string $endpoint
112
- * @param array $data
113
- *
114
- * @return object|array
115
- */
116
- public function request( $method, $endpoint, $data = array() ) {
117
-
118
- $url = $this->url . $endpoint;
119
- $args = array(
120
- 'method' => $method,
121
- 'headers' => array(
122
- 'Content-Type' => 'application/json',
123
- 'Accepts' => 'application/json',
124
- ),
125
- );
126
-
127
- // add license key to headers if set
128
- if( ! empty( $this->license->key ) ) {
129
- $args['headers']['Authorization'] = 'Bearer ' . urlencode( $this->license->key );
130
- }
131
-
132
- if( ! empty( $data ) ) {
133
- if( in_array( $method, array( 'GET', 'DELETE' ) ) ) {
134
- $url = add_query_arg( $data, $url );
135
- } else {
136
- $args['body'] = json_encode( $data );
137
- }
138
- }
139
-
140
- $response = wp_remote_request( $url, $args );
141
- return $this->parse_response( $response );
142
- }
143
-
144
- /**
145
- * @param mixed $response
146
- *
147
- * @return object|null
148
- *
149
- * @throws API_Exception
150
- */
151
- public function parse_response( $response ) {
152
- // test for wp errors (request failures)
153
- if( $response instanceof WP_Error) {
154
- throw new API_Exception( $response->get_error_message() );
155
- }
156
-
157
- // retrieve response body
158
- $body = wp_remote_retrieve_body( $response );
159
- if( empty( $body) ) {
160
- return null;
161
- }
162
-
163
- $json = json_decode( $body, false );
164
- if( is_null( $json ) ) {
165
- throw new API_Exception( __( "The Boxzilla server returned an invalid response.", 'boxzilla' ) );
166
- }
167
-
168
- // did request return an error response?
169
- if( wp_remote_retrieve_response_code( $response ) >= 400 ) {
170
- throw new API_Exception( $json->message, $json->code );
171
- }
172
-
173
- // return actual response data
174
- return $json;
175
- }
176
-
 
 
 
 
 
 
177
  }
6
  use Exception;
7
  use WP_Error;
8
 
9
+ class API
10
+ {
11
+
12
+ /**
13
+ * @var License
14
+ */
15
+ protected $license;
16
+
17
+ /**
18
+ * The API url
19
+ *
20
+ * @var string
21
+ */
22
+ public $url = '';
23
+
24
+ /**
25
+ * @var int
26
+ */
27
+ protected $error_code = 0;
28
+
29
+ /**
30
+ * @var string
31
+ */
32
+ protected $error_message = '';
33
+
34
+ /**
35
+ * @var
36
+ */
37
+ protected $last_response;
38
+
39
+ /**
40
+ * @param string $url
41
+ * @param License $license
42
+ */
43
+ public function __construct($url, License $license)
44
+ {
45
+ $this->url = $url;
46
+ $this->license = $license;
47
+ }
48
+
49
+ /**
50
+ * Gets license status
51
+ *
52
+ * @return object
53
+ */
54
+ public function get_license()
55
+ {
56
+ $endpoint = '/license';
57
+ $response = $this->request('GET', $endpoint);
58
+ return $response;
59
+ }
60
+
61
+
62
+ /**
63
+ * Logs the current site in to the remote API
64
+ *
65
+ * @return object
66
+ */
67
+ public function activate_license()
68
+ {
69
+ $endpoint = '/license/activations';
70
+ $args = array(
71
+ 'site_url' => get_option('siteurl')
72
+ );
73
+ $response = $this->request('POST', $endpoint, $args);
74
+ return $response;
75
+ }
76
+
77
+ /**
78
+ * Logs the current site out of the remote API
79
+ *
80
+ * @return object
81
+ */
82
+ public function deactivate_license()
83
+ {
84
+ $endpoint = sprintf('/license/activations/%s', $this->license->activation_key);
85
+ $response = $this->request('DELETE', $endpoint);
86
+ return $response;
87
+ }
88
+
89
+ /**
90
+ * @param Plugin $plugin
91
+ * @return object
92
+ */
93
+ public function get_plugin(Plugin $plugin)
94
+ {
95
+ $endpoint = sprintf('/plugins/%s?format=wp', $plugin->id());
96
+ $response = $this->request('GET', $endpoint);
97
+ return $response;
98
+ }
99
+
100
+ /**
101
+ * @param Plugin[] $plugins (optional)
102
+ * @return object
103
+ */
104
+ public function get_plugins($plugins = null)
105
+ {
106
+ $args = array(
107
+ 'format' => 'wp',
108
+ );
109
+
110
+ $endpoint = add_query_arg($args, '/plugins');
111
+ $response = $this->request('GET', $endpoint);
112
+ return $response;
113
+ }
114
+
115
+ /**
116
+ * @param string $method
117
+ * @param string $endpoint
118
+ * @param array $data
119
+ *
120
+ * @return object|array
121
+ */
122
+ public function request($method, $endpoint, $data = array())
123
+ {
124
+ $url = $this->url . $endpoint;
125
+ $args = array(
126
+ 'method' => $method,
127
+ 'headers' => array(
128
+ 'Content-Type' => 'application/json',
129
+ 'Accepts' => 'application/json',
130
+ ),
131
+ );
132
+
133
+ // add license key to headers if set
134
+ if (! empty($this->license->key)) {
135
+ $args['headers']['Authorization'] = 'Bearer ' . urlencode($this->license->key);
136
+ }
137
+
138
+ if (! empty($data)) {
139
+ if (in_array($method, array( 'GET', 'DELETE' ))) {
140
+ $url = add_query_arg($data, $url);
141
+ } else {
142
+ $args['body'] = json_encode($data);
143
+ }
144
+ }
145
+
146
+ $response = wp_remote_request($url, $args);
147
+ return $this->parse_response($response);
148
+ }
149
+
150
+ /**
151
+ * @param mixed $response
152
+ *
153
+ * @return object|null
154
+ *
155
+ * @throws API_Exception
156
+ */
157
+ public function parse_response($response)
158
+ {
159
+ // test for wp errors (request failures)
160
+ if ($response instanceof WP_Error) {
161
+ throw new API_Exception($response->get_error_message());
162
+ }
163
+
164
+ // retrieve response body
165
+ $body = wp_remote_retrieve_body($response);
166
+ if (empty($body)) {
167
+ return null;
168
+ }
169
+
170
+ $json = json_decode($body, false);
171
+ if (is_null($json)) {
172
+ throw new API_Exception(__("The Boxzilla server returned an invalid response.", 'boxzilla'));
173
+ }
174
+
175
+ // did request return an error response?
176
+ if (wp_remote_retrieve_response_code($response) >= 400) {
177
+ throw new API_Exception($json->message, $json->code);
178
+ }
179
+
180
+ // return actual response data
181
+ return $json;
182
+ }
183
  }
src/licensing/class-license-manager.php CHANGED
@@ -6,177 +6,185 @@ use Boxzilla\Admin\Notices;
6
  use Boxzilla\Collection;
7
  use Exception;
8
 
9
- class LicenseManager {
10
-
11
- /**
12
- * @var array
13
- */
14
- protected $extensions = array();
15
-
16
- /**
17
- * @var License
18
- */
19
- protected $license;
20
-
21
- /**
22
- * @var API
23
- */
24
- protected $api;
25
-
26
- /**
27
- * @var array
28
- */
29
- protected $notices = array();
30
-
31
- /**
32
- * @param array $extensions
33
- * @param API $api
34
- * @param License $license
35
- * @param Notices $notices
36
- */
37
- public function __construct( array $extensions, API $api, License $license ) {
38
- $this->extensions = $extensions;
39
- $this->license = $license;
40
- $this->api = $api;
41
- }
42
-
43
- /**
44
- * @param mixed $object
45
- * @param string $property
46
- * @param string $default
47
- *
48
- * @return string
49
- */
50
- protected function get_object_property( $object, $property, $default = '' ) {
51
- return isset( $object->$property ) ? $object->$property : $default;
52
- }
53
-
54
- /**
55
- * @return void
56
- */
57
- public function hook() {
58
-
59
- // do nothing if no extensions are registered at this point
60
- if( empty( $this->extensions ) ) {
61
- return;
62
- }
63
-
64
- // hooks
65
- add_action( 'boxzilla_after_settings', array( $this, 'show_license_form' ) );
66
- add_action( 'admin_notices', array( $this, 'show_notice' ), 1 );
67
-
68
- // listen for activation / deactivation requests
69
- $this->listen();
70
- }
71
-
72
- /**
73
- * Maybe show notice to activate license.
74
- */
75
- public function show_notice() {
76
- global $current_screen;
77
-
78
- if( $this->license->activated ) {
79
- return;
80
- }
81
-
82
- if( $this->get_object_property( $current_screen, 'post_type' ) !== 'boxzilla-box' ) {
83
- return;
84
- }
85
-
86
- $plugin = $this->extensions[ array_rand( $this->extensions ) ];
87
- $message = sprintf( 'Please <a href="%s">activate your Boxzilla license</a> to use %s.', admin_url( 'edit.php?post_type=boxzilla-box&page=boxzilla-settings' ), '<strong>' . $plugin->name() . '</strong>' );
88
- echo sprintf( '<div class="notice notice-%s"><p>%s</p></div>', 'warning', $message );
89
- }
90
-
91
- /**
92
- * @return void
93
- */
94
- protected function listen() {
95
-
96
- // do nothing if not authenticated
97
- if( ! current_user_can( 'manage_options' ) ) {
98
- return;
99
- }
100
-
101
- // nothing to do
102
- if( ! isset( $_POST['boxzilla_license_form'] ) ) {
103
- return;
104
- }
105
-
106
- $action = isset( $_POST['action'] ) ? $_POST['action'] : 'activate';
107
- $key_changed = false;
108
-
109
- // did key change or was "activate" button pressed?
110
- $new_license_key = sanitize_text_field( $_POST['boxzilla_license_key'] );
111
- if( $new_license_key !== $this->license->key ) {
112
- $this->license->key = $new_license_key;
113
- $key_changed = true;
114
- }
115
-
116
- // run actions
117
- if( $action === 'deactivate' ) {
118
- $this->deactivate_license();
119
- } elseif( $action === 'activate' || $key_changed ) {
120
- $this->activate_license();
121
- }
122
-
123
- $this->license->save();
124
- }
125
-
126
- /**
127
- * Deactivate the license
128
- */
129
- protected function deactivate_license() {
130
- try {
131
- $this->api->deactivate_license();
132
- $this->notices[] = array(
133
- 'type' => 'info',
134
- 'message' => 'Your license was successfully deactivated!',
135
- );
136
- } catch( API_Exception $e ) {
137
- $this->notices[] = array(
138
- 'type' => 'warning',
139
- 'message' => $e->getMessage(),
140
- );
141
- }
142
-
143
- $this->license->activated = false;
144
- $this->license->activation_key = '';
145
- }
146
-
147
- /**
148
- * Activate the license
149
- */
150
- protected function activate_license() {
151
- try {
152
- $activation = $this->api->activate_license();
153
- } catch( API_Exception $e ) {
154
- $message = $e->getMessage();
155
- if( $e->getApiCode() == 'license_at_limit' ) {
156
- $message .= ' You can <a href="https://platform.boxzillaplugin.com/licenses">manage your site activations here</a>.';
157
- }
158
- $this->notices[] = array(
159
- 'type' => 'warning',
160
- 'message' => $message,
161
- );
162
- return;
163
- }
164
-
165
- $this->license->activation_key = $activation->token;
166
- $this->license->activated = true;
167
-
168
- $this->notices[] = array(
169
- 'type' => 'info',
170
- 'message' => 'Your license was successfully activated!',
171
- );
172
- }
173
-
174
- /**
175
- * Shows the license form
176
- */
177
- public function show_license_form() {
178
- $license = $this->license;
179
- require __DIR__ . '/views/license-form.php';
180
- }
181
-
 
 
 
 
 
 
 
 
182
  }
6
  use Boxzilla\Collection;
7
  use Exception;
8
 
9
+ class LicenseManager
10
+ {
11
+
12
+ /**
13
+ * @var array
14
+ */
15
+ protected $extensions = array();
16
+
17
+ /**
18
+ * @var License
19
+ */
20
+ protected $license;
21
+
22
+ /**
23
+ * @var API
24
+ */
25
+ protected $api;
26
+
27
+ /**
28
+ * @var array
29
+ */
30
+ protected $notices = array();
31
+
32
+ /**
33
+ * @param array $extensions
34
+ * @param API $api
35
+ * @param License $license
36
+ * @param Notices $notices
37
+ */
38
+ public function __construct(array $extensions, API $api, License $license)
39
+ {
40
+ $this->extensions = $extensions;
41
+ $this->license = $license;
42
+ $this->api = $api;
43
+ }
44
+
45
+ /**
46
+ * @param mixed $object
47
+ * @param string $property
48
+ * @param string $default
49
+ *
50
+ * @return string
51
+ */
52
+ protected function get_object_property($object, $property, $default = '')
53
+ {
54
+ return isset($object->$property) ? $object->$property : $default;
55
+ }
56
+
57
+ /**
58
+ * @return void
59
+ */
60
+ public function hook()
61
+ {
62
+
63
+ // do nothing if no extensions are registered at this point
64
+ if (empty($this->extensions)) {
65
+ return;
66
+ }
67
+
68
+ // hooks
69
+ add_action('boxzilla_after_settings', array( $this, 'show_license_form' ));
70
+ add_action('admin_notices', array( $this, 'show_notice' ), 1);
71
+
72
+ // listen for activation / deactivation requests
73
+ $this->listen();
74
+ }
75
+
76
+ /**
77
+ * Maybe show notice to activate license.
78
+ */
79
+ public function show_notice()
80
+ {
81
+ global $current_screen;
82
+
83
+ if ($this->license->activated) {
84
+ return;
85
+ }
86
+
87
+ if ($this->get_object_property($current_screen, 'post_type') !== 'boxzilla-box') {
88
+ return;
89
+ }
90
+
91
+ $plugin = $this->extensions[ array_rand($this->extensions) ];
92
+ $message = sprintf('Please <a href="%s">activate your Boxzilla license</a> to use %s.', admin_url('edit.php?post_type=boxzilla-box&page=boxzilla-settings'), '<strong>' . $plugin->name() . '</strong>');
93
+ echo sprintf('<div class="notice notice-%s"><p>%s</p></div>', 'warning', $message);
94
+ }
95
+
96
+ /**
97
+ * @return void
98
+ */
99
+ protected function listen()
100
+ {
101
+
102
+ // do nothing if not authenticated
103
+ if (! current_user_can('manage_options')) {
104
+ return;
105
+ }
106
+
107
+ // nothing to do
108
+ if (! isset($_POST['boxzilla_license_form'])) {
109
+ return;
110
+ }
111
+
112
+ $action = isset($_POST['action']) ? $_POST['action'] : 'activate';
113
+ $key_changed = false;
114
+
115
+ // did key change or was "activate" button pressed?
116
+ $new_license_key = sanitize_text_field($_POST['boxzilla_license_key']);
117
+ if ($new_license_key !== $this->license->key) {
118
+ $this->license->key = $new_license_key;
119
+ $key_changed = true;
120
+ }
121
+
122
+ // run actions
123
+ if ($action === 'deactivate') {
124
+ $this->deactivate_license();
125
+ } elseif ($action === 'activate' || $key_changed) {
126
+ $this->activate_license();
127
+ }
128
+
129
+ $this->license->save();
130
+ }
131
+
132
+ /**
133
+ * Deactivate the license
134
+ */
135
+ protected function deactivate_license()
136
+ {
137
+ try {
138
+ $this->api->deactivate_license();
139
+ $this->notices[] = array(
140
+ 'type' => 'info',
141
+ 'message' => 'Your license was successfully deactivated!',
142
+ );
143
+ } catch (API_Exception $e) {
144
+ $this->notices[] = array(
145
+ 'type' => 'warning',
146
+ 'message' => $e->getMessage(),
147
+ );
148
+ }
149
+
150
+ $this->license->activated = false;
151
+ $this->license->activation_key = '';
152
+ }
153
+
154
+ /**
155
+ * Activate the license
156
+ */
157
+ protected function activate_license()
158
+ {
159
+ try {
160
+ $activation = $this->api->activate_license();
161
+ } catch (API_Exception $e) {
162
+ $message = $e->getMessage();
163
+ if ($e->getApiCode() == 'license_at_limit') {
164
+ $message .= ' You can <a href="https://platform.boxzillaplugin.com/licenses">manage your site activations here</a>.';
165
+ }
166
+ $this->notices[] = array(
167
+ 'type' => 'warning',
168
+ 'message' => $message,
169
+ );
170
+ return;
171
+ }
172
+
173
+ $this->license->activation_key = $activation->token;
174
+ $this->license->activated = true;
175
+
176
+ $this->notices[] = array(
177
+ 'type' => 'info',
178
+ 'message' => 'Your license was successfully activated!',
179
+ );
180
+ }
181
+
182
+ /**
183
+ * Shows the license form
184
+ */
185
+ public function show_license_form()
186
+ {
187
+ $license = $this->license;
188
+ require __DIR__ . '/views/license-form.php';
189
+ }
190
  }
src/licensing/class-license-service-provider.php CHANGED
@@ -2,38 +2,38 @@
2
 
3
  namespace Boxzilla\Licensing;
4
 
5
- use Boxzilla\DI\Container,
6
- Boxzilla\DI\ServiceProviderInterface;
7
-
8
- class LicenseServiceProvider implements ServiceProviderInterface {
9
-
10
- /**
11
- * Registers all licensing related services
12
- *
13
- * @param Container $container
14
- */
15
- public function register( Container $container ) {
16
-
17
- $container['license'] = function( $container ) {
18
- return new License( 'boxzilla_license' );
19
- };
20
-
21
- $container['license_api'] = function( $container ) {
22
- $api_url = 'https://platform.boxzillaplugin.com/api/v2';
23
- return new API( $api_url, $container['license'] );
24
- };
25
-
26
- $container['license_manager'] = function( $container ) {
27
- return new LicenseManager( $container['plugins'], $container['license_api'], $container['license'] );
28
- };
29
-
30
- $container['update_manager'] = function( $container ) {
31
- return new UpdateManager( $container['plugins'], $container['license_api'], $container['license'] );
32
- };
33
-
34
- $container['license_poller'] = function( $container ) {
35
- return new Poller( $container['license_api'], $container['license'] );
36
- };
37
- }
38
-
39
  }
2
 
3
  namespace Boxzilla\Licensing;
4
 
5
+ use Boxzilla\DI\Container;
6
+ use Boxzilla\DI\ServiceProviderInterface;
7
+
8
+ class LicenseServiceProvider implements ServiceProviderInterface
9
+ {
10
+
11
+ /**
12
+ * Registers all licensing related services
13
+ *
14
+ * @param Container $container
15
+ */
16
+ public function register(Container $container)
17
+ {
18
+ $container['license'] = function ($container) {
19
+ return new License('boxzilla_license');
20
+ };
21
+
22
+ $container['license_api'] = function ($container) {
23
+ $api_url = 'https://my.boxzillaplugin.com/api/v2';
24
+ return new API($api_url, $container['license']);
25
+ };
26
+
27
+ $container['license_manager'] = function ($container) {
28
+ return new LicenseManager($container['plugins'], $container['license_api'], $container['license']);
29
+ };
30
+
31
+ $container['update_manager'] = function ($container) {
32
+ return new UpdateManager($container['plugins'], $container['license_api'], $container['license']);
33
+ };
34
+
35
+ $container['license_poller'] = function ($container) {
36
+ return new Poller($container['license_api'], $container['license']);
37
+ };
38
+ }
39
  }
src/licensing/class-license.php CHANGED
@@ -14,105 +14,112 @@ namespace Boxzilla\Licensing;
14
  *
15
  * @package Boxzilla\Licensing
16
  */
17
- class License {
18
-
19
- /**
20
- * @var string The name of the option that holds the License data
21
- */
22
- protected $option_key = '';
23
-
24
- /**
25
- * @var bool Loaded?
26
- */
27
- protected $loaded = false;
28
-
29
- /**
30
- * @var bool Any changes?
31
- */
32
- protected $dirty = false;
33
-
34
- /**
35
- * @var array
36
- */
37
- protected $data;
38
-
39
- /**
40
- * @param string $option_key
41
- */
42
- public function __construct( $option_key ) {
43
- $this->option_key = $option_key;
44
- }
45
-
46
- /**
47
- * @param string $name
48
- * @param mixed $value
49
- */
50
- public function __set($name, $value) {
51
- $this->load();
52
- $this->data[ $name ] = $value;
53
- $this->dirty = true;
54
- }
55
-
56
- /**
57
- * @param string $name
58
- *
59
- * @return mixed
60
- */
61
- public function __get($name) {
62
- $this->load();
63
- return $this->data[ $name ];
64
- }
65
-
66
- /**
67
- * @param $name
68
- *
69
- * @return bool
70
- */
71
- public function __isset( $name ) {
72
- $this->load();
73
- return isset( $this->data[ $name ] );
74
- }
75
-
76
- /**
77
- * Load the license data from the database
78
- */
79
- protected function load() {
80
- if( $this->loaded ) {
81
- return;
82
- }
83
-
84
- $defaults = array(
85
- 'key' => '',
86
- 'activation_key' => '',
87
- 'activated' => false,
88
- 'expires_at' => ''
89
- );
90
-
91
- $data = (array) get_option( $this->option_key, array() );
92
- $this->data = array_replace( $defaults, $data );
93
- $this->loaded = true;
94
- }
95
-
96
- /**
97
- * Reload the license data from DB
98
- */
99
- public function reload() {
100
- $this->loaded = false;
101
- $this->load();
102
- }
103
-
104
- /**
105
- * Save the license in the database
106
- *
107
- * @return License
108
- */
109
- public function save() {
110
- if( ! $this->dirty ) {
111
- return;
112
- }
113
-
114
- update_option( $this->option_key, $this->data );
115
- $this->dirty = false;
116
- }
117
-
 
 
 
 
 
 
 
118
  }
14
  *
15
  * @package Boxzilla\Licensing
16
  */
17
+ class License
18
+ {
19
+
20
+ /**
21
+ * @var string The name of the option that holds the License data
22
+ */
23
+ protected $option_key = '';
24
+
25
+ /**
26
+ * @var bool Loaded?
27
+ */
28
+ protected $loaded = false;
29
+
30
+ /**
31
+ * @var bool Any changes?
32
+ */
33
+ protected $dirty = false;
34
+
35
+ /**
36
+ * @var array
37
+ */
38
+ protected $data;
39
+
40
+ /**
41
+ * @param string $option_key
42
+ */
43
+ public function __construct($option_key)
44
+ {
45
+ $this->option_key = $option_key;
46
+ }
47
+
48
+ /**
49
+ * @param string $name
50
+ * @param mixed $value
51
+ */
52
+ public function __set($name, $value)
53
+ {
54
+ $this->load();
55
+ $this->data[ $name ] = $value;
56
+ $this->dirty = true;
57
+ }
58
+
59
+ /**
60
+ * @param string $name
61
+ *
62
+ * @return mixed
63
+ */
64
+ public function __get($name)
65
+ {
66
+ $this->load();
67
+ return $this->data[ $name ];
68
+ }
69
+
70
+ /**
71
+ * @param $name
72
+ *
73
+ * @return bool
74
+ */
75
+ public function __isset($name)
76
+ {
77
+ $this->load();
78
+ return isset($this->data[ $name ]);
79
+ }
80
+
81
+ /**
82
+ * Load the license data from the database
83
+ */
84
+ protected function load()
85
+ {
86
+ if ($this->loaded) {
87
+ return;
88
+ }
89
+
90
+ $defaults = array(
91
+ 'key' => '',
92
+ 'activation_key' => '',
93
+ 'activated' => false,
94
+ 'expires_at' => ''
95
+ );
96
+
97
+ $data = (array) get_option($this->option_key, array());
98
+ $this->data = array_replace($defaults, $data);
99
+ $this->loaded = true;
100
+ }
101
+
102
+ /**
103
+ * Reload the license data from DB
104
+ */
105
+ public function reload()
106
+ {
107
+ $this->loaded = false;
108
+ $this->load();
109
+ }
110
+
111
+ /**
112
+ * Save the license in the database
113
+ *
114
+ * @return License
115
+ */
116
+ public function save()
117
+ {
118
+ if (! $this->dirty) {
119
+ return;
120
+ }
121
+
122
+ update_option($this->option_key, $this->data);
123
+ $this->dirty = false;
124
+ }
125
  }
src/licensing/class-poller.php CHANGED
@@ -2,7 +2,8 @@
2
 
3
  namespace Boxzilla\Licensing;
4
 
5
- class Poller {
 
6
 
7
  /**
8
  * @var API
@@ -20,7 +21,8 @@ class Poller {
20
  * @param API $api
21
  * @param License $license
22
  */
23
- public function __construct( API $api, License $license ) {
 
24
  $this->api = $api;
25
  $this->license = $license;
26
  }
@@ -28,20 +30,22 @@ class Poller {
28
  /**
29
  * Add hooks.
30
  */
31
- public function hook() {
32
- if( ! wp_next_scheduled( 'boxzilla_check_license_status' ) ) {
33
- wp_schedule_event( time(), 'daily', 'boxzilla_check_license_status' );
 
34
  };
35
 
36
- add_action( 'boxzilla_check_license_status', array( $this, 'run' ) );
37
  }
38
 
39
  /**
40
  * Run!
41
  */
42
- public function run() {
 
43
  // don't run if license not active
44
- if( ! $this->license->activated ) {
45
  return;
46
  }
47
 
@@ -51,17 +55,16 @@ class Poller {
51
  try {
52
  $remote_license = $this->api->get_license();
53
  $license_still_valid = $remote_license->valid;
54
- } catch( API_Exception $e ) {
55
  // license key wasn't found or expired
56
- if( in_array( $e->getApiCode(), array( 'license_invalid', 'license_expired' ) ) ) {
57
  $license_still_valid = false;
58
  }
59
  }
60
 
61
- if( ! $license_still_valid ) {
62
  $this->license->activated = false;
63
  $this->license->save();
64
  }
65
-
66
  }
67
- }
2
 
3
  namespace Boxzilla\Licensing;
4
 
5
+ class Poller
6
+ {
7
 
8
  /**
9
  * @var API
21
  * @param API $api
22
  * @param License $license
23
  */
24
+ public function __construct(API $api, License $license)
25
+ {
26
  $this->api = $api;
27
  $this->license = $license;
28
  }
30
  /**
31
  * Add hooks.
32
  */
33
+ public function hook()
34
+ {
35
+ if (! wp_next_scheduled('boxzilla_check_license_status')) {
36
+ wp_schedule_event(time(), 'daily', 'boxzilla_check_license_status');
37
  };
38
 
39
+ add_action('boxzilla_check_license_status', array( $this, 'run' ));
40
  }
41
 
42
  /**
43
  * Run!
44
  */
45
+ public function run()
46
+ {
47
  // don't run if license not active
48
+ if (! $this->license->activated) {
49
  return;
50
  }
51
 
55
  try {
56
  $remote_license = $this->api->get_license();
57
  $license_still_valid = $remote_license->valid;
58
+ } catch (API_Exception $e) {
59
  // license key wasn't found or expired
60
+ if (in_array($e->getApiCode(), array( 'license_invalid', 'license_expired' ))) {
61
  $license_still_valid = false;
62
  }
63
  }
64
 
65
+ if (! $license_still_valid) {
66
  $this->license->activated = false;
67
  $this->license->save();
68
  }
 
69
  }
70
+ }
src/licensing/class-update-manager.php CHANGED
@@ -2,245 +2,253 @@
2
 
3
  namespace Boxzilla\Licensing;
4
 
5
- use Boxzilla\Collection,
6
- Boxzilla\Plugin,
7
- Boxzilla\Admin\Notices;
8
-
9
- class UpdateManager {
10
-
11
- /**
12
- * @var array
13
- */
14
- protected $extensions = array();
15
-
16
- /**
17
- * @var API
18
- */
19
- protected $api;
20
-
21
- /**
22
- * @var License
23
- */
24
- protected $license;
25
-
26
- /**
27
- * @var
28
- */
29
- protected $available_updates;
30
-
31
- /**
32
- * @param Collection $extensions
33
- * @param API $api
34
- * @param License $license
35
- */
36
- public function __construct( array $extensions, API $api, License $license ) {
37
- $this->extensions = $extensions;
38
- $this->license = $license;
39
- $this->api = $api;
40
- }
41
-
42
- /**
43
- * Add hooks
44
- */
45
- public function hook() {
46
- add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'add_updates' ) );
47
- add_filter( 'plugins_api', array( $this, 'get_plugin_info' ), 20, 3 );
48
- add_filter( 'http_request_args', array( $this, 'add_auth_headers' ), 10, 2 );
49
- }
50
-
51
- /**
52
- * This adds the license key header to download package requests.
53
- *
54
- * @param array $args
55
- * @param string $url
56
- *
57
- * @return mixed
58
- */
59
- public function add_auth_headers( $args, $url ) {
60
-
61
- // only act on download request's
62
- if( strpos( $url, $this->api->url ) !== 0 || strpos( $url, 'download' ) === false ) {
63
- return $args;
64
- }
65
-
66
- // only add if activation key not empty
67
- if( empty( $this->license->activation_key ) ) {
68
- return $args;
69
- }
70
-
71
- if( empty( $args['headers'] ) ) {
72
- $args['headers'] = array();
73
- }
74
-
75
- $args['headers']['Authorization'] = 'Bearer ' . urlencode( $this->license->activation_key );
76
- return $args;
77
- }
78
-
79
- /**
80
- * @param $result
81
- * @param string $action
82
- * @param null $args
83
- *
84
- * @return object
85
- */
86
- public function get_plugin_info( $result, $action = '', $args = null ) {
87
-
88
- // do nothing for unrelated requests
89
- if( $action !== 'plugin_information' || ! isset( $args->slug ) ) {
90
- return $result;
91
- }
92
-
93
- $plugin = null;
94
- foreach( $this->extensions as $p ) {
95
- // find plugin by slug
96
- if( dirname( $p->slug() ) == $args->slug ) {
97
- $plugin = $p;
98
- break;
99
- }
100
- }
101
-
102
- // was it a plugin of ours?
103
- if( ! empty( $plugin ) ) {
104
- return $this->get_update_info( $plugin );
105
- }
106
-
107
- return $result;
108
- }
109
-
110
- /**
111
- * @param object $updates
112
- * @return object
113
- */
114
- public function add_updates( $updates ) {
115
-
116
- // do nothing if no plugins registered
117
- if( empty( $this->extensions ) ) {
118
- return $updates;
119
- }
120
-
121
- // failsafe WP bug
122
- if( empty( $updates )
123
- || empty( $updates->response )
124
- || ! is_array( $updates->response ) ) {
125
- return $updates;
126
- }
127
-
128
- // fetch available updates
129
- $available_updates = $this->fetch_updates();
130
-
131
- // merge with other updates
132
- $updates->response = array_merge( $updates->response, $available_updates );
133
-
134
- return $updates;
135
- }
136
-
137
- /**
138
- * Fetch array of available updates from remote server
139
- *
140
- * @return array
141
- */
142
- protected function fetch_updates() {
143
-
144
- if( is_array( $this->available_updates ) ) {
145
- return $this->available_updates;
 
 
 
 
146
  }
147
 
148
  // don't try if we failed a request recently.
149
- $failed_at = get_transient( 'boxzilla_request_failed' );
150
- if( ! empty( $failed_at ) && ( (strtotime('now') - 300) < $failed_at ) ) {
151
  return array();
152
  }
153
 
154
- // fetch remote info
155
- try {
156
- $remote_plugins = $this->api->get_plugins();
157
- } catch( API_Exception $e ) {
158
  // set flag for 5 minutes
159
- set_transient( 'boxzilla_request_failed', strtotime('now'), 300 );
160
  return array();
161
- }
162
-
163
- // filter remote plugins, we only want the ones with an update available
164
- $this->available_updates = $this->filter_remote_plugins( $remote_plugins );
165
-
166
- return $this->available_updates;
167
-
168
- }
169
-
170
- /**
171
- * @param $remote_plugins
172
- * @return array
173
- */
174
- protected function filter_remote_plugins( $remote_plugins ) {
175
-
176
- $available_updates = array();
177
-
178
- // find new versions
179
- foreach( $remote_plugins as $remote_plugin ) {
180
-
181
- // only act on update info of our own plugins
182
- if( ! isset( $this->extensions[ $remote_plugin->sid ] ) ) {
183
- continue;
184
- }
185
-
186
- // sanity check
187
- if ( ! isset( $remote_plugin->new_version ) ) {
188
- continue;
189
- }
190
-
191
- // find corresponding local plugin
192
- /** @var Plugin $local_plugin */
193
- $local_plugin = $this->extensions[ $remote_plugin->sid ];
194
-
195
- // plugin found and local plugin version not same as remote version?
196
- if( ! $local_plugin || version_compare( $local_plugin->version(), $remote_plugin->new_version, '>=' ) ) {
197
- continue;
198
- }
199
-
200
- // add some dynamic data
201
- $available_updates[ $local_plugin->slug() ] = $this->format_response( $local_plugin, $remote_plugin );
202
- }
203
-
204
- return $available_updates;
205
- }
206
-
207
- /**
208
- * @param Plugin $plugin
209
- *
210
- * @return null
211
- */
212
- public function get_update_info( Plugin $plugin ) {
213
- $available_updates = $this->fetch_updates();
214
-
215
- if( isset( $available_updates[ $plugin->slug() ] ) ) {
216
- return $available_updates[ $plugin->slug() ];
217
- }
218
-
219
- return null;
220
- }
221
-
222
- /**
223
- * @param Plugin $plugin
224
- * @param $response
225
- *
226
- * @return mixed
227
- */
228
- protected function format_response( Plugin $plugin, $response ) {
229
- $response->slug = dirname( $plugin->slug() );
230
- $response->plugin = $plugin->slug();
231
-
232
- // add some notices if license is inactive
233
- if( ! $this->license->activated ) {
234
- $response->upgrade_notice = sprintf( 'You will need to <a href="%s">activate your license</a> to install this plugin update.', admin_url( 'edit.php?post_type=boxzilla-box&page=boxzilla-settings' ) );
235
- $response->sections->changelog = '<p>' . sprintf( 'You will need to <a href="%s" target="_top">activate your license</a> to install this plugin update.', admin_url( 'edit.php?post_type=boxzilla-box&page=boxzilla-settings' ) ) . '</p>' . $response->sections->changelog;
236
- $response->package = null;
237
- }
238
-
239
- // cast subkey objects to array as that is what WP expects
240
- $response->sections = get_object_vars( $response->sections );
241
- $response->banners = get_object_vars( $response->banners );
242
-
243
- return $response;
244
- }
 
 
245
 
 
 
246
  }
2
 
3
  namespace Boxzilla\Licensing;
4
 
5
+ use Boxzilla\Plugin;
6
+
7
+ class UpdateManager
8
+ {
9
+
10
+ /**
11
+ * @var array
12
+ */
13
+ protected $extensions = array();
14
+
15
+ /**
16
+ * @var API
17
+ */
18
+ protected $api;
19
+
20
+ /**
21
+ * @var License
22
+ */
23
+ protected $license;
24
+
25
+ /**
26
+ * @var
27
+ */
28
+ protected $available_updates;
29
+
30
+ /**
31
+ * @param array $extensions
32
+ * @param API $api
33
+ * @param License $license
34
+ */
35
+ public function __construct(array $extensions, API $api, License $license)
36
+ {
37
+ $this->extensions = $extensions;
38
+ $this->license = $license;
39
+ $this->api = $api;
40
+ }
41
+
42
+ /**
43
+ * Add hooks
44
+ */
45
+ public function hook()
46
+ {
47
+ add_filter('pre_set_site_transient_update_plugins', array( $this, 'add_updates' ));
48
+ add_filter('plugins_api', array( $this, 'get_plugin_info' ), 20, 3);
49
+ add_filter('http_request_args', array( $this, 'add_auth_headers' ), 10, 2);
50
+ }
51
+
52
+ /**
53
+ * This adds the license key header to download package requests.
54
+ *
55
+ * @param array $args
56
+ * @param string $url
57
+ *
58
+ * @return mixed
59
+ */
60
+ public function add_auth_headers($args, $url)
61
+ {
62
+ // only act on download request's to the Boxzilla update API
63
+ if (strpos($url, $this->api->url) !== 0 || strpos($url, '/download') === false) {
64
+ return $args;
65
+ }
66
+
67
+ // only add if activation key not empty
68
+ if (empty($this->license->activation_key)) {
69
+ return $args;
70
+ }
71
+
72
+ if (! isset($args['headers'])) {
73
+ $args['headers'] = array();
74
+ }
75
+
76
+ $args['headers']['Authorization'] = sprintf('Bearer %s', $this->license->activation_key);
77
+ return $args;
78
+ }
79
+
80
+ private function get_plugin_by_slug($slug)
81
+ {
82
+ foreach ($this->extensions as $p) {
83
+ // find plugin by slug
84
+ if (dirname($p->slug()) === $slug) {
85
+ return $p;
86
+ }
87
+ }
88
+
89
+ return null;
90
+ }
91
+
92
+ /**
93
+ * @param $result
94
+ * @param string $action
95
+ * @param null $args
96
+ *
97
+ * @return object
98
+ */
99
+ public function get_plugin_info($result, $action = '', $args = null)
100
+ {
101
+ // do nothing for unrelated requests
102
+ if ($action !== 'plugin_information' || ! isset($args->slug)) {
103
+ return $result;
104
+ }
105
+
106
+ // only act on our own plugins
107
+ $plugin = $this->get_plugin_by_slug($args->slug);
108
+ if ($plugin === null) {
109
+ return $result;
110
+ }
111
+
112
+ return $this->get_update_info($plugin);
113
+ }
114
+
115
+ /**
116
+ * @param object $updates
117
+ * @return object
118
+ */
119
+ public function add_updates($updates)
120
+ {
121
+
122
+ // do nothing if no plugins registered
123
+ if (empty($this->extensions)) {
124
+ return $updates;
125
+ }
126
+
127
+ // failsafe WP bug
128
+ if (empty($updates) || ! isset($updates->response) || ! is_array($updates->response)) {
129
+ return $updates;
130
+ }
131
+
132
+ // fetch available updates
133
+ $available_updates = $this->fetch_updates();
134
+
135
+ // merge with other updates
136
+ $updates->response = array_merge($updates->response, $available_updates);
137
+
138
+ return $updates;
139
+ }
140
+
141
+ /**
142
+ * Fetch array of available updates from remote server
143
+ *
144
+ * @return array
145
+ */
146
+ protected function fetch_updates()
147
+ {
148
+ if (is_array($this->available_updates)) {
149
+ return $this->available_updates;
150
  }
151
 
152
  // don't try if we failed a request recently.
153
+ $failed_at = get_transient('boxzilla_request_failed');
154
+ if (! empty($failed_at) && ((strtotime('now') - 300) < $failed_at)) {
155
  return array();
156
  }
157
 
158
+ // fetch remote info
159
+ try {
160
+ $remote_plugins = $this->api->get_plugins();
161
+ } catch (API_Exception $e) {
162
  // set flag for 5 minutes
163
+ set_transient('boxzilla_request_failed', strtotime('now'), 300);
164
  return array();
165
+ }
166
+
167
+ // filter remote plugins, we only want the ones with an update available
168
+ $this->available_updates = $this->filter_remote_plugins($remote_plugins);
169
+
170
+ return $this->available_updates;
171
+ }
172
+
173
+ /**
174
+ * @param $remote_plugins
175
+ * @return array
176
+ */
177
+ protected function filter_remote_plugins($remote_plugins)
178
+ {
179
+ $available_updates = array();
180
+
181
+ // find new versions
182
+ foreach ($remote_plugins as $remote_plugin) {
183
+
184
+ // only act on update info of our own plugins
185
+ if (! isset($this->extensions[ $remote_plugin->sid ])) {
186
+ continue;
187
+ }
188
+
189
+ // sanity check
190
+ if (! isset($remote_plugin->new_version)) {
191
+ continue;
192
+ }
193
+
194
+ // find corresponding local plugin
195
+ /** @var Plugin $local_plugin */
196
+ $local_plugin = $this->extensions[ $remote_plugin->sid ];
197
+
198
+ // plugin found and local plugin version not same as remote version?
199
+ if (! $local_plugin || version_compare($local_plugin->version(), $remote_plugin->new_version, '>=')) {
200
+ continue;
201
+ }
202
+
203
+ // add some dynamic data
204
+ $available_updates[ $local_plugin->slug() ] = $this->format_response($local_plugin, $remote_plugin);
205
+ }
206
+
207
+ return $available_updates;
208
+ }
209
+
210
+ /**
211
+ * @param Plugin $plugin
212
+ *
213
+ * @return null
214
+ */
215
+ public function get_update_info(Plugin $plugin)
216
+ {
217
+ $available_updates = $this->fetch_updates();
218
+
219
+ if (isset($available_updates[ $plugin->slug() ])) {
220
+ return $available_updates[ $plugin->slug() ];
221
+ }
222
+
223
+ return null;
224
+ }
225
+
226
+ /**
227
+ * @param Plugin $plugin
228
+ * @param $response
229
+ *
230
+ * @return mixed
231
+ */
232
+ protected function format_response(Plugin $plugin, $response)
233
+ {
234
+ $response->slug = dirname($plugin->slug());
235
+ $response->plugin = $plugin->slug();
236
+
237
+ // add some notices if license is inactive
238
+ if (! $this->license->activated) {
239
+ $response->upgrade_notice = sprintf('You will need to <a href="%s">activate your license</a> to install this plugin update.', admin_url('edit.php?post_type=boxzilla-box&page=boxzilla-settings'));
240
+ $response->sections->changelog = '<p>' . sprintf('You will need to <a href="%s" target="_top">activate your license</a> to install this plugin update.', admin_url('edit.php?post_type=boxzilla-box&page=boxzilla-settings')) . '</p>' . $response->sections->changelog;
241
+ $response->package = null;
242
+ }
243
+
244
+ // cast subkey objects to array as that is what WP expects
245
+ $response->sections = get_object_vars($response->sections);
246
+ $response->banners = get_object_vars($response->banners);
247
+ $response->contributors = get_object_vars($response->contributors);
248
+ $response->contributors = array_map(function ($v) {
249
+ return get_object_vars($v);
250
+ }, $response->contributors);
251
 
252
+ return $response;
253
+ }
254
  }
src/licensing/views/license-form.php CHANGED
@@ -1,57 +1,66 @@
1
  <?php
2
  // prevent direct file access
3
- defined( 'ABSPATH' ) or exit;
4
 
5
  /** @var Boxzilla\Licensing\License $license */
6
  ?>
7
 
8
- <h2><?php _e( 'License & Plugin Updates', 'boxzilla' ); ?></h2>
9
 
10
- <?php if( ! $license->activated ) { ?>
 
11
  <div class="error inline">
12
  <p>
13
- <strong><?php _e( 'Warning! You are <u>not</u> receiving plugin updates for the following plugin(s):', 'boxzilla' ); ?></strong>
14
  </p>
15
  <ul class="ul-square">
16
- <?php
17
- foreach( $this->extensions as $p ) {
18
- echo '<li>' . esc_html( $p->name() ) . '</li>';
19
- }
20
- ?>
21
  </ul>
22
  <p>
23
  To fix this, please activate your license using the form below.
24
  </p>
25
  </div>
26
- <?php } ?>
 
27
 
28
- <?php foreach( $this->notices as $notice ) { ?>
 
29
  <div class="notice notice-<?php echo $notice['type']; ?> inline">
30
  <p><?php echo $notice['message']; ?></p>
31
  </div>
32
- <?php } ?>
 
33
 
34
  <form method="post">
35
  <table class="form-table">
36
  <tr valign="top">
37
- <th><?php _e( 'License Key', 'boxzilla' ); ?></th>
38
  <td>
39
- <input size="40" name="boxzilla_license_key" placeholder="<?php esc_attr_e( 'Enter your license key..', 'boxzilla' ); ?>" value="<?php echo esc_attr( $this->license->key ); ?>" <?php if( $this->license->activated ) { echo 'readonly'; } ?> />
40
- <input class="button" type="submit" name="action" value="<?php echo ( $this->license->activated ) ? 'deactivate' : 'activate'; ?>" />
 
 
41
  <p class="help">
42
- <?php echo sprintf( __( 'The license key received when purchasing your premium Boxzilla plan. <a href="%s">You can find it here</a>.', 'boxzilla' ), 'https://platform.boxzillaplugin.com/' ); ?>
43
  </p>
44
  </td>
45
  </tr>
46
  <tr valign="top">
47
- <th><?php _e( 'License Status', 'boxzilla' ); ?></th>
48
  <td>
49
  <?php
50
- if( $license->activated ) { ?>
51
- <p><span class="status positive"><?php _e( 'ACTIVE', 'boxzilla' ); ?></span> - <?php _e( 'you are receiving plugin updates', 'boxzilla' ); ?></p>
52
- <?php } else { ?>
53
- <p><span class="status negative"><?php _e( 'INACTIVE', 'boxzilla' ); ?></span> - <?php _e( 'you are <strong>not</strong> receiving plugin updates', 'boxzilla' ); ?></p>
54
- <?php } ?>
 
 
 
 
55
  </td>
56
  </tr>
57
  </table>
@@ -60,7 +69,7 @@ defined( 'ABSPATH' ) or exit;
60
 
61
 
62
  <p>
63
- <input type="submit" class="button button-primary" name="action" value="<?php _e( 'Save Changes' ); ?>" />
64
  </p>
65
 
66
  <input type="hidden" name="boxzilla_license_form" value="1" />
1
  <?php
2
  // prevent direct file access
3
+ defined('ABSPATH') or exit;
4
 
5
  /** @var Boxzilla\Licensing\License $license */
6
  ?>
7
 
8
+ <h2><?php _e('License & Plugin Updates', 'boxzilla'); ?></h2>
9
 
10
+ <?php if (! $license->activated) {
11
+ ?>
12
  <div class="error inline">
13
  <p>
14
+ <strong><?php _e('Warning! You are <u>not</u> receiving plugin updates for the following plugin(s):', 'boxzilla'); ?></strong>
15
  </p>
16
  <ul class="ul-square">
17
+ <?php
18
+ foreach ($this->extensions as $p) {
19
+ echo '<li>' . esc_html($p->name()) . '</li>';
20
+ } ?>
 
21
  </ul>
22
  <p>
23
  To fix this, please activate your license using the form below.
24
  </p>
25
  </div>
26
+ <?php
27
+ } ?>
28
 
29
+ <?php foreach ($this->notices as $notice) {
30
+ ?>
31
  <div class="notice notice-<?php echo $notice['type']; ?> inline">
32
  <p><?php echo $notice['message']; ?></p>
33
  </div>
34
+ <?php
35
+ } ?>
36
 
37
  <form method="post">
38
  <table class="form-table">
39
  <tr valign="top">
40
+ <th><?php _e('License Key', 'boxzilla'); ?></th>
41
  <td>
42
+ <input size="40" name="boxzilla_license_key" placeholder="<?php esc_attr_e('Enter your license key..', 'boxzilla'); ?>" value="<?php echo esc_attr($this->license->key); ?>" <?php if ($this->license->activated) {
43
+ echo 'readonly';
44
+ } ?> />
45
+ <input class="button" type="submit" name="action" value="<?php echo ($this->license->activated) ? 'deactivate' : 'activate'; ?>" />
46
  <p class="help">
47
+ <?php echo sprintf(__('The license key received when purchasing your premium Boxzilla plan. <a href="%s">You can find it here</a>.', 'boxzilla'), 'https://platform.boxzillaplugin.com/'); ?>
48
  </p>
49
  </td>
50
  </tr>
51
  <tr valign="top">
52
+ <th><?php _e('License Status', 'boxzilla'); ?></th>
53
  <td>
54
  <?php
55
+ if ($license->activated) {
56
+ ?>
57
+ <p><span class="status positive"><?php _e('ACTIVE', 'boxzilla'); ?></span> - <?php _e('you are receiving plugin updates', 'boxzilla'); ?></p>
58
+ <?php
59
+ } else {
60
+ ?>
61
+ <p><span class="status negative"><?php _e('INACTIVE', 'boxzilla'); ?></span> - <?php _e('you are <strong>not</strong> receiving plugin updates', 'boxzilla'); ?></p>
62
+ <?php
63
+ } ?>
64
  </td>
65
  </tr>
66
  </table>
69
 
70
 
71
  <p>
72
+ <input type="submit" class="button button-primary" name="action" value="<?php _e('Save Changes'); ?>" />
73
  </p>
74
 
75
  <input type="hidden" name="boxzilla_license_form" value="1" />