Add smartbanner to site.
authorSteve Sutton <steve@gaslightmedia.com>
Thu, 16 Nov 2017 15:34:17 +0000 (10:34 -0500)
committerSteve Sutton <steve@gaslightmedia.com>
Thu, 16 Nov 2017 15:34:17 +0000 (10:34 -0500)
Adding to pages Home, About, and Essentials

assets/smartbanner/smartbanner.css [new file with mode: 0644]
assets/smartbanner/smartbanner.js [new file with mode: 0644]
assets/smartbanner/smartbanner.png [new file with mode: 0644]
parts/header-meta.php

diff --git a/assets/smartbanner/smartbanner.css b/assets/smartbanner/smartbanner.css
new file mode 100644 (file)
index 0000000..1378f23
--- /dev/null
@@ -0,0 +1,76 @@
+/*!
+ * smartbanner.js v1.5.0 <https://github.com/ain/smartbanner.js>
+ * Copyright © 2017 Ain Tohvri, contributors. Licensed under GPL-3.0.
+ */
+.smartbanner {
+  position: absolute;
+  top: 0;
+  left: 0;
+  overflow-x: hidden;
+  width: 100%;
+  height: 84px;
+  background: #f3f3f3;
+  font-family: Helvetica, sans, sans-serif; }
+  .smartbanner__exit {
+    position: absolute;
+    top: calc(50% - 6px);
+    left: 9px;
+    display: block;
+    margin: 0;
+    width: 12px;
+    height: 12px;
+    border: 0;
+    text-align: center; }
+    .smartbanner__exit::before, .smartbanner__exit::after {
+      position: absolute;
+      width: 1px;
+      height: 12px;
+      background: #767676;
+      content: ' '; }
+    .smartbanner__exit::before {
+      -webkit-transform: rotate(45deg);
+              transform: rotate(45deg); }
+    .smartbanner__exit::after {
+      -webkit-transform: rotate(-45deg);
+              transform: rotate(-45deg); }
+  .smartbanner__icon {
+    position: absolute;
+    top: 10px;
+    left: 30px;
+    width: 64px;
+    height: 64px;
+    border-radius: 15px;
+    background-size: 64px 64px; }
+  .smartbanner__info {
+    position: absolute;
+    top: 10px;
+    left: 104px;
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    overflow-y: hidden;
+    width: 60%;
+    height: 64px;
+    -webkit-box-align: center;
+        -ms-flex-align: center;
+            align-items: center; }
+    .smartbanner__info__title {
+      font-size: 14px; }
+    .smartbanner__info__author, .smartbanner__info__price {
+      font-size: 12px; }
+  .smartbanner__button {
+    position: absolute;
+    top: 32px;
+    right: 10px;
+    z-index: 1;
+    display: block;
+    padding: 0 10px;
+    min-width: 10%;
+    border-radius: 5px;
+    background: #f3f3f3;
+    color: #1474fc;
+    font-size: 18px;
+    text-align: center;
+    text-decoration: none; }
+    .smartbanner__button__label {
+      text-align: center; }
diff --git a/assets/smartbanner/smartbanner.js b/assets/smartbanner/smartbanner.js
new file mode 100644 (file)
index 0000000..d495cc1
--- /dev/null
@@ -0,0 +1,531 @@
+/*!
+ * smartbanner.js v1.5.0 <https://github.com/ain/smartbanner.js>
+ * Copyright © 2017 Ain Tohvri, contributors. Licensed under GPL-3.0.
+ */
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var Bakery = function () {
+  function Bakery() {
+    _classCallCheck(this, Bakery);
+  }
+
+  _createClass(Bakery, null, [{
+    key: 'bake',
+    value: function bake() {
+      document.cookie = 'smartbanner_exited=1';
+    }
+  }, {
+    key: 'unbake',
+    value: function unbake() {
+      document.cookie = 'smartbanner_exited=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
+    }
+  }, {
+    key: 'baked',
+    get: function get() {
+      var value = document.cookie.replace(/(?:(?:^|.*;\s*)smartbanner_exited\s*\=\s*([^;]*).*$)|^.*$/, '$1');
+      return value === '1';
+    }
+  }]);
+
+  return Bakery;
+}();
+
+exports.default = Bakery;
+
+},{}],2:[function(require,module,exports){
+(function (global){
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var Detector = function () {
+  function Detector() {
+    _classCallCheck(this, Detector);
+  }
+
+  _createClass(Detector, null, [{
+    key: 'platform',
+    value: function platform() {
+      if (/iPhone|iPad|iPod/i.test(window.navigator.userAgent)) {
+        return 'ios';
+      } else if (/Android/i.test(window.navigator.userAgent)) {
+        return 'android';
+      }
+    }
+  }, {
+    key: 'userAgentMatchesRegex',
+    value: function userAgentMatchesRegex(regexString) {
+      return new RegExp(regexString).test(window.navigator.userAgent);
+    }
+  }, {
+    key: 'jQueryMobilePage',
+    value: function jQueryMobilePage() {
+      return typeof global.$ !== 'undefined' && global.$.mobile !== 'undefined' && document.querySelector('.ui-page') !== null;
+    }
+  }, {
+    key: 'wrapperElement',
+    value: function wrapperElement() {
+      var selector = Detector.jQueryMobilePage() ? '.ui-page' : 'html';
+      return document.querySelectorAll(selector);
+    }
+  }]);
+
+  return Detector;
+}();
+
+exports.default = Detector;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],3:[function(require,module,exports){
+'use strict';
+
+var _smartbanner = require('./smartbanner.js');
+
+var _smartbanner2 = _interopRequireDefault(_smartbanner);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var smartbanner = void 0;
+
+window.addEventListener('load', function () {
+  smartbanner = new _smartbanner2.default();
+  smartbanner.publish();
+});
+
+},{"./smartbanner.js":7}],4:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+require('./polyfills/array/from.js');
+
+require('./polyfills/array/includes.js');
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function valid(name) {
+  // TODO: validate against options dictionary
+  return name.indexOf('smartbanner:') !== -1 && name.split(':')[1].length > 0;
+}
+
+function convertToCamelCase(name) {
+  var parts = name.split('-');
+  parts.map(function (part, index) {
+    if (index > 0) {
+      parts[index] = part.charAt(0).toUpperCase() + part.substring(1);
+    }
+  });
+  return parts.join('');
+}
+
+var OptionParser = function () {
+  function OptionParser() {
+    _classCallCheck(this, OptionParser);
+  }
+
+  _createClass(OptionParser, [{
+    key: 'parse',
+    value: function parse() {
+      var metas = document.getElementsByTagName('meta');
+      var options = {};
+      var optionName = null;
+      Array.from(metas).forEach(function (meta) {
+        var name = meta.getAttribute('name');
+        var content = meta.getAttribute('content');
+        if (name && content && valid(name) && content.length > 0) {
+          optionName = name.split(':')[1];
+          if (Array.from(optionName).includes('-')) {
+            optionName = convertToCamelCase(optionName);
+          }
+          options[optionName] = content;
+        }
+      });
+      return options;
+    }
+  }]);
+
+  return OptionParser;
+}();
+
+exports.default = OptionParser;
+
+},{"./polyfills/array/from.js":5,"./polyfills/array/includes.js":6}],5:[function(require,module,exports){
+'use strict';
+
+// Production steps of ECMA-262, Edition 6, 22.1.2.1
+// Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from
+if (!Array.from) {
+  Array.from = function () {
+    var toStr = Object.prototype.toString;
+    var isCallable = function isCallable(fn) {
+      return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
+    };
+    var toInteger = function toInteger(value) {
+      var number = Number(value);
+      if (isNaN(number)) {
+        return 0;
+      }
+      if (number === 0 || !isFinite(number)) {
+        return number;
+      }
+      return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
+    };
+    var maxSafeInteger = Math.pow(2, 53) - 1;
+    var toLength = function toLength(value) {
+      var len = toInteger(value);
+      return Math.min(Math.max(len, 0), maxSafeInteger);
+    };
+
+    // The length property of the from method is 1.
+    return function from(arrayLike /*, mapFn, thisArg */) {
+      // 1. Let C be the this value.
+      var C = this;
+
+      // 2. Let items be ToObject(arrayLike).
+      var items = Object(arrayLike);
+
+      // 3. ReturnIfAbrupt(items).
+      if (arrayLike == null) {
+        throw new TypeError("Array.from requires an array-like object - not null or undefined");
+      }
+
+      // 4. If mapfn is undefined, then let mapping be false.
+      var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
+      var T;
+      if (typeof mapFn !== 'undefined') {
+        // 5. else
+        // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
+        if (!isCallable(mapFn)) {
+          throw new TypeError('Array.from: when provided, the second argument must be a function');
+        }
+
+        // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
+        if (arguments.length > 2) {
+          T = arguments[2];
+        }
+      }
+
+      // 10. Let lenValue be Get(items, "length").
+      // 11. Let len be ToLength(lenValue).
+      var len = toLength(items.length);
+
+      // 13. If IsConstructor(C) is true, then
+      // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
+      // 14. a. Else, Let A be ArrayCreate(len).
+      var A = isCallable(C) ? Object(new C(len)) : new Array(len);
+
+      // 16. Let k be 0.
+      var k = 0;
+      // 17. Repeat, while k < len… (also steps a - h)
+      var kValue;
+      while (k < len) {
+        kValue = items[k];
+        if (mapFn) {
+          A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
+        } else {
+          A[k] = kValue;
+        }
+        k += 1;
+      }
+      // 18. Let putStatus be Put(A, "length", len, true).
+      A.length = len;
+      // 20. Return A.
+      return A;
+    };
+  }();
+}
+
+},{}],6:[function(require,module,exports){
+'use strict';
+
+if (!Array.prototype.includes) {
+  Array.prototype.includes = function (searchElement /*, fromIndex*/) {
+    'use strict';
+
+    if (this == null) {
+      throw new TypeError('Array.prototype.includes called on null or undefined');
+    }
+
+    var O = Object(this);
+    var len = parseInt(O.length, 10) || 0;
+    if (len === 0) {
+      return false;
+    }
+    var n = parseInt(arguments[1], 10) || 0;
+    var k;
+    if (n >= 0) {
+      k = n;
+    } else {
+      k = len + n;
+      if (k < 0) {
+        k = 0;
+      }
+    }
+    var currentElement;
+    while (k < len) {
+      currentElement = O[k];
+      if (searchElement === currentElement || searchElement !== searchElement && currentElement !== currentElement) {
+        // NaN !== NaN
+        return true;
+      }
+      k++;
+    }
+    return false;
+  };
+}
+
+},{}],7:[function(require,module,exports){
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _optionparser = require('./optionparser.js');
+
+var _optionparser2 = _interopRequireDefault(_optionparser);
+
+var _detector = require('./detector.js');
+
+var _detector2 = _interopRequireDefault(_detector);
+
+var _bakery = require('./bakery.js');
+
+var _bakery2 = _interopRequireDefault(_bakery);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var DEFAULT_PLATFORMS = 'android,ios';
+
+var datas = {
+  originalTop: 'data-smartbanner-original-top',
+  originalMarginTop: 'data-smartbanner-original-margin-top'
+};
+
+function handleExitClick(event, self) {
+  self.exit();
+  event.preventDefault();
+}
+
+function handleJQueryMobilePageLoad(event) {
+  if (!this.positioningDisabled) {
+    setContentPosition(event.data.height);
+  }
+}
+
+function addEventListeners(self) {
+  var closeIcon = document.querySelector('.js_smartbanner__exit');
+  closeIcon.addEventListener('click', function (event) {
+    return handleExitClick(event, self);
+  });
+  if (_detector2.default.jQueryMobilePage()) {
+    $(document).on('pagebeforeshow', self, handleJQueryMobilePageLoad);
+  }
+}
+
+function removeEventListeners() {
+  if (_detector2.default.jQueryMobilePage()) {
+    $(document).off('pagebeforeshow', handleJQueryMobilePageLoad);
+  }
+}
+
+function setContentPosition(value) {
+  var wrappers = _detector2.default.wrapperElement();
+  for (var i = 0, l = wrappers.length, wrapper; i < l; i++) {
+    wrapper = wrappers[i];
+    if (_detector2.default.jQueryMobilePage()) {
+      if (wrapper.getAttribute(datas.originalTop)) {
+        continue;
+      }
+      var top = parseFloat(getComputedStyle(wrapper).top);
+      wrapper.setAttribute(datas.originalTop, isNaN(top) ? 0 : top);
+      wrapper.style.top = value + 'px';
+    } else {
+      if (wrapper.getAttribute(datas.originalMarginTop)) {
+        continue;
+      }
+      var margin = parseFloat(getComputedStyle(wrapper).marginTop);
+      wrapper.setAttribute(datas.originalMarginTop, isNaN(margin) ? 0 : margin);
+      wrapper.style.marginTop = value + 'px';
+    }
+  }
+}
+
+function restoreContentPosition() {
+  var wrappers = _detector2.default.wrapperElement();
+  for (var i = 0, l = wrappers.length, wrapper; i < l; i++) {
+    wrapper = wrappers[i];
+    if (_detector2.default.jQueryMobilePage() && wrapper.getAttribute(datas.originalTop)) {
+      wrapper.style.top = wrapper.getAttribute(datas.originalTop) + 'px';
+    } else if (wrapper.getAttribute(datas.originalMarginTop)) {
+      wrapper.style.marginTop = wrapper.getAttribute(datas.originalMarginTop) + 'px';
+    }
+  }
+}
+
+var SmartBanner = function () {
+  function SmartBanner() {
+    _classCallCheck(this, SmartBanner);
+
+    var parser = new _optionparser2.default();
+    this.options = parser.parse();
+    this.platform = _detector2.default.platform();
+  }
+
+  // DEPRECATED. Will be removed.
+
+
+  _createClass(SmartBanner, [{
+    key: 'publish',
+    value: function publish() {
+      if (Object.keys(this.options).length === 0) {
+        throw new Error('No options detected. Please consult documentation.');
+      }
+
+      if (_bakery2.default.baked) {
+        return false;
+      }
+
+      // User Agent was explicetely excluded by defined excludeUserAgentRegex
+      if (this.userAgentExcluded) {
+        return false;
+      }
+
+      // User agent was neither included by platformEnabled,
+      // nor by defined includeUserAgentRegex
+      if (!(this.platformEnabled || this.userAgentIncluded)) {
+        return false;
+      }
+
+      var bannerDiv = document.createElement('div');
+      document.querySelector('body').appendChild(bannerDiv);
+      bannerDiv.outerHTML = this.html;
+      if (!this.positioningDisabled) {
+        setContentPosition(this.height);
+      }
+      addEventListeners(this);
+    }
+  }, {
+    key: 'exit',
+    value: function exit() {
+      removeEventListeners();
+      if (!this.positioningDisabled) {
+        restoreContentPosition();
+      }
+      var banner = document.querySelector('.js_smartbanner');
+      document.querySelector('body').removeChild(banner);
+      _bakery2.default.bake();
+    }
+  }, {
+    key: 'originalTop',
+    get: function get() {
+      var wrapper = _detector2.default.wrapperElement()[0];
+      return parseFloat(wrapper.getAttribute(datas.originalTop));
+    }
+
+    // DEPRECATED. Will be removed.
+
+  }, {
+    key: 'originalTopMargin',
+    get: function get() {
+      var wrapper = _detector2.default.wrapperElement()[0];
+      return parseFloat(wrapper.getAttribute(datas.originalMarginTop));
+    }
+  }, {
+    key: 'priceSuffix',
+    get: function get() {
+      if (this.platform === 'ios') {
+        return this.options.priceSuffixApple;
+      } else if (this.platform === 'android') {
+        return this.options.priceSuffixGoogle;
+      }
+      return '';
+    }
+  }, {
+    key: 'icon',
+    get: function get() {
+      if (this.platform === 'android') {
+        return this.options.iconGoogle;
+      } else {
+        return this.options.iconApple;
+      }
+    }
+  }, {
+    key: 'buttonUrl',
+    get: function get() {
+      if (this.platform === 'android') {
+        return this.options.buttonUrlGoogle;
+      } else if (this.platform === 'ios') {
+        return this.options.buttonUrlApple;
+      }
+      return '#';
+    }
+  }, {
+    key: 'html',
+    get: function get() {
+      return '<div class="smartbanner smartbanner--' + this.platform + ' js_smartbanner">\n      <a href="javascript:void();" class="smartbanner__exit js_smartbanner__exit"></a>\n      <div class="smartbanner__icon" style="background-image: url(' + this.icon + ');"></div>\n      <div class="smartbanner__info">\n        <div>\n          <div class="smartbanner__info__title">' + this.options.title + '</div>\n          <div class="smartbanner__info__author">' + this.options.author + '</div>\n          <div class="smartbanner__info__price">' + this.options.price + this.priceSuffix + '</div>\n        </div>\n      </div>\n      <a href="' + this.buttonUrl + '" target="_blank" class="smartbanner__button"><span class="smartbanner__button__label">' + this.options.button + '</span></a>\n    </div>';
+    }
+  }, {
+    key: 'height',
+    get: function get() {
+      var height = document.querySelector('.js_smartbanner').offsetHeight;
+      return height !== undefined ? height : 0;
+    }
+  }, {
+    key: 'platformEnabled',
+    get: function get() {
+      var enabledPlatforms = this.options.enabledPlatforms || DEFAULT_PLATFORMS;
+      return enabledPlatforms && enabledPlatforms.replace(/\s+/g, '').split(',').indexOf(this.platform) !== -1;
+    }
+  }, {
+    key: 'positioningDisabled',
+    get: function get() {
+      return this.options.disablePositioning === 'true';
+    }
+  }, {
+    key: 'userAgentExcluded',
+    get: function get() {
+      if (!this.options.excludeUserAgentRegex) {
+        return false;
+      }
+      return _detector2.default.userAgentMatchesRegex(this.options.excludeUserAgentRegex);
+    }
+  }, {
+    key: 'userAgentIncluded',
+    get: function get() {
+      if (!this.options.includeUserAgentRegex) {
+        return false;
+      }
+      return _detector2.default.userAgentMatchesRegex(this.options.includeUserAgentRegex);
+    }
+  }]);
+
+  return SmartBanner;
+}();
+
+exports.default = SmartBanner;
+
+},{"./bakery.js":1,"./detector.js":2,"./optionparser.js":4}]},{},[3]);
diff --git a/assets/smartbanner/smartbanner.png b/assets/smartbanner/smartbanner.png
new file mode 100644 (file)
index 0000000..cabccd0
Binary files /dev/null and b/assets/smartbanner/smartbanner.png differ
index 8e31621..886c48d 100644 (file)
         src="https://www.facebook.com/tr?id=989049894545336&ev=PageView&noscript=1"/>
     </noscript>
     <!-- End Facebook Pixel Code -->
+    <?php if ( in_array( $post->ID, array( 221, 228, 465 ) ) ) : ?>
+        <meta name="smartbanner:title" content="The Official Keweenaw App">
+        <meta name="smartbanner:author" content="VisitApps">
+        <meta name="smartbanner:price" content="FREE">
+        <meta name="smartbanner:price-suffix-apple" content=" - On the App Store">
+        <meta name="smartbanner:price-suffix-google" content=" - In Google Play">
+        <meta name="smartbanner:icon-apple" content="<?php echo get_stylesheet_directory_uri() ; ?>assets/smartbanner/smartbanner.png">
+        <meta name="smartbanner:icon-google" content="<?php echo get_stylesheet_directory_uri() ; ?>assets/smartbanner/smartbanner.png">
+        <link rel="stylesheet" href="<?php echo get_stylesheet_directory_uri() ; ?>assets/smartbanner/smartbanner.css">
+        <script src="<?php echo get_stylesheet_directory_uri() ; ?>assets/smartbanner/smartbanner.js"></script>
+    <?php endif; ?>
   </head>