2013-05-08 14:51:02 +08:00
|
|
|
(function() {
|
|
|
|
/**
|
|
|
|
* Collection constructor. Collection extends
|
2013-05-19 01:40:05 +08:00
|
|
|
* Array. This class is used in conjunction with {@link Kinetic.Container#get}
|
2013-05-08 14:51:02 +08:00
|
|
|
* @constructor
|
2013-05-16 13:03:52 +08:00
|
|
|
* @memberof Kinetic
|
2013-05-08 14:51:02 +08:00
|
|
|
*/
|
|
|
|
Kinetic.Collection = function() {
|
|
|
|
var args = [].slice.call(arguments), length = args.length, i = 0;
|
|
|
|
|
|
|
|
this.length = length;
|
|
|
|
for(; i < length; i++) {
|
|
|
|
this[i] = args[i];
|
|
|
|
}
|
|
|
|
return this;
|
2013-06-02 13:03:02 +08:00
|
|
|
};
|
|
|
|
Kinetic.Collection.prototype = [];
|
2013-05-08 14:51:02 +08:00
|
|
|
/**
|
2013-05-16 13:03:52 +08:00
|
|
|
* iterate through node array and run a function for each node.
|
|
|
|
* The node and index is passed into the function
|
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Collection.prototype
|
2013-05-08 14:51:02 +08:00
|
|
|
* @param {Function} func
|
2013-05-19 01:40:05 +08:00
|
|
|
* @example
|
|
|
|
* // get all nodes with name foo inside layer, and set x to 10 for each
|
|
|
|
* layer.get('.foo').each(function(shape, n) {<br>
|
|
|
|
* shape.setX(10);<br>
|
|
|
|
* });
|
2013-05-08 14:51:02 +08:00
|
|
|
*/
|
|
|
|
Kinetic.Collection.prototype.each = function(func) {
|
|
|
|
for(var n = 0; n < this.length; n++) {
|
|
|
|
func(this[n], n);
|
|
|
|
}
|
|
|
|
};
|
2013-05-20 12:07:43 +08:00
|
|
|
/**
|
|
|
|
* convert collection into an array
|
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Collection.prototype
|
|
|
|
*/
|
|
|
|
Kinetic.Collection.prototype.toArray = function() {
|
2013-07-22 07:05:40 +08:00
|
|
|
var arr = [],
|
|
|
|
len = this.length,
|
|
|
|
n;
|
|
|
|
|
|
|
|
for(n = 0; n < len; n++) {
|
2013-05-20 12:07:43 +08:00
|
|
|
arr.push(this[n]);
|
|
|
|
}
|
|
|
|
return arr;
|
|
|
|
};
|
2013-07-22 07:05:40 +08:00
|
|
|
/**
|
|
|
|
* convert array into a collection
|
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Collection
|
|
|
|
* @param {Array} arr
|
|
|
|
*/
|
|
|
|
Kinetic.Collection.toCollection = function(arr) {
|
|
|
|
var collection = new Kinetic.Collection(),
|
|
|
|
len = arr.length,
|
|
|
|
n;
|
|
|
|
|
|
|
|
for(n = 0; n < len; n++) {
|
|
|
|
collection.push(arr[n]);
|
|
|
|
}
|
|
|
|
return collection;
|
|
|
|
};
|
2013-05-08 14:51:02 +08:00
|
|
|
|
|
|
|
Kinetic.Collection.mapMethods = function(arr) {
|
|
|
|
var leng = arr.length,
|
|
|
|
n;
|
2013-07-22 07:05:40 +08:00
|
|
|
|
2013-05-08 14:51:02 +08:00
|
|
|
for(n = 0; n < leng; n++) {
|
|
|
|
// induce scope
|
|
|
|
(function(i) {
|
|
|
|
var method = arr[i];
|
|
|
|
Kinetic.Collection.prototype[method] = function() {
|
|
|
|
var len = this.length,
|
|
|
|
i;
|
2013-07-22 07:05:40 +08:00
|
|
|
|
2013-05-08 14:51:02 +08:00
|
|
|
args = [].slice.call(arguments);
|
|
|
|
for(i = 0; i < len; i++) {
|
|
|
|
this[i][method].apply(this[i], args);
|
2013-07-22 07:05:40 +08:00
|
|
|
}
|
2013-05-08 14:51:02 +08:00
|
|
|
};
|
|
|
|
})(n);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
/*
|
|
|
|
* Last updated November 2011
|
|
|
|
* By Simon Sarris
|
|
|
|
* www.simonsarris.com
|
|
|
|
* sarris@acm.org
|
|
|
|
*
|
|
|
|
* Free to use and distribute at will
|
|
|
|
* So long as you are nice to people, etc
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The usage of this class was inspired by some of the work done by a forked
|
|
|
|
* project, KineticJS-Ext by Wappworks, which is based on Simon's Transform
|
2013-05-16 13:03:52 +08:00
|
|
|
* class. Modified by Eric Rowell
|
2013-05-08 14:51:02 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transform constructor
|
|
|
|
* @constructor
|
2013-05-16 13:03:52 +08:00
|
|
|
* @memberof Kinetic
|
2013-05-08 14:51:02 +08:00
|
|
|
*/
|
|
|
|
Kinetic.Transform = function() {
|
|
|
|
this.m = [1, 0, 0, 1, 0, 0];
|
2013-06-02 13:03:02 +08:00
|
|
|
};
|
2013-05-08 14:51:02 +08:00
|
|
|
|
|
|
|
Kinetic.Transform.prototype = {
|
|
|
|
/**
|
|
|
|
* Apply translation
|
2013-05-16 13:03:52 +08:00
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Transform.prototype
|
2013-05-08 14:51:02 +08:00
|
|
|
* @param {Number} x
|
|
|
|
* @param {Number} y
|
|
|
|
*/
|
|
|
|
translate: function(x, y) {
|
|
|
|
this.m[4] += this.m[0] * x + this.m[2] * y;
|
|
|
|
this.m[5] += this.m[1] * x + this.m[3] * y;
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Apply scale
|
2013-05-16 13:03:52 +08:00
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Transform.prototype
|
2013-05-08 14:51:02 +08:00
|
|
|
* @param {Number} sx
|
|
|
|
* @param {Number} sy
|
|
|
|
*/
|
|
|
|
scale: function(sx, sy) {
|
|
|
|
this.m[0] *= sx;
|
|
|
|
this.m[1] *= sx;
|
|
|
|
this.m[2] *= sy;
|
|
|
|
this.m[3] *= sy;
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Apply rotation
|
2013-05-16 13:03:52 +08:00
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Transform.prototype
|
2013-05-08 14:51:02 +08:00
|
|
|
* @param {Number} rad Angle in radians
|
|
|
|
*/
|
|
|
|
rotate: function(rad) {
|
|
|
|
var c = Math.cos(rad);
|
|
|
|
var s = Math.sin(rad);
|
|
|
|
var m11 = this.m[0] * c + this.m[2] * s;
|
|
|
|
var m12 = this.m[1] * c + this.m[3] * s;
|
|
|
|
var m21 = this.m[0] * -s + this.m[2] * c;
|
|
|
|
var m22 = this.m[1] * -s + this.m[3] * c;
|
|
|
|
this.m[0] = m11;
|
|
|
|
this.m[1] = m12;
|
|
|
|
this.m[2] = m21;
|
|
|
|
this.m[3] = m22;
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Returns the translation
|
2013-05-16 13:03:52 +08:00
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Transform.prototype
|
2013-05-08 14:51:02 +08:00
|
|
|
* @returns {Object} 2D point(x, y)
|
|
|
|
*/
|
|
|
|
getTranslation: function() {
|
|
|
|
return {
|
|
|
|
x: this.m[4],
|
|
|
|
y: this.m[5]
|
|
|
|
};
|
|
|
|
},
|
|
|
|
/**
|
2013-07-22 07:05:40 +08:00
|
|
|
* Apply skew
|
2013-05-16 13:03:52 +08:00
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Transform.prototype
|
2013-05-08 14:51:02 +08:00
|
|
|
* @param {Number} sx
|
|
|
|
* @param {Number} sy
|
|
|
|
*/
|
|
|
|
skew: function(sx, sy) {
|
|
|
|
var m11 = this.m[0] + this.m[2] * sy;
|
|
|
|
var m12 = this.m[1] + this.m[3] * sy;
|
|
|
|
var m21 = this.m[2] + this.m[0] * sx;
|
|
|
|
var m22 = this.m[3] + this.m[1] * sx;
|
|
|
|
this.m[0] = m11;
|
|
|
|
this.m[1] = m12;
|
|
|
|
this.m[2] = m21;
|
|
|
|
this.m[3] = m22;
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Transform multiplication
|
2013-05-16 13:03:52 +08:00
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Transform.prototype
|
2013-05-08 14:51:02 +08:00
|
|
|
* @param {Kinetic.Transform} matrix
|
|
|
|
*/
|
|
|
|
multiply: function(matrix) {
|
|
|
|
var m11 = this.m[0] * matrix.m[0] + this.m[2] * matrix.m[1];
|
|
|
|
var m12 = this.m[1] * matrix.m[0] + this.m[3] * matrix.m[1];
|
|
|
|
|
|
|
|
var m21 = this.m[0] * matrix.m[2] + this.m[2] * matrix.m[3];
|
|
|
|
var m22 = this.m[1] * matrix.m[2] + this.m[3] * matrix.m[3];
|
|
|
|
|
|
|
|
var dx = this.m[0] * matrix.m[4] + this.m[2] * matrix.m[5] + this.m[4];
|
|
|
|
var dy = this.m[1] * matrix.m[4] + this.m[3] * matrix.m[5] + this.m[5];
|
|
|
|
|
|
|
|
this.m[0] = m11;
|
|
|
|
this.m[1] = m12;
|
|
|
|
this.m[2] = m21;
|
|
|
|
this.m[3] = m22;
|
|
|
|
this.m[4] = dx;
|
|
|
|
this.m[5] = dy;
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Invert the matrix
|
2013-05-16 13:03:52 +08:00
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Transform.prototype
|
2013-05-08 14:51:02 +08:00
|
|
|
*/
|
|
|
|
invert: function() {
|
|
|
|
var d = 1 / (this.m[0] * this.m[3] - this.m[1] * this.m[2]);
|
|
|
|
var m0 = this.m[3] * d;
|
|
|
|
var m1 = -this.m[1] * d;
|
|
|
|
var m2 = -this.m[2] * d;
|
|
|
|
var m3 = this.m[0] * d;
|
|
|
|
var m4 = d * (this.m[2] * this.m[5] - this.m[3] * this.m[4]);
|
|
|
|
var m5 = d * (this.m[1] * this.m[4] - this.m[0] * this.m[5]);
|
|
|
|
this.m[0] = m0;
|
|
|
|
this.m[1] = m1;
|
|
|
|
this.m[2] = m2;
|
|
|
|
this.m[3] = m3;
|
|
|
|
this.m[4] = m4;
|
|
|
|
this.m[5] = m5;
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* return matrix
|
2013-05-16 13:03:52 +08:00
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Transform.prototype
|
2013-05-08 14:51:02 +08:00
|
|
|
*/
|
|
|
|
getMatrix: function() {
|
|
|
|
return this.m;
|
2013-08-12 18:00:08 +08:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* set to absolute position via translation
|
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Transform.prototype
|
|
|
|
* @author ericdrowell
|
|
|
|
*/
|
|
|
|
setAbsolutePosition: function(x, y) {
|
|
|
|
var m0 = this.m[0],
|
|
|
|
m1 = this.m[1],
|
|
|
|
m2 = this.m[2],
|
|
|
|
m3 = this.m[3],
|
|
|
|
m4 = this.m[4],
|
|
|
|
m5 = this.m[5],
|
|
|
|
yt = ((m0 * (y - m5)) - (m1 * (x - m4))) / ((m0 * m3) - (m1 * m2)),
|
|
|
|
xt = (x - m4 - (m2 * yt)) / m0;
|
|
|
|
|
|
|
|
this.translate(xt, yt);
|
2013-05-08 14:51:02 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
2012-12-02 04:04:10 +08:00
|
|
|
(function() {
|
2013-03-25 04:12:33 +08:00
|
|
|
// CONSTANTS
|
|
|
|
var CANVAS = 'canvas',
|
|
|
|
CONTEXT_2D = '2d',
|
|
|
|
OBJECT_ARRAY = '[object Array]',
|
|
|
|
OBJECT_NUMBER = '[object Number]',
|
|
|
|
OBJECT_STRING = '[object String]',
|
|
|
|
PI_OVER_DEG180 = Math.PI / 180,
|
2013-04-30 14:12:28 +08:00
|
|
|
DEG180_OVER_PI = 180 / Math.PI,
|
|
|
|
HASH = '#',
|
2013-05-21 14:35:05 +08:00
|
|
|
EMPTY_STRING = '',
|
2013-05-19 01:40:05 +08:00
|
|
|
ZERO = '0',
|
|
|
|
KINETIC_WARNING = 'Kinetic warning: ',
|
2013-07-23 13:47:13 +08:00
|
|
|
KINETIC_ERROR = 'Kinetic error: ',
|
2013-04-30 14:12:28 +08:00
|
|
|
RGB_PAREN = 'rgb(',
|
|
|
|
COLORS = {
|
|
|
|
aqua: [0,255,255],
|
|
|
|
lime: [0,255,0],
|
|
|
|
silver: [192,192,192],
|
|
|
|
black: [0,0,0],
|
|
|
|
maroon: [128,0,0],
|
|
|
|
teal: [0,128,128],
|
|
|
|
blue: [0,0,255],
|
|
|
|
navy: [0,0,128],
|
|
|
|
white: [255,255,255],
|
|
|
|
fuchsia: [255,0,255],
|
|
|
|
olive:[128,128,0],
|
|
|
|
yellow: [255,255,0],
|
|
|
|
orange: [255,165,0],
|
|
|
|
gray: [128,128,128],
|
|
|
|
purple: [128,0,128],
|
|
|
|
green: [0,128,0],
|
|
|
|
red: [255,0,0],
|
|
|
|
pink: [255,192,203],
|
|
|
|
cyan: [0,255,255],
|
|
|
|
transparent: [255,255,255,0]
|
|
|
|
},
|
|
|
|
|
|
|
|
RGB_REGEX = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/;
|
|
|
|
|
2013-07-22 07:05:40 +08:00
|
|
|
/**
|
2013-05-16 13:03:52 +08:00
|
|
|
* @namespace Util
|
|
|
|
* @memberof Kinetic
|
2012-04-29 08:45:13 +08:00
|
|
|
*/
|
2013-05-08 14:51:02 +08:00
|
|
|
Kinetic.Util = {
|
2012-12-02 04:04:10 +08:00
|
|
|
/*
|
|
|
|
* cherry-picked utilities from underscore.js
|
|
|
|
*/
|
|
|
|
_isElement: function(obj) {
|
|
|
|
return !!(obj && obj.nodeType == 1);
|
|
|
|
},
|
|
|
|
_isFunction: function(obj) {
|
|
|
|
return !!(obj && obj.constructor && obj.call && obj.apply);
|
|
|
|
},
|
|
|
|
_isObject: function(obj) {
|
|
|
|
return (!!obj && obj.constructor == Object);
|
|
|
|
},
|
|
|
|
_isArray: function(obj) {
|
2013-03-25 04:12:33 +08:00
|
|
|
return Object.prototype.toString.call(obj) == OBJECT_ARRAY;
|
2012-12-02 04:04:10 +08:00
|
|
|
},
|
|
|
|
_isNumber: function(obj) {
|
2013-03-25 04:12:33 +08:00
|
|
|
return Object.prototype.toString.call(obj) == OBJECT_NUMBER;
|
2012-12-02 04:04:10 +08:00
|
|
|
},
|
|
|
|
_isString: function(obj) {
|
2013-03-25 04:12:33 +08:00
|
|
|
return Object.prototype.toString.call(obj) == OBJECT_STRING;
|
2012-12-02 04:04:10 +08:00
|
|
|
},
|
|
|
|
/*
|
|
|
|
* other utils
|
|
|
|
*/
|
|
|
|
_hasMethods: function(obj) {
|
2013-03-25 04:12:33 +08:00
|
|
|
var names = [],
|
|
|
|
key;
|
2013-07-22 07:05:40 +08:00
|
|
|
|
2013-03-25 04:12:33 +08:00
|
|
|
for(key in obj) {
|
|
|
|
if(this._isFunction(obj[key])) {
|
2012-12-02 04:04:10 +08:00
|
|
|
names.push(key);
|
2013-03-25 04:12:33 +08:00
|
|
|
}
|
2012-05-13 09:37:07 +08:00
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
return names.length > 0;
|
|
|
|
},
|
2013-01-13 14:01:12 +08:00
|
|
|
_isInDocument: function(el) {
|
2013-03-25 04:12:33 +08:00
|
|
|
while(el = el.parentNode) {
|
2013-01-13 14:01:12 +08:00
|
|
|
if(el == document) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
2012-12-02 04:04:10 +08:00
|
|
|
/*
|
|
|
|
* The argument can be:
|
|
|
|
* - an integer (will be applied to both x and y)
|
|
|
|
* - an array of one integer (will be applied to both x and y)
|
|
|
|
* - an array of two integers (contains x and y)
|
|
|
|
* - an array of four integers (contains x, y, width, and height)
|
|
|
|
* - an object with x and y properties
|
|
|
|
* - an array of one element which is an array of integers
|
|
|
|
* - an array of one element of an object
|
|
|
|
*/
|
|
|
|
_getXY: function(arg) {
|
|
|
|
if(this._isNumber(arg)) {
|
2012-05-20 12:14:04 +08:00
|
|
|
return {
|
2012-12-02 04:04:10 +08:00
|
|
|
x: arg,
|
|
|
|
y: arg
|
2012-05-20 12:14:04 +08:00
|
|
|
};
|
2012-05-13 09:37:07 +08:00
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
else if(this._isArray(arg)) {
|
|
|
|
// if arg is an array of one element
|
|
|
|
if(arg.length === 1) {
|
|
|
|
var val = arg[0];
|
|
|
|
// if arg is an array of one element which is a number
|
|
|
|
if(this._isNumber(val)) {
|
2012-05-20 12:14:04 +08:00
|
|
|
return {
|
2012-12-02 04:04:10 +08:00
|
|
|
x: val,
|
|
|
|
y: val
|
2012-05-20 12:14:04 +08:00
|
|
|
};
|
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
// if arg is an array of one element which is an array
|
|
|
|
else if(this._isArray(val)) {
|
2012-05-20 12:14:04 +08:00
|
|
|
return {
|
2012-12-02 04:04:10 +08:00
|
|
|
x: val[0],
|
|
|
|
y: val[1]
|
2012-05-20 12:14:04 +08:00
|
|
|
};
|
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
// if arg is an array of one element which is an object
|
|
|
|
else if(this._isObject(val)) {
|
|
|
|
return val;
|
|
|
|
}
|
2012-05-20 12:14:04 +08:00
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
// if arg is an array of two or more elements
|
|
|
|
else if(arg.length >= 2) {
|
|
|
|
return {
|
|
|
|
x: arg[0],
|
|
|
|
y: arg[1]
|
|
|
|
};
|
2012-05-20 12:14:04 +08:00
|
|
|
}
|
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
// if arg is an object return the object
|
|
|
|
else if(this._isObject(arg)) {
|
|
|
|
return arg;
|
2012-05-13 09:37:07 +08:00
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
|
|
|
|
// default
|
2013-01-02 15:54:02 +08:00
|
|
|
return null;
|
2012-12-02 04:04:10 +08:00
|
|
|
},
|
|
|
|
/*
|
|
|
|
* The argument can be:
|
|
|
|
* - an integer (will be applied to both width and height)
|
|
|
|
* - an array of one integer (will be applied to both width and height)
|
|
|
|
* - an array of two integers (contains width and height)
|
|
|
|
* - an array of four integers (contains x, y, width, and height)
|
|
|
|
* - an object with width and height properties
|
|
|
|
* - an array of one element which is an array of integers
|
|
|
|
* - an array of one element of an object
|
|
|
|
*/
|
|
|
|
_getSize: function(arg) {
|
|
|
|
if(this._isNumber(arg)) {
|
2012-05-20 12:14:04 +08:00
|
|
|
return {
|
2012-12-02 04:04:10 +08:00
|
|
|
width: arg,
|
|
|
|
height: arg
|
2012-05-20 12:14:04 +08:00
|
|
|
};
|
2012-05-13 09:37:07 +08:00
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
else if(this._isArray(arg)) {
|
|
|
|
// if arg is an array of one element
|
|
|
|
if(arg.length === 1) {
|
|
|
|
var val = arg[0];
|
|
|
|
// if arg is an array of one element which is a number
|
|
|
|
if(this._isNumber(val)) {
|
|
|
|
return {
|
|
|
|
width: val,
|
|
|
|
height: val
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// if arg is an array of one element which is an array
|
|
|
|
else if(this._isArray(val)) {
|
|
|
|
/*
|
|
|
|
* if arg is an array of one element which is an
|
|
|
|
* array of four elements
|
|
|
|
*/
|
|
|
|
if(val.length >= 4) {
|
|
|
|
return {
|
|
|
|
width: val[2],
|
|
|
|
height: val[3]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* if arg is an array of one element which is an
|
|
|
|
* array of two elements
|
|
|
|
*/
|
|
|
|
else if(val.length >= 2) {
|
|
|
|
return {
|
|
|
|
width: val[0],
|
|
|
|
height: val[1]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if arg is an array of one element which is an object
|
|
|
|
else if(this._isObject(val)) {
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if arg is an array of four elements
|
|
|
|
else if(arg.length >= 4) {
|
|
|
|
return {
|
|
|
|
width: arg[2],
|
|
|
|
height: arg[3]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// if arg is an array of two elements
|
|
|
|
else if(arg.length >= 2) {
|
|
|
|
return {
|
|
|
|
width: arg[0],
|
|
|
|
height: arg[1]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if arg is an object return the object
|
|
|
|
else if(this._isObject(arg)) {
|
|
|
|
return arg;
|
|
|
|
}
|
2012-05-20 12:14:04 +08:00
|
|
|
|
2012-12-02 04:04:10 +08:00
|
|
|
// default
|
2013-01-02 15:54:02 +08:00
|
|
|
return null;
|
2012-12-02 04:04:10 +08:00
|
|
|
},
|
|
|
|
/*
|
|
|
|
* arg will be an array of numbers or
|
2013-01-09 16:21:47 +08:00
|
|
|
* an array of point arrays or
|
2012-12-02 04:04:10 +08:00
|
|
|
* an array of point objects
|
|
|
|
*/
|
|
|
|
_getPoints: function(arg) {
|
2013-06-02 13:03:02 +08:00
|
|
|
var arr = [],
|
|
|
|
n, len;
|
|
|
|
|
2012-12-02 04:04:10 +08:00
|
|
|
if(arg === undefined) {
|
|
|
|
return [];
|
2012-05-14 02:58:40 +08:00
|
|
|
}
|
2012-05-20 12:14:04 +08:00
|
|
|
|
2013-06-02 13:03:02 +08:00
|
|
|
len = arg.length;
|
|
|
|
|
2013-01-09 16:21:47 +08:00
|
|
|
// an array of arrays
|
|
|
|
if(this._isArray(arg[0])) {
|
|
|
|
/*
|
|
|
|
* convert array of arrays into an array
|
|
|
|
* of objects containing x, y
|
|
|
|
*/
|
2013-06-02 13:03:02 +08:00
|
|
|
for(n = 0; n < len; n++) {
|
2013-01-09 16:21:47 +08:00
|
|
|
arr.push({
|
|
|
|
x: arg[n][0],
|
|
|
|
y: arg[n][1]
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return arr;
|
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
// an array of objects
|
|
|
|
if(this._isObject(arg[0])) {
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
// an array of integers
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* convert array of numbers into an array
|
|
|
|
* of objects containing x, y
|
|
|
|
*/
|
2013-06-02 13:03:02 +08:00
|
|
|
for(n = 0; n < len; n += 2) {
|
2012-12-02 04:04:10 +08:00
|
|
|
arr.push({
|
|
|
|
x: arg[n],
|
|
|
|
y: arg[n + 1]
|
|
|
|
});
|
|
|
|
}
|
2012-07-16 11:12:18 +08:00
|
|
|
|
2012-12-02 04:04:10 +08:00
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/*
|
|
|
|
* arg can be an image object or image data
|
|
|
|
*/
|
|
|
|
_getImage: function(arg, callback) {
|
2013-03-25 04:12:33 +08:00
|
|
|
var imageObj, canvas, context, dataUrl;
|
2013-07-22 07:05:40 +08:00
|
|
|
|
2012-12-02 04:04:10 +08:00
|
|
|
// if arg is null or undefined
|
|
|
|
if(!arg) {
|
|
|
|
callback(null);
|
|
|
|
}
|
2012-07-15 07:25:56 +08:00
|
|
|
|
2012-12-02 04:04:10 +08:00
|
|
|
// if arg is already an image object
|
|
|
|
else if(this._isElement(arg)) {
|
|
|
|
callback(arg);
|
2012-07-16 11:12:18 +08:00
|
|
|
}
|
2012-07-15 09:32:00 +08:00
|
|
|
|
2012-12-02 04:04:10 +08:00
|
|
|
// if arg is a string, then it's a data url
|
|
|
|
else if(this._isString(arg)) {
|
2013-03-25 04:12:33 +08:00
|
|
|
imageObj = new Image();
|
2012-12-02 04:04:10 +08:00
|
|
|
imageObj.onload = function() {
|
|
|
|
callback(imageObj);
|
2013-06-02 13:03:02 +08:00
|
|
|
};
|
2012-12-02 04:04:10 +08:00
|
|
|
imageObj.src = arg;
|
2012-07-16 11:12:18 +08:00
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
|
|
|
|
//if arg is an object that contains the data property, it's an image object
|
|
|
|
else if(arg.data) {
|
2013-03-25 04:12:33 +08:00
|
|
|
canvas = document.createElement(CANVAS);
|
2012-12-02 04:04:10 +08:00
|
|
|
canvas.width = arg.width;
|
|
|
|
canvas.height = arg.height;
|
2013-03-25 04:12:33 +08:00
|
|
|
context = canvas.getContext(CONTEXT_2D);
|
2012-12-02 04:04:10 +08:00
|
|
|
context.putImageData(arg, 0, 0);
|
2013-03-25 04:12:33 +08:00
|
|
|
dataUrl = canvas.toDataURL();
|
|
|
|
imageObj = new Image();
|
2012-12-02 04:04:10 +08:00
|
|
|
imageObj.onload = function() {
|
|
|
|
callback(imageObj);
|
2013-06-02 13:03:02 +08:00
|
|
|
};
|
2012-12-02 04:04:10 +08:00
|
|
|
imageObj.src = dataUrl;
|
2012-09-25 11:34:23 +08:00
|
|
|
}
|
|
|
|
else {
|
2012-12-02 04:04:10 +08:00
|
|
|
callback(null);
|
2012-09-25 11:34:23 +08:00
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
},
|
|
|
|
_rgbToHex: function(r, g, b) {
|
|
|
|
return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
|
|
|
},
|
2013-04-14 22:43:02 +08:00
|
|
|
_hexToRgb: function(hex) {
|
2013-05-21 14:35:05 +08:00
|
|
|
hex = hex.replace(HASH, EMPTY_STRING);
|
2013-04-14 22:43:02 +08:00
|
|
|
var bigint = parseInt(hex, 16);
|
|
|
|
return {
|
|
|
|
r: (bigint >> 16) & 255,
|
|
|
|
g: (bigint >> 8) & 255,
|
|
|
|
b: bigint & 255
|
|
|
|
};
|
|
|
|
},
|
2013-05-16 13:03:52 +08:00
|
|
|
/**
|
2013-05-19 01:40:05 +08:00
|
|
|
* return random hex color
|
2013-05-16 13:03:52 +08:00
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Util.prototype
|
|
|
|
*/
|
2013-05-06 15:17:10 +08:00
|
|
|
getRandomColor: function() {
|
2013-04-12 14:09:41 +08:00
|
|
|
var randColor = (Math.random() * 0xFFFFFF << 0).toString(16);
|
|
|
|
while (randColor.length < 6) {
|
2013-05-19 01:40:05 +08:00
|
|
|
randColor = ZERO + randColor;
|
2013-04-12 14:09:41 +08:00
|
|
|
}
|
2013-05-19 01:40:05 +08:00
|
|
|
return HASH + randColor;
|
2012-12-02 04:04:10 +08:00
|
|
|
},
|
2013-08-29 00:48:42 +08:00
|
|
|
/**
|
|
|
|
* return value with default fallback
|
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Util.prototype
|
|
|
|
*/
|
|
|
|
get: function(val, def) {
|
|
|
|
if (val === undefined) {
|
|
|
|
return def;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
},
|
2013-05-16 13:03:52 +08:00
|
|
|
/**
|
|
|
|
* get RGB components of a color
|
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Util.prototype
|
2013-07-22 07:05:40 +08:00
|
|
|
* @param {String} color
|
2013-05-19 01:40:05 +08:00
|
|
|
* @example
|
|
|
|
* // each of the following examples return {r:0, g:0, b:255}<br>
|
|
|
|
* var rgb = Kinetic.Util.getRGB('blue');<br>
|
|
|
|
* var rgb = Kinetic.Util.getRGB('#0000ff');<br>
|
|
|
|
* var rgb = Kinetic.Util.getRGB('rgb(0,0,255)');
|
2013-05-16 13:03:52 +08:00
|
|
|
*/
|
2013-04-30 14:12:28 +08:00
|
|
|
getRGB: function(color) {
|
|
|
|
var rgb;
|
|
|
|
// color string
|
|
|
|
if (color in COLORS) {
|
|
|
|
rgb = COLORS[color];
|
|
|
|
return {
|
|
|
|
r: rgb[0],
|
|
|
|
g: rgb[1],
|
|
|
|
b: rgb[2]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// hex
|
|
|
|
else if (color[0] === HASH) {
|
|
|
|
return this._hexToRgb(color.substring(1));
|
|
|
|
}
|
|
|
|
// rgb string
|
|
|
|
else if (color.substr(0, 4) === RGB_PAREN) {
|
2013-07-22 07:05:40 +08:00
|
|
|
rgb = RGB_REGEX.exec(color.replace(/ /g,''));
|
2013-04-30 14:12:28 +08:00
|
|
|
return {
|
2013-06-02 13:03:02 +08:00
|
|
|
r: parseInt(rgb[1], 10),
|
|
|
|
g: parseInt(rgb[2], 10),
|
|
|
|
b: parseInt(rgb[3], 10)
|
2013-04-30 14:12:28 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
// default
|
|
|
|
else {
|
|
|
|
return {
|
|
|
|
r: 0,
|
|
|
|
g: 0,
|
|
|
|
b: 0
|
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
2012-12-02 04:04:10 +08:00
|
|
|
// o1 takes precedence over o2
|
|
|
|
_merge: function(o1, o2) {
|
|
|
|
var retObj = this._clone(o2);
|
|
|
|
for(var key in o1) {
|
|
|
|
if(this._isObject(o1[key])) {
|
|
|
|
retObj[key] = this._merge(o1[key], retObj[key]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
retObj[key] = o1[key];
|
|
|
|
}
|
2012-09-25 11:34:23 +08:00
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
return retObj;
|
|
|
|
},
|
2013-08-25 15:34:49 +08:00
|
|
|
// deep object clone
|
2012-12-02 04:04:10 +08:00
|
|
|
_clone: function(obj) {
|
|
|
|
var retObj = {};
|
|
|
|
for(var key in obj) {
|
|
|
|
if(this._isObject(obj[key])) {
|
|
|
|
retObj[key] = this._clone(obj[key]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
retObj[key] = obj[key];
|
|
|
|
}
|
2012-09-25 11:34:23 +08:00
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
return retObj;
|
|
|
|
},
|
|
|
|
_degToRad: function(deg) {
|
2013-03-25 04:12:33 +08:00
|
|
|
return deg * PI_OVER_DEG180;
|
2012-12-02 04:04:10 +08:00
|
|
|
},
|
|
|
|
_radToDeg: function(rad) {
|
2013-03-25 04:12:33 +08:00
|
|
|
return rad * DEG180_OVER_PI;
|
2013-03-22 11:24:03 +08:00
|
|
|
},
|
|
|
|
_capitalize: function(str) {
|
|
|
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
2013-05-08 14:51:02 +08:00
|
|
|
},
|
2013-07-23 13:47:13 +08:00
|
|
|
error: function(str) {
|
|
|
|
throw new Error(KINETIC_ERROR + str);
|
|
|
|
},
|
2013-05-08 14:51:02 +08:00
|
|
|
warn: function(str) {
|
|
|
|
/*
|
|
|
|
* IE9 on Windows7 64bit will throw a JS error
|
|
|
|
* if we don't use window.console in the conditional
|
|
|
|
*/
|
|
|
|
if(window.console && console.warn) {
|
2013-05-19 01:40:05 +08:00
|
|
|
console.warn(KINETIC_WARNING + str);
|
2013-05-08 14:51:02 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
extend: function(c1, c2) {
|
|
|
|
for(var key in c2.prototype) {
|
|
|
|
if(!( key in c1.prototype)) {
|
|
|
|
c1.prototype[key] = c2.prototype[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
2013-05-16 13:03:52 +08:00
|
|
|
* adds methods to a constructor prototype
|
|
|
|
* @method
|
|
|
|
* @memberof Kinetic.Util.prototype
|
2013-05-08 14:51:02 +08:00
|
|
|
* @param {Function} constructor
|
|
|
|
* @param {Object} methods
|
|
|
|
*/
|
|
|
|
addMethods: function(constructor, methods) {
|
|
|
|
var key;
|
|
|
|
|
|
|
|
for (key in methods) {
|
|
|
|
constructor.prototype[key] = methods[key];
|
|
|
|
}
|
2013-06-09 05:17:26 +08:00
|
|
|
},
|
|
|
|
_getControlPoints: function(p0, p1, p2, t) {
|
|
|
|
var x0 = p0.x;
|
|
|
|
var y0 = p0.y;
|
|
|
|
var x1 = p1.x;
|
|
|
|
var y1 = p1.y;
|
|
|
|
var x2 = p2.x;
|
|
|
|
var y2 = p2.y;
|
|
|
|
var d01 = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2));
|
|
|
|
var d12 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
|
|
|
|
var fa = t * d01 / (d01 + d12);
|
|
|
|
var fb = t * d12 / (d01 + d12);
|
|
|
|
var p1x = x1 - fa * (x2 - x0);
|
|
|
|
var p1y = y1 - fa * (y2 - y0);
|
|
|
|
var p2x = x1 + fb * (x2 - x0);
|
|
|
|
var p2y = y1 + fb * (y2 - y0);
|
|
|
|
return [{
|
|
|
|
x: p1x,
|
|
|
|
y: p1y
|
|
|
|
}, {
|
|
|
|
x: p2x,
|
|
|
|
y: p2y
|
|
|
|
}];
|
|
|
|
},
|
|
|
|
_expandPoints: function(points, tension) {
|
2013-07-22 07:05:40 +08:00
|
|
|
var length = points.length,
|
2013-06-09 05:17:26 +08:00
|
|
|
allPoints = [],
|
|
|
|
n, cp;
|
|
|
|
|
|
|
|
for(n = 1; n < length - 1; n++) {
|
|
|
|
cp = Kinetic.Util._getControlPoints(points[n - 1], points[n], points[n + 1], tension);
|
|
|
|
allPoints.push(cp[0]);
|
|
|
|
allPoints.push(points[n]);
|
|
|
|
allPoints.push(cp[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return allPoints;
|
2013-06-28 14:28:57 +08:00
|
|
|
},
|
|
|
|
_removeLastLetter: function(str) {
|
|
|
|
return str.substring(0, str.length - 1);
|
2013-05-21 15:21:21 +08:00
|
|
|
}
|
2012-12-02 04:04:10 +08:00
|
|
|
};
|
|
|
|
})();
|