social_cheesecake 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
1
  /**
2
- * KineticJS JavaScript Library v3.6.2
2
+ * KineticJS JavaScript Library v3.8.1
3
3
  * http://www.kineticjs.com/
4
4
  * Copyright 2012, Eric Rowell
5
5
  * Licensed under the MIT or GPL Version 2 licenses.
6
- * Date: Jan 21 2012
6
+ * Date: Feb 26 2012
7
7
  *
8
- * Copyright (C) 2012 by Eric Rowell
8
+ * Copyright (C) 2011 - 2012 by Eric Rowell
9
9
  *
10
10
  * Permission is hereby granted, free of charge, to any person obtaining a copy
11
11
  * of this software and associated documentation files (the "Software"), to deal
@@ -25,1241 +25,1870 @@
25
25
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
26
  * THE SOFTWARE.
27
27
  */
28
- var Kinetic = {};
29
-
30
- /*
31
- * I know, globals suck. But since Shape objects
32
- * and Layer objects can exist before adding them to
33
- * the stage, a global shape id counter is necessary
34
- */
35
- Kinetic.GLOBALS = {
36
- shapeIdCounter: 0
37
- };
38
-
39
28
  ///////////////////////////////////////////////////////////////////////
40
- //// Link
29
+ // Global Object
41
30
  ///////////////////////////////////////////////////////////////////////
