Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merged ac80261

removed lib directory and used src instead.  Moved license.js outside of the source directory.  Created a geometries directory inside src.  Also had problems getting the Ruby rake file to build a minified file with uglifiy, so after a bit of searching I found that many people suggested to include the json-pure gem, so I added it to the Gemfile
  • Loading branch information...
commit 3c140ca762f382c57b3b3f6118c0030a517eb3d4 2 parents 5679cbf + ac80261
@ericdrowell authored
View
5 Gemfile
@@ -0,0 +1,5 @@
+source :rubygems
+
+gem 'json-pure'
+gem 'rake'
+gem 'uglifier'
View
17 Gemfile.lock
@@ -0,0 +1,17 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ execjs (1.3.0)
+ multi_json (~> 1.0)
+ multi_json (1.1.0)
+ rake (0.9.2.2)
+ uglifier (1.2.3)
+ execjs (>= 0.3.0)
+ multi_json (>= 1.0.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ rake
+ uglifier
View
8 README.md
@@ -8,3 +8,11 @@ You can draw your own shapes or images using the existing canvas API, add event
# Tutorials
Check out the official [KineticJS Tutorials](http://www.html5canvastutorials.com/kineticjs/html5-canvas-events-tutorials-introduction-with-kineticjs/) hosted on [HTML5 Canvas Tutorials](http://www.html5canvastutorials.com/).
+
+# Building the library
+To build the library, you need to have Ruby and Rubygems installed. After that, install the dependencies by running `bundle install`.
+
+To build a development version of the library, run `rake build:dev`. To build a minify version of the library, run `rake build:prod`.
+
+# Adding a new file in the src directory
+If you add a file in the src directory, add into the array in the Rakefile.
View
40 Rakefile
@@ -0,0 +1,40 @@
+require 'json/pure'
+
+# 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 = [
+ "license.js", "src/GlobalObject.js", "src/Node.js", "src/Container.js", "src/Stage.js",
+ "src/Layer.js", "src/Group.js", "src/geometries/Shape.js", "src/geometries/Rect.js", "src/geometries/Circle.js", "src/geometries/Image.js",
+ "src/geometries/Polygon.js", "src/geometries/RegularPolygon.js", "src/geometries/Star.js", "src/geometries/Text.js"
+]
+
+def concatenate
+ content = ""
+ FILES.each do |file|
+ content << IO.read(File.expand_path(file)) << "\n"
+ end
+
+ return content
+end
+
+namespace :build do
+ desc "Concatenate all the js files into /dist/kinetic.js."
+ task :dev do
+ puts ":: Building the file /dist/kinetic.js..."
+ File.open("dist/kinetic.js", "w") do |file|
+ file.puts concatenate()
+ end
+ puts " -> Done!"
+ end
+
+ desc "Concatenate all the js files in into /dist/kinetic.min.js and minify it."
+ task :prod do
+ puts ":: Building the file /dist/kinetic.min.js..."
+ require 'json/pure'
+ require 'uglifier'
+ File.open("dist/kinetic.min.js", "w") do |file|
+ file.puts Uglifier.compile(concatenate())
+ end
+ puts ":: Minifying the file /dist/kinetic.min.js..."
+ puts " -> Done!"
+ end
+end
View
2,257 dist/kinetic.js
@@ -0,0 +1,2257 @@
+/**
+ * KineticJS JavaScript Library v3.8.3
+ * http://www.kineticjs.com/
+ * Copyright 2012, Eric Rowell
+ * Licensed under the MIT or GPL Version 2 licenses.
+ * Date: Mar 03 2012
+ *
+ * Copyright (C) 2011 - 2012 by Eric Rowell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+///////////////////////////////////////////////////////////////////////
+// Global Object
+///////////////////////////////////////////////////////////////////////
+/**
+ * Kinetic Namespace
+ * @namespace
+ */
+var Kinetic = {};
+/**
+ * Kinetic Global Object
+ * @property {Object} GlobalObjet
+ */
+Kinetic.GlobalObject = {
+ stages: [],
+ idCounter: 0,
+ isAnimating: false,
+ frame: {
+ time: 0,
+ timeDiff: 0,
+ lastTime: 0
+ },
+ drag: {
+ moving: false,
+ node: undefined,
+ offset: {
+ x: 0,
+ y: 0
+ }
+ },
+ extend: function(obj1, obj2) {
+ for(var key in obj2.prototype) {
+ if(obj2.prototype.hasOwnProperty(key)) {
+ obj1.prototype[key] = obj2.prototype[key];
+ }
+ }
+ },
+ _isaCanvasAnimating: function() {
+ for(var n = 0; n < this.stages.length; n++) {
+ if(this.stages[n].isAnimating) {
+ return true;
+ }
+ }
+ return false;
+ },
+ _runFrames: function() {
+ for(var n = 0; n < this.stages.length; n++) {
+ if(this.stages[n].isAnimating) {
+ this.stages[n].onFrameFunc(this.frame);
+ }
+ }
+ },
+ _updateFrameObject: function() {
+ var date = new Date();
+ var time = date.getTime();
+ if(this.frame.lastTime === 0) {
+ this.frame.lastTime = time;
+ } else {
+ this.frame.timeDiff = time - this.frame.lastTime;
+ this.frame.lastTime = time;
+ this.frame.time += this.frame.timeDiff;
+ }
+ },
+ _animationLoop: function() {
+ if(this.isAnimating) {
+ this._updateFrameObject();
+ this._runFrames();
+ var that = this;
+ requestAnimFrame(function() {
+ that._animationLoop();
+ });
+ }
+ },
+ _handleAnimation: function() {
+ var that = this;
+ if(!this.isAnimating && this._isaCanvasAnimating()) {
+ this.isAnimating = true;
+ that._animationLoop();
+ } else if(this.isAnimating && !this._isaCanvasAnimating()) {
+ this.isAnimating = false;
+ }
+ }
+};
+
+window.requestAnimFrame = (function(callback) {
+ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
+ function(callback) {
+ window.setTimeout(callback, 1000 / 60);
+ };
+})();
+///////////////////////////////////////////////////////////////////////
+// Node
+///////////////////////////////////////////////////////////////////////
+/**
+ * Node constructor.&nbsp; Nodes are entities that can move around
+ * and have events bound to them. They are the building blocks of a KineticJS
+ * application
+ * @constructor
+ * @param {Object} config
+ */
+Kinetic.Node = function(config) {
+ this.visible = true;
+ this.isListening = true;
+ this.name = undefined;
+ this.alpha = 1;
+ this.x = 0;
+ this.y = 0;
+ this.scale = {
+ x: 1,
+ y: 1
+ };
+ this.rotation = 0;
+ this.centerOffset = {
+ x: 0,
+ y: 0
+ };
+ this.eventListeners = {};
+ this.drag = {
+ x: false,
+ y: false
+ };
+
+ // set properties from config
+ if(config) {
+ for(var key in config) {
+ // handle special keys
+ switch (key) {
+ case "draggable":
+ this.draggable(config[key]);
+ break;
+ case "draggableX":
+ this.draggableX(config[key]);
+ break;
+ case "draggableY":
+ this.draggableY(config[key]);
+ break;
+ case "listen":
+ this.listen(config[key]);
+ break;
+ case "rotationDeg":
+ this.rotation = config[key] * Math.PI / 180;
+ break;
+ default:
+ this[key] = config[key];
+ break;
+ }
+ }
+ }
+
+ // overrides
+ if(this.centerOffset.x === undefined) {
+ this.centerOffset.x = 0;
+ }
+ if(this.centerOffset.y === undefined) {
+ this.centerOffset.y = 0;
+ }
+};
+/*
+ * Node methods
+ */
+Kinetic.Node.prototype = {
+ /**
+ * bind events to the node. KineticJS supports mouseover, mousemove,
+ * mouseout, mousedown, mouseup, click, dblclick, touchstart, touchmove,
+ * touchend, dbltap, dragstart, dragmove, and dragend. Pass in a string
+ * of event types delimmited by a space to bind multiple events at once
+ * such as "mousedown mouseup mousemove". include a namespace to bind an
+ * event by name such as "click.foobar".
+ * @param {String} typesStr
+ * @param {function} handler
+ */
+ on: function(typesStr, handler) {
+ var types = typesStr.split(" ");
+ /*
+ * loop through types and attach event listeners to
+ * each one. eg. "click mouseover.namespace mouseout"
+ * will create three event bindings
+ */
+ for(var n = 0; n < types.length; n++) {
+ var type = types[n];
+ var event = (type.indexOf('touch') === -1) ? 'on' + type : type;
+ var parts = event.split(".");
+ var baseEvent = parts[0];
+ var name = parts.length > 1 ? parts[1] : "";
+
+ if(!this.eventListeners[baseEvent]) {
+ this.eventListeners[baseEvent] = [];
+ }
+
+ this.eventListeners[baseEvent].push({
+ name: name,
+ handler: handler
+ });
+ }
+ },
+ /**
+ * remove event bindings from the node. Pass in a string of
+ * event types delimmited by a space to remove multiple event
+ * bindings at once such as "mousedown mouseup mousemove".
+ * include a namespace to remove an event binding by name
+ * such as "click.foobar".
+ * @param {String} typesStr
+ */
+ off: function(typesStr) {
+ var types = typesStr.split(" ");
+
+ for(var n = 0; n < types.length; n++) {
+ var type = types[n];
+ var event = (type.indexOf('touch') === -1) ? 'on' + type : type;
+ var parts = event.split(".");
+ var baseEvent = parts[0];
+
+ if(this.eventListeners[baseEvent] && parts.length > 1) {
+ var name = parts[1];
+
+ for(var i = 0; i < this.eventListeners[baseEvent].length; i++) {
+ if(this.eventListeners[baseEvent][i].name === name) {
+ this.eventListeners[baseEvent].splice(i, 1);
+ if(this.eventListeners[baseEvent].length === 0) {
+ this.eventListeners[baseEvent] = undefined;
+ }
+ break;
+ }
+ }
+ } else {
+ this.eventListeners[baseEvent] = undefined;
+ }
+ }
+ },
+ /**
+ * show node
+ */
+ show: function() {
+ this.visible = true;
+ },
+ /**
+ * hide node
+ */
+ hide: function() {
+ this.visible = false;
+ },
+ /**
+ * get zIndex
+ */
+ getZIndex: function() {
+ return this.index;
+ },
+ /**
+ * set node scale. If only one parameter is passed in,
+ * then both scaleX and scaleY are set with that parameter
+ * @param {Number} scaleX
+ * @param {Number} scaleY
+ */
+ setScale: function(scaleX, scaleY) {
+ if(scaleY) {
+ this.scale.x = scaleX;
+ this.scale.y = scaleY;
+ } else {
+ this.scale.x = scaleX;
+ this.scale.y = scaleX;
+ }
+ },
+ /**
+ * get scale
+ */
+ getScale: function() {
+ return this.scale;
+ },
+ /**
+ * set node position
+ * @param {Number} x
+ * @param {Number} y
+ */
+ setPosition: function(x, y) {
+ this.x = x;
+ this.y = y;
+ },
+ /**
+ * get node position relative to container
+ */
+ getPosition: function() {
+ return {
+ x: this.x,
+ y: this.y
+ };
+ },
+ /**
+ * get absolute position relative to stage
+ */
+ getAbsolutePosition: function() {
+ var x = this.x;
+ var y = this.y;
+ var parent = this.getParent();
+ while(parent.className !== "Stage") {
+ x += parent.x;
+ y += parent.y;
+ parent = parent.parent;
+ }
+ return {
+ x: x,
+ y: y
+ };
+ },
+ /**
+ * move node by an amount
+ * @param {Number} x
+ * @param {Number} y
+ */
+ move: function(x, y) {
+ this.x += x;
+ this.y += y;
+ },
+ /**
+ * set node rotation in radians
+ * @param {Number} theta
+ */
+ setRotation: function(theta) {
+ this.rotation = theta;
+ },
+ /**
+ * set node rotation in degrees
+ * @param {Number} deg
+ */
+ setRotationDeg: function(deg) {
+ this.rotation = (deg * Math.PI / 180);
+ },
+ /**
+ * get rotation in radians
+ */
+ getRotation: function() {
+ return this.rotation;
+ },
+ /**
+ * get rotation in degrees
+ */
+ getRotationDeg: function() {
+ return this.rotation * 180 / Math.PI;
+ },
+ /**
+ * rotate node by an amount in radians
+ * @param {Number} theta
+ */
+ rotate: function(theta) {
+ this.rotation += theta;
+ },
+ /**
+ * rotate node by an amount in degrees
+ * @param {Number} deg
+ */
+ rotateDeg: function(deg) {
+ this.rotation += (deg * Math.PI / 180);
+ },
+ /**
+ * listen or don't listen to events
+ * @param {Boolean} isListening
+ */
+ listen: function(isListening) {
+ this.isListening = isListening;
+ },
+ /**
+ * move node to top
+ */
+ moveToTop: function() {
+ var index = this.index;
+ this.parent.children.splice(index, 1);
+ this.parent.children.push(this);
+ this.parent._setChildrenIndices();
+ },
+ /**
+ * move node up
+ */
+ moveUp: function() {
+ var index = this.index;
+ this.parent.children.splice(index, 1);
+ this.parent.children.splice(index + 1, 0, this);
+ this.parent._setChildrenIndices();
+ },
+ /**
+ * move node down
+ */
+ moveDown: function() {
+ var index = this.index;
+ if(index > 0) {
+ this.parent.children.splice(index, 1);
+ this.parent.children.splice(index - 1, 0, this);
+ this.parent._setChildrenIndices();
+ }
+ },
+ /**
+ * move node to bottom
+ */
+ moveToBottom: function() {
+ var index = this.index;
+ this.parent.children.splice(index, 1);
+ this.parent.children.unshift(this);
+ this.parent._setChildrenIndices();
+ },
+ /**
+ * set zIndex
+ * @param {int} index
+ */
+ setZIndex: function(zIndex) {
+ var index = this.index;
+ this.parent.children.splice(index, 1);
+ this.parent.children.splice(zIndex, 0, this);
+ this.parent._setChildrenIndices();
+ },
+ /**
+ * set alpha. Alpha values range from 0 to 1.
+ * A node with an alpha of 0 is fully transparent, and a node
+ * with an alpha of 1 is fully opaque
+ * @param {Object} alpha
+ */
+ setAlpha: function(alpha) {
+ this.alpha = alpha;
+ },
+ /**
+ * get alpha. Alpha values range from 0 to 1.
+ * A node with an alpha of 0 is fully transparent, and a node
+ * with an alpha of 1 is fully opaque
+ */
+ getAlpha: function() {
+ return this.alpha;
+ },
+ /**
+ * get absolute alpha
+ */
+ getAbsoluteAlpha: function() {
+ var absAlpha = 1;
+ var node = this;
+ // traverse upwards
+ while(node.className !== "Stage") {
+ absAlpha *= node.alpha;
+ node = node.parent;
+ }
+ return absAlpha;
+ },
+ /**
+ * enable or disable drag and drop
+ * @param {Boolean} setDraggable
+ */
+ draggable: function(setDraggable) {
+ if(setDraggable) {
+ var needInit = !this.drag.x && !this.drag.y;
+ this.drag.x = true;
+ this.drag.y = true;
+
+ if(needInit) {
+ this._initDrag();
+ }
+ } else {
+ this.drag.x = false;
+ this.drag.y = false;
+ this._dragCleanup();
+ }
+ },
+ /**
+ * enable or disable horizontal drag and drop
+ * @param {Boolean} setDraggable
+ */
+ draggableX: function(setDraggable) {
+ if(setDraggable) {
+ var needInit = !this.drag.x && !this.drag.y;
+ this.drag.x = true;
+ if(needInit) {
+ this._initDrag();
+ }
+ } else {
+ this.drag.x = false;
+ this._dragCleanup();
+ }
+ },
+ /**
+ * enable or disable vertical drag and drop
+ * @param {Boolean} setDraggable
+ */
+ draggableY: function(setDraggable) {
+ if(setDraggable) {
+ var needInit = !this.drag.x && !this.drag.y;
+ this.drag.y = true;
+ if(needInit) {
+ this._initDrag();
+ }
+ } else {
+ this.drag.y = false;
+ this._dragCleanup();
+ }
+ },
+ /**
+ * determine if node is currently in drag and drop mode
+ */
+ isDragging: function() {
+ var go = Kinetic.GlobalObject;
+ return go.drag.node !== undefined && go.drag.node.id === this.id && go.drag.moving;
+ },
+ /**
+ * move node to another container
+ * @param {Layer} newLayer
+ */
+ moveTo: function(newContainer) {
+ var parent = this.parent;
+ // remove from parent's children
+ parent.children.splice(this.index, 1);
+ parent._setChildrenIndices();
+
+ // add to new parent
+ newContainer.children.push(this);
+ this.index = newContainer.children.length - 1;
+ this.parent = newContainer;
+ newContainer._setChildrenIndices();
+
+ // update children hashes
+ if(this.name) {
+ parent.childrenNames[this.name] = undefined;
+ newContainer.childrenNames[this.name] = this;
+ }
+ },
+ /**
+ * get parent container
+ */
+ getParent: function() {
+ return this.parent;
+ },
+ /**
+ * get layer associated to node
+ */
+ getLayer: function() {
+ if(this.className === 'Layer') {
+ return this;
+ } else {
+ return this.getParent().getLayer();
+ }
+ },
+ /**
+ * get stage associated to node
+ */
+ getStage: function() {
+ return this.getParent().getStage();
+ },
+ /**
+ * get name
+ */
+ getName: function() {
+ return this.name;
+ },
+ /**
+ * set center offset
+ * @param {Number} x
+ * @param {Number} y
+ */
+ setCenterOffset: function(x, y) {
+ this.centerOffset.x = x;
+ this.centerOffset.y = y;
+ },
+ /**
+ * get center offset
+ */
+ getCenterOffset: function() {
+ return this.centerOffset;
+ },
+ /**
+ * initialize drag and drop
+ */
+ _initDrag: function() {
+ var go = Kinetic.GlobalObject;
+ var that = this;
+ this.on("mousedown.initdrag touchstart.initdrag", function(evt) {
+ var stage = that.getStage();
+ var pos = stage.getUserPosition();
+
+ if(pos) {
+ go.drag.node = that;
+ go.drag.offset.x = pos.x - that.x;
+ go.drag.offset.y = pos.y - that.y;
+ }
+ });
+ },
+ /**
+ * remove drag and drop event listener
+ */
+ _dragCleanup: function() {
+ if(!this.drag.x && !this.drag.y) {
+ this.off("mousedown.initdrag");
+ this.off("touchstart.initdrag");
+ }
+ },
+ /**
+ * handle node events
+ * @param {String} eventType
+ * @param {Event} evt
+ */
+ _handleEvents: function(eventType, evt) {
+ // generic events handler
+ function handle(obj) {
+ var el = obj.eventListeners;
+ if(el[eventType]) {
+ var events = el[eventType];
+ for(var i = 0; i < events.length; i++) {
+ events[i].handler.apply(obj, [evt]);
+ }
+ }
+
+ if(obj.parent.className !== "Stage") {
+ handle(obj.parent);
+ }
+ }
+
+ /*
+ * simulate bubbling by handling node events
+ * first, followed by group events, followed
+ * by layer events
+ */
+ handle(this);
+ }
+};
+///////////////////////////////////////////////////////////////////////
+// Container
+///////////////////////////////////////////////////////////////////////
+
+/**
+ * Container constructor.&nbsp; Containers are used to contain nodes or other containers
+ * @constructor
+ */
+Kinetic.Container = function() {
+ this.children = [];
+ this.childrenNames = {};
+};
+/*
+ * Container methods
+ */
+Kinetic.Container.prototype = {
+ /**
+ * get children
+ */
+ getChildren: function() {
+ return this.children;
+ },
+ /**
+ * get child node by name
+ * @param {String} name
+ */
+ getChild: function(name) {
+ return this.childrenNames[name];
+ },
+ /**
+ * remove all children
+ */
+ removeChildren: function() {
+ while(this.children.length > 0) {
+ this.remove(this.children[0]);
+ }
+ },
+ /**
+ * remove child from container
+ * @param {Node} child
+ */
+ _remove: function(child) {
+ if(child.name !== undefined) {
+ this.childrenNames[child.name] = undefined;
+ }
+ this.children.splice(child.index, 1);
+ this._setChildrenIndices();
+ child = undefined;
+ },
+ /**
+ * draw children
+ */
+ _drawChildren: function() {
+ var children = this.children;
+ for(var n = 0; n < children.length; n++) {
+ var child = children[n];
+ if(child.className === "Shape") {
+ child._draw(child.getLayer());
+ } else {
+ child._draw();
+ }
+ }
+ },
+ /**
+ * add node to container
+ * @param {Node} child
+ */
+ _add: function(child) {
+ if(child.name) {
+ this.childrenNames[child.name] = child;
+ }
+ child.id = Kinetic.GlobalObject.idCounter++;
+ child.index = this.children.length;
+ child.parent = this;
+
+ this.children.push(child);
+ },
+ /**
+ * set children indices
+ */
+ _setChildrenIndices: function() {
+ /*
+ * if reordering Layers, remove all canvas elements
+ * from the container except the buffer and backstage canvases
+ * and then readd all the layers
+ */
+ if(this.className === "Stage") {
+ var canvases = this.container.childNodes;
+ var bufferCanvas = canvases[0];
+ var backstageCanvas = canvases[1];
+
+ this.container.innerHTML = "";
+ this.container.appendChild(bufferCanvas);
+ this.container.appendChild(backstageCanvas);
+ }
+
+ for(var n = 0; n < this.children.length; n++) {
+ this.children[n].index = n;
+
+ if(this.className === "Stage") {
+ this.container.appendChild(this.children[n].canvas);
+ }
+ }
+ }
+};
+///////////////////////////////////////////////////////////////////////
+// Stage
+///////////////////////////////////////////////////////////////////////
+/**
+ * Stage constructor. A stage is used to contain multiple layers and handle
+ * animations
+ * @constructor
+ * @param {String|DomElement} cont Container id or DOM element
+ * @param {int} width
+ * @param {int} height
+ */
+Kinetic.Stage = function(cont, width, height) {
+ this.className = "Stage";
+ this.container = typeof cont === "string" ? document.getElementById(cont) : cont;
+ this.width = width;
+ this.height = height;
+ this.scale = {
+ x: 1,
+ y: 1
+ };
+ this.dblClickWindow = 400;
+ this.targetShape = undefined;
+ this.clickStart = false;
+
+ // desktop flags
+ this.mousePos = undefined;
+ this.mouseDown = false;
+ this.mouseUp = false;
+
+ // mobile flags
+ this.touchPos = undefined;
+ this.touchStart = false;
+ this.touchEnd = false;
+
+ /*
+ * Layer roles
+ *
+ * buffer - canvas compositing
+ * backstage - path detection
+ */
+ this.bufferLayer = new Kinetic.Layer();
+ this.backstageLayer = new Kinetic.Layer();
+
+ // set parents
+ this.bufferLayer.parent = this;
+ this.backstageLayer.parent = this;
+
+ // customize back stage context
+ var backstageLayer = this.backstageLayer;
+ this._stripLayer(backstageLayer);
+
+ this.bufferLayer.getCanvas().style.display = 'none';
+ this.backstageLayer.getCanvas().style.display = 'none';
+
+ // add buffer layer
+ this.bufferLayer.canvas.width = this.width;
+ this.bufferLayer.canvas.height = this.height;
+ this.container.appendChild(this.bufferLayer.canvas);
+
+ // add backstage layer
+ this.backstageLayer.canvas.width = this.width;
+ this.backstageLayer.canvas.height = this.height;
+ this.container.appendChild(this.backstageLayer.canvas);
+
+ this._listen();
+ this._prepareDrag();
+
+ // add stage to global object
+ var stages = Kinetic.GlobalObject.stages;
+ stages.push(this);
+
+ // set stage id
+ this.id = Kinetic.GlobalObject.idCounter++;
+
+ // animation support
+ this.isAnimating = false;
+ this.onFrameFunc = undefined;
+
+ // call super constructor
+ Kinetic.Container.apply(this, []);
+};
+/*
+ * Stage methods
+ */
+Kinetic.Stage.prototype = {
+ /**
+ * sets onFrameFunc for animation
+ * @param {function} func
+ */
+ onFrame: function(func) {
+ this.onFrameFunc = func;
+ },
+ /**
+ * start animation
+ */
+ start: function() {
+ this.isAnimating = true;
+ Kinetic.GlobalObject._handleAnimation();
+ },
+ /**
+ * stop animation
+ */
+ stop: function() {
+ this.isAnimating = false;
+ Kinetic.GlobalObject._handleAnimation();
+ },
+ /**
+ * draw children
+ */
+ draw: function() {
+ this._drawChildren();
+ },
+ /**
+ * set stage size
+ * @param {int} width
+ * @param {int} height
+ */
+ setSize: function(width, height) {
+ var layers = this.children;
+ for(var n = 0; n < layers.length; n++) {
+ var layer = layers[n];
+ layer.getCanvas().width = width;
+ layer.getCanvas().height = height;
+ layer.draw();
+ }
+
+ // set stage dimensions
+ this.width = width;
+ this.height = height;
+
+ // set buffer layer and backstage layer sizes
+ this.bufferLayer.getCanvas().width = width;
+ this.bufferLayer.getCanvas().height = height;
+ this.backstageLayer.getCanvas().width = width;
+ this.backstageLayer.getCanvas().height = height;
+ },
+ /**
+ * set stage scale. If only one parameter is passed in, then
+ * both scaleX and scaleY are set to the parameter
+ * @param {int} scaleX
+ * @param {int} scaleY
+ */
+ setScale: function(scaleX, scaleY) {
+ var oldScaleX = this.scale.x;
+ var oldScaleY = this.scale.y;
+
+ if(scaleY) {
+ this.scale.x = scaleX;
+ this.scale.y = scaleY;
+ } else {
+ this.scale.x = scaleX;
+ this.scale.y = scaleX;
+ }
+
+ /*
+ * scale all shape positions
+ */
+ var layers = this.children;
+ var that = this;
+ function scaleChildren(children) {
+ for(var i = 0; i < children.length; i++) {
+ var child = children[i];
+ child.x *= that.scale.x / oldScaleX;
+ child.y *= that.scale.y / oldScaleY;
+ if(child.children) {
+ scaleChildren(child.children);
+ }
+ }
+ }
+
+ scaleChildren(layers);
+ },
+ /**
+ * get scale
+ */
+ getScale: function() {
+ return this.scale;
+ },
+ /**
+ * clear all layers
+ */
+ clear: function() {
+ var layers = this.children;
+ for(var n = 0; n < layers.length; n++) {
+ layers[n].clear();
+ }
+ },
+ /**
+ * creates a composite data URL and passes it to a callback
+ * @param {function} callback
+ */
+ toDataURL: function(callback) {
+ var bufferLayer = this.bufferLayer;
+ var bufferContext = bufferLayer.getContext();
+ var layers = this.children;
+
+ function addLayer(n) {
+ var dataURL = layers[n].getCanvas().toDataURL();
+ var imageObj = new Image();
+ imageObj.onload = function() {
+ bufferContext.drawImage(this, 0, 0);
+ n++;
+ if(n < layers.length) {
+ addLayer(n);
+ } else {
+ callback(bufferLayer.getCanvas().toDataURL());
+ }
+ };
+ imageObj.src = dataURL;
+ }
+
+
+ bufferLayer.clear();
+ addLayer(0);
+ },
+ /**
+ * remove layer from stage
+ * @param {Layer} layer
+ */
+ remove: function(layer) {
+ // remove layer canvas from dom
+ this.container.removeChild(layer.canvas);
+
+ this._remove(layer);
+ },
+ /**
+ * bind event listener to stage (which is essentially
+ * the container DOM)
+ * @param {String} typesStr
+ * @param {function} handler
+ */
+ on: function(typesStr, handler) {
+ var types = typesStr.split(" ");
+ for(var n = 0; n < types.length; n++) {
+ var baseEvent = types[n];
+ this.container.addEventListener(baseEvent, handler, false);
+ }
+ },
+ /**
+ * add layer to stage
+ * @param {Layer} layer
+ */
+ add: function(layer) {
+ if(layer.name) {
+ this.childrenNames[layer.name] = layer;
+ }
+ layer.canvas.width = this.width;
+ layer.canvas.height = this.height;
+ this._add(layer);
+
+ // draw layer and append canvas to container
+ layer.draw();
+ this.container.appendChild(layer.canvas);
+ },
+ /**
+ * get mouse position for desktop apps
+ * @param {Event} evt
+ */
+ getMousePosition: function(evt) {
+ return this.mousePos;
+ },
+ /**
+ * get touch position for mobile apps
+ * @param {Event} evt
+ */
+ getTouchPosition: function(evt) {
+ return this.touchPos;
+ },
+ /**
+ * get user position (mouse position or touch position)
+ * @param {Event} evt
+ */
+ getUserPosition: function(evt) {
+ return this.getTouchPosition() || this.getMousePosition();
+ },
+ /**
+ * get container DOM element
+ */
+ getContainer: function() {
+ return this.container;
+ },
+ /**
+ * get stage
+ */
+ getStage: function() {
+ return this;
+ },
+ /**
+ * handle incoming event
+ * @param {Event} evt
+ */
+ _handleEvent: function(evt) {
+ var go = Kinetic.GlobalObject;
+ if(!evt) {
+ evt = window.event;
+ }
+
+ this._setMousePosition(evt);
+ this._setTouchPosition(evt);
+
+ var backstageLayer = this.backstageLayer;
+ var backstageLayerContext = backstageLayer.getContext();
+ var that = this;
+
+ backstageLayer.clear();
+
+ /*
+ * loop through layers. If at any point an event
+ * is triggered, n is set to -1 which will break out of the
+ * three nested loops
+ */
+ var targetFound = false;
+
+ function detectEvent(shape) {
+ shape._draw(backstageLayer);
+ var pos = that.getUserPosition();
+ var el = shape.eventListeners;
+
+ if(that.targetShape && shape.id === that.targetShape.id) {
+ targetFound = true;
+ }
+
+ if(shape.visible && pos !== undefined && backstageLayerContext.isPointInPath(pos.x, pos.y)) {
+ // handle onmousedown
+ if(that.mouseDown) {
+ that.mouseDown = false;
+ that.clickStart = true;
+ shape._handleEvents("onmousedown", evt);
+ return true;
+ }
+ // handle onmouseup & onclick
+ else if(that.mouseUp) {
+ that.mouseUp = false;
+ shape._handleEvents("onmouseup", evt);
+
+ // detect if click or double click occurred
+ if(that.clickStart) {
+ /*
+ * if dragging and dropping, don't fire click or dbl click
+ * event
+ */
+ if((!go.drag.moving) || !go.drag.node) {
+ shape._handleEvents("onclick", evt);
+
+ if(shape.inDoubleClickWindow) {
+ shape._handleEvents("ondblclick", evt);
+ }
+ shape.inDoubleClickWindow = true;
+ setTimeout(function() {
+ shape.inDoubleClickWindow = false;
+ }, that.dblClickWindow);
+ }
+ }
+ return true;
+ }
+
+ // handle touchstart
+ else if(that.touchStart) {
+ that.touchStart = false;
+ shape._handleEvents("touchstart", evt);
+
+ if(el.ondbltap && shape.inDoubleClickWindow) {
+ var events = el.ondbltap;
+ for(var i = 0; i < events.length; i++) {
+ events[i].handler.apply(shape, [evt]);
+ }
+ }
+
+ shape.inDoubleClickWindow = true;
+
+ setTimeout(function() {
+ shape.inDoubleClickWindow = false;
+ }, that.dblClickWindow);
+ return true;
+ }
+
+ // handle touchend
+ else if(that.touchEnd) {
+ that.touchEnd = false;
+ shape._handleEvents("touchend", evt);
+ return true;
+ }
+
+ // handle touchmove
+ else if(el.touchmove) {
+ shape._handleEvents("touchmove", evt);
+ return true;
+ }
+
+ //this condition is used to identify a new target shape.
+ else if(!that.targetShape || (!targetFound && shape.id !== that.targetShape.id)) {
+ /*
+ * check if old target has an onmouseout event listener
+ */
+ if(that.targetShape) {
+ var oldEl = that.targetShape.eventListeners;
+ if(oldEl) {
+ that.targetShape._handleEvents("onmouseout", evt);
+ }
+ }
+
+ // set new target shape
+ that.targetShape = shape;
+
+ // handle onmouseover
+ shape._handleEvents("onmouseover", evt);
+ return true;
+ }
+
+ // handle onmousemove
+ else {
+ shape._handleEvents("onmousemove", evt);
+ return true;
+ }
+ }
+ // handle mouseout condition
+ else if(that.targetShape && that.targetShape.id === shape.id) {
+ that.targetShape = undefined;
+ shape._handleEvents("onmouseout", evt);
+ return true;
+ }
+
+ return false;
+ }
+
+ function traverseChildren(obj) {
+ var children = obj.children;
+ // propapgate backwards through children
+ for(var i = children.length - 1; i >= 0; i--) {
+ var child = children[i];
+ if(child.className === "Shape") {
+ var exit = detectEvent(child);
+ if(exit) {
+ return true;
+ }
+ } else {
+ traverseChildren(child);
+ }
+ }
+
+ return false;
+ }
+
+ if(go.drag.node === undefined) {
+ for(var n = this.children.length - 1; n >= 0; n--) {
+ var layer = this.children[n];
+ if(layer.visible && n >= 0 && layer.isListening) {
+ if(traverseChildren(layer)) {
+ n = -1;
+ }
+ }
+ }
+ }
+ },
+ /**
+ * begin listening for events by adding event handlers
+ * to the container
+ */
+ _listen: function() {
+ var that = this;
+
+ // desktop events
+ this.container.addEventListener("mousedown", function(evt) {
+ that.mouseDown = true;
+ that._handleEvent(evt);
+ }, false);
+
+ this.container.addEventListener("mousemove", function(evt) {
+ that.mouseUp = false;
+ that.mouseDown = false;
+ that._handleEvent(evt);
+ }, false);
+
+ this.container.addEventListener("mouseup", function(evt) {
+ that.mouseUp = true;
+ that.mouseDown = false;
+ that._handleEvent(evt);
+
+ that.clickStart = false;
+ }, false);
+
+ this.container.addEventListener("mouseover", function(evt) {
+ that._handleEvent(evt);
+ }, false);
+
+ this.container.addEventListener("mouseout", function(evt) {
+ that.mousePos = undefined;
+ }, false);
+ // mobile events
+ this.container.addEventListener("touchstart", function(evt) {
+ evt.preventDefault();
+ that.touchStart = true;
+ that._handleEvent(evt);
+ }, false);
+
+ this.container.addEventListener("touchmove", function(evt) {
+ evt.preventDefault();
+ that._handleEvent(evt);
+ }, false);
+
+ this.container.addEventListener("touchend", function(evt) {
+ evt.preventDefault();
+ that.touchEnd = true;
+ that._handleEvent(evt);
+ }, false);
+ },
+ /**
+ * set mouse positon for desktop apps
+ * @param {Event} evt
+ */
+ _setMousePosition: function(evt) {
+ var mouseX = evt.clientX - this._getContainerPosition().left + window.pageXOffset;
+ var mouseY = evt.clientY - this._getContainerPosition().top + window.pageYOffset;
+ 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) {// Only deal with
+ // one finger
+ var touch = evt.touches[0];
+ // Get the information for finger #1
+ var touchX = touch.clientX - this._getContainerPosition().left + window.pageXOffset;
+ var touchY = touch.clientY - this._getContainerPosition().top + window.pageYOffset;
+
+ this.touchPos = {
+ x: touchX,
+ y: touchY
+ };
+ }
+ },
+ /**
+ * get container position
+ */
+ _getContainerPosition: function() {
+ var obj = this.container;
+ var top = 0;
+ var left = 0;
+ while(obj && obj.tagName !== "BODY") {
+ top += obj.offsetTop;
+ left += obj.offsetLeft;
+ obj = obj.offsetParent;
+ }
+ return {
+ top: top,
+ left: left
+ };
+ },
+ /**
+ * disable layer rendering
+ * @param {Layer} layer
+ */
+ _stripLayer: function(layer) {
+ layer.context.stroke = function() {
+ };
+ layer.context.fill = function() {
+ };
+ layer.context.fillRect = function(x, y, width, height) {
+ layer.context.rect(x, y, width, height);
+ };
+ layer.context.strokeRect = function(x, y, width, height) {
+ layer.context.rect(x, y, width, height);
+ };
+ layer.context.drawImage = function() {
+ };
+ layer.context.fillText = function() {
+ };
+ layer.context.strokeText = function() {
+ };
+ },
+ /**
+ * end drag and drop
+ */
+ _endDrag: function(evt) {
+ var go = Kinetic.GlobalObject;
+ if(go.drag.node) {
+ if(go.drag.moving) {
+ go.drag.moving = false;
+ go.drag.node._handleEvents("ondragend", evt);
+ }
+ }
+ go.drag.node = undefined;
+ },
+ /**
+ * prepare drag and drop
+ */
+ _prepareDrag: function() {
+ var that = this;
+
+ this.on("mousemove touchmove", function(evt) {
+ var go = Kinetic.GlobalObject;
+ if(go.drag.node) {
+ var pos = that.getUserPosition();
+ if(go.drag.node.drag.x) {
+ go.drag.node.x = pos.x - go.drag.offset.x;
+ }
+ if(go.drag.node.drag.y) {
+ go.drag.node.y = pos.y - go.drag.offset.y;
+ }
+ go.drag.node.getLayer().draw();
+
+ if(!go.drag.moving) {
+ go.drag.moving = true;
+ // execute dragstart events if defined
+ go.drag.node._handleEvents("ondragstart", evt);
+ }
+ // execute user defined ondragmove if defined
+ go.drag.node._handleEvents("ondragmove", evt);
+ }
+ }, false);
+
+ this.on("mouseup touchend mouseout", function(evt) {
+ that._endDrag(evt);
+ });
+ }
+};
+// extend Container
+Kinetic.GlobalObject.extend(Kinetic.Stage, Kinetic.Container);
+///////////////////////////////////////////////////////////////////////
+// Layer
+///////////////////////////////////////////////////////////////////////
+/**
+ * Layer constructor. Layers are tied to their own canvas element and are used
+ * to contain groups or shapes
+ * @constructor
+ * @augments Kinetic.Container
+ * @augments Kinetic.Node
+ * @param {Object} config
+ */
+Kinetic.Layer = function(config) {
+ this.className = "Layer";
+ this.canvas = document.createElement('canvas');
+ this.context = this.canvas.getContext('2d');
+ this.canvas.style.position = 'absolute';
+
+ // call super constructors
+ Kinetic.Container.apply(this, []);
+ Kinetic.Node.apply(this, [config]);
+};
+/*
+ * Layer methods
+ */
+Kinetic.Layer.prototype = {
+ /**
+ * public draw children
+ */
+ draw: function() {
+ this._draw();
+ },
+ /**
+ * clear layer
+ */
+ clear: function() {
+ var context = this.getContext();
+ var canvas = this.getCanvas();
+ context.clearRect(0, 0, canvas.width, canvas.height);
+ },
+ /**
+ * get layer canvas
+ */
+ getCanvas: function() {
+ return this.canvas;
+ },
+ /**
+ * get layer context
+ */
+ getContext: function() {
+ return this.context;
+ },
+ /**
+ * add node to layer
+ * @param {Node} node
+ */
+ add: function(child) {
+ this._add(child);
+ },
+ /**
+ * remove a child from the layer
+ * @param {Node} child
+ */
+ remove: function(child) {
+ this._remove(child);
+ },
+ /**
+ * private draw children
+ */
+ _draw: function() {
+ this.clear();
+ if(this.visible) {
+ this._drawChildren();
+ }
+ }
+};
+// Extend Container and Node
+Kinetic.GlobalObject.extend(Kinetic.Layer, Kinetic.Container);
+Kinetic.GlobalObject.extend(Kinetic.Layer, Kinetic.Node);
+///////////////////////////////////////////////////////////////////////
+// Group
+///////////////////////////////////////////////////////////////////////
+
+/**
+ * Group constructor. Groups are used to contain shapes or other groups.
+ * @constructor
+ * @augments Kinetic.Container
+ * @augments Kinetic.Node
+ * @param {Object} config
+ */
+Kinetic.Group = function(config) {
+ this.className = "Group";
+
+ // call super constructors
+ Kinetic.Container.apply(this, []);
+ Kinetic.Node.apply(this, [config]);
+};
+/*
+ * Group methods
+ */
+Kinetic.Group.prototype = {
+ /**
+ * add node to group
+ * @param {Node} child
+ */
+ add: function(child) {
+ this._add(child);
+ },
+ /**
+ * remove a child node from the group
+ * @param {Node} child
+ */
+ remove: function(child) {
+ this._remove(child);
+ },
+ /**
+ * draw children
+ */
+ _draw: function() {
+ if(this.visible) {
+ this._drawChildren();
+ }
+ }
+};
+
+// Extend Container and Node
+Kinetic.GlobalObject.extend(Kinetic.Group, Kinetic.Container);
+Kinetic.GlobalObject.extend(Kinetic.Group, Kinetic.Node);
+///////////////////////////////////////////////////////////////////////
+// Shape
+///////////////////////////////////////////////////////////////////////
+/**
+ * Shape constructor. Shapes are used to objectify drawing bits of a KineticJS
+ * application
+ * @constructor
+ * @augments Kinetic.Node
+ * @param {Object} config
+ */
+Kinetic.Shape = function(config) {
+ this.className = "Shape";
+
+ // defaults
+ if(config.stroke !== undefined || config.strokeWidth !== undefined) {
+ if(config.stroke === undefined) {
+ config.stroke = "black";
+ } else if(config.strokeWidth === undefined) {
+ config.strokeWidth = 2;
+ }
+ }
+
+ // required
+ this.drawFunc = config.drawFunc;
+
+ // call super constructor
+ Kinetic.Node.apply(this, [config]);
+};
+/*
+ * Shape methods
+ */
+Kinetic.Shape.prototype = {
+ /**
+ * get temporary shape layer context
+ */
+ getContext: function() {
+ return this.tempLayer.getContext();
+ },
+ /**
+ * get shape temp layer canvas
+ */
+ getCanvas: function() {
+ return this.tempLayer.getCanvas();
+ },
+ /**
+ * helper method to fill and stroke a shape based on its
+ * fill, stroke, and strokeWidth properties
+ */
+ fillStroke: function() {
+ var context = this.getContext();
+
+ if(this.fill !== undefined) {
+ context.fillStyle = this.fill;
+ context.fill();
+ }
+ if(this.stroke !== undefined) {
+ context.lineWidth = this.strokeWidth === undefined ? 1 : this.strokeWidth;
+ context.strokeStyle = this.stroke;
+ context.stroke();
+ }
+ },
+ /**
+ * set fill which can be a color, gradient object,
+ * or pattern object
+ * @param {String|CanvasGradient|CanvasPattern} fill
+ */
+ setFill: function(fill) {
+ this.fill = fill;
+ },
+ /**
+ * get fill
+ */
+ getFill: function() {
+ return this.fill;
+ },
+ /**
+ * set stroke color
+ * @param {String} stroke
+ */
+ setStroke: function(stroke) {
+ this.stroke = stroke;
+ },
+ /**
+ * get stroke color
+ */
+ getStroke: function() {
+ return this.stroke;
+ },
+ /**
+ * set stroke width
+ * @param {Number} strokeWidth
+ */
+ setStrokeWidth: function(strokeWidth) {
+ this.strokeWidth = strokeWidth;
+ },
+ /**
+ * get stroke width
+ */
+ getStrokeWidth: function() {
+ return this.strokeWidth;
+ },
+ /**
+ * draw shape
+ * @param {Layer} layer Layer that the shape will be drawn on
+ */
+ _draw: function(layer) {
+ if(this.visible) {
+ var stage = layer.getStage();
+ var context = layer.getContext();
+
+ var family = [];
+
+ family.unshift(this);
+ var parent = this.parent;
+ while(parent.className !== "Stage") {
+ family.unshift(parent);
+ parent = parent.parent;
+ }
+
+ // children transforms
+ for(var n = 0; n < family.length; n++) {
+ var obj = family[n];
+
+ context.save();
+ if(obj.x !== 0 || obj.y !== 0) {
+ context.translate(obj.x, obj.y);
+ }
+ if(obj.centerOffset.x !== 0 || obj.centerOffset.y !== 0) {
+ context.translate(obj.centerOffset.x, obj.centerOffset.y);
+ }
+ if(obj.rotation !== 0) {
+ context.rotate(obj.rotation);
+ }
+ if(obj.scale.x !== 1 || obj.scale.y !== 1) {
+ context.scale(obj.scale.x, obj.scale.y);
+ }
+ if(obj.centerOffset.x !== 0 || obj.centerOffset.y !== 0) {
+ context.translate(-1 * obj.centerOffset.x, -1 * obj.centerOffset.y);
+ }
+ if(obj.getAbsoluteAlpha() !== 1) {
+ context.globalAlpha = obj.getAbsoluteAlpha();
+ }
+ }
+
+ // stage transform
+ context.save();
+ if(stage && (stage.scale.x !== 1 || stage.scale.y !== 1)) {
+ context.scale(stage.scale.x, stage.scale.y);
+ }
+
+ this.tempLayer = layer;
+ this.drawFunc.call(this);
+
+ // children restore
+ for(var i = 0; i < family.length; i++) {
+ context.restore();
+ }
+
+ // stage restore
+ context.restore();
+ }
+ }
+};
+// extend Node
+Kinetic.GlobalObject.extend(Kinetic.Shape, Kinetic.Node);
+///////////////////////////////////////////////////////////////////////
+// Rect
+///////////////////////////////////////////////////////////////////////
+/**
+ * Rect constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+Kinetic.Rect = function(config) {
+ config.drawFunc = function() {
+ var canvas = this.getCanvas();
+ var context = this.getContext();
+ context.beginPath();
+ context.rect(0, 0, this.width, this.height);
+ context.closePath();
+ this.fillStroke();
+ };
+ // call super constructor
+ Kinetic.Shape.apply(this, [config]);
+};
+/*
+ * Rect methods
+ */
+Kinetic.Rect.prototype = {
+ /**
+ * set width
+ * @param {Number} width
+ */
+ setWidth: function(width) {
+ this.width = width;
+ },
+ /**
+ * get width
+ */
+ getWidth: function() {
+ return this.width;
+ },
+ /**
+ * set height
+ * @param {Number} height
+ */
+ setHeight: function(height) {
+ this.height = height;
+ },
+ /**
+ * get height
+ */
+ getHeight: function() {
+ return this.height;
+ },
+ /**
+ * set width and height
+ * @param {Number} width
+ * @param {Number} height
+ */
+ setSize: function(width, height) {
+ this.width = width;
+ this.height = height;
+ }
+};
+
+// extend Shape
+Kinetic.GlobalObject.extend(Kinetic.Rect, Kinetic.Shape);
+///////////////////////////////////////////////////////////////////////
+// Circle
+///////////////////////////////////////////////////////////////////////
+/**
+ * Circle constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+Kinetic.Circle = function(config) {
+ config.drawFunc = function() {
+ var canvas = this.getCanvas();
+ var context = this.getContext();
+ context.beginPath();
+ context.arc(0, 0, this.radius, 0, Math.PI * 2, true);
+ context.closePath();
+ this.fillStroke();
+ };
+ // call super constructor
+ Kinetic.Shape.apply(this, [config]);
+};
+/*
+ * Circle methods
+ */
+Kinetic.Circle.prototype = {
+ /**
+ * set radius
+ * @param {Number} radius
+ */
+ setRadius: function(radius) {
+ this.radius = radius;
+ },
+ /**
+ * get radius
+ */
+ getRadius: function() {
+ return this.radius;
+ }
+};
+
+// extend Shape
+Kinetic.GlobalObject.extend(Kinetic.Circle, Kinetic.Shape);
+///////////////////////////////////////////////////////////////////////
+// Image
+///////////////////////////////////////////////////////////////////////
+/**
+ * Image constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+Kinetic.Image = function(config) {
+ // defaults
+ if(config.width === undefined) {
+ config.width = config.image.width;
+ }
+ if(config.height === undefined) {
+ config.height = config.image.height;
+ }
+
+ config.drawFunc = function() {
+ var canvas = this.getCanvas();
+ var context = this.getContext();
+ context.beginPath();
+ context.rect(0, 0, this.width, this.height);
+ context.closePath();
+ this.fillStroke();
+ context.drawImage(this.image, 0, 0, this.width, this.height);
+ };
+ // call super constructor
+ Kinetic.Shape.apply(this, [config]);
+};
+/*
+ * Image methods
+ */
+Kinetic.Image.prototype = {
+ /**
+ * set image
+ * @param {ImageObject} image
+ */
+ setImage: function(image) {
+ this.image = image;
+ },
+ /**
+ * get image
+ */
+ getImage: function(image) {
+ return this.image;
+ },
+ /**
+ * set width
+ * @param {Number} width
+ */
+ setWidth: function(width) {
+ this.width = width;
+ },
+ /**
+ * get width
+ */
+ getWidth: function() {
+ return this.width;
+ },
+ /**
+ * set height
+ * @param {Number} height
+ */
+ setHeight: function(height) {
+ this.height = height;
+ },
+ /**
+ * get height
+ */
+ getHeight: function() {
+ return this.height;
+ },
+ /**
+ * set width and height
+ * @param {Number} width
+ * @param {Number} height
+ */
+ setSize: function(width, height) {
+ this.width = width;
+ this.height = height;
+ }
+};
+// extend Shape
+Kinetic.GlobalObject.extend(Kinetic.Image, Kinetic.Shape);
+///////////////////////////////////////////////////////////////////////
+// Polygon
+///////////////////////////////////////////////////////////////////////
+/**
+ * Polygon constructor.&nbsp; Polygons are defined by an array of points
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+Kinetic.Polygon = function(config) {
+ config.drawFunc = function() {
+ var context = this.getContext();
+ context.beginPath();
+ context.moveTo(this.points[0].x, this.points[0].y);
+ for(var n = 1; n < this.points.length; n++) {
+ context.lineTo(this.points[n].x, this.points[n].y);
+ }
+ context.closePath();
+ this.fillStroke();
+ };
+ // call super constructor
+ Kinetic.Shape.apply(this, [config]);
+};
+/*
+ * Polygon methods
+ */
+Kinetic.Polygon.prototype = {
+ /**
+ * set points array
+ * @param {Array} points
+ */
+ setPoints: function(points) {
+ this.points = points;
+ },
+ /**
+ * get points array
+ */
+ getPoints: function() {
+ return this.points;
+ }
+};
+
+// extend Shape
+Kinetic.GlobalObject.extend(Kinetic.Polygon, Kinetic.Shape);
+///////////////////////////////////////////////////////////////////////
+// RegularPolygon
+///////////////////////////////////////////////////////////////////////
+/**
+ * RegularPolygon constructor.&nbsp; Examples include triangles, squares, pentagons, hexagons, etc.
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+Kinetic.RegularPolygon = function(config) {
+ config.drawFunc = function() {
+ var context = this.getContext();
+ context.beginPath();
+ context.moveTo(0, 0 - this.radius);
+
+ for(var n = 1; n < this.sides; n++) {
+ var x = this.radius * Math.sin(n * 2 * Math.PI / this.sides);
+ var y = -1 * this.radius * Math.cos(n * 2 * Math.PI / this.sides);
+ context.lineTo(x, y);
+ }
+ context.closePath();
+ this.fillStroke();
+ };
+ // call super constructor
+ Kinetic.Shape.apply(this, [config]);
+};
+/*
+ * RegularPolygon methods
+ */
+Kinetic.RegularPolygon.prototype = {
+ /**
+ * set number of points
+ * @param {int} points
+ */
+ setPoints: function(points) {
+ this.points = points;
+ },
+ /**
+ * get number of points
+ */
+ getPoints: function() {
+ return this.points;
+ },
+ /**
+ * set radius
+ * @param {Number} radius
+ */
+ setRadius: function(radius) {
+ this.radius = radius;
+ },
+ /**
+ * get radius
+ */
+ getRadius: function() {
+ return this.radius;
+ },
+ /**
+ * set number of sides
+ * @param {int} sides
+ */
+ setSides: function(sides) {
+ this.sides = sides;
+ },
+ /**
+ * get number of sides
+ */
+ getSides: function() {
+ return this.sides;
+ }
+};
+
+// extend Shape
+Kinetic.GlobalObject.extend(Kinetic.RegularPolygon, Kinetic.Shape);
+///////////////////////////////////////////////////////////////////////
+// Star
+///////////////////////////////////////////////////////////////////////
+/**
+ * Star constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+Kinetic.Star = function(config) {
+ config.drawFunc = function() {
+ var context = this.getContext();
+ context.beginPath();
+ context.moveTo(0, 0 - this.outerRadius);
+
+ for(var n = 1; n < this.points * 2; n++) {
+ var radius = n % 2 === 0 ? this.outerRadius : this.innerRadius;
+ var x = radius * Math.sin(n * Math.PI / this.points);
+ var y = -1 * radius * Math.cos(n * Math.PI / this.points);
+ context.lineTo(x, y);
+ }
+ context.closePath();
+ this.fillStroke();
+ };
+ // call super constructor
+ Kinetic.Shape.apply(this, [config]);
+};
+/*
+ * Star methods
+ */
+Kinetic.Star.prototype = {
+ /**
+ * set points array
+ * @param {Array} points
+ */
+ setPoints: function(points) {
+ this.points = points;
+ },
+ /**
+ * get points array
+ */
+ getPoints: function() {
+ return this.points;
+ },
+ /**
+ * set outer radius
+ * @param {Number} radius
+ */
+ setOuterRadius: function(radius) {
+ this.outerRadius = radius;
+ },
+ /**
+ * get outer radius
+ */
+ getOuterRadius: function() {
+ return this.outerRadius;
+ },
+ /**
+ * set inner radius
+ * @param {Number} radius
+ */
+ setInnerRadius: function(radius) {
+ this.innerRadius = radius;
+ },
+ /**
+ * get inner radius
+ */
+ getInnerRadius: function() {
+ return this.innerRadius;
+ }
+};
+// extend Shape
+Kinetic.GlobalObject.extend(Kinetic.Star, Kinetic.Shape);
+///////////////////////////////////////////////////////////////////////
+// Text
+///////////////////////////////////////////////////////////////////////
+/**
+ * Text constructor
+ * @constructor
+ * @augments Kinetic.Shape
+ * @param {Object} config
+ */
+Kinetic.Text = function(config) {
+ /*
+ * defaults
+ */
+ if(config.textStroke !== undefined || config.textStrokeWidth !== undefined) {
+ if(config.textStroke === undefined) {
+ config.textStroke = "black";
+ } else if(config.textStrokeWidth === undefined) {
+ config.textStrokeWidth = 2;
+ }
+ }
+ if(config.align === undefined) {
+ config.align = "left";
+ }
+ if(config.verticalAlign === undefined) {
+ config.verticalAlign = "top";
+ }
+ if(config.padding === undefined) {
+ config.padding = 0;
+ }
+
+ config.drawFunc = function() {
+ var canvas = this.getCanvas();
+ var context = this.getContext();
+ context.font = this.fontSize + "pt " + this.fontFamily;
+ context.textBaseline = "middle";
+ var metrics = context.measureText(this.text);
+ var textHeight = this.fontSize;
+ var textWidth = metrics.width;
+ var p = this.padding;
+ var x = 0;
+ var y = 0;
+
+ switch (this.align) {
+ case "center":
+ x = textWidth / -2 - p;
+ break;
+ case "right":
+ x = -1 * textWidth - p;
+ break;
+ }
+
+ switch (this.verticalAlign) {
+ case "middle":
+ y = textHeight / -2 - p;
+ break;
+ case "bottom":
+ y = -1 * textHeight - p;
+ break;
+ }
+
+ // draw path
+ context.save();
+ context.beginPath();
+ context.rect(x, y, textWidth + p * 2, textHeight + p * 2);
+ context.closePath();
+ this.fillStroke();
+ context.restore();
+
+ var tx = p + x;
+ var ty = textHeight / 2 + p + y;
+
+ // draw text
+ if(this.textFill !== undefined) {
+ context.fillStyle = this.textFill;
+ context.fillText(this.text, tx, ty);
+ }
+ if(this.textStroke !== undefined || this.textStrokeWidth !== undefined) {
+ // defaults
+ if(this.textStroke === undefined) {
+ this.textStroke = "black";
+ } else if(this.textStrokeWidth === undefined) {
+ this.textStrokeWidth = 2;
+ }
+ context.lineWidth = this.textStrokeWidth;
+ context.strokeStyle = this.textStroke;
+ context.strokeText(this.text, tx, ty);
+ }
+ };
+ // call super constructor
+ Kinetic.Shape.apply(this, [config]);
+};
+/*
+ * Text methods
+ */
+Kinetic.Text.prototype = {
+ /**
+ * set font family
+ * @param {String} fontFamily
+ */
+ setFontFamily: function(fontFamily) {
+ this.fontFamily = fontFamily;
+ },
+ /**
+ * get font family
+ */
+ getFontFamily: function() {
+ return this.fontFamily;
+ },
+ /**
+ * set font size
+ * @param {int} fontSize
+ */
+ setFontSize: function(fontSize) {
+ this.fontSize = fontSize;
+ },
+ /**
+ * get font size
+ */
+ getFontSize: function() {
+ return this.fontSize;
+ },
+ /**
+ * set text fill color
+ * @param {String} textFill
+ */
+ setTextFill: function(textFill) {
+ this.textFill = textFill;
+ },
+ /**
+ * get text fill color
+ */
+ getTextFill: function() {
+ return this.textFill;
+ },
+ /**
+ * set text stroke color
+ * @param {String} textStroke
+ */
+ setTextStroke: function(textStroke) {
+ this.textStroke = textStroke;
+ },
+ /**
+ * get text stroke color
+ */
+ getTextStroke: function() {
+ return this.textStroke;
+ },
+ /**
+ * set text stroke width
+ * @param {int} textStrokeWidth
+ */
+ setTextStrokeWidth: function(textStrokeWidth) {
+ this.textStrokeWidth = textStrokeWidth;
+ },
+ /**
+ * get text stroke width
+ */
+ getTextStrokeWidth: function() {
+ return this.textStrokeWidth;
+ },
+ /**
+ * set padding
+ * @param {int} padding
+ */
+ setPadding: function(padding) {
+ this.padding = padding;
+ },
+ /**
+ * get padding
+ */
+ getPadding: function() {
+ return this.padding;
+ },
+ /**
+ * set horizontal align of text
+ * @param {String} align align can be "left", "center", or "right"
+ */
+ setAlign: function(align) {
+ this.align = align;
+ },
+ /**
+ * get horizontal align
+ */
+ getAlign: function() {
+ return this.align;
+ },
+ /**
+ * set vertical align of text
+ * @param {String} verticalAlign verticalAlign can be "top", "middle", or "bottom"
+ */
+ setVerticalAlign: function(verticalAlign) {
+ this.verticalAlign = verticalAlign;
+ },
+ /**
+ * get vertical align
+ */
+ getVerticalAlign: function() {
+ return this.verticalAlign;
+ },
+ /**
+ * set text
+ * @param {String} text
+ */
+ setText: function(text) {
+ this.text = text;
+ },
+ /**
+ * get text
+ */
+ getText: function() {
+ return this.text;
+ }
+};
+// extend Shape
+Kinetic.GlobalObject.extend(Kinetic.Text, Kinetic.Shape);
View
35 dist/kinetic.min.js
@@ -0,0 +1,35 @@
+/**
+ * KineticJS JavaScript Library v3.8.3
+ * http://www.kineticjs.com/
+ * Copyright 2012, Eric Rowell
+ * Licensed under the MIT or GPL Version 2 licenses.
+ * Date: Mar 03 2012
+ *
+ * Copyright (C) 2011 - 2012 by Eric Rowell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+///////////////////////////////////////////////////////////////////////
+// Global Object
+///////////////////////////////////////////////////////////////////////
+/**
+ * Kinetic Namespace
+ * @namespace
+ */
+var Kinetic={};Kinetic.GlobalObject={stages:[],idCounter:0,isAnimating:!1,frame:{time:0,timeDiff:0,lastTime:0},drag:{moving:!1,node:undefined,offset:{x:0,y:0}},extend:function(a,b){for(var c in b.prototype)b.prototype.hasOwnProperty(c)&&(a.prototype[c]=b.prototype[c])},_isaCanvasAnimating:function(){for(var a=0;a<this.stages.length;a++)if(this.stages[a].isAnimating)return!0;return!1},_runFrames:function(){for(var a=0;a<this.stages.length;a++)this.stages[a].isAnimating&&this.stages[a].onFrameFunc(this.frame)},_updateFrameObject:function(){var a=new Date,b=a.getTime();this.frame.lastTime===0?this.frame.lastTime=b:(this.frame.timeDiff=b-this.frame.lastTime,this.frame.lastTime=b,this.frame.time+=this.frame.timeDiff)},_animationLoop:function(){if(this.isAnimating){this._updateFrameObject(),this._runFrames();var a=this;requestAnimFrame(function(){a._animationLoop()})}},_handleAnimation:function(){var a=this;!this.isAnimating&&this._isaCanvasAnimating()?(this.isAnimating=!0,a._animationLoop()):this.isAnimating&&!this._isaCanvasAnimating()&&(this.isAnimating=!1)}},window.requestAnimFrame=function(a){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1e3/60)}}(),Kinetic.Node=function(a){this.visible=!0,this.isListening=!0,this.name=undefined,this.alpha=1,this.x=0,this.y=0,this.scale={x:1,y:1},this.rotation=0,this.centerOffset={x:0,y:0},this.eventListeners={},this.drag={x:!1,y:!1};if(a)for(var b in a)switch(b){case"draggable":this.draggable(a[b]);break;case"draggableX":this.draggableX(a[b]);break;case"draggableY":this.draggableY(a[b]);break;case"listen":this.listen(a[b]);break;case"rotationDeg":this.rotation=a[b]*Math.PI/180;break;default:this[b]=a[b]}this.centerOffset.x===undefined&&(this.centerOffset.x=0),this.centerOffset.y===undefined&&(this.centerOffset.y=0)},Kinetic.Node.prototype={on:function(a,b){var c=a.split(" ");for(var d=0;d<c.length;d++){var e=c[d],f=e.indexOf("touch")===-1?"on"+e:e,g=f.split("."),h=g[0],i=g.length>1?g[1]:"";this.eventListeners[h]||(this.eventListeners[h]=[]),this.eventListeners[h].push({name:i,handler:b})}},off:function(a){var b=a.split(" ");for(var c=0;c<b.length;c++){var d=b[c],e=d.indexOf("touch")===-1?"on"+d:d,f=e.split("."),g=f[0];if(this.eventListeners[g]&&f.length>1){var h=f[1];for(var i=0;i<this.eventListeners[g].length;i++)if(this.eventListeners[g][i].name===h){this.eventListeners[g].splice(i,1),this.eventListeners[g].length===0&&(this.eventListeners[g]=undefined);break}}else this.eventListeners[g]=undefined}},show:function(){this.visible=!0},hide:function(){this.visible=!1},getZIndex:function(){return this.index},setScale:function(a,b){b?(this.scale.x=a,this.scale.y=b):(this.scale.x=a,this.scale.y=a)},getScale:function(){return this.scale},setPosition:function(a,b){this.x=a,this.y=b},getPosition:function(){return{x:this.x,y:this.y}},getAbsolutePosition:function(){var a=this.x,b=this.y,c=this.getParent();while(c.className!=="Stage")a+=c.x,b+=c.y,c=c.parent;return{x:a,y:b}},move:function(a,b){this.x+=a,this.y+=b},setRotation:function(a){this.rotation=a},setRotationDeg:function(a){this.rotation=a*Math.PI/180},getRotation:function(){return this.rotation},getRotationDeg:function(){return this.rotation*180/Math.PI},rotate:function(a){this.rotation+=a},rotateDeg:function(a){this.rotation+=a*Math.PI/180},listen:function(a){this.isListening=a},moveToTop:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.push(this),this.parent._setChildrenIndices()},moveUp:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.splice(a+1,0,this),this.parent._setChildrenIndices()},moveDown:function(){var a=this.index;a>0&&(this.parent.children.splice(a,1),this.parent.children.splice(a-1,0,this),this.parent._setChildrenIndices())},moveToBottom:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.unshift(this),this.parent._setChildrenIndices()},setZIndex:function(a){var b=this.index;this.parent.children.splice(b,1),this.parent.children.splice(a,0,this),this.parent._setChildrenIndices()},setAlpha:function(a){this.alpha=a},getAlpha:function(){return this.alpha},getAbsoluteAlpha:function(){var a=1,b=this;while(b.className!=="Stage")a*=b.alpha,b=b.parent;return a},draggable:function(a){if(a){var b=!this.drag.x&&!this.drag.y;this.drag.x=!0,this.drag.y=!0,b&&this._initDrag()}else this.drag.x=!1,this.drag.y=!1,this._dragCleanup()},draggableX:function(a){if(a){var b=!this.drag.x&&!this.drag.y;this.drag.x=!0,b&&this._initDrag()}else this.drag.x=!1,this._dragCleanup()},draggableY:function(a){if(a){var b=!this.drag.x&&!this.drag.y;this.drag.y=!0,b&&this._initDrag()}else this.drag.y=!1,this._dragCleanup()},isDragging:function(){var a=Kinetic.GlobalObject;return a.drag.node!==undefined&&a.drag.node.id===this.id&&a.drag.moving},moveTo:function(a){var b=this.parent;b.children.splice(this.index,1),b._setChildrenIndices(),a.children.push(this),this.index=a.children.length-1,this.parent=a,a._setChildrenIndices(),this.name&&(b.childrenNames[this.name]=undefined,a.childrenNames[this.name]=this)},getParent:function(){return this.parent},getLayer:function(){return this.className==="Layer"?this:this.getParent().getLayer()},getStage:function(){return this.getParent().getStage()},getName:function(){return this.name},setCenterOffset:function(a,b){this.centerOffset.x=a,this.centerOffset.y=b},getCenterOffset:function(){return this.centerOffset},_initDrag:function(){var a=Kinetic.GlobalObject,b=this;this.on("mousedown.initdrag touchstart.initdrag",function(c){var d=b.getStage(),e=d.getUserPosition();e&&(a.drag.node=b,a.drag.offset.x=e.x-b.x,a.drag.offset.y=e.y-b.y)})},_dragCleanup:function(){!this.drag.x&&!this.drag.y&&(this.off("mousedown.initdrag"),this.off("touchstart.initdrag"))},_handleEvents:function(a,b){function c(d){var e=d.eventListeners;if(e[a]){var f=e[a];for(var g=0;g<f.length;g++)f[g].handler.apply(d,[b])}d.parent.className!=="Stage"&&c(d.parent)}c(this)}},Kinetic.Container=function(){this.children=[],this.childrenNames={}},Kinetic.Container.prototype={getChildren:function(){return this.children},getChild:function(a){return this.childrenNames[a]},removeChildren:function(){while(this.children.length>0)this.remove(this.children[0])},_remove:function(a){a.name!==undefined&&(this.childrenNames[a.name]=undefined),this.children.splice(a.index,1),this._setChildrenIndices(),a=undefined},_drawChildren:function(){var a=this.children;for(var b=0;b<a.length;b++){var c=a[b];c.className==="Shape"?c._draw(c.getLayer()):c._draw()}},_add:function(a){a.name&&(this.childrenNames[a.name]=a),a.id=Kinetic.GlobalObject.idCounter++,a.index=this.children.length,a.parent=this,this.children.push(a)},_setChildrenIndices:function(){if(this.className==="Stage"){var a=this.container.childNodes,b=a[0],c=a[1];this.container.innerHTML="",this.container.appendChild(b),this.container.appendChild(c)}for(var d=0;d<this.children.length;d++)this.children[d].index=d,this.className==="Stage"&&this.container.appendChild(this.children[d].canvas)}},Kinetic.Stage=function(a,b,c){this.className="Stage",this.container=typeof a=="string"?document.getElementById(a):a,this.width=b,this.height=c,this.scale={x:1,y:1},this.dblClickWindow=400,this.targetShape=undefined,this.clickStart=!1,this.mousePos=undefined,this.mouseDown=!1,this.mouseUp=!1,this.touchPos=undefined,this.touchStart=!1,this.touchEnd=!1,this.bufferLayer=new Kinetic.Layer,this.backstageLayer=new Kinetic.Layer,this.bufferLayer.parent=this,this.backstageLayer.parent=this;var d=this.backstageLayer;this._stripLayer(d),this.bufferLayer.getCanvas().style.display="none",this.backstageLayer.getCanvas().style.display="none",this.bufferLayer.canvas.width=this.width,this.bufferLayer.canvas.height=this.height,this.container.appendChild(this.bufferLayer.canvas),this.backstageLayer.canvas.width=this.width,this.backstageLayer.canvas.height=this.height,this.container.appendChild(this.backstageLayer.canvas),this._listen(),this._prepareDrag();var e=Kinetic.GlobalObject.stages;e.push(this),this.id=Kinetic.GlobalObject.idCounter++,this.isAnimating=!1,this.onFrameFunc=undefined,Kinetic.Container.apply(this,[])},Kinetic.Stage.prototype={onFrame:function(a){this.onFrameFunc=a},start:function(){this.isAnimating=!0,Kinetic.GlobalObject._handleAnimation()},stop:function(){this.isAnimating=!1,Kinetic.GlobalObject._handleAnimation()},draw:function(){this._drawChildren()},setSize:function(a,b){var c=this.children;for(var d=0;d<c.length;d++){var e=c[d];e.getCanvas().width=a,e.getCanvas().height=b,e.draw()}this.width=a,this.height=b,this.bufferLayer.getCanvas().width=a,this.bufferLayer.getCanvas().height=b,this.backstageLayer.getCanvas().width=a,this.backstageLayer.getCanvas().height=b},setScale:function(a,b){function g(a){for(var b=0;b<a.length;b++){var e=a[b];e.x*=f.scale.x/c,e.y*=f.scale.y/d,e.children&&g(e.children)}}var c=this.scale.x,d=this.scale.y;b?(this.scale.x=a,this.scale.y=b):(this.scale.x=a,this.scale.y=a);var e=this.children,f=this;g(e)},getScale:function(){return this.scale},clear:function(){var a=this.children;for(var b=0;b<a.length;b++)a[b].clear()},toDataURL:function(a){function e(f){var g=d[f].getCanvas().toDataURL(),h=new Image;h.onload=function(){c.drawImage(this,0,0),f++,f<d.length?e(f):a(b.getCanvas().toDataURL())},h.src=g}var b=this.bufferLayer,c=b.getContext(),d=this.children;b.clear(),e(0)},remove:function(a){this.container.removeChild(a.canvas),this._remove(a)},on:function(a,b){var c=a.split(" ");for(var d=0;d<c.length;d++){var e=c[d];this.container.addEventListener(e,b,!1)}},add:function(a){a.name&&(this.childrenNames[a.name]=a),a.canvas.width=this.width,a.canvas.height=this.height,this._add(a),a.draw(),this.container.appendChild(a.canvas)},getMousePosition:function(a){return this.mousePos},getTouchPosition:function(a){return this.touchPos},getUserPosition:function(a){return this.getTouchPosition()||this.getMousePosition()},getContainer:function(){return this.container},getStage:function(){return this},_handleEvent:function(a){function g(g){g._draw(c);var h=e.getUserPosition(),i=g.eventListeners;e.targetShape&&g.id===e.targetShape.id&&(f=!0);if(g.visible&&h!==undefined&&d.isPointInPath(h.x,h.y)){if(e.mouseDown)return e.mouseDown=!1,e.clickStart=!0,g._handleEvents("onmousedown",a),!0;if(e.mouseUp)return e.mouseUp=!1,g._handleEvents("onmouseup",a),e.clickStart&&(!b.drag.moving||!b.drag.node)&&(g._handleEvents("onclick",a),g.inDoubleClickWindow&&g._handleEvents("ondblclick",a),g.inDoubleClickWindow=!0,setTimeout(function(){g.inDoubleClickWindow=!1},e.dblClickWindow)),!0;if(e.touchStart){e.touchStart=!1,g._handleEvents("touchstart",a);if(i.ondbltap&&g.inDoubleClickWindow){var j=i.ondbltap;for(var k=0;k<j.length;k++)j[k].handler.apply(g,[a])}return g.inDoubleClickWindow=!0,setTimeout(function(){g.inDoubleClickWindow=!1},e.dblClickWindow),!0}if(e.touchEnd)return e.touchEnd=!1,g._handleEvents("touchend",a),!0;if(i.touchmove)return g._handleEvents("touchmove",a),!0;if(!e.targetShape||!f&&g.id!==e.targetShape.id){if(e.targetShape){var l=e.targetShape.eventListeners;l&&e.targetShape._handleEvents("onmouseout",a)}return e.targetShape=g,g._handleEvents("onmouseover",a),!0}return g._handleEvents("onmousemove",a),!0}return e.targetShape&&e.targetShape.id===g.id?(e.targetShape=undefined,g._handleEvents("onmouseout",a),!0):!1}function h(a){var b=a.children;for(var c=b.length-1;c>=0;c--){var d=b[c];if(d.className==="Shape"){var e=g(d);if(e)return!0}else h(d)}return!1}var b=Kinetic.GlobalObject;a||(a=window.event),this._setMousePosition(a),this._setTouchPosition(a);var c=this.backstageLayer,d=c.getContext(),e=this;c.clear();var f=!1;if(b.drag.node===undefined)for(var i=this.children.length-1;i>=0;i--){var j=this.children[i];j.visible&&i>=0&&j.isListening&&h(j)&&(i=-1)}},_listen:function(){var a=this;this.container.addEventListener("mousedown",function(b){a.mouseDown=!0,a._handleEvent(b)},!1),this.container.addEventListener("mousemove",function(b){a.mouseUp=!1,a.mouseDown=!1,a._handleEvent(b)},!1),this.container.addEventListener("mouseup",function(b){a.mouseUp=!0,a.mouseDown=!1,a._handleEvent(b),a.clickStart=!1},!1),this.container.addEventListener("mouseover",function(b){a._handleEvent(b)},!1),this.container.addEventListener("mouseout",function(b){a.mousePos=undefined},!1),this.container.addEventListener("touchstart",function(b){b.preventDefault(),a.touchStart=!0,a._handleEvent(b)},!1),this.container.addEventListener("touchmove",function(b){b.preventDefault(),a._handleEvent(b)},!1),this.container.addEventListener("touchend",function(b){b.preventDefault(),a.touchEnd=!0,a._handleEvent(b)},!1)},_setMousePosition:function(a){var b=a.clientX-this._getContainerPosition().left+window.pageXOffset,c=a.clientY-this._getContainerPosition().top+window.pageYOffset;this.mousePos={x:b,y:c}},_setTouchPosition:function(a){if(a.touches!==undefined&&a.touches.length===1){var b=a.touches[0],c=b.clientX-this._getContainerPosition().left+window.pageXOffset,d=b.clientY-this._getContainerPosition().top+window.pageYOffset;this.touchPos={x:c,y:d}}},_getContainerPosition:function(){var a=this.container,b=0,c=0;while(a&&a.tagName!=="BODY")b+=a.offsetTop,c+=a.offsetLeft,a=a.offsetParent;return{top:b,left:c}},_stripLayer:function(a){a.context.stroke=function(){},a.context.fill=function(){},a.context.fillRect=function(b,c,d,e){a.context.rect(b,c,d,e)},a.context.strokeRect=function(b,c,d,e){a.context.rect(b,c,d,e)},a.context.drawImage=function(){},a.context.fillText=function(){},a.context.strokeText=function(){}},_endDrag:function(a){var b=Kinetic.GlobalObject;b.drag.node&&b.drag.moving&&(b.drag.moving=!1,b.drag.node._handleEvents("ondragend",a)),b.drag.node=undefined},_prepareDrag:function(){var a=this;this.on("mousemove touchmove",function(b){var c=Kinetic.GlobalObject;if(c.drag.node){var d=a.getUserPosition();c.drag.node.drag.x&&(c.drag.node.x=d.x-c.drag.offset.x),c.drag.node.drag.y&&(c.drag.node.y=d.y-c.drag.offset.y),c.drag.node.getLayer().draw(),c.drag.moving||(c.drag.moving=!0,c.drag.node._handleEvents("ondragstart",b)),c.drag.node._handleEvents("ondragmove",b)}},!1),this.on("mouseup touchend mouseout",function(b){a._endDrag(b)})}},Kinetic.GlobalObject.extend(Kinetic.Stage,Kinetic.Container),Kinetic.Layer=function(a){this.className="Layer",this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),this.canvas.style.position="absolute",Kinetic.Container.apply(this,[]),Kinetic.Node.apply(this,[a])},Kinetic.Layer.prototype={draw:function(){this._draw()},clear:function(){var a=this.getContext(),b=this.getCanvas();a.clearRect(0,0,b.width,b.height)},getCanvas:function(){return this.canvas},getContext:function(){return this.context},add:function(a){this._add(a)},remove:function(a){this._remove(a)},_draw:function(){this.clear(),this.visible&&this._drawChildren()}},Kinetic.GlobalObject.extend(Kinetic.Layer,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Layer,Kinetic.Node),Kinetic.Group=function(a){this.className="Group",Kinetic.Container.apply(this,[]),Kinetic.Node.apply(this,[a])},Kinetic.Group.prototype={add:function(a){this._add(a)},remove:function(a){this._remove(a)},_draw:function(){this.visible&&this._drawChildren()}},Kinetic.GlobalObject.extend(Kinetic.Group,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Group,Kinetic.Node),Kinetic.Shape=function(a){this.className="Shape";if(a.stroke!==undefined||a.strokeWidth!==undefined)a.stroke===undefined?a.stroke="black":a.strokeWidth===undefined&&(a.strokeWidth=2);this.drawFunc=a.drawFunc,Kinetic.Node.apply(this,[a])},Kinetic.Shape.prototype={getContext:function(){return this.tempLayer.getContext()},getCanvas:function(){return this.tempLayer.getCanvas()},fillStroke:function(){var a=this.getContext();this.fill!==undefined&&(a.fillStyle=this.fill,a.fill()),this.stroke!==undefined&&(a.lineWidth=this.strokeWidth===undefined?1:this.strokeWidth,a.strokeStyle=this.stroke,a.stroke())},setFill:function(a){this.fill=a},getFill:function(){return this.fill},setStroke:function(a){this.stroke=a},getStroke:function(){return this.stroke},setStrokeWidth:function(a){this.strokeWidth=a},getStrokeWidth:function(){return this.strokeWidth},_draw:function(a){if(this.visible){var b=a.getStage(),c=a.getContext(),d=[];d.unshift(this);var e=this.parent;while(e.className!=="Stage")d.unshift(e),e=e.parent;for(var f=0;f<d.length;f++){var g=d[f];c.save(),(g.x!==0||g.y!==0)&&c.translate(g.x,g.y),(g.centerOffset.x!==0||g.centerOffset.y!==0)&&c.translate(g.centerOffset.x,g.centerOffset.y),g.rotation!==0&&c.rotate(g.rotation),(g.scale.x!==1||g.scale.y!==1)&&c.scale(g.scale.x,g.scale.y),(g.centerOffset.x!==0||g.centerOffset.y!==0)&&c.translate(-1*g.centerOffset.x,-1*g.centerOffset.y),g.getAbsoluteAlpha()!==1&&(c.globalAlpha=g.getAbsoluteAlpha())}c.save(),b&&(b.scale.x!==1||b.scale.y!==1)&&c.scale(b.scale.x,b.scale.y),this.tempLayer=a,this.drawFunc.call(this);for(var h=0;h<d.length;h++)c.restore();c.restore()}}},Kinetic.GlobalObject.extend(Kinetic.Shape,Kinetic.Node),Kinetic.Rect=function(a){a.drawFunc=function(){var a=this.getCanvas(),b=this.getContext();b.beginPath(),b.rect(0,0,this.width,this.height),b.closePath(),this.fillStroke()},Kinetic.Shape.apply(this,[a])},Kinetic.Rect.prototype={setWidth:function(a){this.width=a},getWidth:function(){return this.width},setHeight:function(a){this.height=a},getHeight:function(){return this.height},setSize:function(a,b){this.width=a,this.height=b}},Kinetic.GlobalObject.extend(Kinetic.Rect,Kinetic.Shape),Kinetic.Circle=function(a){a.drawFunc=function(){var a=this.getCanvas(),b=this.getContext();b.beginPath(),b.arc(0,0,this.radius,0,Math.PI*2,!0),b.closePath(),this.fillStroke()},Kinetic.Shape.apply(this,[a])},Kinetic.Circle.prototype={setRadius:function(a){this.radius=a},getRadius:function(){return this.radius}},Kinetic.GlobalObject.extend(Kinetic.Circle,Kinetic.Shape),Kinetic.Image=function(a){a.width===undefined&&(a.width=a.image.width),a.height===undefined&&(a.height=a.image.height),a.drawFunc=function(){var a=this.getCanvas(),b=this.getContext();b.beginPath(),b.rect(0,0,this.width,this.height),b.closePath(),this.fillStroke(),b.drawImage(this.image,0,0,this.width,this.height)},Kinetic.Shape.apply(this,[a])},Kinetic.Image.prototype={setImage:function(a){this.image=a},getImage:function(a){return this.image},setWidth:function(a){this.width=a},getWidth:function(){return this.width},setHeight:function(a){this.height=a},getHeight:function(){return this.height},setSize:function(a,b){this.width=a,this.height=b}},Kinetic.GlobalObject.extend(Kinetic.Image,Kinetic.Shape),Kinetic.Polygon=function(a){a.drawFunc=function(){var a=this.getContext();a.beginPath(),a.moveTo(this.points[0].x,this.points[0].y);for(var b=1;b<this.points.length;b++)a.lineTo(this.points[b].x,this.points[b].y);a.closePath(),this.fillStroke()},Kinetic.Shape.apply(this,[a])},Kinetic.Polygon.prototype={setPoints:function(a){this.points=a},getPoints:function(){return this.points}},Kinetic.GlobalObject.extend(Kinetic.Polygon,Kinetic.Shape),Kinetic.RegularPolygon=function(a){a.drawFunc=function(){var a=this.getContext();a.beginPath(),a.moveTo(0,0-this.radius);for(var b=1;b<this.sides;b++){var c=this.radius*Math.sin(b*2*Math.PI/this.sides),d=-1*this.radius*Math.cos(b*2*Math.PI/this.sides);a.lineTo(c,d)}a.closePath(),this.fillStroke()},Kinetic.Shape.apply(this,[a])},Kinetic.RegularPolygon.prototype={setPoints:function(a){this.points=a},getPoints:function(){return this.points},setRadius:function(a){this.radius=a},getRadius:function(){return this.radius},setSides:function(a){this.sides=a},getSides:function(){return this.sides}},Kinetic.GlobalObject.extend(Kinetic.RegularPolygon,Kinetic.Shape),Kinetic.Star=function(a){a.drawFunc=function(){var a=this.getContext();a.beginPath(),a.moveTo(0,0-this.outerRadius);for(var b=1;b<this.points*2;b++){var c=b%2===0?this.outerRadius:this.innerRadius,d=c*Math.sin(b*Math.PI/this.points),e=-1*c*Math.cos(b*Math.PI/this.points);a.lineTo(d,e)}a.closePath(),this.fillStroke()},Kinetic.Shape.apply(this,[a])},Kinetic.Star.prototype={setPoints:function(a){this.points=a},getPoints:function(){return this.points},setOuterRadius:function(a){this.outerRadius=a},getOuterRadius:function(){return this.outerRadius},setInnerRadius:function(a){this.innerRadius=a},getInnerRadius:function(){return this.innerRadius}},Kinetic.GlobalObject.extend(Kinetic.Star,Kinetic.Shape),Kinetic.Text=function(a){if(a.textStroke!==undefined||a.textStrokeWidth!==undefined)a.textStroke===undefined?a.textStroke="black":a.textStrokeWidth===undefined&&(a.textStrokeWidth=2);a.align===undefined&&(a.align="left"),a.verticalAlign===undefined&&(a.verticalAlign="top"),a.padding===undefined&&(a.padding=0),a.drawFunc=function(){var a=this.getCanvas(),b=this.getContext();b.font=this.fontSize+"pt "+this.fontFamily,b.textBaseline="middle";var c=b.measureText(this.text),d=this.fontSize,e=c.width,f=this.padding,g=0,h=0;switch(this.align){case"center":g=e/-2-f;break;case"right":g=-1*e-f}switch(this.verticalAlign){case"middle":h=d/-2-f;break;case"bottom":h=-1*d-f}b.save(),b.beginPath(),b.rect(g,h,e+f*2,d+f*2),b.closePath(),this.fillStroke(),b.restore();var i=f+g,j=d/2+f+h;this.textFill!==undefined&&(b.fillStyle=this.textFill,b.fillText(this.text,i,j));if(this.textStroke!==undefined||this.textStrokeWidth!==undefined)this.textStroke===undefined?this.textStroke="black":this.textStrokeWidth===undefined&&(this.textStrokeWidth=2),b.lineWidth=this.textStrokeWidth,b.strokeStyle=this.textStroke,b.strokeText(this.text,i,j)},Kinetic.Shape.apply(this,[a])},Kinetic.Text.prototype={setFontFamily:function(a){this.fontFamily=a},getFontFamily:function(){return this.fontFamily},setFontSize:function(a){this.fontSize=a},getFontSize:function(){return this.fontSize},setTextFill:function(a){this.textFill=a},getTextFill:function(){return this.textFill},setTextStroke:function(a){this.textStroke=a},getTextStroke:function(){return this.textStroke},setTextStrokeWidth:function(a){this.textStrokeWidth=a},getTextStrokeWidth:function(){return this.textStrokeWidth},setPadding:function(a){this.padding=a},getPadding:function(){return this.padding},setAlign:function(a){this.align=a},getAlign:function(){return this.align},setVerticalAlign:function(a){this.verticalAlign=a},getVerticalAlign:function(){return this.verticalAlign},setText:function(a){this.text=a},getText:function(){return this.text}},Kinetic.GlobalObject.extend(Kinetic.Text,Kinetic.Shape);
View
21 license.js
@@ -24,23 +24,4 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
- */
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ */
Please sign in to comment.
Something went wrong with that request. Please try again.