social_cheesecake 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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);