mirror of
https://github.com/imsky/holder.git
synced 2026-01-09 22:08:03 -05:00
4612 lines
135 KiB
JavaScript
4612 lines
135 KiB
JavaScript
/*!
|
|
|
|
Holder - client side image placeholders
|
|
Version 2.9.9+jl7z
|
|
© 2021 Ivan Malopinsky - https://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 && window.webkitCancelAnimationFrame) {
|
|
//https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/requestAnimationFrame/polyfill-webkit.js
|
|
(function (global) {
|
|
global.requestAnimationFrame = function (callback) {
|
|
return webkitRequestAnimationFrame(function () {
|
|
callback(global.performance.now());
|
|
});
|
|
};
|
|
|
|
global.cancelAnimationFrame = global.webkitCancelAnimationFrame;
|
|
}(window));
|
|
} else if (window.mozRequestAnimationFrame && window.mozCancelAnimationFrame) {
|
|
//https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/requestAnimationFrame/polyfill-moz.js
|
|
(function (global) {
|
|
global.requestAnimationFrame = function (callback) {
|
|
return mozRequestAnimationFrame(function () {
|
|
callback(global.performance.now());
|
|
});
|
|
};
|
|
|
|
global.cancelAnimationFrame = global.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-2020 Ivan Malopinsky - https://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-2020 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__(27);
|
|
|
|
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;
|
|
engineSettings.noBackgroundSize = !!options.noBackgroundSize;
|
|
|
|
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) {
|
|
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) {
|
|
objectAttr.error = 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) {
|
|
imageAttr.error = 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 firstQuestionMark = url.indexOf('?');
|
|
var parts = [url];
|
|
|
|
if (firstQuestionMark !== -1) {
|
|
parts = [url.slice(0, firstQuestionMark), url.slice(firstQuestionMark + 1)];
|
|
}
|
|
|
|
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]);
|
|
|
|
// Dimensions
|
|
|
|
if (utils.truthy(options.ratio)) {
|
|
holder.fluid = true;
|
|
var ratioWidth = parseFloat(holder.dimensions.width.replace('%', ''));
|
|
var ratioHeight = parseFloat(holder.dimensions.height.replace('%', ''));
|
|
|
|
ratioHeight = Math.floor(100 * (ratioHeight / ratioWidth));
|
|
ratioWidth = 100;
|
|
|
|
holder.dimensions.width = ratioWidth + '%';
|
|
holder.dimensions.height = ratioHeight + '%';
|
|
}
|
|
|
|
holder.auto = utils.truthy(options.auto);
|
|
|
|
// 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 && Object.prototype.hasOwnProperty.call(holder.instanceOptions.themes, 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 && parseFloat(options.size)) {
|
|
holder.size = parseFloat(options.size);
|
|
}
|
|
|
|
if (options.fixedSize != null) {
|
|
holder.fixedSize = utils.truthy(options.fixedSize);
|
|
}
|
|
|
|
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.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.bg;
|
|
} 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.bg;
|
|
} 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 + ')';
|
|
|
|
if (!engineSettings.noBackgroundSize) {
|
|
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;
|
|
var fixedSize = scene.flags.fixedSize != null ? scene.flags.fixedSize : scene.theme.fixedSize;
|
|
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: fixedSize ? fontSize : 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
|
|
})
|
|
});
|
|
|
|
//Unescape HTML entities to get approximately the right width
|
|
var txt = DOM.newEl('textarea');
|
|
txt.innerHTML = htgProps.text;
|
|
stagingTextNode.nodeValue = txt.value;
|
|
|
|
//Get bounding box for the whole string (total width and height)
|
|
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 (!Object.prototype.hasOwnProperty.call(App.flags, 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 (Object.prototype.hasOwnProperty.call(a,x)) {
|
|
c[x] = a[x];
|
|
}
|
|
}
|
|
if (b != null) {
|
|
for (var y in b) {
|
|
if (Object.prototype.hasOwnProperty.call(b, 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 (Object.prototype.hasOwnProperty.call(props, 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) {
|
|
var normalizeAlpha = function (a) { return '0.' + a.split('.')[1]; };
|
|
var fixedMatch = match.slice(1).map(function (e, i) {
|
|
return (i === 3) ? normalizeAlpha(e) : e;
|
|
});
|
|
retval = 'rgba(' + fixedMatch.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(/&(#[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.9',
|
|
'svg_ns': 'http://www.w3.org/2000/svg'
|
|
};
|
|
|
|
/***/ }),
|
|
/* 12 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
var shaven = __webpack_require__(13).default;
|
|
|
|
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 = String(shaven(svg));
|
|
|
|
if (/&(x)?#[0-9A-Fa-f]/.test(output[0])) {
|
|
output = output.replace(/&#/gm, '&#');
|
|
}
|
|
|
|
output = stylesheetXml + output;
|
|
|
|
var svgString = SVG.svgStringToDataURI(output, renderSettings.mode === 'background');
|
|
|
|
return svgString;
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
/* 13 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
// vendored shaven 1.3.0 due to published package.json including an outdated node engine
|
|
module.exports = __webpack_require__(14);
|
|
|
|
|
|
/***/ }),
|
|
/* 14 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
|
|
exports.default = shaven;
|
|
|
|
var _parseSugarString = __webpack_require__(15);
|
|
|
|
var _parseSugarString2 = _interopRequireDefault(_parseSugarString);
|
|
|
|
var _escape = __webpack_require__(16);
|
|
|
|
var escape = _interopRequireWildcard(_escape);
|
|
|
|
var _defaults = __webpack_require__(17);
|
|
|
|
var _defaults2 = _interopRequireDefault(_defaults);
|
|
|
|
var _mapAttributeValue = __webpack_require__(18);
|
|
|
|
var _mapAttributeValue2 = _interopRequireDefault(_mapAttributeValue);
|
|
|
|
var _assert = __webpack_require__(21);
|
|
|
|
var _assert2 = _interopRequireDefault(_assert);
|
|
|
|
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function shaven(arrayOrObject) {
|
|
var isArray = Array.isArray(arrayOrObject);
|
|
var objType = typeof arrayOrObject === 'undefined' ? 'undefined' : _typeof(arrayOrObject);
|
|
|
|
if (!isArray && objType !== 'object') {
|
|
throw new Error('Argument must be either an array or an object ' + 'and not ' + JSON.stringify(arrayOrObject));
|
|
}
|
|
|
|
if (isArray && arrayOrObject.length === 0) {
|
|
// Ignore empty arrays
|
|
return {};
|
|
}
|
|
|
|
var config = {};
|
|
var elementArray = [];
|
|
|
|
if (Array.isArray(arrayOrObject)) {
|
|
elementArray = arrayOrObject.slice(0);
|
|
} else {
|
|
elementArray = arrayOrObject.elementArray.slice(0);
|
|
config = Object.assign(config, arrayOrObject);
|
|
delete config.elementArray;
|
|
}
|
|
|
|
config = Object.assign({}, _defaults2.default, config, {
|
|
returnObject: { // Shaven object to return at last
|
|
ids: {},
|
|
references: {}
|
|
}
|
|
});
|
|
|
|
function createElement(sugarString) {
|
|
var properties = (0, _parseSugarString2.default)(sugarString);
|
|
var element = {
|
|
tag: properties.tag,
|
|
attr: {},
|
|
children: []
|
|
};
|
|
|
|
if (properties.id) {
|
|
element.attr.id = properties.id;
|
|
(0, _assert2.default)(!config.returnObject.ids.hasOwnProperty(properties.id), 'Ids must be unique and "' + properties.id + '" is already assigned');
|
|
config.returnObject.ids[properties.id] = element;
|
|
}
|
|
if (properties.class) {
|
|
element.attr.class = properties.class;
|
|
}
|
|
if (properties.reference) {
|
|
(0, _assert2.default)(!config.returnObject.ids.hasOwnProperty(properties.reference), 'References must be unique and "' + properties.id + '" is already assigned');
|
|
config.returnObject.references[properties.reference] = element;
|
|
}
|
|
|
|
config.escapeHTML = properties.escapeHTML != null ? properties.escapeHTML : config.escapeHTML;
|
|
|
|
return element;
|
|
}
|
|
|
|
function buildDom(elemArray) {
|
|
if (Array.isArray(elemArray) && elemArray.length === 0) {
|
|
// Ignore empty arrays
|
|
return {};
|
|
}
|
|
|
|
var index = 1;
|
|
var createdCallback = void 0;
|
|
var selfClosingHTMLTags = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'];
|
|
// Clone to avoid mutation problems
|
|
var array = elemArray.slice(0);
|
|
|
|
if (typeof array[0] === 'string') {
|
|
array[0] = createElement(array[0]);
|
|
} else if (Array.isArray(array[0])) {
|
|
index = 0;
|
|
} else {
|
|
throw new Error('First element of array must be a string, ' + 'or an array and not ' + JSON.stringify(array[0]));
|
|
}
|
|
|
|
for (; index < array.length; index++) {
|
|
|
|
// Don't render element if value is false or null
|
|
if (array[index] === false || array[index] === null) {
|
|
array[0] = false;
|
|
break;
|
|
}
|
|
|
|
// Continue with next array value if current value is undefined or true
|
|
else if (array[index] === undefined || array[index] === true) {
|
|
continue;
|
|
} else if (typeof array[index] === 'string') {
|
|
if (config.escapeHTML) {
|
|
// eslint-disable-next-line new-cap
|
|
array[index] = escape.HTML(array[index]);
|
|
}
|
|
|
|
array[0].children.push(array[index]);
|
|
} else if (typeof array[index] === 'number') {
|
|
|
|
array[0].children.push(array[index]);
|
|
} else if (Array.isArray(array[index])) {
|
|
|
|
if (Array.isArray(array[index][0])) {
|
|
array[index].reverse().forEach(function (subArray) {
|
|
// eslint-disable-line no-loop-func
|
|
array.splice(index + 1, 0, subArray);
|
|
});
|
|
|
|
if (index !== 0) continue;
|
|
index++;
|
|
}
|
|
|
|
array[index] = buildDom(array[index]);
|
|
|
|
if (array[index][0]) {
|
|
array[0].children.push(array[index][0]);
|
|
}
|
|
} else if (typeof array[index] === 'function') {
|
|
createdCallback = array[index];
|
|
} else if (_typeof(array[index]) === 'object') {
|
|
for (var attributeKey in array[index]) {
|
|
if (!array[index].hasOwnProperty(attributeKey)) continue;
|
|
|
|
var attributeValue = array[index][attributeKey];
|
|
|
|
if (array[index].hasOwnProperty(attributeKey) && attributeValue !== null && attributeValue !== false) {
|
|
array[0].attr[attributeKey] = (0, _mapAttributeValue2.default)(attributeKey, attributeValue);
|
|
}
|
|
}
|
|
} else {
|
|
throw new TypeError('"' + array[index] + '" is not allowed as a value');
|
|
}
|
|
}
|
|
|
|
if (array[0] !== false) {
|
|
var HTMLString = '<' + array[0].tag;
|
|
|
|
for (var key in array[0].attr) {
|
|
if (array[0].attr.hasOwnProperty(key)) {
|
|
var _attributeValue = escape.attribute(array[0].attr[key]);
|
|
var value = _attributeValue;
|
|
|
|
if (config.quoteAttributes || /[ "'=<>]/.test(_attributeValue)) {
|
|
value = config.quotationMark + _attributeValue + config.quotationMark;
|
|
}
|
|
|
|
HTMLString += ' ' + key + '=' + value;
|
|
}
|
|
}
|
|
|
|
HTMLString += '>';
|
|
|
|
if (!(selfClosingHTMLTags.indexOf(array[0].tag) !== -1)) {
|
|
array[0].children.forEach(function (child) {
|
|
return HTMLString += child;
|
|
});
|
|
|
|
HTMLString += '</' + array[0].tag + '>';
|
|
}
|
|
|
|
array[0] = HTMLString;
|
|
}
|
|
|
|
// Return root element on index 0
|
|
config.returnObject[0] = array[0];
|
|
config.returnObject.rootElement = array[0];
|
|
|
|
config.returnObject.toString = function () {
|
|
return array[0];
|
|
};
|
|
|
|
if (createdCallback) createdCallback(array[0]);
|
|
|
|
return config.returnObject;
|
|
}
|
|
|
|
return buildDom(elementArray);
|
|
}
|
|
|
|
shaven.setDefaults = function (object) {
|
|
Object.assign(_defaults2.default, object);
|
|
return shaven;
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
/* 15 */
|
|
/***/ (function(module, exports) {
|
|
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
exports.default = function (sugarString) {
|
|
var tags = sugarString.match(/^[\w-]+/);
|
|
var properties = {
|
|
tag: tags ? tags[0] : 'div'
|
|
};
|
|
var ids = sugarString.match(/#([\w-]+)/);
|
|
var classes = sugarString.match(/\.[\w-]+/g);
|
|
var references = sugarString.match(/\$([\w-]+)/);
|
|
|
|
if (ids) properties.id = ids[1];
|
|
|
|
if (classes) {
|
|
properties.class = classes.join(' ').replace(/\./g, '');
|
|
}
|
|
|
|
if (references) properties.reference = references[1];
|
|
|
|
if (sugarString.endsWith('&') || sugarString.endsWith('!')) {
|
|
properties.escapeHTML = false;
|
|
}
|
|
|
|
return properties;
|
|
};
|
|
|
|
/***/ }),
|
|
/* 16 */
|
|
/***/ (function(module, exports) {
|
|
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.attribute = attribute;
|
|
exports.HTML = HTML;
|
|
function attribute(string) {
|
|
return string || string === 0 ? String(string).replace(/&/g, '&').replace(/"/g, '"') : '';
|
|
}
|
|
|
|
function HTML(string) {
|
|
return String(string).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>');
|
|
}
|
|
|
|
/***/ }),
|
|
/* 17 */
|
|
/***/ (function(module, exports) {
|
|
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.default = {
|
|
namespace: 'xhtml',
|
|
autoNamespacing: true,
|
|
escapeHTML: true,
|
|
quotationMark: '"',
|
|
quoteAttributes: true,
|
|
convertTransformArray: true
|
|
};
|
|
|
|
/***/ }),
|
|
/* 18 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
|
|
var _buildTransformString = __webpack_require__(19);
|
|
|
|
var _buildTransformString2 = _interopRequireDefault(_buildTransformString);
|
|
|
|
var _stringifyStyleObject = __webpack_require__(20);
|
|
|
|
var _stringifyStyleObject2 = _interopRequireDefault(_stringifyStyleObject);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
exports.default = function (key, value) {
|
|
if (value === undefined) {
|
|
return '';
|
|
}
|
|
|
|
if (key === 'style' && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') {
|
|
return (0, _stringifyStyleObject2.default)(value);
|
|
}
|
|
|
|
if (key === 'transform' && Array.isArray(value)) {
|
|
return (0, _buildTransformString2.default)(value);
|
|
}
|
|
|
|
return value;
|
|
};
|
|
|
|
/***/ }),
|
|
/* 19 */
|
|
/***/ (function(module, exports) {
|
|
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
// Create transform string from list transform objects
|
|
|
|
exports.default = function (transformObjects) {
|
|
|
|
return transformObjects.map(function (transformation) {
|
|
var values = [];
|
|
|
|
if (transformation.type === 'rotate' && transformation.degrees) {
|
|
values.push(transformation.degrees);
|
|
}
|
|
if (transformation.x) values.push(transformation.x);
|
|
if (transformation.y) values.push(transformation.y);
|
|
|
|
return transformation.type + '(' + values + ')';
|
|
}).join(' ');
|
|
};
|
|
|
|
/***/ }),
|
|
/* 20 */
|
|
/***/ (function(module, exports) {
|
|
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
|
|
function sanitizeProperties(key, value) {
|
|
if (value === null || value === false || value === undefined) return;
|
|
if (typeof value === 'string' || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') return value;
|
|
|
|
return String(value);
|
|
}
|
|
|
|
exports.default = function (styleObject) {
|
|
return JSON.stringify(styleObject, sanitizeProperties).slice(2, -2).replace(/","/g, ';').replace(/":"/g, ':').replace(/\\"/g, '\'');
|
|
};
|
|
|
|
/***/ }),
|
|
/* 21 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
/* WEBPACK VAR INJECTION */(function(global) {'use strict';
|
|
|
|
var objectAssign = __webpack_require__(22);
|
|
|
|
// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js
|
|
// original notice:
|
|
|
|
/*!
|
|
* The buffer module from node.js, for the browser.
|
|
*
|
|
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
|
* @license MIT
|
|
*/
|
|
function compare(a, b) {
|
|
if (a === b) {
|
|
return 0;
|
|
}
|
|
|
|
var x = a.length;
|
|
var y = b.length;
|
|
|
|
for (var i = 0, len = Math.min(x, y); i < len; ++i) {
|
|
if (a[i] !== b[i]) {
|
|
x = a[i];
|
|
y = b[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (x < y) {
|
|
return -1;
|
|
}
|
|
if (y < x) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
function isBuffer(b) {
|
|
if (global.Buffer && typeof global.Buffer.isBuffer === 'function') {
|
|
return global.Buffer.isBuffer(b);
|
|
}
|
|
return !!(b != null && b._isBuffer);
|
|
}
|
|
|
|
// based on node assert, original notice:
|
|
// NB: The URL to the CommonJS spec is kept just for tradition.
|
|
// node-assert has evolved a lot since then, both in API and behavior.
|
|
|
|
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
|
|
//
|
|
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
|
|
//
|
|
// Originally from narwhal.js (http://narwhaljs.org)
|
|
// Copyright (c) 2009 Thomas Robinson <280north.com>
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the 'Software'), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
var util = __webpack_require__(23);
|
|
var hasOwn = Object.prototype.hasOwnProperty;
|
|
var pSlice = Array.prototype.slice;
|
|
var functionsHaveNames = (function () {
|
|
return function foo() {}.name === 'foo';
|
|
}());
|
|
function pToString (obj) {
|
|
return Object.prototype.toString.call(obj);
|
|
}
|
|
function isView(arrbuf) {
|
|
if (isBuffer(arrbuf)) {
|
|
return false;
|
|
}
|
|
if (typeof global.ArrayBuffer !== 'function') {
|
|
return false;
|
|
}
|
|
if (typeof ArrayBuffer.isView === 'function') {
|
|
return ArrayBuffer.isView(arrbuf);
|
|
}
|
|
if (!arrbuf) {
|
|
return false;
|
|
}
|
|
if (arrbuf instanceof DataView) {
|
|
return true;
|
|
}
|
|
if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
// 1. The assert module provides functions that throw
|
|
// AssertionError's when particular conditions are not met. The
|
|
// assert module must conform to the following interface.
|
|
|
|
var assert = module.exports = ok;
|
|
|
|
// 2. The AssertionError is defined in assert.
|
|
// new assert.AssertionError({ message: message,
|
|
// actual: actual,
|
|
// expected: expected })
|
|
|
|
var regex = /\s*function\s+([^\(\s]*)\s*/;
|
|
// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js
|
|
function getName(func) {
|
|
if (!util.isFunction(func)) {
|
|
return;
|
|
}
|
|
if (functionsHaveNames) {
|
|
return func.name;
|
|
}
|
|
var str = func.toString();
|
|
var match = str.match(regex);
|
|
return match && match[1];
|
|
}
|
|
assert.AssertionError = function AssertionError(options) {
|
|
this.name = 'AssertionError';
|
|
this.actual = options.actual;
|
|
this.expected = options.expected;
|
|
this.operator = options.operator;
|
|
if (options.message) {
|
|
this.message = options.message;
|
|
this.generatedMessage = false;
|
|
} else {
|
|
this.message = getMessage(this);
|
|
this.generatedMessage = true;
|
|
}
|
|
var stackStartFunction = options.stackStartFunction || fail;
|
|
if (Error.captureStackTrace) {
|
|
Error.captureStackTrace(this, stackStartFunction);
|
|
} else {
|
|
// non v8 browsers so we can have a stacktrace
|
|
var err = new Error();
|
|
if (err.stack) {
|
|
var out = err.stack;
|
|
|
|
// try to strip useless frames
|
|
var fn_name = getName(stackStartFunction);
|
|
var idx = out.indexOf('\n' + fn_name);
|
|
if (idx >= 0) {
|
|
// once we have located the function frame
|
|
// we need to strip out everything before it (and its line)
|
|
var next_line = out.indexOf('\n', idx + 1);
|
|
out = out.substring(next_line + 1);
|
|
}
|
|
|
|
this.stack = out;
|
|
}
|
|
}
|
|
};
|
|
|
|
// assert.AssertionError instanceof Error
|
|
util.inherits(assert.AssertionError, Error);
|
|
|
|
function truncate(s, n) {
|
|
if (typeof s === 'string') {
|
|
return s.length < n ? s : s.slice(0, n);
|
|
} else {
|
|
return s;
|
|
}
|
|
}
|
|
function inspect(something) {
|
|
if (functionsHaveNames || !util.isFunction(something)) {
|
|
return util.inspect(something);
|
|
}
|
|
var rawname = getName(something);
|
|
var name = rawname ? ': ' + rawname : '';
|
|
return '[Function' + name + ']';
|
|
}
|
|
function getMessage(self) {
|
|
return truncate(inspect(self.actual), 128) + ' ' +
|
|
self.operator + ' ' +
|
|
truncate(inspect(self.expected), 128);
|
|
}
|
|
|
|
// At present only the three keys mentioned above are used and
|
|
// understood by the spec. Implementations or sub modules can pass
|
|
// other keys to the AssertionError's constructor - they will be
|
|
// ignored.
|
|
|
|
// 3. All of the following functions must throw an AssertionError
|
|
// when a corresponding condition is not met, with a message that
|
|
// may be undefined if not provided. All assertion methods provide
|
|
// both the actual and expected values to the assertion error for
|
|
// display purposes.
|
|
|
|
function fail(actual, expected, message, operator, stackStartFunction) {
|
|
throw new assert.AssertionError({
|
|
message: message,
|
|
actual: actual,
|
|
expected: expected,
|
|
operator: operator,
|
|
stackStartFunction: stackStartFunction
|
|
});
|
|
}
|
|
|
|
// EXTENSION! allows for well behaved errors defined elsewhere.
|
|
assert.fail = fail;
|
|
|
|
// 4. Pure assertion tests whether a value is truthy, as determined
|
|
// by !!guard.
|
|
// assert.ok(guard, message_opt);
|
|
// This statement is equivalent to assert.equal(true, !!guard,
|
|
// message_opt);. To test strictly for the value true, use
|
|
// assert.strictEqual(true, guard, message_opt);.
|
|
|
|
function ok(value, message) {
|
|
if (!value) fail(value, true, message, '==', assert.ok);
|
|
}
|
|
assert.ok = ok;
|
|
|
|
// 5. The equality assertion tests shallow, coercive equality with
|
|
// ==.
|
|
// assert.equal(actual, expected, message_opt);
|
|
|
|
assert.equal = function equal(actual, expected, message) {
|
|
if (actual != expected) fail(actual, expected, message, '==', assert.equal);
|
|
};
|
|
|
|
// 6. The non-equality assertion tests for whether two objects are not equal
|
|
// with != assert.notEqual(actual, expected, message_opt);
|
|
|
|
assert.notEqual = function notEqual(actual, expected, message) {
|
|
if (actual == expected) {
|
|
fail(actual, expected, message, '!=', assert.notEqual);
|
|
}
|
|
};
|
|
|
|
// 7. The equivalence assertion tests a deep equality relation.
|
|
// assert.deepEqual(actual, expected, message_opt);
|
|
|
|
assert.deepEqual = function deepEqual(actual, expected, message) {
|
|
if (!_deepEqual(actual, expected, false)) {
|
|
fail(actual, expected, message, 'deepEqual', assert.deepEqual);
|
|
}
|
|
};
|
|
|
|
assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
|
|
if (!_deepEqual(actual, expected, true)) {
|
|
fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual);
|
|
}
|
|
};
|
|
|
|
function _deepEqual(actual, expected, strict, memos) {
|
|
// 7.1. All identical values are equivalent, as determined by ===.
|
|
if (actual === expected) {
|
|
return true;
|
|
} else if (isBuffer(actual) && isBuffer(expected)) {
|
|
return compare(actual, expected) === 0;
|
|
|
|
// 7.2. If the expected value is a Date object, the actual value is
|
|
// equivalent if it is also a Date object that refers to the same time.
|
|
} else if (util.isDate(actual) && util.isDate(expected)) {
|
|
return actual.getTime() === expected.getTime();
|
|
|
|
// 7.3 If the expected value is a RegExp object, the actual value is
|
|
// equivalent if it is also a RegExp object with the same source and
|
|
// properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
|
|
} else if (util.isRegExp(actual) && util.isRegExp(expected)) {
|
|
return actual.source === expected.source &&
|
|
actual.global === expected.global &&
|
|
actual.multiline === expected.multiline &&
|
|
actual.lastIndex === expected.lastIndex &&
|
|
actual.ignoreCase === expected.ignoreCase;
|
|
|
|
// 7.4. Other pairs that do not both pass typeof value == 'object',
|
|
// equivalence is determined by ==.
|
|
} else if ((actual === null || typeof actual !== 'object') &&
|
|
(expected === null || typeof expected !== 'object')) {
|
|
return strict ? actual === expected : actual == expected;
|
|
|
|
// If both values are instances of typed arrays, wrap their underlying
|
|
// ArrayBuffers in a Buffer each to increase performance
|
|
// This optimization requires the arrays to have the same type as checked by
|
|
// Object.prototype.toString (aka pToString). Never perform binary
|
|
// comparisons for Float*Arrays, though, since e.g. +0 === -0 but their
|
|
// bit patterns are not identical.
|
|
} else if (isView(actual) && isView(expected) &&
|
|
pToString(actual) === pToString(expected) &&
|
|
!(actual instanceof Float32Array ||
|
|
actual instanceof Float64Array)) {
|
|
return compare(new Uint8Array(actual.buffer),
|
|
new Uint8Array(expected.buffer)) === 0;
|
|
|
|
// 7.5 For all other Object pairs, including Array objects, equivalence is
|
|
// determined by having the same number of owned properties (as verified
|
|
// with Object.prototype.hasOwnProperty.call), the same set of keys
|
|
// (although not necessarily the same order), equivalent values for every
|
|
// corresponding key, and an identical 'prototype' property. Note: this
|
|
// accounts for both named and indexed properties on Arrays.
|
|
} else if (isBuffer(actual) !== isBuffer(expected)) {
|
|
return false;
|
|
} else {
|
|
memos = memos || {actual: [], expected: []};
|
|
|
|
var actualIndex = memos.actual.indexOf(actual);
|
|
if (actualIndex !== -1) {
|
|
if (actualIndex === memos.expected.indexOf(expected)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
memos.actual.push(actual);
|
|
memos.expected.push(expected);
|
|
|
|
return objEquiv(actual, expected, strict, memos);
|
|
}
|
|
}
|
|
|
|
function isArguments(object) {
|
|
return Object.prototype.toString.call(object) == '[object Arguments]';
|
|
}
|
|
|
|
function objEquiv(a, b, strict, actualVisitedObjects) {
|
|
if (a === null || a === undefined || b === null || b === undefined)
|
|
return false;
|
|
// if one is a primitive, the other must be same
|
|
if (util.isPrimitive(a) || util.isPrimitive(b))
|
|
return a === b;
|
|
if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
|
|
return false;
|
|
var aIsArgs = isArguments(a);
|
|
var bIsArgs = isArguments(b);
|
|
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))
|
|
return false;
|
|
if (aIsArgs) {
|
|
a = pSlice.call(a);
|
|
b = pSlice.call(b);
|
|
return _deepEqual(a, b, strict);
|
|
}
|
|
var ka = objectKeys(a);
|
|
var kb = objectKeys(b);
|
|
var key, i;
|
|
// having the same number of owned properties (keys incorporates
|
|
// hasOwnProperty)
|
|
if (ka.length !== kb.length)
|
|
return false;
|
|
//the same set of keys (although not necessarily the same order),
|
|
ka.sort();
|
|
kb.sort();
|
|
//~~~cheap key test
|
|
for (i = ka.length - 1; i >= 0; i--) {
|
|
if (ka[i] !== kb[i])
|
|
return false;
|
|
}
|
|
//equivalent values for every corresponding key, and
|
|
//~~~possibly expensive deep test
|
|
for (i = ka.length - 1; i >= 0; i--) {
|
|
key = ka[i];
|
|
if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// 8. The non-equivalence assertion tests for any deep inequality.
|
|
// assert.notDeepEqual(actual, expected, message_opt);
|
|
|
|
assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
|
|
if (_deepEqual(actual, expected, false)) {
|
|
fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
|
|
}
|
|
};
|
|
|
|
assert.notDeepStrictEqual = notDeepStrictEqual;
|
|
function notDeepStrictEqual(actual, expected, message) {
|
|
if (_deepEqual(actual, expected, true)) {
|
|
fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual);
|
|
}
|
|
}
|
|
|
|
|
|
// 9. The strict equality assertion tests strict equality, as determined by ===.
|
|
// assert.strictEqual(actual, expected, message_opt);
|
|
|
|
assert.strictEqual = function strictEqual(actual, expected, message) {
|
|
if (actual !== expected) {
|
|
fail(actual, expected, message, '===', assert.strictEqual);
|
|
}
|
|
};
|
|
|
|
// 10. The strict non-equality assertion tests for strict inequality, as
|
|
// determined by !==. assert.notStrictEqual(actual, expected, message_opt);
|
|
|
|
assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
|
|
if (actual === expected) {
|
|
fail(actual, expected, message, '!==', assert.notStrictEqual);
|
|
}
|
|
};
|
|
|
|
function expectedException(actual, expected) {
|
|
if (!actual || !expected) {
|
|
return false;
|
|
}
|
|
|
|
if (Object.prototype.toString.call(expected) == '[object RegExp]') {
|
|
return expected.test(actual);
|
|
}
|
|
|
|
try {
|
|
if (actual instanceof expected) {
|
|
return true;
|
|
}
|
|
} catch (e) {
|
|
// Ignore. The instanceof check doesn't work for arrow functions.
|
|
}
|
|
|
|
if (Error.isPrototypeOf(expected)) {
|
|
return false;
|
|
}
|
|
|
|
return expected.call({}, actual) === true;
|
|
}
|
|
|
|
function _tryBlock(block) {
|
|
var error;
|
|
try {
|
|
block();
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
function _throws(shouldThrow, block, expected, message) {
|
|
var actual;
|
|
|
|
if (typeof block !== 'function') {
|
|
throw new TypeError('"block" argument must be a function');
|
|
}
|
|
|
|
if (typeof expected === 'string') {
|
|
message = expected;
|
|
expected = null;
|
|
}
|
|
|
|
actual = _tryBlock(block);
|
|
|
|
message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
|
|
(message ? ' ' + message : '.');
|
|
|
|
if (shouldThrow && !actual) {
|
|
fail(actual, expected, 'Missing expected exception' + message);
|
|
}
|
|
|
|
var userProvidedMessage = typeof message === 'string';
|
|
var isUnwantedException = !shouldThrow && util.isError(actual);
|
|
var isUnexpectedException = !shouldThrow && actual && !expected;
|
|
|
|
if ((isUnwantedException &&
|
|
userProvidedMessage &&
|
|
expectedException(actual, expected)) ||
|
|
isUnexpectedException) {
|
|
fail(actual, expected, 'Got unwanted exception' + message);
|
|
}
|
|
|
|
if ((shouldThrow && actual && expected &&
|
|
!expectedException(actual, expected)) || (!shouldThrow && actual)) {
|
|
throw actual;
|
|
}
|
|
}
|
|
|
|
// 11. Expected to throw an error:
|
|
// assert.throws(block, Error_opt, message_opt);
|
|
|
|
assert.throws = function(block, /*optional*/error, /*optional*/message) {
|
|
_throws(true, block, error, message);
|
|
};
|
|
|
|
// EXTENSION! This is annoying to write outside this module.
|
|
assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
|
|
_throws(false, block, error, message);
|
|
};
|
|
|
|
assert.ifError = function(err) { if (err) throw err; };
|
|
|
|
// Expose a strict only variant of assert
|
|
function strict(value, message) {
|
|
if (!value) fail(value, true, message, '==', strict);
|
|
}
|
|
assert.strict = objectAssign(strict, assert, {
|
|
equal: assert.strictEqual,
|
|
deepEqual: assert.deepStrictEqual,
|
|
notEqual: assert.notStrictEqual,
|
|
notDeepEqual: assert.notDeepStrictEqual
|
|
});
|
|
assert.strict.strict = assert.strict;
|
|
|
|
var objectKeys = Object.keys || function (obj) {
|
|
var keys = [];
|
|
for (var key in obj) {
|
|
if (hasOwn.call(obj, key)) keys.push(key);
|
|
}
|
|
return keys;
|
|
};
|
|
|
|
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
|
|
|
|
/***/ }),
|
|
/* 22 */
|
|
/***/ (function(module, exports) {
|
|
|
|
/*
|
|
object-assign
|
|
(c) Sindre Sorhus
|
|
@license MIT
|
|
*/
|
|
|
|
'use strict';
|
|
/* eslint-disable no-unused-vars */
|
|
var getOwnPropertySymbols = Object.getOwnPropertySymbols;
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
var propIsEnumerable = Object.prototype.propertyIsEnumerable;
|
|
|
|
function toObject(val) {
|
|
if (val === null || val === undefined) {
|
|
throw new TypeError('Object.assign cannot be called with null or undefined');
|
|
}
|
|
|
|
return Object(val);
|
|
}
|
|
|
|
function shouldUseNative() {
|
|
try {
|
|
if (!Object.assign) {
|
|
return false;
|
|
}
|
|
|
|
// Detect buggy property enumeration order in older V8 versions.
|
|
|
|
// https://bugs.chromium.org/p/v8/issues/detail?id=4118
|
|
var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
|
|
test1[5] = 'de';
|
|
if (Object.getOwnPropertyNames(test1)[0] === '5') {
|
|
return false;
|
|
}
|
|
|
|
// https://bugs.chromium.org/p/v8/issues/detail?id=3056
|
|
var test2 = {};
|
|
for (var i = 0; i < 10; i++) {
|
|
test2['_' + String.fromCharCode(i)] = i;
|
|
}
|
|
var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
|
|
return test2[n];
|
|
});
|
|
if (order2.join('') !== '0123456789') {
|
|
return false;
|
|
}
|
|
|
|
// https://bugs.chromium.org/p/v8/issues/detail?id=3056
|
|
var test3 = {};
|
|
'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
|
|
test3[letter] = letter;
|
|
});
|
|
if (Object.keys(Object.assign({}, test3)).join('') !==
|
|
'abcdefghijklmnopqrst') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} catch (err) {
|
|
// We don't expect any of the above to throw, but better to be safe.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
module.exports = shouldUseNative() ? Object.assign : function (target, source) {
|
|
var from;
|
|
var to = toObject(target);
|
|
var symbols;
|
|
|
|
for (var s = 1; s < arguments.length; s++) {
|
|
from = Object(arguments[s]);
|
|
|
|
for (var key in from) {
|
|
if (hasOwnProperty.call(from, key)) {
|
|
to[key] = from[key];
|
|
}
|
|
}
|
|
|
|
if (getOwnPropertySymbols) {
|
|
symbols = getOwnPropertySymbols(from);
|
|
for (var i = 0; i < symbols.length; i++) {
|
|
if (propIsEnumerable.call(from, symbols[i])) {
|
|
to[symbols[i]] = from[symbols[i]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return to;
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
/* 23 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
/* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
var formatRegExp = /%[sdj%]/g;
|
|
exports.format = function(f) {
|
|
if (!isString(f)) {
|
|
var objects = [];
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
objects.push(inspect(arguments[i]));
|
|
}
|
|
return objects.join(' ');
|
|
}
|
|
|
|
var i = 1;
|
|
var args = arguments;
|
|
var len = args.length;
|
|
var str = String(f).replace(formatRegExp, function(x) {
|
|
if (x === '%%') return '%';
|
|
if (i >= len) return x;
|
|
switch (x) {
|
|
case '%s': return String(args[i++]);
|
|
case '%d': return Number(args[i++]);
|
|
case '%j':
|
|
try {
|
|
return JSON.stringify(args[i++]);
|
|
} catch (_) {
|
|
return '[Circular]';
|
|
}
|
|
default:
|
|
return x;
|
|
}
|
|
});
|
|
for (var x = args[i]; i < len; x = args[++i]) {
|
|
if (isNull(x) || !isObject(x)) {
|
|
str += ' ' + x;
|
|
} else {
|
|
str += ' ' + inspect(x);
|
|
}
|
|
}
|
|
return str;
|
|
};
|
|
|
|
|
|
// Mark that a method should not be used.
|
|
// Returns a modified function which warns once by default.
|
|
// If --no-deprecation is set, then it is a no-op.
|
|
exports.deprecate = function(fn, msg) {
|
|
// Allow for deprecating things in the process of starting up.
|
|
if (isUndefined(global.process)) {
|
|
return function() {
|
|
return exports.deprecate(fn, msg).apply(this, arguments);
|
|
};
|
|
}
|
|
|
|
if (process.noDeprecation === true) {
|
|
return fn;
|
|
}
|
|
|
|
var warned = false;
|
|
function deprecated() {
|
|
if (!warned) {
|
|
if (process.throwDeprecation) {
|
|
throw new Error(msg);
|
|
} else if (process.traceDeprecation) {
|
|
console.trace(msg);
|
|
} else {
|
|
console.error(msg);
|
|
}
|
|
warned = true;
|
|
}
|
|
return fn.apply(this, arguments);
|
|
}
|
|
|
|
return deprecated;
|
|
};
|
|
|
|
|
|
var debugs = {};
|
|
var debugEnviron;
|
|
exports.debuglog = function(set) {
|
|
if (isUndefined(debugEnviron))
|
|
debugEnviron = process.env.NODE_DEBUG || '';
|
|
set = set.toUpperCase();
|
|
if (!debugs[set]) {
|
|
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
|
|
var pid = process.pid;
|
|
debugs[set] = function() {
|
|
var msg = exports.format.apply(exports, arguments);
|
|
console.error('%s %d: %s', set, pid, msg);
|
|
};
|
|
} else {
|
|
debugs[set] = function() {};
|
|
}
|
|
}
|
|
return debugs[set];
|
|
};
|
|
|
|
|
|
/**
|
|
* Echos the value of a value. Trys to print the value out
|
|
* in the best way possible given the different types.
|
|
*
|
|
* @param {Object} obj The object to print out.
|
|
* @param {Object} opts Optional options object that alters the output.
|
|
*/
|
|
/* legacy: obj, showHidden, depth, colors*/
|
|
function inspect(obj, opts) {
|
|
// default options
|
|
var ctx = {
|
|
seen: [],
|
|
stylize: stylizeNoColor
|
|
};
|
|
// legacy...
|
|
if (arguments.length >= 3) ctx.depth = arguments[2];
|
|
if (arguments.length >= 4) ctx.colors = arguments[3];
|
|
if (isBoolean(opts)) {
|
|
// legacy...
|
|
ctx.showHidden = opts;
|
|
} else if (opts) {
|
|
// got an "options" object
|
|
exports._extend(ctx, opts);
|
|
}
|
|
// set default options
|
|
if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
|
|
if (isUndefined(ctx.depth)) ctx.depth = 2;
|
|
if (isUndefined(ctx.colors)) ctx.colors = false;
|
|
if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
|
|
if (ctx.colors) ctx.stylize = stylizeWithColor;
|
|
return formatValue(ctx, obj, ctx.depth);
|
|
}
|
|
exports.inspect = inspect;
|
|
|
|
|
|
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
|
|
inspect.colors = {
|
|
'bold' : [1, 22],
|
|
'italic' : [3, 23],
|
|
'underline' : [4, 24],
|
|
'inverse' : [7, 27],
|
|
'white' : [37, 39],
|
|
'grey' : [90, 39],
|
|
'black' : [30, 39],
|
|
'blue' : [34, 39],
|
|
'cyan' : [36, 39],
|
|
'green' : [32, 39],
|
|
'magenta' : [35, 39],
|
|
'red' : [31, 39],
|
|
'yellow' : [33, 39]
|
|
};
|
|
|
|
// Don't use 'blue' not visible on cmd.exe
|
|
inspect.styles = {
|
|
'special': 'cyan',
|
|
'number': 'yellow',
|
|
'boolean': 'yellow',
|
|
'undefined': 'grey',
|
|
'null': 'bold',
|
|
'string': 'green',
|
|
'date': 'magenta',
|
|
// "name": intentionally not styling
|
|
'regexp': 'red'
|
|
};
|
|
|
|
|
|
function stylizeWithColor(str, styleType) {
|
|
var style = inspect.styles[styleType];
|
|
|
|
if (style) {
|
|
return '\u001b[' + inspect.colors[style][0] + 'm' + str +
|
|
'\u001b[' + inspect.colors[style][1] + 'm';
|
|
} else {
|
|
return str;
|
|
}
|
|
}
|
|
|
|
|
|
function stylizeNoColor(str, styleType) {
|
|
return str;
|
|
}
|
|
|
|
|
|
function arrayToHash(array) {
|
|
var hash = {};
|
|
|
|
array.forEach(function(val, idx) {
|
|
hash[val] = true;
|
|
});
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
function formatValue(ctx, value, recurseTimes) {
|
|
// Provide a hook for user-specified inspect functions.
|
|
// Check that value is an object with an inspect function on it
|
|
if (ctx.customInspect &&
|
|
value &&
|
|
isFunction(value.inspect) &&
|
|
// Filter out the util module, it's inspect function is special
|
|
value.inspect !== exports.inspect &&
|
|
// Also filter out any prototype objects using the circular check.
|
|
!(value.constructor && value.constructor.prototype === value)) {
|
|
var ret = value.inspect(recurseTimes, ctx);
|
|
if (!isString(ret)) {
|
|
ret = formatValue(ctx, ret, recurseTimes);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Primitive types cannot have properties
|
|
var primitive = formatPrimitive(ctx, value);
|
|
if (primitive) {
|
|
return primitive;
|
|
}
|
|
|
|
// Look up the keys of the object.
|
|
var keys = Object.keys(value);
|
|
var visibleKeys = arrayToHash(keys);
|
|
|
|
if (ctx.showHidden) {
|
|
keys = Object.getOwnPropertyNames(value);
|
|
}
|
|
|
|
// IE doesn't make error fields non-enumerable
|
|
// http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
|
|
if (isError(value)
|
|
&& (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
|
|
return formatError(value);
|
|
}
|
|
|
|
// Some type of object without properties can be shortcutted.
|
|
if (keys.length === 0) {
|
|
if (isFunction(value)) {
|
|
var name = value.name ? ': ' + value.name : '';
|
|
return ctx.stylize('[Function' + name + ']', 'special');
|
|
}
|
|
if (isRegExp(value)) {
|
|
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
|
|
}
|
|
if (isDate(value)) {
|
|
return ctx.stylize(Date.prototype.toString.call(value), 'date');
|
|
}
|
|
if (isError(value)) {
|
|
return formatError(value);
|
|
}
|
|
}
|
|
|
|
var base = '', array = false, braces = ['{', '}'];
|
|
|
|
// Make Array say that they are Array
|
|
if (isArray(value)) {
|
|
array = true;
|
|
braces = ['[', ']'];
|
|
}
|
|
|
|
// Make functions say that they are functions
|
|
if (isFunction(value)) {
|
|
var n = value.name ? ': ' + value.name : '';
|
|
base = ' [Function' + n + ']';
|
|
}
|
|
|
|
// Make RegExps say that they are RegExps
|
|
if (isRegExp(value)) {
|
|
base = ' ' + RegExp.prototype.toString.call(value);
|
|
}
|
|
|
|
// Make dates with properties first say the date
|
|
if (isDate(value)) {
|
|
base = ' ' + Date.prototype.toUTCString.call(value);
|
|
}
|
|
|
|
// Make error with message first say the error
|
|
if (isError(value)) {
|
|
base = ' ' + formatError(value);
|
|
}
|
|
|
|
if (keys.length === 0 && (!array || value.length == 0)) {
|
|
return braces[0] + base + braces[1];
|
|
}
|
|
|
|
if (recurseTimes < 0) {
|
|
if (isRegExp(value)) {
|
|
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
|
|
} else {
|
|
return ctx.stylize('[Object]', 'special');
|
|
}
|
|
}
|
|
|
|
ctx.seen.push(value);
|
|
|
|
var output;
|
|
if (array) {
|
|
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
|
|
} else {
|
|
output = keys.map(function(key) {
|
|
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
|
|
});
|
|
}
|
|
|
|
ctx.seen.pop();
|
|
|
|
return reduceToSingleString(output, base, braces);
|
|
}
|
|
|
|
|
|
function formatPrimitive(ctx, value) {
|
|
if (isUndefined(value))
|
|
return ctx.stylize('undefined', 'undefined');
|
|
if (isString(value)) {
|
|
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
|
|
.replace(/'/g, "\\'")
|
|
.replace(/\\"/g, '"') + '\'';
|
|
return ctx.stylize(simple, 'string');
|
|
}
|
|
if (isNumber(value))
|
|
return ctx.stylize('' + value, 'number');
|
|
if (isBoolean(value))
|
|
return ctx.stylize('' + value, 'boolean');
|
|
// For some reason typeof null is "object", so special case here.
|
|
if (isNull(value))
|
|
return ctx.stylize('null', 'null');
|
|
}
|
|
|
|
|
|
function formatError(value) {
|
|
return '[' + Error.prototype.toString.call(value) + ']';
|
|
}
|
|
|
|
|
|
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
|
|
var output = [];
|
|
for (var i = 0, l = value.length; i < l; ++i) {
|
|
if (hasOwnProperty(value, String(i))) {
|
|
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
|
String(i), true));
|
|
} else {
|
|
output.push('');
|
|
}
|
|
}
|
|
keys.forEach(function(key) {
|
|
if (!key.match(/^\d+$/)) {
|
|
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
|
key, true));
|
|
}
|
|
});
|
|
return output;
|
|
}
|
|
|
|
|
|
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
|
|
var name, str, desc;
|
|
desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
|
|
if (desc.get) {
|
|
if (desc.set) {
|
|
str = ctx.stylize('[Getter/Setter]', 'special');
|
|
} else {
|
|
str = ctx.stylize('[Getter]', 'special');
|
|
}
|
|
} else {
|
|
if (desc.set) {
|
|
str = ctx.stylize('[Setter]', 'special');
|
|
}
|
|
}
|
|
if (!hasOwnProperty(visibleKeys, key)) {
|
|
name = '[' + key + ']';
|
|
}
|
|
if (!str) {
|
|
if (ctx.seen.indexOf(desc.value) < 0) {
|
|
if (isNull(recurseTimes)) {
|
|
str = formatValue(ctx, desc.value, null);
|
|
} else {
|
|
str = formatValue(ctx, desc.value, recurseTimes - 1);
|
|
}
|
|
if (str.indexOf('\n') > -1) {
|
|
if (array) {
|
|
str = str.split('\n').map(function(line) {
|
|
return ' ' + line;
|
|
}).join('\n').substr(2);
|
|
} else {
|
|
str = '\n' + str.split('\n').map(function(line) {
|
|
return ' ' + line;
|
|
}).join('\n');
|
|
}
|
|
}
|
|
} else {
|
|
str = ctx.stylize('[Circular]', 'special');
|
|
}
|
|
}
|
|
if (isUndefined(name)) {
|
|
if (array && key.match(/^\d+$/)) {
|
|
return str;
|
|
}
|
|
name = JSON.stringify('' + key);
|
|
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
|
|
name = name.substr(1, name.length - 2);
|
|
name = ctx.stylize(name, 'name');
|
|
} else {
|
|
name = name.replace(/'/g, "\\'")
|
|
.replace(/\\"/g, '"')
|
|
.replace(/(^"|"$)/g, "'");
|
|
name = ctx.stylize(name, 'string');
|
|
}
|
|
}
|
|
|
|
return name + ': ' + str;
|
|
}
|
|
|
|
|
|
function reduceToSingleString(output, base, braces) {
|
|
var numLinesEst = 0;
|
|
var length = output.reduce(function(prev, cur) {
|
|
numLinesEst++;
|
|
if (cur.indexOf('\n') >= 0) numLinesEst++;
|
|
return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
|
|
}, 0);
|
|
|
|
if (length > 60) {
|
|
return braces[0] +
|
|
(base === '' ? '' : base + '\n ') +
|
|
' ' +
|
|
output.join(',\n ') +
|
|
' ' +
|
|
braces[1];
|
|
}
|
|
|
|
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
|
|
}
|
|
|
|
|
|
// NOTE: These type checking functions intentionally don't use `instanceof`
|
|
// because it is fragile and can be easily faked with `Object.create()`.
|
|
function isArray(ar) {
|
|
return Array.isArray(ar);
|
|
}
|
|
exports.isArray = isArray;
|
|
|
|
function isBoolean(arg) {
|
|
return typeof arg === 'boolean';
|
|
}
|
|
exports.isBoolean = isBoolean;
|
|
|
|
function isNull(arg) {
|
|
return arg === null;
|
|
}
|
|
exports.isNull = isNull;
|
|
|
|
function isNullOrUndefined(arg) {
|
|
return arg == null;
|
|
}
|
|
exports.isNullOrUndefined = isNullOrUndefined;
|
|
|
|
function isNumber(arg) {
|
|
return typeof arg === 'number';
|
|
}
|
|
exports.isNumber = isNumber;
|
|
|
|
function isString(arg) {
|
|
return typeof arg === 'string';
|
|
}
|
|
exports.isString = isString;
|
|
|
|
function isSymbol(arg) {
|
|
return typeof arg === 'symbol';
|
|
}
|
|
exports.isSymbol = isSymbol;
|
|
|
|
function isUndefined(arg) {
|
|
return arg === void 0;
|
|
}
|
|
exports.isUndefined = isUndefined;
|
|
|
|
function isRegExp(re) {
|
|
return isObject(re) && objectToString(re) === '[object RegExp]';
|
|
}
|
|
exports.isRegExp = isRegExp;
|
|
|
|
function isObject(arg) {
|
|
return typeof arg === 'object' && arg !== null;
|
|
}
|
|
exports.isObject = isObject;
|
|
|
|
function isDate(d) {
|
|
return isObject(d) && objectToString(d) === '[object Date]';
|
|
}
|
|
exports.isDate = isDate;
|
|
|
|
function isError(e) {
|
|
return isObject(e) &&
|
|
(objectToString(e) === '[object Error]' || e instanceof Error);
|
|
}
|
|
exports.isError = isError;
|
|
|
|
function isFunction(arg) {
|
|
return typeof arg === 'function';
|
|
}
|
|
exports.isFunction = isFunction;
|
|
|
|
function isPrimitive(arg) {
|
|
return arg === null ||
|
|
typeof arg === 'boolean' ||
|
|
typeof arg === 'number' ||
|
|
typeof arg === 'string' ||
|
|
typeof arg === 'symbol' || // ES6 symbol
|
|
typeof arg === 'undefined';
|
|
}
|
|
exports.isPrimitive = isPrimitive;
|
|
|
|
exports.isBuffer = __webpack_require__(25);
|
|
|
|
function objectToString(o) {
|
|
return Object.prototype.toString.call(o);
|
|
}
|
|
|
|
|
|
function pad(n) {
|
|
return n < 10 ? '0' + n.toString(10) : n.toString(10);
|
|
}
|
|
|
|
|
|
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
|
|
'Oct', 'Nov', 'Dec'];
|
|
|
|
// 26 Feb 16:19:34
|
|
function timestamp() {
|
|
var d = new Date();
|
|
var time = [pad(d.getHours()),
|
|
pad(d.getMinutes()),
|
|
pad(d.getSeconds())].join(':');
|
|
return [d.getDate(), months[d.getMonth()], time].join(' ');
|
|
}
|
|
|
|
|
|
// log is just a thin wrapper to console.log that prepends a timestamp
|
|
exports.log = function() {
|
|
console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
|
|
};
|
|
|
|
|
|
/**
|
|
* Inherit the prototype methods from one constructor into another.
|
|
*
|
|
* The Function.prototype.inherits from lang.js rewritten as a standalone
|
|
* function (not on Function.prototype). NOTE: If this file is to be loaded
|
|
* during bootstrapping this function needs to be rewritten using some native
|
|
* functions as prototype setup using normal JavaScript does not work as
|
|
* expected during bootstrapping (see mirror.js in r114903).
|
|
*
|
|
* @param {function} ctor Constructor function which needs to inherit the
|
|
* prototype.
|
|
* @param {function} superCtor Constructor function to inherit prototype from.
|
|
*/
|
|
exports.inherits = __webpack_require__(26);
|
|
|
|
exports._extend = function(origin, add) {
|
|
// Don't do anything if add isn't an object
|
|
if (!add || !isObject(add)) return origin;
|
|
|
|
var keys = Object.keys(add);
|
|
var i = keys.length;
|
|
while (i--) {
|
|
origin[keys[i]] = add[keys[i]];
|
|
}
|
|
return origin;
|
|
};
|
|
|
|
function hasOwnProperty(obj, prop) {
|
|
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
}
|
|
|
|
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(24)))
|
|
|
|
/***/ }),
|
|
/* 24 */
|
|
/***/ (function(module, exports) {
|
|
|
|
// shim for using process in browser
|
|
var process = module.exports = {};
|
|
|
|
// cached from whatever global is present so that test runners that stub it
|
|
// don't break things. But we need to wrap it in a try catch in case it is
|
|
// wrapped in strict mode code which doesn't define any globals. It's inside a
|
|
// function because try/catches deoptimize in certain engines.
|
|
|
|
var cachedSetTimeout;
|
|
var cachedClearTimeout;
|
|
|
|
function defaultSetTimout() {
|
|
throw new Error('setTimeout has not been defined');
|
|
}
|
|
function defaultClearTimeout () {
|
|
throw new Error('clearTimeout has not been defined');
|
|
}
|
|
(function () {
|
|
try {
|
|
if (typeof setTimeout === 'function') {
|
|
cachedSetTimeout = setTimeout;
|
|
} else {
|
|
cachedSetTimeout = defaultSetTimout;
|
|
}
|
|
} catch (e) {
|
|
cachedSetTimeout = defaultSetTimout;
|
|
}
|
|
try {
|
|
if (typeof clearTimeout === 'function') {
|
|
cachedClearTimeout = clearTimeout;
|
|
} else {
|
|
cachedClearTimeout = defaultClearTimeout;
|
|
}
|
|
} catch (e) {
|
|
cachedClearTimeout = defaultClearTimeout;
|
|
}
|
|
} ())
|
|
function runTimeout(fun) {
|
|
if (cachedSetTimeout === setTimeout) {
|
|
//normal enviroments in sane situations
|
|
return setTimeout(fun, 0);
|
|
}
|
|
// if setTimeout wasn't available but was latter defined
|
|
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
|
cachedSetTimeout = setTimeout;
|
|
return setTimeout(fun, 0);
|
|
}
|
|
try {
|
|
// when when somebody has screwed with setTimeout but no I.E. maddness
|
|
return cachedSetTimeout(fun, 0);
|
|
} catch(e){
|
|
try {
|
|
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
|
return cachedSetTimeout.call(null, fun, 0);
|
|
} catch(e){
|
|
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
|
|
return cachedSetTimeout.call(this, fun, 0);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
function runClearTimeout(marker) {
|
|
if (cachedClearTimeout === clearTimeout) {
|
|
//normal enviroments in sane situations
|
|
return clearTimeout(marker);
|
|
}
|
|
// if clearTimeout wasn't available but was latter defined
|
|
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
|
cachedClearTimeout = clearTimeout;
|
|
return clearTimeout(marker);
|
|
}
|
|
try {
|
|
// when when somebody has screwed with setTimeout but no I.E. maddness
|
|
return cachedClearTimeout(marker);
|
|
} catch (e){
|
|
try {
|
|
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
|
return cachedClearTimeout.call(null, marker);
|
|
} catch (e){
|
|
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
|
|
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
|
|
return cachedClearTimeout.call(this, marker);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
var queue = [];
|
|
var draining = false;
|
|
var currentQueue;
|
|
var queueIndex = -1;
|
|
|
|
function cleanUpNextTick() {
|
|
if (!draining || !currentQueue) {
|
|
return;
|
|
}
|
|
draining = false;
|
|
if (currentQueue.length) {
|
|
queue = currentQueue.concat(queue);
|
|
} else {
|
|
queueIndex = -1;
|
|
}
|
|
if (queue.length) {
|
|
drainQueue();
|
|
}
|
|
}
|
|
|
|
function drainQueue() {
|
|
if (draining) {
|
|
return;
|
|
}
|
|
var timeout = runTimeout(cleanUpNextTick);
|
|
draining = true;
|
|
|
|
var len = queue.length;
|
|
while(len) {
|
|
currentQueue = queue;
|
|
queue = [];
|
|
while (++queueIndex < len) {
|
|
if (currentQueue) {
|
|
currentQueue[queueIndex].run();
|
|
}
|
|
}
|
|
queueIndex = -1;
|
|
len = queue.length;
|
|
}
|
|
currentQueue = null;
|
|
draining = false;
|
|
runClearTimeout(timeout);
|
|
}
|
|
|
|
process.nextTick = function (fun) {
|
|
var args = new Array(arguments.length - 1);
|
|
if (arguments.length > 1) {
|
|
for (var i = 1; i < arguments.length; i++) {
|
|
args[i - 1] = arguments[i];
|
|
}
|
|
}
|
|
queue.push(new Item(fun, args));
|
|
if (queue.length === 1 && !draining) {
|
|
runTimeout(drainQueue);
|
|
}
|
|
};
|
|
|
|
// v8 likes predictible objects
|
|
function Item(fun, array) {
|
|
this.fun = fun;
|
|
this.array = array;
|
|
}
|
|
Item.prototype.run = function () {
|
|
this.fun.apply(null, this.array);
|
|
};
|
|
process.title = 'browser';
|
|
process.browser = true;
|
|
process.env = {};
|
|
process.argv = [];
|
|
process.version = ''; // empty string to avoid regexp issues
|
|
process.versions = {};
|
|
|
|
function noop() {}
|
|
|
|
process.on = noop;
|
|
process.addListener = noop;
|
|
process.once = noop;
|
|
process.off = noop;
|
|
process.removeListener = noop;
|
|
process.removeAllListeners = noop;
|
|
process.emit = noop;
|
|
process.prependListener = noop;
|
|
process.prependOnceListener = noop;
|
|
|
|
process.listeners = function (name) { return [] }
|
|
|
|
process.binding = function (name) {
|
|
throw new Error('process.binding is not supported');
|
|
};
|
|
|
|
process.cwd = function () { return '/' };
|
|
process.chdir = function (dir) {
|
|
throw new Error('process.chdir is not supported');
|
|
};
|
|
process.umask = function() { return 0; };
|
|
|
|
|
|
/***/ }),
|
|
/* 25 */
|
|
/***/ (function(module, exports) {
|
|
|
|
module.exports = function isBuffer(arg) {
|
|
return arg && typeof arg === 'object'
|
|
&& typeof arg.copy === 'function'
|
|
&& typeof arg.fill === 'function'
|
|
&& typeof arg.readUInt8 === 'function';
|
|
}
|
|
|
|
/***/ }),
|
|
/* 26 */
|
|
/***/ (function(module, exports) {
|
|
|
|
if (typeof Object.create === 'function') {
|
|
// implementation from standard node.js 'util' module
|
|
module.exports = function inherits(ctor, superCtor) {
|
|
ctor.super_ = superCtor
|
|
ctor.prototype = Object.create(superCtor.prototype, {
|
|
constructor: {
|
|
value: ctor,
|
|
enumerable: false,
|
|
writable: true,
|
|
configurable: true
|
|
}
|
|
});
|
|
};
|
|
} else {
|
|
// old school shim for old browsers
|
|
module.exports = function inherits(ctor, superCtor) {
|
|
ctor.super_ = superCtor
|
|
var TempCtor = function () {}
|
|
TempCtor.prototype = superCtor.prototype
|
|
ctor.prototype = new TempCtor()
|
|
ctor.prototype.constructor = ctor
|
|
}
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
/* 27 */
|
|
/***/ (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');
|