42
- ///////////////////////////////////////////////////////////////////////
43
-
44
- Kinetic.Link = function(shape){
45
- this.shape = shape;
46
- shape.link = this;
47
- this.id = shape.id;
48
- this.index = undefined;
49
-
50
- // thes params are string ids
51
- this.nextId = undefined;
52
- this.prevId = undefined;
31
+ var Kinetic = {};
32
+ Kinetic.GlobalObject = {
33
+ stages: [],
34
+ idCounter: 0,
35
+ isAnimating: false,
36
+ frame: {
37
+ time: 0,
38
+ timeDiff: 0,
39
+ lastTime: 0
40
+ },
41
+ extend: function(obj1, obj2){
42
+ for (var key in obj2.prototype) {
43
+ if (obj2.prototype.hasOwnProperty(key)) {
44
+ obj1.prototype[key] = obj2.prototype[key];
45
+ }
46
+ }
47
+ },
48
+ /*
49
+ * animation methods
50
+ */
51
+ _isaCanvasAnimating: function(){
52
+ for (var n = 0; n < this.stages.length; n++) {
53
+ if (this.stages[n].isAnimating) {
54
+ return true;
55
+ }
56
+ }
57
+ return false;
58
+ },
59
+ _runFrames: function(){
60
+ for (var n = 0; n < this.stages.length; n++) {
61
+ if (this.stages[n].isAnimating) {
62
+ this.stages[n].onFrameFunc(this.frame);
63
+ }
64
+ }
65
+ },
66
+ _updateFrameObject: function(){
67
+ var date = new Date();
68
+ var time = date.getTime();
69
+ if (this.frame.lastTime === 0) {
70
+ this.frame.lastTime = time;
71
+ }
72
+ else {
73
+ this.frame.timeDiff = time - this.frame.lastTime;
74
+ this.frame.lastTime = time;
75
+ this.frame.time += this.frame.timeDiff;
76
+ }
77
+ },
78
+ _animationLoop: function(){
79
+ if (this.isAnimating) {
80
+ this._updateFrameObject();
81
+ this._runFrames();
82
+ var that = this;
83
+ requestAnimFrame(function(){
84
+ that._animationLoop();
85
+ });
86
+ }
87
+ },
88
+ _handleAnimation: function(){
89
+ var that = this;
90
+ if (!this.isAnimating && this._isaCanvasAnimating()) {
91
+ this.isAnimating = true;
92
+ that._animationLoop();
93
+ }
94
+ else if (this.isAnimating && !this._isaCanvasAnimating()) {
95
+ this.isAnimating = false;
96
+ }
97
+ }
53
98
  };
54
99
 
100
+ /**
101
+ * requestAnimFrame shim
102
+ * @param {function} callback
103
+ */
104
+ window.requestAnimFrame = (function(callback){
105
+ return window.requestAnimationFrame ||
106
+ window.webkitRequestAnimationFrame ||
107
+ window.mozRequestAnimationFrame ||
108
+ window.oRequestAnimationFrame ||
109
+ window.msRequestAnimationFrame ||
110
+ function(callback){
111
+ window.setTimeout(callback, 1000 / 60);
112
+ };
113
+ })();
114
+
55
115
  ///////////////////////////////////////////////////////////////////////
56
- //// Layer
57
- ///////////////////////////////////////////////////////////////////////
116
+ // Node
58
117
  ///////////////////////////////////////////////////////////////////////
59
-
60
- Kinetic.Layer = function(name){
61
- this.name = name;
62
- this.shapeIndexCounter = 0;
118
+ /**
119
+ * Node constructor. Node is a base class for the
120
+ * Layer, Group, and Shape classes
121
+ * @param {Object} name
122
+ */
123
+ Kinetic.Node = function(config){
124
+ this.visible = true;
63
125
  this.isListening = true;
64
- this.shapeNames = {};
65
- this.canvas = document.createElement('canvas');
66
- this.context = this.canvas.getContext('2d');
67
- this.canvas.style.position = 'absolute';
68
-
69
- //links is an array of links which point to event links
70
- this.links = [];
71
- this.linkHash = {};
126
+ this.name = undefined;
127
+ this.alpha = 1;
128
+ this.x = 0;
129
+ this.y = 0;
130
+ this.scale = {
131
+ x: 1,
132
+ y: 1
133
+ };
134
+ this.rotation = 0;
135
+ this.centerOffset = {
136
+ x: 0,
137
+ y: 0
138
+ };
139
+ this.eventListeners = {};
140
+ this.drag = {
141
+ x: false,
142
+ y: false,
143
+ moving: false
144
+ };
72
145
 
73
- this.headId = undefined;
74
- this.tailId = undefined;
75
- };
76
- /*
77
- * listen or don't listen to events
78
- */
79
- Kinetic.Layer.prototype.listen = function(isListening){
80
- this.isListening = isListening;
81
- };
82
- /*
83
- * clear layer
84
- */
85
- Kinetic.Layer.prototype.clear = function(){
86
- var context = this.getContext();
87
- var canvas = this.getCanvas();
88
- context.clearRect(0, 0, canvas.width, canvas.height);
89
- };
90
- /*
91
- * get layer canvas
92
- */
93
- Kinetic.Layer.prototype.getCanvas = function(){
94
- return this.canvas;
95
- }
96
- /*
97
- * get layer context
98
- */
99
- Kinetic.Layer.prototype.getContext = function(){
100
- return this.context;
101
- }
102
- /*
103
- * get shapes as an array
104
- */
105
- Kinetic.Layer.prototype.getShapes = function(){
106
- var shapes = [];
107
- for (var n = 0; n < this.links.length; n++) {
108
- shapes.push(this.links[n].shape);
109
- }
110
- return shapes;
111
- };
112
- /*
113
- * draw all shapes in layer
114
- */
115
- Kinetic.Layer.prototype.draw = function(){
116
- this.clear();
117
- var links = this.links;
118
- for (var n = 0; n < links.length; n++) {
119
- var shape = links[n].shape;
120
- shape.draw(shape.layer);
146
+ // set properties from config
147
+ if (config) {
148
+ for (var key in config) {
149
+ // handle special keys
150
+ if (key === "draggable") {
151
+ this.draggable(config[key]);
152
+ }
153
+ else if (key === "draggableX") {
154
+ this.draggableX(config[key]);
155
+ }
156
+ else if (key === "draggableY") {
157
+ this.draggableY(config[key]);
158
+ }
159
+ else if (key === "listen") {
160
+ this.listen(config[key]);
161
+ }
162
+ else {
163
+ this[key] = config[key];
164
+ }
165
+ }
121
166
  }
122
167
  };
123
- /*
124
- * add link to data structure
125
- */
126
- Kinetic.Layer.prototype.addLink = function(link){
127
- var shape = link.shape;
128
- shape.layer = this;
129
- // add link to array
130
- this.links.push(link);
131
- // add link to hash
132
- this.linkHash[link.id] = link;
133
- link.index = this.links.length - 1;
134
-
135
- if (shape.isListening) {
136
- // if tail doesnt exist, add tail and head
137
- if (this.tailId === undefined) {
138
- this.tailId = link.id;
139
- this.headId = link.id;
168
+
169
+ Kinetic.Node.prototype = {
170
+ /**
171
+ * bind event to node
172
+ * @param {String} typesStr
173
+ * @param {function} handler
174
+ */
175
+ on: function(typesStr, handler){
176
+ var types = typesStr.split(" ");
177
+ /*
178
+ * loop through types and attach event listeners to
179
+ * each one. eg. "click mouseover.namespace mouseout"
180
+ * will create three event bindings
181
+ */
182
+ for (var n = 0; n < types.length; n++) {
183
+ var type = types[n];
184
+ var event = (type.indexOf('touch') === -1) ? 'on' + type : type;
185
+ var parts = event.split(".");
186
+ var baseEvent = parts[0];
187
+ var name = parts.length > 1 ? parts[1] : "";
188
+
189
+ if (!this.eventListeners[baseEvent]) {
190
+ this.eventListeners[baseEvent] = [];
191
+ }
192
+
193
+ this.eventListeners[baseEvent].push({
194
+ name: name,
195
+ handler: handler
196
+ });
197
+ }
198
+ },
199
+ /**
200
+ * unbind event to node
201
+ * @param {String} typesStr
202
+ */
203
+ off: function(typesStr){
204
+ var types = typesStr.split(" ");
205
+
206
+ for (var n = 0; n < types.length; n++) {
207
+ var type = types[n];
208
+ var event = (type.indexOf('touch') === -1) ? 'on' + type : type;
209
+ var parts = event.split(".");
210
+ var baseEvent = parts[0];
211
+
212
+ if (this.eventListeners[baseEvent] && parts.length > 1) {
213
+ var name = parts[1];
214
+
215
+ for (var i = 0; i < this.eventListeners[baseEvent].length; i++) {
216
+ if (this.eventListeners[baseEvent][i].name === name) {
217
+ this.eventListeners[baseEvent].splice(i, 1);
218
+ if (this.eventListeners[baseEvent].length === 0) {
219
+ this.eventListeners[baseEvent] = undefined;
220
+ }
221
+ break;
222
+ }
223
+ }
224
+ }
225
+ else {
226
+ this.eventListeners[baseEvent] = undefined;
227
+ }
228
+ }
229
+ },
230
+ /**
231
+ * show node
232
+ */
233
+ show: function(){
234
+ this.visible = true;
235
+ },
236
+ /**
237
+ * hide node
238
+ */
239
+ hide: function(){
240
+ this.visible = false;
241
+ },
242
+ /**
243
+ * get zIndex
244
+ */
245
+ getZIndex: function(){
246
+ return this.index;
247
+ },
248
+ /**
249
+ * set node scale
250
+ * @param {number} scaleX
251
+ * @param {number} scaleY
252
+ */
253
+ setScale: function(scaleX, scaleY){
254
+ if (scaleY) {
255
+ this.scale.x = scaleX;
256
+ this.scale.y = scaleY;
140
257
  }
141
- // if tail does exist, this means there's at least one link
142
258
  else {
143
- var tail = this.linkHash[this.tailId];
144
- tail.nextId = link.id;
145
- link.prevId = tail.id;
146
- this.tailId = link.id;
259
+ this.scale.x = scaleX;
260
+ this.scale.y = scaleX;
147
261
  }
262
+ },
263
+ /**
264
+ * get scale
265
+ */
266
+ getScale: function(){
267
+ return this.scale;
268
+ },
269
+ /**
270
+ * set node position
271
+ * @param {number} x
272
+ * @param {number} y
273
+ */
274
+ setPosition: function(x, y){
275
+ this.x = x;
276
+ this.y = y;
277
+ },
278
+ /**
279
+ * get node position relative to container
280
+ */
281
+ getPosition: function(){
282
+ return {
283
+ x: this.x,
284
+ y: this.y
285
+ };
286
+ },
287
+ /**
288
+ * get absolute position relative to stage
289
+ */
290
+ getAbsolutePosition: function(){
291
+ var x = this.x;
292
+ var y = this.y;
293
+ var parent = this.getParent();
294
+ while (parent.className !== "Stage") {
295
+ x += parent.x;
296
+ y += parent.y;
297
+ parent = parent.parent;
298
+ }
299
+ return {
300
+ x: x,
301
+ y: y
302
+ };
303
+ },
304
+ /**
305
+ * move node
306
+ * @param {number} x
307
+ * @param {number} y
308
+ */
309
+ move: function(x, y){
310
+ this.x += x;
311
+ this.y += y;
312
+ },
313
+ /**
314
+ * set node rotation
315
+ * @param {number} theta
316
+ */
317
+ setRotation: function(theta){
318
+ this.rotation = theta;
319
+ },
320
+ /**
321
+ * get rotation
322
+ */
323
+ getRotation: function(){
324
+ return this.rotation;
325
+ },
326
+ /**
327
+ * rotate node
328
+ * @param {number} theta
329
+ */
330
+ rotate: function(theta){
331
+ this.rotation += theta;
332
+ },
333
+ /**
334
+ * listen or don't listen to events
335
+ * @param {boolean} isListening
336
+ */
337
+ listen: function(isListening){
338
+ this.isListening = isListening;
339
+ },
340
+ /**
341
+ * move node to top
342
+ */
343
+ moveToTop: function(){
344
+ var index = this.index;
345
+ this.parent.children.splice(index, 1);
346
+ this.parent.children.push(this);
347
+ this.parent._setChildrenIndices();
348
+ },
349
+ /**
350
+ * move node up
351
+ */
352
+ moveUp: function(){
353
+ var index = this.index;
354
+ this.parent.children.splice(index, 1);
355
+ this.parent.children.splice(index + 1, 0, this);
356
+ this.parent._setChildrenIndices();
357
+ },
358
+ /**
359
+ * move node down
360
+ */
361
+ moveDown: function(){
362
+ var index = this.index;
363
+ if (index > 0) {
364
+ this.parent.children.splice(index, 1);
365
+ this.parent.children.splice(index - 1, 0, this);
366
+ this.parent._setChildrenIndices();
367
+ }
368
+ },
369
+ /**
370
+ * move node to bottom
371
+ */
372
+ moveToBottom: function(){
373
+ var index = this.index;
374
+ this.parent.children.splice(index, 1);
375
+ this.parent.children.unshift(this);
376
+ this.parent._setChildrenIndices();
377
+ },
378
+ /**
379
+ * set zIndex
380
+ * @param {int} index
381
+ */
382
+ setZIndex: function(zIndex){
383
+ var index = this.index;
384
+ this.parent.children.splice(index, 1);
385
+ this.parent.children.splice(zIndex, 0, this);
386
+ this.parent._setChildrenIndices();
387
+ },
388
+ /**
389
+ * set alpha
390
+ * @param {Object} alpha
391
+ */
392
+ setAlpha: function(alpha){
393
+ this.alpha = alpha;
394
+ },
395
+ /**
396
+ * get alpha
397
+ */
398
+ getAlpha: function(){
399
+ return this.alpha;
400
+ },
401
+ /**
402
+ * get absolute alpha
403
+ */
404
+ getAbsoluteAlpha: function(){
405
+ var absAlpha = 1;
406
+ var node = this;
407
+ // traverse upwards
408
+ while (node.className !== "Stage") {
409
+ absAlpha *= node.alpha;
410
+ node = node.parent;
411
+ }
412
+ return absAlpha;
413
+ },
414
+ /**
415
+ * initialize drag and drop
416
+ */
417
+ _initDrag: function(){
418
+ var that = this;
419
+ this.on("mousedown.initdrag touchstart.initdrag", function(evt){
420
+ var stage = that.getStage();
421
+ var pos = stage.getUserPosition();
422
+
423
+ if (pos) {
424
+ stage.nodeDragging = that;
425
+ stage.nodeDragging.offset = {};
426
+ stage.nodeDragging.offset.x = pos.x - that.x;
427
+ stage.nodeDragging.offset.y = pos.y - that.y;
428
+ }
429
+ });
430
+ },
431
+ /**
432
+ * remove drag and drop event listener
433
+ */
434
+ _dragCleanup: function(){
435
+ if (!this.drag.x && !this.drag.y) {
436
+ this.off("mousedown.initdrag");
437
+ this.off("touchstart.initdrag");
438
+ }
439
+ },
440
+ /**
441
+ * enable/disable drag and drop for box x and y direction
442
+ * @param {boolean} setDraggable
443
+ */
444
+ draggable: function(setDraggable){
445
+ if (setDraggable) {
446
+ var needInit = !this.drag.x && !this.drag.y;
447
+ this.drag.x = true;
448
+ this.drag.y = true;
449
+
450
+ if (needInit) {
451
+ this._initDrag();
452
+ }
453
+ }
454
+ else {
455
+ this.drag.x = false;
456
+ this.drag.y = false;
457
+ this._dragCleanup();
458
+ }
459
+ },
460
+ /**
461
+ * enable/disable drag and drop for x only
462
+ * @param {boolean} setDraggable
463
+ */
464
+ draggableX: function(setDraggable){
465
+ if (setDraggable) {
466
+ var needInit = !this.drag.x && !this.drag.y;
467
+ this.drag.x = true;
468
+ if (needInit) {
469
+ this._initDrag();
470
+ }
471
+ }
472
+ else {
473
+ this.drag.x = false;
474
+ this._dragCleanup();
475
+ }
476
+ },
477
+ /**
478
+ * enable/disable drag and drop for y only
479
+ * @param {boolean} setDraggable
480
+ */
481
+ draggableY: function(setDraggable){
482
+ if (setDraggable) {
483
+ var needInit = !this.drag.x && !this.drag.y;
484
+ this.drag.y = true;
485
+ if (needInit) {
486
+ this._initDrag();
487
+ }
488
+ }
489
+ else {
490
+ this.drag.y = false;
491
+ this._dragCleanup();
492
+ }
493
+ },
494
+ /**
495
+ * determine if node is currently in drag mode
496
+ */
497
+ isDragging: function(){
498
+ return this.drag.moving;
499
+ },
500
+ /**
501
+ * handle node events
502
+ * @param {string} eventType
503
+ * @param {Event} evt
504
+ */
505
+ _handleEvents: function(eventType, evt){
506
+ // generic events handler
507
+ function handle(obj){
508
+ var el = obj.eventListeners;
509
+ if (el[eventType]) {
510
+ var events = el[eventType];
511
+ for (var i = 0; i < events.length; i++) {
512
+ events[i].handler.apply(obj, [evt]);
513
+ }
514
+ }
515
+
516
+ if (obj.parent.className !== "Stage") {
517
+ handle(obj.parent);
518
+ }
519
+ }
520
+ /*
521
+ * simulate bubbling by handling node events
522
+ * first, followed by group events, followed
523
+ * by layer events
524
+ */
525
+ handle(this);
526
+ },
527
+ /**
528
+ * move node to another container
529
+ * @param {Layer} newLayer
530
+ */
531
+ moveTo: function(newContainer){
532
+ var parent = this.parent;
533
+ // remove from parent's children
534
+ parent.children.splice(this.index, 1);
535
+ parent._setChildrenIndices();
536
+
537
+ // add to new parent
538
+ newContainer.children.push(this);
539
+ this.index = newContainer.children.length - 1;
540
+ this.parent = newContainer;
541
+ newContainer._setChildrenIndices();
542
+
543
+ // update children hashes
544
+ if (this.name) {
545
+ parent.childrenNames[this.name] = undefined;
546
+ newContainer.childrenNames[this.name] = this;
547
+ }
548
+ },
549
+ /**
550
+ * get parent
551
+ */
552
+ getParent: function(){
553
+ return this.parent;
554
+ },
555
+ /**
556
+ * get node's layer
557
+ */
558
+ getLayer: function(){
559
+ if (this.className === 'Layer') {
560
+ return this;
561
+ }
562
+ else {
563
+ return this.getParent().getLayer();
564
+ }
565
+ },
566
+ /**
567
+ * get stage
568
+ */
569
+ getStage: function(){
570
+ return this.getParent().getStage();
571
+ },
572
+ /**
573
+ * get name
574
+ */
575
+ getName: function(){
576
+ return this.name;
148
577
  }
149
578
  };
150
- /*
151
- * add shape
152
- */
153
- Kinetic.Layer.prototype.add = function(shape){
154
- if (shape.name) {
155
- this.shapeNames[shape.name] = shape;
156
- }
157
- shape.id = Kinetic.GLOBALS.shapeIdCounter++;
158
- var link = new Kinetic.Link(shape);
159
- this.addLink(link);
160
- };
161
- /*
162
- * get shape by name
163
- */
164
- Kinetic.Layer.prototype.getShape = function(name){
165
- return this.shapeNames[name];
166
- };
167
- /*
168
- * remove a shape from layer (link + shape deconstructor)
169
- */
170
- Kinetic.Layer.prototype.remove = function(shape){
171
- var link = shape.link;
172
- this.removeLink(link);
173
- this.shape = null;
174
- this.link = null;
175
- };
176
- /*
177
- * remove link from layer. this does not deconstruct
178
- * the link or the shape
179
- */
180
- Kinetic.Layer.prototype.removeLink = function(link){
181
- link.shape.layer = undefined;
182
- this.unlink(link);
183
- this.links.splice(link.index, 1);
184
- this.linkHash[link.id] = undefined;
185
- this.setLinkIndices();
186
- };
187
579
 
188
- /*
189
- * unlink link. This is different from removeLink because it
190
- * keeps the link in the layer data structure
580
+ ///////////////////////////////////////////////////////////////////////
581
+ // Container
582
+ ///////////////////////////////////////////////////////////////////////
583
+
584
+ /**
585
+ * Container constructor. Container is the base class for
586
+ * Stage, Layer, and Group
191
587
  */
192
- Kinetic.Layer.prototype.unlink = function(link){
193
- // set head if needed
194
- if (link.id === this.headId) {
195
- this.headId = link.nextId;
196
- }
197
- // set tail if needed
198
- if (link.id === this.tailId) {
199
- this.tailId = link.prevId;
200
- }
201
- // link prev to next
202
- if (link.prevId !== undefined) {
203
- this.linkHash[link.prevId].nextId = link.nextId;
204
- }
205
- if (link.nextId !== undefined) {
206
- this.linkHash[link.nextId].prevId = link.prevId;
207
- }
208
- // clear pointers
209
- link.prevId = undefined;
210
- link.nextId = undefined;
588
+ Kinetic.Container = function(){
589
+ this.children = [];
590
+ this.childrenNames = {};
211
591
  };
212
- /*
213
- * set link indices
214
- */
215
- Kinetic.Layer.prototype.setLinkIndices = function(){
216
- for (var n = 0; n < this.links.length; n++) {
217
- this.links[n].index = n;
592
+
593
+ // methods
594
+ Kinetic.Container.prototype = {
595
+ /**
596
+ * set children indices
597
+ */
598
+ _setChildrenIndices: function(){
599
+ /*
600
+ * if reordering Layers, remove all canvas elements
601
+ * from the container except the buffer and backstage canvases
602
+ * and then readd all the layers
603
+ */
604
+ if (this.className === "Stage") {
605
+ var canvases = this.container.childNodes;
606
+ var bufferCanvas = canvases[0];
607
+ var backstageCanvas = canvases[1];
608
+
609
+ this.container.innerHTML = "";
610
+ this.container.appendChild(bufferCanvas);
611
+ this.container.appendChild(backstageCanvas);
612
+ }
613
+
614
+ for (var n = 0; n < this.children.length; n++) {
615
+ this.children[n].index = n;
616
+
617
+ if (this.className === "Stage") {
618
+ this.container.appendChild(this.children[n].canvas);
619
+ }
620
+ }
621
+ },
622
+ /**
623
+ * recursively traverse the container tree
624
+ * and draw the children
625
+ * @param {Object} obj
626
+ */
627
+ _drawChildren: function(){
628
+ var children = this.children;
629
+ for (var n = 0; n < children.length; n++) {
630
+ var child = children[n];
631
+ if (child.className === "Shape") {
632
+ child._draw(child.getLayer());
633
+ }
634
+ else {
635
+ child._draw();
636
+ }
637
+ }
638
+ },
639
+ /**
640
+ * get children
641
+ */
642
+ getChildren: function(){
643
+ return this.children;
644
+ },
645
+ /**
646
+ * get node by name
647
+ * @param {string} name
648
+ */
649
+ getChild: function(name){
650
+ return this.childrenNames[name];
651
+ },
652
+ /**
653
+ * add node to container
654
+ * @param {Node} child
655
+ */
656
+ _add: function(child){
657
+ if (child.name) {
658
+ this.childrenNames[child.name] = child;
659
+ }
660
+ child.id = Kinetic.GlobalObject.idCounter++;
661
+ child.index = this.children.length;
662
+ child.parent = this;
663
+
664
+ this.children.push(child);
665
+ },
666
+ /**
667
+ * remove child from container
668
+ * @param {Node} child
669
+ */
670
+ _remove: function(child){
671
+ if (child.name !== undefined) {
672
+ this.childrenNames[child.name] = undefined;
673
+ }
674
+ this.children.splice(child.index, 1);
675
+ this._setChildrenIndices();
676
+ child = undefined;
218
677
  }
219
678
  };
679
+
220
680
  ///////////////////////////////////////////////////////////////////////
221
- //// Stage
222
- ///////////////////////////////////////////////////////////////////////
681
+ // Stage
223
682
  ///////////////////////////////////////////////////////////////////////
224
-
225
- Kinetic.Stage = function(containerId, width, height){
226
- this.container = document.getElementById(containerId);
683
+ /**
684
+ * Stage constructor. Stage extends Container
685
+ * @param {String} containerId
686
+ * @param {int} width
687
+ * @param {int} height
688
+ */
689
+ Kinetic.Stage = function(cont, width, height){
690
+ this.className = "Stage";
691
+ this.container = typeof cont === "string" ? document.getElementById(cont) : cont;
227
692
  this.width = width;
228
693
  this.height = height;
229
694
  this.scale = {
230
695
  x: 1,
231
696
  y: 1
232
697
  };
233
- this.layerIdCounter = 0;
234
698
  this.dblClickWindow = 400;
235
- this.targetShape = {};
699
+ this.targetShape = undefined;
236
700
  this.clickStart = false;
237
- this.layerNames = {};
238
701
 
239
702
  // desktop flags
240
- this.mousePos = null;
703
+ this.mousePos = undefined;
241
704
  this.mouseDown = false;
242
705
  this.mouseUp = false;
243
706
 
244
707
  // mobile flags
245
- this.touchPos = null;
708
+ this.touchPos = undefined;
246
709
  this.touchStart = false;
247
710
  this.touchEnd = false;
248
711
 
249
- // user defined layers
250
- this.layers = [];
251
-
252
712
  /*
253
713
  * Layer roles
254
714
  *
255
715
  * buffer - canvas compositing
256
716
  * backstage - path detection
257
717
  */
258
- var that = this;
259
718
  this.bufferLayer = new Kinetic.Layer();
260
719
  this.backstageLayer = new Kinetic.Layer();
261
720
 
721
+ // set parents
722
+ this.bufferLayer.parent = this;
723
+ this.backstageLayer.parent = this;
724
+
262
725
  // customize back stage context
263
726
  var backstageLayer = this.backstageLayer;
264
- backstageLayer.context.stroke = function(){
265
- };
266
- backstageLayer.context.fill = function(){
267
- };
268
- backstageLayer.context.fillRect = function(x, y, width, height){
269
- backstageLayer.context.rect(x, y, width, height);
270
- };
271
- backstageLayer.context.strokeRect = function(x, y, width, height){
272
- that.context.rect(x, y, width, height);
273
- };
274
- backstageLayer.context.drawImage = function(){
275
- };
276
- backstageLayer.context.fillText = function(){
277
- };
278
- backstageLayer.context.strokeText = function(){
279
- };
727
+ this._stripLayer(backstageLayer);
280
728
 
281
729
  this.bufferLayer.getCanvas().style.display = 'none';
282
730
  this.backstageLayer.getCanvas().style.display = 'none';
283
731
 
284
732
  // add buffer layer
285
- this.bufferLayer.stage = this;
286
733
  this.bufferLayer.canvas.width = this.width;
287
734
  this.bufferLayer.canvas.height = this.height;
288
735
  this.container.appendChild(this.bufferLayer.canvas);
289
736
 
290
737
  // add backstage layer
291
- this.backstageLayer.stage = this;
292
738
  this.backstageLayer.canvas.width = this.width;
293
739
  this.backstageLayer.canvas.height = this.height;
294
740
  this.container.appendChild(this.backstageLayer.canvas);
295
741
 
296
- this.listen();
297
-
298
- this.addEventListener("mouseout", function(evt){
299
- that.shapeDragging = undefined;
300
- }, false);
742
+ this._listen();
743
+ this._prepareDrag();
301
744
 
302
- /*
303
- * prepare drag and drop
304
- */
305
- var types = [{
306
- end: "mouseup",
307
- move: "mousemove"
308
- }, {
309
- end: "touchend",
310
- move: "touchmove"
311
- }];
745
+ // add stage to global object
746
+ var stages = Kinetic.GlobalObject.stages;
747
+ stages.push(this);
312
748
 
313
- for (var n = 0; n < types.length; n++) {
314
- var pubType = types[n];
315
- (function(){
316
- var type = pubType;
317
- that.on(type.move, function(evt){
318
- if (that.shapeDragging) {
319
- var pos = type.move == "mousemove" ? that.getMousePosition() : that.getTouchPosition();
320
- if (that.shapeDragging.drag.x) {
321
- that.shapeDragging.x = pos.x - that.shapeDragging.offset.x;
322
- }
323
- if (that.shapeDragging.drag.y) {
324
- that.shapeDragging.y = pos.y - that.shapeDragging.offset.y;
325
- }
326
- that.shapeDragging.layer.draw();
327
-
328
- // execute user defined ondragend if defined
329
- var dragmove = that.shapeDragging.eventListeners.ondragmove;
330
- if (dragmove) {
331
- var events = dragmove;
332
- for (var i = 0; i < events.length; i++) {
333
- events[i].handler.apply(that.shapeDragging, [evt]);
334
- }
335
- }
336
- }
337
- }, false);
338
- that.on(type.end, function(evt){
339
- // execute user defined ondragend if defined
340
- if (that.shapeDragging) {
341
- var dragend = that.shapeDragging.eventListeners.ondragend;
342
- if (dragend) {
343
- var events = dragend;
344
- for (var i = 0; i < events.length; i++) {
345
- events[i].handler.apply(that.shapeDragging, [evt]);
346
- }
347
- }
348
- }
349
- that.shapeDragging = undefined;
350
- });
351
- })();
352
- }
749
+ // set stage id
750
+ this.id = Kinetic.GlobalObject.idCounter++;
353
751
 
354
- this.on("touchend", function(evt){
355
- // execute user defined ondragend if defined
356
- if (that.shapeDragging) {
357
- var dragend = that.shapeDragging.eventListeners.ondragend;
358
- if (dragend) {
359
- var events = dragend;
360
- for (var i = 0; i < events.length; i++) {
361
- events[i].handler.apply(that.shapeDragging, [evt]);
362
- }
363
- }
364
- }
365
- that.shapeDragging = undefined;
366
- });
367
- };
368
- /*
369
- * set stage size
370
- */
371
- Kinetic.Stage.prototype.setSize = function(width, height){
372
- var layers = this.layers;
373
- for (n = 0; n < layers.length; n++) {
374
- var layer = layers[n];
375
- layer.getCanvas().width = width;
376
- layer.getCanvas().height = height;
377
- layer.draw();
378
- }
752
+ // animation support
753
+ this.isAnimating = false;
754
+ this.onFrameFunc = undefined;
379
755
 
380
- // set stage dimensions
381
- this.width = width;
382
- this.height = height;
383
- };
384
- /*
385
- * scale stage
386
- */
387
- Kinetic.Stage.prototype.setScale = function(scaleX, scaleY){
388
- if (scaleY) {
389
- this.scale.x = scaleX;
390
- this.scale.y = scaleY;
391
- }
392
- else {
393
- this.scale.x = scaleX;
394
- this.scale.y = scaleX;
395
- }
756
+ // call super constructor
757
+ Kinetic.Container.apply(this, []);
396
758
  };
759
+
397
760
  /*
398
- * Composite toDataURL
761
+ * Stage methods
399
762
  */
400
- Kinetic.Stage.prototype.toDataURL = function(callback){
401
- var bufferLayer = this.bufferLayer;
402
- var bufferContext = bufferLayer.getContext();
403
- var layers = this.layers;
404
-
405
- function addLayer(n){
406
- var dataURL = layers[n].getCanvas().toDataURL();
407
- var imageObj = new Image();
408
- imageObj.onload = function(){
409
- bufferContext.drawImage(this, 0, 0);
410
- n++;
411
- if (n < layers.length) {
412
- addLayer(n);
763
+ Kinetic.Stage.prototype = {
764
+ /**
765
+ * sets onFrameFunc for animation
766
+ */
767
+ onFrame: function(func){
768
+ this.onFrameFunc = func;
769
+ },
770
+ /**
771
+ * start animation
772
+ */
773
+ start: function(){
774
+ this.isAnimating = true;
775
+ Kinetic.GlobalObject._handleAnimation();
776
+ },
777
+ /**
778
+ * stop animation
779
+ */
780
+ stop: function(){
781
+ this.isAnimating = false;
782
+ Kinetic.GlobalObject._handleAnimation();
783
+ },
784
+ /**
785
+ * draw children
786
+ */
787
+ draw: function(){
788
+ this._drawChildren();
789
+ },
790
+ /**
791
+ * disable layer rendering
792
+ * @param {Layer} layer
793
+ */
794
+ _stripLayer: function(layer){
795
+ layer.context.stroke = function(){
796
+ };
797
+ layer.context.fill = function(){
798
+ };
799
+ layer.context.fillRect = function(x, y, width, height){
800
+ layer.context.rect(x, y, width, height);
801
+ };
802
+ layer.context.strokeRect = function(x, y, width, height){
803
+ layer.context.rect(x, y, width, height);
804
+ };
805
+ layer.context.drawImage = function(){
806
+ };
807
+ layer.context.fillText = function(){
808
+ };
809
+ layer.context.strokeText = function(){
810
+ };
811
+ },
812
+ /**
813
+ * end drag and drop
814
+ */
815
+ _endDrag: function(evt){
816
+ if (this.nodeDragging) {
817
+ if (this.nodeDragging.drag.moving) {
818
+ this.nodeDragging.drag.moving = false;
819
+ this.nodeDragging._handleEvents("ondragend", evt);
413
820
  }
414
- else {
415
- callback(bufferLayer.getCanvas().toDataURL());
821
+ }
822
+ this.nodeDragging = undefined;
823
+ },
824
+ /**
825
+ * prepare drag and drop
826
+ */
827
+ _prepareDrag: function(){
828
+ var that = this;
829
+
830
+ this.on("mousemove touchmove", function(evt){
831
+ if (that.nodeDragging) {
832
+ var pos = that.getUserPosition();
833
+ if (that.nodeDragging.drag.x) {
834
+ that.nodeDragging.x = pos.x - that.nodeDragging.offset.x;
835
+ }
836
+ if (that.nodeDragging.drag.y) {
837
+ that.nodeDragging.y = pos.y - that.nodeDragging.offset.y;
838
+ }
839
+ that.nodeDragging.getLayer().draw();
840
+
841
+ if (!that.nodeDragging.drag.moving) {
842
+ that.nodeDragging.drag.moving = true;
843
+ // execute dragstart events if defined
844
+ that.nodeDragging._handleEvents("ondragstart", evt);
845
+ }
846
+ // execute user defined ondragmove if defined
847
+ that.nodeDragging._handleEvents("ondragmove", evt);
416
848
  }
417
- };
418
- imageObj.src = dataURL;
419
- }
420
-
421
-
422
- bufferLayer.clear();
423
- addLayer(0);
424
- };
425
-
426
- /*
427
- * draw shapes
428
- */
429
- Kinetic.Stage.prototype.draw = function(){
430
- var layers = this.layers;
431
- for (var n = 0; n < layers.length; n++) {
432
- layers[n].draw();
433
- }
434
- };
435
- /*
436
- * remove a layer from the stage
437
- */
438
- Kinetic.Stage.prototype.remove = function(shape){
439
- // TODO
440
- };
441
- /*
442
- * short-hand add event listener to stage (which is essentially
443
- * the container DOM)
444
- */
445
- Kinetic.Stage.prototype.on = function(type, handler){
446
- this.container.addEventListener(type, handler);
447
- };
448
- /*
449
- * long-hand add event listener to stage (which is essentially
450
- * the container DOM)
451
- */
452
- Kinetic.Stage.prototype.addEventListener = function(type, handler){
453
- this.on(type, handler);
454
- };
455
- /*
456
- * add layer to stage
457
- */
458
- Kinetic.Stage.prototype.add = function(layer){
459
- if (layer.name) {
460
- this.layerNames[layer.name] = layer;
461
- }
462
- layer.canvas.width = this.width;
463
- layer.canvas.height = this.height;
464
- layer.stage = this;
465
- this.layers.push(layer);
466
- layer.draw();
467
- this.container.appendChild(layer.canvas);
468
- };
469
- /*
470
- * get layer by name
471
- */
472
- Kinetic.Stage.prototype.getLayer = function(name){
473
- return this.layerNames[name];
474
- };
475
- /*
476
- * handle incoming event
477
- */
478
- Kinetic.Stage.prototype.handleEvent = function(evt){
479
- if (!evt) {
480
- evt = window.event;
481
- }
482
-
483
- this.setMousePosition(evt);
484
- this.setTouchPosition(evt);
485
-
486
- var backstageLayer = this.backstageLayer;
487
- var backstageLayerContext = backstageLayer.getContext();
488
- var that = this;
489
-
490
- backstageLayer.clear();
491
-
492
- /*
493
- * loop through layers. If at any point an event
494
- * is triggered, n is set to -1 which will break out of the
495
- * three nested loops
496
- */
497
- for (var n = this.layers.length - 1; n >= 0; n--) {
498
- var layer = this.layers[n];
499
- if (n >= 0 && layer.isListening) {
500
- var linkId = layer.tailId;
849
+ }, false);
850
+
851
+ this.on("mouseup touchend mouseout", function(evt){
852
+ that._endDrag(evt);
853
+ });
854
+ },
855
+ /**
856
+ * set stage size
857
+ * @param {int} width
858
+ * @param {int} height
859
+ */
860
+ setSize: function(width, height){
861
+ var layers = this.children;
862
+ for (var n = 0; n < layers.length; n++) {
863
+ var layer = layers[n];
864
+ layer.getCanvas().width = width;
865
+ layer.getCanvas().height = height;
866
+ layer.draw();
867
+ }
868
+
869
+ // set stage dimensions
870
+ this.width = width;
871
+ this.height = height;
872
+
873
+ // set buffer layer and backstage layer sizes
874
+ this.bufferLayer.getCanvas().width = width;
875
+ this.bufferLayer.getCanvas().height = height;
876
+ this.backstageLayer.getCanvas().width = width;
877
+ this.backstageLayer.getCanvas().height = height;
878
+ },
879
+ /**
880
+ * set stage scale
881
+ * @param {int} scaleX
882
+ * @param {int} scaleY
883
+ */
884
+ setScale: function(scaleX, scaleY){
885
+ var oldScaleX = this.scale.x;
886
+ var oldScaleY = this.scale.y;
887
+
888
+ if (scaleY) {
889
+ this.scale.x = scaleX;
890
+ this.scale.y = scaleY;
891
+ }
892
+ else {
893
+ this.scale.x = scaleX;
894
+ this.scale.y = scaleX;
895
+ }
896
+
897
+ /*
898
+ * scale all shape positions
899
+ */
900
+ var layers = this.children;
901
+ var that = this;
902
+ function scaleChildren(children){
903
+ for (var i = 0; i < children.length; i++) {
904
+ var child = children[i];
905
+ child.x *= that.scale.x / oldScaleX;
906
+ child.y *= that.scale.y / oldScaleY;
907
+ if (child.children) {
908
+ scaleChildren(child.children);
909
+ }
910
+ }
911
+ }
912
+
913
+ scaleChildren(layers);
914
+ },
915
+ /**
916
+ * get scale
917
+ */
918
+ getScale: function(){
919
+ return this.scale;
920
+ },
921
+ /**
922
+ * clear all layers
923
+ */
924
+ clear: function(){
925
+ var layers = this.children;
926
+ for (var n = 0; n < layers.length; n++) {
927
+ layers[n].clear();
928
+ }
929
+ },
930
+ /**
931
+ * creates a composite data URL and passes it to a callback
932
+ * @param {function} callback
933
+ */
934
+ toDataURL: function(callback){
935
+ var bufferLayer = this.bufferLayer;
936
+ var bufferContext = bufferLayer.getContext();
937
+ var layers = this.children;
938
+
939
+ function addLayer(n){
940
+ var dataURL = layers[n].getCanvas().toDataURL();
941
+ var imageObj = new Image();
942
+ imageObj.onload = function(){
943
+ bufferContext.drawImage(this, 0, 0);
944
+ n++;
945
+ if (n < layers.length) {
946
+ addLayer(n);
947
+ }
948
+ else {
949
+ callback(bufferLayer.getCanvas().toDataURL());
950
+ }
951
+ };
952
+ imageObj.src = dataURL;
953
+ }
954
+
955
+
956
+ bufferLayer.clear();
957
+ addLayer(0);
958
+ },
959
+ /**
960
+ * remove layer from stage
961
+ * @param {Layer} layer
962
+ */
963
+ remove: function(layer){
964
+ // remove layer canvas from dom
965
+ this.container.removeChild(layer.canvas);
966
+
967
+ this._remove(layer);
968
+ },
969
+ /**
970
+ * bind event listener to stage (which is essentially
971
+ * the container DOM)
972
+ * @param {string} type
973
+ * @param {function} handler
974
+ */
975
+ on: function(typesStr, handler){
976
+ var types = typesStr.split(" ");
977
+ for (var n = 0; n < types.length; n++) {
978
+ var baseEvent = types[n];
979
+ this.container.addEventListener(baseEvent, handler, false);
980
+ }
981
+ },
982
+ /**
983
+ * add layer to stage
984
+ * @param {Layer} layer
985
+ */
986
+ add: function(layer){
987
+ if (layer.name) {
988
+ this.childrenNames[layer.name] = layer;
989
+ }
990
+ layer.canvas.width = this.width;
991
+ layer.canvas.height = this.height;
992
+ this._add(layer);
993
+
994
+ // draw layer and append canvas to container
995
+ layer.draw();
996
+ this.container.appendChild(layer.canvas);
997
+ },
998
+ /**
999
+ * handle incoming event
1000
+ * @param {Event} evt
1001
+ */
1002
+ _handleEvent: function(evt){
1003
+ if (!evt) {
1004
+ evt = window.event;
1005
+ }
1006
+
1007
+ this._setMousePosition(evt);
1008
+ this._setTouchPosition(evt);
1009
+
1010
+ var backstageLayer = this.backstageLayer;
1011
+ var backstageLayerContext = backstageLayer.getContext();
1012
+ var that = this;
1013
+
1014
+ backstageLayer.clear();
1015
+
1016
+ /*
1017
+ * loop through layers. If at any point an event
1018
+ * is triggered, n is set to -1 which will break out of the
1019
+ * three nested loops
1020
+ */
1021
+ var targetFound = false;
1022
+
1023
+ function detectEvent(shape){
1024
+ shape._draw(backstageLayer);
1025
+ var pos = that.getUserPosition();
1026
+ var el = shape.eventListeners;
501
1027
 
502
- // propapgate backwards through event links
503
- while (n >= 0 && linkId !== undefined) {
504
- //for (var n = this.getEventShapes().length - 1; n >= 0; n--) {
505
- //var pubShape = this.getEventShapes()[n];
506
- var link = layer.linkHash[linkId];
507
- var pubShape = link.shape;
508
- (function(){
509
- var shape = pubShape;
510
- shape.draw(backstageLayer);
511
- var pos = that.getUserPosition();
512
- var el = shape.eventListeners;
1028
+ if (that.targetShape && shape.id === that.targetShape.id) {
1029
+ targetFound = true;
1030
+ }
1031
+
1032
+ if (shape.visible && pos !== undefined && backstageLayerContext.isPointInPath(pos.x, pos.y)) {
1033
+ // handle onmousedown
1034
+ if (that.mouseDown) {
1035
+ that.mouseDown = false;
1036
+ that.clickStart = true;
1037
+ shape._handleEvents("onmousedown", evt);
1038
+ return true;
1039
+ }
1040
+ // handle onmouseup & onclick
1041
+ else if (that.mouseUp) {
1042
+ that.mouseUp = false;
1043
+ shape._handleEvents("onmouseup", evt);
513
1044
 
514
- if (shape.visible && pos !== null && backstageLayerContext.isPointInPath(pos.x, pos.y)) {
515
- // handle onmousedown
516
- if (that.mouseDown) {
517
- that.mouseDown = false;
518
- that.clickStart = true;
519
-
520
- if (el.onmousedown) {
521
- var events = el.onmousedown;
522
- for (var i = 0; i < events.length; i++) {
523
- events[i].handler.apply(shape, [evt]);
524
- }
525
- }
526
- n = -1;
527
- }
528
- // handle onmouseup & onclick
529
- else if (that.mouseUp) {
530
- that.mouseUp = false;
531
- if (el.onmouseup) {
532
- var events = el.onmouseup;
533
- for (var i = 0; i < events.length; i++) {
534
- events[i].handler.apply(shape, [evt]);
535
- }
536
- }
537
-
538
- // detect if click or double click occurred
539
- if (that.clickStart) {
540
- if (el.onclick) {
541
- var events = el.onclick;
542
- for (var i = 0; i < events.length; i++) {
543
- events[i].handler.apply(shape, [evt]);
544
- }
545
- }
546
-
547
- if (el.ondblclick && shape.inDoubleClickWindow) {
548
- var events = el.ondblclick;
549
- for (var i = 0; i < events.length; i++) {
550
- events[i].handler.apply(shape, [evt]);
551
- }
552
- }
553
-
554
- shape.inDoubleClickWindow = true;
555
-
556
- setTimeout(function(){
557
- shape.inDoubleClickWindow = false;
558
- }, that.dblClickWindow);
559
- }
560
- n = -1;
561
- }
562
-
563
- // handle touchstart
564
- else if (that.touchStart) {
565
- that.touchStart = false;
566
- if (el.touchstart) {
567
- var events = el.touchstart;
568
- for (var i = 0; i < events.length; i++) {
569
- events[i].handler.apply(shape, [evt]);
570
- }
571
- }
1045
+ // detect if click or double click occurred
1046
+ if (that.clickStart) {
1047
+ /*
1048
+ * if dragging and dropping, don't fire click or dbl click
1049
+ * event
1050
+ */
1051
+ if ((that.nodeDragging && !that.nodeDragging.drag.moving) || !that.nodeDragging) {
1052
+ shape._handleEvents("onclick", evt);
572
1053
 
573
- if (el.ondbltap && shape.inDoubleClickWindow) {
574
- var events = el.ondbltap;
575
- for (var i = 0; i < events.length; i++) {
576
- events[i].handler.apply(shape, [evt]);
577
- }
1054
+ if (shape.inDoubleClickWindow) {
1055
+ shape._handleEvents("ondblclick", evt);
578
1056
  }
579
-
580
1057
  shape.inDoubleClickWindow = true;
581
-
582
1058
  setTimeout(function(){
583
1059
  shape.inDoubleClickWindow = false;
584
1060
  }, that.dblClickWindow);
585
- n = -1;
586
- }
587
-
588
- // handle touchend
589
- else if (that.touchEnd) {
590
- that.touchEnd = false;
591
- if (el.touchend) {
592
- var events = el.touchend;
593
- for (var i = 0; i < events.length; i++) {
594
- events[i].handler.apply(shape, [evt]);
595
- }
596
- }
597
- n = -1;
598
1061
  }
599
-
600
- // handle touchmove
601
- else if (el.touchmove) {
602
- var events = el.touchmove;
603
- for (var i = 0; i < events.length; i++) {
604
- events[i].handler.apply(shape, [evt]);
605
- }
606
- n = -1;
607
- }
608
-
609
- /*
610
- * this condition is used to identify a new target shape.
611
- * A new target shape occurs if a target shape is not defined or
612
- * if the current shape is different from the current target shape and
613
- * the current shape is beneath the target
614
- */
615
- else if (that.targetShape.id === undefined || (that.targetShape.id != shape.id && that.targetShape.getZIndex() < shape.getZIndex())) {
616
- /*
617
- * check if old target has an onmouseout event listener
618
- */
619
- var oldEl = that.targetShape.eventListeners;
620
- if (oldEl && oldEl.onmouseout) {
621
- var events = oldEl.onmouseout;
622
- for (var i = 0; i < events.length; i++) {
623
- events[i].handler.apply(that.targetShape, [evt]);
624
- }
625
- }
626
-
627
- // set new target shape
628
- that.targetShape = shape;
629
-
630
- // handle onmouseover
631
- if (el.onmouseover) {
632
- var events = el.onmouseover;
633
- for (var i = 0; i < events.length; i++) {
634
- events[i].handler.apply(shape, [evt]);
635
- }
636
- }
637
- n = -1;
638
- }
639
-
640
- // handle onmousemove
641
- else if (el.onmousemove) {
642
- var events = el.onmousemove;
643
- for (var i = 0; i < events.length; i++) {
644
- events[i].handler.apply(shape, [evt]);
645
- }
646
- n = -1;
1062
+ }
1063
+ return true;
1064
+ }
1065
+
1066
+ // handle touchstart
1067
+ else if (that.touchStart) {
1068
+ that.touchStart = false;
1069
+ shape._handleEvents("touchstart", evt);
1070
+
1071
+ if (el.ondbltap && shape.inDoubleClickWindow) {
1072
+ var events = el.ondbltap;
1073
+ for (var i = 0; i < events.length; i++) {
1074
+ events[i].handler.apply(shape, [evt]);
647
1075
  }
648
1076
  }
649
- // handle mouseout condition
650
- else if (that.targetShape.id == shape.id) {
651
- that.targetShape = {};
652
- if (el.onmouseout) {
653
- var events = el.onmouseout;
654
- for (var i = 0; i < events.length; i++) {
655
- events[i].handler.apply(shape, [evt]);
656
- }
1077
+
1078
+ shape.inDoubleClickWindow = true;
1079
+
1080
+ setTimeout(function(){
1081
+ shape.inDoubleClickWindow = false;
1082
+ }, that.dblClickWindow);
1083
+ return true;
1084
+ }
1085
+
1086
+ // handle touchend
1087
+ else if (that.touchEnd) {
1088
+ that.touchEnd = false;
1089
+ shape._handleEvents("touchend", evt);
1090
+ return true;
1091
+ }
1092
+
1093
+ // handle touchmove
1094
+ else if (el.touchmove) {
1095
+ shape._handleEvents("touchmove", evt);
1096
+ return true;
1097
+ }
1098
+
1099
+ //this condition is used to identify a new target shape.
1100
+ else if (!that.targetShape || (!targetFound && shape.id !== that.targetShape.id)) {
1101
+ /*
1102
+ * check if old target has an onmouseout event listener
1103
+ */
1104
+ if (that.targetShape) {
1105
+ var oldEl = that.targetShape.eventListeners;
1106
+ if (oldEl) {
1107
+ that.targetShape._handleEvents("onmouseout", evt);
657
1108
  }
658
- n = -1;
659
1109
  }
660
- }());
1110
+
1111
+ // set new target shape
1112
+ that.targetShape = shape;
1113
+
1114
+ // handle onmouseover
1115
+ shape._handleEvents("onmouseover", evt);
1116
+ return true;
1117
+ }
661
1118
 
662
- linkId = link.prevId;
663
- } // end links loop
1119
+ // handle onmousemove
1120
+ else {
1121
+ shape._handleEvents("onmousemove", evt);
1122
+ return true;
1123
+ }
1124
+ }
1125
+ // handle mouseout condition
1126
+ else if (that.targetShape && that.targetShape.id === shape.id) {
1127
+ that.targetShape = undefined;
1128
+ shape._handleEvents("onmouseout", evt);
1129
+ return true;
1130
+ }
1131
+
1132
+ return false;
664
1133
  }
665
- } // end layer loop
666
- };
667
- /*
668
- * begin listening for events by adding event handlers
669
- * to the container
670
- */
671
- Kinetic.Stage.prototype.listen = function(){
672
- var that = this;
673
-
674
- // desktop events
675
- this.container.addEventListener("mousedown", function(evt){
676
- that.mouseDown = true;
677
- that.handleEvent(evt);
678
- }, false);
679
-
680
- this.container.addEventListener("mousemove", function(evt){
681
- that.mouseUp = false;
682
- that.mouseDown = false;
683
- that.handleEvent(evt);
684
- }, false);
685
-
686
- this.container.addEventListener("mouseup", function(evt){
687
- that.mouseUp = true;
688
- that.mouseDown = false;
689
- that.handleEvent(evt);
690
1134
 
691
- that.clickStart = false;
692
- }, false);
693
-
694
- this.container.addEventListener("mouseover", function(evt){
695
- that.handleEvent(evt);
696
- }, false);
697
-
698
- this.container.addEventListener("mouseout", function(evt){
699
- that.mousePos = null;
700
- }, false);
701
- // mobile events
702
- this.container.addEventListener("touchstart", function(evt){
703
- evt.preventDefault();
704
- that.touchStart = true;
705
- that.handleEvent(evt);
706
- }, false);
707
-
708
- this.container.addEventListener("touchmove", function(evt){
709
- evt.preventDefault();
710
- that.handleEvent(evt);
711
- }, false);
712
-
713
- this.container.addEventListener("touchend", function(evt){
714
- evt.preventDefault();
715
- that.touchEnd = true;
716
- that.handleEvent(evt);
717
- }, false);
718
- };
719
- /*
720
- * get mouse position for desktop apps
721
- */
722
- Kinetic.Stage.prototype.getMousePosition = function(evt){
723
- return this.mousePos;
724
- };
725
- /*
726
- * get touch position for mobile apps
727
- */
728
- Kinetic.Stage.prototype.getTouchPosition = function(evt){
729
- return this.touchPos;
730
- };
731
- /*
732
- * get user position (mouse position or touch position)
733
- *
734
- */
735
- Kinetic.Stage.prototype.getUserPosition = function(evt){
736
- return this.getTouchPosition() || this.getMousePosition();
737
- };
738
- /*
739
- * set mouse positon for desktop apps
740
- */
741
- Kinetic.Stage.prototype.setMousePosition = function(evt){
742
- var mouseX = evt.clientX - this.getContainerPosition().left + window.pageXOffset;
743
- var mouseY = evt.clientY - this.getContainerPosition().top + window.pageYOffset;
744
- this.mousePos = {
745
- x: mouseX,
746
- y: mouseY
747
- };
748
- };
749
- /*
750
- * set touch position for mobile apps
751
- */
752
- Kinetic.Stage.prototype.setTouchPosition = function(evt){
753
- if (evt.touches !== undefined && evt.touches.length == 1) {// Only deal with
754
- // one finger
755
- var touch = evt.touches[0];
756
- // Get the information for finger #1
757
- var touchX = touch.clientX - this.getContainerPosition().left + window.pageXOffset;
758
- var touchY = touch.clientY - this.getContainerPosition().top + window.pageYOffset;
1135
+ function traverseChildren(obj){
1136
+ var children = obj.children;
1137
+ // propapgate backwards through children
1138
+ for (var i = children.length - 1; i >= 0; i--) {
1139
+ var child = children[i];
1140
+ if (child.className === "Shape") {
1141
+ var exit = detectEvent(child);
1142
+ if (exit) {
1143
+ return true;
1144
+ }
1145
+ }
1146
+ else {
1147
+ traverseChildren(child);
1148
+ }
1149
+ }
1150
+
1151
+ return false;
1152
+ }
759
1153
 
760
- this.touchPos = {
761
- x: touchX,
762
- y: touchY
1154
+ for (var n = this.children.length - 1; n >= 0; n--) {
1155
+ var layer = this.children[n];
1156
+ if (layer.visible && n >= 0 && layer.isListening) {
1157
+ if (traverseChildren(layer)) {
1158
+ n = -1;
1159
+ }
1160
+ }
1161
+ }
1162
+ },
1163
+ /**
1164
+ * begin listening for events by adding event handlers
1165
+ * to the container
1166
+ */
1167
+ _listen: function(){
1168
+ var that = this;
1169
+
1170
+ // desktop events
1171
+ this.container.addEventListener("mousedown", function(evt){
1172
+ that.mouseDown = true;
1173
+ that._handleEvent(evt);
1174
+ }, false);
1175
+
1176
+ this.container.addEventListener("mousemove", function(evt){
1177
+ that.mouseUp = false;
1178
+ that.mouseDown = false;
1179
+ that._handleEvent(evt);
1180
+ }, false);
1181
+
1182
+ this.container.addEventListener("mouseup", function(evt){
1183
+ that.mouseUp = true;
1184
+ that.mouseDown = false;
1185
+ that._handleEvent(evt);
1186
+
1187
+ that.clickStart = false;
1188
+ }, false);
1189
+
1190
+ this.container.addEventListener("mouseover", function(evt){
1191
+ that._handleEvent(evt);
1192
+ }, false);
1193
+
1194
+ this.container.addEventListener("mouseout", function(evt){
1195
+ that.mousePos = undefined;
1196
+ }, false);
1197
+ // mobile events
1198
+ this.container.addEventListener("touchstart", function(evt){
1199
+ evt.preventDefault();
1200
+ that.touchStart = true;
1201
+ that._handleEvent(evt);
1202
+ }, false);
1203
+
1204
+ this.container.addEventListener("touchmove", function(evt){
1205
+ evt.preventDefault();
1206
+ that._handleEvent(evt);
1207
+ }, false);
1208
+
1209
+ this.container.addEventListener("touchend", function(evt){
1210
+ evt.preventDefault();
1211
+ that.touchEnd = true;
1212
+ that._handleEvent(evt);
1213
+ }, false);
1214
+ },
1215
+ /**
1216
+ * get mouse position for desktop apps
1217
+ * @param {Event} evt
1218
+ */
1219
+ getMousePosition: function(evt){
1220
+ return this.mousePos;
1221
+ },
1222
+ /**
1223
+ * get touch position for mobile apps
1224
+ * @param {Event} evt
1225
+ */
1226
+ getTouchPosition: function(evt){
1227
+ return this.touchPos;
1228
+ },
1229
+ /**
1230
+ * get user position (mouse position or touch position)
1231
+ * @param {Event} evt
1232
+ */
1233
+ getUserPosition: function(evt){
1234
+ return this.getTouchPosition() || this.getMousePosition();
1235
+ },
1236
+ /**
1237
+ * set mouse positon for desktop apps
1238
+ * @param {Event} evt
1239
+ */
1240
+ _setMousePosition: function(evt){
1241
+ var mouseX = evt.clientX - this._getContainerPosition().left + window.pageXOffset;
1242
+ var mouseY = evt.clientY - this._getContainerPosition().top + window.pageYOffset;
1243
+ this.mousePos = {
1244
+ x: mouseX,
1245
+ y: mouseY
763
1246
  };
1247
+ },
1248
+ /**
1249
+ * set touch position for mobile apps
1250
+ * @param {Event} evt
1251
+ */
1252
+ _setTouchPosition: function(evt){
1253
+ if (evt.touches !== undefined && evt.touches.length === 1) {// Only deal with
1254
+ // one finger
1255
+ var touch = evt.touches[0];
1256
+ // Get the information for finger #1
1257
+ var touchX = touch.clientX - this._getContainerPosition().left + window.pageXOffset;
1258
+ var touchY = touch.clientY - this._getContainerPosition().top + window.pageYOffset;
1259
+
1260
+ this.touchPos = {
1261
+ x: touchX,
1262
+ y: touchY
1263
+ };
1264
+ }
1265
+ },
1266
+ /**
1267
+ * get container position
1268
+ */
1269
+ _getContainerPosition: function(){
1270
+ var obj = this.container;
1271
+ var top = 0;
1272
+ var left = 0;
1273
+ while (obj && obj.tagName !== "BODY") {
1274
+ top += obj.offsetTop;
1275
+ left += obj.offsetLeft;
1276
+ obj = obj.offsetParent;
1277
+ }
1278
+ return {
1279
+ top: top,
1280
+ left: left
1281
+ };
1282
+ },
1283
+ /**
1284
+ * get container DOM element
1285
+ */
1286
+ getContainer: function(){
1287
+ return this.container;
1288
+ },
1289
+ /**
1290
+ * get stage
1291
+ */
1292
+ getStage: function(){
1293
+ return this;
1294
+ },
1295
+ /**
1296
+ * get target shape
1297
+ */
1298
+ getTargetShape: function(){
1299
+ return this.targetShape;
764
1300
  }
765
1301
  };
766
- /*
767
- * get container position
768
- */
769
- Kinetic.Stage.prototype.getContainerPosition = function(){
770
- var obj = this.container;
771
- var top = 0;
772
- var left = 0;
773
- while (obj && obj.tagName != "BODY") {
774
- top += obj.offsetTop;
775
- left += obj.offsetLeft;
776
- obj = obj.offsetParent;
777
- }
778
- return {
779
- top: top,
780
- left: left
781
- };
782
- };
783
- /*
784
- * get container DOM element
785
- */
786
- Kinetic.Stage.prototype.getContainer = function(){
787
- return this.container;
788
- };
1302
+ // extend Container
1303
+ Kinetic.GlobalObject.extend(Kinetic.Stage, Kinetic.Container);
789
1304
 
790
1305
  ///////////////////////////////////////////////////////////////////////
791
- //// Shape
1306
+ // Layer
792
1307
  ///////////////////////////////////////////////////////////////////////
793
- ///////////////////////////////////////////////////////////////////////
794
-
795
- Kinetic.Shape = function(drawFunc, name){
796
- this.isListening = true;
797
- this.drawFunc = drawFunc;
798
- this.name = name;
799
- this.x = 0;
800
- this.y = 0;
801
- this.scale = {
802
- x: 1,
803
- y: 1
804
- };
805
- this.rotation = 0;
806
- // radians
807
- // store state for next clear
808
- this.lastX = 0;
809
- this.lastY = 0;
810
- this.lastRotation = 0;
811
- // radians
812
- this.lastScale = {
813
- x: 1,
814
- y: 1
815
- };
1308
+ /**
1309
+ * Layer constructor. Layer extends Container and Node
1310
+ * @param {string} name
1311
+ */
1312
+ Kinetic.Layer = function(config){
1313
+ this.className = "Layer";
1314
+ this.canvas = document.createElement('canvas');
1315
+ this.context = this.canvas.getContext('2d');
1316
+ this.canvas.style.position = 'absolute';
816
1317
 
817
- this.eventListeners = {};
818
- this.visible = true;
819
- this.drag = {
820
- x: false,
821
- y: false
822
- };
1318
+ // call super constructors
1319
+ Kinetic.Container.apply(this, []);
1320
+ Kinetic.Node.apply(this, [config]);
823
1321
  };
824
1322
  /*
825
- * listen or don't listen to events
1323
+ * Layer methods
826
1324
  */
827
- Kinetic.Shape.prototype.listen = function(isListening){
828
- // if shape is in layer
829
- if (this.link) {
830
- // if changing isListening
831
- if (isListening != this.isListening) {
832
- // is now listening
833
- if (isListening) {
834
- //TODO
835
- }
836
- // if now not listening
837
- else {
838
- //TODO
839
- }
1325
+ Kinetic.Layer.prototype = {
1326
+ /**
1327
+ * public draw children
1328
+ */
1329
+ draw: function(){
1330
+ this._draw();
1331
+ },
1332
+ /**
1333
+ * private draw children
1334
+ */
1335
+ _draw: function(){
1336
+ this.clear();
1337
+ if (this.visible) {
1338
+ this._drawChildren();
840
1339
  }
1340
+ },
1341
+ /**
1342
+ * clear layer
1343
+ */
1344
+ clear: function(){
1345
+ var context = this.getContext();
1346
+ var canvas = this.getCanvas();
1347
+ context.clearRect(0, 0, canvas.width, canvas.height);
1348
+ },
1349
+ /**
1350
+ * get layer canvas
1351
+ */
1352
+ getCanvas: function(){
1353
+ return this.canvas;
1354
+ },
1355
+ /**
1356
+ * get layer context
1357
+ */
1358
+ getContext: function(){
1359
+ return this.context;
1360
+ },
1361
+ /**
1362
+ * add node to layer
1363
+ * @param {Node} node
1364
+ */
1365
+ add: function(child){
1366
+ this._add(child);
1367
+ },
1368
+ /**
1369
+ * remove a child from the layer
1370
+ * @param {Node} child
1371
+ */
1372
+ remove: function(child){
1373
+ this._remove(child);
841
1374
  }
842
-
843
- this.isListening = isListening;
844
- };
845
- /*
846
- * get shape temp layer context
847
- */
848
- Kinetic.Shape.prototype.getContext = function(){
849
- return this.tempLayer.getContext();
850
- };
851
- /*
852
- * get shape temp layer canvas
853
- */
854
- Kinetic.Shape.prototype.getCanvas = function(){
855
- return this.tempLayer.getCanvas();
856
1375
  };
857
- /*
858
- * get stage
1376
+ // Extend Container and Node
1377
+ Kinetic.GlobalObject.extend(Kinetic.Layer, Kinetic.Container);
1378
+ Kinetic.GlobalObject.extend(Kinetic.Layer, Kinetic.Node);
1379
+
1380
+ ///////////////////////////////////////////////////////////////////////
1381
+ // Group
1382
+ ///////////////////////////////////////////////////////////////////////
1383
+
1384
+ /**
1385
+ * Group constructor. Group extends Container and Node
1386
+ * @param {String} name
859
1387
  */
860
- Kinetic.Shape.prototype.getStage = function(){
861
- return this.layer.stage;
1388
+ Kinetic.Group = function(config){
1389
+ this.className = "Group";
1390
+
1391
+ // call super constructors
1392
+ Kinetic.Container.apply(this, []);
1393
+ Kinetic.Node.apply(this, [config]);
862
1394
  };
863
- /*
864
- * draw shape
865
- */
866
- Kinetic.Shape.prototype.draw = function(layer){
867
- if (this.visible) {
868
- //var layer = this.layer;
869
- var stage = layer.stage;
870
- var context = layer.getContext();
871
-
872
- // layer transform
873
- context.save();
874
- if (stage.scale.x != 1 || stage.scale.y != 1) {
875
- context.scale(stage.scale.x, stage.scale.y);
876
- }
877
-
878
- // shape transform
879
- context.save();
880
- if (this.x !== 0 || this.y !== 0) {
881
- context.translate(this.x, this.y);
882
- }
883
- if (this.rotation !== 0) {
884
- context.rotate(this.rotation);
885
- }
886
- if (this.scale.x != 1 || this.scale.y != 1) {
887
- context.scale(this.scale.x, this.scale.y);
1395
+
1396
+ Kinetic.Group.prototype = {
1397
+ /**
1398
+ * draw children
1399
+ */
1400
+ _draw: function(){
1401
+ if (this.visible) {
1402
+ this._drawChildren();
888
1403
  }
889
-
890
- this.tempLayer = layer;
891
- this.drawFunc.call(this);
892
-
893
- context.restore();
894
- context.restore();
1404
+ },
1405
+ /**
1406
+ * add node to group
1407
+ * @param {Node} child
1408
+ */
1409
+ add: function(child){
1410
+ this._add(child);
1411
+ },
1412
+ /**
1413
+ * remove a child from the group
1414
+ * @param {Node} child
1415
+ */
1416
+ remove: function(child){
1417
+ this._remove(child);
895
1418
  }
896
1419
  };
897
- /*
898
- * initialize drag and drop
1420
+
1421
+ // Extend Container and Node
1422
+ Kinetic.GlobalObject.extend(Kinetic.Group, Kinetic.Container);
1423
+ Kinetic.GlobalObject.extend(Kinetic.Group, Kinetic.Node);
1424
+
1425
+ ///////////////////////////////////////////////////////////////////////
1426
+ // Shape
1427
+ ///////////////////////////////////////////////////////////////////////
1428
+ /**
1429
+ * Shape constructor
1430
+ * @param {Object} config
899
1431
  */
900
- Kinetic.Shape.prototype.initDrag = function(){
901
- var that = this;
902
- var types = ["mousedown", "touchstart"];
1432
+ Kinetic.Shape = function(config){
1433
+ this.className = "Shape";
903
1434
 
904
- for (var n = 0; n < types.length; n++) {
905
- var pubType = types[n];
906
- (function(){
907
- var type = pubType;
908
- that.on(type + ".initdrag", function(evt){
909
- var stage = that.layer.stage;
910
- var pos = stage.getUserPosition();
911
-
912
- if (pos) {
913
- stage.shapeDragging = that;
914
- stage.shapeDragging.offset = {};
915
- stage.shapeDragging.offset.x = pos.x - that.x;
916
- stage.shapeDragging.offset.y = pos.y - that.y;
917
-
918
- // execute dragstart events if defined
919
- var dragstart = that.eventListeners.ondragstart;
920
- if (dragstart) {
921
- var events = dragstart;
922
- for (var i = 0; i < events.length; i++) {
923
- events[i].handler.apply(that, [evt]);
924
- }
925
- }
926
- }
927
- });
928
- })();
929
- }
930
- };
931
- /*
932
- * remove drag and drop event listener
933
- */
934
- Kinetic.Shape.prototype.dragCleanup = function(){
935
- if (!this.drag.x && !this.drag.y) {
936
- this.off("mousedown.initdrag");
937
- this.off("touchstart.initdrag");
938
- }
1435
+ // required
1436
+ this.drawFunc = config.drawFunc;
1437
+
1438
+ // optional
1439
+ this.fill = config.fill;
1440
+ this.stroke = config.stroke;
1441
+ this.strokeWidth = config.strokeWidth;
1442
+
1443
+ // call super constructor
1444
+ Kinetic.Node.apply(this, [config]);
939
1445
  };
940
1446
  /*
941
- * enable/disable drag and drop for box x and y direction
1447
+ * Shape methods
942
1448
  */
943
- Kinetic.Shape.prototype.draggable = function(setDraggable){
944
- if (setDraggable) {
945
- var needInit = !this.drag.x && !this.drag.y;
946
- this.drag = {
947
- x: true,
948
- y: true
949
- };
950
- if (needInit) {
951
- this.initDrag();
1449
+ Kinetic.Shape.prototype = {
1450
+ /**
1451
+ * get shape temp layer context
1452
+ */
1453
+ getContext: function(){
1454
+ return this.tempLayer.getContext();
1455
+ },
1456
+ /**
1457
+ * get shape temp layer canvas
1458
+ */
1459
+ getCanvas: function(){
1460
+ return this.tempLayer.getCanvas();
1461
+ },
1462
+ /**
1463
+ * draw shape
1464
+ * @param {Layer} layer
1465
+ */
1466
+ _draw: function(layer){
1467
+ if (this.visible) {
1468
+ var stage = layer.getStage();
1469
+ var context = layer.getContext();
1470
+
1471
+ var family = [];
1472
+
1473
+ family.unshift(this);
1474
+ var parent = this.parent;
1475
+ while (parent.className !== "Stage") {
1476
+ family.unshift(parent);
1477
+ parent = parent.parent;
1478
+ }
1479
+
1480
+ // children transforms
1481
+ for (var n = 0; n < family.length; n++) {
1482
+ var obj = family[n];
1483
+
1484
+ context.save();
1485
+ if (obj.x !== 0 || obj.y !== 0) {
1486
+ context.translate(obj.x, obj.y);
1487
+ }
1488
+ if (obj.centerOffset.x !== 0 || obj.centerOffset.y !== 0) {
1489
+ context.translate(obj.centerOffset.x, obj.centerOffset.y);
1490
+ }
1491
+ if (obj.rotation !== 0) {
1492
+ context.rotate(obj.rotation);
1493
+ }
1494
+ if (obj.scale.x !== 1 || obj.scale.y !== 1) {
1495
+ context.scale(obj.scale.x, obj.scale.y);
1496
+ }
1497
+ if (obj.centerOffset.x !== 0 || obj.centerOffset.y !== 0) {
1498
+ context.translate(-1 * obj.centerOffset.x, -1 * obj.centerOffset.y);
1499
+ }
1500
+ if (obj.getAbsoluteAlpha() !== 1) {
1501
+ context.globalAlpha = obj.getAbsoluteAlpha();
1502
+ }
1503
+ }
1504
+
1505
+ // stage transform
1506
+ context.save();
1507
+ if (stage && (stage.scale.x !== 1 || stage.scale.y !== 1)) {
1508
+ context.scale(stage.scale.x, stage.scale.y);
1509
+ }
1510
+
1511
+ this.tempLayer = layer;
1512
+ this.drawFunc.call(this);
1513
+
1514
+ // children restore
1515
+ for (var i = 0; i < family.length; i++) {
1516
+ context.restore();
1517
+ }
1518
+
1519
+ // stage restore
1520
+ context.restore();
952
1521
  }
953
1522
  }
954
- else {
955
- this.drag = {
956
- x: false,
957
- y: false
958
- };
959
- this.dragCleanup();
960
- }
961
1523
  };
962
- /*
963
- * enable/disable drag and drop for x only
1524
+ // extend Node
1525
+ Kinetic.GlobalObject.extend(Kinetic.Shape, Kinetic.Node);
1526
+
1527
+ ///////////////////////////////////////////////////////////////////////
1528
+ // Geometry
1529
+ ///////////////////////////////////////////////////////////////////////
1530
+ /**
1531
+ * Geometry constructor
1532
+ * @param {Object} config
1533
+ * @param {function} drawFunc
964
1534
  */
965
- Kinetic.Shape.prototype.draggableX = function(setDraggable){
966
- if (setDraggable) {
967
- var needInit = !this.drag.x && !this.drag.y;
968
- this.drag.x = true;
969
- if (needInit) {
970
- this.initDrag();
971
- }
972
- }
973
- else {
974
- this.drag.x = false;
975
- this.dragCleanup();
976
- }
1535
+ Kinetic.Geometry = function(config){
1536
+ this.fill = config.fill;
1537
+ this.stroke = config.stroke;
1538
+ this.strokeWidth = config.strokeWidth;
1539
+
1540
+ // call super constructor
1541
+ Kinetic.Shape.apply(this, [config]);
977
1542
  };
978
1543
  /*
979
- * enable/disable drag and drop for y only
1544
+ * Geometry methods
980
1545
  */
981
- Kinetic.Shape.prototype.draggableY = function(setDraggable){
982
- if (setDraggable) {
983
- var needInit = !this.drag.x && !this.drag.y;
984
- this.drag.y = true;
985
- if (needInit) {
986
- this.initDrag();
1546
+ Kinetic.Geometry.prototype = {
1547
+ _fillStroke: function(){
1548
+ var context = this.getContext();
1549
+ // optional properties
1550
+ if (this.fill) {
1551
+ context.fillStyle = this.fill;
1552
+ context.fill();
987
1553
  }
988
- }
989
- else {
990
- this.drag.y = false;
991
- this.dragCleanup();
1554
+ if (this.stroke) {
1555
+ context.lineWidth = this.strokeWidth === undefined ? 1 : this.strokeWidth;
1556
+ context.strokeStyle = this.stroke;
1557
+ context.stroke();
1558
+ }
1559
+ },
1560
+ /*
1561
+ * set fill which can be a color, gradient object,
1562
+ * or pattern object
1563
+ */
1564
+ setFill: function(fill){
1565
+ this.fill = fill;
1566
+ },
1567
+ /*
1568
+ * get fill
1569
+ */
1570
+ getFill: function(){
1571
+ return this.fill;
1572
+ },
1573
+ /*
1574
+ * set stroke color
1575
+ */
1576
+ setStroke: function(stroke){
1577
+ this.stroke = stroke;
1578
+ },
1579
+ /*
1580
+ * get stroke
1581
+ */
1582
+ getStroke: function(){
1583
+ return this.stroke;
1584
+ },
1585
+ /*
1586
+ * set stroke width
1587
+ */
1588
+ setStrokeWidth: function(strokeWidth){
1589
+ this.strokeWidth = strokeWidth;
1590
+ },
1591
+ /*
1592
+ * get stroke width
1593
+ */
1594
+ getStrokeWidth: function(){
1595
+ return this.strokeWidth;
992
1596
  }
993
1597
  };
994
- /*
995
- * get zIndex
1598
+ // extend Shape
1599
+ Kinetic.GlobalObject.extend(Kinetic.Geometry, Kinetic.Shape);
1600
+
1601
+ ///////////////////////////////////////////////////////////////////////
1602
+ // Rect
1603
+ ///////////////////////////////////////////////////////////////////////
1604
+ /**
1605
+ * Rect constructor
1606
+ * @param {Object} config
996
1607
  */
997
- Kinetic.Shape.prototype.getZIndex = function(){
998
- return this.link.index;
1608
+ Kinetic.Rect = function(config){
1609
+ this.width = config.width;
1610
+ this.height = config.height;
1611
+
1612
+ config.drawFunc = function(){
1613
+ var canvas = this.getCanvas();
1614
+ var context = this.getContext();
1615
+ context.beginPath();
1616
+ context.rect(0, 0, this.width, this.height);
1617
+ context.closePath();
1618
+ this._fillStroke();
1619
+ };
1620
+
1621
+ // call super constructor
1622
+ Kinetic.Geometry.apply(this, [config]);
999
1623
  };
1624
+
1000
1625
  /*
1001
- * set shape scale
1626
+ * Rect methods
1002
1627
  */
1003
- Kinetic.Shape.prototype.setScale = function(scaleX, scaleY){
1004
- if (scaleY) {
1005
- this.scale.x = scaleX;
1006
- this.scale.y = scaleY;
1007
- }
1008
- else {
1009
- this.scale.x = scaleX;
1010
- this.scale.y = scaleX;
1628
+ Kinetic.Rect.prototype = {
1629
+ setWidth: function(width){
1630
+ this.width = width;
1631
+ },
1632
+ getWidth: function(){
1633
+ return this.width;
1634
+ },
1635
+ setHeight: function(height){
1636
+ this.height = height;
1637
+ },
1638
+ getHeight: function(){
1639
+ return this.height;
1640
+ },
1641
+ /**
1642
+ * set width and height
1643
+ * @param {number} width
1644
+ * @param {number} height
1645
+ */
1646
+ setSize: function(width, height){
1647
+ this.width = width;
1648
+ this.height = height;
1011
1649
  }
1012
1650
  };
1013
- /*
1014
- * move shape to position
1015
- */
1016
- Kinetic.Shape.prototype.setPosition = function(x, y){
1017
- this.x = x;
1018
- this.y = y;
1019
- };
1020
- /*
1021
- * get shape position
1651
+
1652
+ // extend Geometry
1653
+ Kinetic.GlobalObject.extend(Kinetic.Rect, Kinetic.Geometry);
1654
+
1655
+ ///////////////////////////////////////////////////////////////////////
1656
+ // Circle
1657
+ ///////////////////////////////////////////////////////////////////////
1658
+ /**
1659
+ * Circle constructor
1660
+ * @param {Object} config
1022
1661
  */
1023
- Kinetic.Shape.prototype.getPosition = function(){
1024
- return {
1025
- x: this.x,
1026
- y: this.y
1662
+ Kinetic.Circle = function(config){
1663
+ this.radius = config.radius;
1664
+
1665
+ config.drawFunc = function(){
1666
+ var canvas = this.getCanvas();
1667
+ var context = this.getContext();
1668
+ context.beginPath();
1669
+ context.arc(0, 0, this.radius, 0, Math.PI * 2, true);
1670
+ context.closePath();
1671
+ this._fillStroke();
1027
1672
  };
1673
+
1674
+ // call super constructor
1675
+ Kinetic.Geometry.apply(this, [config]);
1028
1676
  };
1677
+
1029
1678
  /*
1030
- * move shape by amount
1031
- */
1032
- Kinetic.Shape.prototype.move = function(x, y){
1033
- this.x += x;
1034
- this.y += y;
1035
- };
1036
- /*
1037
- * set shape rotation
1679
+ * Circle methods
1038
1680
  */
1039
- Kinetic.Shape.prototype.setRotation = function(theta){
1040
- this.rotation = theta;
1681
+ Kinetic.Circle.prototype = {
1682
+ setRadius: function(radius){
1683
+ this.radius = radius;
1684
+ },
1685
+ getRadius: function(){
1686
+ return this.radius;
1687
+ }
1041
1688
  };
1042
- /*
1043
- * rotate shape by amount
1689
+
1690
+ // extend Geometry
1691
+ Kinetic.GlobalObject.extend(Kinetic.Circle, Kinetic.Geometry);
1692
+
1693
+ ///////////////////////////////////////////////////////////////////////
1694
+ // Image
1695
+ ///////////////////////////////////////////////////////////////////////
1696
+ /**
1697
+ * Image constructor
1698
+ * @param {Object} config
1044
1699
  */
1045
- Kinetic.Shape.prototype.rotate = function(theta){
1046
- this.rotation += theta;
1700
+ Kinetic.Image = function(config){
1701
+ this.image = config.image;
1702
+
1703
+ // defaults
1704
+ this.width = config.width !== undefined ? config.width : config.image.width;
1705
+ this.height = config.height !== undefined ? config.height : config.image.height;
1706
+
1707
+ config.drawFunc = function(){
1708
+ var canvas = this.getCanvas();
1709
+ var context = this.getContext();
1710
+ context.beginPath();
1711
+ context.rect(0, 0, this.width, this.height);
1712
+ context.closePath();
1713
+ this._fillStroke();
1714
+ context.drawImage(this.image, 0, 0, this.width, this.height);
1715
+ };
1716
+
1717
+ // call super constructor
1718
+ Kinetic.Geometry.apply(this, [config]);
1047
1719
  };
1048
- /*
1049
- * short-hand add event listener to shape
1050
- */
1051
- Kinetic.Shape.prototype.on = function(typesStr, handler){
1052
- var types = typesStr.split(" ");
1053
- /*
1054
- * loop through types and attach event listeners to
1055
- * each one. eg. "click mouseover.namespace mouseout"
1056
- * will create three event bindings
1057
- */
1058
- for (var n = 0; n < types.length; n++) {
1059
- var type = types[n];
1060
- var event = (type.indexOf('touch') == -1) ? 'on' + type : type;
1061
- var parts = event.split(".");
1062
- var baseEvent = parts[0];
1063
- var name = parts.length > 1 ? parts[1] : "";
1064
-
1065
- if (!this.eventListeners[baseEvent]) {
1066
- this.eventListeners[baseEvent] = [];
1067
- }
1068
-
1069
- this.eventListeners[baseEvent].push({
1070
- name: name,
1071
- handler: handler
1072
- });
1720
+
1721
+ Kinetic.Image.prototype = {
1722
+ setImage: function(image){
1723
+ this.image = image;
1724
+ },
1725
+ getImage: function(image){
1726
+ return this.image;
1727
+ },
1728
+ setWidth: function(width){
1729
+ this.width = width;
1730
+ },
1731
+ getWidth: function(){
1732
+ return this.width;
1733
+ },
1734
+ setHeight: function(height){
1735
+ this.height = height;
1736
+ },
1737
+ getHeight: function(){
1738
+ return this.height;
1739
+ },
1740
+ /**
1741
+ * set width and height
1742
+ * @param {number} width
1743
+ * @param {number} height
1744
+ */
1745
+ setSize: function(width, height){
1746
+ this.width = width;
1747
+ this.height = height;
1073
1748
  }
1074
1749
  };
1075
- /*
1076
- * long-hand add event listener to shape
1750
+ // extend Geometry
1751
+ Kinetic.GlobalObject.extend(Kinetic.Image, Kinetic.Geometry);
1752
+
1753
+ ///////////////////////////////////////////////////////////////////////
1754
+ // Polygon
1755
+ ///////////////////////////////////////////////////////////////////////
1756
+ /**
1757
+ * Polygon constructor
1758
+ * @param {Object} config
1077
1759
  */
1078
- Kinetic.Shape.prototype.addEventListener = function(type, handler){
1079
- this.on(type, handler);
1760
+ Kinetic.Polygon = function(config){
1761
+ this.points = config.points;
1762
+ config.drawFunc = function(){
1763
+ var context = this.getContext();
1764
+ context.beginPath();
1765
+ context.moveTo(this.points[0].x, this.points[0].y);
1766
+ for (var n = 1; n < this.points.length; n++) {
1767
+ context.lineTo(this.points[n].x, this.points[n].y);
1768
+ }
1769
+ context.closePath();
1770
+ this._fillStroke();
1771
+ };
1772
+
1773
+ // call super constructor
1774
+ Kinetic.Geometry.apply(this, [config]);
1080
1775
  };
1081
- /*
1082
- * short-hand remove event listener(s)
1776
+
1777
+ // extend Geometry
1778
+ Kinetic.GlobalObject.extend(Kinetic.Polygon, Kinetic.Geometry);
1779
+
1780
+
1781
+ ///////////////////////////////////////////////////////////////////////
1782
+ // RegularPolygon
1783
+ ///////////////////////////////////////////////////////////////////////
1784
+ /**
1785
+ * Polygon constructor
1786
+ * @param {Object} config
1083
1787
  */
1084
- Kinetic.Shape.prototype.off = function(type){
1085
- var event = (type.indexOf('touch') == -1) ? 'on' + type : type;
1086
- var parts = event.split(".");
1087
- var baseEvent = parts[0];
1788
+ Kinetic.RegularPolygon = function(config){
1789
+ this.points = config.points;
1790
+ this.radius = config.radius;
1791
+ this.sides = config.sides;
1088
1792
 
1089
- if (this.eventListeners[baseEvent] && parts.length > 1) {
1090
- var name = parts[1];
1793
+ config.drawFunc = function(){
1794
+ var context = this.getContext();
1795
+ context.beginPath();
1796
+ context.moveTo(0, 0 - this.radius);
1091
1797
 
1092
- for (var i = 0; i < this.eventListeners[baseEvent].length; i++) {
1093
- if (this.eventListeners[baseEvent][i].name == name) {
1094
- this.eventListeners[baseEvent].splice(i, 1);
1095
- if (this.eventListeners[baseEvent].length === 0) {
1096
- this.eventListeners[baseEvent] = undefined;
1097
- }
1098
- break;
1099
- }
1798
+ for (var n = 1; n < config.sides; n++) {
1799
+ var x = this.radius * Math.sin(n * 2 * Math.PI / this.sides);
1800
+ var y = -1 * this.radius * Math.cos(n * 2 * Math.PI / this.sides);
1801
+ context.lineTo(x, y);
1100
1802
  }
1101
- }
1102
- else {
1103
- this.eventListeners[baseEvent] = undefined;
1104
- }
1105
- };
1106
- /*
1107
- * long-hand remove event listener(s)
1108
- */
1109
- Kinetic.Shape.prototype.removeEventListener = function(type){
1110
- this.off(type);
1111
- };
1112
- /*
1113
- * show shape
1114
- */
1115
- Kinetic.Shape.prototype.show = function(){
1116
- this.visible = true;
1117
- };
1118
- /*
1119
- * hide shape
1120
- */
1121
- Kinetic.Shape.prototype.hide = function(){
1122
- this.visible = false;
1123
- };
1124
- /*
1125
- * move shape to top
1126
- */
1127
- Kinetic.Shape.prototype.moveToTop = function(){
1128
- var link = this.link;
1129
- var index = link.index;
1130
- var layer = this.layer;
1131
- this.layer.links.splice(index, 1);
1132
- this.layer.links.push(link);
1133
-
1134
- layer.setLinkIndices();
1803
+ context.closePath();
1804
+ this._fillStroke();
1805
+ };
1135
1806
 
1136
- if (this.isListening) {
1137
- // alter link structure if more than one link in the layer
1138
- if (link.nextId !== undefined || link.prevId !== undefined) {
1139
- layer.unlink(link);
1140
- var tail = layer.linkHash[layer.tailId];
1141
- tail.nextId = link.id;
1142
- link.prevId = tail.id;
1143
- layer.tailId = link.id;
1144
- }
1145
- }
1807
+ // call super constructor
1808
+ Kinetic.Geometry.apply(this, [config]);
1146
1809
  };
1810
+
1147
1811
  /*
1148
- * move shape up
1812
+ * RegularPolygon methods
1149
1813
  */
1150
- Kinetic.Shape.prototype.moveUp = function(){
1151
- var link = this.link;
1152
- var index = link.index;
1153
- var layer = this.layer;
1154
- var nextLink = layer.linkHash[link.nextId];
1155
-
1156
- // only do something if there's a link above
1157
- if (nextLink) {
1158
- // swap links
1159
- this.layer.links.splice(index, 1);
1160
- this.layer.links.splice(index + 1, 0, link);
1161
-
1162
- layer.setLinkIndices();
1163
-
1164
- nextLink.prevId = link.prevId;
1165
- link.nextId = nextLink.nextId;
1166
-
1167
- if (link.prevId !== undefined) {
1168
- layer.linkHash[link.prevId].nextId = nextLink.id;
1169
- }
1170
- if (nextLink.nextId !== undefined) {
1171
- layer.linkHash[nextLink.nextId].prevId = link.id;
1172
- }
1173
-
1174
- // link to eachother
1175
- link.prevId = nextLink.id;
1176
- nextLink.nextId = link.id;
1177
-
1178
- // handle tail and head reassignment
1179
- if (link.id == layer.headId) {
1180
- layer.headId = nextLink.id;
1181
- }
1182
- if (nextLink.id == layer.tailId) {
1183
- layer.tailId = link.id;
1184
- }
1814
+ Kinetic.RegularPolygon.prototype = {
1815
+ setPoints: function(points){
1816
+ this.points = points;
1817
+ },
1818
+ getPoints: function(){
1819
+ return this.points;
1820
+ },
1821
+ setRadius: function(radius){
1822
+ this.radius = radius;
1823
+ },
1824
+ getRadius: function(){
1825
+ return this.radius;
1826
+ },
1827
+ setSides: function(sides){
1828
+ this.sides = sides;
1829
+ },
1830
+ getSides: function(){
1831
+ return this.sides;
1185
1832
  }
1186
1833
  };
1187
- /*
1188
- * move shape down
1834
+
1835
+ // extend Geometry
1836
+ Kinetic.GlobalObject.extend(Kinetic.RegularPolygon, Kinetic.Geometry);
1837
+
1838
+
1839
+ ///////////////////////////////////////////////////////////////////////
1840
+ // Star
1841
+ ///////////////////////////////////////////////////////////////////////
1842
+ /**
1843
+ * Star constructor
1844
+ * @param {Object} config
1189
1845
  */
1190
- Kinetic.Shape.prototype.moveDown = function(){
1191
- var link = this.link;
1192
- var index = link.index;
1193
- var layer = this.layer;
1194
- var prevLink = layer.linkHash[link.prevId];
1846
+ Kinetic.Star = function(config){
1847
+ this.points = config.points;
1848
+ this.outerRadius = config.outerRadius;
1849
+ this.innerRadius = config.innerRadius;
1195
1850
 
1196
- // only do something if there's a link above
1197
- if (prevLink) {
1198
- // swap links
1199
- this.layer.links.splice(index, 1);
1200
- this.layer.links.splice(index - 1, 0, link);
1201
-
1202
- layer.setLinkIndices();
1203
-
1204
- link.prevId = prevLink.prevId;
1205
- prevLink.nextId = link.nextId;
1206
-
1207
- if (link.nextId !== undefined) {
1208
- layer.linkHash[link.nextId].prevId = prevLink.id;
1209
- }
1210
- if (prevLink.prevId !== undefined) {
1211
- layer.linkHash[prevLink.prevId].nextId = link.id;
1212
- }
1851
+ config.drawFunc = function(){
1852
+ var context = this.getContext();
1853
+ context.beginPath();
1854
+ context.moveTo(0, 0 - this.outerRadius);
1213
1855
 
1214
- // link to eachother
1215
- link.nextId = prevLink.id;
1216
- prevLink.prevId = link.id;
1217
-
1218
- // handle tail and head reassignment
1219
- if (prevLink.id == layer.headId) {
1220
- layer.headId = link.id;
1221
- }
1222
- if (link.id == layer.tailId) {
1223
- layer.tailId = prevLink.id;
1856
+ for (var n = 1; n < config.points * 2; n++) {
1857
+ var radius = n % 2 === 0 ? this.outerRadius : this.innerRadius;
1858
+ var x = radius * Math.sin(n * Math.PI / this.points);
1859
+ var y = -1 * radius * Math.cos(n * Math.PI / this.points);
1860
+ context.lineTo(x, y);
1224
1861
  }
1225
- }
1226
- };
1227
- /*
1228
- * move shape to bottom
1229
- */
1230
- Kinetic.Shape.prototype.moveToBottom = function(){
1231
- var link = this.link;
1232
- var index = link.index;
1233
- var layer = this.layer;
1234
- this.layer.links.splice(index, 1);
1235
- this.layer.links.unshift(link);
1236
-
1237
- layer.setLinkIndices();
1862
+ context.closePath();
1863
+ this._fillStroke();
1864
+ };
1238
1865
 
1239
- if (this.isListening) {
1240
- // alter link structure if more than one link in the layer
1241
- if (link.nextId !== undefined || link.prevId !== undefined) {
1242
- layer.unlink(link);
1243
- var head = layer.linkHash[layer.headId];
1244
- head.prevId = link.id;
1245
- link.nextId = head.id;
1246
- layer.headId = link.id;
1247
- }
1248
- }
1866
+ // call super constructor
1867
+ Kinetic.Geometry.apply(this, [config]);
1249
1868
  };
1869
+
1250
1870
  /*
1251
- * get shape layer
1871
+ * Star methods
1252
1872
  */
1253
- Kinetic.Shape.prototype.getLayer = function(){
1254
- return this.layer;
1873
+ Kinetic.Star.prototype = {
1874
+ setPoints: function(points){
1875
+ this.points = points;
1876
+ },
1877
+ getPoints: function(){
1878
+ return this.points;
1879
+ },
1880
+ setOuterRadius: function(radius){
1881
+ this.outerRadius = radius;
1882
+ },
1883
+ getOuterRadius: function(){
1884
+ return this.outerRadius;
1885
+ },
1886
+ setInnerRadius: function(radius){
1887
+ this.innerRadius = radius;
1888
+ },
1889
+ getInnerRadius: function(){
1890
+ return this.innerRadius;
1891
+ }
1255
1892
  };
1256
- /*
1257
- * move shape to another layer
1258
- */
1259
- Kinetic.Shape.prototype.moveToLayer = function(newLayer){
1260
- var layer = this.layer;
1261
- var link = this.link;
1262
- layer.unlink(link);
1263
- layer.removeLink(link);
1264
- newLayer.addLink(link);
1265
- };
1893
+ // extend Geometry
1894
+ Kinetic.GlobalObject.extend(Kinetic.Star, Kinetic.Geometry);