(function() { /** * Collection constructor. Collection extends * Array. This class is used in conjunction with {@link Kinetic.Container#get} * @constructor * @memberof Kinetic */ 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; } Kinetic.Collection.prototype = new Array(); /** * 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 * @param {Function} func * @example * // get all nodes with name foo inside layer, and set x to 10 for each * layer.get('.foo').each(function(shape, n) {
* shape.setX(10);
* }); */ Kinetic.Collection.prototype.each = function(func) { for(var n = 0; n < this.length; n++) { func(this[n], n); } }; Kinetic.Collection.mapMethods = function(arr) { var leng = arr.length, n; for(n = 0; n < leng; n++) { // induce scope (function(i) { var method = arr[i]; Kinetic.Collection.prototype[method] = function() { var len = this.length, i; args = [].slice.call(arguments); for(i = 0; i < len; i++) { this[i][method].apply(this[i], args); } }; })(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 * class. Modified by Eric Rowell */ /** * Transform constructor * @constructor * @memberof Kinetic */ Kinetic.Transform = function() { this.m = [1, 0, 0, 1, 0, 0]; } Kinetic.Transform.prototype = { /** * Apply translation * @method * @memberof Kinetic.Transform.prototype * @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 * @method * @memberof Kinetic.Transform.prototype * @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 * @method * @memberof Kinetic.Transform.prototype * @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 * @method * @memberof Kinetic.Transform.prototype * @returns {Object} 2D point(x, y) */ getTranslation: function() { return { x: this.m[4], y: this.m[5] }; }, /** * Apply skew * @method * @memberof Kinetic.Transform.prototype * @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 * @method * @memberof Kinetic.Transform.prototype * @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 * @method * @memberof Kinetic.Transform.prototype */ 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 * @method * @memberof Kinetic.Transform.prototype */ getMatrix: function() { return this.m; } }; })(); (function() { // 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, DEG180_OVER_PI = 180 / Math.PI, HASH = '#', ZERO = '0', KINETIC_WARNING = 'Kinetic warning: ', 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})\)/; /** * @namespace Util * @memberof Kinetic */ Kinetic.Util = { /* * 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) { return Object.prototype.toString.call(obj) == OBJECT_ARRAY; }, _isNumber: function(obj) { return Object.prototype.toString.call(obj) == OBJECT_NUMBER; }, _isString: function(obj) { return Object.prototype.toString.call(obj) == OBJECT_STRING; }, /* * other utils */ _hasMethods: function(obj) { var names = [], key; for(key in obj) { if(this._isFunction(obj[key])) { names.push(key); } } return names.length > 0; }, _isInDocument: function(el) { while(el = el.parentNode) { if(el == document) { return true; } } return false; }, /* * 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)) { return { x: arg, y: arg }; } 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 { x: val, y: val }; } // if arg is an array of one element which is an array else if(this._isArray(val)) { return { x: val[0], y: 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 two or more elements else if(arg.length >= 2) { return { x: arg[0], y: arg[1] }; } } // if arg is an object return the object else if(this._isObject(arg)) { return arg; } // default return null; }, /* * 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)) { return { width: arg, height: arg }; } 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; } // default return null; }, /* * arg will be an array of numbers or * an array of point arrays or * an array of point objects */ _getPoints: function(arg) { if(arg === undefined) { return []; } // an array of arrays if(this._isArray(arg[0])) { /* * convert array of arrays into an array * of objects containing x, y */ var arr = []; for(var n = 0; n < arg.length; n++) { arr.push({ x: arg[n][0], y: arg[n][1] }); } return arr; } // 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 */ var arr = []; for(var n = 0; n < arg.length; n += 2) { arr.push({ x: arg[n], y: arg[n + 1] }); } return arr; } }, /* * arg can be an image object or image data */ _getImage: function(arg, callback) { var imageObj, canvas, context, dataUrl; // if arg is null or undefined if(!arg) { callback(null); } // if arg is already an image object else if(this._isElement(arg)) { callback(arg); } // if arg is a string, then it's a data url else if(this._isString(arg)) { imageObj = new Image(); imageObj.onload = function() { callback(imageObj); } imageObj.src = arg; } //if arg is an object that contains the data property, it's an image object else if(arg.data) { canvas = document.createElement(CANVAS); canvas.width = arg.width; canvas.height = arg.height; context = canvas.getContext(CONTEXT_2D); context.putImageData(arg, 0, 0); dataUrl = canvas.toDataURL(); imageObj = new Image(); imageObj.onload = function() { callback(imageObj); } imageObj.src = dataUrl; } else { callback(null); } }, _rgbToHex: function(r, g, b) { return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); }, _hexToRgb: function(hex) { var bigint = parseInt(hex, 16); return { r: (bigint >> 16) & 255, g: (bigint >> 8) & 255, b: bigint & 255 }; }, /** * return random hex color * @method * @memberof Kinetic.Util.prototype */ getRandomColor: function() { var randColor = (Math.random() * 0xFFFFFF << 0).toString(16); while (randColor.length < 6) { randColor = ZERO + randColor; } return HASH + randColor; }, /** * get RGB components of a color * @method * @memberof Kinetic.Util.prototype * @param {String} color * @example * // each of the following examples return {r:0, g:0, b:255}
* var rgb = Kinetic.Util.getRGB('blue');
* var rgb = Kinetic.Util.getRGB('#0000ff');
* var rgb = Kinetic.Util.getRGB('rgb(0,0,255)'); */ 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) { rgb = RGB_REGEX.exec(color.replace(/ /g,'')); return { r: parseInt(rgb[1]), g: parseInt(rgb[2]), b: parseInt(rgb[3]) }; } // default else { return { r: 0, g: 0, b: 0 }; } }, // 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]; } } return retObj; }, // deep clone _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]; } } return retObj; }, _degToRad: function(deg) { return deg * PI_OVER_DEG180; }, _radToDeg: function(rad) { return rad * DEG180_OVER_PI; }, _capitalize: function(str) { return str.charAt(0).toUpperCase() + str.slice(1); }, 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) { console.warn(KINETIC_WARNING + str); } }, extend: function(c1, c2) { for(var key in c2.prototype) { if(!( key in c1.prototype)) { c1.prototype[key] = c2.prototype[key]; } } }, /** * adds methods to a constructor prototype * @method * @memberof Kinetic.Util.prototype * @param {Function} constructor * @param {Object} methods */ addMethods: function(constructor, methods) { var key; for (key in methods) { constructor.prototype[key] = methods[key]; } }, }; })();