vendor/assets/javascripts/holder.js in holder_rails-2.6.1 vs vendor/assets/javascripts/holder.js in holder_rails-2.7.0

- old
+ new

@@ -1,230 +1,235 @@ /*! Holder - client side image placeholders -Version 2.6.1+6bti3 +Version 2.7.0+6h7nj © 2015 Ivan Malopinsky - http://imsky.co Site: http://holderjs.com Issues: https://github.com/imsky/holder/issues License: http://opensource.org/licenses/MIT */ -//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 = []; +(function (window) { + if (!window.document) return; + var document = window.document; - style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}'; - window.scrollBy(0, 0); - style.parentNode.removeChild(style); + //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 = []; - while (document._qsa.length) { - element = document._qsa.shift(); - element.style.removeAttribute('x-qsa'); - elements.push(element); - } - document._qsa = null; - return elements; - }; - } + style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}'; + window.scrollBy(0, 0); + style.parentNode.removeChild(style); - if (!document.querySelector) { - document.querySelector = function (selectors) { - var elements = document.querySelectorAll(selectors); - return (elements.length) ? elements[0] : null; - }; - } + while (document._qsa.length) { + element = document._qsa.shift(); + element.style.removeAttribute('x-qsa'); + elements.push(element); + } + document._qsa = null; + return elements; + }; + } - if (!document.getElementsByClassName) { - document.getElementsByClassName = function (classNames) { - classNames = String(classNames).replace(/^|\s+/g, '.'); - return document.querySelectorAll(classNames); + 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; }; } -//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; - }; -} + //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; -//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'); } - 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; - 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 === 24) { - output.push(String.fromCharCode((buffer >> 16) & 0xFF)); - output.push(String.fromCharCode((buffer >> 8) & 0xFF)); + if (bits === 12) { + buffer = buffer >> 4; output.push(String.fromCharCode(buffer & 0xFF)); - bits = 0; - buffer = 0; + } else if (bits === 18) { + buffer = buffer >> 2; + output.push(String.fromCharCode((buffer >> 8) & 0xFF)); + output.push(String.fromCharCode(buffer & 0xFF)); } - 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(''); + }; - return output.join(''); - }; + global.btoa = global.btoa || function (input) { + input = String(input); + var position = 0, + out = [], + o1, o2, o3, + e1, e2, e3, e4; - 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'); } - if (/[^\x00-\xFF]/.test(input)) { throw Error('InvalidCharacterError'); } + while (position < input.length) { + o1 = input.charCodeAt(position++); + o2 = input.charCodeAt(position++); + o3 = input.charCodeAt(position++); - 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; - // 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; + } - if (position === input.length + 2) { - e3 = 64; e4 = 64; + out.push(B64_ALPHABET.charAt(e1), + B64_ALPHABET.charAt(e2), + B64_ALPHABET.charAt(e3), + B64_ALPHABET.charAt(e4)); } - 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)); - return out.join(''); - }; -}(this)); + //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 */ + } -//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 -// @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 -// 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 -// 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(){ -(function(){ + if ('performance' in window === false) { + window.performance = {}; + } + + Date.now = (Date.now || function () { // thanks IE8 + return new Date().getTime(); + }); - 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; + } - 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; + }; } - window.performance.now = function now(){ - return Date.now() - nowOffset; - }; - } + })(); -})(); + //requestAnimationFrame polyfill for older Firefox/Chrome versions + if (!window.requestAnimationFrame) { + if (window.webkitRequestAnimationFrame) { + //https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/requestAnimationFrame/polyfill-webkit.js + (function (global) { + // window.requestAnimationFrame + global.requestAnimationFrame = function (callback) { + return webkitRequestAnimationFrame(function () { + callback(global.performance.now()); + }); + }; -//requestAnimationFrame polyfill for older Firefox/Chrome versions -if (!window.requestAnimationFrame) { - if (window.webkitRequestAnimationFrame) { - //https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/requestAnimationFrame/polyfill-webkit.js - (function (global) { - // window.requestAnimationFrame - global.requestAnimationFrame = function (callback) { - return webkitRequestAnimationFrame(function () { - callback(global.performance.now()); - }); - }; + // window.cancelAnimationFrame + global.cancelAnimationFrame = webkitCancelAnimationFrame; + }(window)); + } else if (window.mozRequestAnimationFrame) { + //https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/requestAnimationFrame/polyfill-moz.js + (function (global) { + // window.requestAnimationFrame + global.requestAnimationFrame = function (callback) { + return mozRequestAnimationFrame(function () { + callback(global.performance.now()); + }); + }; - // window.cancelAnimationFrame - global.cancelAnimationFrame = webkitCancelAnimationFrame; - }(this)); - } else if (window.mozRequestAnimationFrame) { - //https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/requestAnimationFrame/polyfill-moz.js - (function (global) { - // window.requestAnimationFrame - global.requestAnimationFrame = function (callback) { - return mozRequestAnimationFrame(function () { - callback(global.performance.now()); - }); - }; + // window.cancelAnimationFrame + global.cancelAnimationFrame = mozCancelAnimationFrame; + }(window)); + } else { + (function (global) { + global.requestAnimationFrame = function (callback) { + return global.setTimeout(callback, 1000 / 60); + }; - // window.cancelAnimationFrame - global.cancelAnimationFrame = mozCancelAnimationFrame; - }(this)); - } else { - (function (global) { - global.requestAnimationFrame = function (callback) { - return global.setTimeout(callback, 1000 / 60); - }; - - global.cancelAnimationFrame = global.clearTimeout; - })(this); + 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) @@ -287,23 +292,20 @@ //Libraries and functions var onDomReady = __webpack_require__(1); var SceneGraph = __webpack_require__(2); var utils = __webpack_require__(3); + var querystring = __webpack_require__(4); var extend = utils.extend; - var cssProps = utils.cssProps; - var encodeHtmlEntity = utils.encodeHtmlEntity; - var decodeHtmlEntity = utils.decodeHtmlEntity; - var imageExists = utils.imageExists; var getNodeArray = utils.getNodeArray; var dimensionCheck = utils.dimensionCheck; //Constants and definitions var SVG_NS = 'http://www.w3.org/2000/svg'; var NODE_TYPE_COMMENT = 8; - var version = '2.6.1'; + var version = '2.7.0'; var generatorComment = '\n' + 'Created with Holder.js ' + version + '.\n' + 'Learn more at http://holderjs.com\n' + '(c) 2012-2015 Ivan Malopinsky - http://imsky.co\n'; @@ -479,11 +481,11 @@ //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) { - imageExists(src, function(exists) { + utils.imageExists(src, function(exists) { if (!exists) { prepareImageElement(options, engineSettings, dataSrc, image); } }); })(imageAttr.src, options, engineSettings, imageAttr.dataSrc, image); @@ -536,10 +538,11 @@ defaults: { size: 10, units: 'pt', scale: 1 / 16 }, + //todo: remove in 2.8 flags: { dimensions: { regex: /^(\d+)x(\d+)$/, output: function(val) { var exec = this.regex.exec(val); @@ -622,26 +625,128 @@ }); } } /** - * Processes a Holder URL and extracts flags + * Processes a Holder URL * * @private * @param url URL * @param options Instance options from Holder.run */ function parseURL(url, options) { - var ret = { + var holder = { theme: extend(App.settings.themes.gray, null), stylesheets: options.stylesheets, - holderURL: [] + instanceOptions: options }; + + if (url.match(/([\d]+p?)x([\d]+p?)(?:\?|$)/)) { + return parseQueryString(url, holder); + } else { + return parseFlags(url, holder); + } + } + + /** + * Processes a Holder URL and extracts configuration from query string + * + * @private + * @param url URL + * @param holder Staging Holder object + */ + function parseQueryString(url, holder) { + var parts = url.split('?'); + var basics = parts[0].split('/'); + + holder.holderURL = url; + + var dimensions = basics[1]; + var dimensionData = dimensions.match(/([\d]+p?)x([\d]+p?)/); + + if (!dimensionData) return false; + + holder.fluid = dimensions.indexOf('p') !== -1; + + holder.dimensions = { + width: dimensionData[1].replace('p', '%'), + height: dimensionData[2].replace('p', '%') + }; + + if (parts.length === 2) { + var options = querystring.parse(parts[1]); + + // Colors + + if (options.bg) { + holder.theme.background = (options.bg.indexOf('#') === -1 ? '#' : '') + options.bg; + } + + if (options.fg) { + holder.theme.foreground = (options.fg.indexOf('#') === -1 ? '#' : '') + options.fg; + } + + if (options.theme && holder.instanceOptions.themes.hasOwnProperty(options.theme)) { + holder.theme = extend(holder.instanceOptions.themes[options.theme], null); + } + + // Text + + if (options.text) { + holder.text = options.text; + } + + if (options.textmode) { + holder.textmode = options.textmode; + } + + if (options.size) { + holder.size = options.size; + } + + if (options.font) { + holder.font = options.font; + } + + if (options.align) { + holder.align = options.align; + } + + holder.nowrap = utils.truthy(options.nowrap); + + // Miscellaneous + + holder.auto = utils.truthy(options.auto); + + 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; + } + + //todo: remove in 2.8 + /** + * Processes a Holder URL and extracts flags + * + * @private + * @deprecated + * @param url URL + * @param holder Staging Holder object + */ + function parseFlags(url, holder) { var render = false; var vtab = String.fromCharCode(11); var flags = url.replace(/([^\\])\//g, '$1' + vtab).split(vtab); var uriRegex = /%[0-9a-f]{2}/gi; + var options = holder.instanceOptions; + + holder.holderURL = []; + for (var fl = flags.length, j = 0; j < fl; j++) { var flag = flags[j]; if (flag.match(uriRegex)) { try { flag = decodeURIComponent(flag); @@ -652,59 +757,58 @@ var push = false; if (App.flags.dimensions.match(flag)) { render = true; - ret.dimensions = App.flags.dimensions.output(flag); + holder.dimensions = App.flags.dimensions.output(flag); push = true; } else if (App.flags.fluid.match(flag)) { render = true; - ret.dimensions = App.flags.fluid.output(flag); - ret.fluid = true; + holder.dimensions = App.flags.fluid.output(flag); + holder.fluid = true; push = true; } else if (App.flags.textmode.match(flag)) { - ret.textmode = App.flags.textmode.output(flag); + holder.textmode = App.flags.textmode.output(flag); push = true; } else if (App.flags.colors.match(flag)) { var colors = App.flags.colors.output(flag); - ret.theme = extend(ret.theme, colors); - //todo: convert implicit theme use to a theme: flag + holder.theme = extend(holder.theme, colors); push = true; } else if (options.themes[flag]) { //If a theme is specified, it will override custom colors if (options.themes.hasOwnProperty(flag)) { - ret.theme = extend(options.themes[flag], null); + holder.theme = extend(options.themes[flag], null); } push = true; } else if (App.flags.font.match(flag)) { - ret.font = App.flags.font.output(flag); + holder.font = App.flags.font.output(flag); push = true; } else if (App.flags.auto.match(flag)) { - ret.auto = true; + holder.auto = true; push = true; } else if (App.flags.text.match(flag)) { - ret.text = App.flags.text.output(flag); + holder.text = App.flags.text.output(flag); push = true; } else if (App.flags.size.match(flag)) { - ret.size = App.flags.size.output(flag); + holder.size = App.flags.size.output(flag); push = true; } else if (App.flags.random.match(flag)) { if (App.vars.cache.themeKeys == null) { App.vars.cache.themeKeys = Object.keys(options.themes); } var theme = App.vars.cache.themeKeys[0 | Math.random() * App.vars.cache.themeKeys.length]; - ret.theme = extend(options.themes[theme], null); + holder.theme = extend(options.themes[theme], null); push = true; } if (push) { - ret.holderURL.push(flag); + holder.holderURL.push(flag); } } - ret.holderURL.unshift(options.domain); - ret.holderURL = ret.holderURL.join('/'); - return render ? ret : false; + holder.holderURL.unshift(options.domain); + holder.holderURL = holder.holderURL.join('/'); + return render ? holder : false; } /** * Modifies the DOM to fit placeholders and sets up resizable image callbacks (for fluid and automatically sized placeholders) * @@ -726,11 +830,11 @@ //<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] = encodeHtmlEntity(textLines[k]); + textLines[k] = utils.encodeHtmlEntity(textLines[k]); } theme.text = textLines.join('\\n'); } } @@ -879,10 +983,11 @@ image = sgSVGRenderer(sceneGraph, renderSettings); break; default: throw 'Holder: invalid renderer: ' + engineSettings.renderer; } + return image; } image = getRenderedImage(); @@ -939,10 +1044,12 @@ * Core function that takes a Holder scene description and builds a scene graph * * @private * @param scene Holder scene object */ + //todo: make this function reusable + //todo: merge app defaults and setup properties into the scene argument function buildSceneGraph(scene) { var fontSize = App.defaults.size; if (parseFloat(scene.theme.size)) { fontSize = scene.theme.size; } else if (parseFloat(scene.flags.size)) { @@ -953,12 +1060,17 @@ family: scene.theme.font ? scene.theme.font : 'Arial, Helvetica, Open Sans, sans-serif', size: textSize(scene.width, scene.height, fontSize), units: scene.theme.units ? scene.theme.units : App.defaults.units, weight: scene.theme.fontweight ? scene.theme.fontweight : 'bold' }; - scene.text = scene.theme.text ? scene.theme.text : Math.floor(scene.width) + 'x' + Math.floor(scene.height); + 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': @@ -981,11 +1093,11 @@ holderBg.resize(scene.width, scene.height); sceneGraph.root.add(holderBg); var holderTextGroup = new Shape.Group('holderTextGroup', { text: scene.text, - align: 'center', + align: scene.align, font: scene.font, fill: scene.theme.foreground }); holderTextGroup.moveTo(null, null, 1); @@ -995,35 +1107,42 @@ if (!tpdata) { throw 'Holder: staging fallback not supported yet.'; } holderTextGroup.properties.leading = tpdata.boundingBox.height; - //todo: alignment: TL, TC, TR, CL, CR, BL, BC, BR 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; - parent.add(line); } + var sceneMargin = scene.width * App.setup.lineWrapRatio; + var maxLineWidth = sceneMargin; + if (tpdata.lineCount > 1) { var offsetX = 0; var offsetY = 0; - var maxLineWidth = scene.width * App.setup.lineWrapRatio; 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 - (App.setup.lineWrapRatio)) * 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 (offsetX + word.width >= maxLineWidth || newline === true) { + 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; @@ -1035,22 +1154,31 @@ offsetX += tpdata.spaceWidth + word.width; line.add(textNode); } finalizeLine(holderTextGroup, line, offsetX, holderTextGroup.properties.leading); + holderTextGroup.add(line); - for (var lineKey in holderTextGroup.children) { - line = holderTextGroup.children[lineKey]; - line.moveTo( - (holderTextGroup.width - line.width) / 2, - null, - null); + 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( - (scene.width - holderTextGroup.width) / 2, (scene.height - holderTextGroup.height) / 2, - 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); } @@ -1058,13 +1186,19 @@ textNode = new Shape.Text(scene.text); line = new Shape.Group('line0'); line.add(textNode); holderTextGroup.add(line); - holderTextGroup.moveTo( - (scene.width - tpdata.boundingBox.width) / 2, (scene.height - tpdata.boundingBox.height) / 2, - null); + 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; @@ -1274,11 +1408,11 @@ var holderTextGroup = rootNode.children.holderTextGroup; var htgProps = holderTextGroup.properties; setAttr(stagingText, { 'y': htgProps.font.size, - 'style': cssProps({ + 'style': utils.cssProps({ 'font-weight': htgProps.font.weight, 'font-size': htgProps.font.size + htgProps.font.units, 'font-family': htgProps.font.family }) }); @@ -1305,11 +1439,11 @@ var wordWidths = []; if (lineCount > 1) { stagingTextNode.nodeValue = ''; for (var i = 0; i < words.length; i++) { if (words[i].length === 0) continue; - stagingTextNode.nodeValue = decodeHtmlEntity(words[i]); + stagingTextNode.nodeValue = utils.decodeHtmlEntity(words[i]); var bbox = stagingText.getBBox(); wordWidths.push({ text: words[i], width: bbox.width }); @@ -1395,11 +1529,11 @@ var textGroup = root.children.holderTextGroup; var tgProps = textGroup.properties; var textGroupEl = newEl('g', SVG_NS); var tpdata = textGroup.textPositionData; var textCSSRule = '#' + holderId + ' text { ' + - cssProps({ + utils.cssProps({ 'fill': tgProps.fill, 'font-weight': tgProps.font.weight, 'font-family': tgProps.font.family + ', monospace', 'font-size': tgProps.font.size + tgProps.font.units }) + ' } '; @@ -1445,19 +1579,39 @@ textEl.appendChild(textNode); textGroupEl.appendChild(textEl); } } - var svgString = 'data:image/svg+xml;base64,' + - btoa(unescape(encodeURIComponent(serializeSVG(svg, renderSettings.engineSettings)))); + //todo: factor the background check up the chain, perhaps only return reference + var svgString = svgStringToDataURI(serializeSVG(svg, renderSettings.engineSettings), renderSettings.mode === 'background'); return svgString; }; })(); //Helpers + //todo: move svg-related helpers to a dedicated file + /** + * Converts serialized SVG to a string suitable for data URI use + * @param svgString Serialized SVG string + * @param [base64] Use base64 encoding for data URI + */ + var 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(unescape(encodeURIComponent(svgString))); + } else { + return rawPrefix + encodeURIComponent(svgString); + } + }; + }(); + + /** * Generic new DOM element function * * @private * @param tag Tag to create * @param namespace Optional namespace value @@ -1554,16 +1708,10 @@ 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); } - //Add <?xml ... ?> UTF-8 directive - //todo: remove in 2.7 - /* - var xmlpi = xml.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8" standalone="yes"'); - xml.insertBefore(xmlpi, xml.firstChild); - */ xml.removeChild(xml.documentElement); svgCSS = serializer.serializeToString(xml); } var svgText = serializer.serializeToString(svg); @@ -1860,11 +2008,11 @@ /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { - var augment = __webpack_require__(4); + var augment = __webpack_require__(5); var SceneGraph = function(sceneProperties) { var nodeCount = 1; //todo: move merge to helpers section @@ -2081,11 +2229,10 @@ /** * Returns an element's dimensions if it's visible, `false` otherwise. * - * @private * @param el DOM element */ exports.dimensionCheck = function(el) { var dimensions = { height: el.clientHeight, @@ -2097,16 +2244,136 @@ } 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; + }; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) /***/ }, /* 4 */ /***/ 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__(7); + var type = __webpack_require__(6); + + 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('&'); + }; + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + var Factory = function () {}; var slice = Array.prototype.slice; var augment = function (base, body) { var uber = Factory.prototype = typeof base === "function" ? base.prototype : base; @@ -2131,9 +2398,74 @@ }); }; module.exports = augment; +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 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; + }; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + + 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*$/, ''); + }; + + /***/ } /******/ ]) }); -; \ No newline at end of file +; +(function(ctx, isMeteorPackage) { + if (isMeteorPackage) { + Holder = ctx.Holder; + } +})(this, Meteor && Package);