vendor/assets/javascripts/holder.js in holder_rails-2.7.1 vs vendor/assets/javascripts/holder.js in holder_rails-2.8.0
- old
+ new
@@ -1,14 +1,14 @@
/*!
Holder - client side image placeholders
-Version 2.7.1+6hydf
+Version 2.8.0+7srgw
© 2015 Ivan Malopinsky - http://imsky.co
Site: http://holderjs.com
Issues: https://github.com/imsky/holder/issues
-License: http://opensource.org/licenses/MIT
+License: MIT
*/
(function (window) {
if (!window.document) return;
var document = window.document;
@@ -283,29 +283,44 @@
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
+ /*
+ Holder.js - client side image placeholders
+ (c) 2012-2015 Ivan Malopinsky - http://imsky.co
+ */
+
+ module.exports = __webpack_require__(1);
+
+
+/***/ },
+/* 1 */
+/***/ function(module, exports, __webpack_require__) {
+
/* WEBPACK VAR INJECTION */(function(global) {/*
Holder.js - client side image placeholders
- © 2012-2015 Ivan Malopinsky - http://imsky.co
+ (c) 2012-2015 Ivan Malopinsky - http://imsky.co
*/
//Libraries and functions
- var onDomReady = __webpack_require__(1);
- var SceneGraph = __webpack_require__(2);
- var utils = __webpack_require__(3);
- var querystring = __webpack_require__(4);
+ var onDomReady = __webpack_require__(3);
+ var querystring = __webpack_require__(2);
+ var SceneGraph = __webpack_require__(4);
+ var utils = __webpack_require__(5);
+ var SVG = __webpack_require__(6);
+ var DOM = __webpack_require__(7);
+ var Color = __webpack_require__(8);
+
var extend = utils.extend;
- 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.7.1';
+ var version = '2.8.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';
@@ -326,20 +341,21 @@
/**
* Appends a placeholder to an element
*
* @param {string} src Placeholder URL string
- * @param {string} el Selector of target element(s)
+ * @param el A selector or a reference to a DOM node
*/
addImage: function(src, el) {
- var node = document.querySelectorAll(el);
+ //todo: use jquery fallback if available for all QSA references
+ var node = DOM.getNodeArray(el);
if (node.length) {
for (var i = 0, l = node.length; i < l; i++) {
- var img = newEl('img');
+ var img = DOM.newEl('img');
var domProps = {};
- domProps[App.vars.dataAttr] = src;
- setAttr(img, domProps);
+ domProps[App.setup.dataAttr] = src;
+ DOM.setAttr(img, domProps);
node[i].appendChild(img);
}
}
return this;
},
@@ -364,37 +380,39 @@
* 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.vars.dataAttr;
+ App.vars.dataAttr = options.dataAttr || App.setup.dataAttr;
+ App.vars.lineWrapRatio = options.lineWrapRatio || App.setup.lineWrapRatio;
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 = getNodeArray(options.images);
- var bgnodes = getNodeArray(options.bgnodes);
- var stylenodes = getNodeArray(options.stylenodes);
- var objects = getNodeArray(options.objects);
+ var images = DOM.getNodeArray(options.images);
+ var bgnodes = DOM.getNodeArray(options.bgnodes);
+ var stylenodes = DOM.getNodeArray(options.stylenodes);
+ var objects = DOM.getNodeArray(options.objects);
engineSettings.stylesheets = [];
engineSettings.svgXMLStylesheet = true;
engineSettings.noFontFallback = options.noFontFallback ? options.noFontFallback : false;
for (var i = 0; i < stylenodes.length; i++) {
var styleNode = stylenodes[i];
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 = newEl('a');
+ var proxyLink = DOM.newEl('a');
proxyLink.href = href;
var stylesheetURL = proxyLink.protocol + '//' + proxyLink.host + proxyLink.pathname + proxyLink.search;
engineSettings.stylesheets.push(stylesheetURL);
}
}
@@ -402,18 +420,12 @@
for (i = 0; i < bgnodes.length; i++) {
//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) continue;
var backgroundImage = global.getComputedStyle(bgnodes[i], null).getPropertyValue('background-image');
var dataBackgroundImage = bgnodes[i].getAttribute('data-background-src');
- var rawURL = null;
+ var rawURL = dataBackgroundImage || backgroundImage;
- if (dataBackgroundImage == null) {
- rawURL = backgroundImage;
- } else {
- rawURL = dataBackgroundImage;
- }
-
var holderURL = null;
var holderString = '?' + options.domain + '/';
if (rawURL.indexOf(holderString) === 0) {
holderURL = rawURL.slice(1);
@@ -505,11 +517,10 @@
domain: 'holder.js',
images: 'img',
objects: 'object',
bgnodes: 'body .holderjs',
stylenodes: 'head link.holderjs',
- stylesheets: [],
themes: {
'gray': {
background: '#EEEEEE',
foreground: '#AAAAAA'
},
@@ -537,73 +548,10 @@
},
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);
- return {
- width: +exec[1],
- height: +exec[2]
- };
- }
- },
- fluid: {
- regex: /^([0-9]+%?)x([0-9]+%?)$/,
- output: function(val) {
- var exec = this.regex.exec(val);
- return {
- width: exec[1],
- height: exec[2]
- };
- }
- },
- colors: {
- regex: /(?:#|\^)([0-9a-f]{3,})\:(?:#|\^)([0-9a-f]{3,})/i,
- output: function(val) {
- var exec = this.regex.exec(val);
- return {
- foreground: '#' + exec[2],
- background: '#' + exec[1]
- };
- }
- },
- text: {
- regex: /text\:(.*)/,
- output: function(val) {
- return this.regex.exec(val)[1].replace('\\/', '/');
- }
- },
- font: {
- regex: /font\:(.*)/,
- output: function(val) {
- return this.regex.exec(val)[1];
- }
- },
- auto: {
- regex: /^auto$/
- },
- textmode: {
- regex: /textmode\:(.*)/,
- output: function(val) {
- return this.regex.exec(val)[1];
- }
- },
- random: {
- regex: /^random$/
- },
- size: {
- regex: /size\:(\d+)/,
- output: function(val) {
- return this.regex.exec(val)[1];
- }
- }
}
};
/**
* Processes provided source attribute and sets up the appropriate rendering workflow
@@ -638,15 +586,11 @@
theme: extend(App.settings.themes.gray, null),
stylesheets: options.stylesheets,
instanceOptions: options
};
- if (url.match(/([\d]+p?)x([\d]+p?)(?:\?|$)/)) {
- return parseQueryString(url, holder);
- } else {
- return parseFlags(url, holder);
- }
+ return parseQueryString(url, holder);
}
/**
* Processes a Holder URL and extracts configuration from query string
*
@@ -683,10 +627,15 @@
if (options.fg) {
holder.theme.foreground = (options.fg.indexOf('#') === -1 ? '#' : '') + options.fg;
}
+ //todo: add automatic foreground to themes without foreground
+ if (options.bg && !options.fg) {
+ holder.autoFg = true;
+ }
+
if (options.theme && holder.instanceOptions.themes.hasOwnProperty(options.theme)) {
holder.theme = extend(holder.instanceOptions.themes[options.theme], null);
}
// Text
@@ -715,103 +664,23 @@
// Miscellaneous
holder.auto = utils.truthy(options.auto);
+ holder.outline = utils.truthy(options.outline);
+
if (utils.truthy(options.random)) {
App.vars.cache.themeKeys = App.vars.cache.themeKeys || Object.keys(holder.instanceOptions.themes);
var _theme = App.vars.cache.themeKeys[0 | Math.random() * App.vars.cache.themeKeys.length];
holder.theme = extend(holder.instanceOptions.themes[_theme], null);
}
}
return holder;
}
- //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);
- } catch (e) {
- flag = flags[j];
- }
- }
-
- var push = false;
-
- if (App.flags.dimensions.match(flag)) {
- render = true;
- holder.dimensions = App.flags.dimensions.output(flag);
- push = true;
- } else if (App.flags.fluid.match(flag)) {
- render = true;
- holder.dimensions = App.flags.fluid.output(flag);
- holder.fluid = true;
- push = true;
- } else if (App.flags.textmode.match(flag)) {
- holder.textmode = App.flags.textmode.output(flag);
- push = true;
- } else if (App.flags.colors.match(flag)) {
- var colors = App.flags.colors.output(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)) {
- holder.theme = extend(options.themes[flag], null);
- }
- push = true;
- } else if (App.flags.font.match(flag)) {
- holder.font = App.flags.font.output(flag);
- push = true;
- } else if (App.flags.auto.match(flag)) {
- holder.auto = true;
- push = true;
- } else if (App.flags.text.match(flag)) {
- holder.text = App.flags.text.output(flag);
- push = true;
- } else if (App.flags.size.match(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];
- holder.theme = extend(options.themes[theme], null);
- push = true;
- }
-
- if (push) {
- holder.holderURL.push(flag);
- }
- }
- 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)
*
* @private
* @param settings DOM prep settings
*/
@@ -856,18 +725,18 @@
engineSettings.reRender = true;
}
if (mode == 'background') {
if (el.getAttribute('data-background-src') == null) {
- setAttr(el, {
+ DOM.setAttr(el, {
'data-background-src': holderURL
});
}
} else {
var domProps = {};
domProps[App.vars.dataAttr] = holderURL;
- setAttr(el, domProps);
+ DOM.setAttr(el, domProps);
}
flags.theme = theme;
//todo consider using all renderSettings in holderData
@@ -875,11 +744,11 @@
flags: flags,
engineSettings: engineSettings
};
if (mode == 'image' || mode == 'fluid') {
- setAttr(el, {
+ DOM.setAttr(el, {
'alt': (theme.text ? theme.text + ' [' + dimensionsCaption + ']' : dimensionsCaption)
});
}
var renderSettings = {
@@ -946,12 +815,12 @@
* @param renderSettings Renderer settings
*/
function render(renderSettings) {
var image = null;
var mode = renderSettings.mode;
- var holderSettings = renderSettings.holderSettings;
var el = renderSettings.el;
+ var holderSettings = renderSettings.holderSettings;
var engineSettings = renderSettings.engineSettings;
switch (engineSettings.renderer) {
case 'svg':
if (!App.setup.supportsSVG) return;
@@ -999,18 +868,18 @@
if (mode == 'background') {
el.style.backgroundImage = 'url(' + image + ')';
el.style.backgroundSize = scene.width + 'px ' + scene.height + 'px';
} else {
if (el.nodeName.toLowerCase() === 'img') {
- setAttr(el, {
+ DOM.setAttr(el, {
'src': image
});
} else if (el.nodeName.toLowerCase() === 'object') {
- setAttr(el, {
+ DOM.setAttr(el, {
'data': image
});
- setAttr(el, {
+ DOM.setAttr(el, {
'type': 'image/svg+xml'
});
}
if (engineSettings.reRender) {
global.setTimeout(function() {
@@ -1018,26 +887,26 @@
if (image == null) {
throw 'Holder: couldn\'t render placeholder';
}
//todo: refactor this code into a function
if (el.nodeName.toLowerCase() === 'img') {
- setAttr(el, {
+ DOM.setAttr(el, {
'src': image
});
} else if (el.nodeName.toLowerCase() === 'object') {
- setAttr(el, {
+ DOM.setAttr(el, {
'data': image
});
- setAttr(el, {
+ DOM.setAttr(el, {
'type': 'image/svg+xml'
});
}
- }, 100);
+ }, 150);
}
}
//todo: account for re-rendering
- setAttr(el, {
+ DOM.setAttr(el, {
'data-holder-rendered': true
});
}
/**
@@ -1091,15 +960,37 @@
});
holderBg.resize(scene.width, scene.height);
sceneGraph.root.add(holderBg);
+ if (scene.flags.outline) {
+ //todo: generalize darken/lighten to more than RRGGBB hex values
+ 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.foreground;
+
+ 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: scene.theme.foreground
+ fill: holderTextColor
});
holderTextGroup.moveTo(null, null, 1);
sceneGraph.root.add(holderTextGroup);
@@ -1117,11 +1008,11 @@
line.height = height;
parent.width = Math.max(parent.width, line.width);
parent.height += line.height;
}
- var sceneMargin = scene.width * App.setup.lineWrapRatio;
+ var sceneMargin = scene.width * App.vars.lineWrapRatio;
var maxLineWidth = sceneMargin;
if (tpdata.lineCount > 1) {
var offsetX = 0;
var offsetY = 0;
@@ -1129,11 +1020,11 @@
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);
+ maxLineWidth = scene.width * (1 - (1 - (App.vars.lineWrapRatio)) * 2);
}
for (var i = 0; i < tpdata.words.length; i++) {
var word = tpdata.words[i];
textNode = new Shape.Text(word.text);
@@ -1198,11 +1089,10 @@
holderTextGroup.moveTo(null, (scene.height - tpdata.boundingBox.height) / 2, null);
}
//todo: renderlist
-
return sceneGraph;
}
/**
* Adaptive text sizing function
@@ -1382,18 +1272,18 @@
};
if (svg == null || svg.parentNode !== document.body) {
firstTimeSetup = true;
}
- svg = initSVG(svg, rootNode.properties.width, rootNode.properties.height);
+ svg = SVG.initSVG(svg, rootNode.properties.width, rootNode.properties.height);
//Show staging element before staging
svg.style.display = 'block';
if (firstTimeSetup) {
- stagingText = newEl('text', SVG_NS);
+ stagingText = DOM.newEl('text', SVG_NS);
stagingTextNode = tnode(null);
- setAttr(stagingText, {
+ DOM.setAttr(stagingText, {
x: 0
});
stagingText.appendChild(stagingTextNode);
svg.appendChild(stagingText);
document.body.appendChild(svg);
@@ -1406,11 +1296,11 @@
//svg.setAttribute('height', 0);
}
var holderTextGroup = rootNode.children.holderTextGroup;
var htgProps = holderTextGroup.properties;
- setAttr(stagingText, {
+ 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
@@ -1420,11 +1310,11 @@
//Get bounding box for the whole string (total width and height)
stagingTextNode.nodeValue = htgProps.text;
var stagingTextBBox = stagingText.getBBox();
//Get line count and split the string into words
- var lineCount = Math.ceil(stagingTextBBox.width / (rootNode.properties.width * App.setup.lineWrapRatio));
+ var lineCount = Math.ceil(stagingTextBBox.width / (rootNode.properties.width * App.vars.lineWrapRatio));
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
@@ -1465,11 +1355,11 @@
}
};
})();
var sgCanvasRenderer = (function() {
- var canvas = newEl('canvas');
+ var canvas = DOM.newEl('canvas');
var ctx = null;
return function(sceneGraph) {
if (ctx == null) {
ctx = canvas.getContext('2d');
@@ -1477,13 +1367,38 @@
var root = sceneGraph.root;
canvas.width = App.dpr(root.properties.width);
canvas.height = App.dpr(root.properties.height);
ctx.textBaseline = 'middle';
- ctx.fillStyle = root.children.holderBg.properties.fill;
- ctx.fillRect(0, 0, App.dpr(root.children.holderBg.width), App.dpr(root.children.holderBg.height));
+ var bg = root.children.holderBg;
+ var bgWidth = App.dpr(bg.width);
+ var bgHeight = App.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;
var tgProps = textGroup.properties;
ctx.font = textGroup.properties.font.weight + ' ' + App.dpr(textGroup.properties.font.size) + textGroup.properties.font.units + ' ' + textGroup.properties.font.family + ', monospace';
ctx.fillStyle = textGroup.properties.fill;
@@ -1503,34 +1418,34 @@
})();
var sgSVGRenderer = (function() {
//Prevent IE <9 from initializing SVG renderer
if (!global.XMLSerializer) return;
- var xml = createXML();
- var svg = initSVG(null, 0, 0);
- var bgEl = newEl('rect', SVG_NS);
+ var xml = DOM.createXML();
+ var svg = SVG.initSVG(null, 0, 0);
+ var bgEl = DOM.newEl('rect', SVG_NS);
svg.appendChild(bgEl);
//todo: create a reusable pool for textNodes, resize if more words present
return function(sceneGraph, renderSettings) {
var root = sceneGraph.root;
- initSVG(svg, root.properties.width, root.properties.height);
+ SVG.initSVG(svg, root.properties.width, root.properties.height);
var groups = svg.querySelectorAll('g');
for (var i = 0; i < groups.length; i++) {
groups[i].parentNode.removeChild(groups[i]);
}
var holderURL = renderSettings.holderSettings.flags.holderURL;
var holderId = 'holder_' + (Number(new Date()) + 32768 + (0 | Math.random() * 32768)).toString(16);
- var sceneGroupEl = newEl('g', SVG_NS);
+ var sceneGroupEl = DOM.newEl('g', SVG_NS);
var textGroup = root.children.holderTextGroup;
var tgProps = textGroup.properties;
- var textGroupEl = newEl('g', SVG_NS);
+ var textGroupEl = DOM.newEl('g', SVG_NS);
var tpdata = textGroup.textPositionData;
var textCSSRule = '#' + holderId + ' text { ' +
utils.cssProps({
'fill': tgProps.fill,
'font-weight': tgProps.font.weight,
@@ -1538,26 +1453,52 @@
'font-size': tgProps.font.size + tgProps.font.units
}) + ' } ';
var commentNode = xml.createComment('\n' + 'Source URL: ' + holderURL + generatorComment);
var holderCSS = xml.createCDATASection(textCSSRule);
var styleEl = svg.querySelector('style');
+ var bg = root.children.holderBg;
- setAttr(sceneGroupEl, {
+ DOM.setAttr(sceneGroupEl, {
id: holderId
});
svg.insertBefore(commentNode, svg.firstChild);
styleEl.appendChild(holderCSS);
sceneGroupEl.appendChild(bgEl);
+
+ //todo: abstract this into a cross-browser SVG outline method
+ if (bg.properties.outline) {
+ var outlineEl = DOM.newEl('path', SVG_NS);
+ var outlineWidth = bg.properties.outline.width;
+ var outlineOffsetWidth = outlineWidth / 2;
+ DOM.setAttr(outlineEl, {
+ 'd': [
+ 'M', outlineOffsetWidth, outlineOffsetWidth,
+ 'H', bg.width - outlineOffsetWidth,
+ 'V', bg.height - outlineOffsetWidth,
+ 'H', outlineOffsetWidth,
+ 'V', 0,
+ 'M', 0, outlineOffsetWidth,
+ 'L', bg.width, bg.height - outlineOffsetWidth,
+ 'M', 0, bg.height - outlineOffsetWidth,
+ 'L', bg.width, outlineOffsetWidth
+ ].join(' '),
+ 'stroke-width': bg.properties.outline.width,
+ 'stroke': bg.properties.outline.fill,
+ 'fill': 'none'
+ });
+ sceneGroupEl.appendChild(outlineEl);
+ }
+
sceneGroupEl.appendChild(textGroupEl);
svg.appendChild(sceneGroupEl);
- setAttr(bgEl, {
- 'width': root.children.holderBg.width,
- 'height': root.children.holderBg.height,
- 'fill': root.children.holderBg.properties.fill
+ DOM.setAttr(bgEl, {
+ 'width': bg.width,
+ 'height': bg.height,
+ 'fill': bg.properties.fill
});
textGroup.y += tpdata.boundingBox.height * 0.8;
for (var lineKey in textGroup.children) {
@@ -1565,14 +1506,14 @@
for (var wordKey in line.children) {
var word = line.children[wordKey];
var x = textGroup.x + line.x + word.x;
var y = textGroup.y + line.y + word.y;
- var textEl = newEl('text', SVG_NS);
+ var textEl = DOM.newEl('text', SVG_NS);
var textNode = document.createTextNode(null);
- setAttr(textEl, {
+ DOM.setAttr(textEl, {
'x': x,
'y': y
});
textNode.nodeValue = word.properties.text;
@@ -1580,157 +1521,18 @@
textGroupEl.appendChild(textEl);
}
}
//todo: factor the background check up the chain, perhaps only return reference
- var svgString = svgStringToDataURI(serializeSVG(svg, renderSettings.engineSettings), renderSettings.mode === 'background');
+ var svgString = SVG.svgStringToDataURI(SVG.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
- */
- function newEl(tag, namespace) {
- if (namespace == null) {
- return document.createElement(tag);
- } else {
- return document.createElementNS(namespace, tag);
- }
- }
-
- /**
- * Generic setAttribute function
- *
- * @private
- * @param el Reference to DOM element
- * @param attrs Object with attribute keys and values
- */
- function setAttr(el, attrs) {
- for (var a in attrs) {
- el.setAttribute(a, attrs[a]);
- }
- }
-
- /**
- * Generic SVG element creation function
- *
- * @private
- * @param svg SVG context, set to null if new
- * @param width Document width
- * @param height Document height
- */
- function initSVG(svg, width, height) {
- var defs, style;
-
- if (svg == null) {
- svg = newEl('svg', SVG_NS);
- defs = newEl('defs', SVG_NS);
- style = newEl('style', SVG_NS);
- setAttr(style, {
- 'type': 'text/css'
- });
- defs.appendChild(style);
- svg.appendChild(defs);
- } else {
- style = svg.querySelector('style');
- }
-
- //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]);
- }
-
- setAttr(svg, {
- 'width': width,
- 'height': height,
- 'viewBox': '0 0 ' + width + ' ' + height,
- 'preserveAspectRatio': 'none'
- });
-
- return svg;
- }
-
- /**
- * Returns serialized SVG with XML processing instructions
- *
- * @private
- * @param svg SVG context
- * @param stylesheets CSS stylesheets to include
- */
- function serializeSVG(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 = 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;
- }
-
- /**
- * Creates a XML document
- * @private
- */
- function createXML() {
- if (!global.DOMParser) return;
- return new DOMParser().parseFromString('<xml />', 'application/xml');
- }
-
- /**
* 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) {
@@ -1767,10 +1569,11 @@
debounce: 100,
ratio: 1,
supportsCanvas: false,
supportsSVG: false,
lineWrapRatio: 0.9,
+ dataAttr: 'data-src',
renderers: ['html', 'canvas', 'svg']
};
App.dpr = function(val) {
return val * App.setup.ratio;
@@ -1783,21 +1586,20 @@
resizableImages: [],
invisibleImages: {},
invisibleId: 0,
visibilityCheckStarted: false,
debounceTimer: null,
- cache: {},
- dataAttr: 'data-src'
+ cache: {}
};
//Pre-flight
(function() {
var devicePixelRatio = 1,
backingStoreRatio = 1;
- var canvas = newEl('canvas');
+ var canvas = DOM.newEl('canvas');
var ctx = null;
if (canvas.getContext) {
if (canvas.toDataURL('image/png').indexOf('data:image/png') != -1) {
App.setup.renderer = 'canvas';
@@ -1845,13 +1647,121 @@
module.exports = Holder;
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
-/* 1 */
+/* 2 */
/***/ 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__(10);
+ var type = __webpack_require__(9);
+
+ 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('&');
+ };
+
+
+/***/ },
+/* 3 */
+/***/ function(module, exports, __webpack_require__) {
+
/*!
* onDomReady.js 1.4.0 (c) 2013 Tubal Martin - MIT license
*
* Specially modified to work with Holder.js
*/
@@ -2005,15 +1915,13 @@
}
module.exports = typeof window !== "undefined" && _onDomReady(window);
/***/ },
-/* 2 */
+/* 4 */
/***/ function(module, exports, __webpack_require__) {
- var augment = __webpack_require__(5);
-
var SceneGraph = function(sceneProperties) {
var nodeCount = 1;
//todo: move merge to helpers section
function merge(parent, child) {
@@ -2021,105 +1929,111 @@
parent[prop] = child[prop];
}
return parent;
}
- var SceneNode = augment.defclass({
- constructor: function(name) {
- nodeCount++;
- this.parent = null;
- this.children = {};
- this.id = nodeCount;
- this.name = 'n' + nodeCount;
- if (name != null) {
- this.name = name;
- }
- this.x = 0;
- this.y = 0;
- this.z = 0;
- this.width = 0;
- this.height = 0;
- },
- resize: function(width, height) {
- if (width != null) {
- this.width = width;
- }
- if (height != null) {
- this.height = height;
- }
- },
- 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;
- },
- add: function(child) {
- var name = child.name;
- if (this.children[name] == null) {
- this.children[name] = child;
- child.parent = this;
- } else {
- throw 'SceneGraph: child with that name already exists: ' + name;
- }
+ 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;
+ };
- var RootNode = augment(SceneNode, function(uber) {
- this.constructor = function() {
- uber.constructor.call(this, 'root');
- this.properties = sceneProperties;
- };
- });
+ SceneNode.prototype.resize = function(width, height) {
+ if (width != null) {
+ this.width = width;
+ }
+ if (height != null) {
+ this.height = height;
+ }
+ };
- var Shape = augment(SceneNode, function(uber) {
- function constructor(name, props) {
- uber.constructor.call(this, name);
- this.properties = {
- fill: '#000'
- };
- if (props != null) {
- merge(this.properties, props);
- } else if (name != null && typeof name !== 'string') {
- throw 'SceneGraph: invalid node name';
- }
+ 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;
}
+ };
- this.Group = augment.extend(this, {
- constructor: constructor,
- type: 'group'
- });
+ var RootNode = function() {
+ SceneNode.call(this, 'root');
+ this.properties = sceneProperties;
+ };
- this.Rect = augment.extend(this, {
- constructor: constructor,
- type: 'rect'
- });
+ RootNode.prototype = new SceneNode();
- this.Text = augment.extend(this, {
- constructor: function(text) {
- constructor.call(this);
- this.properties.text = text;
- },
- type: 'text'
- });
- });
+ 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 = Shape;
- this.root = root;
+ this.Shape = {
+ 'Rect': Rect,
+ 'Text': Text,
+ 'Group': Group
+ };
+ this.root = root;
return this;
};
module.exports = SceneGraph;
/***/ },
-/* 3 */
+/* 5 */
/***/ function(module, exports, __webpack_require__) {
- /* 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
@@ -2173,35 +2087,11 @@
}
}
return buf.join('');
};
-
/**
- * 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 = [];
- }
- return retval;
- };
-
- /**
* Checks if an image exists
*
* @param src URL of image
* @param callback Callback to call once image status has been found
*/
@@ -2256,174 +2146,343 @@
return val === 'true' || val === 'yes' || val === '1' || val === 'on' || val === '✓';
}
return !!val;
};
- /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
-/* 4 */
+/* 6 */
/***/ 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
+ /* WEBPACK VAR INJECTION */(function(global) {var DOM = __webpack_require__(7);
+ var SVG_NS = 'http://www.w3.org/2000/svg';
+ var NODE_TYPE_COMMENT = 8;
+
/**
- * Module dependencies.
+ * Generic SVG element creation function
+ *
+ * @private
+ * @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;
- var encode = encodeURIComponent;
- var decode = decodeURIComponent;
- var trim = __webpack_require__(6);
- var type = __webpack_require__(7);
+ if (svg && svg.querySelector) {
+ style = svg.querySelector('style');
+ if (style === null) {
+ initialize = true;
+ }
+ } else {
+ svg = DOM.newEl('svg', SVG_NS);
+ initialize = true;
+ }
- var arrayRegex = /(\w+)\[(\d+)\]/;
- var objectRegex = /\w+\.\w+/;
+ 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;
+ };
+
/**
- * Parse the given query `str`.
- *
- * @param {String} str
- * @return {Object}
- * @api public
+ * 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,';
- exports.parse = function(str){
- if ('string' !== typeof str) return {};
+ return function(svgString, base64) {
+ if (base64) {
+ return base64Prefix + btoa(unescape(encodeURIComponent(svgString)));
+ } else {
+ return rawPrefix + encodeURIComponent(svgString);
+ }
+ };
+ }();
- str = trim(str);
- if ('' === str) return {};
- if ('?' === str.charAt(0)) str = str.slice(1);
+ /**
+ * Returns serialized SVG with XML processing instructions
+ *
+ * @private
+ * @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;
- 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;
+ //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);
+ }
- if (m = arrayRegex.exec(key)) {
- obj[m[1]] = obj[m[1]] || [];
- obj[m[1]][m[2]] = decode(parts[1]);
- continue;
+ xml.removeChild(xml.documentElement);
+ svgCSS = serializer.serializeToString(xml);
}
- if (m = objectRegex.test(key)) {
- m = key.split('.');
- ctx = obj;
-
- while (m.length) {
- prop = m.shift();
+ var svgText = serializer.serializeToString(svg);
+ svgText = svgText.replace(/\&(\#[0-9]{2,}\;)/g, '&$1');
+ return svgCSS + svgText;
+ };
- if (!prop.length) continue;
+ /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
- if (!ctx[prop]) {
- ctx[prop] = {};
- } else if (ctx[prop] && typeof ctx[prop] !== 'object') {
- break;
- }
+/***/ },
+/* 7 */
+/***/ function(module, exports, __webpack_require__) {
- if (!m.length) {
- ctx[prop] = decode(parts[1]);
- }
+ /* WEBPACK VAR INJECTION */(function(global) {/**
+ * Generic new DOM element function
+ *
+ * @private
+ * @param tag Tag to create
+ * @param namespace Optional namespace value
+ */
+ exports.newEl = function(tag, namespace) {
+ if (!global.document) return;
- ctx = ctx[prop];
- }
+ if (namespace == null) {
+ return document.createElement(tag);
+ } else {
+ return document.createElementNS(namespace, tag);
+ }
+ };
- continue;
+ /**
+ * Generic setAttribute function
+ *
+ * @private
+ * @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]);
}
+ };
- obj[parts[0]] = null == parts[1] ? '' : decode(parts[1]);
- }
-
- return obj;
+ /**
+ * Creates a XML document
+ * @private
+ */
+ exports.createXML = function() {
+ if (!global.DOMParser) return;
+ return new DOMParser().parseFromString('<xml />', 'application/xml');
};
/**
- * Stringify the given `obj`.
+ * Converts a value into an array of DOM nodes
*
- * @param {Object} obj
- * @return {String}
- * @api public
+ * @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 = [];
+ }
+ return retval;
+ };
- exports.stringify = function(obj){
- if (!obj) return '';
- var pairs = [];
+ /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
- for (var key in obj) {
- var value = obj[key];
+/***/ },
+/* 8 */
+/***/ function(module, exports, __webpack_require__) {
- if ('array' == type(value)) {
- for (var i = 0; i < value.length; ++i) {
- pairs.push(encode(key + '[' + i + ']') + '=' + encode(value[i]));
- }
- continue;
+ var Color = function (color, options) {
+ //todo: support array->color conversion
+ //todo: support rgba, hsla, and rrggbbaa notation
+ if (typeof color !== 'string') return;
+
+ if (color.charAt(0) === '#') {
+ color = color.slice(1);
}
- pairs.push(encode(key) + '=' + encode(obj[key]));
- }
+ if (/[^a-f0-9]+/i.test(color)) return;
- return pairs.join('&');
+ if (color.length === 3) {
+ color = color.replace(/./g, '$&$&');
+ }
+
+ if (color.length !== 6) return;
+
+ this.alpha = 1;
+
+ if (options) {
+ this.alpha = options.alpha || this.alpha;
+ }
+
+ colorSet.call(this, parseInt(color, 16));
};
+ Color.rgbToHex = function (r, g, b) {
+ return (((r | 0) << 16) + ((g | 0) << 8) + (b | 0)).toString(16);
+ };
-/***/ },
-/* 5 */
-/***/ function(module, exports, __webpack_require__) {
+ /**
+ * Sets the color from a raw RGB888 integer
+ * @param raw RGB888 representation of color
+ */
+ //todo: refactor into a more generic method
+ function colorSet (raw) {
+ this.rgb = {};
+ this.yuv = {};
+ this.raw = raw;
- var Factory = function () {};
- var slice = Array.prototype.slice;
+ this.rgb.r = (raw & 0xFF0000) >> 16;
+ this.rgb.g = (raw & 0x00FF00) >> 8;
+ this.rgb.b = (raw & 0x0000FF);
- var augment = function (base, body) {
- var uber = Factory.prototype = typeof base === "function" ? base.prototype : base;
- var prototype = new Factory(), properties = body.apply(prototype, slice.call(arguments, 2).concat(uber));
- if (typeof properties === "object") for (var key in properties) prototype[key] = properties[key];
- if (!prototype.hasOwnProperty("constructor")) return prototype;
- var constructor = prototype.constructor;
- constructor.prototype = prototype;
- return constructor;
+ // BT.709
+ this.yuv.y = 0.2126 * this.rgb.r + 0.7152 * this.rgb.g + 0.0722 * this.rgb.b;
+ this.yuv.u = -0.09991 * this.rgb.r - 0.33609 * this.rgb.g + 0.436 * this.rgb.b;
+ this.yuv.v = 0.615 * this.rgb.r - 0.55861 * this.rgb.g - 0.05639 * this.rgb.b;
+
+ return this;
+ }
+
+ /**
+ * Lighten or darken a color
+ * @param multiplier Amount to lighten or darken (-1 to 1)
+ */
+ Color.prototype.lighten = function (multiplier) {
+ var r = this.rgb.r;
+ var g = this.rgb.g;
+ var b = this.rgb.b;
+
+ var m = (255 * multiplier) | 0;
+
+ return new Color(Color.rgbToHex(r + m, g + m, b + m));
};
- augment.defclass = function (prototype) {
- var constructor = prototype.constructor;
- constructor.prototype = prototype;
- return constructor;
+ /**
+ * 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);
};
- augment.extend = function (base, body) {
- return augment(base, function (uber) {
- this.uber = uber;
- return body;
- });
+ /**
+ * 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;
};
- module.exports = augment;
+ /**
+ * 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);
+ }
-/***/ },
-/* 6 */
-/***/ function(module, exports, __webpack_require__) {
+ var r = this.rgb.r;
+ var g = this.rgb.g;
+ var b = this.rgb.b;
+ var a = this.alpha;
-
- exports = module.exports = trim;
+ var m = typeof multiplier !== 'undefined' ? multiplier : 0.5;
- function trim(str){
- return str.replace(/^\s*|\s*$/g, '');
- }
+ //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);
- exports.left = function(str){
- return str.replace(/^\s*/, '');
+ return new Color(Color.rgbToHex(r, g, b), {
+ 'alpha': a
+ });
};
+ */
- exports.right = function(str){
- return str.replace(/\s*$/, '');
+ /**
+ * 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.rgbToHex(r, g, b));
};
+ module.exports = Color;
+
/***/ },
-/* 7 */
+/* 9 */
/***/ function(module, exports, __webpack_require__) {
/**
* toString ref.
*/
@@ -2455,9 +2514,29 @@
val = val.valueOf
? val.valueOf()
: Object.prototype.valueOf.apply(val)
return typeof val;
+ };
+
+
+/***/ },
+/* 10 */
+/***/ 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*$/, '');
};
/***/ }
/******/ ])