Browse Source

Cleanup, Scriüt optimizations..

Moritz Schmidt 9 năm trước cách đây
mục cha
commit
f0e12f5318
3 tập tin đã thay đổi với 13 bổ sung2981 xóa
  1. 0 2980
      dist/holder.js
  2. 0 1
      footer.php
  3. 13 0
      functions.php

+ 0 - 2980
dist/holder.js

@@ -1,2980 +0,0 @@
-/*!
-
-Holder - client side image placeholders
-Version 2.9.0+f2dkw
-© 2015 Ivan Malopinsky - http://imsky.co
-
-Site:     http://holderjs.com
-Issues:   https://github.com/imsky/holder/issues
-License:  MIT
-
-*/
-(function (window) {
-  if (!window.document) return;
-  var document = window.document;
-
-  //https://github.com/inexorabletash/polyfill/blob/master/web.js
-    if (!document.querySelectorAll) {
-      document.querySelectorAll = function (selectors) {
-        var style = document.createElement('style'), elements = [], element;
-        document.documentElement.firstChild.appendChild(style);
-        document._qsa = [];
-
-        style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
-        window.scrollBy(0, 0);
-        style.parentNode.removeChild(style);
-
-        while (document._qsa.length) {
-          element = document._qsa.shift();
-          element.style.removeAttribute('x-qsa');
-          elements.push(element);
-        }
-        document._qsa = null;
-        return elements;
-      };
-    }
-
-    if (!document.querySelector) {
-      document.querySelector = function (selectors) {
-        var elements = document.querySelectorAll(selectors);
-        return (elements.length) ? elements[0] : null;
-      };
-    }
-
-    if (!document.getElementsByClassName) {
-      document.getElementsByClassName = function (classNames) {
-        classNames = String(classNames).replace(/^|\s+/g, '.');
-        return document.querySelectorAll(classNames);
-      };
-    }
-
-  //https://github.com/inexorabletash/polyfill
-  // ES5 15.2.3.14 Object.keys ( O )
-  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
-  if (!Object.keys) {
-    Object.keys = function (o) {
-      if (o !== Object(o)) { throw TypeError('Object.keys called on non-object'); }
-      var ret = [], p;
-      for (p in o) {
-        if (Object.prototype.hasOwnProperty.call(o, p)) {
-          ret.push(p);
-        }
-      }
-      return ret;
-    };
-  }
-
-  // ES5 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )
-  // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
-  if (!Array.prototype.forEach) {
-    Array.prototype.forEach = function (fun /*, thisp */) {
-      if (this === void 0 || this === null) { throw TypeError(); }
-
-      var t = Object(this);
-      var len = t.length >>> 0;
-      if (typeof fun !== "function") { throw TypeError(); }
-
-      var thisp = arguments[1], i;
-      for (i = 0; i < len; i++) {
-        if (i in t) {
-          fun.call(thisp, t[i], i, t);
-        }
-      }
-    };
-  }
-
-  //https://github.com/inexorabletash/polyfill/blob/master/web.js
-  (function (global) {
-    var B64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
-    global.atob = global.atob || function (input) {
-      input = String(input);
-      var position = 0,
-          output = [],
-          buffer = 0, bits = 0, n;
-
-      input = input.replace(/\s/g, '');
-      if ((input.length % 4) === 0) { input = input.replace(/=+$/, ''); }
-      if ((input.length % 4) === 1) { throw Error('InvalidCharacterError'); }
-      if (/[^+/0-9A-Za-z]/.test(input)) { throw Error('InvalidCharacterError'); }
-
-      while (position < input.length) {
-        n = B64_ALPHABET.indexOf(input.charAt(position));
-        buffer = (buffer << 6) | n;
-        bits += 6;
-
-        if (bits === 24) {
-          output.push(String.fromCharCode((buffer >> 16) & 0xFF));
-          output.push(String.fromCharCode((buffer >>  8) & 0xFF));
-          output.push(String.fromCharCode(buffer & 0xFF));
-          bits = 0;
-          buffer = 0;
-        }
-        position += 1;
-      }
-
-      if (bits === 12) {
-        buffer = buffer >> 4;
-        output.push(String.fromCharCode(buffer & 0xFF));
-      } else if (bits === 18) {
-        buffer = buffer >> 2;
-        output.push(String.fromCharCode((buffer >> 8) & 0xFF));
-        output.push(String.fromCharCode(buffer & 0xFF));
-      }
-
-      return output.join('');
-    };
-
-    global.btoa = global.btoa || function (input) {
-      input = String(input);
-      var position = 0,
-          out = [],
-          o1, o2, o3,
-          e1, e2, e3, e4;
-
-      if (/[^\x00-\xFF]/.test(input)) { throw Error('InvalidCharacterError'); }
-
-      while (position < input.length) {
-        o1 = input.charCodeAt(position++);
-        o2 = input.charCodeAt(position++);
-        o3 = input.charCodeAt(position++);
-
-        // 111111 112222 222233 333333
-        e1 = o1 >> 2;
-        e2 = ((o1 & 0x3) << 4) | (o2 >> 4);
-        e3 = ((o2 & 0xf) << 2) | (o3 >> 6);
-        e4 = o3 & 0x3f;
-
-        if (position === input.length + 2) {
-          e3 = 64; e4 = 64;
-        }
-        else if (position === input.length + 1) {
-          e4 = 64;
-        }
-
-        out.push(B64_ALPHABET.charAt(e1),
-                 B64_ALPHABET.charAt(e2),
-                 B64_ALPHABET.charAt(e3),
-                 B64_ALPHABET.charAt(e4));
-      }
-
-      return out.join('');
-    };
-  }(window));
-
-  //https://gist.github.com/jimeh/332357
-  if (!Object.prototype.hasOwnProperty){
-      /*jshint -W001, -W103 */
-      Object.prototype.hasOwnProperty = function(prop) {
-      var proto = this.__proto__ || this.constructor.prototype;
-      return (prop in this) && (!(prop in proto) || proto[prop] !== this[prop]);
-    };
-      /*jshint +W001, +W103 */
-  }
-
-  // @license http://opensource.org/licenses/MIT
-  // copyright Paul Irish 2015
-
-
-  // Date.now() is supported everywhere except IE8. For IE8 we use the Date.now polyfill
-  //   github.com/Financial-Times/polyfill-service/blob/master/polyfills/Date.now/polyfill.js
-  // as Safari 6 doesn't have support for NavigationTiming, we use a Date.now() timestamp for relative values
-
-  // if you want values similar to what you'd get with real perf.now, place this towards the head of the page
-  // but in reality, you're just getting the delta between now() calls, so it's not terribly important where it's placed
-
-
-  (function(){
-
-    if ('performance' in window === false) {
-        window.performance = {};
-    }
-    
-    Date.now = (Date.now || function () {  // thanks IE8
-      return new Date().getTime();
-    });
-
-    if ('now' in window.performance === false){
-      
-      var nowOffset = Date.now();
-      
-      if (performance.timing && performance.timing.navigationStart){
-        nowOffset = performance.timing.navigationStart;
-      }
-
-      window.performance.now = function now(){
-        return Date.now() - nowOffset;
-      };
-    }
-
-  })();
-
-  //requestAnimationFrame polyfill for older Firefox/Chrome versions
-  if (!window.requestAnimationFrame) {
-    if (window.webkitRequestAnimationFrame) {
-    //https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/requestAnimationFrame/polyfill-webkit.js
-    (function (global) {
-      // window.requestAnimationFrame
-      global.requestAnimationFrame = function (callback) {
-        return webkitRequestAnimationFrame(function () {
-          callback(global.performance.now());
-        });
-      };
-
-      // window.cancelAnimationFrame
-      global.cancelAnimationFrame = webkitCancelAnimationFrame;
-    }(window));
-    } else if (window.mozRequestAnimationFrame) {
-      //https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/requestAnimationFrame/polyfill-moz.js
-    (function (global) {
-      // window.requestAnimationFrame
-      global.requestAnimationFrame = function (callback) {
-        return mozRequestAnimationFrame(function () {
-          callback(global.performance.now());
-        });
-      };
-
-      // window.cancelAnimationFrame
-      global.cancelAnimationFrame = mozCancelAnimationFrame;
-    }(window));
-    } else {
-    (function (global) {
-      global.requestAnimationFrame = function (callback) {
-      return global.setTimeout(callback, 1000 / 60);
-      };
-
-      global.cancelAnimationFrame = global.clearTimeout;
-    })(window);
-    }
-  }
-})(this);
-
-(function webpackUniversalModuleDefinition(root, factory) {
-	if(typeof exports === 'object' && typeof module === 'object')
-		module.exports = factory();
-	else if(typeof define === 'function' && define.amd)
-		define([], factory);
-	else if(typeof exports === 'object')
-		exports["Holder"] = factory();
-	else
-		root["Holder"] = factory();
-})(this, function() {
-return /******/ (function(modules) { // webpackBootstrap
-/******/ 	// The module cache
-/******/ 	var installedModules = {};
-
-/******/ 	// The require function
-/******/ 	function __webpack_require__(moduleId) {
-
-/******/ 		// Check if module is in cache
-/******/ 		if(installedModules[moduleId])
-/******/ 			return installedModules[moduleId].exports;
-
-/******/ 		// Create a new module (and put it into the cache)
-/******/ 		var module = installedModules[moduleId] = {
-/******/ 			exports: {},
-/******/ 			id: moduleId,
-/******/ 			loaded: false
-/******/ 		};
-
-/******/ 		// Execute the module function
-/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
-
-/******/ 		// Flag the module as loaded
-/******/ 		module.loaded = true;
-
-/******/ 		// Return the exports of the module
-/******/ 		return module.exports;
-/******/ 	}
-
-
-/******/ 	// expose the modules object (__webpack_modules__)
-/******/ 	__webpack_require__.m = modules;
-
-/******/ 	// expose the module cache
-/******/ 	__webpack_require__.c = installedModules;
-
-/******/ 	// __webpack_public_path__
-/******/ 	__webpack_require__.p = "";
-
-/******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(0);
-/******/ })
-/************************************************************************/
-/******/ ([
-/* 0 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/*
-	Holder.js - client side image placeholders
-	(c) 2012-2015 Ivan Malopinsky - http://imsky.co
-	*/
-
-	module.exports = __webpack_require__(1);
-
-
-/***/ },
-/* 1 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* WEBPACK VAR INJECTION */(function(global) {/*
-	Holder.js - client side image placeholders
-	(c) 2012-2015 Ivan Malopinsky - http://imsky.co
-	*/
-
-	//Libraries and functions
-	var onDomReady = __webpack_require__(2);
-	var querystring = __webpack_require__(3);
-
-	var SceneGraph = __webpack_require__(6);
-	var utils = __webpack_require__(7);
-	var SVG = __webpack_require__(8);
-	var DOM = __webpack_require__(9);
-	var Color = __webpack_require__(10);
-	var constants = __webpack_require__(11);
-
-	var svgRenderer = __webpack_require__(12);
-	var sgCanvasRenderer = __webpack_require__(15);
-
-	var extend = utils.extend;
-	var dimensionCheck = utils.dimensionCheck;
-
-	//Constants and definitions
-	var SVG_NS = constants.svg_ns;
-
-	var Holder = {
-	    version: constants.version,
-
-	    /**
-	     * Adds a theme to default settings
-	     *
-	     * @param {string} name Theme name
-	     * @param {Object} theme Theme object, with foreground, background, size, font, and fontweight properties.
-	     */
-	    addTheme: function(name, theme) {
-	        name != null && theme != null && (App.settings.themes[name] = theme);
-	        delete App.vars.cache.themeKeys;
-	        return this;
-	    },
-
-	    /**
-	     * Appends a placeholder to an element
-	     *
-	     * @param {string} src Placeholder URL string
-	     * @param el A selector or a reference to a DOM node
-	     */
-	    addImage: function(src, el) {
-	        //todo: use jquery fallback if available for all QSA references
-	        var nodes = DOM.getNodeArray(el);
-	        nodes.forEach(function (node) {
-	            var img = DOM.newEl('img');
-	            var domProps = {};
-	            domProps[App.setup.dataAttr] = src;
-	            DOM.setAttr(img, domProps);
-	            node.appendChild(img);
-	        });
-	        return this;
-	    },
-
-	    /**
-	     * Sets whether or not an image is updated on resize.
-	     * If an image is set to be updated, it is immediately rendered.
-	     *
-	     * @param {Object} el Image DOM element
-	     * @param {Boolean} value Resizable update flag value
-	     */
-	    setResizeUpdate: function(el, value) {
-	        if (el.holderData) {
-	            el.holderData.resizeUpdate = !!value;
-	            if (el.holderData.resizeUpdate) {
-	                updateResizableElements(el);
-	            }
-	        }
-	    },
-
-	    /**
-	     * Runs Holder with options. By default runs Holder on all images with "holder.js" in their source attributes.
-	     *
-	     * @param {Object} userOptions Options object, can contain domain, themes, images, and bgnodes properties
-	     */
-	    run: function(userOptions) {
-	        //todo: split processing into separate queues
-	        userOptions = userOptions || {};
-	        var engineSettings = {};
-	        var options = extend(App.settings, userOptions);
-
-	        App.vars.preempted = true;
-	        App.vars.dataAttr = options.dataAttr || App.setup.dataAttr;
-
-	        engineSettings.renderer = options.renderer ? options.renderer : App.setup.renderer;
-	        if (App.setup.renderers.join(',').indexOf(engineSettings.renderer) === -1) {
-	            engineSettings.renderer = App.setup.supportsSVG ? 'svg' : (App.setup.supportsCanvas ? 'canvas' : 'html');
-	        }
-
-	        var images = DOM.getNodeArray(options.images);
-	        var bgnodes = DOM.getNodeArray(options.bgnodes);
-	        var stylenodes = DOM.getNodeArray(options.stylenodes);
-	        var objects = DOM.getNodeArray(options.objects);
-
-	        engineSettings.stylesheets = [];
-	        engineSettings.svgXMLStylesheet = true;
-	        engineSettings.noFontFallback = options.noFontFallback ? options.noFontFallback : false;
-
-	        stylenodes.forEach(function (styleNode) {
-	            if (styleNode.attributes.rel && styleNode.attributes.href && styleNode.attributes.rel.value == 'stylesheet') {
-	                var href = styleNode.attributes.href.value;
-	                //todo: write isomorphic relative-to-absolute URL function
-	                var proxyLink = DOM.newEl('a');
-	                proxyLink.href = href;
-	                var stylesheetURL = proxyLink.protocol + '//' + proxyLink.host + proxyLink.pathname + proxyLink.search;
-	                engineSettings.stylesheets.push(stylesheetURL);
-	            }
-	        });
-
-	        bgnodes.forEach(function (bgNode) {
-	            //Skip processing background nodes if getComputedStyle is unavailable, since only modern browsers would be able to use canvas or SVG to render to background
-	            if (!global.getComputedStyle) return;
-	            var backgroundImage = global.getComputedStyle(bgNode, null).getPropertyValue('background-image');
-	            var dataBackgroundImage = bgNode.getAttribute('data-background-src');
-	            var rawURL = dataBackgroundImage || backgroundImage;
-
-	            var holderURL = null;
-	            var holderString = options.domain + '/';
-	            var holderStringIndex = rawURL.indexOf(holderString);
-
-	            if (holderStringIndex === 0) {
-	                holderURL = rawURL;
-	            } else if (holderStringIndex === 1 && rawURL[0] === '?') {
-	                holderURL = rawURL.slice(1);
-	            } else {
-	                var fragment = rawURL.substr(holderStringIndex).match(/([^\"]*)"?\)/);
-	                if (fragment !== null) {
-	                    holderURL = fragment[1];
-	                } else if (rawURL.indexOf('url(') === 0) {
-	                    throw 'Holder: unable to parse background URL: ' + rawURL;
-	                }
-	            }
-
-	            if (holderURL != null) {
-	                var holderFlags = parseURL(holderURL, options);
-	                if (holderFlags) {
-	                    prepareDOMElement({
-	                        mode: 'background',
-	                        el: bgNode,
-	                        flags: holderFlags,
-	                        engineSettings: engineSettings
-	                    });
-	                }
-	            }
-	        });
-
-	        objects.forEach(function (object) {
-	            var objectAttr = {};
-
-	            try {
-	                objectAttr.data = object.getAttribute('data');
-	                objectAttr.dataSrc = object.getAttribute(App.vars.dataAttr);
-	            } catch (e) {}
-
-	            var objectHasSrcURL = objectAttr.data != null && objectAttr.data.indexOf(options.domain) === 0;
-	            var objectHasDataSrcURL = objectAttr.dataSrc != null && objectAttr.dataSrc.indexOf(options.domain) === 0;
-
-	            if (objectHasSrcURL) {
-	                prepareImageElement(options, engineSettings, objectAttr.data, object);
-	            } else if (objectHasDataSrcURL) {
-	                prepareImageElement(options, engineSettings, objectAttr.dataSrc, object);
-	            }
-	        });
-
-	        images.forEach(function (image) {
-	            var imageAttr = {};
-
-	            try {
-	                imageAttr.src = image.getAttribute('src');
-	                imageAttr.dataSrc = image.getAttribute(App.vars.dataAttr);
-	                imageAttr.rendered = image.getAttribute('data-holder-rendered');
-	            } catch (e) {}
-
-	            var imageHasSrc = imageAttr.src != null;
-	            var imageHasDataSrcURL = imageAttr.dataSrc != null && imageAttr.dataSrc.indexOf(options.domain) === 0;
-	            var imageRendered = imageAttr.rendered != null && imageAttr.rendered == 'true';
-
-	            if (imageHasSrc) {
-	                if (imageAttr.src.indexOf(options.domain) === 0) {
-	                    prepareImageElement(options, engineSettings, imageAttr.src, image);
-	                } else if (imageHasDataSrcURL) {
-	                    //Image has a valid data-src and an invalid src
-	                    if (imageRendered) {
-	                        //If the placeholder has already been render, re-render it
-	                        prepareImageElement(options, engineSettings, imageAttr.dataSrc, image);
-	                    } else {
-	                        //If the placeholder has not been rendered, check if the image exists and render a fallback if it doesn't
-	                        (function(src, options, engineSettings, dataSrc, image) {
-	                            utils.imageExists(src, function(exists) {
-	                                if (!exists) {
-	                                    prepareImageElement(options, engineSettings, dataSrc, image);
-	                                }
-	                            });
-	                        })(imageAttr.src, options, engineSettings, imageAttr.dataSrc, image);
-	                    }
-	                }
-	            } else if (imageHasDataSrcURL) {
-	                prepareImageElement(options, engineSettings, imageAttr.dataSrc, image);
-	            }
-	        });
-
-	        return this;
-	    }
-	};
-
-	var App = {
-	    settings: {
-	        domain: 'holder.js',
-	        images: 'img',
-	        objects: 'object',
-	        bgnodes: 'body .holderjs',
-	        stylenodes: 'head link.holderjs',
-	        themes: {
-	            'gray': {
-	                bg: '#EEEEEE',
-	                fg: '#AAAAAA'
-	            },
-	            'social': {
-	                bg: '#3a5a97',
-	                fg: '#FFFFFF'
-	            },
-	            'industrial': {
-	                bg: '#434A52',
-	                fg: '#C2F200'
-	            },
-	            'sky': {
-	                bg: '#0D8FDB',
-	                fg: '#FFFFFF'
-	            },
-	            'vine': {
-	                bg: '#39DBAC',
-	                fg: '#1E292C'
-	            },
-	            'lava': {
-	                bg: '#F8591A',
-	                fg: '#1C2846'
-	            }
-	        }
-	    },
-	    defaults: {
-	        size: 10,
-	        units: 'pt',
-	        scale: 1 / 16
-	    }
-	};
-
-	/**
-	 * Processes provided source attribute and sets up the appropriate rendering workflow
-	 *
-	 * @private
-	 * @param options Instance options from Holder.run
-	 * @param renderSettings Instance configuration
-	 * @param src Image URL
-	 * @param el Image DOM element
-	 */
-	function prepareImageElement(options, engineSettings, src, el) {
-	    var holderFlags = parseURL(src.substr(src.lastIndexOf(options.domain)), options);
-	    if (holderFlags) {
-	        prepareDOMElement({
-	            mode: null,
-	            el: el,
-	            flags: holderFlags,
-	            engineSettings: engineSettings
-	        });
-	    }
-	}
-
-	/**
-	 * Processes a Holder URL and extracts configuration from query string
-	 *
-	 * @private
-	 * @param url URL
-	 * @param instanceOptions Instance options from Holder.run
-	 */
-	function parseURL(url, instanceOptions) {
-	    var holder = {
-	        theme: extend(App.settings.themes.gray, null),
-	        stylesheets: instanceOptions.stylesheets,
-	        instanceOptions: instanceOptions
-	    };
-
-	    var parts = url.split('?');
-	    var basics = parts[0].split('/');
-
-	    holder.holderURL = url;
-
-	    var dimensions = basics[1];
-	    var dimensionData = dimensions.match(/([\d]+p?)x([\d]+p?)/);
-
-	    if (!dimensionData) return false;
-
-	    holder.fluid = dimensions.indexOf('p') !== -1;
-
-	    holder.dimensions = {
-	        width: dimensionData[1].replace('p', '%'),
-	        height: dimensionData[2].replace('p', '%')
-	    };
-
-	    if (parts.length === 2) {
-	        var options = querystring.parse(parts[1]);
-
-	        // Colors
-
-	        if (options.bg) {
-	            holder.theme.bg = utils.parseColor(options.bg);
-	        }
-
-	        if (options.fg) {
-	            holder.theme.fg = utils.parseColor(options.fg);
-	        }
-
-	        //todo: add automatic foreground to themes without foreground
-	        if (options.bg && !options.fg) {
-	            holder.autoFg = true;
-	        }
-
-	        if (options.theme && holder.instanceOptions.themes.hasOwnProperty(options.theme)) {
-	            holder.theme = extend(holder.instanceOptions.themes[options.theme], null);
-	        }
-
-	        // Text
-
-	        if (options.text) {
-	            holder.text = options.text;
-	        }
-
-	        if (options.textmode) {
-	            holder.textmode = options.textmode;
-	        }
-
-	        if (options.size) {
-	            holder.size = options.size;
-	        }
-
-	        if (options.font) {
-	            holder.font = options.font;
-	        }
-
-	        if (options.align) {
-	            holder.align = options.align;
-	        }
-
-	        if (options.lineWrap) {
-	            holder.lineWrap = options.lineWrap;
-	        }
-
-	        holder.nowrap = utils.truthy(options.nowrap);
-
-	        // Miscellaneous
-
-	        holder.auto = utils.truthy(options.auto);
-
-	        holder.outline = utils.truthy(options.outline);
-
-	        if (utils.truthy(options.random)) {
-	            App.vars.cache.themeKeys = App.vars.cache.themeKeys || Object.keys(holder.instanceOptions.themes);
-	            var _theme = App.vars.cache.themeKeys[0 | Math.random() * App.vars.cache.themeKeys.length];
-	            holder.theme = extend(holder.instanceOptions.themes[_theme], null);
-	        }
-	    }
-
-	    return holder;
-	}
-
-	/**
-	 * Modifies the DOM to fit placeholders and sets up resizable image callbacks (for fluid and automatically sized placeholders)
-	 *
-	 * @private
-	 * @param settings DOM prep settings
-	 */
-	function prepareDOMElement(prepSettings) {
-	    var mode = prepSettings.mode;
-	    var el = prepSettings.el;
-	    var flags = prepSettings.flags;
-	    var _engineSettings = prepSettings.engineSettings;
-	    var dimensions = flags.dimensions,
-	        theme = flags.theme;
-	    var dimensionsCaption = dimensions.width + 'x' + dimensions.height;
-	    mode = mode == null ? (flags.fluid ? 'fluid' : 'image') : mode;
-	    var holderTemplateRe = /holder_([a-z]+)/g;
-	    var dimensionsInText = false;
-
-	    if (flags.text != null) {
-	        theme.text = flags.text;
-
-	        //<object> SVG embedding doesn't parse Unicode properly
-	        if (el.nodeName.toLowerCase() === 'object') {
-	            var textLines = theme.text.split('\\n');
-	            for (var k = 0; k < textLines.length; k++) {
-	                textLines[k] = utils.encodeHtmlEntity(textLines[k]);
-	            }
-	            theme.text = textLines.join('\\n');
-	        }
-	    }
-
-	    if (theme.text) {
-	        var holderTemplateMatches = theme.text.match(holderTemplateRe);
-
-	        if (holderTemplateMatches !== null) {
-	            //todo: optimize template replacement
-	            holderTemplateMatches.forEach(function (match) {
-	                if (match === 'holder_dimensions') {
-	                    theme.text = theme.text.replace(match, dimensionsCaption);
-	                }
-	            });
-	        }
-	    }
-
-	    var holderURL = flags.holderURL;
-	    var engineSettings = extend(_engineSettings, null);
-
-	    if (flags.font) {
-	        /*
-	        If external fonts are used in a <img> placeholder rendered with SVG, Holder falls back to canvas.
-
-	        This is done because Firefox and Chrome disallow embedded SVGs from referencing external assets.
-	        The workaround is either to change the placeholder tag from <img> to <object> or to use the canvas renderer.
-	        */
-	        theme.font = flags.font;
-	        if (!engineSettings.noFontFallback && el.nodeName.toLowerCase() === 'img' && App.setup.supportsCanvas && engineSettings.renderer === 'svg') {
-	            engineSettings = extend(engineSettings, {
-	                renderer: 'canvas'
-	            });
-	        }
-	    }
-
-	    //Chrome and Opera require a quick 10ms re-render if web fonts are used with canvas
-	    if (flags.font && engineSettings.renderer == 'canvas') {
-	        engineSettings.reRender = true;
-	    }
-
-	    if (mode == 'background') {
-	        if (el.getAttribute('data-background-src') == null) {
-	            DOM.setAttr(el, {
-	                'data-background-src': holderURL
-	            });
-	        }
-	    } else {
-	        var domProps = {};
-	        domProps[App.vars.dataAttr] = holderURL;
-	        DOM.setAttr(el, domProps);
-	    }
-
-	    flags.theme = theme;
-
-	    //todo consider using all renderSettings in holderData
-	    el.holderData = {
-	        flags: flags,
-	        engineSettings: engineSettings
-	    };
-
-	    if (mode == 'image' || mode == 'fluid') {
-	        DOM.setAttr(el, {
-	            'alt': theme.text ? (dimensionsInText ? theme.text : theme.text + ' [' + dimensionsCaption + ']') : dimensionsCaption
-	        });
-	    }
-
-	    var renderSettings = {
-	        mode: mode,
-	        el: el,
-	        holderSettings: {
-	            dimensions: dimensions,
-	            theme: theme,
-	            flags: flags
-	        },
-	        engineSettings: engineSettings
-	    };
-
-	    if (mode == 'image') {
-	        if (!flags.auto) {
-	            el.style.width = dimensions.width + 'px';
-	            el.style.height = dimensions.height + 'px';
-	        }
-
-	        if (engineSettings.renderer == 'html') {
-	            el.style.backgroundColor = theme.background;
-	        } else {
-	            render(renderSettings);
-
-	            if (flags.textmode == 'exact') {
-	                el.holderData.resizeUpdate = true;
-	                App.vars.resizableImages.push(el);
-	                updateResizableElements(el);
-	            }
-	        }
-	    } else if (mode == 'background' && engineSettings.renderer != 'html') {
-	        render(renderSettings);
-	    } else if (mode == 'fluid') {
-	        el.holderData.resizeUpdate = true;
-
-	        if (dimensions.height.slice(-1) == '%') {
-	            el.style.height = dimensions.height;
-	        } else if (flags.auto == null || !flags.auto) {
-	            el.style.height = dimensions.height + 'px';
-	        }
-	        if (dimensions.width.slice(-1) == '%') {
-	            el.style.width = dimensions.width;
-	        } else if (flags.auto == null || !flags.auto) {
-	            el.style.width = dimensions.width + 'px';
-	        }
-	        if (el.style.display == 'inline' || el.style.display === '' || el.style.display == 'none') {
-	            el.style.display = 'block';
-	        }
-
-	        setInitialDimensions(el);
-
-	        if (engineSettings.renderer == 'html') {
-	            el.style.backgroundColor = theme.background;
-	        } else {
-	            App.vars.resizableImages.push(el);
-	            updateResizableElements(el);
-	        }
-	    }
-	}
-
-	/**
-	 * Core function that takes output from renderers and sets it as the source or background-image of the target element
-	 *
-	 * @private
-	 * @param renderSettings Renderer settings
-	 */
-	function render(renderSettings) {
-	    var image = null;
-	    var mode = renderSettings.mode;
-	    var el = renderSettings.el;
-	    var holderSettings = renderSettings.holderSettings;
-	    var engineSettings = renderSettings.engineSettings;
-
-	    switch (engineSettings.renderer) {
-	        case 'svg':
-	            if (!App.setup.supportsSVG) return;
-	            break;
-	        case 'canvas':
-	            if (!App.setup.supportsCanvas) return;
-	            break;
-	        default:
-	            return;
-	    }
-
-	    //todo: move generation of scene up to flag generation to reduce extra object creation
-	    var scene = {
-	        width: holderSettings.dimensions.width,
-	        height: holderSettings.dimensions.height,
-	        theme: holderSettings.theme,
-	        flags: holderSettings.flags
-	    };
-
-	    var sceneGraph = buildSceneGraph(scene);
-
-	    function getRenderedImage() {
-	        var image = null;
-	        switch (engineSettings.renderer) {
-	            case 'canvas':
-	                image = sgCanvasRenderer(sceneGraph, renderSettings);
-	                break;
-	            case 'svg':
-	                image = svgRenderer(sceneGraph, renderSettings);
-	                break;
-	            default:
-	                throw 'Holder: invalid renderer: ' + engineSettings.renderer;
-	        }
-
-	        return image;
-	    }
-
-	    image = getRenderedImage();
-
-	    if (image == null) {
-	        throw 'Holder: couldn\'t render placeholder';
-	    }
-
-	    //todo: add <object> canvas rendering
-	    if (mode == 'background') {
-	        el.style.backgroundImage = 'url(' + image + ')';
-	        el.style.backgroundSize = scene.width + 'px ' + scene.height + 'px';
-	    } else {
-	        if (el.nodeName.toLowerCase() === 'img') {
-	            DOM.setAttr(el, {
-	                'src': image
-	            });
-	        } else if (el.nodeName.toLowerCase() === 'object') {
-	            DOM.setAttr(el, {
-	                'data': image,
-	                'type': 'image/svg+xml'
-	            });
-	        }
-	        if (engineSettings.reRender) {
-	            global.setTimeout(function () {
-	                var image = getRenderedImage();
-	                if (image == null) {
-	                    throw 'Holder: couldn\'t render placeholder';
-	                }
-	                //todo: refactor this code into a function
-	                if (el.nodeName.toLowerCase() === 'img') {
-	                    DOM.setAttr(el, {
-	                        'src': image
-	                    });
-	                } else if (el.nodeName.toLowerCase() === 'object') {
-	                    DOM.setAttr(el, {
-	                        'data': image,
-	                        'type': 'image/svg+xml'
-	                    });
-	                }
-	            }, 150);
-	        }
-	    }
-	    //todo: account for re-rendering
-	    DOM.setAttr(el, {
-	        'data-holder-rendered': true
-	    });
-	}
-
-	/**
-	 * Core function that takes a Holder scene description and builds a scene graph
-	 *
-	 * @private
-	 * @param scene Holder scene object
-	 */
-	//todo: make this function reusable
-	//todo: merge app defaults and setup properties into the scene argument
-	function buildSceneGraph(scene) {
-	    var fontSize = App.defaults.size;
-	    if (parseFloat(scene.theme.size)) {
-	        fontSize = scene.theme.size;
-	    } else if (parseFloat(scene.flags.size)) {
-	        fontSize = scene.flags.size;
-	    }
-
-	    scene.font = {
-	        family: scene.theme.font ? scene.theme.font : 'Arial, Helvetica, Open Sans, sans-serif',
-	        size: textSize(scene.width, scene.height, fontSize, App.defaults.scale),
-	        units: scene.theme.units ? scene.theme.units : App.defaults.units,
-	        weight: scene.theme.fontweight ? scene.theme.fontweight : 'bold'
-	    };
-
-	    scene.text = scene.theme.text || Math.floor(scene.width) + 'x' + Math.floor(scene.height);
-
-	    scene.noWrap = scene.theme.nowrap || scene.flags.nowrap;
-
-	    scene.align = scene.theme.align || scene.flags.align || 'center';
-
-	    switch (scene.flags.textmode) {
-	        case 'literal':
-	            scene.text = scene.flags.dimensions.width + 'x' + scene.flags.dimensions.height;
-	            break;
-	        case 'exact':
-	            if (!scene.flags.exactDimensions) break;
-	            scene.text = Math.floor(scene.flags.exactDimensions.width) + 'x' + Math.floor(scene.flags.exactDimensions.height);
-	            break;
-	    }
-
-	    var lineWrap = scene.flags.lineWrap || App.setup.lineWrapRatio;
-	    var sceneMargin = scene.width * lineWrap;
-	    var maxLineWidth = sceneMargin;
-
-	    var sceneGraph = new SceneGraph({
-	        width: scene.width,
-	        height: scene.height
-	    });
-
-	    var Shape = sceneGraph.Shape;
-
-	    var holderBg = new Shape.Rect('holderBg', {
-	        fill: scene.theme.bg
-	    });
-
-	    holderBg.resize(scene.width, scene.height);
-	    sceneGraph.root.add(holderBg);
-
-	    if (scene.flags.outline) {
-	        var outlineColor = new Color(holderBg.properties.fill);
-	        outlineColor = outlineColor.lighten(outlineColor.lighterThan('7f7f7f') ? -0.1 : 0.1);
-	        holderBg.properties.outline = {
-	            fill: outlineColor.toHex(true),
-	            width: 2
-	        };
-	    }
-
-	    var holderTextColor = scene.theme.fg;
-
-	    if (scene.flags.autoFg) {
-	        var holderBgColor = new Color(holderBg.properties.fill);
-	        var lightColor = new Color('fff');
-	        var darkColor = new Color('000', {
-	            'alpha': 0.285714
-	        });
-
-	        holderTextColor = holderBgColor.blendAlpha(holderBgColor.lighterThan('7f7f7f') ? darkColor : lightColor).toHex(true);
-	    }
-
-	    var holderTextGroup = new Shape.Group('holderTextGroup', {
-	        text: scene.text,
-	        align: scene.align,
-	        font: scene.font,
-	        fill: holderTextColor
-	    });
-
-	    holderTextGroup.moveTo(null, null, 1);
-	    sceneGraph.root.add(holderTextGroup);
-
-	    var tpdata = holderTextGroup.textPositionData = stagingRenderer(sceneGraph);
-	    if (!tpdata) {
-	        throw 'Holder: staging fallback not supported yet.';
-	    }
-	    holderTextGroup.properties.leading = tpdata.boundingBox.height;
-
-	    var textNode = null;
-	    var line = null;
-
-	    function finalizeLine(parent, line, width, height) {
-	        line.width = width;
-	        line.height = height;
-	        parent.width = Math.max(parent.width, line.width);
-	        parent.height += line.height;
-	    }
-
-	    if (tpdata.lineCount > 1) {
-	        var offsetX = 0;
-	        var offsetY = 0;
-	        var lineIndex = 0;
-	        var lineKey;
-	        line = new Shape.Group('line' + lineIndex);
-
-	        //Double margin so that left/right-aligned next is not flush with edge of image
-	        if (scene.align === 'left' || scene.align === 'right') {
-	            maxLineWidth = scene.width * (1 - (1 - lineWrap) * 2);
-	        }
-
-	        for (var i = 0; i < tpdata.words.length; i++) {
-	            var word = tpdata.words[i];
-	            textNode = new Shape.Text(word.text);
-	            var newline = word.text == '\\n';
-	            if (!scene.noWrap && (offsetX + word.width >= maxLineWidth || newline === true)) {
-	                finalizeLine(holderTextGroup, line, offsetX, holderTextGroup.properties.leading);
-	                holderTextGroup.add(line);
-	                offsetX = 0;
-	                offsetY += holderTextGroup.properties.leading;
-	                lineIndex += 1;
-	                line = new Shape.Group('line' + lineIndex);
-	                line.y = offsetY;
-	            }
-	            if (newline === true) {
-	                continue;
-	            }
-	            textNode.moveTo(offsetX, 0);
-	            offsetX += tpdata.spaceWidth + word.width;
-	            line.add(textNode);
-	        }
-
-	        finalizeLine(holderTextGroup, line, offsetX, holderTextGroup.properties.leading);
-	        holderTextGroup.add(line);
-
-	        if (scene.align === 'left') {
-	            holderTextGroup.moveTo(scene.width - sceneMargin, null, null);
-	        } else if (scene.align === 'right') {
-	            for (lineKey in holderTextGroup.children) {
-	                line = holderTextGroup.children[lineKey];
-	                line.moveTo(scene.width - line.width, null, null);
-	            }
-
-	            holderTextGroup.moveTo(0 - (scene.width - sceneMargin), null, null);
-	        } else {
-	            for (lineKey in holderTextGroup.children) {
-	                line = holderTextGroup.children[lineKey];
-	                line.moveTo((holderTextGroup.width - line.width) / 2, null, null);
-	            }
-
-	            holderTextGroup.moveTo((scene.width - holderTextGroup.width) / 2, null, null);
-	        }
-
-	        holderTextGroup.moveTo(null, (scene.height - holderTextGroup.height) / 2, null);
-
-	        //If the text exceeds vertical space, move it down so the first line is visible
-	        if ((scene.height - holderTextGroup.height) / 2 < 0) {
-	            holderTextGroup.moveTo(null, 0, null);
-	        }
-	    } else {
-	        textNode = new Shape.Text(scene.text);
-	        line = new Shape.Group('line0');
-	        line.add(textNode);
-	        holderTextGroup.add(line);
-
-	        if (scene.align === 'left') {
-	            holderTextGroup.moveTo(scene.width - sceneMargin, null, null);
-	        } else if (scene.align === 'right') {
-	            holderTextGroup.moveTo(0 - (scene.width - sceneMargin), null, null);
-	        } else {
-	            holderTextGroup.moveTo((scene.width - tpdata.boundingBox.width) / 2, null, null);
-	        }
-
-	        holderTextGroup.moveTo(null, (scene.height - tpdata.boundingBox.height) / 2, null);
-	    }
-
-	    //todo: renderlist
-	    return sceneGraph;
-	}
-
-	/**
-	 * Adaptive text sizing function
-	 *
-	 * @private
-	 * @param width Parent width
-	 * @param height Parent height
-	 * @param fontSize Requested text size
-	 * @param scale Proportional scale of text
-	 */
-	function textSize(width, height, fontSize, scale) {
-	    var stageWidth = parseInt(width, 10);
-	    var stageHeight = parseInt(height, 10);
-
-	    var bigSide = Math.max(stageWidth, stageHeight);
-	    var smallSide = Math.min(stageWidth, stageHeight);
-
-	    var newHeight = 0.8 * Math.min(smallSide, bigSide * scale);
-	    return Math.round(Math.max(fontSize, newHeight));
-	}
-
-	/**
-	 * Iterates over resizable (fluid or auto) placeholders and renders them
-	 *
-	 * @private
-	 * @param element Optional element selector, specified only if a specific element needs to be re-rendered
-	 */
-	function updateResizableElements(element) {
-	    var images;
-	    if (element == null || element.nodeType == null) {
-	        images = App.vars.resizableImages;
-	    } else {
-	        images = [element];
-	    }
-	    for (var i = 0, l = images.length; i < l; i++) {
-	        var el = images[i];
-	        if (el.holderData) {
-	            var flags = el.holderData.flags;
-	            var dimensions = dimensionCheck(el);
-	            if (dimensions) {
-	                if (!el.holderData.resizeUpdate) {
-	                    continue;
-	                }
-
-	                if (flags.fluid && flags.auto) {
-	                    var fluidConfig = el.holderData.fluidConfig;
-	                    switch (fluidConfig.mode) {
-	                        case 'width':
-	                            dimensions.height = dimensions.width / fluidConfig.ratio;
-	                            break;
-	                        case 'height':
-	                            dimensions.width = dimensions.height * fluidConfig.ratio;
-	                            break;
-	                    }
-	                }
-
-	                var settings = {
-	                    mode: 'image',
-	                    holderSettings: {
-	                        dimensions: dimensions,
-	                        theme: flags.theme,
-	                        flags: flags
-	                    },
-	                    el: el,
-	                    engineSettings: el.holderData.engineSettings
-	                };
-
-	                if (flags.textmode == 'exact') {
-	                    flags.exactDimensions = dimensions;
-	                    settings.holderSettings.dimensions = flags.dimensions;
-	                }
-
-	                render(settings);
-	            } else {
-	                setInvisible(el);
-	            }
-	        }
-	    }
-	}
-
-	/**
-	 * Sets up aspect ratio metadata for fluid placeholders, in order to preserve proportions when resizing
-	 *
-	 * @private
-	 * @param el Image DOM element
-	 */
-	function setInitialDimensions(el) {
-	    if (el.holderData) {
-	        var dimensions = dimensionCheck(el);
-	        if (dimensions) {
-	            var flags = el.holderData.flags;
-
-	            var fluidConfig = {
-	                fluidHeight: flags.dimensions.height.slice(-1) == '%',
-	                fluidWidth: flags.dimensions.width.slice(-1) == '%',
-	                mode: null,
-	                initialDimensions: dimensions
-	            };
-
-	            if (fluidConfig.fluidWidth && !fluidConfig.fluidHeight) {
-	                fluidConfig.mode = 'width';
-	                fluidConfig.ratio = fluidConfig.initialDimensions.width / parseFloat(flags.dimensions.height);
-	            } else if (!fluidConfig.fluidWidth && fluidConfig.fluidHeight) {
-	                fluidConfig.mode = 'height';
-	                fluidConfig.ratio = parseFloat(flags.dimensions.width) / fluidConfig.initialDimensions.height;
-	            }
-
-	            el.holderData.fluidConfig = fluidConfig;
-	        } else {
-	            setInvisible(el);
-	        }
-	    }
-	}
-
-	/**
-	 * Iterates through all current invisible images, and if they're visible, renders them and removes them from further checks. Runs every animation frame.
-	 *
-	 * @private
-	 */
-	function visibilityCheck() {
-	    var renderableImages = [];
-	    var keys = Object.keys(App.vars.invisibleImages);
-	    var el;
-
-	    keys.forEach(function (key) {
-	        el = App.vars.invisibleImages[key];
-	        if (dimensionCheck(el) && el.nodeName.toLowerCase() == 'img') {
-	            renderableImages.push(el);
-	            delete App.vars.invisibleImages[key];
-	        }
-	    });
-
-	    if (renderableImages.length) {
-	        Holder.run({
-	            images: renderableImages
-	        });
-	    }
-
-	    // Done to prevent 100% CPU usage via aggressive calling of requestAnimationFrame
-	    setTimeout(function () {
-	        global.requestAnimationFrame(visibilityCheck);
-	    }, 10);
-	}
-
-	/**
-	 * Starts checking for invisible placeholders if not doing so yet. Does nothing otherwise.
-	 *
-	 * @private
-	 */
-	function startVisibilityCheck() {
-	    if (!App.vars.visibilityCheckStarted) {
-	        global.requestAnimationFrame(visibilityCheck);
-	        App.vars.visibilityCheckStarted = true;
-	    }
-	}
-
-	/**
-	 * Sets a unique ID for an image detected to be invisible and adds it to the map of invisible images checked by visibilityCheck
-	 *
-	 * @private
-	 * @param el Invisible DOM element
-	 */
-	function setInvisible(el) {
-	    if (!el.holderData.invisibleId) {
-	        App.vars.invisibleId += 1;
-	        App.vars.invisibleImages['i' + App.vars.invisibleId] = el;
-	        el.holderData.invisibleId = App.vars.invisibleId;
-	    }
-	}
-
-	//todo: see if possible to convert stagingRenderer to use HTML only
-	var stagingRenderer = (function() {
-	    var svg = null,
-	        stagingText = null,
-	        stagingTextNode = null;
-	    return function(graph) {
-	        var rootNode = graph.root;
-	        if (App.setup.supportsSVG) {
-	            var firstTimeSetup = false;
-	            var tnode = function(text) {
-	                return document.createTextNode(text);
-	            };
-	            if (svg == null || svg.parentNode !== document.body) {
-	                firstTimeSetup = true;
-	            }
-
-	            svg = SVG.initSVG(svg, rootNode.properties.width, rootNode.properties.height);
-	            //Show staging element before staging
-	            svg.style.display = 'block';
-
-	            if (firstTimeSetup) {
-	                stagingText = DOM.newEl('text', SVG_NS);
-	                stagingTextNode = tnode(null);
-	                DOM.setAttr(stagingText, {
-	                    x: 0
-	                });
-	                stagingText.appendChild(stagingTextNode);
-	                svg.appendChild(stagingText);
-	                document.body.appendChild(svg);
-	                svg.style.visibility = 'hidden';
-	                svg.style.position = 'absolute';
-	                svg.style.top = '-100%';
-	                svg.style.left = '-100%';
-	                //todo: workaround for zero-dimension <svg> tag in Opera 12
-	                //svg.setAttribute('width', 0);
-	                //svg.setAttribute('height', 0);
-	            }
-
-	            var holderTextGroup = rootNode.children.holderTextGroup;
-	            var htgProps = holderTextGroup.properties;
-	            DOM.setAttr(stagingText, {
-	                'y': htgProps.font.size,
-	                'style': utils.cssProps({
-	                    'font-weight': htgProps.font.weight,
-	                    'font-size': htgProps.font.size + htgProps.font.units,
-	                    'font-family': htgProps.font.family
-	                })
-	            });
-
-	            //Get bounding box for the whole string (total width and height)
-	            stagingTextNode.nodeValue = htgProps.text;
-	            var stagingTextBBox = stagingText.getBBox();
-
-	            //Get line count and split the string into words
-	            var lineCount = Math.ceil(stagingTextBBox.width / rootNode.properties.width);
-	            var words = htgProps.text.split(' ');
-	            var newlines = htgProps.text.match(/\\n/g);
-	            lineCount += newlines == null ? 0 : newlines.length;
-
-	            //Get bounding box for the string with spaces removed
-	            stagingTextNode.nodeValue = htgProps.text.replace(/[ ]+/g, '');
-	            var computedNoSpaceLength = stagingText.getComputedTextLength();
-
-	            //Compute average space width
-	            var diffLength = stagingTextBBox.width - computedNoSpaceLength;
-	            var spaceWidth = Math.round(diffLength / Math.max(1, words.length - 1));
-
-	            //Get widths for every word with space only if there is more than one line
-	            var wordWidths = [];
-	            if (lineCount > 1) {
-	                stagingTextNode.nodeValue = '';
-	                for (var i = 0; i < words.length; i++) {
-	                    if (words[i].length === 0) continue;
-	                    stagingTextNode.nodeValue = utils.decodeHtmlEntity(words[i]);
-	                    var bbox = stagingText.getBBox();
-	                    wordWidths.push({
-	                        text: words[i],
-	                        width: bbox.width
-	                    });
-	                }
-	            }
-
-	            //Hide staging element after staging
-	            svg.style.display = 'none';
-
-	            return {
-	                spaceWidth: spaceWidth,
-	                lineCount: lineCount,
-	                boundingBox: stagingTextBBox,
-	                words: wordWidths
-	            };
-	        } else {
-	            //todo: canvas fallback for measuring text on android 2.3
-	            return false;
-	        }
-	    };
-	})();
-
-	//Helpers
-
-	/**
-	 * Prevents a function from being called too often, waits until a timer elapses to call it again
-	 *
-	 * @param fn Function to call
-	 */
-	function debounce(fn) {
-	    if (!App.vars.debounceTimer) fn.call(this);
-	    if (App.vars.debounceTimer) global.clearTimeout(App.vars.debounceTimer);
-	    App.vars.debounceTimer = global.setTimeout(function() {
-	        App.vars.debounceTimer = null;
-	        fn.call(this);
-	    }, App.setup.debounce);
-	}
-
-	/**
-	 * Holder-specific resize/orientation change callback, debounced to prevent excessive execution
-	 */
-	function resizeEvent() {
-	    debounce(function() {
-	        updateResizableElements(null);
-	    });
-	}
-
-	//Set up flags
-
-	for (var flag in App.flags) {
-	    if (!App.flags.hasOwnProperty(flag)) continue;
-	    App.flags[flag].match = function(val) {
-	        return val.match(this.regex);
-	    };
-	}
-
-	//Properties set once on setup
-
-	App.setup = {
-	    renderer: 'html',
-	    debounce: 100,
-	    ratio: 1,
-	    supportsCanvas: false,
-	    supportsSVG: false,
-	    lineWrapRatio: 0.9,
-	    dataAttr: 'data-src',
-	    renderers: ['html', 'canvas', 'svg']
-	};
-
-	//Properties modified during runtime
-
-	App.vars = {
-	    preempted: false,
-	    resizableImages: [],
-	    invisibleImages: {},
-	    invisibleId: 0,
-	    visibilityCheckStarted: false,
-	    debounceTimer: null,
-	    cache: {}
-	};
-
-	//Pre-flight
-
-	(function() {
-	    var canvas = DOM.newEl('canvas');
-
-	    if (canvas.getContext) {
-	        if (canvas.toDataURL('image/png').indexOf('data:image/png') != -1) {
-	            App.setup.renderer = 'canvas';
-	            App.setup.supportsCanvas = true;
-	        }
-	    }
-
-	    if (!!document.createElementNS && !!document.createElementNS(SVG_NS, 'svg').createSVGRect) {
-	        App.setup.renderer = 'svg';
-	        App.setup.supportsSVG = true;
-	    }
-	})();
-
-	//Starts checking for invisible placeholders
-	startVisibilityCheck();
-
-	if (onDomReady) {
-	    onDomReady(function() {
-	        if (!App.vars.preempted) {
-	            Holder.run();
-	        }
-	        if (global.addEventListener) {
-	            global.addEventListener('resize', resizeEvent, false);
-	            global.addEventListener('orientationchange', resizeEvent, false);
-	        } else {
-	            global.attachEvent('onresize', resizeEvent);
-	        }
-
-	        if (typeof global.Turbolinks == 'object') {
-	            global.document.addEventListener('page:change', function() {
-	                Holder.run();
-	            });
-	        }
-	    });
-	}
-
-	module.exports = Holder;
-
-	/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
-
-/***/ },
-/* 2 */
-/***/ function(module, exports) {
-
-	/*!
-	 * onDomReady.js 1.4.0 (c) 2013 Tubal Martin - MIT license
-	 *
-	 * Specially modified to work with Holder.js
-	 */
-
-	function _onDomReady(win) {
-	    //Lazy loading fix for Firefox < 3.6
-	    //http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
-	    if (document.readyState == null && document.addEventListener) {
-	        document.addEventListener("DOMContentLoaded", function DOMContentLoaded() {
-	            document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
-	            document.readyState = "complete";
-	        }, false);
-	        document.readyState = "loading";
-	    }
-	    
-	    var doc = win.document,
-	        docElem = doc.documentElement,
-	    
-	        LOAD = "load",
-	        FALSE = false,
-	        ONLOAD = "on"+LOAD,
-	        COMPLETE = "complete",
-	        READYSTATE = "readyState",
-	        ATTACHEVENT = "attachEvent",
-	        DETACHEVENT = "detachEvent",
-	        ADDEVENTLISTENER = "addEventListener",
-	        DOMCONTENTLOADED = "DOMContentLoaded",
-	        ONREADYSTATECHANGE = "onreadystatechange",
-	        REMOVEEVENTLISTENER = "removeEventListener",
-	    
-	        // W3C Event model
-	        w3c = ADDEVENTLISTENER in doc,
-	        _top = FALSE,
-	    
-	        // isReady: Is the DOM ready to be used? Set to true once it occurs.
-	        isReady = FALSE,
-	    
-	        // Callbacks pending execution until DOM is ready
-	        callbacks = [];
-	    
-	    // Handle when the DOM is ready
-	    function ready( fn ) {
-	        if ( !isReady ) {
-	    
-	            // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
-	            if ( !doc.body ) {
-	                return defer( ready );
-	            }
-	    
-	            // Remember that the DOM is ready
-	            isReady = true;
-	    
-	            // Execute all callbacks
-	            while ( fn = callbacks.shift() ) {
-	                defer( fn );
-	            }
-	        }
-	    }
-	    
-	    // The ready event handler
-	    function completed( event ) {
-	        // readyState === "complete" is good enough for us to call the dom ready in oldIE
-	        if ( w3c || event.type === LOAD || doc[READYSTATE] === COMPLETE ) {
-	            detach();
-	            ready();
-	        }
-	    }
-	    
-	    // Clean-up method for dom ready events
-	    function detach() {
-	        if ( w3c ) {
-	            doc[REMOVEEVENTLISTENER]( DOMCONTENTLOADED, completed, FALSE );
-	            win[REMOVEEVENTLISTENER]( LOAD, completed, FALSE );
-	        } else {
-	            doc[DETACHEVENT]( ONREADYSTATECHANGE, completed );
-	            win[DETACHEVENT]( ONLOAD, completed );
-	        }
-	    }
-	    
-	    // Defers a function, scheduling it to run after the current call stack has cleared.
-	    function defer( fn, wait ) {
-	        // Allow 0 to be passed
-	        setTimeout( fn, +wait >= 0 ? wait : 1 );
-	    }
-	    
-	    // Attach the listeners:
-	    
-	    // Catch cases where onDomReady is called after the browser event has already occurred.
-	    // we once tried to use readyState "interactive" here, but it caused issues like the one
-	    // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
-	    if ( doc[READYSTATE] === COMPLETE ) {
-	        // Handle it asynchronously to allow scripts the opportunity to delay ready
-	        defer( ready );
-	    
-	    // Standards-based browsers support DOMContentLoaded
-	    } else if ( w3c ) {
-	        // Use the handy event callback
-	        doc[ADDEVENTLISTENER]( DOMCONTENTLOADED, completed, FALSE );
-	    
-	        // A fallback to window.onload, that will always work
-	        win[ADDEVENTLISTENER]( LOAD, completed, FALSE );
-	    
-	    // If IE event model is used
-	    } else {
-	        // Ensure firing before onload, maybe late but safe also for iframes
-	        doc[ATTACHEVENT]( ONREADYSTATECHANGE, completed );
-	    
-	        // A fallback to window.onload, that will always work
-	        win[ATTACHEVENT]( ONLOAD, completed );
-	    
-	        // If IE and not a frame
-	        // continually check to see if the document is ready
-	        try {
-	            _top = win.frameElement == null && docElem;
-	        } catch(e) {}
-	    
-	        if ( _top && _top.doScroll ) {
-	            (function doScrollCheck() {
-	                if ( !isReady ) {
-	                    try {
-	                        // Use the trick by Diego Perini
-	                        // http://javascript.nwbox.com/IEContentLoaded/
-	                        _top.doScroll("left");
-	                    } catch(e) {
-	                        return defer( doScrollCheck, 50 );
-	                    }
-	    
-	                    // detach all dom ready events
-	                    detach();
-	    
-	                    // and execute any waiting functions
-	                    ready();
-	                }
-	            })();
-	        }
-	    }
-	    
-	    function onDomReady( fn ) {
-	        // If DOM is ready, execute the function (async), otherwise wait
-	        isReady ? defer( fn ) : callbacks.push( fn );
-	    }
-	    
-	    // Add version
-	    onDomReady.version = "1.4.0";
-	    // Add method to check if DOM is ready
-	    onDomReady.isReady = function(){
-	        return isReady;
-	    };
-
-	    return onDomReady;
-	}
-
-	module.exports = typeof window !== "undefined" && _onDomReady(window);
-
-/***/ },
-/* 3 */
-/***/ function(module, exports, __webpack_require__) {
-
-	//Modified version of component/querystring
-	//Changes: updated dependencies, dot notation parsing, JSHint fixes
-	//Fork at https://github.com/imsky/querystring
-
-	/**
-	 * Module dependencies.
-	 */
-
-	var encode = encodeURIComponent;
-	var decode = decodeURIComponent;
-	var trim = __webpack_require__(4);
-	var type = __webpack_require__(5);
-
-	var arrayRegex = /(\w+)\[(\d+)\]/;
-	var objectRegex = /\w+\.\w+/;
-
-	/**
-	 * Parse the given query `str`.
-	 *
-	 * @param {String} str
-	 * @return {Object}
-	 * @api public
-	 */
-
-	exports.parse = function(str){
-	  if ('string' !== typeof str) return {};
-
-	  str = trim(str);
-	  if ('' === str) return {};
-	  if ('?' === str.charAt(0)) str = str.slice(1);
-
-	  var obj = {};
-	  var pairs = str.split('&');
-	  for (var i = 0; i < pairs.length; i++) {
-	    var parts = pairs[i].split('=');
-	    var key = decode(parts[0]);
-	    var m, ctx, prop;
-
-	    if (m = arrayRegex.exec(key)) {
-	      obj[m[1]] = obj[m[1]] || [];
-	      obj[m[1]][m[2]] = decode(parts[1]);
-	      continue;
-	    }
-
-	    if (m = objectRegex.test(key)) {
-	      m = key.split('.');
-	      ctx = obj;
-	      
-	      while (m.length) {
-	        prop = m.shift();
-
-	        if (!prop.length) continue;
-
-	        if (!ctx[prop]) {
-	          ctx[prop] = {};
-	        } else if (ctx[prop] && typeof ctx[prop] !== 'object') {
-	          break;
-	        }
-
-	        if (!m.length) {
-	          ctx[prop] = decode(parts[1]);
-	        }
-
-	        ctx = ctx[prop];
-	      }
-
-	      continue;
-	    }
-
-	    obj[parts[0]] = null == parts[1] ? '' : decode(parts[1]);
-	  }
-
-	  return obj;
-	};
-
-	/**
-	 * Stringify the given `obj`.
-	 *
-	 * @param {Object} obj
-	 * @return {String}
-	 * @api public
-	 */
-
-	exports.stringify = function(obj){
-	  if (!obj) return '';
-	  var pairs = [];
-
-	  for (var key in obj) {
-	    var value = obj[key];
-
-	    if ('array' == type(value)) {
-	      for (var i = 0; i < value.length; ++i) {
-	        pairs.push(encode(key + '[' + i + ']') + '=' + encode(value[i]));
-	      }
-	      continue;
-	    }
-
-	    pairs.push(encode(key) + '=' + encode(obj[key]));
-	  }
-
-	  return pairs.join('&');
-	};
-
-
-/***/ },
-/* 4 */
-/***/ function(module, exports) {
-
-	
-	exports = module.exports = trim;
-
-	function trim(str){
-	  return str.replace(/^\s*|\s*$/g, '');
-	}
-
-	exports.left = function(str){
-	  return str.replace(/^\s*/, '');
-	};
-
-	exports.right = function(str){
-	  return str.replace(/\s*$/, '');
-	};
-
-
-/***/ },
-/* 5 */
-/***/ function(module, exports) {
-
-	/**
-	 * toString ref.
-	 */
-
-	var toString = Object.prototype.toString;
-
-	/**
-	 * Return the type of `val`.
-	 *
-	 * @param {Mixed} val
-	 * @return {String}
-	 * @api public
-	 */
-
-	module.exports = function(val){
-	  switch (toString.call(val)) {
-	    case '[object Date]': return 'date';
-	    case '[object RegExp]': return 'regexp';
-	    case '[object Arguments]': return 'arguments';
-	    case '[object Array]': return 'array';
-	    case '[object Error]': return 'error';
-	  }
-
-	  if (val === null) return 'null';
-	  if (val === undefined) return 'undefined';
-	  if (val !== val) return 'nan';
-	  if (val && val.nodeType === 1) return 'element';
-
-	  val = val.valueOf
-	    ? val.valueOf()
-	    : Object.prototype.valueOf.apply(val)
-
-	  return typeof val;
-	};
-
-
-/***/ },
-/* 6 */
-/***/ function(module, exports) {
-
-	var SceneGraph = function(sceneProperties) {
-	    var nodeCount = 1;
-
-	    //todo: move merge to helpers section
-	    function merge(parent, child) {
-	        for (var prop in child) {
-	            parent[prop] = child[prop];
-	        }
-	        return parent;
-	    }
-
-	    var SceneNode = function(name) {
-	        nodeCount++;
-	        this.parent = null;
-	        this.children = {};
-	        this.id = nodeCount;
-	        this.name = 'n' + nodeCount;
-	        if (typeof name !== 'undefined') {
-	            this.name = name;
-	        }
-	        this.x = this.y = this.z = 0;
-	        this.width = this.height = 0;
-	    };
-
-	    SceneNode.prototype.resize = function(width, height) {
-	        if (width != null) {
-	            this.width = width;
-	        }
-	        if (height != null) {
-	            this.height = height;
-	        }
-	    };
-
-	    SceneNode.prototype.moveTo = function(x, y, z) {
-	        this.x = x != null ? x : this.x;
-	        this.y = y != null ? y : this.y;
-	        this.z = z != null ? z : this.z;
-	    };
-
-	    SceneNode.prototype.add = function(child) {
-	        var name = child.name;
-	        if (typeof this.children[name] === 'undefined') {
-	            this.children[name] = child;
-	            child.parent = this;
-	        } else {
-	            throw 'SceneGraph: child already exists: ' + name;
-	        }
-	    };
-
-	    var RootNode = function() {
-	        SceneNode.call(this, 'root');
-	        this.properties = sceneProperties;
-	    };
-
-	    RootNode.prototype = new SceneNode();
-
-	    var Shape = function(name, props) {
-	        SceneNode.call(this, name);
-	        this.properties = {
-	            'fill': '#000000'
-	        };
-	        if (typeof props !== 'undefined') {
-	            merge(this.properties, props);
-	        } else if (typeof name !== 'undefined' && typeof name !== 'string') {
-	            throw 'SceneGraph: invalid node name';
-	        }
-	    };
-
-	    Shape.prototype = new SceneNode();
-
-	    var Group = function() {
-	        Shape.apply(this, arguments);
-	        this.type = 'group';
-	    };
-
-	    Group.prototype = new Shape();
-
-	    var Rect = function() {
-	        Shape.apply(this, arguments);
-	        this.type = 'rect';
-	    };
-
-	    Rect.prototype = new Shape();
-
-	    var Text = function(text) {
-	        Shape.call(this);
-	        this.type = 'text';
-	        this.properties.text = text;
-	    };
-
-	    Text.prototype = new Shape();
-
-	    var root = new RootNode();
-
-	    this.Shape = {
-	        'Rect': Rect,
-	        'Text': Text,
-	        'Group': Group
-	    };
-
-	    this.root = root;
-	    return this;
-	};
-
-	module.exports = SceneGraph;
-
-
-/***/ },
-/* 7 */
-/***/ function(module, exports) {
-
-	/* WEBPACK VAR INJECTION */(function(global) {/**
-	 * Shallow object clone and merge
-	 *
-	 * @param a Object A
-	 * @param b Object B
-	 * @returns {Object} New object with all of A's properties, and all of B's properties, overwriting A's properties
-	 */
-	exports.extend = function(a, b) {
-	    var c = {};
-	    for (var x in a) {
-	        if (a.hasOwnProperty(x)) {
-	            c[x] = a[x];
-	        }
-	    }
-	    if (b != null) {
-	        for (var y in b) {
-	            if (b.hasOwnProperty(y)) {
-	                c[y] = b[y];
-	            }
-	        }
-	    }
-	    return c;
-	};
-
-	/**
-	 * Takes a k/v list of CSS properties and returns a rule
-	 *
-	 * @param props CSS properties object
-	 */
-	exports.cssProps = function(props) {
-	    var ret = [];
-	    for (var p in props) {
-	        if (props.hasOwnProperty(p)) {
-	            ret.push(p + ':' + props[p]);
-	        }
-	    }
-	    return ret.join(';');
-	};
-
-	/**
-	 * Encodes HTML entities in a string
-	 *
-	 * @param str Input string
-	 */
-	exports.encodeHtmlEntity = function(str) {
-	    var buf = [];
-	    var charCode = 0;
-	    for (var i = str.length - 1; i >= 0; i--) {
-	        charCode = str.charCodeAt(i);
-	        if (charCode > 128) {
-	            buf.unshift(['&#', charCode, ';'].join(''));
-	        } else {
-	            buf.unshift(str[i]);
-	        }
-	    }
-	    return buf.join('');
-	};
-
-	/**
-	 * Checks if an image exists
-	 *
-	 * @param src URL of image
-	 * @param callback Callback to call once image status has been found
-	 */
-	exports.imageExists = function(src, callback) {
-	    var image = new Image();
-	    image.onerror = function() {
-	        callback.call(this, false);
-	    };
-	    image.onload = function() {
-	        callback.call(this, true);
-	    };
-	    image.src = src;
-	};
-
-	/**
-	 * Decodes HTML entities in a string
-	 *
-	 * @param str Input string
-	 */
-	exports.decodeHtmlEntity = function(str) {
-	    return str.replace(/&#(\d+);/g, function(match, dec) {
-	        return String.fromCharCode(dec);
-	    });
-	};
-
-
-	/**
-	 * Returns an element's dimensions if it's visible, `false` otherwise.
-	 *
-	 * @param el DOM element
-	 */
-	exports.dimensionCheck = function(el) {
-	    var dimensions = {
-	        height: el.clientHeight,
-	        width: el.clientWidth
-	    };
-
-	    if (dimensions.height && dimensions.width) {
-	        return dimensions;
-	    } else {
-	        return false;
-	    }
-	};
-
-
-	/**
-	 * Returns true if value is truthy or if it is "semantically truthy"
-	 * @param val
-	 */
-	exports.truthy = function(val) {
-	    if (typeof val === 'string') {
-	        return val === 'true' || val === 'yes' || val === '1' || val === 'on' || val === '✓';
-	    }
-	    return !!val;
-	};
-
-	/**
-	 * Parses input into a well-formed CSS color
-	 * @param val
-	 */
-	exports.parseColor = function(val) {
-	    var hexre = /(^(?:#?)[0-9a-f]{6}$)|(^(?:#?)[0-9a-f]{3}$)/i;
-	    var rgbre = /^rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/;
-	    var rgbare = /^rgba\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(0\.\d{1,}|1)\)$/;
-
-	    var match = val.match(hexre);
-	    var retval;
-
-	    if (match !== null) {
-	        retval = match[1] || match[2];
-	        if (retval[0] !== '#') {
-	            return '#' + retval;
-	        } else {
-	            return retval;
-	        }
-	    }
-
-	    match = val.match(rgbre);
-
-	    if (match !== null) {
-	        retval = 'rgb(' + match.slice(1).join(',') + ')';
-	        return retval;
-	    }
-
-	    match = val.match(rgbare);
-
-	    if (match !== null) {
-	        retval = 'rgba(' + match.slice(1).join(',') + ')';
-	        return retval;
-	    }
-
-	    return null;
-	};
-
-	/**
-	 * Provides the correct scaling ratio for canvas drawing operations on HiDPI screens (e.g. Retina displays)
-	 */
-	exports.canvasRatio = function () {
-	    var devicePixelRatio = 1;
-	    var backingStoreRatio = 1;
-
-	    if (global.document) {
-	        var canvas = global.document.createElement('canvas');
-	        if (canvas.getContext) {
-	            var ctx = canvas.getContext('2d');
-	            devicePixelRatio = global.devicePixelRatio || 1;
-	            backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
-	        }
-	    }
-
-	    return devicePixelRatio / backingStoreRatio;
-	};
-	/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
-
-/***/ },
-/* 8 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* WEBPACK VAR INJECTION */(function(global) {var DOM = __webpack_require__(9);
-
-	var SVG_NS = 'http://www.w3.org/2000/svg';
-	var NODE_TYPE_COMMENT = 8;
-
-	/**
-	 * Generic SVG element creation function
-	 *
-	 * @param svg SVG context, set to null if new
-	 * @param width Document width
-	 * @param height Document height
-	 */
-	exports.initSVG = function(svg, width, height) {
-	    var defs, style, initialize = false;
-
-	    if (svg && svg.querySelector) {
-	        style = svg.querySelector('style');
-	        if (style === null) {
-	            initialize = true;
-	        }
-	    } else {
-	        svg = DOM.newEl('svg', SVG_NS);
-	        initialize = true;
-	    }
-
-	    if (initialize) {
-	        defs = DOM.newEl('defs', SVG_NS);
-	        style = DOM.newEl('style', SVG_NS);
-	        DOM.setAttr(style, {
-	            'type': 'text/css'
-	        });
-	        defs.appendChild(style);
-	        svg.appendChild(defs);
-	    }
-
-	    //IE throws an exception if this is set and Chrome requires it to be set
-	    if (svg.webkitMatchesSelector) {
-	        svg.setAttribute('xmlns', SVG_NS);
-	    }
-
-	    //Remove comment nodes
-	    for (var i = 0; i < svg.childNodes.length; i++) {
-	        if (svg.childNodes[i].nodeType === NODE_TYPE_COMMENT) {
-	            svg.removeChild(svg.childNodes[i]);
-	        }
-	    }
-
-	    //Remove CSS
-	    while (style.childNodes.length) {
-	        style.removeChild(style.childNodes[0]);
-	    }
-
-	    DOM.setAttr(svg, {
-	        'width': width,
-	        'height': height,
-	        'viewBox': '0 0 ' + width + ' ' + height,
-	        'preserveAspectRatio': 'none'
-	    });
-
-	    return svg;
-	};
-
-	/**
-	 * Converts serialized SVG to a string suitable for data URI use
-	 * @param svgString Serialized SVG string
-	 * @param [base64] Use base64 encoding for data URI
-	 */
-	exports.svgStringToDataURI = function() {
-	    var rawPrefix = 'data:image/svg+xml;charset=UTF-8,';
-	    var base64Prefix = 'data:image/svg+xml;charset=UTF-8;base64,';
-
-	    return function(svgString, base64) {
-	        if (base64) {
-	            return base64Prefix + btoa(global.unescape(encodeURIComponent(svgString)));
-	        } else {
-	            return rawPrefix + encodeURIComponent(svgString);
-	        }
-	    };
-	}();
-
-	/**
-	 * Returns serialized SVG with XML processing instructions
-	 *
-	 * @param svg SVG context
-	 * @param stylesheets CSS stylesheets to include
-	 */
-	exports.serializeSVG = function(svg, engineSettings) {
-	    if (!global.XMLSerializer) return;
-	    var serializer = new XMLSerializer();
-	    var svgCSS = '';
-	    var stylesheets = engineSettings.stylesheets;
-
-	    //External stylesheets: Processing Instruction method
-	    if (engineSettings.svgXMLStylesheet) {
-	        var xml = DOM.createXML();
-	        //Add <?xml-stylesheet ?> directives
-	        for (var i = stylesheets.length - 1; i >= 0; i--) {
-	            var csspi = xml.createProcessingInstruction('xml-stylesheet', 'href="' + stylesheets[i] + '" rel="stylesheet"');
-	            xml.insertBefore(csspi, xml.firstChild);
-	        }
-
-	        xml.removeChild(xml.documentElement);
-	        svgCSS = serializer.serializeToString(xml);
-	    }
-
-	    var svgText = serializer.serializeToString(svg);
-	    svgText = svgText.replace(/\&amp;(\#[0-9]{2,}\;)/g, '&$1');
-	    return svgCSS + svgText;
-	};
-
-	/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
-
-/***/ },
-/* 9 */
-/***/ function(module, exports) {
-
-	/* WEBPACK VAR INJECTION */(function(global) {/**
-	 * Generic new DOM element function
-	 *
-	 * @param tag Tag to create
-	 * @param namespace Optional namespace value
-	 */
-	exports.newEl = function(tag, namespace) {
-	    if (!global.document) return;
-
-	    if (namespace == null) {
-	        return global.document.createElement(tag);
-	    } else {
-	        return global.document.createElementNS(namespace, tag);
-	    }
-	};
-
-	/**
-	 * Generic setAttribute function
-	 *
-	 * @param el Reference to DOM element
-	 * @param attrs Object with attribute keys and values
-	 */
-	exports.setAttr = function (el, attrs) {
-	    for (var a in attrs) {
-	        el.setAttribute(a, attrs[a]);
-	    }
-	};
-
-	/**
-	 * Creates a XML document
-	 * @private
-	 */
-	exports.createXML = function() {
-	    if (!global.DOMParser) return;
-	    return new DOMParser().parseFromString('<xml />', 'application/xml');
-	};
-
-	/**
-	 * Converts a value into an array of DOM nodes
-	 *
-	 * @param val A string, a NodeList, a Node, or an HTMLCollection
-	 */
-	exports.getNodeArray = function(val) {
-	    var retval = null;
-	    if (typeof(val) == 'string') {
-	        retval = document.querySelectorAll(val);
-	    } else if (global.NodeList && val instanceof global.NodeList) {
-	        retval = val;
-	    } else if (global.Node && val instanceof global.Node) {
-	        retval = [val];
-	    } else if (global.HTMLCollection && val instanceof global.HTMLCollection) {
-	        retval = val;
-	    } else if (val instanceof Array) {
-	        retval = val;
-	    } else if (val === null) {
-	        retval = [];
-	    }
-
-	    retval = Array.prototype.slice.call(retval);
-
-	    return retval;
-	};
-
-	/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
-
-/***/ },
-/* 10 */
-/***/ function(module, exports) {
-
-	var Color = function(color, options) {
-	    //todo: support rgba, hsla, and rrggbbaa notation
-	    //todo: use CIELAB internally
-	    //todo: add clamp function (with sign)
-	    if (typeof color !== 'string') return;
-
-	    this.original = color;
-
-	    if (color.charAt(0) === '#') {
-	        color = color.slice(1);
-	    }
-
-	    if (/[^a-f0-9]+/i.test(color)) return;
-
-	    if (color.length === 3) {
-	        color = color.replace(/./g, '$&$&');
-	    }
-
-	    if (color.length !== 6) return;
-
-	    this.alpha = 1;
-
-	    if (options && options.alpha) {
-	        this.alpha = options.alpha;
-	    }
-
-	    this.set(parseInt(color, 16));
-	};
-
-	//todo: jsdocs
-	Color.rgb2hex = function(r, g, b) {
-	    function format (decimal) {
-	        var hex = (decimal | 0).toString(16);
-	        if (decimal < 16) {
-	            hex = '0' + hex;
-	        }
-	        return hex;
-	    }
-
-	    return [r, g, b].map(format).join('');
-	};
-
-	//todo: jsdocs
-	Color.hsl2rgb = function (h, s, l) {
-	    var H = h / 60;
-	    var C = (1 - Math.abs(2 * l - 1)) * s;
-	    var X = C * (1 - Math.abs(parseInt(H) % 2 - 1));
-	    var m = l - (C / 2);
-
-	    var r = 0, g = 0, b = 0;
-
-	    if (H >= 0 && H < 1) {
-	        r = C;
-	        g = X;
-	    } else if (H >= 1 && H < 2) {
-	        r = X;
-	        g = C;
-	    } else if (H >= 2 && H < 3) {
-	        g = C;
-	        b = X;
-	    } else if (H >= 3 && H < 4) {
-	        g = X;
-	        b = C;
-	    } else if (H >= 4 && H < 5) {
-	        r = X;
-	        b = C;
-	    } else if (H >= 5 && H < 6) {
-	        r = C;
-	        b = X;
-	    }
-
-	    r += m;
-	    g += m;
-	    b += m;
-
-	    r = parseInt(r * 255);
-	    g = parseInt(g * 255);
-	    b = parseInt(b * 255);
-
-	    return [r, g, b];
-	};
-
-	/**
-	 * Sets the color from a raw RGB888 integer
-	 * @param raw RGB888 representation of color
-	 */
-	//todo: refactor into a static method
-	//todo: factor out individual color spaces
-	//todo: add HSL, CIELAB, and CIELUV
-	Color.prototype.set = function (val) {
-	    this.raw = val;
-
-	    var r = (this.raw & 0xFF0000) >> 16;
-	    var g = (this.raw & 0x00FF00) >> 8;
-	    var b = (this.raw & 0x0000FF);
-
-	    // BT.709
-	    var y = 0.2126 * r + 0.7152 * g + 0.0722 * b;
-	    var u = -0.09991 * r - 0.33609 * g + 0.436 * b;
-	    var v = 0.615 * r - 0.55861 * g - 0.05639 * b;
-
-	    this.rgb = {
-	        r: r,
-	        g: g,
-	        b: b
-	    };
-
-	    this.yuv = {
-	        y: y,
-	        u: u,
-	        v: v
-	    };
-
-	    return this;
-	};
-
-	/**
-	 * Lighten or darken a color
-	 * @param multiplier Amount to lighten or darken (-1 to 1)
-	 */
-	Color.prototype.lighten = function(multiplier) {
-	    var cm = Math.min(1, Math.max(0, Math.abs(multiplier))) * (multiplier < 0 ? -1 : 1);
-	    var bm = (255 * cm) | 0;
-	    var cr = Math.min(255, Math.max(0, this.rgb.r + bm));
-	    var cg = Math.min(255, Math.max(0, this.rgb.g + bm));
-	    var cb = Math.min(255, Math.max(0, this.rgb.b + bm));
-	    var hex = Color.rgb2hex(cr, cg, cb);
-	    return new Color(hex);
-	};
-
-	/**
-	 * Output color in hex format
-	 * @param addHash Add a hash character to the beginning of the output
-	 */
-	Color.prototype.toHex = function(addHash) {
-	    return (addHash ? '#' : '') + this.raw.toString(16);
-	};
-
-	/**
-	 * Returns whether or not current color is lighter than another color
-	 * @param color Color to compare against
-	 */
-	Color.prototype.lighterThan = function(color) {
-	    if (!(color instanceof Color)) {
-	        color = new Color(color);
-	    }
-
-	    return this.yuv.y > color.yuv.y;
-	};
-
-	/**
-	 * Returns the result of mixing current color with another color
-	 * @param color Color to mix with
-	 * @param multiplier How much to mix with the other color
-	 */
-	/*
-	Color.prototype.mix = function (color, multiplier) {
-	    if (!(color instanceof Color)) {
-	        color = new Color(color);
-	    }
-
-	    var r = this.rgb.r;
-	    var g = this.rgb.g;
-	    var b = this.rgb.b;
-	    var a = this.alpha;
-
-	    var m = typeof multiplier !== 'undefined' ? multiplier : 0.5;
-
-	    //todo: write a lerp function
-	    r = r + m * (color.rgb.r - r);
-	    g = g + m * (color.rgb.g - g);
-	    b = b + m * (color.rgb.b - b);
-	    a = a + m * (color.alpha - a);
-
-	    return new Color(Color.rgbToHex(r, g, b), {
-	        'alpha': a
-	    });
-	};
-	*/
-
-	/**
-	 * Returns the result of blending another color on top of current color with alpha
-	 * @param color Color to blend on top of current color, i.e. "Ca"
-	 */
-	//todo: see if .blendAlpha can be merged into .mix
-	Color.prototype.blendAlpha = function(color) {
-	    if (!(color instanceof Color)) {
-	        color = new Color(color);
-	    }
-
-	    var Ca = color;
-	    var Cb = this;
-
-	    //todo: write alpha blending function
-	    var r = Ca.alpha * Ca.rgb.r + (1 - Ca.alpha) * Cb.rgb.r;
-	    var g = Ca.alpha * Ca.rgb.g + (1 - Ca.alpha) * Cb.rgb.g;
-	    var b = Ca.alpha * Ca.rgb.b + (1 - Ca.alpha) * Cb.rgb.b;
-
-	    return new Color(Color.rgb2hex(r, g, b));
-	};
-
-	module.exports = Color;
-
-
-/***/ },
-/* 11 */
-/***/ function(module, exports) {
-
-	module.exports = {
-	  'version': '2.9.0',
-	  'svg_ns': 'http://www.w3.org/2000/svg'
-	};
-
-/***/ },
-/* 12 */
-/***/ function(module, exports, __webpack_require__) {
-
-	var shaven = __webpack_require__(13);
-
-	var SVG = __webpack_require__(8);
-	var constants = __webpack_require__(11);
-	var utils = __webpack_require__(7);
-
-	var SVG_NS = constants.svg_ns;
-
-	var templates = {
-	  'element': function (options) {
-	    var tag = options.tag;
-	    var content = options.content || '';
-	    delete options.tag;
-	    delete options.content;
-	    return  [tag, content, options];
-	  }
-	};
-
-	//todo: deprecate tag arg, infer tag from shape object
-	function convertShape (shape, tag) {
-	  return templates.element({
-	    'tag': tag,
-	    'width': shape.width,
-	    'height': shape.height,
-	    'fill': shape.properties.fill
-	  });
-	}
-
-	function textCss (properties) {
-	  return utils.cssProps({
-	    'fill': properties.fill,
-	    'font-weight': properties.font.weight,
-	    'font-family': properties.font.family + ', monospace',
-	    'font-size': properties.font.size + properties.font.units
-	  });
-	}
-
-	function outlinePath (bgWidth, bgHeight, outlineWidth) {
-	  var outlineOffsetWidth = outlineWidth / 2;
-
-	  return [
-	    'M', outlineOffsetWidth, outlineOffsetWidth,
-	    'H', bgWidth - outlineOffsetWidth,
-	    'V', bgHeight - outlineOffsetWidth,
-	    'H', outlineOffsetWidth,
-	    'V', 0,
-	    'M', 0, outlineOffsetWidth,
-	    'L', bgWidth, bgHeight - outlineOffsetWidth,
-	    'M', 0, bgHeight - outlineOffsetWidth,
-	    'L', bgWidth, outlineOffsetWidth
-	  ].join(' ');
-	}
-
-	module.exports = function (sceneGraph, renderSettings) {
-	  var engineSettings = renderSettings.engineSettings;
-	  var stylesheets = engineSettings.stylesheets;
-	  var stylesheetXml = stylesheets.map(function (stylesheet) {
-	    return '<?xml-stylesheet rel="stylesheet" href="' + stylesheet + '"?>';
-	  }).join('\n');
-
-	  var holderId = 'holder_' + Number(new Date()).toString(16);
-
-	  var root = sceneGraph.root;
-	  var textGroup = root.children.holderTextGroup;
-
-	  var css = '#' + holderId + ' text { ' + textCss(textGroup.properties) + ' } ';
-
-	  // push text down to be equally vertically aligned with canvas renderer
-	  textGroup.y += textGroup.textPositionData.boundingBox.height * 0.8;
-
-	  var wordTags = [];
-
-	  Object.keys(textGroup.children).forEach(function (lineKey) {
-	    var line = textGroup.children[lineKey];
-
-	    Object.keys(line.children).forEach(function (wordKey) {
-	      var word = line.children[wordKey];
-	      var x = textGroup.x + line.x + word.x;
-	      var y = textGroup.y + line.y + word.y;
-
-	      var wordTag = templates.element({
-	        'tag': 'text',
-	        'content': word.properties.text,
-	        'x': x,
-	        'y': y
-	      });
-
-	      wordTags.push(wordTag);
-	    });
-	  });
-
-	  var text = templates.element({
-	    'tag': 'g',
-	    'content': wordTags
-	  });
-
-	  var outline = null;
-
-	  if (root.children.holderBg.properties.outline) {
-	    var outlineProperties = root.children.holderBg.properties.outline;
-	    outline = templates.element({
-	      'tag': 'path',
-	      'd': outlinePath(root.children.holderBg.width, root.children.holderBg.height, outlineProperties.width),
-	      'stroke-width': outlineProperties.width,
-	      'stroke': outlineProperties.fill,
-	      'fill': 'none'
-	    });
-	  }
-
-	  var bg = convertShape(root.children.holderBg, 'rect');
-
-	  var sceneContent = [];
-
-	  sceneContent.push(bg);
-	  if (outlineProperties) {
-	    sceneContent.push(outline);
-	  }
-	  sceneContent.push(text);
-
-	  var scene = templates.element({
-	    'tag': 'g',
-	    'id': holderId,
-	    'content': sceneContent
-	  });
-
-	  var style = templates.element({
-	    'tag': 'style',
-	    //todo: figure out how to add CDATA directive
-	    'content': css,
-	    'type': 'text/css'
-	  });
-
-	  var defs = templates.element({
-	    'tag': 'defs',
-	    'content': style
-	  });
-
-	  var svg = templates.element({
-	    'tag': 'svg',
-	    'content': [defs, scene],
-	    'width': root.properties.width,
-	    'height': root.properties.height,
-	    'xmlns': SVG_NS,
-	    'viewBox': [0, 0, root.properties.width, root.properties.height].join(' '),
-	    'preserveAspectRatio': 'none'
-	  });
-
-	  var output = shaven(svg);
-	  
-	  output = stylesheetXml + output[0];
-
-	  var svgString = SVG.svgStringToDataURI(output, renderSettings.mode === 'background');
-	  return svgString;
-	};
-
-/***/ },
-/* 13 */
-/***/ function(module, exports, __webpack_require__) {
-
-	var escape = __webpack_require__(14)
-
-	// TODO: remove namespace
-
-	module.exports = function shaven (array, namespace, returnObject) {
-
-		'use strict'
-
-		var i = 1,
-			doesEscape = true,
-			HTMLString,
-			attributeKey,
-			callback,
-			key
-
-
-		returnObject = returnObject || {}
-
-
-		function createElement (sugarString) {
-
-			var tags = sugarString.match(/^\w+/),
-				element = {
-					tag: tags ? tags[0] : 'div',
-					attr: {},
-					children: []
-				},
-				id = sugarString.match(/#([\w-]+)/),
-				reference = sugarString.match(/\$([\w-]+)/),
-				classNames = sugarString.match(/\.[\w-]+/g)
-
-
-			// Assign id if is set
-			if (id) {
-				element.attr.id = id[1]
-
-				// Add element to the return object
-				returnObject[id[1]] = element
-			}
-
-			if (reference)
-				returnObject[reference[1]] = element
-
-			if (classNames)
-				element.attr.class = classNames.join(' ').replace(/\./g, '')
-
-			if (sugarString.match(/&$/g))
-				doesEscape = false
-
-			return element
-		}
-
-		function replacer (key, value) {
-
-			if (value === null || value === false || value === undefined)
-				return
-
-			if (typeof value !== 'string' && typeof value !== 'object')
-				return String(value)
-
-			return value
-		}
-
-		function escapeAttribute (string) {
-			return String(string)
-				.replace(/&/g, '&amp;')
-				.replace(/"/g, '&quot;')
-		}
-
-		function escapeHTML (string) {
-			return String(string)
-				.replace(/&/g, '&amp;')
-				.replace(/"/g, '&quot;')
-				.replace(/'/g, '&apos;')
-				.replace(/</g, '&lt;')
-				.replace(/>/g, '&gt;')
-		}
-
-
-		if (typeof array[0] === 'string')
-			array[0] = createElement(array[0])
-
-		else if (Array.isArray(array[0]))
-			i = 0
-
-		else
-			throw new Error(
-				'First element of array must be a string, ' +
-				'or an array and not ' + JSON.stringify(array[0])
-			)
-
-
-		for (; i < array.length; i++) {
-
-			// Don't render element if value is false or null
-			if (array[i] === false || array[i] === null) {
-				array[0] = false
-				break
-			}
-
-			// Continue with next array value if current value is undefined or true
-			else if (array[i] === undefined || array[i] === true) {
-				continue
-			}
-
-			else if (typeof array[i] === 'string') {
-				if (doesEscape)
-					array[i] = escapeHTML(array[i])
-
-				array[0].children.push(array[i])
-			}
-
-			else if (typeof array[i] === 'number') {
-
-				array[0].children.push(array[i])
-			}
-
-			else if (Array.isArray(array[i])) {
-
-				if (Array.isArray(array[i][0])) {
-					array[i].reverse().forEach(function (subArray) {
-						array.splice(i + 1, 0, subArray)
-					})
-
-					if (i !== 0)
-						continue
-					i++
-				}
-
-				shaven(array[i], namespace, returnObject)
-
-				if (array[i][0])
-					array[0].children.push(array[i][0])
-			}
-
-			else if (typeof array[i] === 'function')
-				callback = array[i]
-
-
-			else if (typeof array[i] === 'object') {
-				for (attributeKey in array[i])
-					if (array[i].hasOwnProperty(attributeKey))
-						if (array[i][attributeKey] !== null &&
-							array[i][attributeKey] !== false)
-							if (attributeKey === 'style' &&
-								typeof array[i][attributeKey] === 'object')
-								array[0].attr[attributeKey] = JSON
-									.stringify(array[i][attributeKey], replacer)
-									.slice(2, -2)
-									.replace(/","/g, ';')
-									.replace(/":"/g, ':')
-									.replace(/\\"/g, '\'')
-
-							else
-								array[0].attr[attributeKey] = array[i][attributeKey]
-			}
-
-			else
-				throw new TypeError('"' + array[i] + '" is not allowed as a value.')
-		}
-
-
-		if (array[0] !== false) {
-
-			HTMLString = '<' + array[0].tag
-
-			for (key in array[0].attr)
-				if (array[0].attr.hasOwnProperty(key))
-					HTMLString += ' ' + key + '="' +
-						escapeAttribute(array[0].attr[key] || '') + '"'
-
-			HTMLString += '>'
-
-			array[0].children.forEach(function (child) {
-				HTMLString += child
-			})
-
-			HTMLString += '</' + array[0].tag + '>'
-
-			array[0] = HTMLString
-		}
-
-		// Return root element on index 0
-		returnObject[0] = array[0]
-
-		if (callback)
-			callback(array[0])
-
-		// returns object containing all elements with an id and the root element
-		return returnObject
-	}
-
-
-/***/ },
-/* 14 */
-/***/ function(module, exports) {
-
-	/*!
-	 * escape-html
-	 * Copyright(c) 2012-2013 TJ Holowaychuk
-	 * MIT Licensed
-	 */
-
-	/**
-	 * Module exports.
-	 * @public
-	 */
-
-	module.exports = escapeHtml;
-
-	/**
-	 * Escape special characters in the given string of html.
-	 *
-	 * @param  {string} str The string to escape for inserting into HTML
-	 * @return {string}
-	 * @public
-	 */
-
-	function escapeHtml(html) {
-	  return String(html)
-	    .replace(/&/g, '&amp;')
-	    .replace(/"/g, '&quot;')
-	    .replace(/'/g, '&#39;')
-	    .replace(/</g, '&lt;')
-	    .replace(/>/g, '&gt;');
-	}
-
-
-/***/ },
-/* 15 */
-/***/ function(module, exports, __webpack_require__) {
-
-	var DOM = __webpack_require__(9);
-	var utils = __webpack_require__(7);
-
-	module.exports = (function() {
-	    var canvas = DOM.newEl('canvas');
-	    var ctx = null;
-
-	    return function(sceneGraph) {
-	        if (ctx == null) {
-	            ctx = canvas.getContext('2d');
-	        }
-
-	        var dpr = utils.canvasRatio();
-	        var root = sceneGraph.root;
-	        canvas.width = dpr * root.properties.width;
-	        canvas.height = dpr * root.properties.height ;
-	        ctx.textBaseline = 'middle';
-
-	        var bg = root.children.holderBg;
-	        var bgWidth = dpr * bg.width;
-	        var bgHeight = dpr * bg.height;
-	        //todo: parametrize outline width (e.g. in scene object)
-	        var outlineWidth = 2;
-	        var outlineOffsetWidth = outlineWidth / 2;
-
-	        ctx.fillStyle = bg.properties.fill;
-	        ctx.fillRect(0, 0, bgWidth, bgHeight);
-
-	        if (bg.properties.outline) {
-	            //todo: abstract this into a method
-	            ctx.strokeStyle = bg.properties.outline.fill;
-	            ctx.lineWidth = bg.properties.outline.width;
-	            ctx.moveTo(outlineOffsetWidth, outlineOffsetWidth);
-	            // TL, TR, BR, BL
-	            ctx.lineTo(bgWidth - outlineOffsetWidth, outlineOffsetWidth);
-	            ctx.lineTo(bgWidth - outlineOffsetWidth, bgHeight - outlineOffsetWidth);
-	            ctx.lineTo(outlineOffsetWidth, bgHeight - outlineOffsetWidth);
-	            ctx.lineTo(outlineOffsetWidth, outlineOffsetWidth);
-	            // Diagonals
-	            ctx.moveTo(0, outlineOffsetWidth);
-	            ctx.lineTo(bgWidth, bgHeight - outlineOffsetWidth);
-	            ctx.moveTo(0, bgHeight - outlineOffsetWidth);
-	            ctx.lineTo(bgWidth, outlineOffsetWidth);
-	            ctx.stroke();
-	        }
-
-	        var textGroup = root.children.holderTextGroup;
-	        ctx.font = textGroup.properties.font.weight + ' ' + (dpr * textGroup.properties.font.size) + textGroup.properties.font.units + ' ' + textGroup.properties.font.family + ', monospace';
-	        ctx.fillStyle = textGroup.properties.fill;
-
-	        for (var lineKey in textGroup.children) {
-	            var line = textGroup.children[lineKey];
-	            for (var wordKey in line.children) {
-	                var word = line.children[wordKey];
-	                var x = dpr * (textGroup.x + line.x + word.x);
-	                var y = dpr * (textGroup.y + line.y + word.y + (textGroup.properties.leading / 2));
-
-	                ctx.fillText(word.properties.text, x, y);
-	            }
-	        }
-
-	        return canvas.toDataURL('image/png');
-	    };
-	})();
-
-/***/ }
-/******/ ])
-});
-;
-(function(ctx, isMeteorPackage) {
-    if (isMeteorPackage) {
-        Holder = ctx.Holder;
-    }
-})(this, typeof Meteor !== 'undefined' && typeof Package !== 'undefined');

+ 0 - 1
footer.php

@@ -13,7 +13,6 @@
     <?php wp_footer(); ?>
 
     <!-- jQuery first, then Bootstrap JS. -->
-    <script src="http://holderjs.com/holder.js"></script>
     <script type="text/javascript">
       jQuery( document ).ready(function($) {
         $('#nav').Stickyfill(); // NAV

+ 13 - 0
functions.php

@@ -756,4 +756,17 @@ function ams_modify_titles($title, $id = null) {
 }
 add_filter( 'wp_title', 'ams_modify_titles', 10, 2 );
 
+
+function my_async_scripts( $tag, $handle, $src ) {
+    // the handles of the enqueued scripts we want to async
+    $async_scripts = array( 'jquery', 'bootstrap', 'stickyfill', 'slick', 'wp-embed');
+
+    if ( in_array( $handle, $async_scripts ) ) {
+        return '<script type="text/javascript" src="' . $src . '" async></script>' . "\n";
+    }
+
+    return $tag;
+}
+add_filter( 'script_loader_tag', 'my_async_scripts', 10, 3 );
+
 ?>