svg-text
Version:
Utilities for working with SVG, including SvgText for multiline text.
1,673 lines (1,453 loc) • 116 kB
JavaScript
/*! svg-text v0.5.1 */
(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["SvgText"] = factory();
else
root["SvgText"] = 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__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SvgUtil = undefined;
var _SvgText = __webpack_require__(1);
var _SvgText2 = _interopRequireDefault(_SvgText);
var _svg = __webpack_require__(2);
var _math = __webpack_require__(5);
var math = _interopRequireWildcard(_math);
var _keys = __webpack_require__(3);
var keys = _interopRequireWildcard(_keys);
var _text = __webpack_require__(11);
var text = _interopRequireWildcard(_text);
var _style = __webpack_require__(8);
var style = _interopRequireWildcard(_style);
var _lodash = __webpack_require__(12);
var _lodash2 = _interopRequireDefault(_lodash);
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 }; }
var SvgUtil = exports.SvgUtil = (0, _lodash2.default)({ createElement: _svg.createElement }, math, keys, text, style);
exports.default = _SvgText2.default;
/***/ },
/* 1 */
/***/ 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 _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _svg = __webpack_require__(2);
var _math = __webpack_require__(5);
var _keys = __webpack_require__(3);
var _style = __webpack_require__(8);
var _render = __webpack_require__(9);
var _render2 = _interopRequireDefault(_render);
var _lodash = __webpack_require__(4);
var _lodash2 = _interopRequireDefault(_lodash);
var _lodash3 = __webpack_require__(6);
var _lodash4 = _interopRequireDefault(_lodash3);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var _svgEl = null;
var _styleEl = null;
var SvgText = function () {
/**
* @construtor
* @param {object} options
* @param {SVGElement} options.element to append the text into
* @param {string} options.text
* @param {number=} options.x
* @param {number=} options.y
* @param {number=} options.width
* @param {number=} options.height
* @param {number=} options.maxWidth
* @param {number=} options.maxHeight
* @param {number=} options.maxLines
* @param {string=} options.align [left|center|right] Default is left
* @param {string=} options.verticalAlign [top|middle|bottom] Default is top
* @param {string=} options.textOverflow [clip|ellipsis|custom] Default is clip
* @param {string=} options.selectorNamespace
* @param {string=} options.className
* @param {object=} options.style Styles to be written as CSS to a `style` element.
* @param {object=} options.styleElement The `style` element to write styles to.
* @param {object=} options.attrs Attributes applied to the `text` element
* @param {object=} options.rect Attributes applied to an underlying `rect`
* @param {array=} options.padding [top, right, bottom, left]
* @param {array=} options.margin [top, right, bottom, left]
*/
function SvgText(options) {
_classCallCheck(this, SvgText);
this.options = updateOptions(options);
this.uid = this.options.uid;
this.rect = this.options.rect ? (0, _svg.createRect)(this.options) : null;
this.text = createText(this.options);
writeStyleAsCss(this.options);
var compStyle = window.getComputedStyle(this.text, null);
this.fontSize = parseFloat(compStyle.getPropertyValue('font-size'));
this.lineHeight = parseFloat(compStyle.getPropertyValue('line-height')) || this.fontSize * 1.2;
this.lines = (0, _render2.default)(this.text, this.options, this.lineHeight);
this.bounds = sizeBounds(this.text, this.options);
sizeRect(this.rect, this.bounds, this.options.rect);
offsetByMargin(this.bounds, this.options.margin);
moveText(this.text, this.options, {
fontSize: this.fontSize,
lineHeight: this.lineHeight,
lines: this.lines
});
}
/**
* Transforms `text` into a form ready to be opened in Adobe Illustrator.
* @param {SVG text element} textWeb SVG element prepared for the web.
* @param {SVG text element} textAi Duplicate SVG element to be prepared for Illustrator.
* @param {string} font The font as a name that Illustrator will recognize.
*/
_createClass(SvgText, null, [{
key: 'forIllustrator',
value: function forIllustrator(textWeb, textAi, font) {
var compStyle = window.getComputedStyle(textWeb, null);
if (font) {
textAi.setAttribute('font-family', font);
}
textAi.setAttribute('font-size', compStyle.getPropertyValue('font-size'));
textAi.setAttribute('line-height', compStyle.getPropertyValue('line-height'));
textAi.setAttribute('fill', compStyle.getPropertyValue('fill'));
textAi.setAttribute('fill-opacity', compStyle.getPropertyValue('fill-opacity'));
textAi.removeAttribute('class');
return textAi;
}
}, {
key: 'writeStyle',
value: function writeStyle(selector, css, style) {
var styleEl = style || SvgText.style || null;
if (styleEl && SvgText.svg) {
selector = getSelectorNamespace(SvgText.svg) + ' ' + selector;
(0, _style.writeStyle)(selector, css, styleEl);
}
}
}, {
key: 'svg',
set: function set(value) {
_svgEl = value;
},
get: function get() {
return _svgEl;
}
}, {
key: 'style',
set: function set(value) {
_styleEl = value;
},
get: function get() {
return _styleEl;
}
}]);
return SvgText;
}();
exports.default = SvgText;
function updateOptions(options) {
options.uid = uid();
options = updateEnvironment(options);
options = updateClassname(options);
options = (0, _math.updateSizeOptions)(options);
options.attrs = options.attrs && _typeof(options.attrs) === 'object' ? (0, _keys.normalizeKeys)(options.attrs, 'css') : {};
return options;
}
// Ensure svg, selectorNamespace, and style properties are set.
function updateEnvironment(options) {
options.svg = options.svg || _svgEl || null;
options.styleElement = options.styleElement || _styleEl || null;
var svgEl = options.element || document.body;
while (svgEl && svgEl.nodeName.toUpperCase() !== 'SVG') {
svgEl = svgEl.parentElement;
}
svgEl = svgEl || document.body;
if (svgEl.nodeName.toUpperCase() !== 'SVG') {
svgEl = (0, _svg.createElement)('svg', {
width: 640, height: 480, 'data-svgtext': getSvgUid()
});
(options.element || document.body).appendChild(svgEl);
}
options.svg = svgEl;
if (!options.svg.hasAttribute('data-svgtext')) {
options.svg.setAttribute('data-svgtext', getSvgUid());
}
if (!options.selectorNamespace || typeof options.selectorNamespace !== 'string') {
options.selectorNamespace = getSelectorNamespace(options.svg);
}
options.styleElement = options.styleElement || options.svg.querySelector('style');
if (!options.styleElement) {
options.styleElement = document.createElement('style');
var firstChild = options.svg.childNodes[0];
if (firstChild) {
options.svg.insertBefore(options.styleElement, firstChild);
} else {
options.svg.appendChild(options.styleElement);
}
}
options.element = options.element || options.svg;
_svgEl = options.svg;
_styleEl = options.styleElement;
return options;
}
// Set default className to 'svg-text svg-text-[uid]'.
function updateClassname(options) {
if (!options.className || typeof options.className !== 'string') {
options.className = 'svg-text';
}
options.className += '.' + options.className.split(' ')[0] + '-' + options.uid;
return options;
}
function offsetByMargin(bounds, margin) {
bounds.x -= margin[3];
bounds.y -= margin[0];
bounds.width += margin[3] + margin[1];
bounds.height += margin[0] + margin[2];
}
// Create the text element.
function createText(options) {
var textOptions = (0, _keys.normalizeKeys)((0, _lodash4.default)({}, options.attrs, { 'ai-id': options.uid }));
var text = (0, _svg.createElement)('text', textOptions);
if (options.className) {
text.setAttribute('class', options.className.replace(/\./, ' '));
}
options.element.appendChild(text);
return text;
}
function writeStyleAsCss(options) {
if (options.style && options.styleElement) {
var selectorNamespace = options.selectorNamespace || null;
var className = options.className ? ('.' + options.className).replace(' ', '.') : null;
var textClassName = className ? 'text' + className : null;
var selector = [selectorNamespace ? selectorNamespace + ' ' : '', textClassName || ''].join('');
if (selector) {
(0, _style.writeStyle)(selector, options.style, options.styleElement);
}
}
}
function sizeBounds(text, options) {
var p = options.padding;
var textRect = text.getBoundingClientRect();
var bounds = {
x: options.x,
y: options.y,
width: textRect.width,
height: textRect.height
};
bounds.width = (0, _math.isPosNum)(options.width) ? options.width : textRect.width + p[3] + p[1];
if (options.align === 'right') {
bounds.x -= bounds.width;
} else if (options.align === 'center') {
bounds.x -= bounds.width / 2;
}
bounds.height = (0, _math.isPosNum)(options.height) ? options.height : textRect.height + p[0] + p[2];
if (options.verticalAlign === 'bottom') {
bounds.y -= bounds.height;
} else if (options.verticalAlign === 'middle') {
bounds.y -= bounds.height / 2;
}
return bounds;
}
function sizeRect(rect, bounds, rectSize) {
if (rect) {
rect.setAttribute('width', (0, _lodash2.default)(rectSize.width) ? rectSize.width : bounds.width);
rect.setAttribute('height', (0, _lodash2.default)(rectSize.height) ? rectSize.height : bounds.height);
rect.setAttribute('x', bounds.x + ((0, _lodash2.default)(rectSize.x) ? rectSize.x : 0));
rect.setAttribute('y', bounds.y + ((0, _lodash2.default)(rectSize.y) ? rectSize.y : 0));
}
}
function moveText(text, options, attrs) {
alignText(text, options.align);
options = verticalAlignText(text, options, attrs);
text.setAttribute('transform', 'translate(' + options.textPos.x + ',' + options.textPos.y + ')');
}
function alignText(text, align) {
if (align === 'center') {
text.setAttribute('text-anchor', 'middle');
} else if (align === 'right') {
text.setAttribute('text-anchor', 'end');
} else if (text.hasAttribute('text-anchor')) {
text.removeAttribute('text-anchor');
}
}
function verticalAlignText(text, options, attrs) {
options.textPos.y += attrs.fontSize;
if (options.verticalAlign === 'middle') {
options.textPos.y -= Math.max(attrs.lineHeight, textHeight(text, options, attrs)) / 2;
} else if (options.verticalAlign === 'bottom') {
options.textPos.y -= textHeight(text, options, attrs);
}
return options;
}
function textHeight(text, options, attrs) {
return Math.max(attrs.fontSize, (0, _math.isPosNum)(options.textPos.height) ? options.textPos.height : text.getBoundingClientRect().height);
}
function getSelectorNamespace(svg) {
var svgId = svg.getAttribute('id');
if (svgId) {
return 'svg#' + svgId;
} else {
var svgAttr = svg.getAttribute('data-svgtext');
return 'svg[data-svgtext="' + svgAttr + '"]';
}
}
// Each text field gets its own unique id so it styles can be namespaced to it
// and also so original SVG text elements can be synced with Illustrator SVG.
var __uid = 0;
function uid() {
return __uid++;
}
function getSvgUid() {
var maxId = 0;
var svgEls = document.querySelectorAll('svg[data-svgtext]');
for (var i = 0; i < svgEls.length; i++) {
var id = +svgEls[i].getAttribute('data-svgtext');
maxId = isNaN(id) ? maxId : Math.max(id, maxId);
}
return maxId + 1;
}
/***/ },
/* 2 */
/***/ 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.createElement = createElement;
exports.createRect = createRect;
exports.appendTspan = appendTspan;
exports.createTspan = createTspan;
exports.writeInnerHTML = writeInnerHTML;
var _keys = __webpack_require__(3);
/**
* Convenience/util function to create SVG elements.
*/
function createElement(name, attrs) {
var node = document.createElementNS('http://www.w3.org/2000/svg', name);
if (attrs && (typeof attrs === 'undefined' ? 'undefined' : _typeof(attrs)) === 'object') {
attrs = (0, _keys.normalizeKeys)(attrs, 'css');
var keys = Object.keys(attrs);
for (var i = 0; i < keys.length; i++) {
node.setAttribute(keys[i], attrs[keys[i]]);
}
}
return node;
}
/**
* Create a rect for background color and borders. Width and height will be
* added later once text width and height are known.
*/
function createRect(options) {
var rectOptions = (0, _keys.normalizeKeys)(options.rect, 'css');
if (!rectOptions.hasOwnProperty('fill')) {
// If `fill` is not specified, make invisible not black.
rectOptions['fill-opacity'] = 0;
}
var rect = createElement('rect', rectOptions);
if (options.element) {
options.element.appendChild(rect);
}
return rect;
}
/**
* Create and append a `tspan`.
*/
function appendTspan(text, str, x, y) {
var tspan = createTspan(str, x, y);
text.appendChild(tspan);
return tspan;
}
/**
* Create a new `tspan`.
*/
function createTspan(str) {
var x = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var y = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
var tspan = createElement('tspan', { x: x, y: y });
writeInnerHTML(tspan, str);
return tspan;
}
/**
* Because `innerHTML` does not work with SVG in older browsers.
*/
function writeInnerHTML(svgEl, content) {
svgEl.innerHTML = content;
var tempEl = document.createElement('div');
tempEl.innerHTML = '<svg>' + content + '</svg>';
Array.prototype.slice.call(svgEl.childNodes).forEach(function (el) {
svgEl.removeChild(el);
});
Array.prototype.slice.call(tempEl.childNodes[0].childNodes).forEach(function (el) {
svgEl.appendChild(el);
});
return svgEl;
}
/***/ },
/* 3 */
/***/ 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.toJs = toJs;
exports.toCss = toCss;
exports.normalizeKeys = normalizeKeys;
var _lodash = __webpack_require__(4);
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @param {string} prop - String in JavaScript-ready camel case.
* @returns {string} String hyphenated in CSS style.
*/
function toJs(prop) {
return prop.replace(/-([a-z])/g, function (match, p1) {
return p1.toUpperCase();
});
}
/**
* @param {string} prop - String in JavaScript-ready camel case.
* @returns {string} String hyphenated in CSS style.
*/
function toCss(prop) {
return prop.replace(/([A-Z])/g, function (match, p1) {
return '-' + p1.toLowerCase();
});
}
/**
* Returns a copy of @param object with keys transformed to the desired style,
* either 'js' or 'css'.
*/
function normalizeKeys(object, style) {
var normalizedObj = {};
if (object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object') {
var keys = Object.keys(object);
keys.forEach(function (key) {
var normalizedKey = style === 'js' ? toJs(key) : toCss(key);
var value = addUnits(key, object[key]);
normalizedObj[normalizedKey] = value;
});
}
return normalizedObj;
}
// Default units are pixels (px) so add 'px' to raw numbers.
function addUnits(key, value) {
switch (key) {
case 'font-size':
case 'fontSize':
case 'line-height':
case 'lineHeight':
if ((0, _lodash2.default)(value)) {
value += 'px';
}
break;
}
return value;
}
/***/ },
/* 4 */
/***/ function(module, exports) {
/* WEBPACK VAR INJECTION */(function(global) {/**
* lodash (Custom Build) <https://lodash.com/>
* Build: `lodash modularize exports="npm" -o ./`
* Copyright jQuery Foundation and other contributors <https://jquery.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeIsFinite = root.isFinite;
/**
* Checks if `value` is a finite primitive number.
*
* **Note:** This method is based on
* [`Number.isFinite`](https://mdn.io/Number/isFinite).
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a finite number,
* else `false`.
* @example
*
* _.isFinite(3);
* // => true
*
* _.isFinite(Number.MIN_VALUE);
* // => true
*
* _.isFinite(Infinity);
* // => false
*
* _.isFinite('3');
* // => false
*/
function isFinite(value) {
return typeof value == 'number' && nativeIsFinite(value);
}
module.exports = isFinite;
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 5 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isPosNum = isPosNum;
exports.minNum = minNum;
exports.maxNum = maxNum;
exports.autoNum = autoNum;
exports.toArrayLen4 = toArrayLen4;
exports.bestSize = bestSize;
exports.updateSizeOptions = updateSizeOptions;
var _keys = __webpack_require__(3);
var _lodash = __webpack_require__(4);
var _lodash2 = _interopRequireDefault(_lodash);
var _lodash3 = __webpack_require__(6);
var _lodash4 = _interopRequireDefault(_lodash3);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Tests if a value is a valid number and also >= 0.
* @returns {boolean}
*/
function isPosNum(n) {
return (0, _lodash2.default)(n) && n >= 0;
}
/**
* Returns the minimum numeric value amongst the arguments, or else "auto".
*/
function minNum() {
return minMax('min', arguments);
}
/**
* Returns the maximum numeric value amongst the arguments, or else "auto".
*/
function maxNum() {
return minMax('max', arguments);
}
/**
* If @param value is "auto", return @param altNum instead.
*/
function autoNum(value, altNum) {
return isPosNum(value) ? value : altNum;
}
function minMax(compare, args) {
var value = 'auto';
for (var i = 0; i < args.length; i++) {
var n = args[i];
if (isPosNum(n)) {
if (isPosNum(value)) {
if (compare === 'min' && n < value || compare === 'max' && n > value) {
value = n;
}
} else if (value === 'auto') {
value = n;
}
}
}
return value;
}
/**
* Transforms value into an array with 4 numbers.
* @param {string|number} value - '10px' or 10
* @returns {number[]} of length 4
*/
function toArrayLen4(value) {
var array;
var i;
if (Array.isArray(value)) {
array = value.slice(0, 4);
} else if (typeof value === 'string') {
var parts = value.replace(/^(\s+)|(\s+)$/g, '').split(/\s+/g).slice(0, 4);
array = parts.length ? parts : [value];
} else if ((0, _lodash2.default)(value)) {
array = [value];
} else {
return [0, 0, 0, 0];
}
switch (array.length) {
case 1:
for (i = 1; i < 4; i++) {
array[i] = array[0];
}
break;
case 2:
array[2] = array[0];
array[3] = array[1];
break;
case 3:
array[3] = array[1];
break;
default:
break;
}
for (i = 0; i < 4; i++) {
array[i] = parseFloat(array[i]);
if (isNaN(array[i])) {
array[i] = 0;
}
}
return array;
}
/**
* Returns the preferred width or height amongst width and maxWidth or height
* and maxHeight values in options.
*/
function bestSize(options, dimension) {
var maxProp = dimension === 'height' ? 'maxHeight' : 'maxWidth';
var prop = dimension === 'height' ? 'height' : 'width';
var maxOk = isPosNum(options[maxProp]);
var valOk = isPosNum(options[prop]);
if (maxOk && valOk) {
return Math.min(options[maxProp], options[prop]);
} else if (maxOk) {
return options[maxProp];
} else if (valOk) {
return options[prop];
} else {
return 'auto';
}
}
/**
* Validates and updates x, y, width, height, maxWidth, maxHeight options.
*/
function updateSizeOptions(options) {
options = (0, _lodash4.default)({}, options);
options.padding = toArrayLen4(options.padding);
options.margin = toArrayLen4(options.margin);
options.x = addMarginToX(+(options.x || 0), options.align, options.margin);
options.y = addMarginToY(+(options.y || 0), options.verticalAlign, options.margin);
options.width = isPosNum(options.width) ? options.width : 'auto';
options.maxWidth = isPosNum(options.maxWidth) ? options.maxWidth : 'auto';
options.height = isPosNum(options.height) ? options.height : 'auto';
options.maxHeight = isPosNum(options.maxHeight) ? options.maxHeight : 'auto';
options.maxLines = isPosNum(options.maxLines) ? options.maxLines : 'auto';
// options.width = minNum(options.width, options.maxWidth);
if (isPosNum(options.outerWidth)) {
var maxWidth = options.outerWidth - options.margin[3] - options.margin[1];
options.maxWidth = isPosNum(options.maxWidth) ? Math.min(maxWidth, options.maxWidth) : maxWidth;
}
if (isPosNum(options.outerHeight)) {
var maxHeight = options.outerHeight - options.margin[0] - options.margin[2];
options.maxHeight = isPosNum(options.maxHeight) ? Math.min(maxHeight, options.maxHeight) : maxHeight;
}
options.textPos = createTextPos(options);
return options;
}
function createTextPos(options) {
var padding = options.padding;
var rect = {
x: addMarginToX(options.x, options.align, options.padding),
y: addMarginToY(options.y, options.verticalAlign, options.padding),
width: isPosNum(options.width) ? Math.max(0, options.width - padding[3] - padding[1]) : 'auto',
height: isPosNum(options.height) ? Math.max(0, options.height - padding[0] - padding[2]) : 'auto',
maxWidth: isPosNum(options.maxWidth) ? Math.max(0, options.maxWidth - padding[3] - padding[1]) : 'auto',
maxHeight: isPosNum(options.maxHeight) ? Math.max(0, options.maxHeight - padding[0] - padding[2]) : 'auto'
};
rect.width = minNum(rect.width, rect.maxWidth);
return rect;
}
// Also works for padding.
function addMarginToX(x, align, margin) {
if (align === 'right') {
x -= margin[1];
} else if (align === 'center') {
x += margin[3] / 2;
x -= margin[1] / 2;
} else {
x += margin[3];
}
return x;
}
function addMarginToY(y, verticalAlign, margin) {
if (verticalAlign === 'bottom') {
y -= margin[2];
} else if (verticalAlign === 'middle') {
y += margin[0] / 2;
y -= margin[2] / 2;
} else {
y += margin[0];
}
return y;
}
/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global, module) {/**
* lodash (Custom Build) <https://lodash.com/>
* Build: `lodash modularize exports="npm" -o ./`
* Copyright jQuery Foundation and other contributors <https://jquery.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
/** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200;
/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';
/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;
/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
arrayTag = '[object Array]',
boolTag = '[object Boolean]',
dateTag = '[object Date]',
errorTag = '[object Error]',
funcTag = '[object Function]',
genTag = '[object GeneratorFunction]',
mapTag = '[object Map]',
numberTag = '[object Number]',
objectTag = '[object Object]',
promiseTag = '[object Promise]',
regexpTag = '[object RegExp]',
setTag = '[object Set]',
stringTag = '[object String]',
symbolTag = '[object Symbol]',
weakMapTag = '[object WeakMap]';
var arrayBufferTag = '[object ArrayBuffer]',
dataViewTag = '[object DataView]',
float32Tag = '[object Float32Array]',
float64Tag = '[object Float64Array]',
int8Tag = '[object Int8Array]',
int16Tag = '[object Int16Array]',
int32Tag = '[object Int32Array]',
uint8Tag = '[object Uint8Array]',
uint8ClampedTag = '[object Uint8ClampedArray]',
uint16Tag = '[object Uint16Array]',
uint32Tag = '[object Uint32Array]';
/**
* Used to match `RegExp`
* [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
*/
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
/** Used to match `RegExp` flags from their coerced string values. */
var reFlags = /\w*$/;
/** Used to detect host constructors (Safari). */
var reIsHostCtor = /^\[object .+?Constructor\]$/;
/** Used to detect unsigned integer values. */
var reIsUint = /^(?:0|[1-9]\d*)$/;
/** Used to identify `toStringTag` values of typed arrays. */
var typedArrayTags = {};
typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
typedArrayTags[uint32Tag] = true;
typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
typedArrayTags[errorTag] = typedArrayTags[funcTag] =
typedArrayTags[mapTag] = typedArrayTags[numberTag] =
typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
typedArrayTags[setTag] = typedArrayTags[stringTag] =
typedArrayTags[weakMapTag] = false;
/** Used to identify `toStringTag` values supported by `_.clone`. */
var cloneableTags = {};
cloneableTags[argsTag] = cloneableTags[arrayTag] =
cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
cloneableTags[boolTag] = cloneableTags[dateTag] =
cloneableTags[float32Tag] = cloneableTags[float64Tag] =
cloneableTags[int8Tag] = cloneableTags[int16Tag] =
cloneableTags[int32Tag] = cloneableTags[mapTag] =
cloneableTags[numberTag] = cloneableTags[objectTag] =
cloneableTags[regexpTag] = cloneableTags[setTag] =
cloneableTags[stringTag] = cloneableTags[symbolTag] =
cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
cloneableTags[errorTag] = cloneableTags[funcTag] =
cloneableTags[weakMapTag] = false;
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();
/** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;
/** Detect free variable `process` from Node.js. */
var freeProcess = moduleExports && freeGlobal.process;
/** Used to access faster Node.js helpers. */
var nodeUtil = (function() {
try {
return freeProcess && freeProcess.binding('util');
} catch (e) {}
}());
/* Node.js helper references. */
var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
/**
* Adds the key-value `pair` to `map`.
*
* @private
* @param {Object} map The map to modify.
* @param {Array} pair The key-value pair to add.
* @returns {Object} Returns `map`.
*/
function addMapEntry(map, pair) {
// Don't return `map.set` because it's not chainable in IE 11.
map.set(pair[0], pair[1]);
return map;
}
/**
* Adds `value` to `set`.
*
* @private
* @param {Object} set The set to modify.
* @param {*} value The value to add.
* @returns {Object} Returns `set`.
*/
function addSetEntry(set, value) {
// Don't return `set.add` because it's not chainable in IE 11.
set.add(value);
return set;
}
/**
* A faster alternative to `Function#apply`, this function invokes `func`
* with the `this` binding of `thisArg` and the arguments of `args`.
*
* @private
* @param {Function} func The function to invoke.
* @param {*} thisArg The `this` binding of `func`.
* @param {Array} args The arguments to invoke `func` with.
* @returns {*} Returns the result of `func`.
*/
function apply(func, thisArg, args) {
switch (args.length) {
case 0: return func.call(thisArg);
case 1: return func.call(thisArg, args[0]);
case 2: return func.call(thisArg, args[0], args[1]);
case 3: return func.call(thisArg, args[0], args[1], args[2]);
}
return func.apply(thisArg, args);
}
/**
* A specialized version of `_.forEach` for arrays without support for
* iteratee shorthands.
*
* @private
* @param {Array} [array] The array to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Array} Returns `array`.
*/
function arrayEach(array, iteratee) {
var index = -1,
length = array ? array.length : 0;
while (++index < length) {
if (iteratee(array[index], index, array) === false) {
break;
}
}
return array;
}
/**
* Appends the elements of `values` to `array`.
*
* @private
* @param {Array} array The array to modify.
* @param {Array} values The values to append.
* @returns {Array} Returns `array`.
*/
function arrayPush(array, values) {
var index = -1,
length = values.length,
offset = array.length;
while (++index < length) {
array[offset + index] = values[index];
}
return array;
}
/**
* A specialized version of `_.reduce` for arrays without support for
* iteratee shorthands.
*
* @private
* @param {Array} [array] The array to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @param {*} [accumulator] The initial value.
* @param {boolean} [initAccum] Specify using the first element of `array` as
* the initial value.
* @returns {*} Returns the accumulated value.
*/
function arrayReduce(array, iteratee, accumulator, initAccum) {
var index = -1,
length = array ? array.length : 0;
if (initAccum && length) {
accumulator = array[++index];
}
while (++index < length) {
accumulator = iteratee(accumulator, array[index], index, array);
}
return accumulator;
}
/**
* The base implementation of `_.times` without support for iteratee shorthands
* or max array length checks.
*
* @private
* @param {number} n The number of times to invoke `iteratee`.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Array} Returns the array of results.
*/
function baseTimes(n, iteratee) {
var index = -1,
result = Array(n);
while (++index < n) {
result[index] = iteratee(index);
}
return result;
}
/**
* The base implementation of `_.unary` without support for storing metadata.
*
* @private
* @param {Function} func The function to cap arguments for.
* @returns {Function} Returns the new capped function.
*/
function baseUnary(func) {
return function(value) {
return func(value);
};
}
/**
* Gets the value at `key` of `object`.
*
* @private
* @param {Object} [object] The object to query.
* @param {string} key The key of the property to get.
* @returns {*} Returns the property value.
*/
function getValue(object, key) {
return object == null ? undefined : object[key];
}
/**
* Checks if `value` is a host object in IE < 9.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a host object, else `false`.
*/
function isHostObject(value) {
// Many host objects are `Object` objects that can coerce to strings
// despite having improperly defined `toString` methods.
var result = false;
if (value != null && typeof value.toString != 'function') {
try {
result = !!(value + '');
} catch (e) {}
}
return result;
}
/**
* Converts `map` to its key-value pairs.
*
* @private
* @param {Object} map The map to convert.
* @returns {Array} Returns the key-value pairs.
*/
function mapToArray(map) {
var index = -1,
result = Array(map.size);
map.forEach(function(value, key) {
result[++index] = [key, value];
});
return result;
}
/**
* Creates a unary function that invokes `func` with its argument transformed.
*
* @private
* @param {Function} func The function to wrap.
* @param {Function} transform The argument transform.
* @returns {Function} Returns the new function.
*/
function overArg(func, transform) {
return function(arg) {
return func(transform(arg));
};
}
/**
* Converts `set` to an array of its values.
*
* @private
* @param {Object} set The set to convert.
* @returns {Array} Returns the values.
*/
function setToArray(set) {
var index = -1,
result = Array(set.size);
set.forEach(function(value) {
result[++index] = value;
});
return result;
}
/** Used for built-in method references. */
var arrayProto = Array.prototype,
funcProto = Function.prototype,
objectProto = Object.prototype;
/** Used to detect overreaching core-js shims. */
var coreJsData = root['__core-js_shared__'];
/** Used to detect methods masquerading as native. */
var maskSrcKey = (function() {
var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
return uid ? ('Symbol(src)_1.' + uid) : '';
}());
/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;
/** Used to infer the `Object` constructor. */
var objectCtorString = funcToString.call(Object);
/**
* Used to resolve the
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
* of values.
*/
var objectToString = objectProto.toString;
/** Used to detect if a method is native. */
var reIsNative = RegExp('^' +
funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
.replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);
/** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined,
Symbol = root.Symbol,
Uint8Array = root.Uint8Array,
getPrototype = overArg(Object.getPrototypeOf, Object),
objectCreate = Object.create,
propertyIsEnumerable = objectProto.propertyIsEnumerable,
splice = arrayProto.splice;
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeGetSymbols = Object.getOwnPropertySymbols,
nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,
nativeKeys = overArg(Object.keys, Object),
nativeMax = Math.max;
/* Built-in method references that are verified to be native. */
var DataView = getNative(root, 'DataView'),
Map = getNative(root, 'Map'),
Promise = getNative(root, 'Promise'),
Set = getNative(root, 'Set'),
WeakMap = getNative(root, 'WeakMap'),
nativeCreate = getNative(Object, 'create');
/** Used to detect maps, sets, and weakmaps. */
var dataViewCtorString = toSource(DataView),
mapCtorString = toSource(Map),
promiseCtorString = toSource(Promise),
setCtorString = toSource(Set),
weakMapCtorString = toSource(WeakMap);
/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;
/**
* Creates a hash object.
*
* @private
* @constructor
* @param {Array} [entries] The key-value pairs to cache.
*/
function Hash(entries) {
var index = -1,
length = entries ? entries.length : 0;
this.clear();
while (++index < length) {
var entry = entries[index];
this.set(entry[0], entry[1]);
}
}
/**
* Removes all key-value entries from the hash.
*
* @private
* @name clear
* @memberOf Hash
*/
function hashClear() {
this.__data__ = nativeCreate ? nativeCreate(null) : {};
}
/**
* Removes `key` and its value from the hash.
*
* @private
* @name delete
* @memberOf Hash
* @param {Object} hash The hash to modify.
* @param {string} key The key of the value to remove.
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
*/
function hashDelete(key) {
return this.has(key) && delete this.__data__[key];
}
/**
* Gets the hash value for `key`.
*
* @private
* @name get
* @memberOf Hash
* @param {string} key The key of the value to get.
* @returns {*} Returns the entry value.
*/
function hashGet(key) {
var data = this.__data__;
if (nativeCreate) {
var result = data[key];
return result === HASH_UNDEFINED ? undefined : result;
}
return hasOwnProperty.call(data, key) ? data[key] : undefined;
}
/**
* Checks if a hash value for `key` exists.
*
* @private
* @name has
* @memberOf Hash
* @param {string} key The key of the entry to check.
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
*/
function hashHas(key) {
var data = this.__data__;
return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
}
/**
* Sets the hash `key` to `value`.
*
* @private
* @name set
* @memberOf Hash
* @param {string} key The key of the value to set.
* @param {*} value The value to set.
* @returns {Object} Returns the hash instance.
*/
function hashSet(key, value) {
var data = this.__data__;
data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
return this;
}
// Add methods to `Hash`.
Hash.prototype.clear = hashClear;
Hash.prototype['delete'] = hashDelete;
Hash.prototype.get = hashGet;
Hash.prototype.has = hashHas;
Hash.prototype.set = hashSet;
/**
* Creates an list cache object.
*
* @private
* @constructor
* @param {Array} [entries] The key-value pairs to cache.
*/
function ListCache(entries) {
var index = -1,
length = entries ? entries.length : 0;
this.clear();
while (++index < length) {
var entry = entries[index];
this.set(entry[0], entry[1]);
}
}
/**
* Removes all key-value entries from the list cache.
*
* @private
* @name clear
* @memberOf ListCache
*/
function listCacheClear() {
this.__data__ = [];
}
/**
* Removes `key` and its value from the list cache.
*
* @private
* @name delete
* @memberOf ListCache
* @param {string} key The key of the value to remove.
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
*/
function listCacheDelete(key) {
var data = this.__data__,
index = assocIndexOf(data, key);
if (index < 0) {
return false;
}
var lastIndex = data.length - 1;
if (index == lastIndex) {
data.pop();
} else {
splice.call(data, index, 1);
}
return true;
}
/**
* Gets the list cache value for `key`.
*
* @private
* @name get
* @memberOf ListCache
* @param {string} key The key of the value to get.
* @returns {*} Returns the entry value.
*/
function listCacheGet(key) {
var data = this.__data__,
index = assocIndexOf(data, key);
return index < 0 ? undefined : data[index][1];
}
/**
* Checks if a list cache value for `key` exists.
*
* @private
* @name has
* @memberOf ListCache
* @param {string} key The key of the entry to check.
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
*/
function listCacheHas(key) {
return assocIndexOf(this.__data__, key) > -1;
}
/**
* Sets the list cache `key` to `value`.
*
* @private
* @name set
* @memberOf ListCache
* @param {string} key The key of the value to set.
* @param {*} value The value to set.
* @returns {Object} Returns the list cache instance.
*/
function listCacheSet(key, value) {
var data = this.__data__,
index = assocIndexOf(data, key);
if (index < 0) {
data.push([key, value]);
} else {
data[index][1] = value;
}
return this;
}
// Add methods to `ListCache`.
ListCache.prototype.clear = listCacheClear;
ListCache.prototype['delete'] = listCacheDelete;
ListCache.prototype.get = listCacheGet;
ListCache.prototype.has = listCacheHas;
ListCache.prototype.set = listCacheSet;
/**
* Creates a map cache object to store key-value pairs.
*
* @private
* @constructor
* @param {Array} [entries] The key-value pairs to cache.
*/
function MapCache(entries) {
var index = -1,
length = entries ? entries.length : 0;
this.clear();
while (++index < length) {
var entry = entries[index];
this.set(entry[0], entry[1]);
}
}
/**
* Removes all key-value entries from the map.
*
* @private
* @name clear
* @memberOf MapCache
*/
function mapCacheClear() {
this.__data__ = {
'hash': new Hash,
'map': new (Map || ListCache),
'string': new Hash
};
}
/**
* Removes `key` and its value from the map.
*
* @private
* @name delete
* @memberOf MapCache
* @param {string} key The key of the value to remove.
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
*/
function mapCacheDelete(key) {
return getMapData(this, key)['delete'](key);
}
/**
* Gets the map value for `key`.
*
* @private
* @name get
* @memberOf MapCache
* @param {string} key The key of the value to get.
* @returns {*} Returns the entry value.
*/
function mapCacheGet(key) {
return getMapData(this, key).get(key);
}
/**
* Checks if a map value for `key` exists.
*
* @private
* @name has
* @memberOf MapCache
* @param {string} key The key of the entry to check.
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
*/
function mapCacheHas(key) {
return getMapData(this, key).has(key);
}
/**
* Sets the map `key` to `value`.
*
* @private
* @name set
* @memberOf MapCache
* @param {string} key The key of the value to set.
* @param {*} value The value to set.
* @returns {Object} Returns the map cache instance.
*/
function mapCacheSet(key, value) {
getMapData(this, key).set(key, value);
return this;
}
// Add methods to `MapCache`.
MapCache.prototype.clear = mapCacheClear;
MapCache.prototype['delete'] = mapCacheDelete;
MapCache.prototype.get = mapCacheGet;
MapCache.prototype.has = mapCacheHas;
MapCache.prototype.set = mapCacheSet;
/**
* Creates a stack cache object to store key-value pairs.
*
* @private
* @constructor
* @param {Array} [entries] The key-value pairs to cache.
*/
function Stack(entries) {
this.__data__ = new ListCache(entries);
}
/**
* Removes all key-value entries from the stack.
*
* @private
* @name clear
* @memberOf Stack
*/
function stackClear() {
this.__data__ = new ListCache;
}
/**
* Removes `key` and its value from the stack.
*
* @private
* @name delete
* @memberOf Stack
* @param {string} key The key of the value to remove.
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
*/
function stackDelete(key) {
return this.__data__['delete'](key);
}
/**
* Gets the stack value for `key`.
*
* @private
* @name get
* @memberOf Stack
* @param {string} key The key of the value to get.
* @returns {*} Returns the entry value.
*/
function stackGet(key) {
return this.__data__.get(key);
}
/**
* Checks if a stack value for `key` exists.
*
* @private
* @name has
* @memberOf Stack
* @param {string} key The key of the entry to check.
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
*/
function stackHas(key) {
return this.__data__.has(key);
}
/**
* Sets the stack `key` to `value`.
*
* @private
* @name set
* @memberOf Stack
* @param {string} key The key of the value to set.
* @param {*} value The value to set.
* @returns {Object} Returns the stack cache instance.
*/
function stackSet(key, value) {
var cache = this.__data__;
if (cache instanceof ListCache) {
var pairs = cache.__data__;
if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
pairs.push([key, value]);
return this;
}
cache = this.__data__ = new MapCache(pairs);
}
cache.set(key, value);
return this;
}
// Add methods to `Stack`.
Stack.prototype.clear = stackClear;
Stack.prototype['delete'] = stackDelete;
Stack.prototype.get = stackGet;
Stack.prototype.has = stackHas;
Stack.prototype.set = stackSet;
/**
* Creates an array of the enumerable property names of the array-like `v