/*
* ColorPlugin
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @module TweenJS
*/
this.createjs = this.createjs||{};
(function() {
"use strict";
/**
* The ColorPlugin enables tweening of almost any CSS color values. This includes 3 or 6 digit hex colors (`#00FF00`),
* rgb, rgba, hsl, and hsla colors (but not named colors, such as `red`).
*
* It can operate in either `rgb` or `hsl` mode. It will convert all colors into that mode, and output them accordingly.
* @class ColorPlugin
* @constructor
*/
function ColorPlugin() {
throw("ColorPlugin cannot be instantiated.");
}
var s = ColorPlugin;
/**
* READ-ONLY. RegExp pattern that detects CSS color values.
* @property COLOR_RE
* @type {RegExp}
* @static
* @readonly
*/
s.COLOR_RE = /^#[0-9a-fA-F]{3}|^hsla?\(|^rgba?\(/
/**
* READ-ONLY. RegExp pattern that matches rgb or hsl color strings, with groups for each value.
* @property RGB_HSL_RE
* @type {RegExp}
* @static
* @readonly
*/
s.RGB_HSL_RE = /^(?:rgb|hsl)a?\((\d{1,3})%?, ?(\d{1,3})%?, ?(\d{1,3})%?(?:, ?([0-9.]+))?\)$/;
/**
* READ-ONLY. RegExp pattern that matches a 3 or 6 digit RGB string with a preceding #.
* @property HEX_RE
* @type {RegExp}
* @static
* @readonly
*/
s.HEX_RE = /^#((?:[a-f0-9]{3}){1,2})$/i;
/**
* @property _mode
* @type {string}
* @static
* @default rgb
* @protected
*/
s._mode = "rgb";
/**
* READ-ONLY. A unique identifying string for this plugin. Used by TweenJS to ensure duplicate plugins are not installed on a tween.
* @property ID
* @type {String}
* @static
* @readonly
**/
s.ID = "Color";
/**
* Installs this plugin for use with TweenJS. Call this once after TweenJS is loaded to enable this plugin.
* @method install
* @param {String} mode A string equalling either "rgb" or "hsl" indicating what color mode should be used for calculations
* and output. You can input any color type regardless of the mode setting.
* @static
**/
s.install = function(mode) {
s._mode = mode || s._mode;
createjs.Tween._installPlugin(s);
};
/**
* Called by TweenJS when a new property initializes on a tween.
* See {{#crossLink "SamplePlugin/init"}}{{/crossLink}} for more info.
* @method init
* @param {Tween} tween
* @param {String} prop
* @param {any} value
* @return {any}
* @static
**/
s.init = function(tween, prop, value) {
var data = tween.pluginData;
value = value === undefined ? tween.target[prop] : value;
if (!data.Color_disabled && typeof value === "string" && s.COLOR_RE.exec(value)) {
tween._addPlugin(s);
var colorData = data.Color || (data.Color = {});
colorData[prop] = true;
return getColorObj(value, s._mode);
}
};
/**
* Called when a new step is added to a tween (ie. a new "to" action is added to a tween).
* See {{#crossLink "SamplePlugin/step"}}{{/crossLink}} for more info.
* @method step
* @param {Tween} tween
* @param {TweenStep} step
* @param {Object} props
* @static
**/
s.step = function(tween, step, props) {
var n, colorData = tween.pluginData.Color;
for (n in props) {
if (!colorData[n]) { continue; }
step.props[n] = getColorObj(step.props[n], s._mode);
}
};
/**
* Called before a property is updated by the tween.
* See {{#crossLink "SamplePlugin/change"}}{{/crossLink}} for more info.
* @method change
* @param {Tween} tween
* @param {TweenStep} step
* @param {String} prop
* @param {any} value
* @param {Number} ratio
* @param {Boolean} end
* @return {any}
* @static
**/
s.change = function(tween, step, prop, value, ratio, end) {
if (!tween.pluginData.Color[prop]) { return; }
var o0=step.prev.props[prop], o1 = step.props[prop];
var d0 = o1[0]-o0[0], v1=o0[1]+(o1[1]-o0[1])*ratio+0.5|0, v2=o0[2]+(o1[2]-o0[2])*ratio+0.5|0, a=(o0[3]+(o1[3]-o0[3])*ratio).toFixed(3);
if (s._mode === "rgb") {
return "rgba("+(o0[0]+d0*ratio+0.5|0)+", "+v1+", "+v2+", "+a+")";
} else {
if (d0 > 180) { d0 -= 360; }
else if (d0 < -180) { d0 += 360; }
return "hsla("+((o0[0]+d0*ratio+360)%360+0.5|0)+", "+v1+"%, "+v2+"%, "+a+")";
}
};
// private helper methods:
function getColorObj(value, mode) {
if (value[0] === "#") { return parseHex(value, mode); }
else { return parseRgbOrHsl(value, mode); }
}
function parseRgbOrHsl(value, mode) {
var o=[0,0,0,1], result = s.RGB_HSL_RE.exec(value);
if (!result) { return o;}
var rgb = value[0] === "r", m = (rgb && value.lastIndexOf("%") !== -1) ? 255/100 : 1;
o[0] = parseInt(result[1]) * m;
o[1] = parseInt(result[2]) * m;
o[2] = parseInt(result[3]) * m;
o[3] = result[4] === undefined ? 1 : parseFloat(result[4]);
if (!rgb && mode === "rgb") { hslToRgb(o); }
else if (rgb && mode !== "rgb") { rgbToHsl(o); }
return o;
};
function parseHex(value, mode) {
var o=[0,0,0,1], result = s.HEX_RE.exec(value);
if (!result) { return o;}
var hex = result[1];
if (hex.length === 3) { hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; }
var num = parseInt(hex, 16);
o[0] = num>>16;
o[1] = num>>8&0xFF;
o[2] = num&0xFF;
if (mode !== "rgb") { rgbToHsl(o); }
return o;
};
function rgbToHsl(o) {
var r=o[0], g=o[1], b=o[2], m = 1/255;
r *= m, g *= m, b *= m;
// TODO: Math.max/min are pretty slow vs conditional assignment
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) * 0.5;
if (max === min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
if (max === r) { h = (g - b) / d + (g < b ? 6 : 0); }
else if (max === g) { h = (b - r) / d + 2; }
else { h = (r - g) / d + 4; }
h /= 6;
}
o[0] = h*360;
o[1] = s*100;
o[2] = l*100;
}
function hslToRgb(o) {
var h=(o[0]%360)/360, s=o[1]/100, l=o[2]/100;
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
o[0] = r*255;
o[1] = g*255;
o[2] = b*255;
}
function hue2rgb(p, q, t) {
if (t < 0) { t += 1; }
else if (t > 1) { t -= 1; }
if (t < 1 / 6) { return p + (q - p) * 6 * t; }
if (t < 0.5) { return q; }
if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; }
return p;
}
createjs.ColorPlugin = s;
}());