diff --git a/Thorfile b/Thorfile
index b2358321..58436f7f 100644
--- a/Thorfile
+++ b/Thorfile
@@ -4,13 +4,14 @@ require 'uglifier'
class Build < Thor
# This is the list of files to concatenate. The first file will appear at the top of the final file. All files are relative to the lib directory.
FILES = [
- "src/Global.js", "src/util/Type.js", "src/util/Canvas.js", "src/util/Tween.js", "src/util/Transform.js", "src/util/Collection.js",
+ "src/Global.js", "src/util/Type.js", "src/Canvas.js", "src/util/Tween.js", "src/util/Transform.js", "src/util/Collection.js",
"src/filters/Grayscale.js", "src/filters/Brighten.js", "src/filters/Invert.js",
"src/Animation.js", "src/Node.js", "src/DragAndDrop.js", "src/Transition.js", "src/Container.js", "src/Stage.js", "src/Layer.js", "src/Group.js", "src/Shape.js",
"src/shapes/Rect.js", "src/shapes/Circle.js", "src/shapes/Wedge.js", "src/shapes/Ellipse.js", "src/shapes/Image.js", "src/shapes/Polygon.js", "src/shapes/Text.js", "src/shapes/Line.js", "src/shapes/Sprite.js", "src/shapes/Star.js", "src/shapes/RegularPolygon.js", "src/shapes/Path.js", "src/shapes/TextPath.js"
]
UNIT_TESTS = [
+ "tests/js/unit/globalTests.js",
"tests/js/unit/nodeTests.js",
"tests/js/unit/stageTests.js",
"tests/js/unit/containerTests.js",
@@ -38,11 +39,11 @@ class Build < Thor
end
# dev build
- desc "dev", "Concatenate all the js files into /dist/kinetic-VERSION.js."
+ desc "dev", "Concatenate all the js files into /dist/kinetic-vVERSION.js."
def dev(version)
- file_name = "dist/kinetic-#{version}.js"
+ file_name = "dist/kinetic-v#{version}.js"
puts ":: Deleting other development files..."
Dir.foreach("dist") do |file|
@@ -79,9 +80,9 @@ class Build < Thor
end
#prod build
- desc "prod", "Concatenate all the js files in into /dist/kinetic-VERSION.min.js and minify it."
+ desc "prod", "Concatenate all the js files in into /dist/kinetic-vVERSION.min.js and minify it."
def prod(version)
- file_name = "dist/kinetic-#{version}.min.js"
+ file_name = "dist/kinetic-v#{version}.min.js"
puts ":: Deleting other prod files..."
Dir.foreach("dist") do |file|
@@ -106,7 +107,7 @@ class Build < Thor
content = IO.read(File.expand_path(file)) << "\n"
mod = File.basename(file)
mod[".js"] = ""
- module_filename = "dist/kinetic-#{mod}-#{version}.min.js"
+ module_filename = "dist/kinetic-#{mod}-v#{version}.min.js"
File.open(module_filename, "w") do |file2|
uglify = Uglifier.compile(content, :copyright => mod == "Global")
file2.puts replace_tokens(uglify, version)
@@ -140,7 +141,7 @@ class Build < Thor
date = Time.now.strftime("%b %d %Y")
# Add the version number
- content.sub!("@version", version)
+ content.gsub!("@version", version)
# Add the date
content.sub!("@date", date)
diff --git a/src/Animation.js b/src/Animation.js
index 5dcea113..ce21428a 100644
--- a/src/Animation.js
+++ b/src/Animation.js
@@ -1,120 +1,122 @@
-/**
- * Stage constructor. A stage is used to contain multiple layers and handle
- * animations
- * @constructor
- * @augments Kinetic.Container
- * @param {Function} func function executed on each animation frame
- * @param {Kinetic.Node} [node] node to be redrawn. Specifying a node will improve
- * draw performance. This can be a shape, a group, a layer, or the stage.
- */
-Kinetic.Animation = function(func, node) {
- this.func = func;
- this.node = node;
- this.id = Kinetic.Animation.animIdCounter++;
-};
-/*
- * Animation methods
- */
-Kinetic.Animation.prototype = {
+(function() {
/**
- * start animation
- * @name start
- * @methodOf Kinetic.Animation.prototype
+ * Stage constructor. A stage is used to contain multiple layers and handle
+ * animations
+ * @constructor
+ * @augments Kinetic.Container
+ * @param {Function} func function executed on each animation frame
+ * @param {Kinetic.Node} [node] node to be redrawn. Specifying a node will improve
+ * draw performance. This can be a shape, a group, a layer, or the stage.
*/
- start: function() {
- this.stop();
- Kinetic.Animation._addAnimation(this);
- Kinetic.Animation._handleAnimation();
- },
- /**
- * stop animation
- * @name stop
- * @methodOf Kinetic.Animation.prototype
- */
- stop: function() {
- Kinetic.Animation._removeAnimation(this);
- }
-};
-Kinetic.Animation.animations = [];
-Kinetic.Animation.animIdCounter = 0;
-Kinetic.Animation.animRunning = false;
-Kinetic.Animation.frame = {
- time: 0,
- timeDiff: 0,
- lastTime: new Date().getTime(),
- frameRate: 0
-};
-
-Kinetic.Animation.fixedRequestAnimFrame = function(callback) {
- window.setTimeout(callback, 1000 / 60);
-};
-
-Kinetic.Animation._addAnimation = function(anim) {
- this.animations.push(anim);
-};
-Kinetic.Animation._removeAnimation = function(anim) {
- var id = anim.id;
- var animations = this.animations;
- for(var n = 0; n < animations.length; n++) {
- if(animations[n].id === id) {
- this.animations.splice(n, 1);
- break;
- }
- }
-};
-Kinetic.Animation._updateFrameObject = function() {
- var time = new Date().getTime();
- this.frame.timeDiff = time - this.frame.lastTime;
- this.frame.lastTime = time;
- this.frame.time += this.frame.timeDiff;
- this.frame.frameRate = 1000 / this.frame.timeDiff;
-};
-Kinetic.Animation._runFrames = function() {
- this._updateFrameObject();
- var nodes = {};
+ Kinetic.Animation = function(func, node) {
+ this.func = func;
+ this.node = node;
+ this.id = Kinetic.Animation.animIdCounter++;
+ };
/*
- * loop through all animations and execute animation
- * function. if the animation object has specified node,
- * we can add the node to the nodes hash to eliminate
- * drawing the same node multiple times. The node property
- * can be the stage itself or a layer
+ * Animation methods
*/
- for(var n = 0; n < this.animations.length; n++) {
- var anim = this.animations[n];
- if(anim.node && anim.node._id !== undefined) {
- nodes[anim.node._id] = anim.node;
+ Kinetic.Animation.prototype = {
+ /**
+ * start animation
+ * @name start
+ * @methodOf Kinetic.Animation.prototype
+ */
+ start: function() {
+ this.stop();
+ Kinetic.Animation._addAnimation(this);
+ Kinetic.Animation._handleAnimation();
+ },
+ /**
+ * stop animation
+ * @name stop
+ * @methodOf Kinetic.Animation.prototype
+ */
+ stop: function() {
+ Kinetic.Animation._removeAnimation(this);
}
- // if animation object has a function, execute it
- if(anim.func) {
- anim.func(this.frame);
- }
- }
+ };
+ Kinetic.Animation.animations = [];
+ Kinetic.Animation.animIdCounter = 0;
+ Kinetic.Animation.animRunning = false;
+ Kinetic.Animation.frame = {
+ time: 0,
+ timeDiff: 0,
+ lastTime: new Date().getTime(),
+ frameRate: 0
+ };
- for(var key in nodes) {
- nodes[key].draw();
- }
-};
-Kinetic.Animation._animationLoop = function() {
- if(this.animations.length > 0) {
- this._runFrames();
+ Kinetic.Animation.fixedRequestAnimFrame = function(callback) {
+ window.setTimeout(callback, 1000 / 60);
+ };
+
+ Kinetic.Animation._addAnimation = function(anim) {
+ this.animations.push(anim);
+ };
+ Kinetic.Animation._removeAnimation = function(anim) {
+ var id = anim.id;
+ var animations = this.animations;
+ for(var n = 0; n < animations.length; n++) {
+ if(animations[n].id === id) {
+ this.animations.splice(n, 1);
+ break;
+ }
+ }
+ };
+ Kinetic.Animation._updateFrameObject = function() {
+ var time = new Date().getTime();
+ this.frame.timeDiff = time - this.frame.lastTime;
+ this.frame.lastTime = time;
+ this.frame.time += this.frame.timeDiff;
+ this.frame.frameRate = 1000 / this.frame.timeDiff;
+ };
+ Kinetic.Animation._runFrames = function() {
+ this._updateFrameObject();
+ var nodes = {};
+ /*
+ * loop through all animations and execute animation
+ * function. if the animation object has specified node,
+ * we can add the node to the nodes hash to eliminate
+ * drawing the same node multiple times. The node property
+ * can be the stage itself or a layer
+ */
+ for(var n = 0; n < this.animations.length; n++) {
+ var anim = this.animations[n];
+ if(anim.node && anim.node._id !== undefined) {
+ nodes[anim.node._id] = anim.node;
+ }
+ // if animation object has a function, execute it
+ if(anim.func) {
+ anim.func(this.frame);
+ }
+ }
+
+ for(var key in nodes) {
+ nodes[key].draw();
+ }
+ };
+ Kinetic.Animation._animationLoop = function() {
+ if(this.animations.length > 0) {
+ this._runFrames();
+ var that = this;
+ Kinetic.Animation.requestAnimFrame(function() {
+ that._animationLoop();
+ });
+ }
+ else {
+ this.animRunning = false;
+ }
+ };
+ Kinetic.Animation._handleAnimation = function() {
var that = this;
- Kinetic.Animation.requestAnimFrame(function() {
+ if(!this.animRunning) {
+ this.animRunning = true;
that._animationLoop();
- });
- }
- else {
- this.animRunning = false;
- }
-};
-Kinetic.Animation._handleAnimation = function() {
- var that = this;
- if(!this.animRunning) {
- this.animRunning = true;
- that._animationLoop();
- }
-};
-Kinetic.Animation.requestAnimFrame = function(callback) {
- var raf = Kinetic.DD && Kinetic.DD.moving ? this.fixedRequestAnimFrame : window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || Kinetic.Animation.fixedRequestAnimFrame;
-
- raf(callback);
-};
+ }
+ };
+ Kinetic.Animation.requestAnimFrame = function(callback) {
+ var raf = Kinetic.DD && Kinetic.DD.moving ? this.fixedRequestAnimFrame : window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || Kinetic.Animation.fixedRequestAnimFrame;
+
+ raf(callback);
+ };
+})();
diff --git a/src/Canvas.js b/src/Canvas.js
new file mode 100644
index 00000000..4b8a856b
--- /dev/null
+++ b/src/Canvas.js
@@ -0,0 +1,241 @@
+(function() {
+ /**
+ * Canvas wrapper constructor
+ * @constructor
+ * @param {Number} width
+ * @param {Number} height
+ */
+ Kinetic.Canvas = function(width, height, isHit) {
+ this.element = document.createElement('canvas');
+ this.context = this.element.getContext('2d');
+
+ // set dimensions
+ this.element.width = width || 0;
+ this.element.height = height || 0;
+
+ this.context.renderer = isHit ? new Kinetic.HitRenderer(this.context) : new Kinetic.SceneRenderer(this.context);
+ };
+
+ Kinetic.Canvas.prototype = {
+ /**
+ * clear canvas
+ * @name clear
+ * @methodOf Kinetic.Canvas.prototype
+ */
+ clear: function() {
+ var context = this.getContext();
+ var el = this.getElement();
+ context.clearRect(0, 0, el.width, el.height);
+ },
+ /**
+ * get element
+ * @name getElement
+ * @methodOf Kinetic.Canvas.prototype
+ */
+ getElement: function() {
+ return this.element;
+ },
+ /**
+ * get context
+ * @name getContext
+ * @methodOf Kinetic.Canvas.prototype
+ */
+ getContext: function() {
+ return this.context;
+ },
+ /**
+ * set width
+ * @name setWidth
+ * @methodOf Kinetic.Canvas.prototype
+ */
+ setWidth: function(width) {
+ this.element.width = width;
+ },
+ /**
+ * set height
+ * @name setHeight
+ * @methodOf Kinetic.Canvas.prototype
+ */
+ setHeight: function(height) {
+ this.element.height = height;
+ },
+ /**
+ * get width
+ * @name getWidth
+ * @methodOf Kinetic.Canvas.prototype
+ */
+ getWidth: function() {
+ return this.element.width;
+ },
+ /**
+ * get height
+ * @name getHeight
+ * @methodOf Kinetic.Canvas.prototype
+ */
+ getHeight: function() {
+ return this.element.height;
+ },
+ /**
+ * set size
+ * @name setSize
+ * @methodOf Kinetic.Canvas.prototype
+ */
+ setSize: function(width, height) {
+ this.setWidth(width);
+ this.setHeight(height);
+ },
+ /**
+ * toDataURL
+ */
+ toDataURL: function(mimeType, quality) {
+ try {
+ // If this call fails (due to browser bug, like in Firefox 3.6),
+ // then revert to previous no-parameter image/png behavior
+ return this.element.toDataURL(mimeType, quality);
+ }
+ catch(e) {
+ try {
+ return this.element.toDataURL();
+ }
+ catch(e) {
+ Kinetic.Global.warn('Unable to get data URL. ' + e.message)
+ return '';
+ }
+ }
+ }
+ };
+
+ Kinetic.SceneRenderer = function(context) {
+ this.context = context;
+ };
+
+ Kinetic.SceneRenderer.prototype = {
+ _fill: function(shape, skipShadow) {
+ var context = this.context, fill = shape.getFill(), fillType = shape._getFillType(fill), shadow = shape.getShadow();
+ if(fill) {
+ context.save();
+
+ if(!skipShadow && shadow) {
+ this._applyShadow(shape);
+ }
+ var s = fill.start;
+ var e = fill.end;
+
+ // color fill
+ switch(fillType) {
+ case 'COLOR':
+ context.fillStyle = fill;
+ context.fill(context);
+ break;
+ case 'PATTERN':
+ var repeat = !fill.repeat ? 'repeat' : fill.repeat;
+ if(fill.scale) {
+ context.scale(fill.scale.x, fill.scale.y);
+ }
+ if(fill.offset) {
+ context.translate(fill.offset.x, fill.offset.y);
+ } file:///C:/Users/Eric/Documents/Eric/workspaces/KineticJS/dist/kinetic-current.js
+
+ context.fillStyle = context.createPattern(fill.image, repeat);
+ context.fill(context);
+ break;
+ case 'LINEAR_GRADIENT':
+ var grd = context.createLinearGradient(s.x, s.y, e.x, e.y);
+ var colorStops = fill.colorStops;
+
+ // build color stops
+ for(var n = 0; n < colorStops.length; n += 2) {
+ grd.addColorStop(colorStops[n], colorStops[n + 1]);
+ }
+ context.fillStyle = grd;
+ context.fill(context);
+
+ break;
+ case 'RADIAL_GRADIENT':
+ var grd = context.createRadialGradient(s.x, s.y, s.radius, e.x, e.y, e.radius);
+ var colorStops = fill.colorStops;
+
+ // build color stops
+ for(var n = 0; n < colorStops.length; n += 2) {
+ grd.addColorStop(colorStops[n], colorStops[n + 1]);
+ }
+ context.fillStyle = grd;
+ context.fill(context);
+ break;
+ default:
+ context.fillStyle = 'black';
+ context.fill(context);
+ break;
+ }
+
+ context.restore();
+
+ if(!skipShadow && shadow && shadow.opacity) {
+ this._fill(shape, true);
+ }
+ }
+ },
+ _stroke: function(shape, skipShadow) {
+ var context = this.context, stroke = shape.getStroke(), strokeWidth = shape.getStrokeWidth(), shadow = shape.getShadow();
+ if(stroke || strokeWidth) {
+ context.save();
+ if(!skipShadow && shadow) {
+ this._applyShadow(shape);
+ }
+ context.lineWidth = strokeWidth || 2;
+ context.strokeStyle = stroke || 'black';
+ context.stroke(context);
+ context.restore();
+
+ if(!skipShadow && shadow && shadow.opacity) {
+ this._stroke(shape, true);
+ }
+ }
+ },
+ _applyShadow: function(shape) {
+ var context = this.context, shadow = shape.getShadow();
+ if(shadow) {
+ var aa = shape.getAbsoluteOpacity();
+ // defaults
+ var color = shadow.color || 'black';
+ var blur = shadow.blur || 5;
+ var offset = shadow.offset || {
+ x: 0,
+ y: 0
+ };
+
+ if(shadow.opacity) {
+ context.globalAlpha = shadow.opacity * aa;
+ }
+ context.shadowColor = color;
+ context.shadowBlur = blur;
+ context.shadowOffsetX = offset.x;
+ context.shadowOffsetY = offset.y;
+ }
+ }
+ };
+
+ Kinetic.HitRenderer = function(context) {
+ this.context = context;
+ };
+
+ Kinetic.HitRenderer.prototype = {
+ _fill: function(shape) {
+ var context = this.context;
+ context.save();
+ context.fillStyle = '#' + shape.colorKey;
+ context.fill(context);
+ context.restore();
+ },
+ _stroke: function(shape) {
+ var context = this.context, stroke = shape.getStroke(), strokeWidth = shape.getStrokeWidth();
+ if(stroke || strokeWidth) {
+ context.save();
+ context.lineWidth = strokeWidth || 2;
+ context.strokeStyle = '#' + shape.colorKey;
+ context.stroke(context);
+ context.restore();
+ }
+ }
+ };
+})();
diff --git a/src/Container.js b/src/Container.js
index a07db596..5caf22a4 100644
--- a/src/Container.js
+++ b/src/Container.js
@@ -1,268 +1,270 @@
-/**
- * Container constructor. Containers are used to contain nodes or other containers
- * @constructor
- * @augments Kinetic.Node
- * @param {Object} config
- * @param {Number} [config.x]
- * @param {Number} [config.y]
- * @param {Boolean} [config.visible]
- * @param {Boolean} [config.listening] whether or not the node is listening for events
- * @param {String} [config.id] unique id
- * @param {String} [config.name] non-unique name
- * @param {Number} [config.alpha] determines node opacity. Can be any number between 0 and 1
- * @param {Object} [config.scale]
- * @param {Number} [config.scale.x]
- * @param {Number} [config.scale.y]
- * @param {Number} [config.rotation] rotation in radians
- * @param {Number} [config.rotationDeg] rotation in degrees
- * @param {Object} [config.offset] offsets default position point and rotation point
- * @param {Number} [config.offset.x]
- * @param {Number} [config.offset.y]
- * @param {Boolean} [config.draggable]
- * @param {String} [config.dragConstraint] can be vertical, horizontal, or none. The default
- * is none
- * @param {Object} [config.dragBounds]
- * @param {Number} [config.dragBounds.top]
- * @param {Number} [config.dragBounds.right]
- * @param {Number} [config.dragBounds.bottom]
- * @param {Number} [config.dragBounds.left]
- */
-Kinetic.Container = function(config) {
- this._containerInit(config);
-};
-
-Kinetic.Container.prototype = {
- _containerInit: function(config) {
- this.children = [];
- Kinetic.Node.call(this, config);
- },
+(function() {
/**
- * get children
- * @name getChildren
- * @methodOf Kinetic.Container.prototype
+ * Container constructor. Containers are used to contain nodes or other containers
+ * @constructor
+ * @augments Kinetic.Node
+ * @param {Object} config
+ * @param {Number} [config.x]
+ * @param {Number} [config.y]
+ * @param {Boolean} [config.visible]
+ * @param {Boolean} [config.listening] whether or not the node is listening for events
+ * @param {String} [config.id] unique id
+ * @param {String} [config.name] non-unique name
+ * @param {Number} [config.alpha] determines node opacity. Can be any number between 0 and 1
+ * @param {Object} [config.scale]
+ * @param {Number} [config.scale.x]
+ * @param {Number} [config.scale.y]
+ * @param {Number} [config.rotation] rotation in radians
+ * @param {Number} [config.rotationDeg] rotation in degrees
+ * @param {Object} [config.offset] offsets default position point and rotation point
+ * @param {Number} [config.offset.x]
+ * @param {Number} [config.offset.y]
+ * @param {Boolean} [config.draggable]
+ * @param {String} [config.dragConstraint] can be vertical, horizontal, or none. The default
+ * is none
+ * @param {Object} [config.dragBounds]
+ * @param {Number} [config.dragBounds.top]
+ * @param {Number} [config.dragBounds.right]
+ * @param {Number} [config.dragBounds.bottom]
+ * @param {Number} [config.dragBounds.left]
*/
- getChildren: function() {
- return this.children;
- },
- /**
- * remove all children
- * @name removeChildren
- * @methodOf Kinetic.Container.prototype
- */
- removeChildren: function() {
- while(this.children.length > 0) {
- this.children[0].remove();
- }
- },
- /**
- * add node to container
- * @name add
- * @methodOf Kinetic.Container.prototype
- * @param {Node} child
- */
- add: function(child) {
- var go = Kinetic.Global, children = this.children;
+ Kinetic.Container = function(config) {
+ this._containerInit(config);
+ };
- child._id = Kinetic.Global.idCounter++;
- child.index = children.length;
- child.parent = this;
- children.push(child);
- var stage = child.getStage();
-
- if(!stage) {
- go._addTempNode(child);
- }
- else {
- stage._addId(child);
- stage._addName(child);
-
- /*
- * pull in other nodes that are now linked
- * to a stage
- */
- go._pullNodes(stage);
- }
-
- // chainable
- return this;
- },
- /**
- * return an array of nodes that match the selector. Use '#' for id selections
- * and '.' for name selections
- * ex:
- * var node = stage.get('#foo'); // selects node with id foo
- * var nodes = layer.get('.bar'); // selects nodes with name bar inside layer
- * @name get
- * @methodOf Kinetic.Container.prototype
- * @param {String} selector
- */
- get: function(selector) {
- var collection = new Kinetic.Collection();
- // ID selector
- if(selector.charAt(0) === '#') {
- var node = this._getNodeById(selector.slice(1));
- if(node) {
- collection.push(node);
+ Kinetic.Container.prototype = {
+ _containerInit: function(config) {
+ this.children = [];
+ Kinetic.Node.call(this, config);
+ },
+ /**
+ * get children
+ * @name getChildren
+ * @methodOf Kinetic.Container.prototype
+ */
+ getChildren: function() {
+ return this.children;
+ },
+ /**
+ * remove all children
+ * @name removeChildren
+ * @methodOf Kinetic.Container.prototype
+ */
+ removeChildren: function() {
+ while(this.children.length > 0) {
+ this.children[0].remove();
}
- }
- // name selector
- else if(selector.charAt(0) === '.') {
- var nodeList = this._getNodesByName(selector.slice(1));
- Kinetic.Collection.apply(collection, nodeList);
- }
- // unrecognized selector, pass to children
- else {
- var retArr = [];
+ },
+ /**
+ * add node to container
+ * @name add
+ * @methodOf Kinetic.Container.prototype
+ * @param {Node} child
+ */
+ add: function(child) {
+ var go = Kinetic.Global, children = this.children;
+
+ child._id = Kinetic.Global.idCounter++;
+ child.index = children.length;
+ child.parent = this;
+ children.push(child);
+ var stage = child.getStage();
+
+ if(!stage) {
+ go._addTempNode(child);
+ }
+ else {
+ stage._addId(child);
+ stage._addName(child);
+
+ /*
+ * pull in other nodes that are now linked
+ * to a stage
+ */
+ go._pullNodes(stage);
+ }
+
+ // chainable
+ return this;
+ },
+ /**
+ * return an array of nodes that match the selector. Use '#' for id selections
+ * and '.' for name selections
+ * ex:
+ * var node = stage.get('#foo'); // selects node with id foo
+ * var nodes = layer.get('.bar'); // selects nodes with name bar inside layer
+ * @name get
+ * @methodOf Kinetic.Container.prototype
+ * @param {String} selector
+ */
+ get: function(selector) {
+ var collection = new Kinetic.Collection();
+ // ID selector
+ if(selector.charAt(0) === '#') {
+ var node = this._getNodeById(selector.slice(1));
+ if(node) {
+ collection.push(node);
+ }
+ }
+ // name selector
+ else if(selector.charAt(0) === '.') {
+ var nodeList = this._getNodesByName(selector.slice(1));
+ Kinetic.Collection.apply(collection, nodeList);
+ }
+ // unrecognized selector, pass to children
+ else {
+ var retArr = [];
+ var children = this.getChildren();
+ var len = children.length;
+ for(var n = 0; n < len; n++) {
+ retArr = retArr.concat(children[n]._get(selector));
+ }
+ Kinetic.Collection.apply(collection, retArr);
+ }
+ return collection;
+ },
+ _getNodeById: function(key) {
+ var stage = this.getStage();
+ if(stage.ids[key] !== undefined && this.isAncestorOf(stage.ids[key])) {
+ return stage.ids[key];
+ }
+ return null;
+ },
+ _getNodesByName: function(key) {
+ var arr = this.getStage().names[key] || [];
+ return this._getDescendants(arr);
+ },
+ _get: function(selector) {
+ var retArr = Kinetic.Node.prototype._get.call(this, selector);
var children = this.getChildren();
var len = children.length;
for(var n = 0; n < len; n++) {
retArr = retArr.concat(children[n]._get(selector));
}
- Kinetic.Collection.apply(collection, retArr);
- }
- return collection;
- },
- _getNodeById: function(key) {
- var stage = this.getStage();
- if(stage.ids[key] !== undefined && this.isAncestorOf(stage.ids[key])) {
- return stage.ids[key];
- }
- return null;
- },
- _getNodesByName: function(key) {
- var arr = this.getStage().names[key] || [];
- return this._getDescendants(arr);
- },
- _get: function(selector) {
- var retArr = Kinetic.Node.prototype._get.call(this, selector);
- var children = this.getChildren();
- var len = children.length;
- for(var n = 0; n < len; n++) {
- retArr = retArr.concat(children[n]._get(selector));
- }
- return retArr;
- },
- // extenders
- toObject: function() {
- var obj = Kinetic.Node.prototype.toObject.call(this);
+ return retArr;
+ },
+ // extenders
+ toObject: function() {
+ var obj = Kinetic.Node.prototype.toObject.call(this);
- obj.children = [];
+ obj.children = [];
- var children = this.getChildren();
- var len = children.length;
- for(var n = 0; n < len; n++) {
- var child = children[n];
- obj.children.push(child.toObject());
- }
-
- return obj;
- },
- _getDescendants: function(arr) {
- var retArr = [];
- var len = arr.length;
- for(var n = 0; n < len; n++) {
- var node = arr[n];
- if(this.isAncestorOf(node)) {
- retArr.push(node);
+ var children = this.getChildren();
+ var len = children.length;
+ for(var n = 0; n < len; n++) {
+ var child = children[n];
+ obj.children.push(child.toObject());
}
- }
- return retArr;
- },
- /**
- * determine if node is an ancestor
- * of descendant
- * @name isAncestorOf
- * @methodOf Kinetic.Container.prototype
- * @param {Kinetic.Node} node
- */
- isAncestorOf: function(node) {
- var parent = node.getParent();
- while(parent) {
- if(parent._id === this._id) {
- return true;
+ return obj;
+ },
+ _getDescendants: function(arr) {
+ var retArr = [];
+ var len = arr.length;
+ for(var n = 0; n < len; n++) {
+ var node = arr[n];
+ if(this.isAncestorOf(node)) {
+ retArr.push(node);
+ }
}
- parent = parent.getParent();
- }
- return false;
- },
- /**
- * clone node
- * @name clone
- * @methodOf Kinetic.Container.prototype
- * @param {Object} attrs override attrs
- */
- clone: function(obj) {
- // call super method
- var node = Kinetic.Node.prototype.clone.call(this, obj)
-
- // perform deep clone on containers
- for(var key in this.children) {
- node.add(this.children[key].clone());
- }
- return node;
- },
- /**
- * get shapes that intersect a point
- * @name getIntersections
- * @methodOf Kinetic.Container.prototype
- * @param {Object} point
- */
- getIntersections: function() {
- var pos = Kinetic.Type._getXY(Array.prototype.slice.call(arguments));
- var arr = [];
- var shapes = this.get('Shape');
-
- var len = shapes.length;
- for(var n = 0; n < len; n++) {
- var shape = shapes[n];
- if(shape.isVisible() && shape.intersects(pos)) {
- arr.push(shape);
+ return retArr;
+ },
+ /**
+ * determine if node is an ancestor
+ * of descendant
+ * @name isAncestorOf
+ * @methodOf Kinetic.Container.prototype
+ * @param {Kinetic.Node} node
+ */
+ isAncestorOf: function(node) {
+ var parent = node.getParent();
+ while(parent) {
+ if(parent._id === this._id) {
+ return true;
+ }
+ parent = parent.getParent();
}
- }
- return arr;
- },
- /**
- * set children indices
- */
- _setChildrenIndices: function() {
- var children = this.children, len = children.length;
- for(var n = 0; n < len; n++) {
- children[n].index = n;
- }
- },
- /*
- * draw both scene and hit graphs
- */
- draw: function() {
- this.drawScene();
- this.drawHit();
- },
- drawScene: function() {
- if(this.isVisible()) {
+ return false;
+ },
+ /**
+ * clone node
+ * @name clone
+ * @methodOf Kinetic.Container.prototype
+ * @param {Object} attrs override attrs
+ */
+ clone: function(obj) {
+ // call super method
+ var node = Kinetic.Node.prototype.clone.call(this, obj)
+
+ // perform deep clone on containers
+ for(var key in this.children) {
+ node.add(this.children[key].clone());
+ }
+ return node;
+ },
+ /**
+ * get shapes that intersect a point
+ * @name getIntersections
+ * @methodOf Kinetic.Container.prototype
+ * @param {Object} point
+ */
+ getIntersections: function() {
+ var pos = Kinetic.Type._getXY(Array.prototype.slice.call(arguments));
+ var arr = [];
+ var shapes = this.get('Shape');
+
+ var len = shapes.length;
+ for(var n = 0; n < len; n++) {
+ var shape = shapes[n];
+ if(shape.isVisible() && shape.intersects(pos)) {
+ arr.push(shape);
+ }
+ }
+
+ return arr;
+ },
+ /**
+ * set children indices
+ */
+ _setChildrenIndices: function() {
var children = this.children, len = children.length;
for(var n = 0; n < len; n++) {
- children[n].drawScene();
+ children[n].index = n;
+ }
+ },
+ /*
+ * draw both scene and hit graphs
+ */
+ draw: function() {
+ this.drawScene();
+ this.drawHit();
+ },
+ drawScene: function() {
+ if(this.isVisible()) {
+ var children = this.children, len = children.length;
+ for(var n = 0; n < len; n++) {
+ children[n].drawScene();
+ }
+ }
+ },
+ drawHit: function() {
+ if(this.isVisible() && this.isListening()) {
+ var children = this.children, len = children.length;
+ for(var n = 0; n < len; n++) {
+ children[n].drawHit();
+ }
+ }
+ },
+ drawBuffer: function(canvas) {
+ if(this.isVisible()) {
+ var children = this.children, len = children.length;
+ for(var n = 0; n < len; n++) {
+ children[n].drawBuffer(canvas);
+ }
}
}
- },
- drawHit: function() {
- if(this.isVisible() && this.isListening()) {
- var children = this.children, len = children.length;
- for(var n = 0; n < len; n++) {
- children[n].drawHit();
- }
- }
- },
- drawBuffer: function(canvas) {
- if(this.isVisible()) {
- var children = this.children, len = children.length;
- for(var n = 0; n < len; n++) {
- children[n].drawBuffer(canvas);
- }
- }
- }
-};
-Kinetic.Global.extend(Kinetic.Container, Kinetic.Node);
+ };
+ Kinetic.Global.extend(Kinetic.Container, Kinetic.Node);
+})();
diff --git a/src/DragAndDrop.js b/src/DragAndDrop.js
index 9c217a2a..e1000b04 100644
--- a/src/DragAndDrop.js
+++ b/src/DragAndDrop.js
@@ -1,168 +1,170 @@
-Kinetic.DD = {
- anim: new Kinetic.Animation(),
- moving: false,
- offset: {
- x: 0,
- y: 0
- }
-};
-
-Kinetic.DD._startDrag = function(evt) {
- var dd = Kinetic.DD;
- var node = dd.node;
-
- if(node) {
- var pos = node.getStage().getUserPosition();
- var dbf = node.attrs.dragBoundFunc;
-
- var newNodePos = {
- x: pos.x - dd.offset.x,
- y: pos.y - dd.offset.y
- };
-
- if(dbf !== undefined) {
- newNodePos = dbf.call(node, newNodePos, evt);
+(function() {
+ Kinetic.DD = {
+ anim: new Kinetic.Animation(),
+ moving: false,
+ offset: {
+ x: 0,
+ y: 0
}
+ };
- node.setAbsolutePosition(newNodePos);
-
- if(!dd.moving) {
- dd.moving = true;
- node.setListening(false);
-
- // execute dragstart events if defined
- node._handleEvent('dragstart', evt);
- }
-
- // execute user defined ondragmove if defined
- node._handleEvent('dragmove', evt);
- }
-};
-Kinetic.DD._endDrag = function(evt) {
- var dd = Kinetic.DD;
- var node = dd.node;
- if(node) {
- node.setListening(true);
- if(node.nodeType === 'Stage') {
- node.draw();
- }
- else {
- node.getLayer().draw();
- }
-
- // handle dragend
- if(dd.moving) {
- dd.moving = false;
- node._handleEvent('dragend', evt);
- }
- }
- dd.node = null;
- dd.anim.stop();
-};
-/**
- * set draggable
- * @name setDraggable
- * @methodOf Kinetic.Node.prototype
- * @param {String} draggable
- */
-Kinetic.Node.prototype.setDraggable = function(draggable) {
- this.setAttr('draggable', draggable);
- this._dragChange();
-};
-/**
- * get draggable
- * @name getDraggable
- * @methodOf Kinetic.Node.prototype
- */
-Kinetic.Node.prototype.getDraggable = function() {
- return this.attrs.draggable;
-};
-/**
- * determine if node is currently in drag and drop mode
- * @name isDragging
- * @methodOf Kinetic.Node.prototype
- */
-Kinetic.Node.prototype.isDragging = function() {
- var dd = Kinetic.DD;
- return dd.node && dd.node._id === this._id && dd.moving;
-};
-
-Kinetic.Node.prototype._listenDrag = function() {
- this._dragCleanup();
- var that = this;
- this.on('mousedown.kinetic touchstart.kinetic', function(evt) {
- that._initDrag();
- });
-};
-Kinetic.Node.prototype._initDrag = function() {
- var dd = Kinetic.DD;
- var stage = this.getStage();
- var pos = stage.getUserPosition();
-
- if(pos) {
- var m = this.getTransform().getTranslation();
- var am = this.getAbsoluteTransform().getTranslation();
- var ap = this.getAbsolutePosition();
- dd.node = this;
- dd.offset.x = pos.x - ap.x;
- dd.offset.y = pos.y - ap.y;
-
- /*
- * if dragging and dropping the stage,
- * draw all of the layers
- */
- if(this.nodeType === 'Stage') {
- dd.anim.node = this;
- }
- else {
- dd.anim.node = this.getLayer();
- }
- dd.anim.start();
- }
-};
-Kinetic.Node.prototype._dragChange = function() {
- if(this.attrs.draggable) {
- this._listenDrag();
- }
- else {
- // remove event listeners
- this._dragCleanup();
-
- /*
- * force drag and drop to end
- * if this node is currently in
- * drag and drop mode
- */
- var stage = this.getStage();
+ Kinetic.DD._startDrag = function(evt) {
var dd = Kinetic.DD;
- if(stage && dd.node && dd.node._id === this._id) {
- dd._endDrag();
+ var node = dd.node;
+
+ if(node) {
+ var pos = node.getStage().getUserPosition();
+ var dbf = node.attrs.dragBoundFunc;
+
+ var newNodePos = {
+ x: pos.x - dd.offset.x,
+ y: pos.y - dd.offset.y
+ };
+
+ if(dbf !== undefined) {
+ newNodePos = dbf.call(node, newNodePos, evt);
+ }
+
+ node.setAbsolutePosition(newNodePos);
+
+ if(!dd.moving) {
+ dd.moving = true;
+ node.setListening(false);
+
+ // execute dragstart events if defined
+ node._handleEvent('dragstart', evt);
+ }
+
+ // execute user defined ondragmove if defined
+ node._handleEvent('dragmove', evt);
}
- }
-};
-Kinetic.Node.prototype._dragCleanup = function() {
- this.off('mousedown.kinetic');
- this.off('touchstart.kinetic');
-};
-/**
- * get draggable. Alias of getDraggable()
- * @name isDraggable
- * @methodOf Kinetic.Node.prototype
- */
-Kinetic.Node.prototype.isDraggable = Kinetic.Node.prototype.getDraggable;
+ };
+ Kinetic.DD._endDrag = function(evt) {
+ var dd = Kinetic.DD;
+ var node = dd.node;
+ if(node) {
+ node.setListening(true);
+ if(node.nodeType === 'Stage') {
+ node.draw();
+ }
+ else {
+ node.getLayer().draw();
+ }
-Kinetic.Node.addGettersSetters(Kinetic.Node, ['dragBoundFunc']);
+ // handle dragend
+ if(dd.moving) {
+ dd.moving = false;
+ node._handleEvent('dragend', evt);
+ }
+ }
+ dd.node = null;
+ dd.anim.stop();
+ };
+ /**
+ * set draggable
+ * @name setDraggable
+ * @methodOf Kinetic.Node.prototype
+ * @param {String} draggable
+ */
+ Kinetic.Node.prototype.setDraggable = function(draggable) {
+ this.setAttr('draggable', draggable);
+ this._dragChange();
+ };
+ /**
+ * get draggable
+ * @name getDraggable
+ * @methodOf Kinetic.Node.prototype
+ */
+ Kinetic.Node.prototype.getDraggable = function() {
+ return this.attrs.draggable;
+ };
+ /**
+ * determine if node is currently in drag and drop mode
+ * @name isDragging
+ * @methodOf Kinetic.Node.prototype
+ */
+ Kinetic.Node.prototype.isDragging = function() {
+ var dd = Kinetic.DD;
+ return dd.node && dd.node._id === this._id && dd.moving;
+ };
-/**
- * set drag bound function. This is used to override the default
- * drag and drop position
- * @name setDragBoundFunc
- * @methodOf Kinetic.Node.prototype
- * @param {Function} dragBoundFunc
- */
+ Kinetic.Node.prototype._listenDrag = function() {
+ this._dragCleanup();
+ var that = this;
+ this.on('mousedown.kinetic touchstart.kinetic', function(evt) {
+ that._initDrag();
+ });
+ };
+ Kinetic.Node.prototype._initDrag = function() {
+ var dd = Kinetic.DD;
+ var stage = this.getStage();
+ var pos = stage.getUserPosition();
-/**
- * get dragBoundFunc
- * @name getDragBoundFunc
- * @methodOf Kinetic.Node.prototype
- */
\ No newline at end of file
+ if(pos) {
+ var m = this.getTransform().getTranslation();
+ var am = this.getAbsoluteTransform().getTranslation();
+ var ap = this.getAbsolutePosition();
+ dd.node = this;
+ dd.offset.x = pos.x - ap.x;
+ dd.offset.y = pos.y - ap.y;
+
+ /*
+ * if dragging and dropping the stage,
+ * draw all of the layers
+ */
+ if(this.nodeType === 'Stage') {
+ dd.anim.node = this;
+ }
+ else {
+ dd.anim.node = this.getLayer();
+ }
+ dd.anim.start();
+ }
+ };
+ Kinetic.Node.prototype._dragChange = function() {
+ if(this.attrs.draggable) {
+ this._listenDrag();
+ }
+ else {
+ // remove event listeners
+ this._dragCleanup();
+
+ /*
+ * force drag and drop to end
+ * if this node is currently in
+ * drag and drop mode
+ */
+ var stage = this.getStage();
+ var dd = Kinetic.DD;
+ if(stage && dd.node && dd.node._id === this._id) {
+ dd._endDrag();
+ }
+ }
+ };
+ Kinetic.Node.prototype._dragCleanup = function() {
+ this.off('mousedown.kinetic');
+ this.off('touchstart.kinetic');
+ };
+ /**
+ * get draggable. Alias of getDraggable()
+ * @name isDraggable
+ * @methodOf Kinetic.Node.prototype
+ */
+ Kinetic.Node.prototype.isDraggable = Kinetic.Node.prototype.getDraggable;
+
+ Kinetic.Node.addGettersSetters(Kinetic.Node, ['dragBoundFunc']);
+
+ /**
+ * set drag bound function. This is used to override the default
+ * drag and drop position
+ * @name setDragBoundFunc
+ * @methodOf Kinetic.Node.prototype
+ * @param {Function} dragBoundFunc
+ */
+
+ /**
+ * get dragBoundFunc
+ * @name getDragBoundFunc
+ * @methodOf Kinetic.Node.prototype
+ */
+})();
diff --git a/src/Global.js b/src/Global.js
index 2bf51747..f3cc72c8 100644
--- a/src/Global.js
+++ b/src/Global.js
@@ -1,5 +1,5 @@
/**
- * KineticJS JavaScript Library @version
+ * KineticJS JavaScript Library v@version
* http://www.kineticjs.com/
* Copyright 2012, Eric Rowell
* Licensed under the MIT or GPL Version 2 licenses.
@@ -26,53 +26,116 @@
* THE SOFTWARE.
*/
/**
- * @namespace
+ * @namespace
*/
-var Kinetic = {};
-/**
- * @namespace
- */
-Kinetic.Filters = {};
-Kinetic.Plugins = {};
-Kinetic.Global = {
- BUFFER_WHITELIST: ['fill', 'stroke', 'textFill', 'textStroke'],
- BUFFER_BLACKLIST: ['shadow'],
- stages: [],
- idCounter: 0,
- tempNodes: {},
- //shapes hash. rgb keys and shape values
- shapes: {},
- 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];
+var Kinetic = {}; (function() {
+ Kinetic.version = '@version';
+ /**
+ * @namespace
+ */
+ Kinetic.Filters = {};
+ Kinetic.Plugins = {};
+ Kinetic.Global = {
+ stages: [],
+ idCounter: 0,
+ tempNodes: {},
+ //shapes hash. rgb keys and shape values
+ shapes: {},
+ 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);
}
- }
- },
- _pullNodes: function(stage) {
- var tempNodes = this.tempNodes;
- for(var key in tempNodes) {
- var node = tempNodes[key];
- if(node.getStage() !== undefined && node.getStage()._id === stage._id) {
- stage._addId(node);
- stage._addName(node);
- this._removeTempNode(node);
+ },
+ extend: function(c1, c2) {
+ for(var key in c2.prototype) {
+ if(!( key in c1.prototype)) {
+ c1.prototype[key] = c2.prototype[key];
+ }
}
+ },
+ _pullNodes: function(stage) {
+ var tempNodes = this.tempNodes;
+ for(var key in tempNodes) {
+ var node = tempNodes[key];
+ if(node.getStage() !== undefined && node.getStage()._id === stage._id) {
+ stage._addId(node);
+ stage._addName(node);
+ this._removeTempNode(node);
+ }
+ }
+ },
+ _addTempNode: function(node) {
+ this.tempNodes[node._id] = node;
+ },
+ _removeTempNode: function(node) {
+ delete this.tempNodes[node._id];
}
- },
- _addTempNode: function(node) {
- this.tempNodes[node._id] = node;
- },
- _removeTempNode: function(node) {
- delete this.tempNodes[node._id];
+ };
+})();
+
+// Uses Node, AMD or browser globals to create a module.
+
+// If you want something that will work in other stricter CommonJS environments,
+// or if you need to create a circular dependency, see commonJsStrict.js
+
+// Defines a module "returnExports" that depends another module called "b".
+// Note that the name of the module is implied by the file name. It is best
+// if the file name and the exported global have matching names.
+
+// If the 'b' module also uses this type of boilerplate, then
+// in the browser, it will create a global .b that is used below.
+
+// If you do not want to support the browser global path, then you
+// can remove the `root` use and the passing `this` as the first arg to
+// the top function.
+
+( function(root, factory) {
+ if( typeof exports === 'object') {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like enviroments that support module.exports,
+ // like Node.
+ module.exports = factory(require('b'));
}
-};
+ else if( typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['b'], factory);
+ }
+ else {
+ // Browser globals (root is window)
+ root.returnExports = factory(root.b);
+ }
+}(this, function(b) {
+ //use b in some fashion.
+
+ // Just return a value to define the module export.
+ // This example returns an object, but the module
+ // can return a function as the exported value.
+ return {};
+}));
+// if the module has no dependencies, the above pattern can be simplified to
+( function(root, factory) {
+ if( typeof exports === 'object') {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like enviroments that support module.exports,
+ // like Node.
+ module.exports = factory();
+ }
+ else if( typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(factory);
+ }
+ else {
+ // Browser globals (root is window)
+ root.returnExports = factory();
+ }
+}(this, function() {
+
+ // Just return a value to define the module export.
+ // This example returns an object, but the module
+ // can return a function as the exported value.
+ return Kinetic;
+}));
diff --git a/src/Group.js b/src/Group.js
index f3358627..6fbb72a1 100644
--- a/src/Group.js
+++ b/src/Group.js
@@ -1,36 +1,38 @@
-/**
- * Group constructor. Groups are used to contain shapes or other groups.
- * @constructor
- * @augments Kinetic.Container
- * @param {Object} config
- * @param {Number} [config.x]
- * @param {Number} [config.y]
- * @param {Boolean} [config.visible]
- * @param {Boolean} [config.listening] whether or not the node is listening for events
- * @param {String} [config.id] unique id
- * @param {String} [config.name] non-unique name
- * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1
- * @param {Object} [config.scale]
- * @param {Number} [config.scale.x]
- * @param {Number} [config.scale.y]
- * @param {Number} [config.rotation] rotation in radians
- * @param {Number} [config.rotationDeg] rotation in degrees
- * @param {Object} [config.offset] offsets default position point and rotation point
- * @param {Number} [config.offset.x]
- * @param {Number} [config.offset.y]
- * @param {Boolean} [config.draggable]
- * @param {Function} [config.dragBoundFunc] dragBoundFunc(pos, evt) should return new position
- */
-Kinetic.Group = function(config) {
- this._initGroup(config);
-};
+(function() {
+ /**
+ * Group constructor. Groups are used to contain shapes or other groups.
+ * @constructor
+ * @augments Kinetic.Container
+ * @param {Object} config
+ * @param {Number} [config.x]
+ * @param {Number} [config.y]
+ * @param {Boolean} [config.visible]
+ * @param {Boolean} [config.listening] whether or not the node is listening for events
+ * @param {String} [config.id] unique id
+ * @param {String} [config.name] non-unique name
+ * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1
+ * @param {Object} [config.scale]
+ * @param {Number} [config.scale.x]
+ * @param {Number} [config.scale.y]
+ * @param {Number} [config.rotation] rotation in radians
+ * @param {Number} [config.rotationDeg] rotation in degrees
+ * @param {Object} [config.offset] offsets default position point and rotation point
+ * @param {Number} [config.offset.x]
+ * @param {Number} [config.offset.y]
+ * @param {Boolean} [config.draggable]
+ * @param {Function} [config.dragBoundFunc] dragBoundFunc(pos, evt) should return new position
+ */
+ Kinetic.Group = function(config) {
+ this._initGroup(config);
+ };
-Kinetic.Group.prototype = {
- _initGroup: function(config) {
- this.nodeType = 'Group';
+ Kinetic.Group.prototype = {
+ _initGroup: function(config) {
+ this.nodeType = 'Group';
- // call super constructor
- Kinetic.Container.call(this, config);
- }
-};
-Kinetic.Global.extend(Kinetic.Group, Kinetic.Container);
+ // call super constructor
+ Kinetic.Container.call(this, config);
+ }
+ };
+ Kinetic.Global.extend(Kinetic.Group, Kinetic.Container);
+})();
diff --git a/src/Layer.js b/src/Layer.js
index 86947601..06d8afe0 100644
--- a/src/Layer.js
+++ b/src/Layer.js
@@ -1,280 +1,282 @@
-/**
- * Layer constructor. Layers are tied to their own canvas element and are used
- * to contain groups or shapes
- * @constructor
- * @augments Kinetic.Container
- * @param {Object} config
- * @param {Boolean} [config.clearBeforeDraw] set this property to true if you'd like to disable
- * canvas clearing before each new layer draw
- * @param {Number} [config.x]
- * @param {Number} [config.y]
- * @param {Boolean} [config.visible]
- * @param {Boolean} [config.listening] whether or not the node is listening for events
- * @param {String} [config.id] unique id
- * @param {String} [config.name] non-unique name
- * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1
- * @param {Object} [config.scale]
- * @param {Number} [config.scale.x]
- * @param {Number} [config.scale.y]
- * @param {Number} [config.rotation] rotation in radians
- * @param {Number} [config.rotationDeg] rotation in degrees
- * @param {Object} [config.offset] offsets default position point and rotation point
- * @param {Number} [config.offset.x]
- * @param {Number} [config.offset.y]
- * @param {Boolean} [config.draggable]
- * @param {Function} [config.dragBoundFunc] dragBoundFunc(pos, evt) should return new position
- */
-Kinetic.Layer = function(config) {
- this._initLayer(config);
-};
-
-Kinetic.Layer.prototype = {
- _initLayer: function(config) {
- this.setDefaultAttrs({
- clearBeforeDraw: true
- });
-
- this.nodeType = 'Layer';
- this.beforeDrawFunc = undefined;
- this.afterDrawFunc = undefined;
- this.canvas = new Kinetic.Canvas();
- this.canvas.getElement().style.position = 'absolute';
- this.hitCanvas = new Kinetic.Canvas(0, 0, true);
-
- // call super constructor
- Kinetic.Container.call(this, config);
- },
+(function() {
/**
- * draw children nodes. this includes any groups
- * or shapes
- * @name draw
- * @methodOf Kinetic.Layer.prototype
+ * Layer constructor. Layers are tied to their own canvas element and are used
+ * to contain groups or shapes
+ * @constructor
+ * @augments Kinetic.Container
+ * @param {Object} config
+ * @param {Boolean} [config.clearBeforeDraw] set this property to true if you'd like to disable
+ * canvas clearing before each new layer draw
+ * @param {Number} [config.x]
+ * @param {Number} [config.y]
+ * @param {Boolean} [config.visible]
+ * @param {Boolean} [config.listening] whether or not the node is listening for events
+ * @param {String} [config.id] unique id
+ * @param {String} [config.name] non-unique name
+ * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1
+ * @param {Object} [config.scale]
+ * @param {Number} [config.scale.x]
+ * @param {Number} [config.scale.y]
+ * @param {Number} [config.rotation] rotation in radians
+ * @param {Number} [config.rotationDeg] rotation in degrees
+ * @param {Object} [config.offset] offsets default position point and rotation point
+ * @param {Number} [config.offset.x]
+ * @param {Number} [config.offset.y]
+ * @param {Boolean} [config.draggable]
+ * @param {Function} [config.dragBoundFunc] dragBoundFunc(pos, evt) should return new position
*/
- draw: function() {
- // before draw handler
- if(this.beforeDrawFunc !== undefined) {
- this.beforeDrawFunc.call(this);
- }
+ Kinetic.Layer = function(config) {
+ this._initLayer(config);
+ };
- Kinetic.Container.prototype.draw.call(this);
+ Kinetic.Layer.prototype = {
+ _initLayer: function(config) {
+ this.setDefaultAttrs({
+ clearBeforeDraw: true
+ });
- // after draw handler
- if(this.afterDrawFunc !== undefined) {
- this.afterDrawFunc.call(this);
- }
- },
- /**
- * draw children nodes on hit. this includes any groups
- * or shapes
- * @name drawHit
- * @methodOf Kinetic.Layer.prototype
- */
- drawHit: function() {
- this.hitCanvas.clear();
- Kinetic.Container.prototype.drawHit.call(this);
- },
- /**
- * draw children nodes on scene. this includes any groups
- * or shapes
- * @name drawScene
- * @methodOf Kinetic.Layer.prototype
- * @param {Kinetic.Canvas} [canvas]
- */
- drawScene: function() {
- if(this.attrs.clearBeforeDraw) {
+ this.nodeType = 'Layer';
+ this.beforeDrawFunc = undefined;
+ this.afterDrawFunc = undefined;
+ this.canvas = new Kinetic.Canvas();
+ this.canvas.getElement().style.position = 'absolute';
+ this.hitCanvas = new Kinetic.Canvas(0, 0, true);
+
+ // call super constructor
+ Kinetic.Container.call(this, config);
+ },
+ /**
+ * draw children nodes. this includes any groups
+ * or shapes
+ * @name draw
+ * @methodOf Kinetic.Layer.prototype
+ */
+ draw: function() {
+ // before draw handler
+ if(this.beforeDrawFunc !== undefined) {
+ this.beforeDrawFunc.call(this);
+ }
+
+ Kinetic.Container.prototype.draw.call(this);
+
+ // after draw handler
+ if(this.afterDrawFunc !== undefined) {
+ this.afterDrawFunc.call(this);
+ }
+ },
+ /**
+ * draw children nodes on hit. this includes any groups
+ * or shapes
+ * @name drawHit
+ * @methodOf Kinetic.Layer.prototype
+ */
+ drawHit: function() {
+ this.hitCanvas.clear();
+ Kinetic.Container.prototype.drawHit.call(this);
+ },
+ /**
+ * draw children nodes on scene. this includes any groups
+ * or shapes
+ * @name drawScene
+ * @methodOf Kinetic.Layer.prototype
+ * @param {Kinetic.Canvas} [canvas]
+ */
+ drawScene: function() {
+ if(this.attrs.clearBeforeDraw) {
+ this.getCanvas().clear();
+ }
+ Kinetic.Container.prototype.drawScene.call(this);
+ },
+ /**
+ * set before draw handler
+ * @name beforeDraw
+ * @methodOf Kinetic.Layer.prototype
+ * @param {Function} handler
+ */
+ beforeDraw: function(func) {
+ this.beforeDrawFunc = func;
+ },
+ /**
+ * set after draw handler
+ * @name afterDraw
+ * @methodOf Kinetic.Layer.prototype
+ * @param {Function} handler
+ */
+ afterDraw: function(func) {
+ this.afterDrawFunc = func;
+ },
+ /**
+ * get layer canvas
+ * @name getCanvas
+ * @methodOf Kinetic.Layer.prototype
+ */
+ getCanvas: function() {
+ return this.canvas;
+ },
+ /**
+ * get layer canvas context
+ * @name getContext
+ * @methodOf Kinetic.Layer.prototype
+ */
+ getContext: function() {
+ return this.canvas.context;
+ },
+ /**
+ * clear canvas tied to the layer
+ * @name clear
+ * @methodOf Kinetic.Layer.prototype
+ */
+ clear: function() {
this.getCanvas().clear();
- }
- Kinetic.Container.prototype.drawScene.call(this);
- },
- /**
- * set before draw handler
- * @name beforeDraw
- * @methodOf Kinetic.Layer.prototype
- * @param {Function} handler
- */
- beforeDraw: function(func) {
- this.beforeDrawFunc = func;
- },
- /**
- * set after draw handler
- * @name afterDraw
- * @methodOf Kinetic.Layer.prototype
- * @param {Function} handler
- */
- afterDraw: function(func) {
- this.afterDrawFunc = func;
- },
- /**
- * get layer canvas
- * @name getCanvas
- * @methodOf Kinetic.Layer.prototype
- */
- getCanvas: function() {
- return this.canvas;
- },
- /**
- * get layer canvas context
- * @name getContext
- * @methodOf Kinetic.Layer.prototype
- */
- getContext: function() {
- return this.canvas.context;
- },
- /**
- * clear canvas tied to the layer
- * @name clear
- * @methodOf Kinetic.Layer.prototype
- */
- clear: function() {
- this.getCanvas().clear();
- },
- // extenders
- setVisible: function(visible) {
- Kinetic.Node.prototype.setVisible.call(this, visible);
- if(visible) {
- this.canvas.element.style.display = 'block';
- this.hitCanvas.element.style.display = 'block';
- }
- else {
- this.canvas.element.style.display = 'none';
- this.hitCanvas.element.style.display = 'none';
- }
- },
- setZIndex: function(index) {
- Kinetic.Node.prototype.setZIndex.call(this, index);
- var stage = this.getStage();
- if(stage) {
- stage.content.removeChild(this.canvas.element);
-
- if(index < stage.getChildren().length - 1) {
- stage.content.insertBefore(this.canvas.element, stage.getChildren()[index + 1].canvas.element);
+ },
+ // extenders
+ setVisible: function(visible) {
+ Kinetic.Node.prototype.setVisible.call(this, visible);
+ if(visible) {
+ this.canvas.element.style.display = 'block';
+ this.hitCanvas.element.style.display = 'block';
}
else {
- stage.content.appendChild(this.canvas.element);
+ this.canvas.element.style.display = 'none';
+ this.hitCanvas.element.style.display = 'none';
}
- }
- },
- moveToTop: function() {
- Kinetic.Node.prototype.moveToTop.call(this);
- var stage = this.getStage();
- if(stage) {
- stage.content.removeChild(this.canvas.element);
- stage.content.appendChild(this.canvas.element);
- }
- },
- moveUp: function() {
- if(Kinetic.Node.prototype.moveUp.call(this)) {
+ },
+ setZIndex: function(index) {
+ Kinetic.Node.prototype.setZIndex.call(this, index);
var stage = this.getStage();
if(stage) {
stage.content.removeChild(this.canvas.element);
- if(this.index < stage.getChildren().length - 1) {
- stage.content.insertBefore(this.canvas.element, stage.getChildren()[this.index + 1].canvas.element);
+ if(index < stage.getChildren().length - 1) {
+ stage.content.insertBefore(this.canvas.element, stage.getChildren()[index + 1].canvas.element);
}
else {
stage.content.appendChild(this.canvas.element);
}
}
- }
- },
- moveDown: function() {
- if(Kinetic.Node.prototype.moveDown.call(this)) {
+ },
+ moveToTop: function() {
+ Kinetic.Node.prototype.moveToTop.call(this);
var stage = this.getStage();
if(stage) {
- var children = stage.getChildren();
stage.content.removeChild(this.canvas.element);
- stage.content.insertBefore(this.canvas.element, children[this.index + 1].canvas.element);
+ stage.content.appendChild(this.canvas.element);
}
- }
- },
- moveToBottom: function() {
- if(Kinetic.Node.prototype.moveToBottom.call(this)) {
+ },
+ moveUp: function() {
+ if(Kinetic.Node.prototype.moveUp.call(this)) {
+ var stage = this.getStage();
+ if(stage) {
+ stage.content.removeChild(this.canvas.element);
+
+ if(this.index < stage.getChildren().length - 1) {
+ stage.content.insertBefore(this.canvas.element, stage.getChildren()[this.index + 1].canvas.element);
+ }
+ else {
+ stage.content.appendChild(this.canvas.element);
+ }
+ }
+ }
+ },
+ moveDown: function() {
+ if(Kinetic.Node.prototype.moveDown.call(this)) {
+ var stage = this.getStage();
+ if(stage) {
+ var children = stage.getChildren();
+ stage.content.removeChild(this.canvas.element);
+ stage.content.insertBefore(this.canvas.element, children[this.index + 1].canvas.element);
+ }
+ }
+ },
+ moveToBottom: function() {
+ if(Kinetic.Node.prototype.moveToBottom.call(this)) {
+ var stage = this.getStage();
+ if(stage) {
+ var children = stage.getChildren();
+ stage.content.removeChild(this.canvas.element);
+ stage.content.insertBefore(this.canvas.element, children[1].canvas.element);
+ }
+ }
+ },
+ getLayer: function() {
+ return this;
+ },
+ /**
+ * Creates a composite data URL. If MIME type is not
+ * specified, then "image/png" will result. For "image/jpeg", specify a quality
+ * level as quality (range 0.0 - 1.0). Note that this method works
+ * differently from toDataURL() for other nodes because it generates an absolute dataURL
+ * based on what's currently drawn on the layer, rather than drawing
+ * the current state of each child node
+ * @name toDataURL
+ * @methodOf Kinetic.Layer.prototype
+ * @param {Object} config
+ * @param {String} [config.mimeType] mime type. can be "image/png" or "image/jpeg".
+ * "image/png" is the default
+ * @param {Number} [config.width] data url image width
+ * @param {Number} [config.height] data url image height
+ * @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType,
+ * you can specify the quality from 0 to 1, where 0 is very poor quality and 1
+ * is very high quality
+ */
+ toDataURL: function(config) {
+ var canvas;
+ var mimeType = config && config.mimeType ? config.mimeType : null;
+ var quality = config && config.quality ? config.quality : null;
+
+ /*
+ * if layer is hidden, return blank canvas
+ * else if width and height are defined, create blank canvas and draw onto it
+ * else return canvas as is
+ */
+ if(!this.isVisible()) {
+ var stage = this.getStage();
+ canvas = new Kinetic.Canvas(stage.getWidth(), stage.getHeight());
+ }
+ else if(config && config.width && config.height) {
+ canvas = new Kinetic.Canvas(config.width, config.height);
+ this.draw(canvas);
+ }
+ else {
+ canvas = this.getCanvas();
+ }
+ return canvas.toDataURL(mimeType, quality);
+ },
+ /**
+ * remove layer from stage
+ */
+ remove: function() {
var stage = this.getStage();
- if(stage) {
- var children = stage.getChildren();
+ Kinetic.Node.prototype.remove.call(this);
+ /*
+ * remove canvas DOM from the document if
+ * it exists
+ */
+ try {
stage.content.removeChild(this.canvas.element);
- stage.content.insertBefore(this.canvas.element, children[1].canvas.element);
+ }
+ catch(e) {
+ Kinetic.Global.warn('unable to remove layer scene canvas element from the document');
}
}
- },
- getLayer: function() {
- return this;
- },
+ };
+ Kinetic.Global.extend(Kinetic.Layer, Kinetic.Container);
+
+ // add getters and setters
+ Kinetic.Node.addGettersSetters(Kinetic.Layer, ['clearBeforeDraw']);
+
/**
- * Creates a composite data URL. If MIME type is not
- * specified, then "image/png" will result. For "image/jpeg", specify a quality
- * level as quality (range 0.0 - 1.0). Note that this method works
- * differently from toDataURL() for other nodes because it generates an absolute dataURL
- * based on what's currently drawn on the layer, rather than drawing
- * the current state of each child node
- * @name toDataURL
+ * set flag which determines if the layer is cleared or not
+ * before drawing
+ * @name setClearBeforeDraw
* @methodOf Kinetic.Layer.prototype
- * @param {Object} config
- * @param {String} [config.mimeType] mime type. can be "image/png" or "image/jpeg".
- * "image/png" is the default
- * @param {Number} [config.width] data url image width
- * @param {Number} [config.height] data url image height
- * @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType,
- * you can specify the quality from 0 to 1, where 0 is very poor quality and 1
- * is very high quality
+ * @param {Boolean} clearBeforeDraw
*/
- toDataURL: function(config) {
- var canvas;
- var mimeType = config && config.mimeType ? config.mimeType : null;
- var quality = config && config.quality ? config.quality : null;
- /*
- * if layer is hidden, return blank canvas
- * else if width and height are defined, create blank canvas and draw onto it
- * else return canvas as is
- */
- if(!this.isVisible()) {
- var stage = this.getStage();
- canvas = new Kinetic.Canvas(stage.getWidth(), stage.getHeight());
- }
- else if(config && config.width && config.height) {
- canvas = new Kinetic.Canvas(config.width, config.height);
- this.draw(canvas);
- }
- else {
- canvas = this.getCanvas();
- }
- return canvas.toDataURL(mimeType, quality);
- },
/**
- * remove layer from stage
+ * get flag which determines if the layer is cleared or not
+ * before drawing
+ * @name getClearBeforeDraw
+ * @methodOf Kinetic.Layer.prototype
*/
- remove: function() {
- var stage = this.getStage();
- Kinetic.Node.prototype.remove.call(this);
- /*
- * remove canvas DOM from the document if
- * it exists
- */
- try {
- stage.content.removeChild(this.canvas.element);
- }
- catch(e) {
- Kinetic.Global.warn('unable to remove layer scene canvas element from the document');
- }
- }
-};
-Kinetic.Global.extend(Kinetic.Layer, Kinetic.Container);
-
-// add getters and setters
-Kinetic.Node.addGettersSetters(Kinetic.Layer, ['clearBeforeDraw']);
-
-/**
- * set flag which determines if the layer is cleared or not
- * before drawing
- * @name setClearBeforeDraw
- * @methodOf Kinetic.Layer.prototype
- * @param {Boolean} clearBeforeDraw
- */
-
-/**
- * get flag which determines if the layer is cleared or not
- * before drawing
- * @name getClearBeforeDraw
- * @methodOf Kinetic.Layer.prototype
- */
\ No newline at end of file
+})();
diff --git a/src/Stage.js b/src/Stage.js
index 7796e51d..2a0e6de1 100644
--- a/src/Stage.js
+++ b/src/Stage.js
@@ -1,652 +1,654 @@
-/**
- * Stage constructor. A stage is used to contain multiple layers
- * @constructor
- * @augments Kinetic.Container
- * @param {Object} config
- * @param {String|DomElement} config.container Container id or DOM element
- * @param {Number} config.width
- * @param {Number} config.height
- * @param {Number} [config.x]
- * @param {Number} [config.y]
- * @param {Boolean} [config.visible]
- * @param {Boolean} [config.listening] whether or not the node is listening for events
- * @param {String} [config.id] unique id
- * @param {String} [config.name] non-unique name
- * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1
- * @param {Object} [config.scale]
- * @param {Number} [config.scale.x]
- * @param {Number} [config.scale.y]
- * @param {Number} [config.rotation] rotation in radians
- * @param {Number} [config.rotationDeg] rotation in degrees
- * @param {Object} [config.offset] offsets default position point and rotation point
- * @param {Number} [config.offset.x]
- * @param {Number} [config.offset.y]
- * @param {Boolean} [config.draggable]
- * @param {Function} [config.dragBoundFunc] dragBoundFunc(pos, evt) should return new position
- */
-Kinetic.Stage = function(config) {
- this._initStage(config);
-};
-
-Kinetic.Stage.prototype = {
- _initStage: function(config) {
- this.setDefaultAttrs({
- width: 400,
- height: 200
- });
-
- // call super constructor
- Kinetic.Container.call(this, config);
-
- this._setStageDefaultProperties();
- this._id = Kinetic.Global.idCounter++;
- this._buildDOM();
- this._bindContentEvents();
-
- var go = Kinetic.Global;
- go.stages.push(this);
- this._addId(this);
- this._addName(this);
-
- },
- setContainer: function(container) {
- /*
- * if container is a string, assume it's an id for
- * a DOM element
- */
- if( typeof container === 'string') {
- container = document.getElementById(container);
- }
- this.setAttr('container', container);
- },
+(function() {
/**
- * draw layer scenes
- * @name draw
- * @methodOf Kinetic.Stage.prototype
- */
-
- /**
- * draw layer hits
- * @name drawHit
- * @methodOf Kinetic.Stage.prototype
- */
-
- /**
- * set height
- * @name setHeight
- * @methodOf Kinetic.Stage.prototype
- * @param {Number} height
- */
- setHeight: function(height) {
- Kinetic.Node.prototype.setHeight.call(this, height);
- this._resizeDOM();
- },
- /**
- * set width
- * @name setWidth
- * @methodOf Kinetic.Stage.prototype
- * @param {Number} width
- */
- setWidth: function(width) {
- Kinetic.Node.prototype.setWidth.call(this, width);
- this._resizeDOM();
- },
- /**
- * clear all layers
- * @name clear
- * @methodOf Kinetic.Stage.prototype
- */
- clear: function() {
- var layers = this.children;
- for(var n = 0; n < layers.length; n++) {
- layers[n].clear();
- }
- },
- /**
- * reset stage to default state
- * @name reset
- * @methodOf Kinetic.Stage.prototype
- */
- reset: function() {
- // remove children
- this.removeChildren();
-
- // defaults
- this._setStageDefaultProperties();
- this.setAttrs(this.defaultNodeAttrs);
- },
- /**
- * get mouse position for desktop apps
- * @name getMousePosition
- * @methodOf Kinetic.Stage.prototype
- * @param {Event} evt
- */
- getMousePosition: function(evt) {
- return this.mousePos;
- },
- /**
- * get touch position for mobile apps
- * @name getTouchPosition
- * @methodOf Kinetic.Stage.prototype
- * @param {Event} evt
- */
- getTouchPosition: function(evt) {
- return this.touchPos;
- },
- /**
- * get user position (mouse position or touch position)
- * @name getUserPosition
- * @methodOf Kinetic.Stage.prototype
- * @param {Event} evt
- */
- getUserPosition: function(evt) {
- return this.getTouchPosition() || this.getMousePosition();
- },
- /**
- * get stage
- * @name getStage
- * @methodOf Kinetic.Stage.prototype
- */
- getStage: function() {
- return this;
- },
- /**
- * get stage DOM node, which is a div element
- * with the class name "kineticjs-content"
- * @name getDOM
- * @methodOf Kinetic.Stage.prototype
- */
- getDOM: function() {
- return this.content;
- },
- /**
- * Creates a composite data URL and requires a callback because the stage
- * toDataURL method is asynchronous. If MIME type is not
- * specified, then "image/png" will result. For "image/jpeg", specify a quality
- * level as quality (range 0.0 - 1.0). Note that this method works
- * differently from toDataURL() for other nodes because it generates an absolute dataURL
- * based on what's draw onto the canvases for each layer, rather than drawing
- * the current state of each node
- * @name toDataURL
- * @methodOf Kinetic.Stage.prototype
+ * Stage constructor. A stage is used to contain multiple layers
+ * @constructor
+ * @augments Kinetic.Container
* @param {Object} config
- * @param {Function} config.callback since the stage toDataURL() method is asynchronous,
- * the data url string will be passed into the callback
- * @param {String} [config.mimeType] mime type. can be "image/png" or "image/jpeg".
- * "image/png" is the default
- * @param {Number} [config.width] data url image width
- * @param {Number} [config.height] data url image height
- * @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType,
- * you can specify the quality from 0 to 1, where 0 is very poor quality and 1
- * is very high quality
+ * @param {String|DomElement} config.container Container id or DOM element
+ * @param {Number} config.width
+ * @param {Number} config.height
+ * @param {Number} [config.x]
+ * @param {Number} [config.y]
+ * @param {Boolean} [config.visible]
+ * @param {Boolean} [config.listening] whether or not the node is listening for events
+ * @param {String} [config.id] unique id
+ * @param {String} [config.name] non-unique name
+ * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1
+ * @param {Object} [config.scale]
+ * @param {Number} [config.scale.x]
+ * @param {Number} [config.scale.y]
+ * @param {Number} [config.rotation] rotation in radians
+ * @param {Number} [config.rotationDeg] rotation in degrees
+ * @param {Object} [config.offset] offsets default position point and rotation point
+ * @param {Number} [config.offset.x]
+ * @param {Number} [config.offset.y]
+ * @param {Boolean} [config.draggable]
+ * @param {Function} [config.dragBoundFunc] dragBoundFunc(pos, evt) should return new position
*/
- toDataURL: function(config) {
- var mimeType = config && config.mimeType ? config.mimeType : null;
- var quality = config && config.quality ? config.quality : null;
- /*
- * need to create a canvas element rather than using the hit canvas
- * because this method is asynchonous which means that other parts of the
- * code could modify the hit canvas before it's finished
- */
- var width = config && config.width ? config.width : this.attrs.width;
- var height = config && config.height ? config.height : this.attrs.height;
- var canvas = new Kinetic.Canvas(width, height);
- var context = canvas.getContext();
- var layers = this.children;
+ Kinetic.Stage = function(config) {
+ this._initStage(config);
+ };
- function drawLayer(n) {
- var layer = layers[n];
- var layerUrl = layer.toDataURL();
- var imageObj = new Image();
- imageObj.onload = function() {
- context.drawImage(imageObj, 0, 0);
+ Kinetic.Stage.prototype = {
+ _initStage: function(config) {
+ this.setDefaultAttrs({
+ width: 400,
+ height: 200
+ });
- if(n < layers.length - 1) {
- drawLayer(n + 1);
- }
- else {
- config.callback(canvas.toDataURL(mimeType, quality));
- }
- };
- imageObj.src = layerUrl;
- }
- drawLayer(0);
- },
- /**
- * converts stage into an image. Since the stage toImage() method
- * is asynchronous, a callback function is required
- * @name toImage
- * @methodOf Kinetic.Stage.prototype
- * @param {Object} config
- * @param {Function} callback since the toImage() method is asynchonrous, the
- * resulting image object is passed into the callback function
- * @param {String} [config.mimeType] mime type. can be "image/png" or "image/jpeg".
- * "image/png" is the default
- * @param {Number} [config.width] data url image width
- * @param {Number} [config.height] data url image height
- * @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType,
- * you can specify the quality from 0 to 1, where 0 is very poor quality and 1
- * is very high quality
- */
- toImage: function(config) {
- this.toDataURL({
- callback: function(dataUrl) {
- Kinetic.Type._getImage(dataUrl, function(img) {
- config.callback(img);
- });
+ // call super constructor
+ Kinetic.Container.call(this, config);
+
+ this._setStageDefaultProperties();
+ this._id = Kinetic.Global.idCounter++;
+ this._buildDOM();
+ this._bindContentEvents();
+
+ var go = Kinetic.Global;
+ go.stages.push(this);
+ this._addId(this);
+ this._addName(this);
+
+ },
+ setContainer: function(container) {
+ /*
+ * if container is a string, assume it's an id for
+ * a DOM element
+ */
+ if( typeof container === 'string') {
+ container = document.getElementById(container);
}
- });
- },
- /**
- * get intersection object that contains shape and pixel data
- * @name getIntersection
- * @methodOf Kinetic.Stage.prototype
- * @param {Object} pos point object
- */
- getIntersection: function(pos) {
- var shape;
- var layers = this.getChildren();
-
- /*
- * traverse through layers from top to bottom and look
- * for hit detection
+ this.setAttr('container', container);
+ },
+ /**
+ * draw layer scenes
+ * @name draw
+ * @methodOf Kinetic.Stage.prototype
*/
- for(var n = layers.length - 1; n >= 0; n--) {
- var layer = layers[n];
- if(layer.isVisible() && layer.isListening()) {
- var p = layer.hitCanvas.context.getImageData(Math.round(pos.x), Math.round(pos.y), 1, 1).data;
- // this indicates that a hit pixel may have been found
- if(p[3] === 255) {
- var colorKey = Kinetic.Type._rgbToHex(p[0], p[1], p[2]);
- shape = Kinetic.Global.shapes[colorKey];
- return {
- shape: shape,
- pixel: p
- };
- }
- // if no shape mapped to that pixel, return pixel array
- else if(p[0] > 0 || p[1] > 0 || p[2] > 0 || p[3] > 0) {
- return {
- pixel: p
- };
- }
- }
- }
- return null;
- },
- _getNodeById: function(key) {
- return this.ids[key] || null;
- },
- _getNodesByName: function(key) {
- return this.names[key] || [];
- },
- _resizeDOM: function() {
- if(this.content) {
- var width = this.attrs.width;
- var height = this.attrs.height;
+ /**
+ * draw layer hits
+ * @name drawHit
+ * @methodOf Kinetic.Stage.prototype
+ */
- // set content dimensions
- this.content.style.width = width + 'px';
- this.content.style.height = height + 'px';
-
- this.bufferCanvas.setSize(width, height);
- this.hitCanvas.setSize(width, height);
- // set user defined layer dimensions
+ /**
+ * set height
+ * @name setHeight
+ * @methodOf Kinetic.Stage.prototype
+ * @param {Number} height
+ */
+ setHeight: function(height) {
+ Kinetic.Node.prototype.setHeight.call(this, height);
+ this._resizeDOM();
+ },
+ /**
+ * set width
+ * @name setWidth
+ * @methodOf Kinetic.Stage.prototype
+ * @param {Number} width
+ */
+ setWidth: function(width) {
+ Kinetic.Node.prototype.setWidth.call(this, width);
+ this._resizeDOM();
+ },
+ /**
+ * clear all layers
+ * @name clear
+ * @methodOf Kinetic.Stage.prototype
+ */
+ clear: function() {
var layers = this.children;
for(var n = 0; n < layers.length; n++) {
+ layers[n].clear();
+ }
+ },
+ /**
+ * reset stage to default state
+ * @name reset
+ * @methodOf Kinetic.Stage.prototype
+ */
+ reset: function() {
+ // remove children
+ this.removeChildren();
+
+ // defaults
+ this._setStageDefaultProperties();
+ this.setAttrs(this.defaultNodeAttrs);
+ },
+ /**
+ * get mouse position for desktop apps
+ * @name getMousePosition
+ * @methodOf Kinetic.Stage.prototype
+ * @param {Event} evt
+ */
+ getMousePosition: function(evt) {
+ return this.mousePos;
+ },
+ /**
+ * get touch position for mobile apps
+ * @name getTouchPosition
+ * @methodOf Kinetic.Stage.prototype
+ * @param {Event} evt
+ */
+ getTouchPosition: function(evt) {
+ return this.touchPos;
+ },
+ /**
+ * get user position (mouse position or touch position)
+ * @name getUserPosition
+ * @methodOf Kinetic.Stage.prototype
+ * @param {Event} evt
+ */
+ getUserPosition: function(evt) {
+ return this.getTouchPosition() || this.getMousePosition();
+ },
+ /**
+ * get stage
+ * @name getStage
+ * @methodOf Kinetic.Stage.prototype
+ */
+ getStage: function() {
+ return this;
+ },
+ /**
+ * get stage DOM node, which is a div element
+ * with the class name "kineticjs-content"
+ * @name getDOM
+ * @methodOf Kinetic.Stage.prototype
+ */
+ getDOM: function() {
+ return this.content;
+ },
+ /**
+ * Creates a composite data URL and requires a callback because the stage
+ * toDataURL method is asynchronous. If MIME type is not
+ * specified, then "image/png" will result. For "image/jpeg", specify a quality
+ * level as quality (range 0.0 - 1.0). Note that this method works
+ * differently from toDataURL() for other nodes because it generates an absolute dataURL
+ * based on what's draw onto the canvases for each layer, rather than drawing
+ * the current state of each node
+ * @name toDataURL
+ * @methodOf Kinetic.Stage.prototype
+ * @param {Object} config
+ * @param {Function} config.callback since the stage toDataURL() method is asynchronous,
+ * the data url string will be passed into the callback
+ * @param {String} [config.mimeType] mime type. can be "image/png" or "image/jpeg".
+ * "image/png" is the default
+ * @param {Number} [config.width] data url image width
+ * @param {Number} [config.height] data url image height
+ * @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType,
+ * you can specify the quality from 0 to 1, where 0 is very poor quality and 1
+ * is very high quality
+ */
+ toDataURL: function(config) {
+ var mimeType = config && config.mimeType ? config.mimeType : null;
+ var quality = config && config.quality ? config.quality : null;
+ /*
+ * need to create a canvas element rather than using the hit canvas
+ * because this method is asynchonous which means that other parts of the
+ * code could modify the hit canvas before it's finished
+ */
+ var width = config && config.width ? config.width : this.attrs.width;
+ var height = config && config.height ? config.height : this.attrs.height;
+ var canvas = new Kinetic.Canvas(width, height);
+ var context = canvas.getContext();
+ var layers = this.children;
+
+ function drawLayer(n) {
var layer = layers[n];
- layer.getCanvas().setSize(width, height);
- layer.hitCanvas.setSize(width, height);
- layer.draw();
- }
- }
- },
- /**
- * add layer to stage
- * @param {Layer} layer
- */
- add: function(layer) {
- Kinetic.Container.prototype.add.call(this, layer);
- layer.canvas.setSize(this.attrs.width, this.attrs.height);
- layer.hitCanvas.setSize(this.attrs.width, this.attrs.height);
+ var layerUrl = layer.toDataURL();
+ var imageObj = new Image();
+ imageObj.onload = function() {
+ context.drawImage(imageObj, 0, 0);
- // draw layer and append canvas to container
- layer.draw();
- this.content.appendChild(layer.canvas.element);
-
- // chainable
- return this;
- },
- _setUserPosition: function(evt) {
- if(!evt) {
- evt = window.event;
- }
- this._setMousePosition(evt);
- this._setTouchPosition(evt);
- },
- /**
- * begin listening for events by adding event handlers
- * to the container
- */
- _bindContentEvents: function() {
- var go = Kinetic.Global;
- var that = this;
- var events = ['mousedown', 'mousemove', 'mouseup', 'mouseout', 'touchstart', 'touchmove', 'touchend'];
-
- for(var n = 0; n < events.length; n++) {
- var pubEvent = events[n];
- // induce scope
- ( function() {
- var event = pubEvent;
- that.content.addEventListener(event, function(evt) {
- that['_' + event](evt);
- }, false);
- }());
- }
- },
- _mouseout: function(evt) {
- this._setUserPosition(evt);
- var dd = Kinetic.DD;
- // if there's a current target shape, run mouseout handlers
- var targetShape = this.targetShape;
- if(targetShape && (!dd || !dd.moving)) {
- targetShape._handleEvent('mouseout', evt);
- targetShape._handleEvent('mouseleave', evt);
- this.targetShape = null;
- }
- this.mousePos = undefined;
-
- // end drag and drop
- if(dd) {
- dd._endDrag(evt);
- }
- },
- _mousemove: function(evt) {
- this._setUserPosition(evt);
- var dd = Kinetic.DD;
- var obj = this.getIntersection(this.getUserPosition());
-
- if(obj) {
- var shape = obj.shape;
- if(shape) {
- if((!dd || !dd.moving) && obj.pixel[3] === 255 && (!this.targetShape || this.targetShape._id !== shape._id)) {
- if(this.targetShape) {
- this.targetShape._handleEvent('mouseout', evt, shape);
- this.targetShape._handleEvent('mouseleave', evt, shape);
+ if(n < layers.length - 1) {
+ drawLayer(n + 1);
}
- shape._handleEvent('mouseover', evt, this.targetShape);
- shape._handleEvent('mouseenter', evt, this.targetShape);
- this.targetShape = shape;
- }
- else {
- shape._handleEvent('mousemove', evt);
- }
+ else {
+ config.callback(canvas.toDataURL(mimeType, quality));
+ }
+ };
+ imageObj.src = layerUrl;
}
- }
- /*
- * if no shape was detected, clear target shape and try
- * to run mouseout from previous target shape
+ drawLayer(0);
+ },
+ /**
+ * converts stage into an image. Since the stage toImage() method
+ * is asynchronous, a callback function is required
+ * @name toImage
+ * @methodOf Kinetic.Stage.prototype
+ * @param {Object} config
+ * @param {Function} callback since the toImage() method is asynchonrous, the
+ * resulting image object is passed into the callback function
+ * @param {String} [config.mimeType] mime type. can be "image/png" or "image/jpeg".
+ * "image/png" is the default
+ * @param {Number} [config.width] data url image width
+ * @param {Number} [config.height] data url image height
+ * @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType,
+ * you can specify the quality from 0 to 1, where 0 is very poor quality and 1
+ * is very high quality
*/
- else if(this.targetShape && (!dd || !dd.moving)) {
- this.targetShape._handleEvent('mouseout', evt);
- this.targetShape._handleEvent('mouseleave', evt);
- this.targetShape = null;
- }
-
- // start drag and drop
- if(dd) {
- dd._startDrag(evt);
- }
- },
- _mousedown: function(evt) {
- this._setUserPosition(evt);
- var obj = this.getIntersection(this.getUserPosition());
- if(obj && obj.shape) {
- var shape = obj.shape;
- this.clickStart = true;
- shape._handleEvent('mousedown', evt);
- }
-
- //init stage drag and drop
- if(Kinetic.DD && this.attrs.draggable) {
- this._initDrag();
- }
- },
- _mouseup: function(evt) {
- this._setUserPosition(evt);
- var dd = Kinetic.DD;
- var obj = this.getIntersection(this.getUserPosition());
- var that = this;
- if(obj && obj.shape) {
- var shape = obj.shape;
- shape._handleEvent('mouseup', evt);
-
- // detect if click or double click occurred
- if(this.clickStart) {
- /*
- * if dragging and dropping, don't fire click or dbl click
- * event
- */
- if(!dd || !dd.moving || !dd.node) {
- shape._handleEvent('click', evt);
-
- if(this.inDoubleClickWindow) {
- shape._handleEvent('dblclick', evt);
- }
- this.inDoubleClickWindow = true;
- setTimeout(function() {
- that.inDoubleClickWindow = false;
- }, this.dblClickWindow);
+ toImage: function(config) {
+ this.toDataURL({
+ callback: function(dataUrl) {
+ Kinetic.Type._getImage(dataUrl, function(img) {
+ config.callback(img);
+ });
}
- }
- }
- this.clickStart = false;
-
- // end drag and drop
- if(dd) {
- dd._endDrag(evt);
- }
- },
- _touchstart: function(evt) {
- this._setUserPosition(evt);
- evt.preventDefault();
- var obj = this.getIntersection(this.getUserPosition());
-
- if(obj && obj.shape) {
- var shape = obj.shape;
- this.tapStart = true;
- shape._handleEvent('touchstart', evt);
- }
-
- /*
- * init stage drag and drop
+ });
+ },
+ /**
+ * get intersection object that contains shape and pixel data
+ * @name getIntersection
+ * @methodOf Kinetic.Stage.prototype
+ * @param {Object} pos point object
*/
- if(Kinetic.DD && this.attrs.draggable) {
- this._initDrag();
- }
- },
- _touchend: function(evt) {
- this._setUserPosition(evt);
- var dd = Kinetic.DD;
- var obj = this.getIntersection(this.getUserPosition());
- var that = this;
- if(obj && obj.shape) {
- var shape = obj.shape;
- shape._handleEvent('touchend', evt);
+ getIntersection: function(pos) {
+ var shape;
+ var layers = this.getChildren();
- // detect if tap or double tap occurred
- if(this.tapStart) {
- /*
- * if dragging and dropping, don't fire tap or dbltap
- * event
- */
- if(!dd || !dd.moving || !dd.node) {
- shape._handleEvent('tap', evt);
-
- if(this.inDoubleClickWindow) {
- shape._handleEvent('dbltap', evt);
+ /*
+ * traverse through layers from top to bottom and look
+ * for hit detection
+ */
+ for(var n = layers.length - 1; n >= 0; n--) {
+ var layer = layers[n];
+ if(layer.isVisible() && layer.isListening()) {
+ var p = layer.hitCanvas.context.getImageData(Math.round(pos.x), Math.round(pos.y), 1, 1).data;
+ // this indicates that a hit pixel may have been found
+ if(p[3] === 255) {
+ var colorKey = Kinetic.Type._rgbToHex(p[0], p[1], p[2]);
+ shape = Kinetic.Global.shapes[colorKey];
+ return {
+ shape: shape,
+ pixel: p
+ };
+ }
+ // if no shape mapped to that pixel, return pixel array
+ else if(p[0] > 0 || p[1] > 0 || p[2] > 0 || p[3] > 0) {
+ return {
+ pixel: p
+ };
}
- this.inDoubleClickWindow = true;
- setTimeout(function() {
- that.inDoubleClickWindow = false;
- }, this.dblClickWindow);
}
}
- }
- this.tapStart = false;
+ return null;
+ },
+ _getNodeById: function(key) {
+ return this.ids[key] || null;
+ },
+ _getNodesByName: function(key) {
+ return this.names[key] || [];
+ },
+ _resizeDOM: function() {
+ if(this.content) {
+ var width = this.attrs.width;
+ var height = this.attrs.height;
- // end drag and drop
- if(dd) {
- dd._endDrag(evt);
- }
- },
- _touchmove: function(evt) {
- this._setUserPosition(evt);
- var dd = Kinetic.DD;
- evt.preventDefault();
- var obj = this.getIntersection(this.getUserPosition());
- if(obj && obj.shape) {
- var shape = obj.shape;
- shape._handleEvent('touchmove', evt);
- }
+ // set content dimensions
+ this.content.style.width = width + 'px';
+ this.content.style.height = height + 'px';
- // start drag and drop
- if(dd) {
- dd._startDrag(evt);
- }
- },
- /**
- * set mouse positon for desktop apps
- * @param {Event} evt
- */
- _setMousePosition: function(evt) {
- var mouseX = evt.clientX - this._getContentPosition().left;
- var mouseY = evt.clientY - this._getContentPosition().top;
- this.mousePos = {
- x: mouseX,
- y: mouseY
- };
- },
- /**
- * set touch position for mobile apps
- * @param {Event} evt
- */
- _setTouchPosition: function(evt) {
- if(evt.touches !== undefined && evt.touches.length === 1) {
- // one finger
- var touch = evt.touches[0];
- // Get the information for finger #1
- var touchX = touch.clientX - this._getContentPosition().left;
- var touchY = touch.clientY - this._getContentPosition().top;
+ this.bufferCanvas.setSize(width, height);
+ this.hitCanvas.setSize(width, height);
+ // set user defined layer dimensions
+ var layers = this.children;
+ for(var n = 0; n < layers.length; n++) {
+ var layer = layers[n];
+ layer.getCanvas().setSize(width, height);
+ layer.hitCanvas.setSize(width, height);
+ layer.draw();
+ }
+ }
+ },
+ /**
+ * add layer to stage
+ * @param {Layer} layer
+ */
+ add: function(layer) {
+ Kinetic.Container.prototype.add.call(this, layer);
+ layer.canvas.setSize(this.attrs.width, this.attrs.height);
+ layer.hitCanvas.setSize(this.attrs.width, this.attrs.height);
- this.touchPos = {
- x: touchX,
- y: touchY
+ // draw layer and append canvas to container
+ layer.draw();
+ this.content.appendChild(layer.canvas.element);
+
+ // chainable
+ return this;
+ },
+ _setUserPosition: function(evt) {
+ if(!evt) {
+ evt = window.event;
+ }
+ this._setMousePosition(evt);
+ this._setTouchPosition(evt);
+ },
+ /**
+ * begin listening for events by adding event handlers
+ * to the container
+ */
+ _bindContentEvents: function() {
+ var go = Kinetic.Global;
+ var that = this;
+ var events = ['mousedown', 'mousemove', 'mouseup', 'mouseout', 'touchstart', 'touchmove', 'touchend'];
+
+ for(var n = 0; n < events.length; n++) {
+ var pubEvent = events[n];
+ // induce scope
+ ( function() {
+ var event = pubEvent;
+ that.content.addEventListener(event, function(evt) {
+ that['_' + event](evt);
+ }, false);
+ }());
+ }
+ },
+ _mouseout: function(evt) {
+ this._setUserPosition(evt);
+ var dd = Kinetic.DD;
+ // if there's a current target shape, run mouseout handlers
+ var targetShape = this.targetShape;
+ if(targetShape && (!dd || !dd.moving)) {
+ targetShape._handleEvent('mouseout', evt);
+ targetShape._handleEvent('mouseleave', evt);
+ this.targetShape = null;
+ }
+ this.mousePos = undefined;
+
+ // end drag and drop
+ if(dd) {
+ dd._endDrag(evt);
+ }
+ },
+ _mousemove: function(evt) {
+ this._setUserPosition(evt);
+ var dd = Kinetic.DD;
+ var obj = this.getIntersection(this.getUserPosition());
+
+ if(obj) {
+ var shape = obj.shape;
+ if(shape) {
+ if((!dd || !dd.moving) && obj.pixel[3] === 255 && (!this.targetShape || this.targetShape._id !== shape._id)) {
+ if(this.targetShape) {
+ this.targetShape._handleEvent('mouseout', evt, shape);
+ this.targetShape._handleEvent('mouseleave', evt, shape);
+ }
+ shape._handleEvent('mouseover', evt, this.targetShape);
+ shape._handleEvent('mouseenter', evt, this.targetShape);
+ this.targetShape = shape;
+ }
+ else {
+ shape._handleEvent('mousemove', evt);
+ }
+ }
+ }
+ /*
+ * if no shape was detected, clear target shape and try
+ * to run mouseout from previous target shape
+ */
+ else if(this.targetShape && (!dd || !dd.moving)) {
+ this.targetShape._handleEvent('mouseout', evt);
+ this.targetShape._handleEvent('mouseleave', evt);
+ this.targetShape = null;
+ }
+
+ // start drag and drop
+ if(dd) {
+ dd._startDrag(evt);
+ }
+ },
+ _mousedown: function(evt) {
+ this._setUserPosition(evt);
+ var obj = this.getIntersection(this.getUserPosition());
+ if(obj && obj.shape) {
+ var shape = obj.shape;
+ this.clickStart = true;
+ shape._handleEvent('mousedown', evt);
+ }
+
+ //init stage drag and drop
+ if(Kinetic.DD && this.attrs.draggable) {
+ this._initDrag();
+ }
+ },
+ _mouseup: function(evt) {
+ this._setUserPosition(evt);
+ var dd = Kinetic.DD;
+ var obj = this.getIntersection(this.getUserPosition());
+ var that = this;
+ if(obj && obj.shape) {
+ var shape = obj.shape;
+ shape._handleEvent('mouseup', evt);
+
+ // detect if click or double click occurred
+ if(this.clickStart) {
+ /*
+ * if dragging and dropping, don't fire click or dbl click
+ * event
+ */
+ if(!dd || !dd.moving || !dd.node) {
+ shape._handleEvent('click', evt);
+
+ if(this.inDoubleClickWindow) {
+ shape._handleEvent('dblclick', evt);
+ }
+ this.inDoubleClickWindow = true;
+ setTimeout(function() {
+ that.inDoubleClickWindow = false;
+ }, this.dblClickWindow);
+ }
+ }
+ }
+ this.clickStart = false;
+
+ // end drag and drop
+ if(dd) {
+ dd._endDrag(evt);
+ }
+ },
+ _touchstart: function(evt) {
+ this._setUserPosition(evt);
+ evt.preventDefault();
+ var obj = this.getIntersection(this.getUserPosition());
+
+ if(obj && obj.shape) {
+ var shape = obj.shape;
+ this.tapStart = true;
+ shape._handleEvent('touchstart', evt);
+ }
+
+ /*
+ * init stage drag and drop
+ */
+ if(Kinetic.DD && this.attrs.draggable) {
+ this._initDrag();
+ }
+ },
+ _touchend: function(evt) {
+ this._setUserPosition(evt);
+ var dd = Kinetic.DD;
+ var obj = this.getIntersection(this.getUserPosition());
+ var that = this;
+ if(obj && obj.shape) {
+ var shape = obj.shape;
+ shape._handleEvent('touchend', evt);
+
+ // detect if tap or double tap occurred
+ if(this.tapStart) {
+ /*
+ * if dragging and dropping, don't fire tap or dbltap
+ * event
+ */
+ if(!dd || !dd.moving || !dd.node) {
+ shape._handleEvent('tap', evt);
+
+ if(this.inDoubleClickWindow) {
+ shape._handleEvent('dbltap', evt);
+ }
+ this.inDoubleClickWindow = true;
+ setTimeout(function() {
+ that.inDoubleClickWindow = false;
+ }, this.dblClickWindow);
+ }
+ }
+ }
+
+ this.tapStart = false;
+
+ // end drag and drop
+ if(dd) {
+ dd._endDrag(evt);
+ }
+ },
+ _touchmove: function(evt) {
+ this._setUserPosition(evt);
+ var dd = Kinetic.DD;
+ evt.preventDefault();
+ var obj = this.getIntersection(this.getUserPosition());
+ if(obj && obj.shape) {
+ var shape = obj.shape;
+ shape._handleEvent('touchmove', evt);
+ }
+
+ // start drag and drop
+ if(dd) {
+ dd._startDrag(evt);
+ }
+ },
+ /**
+ * set mouse positon for desktop apps
+ * @param {Event} evt
+ */
+ _setMousePosition: function(evt) {
+ var mouseX = evt.clientX - this._getContentPosition().left;
+ var mouseY = evt.clientY - this._getContentPosition().top;
+ this.mousePos = {
+ x: mouseX,
+ y: mouseY
};
- }
- },
- /**
- * get container position
- */
- _getContentPosition: function() {
- var rect = this.content.getBoundingClientRect();
- return {
- top: rect.top,
- left: rect.left
- };
- },
- /**
- * build dom
- */
- _buildDOM: function() {
- // content
- this.content = document.createElement('div');
- this.content.style.position = 'relative';
- this.content.style.display = 'inline-block';
- this.content.className = 'kineticjs-content';
- this.attrs.container.appendChild(this.content);
+ },
+ /**
+ * set touch position for mobile apps
+ * @param {Event} evt
+ */
+ _setTouchPosition: function(evt) {
+ if(evt.touches !== undefined && evt.touches.length === 1) {
+ // one finger
+ var touch = evt.touches[0];
+ // Get the information for finger #1
+ var touchX = touch.clientX - this._getContentPosition().left;
+ var touchY = touch.clientY - this._getContentPosition().top;
- this.bufferCanvas = new Kinetic.Canvas();
- this.hitCanvas = new Kinetic.Canvas(0, 0, true);
-
- this._resizeDOM();
- },
- _addId: function(node) {
- if(node.attrs.id !== undefined) {
- this.ids[node.attrs.id] = node;
- }
- },
- _removeId: function(id) {
- if(id !== undefined) {
- delete this.ids[id];
- }
- },
- _addName: function(node) {
- var name = node.attrs.name;
- if(name !== undefined) {
- if(this.names[name] === undefined) {
- this.names[name] = [];
+ this.touchPos = {
+ x: touchX,
+ y: touchY
+ };
}
- this.names[name].push(node);
- }
- },
- _removeName: function(name, _id) {
- if(name !== undefined) {
- var nodes = this.names[name];
- if(nodes !== undefined) {
- for(var n = 0; n < nodes.length; n++) {
- var no = nodes[n];
- if(no._id === _id) {
- nodes.splice(n, 1);
+ },
+ /**
+ * get container position
+ */
+ _getContentPosition: function() {
+ var rect = this.content.getBoundingClientRect();
+ return {
+ top: rect.top,
+ left: rect.left
+ };
+ },
+ /**
+ * build dom
+ */
+ _buildDOM: function() {
+ // content
+ this.content = document.createElement('div');
+ this.content.style.position = 'relative';
+ this.content.style.display = 'inline-block';
+ this.content.className = 'kineticjs-content';
+ this.attrs.container.appendChild(this.content);
+
+ this.bufferCanvas = new Kinetic.Canvas();
+ this.hitCanvas = new Kinetic.Canvas(0, 0, true);
+
+ this._resizeDOM();
+ },
+ _addId: function(node) {
+ if(node.attrs.id !== undefined) {
+ this.ids[node.attrs.id] = node;
+ }
+ },
+ _removeId: function(id) {
+ if(id !== undefined) {
+ delete this.ids[id];
+ }
+ },
+ _addName: function(node) {
+ var name = node.attrs.name;
+ if(name !== undefined) {
+ if(this.names[name] === undefined) {
+ this.names[name] = [];
+ }
+ this.names[name].push(node);
+ }
+ },
+ _removeName: function(name, _id) {
+ if(name !== undefined) {
+ var nodes = this.names[name];
+ if(nodes !== undefined) {
+ for(var n = 0; n < nodes.length; n++) {
+ var no = nodes[n];
+ if(no._id === _id) {
+ nodes.splice(n, 1);
+ }
+ }
+ if(nodes.length === 0) {
+ delete this.names[name];
}
}
- if(nodes.length === 0) {
- delete this.names[name];
- }
}
- }
- },
- /**
- * bind event listener to container DOM element
- * @param {String} typesStr
- * @param {function} handler
- */
- _onContent: function(typesStr, handler) {
- var types = typesStr.split(' ');
- for(var n = 0; n < types.length; n++) {
- var baseEvent = types[n];
- this.content.addEventListener(baseEvent, handler, false);
- }
- },
- /**
- * set defaults
- */
- _setStageDefaultProperties: function() {
- this.nodeType = 'Stage';
- this.dblClickWindow = 400;
- this.targetShape = null;
- this.mousePos = undefined;
- this.clickStart = false;
- this.touchPos = undefined;
- this.tapStart = false;
-
- /*
- * ids and names hash needs to be stored at the stage level to prevent
- * id and name collisions between multiple stages in the document
+ },
+ /**
+ * bind event listener to container DOM element
+ * @param {String} typesStr
+ * @param {function} handler
*/
- this.ids = {};
- this.names = {};
- }
-};
-Kinetic.Global.extend(Kinetic.Stage, Kinetic.Container);
+ _onContent: function(typesStr, handler) {
+ var types = typesStr.split(' ');
+ for(var n = 0; n < types.length; n++) {
+ var baseEvent = types[n];
+ this.content.addEventListener(baseEvent, handler, false);
+ }
+ },
+ /**
+ * set defaults
+ */
+ _setStageDefaultProperties: function() {
+ this.nodeType = 'Stage';
+ this.dblClickWindow = 400;
+ this.targetShape = null;
+ this.mousePos = undefined;
+ this.clickStart = false;
+ this.touchPos = undefined;
+ this.tapStart = false;
-// add getters and setters
-Kinetic.Node.addGetters(Kinetic.Stage, ['container']);
+ /*
+ * ids and names hash needs to be stored at the stage level to prevent
+ * id and name collisions between multiple stages in the document
+ */
+ this.ids = {};
+ this.names = {};
+ }
+ };
+ Kinetic.Global.extend(Kinetic.Stage, Kinetic.Container);
-/**
- * get container DOM element
- * @name getContainer
- * @methodOf Kinetic.Stage.prototype
- */
\ No newline at end of file
+ // add getters and setters
+ Kinetic.Node.addGetters(Kinetic.Stage, ['container']);
+
+ /**
+ * get container DOM element
+ * @name getContainer
+ * @methodOf Kinetic.Stage.prototype
+ */
+})();
diff --git a/src/Transition.js b/src/Transition.js
index 9d713858..3aed362a 100644
--- a/src/Transition.js
+++ b/src/Transition.js
@@ -1,147 +1,149 @@
-/**
- * Transition constructor. The transitionTo() Node method
- * returns a reference to the transition object which you can use
- * to stop, resume, or restart the transition
- * @constructor
- */
-Kinetic.Transition = function(node, config) {
- this.node = node;
- this.config = config;
- this.tweens = [];
- var that = this;
+(function() {
+ /**
+ * Transition constructor. The transitionTo() Node method
+ * returns a reference to the transition object which you can use
+ * to stop, resume, or restart the transition
+ * @constructor
+ */
+ Kinetic.Transition = function(node, config) {
+ this.node = node;
+ this.config = config;
+ this.tweens = [];
+ var that = this;
- // add tween for each property
- function addTween(c, attrs, obj, rootObj) {
- for(var key in c) {
- if(key !== 'duration' && key !== 'easing' && key !== 'callback') {
- // if val is an object then traverse
- if(Kinetic.Type._isObject(c[key])) {
- obj[key] = {};
- addTween(c[key], attrs[key], obj[key], rootObj);
- }
- else {
- that._add(that._getTween(attrs, key, c[key], obj, rootObj));
+ // add tween for each property
+ function addTween(c, attrs, obj, rootObj) {
+ for(var key in c) {
+ if(key !== 'duration' && key !== 'easing' && key !== 'callback') {
+ // if val is an object then traverse
+ if(Kinetic.Type._isObject(c[key])) {
+ obj[key] = {};
+ addTween(c[key], attrs[key], obj[key], rootObj);
+ }
+ else {
+ that._add(that._getTween(attrs, key, c[key], obj, rootObj));
+ }
}
}
}
- }
- var obj = {};
- addTween(config, node.attrs, obj, obj);
+ var obj = {};
+ addTween(config, node.attrs, obj, obj);
- var finishedTweens = 0;
- for(var n = 0; n < this.tweens.length; n++) {
- var tween = this.tweens[n];
- tween.onFinished = function() {
- finishedTweens++;
- if(finishedTweens >= that.tweens.length) {
- that.onFinished();
+ var finishedTweens = 0;
+ for(var n = 0; n < this.tweens.length; n++) {
+ var tween = this.tweens[n];
+ tween.onFinished = function() {
+ finishedTweens++;
+ if(finishedTweens >= that.tweens.length) {
+ that.onFinished();
+ }
+ };
+ }
+ };
+ /*
+ * Transition methods
+ */
+ Kinetic.Transition.prototype = {
+ /**
+ * start transition
+ * @name start
+ * @methodOf Kinetic.Transition.prototype
+ */
+ start: function() {
+ for(var n = 0; n < this.tweens.length; n++) {
+ this.tweens[n].start();
+ }
+ },
+ /**
+ * stop transition
+ * @name stop
+ * @methodOf Kinetic.Transition.prototype
+ */
+ stop: function() {
+ for(var n = 0; n < this.tweens.length; n++) {
+ this.tweens[n].stop();
+ }
+ },
+ /**
+ * resume transition
+ * @name resume
+ * @methodOf Kinetic.Transition.prototype
+ */
+ resume: function() {
+ for(var n = 0; n < this.tweens.length; n++) {
+ this.tweens[n].resume();
+ }
+ },
+ _onEnterFrame: function() {
+ for(var n = 0; n < this.tweens.length; n++) {
+ this.tweens[n].onEnterFrame();
+ }
+ },
+ _add: function(tween) {
+ this.tweens.push(tween);
+ },
+ _getTween: function(attrs, prop, val, obj, rootObj) {
+ var config = this.config;
+ var node = this.node;
+ var easing = config.easing;
+ if(easing === undefined) {
+ easing = 'linear';
+ }
+
+ var tween = new Kinetic.Tween(node, function(i) {
+ obj[prop] = i;
+ node.setAttrs(rootObj);
+ }, Kinetic.Tweens[easing], attrs[prop], val, config.duration);
+
+ return tween;
+ }
+ };
+
+ /**
+ * transition node to another state. Any property that can accept a real
+ * number can be transitioned, including x, y, rotation, opacity, strokeWidth,
+ * radius, scale.x, scale.y, offset.x, offset.y, etc.
+ * @name transitionTo
+ * @methodOf Kinetic.Node.prototype
+ * @param {Object} config
+ * @config {Number} duration duration that the transition runs in seconds
+ * @config {String} [easing] easing function. can be linear, ease-in, ease-out, ease-in-out,
+ * back-ease-in, back-ease-out, back-ease-in-out, elastic-ease-in, elastic-ease-out,
+ * elastic-ease-in-out, bounce-ease-out, bounce-ease-in, bounce-ease-in-out,
+ * strong-ease-in, strong-ease-out, or strong-ease-in-out
+ * linear is the default
+ * @config {Function} [callback] callback function to be executed when
+ * transition completes
+ */
+ Kinetic.Node.prototype.transitionTo = function(config) {
+ if(!this.transAnim) {
+ this.transAnim = new Kinetic.Animation();
+ }
+ /*
+ * create new transition
+ */
+ var node = this.nodeType === 'Stage' ? this : this.getLayer();
+ var that = this;
+ var trans = new Kinetic.Transition(this, config);
+
+ this.transAnim.func = function() {
+ trans._onEnterFrame();
+ };
+ this.transAnim.node = node;
+
+ // subscribe to onFinished for first tween
+ trans.onFinished = function() {
+ // remove animation
+ that.transAnim.stop();
+
+ // callback
+ if(config.callback) {
+ config.callback();
}
};
- }
-};
-/*
- * Transition methods
- */
-Kinetic.Transition.prototype = {
- /**
- * start transition
- * @name start
- * @methodOf Kinetic.Transition.prototype
- */
- start: function() {
- for(var n = 0; n < this.tweens.length; n++) {
- this.tweens[n].start();
- }
- },
- /**
- * stop transition
- * @name stop
- * @methodOf Kinetic.Transition.prototype
- */
- stop: function() {
- for(var n = 0; n < this.tweens.length; n++) {
- this.tweens[n].stop();
- }
- },
- /**
- * resume transition
- * @name resume
- * @methodOf Kinetic.Transition.prototype
- */
- resume: function() {
- for(var n = 0; n < this.tweens.length; n++) {
- this.tweens[n].resume();
- }
- },
- _onEnterFrame: function() {
- for(var n = 0; n < this.tweens.length; n++) {
- this.tweens[n].onEnterFrame();
- }
- },
- _add: function(tween) {
- this.tweens.push(tween);
- },
- _getTween: function(attrs, prop, val, obj, rootObj) {
- var config = this.config;
- var node = this.node;
- var easing = config.easing;
- if(easing === undefined) {
- easing = 'linear';
- }
-
- var tween = new Kinetic.Tween(node, function(i) {
- obj[prop] = i;
- node.setAttrs(rootObj);
- }, Kinetic.Tweens[easing], attrs[prop], val, config.duration);
-
- return tween;
- }
-};
-
-/**
- * transition node to another state. Any property that can accept a real
- * number can be transitioned, including x, y, rotation, opacity, strokeWidth,
- * radius, scale.x, scale.y, offset.x, offset.y, etc.
- * @name transitionTo
- * @methodOf Kinetic.Node.prototype
- * @param {Object} config
- * @config {Number} duration duration that the transition runs in seconds
- * @config {String} [easing] easing function. can be linear, ease-in, ease-out, ease-in-out,
- * back-ease-in, back-ease-out, back-ease-in-out, elastic-ease-in, elastic-ease-out,
- * elastic-ease-in-out, bounce-ease-out, bounce-ease-in, bounce-ease-in-out,
- * strong-ease-in, strong-ease-out, or strong-ease-in-out
- * linear is the default
- * @config {Function} [callback] callback function to be executed when
- * transition completes
- */
-Kinetic.Node.prototype.transitionTo = function(config) {
- if(!this.transAnim) {
- this.transAnim = new Kinetic.Animation();
- }
- /*
- * create new transition
- */
- var node = this.nodeType === 'Stage' ? this : this.getLayer();
- var that = this;
- var trans = new Kinetic.Transition(this, config);
-
- this.transAnim.func = function() {
- trans._onEnterFrame();
+ // auto start
+ trans.start();
+ this.transAnim.start();
+ return trans;
};
- this.transAnim.node = node;
-
- // subscribe to onFinished for first tween
- trans.onFinished = function() {
- // remove animation
- that.transAnim.stop();
-
- // callback
- if(config.callback) {
- config.callback();
- }
- };
- // auto start
- trans.start();
- this.transAnim.start();
- return trans;
-};
+})();
diff --git a/src/filters/Brighten.js b/src/filters/Brighten.js
index ad9d7615..167cc627 100644
--- a/src/filters/Brighten.js
+++ b/src/filters/Brighten.js
@@ -1,20 +1,22 @@
-/**
- * Brighten Filter
- * @function
- * @memberOf Kinetic.Filters
- * @param {Object} imageData
- * @param {Object} config
- * @param {Integer} config.val brightness number from -255 to 255. Positive values increase the brightness and negative values decrease the brightness, making the image darker
- */
-Kinetic.Filters.Brighten = function(imageData, config) {
- var brightness = config.val || 0;
- var data = imageData.data;
- for(var i = 0; i < data.length; i += 4) {
- // red
- data[i] += brightness;
- // green
- data[i + 1] += brightness;
- // blue
- data[i + 2] += brightness;
- }
-};
+(function() {
+ /**
+ * Brighten Filter
+ * @function
+ * @memberOf Kinetic.Filters
+ * @param {Object} imageData
+ * @param {Object} config
+ * @param {Integer} config.val brightness number from -255 to 255. Positive values increase the brightness and negative values decrease the brightness, making the image darker
+ */
+ Kinetic.Filters.Brighten = function(imageData, config) {
+ var brightness = config.val || 0;
+ var data = imageData.data;
+ for(var i = 0; i < data.length; i += 4) {
+ // red
+ data[i] += brightness;
+ // green
+ data[i + 1] += brightness;
+ // blue
+ data[i + 2] += brightness;
+ }
+ };
+})();
diff --git a/src/filters/Grayscale.js b/src/filters/Grayscale.js
index 32b14d41..2228a35f 100644
--- a/src/filters/Grayscale.js
+++ b/src/filters/Grayscale.js
@@ -1,19 +1,21 @@
-/**
- * Grayscale Filter
- * @function
- * @memberOf Kinetic.Filters
- * @param {Object} imageData
- * @param {Object} config
- */
-Kinetic.Filters.Grayscale = function(imageData, config) {
- var data = imageData.data;
- for(var i = 0; i < data.length; i += 4) {
- var brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
- // red
- data[i] = brightness;
- // green
- data[i + 1] = brightness;
- // blue
- data[i + 2] = brightness;
- }
-};
+(function() {
+ /**
+ * Grayscale Filter
+ * @function
+ * @memberOf Kinetic.Filters
+ * @param {Object} imageData
+ * @param {Object} config
+ */
+ Kinetic.Filters.Grayscale = function(imageData, config) {
+ var data = imageData.data;
+ for(var i = 0; i < data.length; i += 4) {
+ var brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
+ // red
+ data[i] = brightness;
+ // green
+ data[i + 1] = brightness;
+ // blue
+ data[i + 2] = brightness;
+ }
+ };
+})();
diff --git a/src/filters/Invert.js b/src/filters/Invert.js
index ee9c65a7..515b6fdd 100644
--- a/src/filters/Invert.js
+++ b/src/filters/Invert.js
@@ -1,18 +1,20 @@
-/**
- * Invert Filter
- * @function
- * @memberOf Kinetic.Filters
- * @param {Object} imageData
- * @param {Object} config
- */
-Kinetic.Filters.Invert = function(imageData, config) {
- var data = imageData.data;
- for(var i = 0; i < data.length; i += 4) {
- // red
- data[i] = 255 - data[i];
- // green
- data[i + 1] = 255 - data[i + 1];
- // blue
- data[i + 2] = 255 - data[i + 2];
- }
-};
+(function() {
+ /**
+ * Invert Filter
+ * @function
+ * @memberOf Kinetic.Filters
+ * @param {Object} imageData
+ * @param {Object} config
+ */
+ Kinetic.Filters.Invert = function(imageData, config) {
+ var data = imageData.data;
+ for(var i = 0; i < data.length; i += 4) {
+ // red
+ data[i] = 255 - data[i];
+ // green
+ data[i + 1] = 255 - data[i + 1];
+ // blue
+ data[i + 2] = 255 - data[i + 2];
+ }
+ };
+})();
diff --git a/src/shapes/Circle.js b/src/shapes/Circle.js
index 3bc0bfa1..a48a79ff 100644
--- a/src/shapes/Circle.js
+++ b/src/shapes/Circle.js
@@ -1,60 +1,62 @@
-/**
- * Circle constructor
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.Circle = function(config) {
- this._initCircle(config);
-};
+(function() {
+ /**
+ * Circle constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+ Kinetic.Circle = function(config) {
+ this._initCircle(config);
+ };
-Kinetic.Circle.prototype = {
- _initCircle: function(config) {
- this.setDefaultAttrs({
- radius: 0
- });
+ Kinetic.Circle.prototype = {
+ _initCircle: function(config) {
+ this.setDefaultAttrs({
+ radius: 0
+ });
- this.shapeType = 'Circle';
+ this.shapeType = 'Circle';
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
- },
- drawFunc: function(context) {
- context.beginPath();
- context.arc(0, 0, this.getRadius(), 0, Math.PI * 2, true);
- context.closePath();
- this.fillStroke(context);
- },
- getWidth: function() {
- return this.getRadius() * 2;
- },
- getHeight: function() {
- return this.getRadius() * 2;
- },
- setWidth: function(width) {
- Kinetic.Node.prototype.setWidth.call(this, width);
- this.setRadius(width / 2);
- },
- setHeight: function(height) {
- Kinetic.Node.prototype.setHeight.call(this, height);
- this.setRadius(height / 2);
- }
-};
-Kinetic.Global.extend(Kinetic.Circle, Kinetic.Shape);
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
+ },
+ drawFunc: function(context) {
+ context.beginPath();
+ context.arc(0, 0, this.getRadius(), 0, Math.PI * 2, true);
+ context.closePath();
+ this.fillStroke(context);
+ },
+ getWidth: function() {
+ return this.getRadius() * 2;
+ },
+ getHeight: function() {
+ return this.getRadius() * 2;
+ },
+ setWidth: function(width) {
+ Kinetic.Node.prototype.setWidth.call(this, width);
+ this.setRadius(width / 2);
+ },
+ setHeight: function(height) {
+ Kinetic.Node.prototype.setHeight.call(this, height);
+ this.setRadius(height / 2);
+ }
+ };
+ Kinetic.Global.extend(Kinetic.Circle, Kinetic.Shape);
-// add getters setters
-Kinetic.Node.addGettersSetters(Kinetic.Circle, ['radius']);
+ // add getters setters
+ Kinetic.Node.addGettersSetters(Kinetic.Circle, ['radius']);
-/**
- * set radius
- * @name setRadius
- * @methodOf Kinetic.Circle.prototype
- * @param {Number} radius
- */
+ /**
+ * set radius
+ * @name setRadius
+ * @methodOf Kinetic.Circle.prototype
+ * @param {Number} radius
+ */
-/**
- * get radius
- * @name getRadius
- * @methodOf Kinetic.Circle.prototype
- */
\ No newline at end of file
+ /**
+ * get radius
+ * @name getRadius
+ * @methodOf Kinetic.Circle.prototype
+ */
+})();
diff --git a/src/shapes/Ellipse.js b/src/shapes/Ellipse.js
index 0b345dd6..fe223458 100644
--- a/src/shapes/Ellipse.js
+++ b/src/shapes/Ellipse.js
@@ -1,82 +1,84 @@
-/**
- * Ellipse constructor
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.Ellipse = function(config) {
- this._initEllipse(config);
-};
-
-Kinetic.Ellipse.prototype = {
- _initEllipse: function(config) {
- this.setDefaultAttrs({
- radius: {
- x: 0,
- y: 0
- }
- });
-
- this.shapeType = "Ellipse";
-
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
- },
- drawFunc: function(context) {
- var r = this.getRadius();
- context.beginPath();
- context.save();
- if(r.x !== r.y) {
- context.scale(1, r.y / r.x);
- }
- context.arc(0, 0, r.x, 0, Math.PI * 2, true);
- context.restore();
- context.closePath();
- this.fillStroke(context);
- },
+(function() {
/**
- * set radius
- * @name setRadius
- * @methodOf Kinetic.Ellipse.prototype
- * @param {Object|Array} radius
- * radius can be a number, in which the ellipse becomes a circle,
- * it can be an object with an x and y component, or it
- * can be an array in which the first element is the x component
- * and the second element is the y component. The x component
- * defines the horizontal radius and the y component
- * defines the vertical radius
+ * Ellipse constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
*/
- setRadius: function() {
- var pos = Kinetic.Type._getXY([].slice.call(arguments));
- this.setAttr('radius', Kinetic.Type._merge(pos, this.getRadius()));
- },
- getWidth: function() {
- return this.getRadius().x * 2;
- },
- getHeight: function() {
- return this.getRadius().y * 2;
- },
- setWidth: function(width) {
- Kinetic.Node.prototype.setWidth.call(this, width);
- this.setRadius({
- x: width / 2
- });
- },
- setHeight: function(height) {
- Kinetic.Node.prototype.setHeight.call(this, height);
- this.setRadius({
- y: height / 2
- });
- }
-};
-Kinetic.Global.extend(Kinetic.Ellipse, Kinetic.Shape);
+ Kinetic.Ellipse = function(config) {
+ this._initEllipse(config);
+ };
-// add getters setters
-Kinetic.Node.addGetters(Kinetic.Ellipse, ['radius']);
+ Kinetic.Ellipse.prototype = {
+ _initEllipse: function(config) {
+ this.setDefaultAttrs({
+ radius: {
+ x: 0,
+ y: 0
+ }
+ });
-/**
- * get radius
- * @name getRadius
- * @methodOf Kinetic.Ellipse.prototype
- */
\ No newline at end of file
+ this.shapeType = "Ellipse";
+
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
+ },
+ drawFunc: function(context) {
+ var r = this.getRadius();
+ context.beginPath();
+ context.save();
+ if(r.x !== r.y) {
+ context.scale(1, r.y / r.x);
+ }
+ context.arc(0, 0, r.x, 0, Math.PI * 2, true);
+ context.restore();
+ context.closePath();
+ this.fillStroke(context);
+ },
+ /**
+ * set radius
+ * @name setRadius
+ * @methodOf Kinetic.Ellipse.prototype
+ * @param {Object|Array} radius
+ * radius can be a number, in which the ellipse becomes a circle,
+ * it can be an object with an x and y component, or it
+ * can be an array in which the first element is the x component
+ * and the second element is the y component. The x component
+ * defines the horizontal radius and the y component
+ * defines the vertical radius
+ */
+ setRadius: function() {
+ var pos = Kinetic.Type._getXY([].slice.call(arguments));
+ this.setAttr('radius', Kinetic.Type._merge(pos, this.getRadius()));
+ },
+ getWidth: function() {
+ return this.getRadius().x * 2;
+ },
+ getHeight: function() {
+ return this.getRadius().y * 2;
+ },
+ setWidth: function(width) {
+ Kinetic.Node.prototype.setWidth.call(this, width);
+ this.setRadius({
+ x: width / 2
+ });
+ },
+ setHeight: function(height) {
+ Kinetic.Node.prototype.setHeight.call(this, height);
+ this.setRadius({
+ y: height / 2
+ });
+ }
+ };
+ Kinetic.Global.extend(Kinetic.Ellipse, Kinetic.Shape);
+
+ // add getters setters
+ Kinetic.Node.addGetters(Kinetic.Ellipse, ['radius']);
+
+ /**
+ * get radius
+ * @name getRadius
+ * @methodOf Kinetic.Ellipse.prototype
+ */
+})();
diff --git a/src/shapes/Image.js b/src/shapes/Image.js
index 4b32015c..3b80cf69 100644
--- a/src/shapes/Image.js
+++ b/src/shapes/Image.js
@@ -1,207 +1,209 @@
-/**
- * Image constructor
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- * @param {ImageObject} config.image
- * @param {Number} [config.width]
- * @param {Number} [config.height]
- * @param {Object} [config.crop]
- */
-Kinetic.Image = function(config) {
- this._initImage(config);
-};
+(function() {
+ /**
+ * Image constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ * @param {ImageObject} config.image
+ * @param {Number} [config.width]
+ * @param {Number} [config.height]
+ * @param {Object} [config.crop]
+ */
+ Kinetic.Image = function(config) {
+ this._initImage(config);
+ };
-Kinetic.Image.prototype = {
- _initImage: function(config) {
- this.shapeType = "Image";
+ Kinetic.Image.prototype = {
+ _initImage: function(config) {
+ this.shapeType = "Image";
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
- var that = this;
- this.on('imageChange', function(evt) {
- that._syncSize();
- });
+ var that = this;
+ this.on('imageChange', function(evt) {
+ that._syncSize();
+ });
- this._syncSize();
- },
- drawFunc: function(context) {
- var width = this.getWidth(), height = this.getHeight(), params, that = this;
+ this._syncSize();
+ },
+ drawFunc: function(context) {
+ var width = this.getWidth(), height = this.getHeight(), params, that = this;
- context.beginPath();
- context.rect(0, 0, width, height);
- context.closePath();
- this.fillStroke(context);
-
- if(this.attrs.image) {
- // if cropping
- if(this.attrs.crop && this.attrs.crop.width && this.attrs.crop.height) {
- var cropX = this.attrs.crop.x || 0;
- var cropY = this.attrs.crop.y || 0;
- var cropWidth = this.attrs.crop.width;
- var cropHeight = this.attrs.crop.height;
- params = [context, this.attrs.image, cropX, cropY, cropWidth, cropHeight, 0, 0, width, height];
- }
- // no cropping
- else {
- params = [context, this.attrs.image, 0, 0, width, height];
- }
-
- if(this.getShadow()) {
- this.applyShadow(context, function() {
- that.drawImage.apply(that, params);
- });
- }
- else {
- this.drawImage.apply(this, params);
- }
-
- }
-
- },
- drawHitFunc: function(context) {
- var width = this.getWidth(), height = this.getHeight(), imageHitRegion = this.imageHitRegion, appliedShadow = false;
-
- if(imageHitRegion) {
- this.drawImage(context, imageHitRegion, 0, 0, width, height);
-
- context.beginPath();
- context.rect(0, 0, width, height);
- context.closePath();
- this.stroke(context);
- }
- else {
context.beginPath();
context.rect(0, 0, width, height);
context.closePath();
this.fillStroke(context);
- }
- },
- /**
- * apply filter
- * @name applyFilter
- * @methodOf Kinetic.Image.prototype
- * @param {Object} config
- * @param {Function} filter filter function
- * @param {Object} [config] optional config object used to configure filter
- * @param {Function} [callback] callback function to be called once
- * filter has been applied
- */
- applyFilter: function(filter, config, callback) {
- var canvas = new Kinetic.Canvas(this.attrs.image.width, this.attrs.image.height);
- var context = canvas.getContext();
- context.drawImage(this.attrs.image, 0, 0);
- try {
- var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight());
- filter(imageData, config);
- var that = this;
- Kinetic.Type._getImage(imageData, function(imageObj) {
- that.setImage(imageObj);
- if(callback) {
- callback();
+ if(this.attrs.image) {
+ // if cropping
+ if(this.attrs.crop && this.attrs.crop.width && this.attrs.crop.height) {
+ var cropX = this.attrs.crop.x || 0;
+ var cropY = this.attrs.crop.y || 0;
+ var cropWidth = this.attrs.crop.width;
+ var cropHeight = this.attrs.crop.height;
+ params = [context, this.attrs.image, cropX, cropY, cropWidth, cropHeight, 0, 0, width, height];
}
- });
- }
- catch(e) {
- Kinetic.Global.warn('Unable to apply filter. ' + e.message);
- }
- },
- /**
- * set crop
- * @name setCrop
- * @methodOf Kinetic.Image.prototype
- * @param {Object|Array} config
- * @param {Number} config.x
- * @param {Number} config.y
- * @param {Number} config.width
- * @param {Number} config.height
- */
- setCrop: function() {
- var config = [].slice.call(arguments);
- var pos = Kinetic.Type._getXY(config);
- var size = Kinetic.Type._getSize(config);
- var both = Kinetic.Type._merge(pos, size);
- this.setAttr('crop', Kinetic.Type._merge(both, this.getCrop()));
- },
- /**
- * create image hit region which enables more accurate hit detection mapping of the image
- * by avoiding event detections for transparent pixels
- * @name createImageHitRegion
- * @methodOf Kinetic.Image.prototype
- * @param {Function} [callback] callback function to be called once
- * the image hit region has been created
- */
- createImageHitRegion: function(callback) {
- var canvas = new Kinetic.Canvas(this.attrs.width, this.attrs.height);
- var context = canvas.getContext();
- context.drawImage(this.attrs.image, 0, 0);
- try {
- var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight());
- var data = imageData.data;
- var rgbColorKey = Kinetic.Type._hexToRgb(this.colorKey);
- // replace non transparent pixels with color key
- for(var i = 0, n = data.length; i < n; i += 4) {
- data[i] = rgbColorKey.r;
- data[i + 1] = rgbColorKey.g;
- data[i + 2] = rgbColorKey.b;
- // i+3 is alpha (the fourth element)
- }
-
- var that = this;
- Kinetic.Type._getImage(imageData, function(imageObj) {
- that.imageHitRegion = imageObj;
- if(callback) {
- callback();
+ // no cropping
+ else {
+ params = [context, this.attrs.image, 0, 0, width, height];
}
- });
+
+ if(this.getShadow()) {
+ this.applyShadow(context, function() {
+ that.drawImage.apply(that, params);
+ });
+ }
+ else {
+ this.drawImage.apply(this, params);
+ }
+
+ }
+
+ },
+ drawHitFunc: function(context) {
+ var width = this.getWidth(), height = this.getHeight(), imageHitRegion = this.imageHitRegion, appliedShadow = false;
+
+ if(imageHitRegion) {
+ this.drawImage(context, imageHitRegion, 0, 0, width, height);
+
+ context.beginPath();
+ context.rect(0, 0, width, height);
+ context.closePath();
+ this.stroke(context);
+ }
+ else {
+ context.beginPath();
+ context.rect(0, 0, width, height);
+ context.closePath();
+ this.fillStroke(context);
+ }
+ },
+ /**
+ * apply filter
+ * @name applyFilter
+ * @methodOf Kinetic.Image.prototype
+ * @param {Object} config
+ * @param {Function} filter filter function
+ * @param {Object} [config] optional config object used to configure filter
+ * @param {Function} [callback] callback function to be called once
+ * filter has been applied
+ */
+ applyFilter: function(filter, config, callback) {
+ var canvas = new Kinetic.Canvas(this.attrs.image.width, this.attrs.image.height);
+ var context = canvas.getContext();
+ context.drawImage(this.attrs.image, 0, 0);
+ try {
+ var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight());
+ filter(imageData, config);
+ var that = this;
+ Kinetic.Type._getImage(imageData, function(imageObj) {
+ that.setImage(imageObj);
+
+ if(callback) {
+ callback();
+ }
+ });
+ }
+ catch(e) {
+ Kinetic.Global.warn('Unable to apply filter. ' + e.message);
+ }
+ },
+ /**
+ * set crop
+ * @name setCrop
+ * @methodOf Kinetic.Image.prototype
+ * @param {Object|Array} config
+ * @param {Number} config.x
+ * @param {Number} config.y
+ * @param {Number} config.width
+ * @param {Number} config.height
+ */
+ setCrop: function() {
+ var config = [].slice.call(arguments);
+ var pos = Kinetic.Type._getXY(config);
+ var size = Kinetic.Type._getSize(config);
+ var both = Kinetic.Type._merge(pos, size);
+ this.setAttr('crop', Kinetic.Type._merge(both, this.getCrop()));
+ },
+ /**
+ * create image hit region which enables more accurate hit detection mapping of the image
+ * by avoiding event detections for transparent pixels
+ * @name createImageHitRegion
+ * @methodOf Kinetic.Image.prototype
+ * @param {Function} [callback] callback function to be called once
+ * the image hit region has been created
+ */
+ createImageHitRegion: function(callback) {
+ var canvas = new Kinetic.Canvas(this.attrs.width, this.attrs.height);
+ var context = canvas.getContext();
+ context.drawImage(this.attrs.image, 0, 0);
+ try {
+ var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight());
+ var data = imageData.data;
+ var rgbColorKey = Kinetic.Type._hexToRgb(this.colorKey);
+ // replace non transparent pixels with color key
+ for(var i = 0, n = data.length; i < n; i += 4) {
+ data[i] = rgbColorKey.r;
+ data[i + 1] = rgbColorKey.g;
+ data[i + 2] = rgbColorKey.b;
+ // i+3 is alpha (the fourth element)
+ }
+
+ var that = this;
+ Kinetic.Type._getImage(imageData, function(imageObj) {
+ that.imageHitRegion = imageObj;
+ if(callback) {
+ callback();
+ }
+ });
+ }
+ catch(e) {
+ Kinetic.Global.warn('Unable to create image hit region. ' + e.message);
+ }
+ },
+ /**
+ * clear image hit region
+ * @name clearImageHitRegion
+ * @methodOf Kinetic.Image.prototype
+ */
+ clearImageHitRegion: function() {
+ delete this.imageHitRegion;
+ },
+ _syncSize: function() {
+ if(this.attrs.image) {
+ if(!this.attrs.width) {
+ this.setWidth(this.attrs.image.width);
+ }
+ if(!this.attrs.height) {
+ this.setHeight(this.attrs.image.height);
+ }
+ }
}
- catch(e) {
- Kinetic.Global.warn('Unable to create image hit region. ' + e.message);
- }
- },
+ };
+ Kinetic.Global.extend(Kinetic.Image, Kinetic.Shape);
+
+ // add getters setters
+ Kinetic.Node.addGettersSetters(Kinetic.Image, ['image']);
+ Kinetic.Node.addGetters(Kinetic.Image, ['crop']);
+
/**
- * clear image hit region
- * @name clearImageHitRegion
+ * set image
+ * @name setImage
+ * @methodOf Kinetic.Image.prototype
+ * @param {ImageObject} image
+ */
+
+ /**
+ * get crop
+ * @name getCrop
* @methodOf Kinetic.Image.prototype
*/
- clearImageHitRegion: function() {
- delete this.imageHitRegion;
- },
- _syncSize: function() {
- if(this.attrs.image) {
- if(!this.attrs.width) {
- this.setWidth(this.attrs.image.width);
- }
- if(!this.attrs.height) {
- this.setHeight(this.attrs.image.height);
- }
- }
- }
-};
-Kinetic.Global.extend(Kinetic.Image, Kinetic.Shape);
-// add getters setters
-Kinetic.Node.addGettersSetters(Kinetic.Image, ['image']);
-Kinetic.Node.addGetters(Kinetic.Image, ['crop']);
-
-/**
- * set image
- * @name setImage
- * @methodOf Kinetic.Image.prototype
- * @param {ImageObject} image
- */
-
-/**
- * get crop
- * @name getCrop
- * @methodOf Kinetic.Image.prototype
- */
-
-/**
- * get image
- * @name getImage
- * @methodOf Kinetic.Image.prototype
- */
\ No newline at end of file
+ /**
+ * get image
+ * @name getImage
+ * @methodOf Kinetic.Image.prototype
+ */
+})();
diff --git a/src/shapes/Line.js b/src/shapes/Line.js
index 1a34b99c..1f8620a0 100644
--- a/src/shapes/Line.js
+++ b/src/shapes/Line.js
@@ -1,135 +1,137 @@
-/**
- * Line constructor. Lines are defined by an array of points
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.Line = function(config) {
- this._initLine(config);
-};
-
-Kinetic.Line.prototype = {
- _initLine: function(config) {
- this.setDefaultAttrs({
- points: [],
- lineCap: 'butt',
- dashArray: [],
- detectionType: 'pixel'
- });
-
- this.shapeType = "Line";
-
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
- },
- drawFunc: function(context) {
- var lastPos = {}, points = this.getPoints(), length = points.length, dashArray = this.getDashArray(), dashLength = dashArray.length;
- context.beginPath();
-
- context.moveTo(points[0].x, points[0].y);
-
- for(var n = 1; n < length; n++) {
- var x = points[n].x;
- var y = points[n].y;
- if(dashLength > 0) {
- // draw dashed line
- var lastX = points[n - 1].x;
- var lastY = points[n - 1].y;
- this._dashedLine(context, lastX, lastY, x, y, dashArray);
- }
- else {
- // draw normal line
- context.lineTo(x, y);
- }
- }
-
- this.stroke(context);
- },
+(function() {
/**
- * set points array
- * @name setPoints
- * @methodOf Kinetic.Line.prototype
- * @param {Array} can be an array of point objects or an array
- * of Numbers. e.g. [{x:1,y:2},{x:3,y:4}] or [1,2,3,4]
- */
- setPoints: function(val) {
- this.setAttr('points', Kinetic.Type._getPoints(val));
- },
- /**
- * draw dashed line. Written by Phrogz
+ * Line constructor. Lines are defined by an array of points
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
*/
- _dashedLine: function(context, x, y, x2, y2, dashArray) {
- var dashCount = dashArray.length;
+ Kinetic.Line = function(config) {
+ this._initLine(config);
+ };
- var dx = (x2 - x), dy = (y2 - y);
- var xSlope = dx > dy;
- var slope = (xSlope) ? dy / dx : dx / dy;
+ Kinetic.Line.prototype = {
+ _initLine: function(config) {
+ this.setDefaultAttrs({
+ points: [],
+ lineCap: 'butt',
+ dashArray: [],
+ detectionType: 'pixel'
+ });
- /*
- * gaurd against slopes of infinity
+ this.shapeType = "Line";
+
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
+ },
+ drawFunc: function(context) {
+ var lastPos = {}, points = this.getPoints(), length = points.length, dashArray = this.getDashArray(), dashLength = dashArray.length;
+ context.beginPath();
+
+ context.moveTo(points[0].x, points[0].y);
+
+ for(var n = 1; n < length; n++) {
+ var x = points[n].x;
+ var y = points[n].y;
+ if(dashLength > 0) {
+ // draw dashed line
+ var lastX = points[n - 1].x;
+ var lastY = points[n - 1].y;
+ this._dashedLine(context, lastX, lastY, x, y, dashArray);
+ }
+ else {
+ // draw normal line
+ context.lineTo(x, y);
+ }
+ }
+
+ this.stroke(context);
+ },
+ /**
+ * set points array
+ * @name setPoints
+ * @methodOf Kinetic.Line.prototype
+ * @param {Array} can be an array of point objects or an array
+ * of Numbers. e.g. [{x:1,y:2},{x:3,y:4}] or [1,2,3,4]
*/
- if(slope > 9999) {
- slope = 9999;
- }
- else if(slope < -9999) {
- slope = -9999;
+ setPoints: function(val) {
+ this.setAttr('points', Kinetic.Type._getPoints(val));
+ },
+ /**
+ * draw dashed line. Written by Phrogz
+ */
+ _dashedLine: function(context, x, y, x2, y2, dashArray) {
+ var dashCount = dashArray.length;
+
+ var dx = (x2 - x), dy = (y2 - y);
+ var xSlope = dx > dy;
+ var slope = (xSlope) ? dy / dx : dx / dy;
+
+ /*
+ * gaurd against slopes of infinity
+ */
+ if(slope > 9999) {
+ slope = 9999;
+ }
+ else if(slope < -9999) {
+ slope = -9999;
+ }
+
+ var distRemaining = Math.sqrt(dx * dx + dy * dy);
+ var dashIndex = 0, draw = true;
+ while(distRemaining >= 0.1 && dashIndex < 10000) {
+ var dashLength = dashArray[dashIndex++ % dashCount];
+ if(dashLength === 0) {
+ dashLength = 0.001;
+ }
+ if(dashLength > distRemaining) {
+ dashLength = distRemaining;
+ }
+ var step = Math.sqrt(dashLength * dashLength / (1 + slope * slope));
+ if(xSlope) {
+ x += dx < 0 && dy < 0 ? step * -1 : step;
+ y += dx < 0 && dy < 0 ? slope * step * -1 : slope * step;
+ }
+ else {
+ x += dx < 0 && dy < 0 ? slope * step * -1 : slope * step;
+ y += dx < 0 && dy < 0 ? step * -1 : step;
+ }
+ context[draw ? 'lineTo' : 'moveTo'](x, y);
+ distRemaining -= dashLength;
+ draw = !draw;
+ }
+
+ context.moveTo(x2, y2);
}
+ };
+ Kinetic.Global.extend(Kinetic.Line, Kinetic.Shape);
- var distRemaining = Math.sqrt(dx * dx + dy * dy);
- var dashIndex = 0, draw = true;
- while(distRemaining >= 0.1 && dashIndex < 10000) {
- var dashLength = dashArray[dashIndex++ % dashCount];
- if(dashLength === 0) {
- dashLength = 0.001;
- }
- if(dashLength > distRemaining) {
- dashLength = distRemaining;
- }
- var step = Math.sqrt(dashLength * dashLength / (1 + slope * slope));
- if(xSlope) {
- x += dx < 0 && dy < 0 ? step * -1 : step;
- y += dx < 0 && dy < 0 ? slope * step * -1 : slope * step;
- }
- else {
- x += dx < 0 && dy < 0 ? slope * step * -1 : slope * step;
- y += dx < 0 && dy < 0 ? step * -1 : step;
- }
- context[draw ? 'lineTo' : 'moveTo'](x, y);
- distRemaining -= dashLength;
- draw = !draw;
- }
+ // add getters setters
+ Kinetic.Node.addGettersSetters(Kinetic.Line, ['dashArray']);
+ Kinetic.Node.addGetters(Kinetic.Line, ['points']);
- context.moveTo(x2, y2);
- }
-};
-Kinetic.Global.extend(Kinetic.Line, Kinetic.Shape);
+ /**
+ * set dash array.
+ * @name setDashArray
+ * @methodOf Kinetic.Line.prototype
+ * @param {Array} dashArray
+ * examples:
+ * [10, 5] dashes are 10px long and 5 pixels apart
+ * [10, 20, 0, 20] if using a round lineCap, the line will
+ * be made up of alternating dashed lines that are 10px long
+ * and 20px apart, and dots that have a radius of 5 and are 20px
+ * apart
+ */
-// add getters setters
-Kinetic.Node.addGettersSetters(Kinetic.Line, ['dashArray']);
-Kinetic.Node.addGetters(Kinetic.Line, ['points']);
+ /**
+ * get dash array
+ * @name getDashArray
+ * @methodOf Kinetic.Line.prototype
+ */
-/**
- * set dash array.
- * @name setDashArray
- * @methodOf Kinetic.Line.prototype
- * @param {Array} dashArray
- * examples:
- * [10, 5] dashes are 10px long and 5 pixels apart
- * [10, 20, 0, 20] if using a round lineCap, the line will
- * be made up of alternating dashed lines that are 10px long
- * and 20px apart, and dots that have a radius of 5 and are 20px
- * apart
- */
-
-/**
- * get dash array
- * @name getDashArray
- * @methodOf Kinetic.Line.prototype
- */
-
-/**
- * get points array
- * @name getPoints
- * @methodOf Kinetic.Line.prototype
- */
\ No newline at end of file
+ /**
+ * get points array
+ * @name getPoints
+ * @methodOf Kinetic.Line.prototype
+ */
+})();
diff --git a/src/shapes/Path.js b/src/shapes/Path.js
index 85ccc79f..5ebd9cac 100644
--- a/src/shapes/Path.js
+++ b/src/shapes/Path.js
@@ -1,559 +1,560 @@
-/**
- * Path constructor.
- * @author Jason Follas
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.Path = function(config) {
- this._initPath(config);
-};
-
-Kinetic.Path.prototype = {
- _initPath: function(config) {
- this.shapeType = "Path";
- this.dataArray = [];
- var that = this;
-
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
-
- this.dataArray = Kinetic.Path.parsePathData(this.attrs.data);
- this.on('dataChange', function() {
- that.dataArray = Kinetic.Path.parsePathData(that.attrs.data);
- });
- },
- drawFunc: function(context) {
- var ca = this.dataArray;
- // context position
- context.beginPath();
- for(var n = 0; n < ca.length; n++) {
- var c = ca[n].command;
- var p = ca[n].points;
- switch (c) {
- case 'L':
- context.lineTo(p[0], p[1]);
- break;
- case 'M':
- context.moveTo(p[0], p[1]);
- break;
- case 'C':
- context.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
- break;
- case 'Q':
- context.quadraticCurveTo(p[0], p[1], p[2], p[3]);
- break;
- case 'A':
- var cx = p[0], cy = p[1], rx = p[2], ry = p[3], theta = p[4], dTheta = p[5], psi = p[6], fs = p[7];
-
- var r = (rx > ry) ? rx : ry;
- var scaleX = (rx > ry) ? 1 : rx / ry;
- var scaleY = (rx > ry) ? ry / rx : 1;
-
- context.translate(cx, cy);
- context.rotate(psi);
- context.scale(scaleX, scaleY);
- context.arc(0, 0, r, theta, theta + dTheta, 1 - fs);
- context.scale(1 / scaleX, 1 / scaleY);
- context.rotate(-psi);
- context.translate(-cx, -cy);
-
- break;
- case 'z':
- context.closePath();
- break;
- }
- }
- this.fillStroke(context);
- }
-};
-Kinetic.Global.extend(Kinetic.Path, Kinetic.Shape);
-
-/*
- * Utility methods written by jfollas to
- * handle length and point measurements
- */
-Kinetic.Path.getLineLength = function(x1, y1, x2, y2) {
- return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
-};
-Kinetic.Path.getPointOnLine = function(dist, P1x, P1y, P2x, P2y, fromX, fromY) {
- if(fromX === undefined) {
- fromX = P1x;
- }
- if(fromY === undefined) {
- fromY = P1y;
- }
-
- var m = (P2y - P1y) / ((P2x - P1x) + 0.00000001);
- var run = Math.sqrt(dist * dist / (1 + m * m));
- if(P2x < P1x) run *= -1;
- var rise = m * run;
- var pt;
-
- if((fromY - P1y) / ((fromX - P1x) + 0.00000001) === m) {
- pt = {
- x: fromX + run,
- y: fromY + rise
- };
- }
- else {
- var ix, iy;
-
- var len = this.getLineLength(P1x, P1y, P2x, P2y);
- if(len < 0.00000001) {
- return undefined;
- }
- var u = (((fromX - P1x) * (P2x - P1x)) + ((fromY - P1y) * (P2y - P1y)));
- u = u / (len * len);
- ix = P1x + u * (P2x - P1x);
- iy = P1y + u * (P2y - P1y);
-
- var pRise = this.getLineLength(fromX, fromY, ix, iy);
- var pRun = Math.sqrt(dist * dist - pRise * pRise);
- run = Math.sqrt(pRun * pRun / (1 + m * m));
- if(P2x < P1x) run *= -1;
- rise = m * run;
- pt = {
- x: ix + run,
- y: iy + rise
- };
- }
-
- return pt;
-};
-
-Kinetic.Path.getPointOnCubicBezier = function(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
- function CB1(t) {
- return t * t * t;
- }
- function CB2(t) {
- return 3 * t * t * (1 - t);
- }
- function CB3(t) {
- return 3 * t * (1 - t) * (1 - t);
- }
- function CB4(t) {
- return (1 - t) * (1 - t) * (1 - t);
- }
- var x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
- var y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
-
- return {
- x: x,
- y: y
+(function() {
+ /**
+ * Path constructor.
+ * @author Jason Follas
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+ Kinetic.Path = function(config) {
+ this._initPath(config);
};
-};
-Kinetic.Path.getPointOnQuadraticBezier = function(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
- function QB1(t) {
- return t * t;
- }
- function QB2(t) {
- return 2 * t * (1 - t);
- }
- function QB3(t) {
- return (1 - t) * (1 - t);
- }
- var x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
- var y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
- return {
- x: x,
- y: y
- };
-};
-Kinetic.Path.getPointOnEllipticalArc = function(cx, cy, rx, ry, theta, psi) {
- var cosPsi = Math.cos(psi), sinPsi = Math.sin(psi);
- var pt = {
- x: rx * Math.cos(theta),
- y: ry * Math.sin(theta)
- };
- return {
- x: cx + (pt.x * cosPsi - pt.y * sinPsi),
- y: cy + (pt.x * sinPsi + pt.y * cosPsi)
- };
-};
-/**
- * get parsed data array from the data
- * string. V, v, H, h, and l data are converted to
- * L data for the purpose of high performance Path
- * rendering
- */
-Kinetic.Path.parsePathData = function(data) {
- // Path Data Segment must begin with a moveTo
- //m (x y)+ Relative moveTo (subsequent points are treated as lineTo)
- //M (x y)+ Absolute moveTo (subsequent points are treated as lineTo)
- //l (x y)+ Relative lineTo
- //L (x y)+ Absolute LineTo
- //h (x)+ Relative horizontal lineTo
- //H (x)+ Absolute horizontal lineTo
- //v (y)+ Relative vertical lineTo
- //V (y)+ Absolute vertical lineTo
- //z (closepath)
- //Z (closepath)
- //c (x1 y1 x2 y2 x y)+ Relative Bezier curve
- //C (x1 y1 x2 y2 x y)+ Absolute Bezier curve
- //q (x1 y1 x y)+ Relative Quadratic Bezier
- //Q (x1 y1 x y)+ Absolute Quadratic Bezier
- //t (x y)+ Shorthand/Smooth Relative Quadratic Bezier
- //T (x y)+ Shorthand/Smooth Absolute Quadratic Bezier
- //s (x2 y2 x y)+ Shorthand/Smooth Relative Bezier curve
- //S (x2 y2 x y)+ Shorthand/Smooth Absolute Bezier curve
- //a (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Relative Elliptical Arc
- //A (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Absolute Elliptical Arc
+ Kinetic.Path.prototype = {
+ _initPath: function(config) {
+ this.shapeType = "Path";
+ this.dataArray = [];
+ var that = this;
- // return early if data is not defined
- if(!data) {
- return [];
- }
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
- // command string
- var cs = data;
-
- // command chars
- var cc = ['m', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'];
- // convert white spaces to commas
- cs = cs.replace(new RegExp(' ', 'g'), ',');
- // create pipes so that we can split the data
- for(var n = 0; n < cc.length; n++) {
- cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
- }
- // create array
- var arr = cs.split('|');
- var ca = [];
- // init context point
- var cpx = 0;
- var cpy = 0;
- for(var n = 1; n < arr.length; n++) {
- var str = arr[n];
- var c = str.charAt(0);
- str = str.slice(1);
- // remove ,- for consistency
- str = str.replace(new RegExp(',-', 'g'), '-');
- // add commas so that it's easy to split
- str = str.replace(new RegExp('-', 'g'), ',-');
- str = str.replace(new RegExp('e,-', 'g'), 'e-');
- var p = str.split(',');
- if(p.length > 0 && p[0] === '') {
- p.shift();
- }
- // convert strings to floats
- for(var i = 0; i < p.length; i++) {
- p[i] = parseFloat(p[i]);
- }
- while(p.length > 0) {
- if(isNaN(p[0]))// case for a trailing comma before next command
- break;
-
- var cmd = null;
- var points = [];
- var startX = cpx, startY = cpy;
-
- // convert l, H, h, V, and v to L
- switch (c) {
-
- // Note: Keep the lineTo's above the moveTo's in this switch
- case 'l':
- cpx += p.shift();
- cpy += p.shift();
- cmd = 'L';
- points.push(cpx, cpy);
- break;
- case 'L':
- cpx = p.shift();
- cpy = p.shift();
- points.push(cpx, cpy);
- break;
-
- // Note: lineTo handlers need to be above this point
- case 'm':
- cpx += p.shift();
- cpy += p.shift();
- cmd = 'M';
- points.push(cpx, cpy);
- c = 'l';
- // subsequent points are treated as relative lineTo
- break;
- case 'M':
- cpx = p.shift();
- cpy = p.shift();
- cmd = 'M';
- points.push(cpx, cpy);
- c = 'L';
- // subsequent points are treated as absolute lineTo
- break;
-
- case 'h':
- cpx += p.shift();
- cmd = 'L';
- points.push(cpx, cpy);
- break;
- case 'H':
- cpx = p.shift();
- cmd = 'L';
- points.push(cpx, cpy);
- break;
- case 'v':
- cpy += p.shift();
- cmd = 'L';
- points.push(cpx, cpy);
- break;
- case 'V':
- cpy = p.shift();
- cmd = 'L';
- points.push(cpx, cpy);
- break;
- case 'C':
- points.push(p.shift(), p.shift(), p.shift(), p.shift());
- cpx = p.shift();
- cpy = p.shift();
- points.push(cpx, cpy);
- break;
- case 'c':
- points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
- cpx += p.shift();
- cpy += p.shift();
- cmd = 'C';
- points.push(cpx, cpy);
- break;
- case 'S':
- var ctlPtx = cpx, ctlPty = cpy;
- var prevCmd = ca[ca.length - 1];
- if(prevCmd.command === 'C') {
- ctlPtx = cpx + (cpx - prevCmd.points[2]);
- ctlPty = cpy + (cpy - prevCmd.points[3]);
- }
- points.push(ctlPtx, ctlPty, p.shift(), p.shift());
- cpx = p.shift();
- cpy = p.shift();
- cmd = 'C';
- points.push(cpx, cpy);
- break;
- case 's':
- var ctlPtx = cpx, ctlPty = cpy;
- var prevCmd = ca[ca.length - 1];
- if(prevCmd.command === 'C') {
- ctlPtx = cpx + (cpx - prevCmd.points[2]);
- ctlPty = cpy + (cpy - prevCmd.points[3]);
- }
- points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
- cpx += p.shift();
- cpy += p.shift();
- cmd = 'C';
- points.push(cpx, cpy);
- break;
- case 'Q':
- points.push(p.shift(), p.shift());
- cpx = p.shift();
- cpy = p.shift();
- points.push(cpx, cpy);
- break;
- case 'q':
- points.push(cpx + p.shift(), cpy + p.shift());
- cpx += p.shift();
- cpy += p.shift();
- cmd = 'Q';
- points.push(cpx, cpy);
- break;
- case 'T':
- var ctlPtx = cpx, ctlPty = cpy;
- var prevCmd = ca[ca.length - 1];
- if(prevCmd.command === 'Q') {
- ctlPtx = cpx + (cpx - prevCmd.points[0]);
- ctlPty = cpy + (cpy - prevCmd.points[1]);
- }
- cpx = p.shift();
- cpy = p.shift();
- cmd = 'Q';
- points.push(ctlPtx, ctlPty, cpx, cpy);
- break;
- case 't':
- var ctlPtx = cpx, ctlPty = cpy;
- var prevCmd = ca[ca.length - 1];
- if(prevCmd.command === 'Q') {
- ctlPtx = cpx + (cpx - prevCmd.points[0]);
- ctlPty = cpy + (cpy - prevCmd.points[1]);
- }
- cpx += p.shift();
- cpy += p.shift();
- cmd = 'Q';
- points.push(ctlPtx, ctlPty, cpx, cpy);
- break;
- case 'A':
- var rx = p.shift(), ry = p.shift(), psi = p.shift(), fa = p.shift(), fs = p.shift();
- var x1 = cpx, y1 = cpy;
- cpx = p.shift(), cpy = p.shift();
- cmd = 'A';
- points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
- break;
- case 'a':
- var rx = p.shift(), ry = p.shift(), psi = p.shift(), fa = p.shift(), fs = p.shift();
- var x1 = cpx, y1 = cpy;
- cpx += p.shift(), cpy += p.shift();
- cmd = 'A';
- points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
- break;
- }
-
- ca.push({
- command: cmd || c,
- points: points,
- start: {
- x: startX,
- y: startY
- },
- pathLength: this.calcLength(startX, startY, cmd || c, points)
+ this.dataArray = Kinetic.Path.parsePathData(this.attrs.data);
+ this.on('dataChange', function() {
+ that.dataArray = Kinetic.Path.parsePathData(that.attrs.data);
});
+ },
+ drawFunc: function(context) {
+ var ca = this.dataArray;
+ // context position
+ context.beginPath();
+ for(var n = 0; n < ca.length; n++) {
+ var c = ca[n].command;
+ var p = ca[n].points;
+ switch (c) {
+ case 'L':
+ context.lineTo(p[0], p[1]);
+ break;
+ case 'M':
+ context.moveTo(p[0], p[1]);
+ break;
+ case 'C':
+ context.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
+ break;
+ case 'Q':
+ context.quadraticCurveTo(p[0], p[1], p[2], p[3]);
+ break;
+ case 'A':
+ var cx = p[0], cy = p[1], rx = p[2], ry = p[3], theta = p[4], dTheta = p[5], psi = p[6], fs = p[7];
+
+ var r = (rx > ry) ? rx : ry;
+ var scaleX = (rx > ry) ? 1 : rx / ry;
+ var scaleY = (rx > ry) ? ry / rx : 1;
+
+ context.translate(cx, cy);
+ context.rotate(psi);
+ context.scale(scaleX, scaleY);
+ context.arc(0, 0, r, theta, theta + dTheta, 1 - fs);
+ context.scale(1 / scaleX, 1 / scaleY);
+ context.rotate(-psi);
+ context.translate(-cx, -cy);
+
+ break;
+ case 'z':
+ context.closePath();
+ break;
+ }
+ }
+ this.fillStroke(context);
+ }
+ };
+ Kinetic.Global.extend(Kinetic.Path, Kinetic.Shape);
+
+ /*
+ * Utility methods written by jfollas to
+ * handle length and point measurements
+ */
+ Kinetic.Path.getLineLength = function(x1, y1, x2, y2) {
+ return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
+ };
+ Kinetic.Path.getPointOnLine = function(dist, P1x, P1y, P2x, P2y, fromX, fromY) {
+ if(fromX === undefined) {
+ fromX = P1x;
+ }
+ if(fromY === undefined) {
+ fromY = P1y;
}
- if(c === 'z' || c === 'Z') {
- ca.push({
- command: 'z',
- points: [],
- start: undefined,
- pathLength: 0
- });
+ var m = (P2y - P1y) / ((P2x - P1x) + 0.00000001);
+ var run = Math.sqrt(dist * dist / (1 + m * m));
+ if(P2x < P1x)
+ run *= -1;
+ var rise = m * run;
+ var pt;
+
+ if((fromY - P1y) / ((fromX - P1x) + 0.00000001) === m) {
+ pt = {
+ x: fromX + run,
+ y: fromY + rise
+ };
}
- }
+ else {
+ var ix, iy;
- return ca;
-};
-Kinetic.Path.calcLength = function(x, y, cmd, points) {
- var len, p1, p2;
- var path = Kinetic.Path;
+ var len = this.getLineLength(P1x, P1y, P2x, P2y);
+ if(len < 0.00000001) {
+ return undefined;
+ }
+ var u = (((fromX - P1x) * (P2x - P1x)) + ((fromY - P1y) * (P2y - P1y)));
+ u = u / (len * len);
+ ix = P1x + u * (P2x - P1x);
+ iy = P1y + u * (P2y - P1y);
- switch (cmd) {
- case 'L':
- return path.getLineLength(x, y, points[0], points[1]);
- case 'C':
- // Approximates by breaking curve into 100 line segments
- len = 0.0;
- p1 = path.getPointOnCubicBezier(0, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
- for( t = 0.01; t <= 1; t += 0.01) {
- p2 = path.getPointOnCubicBezier(t, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
- len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
- p1 = p2;
+ var pRise = this.getLineLength(fromX, fromY, ix, iy);
+ var pRun = Math.sqrt(dist * dist - pRise * pRise);
+ run = Math.sqrt(pRun * pRun / (1 + m * m));
+ if(P2x < P1x)
+ run *= -1;
+ rise = m * run;
+ pt = {
+ x: ix + run,
+ y: iy + rise
+ };
+ }
+
+ return pt;
+ };
+
+ Kinetic.Path.getPointOnCubicBezier = function(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
+ function CB1(t) {
+ return t * t * t;
+ }
+ function CB2(t) {
+ return 3 * t * t * (1 - t);
+ }
+ function CB3(t) {
+ return 3 * t * (1 - t) * (1 - t);
+ }
+ function CB4(t) {
+ return (1 - t) * (1 - t) * (1 - t);
+ }
+ var x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
+ var y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
+
+ return {
+ x: x,
+ y: y
+ };
+ };
+ Kinetic.Path.getPointOnQuadraticBezier = function(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
+ function QB1(t) {
+ return t * t;
+ }
+ function QB2(t) {
+ return 2 * t * (1 - t);
+ }
+ function QB3(t) {
+ return (1 - t) * (1 - t);
+ }
+ var x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
+ var y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
+
+ return {
+ x: x,
+ y: y
+ };
+ };
+ Kinetic.Path.getPointOnEllipticalArc = function(cx, cy, rx, ry, theta, psi) {
+ var cosPsi = Math.cos(psi), sinPsi = Math.sin(psi);
+ var pt = {
+ x: rx * Math.cos(theta),
+ y: ry * Math.sin(theta)
+ };
+ return {
+ x: cx + (pt.x * cosPsi - pt.y * sinPsi),
+ y: cy + (pt.x * sinPsi + pt.y * cosPsi)
+ };
+ };
+ /**
+ * get parsed data array from the data
+ * string. V, v, H, h, and l data are converted to
+ * L data for the purpose of high performance Path
+ * rendering
+ */
+ Kinetic.Path.parsePathData = function(data) {
+ // Path Data Segment must begin with a moveTo
+ //m (x y)+ Relative moveTo (subsequent points are treated as lineTo)
+ //M (x y)+ Absolute moveTo (subsequent points are treated as lineTo)
+ //l (x y)+ Relative lineTo
+ //L (x y)+ Absolute LineTo
+ //h (x)+ Relative horizontal lineTo
+ //H (x)+ Absolute horizontal lineTo
+ //v (y)+ Relative vertical lineTo
+ //V (y)+ Absolute vertical lineTo
+ //z (closepath)
+ //Z (closepath)
+ //c (x1 y1 x2 y2 x y)+ Relative Bezier curve
+ //C (x1 y1 x2 y2 x y)+ Absolute Bezier curve
+ //q (x1 y1 x y)+ Relative Quadratic Bezier
+ //Q (x1 y1 x y)+ Absolute Quadratic Bezier
+ //t (x y)+ Shorthand/Smooth Relative Quadratic Bezier
+ //T (x y)+ Shorthand/Smooth Absolute Quadratic Bezier
+ //s (x2 y2 x y)+ Shorthand/Smooth Relative Bezier curve
+ //S (x2 y2 x y)+ Shorthand/Smooth Absolute Bezier curve
+ //a (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Relative Elliptical Arc
+ //A (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Absolute Elliptical Arc
+
+ // return early if data is not defined
+ if(!data) {
+ return [];
+ }
+
+ // command string
+ var cs = data;
+
+ // command chars
+ var cc = ['m', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'];
+ // convert white spaces to commas
+ cs = cs.replace(new RegExp(' ', 'g'), ',');
+ // create pipes so that we can split the data
+ for(var n = 0; n < cc.length; n++) {
+ cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
+ }
+ // create array
+ var arr = cs.split('|');
+ var ca = [];
+ // init context point
+ var cpx = 0;
+ var cpy = 0;
+ for(var n = 1; n < arr.length; n++) {
+ var str = arr[n];
+ var c = str.charAt(0);
+ str = str.slice(1);
+ // remove ,- for consistency
+ str = str.replace(new RegExp(',-', 'g'), '-');
+ // add commas so that it's easy to split
+ str = str.replace(new RegExp('-', 'g'), ',-');
+ str = str.replace(new RegExp('e,-', 'g'), 'e-');
+ var p = str.split(',');
+ if(p.length > 0 && p[0] === '') {
+ p.shift();
}
- return len;
- case 'Q':
- // Approximates by breaking curve into 100 line segments
- len = 0.0;
- p1 = path.getPointOnQuadraticBezier(0, x, y, points[0], points[1], points[2], points[3]);
- for( t = 0.01; t <= 1; t += 0.01) {
- p2 = path.getPointOnQuadraticBezier(t, x, y, points[0], points[1], points[2], points[3]);
- len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
- p1 = p2;
+ // convert strings to floats
+ for(var i = 0; i < p.length; i++) {
+ p[i] = parseFloat(p[i]);
}
- return len;
- case 'A':
- // Approximates by breaking curve into line segments
- len = 0.0;
- var start = points[4];
- // 4 = theta
- var dTheta = points[5];
- // 5 = dTheta
- var end = points[4] + dTheta;
- var inc = Math.PI / 180.0;
- // 1 degree resolution
- if(Math.abs(start - end) < inc) {
- inc = Math.abs(start - end);
+ while(p.length > 0) {
+ if(isNaN(p[0]))// case for a trailing comma before next command
+ break;
+
+ var cmd = null;
+ var points = [];
+ var startX = cpx, startY = cpy;
+
+ // convert l, H, h, V, and v to L
+ switch (c) {
+
+ // Note: Keep the lineTo's above the moveTo's in this switch
+ case 'l':
+ cpx += p.shift();
+ cpy += p.shift();
+ cmd = 'L';
+ points.push(cpx, cpy);
+ break;
+ case 'L':
+ cpx = p.shift();
+ cpy = p.shift();
+ points.push(cpx, cpy);
+ break;
+
+ // Note: lineTo handlers need to be above this point
+ case 'm':
+ cpx += p.shift();
+ cpy += p.shift();
+ cmd = 'M';
+ points.push(cpx, cpy);
+ c = 'l';
+ // subsequent points are treated as relative lineTo
+ break;
+ case 'M':
+ cpx = p.shift();
+ cpy = p.shift();
+ cmd = 'M';
+ points.push(cpx, cpy);
+ c = 'L';
+ // subsequent points are treated as absolute lineTo
+ break;
+
+ case 'h':
+ cpx += p.shift();
+ cmd = 'L';
+ points.push(cpx, cpy);
+ break;
+ case 'H':
+ cpx = p.shift();
+ cmd = 'L';
+ points.push(cpx, cpy);
+ break;
+ case 'v':
+ cpy += p.shift();
+ cmd = 'L';
+ points.push(cpx, cpy);
+ break;
+ case 'V':
+ cpy = p.shift();
+ cmd = 'L';
+ points.push(cpx, cpy);
+ break;
+ case 'C':
+ points.push(p.shift(), p.shift(), p.shift(), p.shift());
+ cpx = p.shift();
+ cpy = p.shift();
+ points.push(cpx, cpy);
+ break;
+ case 'c':
+ points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
+ cpx += p.shift();
+ cpy += p.shift();
+ cmd = 'C';
+ points.push(cpx, cpy);
+ break;
+ case 'S':
+ var ctlPtx = cpx, ctlPty = cpy;
+ var prevCmd = ca[ca.length - 1];
+ if(prevCmd.command === 'C') {
+ ctlPtx = cpx + (cpx - prevCmd.points[2]);
+ ctlPty = cpy + (cpy - prevCmd.points[3]);
+ }
+ points.push(ctlPtx, ctlPty, p.shift(), p.shift());
+ cpx = p.shift();
+ cpy = p.shift();
+ cmd = 'C';
+ points.push(cpx, cpy);
+ break;
+ case 's':
+ var ctlPtx = cpx, ctlPty = cpy;
+ var prevCmd = ca[ca.length - 1];
+ if(prevCmd.command === 'C') {
+ ctlPtx = cpx + (cpx - prevCmd.points[2]);
+ ctlPty = cpy + (cpy - prevCmd.points[3]);
+ }
+ points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
+ cpx += p.shift();
+ cpy += p.shift();
+ cmd = 'C';
+ points.push(cpx, cpy);
+ break;
+ case 'Q':
+ points.push(p.shift(), p.shift());
+ cpx = p.shift();
+ cpy = p.shift();
+ points.push(cpx, cpy);
+ break;
+ case 'q':
+ points.push(cpx + p.shift(), cpy + p.shift());
+ cpx += p.shift();
+ cpy += p.shift();
+ cmd = 'Q';
+ points.push(cpx, cpy);
+ break;
+ case 'T':
+ var ctlPtx = cpx, ctlPty = cpy;
+ var prevCmd = ca[ca.length - 1];
+ if(prevCmd.command === 'Q') {
+ ctlPtx = cpx + (cpx - prevCmd.points[0]);
+ ctlPty = cpy + (cpy - prevCmd.points[1]);
+ }
+ cpx = p.shift();
+ cpy = p.shift();
+ cmd = 'Q';
+ points.push(ctlPtx, ctlPty, cpx, cpy);
+ break;
+ case 't':
+ var ctlPtx = cpx, ctlPty = cpy;
+ var prevCmd = ca[ca.length - 1];
+ if(prevCmd.command === 'Q') {
+ ctlPtx = cpx + (cpx - prevCmd.points[0]);
+ ctlPty = cpy + (cpy - prevCmd.points[1]);
+ }
+ cpx += p.shift();
+ cpy += p.shift();
+ cmd = 'Q';
+ points.push(ctlPtx, ctlPty, cpx, cpy);
+ break;
+ case 'A':
+ var rx = p.shift(), ry = p.shift(), psi = p.shift(), fa = p.shift(), fs = p.shift();
+ var x1 = cpx, y1 = cpy; cpx = p.shift(), cpy = p.shift();
+ cmd = 'A';
+ points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
+ break;
+ case 'a':
+ var rx = p.shift(), ry = p.shift(), psi = p.shift(), fa = p.shift(), fs = p.shift();
+ var x1 = cpx, y1 = cpy; cpx += p.shift(), cpy += p.shift();
+ cmd = 'A';
+ points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
+ break;
+ }
+
+ ca.push({
+ command: cmd || c,
+ points: points,
+ start: {
+ x: startX,
+ y: startY
+ },
+ pathLength: this.calcLength(startX, startY, cmd || c, points)
+ });
}
- // Note: for purpose of calculating arc length, not going to worry about rotating X-axis by angle psi
- p1 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], start, 0);
- if(dTheta < 0) {// clockwise
- for( t = start - inc; t > end; t -= inc) {
- p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
+
+ if(c === 'z' || c === 'Z') {
+ ca.push({
+ command: 'z',
+ points: [],
+ start: undefined,
+ pathLength: 0
+ });
+ }
+ }
+
+ return ca;
+ };
+ Kinetic.Path.calcLength = function(x, y, cmd, points) {
+ var len, p1, p2;
+ var path = Kinetic.Path;
+
+ switch (cmd) {
+ case 'L':
+ return path.getLineLength(x, y, points[0], points[1]);
+ case 'C':
+ // Approximates by breaking curve into 100 line segments
+ len = 0.0;
+ p1 = path.getPointOnCubicBezier(0, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
+ for( t = 0.01; t <= 1; t += 0.01) {
+ p2 = path.getPointOnCubicBezier(t, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
- }
- else {// counter-clockwise
- for( t = start + inc; t < end; t += inc) {
- p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
+ return len;
+ case 'Q':
+ // Approximates by breaking curve into 100 line segments
+ len = 0.0;
+ p1 = path.getPointOnQuadraticBezier(0, x, y, points[0], points[1], points[2], points[3]);
+ for( t = 0.01; t <= 1; t += 0.01) {
+ p2 = path.getPointOnQuadraticBezier(t, x, y, points[0], points[1], points[2], points[3]);
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
- }
- p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], end, 0);
- len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
+ return len;
+ case 'A':
+ // Approximates by breaking curve into line segments
+ len = 0.0;
+ var start = points[4];
+ // 4 = theta
+ var dTheta = points[5];
+ // 5 = dTheta
+ var end = points[4] + dTheta;
+ var inc = Math.PI / 180.0;
+ // 1 degree resolution
+ if(Math.abs(start - end) < inc) {
+ inc = Math.abs(start - end);
+ }
+ // Note: for purpose of calculating arc length, not going to worry about rotating X-axis by angle psi
+ p1 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], start, 0);
+ if(dTheta < 0) {// clockwise
+ for( t = start - inc; t > end; t -= inc) {
+ p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
+ len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
+ p1 = p2;
+ }
+ }
+ else {// counter-clockwise
+ for( t = start + inc; t < end; t += inc) {
+ p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
+ len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
+ p1 = p2;
+ }
+ }
+ p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], end, 0);
+ len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
- return len;
- }
+ return len;
+ }
- return 0;
-};
-Kinetic.Path.convertEndpointToCenterParameterization = function(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
- // Derived from: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
- var psi = psiDeg * (Math.PI / 180.0);
- var xp = Math.cos(psi) * (x1 - x2) / 2.0 + Math.sin(psi) * (y1 - y2) / 2.0;
- var yp = -1 * Math.sin(psi) * (x1 - x2) / 2.0 + Math.cos(psi) * (y1 - y2) / 2.0;
-
- var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
-
- if(lambda > 1) {
- rx *= Math.sqrt(lambda);
- ry *= Math.sqrt(lambda);
- }
-
- var f = Math.sqrt((((rx * rx) * (ry * ry)) - ((rx * rx) * (yp * yp)) - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp) + (ry * ry) * (xp * xp)));
-
- if(fa == fs) {
- f *= -1;
- }
- if(isNaN(f)) {
- f = 0;
- }
-
- var cxp = f * rx * yp / ry;
- var cyp = f * -ry * xp / rx;
-
- var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
- var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
-
- var vMag = function(v) {
- return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
+ return 0;
};
- var vRatio = function(u, v) {
- return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
+ Kinetic.Path.convertEndpointToCenterParameterization = function(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
+ // Derived from: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
+ var psi = psiDeg * (Math.PI / 180.0);
+ var xp = Math.cos(psi) * (x1 - x2) / 2.0 + Math.sin(psi) * (y1 - y2) / 2.0;
+ var yp = -1 * Math.sin(psi) * (x1 - x2) / 2.0 + Math.cos(psi) * (y1 - y2) / 2.0;
+
+ var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
+
+ if(lambda > 1) {
+ rx *= Math.sqrt(lambda);
+ ry *= Math.sqrt(lambda);
+ }
+
+ var f = Math.sqrt((((rx * rx) * (ry * ry)) - ((rx * rx) * (yp * yp)) - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp) + (ry * ry) * (xp * xp)));
+
+ if(fa == fs) {
+ f *= -1;
+ }
+ if(isNaN(f)) {
+ f = 0;
+ }
+
+ var cxp = f * rx * yp / ry;
+ var cyp = f * -ry * xp / rx;
+
+ var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
+ var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
+
+ var vMag = function(v) {
+ return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
+ };
+ var vRatio = function(u, v) {
+ return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
+ };
+ var vAngle = function(u, v) {
+ return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
+ };
+ var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
+ var u = [(xp - cxp) / rx, (yp - cyp) / ry];
+ var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
+ var dTheta = vAngle(u, v);
+
+ if(vRatio(u, v) <= -1) {
+ dTheta = Math.PI;
+ }
+ if(vRatio(u, v) >= 1) {
+ dTheta = 0;
+ }
+ if(fs === 0 && dTheta > 0) {
+ dTheta = dTheta - 2 * Math.PI;
+ }
+ if(fs == 1 && dTheta < 0) {
+ dTheta = dTheta + 2 * Math.PI;
+ }
+ return [cx, cy, rx, ry, theta, dTheta, psi, fs];
};
- var vAngle = function(u, v) {
- return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
- };
- var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
- var u = [(xp - cxp) / rx, (yp - cyp) / ry];
- var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
- var dTheta = vAngle(u, v);
+ // add getters setters
+ Kinetic.Node.addGettersSetters(Kinetic.Path, ['data']);
- if(vRatio(u, v) <= -1) {
- dTheta = Math.PI;
- }
- if(vRatio(u, v) >= 1) {
- dTheta = 0;
- }
- if(fs === 0 && dTheta > 0) {
- dTheta = dTheta - 2 * Math.PI;
- }
- if(fs == 1 && dTheta < 0) {
- dTheta = dTheta + 2 * Math.PI;
- }
- return [cx, cy, rx, ry, theta, dTheta, psi, fs];
-};
+ /**
+ * set SVG path data string. This method
+ * also automatically parses the data string
+ * into a data array. Currently supported SVG data:
+ * M, m, L, l, H, h, V, v, Q, q, T, t, C, c, S, s, A, a, Z, z
+ * @name setData
+ * @methodOf Kinetic.Path.prototype
+ * @param {String} SVG path command string
+ */
-// add getters setters
-Kinetic.Node.addGettersSetters(Kinetic.Path, ['data']);
-
-/**
- * set SVG path data string. This method
- * also automatically parses the data string
- * into a data array. Currently supported SVG data:
- * M, m, L, l, H, h, V, v, Q, q, T, t, C, c, S, s, A, a, Z, z
- * @name setData
- * @methodOf Kinetic.Path.prototype
- * @param {String} SVG path command string
- */
-
-/**
- * get SVG path data string
- * @name getData
- * @methodOf Kinetic.Path.prototype
- */
\ No newline at end of file
+ /**
+ * get SVG path data string
+ * @name getData
+ * @methodOf Kinetic.Path.prototype
+ */
+})();
diff --git a/src/shapes/Polygon.js b/src/shapes/Polygon.js
index febf3cca..17afcf65 100644
--- a/src/shapes/Polygon.js
+++ b/src/shapes/Polygon.js
@@ -1,52 +1,54 @@
-/**
- * Polygon constructor. Polygons are defined by an array of points
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.Polygon = function(config) {
- this._initPolygon(config);
-};
-
-Kinetic.Polygon.prototype = {
- _initPolygon: function(config) {
- this.setDefaultAttrs({
- points: []
- });
-
- this.shapeType = "Polygon";
-
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
- },
- drawFunc: function(context) {
- context.beginPath();
- context.moveTo(this.attrs.points[0].x, this.attrs.points[0].y);
- for(var n = 1; n < this.attrs.points.length; n++) {
- context.lineTo(this.attrs.points[n].x, this.attrs.points[n].y);
- }
- context.closePath();
- this.fillStroke(context);
- },
+(function() {
/**
- * set points array
- * @name setPoints
- * @methodOf Kinetic.Polygon.prototype
- * @param {Array} can be an array of point objects or an array
- * of Numbers. e.g. [{x:1,y:2},{x:3,y:4}] or [1,2,3,4]
- */
- setPoints: function(val) {
- this.setAttr('points', Kinetic.Type._getPoints(val));
- }
-};
-Kinetic.Global.extend(Kinetic.Polygon, Kinetic.Shape);
+ * Polygon constructor. Polygons are defined by an array of points
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+ Kinetic.Polygon = function(config) {
+ this._initPolygon(config);
+ };
-// add getters setters
-Kinetic.Node.addGetters(Kinetic.Polygon, ['points']);
+ Kinetic.Polygon.prototype = {
+ _initPolygon: function(config) {
+ this.setDefaultAttrs({
+ points: []
+ });
-/**
- * get points array
- * @name getPoints
- * @methodOf Kinetic.Polygon.prototype
- */
\ No newline at end of file
+ this.shapeType = "Polygon";
+
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
+ },
+ drawFunc: function(context) {
+ context.beginPath();
+ context.moveTo(this.attrs.points[0].x, this.attrs.points[0].y);
+ for(var n = 1; n < this.attrs.points.length; n++) {
+ context.lineTo(this.attrs.points[n].x, this.attrs.points[n].y);
+ }
+ context.closePath();
+ this.fillStroke(context);
+ },
+ /**
+ * set points array
+ * @name setPoints
+ * @methodOf Kinetic.Polygon.prototype
+ * @param {Array} can be an array of point objects or an array
+ * of Numbers. e.g. [{x:1,y:2},{x:3,y:4}] or [1,2,3,4]
+ */
+ setPoints: function(val) {
+ this.setAttr('points', Kinetic.Type._getPoints(val));
+ }
+ };
+ Kinetic.Global.extend(Kinetic.Polygon, Kinetic.Shape);
+
+ // add getters setters
+ Kinetic.Node.addGetters(Kinetic.Polygon, ['points']);
+
+ /**
+ * get points array
+ * @name getPoints
+ * @methodOf Kinetic.Polygon.prototype
+ */
+})();
diff --git a/src/shapes/Rect.js b/src/shapes/Rect.js
index 5e3ae807..ab46625a 100644
--- a/src/shapes/Rect.js
+++ b/src/shapes/Rect.js
@@ -1,46 +1,49 @@
-/**
- * Rect constructor
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.Rect = function(config) {
- this._initRect(config);
-}
-Kinetic.Rect.prototype = {
- _initRect: function(config) {
- this.setDefaultAttrs({
- width: 0,
- height: 0,
- cornerRadius: 0
- });
- this.shapeType = "Rect";
-
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
- },
- drawFunc: function(context) {
- context.beginPath();
- var cornerRadius = this.getCornerRadius(), width = this.getWidth(), height = this.getHeight();
- if(cornerRadius === 0) {
- // simple rect - don't bother doing all that complicated maths stuff.
- context.rect(0, 0, width, height);
- }
- else {
- // arcTo would be nicer, but browser support is patchy (Opera)
- context.moveTo(cornerRadius, 0);
- context.lineTo(width - cornerRadius, 0);
- context.arc(width - cornerRadius, cornerRadius, cornerRadius, Math.PI * 3 / 2, 0, false);
- context.lineTo(width, height - cornerRadius);
- context.arc(width - cornerRadius, height - cornerRadius, cornerRadius, 0, Math.PI / 2, false);
- context.lineTo(cornerRadius, height);
- context.arc(cornerRadius, height - cornerRadius, cornerRadius, Math.PI / 2, Math.PI, false);
- context.lineTo(0, cornerRadius);
- context.arc(cornerRadius, cornerRadius, cornerRadius, Math.PI, Math.PI * 3 / 2, false);
- }
- context.closePath();
- this.fillStroke(context);
+(function() {
+ /**
+ * Rect constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+ Kinetic.Rect = function(config) {
+ this._initRect(config);
}
-};
+ Kinetic.Rect.prototype = {
+ _initRect: function(config) {
+ this.setDefaultAttrs({
+ width: 0,
+ height: 0,
+ cornerRadius: 0
+ });
+ this.shapeType = "Rect";
-Kinetic.Global.extend(Kinetic.Rect, Kinetic.Shape);
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
+ },
+ drawFunc: function(context) {
+ context.beginPath();
+ var cornerRadius = this.getCornerRadius(), width = this.getWidth(), height = this.getHeight();
+ if(cornerRadius === 0) {
+ // simple rect - don't bother doing all that complicated maths stuff.
+ context.rect(0, 0, width, height);
+ }
+ else {
+ // arcTo would be nicer, but browser support is patchy (Opera)
+ context.moveTo(cornerRadius, 0);
+ context.lineTo(width - cornerRadius, 0);
+ context.arc(width - cornerRadius, cornerRadius, cornerRadius, Math.PI * 3 / 2, 0, false);
+ context.lineTo(width, height - cornerRadius);
+ context.arc(width - cornerRadius, height - cornerRadius, cornerRadius, 0, Math.PI / 2, false);
+ context.lineTo(cornerRadius, height);
+ context.arc(cornerRadius, height - cornerRadius, cornerRadius, Math.PI / 2, Math.PI, false);
+ context.lineTo(0, cornerRadius);
+ context.arc(cornerRadius, cornerRadius, cornerRadius, Math.PI, Math.PI * 3 / 2, false);
+ }
+ context.closePath();
+ this.fillStroke(context);
+ }
+ };
+
+ Kinetic.Global.extend(Kinetic.Rect, Kinetic.Shape);
+
+})();
diff --git a/src/shapes/RegularPolygon.js b/src/shapes/RegularPolygon.js
index c19780c2..120e7718 100644
--- a/src/shapes/RegularPolygon.js
+++ b/src/shapes/RegularPolygon.js
@@ -1,65 +1,67 @@
-/**
- * RegularPolygon constructor. Examples include triangles, squares, pentagons, hexagons, etc.
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.RegularPolygon = function(config) {
- this._initRegularPolygon(config);
-};
+(function() {
+ /**
+ * RegularPolygon constructor. Examples include triangles, squares, pentagons, hexagons, etc.
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+ Kinetic.RegularPolygon = function(config) {
+ this._initRegularPolygon(config);
+ };
-Kinetic.RegularPolygon.prototype = {
- _initRegularPolygon: function(config) {
- this.setDefaultAttrs({
- radius: 0,
- sides: 0
- });
+ Kinetic.RegularPolygon.prototype = {
+ _initRegularPolygon: function(config) {
+ this.setDefaultAttrs({
+ radius: 0,
+ sides: 0
+ });
- this.shapeType = "RegularPolygon";
+ this.shapeType = "RegularPolygon";
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
- },
- drawFunc: function(context) {
- context.beginPath();
- context.moveTo(0, 0 - this.attrs.radius);
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
+ },
+ drawFunc: function(context) {
+ context.beginPath();
+ context.moveTo(0, 0 - this.attrs.radius);
- for(var n = 1; n < this.attrs.sides; n++) {
- var x = this.attrs.radius * Math.sin(n * 2 * Math.PI / this.attrs.sides);
- var y = -1 * this.attrs.radius * Math.cos(n * 2 * Math.PI / this.attrs.sides);
- context.lineTo(x, y);
+ for(var n = 1; n < this.attrs.sides; n++) {
+ var x = this.attrs.radius * Math.sin(n * 2 * Math.PI / this.attrs.sides);
+ var y = -1 * this.attrs.radius * Math.cos(n * 2 * Math.PI / this.attrs.sides);
+ context.lineTo(x, y);
+ }
+ context.closePath();
+ this.fillStroke(context);
}
- context.closePath();
- this.fillStroke(context);
- }
-};
-Kinetic.Global.extend(Kinetic.RegularPolygon, Kinetic.Shape);
+ };
+ Kinetic.Global.extend(Kinetic.RegularPolygon, Kinetic.Shape);
-// add getters setters
-Kinetic.Node.addGettersSetters(Kinetic.RegularPolygon, ['radius', 'sides']);
+ // add getters setters
+ Kinetic.Node.addGettersSetters(Kinetic.RegularPolygon, ['radius', 'sides']);
-/**
- * set radius
- * @name setRadius
- * @methodOf Kinetic.RegularPolygon.prototype
- * @param {Number} radius
- */
+ /**
+ * set radius
+ * @name setRadius
+ * @methodOf Kinetic.RegularPolygon.prototype
+ * @param {Number} radius
+ */
-/**
- * set number of sides
- * @name setSides
- * @methodOf Kinetic.RegularPolygon.prototype
- * @param {int} sides
- */
-/**
- * get radius
- * @name getRadius
- * @methodOf Kinetic.RegularPolygon.prototype
- */
+ /**
+ * set number of sides
+ * @name setSides
+ * @methodOf Kinetic.RegularPolygon.prototype
+ * @param {int} sides
+ */
+ /**
+ * get radius
+ * @name getRadius
+ * @methodOf Kinetic.RegularPolygon.prototype
+ */
-/**
- * get number of sides
- * @name getSides
- * @methodOf Kinetic.RegularPolygon.prototype
- */
\ No newline at end of file
+ /**
+ * get number of sides
+ * @name getSides
+ * @methodOf Kinetic.RegularPolygon.prototype
+ */
+})();
diff --git a/src/shapes/Sprite.js b/src/shapes/Sprite.js
index 75c40b4a..184d5891 100644
--- a/src/shapes/Sprite.js
+++ b/src/shapes/Sprite.js
@@ -1,156 +1,158 @@
-/**
- * Sprite constructor
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.Sprite = function(config) {
- this._initSprite(config);
-};
+(function() {
+ /**
+ * Sprite constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+ Kinetic.Sprite = function(config) {
+ this._initSprite(config);
+ };
-Kinetic.Sprite.prototype = {
- _initSprite: function(config) {
- this.setDefaultAttrs({
- index: 0,
- frameRate: 17
- });
- this.shapeType = "Sprite";
-
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
-
- this.anim = new Kinetic.Animation();
- var that = this;
- this.on('animationChange', function() {
- // reset index when animation changes
- that.setIndex(0);
- });
- },
- drawFunc: function(context) {
- var anim = this.attrs.animation;
- var index = this.attrs.index;
- var f = this.attrs.animations[anim][index];
+ Kinetic.Sprite.prototype = {
+ _initSprite: function(config) {
+ this.setDefaultAttrs({
+ index: 0,
+ frameRate: 17
+ });
+ this.shapeType = "Sprite";
- if(this.attrs.image) {
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
+
+ this.anim = new Kinetic.Animation();
+ var that = this;
+ this.on('animationChange', function() {
+ // reset index when animation changes
+ that.setIndex(0);
+ });
+ },
+ drawFunc: function(context) {
+ var anim = this.attrs.animation;
+ var index = this.attrs.index;
+ var f = this.attrs.animations[anim][index];
+
+ if(this.attrs.image) {
+
+ context.beginPath();
+ context.rect(0, 0, f.width, f.height);
+ context.closePath();
+
+ this.drawImage(context, this.attrs.image, f.x, f.y, f.width, f.height, 0, 0, f.width, f.height);
+ }
+ },
+ drawHitFunc: function(context) {
+ var anim = this.attrs.animation;
+ var index = this.attrs.index;
+ var f = this.attrs.animations[anim][index];
context.beginPath();
context.rect(0, 0, f.width, f.height);
context.closePath();
-
- this.drawImage(context, this.attrs.image, f.x, f.y, f.width, f.height, 0, 0, f.width, f.height);
- }
- },
- drawHitFunc: function(context) {
- var anim = this.attrs.animation;
- var index = this.attrs.index;
- var f = this.attrs.animations[anim][index];
-
- context.beginPath();
- context.rect(0, 0, f.width, f.height);
- context.closePath();
- this.fillStroke(context);
- },
- /**
- * start sprite animation
- * @name start
- * @methodOf Kinetic.Sprite.prototype
- */
- start: function() {
- var that = this;
- var layer = this.getLayer();
-
- /*
- * animation object has no executable function because
- * the updates are done with a fixed FPS with the setInterval
- * below. The anim object only needs the layer reference for
- * redraw
+ this.fillStroke(context);
+ },
+ /**
+ * start sprite animation
+ * @name start
+ * @methodOf Kinetic.Sprite.prototype
*/
- this.anim.node = layer;
+ start: function() {
+ var that = this;
+ var layer = this.getLayer();
- this.interval = setInterval(function() {
- var index = that.attrs.index;
- that._updateIndex();
- if(that.afterFrameFunc && index === that.afterFrameIndex) {
- that.afterFrameFunc();
- delete that.afterFrameFunc;
- delete that.afterFrameIndex;
+ /*
+ * animation object has no executable function because
+ * the updates are done with a fixed FPS with the setInterval
+ * below. The anim object only needs the layer reference for
+ * redraw
+ */
+ this.anim.node = layer;
+
+ this.interval = setInterval(function() {
+ var index = that.attrs.index;
+ that._updateIndex();
+ if(that.afterFrameFunc && index === that.afterFrameIndex) {
+ that.afterFrameFunc();
+ delete that.afterFrameFunc;
+ delete that.afterFrameIndex;
+ }
+ }, 1000 / this.attrs.frameRate);
+
+ this.anim.start();
+ },
+ /**
+ * stop sprite animation
+ * @name stop
+ * @methodOf Kinetic.Sprite.prototype
+ */
+ stop: function() {
+ this.anim.stop();
+ clearInterval(this.interval);
+ },
+ /**
+ * set after frame event handler
+ * @name afterFrame
+ * @methodOf Kinetic.Sprite.prototype
+ * @param {Integer} index frame index
+ * @param {Function} func function to be executed after frame has been drawn
+ */
+ afterFrame: function(index, func) {
+ this.afterFrameIndex = index;
+ this.afterFrameFunc = func;
+ },
+ _updateIndex: function() {
+ var i = this.attrs.index;
+ var a = this.attrs.animation;
+ if(i < this.attrs.animations[a].length - 1) {
+ this.attrs.index++;
}
- }, 1000 / this.attrs.frameRate);
+ else {
+ this.attrs.index = 0;
+ }
+ }
+ };
+ Kinetic.Global.extend(Kinetic.Sprite, Kinetic.Shape);
+
+ // add getters setters
+ Kinetic.Node.addGettersSetters(Kinetic.Sprite, ['animation', 'animations', 'index']);
- this.anim.start();
- },
/**
- * stop sprite animation
- * @name stop
+ * set animation key
+ * @name setAnimation
* @methodOf Kinetic.Sprite.prototype
+ * @param {String} anim animation key
*/
- stop: function() {
- this.anim.stop();
- clearInterval(this.interval);
- },
+
/**
- * set after frame event handler
- * @name afterFrame
+ * set animations object
+ * @name setAnimations
+ * @methodOf Kinetic.Sprite.prototype
+ * @param {Object} animations
+ */
+
+ /**
+ * set animation frame index
+ * @name setIndex
* @methodOf Kinetic.Sprite.prototype
* @param {Integer} index frame index
- * @param {Function} func function to be executed after frame has been drawn
*/
- afterFrame: function(index, func) {
- this.afterFrameIndex = index;
- this.afterFrameFunc = func;
- },
- _updateIndex: function() {
- var i = this.attrs.index;
- var a = this.attrs.animation;
- if(i < this.attrs.animations[a].length - 1) {
- this.attrs.index++;
- }
- else {
- this.attrs.index = 0;
- }
- }
-};
-Kinetic.Global.extend(Kinetic.Sprite, Kinetic.Shape);
-// add getters setters
-Kinetic.Node.addGettersSetters(Kinetic.Sprite, ['animation', 'animations', 'index']);
+ /**
+ * get animation key
+ * @name getAnimation
+ * @methodOf Kinetic.Sprite.prototype
+ */
-/**
- * set animation key
- * @name setAnimation
- * @methodOf Kinetic.Sprite.prototype
- * @param {String} anim animation key
- */
+ /**
+ * get animations object
+ * @name getAnimations
+ * @methodOf Kinetic.Sprite.prototype
+ */
-/**
- * set animations object
- * @name setAnimations
- * @methodOf Kinetic.Sprite.prototype
- * @param {Object} animations
- */
-
-/**
- * set animation frame index
- * @name setIndex
- * @methodOf Kinetic.Sprite.prototype
- * @param {Integer} index frame index
- */
-
-/**
- * get animation key
- * @name getAnimation
- * @methodOf Kinetic.Sprite.prototype
- */
-
-/**
- * get animations object
- * @name getAnimations
- * @methodOf Kinetic.Sprite.prototype
- */
-
-/**
- * get animation frame index
- * @name getIndex
- * @methodOf Kinetic.Sprite.prototype
- */
\ No newline at end of file
+ /**
+ * get animation frame index
+ * @name getIndex
+ * @methodOf Kinetic.Sprite.prototype
+ */
+})();
diff --git a/src/shapes/Star.js b/src/shapes/Star.js
index 3061a93c..c8efc659 100644
--- a/src/shapes/Star.js
+++ b/src/shapes/Star.js
@@ -1,82 +1,84 @@
-/**
- * Star constructor
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.Star = function(config) {
- this._initStar(config);
-};
+(function() {
+ /**
+ * Star constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+ Kinetic.Star = function(config) {
+ this._initStar(config);
+ };
-Kinetic.Star.prototype = {
- _initStar: function(config) {
- this.setDefaultAttrs({
- numPoints: 0,
- innerRadius: 0,
- outerRadius: 0
- });
+ Kinetic.Star.prototype = {
+ _initStar: function(config) {
+ this.setDefaultAttrs({
+ numPoints: 0,
+ innerRadius: 0,
+ outerRadius: 0
+ });
- this.shapeType = "Star";
+ this.shapeType = "Star";
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
- },
- drawFunc: function(context) {
- context.beginPath();
- context.moveTo(0, 0 - this.attrs.outerRadius);
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
+ },
+ drawFunc: function(context) {
+ context.beginPath();
+ context.moveTo(0, 0 - this.attrs.outerRadius);
- for(var n = 1; n < this.attrs.numPoints * 2; n++) {
- var radius = n % 2 === 0 ? this.attrs.outerRadius : this.attrs.innerRadius;
- var x = radius * Math.sin(n * Math.PI / this.attrs.numPoints);
- var y = -1 * radius * Math.cos(n * Math.PI / this.attrs.numPoints);
- context.lineTo(x, y);
+ for(var n = 1; n < this.attrs.numPoints * 2; n++) {
+ var radius = n % 2 === 0 ? this.attrs.outerRadius : this.attrs.innerRadius;
+ var x = radius * Math.sin(n * Math.PI / this.attrs.numPoints);
+ var y = -1 * radius * Math.cos(n * Math.PI / this.attrs.numPoints);
+ context.lineTo(x, y);
+ }
+ context.closePath();
+
+ this.fillStroke(context);
}
- context.closePath();
+ };
+ Kinetic.Global.extend(Kinetic.Star, Kinetic.Shape);
- this.fillStroke(context);
- }
-};
-Kinetic.Global.extend(Kinetic.Star, Kinetic.Shape);
+ // add getters setters
+ Kinetic.Node.addGettersSetters(Kinetic.Star, ['numPoints', 'innerRadius', 'outerRadius']);
-// add getters setters
-Kinetic.Node.addGettersSetters(Kinetic.Star, ['numPoints', 'innerRadius', 'outerRadius']);
+ /**
+ * set number of points
+ * @name setNumPoints
+ * @methodOf Kinetic.Star.prototype
+ * @param {Integer} points
+ */
-/**
- * set number of points
- * @name setNumPoints
- * @methodOf Kinetic.Star.prototype
- * @param {Integer} points
- */
+ /**
+ * set outer radius
+ * @name setOuterRadius
+ * @methodOf Kinetic.Star.prototype
+ * @param {Number} radius
+ */
-/**
- * set outer radius
- * @name setOuterRadius
- * @methodOf Kinetic.Star.prototype
- * @param {Number} radius
- */
+ /**
+ * set inner radius
+ * @name setInnerRadius
+ * @methodOf Kinetic.Star.prototype
+ * @param {Number} radius
+ */
-/**
- * set inner radius
- * @name setInnerRadius
- * @methodOf Kinetic.Star.prototype
- * @param {Number} radius
- */
+ /**
+ * get number of points
+ * @name getNumPoints
+ * @methodOf Kinetic.Star.prototype
+ */
-/**
- * get number of points
- * @name getNumPoints
- * @methodOf Kinetic.Star.prototype
- */
+ /**
+ * get outer radius
+ * @name getOuterRadius
+ * @methodOf Kinetic.Star.prototype
+ */
-/**
- * get outer radius
- * @name getOuterRadius
- * @methodOf Kinetic.Star.prototype
- */
-
-/**
- * get inner radius
- * @name getInnerRadius
- * @methodOf Kinetic.Star.prototype
- */
\ No newline at end of file
+ /**
+ * get inner radius
+ * @name getInnerRadius
+ * @methodOf Kinetic.Star.prototype
+ */
+})();
diff --git a/src/shapes/Text.js b/src/shapes/Text.js
index 3ba0d848..74e0fe31 100644
--- a/src/shapes/Text.js
+++ b/src/shapes/Text.js
@@ -1,404 +1,406 @@
-/**
- * Text constructor
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.Text = function(config) {
- this._initText(config);
-};
-
-Kinetic.Text.prototype = {
- _initText: function(config) {
- this.setDefaultAttrs({
- fontFamily: 'Calibri',
- text: '',
- fontSize: 12,
- align: 'left',
- verticalAlign: 'top',
- fontStyle: 'normal',
- padding: 0,
- width: 'auto',
- height: 'auto',
- detectionType: 'path',
- cornerRadius: 0,
- lineHeight: 1.2
- });
-
- this.dummyCanvas = document.createElement('canvas');
- this.shapeType = "Text";
-
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
-
- // update text data for certain attr changes
- var attrs = ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight', 'text', 'width', 'height'];
- var that = this;
- for(var n = 0; n < attrs.length; n++) {
- var attr = attrs[n];
- this.on(attr + 'Change.kinetic', that._setTextData);
- }
- that._setTextData();
- },
- drawFunc: function(context) {
- // draw rect
- Kinetic.Rect.prototype.drawFunc.call(this, context);
-
- // draw text
- var p = this.attrs.padding;
- var lineHeightPx = this.attrs.lineHeight * this.getTextHeight();
- var textArr = this.textArr;
-
- context.font = this.attrs.fontStyle + ' ' + this.attrs.fontSize + 'pt ' + this.attrs.fontFamily;
- context.textBaseline = 'middle';
- context.textAlign = 'left';
- context.save();
- context.translate(p, 0);
- context.translate(0, p + this.getTextHeight() / 2);
-
- // draw text lines
- for(var n = 0; n < textArr.length; n++) {
- var text = textArr[n];
-
- // horizontal alignment
- context.save();
- if(this.attrs.align === 'right') {
- context.translate(this.getWidth() - this._getTextSize(text).width - p * 2, 0);
- }
- else if(this.attrs.align === 'center') {
- context.translate((this.getWidth() - this._getTextSize(text).width - p * 2) / 2, 0);
- }
-
- this.fillStrokeText(context, text);
- context.restore();
- context.translate(0, lineHeightPx);
- }
- context.restore();
- },
- drawHitFunc: Kinetic.Rect.prototype.drawFunc,
+(function() {
/**
- * set text
- * @name setText
- * @methodOf Kinetic.Text.prototype
- * @param {String} text
- */
- setText: function(text) {
- var str = Kinetic.Type._isString(text) ? text : text.toString();
- this.setAttr('text', str);
- },
- /**
- * get width
- * @name getWidth
- * @methodOf Kinetic.Text.prototype
- */
- getWidth: function() {
- return this.attrs.width === 'auto' ? this.getTextWidth() + this.attrs.padding * 2 : this.attrs.width;
- },
- /**
- * get height
- * @name getHeight
- * @methodOf Kinetic.Text.prototype
- */
- getHeight: function() {
- return this.attrs.height === 'auto' ? (this.getTextHeight() * this.textArr.length * this.attrs.lineHeight) + this.attrs.padding * 2 : this.attrs.height;
- },
- /**
- * get text width
- * @name getTextWidth
- * @methodOf Kinetic.Text.prototype
- */
- getTextWidth: function() {
- return this.textWidth;
- },
- /**
- * get text height
- * @name getTextHeight
- * @methodOf Kinetic.Text.prototype
- */
- getTextHeight: function() {
- return this.textHeight;
- },
- _getTextSize: function(text) {
- var dummyCanvas = this.dummyCanvas;
- var context = dummyCanvas.getContext('2d');
-
- context.save();
- context.font = this.attrs.fontStyle + ' ' + this.attrs.fontSize + 'pt ' + this.attrs.fontFamily;
- var metrics = context.measureText(text);
- context.restore();
- return {
- width: metrics.width,
- height: parseInt(this.attrs.fontSize, 10)
- };
- },
- fillText: function(context, text, skipShadow) {
- var textFill = this.getTextFill(), textShadow = this.getTextShadow();
- if(textFill) {
- context.save();
- if(!skipShadow && textShadow) {
- this._applyTextShadow(context);
- }
- context.fillStyle = textFill;
- context.fillText(text, 0, 0);
- context.restore();
-
- if(!skipShadow && textShadow && textShadow.opacity) {
- this.fillText(context, text, true);
- }
- }
- },
- strokeText: function(context, text, skipShadow) {
- var textStroke = this.getTextStroke(), textStrokeWidth = this.getTextStrokeWidth(), textShadow = this.getTextShadow();
- if(textStroke || textStrokeWidth) {
- context.save();
- if(!skipShadow && textShadow) {
- this._applyTextShadow(context);
- }
-
- context.lineWidth = textStrokeWidth || 2;
- context.strokeStyle = textStroke || 'black';
- context.strokeText(text, 0, 0);
- context.restore();
-
- if(!skipShadow && textShadow && textShadow.opacity) {
- this.strokeText(context, text, true);
- }
- }
- },
- fillStrokeText: function(context, text) {
- this.fillText(context, text);
- this.strokeText(context, text, this.getTextShadow() && this.getTextFill());
- },
- /**
- * set text shadow object
- * @name setTextShadow
- * @methodOf Kinetic.Text.prototype
+ * Text constructor
+ * @constructor
+ * @augments Kinetic.Shape
* @param {Object} config
- * @param {String} config.color
- * @param {Number} config.blur
- * @param {Array|Object|Number} config.offset
- * @param {Number} config.opacity
*/
- setTextShadow: function(config) {
- var type = Kinetic.Type;
- if(config.offset !== undefined) {
- config.offset = type._getXY(config.offset);
- }
- this.setAttr('textShadow', type._merge(config, this.getTextShadow()));
- },
- /**
- * set text data. wrap logic and width and height setting occurs
- * here
- */
- _setTextData: function() {
- var charArr = this.attrs.text.split('');
- var arr = [];
- var row = 0;
- var addLine = true;
- this.textWidth = 0;
- this.textHeight = this._getTextSize(this.attrs.text).height;
- var lineHeightPx = this.attrs.lineHeight * this.textHeight;
- while(charArr.length > 0 && addLine && (this.attrs.height === 'auto' || lineHeightPx * (row + 1) < this.attrs.height - this.attrs.padding * 2)) {
- var index = 0;
- var line = undefined;
- addLine = false;
+ Kinetic.Text = function(config) {
+ this._initText(config);
+ };
- while(index < charArr.length) {
- if(charArr.indexOf('\n') === index) {
- // remove newline char
- charArr.splice(index, 1);
- line = charArr.splice(0, index).join('');
- break;
- }
+ Kinetic.Text.prototype = {
+ _initText: function(config) {
+ this.setDefaultAttrs({
+ fontFamily: 'Calibri',
+ text: '',
+ fontSize: 12,
+ align: 'left',
+ verticalAlign: 'top',
+ fontStyle: 'normal',
+ padding: 0,
+ width: 'auto',
+ height: 'auto',
+ detectionType: 'path',
+ cornerRadius: 0,
+ lineHeight: 1.2
+ });
- // if line exceeds inner box width
- var lineArr = charArr.slice(0, index);
- if(this.attrs.width !== 'auto' && this._getTextSize(lineArr.join('')).width > this.attrs.width - this.attrs.padding * 2) {
- /*
- * if a single character is too large to fit inside
- * the text box width, then break out of the loop
- * and stop processing
- */
- if(index == 0) {
- break;
- }
- var lastSpace = lineArr.lastIndexOf(' ');
- var lastDash = lineArr.lastIndexOf('-');
- var wrapIndex = Math.max(lastSpace, lastDash);
- if(wrapIndex >= 0) {
- line = charArr.splice(0, 1 + wrapIndex).join('');
- break;
- }
- /*
- * if not able to word wrap based on space or dash,
- * go ahead and wrap in the middle of a word if needed
- */
- line = charArr.splice(0, index).join('');
- break;
- }
- index++;
+ this.dummyCanvas = document.createElement('canvas');
+ this.shapeType = "Text";
- // if the end is reached
- if(index === charArr.length) {
- line = charArr.splice(0, index).join('');
- }
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
+
+ // update text data for certain attr changes
+ var attrs = ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight', 'text', 'width', 'height'];
+ var that = this;
+ for(var n = 0; n < attrs.length; n++) {
+ var attr = attrs[n];
+ this.on(attr + 'Change.kinetic', that._setTextData);
}
- this.textWidth = Math.max(this.textWidth, this._getTextSize(line).width);
- if(line !== undefined) {
- arr.push(line);
- addLine = true;
+ that._setTextData();
+ },
+ drawFunc: function(context) {
+ // draw rect
+ Kinetic.Rect.prototype.drawFunc.call(this, context);
+
+ // draw text
+ var p = this.attrs.padding;
+ var lineHeightPx = this.attrs.lineHeight * this.getTextHeight();
+ var textArr = this.textArr;
+
+ context.font = this.attrs.fontStyle + ' ' + this.attrs.fontSize + 'pt ' + this.attrs.fontFamily;
+ context.textBaseline = 'middle';
+ context.textAlign = 'left';
+ context.save();
+ context.translate(p, 0);
+ context.translate(0, p + this.getTextHeight() / 2);
+
+ // draw text lines
+ for(var n = 0; n < textArr.length; n++) {
+ var text = textArr[n];
+
+ // horizontal alignment
+ context.save();
+ if(this.attrs.align === 'right') {
+ context.translate(this.getWidth() - this._getTextSize(text).width - p * 2, 0);
+ }
+ else if(this.attrs.align === 'center') {
+ context.translate((this.getWidth() - this._getTextSize(text).width - p * 2) / 2, 0);
+ }
+
+ this.fillStrokeText(context, text);
+ context.restore();
+ context.translate(0, lineHeightPx);
}
- row++;
- }
- this.textArr = arr;
- },
- _applyTextShadow: function(context) {
- var textShadow = this.getTextShadow();
- if(textShadow) {
- var aa = this.getAbsoluteOpacity();
- // defaults
- var color = textShadow.color || 'black';
- var blur = textShadow.blur || 5;
- var offset = textShadow.offset || {
- x: 0,
- y: 0
+ context.restore();
+ },
+ drawHitFunc: Kinetic.Rect.prototype.drawFunc,
+ /**
+ * set text
+ * @name setText
+ * @methodOf Kinetic.Text.prototype
+ * @param {String} text
+ */
+ setText: function(text) {
+ var str = Kinetic.Type._isString(text) ? text : text.toString();
+ this.setAttr('text', str);
+ },
+ /**
+ * get width
+ * @name getWidth
+ * @methodOf Kinetic.Text.prototype
+ */
+ getWidth: function() {
+ return this.attrs.width === 'auto' ? this.getTextWidth() + this.attrs.padding * 2 : this.attrs.width;
+ },
+ /**
+ * get height
+ * @name getHeight
+ * @methodOf Kinetic.Text.prototype
+ */
+ getHeight: function() {
+ return this.attrs.height === 'auto' ? (this.getTextHeight() * this.textArr.length * this.attrs.lineHeight) + this.attrs.padding * 2 : this.attrs.height;
+ },
+ /**
+ * get text width
+ * @name getTextWidth
+ * @methodOf Kinetic.Text.prototype
+ */
+ getTextWidth: function() {
+ return this.textWidth;
+ },
+ /**
+ * get text height
+ * @name getTextHeight
+ * @methodOf Kinetic.Text.prototype
+ */
+ getTextHeight: function() {
+ return this.textHeight;
+ },
+ _getTextSize: function(text) {
+ var dummyCanvas = this.dummyCanvas;
+ var context = dummyCanvas.getContext('2d');
+
+ context.save();
+ context.font = this.attrs.fontStyle + ' ' + this.attrs.fontSize + 'pt ' + this.attrs.fontFamily;
+ var metrics = context.measureText(text);
+ context.restore();
+ return {
+ width: metrics.width,
+ height: parseInt(this.attrs.fontSize, 10)
};
+ },
+ fillText: function(context, text, skipShadow) {
+ var textFill = this.getTextFill(), textShadow = this.getTextShadow();
+ if(textFill) {
+ context.save();
+ if(!skipShadow && textShadow) {
+ this._applyTextShadow(context);
+ }
+ context.fillStyle = textFill;
+ context.fillText(text, 0, 0);
+ context.restore();
- if(textShadow.opacity) {
- context.globalAlpha = textShadow.opacity * aa;
+ if(!skipShadow && textShadow && textShadow.opacity) {
+ this.fillText(context, text, true);
+ }
+ }
+ },
+ strokeText: function(context, text, skipShadow) {
+ var textStroke = this.getTextStroke(), textStrokeWidth = this.getTextStrokeWidth(), textShadow = this.getTextShadow();
+ if(textStroke || textStrokeWidth) {
+ context.save();
+ if(!skipShadow && textShadow) {
+ this._applyTextShadow(context);
+ }
+
+ context.lineWidth = textStrokeWidth || 2;
+ context.strokeStyle = textStroke || 'black';
+ context.strokeText(text, 0, 0);
+ context.restore();
+
+ if(!skipShadow && textShadow && textShadow.opacity) {
+ this.strokeText(context, text, true);
+ }
+ }
+ },
+ fillStrokeText: function(context, text) {
+ this.fillText(context, text);
+ this.strokeText(context, text, this.getTextShadow() && this.getTextFill());
+ },
+ /**
+ * set text shadow object
+ * @name setTextShadow
+ * @methodOf Kinetic.Text.prototype
+ * @param {Object} config
+ * @param {String} config.color
+ * @param {Number} config.blur
+ * @param {Array|Object|Number} config.offset
+ * @param {Number} config.opacity
+ */
+ setTextShadow: function(config) {
+ var type = Kinetic.Type;
+ if(config.offset !== undefined) {
+ config.offset = type._getXY(config.offset);
+ }
+ this.setAttr('textShadow', type._merge(config, this.getTextShadow()));
+ },
+ /**
+ * set text data. wrap logic and width and height setting occurs
+ * here
+ */
+ _setTextData: function() {
+ var charArr = this.attrs.text.split('');
+ var arr = [];
+ var row = 0;
+ var addLine = true;
+ this.textWidth = 0;
+ this.textHeight = this._getTextSize(this.attrs.text).height;
+ var lineHeightPx = this.attrs.lineHeight * this.textHeight;
+ while(charArr.length > 0 && addLine && (this.attrs.height === 'auto' || lineHeightPx * (row + 1) < this.attrs.height - this.attrs.padding * 2)) {
+ var index = 0;
+ var line = undefined;
+ addLine = false;
+
+ while(index < charArr.length) {
+ if(charArr.indexOf('\n') === index) {
+ // remove newline char
+ charArr.splice(index, 1);
+ line = charArr.splice(0, index).join('');
+ break;
+ }
+
+ // if line exceeds inner box width
+ var lineArr = charArr.slice(0, index);
+ if(this.attrs.width !== 'auto' && this._getTextSize(lineArr.join('')).width > this.attrs.width - this.attrs.padding * 2) {
+ /*
+ * if a single character is too large to fit inside
+ * the text box width, then break out of the loop
+ * and stop processing
+ */
+ if(index == 0) {
+ break;
+ }
+ var lastSpace = lineArr.lastIndexOf(' ');
+ var lastDash = lineArr.lastIndexOf('-');
+ var wrapIndex = Math.max(lastSpace, lastDash);
+ if(wrapIndex >= 0) {
+ line = charArr.splice(0, 1 + wrapIndex).join('');
+ break;
+ }
+ /*
+ * if not able to word wrap based on space or dash,
+ * go ahead and wrap in the middle of a word if needed
+ */
+ line = charArr.splice(0, index).join('');
+ break;
+ }
+ index++;
+
+ // if the end is reached
+ if(index === charArr.length) {
+ line = charArr.splice(0, index).join('');
+ }
+ }
+ this.textWidth = Math.max(this.textWidth, this._getTextSize(line).width);
+ if(line !== undefined) {
+ arr.push(line);
+ addLine = true;
+ }
+ row++;
+ }
+ this.textArr = arr;
+ },
+ _applyTextShadow: function(context) {
+ var textShadow = this.getTextShadow();
+ if(textShadow) {
+ var aa = this.getAbsoluteOpacity();
+ // defaults
+ var color = textShadow.color || 'black';
+ var blur = textShadow.blur || 5;
+ var offset = textShadow.offset || {
+ x: 0,
+ y: 0
+ };
+
+ if(textShadow.opacity) {
+ context.globalAlpha = textShadow.opacity * aa;
+ }
+ context.shadowColor = color;
+ context.shadowBlur = blur;
+ context.shadowOffsetX = offset.x;
+ context.shadowOffsetY = offset.y;
}
- context.shadowColor = color;
- context.shadowBlur = blur;
- context.shadowOffsetX = offset.x;
- context.shadowOffsetY = offset.y;
}
- }
-};
-Kinetic.Global.extend(Kinetic.Text, Kinetic.Shape);
+ };
+ Kinetic.Global.extend(Kinetic.Text, Kinetic.Shape);
-// add getters setters
-Kinetic.Node.addGettersSetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth', 'padding', 'align', 'lineHeight']);
-Kinetic.Node.addGetters(Kinetic.Text, ['text', 'textShadow']);
-/**
- * set font family
- * @name setFontFamily
- * @methodOf Kinetic.Text.prototype
- * @param {String} fontFamily
- */
+ // add getters setters
+ Kinetic.Node.addGettersSetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth', 'padding', 'align', 'lineHeight']);
+ Kinetic.Node.addGetters(Kinetic.Text, ['text', 'textShadow']);
+ /**
+ * set font family
+ * @name setFontFamily
+ * @methodOf Kinetic.Text.prototype
+ * @param {String} fontFamily
+ */
-/**
- * set font size
- * @name setFontSize
- * @methodOf Kinetic.Text.prototype
- * @param {int} fontSize
- */
+ /**
+ * set font size
+ * @name setFontSize
+ * @methodOf Kinetic.Text.prototype
+ * @param {int} fontSize
+ */
-/**
- * set font style. Can be "normal", "italic", or "bold". "normal" is the default.
- * @name setFontStyle
- * @methodOf Kinetic.Text.prototype
- * @param {String} fontStyle
- */
+ /**
+ * set font style. Can be "normal", "italic", or "bold". "normal" is the default.
+ * @name setFontStyle
+ * @methodOf Kinetic.Text.prototype
+ * @param {String} fontStyle
+ */
-/**
- * set text fill color
- * @name setTextFill
- * @methodOf Kinetic.Text.prototype
- * @param {String} textFill
- */
+ /**
+ * set text fill color
+ * @name setTextFill
+ * @methodOf Kinetic.Text.prototype
+ * @param {String} textFill
+ */
-/**
- * set text stroke color
- * @name setFontStroke
- * @methodOf Kinetic.Text.prototype
- * @param {String} textStroke
- */
+ /**
+ * set text stroke color
+ * @name setFontStroke
+ * @methodOf Kinetic.Text.prototype
+ * @param {String} textStroke
+ */
-/**
- * set text stroke width
- * @name setTextStrokeWidth
- * @methodOf Kinetic.Text.prototype
- * @param {int} textStrokeWidth
- */
+ /**
+ * set text stroke width
+ * @name setTextStrokeWidth
+ * @methodOf Kinetic.Text.prototype
+ * @param {int} textStrokeWidth
+ */
-/**
- * set padding
- * @name setPadding
- * @methodOf Kinetic.Text.prototype
- * @param {int} padding
- */
+ /**
+ * set padding
+ * @name setPadding
+ * @methodOf Kinetic.Text.prototype
+ * @param {int} padding
+ */
-/**
- * set horizontal align of text
- * @name setAlign
- * @methodOf Kinetic.Text.prototype
- * @param {String} align align can be 'left', 'center', or 'right'
- */
+ /**
+ * set horizontal align of text
+ * @name setAlign
+ * @methodOf Kinetic.Text.prototype
+ * @param {String} align align can be 'left', 'center', or 'right'
+ */
-/**
- * set line height
- * @name setLineHeight
- * @methodOf Kinetic.Text.prototype
- * @param {Number} lineHeight default is 1.2
- */
+ /**
+ * set line height
+ * @name setLineHeight
+ * @methodOf Kinetic.Text.prototype
+ * @param {Number} lineHeight default is 1.2
+ */
-/**
- * get font family
- * @name getFontFamily
- * @methodOf Kinetic.Text.prototype
- */
+ /**
+ * get font family
+ * @name getFontFamily
+ * @methodOf Kinetic.Text.prototype
+ */
-/**
- * get font size
- * @name getFontSize
- * @methodOf Kinetic.Text.prototype
- */
+ /**
+ * get font size
+ * @name getFontSize
+ * @methodOf Kinetic.Text.prototype
+ */
-/**
- * get font style
- * @name getFontStyle
- * @methodOf Kinetic.Text.prototype
- */
+ /**
+ * get font style
+ * @name getFontStyle
+ * @methodOf Kinetic.Text.prototype
+ */
-/**
- * get text fill color
- * @name getTextFill
- * @methodOf Kinetic.Text.prototype
- */
+ /**
+ * get text fill color
+ * @name getTextFill
+ * @methodOf Kinetic.Text.prototype
+ */
-/**
- * get text stroke color
- * @name getTextStroke
- * @methodOf Kinetic.Text.prototype
- */
+ /**
+ * get text stroke color
+ * @name getTextStroke
+ * @methodOf Kinetic.Text.prototype
+ */
-/**
- * get text stroke width
- * @name getTextStrokeWidth
- * @methodOf Kinetic.Text.prototype
- */
+ /**
+ * get text stroke width
+ * @name getTextStrokeWidth
+ * @methodOf Kinetic.Text.prototype
+ */
-/**
- * get padding
- * @name getPadding
- * @methodOf Kinetic.Text.prototype
- */
+ /**
+ * get padding
+ * @name getPadding
+ * @methodOf Kinetic.Text.prototype
+ */
-/**
- * get horizontal align
- * @name getAlign
- * @methodOf Kinetic.Text.prototype
- */
+ /**
+ * get horizontal align
+ * @name getAlign
+ * @methodOf Kinetic.Text.prototype
+ */
-/**
- * get line height
- * @name getLineHeight
- * @methodOf Kinetic.Text.prototype
- */
+ /**
+ * get line height
+ * @name getLineHeight
+ * @methodOf Kinetic.Text.prototype
+ */
-/**
- * get text
- * @name getText
- * @methodOf Kinetic.Text.prototype
- */
\ No newline at end of file
+ /**
+ * get text
+ * @name getText
+ * @methodOf Kinetic.Text.prototype
+ */
+})();
diff --git a/src/shapes/TextPath.js b/src/shapes/TextPath.js
index 70e53d1e..0dddae92 100644
--- a/src/shapes/TextPath.js
+++ b/src/shapes/TextPath.js
@@ -1,393 +1,395 @@
-/**
- * Path constructor.
- * @author Jason Follas
- * @constructor
- * @augments Kinetic.Shape
- * @param {Object} config
- */
-Kinetic.TextPath = function(config) {
- this._initTextPath(config);
-};
+(function() {
+ /**
+ * Path constructor.
+ * @author Jason Follas
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+ Kinetic.TextPath = function(config) {
+ this._initTextPath(config);
+ };
-Kinetic.TextPath.prototype = {
- _initTextPath: function(config) {
- this.setDefaultAttrs({
- fontFamily: 'Calibri',
- fontSize: 12,
- fontStyle: 'normal',
- detectionType: 'path',
- text: ''
- });
+ Kinetic.TextPath.prototype = {
+ _initTextPath: function(config) {
+ this.setDefaultAttrs({
+ fontFamily: 'Calibri',
+ fontSize: 12,
+ fontStyle: 'normal',
+ detectionType: 'path',
+ text: ''
+ });
- this.dummyCanvas = document.createElement('canvas');
- this.shapeType = "TextPath";
- this.dataArray = [];
- var that = this;
+ this.dummyCanvas = document.createElement('canvas');
+ this.shapeType = "TextPath";
+ this.dataArray = [];
+ var that = this;
- // call super constructor
- Kinetic.Shape.call(this, config);
- this._setDrawFuncs();
+ // call super constructor
+ Kinetic.Shape.call(this, config);
+ this._setDrawFuncs();
- this.dataArray = Kinetic.Path.parsePathData(this.attrs.data);
- this.on('dataChange', function() {
- that.dataArray = Kinetic.Path.parsePathData(this.attrs.data);
- });
- // update text data for certain attr changes
- var attrs = ['text', 'textStroke', 'textStrokeWidth'];
- for(var n = 0; n < attrs.length; n++) {
- var attr = attrs[n];
- this.on(attr + 'Change', that._setTextData);
- }
- that._setTextData();
- },
- drawFunc: function(context) {
- var charArr = this.charArr;
+ this.dataArray = Kinetic.Path.parsePathData(this.attrs.data);
+ this.on('dataChange', function() {
+ that.dataArray = Kinetic.Path.parsePathData(this.attrs.data);
+ });
+ // update text data for certain attr changes
+ var attrs = ['text', 'textStroke', 'textStrokeWidth'];
+ for(var n = 0; n < attrs.length; n++) {
+ var attr = attrs[n];
+ this.on(attr + 'Change', that._setTextData);
+ }
+ that._setTextData();
+ },
+ drawFunc: function(context) {
+ var charArr = this.charArr;
- context.font = this.attrs.fontStyle + ' ' + this.attrs.fontSize + 'pt ' + this.attrs.fontFamily;
- context.textBaseline = 'middle';
- context.textAlign = 'left';
- context.save();
+ context.font = this.attrs.fontStyle + ' ' + this.attrs.fontSize + 'pt ' + this.attrs.fontFamily;
+ context.textBaseline = 'middle';
+ context.textAlign = 'left';
+ context.save();
- var glyphInfo = this.glyphInfo;
+ var glyphInfo = this.glyphInfo;
- var appliedShadow = this.appliedShadow;
- for(var i = 0; i < glyphInfo.length; i++) {
- /*
- * need to reset appliedShadow flag so that shadows
- * are appropriately applied to each line of text
- */
- this.appliedShadow = appliedShadow;
+ var appliedShadow = this.appliedShadow;
+ for(var i = 0; i < glyphInfo.length; i++) {
+ /*
+ * need to reset appliedShadow flag so that shadows
+ * are appropriately applied to each line of text
+ */
+ this.appliedShadow = appliedShadow;
+
+ context.save();
+
+ var p0 = glyphInfo[i].p0;
+ var p1 = glyphInfo[i].p1;
+ var ht = parseFloat(this.attrs.fontSize);
+
+ context.translate(p0.x, p0.y);
+ context.rotate(glyphInfo[i].rotation);
+
+ this.fillStrokeText(context, glyphInfo[i].text);
+
+ context.restore();
+
+ //// To assist with debugging visually, uncomment following
+ // context.beginPath();
+ // if (i % 2)
+ // context.strokeStyle = 'cyan';
+ // else
+ // context.strokeStyle = 'green';
+
+ // context.moveTo(p0.x, p0.y);
+ // context.lineTo(p1.x, p1.y);
+ // context.stroke();
+ }
+
+ context.restore();
+ },
+ /**
+ * get text width in pixels
+ * @name getTextWidth
+ * @methodOf Kinetic.TextPath.prototype
+ */
+ getTextWidth: function() {
+ return this.textWidth;
+ },
+ /**
+ * get text height in pixels
+ * @name getTextHeight
+ * @methodOf Kinetic.TextPath.prototype
+ */
+ getTextHeight: function() {
+ return this.textHeight;
+ },
+ /**
+ * set text
+ * @name setText
+ * @methodOf Kinetic.TextPath.prototype
+ * @param {String} text
+ */
+ setText: function(text) {
+ Kinetic.Text.prototype.setText.call(this, text);
+ },
+ _getTextSize: function(text) {
+ var dummyCanvas = this.dummyCanvas;
+ var context = dummyCanvas.getContext('2d');
context.save();
- var p0 = glyphInfo[i].p0;
- var p1 = glyphInfo[i].p1;
- var ht = parseFloat(this.attrs.fontSize);
-
- context.translate(p0.x, p0.y);
- context.rotate(glyphInfo[i].rotation);
-
- this.fillStrokeText(context, glyphInfo[i].text);
+ context.font = this.attrs.fontStyle + ' ' + this.attrs.fontSize + 'pt ' + this.attrs.fontFamily;
+ var metrics = context.measureText(text);
context.restore();
- //// To assist with debugging visually, uncomment following
- // context.beginPath();
- // if (i % 2)
- // context.strokeStyle = 'cyan';
- // else
- // context.strokeStyle = 'green';
+ return {
+ width: metrics.width,
+ height: parseInt(this.attrs.fontSize, 10)
+ };
+ },
+ /**
+ * set text data.
+ */
+ _setTextData: function() {
- // context.moveTo(p0.x, p0.y);
- // context.lineTo(p1.x, p1.y);
- // context.stroke();
- }
+ var that = this;
+ var size = this._getTextSize(this.attrs.text);
+ this.textWidth = size.width;
+ this.textHeight = size.height;
- context.restore();
- },
- /**
- * get text width in pixels
- * @name getTextWidth
- * @methodOf Kinetic.TextPath.prototype
- */
- getTextWidth: function() {
- return this.textWidth;
- },
- /**
- * get text height in pixels
- * @name getTextHeight
- * @methodOf Kinetic.TextPath.prototype
- */
- getTextHeight: function() {
- return this.textHeight;
- },
- /**
- * set text
- * @name setText
- * @methodOf Kinetic.TextPath.prototype
- * @param {String} text
- */
- setText: function(text) {
- Kinetic.Text.prototype.setText.call(this, text);
- },
- _getTextSize: function(text) {
- var dummyCanvas = this.dummyCanvas;
- var context = dummyCanvas.getContext('2d');
+ this.glyphInfo = [];
- context.save();
+ var charArr = this.attrs.text.split('');
- context.font = this.attrs.fontStyle + ' ' + this.attrs.fontSize + 'pt ' + this.attrs.fontFamily;
- var metrics = context.measureText(text);
+ var p0, p1, pathCmd;
- context.restore();
+ var pIndex = -1;
+ var currentT = 0;
- return {
- width: metrics.width,
- height: parseInt(this.attrs.fontSize, 10)
- };
- },
- /**
- * set text data.
- */
- _setTextData: function() {
+ var getNextPathSegment = function() {
+ currentT = 0;
+ var pathData = that.dataArray;
- var that = this;
- var size = this._getTextSize(this.attrs.text);
- this.textWidth = size.width;
- this.textHeight = size.height;
+ for(var i = pIndex + 1; i < pathData.length; i++) {
+ if(pathData[i].pathLength > 0) {
+ pIndex = i;
- this.glyphInfo = [];
-
- var charArr = this.attrs.text.split('');
-
- var p0, p1, pathCmd;
-
- var pIndex = -1;
- var currentT = 0;
-
- var getNextPathSegment = function() {
- currentT = 0;
- var pathData = that.dataArray;
-
- for(var i = pIndex + 1; i < pathData.length; i++) {
- if(pathData[i].pathLength > 0) {
- pIndex = i;
-
- return pathData[i];
- }
- else if(pathData[i].command == 'M') {
- p0 = {
- x: pathData[i].points[0],
- y: pathData[i].points[1]
- };
- }
- }
-
- return {};
- };
- var findSegmentToFitCharacter = function(c, before) {
-
- var glyphWidth = that._getTextSize(c).width;
-
- var currLen = 0;
- var attempts = 0;
- var needNextSegment = false;
- p1 = undefined;
- while(Math.abs(glyphWidth - currLen) / glyphWidth > 0.01 && attempts < 25) {
- attempts++;
- var cumulativePathLength = currLen;
- while(pathCmd === undefined) {
- pathCmd = getNextPathSegment();
-
- if(pathCmd && cumulativePathLength + pathCmd.pathLength < glyphWidth) {
- cumulativePathLength += pathCmd.pathLength;
- pathCmd = undefined;
+ return pathData[i];
+ }
+ else if(pathData[i].command == 'M') {
+ p0 = {
+ x: pathData[i].points[0],
+ y: pathData[i].points[1]
+ };
}
}
- if(pathCmd === {} || p0 === undefined)
- return undefined;
+ return {};
+ };
+ var findSegmentToFitCharacter = function(c, before) {
- var needNewSegment = false;
+ var glyphWidth = that._getTextSize(c).width;
- switch (pathCmd.command) {
- case 'L':
- if(Kinetic.Path.getLineLength(p0.x, p0.y, pathCmd.points[0], pathCmd.points[1]) > glyphWidth) {
- p1 = Kinetic.Path.getPointOnLine(glyphWidth, p0.x, p0.y, pathCmd.points[0], pathCmd.points[1], p0.x, p0.y);
- }
- else
+ var currLen = 0;
+ var attempts = 0;
+ var needNextSegment = false;
+ p1 = undefined;
+ while(Math.abs(glyphWidth - currLen) / glyphWidth > 0.01 && attempts < 25) {
+ attempts++;
+ var cumulativePathLength = currLen;
+ while(pathCmd === undefined) {
+ pathCmd = getNextPathSegment();
+
+ if(pathCmd && cumulativePathLength + pathCmd.pathLength < glyphWidth) {
+ cumulativePathLength += pathCmd.pathLength;
pathCmd = undefined;
- break;
- case 'A':
-
- var start = pathCmd.points[4];
- // 4 = theta
- var dTheta = pathCmd.points[5];
- // 5 = dTheta
- var end = pathCmd.points[4] + dTheta;
-
- if(currentT === 0)
- currentT = start + 0.00000001;
- // Just in case start is 0
- else if(glyphWidth > currLen)
- currentT += (Math.PI / 180.0) * dTheta / Math.abs(dTheta);
- else
- currentT -= Math.PI / 360.0 * dTheta / Math.abs(dTheta);
-
- if(Math.abs(currentT) > Math.abs(end)) {
- currentT = end;
- needNewSegment = true;
}
- p1 = Kinetic.Path.getPointOnEllipticalArc(pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], currentT, pathCmd.points[6]);
- break;
- case 'C':
- if(currentT === 0) {
- if(glyphWidth > pathCmd.pathLength)
- currentT = 0.00000001;
+ }
+
+ if(pathCmd === {} || p0 === undefined)
+ return undefined;
+
+ var needNewSegment = false;
+
+ switch (pathCmd.command) {
+ case 'L':
+ if(Kinetic.Path.getLineLength(p0.x, p0.y, pathCmd.points[0], pathCmd.points[1]) > glyphWidth) {
+ p1 = Kinetic.Path.getPointOnLine(glyphWidth, p0.x, p0.y, pathCmd.points[0], pathCmd.points[1], p0.x, p0.y);
+ }
else
+ pathCmd = undefined;
+ break;
+ case 'A':
+
+ var start = pathCmd.points[4];
+ // 4 = theta
+ var dTheta = pathCmd.points[5];
+ // 5 = dTheta
+ var end = pathCmd.points[4] + dTheta;
+
+ if(currentT === 0)
+ currentT = start + 0.00000001;
+ // Just in case start is 0
+ else if(glyphWidth > currLen)
+ currentT += (Math.PI / 180.0) * dTheta / Math.abs(dTheta);
+ else
+ currentT -= Math.PI / 360.0 * dTheta / Math.abs(dTheta);
+
+ if(Math.abs(currentT) > Math.abs(end)) {
+ currentT = end;
+ needNewSegment = true;
+ }
+ p1 = Kinetic.Path.getPointOnEllipticalArc(pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], currentT, pathCmd.points[6]);
+ break;
+ case 'C':
+ if(currentT === 0) {
+ if(glyphWidth > pathCmd.pathLength)
+ currentT = 0.00000001;
+ else
+ currentT = glyphWidth / pathCmd.pathLength;
+ }
+ else if(glyphWidth > currLen)
+ currentT += (glyphWidth - currLen) / pathCmd.pathLength;
+ else
+ currentT -= (currLen - glyphWidth) / pathCmd.pathLength;
+
+ if(currentT > 1.0) {
+ currentT = 1.0;
+ needNewSegment = true;
+ }
+ p1 = Kinetic.Path.getPointOnCubicBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], pathCmd.points[4], pathCmd.points[5]);
+ break;
+ case 'Q':
+ if(currentT === 0)
currentT = glyphWidth / pathCmd.pathLength;
- }
- else if(glyphWidth > currLen)
- currentT += (glyphWidth - currLen) / pathCmd.pathLength;
- else
- currentT -= (currLen - glyphWidth) / pathCmd.pathLength;
+ else if(glyphWidth > currLen)
+ currentT += (glyphWidth - currLen) / pathCmd.pathLength;
+ else
+ currentT -= (currLen - glyphWidth) / pathCmd.pathLength;
- if(currentT > 1.0) {
- currentT = 1.0;
- needNewSegment = true;
- }
- p1 = Kinetic.Path.getPointOnCubicBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3], pathCmd.points[4], pathCmd.points[5]);
- break;
- case 'Q':
- if(currentT === 0)
- currentT = glyphWidth / pathCmd.pathLength;
- else if(glyphWidth > currLen)
- currentT += (glyphWidth - currLen) / pathCmd.pathLength;
- else
- currentT -= (currLen - glyphWidth) / pathCmd.pathLength;
+ if(currentT > 1.0) {
+ currentT = 1.0;
+ needNewSegment = true;
+ }
+ p1 = Kinetic.Path.getPointOnQuadraticBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3]);
+ break;
- if(currentT > 1.0) {
- currentT = 1.0;
- needNewSegment = true;
- }
- p1 = Kinetic.Path.getPointOnQuadraticBezier(currentT, pathCmd.start.x, pathCmd.start.y, pathCmd.points[0], pathCmd.points[1], pathCmd.points[2], pathCmd.points[3]);
- break;
+ }
+ if(p1 !== undefined) {
+ currLen = Kinetic.Path.getLineLength(p0.x, p0.y, p1.x, p1.y);
+ }
+
+ if(needNewSegment) {
+ needNewSegment = false;
+ pathCmd = undefined;
+ }
}
+ };
+ for(var i = 0; i < charArr.length; i++) {
- if(p1 !== undefined) {
- currLen = Kinetic.Path.getLineLength(p0.x, p0.y, p1.x, p1.y);
- }
+ // Find p1 such that line segment between p0 and p1 is approx. width of glyph
+ findSegmentToFitCharacter(charArr[i]);
- if(needNewSegment) {
- needNewSegment = false;
- pathCmd = undefined;
- }
+ if(p0 === undefined || p1 === undefined)
+ break;
+
+ var width = Kinetic.Path.getLineLength(p0.x, p0.y, p1.x, p1.y);
+
+ // Note: Since glyphs are rendered one at a time, any kerning pair data built into the font will not be used.
+ // Can foresee having a rough pair table built in that the developer can override as needed.
+
+ var kern = 0;
+ // placeholder for future implementation
+
+ var midpoint = Kinetic.Path.getPointOnLine(kern + width / 2.0, p0.x, p0.y, p1.x, p1.y);
+
+ var rotation = Math.atan2((p1.y - p0.y), (p1.x - p0.x));
+ this.glyphInfo.push({
+ transposeX: midpoint.x,
+ transposeY: midpoint.y,
+ text: charArr[i],
+ rotation: rotation,
+ p0: p0,
+ p1: p1
+ });
+ p0 = p1;
}
- };
- for(var i = 0; i < charArr.length; i++) {
-
- // Find p1 such that line segment between p0 and p1 is approx. width of glyph
- findSegmentToFitCharacter(charArr[i]);
-
- if(p0 === undefined || p1 === undefined)
- break;
-
- var width = Kinetic.Path.getLineLength(p0.x, p0.y, p1.x, p1.y);
-
- // Note: Since glyphs are rendered one at a time, any kerning pair data built into the font will not be used.
- // Can foresee having a rough pair table built in that the developer can override as needed.
-
- var kern = 0;
- // placeholder for future implementation
-
- var midpoint = Kinetic.Path.getPointOnLine(kern + width / 2.0, p0.x, p0.y, p1.x, p1.y);
-
- var rotation = Math.atan2((p1.y - p0.y), (p1.x - p0.x));
- this.glyphInfo.push({
- transposeX: midpoint.x,
- transposeY: midpoint.y,
- text: charArr[i],
- rotation: rotation,
- p0: p0,
- p1: p1
- });
- p0 = p1;
}
- }
-};
-Kinetic.Global.extend(Kinetic.TextPath, Kinetic.Shape);
+ };
+ Kinetic.Global.extend(Kinetic.TextPath, Kinetic.Shape);
-// add setters and getters
-Kinetic.Node.addGettersSetters(Kinetic.TextPath, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth']);
-Kinetic.Node.addGetters(Kinetic.TextPath, ['text', 'textShadow']);
+ // add setters and getters
+ Kinetic.Node.addGettersSetters(Kinetic.TextPath, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth']);
+ Kinetic.Node.addGetters(Kinetic.TextPath, ['text', 'textShadow']);
-// reference Text methods
-Kinetic.TextPath.prototype.setTextShadow = Kinetic.Text.prototype.setTextShadow;
-Kinetic.TextPath.prototype.fillText = Kinetic.Text.prototype.fillText;
-Kinetic.TextPath.prototype.strokeText = Kinetic.Text.prototype.strokeText;
-Kinetic.TextPath.prototype.fillStrokeText = Kinetic.Text.prototype.strokeText;
+ // reference Text methods
+ Kinetic.TextPath.prototype.setTextShadow = Kinetic.Text.prototype.setTextShadow;
+ Kinetic.TextPath.prototype.fillText = Kinetic.Text.prototype.fillText;
+ Kinetic.TextPath.prototype.strokeText = Kinetic.Text.prototype.strokeText;
+ Kinetic.TextPath.prototype.fillStrokeText = Kinetic.Text.prototype.strokeText;
-/**
- * set font family
- * @name setFontFamily
- * @methodOf Kinetic.TextPath.prototype
- * @param {String} fontFamily
- */
+ /**
+ * set font family
+ * @name setFontFamily
+ * @methodOf Kinetic.TextPath.prototype
+ * @param {String} fontFamily
+ */
-/**
- * set font size
- * @name setFontSize
- * @methodOf Kinetic.TextPath.prototype
- * @param {int} fontSize
- */
+ /**
+ * set font size
+ * @name setFontSize
+ * @methodOf Kinetic.TextPath.prototype
+ * @param {int} fontSize
+ */
-/**
- * set font style. Can be "normal", "italic", or "bold". "normal" is the default.
- * @name setFontStyle
- * @methodOf Kinetic.TextPath.prototype
- * @param {String} fontStyle
- */
+ /**
+ * set font style. Can be "normal", "italic", or "bold". "normal" is the default.
+ * @name setFontStyle
+ * @methodOf Kinetic.TextPath.prototype
+ * @param {String} fontStyle
+ */
-/**
- * set text fill color
- * @name setTextFill
- * @methodOf Kinetic.TextPath.prototype
- * @param {String} textFill
- */
+ /**
+ * set text fill color
+ * @name setTextFill
+ * @methodOf Kinetic.TextPath.prototype
+ * @param {String} textFill
+ */
-/**
- * set text stroke color
- * @name setFontStroke
- * @methodOf Kinetic.TextPath.prototype
- * @param {String} textStroke
- */
+ /**
+ * set text stroke color
+ * @name setFontStroke
+ * @methodOf Kinetic.TextPath.prototype
+ * @param {String} textStroke
+ */
-/**
- * set text stroke width
- * @name setTextStrokeWidth
- * @methodOf Kinetic.TextPath.prototype
- * @param {int} textStrokeWidth
- */
+ /**
+ * set text stroke width
+ * @name setTextStrokeWidth
+ * @methodOf Kinetic.TextPath.prototype
+ * @param {int} textStrokeWidth
+ */
-/**
- * get font family
- * @name getFontFamily
- * @methodOf Kinetic.TextPath.prototype
- */
+ /**
+ * get font family
+ * @name getFontFamily
+ * @methodOf Kinetic.TextPath.prototype
+ */
-/**
- * get font size
- * @name getFontSize
- * @methodOf Kinetic.TextPath.prototype
- */
+ /**
+ * get font size
+ * @name getFontSize
+ * @methodOf Kinetic.TextPath.prototype
+ */
-/**
- * get font style
- * @name getFontStyle
- * @methodOf Kinetic.TextPath.prototype
- */
+ /**
+ * get font style
+ * @name getFontStyle
+ * @methodOf Kinetic.TextPath.prototype
+ */
-/**
- * get text fill color
- * @name getTextFill
- * @methodOf Kinetic.TextPath.prototype
- */
+ /**
+ * get text fill color
+ * @name getTextFill
+ * @methodOf Kinetic.TextPath.prototype
+ */
-/**
- * get text stroke color
- * @name getTextStroke
- * @methodOf Kinetic.TextPath.prototype
- */
+ /**
+ * get text stroke color
+ * @name getTextStroke
+ * @methodOf Kinetic.TextPath.prototype
+ */
-/**
- * get text stroke width
- * @name getTextStrokeWidth
- * @methodOf Kinetic.TextPath.prototype
- */
+ /**
+ * get text stroke width
+ * @name getTextStrokeWidth
+ * @methodOf Kinetic.TextPath.prototype
+ */
-/**
- * get text
- * @name getText
- * @methodOf Kinetic.TextPath.prototype
- */
\ No newline at end of file
+ /**
+ * get text
+ * @name getText
+ * @methodOf Kinetic.TextPath.prototype
+ */
+})();
diff --git a/src/util/Canvas.js b/src/util/Canvas.js
deleted file mode 100644
index 66efc525..00000000
--- a/src/util/Canvas.js
+++ /dev/null
@@ -1,239 +0,0 @@
-/**
- * Canvas wrapper constructor
- * @constructor
- * @param {Number} width
- * @param {Number} height
- */
-Kinetic.Canvas = function(width, height, isHit) {
- this.element = document.createElement('canvas');
- this.context = this.element.getContext('2d');
-
- // set dimensions
- this.element.width = width || 0;
- this.element.height = height || 0;
-
- this.context.renderer = isHit ? new Kinetic.HitRenderer(this.context) : new Kinetic.SceneRenderer(this.context);
-};
-
-Kinetic.Canvas.prototype = {
- /**
- * clear canvas
- * @name clear
- * @methodOf Kinetic.Canvas.prototype
- */
- clear: function() {
- var context = this.getContext();
- var el = this.getElement();
- context.clearRect(0, 0, el.width, el.height);
- },
- /**
- * get element
- * @name getElement
- * @methodOf Kinetic.Canvas.prototype
- */
- getElement: function() {
- return this.element;
- },
- /**
- * get context
- * @name getContext
- * @methodOf Kinetic.Canvas.prototype
- */
- getContext: function() {
- return this.context;
- },
- /**
- * set width
- * @name setWidth
- * @methodOf Kinetic.Canvas.prototype
- */
- setWidth: function(width) {
- this.element.width = width;
- },
- /**
- * set height
- * @name setHeight
- * @methodOf Kinetic.Canvas.prototype
- */
- setHeight: function(height) {
- this.element.height = height;
- },
- /**
- * get width
- * @name getWidth
- * @methodOf Kinetic.Canvas.prototype
- */
- getWidth: function() {
- return this.element.width;
- },
- /**
- * get height
- * @name getHeight
- * @methodOf Kinetic.Canvas.prototype
- */
- getHeight: function() {
- return this.element.height;
- },
- /**
- * set size
- * @name setSize
- * @methodOf Kinetic.Canvas.prototype
- */
- setSize: function(width, height) {
- this.setWidth(width);
- this.setHeight(height);
- },
- /**
- * toDataURL
- */
- toDataURL: function(mimeType, quality) {
- try {
- // If this call fails (due to browser bug, like in Firefox 3.6),
- // then revert to previous no-parameter image/png behavior
- return this.element.toDataURL(mimeType, quality);
- }
- catch(e) {
- try {
- return this.element.toDataURL();
- }
- catch(e) {
- Kinetic.Global.warn('Unable to get data URL. ' + e.message)
- return '';
- }
- }
- }
-};
-
-Kinetic.SceneRenderer = function(context) {
- this.context = context;
-};
-
-Kinetic.SceneRenderer.prototype = {
- _fill: function(shape, skipShadow) {
- var context = this.context, fill = shape.getFill(), fillType = shape._getFillType(fill), shadow = shape.getShadow();
- if(fill) {
- context.save();
-
- if(!skipShadow && shadow) {
- this._applyShadow(shape);
- }
- var s = fill.start;
- var e = fill.end;
-
- // color fill
- switch(fillType) {
- case 'COLOR':
- context.fillStyle = fill;
- context.fill(context);
- break;
- case 'PATTERN':
- var repeat = !fill.repeat ? 'repeat' : fill.repeat;
- if(fill.scale) {
- context.scale(fill.scale.x, fill.scale.y);
- }
- if(fill.offset) {
- context.translate(fill.offset.x, fill.offset.y);
- } file:///C:/Users/Eric/Documents/Eric/workspaces/KineticJS/dist/kinetic-current.js
-
- context.fillStyle = context.createPattern(fill.image, repeat);
- context.fill(context);
- break;
- case 'LINEAR_GRADIENT':
- var grd = context.createLinearGradient(s.x, s.y, e.x, e.y);
- var colorStops = fill.colorStops;
-
- // build color stops
- for(var n = 0; n < colorStops.length; n += 2) {
- grd.addColorStop(colorStops[n], colorStops[n + 1]);
- }
- context.fillStyle = grd;
- context.fill(context);
-
- break;
- case 'RADIAL_GRADIENT':
- var grd = context.createRadialGradient(s.x, s.y, s.radius, e.x, e.y, e.radius);
- var colorStops = fill.colorStops;
-
- // build color stops
- for(var n = 0; n < colorStops.length; n += 2) {
- grd.addColorStop(colorStops[n], colorStops[n + 1]);
- }
- context.fillStyle = grd;
- context.fill(context);
- break;
- default:
- context.fillStyle = 'black';
- context.fill(context);
- break;
- }
-
- context.restore();
-
- if(!skipShadow && shadow && shadow.opacity) {
- this._fill(shape, true);
- }
- }
- },
- _stroke: function(shape, skipShadow) {
- var context = this.context, stroke = shape.getStroke(), strokeWidth = shape.getStrokeWidth(), shadow = shape.getShadow();
- if(stroke || strokeWidth) {
- context.save();
- if(!skipShadow && shadow) {
- this._applyShadow(shape);
- }
- context.lineWidth = strokeWidth || 2;
- context.strokeStyle = stroke || 'black';
- context.stroke(context);
- context.restore();
-
- if(!skipShadow && shadow && shadow.opacity) {
- this._stroke(shape, true);
- }
- }
- },
- _applyShadow: function(shape) {
- var context = this.context, shadow = shape.getShadow();
- if(shadow) {
- var aa = shape.getAbsoluteOpacity();
- // defaults
- var color = shadow.color || 'black';
- var blur = shadow.blur || 5;
- var offset = shadow.offset || {
- x: 0,
- y: 0
- };
-
- if(shadow.opacity) {
- context.globalAlpha = shadow.opacity * aa;
- }
- context.shadowColor = color;
- context.shadowBlur = blur;
- context.shadowOffsetX = offset.x;
- context.shadowOffsetY = offset.y;
- }
- }
-};
-
-Kinetic.HitRenderer = function(context) {
- this.context = context;
-};
-
-Kinetic.HitRenderer.prototype = {
- _fill: function(shape) {
- var context = this.context;
- context.save();
- context.fillStyle = '#' + shape.colorKey;
- context.fill(context);
- context.restore();
- },
- _stroke: function(shape) {
- var context = this.context, stroke = shape.getStroke(), strokeWidth = shape.getStrokeWidth();
- if(stroke || strokeWidth) {
- context.save();
- context.lineWidth = strokeWidth || 2;
- context.strokeStyle = '#' + shape.colorKey;
- context.stroke(context);
- context.restore();
- }
- }
-};
diff --git a/src/util/Collection.js b/src/util/Collection.js
index c104dbb0..660516e6 100644
--- a/src/util/Collection.js
+++ b/src/util/Collection.js
@@ -1,43 +1,44 @@
-/**
- * Collection constructor. Collection extends
- * Array. This class is used in conjunction with get()
- * @constructor
- */
-Kinetic.Collection = function() {
- var args = [].slice.call( arguments ),
- length = args.length, i = 0;
+(function() {
+ /**
+ * Collection constructor. Collection extends
+ * Array. This class is used in conjunction with get()
+ * @constructor
+ */
+ Kinetic.Collection = function() {
+ var args = [].slice.call(arguments), length = args.length, i = 0;
- this.length = length;
- for (; i < length; i++ ) {
- this[ i ] = args[ i ];
+ this.length = length;
+ for(; i < length; i++) {
+ this[i] = args[i];
+ }
+ return this;
}
- return this;
-}
-Kinetic.Collection.prototype = new Array();
-/**
- * apply a method to all nodes in the array
- * @name apply
- * @methodOf Kinetic.Collection.prototype
- * @param {String} method
- * @param val
- */
- Kinetic.Collection.prototype.apply = function(method) {
- args = [].slice.call(arguments);
- args.shift();
- for (var n=0; n this.getDuration()) {
- if(this.looping) {
- this.rewind(t - this._duration);
+(function() {
+ /*
+ * The Tween class was ported from an Adobe Flash Tween library
+ * to JavaScript by Xaric. In the context of KineticJS, a Tween is
+ * an animation of a single Node property. A Transition is a set of
+ * multiple tweens
+ */
+ Kinetic.Tween = function(obj, propFunc, func, begin, finish, duration) {
+ this._listeners = [];
+ this.addListener(this);
+ this.obj = obj;
+ this.propFunc = propFunc;
+ this.begin = begin;
+ this._pos = begin;
+ this.setDuration(duration);
+ this.isPlaying = false;
+ this._change = 0;
+ this.prevTime = 0;
+ this.prevPos = 0;
+ this.looping = false;
+ this._time = 0;
+ this._position = 0;
+ this._startTime = 0;
+ this._finish = 0;
+ this.name = '';
+ this.func = func;
+ this.setFinish(finish);
+ };
+ /*
+ * Tween methods
+ */
+ Kinetic.Tween.prototype = {
+ setTime: function(t) {
+ this.prevTime = this._time;
+ if(t > this.getDuration()) {
+ if(this.looping) {
+ this.rewind(t - this._duration);
+ this.update();
+ this.broadcastMessage('onLooped', {
+ target: this,
+ type: 'onLooped'
+ });
+ }
+ else {
+ this._time = this._duration;
+ this.update();
+ this.stop();
+ this.broadcastMessage('onFinished', {
+ target: this,
+ type: 'onFinished'
+ });
+ }
+ }
+ else if(t < 0) {
+ this.rewind();
this.update();
- this.broadcastMessage('onLooped', {
- target: this,
- type: 'onLooped'
- });
}
else {
- this._time = this._duration;
+ this._time = t;
this.update();
- this.stop();
- this.broadcastMessage('onFinished', {
- target: this,
- type: 'onFinished'
- });
}
- }
- else if(t < 0) {
+ },
+ getTime: function() {
+ return this._time;
+ },
+ setDuration: function(d) {
+ this._duration = (d === null || d <= 0) ? 100000 : d;
+ },
+ getDuration: function() {
+ return this._duration;
+ },
+ setPosition: function(p) {
+ this.prevPos = this._pos;
+ this.propFunc(p);
+ this._pos = p;
+ this.broadcastMessage('onChanged', {
+ target: this,
+ type: 'onChanged'
+ });
+ },
+ getPosition: function(t) {
+ if(t === undefined) {
+ t = this._time;
+ }
+ return this.func(t, this.begin, this._change, this._duration);
+ },
+ setFinish: function(f) {
+ this._change = f - this.begin;
+ },
+ getFinish: function() {
+ return this.begin + this._change;
+ },
+ start: function() {
this.rewind();
+ this.startEnterFrame();
+ this.broadcastMessage('onStarted', {
+ target: this,
+ type: 'onStarted'
+ });
+ },
+ rewind: function(t) {
+ this.stop();
+ this._time = (t === undefined) ? 0 : t;
+ this.fixTime();
this.update();
- }
- else {
- this._time = t;
+ },
+ fforward: function() {
+ this._time = this._duration;
+ this.fixTime();
this.update();
- }
- },
- getTime: function() {
- return this._time;
- },
- setDuration: function(d) {
- this._duration = (d === null || d <= 0) ? 100000 : d;
- },
- getDuration: function() {
- return this._duration;
- },
- setPosition: function(p) {
- this.prevPos = this._pos;
- this.propFunc(p);
- this._pos = p;
- this.broadcastMessage('onChanged', {
- target: this,
- type: 'onChanged'
- });
- },
- getPosition: function(t) {
- if(t === undefined) {
- t = this._time;
- }
- return this.func(t, this.begin, this._change, this._duration);
- },
- setFinish: function(f) {
- this._change = f - this.begin;
- },
- getFinish: function() {
- return this.begin + this._change;
- },
- start: function() {
- this.rewind();
- this.startEnterFrame();
- this.broadcastMessage('onStarted', {
- target: this,
- type: 'onStarted'
- });
- },
- rewind: function(t) {
- this.stop();
- this._time = (t === undefined) ? 0 : t;
- this.fixTime();
- this.update();
- },
- fforward: function() {
- this._time = this._duration;
- this.fixTime();
- this.update();
- },
- update: function() {
- this.setPosition(this.getPosition(this._time));
- },
- startEnterFrame: function() {
- this.stopEnterFrame();
- this.isPlaying = true;
- this.onEnterFrame();
- },
- onEnterFrame: function() {
- if(this.isPlaying) {
- this.nextFrame();
- }
- },
- nextFrame: function() {
- this.setTime((this.getTimer() - this._startTime) / 1000);
- },
- stop: function() {
- this.stopEnterFrame();
- this.broadcastMessage('onStopped', {
- target: this,
- type: 'onStopped'
- });
- },
- stopEnterFrame: function() {
- this.isPlaying = false;
- },
- continueTo: function(finish, duration) {
- this.begin = this._pos;
- this.setFinish(finish);
- if(this._duration !== undefined) {
- this.setDuration(duration);
- }
- this.start();
- },
- resume: function() {
- this.fixTime();
- this.startEnterFrame();
- this.broadcastMessage('onResumed', {
- target: this,
- type: 'onResumed'
- });
- },
- yoyo: function() {
- this.continueTo(this.begin, this._time);
- },
- addListener: function(o) {
- this.removeListener(o);
- return this._listeners.push(o);
- },
- removeListener: function(o) {
- var a = this._listeners;
- var i = a.length;
- while(i--) {
- if(a[i] == o) {
- a.splice(i, 1);
- return true;
+ },
+ update: function() {
+ this.setPosition(this.getPosition(this._time));
+ },
+ startEnterFrame: function() {
+ this.stopEnterFrame();
+ this.isPlaying = true;
+ this.onEnterFrame();
+ },
+ onEnterFrame: function() {
+ if(this.isPlaying) {
+ this.nextFrame();
}
- }
- return false;
- },
- broadcastMessage: function() {
- var arr = [];
- for(var i = 0; i < arguments.length; i++) {
- arr.push(arguments[i]);
- }
- var e = arr.shift();
- var a = this._listeners;
- var l = a.length;
- for(var i = 0; i < l; i++) {
- if(a[i][e]) {
- a[i][e].apply(a[i], arr);
+ },
+ nextFrame: function() {
+ this.setTime((this.getTimer() - this._startTime) / 1000);
+ },
+ stop: function() {
+ this.stopEnterFrame();
+ this.broadcastMessage('onStopped', {
+ target: this,
+ type: 'onStopped'
+ });
+ },
+ stopEnterFrame: function() {
+ this.isPlaying = false;
+ },
+ continueTo: function(finish, duration) {
+ this.begin = this._pos;
+ this.setFinish(finish);
+ if(this._duration !== undefined) {
+ this.setDuration(duration);
}
+ this.start();
+ },
+ resume: function() {
+ this.fixTime();
+ this.startEnterFrame();
+ this.broadcastMessage('onResumed', {
+ target: this,
+ type: 'onResumed'
+ });
+ },
+ yoyo: function() {
+ this.continueTo(this.begin, this._time);
+ },
+ addListener: function(o) {
+ this.removeListener(o);
+ return this._listeners.push(o);
+ },
+ removeListener: function(o) {
+ var a = this._listeners;
+ var i = a.length;
+ while(i--) {
+ if(a[i] == o) {
+ a.splice(i, 1);
+ return true;
+ }
+ }
+ return false;
+ },
+ broadcastMessage: function() {
+ var arr = [];
+ for(var i = 0; i < arguments.length; i++) {
+ arr.push(arguments[i]);
+ }
+ var e = arr.shift();
+ var a = this._listeners;
+ var l = a.length;
+ for(var i = 0; i < l; i++) {
+ if(a[i][e]) {
+ a[i][e].apply(a[i], arr);
+ }
+ }
+ },
+ fixTime: function() {
+ this._startTime = this.getTimer() - this._time * 1000;
+ },
+ getTimer: function() {
+ return new Date().getTime() - this._time;
}
- },
- fixTime: function() {
- this._startTime = this.getTimer() - this._time * 1000;
- },
- getTimer: function() {
- return new Date().getTime() - this._time;
- }
-};
+ };
-Kinetic.Tweens = {
- 'back-ease-in': function(t, b, c, d, a, p) {
- var s = 1.70158;
- return c * (t /= d) * t * ((s + 1) * t - s) + b;
- },
- 'back-ease-out': function(t, b, c, d, a, p) {
- var s = 1.70158;
- return c * (( t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
- },
- 'back-ease-in-out': function(t, b, c, d, a, p) {
- var s = 1.70158;
- if((t /= d / 2) < 1) {
- return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
+ Kinetic.Tweens = {
+ 'back-ease-in': function(t, b, c, d, a, p) {
+ var s = 1.70158;
+ return c * (t /= d) * t * ((s + 1) * t - s) + b;
+ },
+ 'back-ease-out': function(t, b, c, d, a, p) {
+ var s = 1.70158;
+ return c * (( t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
+ },
+ 'back-ease-in-out': function(t, b, c, d, a, p) {
+ var s = 1.70158;
+ if((t /= d / 2) < 1) {
+ return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
+ }
+ return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
+ },
+ 'elastic-ease-in': function(t, b, c, d, a, p) {
+ // added s = 0
+ var s = 0;
+ if(t === 0) {
+ return b;
+ }
+ if((t /= d) == 1) {
+ return b + c;
+ }
+ if(!p) {
+ p = d * 0.3;
+ }
+ if(!a || a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ }
+ else {
+ s = p / (2 * Math.PI) * Math.asin(c / a);
+ }
+ return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
+ },
+ 'elastic-ease-out': function(t, b, c, d, a, p) {
+ // added s = 0
+ var s = 0;
+ if(t === 0) {
+ return b;
+ }
+ if((t /= d) == 1) {
+ return b + c;
+ }
+ if(!p) {
+ p = d * 0.3;
+ }
+ if(!a || a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ }
+ else {
+ s = p / (2 * Math.PI) * Math.asin(c / a);
+ }
+ return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
+ },
+ 'elastic-ease-in-out': function(t, b, c, d, a, p) {
+ // added s = 0
+ var s = 0;
+ if(t === 0) {
+ return b;
+ }
+ if((t /= d / 2) == 2) {
+ return b + c;
+ }
+ if(!p) {
+ p = d * (0.3 * 1.5);
+ }
+ if(!a || a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ }
+ else {
+ s = p / (2 * Math.PI) * Math.asin(c / a);
+ }
+ if(t < 1) {
+ return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
+ }
+ return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b;
+ },
+ 'bounce-ease-out': function(t, b, c, d) {
+ if((t /= d) < (1 / 2.75)) {
+ return c * (7.5625 * t * t) + b;
+ }
+ else if(t < (2 / 2.75)) {
+ return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
+ }
+ else if(t < (2.5 / 2.75)) {
+ return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
+ }
+ else {
+ return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
+ }
+ },
+ 'bounce-ease-in': function(t, b, c, d) {
+ return c - Kinetic.Tweens['bounce-ease-out'](d - t, 0, c, d) + b;
+ },
+ 'bounce-ease-in-out': function(t, b, c, d) {
+ if(t < d / 2) {
+ return Kinetic.Tweens['bounce-ease-in'](t * 2, 0, c, d) * 0.5 + b;
+ }
+ else {
+ return Kinetic.Tweens['bounce-ease-out'](t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
+ }
+ },
+ // duplicate
+ /*
+ strongEaseInOut: function(t, b, c, d) {
+ return c * (t /= d) * t * t * t * t + b;
+ },
+ */
+ 'ease-in': function(t, b, c, d) {
+ return c * (t /= d) * t + b;
+ },
+ 'ease-out': function(t, b, c, d) {
+ return -c * (t /= d) * (t - 2) + b;
+ },
+ 'ease-in-out': function(t, b, c, d) {
+ if((t /= d / 2) < 1) {
+ return c / 2 * t * t + b;
+ }
+ return -c / 2 * ((--t) * (t - 2) - 1) + b;
+ },
+ 'strong-ease-in': function(t, b, c, d) {
+ return c * (t /= d) * t * t * t * t + b;
+ },
+ 'strong-ease-out': function(t, b, c, d) {
+ return c * (( t = t / d - 1) * t * t * t * t + 1) + b;
+ },
+ 'strong-ease-in-out': function(t, b, c, d) {
+ if((t /= d / 2) < 1) {
+ return c / 2 * t * t * t * t * t + b;
+ }
+ return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
+ },
+ 'linear': function(t, b, c, d) {
+ return c * t / d + b;
}
- return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
- },
- 'elastic-ease-in': function(t, b, c, d, a, p) {
- // added s = 0
- var s = 0;
- if(t === 0) {
- return b;
- }
- if((t /= d) == 1) {
- return b + c;
- }
- if(!p) {
- p = d * 0.3;
- }
- if(!a || a < Math.abs(c)) {
- a = c;
- s = p / 4;
- }
- else {
- s = p / (2 * Math.PI) * Math.asin(c / a);
- }
- return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
- },
- 'elastic-ease-out': function(t, b, c, d, a, p) {
- // added s = 0
- var s = 0;
- if(t === 0) {
- return b;
- }
- if((t /= d) == 1) {
- return b + c;
- }
- if(!p) {
- p = d * 0.3;
- }
- if(!a || a < Math.abs(c)) {
- a = c;
- s = p / 4;
- }
- else {
- s = p / (2 * Math.PI) * Math.asin(c / a);
- }
- return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
- },
- 'elastic-ease-in-out': function(t, b, c, d, a, p) {
- // added s = 0
- var s = 0;
- if(t === 0) {
- return b;
- }
- if((t /= d / 2) == 2) {
- return b + c;
- }
- if(!p) {
- p = d * (0.3 * 1.5);
- }
- if(!a || a < Math.abs(c)) {
- a = c;
- s = p / 4;
- }
- else {
- s = p / (2 * Math.PI) * Math.asin(c / a);
- }
- if(t < 1) {
- return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
- }
- return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b;
- },
- 'bounce-ease-out': function(t, b, c, d) {
- if((t /= d) < (1 / 2.75)) {
- return c * (7.5625 * t * t) + b;
- }
- else if(t < (2 / 2.75)) {
- return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
- }
- else if(t < (2.5 / 2.75)) {
- return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
- }
- else {
- return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
- }
- },
- 'bounce-ease-in': function(t, b, c, d) {
- return c - Kinetic.Tweens['bounce-ease-out'](d - t, 0, c, d) + b;
- },
- 'bounce-ease-in-out': function(t, b, c, d) {
- if(t < d / 2) {
- return Kinetic.Tweens['bounce-ease-in'](t * 2, 0, c, d) * 0.5 + b;
- }
- else {
- return Kinetic.Tweens['bounce-ease-out'](t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
- }
- },
- // duplicate
- /*
- strongEaseInOut: function(t, b, c, d) {
- return c * (t /= d) * t * t * t * t + b;
- },
- */
- 'ease-in': function(t, b, c, d) {
- return c * (t /= d) * t + b;
- },
- 'ease-out': function(t, b, c, d) {
- return -c * (t /= d) * (t - 2) + b;
- },
- 'ease-in-out': function(t, b, c, d) {
- if((t /= d / 2) < 1) {
- return c / 2 * t * t + b;
- }
- return -c / 2 * ((--t) * (t - 2) - 1) + b;
- },
- 'strong-ease-in': function(t, b, c, d) {
- return c * (t /= d) * t * t * t * t + b;
- },
- 'strong-ease-out': function(t, b, c, d) {
- return c * (( t = t / d - 1) * t * t * t * t + 1) + b;
- },
- 'strong-ease-in-out': function(t, b, c, d) {
- if((t /= d / 2) < 1) {
- return c / 2 * t * t * t * t * t + b;
- }
- return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
- },
- 'linear': function(t, b, c, d) {
- return c * t / d + b;
- }
-};
+ };
+})();
diff --git a/src/util/Type.js b/src/util/Type.js
index f3e587ae..6449a3e1 100644
--- a/src/util/Type.js
+++ b/src/util/Type.js
@@ -1,300 +1,302 @@
-/*
- * utilities that handle data type detection, conversion, and manipulation
- */
-Kinetic.Type = {
+(function() {
/*
- * cherry-picked utilities from underscore.js
+ * utilities that handle data type detection, conversion, and manipulation
*/
- _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 = [];
- for(var key in obj) {
- if(this._isFunction(obj[key]))
- names.push(key);
- }
- return names.length > 0;
- },
- /*
- * 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;
- }
+ Kinetic.Type = {
+ /*
+ * 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 = [];
+ for(var key in obj) {
+ if(this._isFunction(obj[key]))
+ names.push(key);
}
- // if arg is an array of two or more elements
- else if(arg.length >= 2) {
+ return names.length > 0;
+ },
+ /*
+ * 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[0],
- y: arg[1]
+ x: arg,
+ y: arg
};
}
- }
- // if arg is an object return the object
- else if(this._isObject(arg)) {
- return arg;
- }
-
- // default
- return {
- x: 0,
- y: 0
- };
- },
- /*
- * 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) {
+ 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[2],
- height: val[3]
+ x: val,
+ y: val
};
}
- /*
- * if arg is an array of one element which is an
- * array of two elements
- */
- else if(val.length >= 2) {
+ // if arg is an array of one element which is an array
+ else if(this._isArray(val)) {
return {
- width: val[0],
- height: val[1]
+ 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 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 array of four elements
- else if(arg.length >= 4) {
+ // if arg is an object return the object
+ else if(this._isObject(arg)) {
+ return arg;
+ }
+
+ // default
+ return {
+ x: 0,
+ y: 0
+ };
+ },
+ /*
+ * 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[2],
- height: arg[3]
+ width: arg,
+ height: arg
};
}
- // if arg is an array of two elements
- else if(arg.length >= 2) {
- return {
- width: arg[0],
- height: arg[1]
- };
+ 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 {
- width: 0,
- height: 0
- };
- },
- /*
- * arg will be an array of numbers or
- * an array of point objects
- */
- _getPoints: function(arg) {
- if(arg === undefined) {
- return [];
- }
-
- // 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]
- });
+ // if arg is an object return the object
+ else if(this._isObject(arg)) {
+ return arg;
}
- return arr;
- }
- },
- /*
- * arg can be an image object or image data
- */
- _getImage: function(arg, callback) {
- // 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)) {
- var imageObj = new Image();
- /** @ignore */
- imageObj.onload = function() {
- callback(imageObj);
+ // default
+ return {
+ width: 0,
+ height: 0
+ };
+ },
+ /*
+ * arg will be an array of numbers or
+ * an array of point objects
+ */
+ _getPoints: function(arg) {
+ if(arg === undefined) {
+ return [];
}
- imageObj.src = arg;
- }
- //if arg is an object that contains the data property, it's an image object
- else if(arg.data) {
- var canvas = document.createElement('canvas');
- canvas.width = arg.width;
- canvas.height = arg.height;
- var context = canvas.getContext('2d');
- context.putImageData(arg, 0, 0);
- var dataUrl = canvas.toDataURL();
- var imageObj = new Image();
- /** @ignore */
- imageObj.onload = function() {
- callback(imageObj);
+ // an array of objects
+ if(this._isObject(arg[0])) {
+ return arg;
}
- 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
- };
- },
- _getRandomColorKey: function() {
- var r = Math.round(Math.random() * 255);
- var g = Math.round(Math.random() * 255);
- var b = Math.round(Math.random() * 255);
- return this._rgbToHex(r, g, b);
- },
- // 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]);
+ // 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) {
+ // 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)) {
+ var imageObj = new Image();
+ /** @ignore */
+ 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) {
+ var canvas = document.createElement('canvas');
+ canvas.width = arg.width;
+ canvas.height = arg.height;
+ var context = canvas.getContext('2d');
+ context.putImageData(arg, 0, 0);
+ var dataUrl = canvas.toDataURL();
+ var imageObj = new Image();
+ /** @ignore */
+ imageObj.onload = function() {
+ callback(imageObj);
+ }
+ imageObj.src = dataUrl;
}
else {
- retObj[key] = o1[key];
+ 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
+ };
+ },
+ _getRandomColorKey: function() {
+ var r = Math.round(Math.random() * 255);
+ var g = Math.round(Math.random() * 255);
+ var b = Math.round(Math.random() * 255);
+ return this._rgbToHex(r, g, b);
+ },
+ // 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 * Math.PI / 180;
+ },
+ _radToDeg: function(rad) {
+ return rad * 180 / Math.PI;
}
- 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 * Math.PI / 180;
- },
- _radToDeg: function(rad) {
- return rad * 180 / Math.PI;
- }
-};
+ };
+})();
diff --git a/tests/html/functionalTests.html b/tests/html/functionalTests.html
index 2738ff69..5f3d4e50 100644
--- a/tests/html/functionalTests.html
+++ b/tests/html/functionalTests.html
@@ -6,7 +6,7 @@
-
+
diff --git a/tests/html/manualTests.html b/tests/html/manualTests.html
index 79a940dc..a4392592 100644
--- a/tests/html/manualTests.html
+++ b/tests/html/manualTests.html
@@ -2,7 +2,7 @@
-
+
diff --git a/tests/html/performanceTests.html b/tests/html/performanceTests.html
index ab7a3f02..c14a829a 100644
--- a/tests/html/performanceTests.html
+++ b/tests/html/performanceTests.html
@@ -2,7 +2,7 @@
-
+
diff --git a/tests/html/special/coreCustomBuild.html b/tests/html/special/coreCustomBuild.html
index 9e1de26f..be7e7590 100644
--- a/tests/html/special/coreCustomBuild.html
+++ b/tests/html/special/coreCustomBuild.html
@@ -7,19 +7,19 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
-
+
-
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
+
+
-
-
+
+
+
-
+
-
-
+
+
-
+
-
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
+
diff --git a/tests/js/unit/globalTests.js b/tests/js/unit/globalTests.js
new file mode 100644
index 00000000..98ce03db
--- /dev/null
+++ b/tests/js/unit/globalTests.js
@@ -0,0 +1,5 @@
+Test.Modules.GLOBAL = {
+ 'test Kinetic version number': function(containerId) {
+ test(Kinetic.version === 'current', 'Kinetic.version should equal current');
+ }
+};