Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('graphics-canvas', function (Y, NAME) {
2
 
3
var IMPLEMENTATION = "canvas",
4
    SHAPE = "shape",
5
	SPLITPATHPATTERN = /[a-z][^a-z]*/ig,
6
    SPLITARGSPATTERN = /[\-]?[0-9]*[0-9|\.][0-9]*/g,
7
    DOCUMENT = Y.config.doc,
8
    Y_LANG = Y.Lang,
9
    AttributeLite = Y.AttributeLite,
10
	CanvasShape,
11
	CanvasPath,
12
	CanvasRect,
13
    CanvasEllipse,
14
	CanvasCircle,
15
    CanvasPieSlice,
16
    Y_DOM = Y.DOM,
17
    Y_Color = Y.Color,
18
    PARSE_INT = parseInt,
19
    PARSE_FLOAT = parseFloat,
20
    IS_NUMBER = Y_LANG.isNumber,
21
    RE = RegExp,
22
    TORGB = Y_Color.toRGB,
23
    TOHEX = Y_Color.toHex,
24
    _getClassName = Y.ClassNameManager.getClassName;
25
 
26
/**
27
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Drawing.html">`Drawing`</a> class.
28
 * `CanvasDrawing` is not intended to be used directly. Instead, use the <a href="Drawing.html">`Drawing`</a> class.
29
 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
30
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Drawing.html">`Drawing`</a>
31
 * class will point to the `CanvasDrawing` class.
32
 *
33
 * @module graphics
34
 * @class CanvasDrawing
35
 * @constructor
36
 */
37
function CanvasDrawing()
38
{
39
}
40
 
41
CanvasDrawing.prototype = {
42
    /**
43
     * Maps path to methods
44
     *
45
     * @property _pathSymbolToMethod
46
     * @type Object
47
     * @private
48
     */
49
    _pathSymbolToMethod: {
50
        M: "moveTo",
51
        m: "relativeMoveTo",
52
        L: "lineTo",
53
        l: "relativeLineTo",
54
        C: "curveTo",
55
        c: "relativeCurveTo",
56
        Q: "quadraticCurveTo",
57
        q: "relativeQuadraticCurveTo",
58
        z: "closePath",
59
        Z: "closePath"
60
    },
61
 
62
    /**
63
     * Current x position of the drawing.
64
     *
65
     * @property _currentX
66
     * @type Number
67
     * @private
68
     */
69
    _currentX: 0,
70
 
71
    /**
72
     * Current y position of the drqwing.
73
     *
74
     * @property _currentY
75
     * @type Number
76
     * @private
77
     */
78
    _currentY: 0,
79
 
80
    /**
81
     * Parses hex color string and alpha value to rgba
82
     *
83
     * @method _toRGBA
84
     * @param {Object} val Color value to parse. Can be hex string, rgb or name.
85
     * @param {Number} alpha Numeric value between 0 and 1 representing the alpha level.
86
     * @private
87
     */
88
    _toRGBA: function(val, alpha) {
89
        alpha = (alpha !== undefined) ? alpha : 1;
90
        if (!Y_Color.re_RGB.test(val)) {
91
            val = TOHEX(val);
92
        }
93
 
94
        if(Y_Color.re_hex.exec(val)) {
95
            val = 'rgba(' + [
96
                PARSE_INT(RE.$1, 16),
97
                PARSE_INT(RE.$2, 16),
98
                PARSE_INT(RE.$3, 16)
99
            ].join(',') + ',' + alpha + ')';
100
        }
101
        return val;
102
    },
103
 
104
    /**
105
     * Converts color to rgb format
106
     *
107
     * @method _toRGB
108
     * @param val Color value to convert.
109
     * @private
110
     */
111
    _toRGB: function(val) {
112
        return TORGB(val);
113
    },
114
 
115
    /**
116
     * Sets the size of the graphics object.
117
     *
118
     * @method setSize
119
     * @param w {Number} width to set for the instance.
120
     * @param h {Number} height to set for the instance.
121
     * @private
122
     */
123
	setSize: function(w, h) {
124
        if(this.get("autoSize"))
125
        {
126
            if(w > this.node.getAttribute("width"))
127
            {
128
                this.node.style.width = w + "px";
129
                this.node.setAttribute("width", w);
130
            }
131
            if(h > this.node.getAttribute("height"))
132
            {
133
                this.node.style.height = h + "px";
134
                this.node.setAttribute("height", h);
135
            }
136
        }
137
    },
138
 
139
	/**
140
     * Tracks coordinates. Used to calculate the start point of dashed lines.
141
     *
142
     * @method _updateCoords
143
     * @param {Number} x x-coordinate
144
     * @param {Number} y y-coordinate
145
	 * @private
146
	 */
147
    _updateCoords: function(x, y)
148
    {
149
        this._xcoords.push(x);
150
        this._ycoords.push(y);
151
        this._currentX = x;
152
        this._currentY = y;
153
    },
154
 
155
	/**
156
     * Clears the coordinate arrays. Called at the end of a drawing operation.
157
	 *
158
     * @method _clearAndUpdateCoords
159
     * @private
160
	 */
161
    _clearAndUpdateCoords: function()
162
    {
163
        var x = this._xcoords.pop() || 0,
164
            y = this._ycoords.pop() || 0;
165
        this._updateCoords(x, y);
166
    },
167
 
168
	/**
169
     * Moves the shape's dom node.
170
     *
171
     * @method _updateNodePosition
172
	 * @private
173
	 */
174
    _updateNodePosition: function()
175
    {
176
        var node = this.get("node"),
177
            x = this.get("x"),
178
            y = this.get("y");
179
        node.style.position = "absolute";
180
        node.style.left = (x + this._left) + "px";
181
        node.style.top = (y + this._top) + "px";
182
    },
183
 
184
    /**
185
     * Queues up a method to be executed when a shape redraws.
186
     *
187
     * @method _updateDrawingQueue
188
     * @param {Array} val An array containing data that can be parsed into a method and arguments. The value at zero-index
189
     * of the array is a string reference of the drawing method that will be called. All subsequent indices are argument for
190
     * that method. For example, `lineTo(10, 100)` would be structured as:
191
     * `["lineTo", 10, 100]`.
192
     * @private
193
     */
194
    _updateDrawingQueue: function(val)
195
    {
196
        this._methods.push(val);
197
    },
198
 
199
    /**
200
     * Draws a line segment from the current drawing position to the specified x and y coordinates.
201
     *
202
     * @method lineTo
203
     * @param {Number} point1 x-coordinate for the end point.
204
     * @param {Number} point2 y-coordinate for the end point.
205
     * @chainable
206
     */
207
    lineTo: function()
208
    {
209
        this._lineTo.apply(this, [Y.Array(arguments), false]);
210
        return this;
211
    },
212
 
213
    /**
214
     * Draws a line segment from the current drawing position to the relative x and y coordinates.
215
     *
216
     * @method relativeLineTo
217
     * @param {Number} point1 x-coordinate for the end point.
218
     * @param {Number} point2 y-coordinate for the end point.
219
     * @chainable
220
     */
221
    relativeLineTo: function()
222
    {
223
        this._lineTo.apply(this, [Y.Array(arguments), true]);
224
        return this;
225
    },
226
 
227
    /**
228
     * Implements lineTo methods.
229
     *
230
     * @method _lineTo
231
     * @param {Array} args The arguments to be used.
232
     * @param {Boolean} relative Indicates whether or not to use relative coordinates.
233
     * @private
234
     */
235
    _lineTo: function(args, relative)
236
    {
237
        var point1 = args[0],
238
            i,
239
            len,
240
            x,
241
            y,
242
            wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
243
            relativeX = relative ? parseFloat(this._currentX) : 0,
244
            relativeY = relative ? parseFloat(this._currentY) : 0;
245
        if(!this._lineToMethods)
246
        {
247
            this._lineToMethods = [];
248
        }
249
        len = args.length - 1;
250
        if (typeof point1 === 'string' || typeof point1 === 'number') {
251
            for (i = 0; i < len; i = i + 2) {
252
                x = parseFloat(args[i]);
253
                y = parseFloat(args[i + 1]);
254
                x = x + relativeX;
255
                y = y + relativeY;
256
                this._updateDrawingQueue(["lineTo", x, y]);
257
                this._trackSize(x - wt, y - wt);
258
                this._trackSize(x + wt, y + wt);
259
                this._updateCoords(x, y);
260
            }
261
        }
262
        else
263
        {
264
            for (i = 0; i < len; i = i + 1)
265
            {
266
                x = parseFloat(args[i][0]);
267
                y = parseFloat(args[i][1]);
268
                this._updateDrawingQueue(["lineTo", x, y]);
269
                this._lineToMethods[this._lineToMethods.length] = this._methods[this._methods.length - 1];
270
                this._trackSize(x - wt, y - wt);
271
                this._trackSize(x + wt, y + wt);
272
                this._updateCoords(x, y);
273
            }
274
        }
275
        this._drawingComplete = false;
276
        return this;
277
    },
278
 
279
    /**
280
     * Moves the current drawing position to specified x and y coordinates.
281
     *
282
     * @method moveTo
283
     * @param {Number} x x-coordinate for the end point.
284
     * @param {Number} y y-coordinate for the end point.
285
     * @chainable
286
     */
287
    moveTo: function()
288
    {
289
        this._moveTo.apply(this, [Y.Array(arguments), false]);
290
        return this;
291
    },
292
 
293
    /**
294
     * Moves the current drawing position relative to specified x and y coordinates.
295
     *
296
     * @method relativeMoveTo
297
     * @param {Number} x x-coordinate for the end point.
298
     * @param {Number} y y-coordinate for the end point.
299
     * @chainable
300
     */
301
    relativeMoveTo: function()
302
    {
303
        this._moveTo.apply(this, [Y.Array(arguments), true]);
304
        return this;
305
    },
306
 
307
    /**
308
     * Implements moveTo methods.
309
     *
310
     * @method _moveTo
311
     * @param {Array} args The arguments to be used.
312
     * @param {Boolean} relative Indicates whether or not to use relative coordinates.
313
     * @private
314
     */
315
    _moveTo: function(args, relative) {
316
        var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
317
            relativeX = relative ? parseFloat(this._currentX) : 0,
318
            relativeY = relative ? parseFloat(this._currentY) : 0,
319
            x = parseFloat(args[0]) + relativeX,
320
            y = parseFloat(args[1]) + relativeY;
321
        this._updateDrawingQueue(["moveTo", x, y]);
322
        this._trackSize(x - wt, y - wt);
323
        this._trackSize(x + wt, y + wt);
324
        this._updateCoords(x, y);
325
        this._drawingComplete = false;
326
        return this;
327
    },
328
 
329
    /**
330
     * Draws a bezier curve.
331
     *
332
     * @method curveTo
333
     * @param {Number} cp1x x-coordinate for the first control point.
334
     * @param {Number} cp1y y-coordinate for the first control point.
335
     * @param {Number} cp2x x-coordinate for the second control point.
336
     * @param {Number} cp2y y-coordinate for the second control point.
337
     * @param {Number} x x-coordinate for the end point.
338
     * @param {Number} y y-coordinate for the end point.
339
     * @chainable
340
     */
341
    curveTo: function() {
342
        this._curveTo.apply(this, [Y.Array(arguments), false]);
343
        return this;
344
    },
345
 
346
    /**
347
     * Draws a bezier curve relative to the current coordinates.
348
     *
349
     * @method relativeCurveTo
350
     * @param {Number} cp1x x-coordinate for the first control point.
351
     * @param {Number} cp1y y-coordinate for the first control point.
352
     * @param {Number} cp2x x-coordinate for the second control point.
353
     * @param {Number} cp2y y-coordinate for the second control point.
354
     * @param {Number} x x-coordinate for the end point.
355
     * @param {Number} y y-coordinate for the end point.
356
     * @chainable
357
     */
358
    relativeCurveTo: function() {
359
        this._curveTo.apply(this, [Y.Array(arguments), true]);
360
        return this;
361
    },
362
 
363
    /**
364
     * Implements curveTo methods.
365
     *
366
     * @method _curveTo
367
     * @param {Array} args The arguments to be used.
368
     * @param {Boolean} relative Indicates whether or not to use relative coordinates.
369
     * @private
370
     */
371
    _curveTo: function(args, relative) {
372
        var w,
373
            h,
374
            cp1x,
375
            cp1y,
376
            cp2x,
377
            cp2y,
378
            x,
379
            y,
380
            pts,
381
            right,
382
            left,
383
            bottom,
384
            top,
385
            i,
386
            len,
387
            relativeX = relative ? parseFloat(this._currentX) : 0,
388
            relativeY = relative ? parseFloat(this._currentY) : 0;
389
        len = args.length - 5;
390
        for(i = 0; i < len; i = i + 6)
391
        {
392
            cp1x = parseFloat(args[i]) + relativeX;
393
            cp1y = parseFloat(args[i + 1]) + relativeY;
394
            cp2x = parseFloat(args[i + 2]) + relativeX;
395
            cp2y = parseFloat(args[i + 3]) + relativeY;
396
            x = parseFloat(args[i + 4]) + relativeX;
397
            y = parseFloat(args[i + 5]) + relativeY;
398
            this._updateDrawingQueue(["bezierCurveTo", cp1x, cp1y, cp2x, cp2y, x, y]);
399
            this._drawingComplete = false;
400
            right = Math.max(x, Math.max(cp1x, cp2x));
401
            bottom = Math.max(y, Math.max(cp1y, cp2y));
402
            left = Math.min(x, Math.min(cp1x, cp2x));
403
            top = Math.min(y, Math.min(cp1y, cp2y));
404
            w = Math.abs(right - left);
405
            h = Math.abs(bottom - top);
406
            pts = [[this._currentX, this._currentY] , [cp1x, cp1y], [cp2x, cp2y], [x, y]];
407
            this._setCurveBoundingBox(pts, w, h);
408
            this._currentX = x;
409
            this._currentY = y;
410
        }
411
    },
412
 
413
    /**
414
     * Draws a quadratic bezier curve.
415
     *
416
     * @method quadraticCurveTo
417
     * @param {Number} cpx x-coordinate for the control point.
418
     * @param {Number} cpy y-coordinate for the control point.
419
     * @param {Number} x x-coordinate for the end point.
420
     * @param {Number} y y-coordinate for the end point.
421
     * @chainable
422
     */
423
    quadraticCurveTo: function() {
424
        this._quadraticCurveTo.apply(this, [Y.Array(arguments), false]);
425
        return this;
426
    },
427
 
428
    /**
429
     * Draws a quadratic bezier curve relative to the current position.
430
     *
431
     * @method relativeQuadraticCurveTo
432
     * @param {Number} cpx x-coordinate for the control point.
433
     * @param {Number} cpy y-coordinate for the control point.
434
     * @param {Number} x x-coordinate for the end point.
435
     * @param {Number} y y-coordinate for the end point.
436
     * @chainable
437
     */
438
    relativeQuadraticCurveTo: function() {
439
        this._quadraticCurveTo.apply(this, [Y.Array(arguments), true]);
440
        return this;
441
    },
442
 
443
    /**
444
     * Implements quadraticCurveTo methods.
445
     *
446
     * @method _quadraticCurveTo
447
     * @param {Array} args The arguments to be used.
448
     * @param {Boolean} relative Indicates whether or not to use relative coordinates.
449
     * @private
450
     */
451
    _quadraticCurveTo: function(args, relative) {
452
        var cpx,
453
            cpy,
454
            x,
455
            y,
456
            w,
457
            h,
458
            pts,
459
            right,
460
            left,
461
            bottom,
462
            top,
463
            i,
464
            len = args.length - 3,
465
            relativeX = relative ? parseFloat(this._currentX) : 0,
466
            relativeY = relative ? parseFloat(this._currentY) : 0;
467
        for(i = 0; i < len; i = i + 4)
468
        {
469
            cpx = parseFloat(args[i]) + relativeX;
470
            cpy = parseFloat(args[i + 1]) + relativeY;
471
            x = parseFloat(args[i + 2]) + relativeX;
472
            y = parseFloat(args[i + 3]) + relativeY;
473
            this._drawingComplete = false;
474
            right = Math.max(x, cpx);
475
            bottom = Math.max(y, cpy);
476
            left = Math.min(x, cpx);
477
            top = Math.min(y, cpy);
478
            w = Math.abs(right - left);
479
            h = Math.abs(bottom - top);
480
            pts = [[this._currentX, this._currentY] , [cpx, cpy], [x, y]];
481
            this._setCurveBoundingBox(pts, w, h);
482
            this._updateDrawingQueue(["quadraticCurveTo", cpx, cpy, x, y]);
483
            this._updateCoords(x, y);
484
        }
485
        return this;
486
    },
487
 
488
    /**
489
     * Draws a circle. Used internally by `CanvasCircle` class.
490
     *
491
     * @method drawCircle
492
     * @param {Number} x y-coordinate
493
     * @param {Number} y x-coordinate
494
     * @param {Number} r radius
495
     * @chainable
496
     * @protected
497
     */
498
	drawCircle: function(x, y, radius) {
499
        var startAngle = 0,
500
            endAngle = 2 * Math.PI,
501
            wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
502
            circum = radius * 2;
503
            circum += wt;
504
        this._drawingComplete = false;
505
        this._trackSize(x + circum, y + circum);
506
        this._trackSize(x - wt, y - wt);
507
        this._updateCoords(x, y);
508
        this._updateDrawingQueue(["arc", x + radius, y + radius, radius, startAngle, endAngle, false]);
509
        return this;
510
    },
511
 
512
    /**
513
     * Draws a diamond.
514
     *
515
     * @method drawDiamond
516
     * @param {Number} x y-coordinate
517
     * @param {Number} y x-coordinate
518
     * @param {Number} width width
519
     * @param {Number} height height
520
     * @chainable
521
     * @protected
522
     */
523
    drawDiamond: function(x, y, width, height)
524
    {
525
        var midWidth = width * 0.5,
526
            midHeight = height * 0.5;
527
        this.moveTo(x + midWidth, y);
528
        this.lineTo(x + width, y + midHeight);
529
        this.lineTo(x + midWidth, y + height);
530
        this.lineTo(x, y + midHeight);
531
        this.lineTo(x + midWidth, y);
532
        return this;
533
    },
534
 
535
    /**
536
     * Draws an ellipse. Used internally by `CanvasEllipse` class.
537
     *
538
     * @method drawEllipse
539
     * @param {Number} x x-coordinate
540
     * @param {Number} y y-coordinate
541
     * @param {Number} w width
542
     * @param {Number} h height
543
     * @chainable
544
     * @protected
545
     */
546
	drawEllipse: function(x, y, w, h) {
547
        var l = 8,
548
            theta = -(45/180) * Math.PI,
549
            angle = 0,
550
            angleMid,
551
            radius = w/2,
552
            yRadius = h/2,
553
            i,
554
            centerX = x + radius,
555
            centerY = y + yRadius,
556
            ax, ay, bx, by, cx, cy,
557
            wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;
558
 
559
        ax = centerX + Math.cos(0) * radius;
560
        ay = centerY + Math.sin(0) * yRadius;
561
        this.moveTo(ax, ay);
562
        for(i = 0; i < l; i++)
563
        {
564
            angle += theta;
565
            angleMid = angle - (theta / 2);
566
            bx = centerX + Math.cos(angle) * radius;
567
            by = centerY + Math.sin(angle) * yRadius;
568
            cx = centerX + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
569
            cy = centerY + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
570
            this._updateDrawingQueue(["quadraticCurveTo", cx, cy, bx, by]);
571
        }
572
        this._trackSize(x + w + wt, y + h + wt);
573
        this._trackSize(x - wt, y - wt);
574
        this._updateCoords(x, y);
575
        return this;
576
    },
577
 
578
    /**
579
     * Draws a rectangle.
580
     *
581
     * @method drawRect
582
     * @param {Number} x x-coordinate
583
     * @param {Number} y y-coordinate
584
     * @param {Number} w width
585
     * @param {Number} h height
586
     * @chainable
587
     */
588
    drawRect: function(x, y, w, h) {
589
        this._drawingComplete = false;
590
        this.moveTo(x, y);
591
        this.lineTo(x + w, y);
592
        this.lineTo(x + w, y + h);
593
        this.lineTo(x, y + h);
594
        this.lineTo(x, y);
595
        return this;
596
    },
597
 
598
    /**
599
     * Draws a rectangle with rounded corners.
600
     *
601
     * @method drawRect
602
     * @param {Number} x x-coordinate
603
     * @param {Number} y y-coordinate
604
     * @param {Number} w width
605
     * @param {Number} h height
606
     * @param {Number} ew width of the ellipse used to draw the rounded corners
607
     * @param {Number} eh height of the ellipse used to draw the rounded corners
608
     * @chainable
609
     */
610
    drawRoundRect: function(x, y, w, h, ew, eh) {
611
        this._drawingComplete = false;
612
        this.moveTo( x, y + eh);
613
        this.lineTo(x, y + h - eh);
614
        this.quadraticCurveTo(x, y + h, x + ew, y + h);
615
        this.lineTo(x + w - ew, y + h);
616
        this.quadraticCurveTo(x + w, y + h, x + w, y + h - eh);
617
        this.lineTo(x + w, y + eh);
618
        this.quadraticCurveTo(x + w, y, x + w - ew, y);
619
        this.lineTo(x + ew, y);
620
        this.quadraticCurveTo(x, y, x, y + eh);
621
        return this;
622
    },
623
 
624
    /**
625
     * Draws a wedge.
626
     *
627
     * @method drawWedge
628
     * @param {Number} x x-coordinate of the wedge's center point
629
     * @param {Number} y y-coordinate of the wedge's center point
630
     * @param {Number} startAngle starting angle in degrees
631
     * @param {Number} arc sweep of the wedge. Negative values draw clockwise.
632
     * @param {Number} radius radius of wedge. If [optional] yRadius is defined, then radius is the x radius.
633
     * @param {Number} yRadius [optional] y radius for wedge.
634
     * @chainable
635
     * @private
636
     */
637
    drawWedge: function(x, y, startAngle, arc, radius, yRadius)
638
    {
639
        var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
640
            segs,
641
            segAngle,
642
            theta,
643
            angle,
644
            angleMid,
645
            ax,
646
            ay,
647
            bx,
648
            by,
649
            cx,
650
            cy,
651
            i = 0;
652
        yRadius = yRadius || radius;
653
 
654
        this._drawingComplete = false;
655
        // move to x,y position
656
        this._updateDrawingQueue(["moveTo", x, y]);
657
 
658
        yRadius = yRadius || radius;
659
 
660
        // limit sweep to reasonable numbers
661
        if(Math.abs(arc) > 360)
662
        {
663
            arc = 360;
664
        }
665
 
666
        // First we calculate how many segments are needed
667
        // for a smooth arc.
668
        segs = Math.ceil(Math.abs(arc) / 45);
669
 
670
        // Now calculate the sweep of each segment.
671
        segAngle = arc / segs;
672
 
673
        // The math requires radians rather than degrees. To convert from degrees
674
        // use the formula (degrees/180)*Math.PI to get radians.
675
        theta = -(segAngle / 180) * Math.PI;
676
 
677
        // convert angle startAngle to radians
678
        angle = (startAngle / 180) * Math.PI;
679
 
680
        // draw the curve in segments no larger than 45 degrees.
681
        if(segs > 0)
682
        {
683
            // draw a line from the center to the start of the curve
684
            ax = x + Math.cos(startAngle / 180 * Math.PI) * radius;
685
            ay = y + Math.sin(startAngle / 180 * Math.PI) * yRadius;
686
            this.lineTo(ax, ay);
687
            // Loop for drawing curve segments
688
            for(i = 0; i < segs; ++i)
689
            {
690
                angle += theta;
691
                angleMid = angle - (theta / 2);
692
                bx = x + Math.cos(angle) * radius;
693
                by = y + Math.sin(angle) * yRadius;
694
                cx = x + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
695
                cy = y + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
696
                this._updateDrawingQueue(["quadraticCurveTo", cx, cy, bx, by]);
697
            }
698
            // close the wedge by drawing a line to the center
699
            this._updateDrawingQueue(["lineTo", x, y]);
700
        }
701
        this._trackSize(-wt , -wt);
702
        this._trackSize((radius * 2) + wt, (radius * 2) + wt);
703
        return this;
704
    },
705
 
706
    /**
707
     * Completes a drawing operation.
708
     *
709
     * @method end
710
     * @chainable
711
     */
712
    end: function() {
713
        this._closePath();
714
        return this;
715
    },
716
 
717
    /**
718
     * Ends a fill and stroke
719
     *
720
     * @method closePath
721
     * @chainable
722
     */
723
    closePath: function()
724
    {
725
        this._updateDrawingQueue(["closePath"]);
726
        this._updateDrawingQueue(["beginPath"]);
727
        return this;
728
    },
729
 
730
	/**
731
	 * Clears the graphics object.
732
	 *
733
	 * @method clear
734
     * @chainable
735
	 */
736
    clear: function() {
737
		this._initProps();
738
        if(this.node)
739
        {
740
            this._context.clearRect(0, 0, this.node.width, this.node.height);
741
        }
742
        return this;
743
	},
744
 
745
 
746
    /**
747
     * Returns a linear gradient fill
748
     *
749
     * @method _getLinearGradient
750
     * @return CanvasGradient
751
     * @private
752
     */
753
    _getLinearGradient: function() {
754
        var isNumber = Y.Lang.isNumber,
755
            fill = this.get("fill"),
756
            stops = fill.stops,
757
            opacity,
758
            color,
759
            stop,
760
            i,
761
            len = stops.length,
762
            gradient,
763
            x = 0,
764
            y = 0,
765
            w = this.get("width"),
766
            h = this.get("height"),
767
            r = fill.rotation || 0,
768
            x1, x2, y1, y2,
769
            cx = x + w/2,
770
            cy = y + h/2,
771
            offset,
772
            radCon = Math.PI/180,
773
            tanRadians = parseFloat(parseFloat(Math.tan(r * radCon)).toFixed(8));
774
        if(Math.abs(tanRadians) * w/2 >= h/2)
775
        {
776
            if(r < 180)
777
            {
778
                y1 = y;
779
                y2 = y + h;
780
            }
781
            else
782
            {
783
                y1 = y + h;
784
                y2 = y;
785
            }
786
            x1 = cx - ((cy - y1)/tanRadians);
787
            x2 = cx - ((cy - y2)/tanRadians);
788
        }
789
        else
790
        {
791
            if(r > 90 && r < 270)
792
            {
793
                x1 = x + w;
794
                x2 = x;
795
            }
796
            else
797
            {
798
                x1 = x;
799
                x2 = x + w;
800
            }
801
            y1 = ((tanRadians * (cx - x1)) - cy) * -1;
802
            y2 = ((tanRadians * (cx - x2)) - cy) * -1;
803
        }
804
        gradient = this._context.createLinearGradient(x1, y1, x2, y2);
805
        for(i = 0; i < len; ++i)
806
        {
807
            stop = stops[i];
808
            opacity = stop.opacity;
809
            color = stop.color;
810
            offset = stop.offset;
811
            if(isNumber(opacity))
812
            {
813
                opacity = Math.max(0, Math.min(1, opacity));
814
                color = this._toRGBA(color, opacity);
815
            }
816
            else
817
            {
818
                color = TORGB(color);
819
            }
820
            offset = stop.offset || i/(len - 1);
821
            gradient.addColorStop(offset, color);
822
        }
823
        return gradient;
824
    },
825
 
826
    /**
827
     * Returns a radial gradient fill
828
     *
829
     * @method _getRadialGradient
830
     * @return CanvasGradient
831
     * @private
832
     */
833
    _getRadialGradient: function() {
834
        var isNumber = Y.Lang.isNumber,
835
            fill = this.get("fill"),
836
            r = fill.r,
837
            fx = fill.fx,
838
            fy = fill.fy,
839
            stops = fill.stops,
840
            opacity,
841
            color,
842
            stop,
843
            i,
844
            len = stops.length,
845
            gradient,
846
            x = 0,
847
            y = 0,
848
            w = this.get("width"),
849
            h = this.get("height"),
850
            x1, x2, y1, y2, r2,
851
            xc, yc, xn, yn, d,
852
            offset,
853
            ratio,
854
            stopMultiplier;
855
        xc = x + w/2;
856
        yc = y + h/2;
857
        x1 = w * fx;
858
        y1 = h * fy;
859
        x2 = x + w/2;
860
        y2 = y + h/2;
861
        r2 = w * r;
862
        d = Math.sqrt( Math.pow(Math.abs(xc - x1), 2) + Math.pow(Math.abs(yc - y1), 2) );
863
        if(d >= r2)
864
        {
865
            ratio = d/r2;
866
            //hack. gradient won't show if it is exactly on the edge of the arc
867
            if(ratio === 1)
868
            {
869
                ratio = 1.01;
870
            }
871
            xn = (x1 - xc)/ratio;
872
            yn = (y1 - yc)/ratio;
873
            xn = xn > 0 ? Math.floor(xn) : Math.ceil(xn);
874
            yn = yn > 0 ? Math.floor(yn) : Math.ceil(yn);
875
            x1 = xc + xn;
876
            y1 = yc + yn;
877
        }
878
 
879
        //If the gradient radius is greater than the circle's, adjusting the radius stretches the gradient properly.
880
        //If the gradient radius is less than the circle's, adjusting the radius of the gradient will not work.
881
        //Instead, adjust the color stops to reflect the smaller radius.
882
        if(r >= 0.5)
883
        {
884
            gradient = this._context.createRadialGradient(x1, y1, r, x2, y2, r * w);
885
            stopMultiplier = 1;
886
        }
887
        else
888
        {
889
            gradient = this._context.createRadialGradient(x1, y1, r, x2, y2, w/2);
890
            stopMultiplier = r * 2;
891
        }
892
        for(i = 0; i < len; ++i)
893
        {
894
            stop = stops[i];
895
            opacity = stop.opacity;
896
            color = stop.color;
897
            offset = stop.offset;
898
            if(isNumber(opacity))
899
            {
900
                opacity = Math.max(0, Math.min(1, opacity));
901
                color = this._toRGBA(color, opacity);
902
            }
903
            else
904
            {
905
                color = TORGB(color);
906
            }
907
            offset = stop.offset || i/(len - 1);
908
            offset *= stopMultiplier;
909
            if(offset <= 1)
910
            {
911
                gradient.addColorStop(offset, color);
912
            }
913
        }
914
        return gradient;
915
    },
916
 
917
 
918
    /**
919
     * Clears all values
920
     *
921
     * @method _initProps
922
     * @private
923
     */
924
    _initProps: function() {
925
        this._methods = [];
926
        this._lineToMethods = [];
927
        this._xcoords = [0];
928
		this._ycoords = [0];
929
		this._width = 0;
930
        this._height = 0;
931
        this._left = 0;
932
        this._top = 0;
933
        this._right = 0;
934
        this._bottom = 0;
935
        this._currentX = 0;
936
        this._currentY = 0;
937
    },
938
 
939
    /**
940
     * Indicates a drawing has completed.
941
     *
942
     * @property _drawingComplete
943
     * @type Boolean
944
     * @private
945
     */
946
    _drawingComplete: false,
947
 
948
    /**
949
     * Creates canvas element
950
     *
951
     * @method _createGraphic
952
     * @return HTMLCanvasElement
953
     * @private
954
     */
955
    _createGraphic: function() {
956
        var graphic = Y.config.doc.createElement('canvas');
957
        return graphic;
958
    },
959
 
960
    /**
961
     * Returns the points on a curve
962
     *
963
     * @method getBezierData
964
     * @param Array points Array containing the begin, end and control points of a curve.
965
     * @param Number t The value for incrementing the next set of points.
966
     * @return Array
967
     * @private
968
     */
969
    getBezierData: function(points, t) {
970
        var n = points.length,
971
            tmp = [],
972
            i,
973
            j;
974
 
975
        for (i = 0; i < n; ++i){
976
            tmp[i] = [points[i][0], points[i][1]]; // save input
977
        }
978
 
979
        for (j = 1; j < n; ++j) {
980
            for (i = 0; i < n - j; ++i) {
981
                tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
982
                tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
983
            }
984
        }
985
        return [ tmp[0][0], tmp[0][1] ];
986
    },
987
 
988
    /**
989
     * Calculates the bounding box for a curve
990
     *
991
     * @method _setCurveBoundingBox
992
     * @param Array pts Array containing points for start, end and control points of a curve.
993
     * @param Number w Width used to calculate the number of points to describe the curve.
994
     * @param Number h Height used to calculate the number of points to describe the curve.
995
     * @private
996
     */
997
    _setCurveBoundingBox: function(pts, w, h)
998
    {
999
        var i = 0,
1000
            left = this._currentX,
1001
            right = left,
1002
            top = this._currentY,
1003
            bottom = top,
1004
            len = Math.round(Math.sqrt((w * w) + (h * h))),
1005
            t = 1/len,
1006
            wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
1007
            xy;
1008
        for(i = 0; i < len; ++i)
1009
        {
1010
            xy = this.getBezierData(pts, t * i);
1011
            left = isNaN(left) ? xy[0] : Math.min(xy[0], left);
1012
            right = isNaN(right) ? xy[0] : Math.max(xy[0], right);
1013
            top = isNaN(top) ? xy[1] : Math.min(xy[1], top);
1014
            bottom = isNaN(bottom) ? xy[1] : Math.max(xy[1], bottom);
1015
        }
1016
        left = Math.round(left * 10)/10;
1017
        right = Math.round(right * 10)/10;
1018
        top = Math.round(top * 10)/10;
1019
        bottom = Math.round(bottom * 10)/10;
1020
        this._trackSize(right + wt, bottom + wt);
1021
        this._trackSize(left - wt, top - wt);
1022
    },
1023
 
1024
    /**
1025
     * Updates the size of the graphics object
1026
     *
1027
     * @method _trackSize
1028
     * @param {Number} w width
1029
     * @param {Number} h height
1030
     * @private
1031
     */
1032
    _trackSize: function(w, h) {
1033
        if (w > this._right) {
1034
            this._right = w;
1035
        }
1036
        if(w < this._left)
1037
        {
1038
            this._left = w;
1039
        }
1040
        if (h < this._top)
1041
        {
1042
            this._top = h;
1043
        }
1044
        if (h > this._bottom)
1045
        {
1046
            this._bottom = h;
1047
        }
1048
        this._width = this._right - this._left;
1049
        this._height = this._bottom - this._top;
1050
    }
1051
};
1052
Y.CanvasDrawing = CanvasDrawing;
1053
/**
1054
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Shape.html">`Shape`</a> class.
1055
 * `CanvasShape` is not intended to be used directly. Instead, use the <a href="Shape.html">`Shape`</a> class.
1056
 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
1057
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Shape.html">`Shape`</a>
1058
 * class will point to the `CanvasShape` class.
1059
 *
1060
 * @module graphics
1061
 * @class CanvasShape
1062
 * @constructor
1063
 */
1064
CanvasShape = function()
1065
{
1066
    this._transforms = [];
1067
    this.matrix = new Y.Matrix();
1068
    CanvasShape.superclass.constructor.apply(this, arguments);
1069
};
1070
 
1071
CanvasShape.NAME = "shape";
1072
 
1073
Y.extend(CanvasShape, Y.GraphicBase, Y.mix({
1074
    /**
1075
     * Init method, invoked during construction.
1076
     * Calls `initializer` method.
1077
     *
1078
     * @method init
1079
     * @protected
1080
     */
1081
    init: function()
1082
	{
1083
		this.initializer.apply(this, arguments);
1084
	},
1085
 
1086
	/**
1087
	 * Initializes the shape
1088
	 *
1089
	 * @private
1090
	 * @method _initialize
1091
	 */
1092
	initializer: function(cfg)
1093
	{
1094
		var host = this,
1095
            graphic = cfg.graphic,
1096
            data = this.get("data");
1097
        host._initProps();
1098
		host.createNode();
1099
		host._xcoords = [0];
1100
		host._ycoords = [0];
1101
        if(graphic)
1102
        {
1103
            this._setGraphic(graphic);
1104
        }
1105
        if(data)
1106
        {
1107
            host._parsePathData(data);
1108
        }
1109
		host._updateHandler();
1110
	},
1111
 
1112
    /**
1113
     * Set the Graphic instance for the shape.
1114
     *
1115
     * @method _setGraphic
1116
     * @param {Graphic | Node | HTMLElement | String} render This param is used to determine the graphic instance. If it is a
1117
     * `Graphic` instance, it will be assigned to the `graphic` attribute. Otherwise, a new Graphic instance will be created
1118
     * and rendered into the dom element that the render represents.
1119
     * @private
1120
     */
1121
    _setGraphic: function(render)
1122
    {
1123
        var graphic;
1124
        if(render instanceof Y.CanvasGraphic)
1125
        {
1126
            this._graphic = render;
1127
        }
1128
        else
1129
        {
1130
            graphic = new Y.CanvasGraphic({
1131
                render: render
1132
            });
1133
            graphic._appendShape(this);
1134
            this._graphic = graphic;
1135
        }
1136
    },
1137
 
1138
	/**
1139
	 * Add a class name to each node.
1140
	 *
1141
	 * @method addClass
1142
	 * @param {String} className the class name to add to the node's class attribute
1143
	 */
1144
	addClass: function(className)
1145
	{
1146
		var node = this.get("node");
1147
		Y.DOM.addClass(node, className);
1148
	},
1149
 
1150
	/**
1151
	 * Removes a class name from each node.
1152
	 *
1153
	 * @method removeClass
1154
	 * @param {String} className the class name to remove from the node's class attribute
1155
	 */
1156
	removeClass: function(className)
1157
	{
1158
		var node = this.get("node");
1159
		Y.DOM.removeClass(node, className);
1160
	},
1161
 
1162
	/**
1163
	 * Gets the current position of the node in page coordinates.
1164
	 *
1165
	 * @method getXY
1166
	 * @return Array The XY position of the shape.
1167
	 */
1168
	getXY: function()
1169
	{
1170
		var graphic = this.get("graphic"),
1171
			parentXY = graphic.getXY(),
1172
			x = this.get("x"),
1173
			y = this.get("y");
1174
		return [parentXY[0] + x, parentXY[1] + y];
1175
	},
1176
 
1177
	/**
1178
	 * Set the position of the shape in page coordinates, regardless of how the node is positioned.
1179
	 *
1180
	 * @method setXY
1181
	 * @param {Array} Contains X & Y values for new position (coordinates are page-based)
1182
	 */
1183
	setXY: function(xy)
1184
	{
1185
		var graphic = this.get("graphic"),
1186
			parentXY = graphic.getXY(),
1187
			x = xy[0] - parentXY[0],
1188
			y = xy[1] - parentXY[1];
1189
		this._set("x", x);
1190
		this._set("y", y);
1191
		this._updateNodePosition(x, y);
1192
	},
1193
 
1194
	/**
1195
	 * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
1196
	 *
1197
	 * @method contains
1198
	 * @param {CanvasShape | HTMLElement} needle The possible node or descendent
1199
	 * @return Boolean Whether or not this shape is the needle or its ancestor.
1200
	 */
1201
	contains: function(needle)
1202
	{
1203
		var node = needle instanceof Y.Node ? needle._node : needle;
1204
        return node === this.node;
1205
	},
1206
 
1207
	/**
1208
	 * Test if the supplied node matches the supplied selector.
1209
	 *
1210
	 * @method test
1211
	 * @param {String} selector The CSS selector to test against.
1212
	 * @return Boolean Wheter or not the shape matches the selector.
1213
	 */
1214
	test: function(selector)
1215
	{
1216
		return Y.Selector.test(this.node, selector);
1217
	},
1218
 
1219
	/**
1220
	 * Compares nodes to determine if they match.
1221
	 * Node instances can be compared to each other and/or HTMLElements.
1222
	 * @method compareTo
1223
	 * @param {HTMLElement | Node} refNode The reference node to compare to the node.
1224
	 * @return {Boolean} True if the nodes match, false if they do not.
1225
	 */
1226
	compareTo: function(refNode) {
1227
		var node = this.node;
1228
		return node === refNode;
1229
	},
1230
 
1231
	/**
1232
	 * Value function for fill attribute
1233
	 *
1234
	 * @method _getDefaultFill
1235
	 * @return Object
1236
	 * @private
1237
	 */
1238
	_getDefaultFill: function() {
1239
		return {
1240
			type: "solid",
1241
			opacity: 1,
1242
			cx: 0.5,
1243
			cy: 0.5,
1244
			fx: 0.5,
1245
			fy: 0.5,
1246
			r: 0.5
1247
		};
1248
	},
1249
 
1250
	/**
1251
	 * Value function for stroke attribute
1252
	 *
1253
	 * @method _getDefaultStroke
1254
	 * @return Object
1255
	 * @private
1256
	 */
1257
	_getDefaultStroke: function()
1258
	{
1259
		return {
1260
			weight: 1,
1261
			dashstyle: "none",
1262
			color: "#000",
1263
			opacity: 1.0
1264
		};
1265
	},
1266
 
1267
	/**
1268
	 * Left edge of the path
1269
	 *
1270
     * @property _left
1271
     * @type Number
1272
	 * @private
1273
	 */
1274
	_left: 0,
1275
 
1276
	/**
1277
	 * Right edge of the path
1278
	 *
1279
     * @property _right
1280
     * @type Number
1281
	 * @private
1282
	 */
1283
	_right: 0,
1284
 
1285
	/**
1286
	 * Top edge of the path
1287
	 *
1288
     * @property _top
1289
     * @type Number
1290
	 * @private
1291
	 */
1292
	_top: 0,
1293
 
1294
	/**
1295
	 * Bottom edge of the path
1296
	 *
1297
     * @property _bottom
1298
     * @type Number
1299
	 * @private
1300
	 */
1301
	_bottom: 0,
1302
 
1303
	/**
1304
	 * Creates the dom node for the shape.
1305
	 *
1306
     * @method createNode
1307
	 * @return HTMLElement
1308
	 * @private
1309
	 */
1310
	createNode: function()
1311
	{
1312
		var host = this,
1313
            node = Y.config.doc.createElement('canvas'),
1314
			id = host.get("id"),
1315
            concat = host._camelCaseConcat,
1316
            name = host.name;
1317
		host._context = node.getContext('2d');
1318
		node.setAttribute("overflow", "visible");
1319
        node.style.overflow = "visible";
1320
        if(!host.get("visible"))
1321
        {
1322
            node.style.visibility = "hidden";
1323
        }
1324
		node.setAttribute("id", id);
1325
		id = "#" + id;
1326
        host.node = node;
1327
		host.addClass(
1328
            _getClassName(SHAPE) +
1329
            " " +
1330
            _getClassName(concat(IMPLEMENTATION, SHAPE)) +
1331
            " " +
1332
            _getClassName(name) +
1333
            " " +
1334
            _getClassName(concat(IMPLEMENTATION, name))
1335
        );
1336
	},
1337
 
1338
	/**
1339
     * Overrides default `on` method. Checks to see if its a dom interaction event. If so,
1340
     * return an event attached to the `node` element. If not, return the normal functionality.
1341
     *
1342
     * @method on
1343
     * @param {String} type event type
1344
     * @param {Object} callback function
1345
	 * @private
1346
	 */
1347
	on: function(type, fn)
1348
	{
1349
		if(Y.Node.DOM_EVENTS[type])
1350
		{
1351
            return Y.on(type, fn, "#" + this.get("id"));
1352
		}
1353
		return Y.on.apply(this, arguments);
1354
	},
1355
 
1356
	/**
1357
	 * Adds a stroke to the shape node.
1358
	 *
1359
	 * @method _strokeChangeHandler
1360
     * @param {Object} stroke Properties of the `stroke` attribute.
1361
	 * @private
1362
	 */
1363
	_setStrokeProps: function(stroke)
1364
	{
1365
		var color,
1366
			weight,
1367
			opacity,
1368
			linejoin,
1369
			linecap,
1370
			dashstyle;
1371
        if(stroke)
1372
        {
1373
            color = stroke.color;
1374
            weight = PARSE_FLOAT(stroke.weight);
1375
            opacity = PARSE_FLOAT(stroke.opacity);
1376
            linejoin = stroke.linejoin || "round";
1377
            linecap = stroke.linecap || "butt";
1378
            dashstyle = stroke.dashstyle;
1379
            this._miterlimit = null;
1380
            this._dashstyle = (dashstyle && Y.Lang.isArray(dashstyle) && dashstyle.length > 1) ? dashstyle : null;
1381
            this._strokeWeight = weight;
1382
 
1383
            if (IS_NUMBER(weight) && weight > 0)
1384
            {
1385
                this._stroke = 1;
1386
            }
1387
            else
1388
            {
1389
                this._stroke = 0;
1390
            }
1391
            if (IS_NUMBER(opacity)) {
1392
                this._strokeStyle = this._toRGBA(color, opacity);
1393
            }
1394
            else
1395
            {
1396
                this._strokeStyle = color;
1397
            }
1398
            this._linecap = linecap;
1399
            if(linejoin === "round" || linejoin === "bevel")
1400
            {
1401
                this._linejoin = linejoin;
1402
            }
1403
            else
1404
            {
1405
                linejoin = parseInt(linejoin, 10);
1406
                if(IS_NUMBER(linejoin))
1407
                {
1408
                    this._miterlimit =  Math.max(linejoin, 1);
1409
                    this._linejoin = "miter";
1410
                }
1411
            }
1412
        }
1413
        else
1414
        {
1415
            this._stroke = 0;
1416
        }
1417
	},
1418
 
1419
    /**
1420
     * Sets the value of an attribute.
1421
     *
1422
     * @method set
1423
     * @param {String|Object} name The name of the attribute. Alternatively, an object of key value pairs can
1424
     * be passed in to set multiple attributes at once.
1425
     * @param {Any} value The value to set the attribute to. This value is ignored if an object is received as
1426
     * the name param.
1427
     */
1428
	set: function()
1429
	{
1430
		var host = this;
1431
		AttributeLite.prototype.set.apply(host, arguments);
1432
		if(host.initialized)
1433
		{
1434
			host._updateHandler();
1435
		}
1436
	},
1437
 
1438
	/**
1439
	 * Adds a fill to the shape node.
1440
	 *
1441
	 * @method _setFillProps
1442
     * @param {Object} fill Properties of the `fill` attribute.
1443
	 * @private
1444
	 */
1445
	_setFillProps: function(fill)
1446
	{
1447
		var isNumber = IS_NUMBER,
1448
			color,
1449
			opacity,
1450
			type;
1451
        if(fill)
1452
        {
1453
            color = fill.color;
1454
            type = fill.type;
1455
            if(type === "linear" || type === "radial")
1456
            {
1457
                this._fillType = type;
1458
            }
1459
            else if(color)
1460
            {
1461
                opacity = fill.opacity;
1462
                if (isNumber(opacity))
1463
                {
1464
                    opacity = Math.max(0, Math.min(1, opacity));
1465
                    color = this._toRGBA(color, opacity);
1466
                }
1467
                else
1468
                {
1469
                    color = TORGB(color);
1470
                }
1471
 
1472
                this._fillColor = color;
1473
                this._fillType = 'solid';
1474
            }
1475
            else
1476
            {
1477
                this._fillColor = null;
1478
            }
1479
        }
1480
		else
1481
		{
1482
            this._fillType = null;
1483
			this._fillColor = null;
1484
		}
1485
	},
1486
 
1487
	/**
1488
	 * Specifies a 2d translation.
1489
	 *
1490
	 * @method translate
1491
	 * @param {Number} x The value to transate on the x-axis.
1492
	 * @param {Number} y The value to translate on the y-axis.
1493
	 */
1494
	translate: function(x, y)
1495
	{
1496
		this._translateX += x;
1497
		this._translateY += y;
1498
		this._addTransform("translate", arguments);
1499
	},
1500
 
1501
	/**
1502
	 * Translates the shape along the x-axis. When translating x and y coordinates,
1503
	 * use the `translate` method.
1504
	 *
1505
	 * @method translateX
1506
	 * @param {Number} x The value to translate.
1507
	 */
1508
	translateX: function(x)
1509
    {
1510
        this._translateX += x;
1511
        this._addTransform("translateX", arguments);
1512
    },
1513
 
1514
	/**
1515
	 * Performs a translate on the y-coordinate. When translating x and y coordinates,
1516
	 * use the `translate` method.
1517
	 *
1518
	 * @method translateY
1519
	 * @param {Number} y The value to translate.
1520
	 */
1521
	translateY: function(y)
1522
    {
1523
        this._translateY += y;
1524
        this._addTransform("translateY", arguments);
1525
    },
1526
 
1527
    /**
1528
     * Skews the shape around the x-axis and y-axis.
1529
     *
1530
     * @method skew
1531
     * @param {Number} x The value to skew on the x-axis.
1532
     * @param {Number} y The value to skew on the y-axis.
1533
     */
1534
    skew: function()
1535
    {
1536
        this._addTransform("skew", arguments);
1537
    },
1538
 
1539
	/**
1540
	 * Skews the shape around the x-axis.
1541
	 *
1542
	 * @method skewX
1543
	 * @param {Number} x x-coordinate
1544
	 */
1545
    skewX: function()
1546
    {
1547
        this._addTransform("skewX", arguments);
1548
    },
1549
 
1550
	/**
1551
	 * Skews the shape around the y-axis.
1552
	 *
1553
	 * @method skewY
1554
	 * @param {Number} y y-coordinate
1555
	 */
1556
    skewY: function()
1557
    {
1558
        this._addTransform("skewY", arguments);
1559
    },
1560
 
1561
	/**
1562
	 * Rotates the shape clockwise around it transformOrigin.
1563
	 *
1564
	 * @method rotate
1565
	 * @param {Number} deg The degree of the rotation.
1566
	 */
1567
    rotate: function()
1568
    {
1569
        this._addTransform("rotate", arguments);
1570
    },
1571
 
1572
	/**
1573
	 * Specifies a 2d scaling operation.
1574
	 *
1575
	 * @method scale
1576
	 * @param {Number} val
1577
	 */
1578
    scale: function()
1579
    {
1580
        this._addTransform("scale", arguments);
1581
    },
1582
 
1583
    /**
1584
     * Storage for the transform attribute.
1585
     *
1586
     * @property _transform
1587
     * @type String
1588
     * @private
1589
     */
1590
    _transform: "",
1591
 
1592
    /**
1593
     * Adds a transform to the shape.
1594
     *
1595
     * @method _addTransform
1596
     * @param {String} type The transform being applied.
1597
     * @param {Array} args The arguments for the transform.
1598
	 * @private
1599
	 */
1600
	_addTransform: function(type, args)
1601
	{
1602
        args = Y.Array(args);
1603
        this._transform = Y_LANG.trim(this._transform + " " + type + "(" + args.join(", ") + ")");
1604
        args.unshift(type);
1605
        this._transforms.push(args);
1606
        if(this.initialized)
1607
        {
1608
            this._updateTransform();
1609
        }
1610
	},
1611
 
1612
	/**
1613
     * Applies all transforms.
1614
     *
1615
     * @method _updateTransform
1616
	 * @private
1617
	 */
1618
	_updateTransform: function()
1619
	{
1620
		var node = this.node,
1621
			key,
1622
			transform,
1623
			transformOrigin = this.get("transformOrigin"),
1624
            matrix = this.matrix,
1625
            i,
1626
            len = this._transforms.length;
1627
 
1628
        if(this._transforms && this._transforms.length > 0)
1629
        {
1630
            for(i = 0; i < len; ++i)
1631
            {
1632
                key = this._transforms[i].shift();
1633
                if(key)
1634
                {
1635
                    matrix[key].apply(matrix, this._transforms[i]);
1636
                }
1637
            }
1638
            transform = matrix.toCSSText();
1639
        }
1640
 
1641
        this._graphic.addToRedrawQueue(this);
1642
		transformOrigin = (100 * transformOrigin[0]) + "% " + (100 * transformOrigin[1]) + "%";
1643
        Y_DOM.setStyle(node, "transformOrigin", transformOrigin);
1644
        if(transform)
1645
		{
1646
            Y_DOM.setStyle(node, "transform", transform);
1647
		}
1648
        this._transforms = [];
1649
	},
1650
 
1651
	/**
1652
     * Updates `Shape` based on attribute changes.
1653
     *
1654
     * @method _updateHandler
1655
	 * @private
1656
	 */
1657
	_updateHandler: function()
1658
	{
1659
		this._draw();
1660
		this._updateTransform();
1661
	},
1662
 
1663
	/**
1664
	 * Updates the shape.
1665
	 *
1666
	 * @method _draw
1667
	 * @private
1668
	 */
1669
	_draw: function()
1670
	{
1671
        var node = this.node;
1672
        this.clear();
1673
		this._closePath();
1674
		node.style.left = this.get("x") + "px";
1675
		node.style.top = this.get("y") + "px";
1676
	},
1677
 
1678
	/**
1679
	 * Completes a shape or drawing
1680
	 *
1681
	 * @method _closePath
1682
	 * @private
1683
	 */
1684
	_closePath: function()
1685
	{
1686
		if(!this._methods)
1687
		{
1688
			return;
1689
		}
1690
		var node = this.get("node"),
1691
			w = this._right - this._left,
1692
			h = this._bottom - this._top,
1693
			context = this._context,
1694
			methods = [],
1695
			cachedMethods = this._methods.concat(),
1696
			i,
1697
			j,
1698
			method,
1699
			args,
1700
            argsLen,
1701
			len = 0;
1702
		this._context.clearRect(0, 0, node.width, node.height);
1703
        if(this._methods)
1704
        {
1705
			len = cachedMethods.length;
1706
			if(!len || len < 1)
1707
			{
1708
				return;
1709
			}
1710
			for(i = 0; i < len; ++i)
1711
			{
1712
				methods[i] = cachedMethods[i].concat();
1713
				args = methods[i];
1714
                argsLen = (args[0] === "quadraticCurveTo" || args[0] === "bezierCurveTo") ? args.length : 3;
1715
				for(j = 1; j < argsLen; ++j)
1716
				{
1717
					if(j % 2 === 0)
1718
					{
1719
						args[j] = args[j] - this._top;
1720
					}
1721
					else
1722
					{
1723
						args[j] = args[j] - this._left;
1724
					}
1725
				}
1726
			}
1727
            node.setAttribute("width", Math.min(w, 2000));
1728
            node.setAttribute("height", Math.min(2000, h));
1729
            context.beginPath();
1730
			for(i = 0; i < len; ++i)
1731
			{
1732
				args = methods[i].concat();
1733
				if(args && args.length > 0)
1734
				{
1735
					method = args.shift();
1736
					if(method)
1737
					{
1738
                        if(method === "closePath")
1739
                        {
1740
                            context.closePath();
1741
                            this._strokeAndFill(context);
1742
                        }
1743
						else if(method && method === "lineTo" && this._dashstyle)
1744
						{
1745
							args.unshift(this._xcoords[i] - this._left, this._ycoords[i] - this._top);
1746
							this._drawDashedLine.apply(this, args);
1747
						}
1748
						else
1749
						{
1750
                            context[method].apply(context, args);
1751
						}
1752
					}
1753
				}
1754
			}
1755
 
1756
            this._strokeAndFill(context);
1757
			this._drawingComplete = true;
1758
			this._clearAndUpdateCoords();
1759
			this._updateNodePosition();
1760
			this._methods = cachedMethods;
1761
		}
1762
	},
1763
 
1764
    /**
1765
     * Completes a stroke and/or fill operation on the context.
1766
     *
1767
     * @method _strokeAndFill
1768
     * @param {Context} Reference to the context element of the canvas instance.
1769
     * @private
1770
     */
1771
    _strokeAndFill: function(context)
1772
    {
1773
        if (this._fillType)
1774
        {
1775
            if(this._fillType === "linear")
1776
            {
1777
                context.fillStyle = this._getLinearGradient();
1778
            }
1779
            else if(this._fillType === "radial")
1780
            {
1781
                context.fillStyle = this._getRadialGradient();
1782
            }
1783
            else
1784
            {
1785
                context.fillStyle = this._fillColor;
1786
            }
1787
            context.closePath();
1788
            context.fill();
1789
        }
1790
 
1791
        if (this._stroke) {
1792
            if(this._strokeWeight)
1793
            {
1794
                context.lineWidth = this._strokeWeight;
1795
            }
1796
            context.lineCap = this._linecap;
1797
            context.lineJoin = this._linejoin;
1798
            if(this._miterlimit)
1799
            {
1800
                context.miterLimit = this._miterlimit;
1801
            }
1802
            context.strokeStyle = this._strokeStyle;
1803
            context.stroke();
1804
        }
1805
    },
1806
 
1807
	/**
1808
	 * Draws a dashed line between two points.
1809
	 *
1810
	 * @method _drawDashedLine
1811
	 * @param {Number} xStart	The x position of the start of the line
1812
	 * @param {Number} yStart	The y position of the start of the line
1813
	 * @param {Number} xEnd		The x position of the end of the line
1814
	 * @param {Number} yEnd		The y position of the end of the line
1815
	 * @private
1816
	 */
1817
	_drawDashedLine: function(xStart, yStart, xEnd, yEnd)
1818
	{
1819
		var context = this._context,
1820
			dashsize = this._dashstyle[0],
1821
			gapsize = this._dashstyle[1],
1822
			segmentLength = dashsize + gapsize,
1823
			xDelta = xEnd - xStart,
1824
			yDelta = yEnd - yStart,
1825
			delta = Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2)),
1826
			segmentCount = Math.floor(Math.abs(delta / segmentLength)),
1827
			radians = Math.atan2(yDelta, xDelta),
1828
			xCurrent = xStart,
1829
			yCurrent = yStart,
1830
			i;
1831
		xDelta = Math.cos(radians) * segmentLength;
1832
		yDelta = Math.sin(radians) * segmentLength;
1833
 
1834
		for(i = 0; i < segmentCount; ++i)
1835
		{
1836
			context.moveTo(xCurrent, yCurrent);
1837
			context.lineTo(xCurrent + Math.cos(radians) * dashsize, yCurrent + Math.sin(radians) * dashsize);
1838
			xCurrent += xDelta;
1839
			yCurrent += yDelta;
1840
		}
1841
 
1842
		context.moveTo(xCurrent, yCurrent);
1843
		delta = Math.sqrt((xEnd - xCurrent) * (xEnd - xCurrent) + (yEnd - yCurrent) * (yEnd - yCurrent));
1844
 
1845
		if(delta > dashsize)
1846
		{
1847
			context.lineTo(xCurrent + Math.cos(radians) * dashsize, yCurrent + Math.sin(radians) * dashsize);
1848
		}
1849
		else if(delta > 0)
1850
		{
1851
			context.lineTo(xCurrent + Math.cos(radians) * delta, yCurrent + Math.sin(radians) * delta);
1852
		}
1853
 
1854
		context.moveTo(xEnd, yEnd);
1855
	},
1856
 
1857
	/**
1858
	 * Returns the bounds for a shape.
1859
	 *
1860
     * Calculates the a new bounding box from the original corner coordinates (base on size and position) and the transform matrix.
1861
     * The calculated bounding box is used by the graphic instance to calculate its viewBox.
1862
     *
1863
	 * @method getBounds
1864
	 * @return Object
1865
	 */
1866
	getBounds: function()
1867
	{
1868
		var type = this._type,
1869
			w = this.get("width"),
1870
			h = this.get("height"),
1871
			x = this.get("x"),
1872
			y = this.get("y");
1873
        if(type === "path")
1874
        {
1875
            x = x + this._left;
1876
            y = y + this._top;
1877
            w = this._right - this._left;
1878
            h = this._bottom - this._top;
1879
        }
1880
        return this._getContentRect(w, h, x, y);
1881
	},
1882
 
1883
    /**
1884
     * Calculates the bounding box for the shape.
1885
     *
1886
     * @method _getContentRect
1887
     * @param {Number} w width of the shape
1888
     * @param {Number} h height of the shape
1889
     * @param {Number} x x-coordinate of the shape
1890
     * @param {Number} y y-coordinate of the shape
1891
     * @private
1892
     */
1893
    _getContentRect: function(w, h, x, y)
1894
    {
1895
        var transformOrigin = this.get("transformOrigin"),
1896
            transformX = transformOrigin[0] * w,
1897
            transformY = transformOrigin[1] * h,
1898
            transforms = this.matrix.getTransformArray(this.get("transform")),
1899
            matrix = new Y.Matrix(),
1900
            i,
1901
            len = transforms.length,
1902
            transform,
1903
            key,
1904
            contentRect;
1905
        if(this._type === "path")
1906
        {
1907
            transformX = transformX + x;
1908
            transformY = transformY + y;
1909
        }
1910
        transformX = !isNaN(transformX) ? transformX : 0;
1911
        transformY = !isNaN(transformY) ? transformY : 0;
1912
        matrix.translate(transformX, transformY);
1913
        for(i = 0; i < len; i = i + 1)
1914
        {
1915
            transform = transforms[i];
1916
            key = transform.shift();
1917
            if(key)
1918
            {
1919
                matrix[key].apply(matrix, transform);
1920
            }
1921
        }
1922
        matrix.translate(-transformX, -transformY);
1923
        contentRect = matrix.getContentRect(w, h, x, y);
1924
        return contentRect;
1925
    },
1926
 
1927
    /**
1928
     * Places the shape above all other shapes.
1929
     *
1930
     * @method toFront
1931
     */
1932
    toFront: function()
1933
    {
1934
        var graphic = this.get("graphic");
1935
        if(graphic)
1936
        {
1937
            graphic._toFront(this);
1938
        }
1939
    },
1940
 
1941
    /**
1942
     * Places the shape underneath all other shapes.
1943
     *
1944
     * @method toFront
1945
     */
1946
    toBack: function()
1947
    {
1948
        var graphic = this.get("graphic");
1949
        if(graphic)
1950
        {
1951
            graphic._toBack(this);
1952
        }
1953
    },
1954
 
1955
    /**
1956
     * Parses path data string and call mapped methods.
1957
     *
1958
     * @method _parsePathData
1959
     * @param {String} val The path data
1960
     * @private
1961
     */
1962
    _parsePathData: function(val)
1963
    {
1964
        var method,
1965
            methodSymbol,
1966
            args,
1967
            commandArray = Y.Lang.trim(val.match(SPLITPATHPATTERN)),
1968
            i,
1969
            len,
1970
            str,
1971
            symbolToMethod = this._pathSymbolToMethod;
1972
        if(commandArray)
1973
        {
1974
            this.clear();
1975
            len = commandArray.length || 0;
1976
            for(i = 0; i < len; i = i + 1)
1977
            {
1978
                str = commandArray[i];
1979
                methodSymbol = str.substr(0, 1);
1980
                args = str.substr(1).match(SPLITARGSPATTERN);
1981
                method = symbolToMethod[methodSymbol];
1982
                if(method)
1983
                {
1984
                    if(args)
1985
                    {
1986
                        this[method].apply(this, args);
1987
                    }
1988
                    else
1989
                    {
1990
                        this[method].apply(this);
1991
                    }
1992
                }
1993
            }
1994
            this.end();
1995
        }
1996
    },
1997
 
1998
    /**
1999
     * Destroys the shape instance.
2000
     *
2001
     * @method destroy
2002
     */
2003
    destroy: function()
2004
    {
2005
        var graphic = this.get("graphic");
2006
        if(graphic)
2007
        {
2008
            graphic.removeShape(this);
2009
        }
2010
        else
2011
        {
2012
            this._destroy();
2013
        }
2014
    },
2015
 
2016
    /**
2017
     *  Implementation for shape destruction
2018
     *
2019
     *  @method destroy
2020
     *  @protected
2021
     */
2022
    _destroy: function()
2023
    {
2024
        if(this.node)
2025
        {
2026
            Y.Event.purgeElement(this.node, true);
2027
            if(this.node.parentNode)
2028
            {
2029
                this.node.style.visibility = "";
2030
                this.node.parentNode.removeChild(this.node);
2031
            }
2032
            this._context = null;
2033
            this.node = null;
2034
        }
2035
    }
2036
}, Y.CanvasDrawing.prototype));
2037
 
2038
CanvasShape.ATTRS =  {
2039
	/**
2040
	 * An array of x, y values which indicates the transformOrigin in which to rotate the shape. Valid values range between 0 and 1 representing a
2041
	 * fraction of the shape's corresponding bounding box dimension. The default value is [0.5, 0.5].
2042
	 *
2043
	 * @config transformOrigin
2044
	 * @type Array
2045
	 */
2046
	transformOrigin: {
2047
		valueFn: function()
2048
		{
2049
			return [0.5, 0.5];
2050
		}
2051
	},
2052
 
2053
    /**
2054
     * <p>A string containing, in order, transform operations applied to the shape instance. The `transform` string can contain the following values:
2055
     *
2056
     *    <dl>
2057
     *        <dt>rotate</dt><dd>Rotates the shape clockwise around it transformOrigin.</dd>
2058
     *        <dt>translate</dt><dd>Specifies a 2d translation.</dd>
2059
     *        <dt>skew</dt><dd>Skews the shape around the x-axis and y-axis.</dd>
2060
     *        <dt>scale</dt><dd>Specifies a 2d scaling operation.</dd>
2061
     *        <dt>translateX</dt><dd>Translates the shape along the x-axis.</dd>
2062
     *        <dt>translateY</dt><dd>Translates the shape along the y-axis.</dd>
2063
     *        <dt>skewX</dt><dd>Skews the shape around the x-axis.</dd>
2064
     *        <dt>skewY</dt><dd>Skews the shape around the y-axis.</dd>
2065
     *        <dt>matrix</dt><dd>Specifies a 2D transformation matrix comprised of the specified six values.</dd>
2066
     *    </dl>
2067
     * </p>
2068
     * <p>Applying transforms through the transform attribute will reset the transform matrix and apply a new transform. The shape class also contains
2069
     * corresponding methods for each transform that will apply the transform to the current matrix. The below code illustrates how you might use the
2070
     * `transform` attribute to instantiate a recangle with a rotation of 45 degrees.</p>
2071
            var myRect = new Y.Rect({
2072
                type:"rect",
2073
                width: 50,
2074
                height: 40,
2075
                transform: "rotate(45)"
2076
            };
2077
     * <p>The code below would apply `translate` and `rotate` to an existing shape.</p>
2078
 
2079
        myRect.set("transform", "translate(40, 50) rotate(45)");
2080
	 * @config transform
2081
     * @type String
2082
	 */
2083
	transform: {
2084
		setter: function(val)
2085
		{
2086
            this.matrix.init();
2087
            this._transforms = this.matrix.getTransformArray(val);
2088
            this._transform = val;
2089
            return val;
2090
		},
2091
 
2092
        getter: function()
2093
        {
2094
            return this._transform;
2095
        }
2096
	},
2097
 
2098
	/**
2099
	 * Dom node for the shape
2100
	 *
2101
	 * @config node
2102
	 * @type HTMLElement
2103
	 * @readOnly
2104
	 */
2105
	node: {
2106
		readOnly: true,
2107
 
2108
		getter: function()
2109
		{
2110
			return this.node;
2111
		}
2112
	},
2113
 
2114
	/**
2115
	 * Unique id for class instance.
2116
	 *
2117
	 * @config id
2118
	 * @type String
2119
	 */
2120
	id: {
2121
		valueFn: function()
2122
		{
2123
			return Y.guid();
2124
		},
2125
 
2126
		setter: function(val)
2127
		{
2128
			var node = this.node;
2129
			if(node)
2130
			{
2131
				node.setAttribute("id", val);
2132
			}
2133
			return val;
2134
		}
2135
	},
2136
 
2137
	/**
2138
	 * Indicates the width of the shape
2139
	 *
2140
	 * @config width
2141
	 * @type Number
2142
	 */
2143
	width: {
2144
        value: 0
2145
    },
2146
 
2147
	/**
2148
	 * Indicates the height of the shape
2149
	 *
2150
	 * @config height
2151
	 * @type Number
2152
	 */
2153
	height: {
2154
        value: 0
2155
    },
2156
 
2157
	/**
2158
	 * Indicates the x position of shape.
2159
	 *
2160
	 * @config x
2161
	 * @type Number
2162
	 */
2163
	x: {
2164
		value: 0
2165
	},
2166
 
2167
	/**
2168
	 * Indicates the y position of shape.
2169
	 *
2170
	 * @config y
2171
	 * @type Number
2172
	 */
2173
	y: {
2174
		value: 0
2175
	},
2176
 
2177
	/**
2178
	 * Indicates whether the shape is visible.
2179
	 *
2180
	 * @config visible
2181
	 * @type Boolean
2182
	 */
2183
	visible: {
2184
		value: true,
2185
 
2186
		setter: function(val){
2187
			var node = this.get("node"),
2188
                visibility = val ? "visible" : "hidden";
2189
			if(node)
2190
            {
2191
                node.style.visibility = visibility;
2192
            }
2193
			return val;
2194
		}
2195
	},
2196
 
2197
	/**
2198
	 * Contains information about the fill of the shape.
2199
     *  <dl>
2200
     *      <dt>color</dt><dd>The color of the fill.</dd>
2201
     *      <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1.</dd>
2202
     *      <dt>type</dt><dd>Type of fill.
2203
     *          <dl>
2204
     *              <dt>solid</dt><dd>Solid single color fill. (default)</dd>
2205
     *              <dt>linear</dt><dd>Linear gradient fill.</dd>
2206
     *              <dt>radial</dt><dd>Radial gradient fill.</dd>
2207
     *          </dl>
2208
     *      </dd>
2209
     *  </dl>
2210
     *  <p>If a `linear` or `radial` is specified as the fill type. The following additional property is used:
2211
     *  <dl>
2212
     *      <dt>stops</dt><dd>An array of objects containing the following properties:
2213
     *          <dl>
2214
     *              <dt>color</dt><dd>The color of the stop.</dd>
2215
     *              <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the stop. The default value is 1.
2216
     *              Note: No effect for IE 6 - 8</dd>
2217
     *              <dt>offset</dt><dd>Number between 0 and 1 indicating where the color stop is positioned.</dd>
2218
     *          </dl>
2219
     *      </dd>
2220
     *      <p>Linear gradients also have the following property:</p>
2221
     *      <dt>rotation</dt><dd>Linear gradients flow left to right by default. The rotation property allows you to change the
2222
     *      flow by rotation. (e.g. A rotation of 180 would make the gradient pain from right to left.)</dd>
2223
     *      <p>Radial gradients have the following additional properties:</p>
2224
     *      <dt>r</dt><dd>Radius of the gradient circle.</dd>
2225
     *      <dt>fx</dt><dd>Focal point x-coordinate of the gradient.</dd>
2226
     *      <dt>fy</dt><dd>Focal point y-coordinate of the gradient.</dd>
2227
     *  </dl>
2228
     *  <p>The corresponding `SVGShape` class implements the following additional properties.</p>
2229
     *  <dl>
2230
     *      <dt>cx</dt><dd>
2231
     *          <p>The x-coordinate of the center of the gradient circle. Determines where the color stop begins. The default value 0.5.</p>
2232
     *          <p><strong>Note: </strong>Currently, this property is not implemented for corresponding `CanvasShape` and
2233
     *          `VMLShape` classes which are used on Android or IE 6 - 8.</p>
2234
     *      </dd>
2235
     *      <dt>cy</dt><dd>
2236
     *          <p>The y-coordinate of the center of the gradient circle. Determines where the color stop begins. The default value 0.5.</p>
2237
     *          <p><strong>Note: </strong>Currently, this property is not implemented for corresponding `CanvasShape` and `VMLShape`
2238
     *          classes which are used on Android or IE 6 - 8.</p>
2239
     *      </dd>
2240
     *  </dl>
2241
     *  <p>These properties are not currently implemented in `CanvasShape` or `VMLShape`.</p>
2242
	 *
2243
	 * @config fill
2244
	 * @type Object
2245
	 */
2246
	fill: {
2247
		valueFn: "_getDefaultFill",
2248
 
2249
		setter: function(val)
2250
		{
2251
			var fill,
2252
				tmpl = this.get("fill") || this._getDefaultFill();
2253
			fill = (val) ? Y.merge(tmpl, val) : null;
2254
			if(fill && fill.color)
2255
			{
2256
				if(fill.color === undefined || fill.color === "none")
2257
				{
2258
					fill.color = null;
2259
				}
2260
			}
2261
			this._setFillProps(fill);
2262
			return fill;
2263
		}
2264
	},
2265
 
2266
	/**
2267
	 * Contains information about the stroke of the shape.
2268
     *  <dl>
2269
     *      <dt>color</dt><dd>The color of the stroke.</dd>
2270
     *      <dt>weight</dt><dd>Number that indicates the width of the stroke.</dd>
2271
     *      <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the stroke. The default value is 1.</dd>
2272
     *      <dt>dashstyle</dt>Indicates whether to draw a dashed stroke. When set to "none", a solid stroke is drawn. When set
2273
     *      to an array, the first index indicates the length of the dash. The second index indicates the length of gap.
2274
     *      <dt>linecap</dt><dd>Specifies the linecap for the stroke. The following values can be specified:
2275
     *          <dl>
2276
     *              <dt>butt (default)</dt><dd>Specifies a butt linecap.</dd>
2277
     *              <dt>square</dt><dd>Specifies a sqare linecap.</dd>
2278
     *              <dt>round</dt><dd>Specifies a round linecap.</dd>
2279
     *          </dl>
2280
     *      </dd>
2281
     *      <dt>linejoin</dt><dd>Specifies a linejoin for the stroke. The following values can be specified:
2282
     *          <dl>
2283
     *              <dt>round (default)</dt><dd>Specifies that the linejoin will be round.</dd>
2284
     *              <dt>bevel</dt><dd>Specifies a bevel for the linejoin.</dd>
2285
     *              <dt>miter limit</dt><dd>An integer specifying the miter limit of a miter linejoin. If you want to specify a linejoin
2286
     *              of miter, you simply specify the limit as opposed to having separate miter and miter limit values.</dd>
2287
     *          </dl>
2288
     *      </dd>
2289
     *  </dl>
2290
	 *
2291
	 * @config stroke
2292
	 * @type Object
2293
	 */
2294
	stroke: {
2295
		valueFn: "_getDefaultStroke",
2296
 
2297
		setter: function(val)
2298
		{
2299
			var tmpl = this.get("stroke") || this._getDefaultStroke(),
2300
                wt;
2301
            if(val && val.hasOwnProperty("weight"))
2302
            {
2303
                wt = parseInt(val.weight, 10);
2304
                if(!isNaN(wt))
2305
                {
2306
                    val.weight = wt;
2307
                }
2308
            }
2309
			val = (val) ? Y.merge(tmpl, val) : null;
2310
			this._setStrokeProps(val);
2311
			return val;
2312
		}
2313
	},
2314
 
2315
	//Not used. Remove in future.
2316
	autoSize: {
2317
		value: false
2318
	},
2319
 
2320
	// Only implemented in SVG
2321
	// Determines whether the instance will receive mouse events.
2322
	//
2323
	// @config pointerEvents
2324
	// @type string
2325
	//
2326
	pointerEvents: {
2327
		value: "visiblePainted"
2328
	},
2329
 
2330
    /**
2331
     * Represents an SVG Path string. This will be parsed and added to shape's API to represent the SVG data across all
2332
     * implementations. Note that when using VML or SVG implementations, part of this content will be added to the DOM using
2333
     * respective VML/SVG attributes. If your content comes from an untrusted source, you will need to ensure that no
2334
     * malicious code is included in that content.
2335
     *
2336
     * @config data
2337
     * @type String
2338
     */
2339
    data: {
2340
        setter: function(val)
2341
        {
2342
            if(this.get("node"))
2343
            {
2344
                this._parsePathData(val);
2345
            }
2346
            return val;
2347
        }
2348
    },
2349
 
2350
	/**
2351
	 * Reference to the container Graphic.
2352
	 *
2353
	 * @config graphic
2354
	 * @type Graphic
2355
	 */
2356
	graphic: {
2357
		readOnly: true,
2358
 
2359
		getter: function()
2360
		{
2361
			return this._graphic;
2362
		}
2363
    }
2364
};
2365
Y.CanvasShape = CanvasShape;
2366
/**
2367
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Path.html">`Path`</a> class.
2368
 * `CanvasPath` is not intended to be used directly. Instead, use the <a href="Path.html">`Path`</a> class.
2369
 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2370
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Path.html">`Path`</a>
2371
 * class will point to the `CanvasPath` class.
2372
 *
2373
 * @module graphics
2374
 * @class CanvasPath
2375
 * @extends CanvasShape
2376
 */
2377
CanvasPath = function()
2378
{
2379
	CanvasPath.superclass.constructor.apply(this, arguments);
2380
};
2381
CanvasPath.NAME = "path";
2382
Y.extend(CanvasPath, Y.CanvasShape, {
2383
    /**
2384
     * Indicates the type of shape
2385
     *
2386
     * @property _type
2387
     * @type String
2388
     * @private
2389
     */
2390
    _type: "path",
2391
 
2392
	/**
2393
	 * Draws the shape.
2394
	 *
2395
	 * @method _draw
2396
	 * @private
2397
	 */
2398
    _draw: function()
2399
    {
2400
        this._closePath();
2401
        this._updateTransform();
2402
    },
2403
 
2404
	/**
2405
	 * Creates the dom node for the shape.
2406
	 *
2407
     * @method createNode
2408
	 * @return HTMLElement
2409
	 * @private
2410
	 */
2411
	createNode: function()
2412
	{
2413
		var host = this,
2414
            node = Y.config.doc.createElement('canvas'),
2415
			name = host.name,
2416
            concat = host._camelCaseConcat,
2417
            id = host.get("id");
2418
		host._context = node.getContext('2d');
2419
		node.setAttribute("overflow", "visible");
2420
        node.setAttribute("pointer-events", "none");
2421
        node.style.pointerEvents = "none";
2422
        node.style.overflow = "visible";
2423
		node.setAttribute("id", id);
2424
		id = "#" + id;
2425
		host.node = node;
2426
		host.addClass(
2427
            _getClassName(SHAPE) +
2428
            " " +
2429
            _getClassName(concat(IMPLEMENTATION, SHAPE)) +
2430
            " " +
2431
            _getClassName(name) +
2432
            " " +
2433
            _getClassName(concat(IMPLEMENTATION, name))
2434
        );
2435
	},
2436
 
2437
    /**
2438
     * Completes a drawing operation.
2439
     *
2440
     * @method end
2441
     */
2442
    end: function()
2443
    {
2444
        this._draw();
2445
        return this;
2446
    }
2447
});
2448
 
2449
CanvasPath.ATTRS = Y.merge(Y.CanvasShape.ATTRS, {
2450
	/**
2451
	 * Indicates the width of the shape
2452
	 *
2453
	 * @config width
2454
	 * @type Number
2455
	 */
2456
	width: {
2457
		getter: function()
2458
		{
2459
			var offset = this._stroke && this._strokeWeight ? (this._strokeWeight * 2) : 0;
2460
			return this._width - offset;
2461
		},
2462
 
2463
		setter: function(val)
2464
		{
2465
			this._width = val;
2466
			return val;
2467
		}
2468
	},
2469
 
2470
	/**
2471
	 * Indicates the height of the shape
2472
	 *
2473
	 * @config height
2474
	 * @type Number
2475
	 */
2476
	height: {
2477
		getter: function()
2478
		{
2479
			var offset = this._stroke && this._strokeWeight ? (this._strokeWeight * 2) : 0;
2480
            return this._height - offset;
2481
		},
2482
 
2483
		setter: function(val)
2484
		{
2485
			this._height = val;
2486
			return val;
2487
		}
2488
	},
2489
 
2490
	/**
2491
	 * Indicates the path used for the node.
2492
	 *
2493
	 * @config path
2494
	 * @type String
2495
     * @readOnly
2496
	 */
2497
	path: {
2498
        readOnly: true,
2499
 
2500
		getter: function()
2501
		{
2502
			return this._path;
2503
		}
2504
	}
2505
});
2506
Y.CanvasPath = CanvasPath;
2507
/**
2508
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Rect.html">`Rect`</a> class.
2509
 * `CanvasRect` is not intended to be used directly. Instead, use the <a href="Rect.html">`Rect`</a> class.
2510
 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2511
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Rect.html">`Rect`</a>
2512
 * class will point to the `CanvasRect` class.
2513
 *
2514
 * @module graphics
2515
 * @class CanvasRect
2516
 * @constructor
2517
 */
2518
CanvasRect = function()
2519
{
2520
	CanvasRect.superclass.constructor.apply(this, arguments);
2521
};
2522
CanvasRect.NAME = "rect";
2523
Y.extend(CanvasRect, Y.CanvasShape, {
2524
	/**
2525
	 * Indicates the type of shape
2526
	 *
2527
	 * @property _type
2528
	 * @type String
2529
     * @private
2530
	 */
2531
	_type: "rect",
2532
 
2533
	/**
2534
	 * Draws the shape.
2535
	 *
2536
	 * @method _draw
2537
	 * @private
2538
	 */
2539
	_draw: function()
2540
	{
2541
		var w = this.get("width"),
2542
			h = this.get("height");
2543
		this.clear();
2544
        this.drawRect(0, 0, w, h);
2545
		this._closePath();
2546
	}
2547
});
2548
CanvasRect.ATTRS = Y.CanvasShape.ATTRS;
2549
Y.CanvasRect = CanvasRect;
2550
/**
2551
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Ellipse.html">`Ellipse`</a> class.
2552
 * `CanvasEllipse` is not intended to be used directly. Instead, use the <a href="Ellipse.html">`Ellipse`</a> class.
2553
 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2554
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Ellipse.html">`Ellipse`</a>
2555
 * class will point to the `CanvasEllipse` class.
2556
 *
2557
 * @module graphics
2558
 * @class CanvasEllipse
2559
 * @constructor
2560
 */
2561
CanvasEllipse = function()
2562
{
2563
	CanvasEllipse.superclass.constructor.apply(this, arguments);
2564
};
2565
 
2566
CanvasEllipse.NAME = "ellipse";
2567
 
2568
Y.extend(CanvasEllipse, CanvasShape, {
2569
	/**
2570
	 * Indicates the type of shape
2571
	 *
2572
	 * @property _type
2573
	 * @type String
2574
     * @private
2575
	 */
2576
	_type: "ellipse",
2577
 
2578
	/**
2579
     * Draws the shape.
2580
     *
2581
     * @method _draw
2582
	 * @private
2583
	 */
2584
	_draw: function()
2585
	{
2586
		var w = this.get("width"),
2587
			h = this.get("height");
2588
		this.clear();
2589
        this.drawEllipse(0, 0, w, h);
2590
		this._closePath();
2591
	}
2592
});
2593
CanvasEllipse.ATTRS = Y.merge(CanvasShape.ATTRS, {
2594
	/**
2595
	 * Horizontal radius for the ellipse.
2596
	 *
2597
	 * @config xRadius
2598
	 * @type Number
2599
	 */
2600
	xRadius: {
2601
		setter: function(val)
2602
		{
2603
			this.set("width", val * 2);
2604
		},
2605
 
2606
		getter: function()
2607
		{
2608
			var val = this.get("width");
2609
			if(val)
2610
			{
2611
				val *= 0.5;
2612
			}
2613
			return val;
2614
		}
2615
	},
2616
 
2617
	/**
2618
	 * Vertical radius for the ellipse.
2619
	 *
2620
	 * @config yRadius
2621
	 * @type Number
2622
	 * @readOnly
2623
	 */
2624
	yRadius: {
2625
		setter: function(val)
2626
		{
2627
			this.set("height", val * 2);
2628
		},
2629
 
2630
		getter: function()
2631
		{
2632
			var val = this.get("height");
2633
			if(val)
2634
			{
2635
				val *= 0.5;
2636
			}
2637
			return val;
2638
		}
2639
	}
2640
});
2641
Y.CanvasEllipse = CanvasEllipse;
2642
/**
2643
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Circle.html">`Circle`</a> class.
2644
 * `CanvasCircle` is not intended to be used directly. Instead, use the <a href="Circle.html">`Circle`</a> class.
2645
 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2646
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Circle.html">`Circle`</a>
2647
 * class will point to the `CanvasCircle` class.
2648
 *
2649
 * @module graphics
2650
 * @class CanvasCircle
2651
 * @constructor
2652
 */
2653
CanvasCircle = function()
2654
{
2655
	CanvasCircle.superclass.constructor.apply(this, arguments);
2656
};
2657
 
2658
CanvasCircle.NAME = "circle";
2659
 
2660
Y.extend(CanvasCircle, Y.CanvasShape, {
2661
	/**
2662
	 * Indicates the type of shape
2663
	 *
2664
	 * @property _type
2665
	 * @type String
2666
     * @private
2667
	 */
2668
	_type: "circle",
2669
 
2670
	/**
2671
     * Draws the shape.
2672
     *
2673
     * @method _draw
2674
	 * @private
2675
	 */
2676
	_draw: function()
2677
	{
2678
		var radius = this.get("radius");
2679
		if(radius)
2680
		{
2681
            this.clear();
2682
            this.drawCircle(0, 0, radius);
2683
			this._closePath();
2684
		}
2685
	}
2686
});
2687
 
2688
CanvasCircle.ATTRS = Y.merge(Y.CanvasShape.ATTRS, {
2689
	/**
2690
	 * Indicates the width of the shape
2691
	 *
2692
	 * @config width
2693
	 * @type Number
2694
	 */
2695
	width: {
2696
        setter: function(val)
2697
        {
2698
            this.set("radius", val/2);
2699
            return val;
2700
        },
2701
 
2702
		getter: function()
2703
		{
2704
			return this.get("radius") * 2;
2705
		}
2706
	},
2707
 
2708
	/**
2709
	 * Indicates the height of the shape
2710
	 *
2711
	 * @config height
2712
	 * @type Number
2713
	 */
2714
	height: {
2715
        setter: function(val)
2716
        {
2717
            this.set("radius", val/2);
2718
            return val;
2719
        },
2720
 
2721
		getter: function()
2722
		{
2723
			return this.get("radius") * 2;
2724
		}
2725
	},
2726
 
2727
	/**
2728
	 * Radius of the circle
2729
	 *
2730
	 * @config radius
2731
     * @type Number
2732
	 */
2733
	radius: {
2734
		lazyAdd: false
2735
	}
2736
});
2737
Y.CanvasCircle = CanvasCircle;
2738
/**
2739
 * Draws pie slices
2740
 *
2741
 * @module graphics
2742
 * @class CanvasPieSlice
2743
 * @constructor
2744
 */
2745
CanvasPieSlice = function()
2746
{
2747
	CanvasPieSlice.superclass.constructor.apply(this, arguments);
2748
};
2749
CanvasPieSlice.NAME = "canvasPieSlice";
2750
Y.extend(CanvasPieSlice, Y.CanvasShape, {
2751
    /**
2752
     * Indicates the type of shape
2753
     *
2754
     * @property _type
2755
     * @type String
2756
     * @private
2757
     */
2758
    _type: "path",
2759
 
2760
	/**
2761
	 * Change event listener
2762
	 *
2763
	 * @private
2764
	 * @method _updateHandler
2765
	 */
2766
	_draw: function()
2767
	{
2768
        var x = this.get("cx"),
2769
            y = this.get("cy"),
2770
            startAngle = this.get("startAngle"),
2771
            arc = this.get("arc"),
2772
            radius = this.get("radius");
2773
        this.clear();
2774
        this._left = x;
2775
        this._right = radius;
2776
        this._top = y;
2777
        this._bottom = radius;
2778
        this.drawWedge(x, y, startAngle, arc, radius);
2779
		this.end();
2780
	}
2781
 });
2782
CanvasPieSlice.ATTRS = Y.mix({
2783
    cx: {
2784
        value: 0
2785
    },
2786
 
2787
    cy: {
2788
        value: 0
2789
    },
2790
    /**
2791
     * Starting angle in relation to a circle in which to begin the pie slice drawing.
2792
     *
2793
     * @config startAngle
2794
     * @type Number
2795
     */
2796
    startAngle: {
2797
        value: 0
2798
    },
2799
 
2800
    /**
2801
     * Arc of the slice.
2802
     *
2803
     * @config arc
2804
     * @type Number
2805
     */
2806
    arc: {
2807
        value: 0
2808
    },
2809
 
2810
    /**
2811
     * Radius of the circle in which the pie slice is drawn
2812
     *
2813
     * @config radius
2814
     * @type Number
2815
     */
2816
    radius: {
2817
        value: 0
2818
    }
2819
}, Y.CanvasShape.ATTRS);
2820
Y.CanvasPieSlice = CanvasPieSlice;
2821
/**
2822
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the `Graphic` class.
2823
 * `CanvasGraphic` is not intended to be used directly. Instead, use the <a href="Graphic.html">`Graphic`</a> class.
2824
 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2825
 * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Graphic.html">`Graphic`</a>
2826
 * class will point to the `CanvasGraphic` class.
2827
 *
2828
 * @module graphics
2829
 * @class CanvasGraphic
2830
 * @constructor
2831
 */
2832
function CanvasGraphic() {
2833
 
2834
    CanvasGraphic.superclass.constructor.apply(this, arguments);
2835
}
2836
 
2837
CanvasGraphic.NAME = "canvasGraphic";
2838
 
2839
CanvasGraphic.ATTRS = {
2840
    /**
2841
     * Whether or not to render the `Graphic` automatically after to a specified parent node after init. This can be a Node
2842
     * instance or a CSS selector string.
2843
     *
2844
     * @config render
2845
     * @type Node | String
2846
     */
2847
    render: {},
2848
 
2849
    /**
2850
	 * Unique id for class instance.
2851
	 *
2852
	 * @config id
2853
	 * @type String
2854
	 */
2855
	id: {
2856
		valueFn: function()
2857
		{
2858
			return Y.guid();
2859
		},
2860
 
2861
		setter: function(val)
2862
		{
2863
			var node = this._node;
2864
			if(node)
2865
			{
2866
				node.setAttribute("id", val);
2867
			}
2868
			return val;
2869
		}
2870
	},
2871
 
2872
    /**
2873
     * Key value pairs in which a shape instance is associated with its id.
2874
     *
2875
     *  @config shapes
2876
     *  @type Object
2877
     *  @readOnly
2878
     */
2879
    shapes: {
2880
        readOnly: true,
2881
 
2882
        getter: function()
2883
        {
2884
            return this._shapes;
2885
        }
2886
    },
2887
 
2888
    /**
2889
     *  Object containing size and coordinate data for the content of a Graphic in relation to the graphic instance's position.
2890
     *
2891
     *  @config contentBounds
2892
     *  @type Object
2893
     *  @readOnly
2894
     */
2895
    contentBounds: {
2896
        readOnly: true,
2897
 
2898
        getter: function()
2899
        {
2900
            return this._contentBounds;
2901
        }
2902
    },
2903
 
2904
    /**
2905
     *  The outermost html element of the Graphic instance.
2906
     *
2907
     *  @config node
2908
     *  @type HTMLElement
2909
     *  @readOnly
2910
     */
2911
    node: {
2912
        readOnly: true,
2913
 
2914
        getter: function()
2915
        {
2916
            return this._node;
2917
        }
2918
    },
2919
 
2920
	/**
2921
	 * Indicates the width of the `Graphic`.
2922
	 *
2923
	 * @config width
2924
	 * @type Number
2925
	 */
2926
    width: {
2927
        setter: function(val)
2928
        {
2929
            if(this._node)
2930
            {
2931
                this._node.style.width = val + "px";
2932
            }
2933
            return val;
2934
        }
2935
    },
2936
 
2937
	/**
2938
	 * Indicates the height of the `Graphic`.
2939
	 *
2940
	 * @config height
2941
	 * @type Number
2942
	 */
2943
    height: {
2944
        setter: function(val)
2945
        {
2946
            if(this._node)
2947
            {
2948
                this._node.style.height = val + "px";
2949
            }
2950
            return val;
2951
        }
2952
    },
2953
 
2954
    /**
2955
     *  Determines the sizing of the Graphic.
2956
     *
2957
     *  <dl>
2958
     *      <dt>sizeContentToGraphic</dt><dd>The Graphic's width and height attributes are, either explicitly set through the
2959
     *      <code>width</code> and <code>height</code> attributes or are determined by the dimensions of the parent element. The
2960
     *      content contained in the Graphic will be sized to fit with in the Graphic instance's dimensions. When using this
2961
     *      setting, the <code>preserveAspectRatio</code> attribute will determine how the contents are sized.</dd>
2962
     *      <dt>sizeGraphicToContent</dt><dd>(Also accepts a value of true) The Graphic's width and height are determined by the
2963
     *      size and positioning of the content.</dd>
2964
     *      <dt>false</dt><dd>The Graphic's width and height attributes are, either explicitly set through the <code>width</code>
2965
     *      and <code>height</code> attributes or are determined by the dimensions of the parent element. The contents of the
2966
     *      Graphic instance are not affected by this setting.</dd>
2967
     *  </dl>
2968
     *
2969
     *
2970
     *  @config autoSize
2971
     *  @type Boolean | String
2972
     *  @default false
2973
     */
2974
    autoSize: {
2975
        value: false
2976
    },
2977
 
2978
    /**
2979
     * Determines how content is sized when <code>autoSize</code> is set to <code>sizeContentToGraphic</code>.
2980
     *
2981
     *  <dl>
2982
     *      <dt>none<dt><dd>Do not force uniform scaling. Scale the graphic content of the given element non-uniformly if necessary
2983
     *      such that the element's bounding box exactly matches the viewport rectangle.</dd>
2984
     *      <dt>xMinYMin</dt><dd>Force uniform scaling position along the top left of the Graphic's node.</dd>
2985
     *      <dt>xMidYMin</dt><dd>Force uniform scaling horizontally centered and positioned at the top of the Graphic's node.<dd>
2986
     *      <dt>xMaxYMin</dt><dd>Force uniform scaling positioned horizontally from the right and vertically from the top.</dd>
2987
     *      <dt>xMinYMid</dt>Force uniform scaling positioned horizontally from the left and vertically centered.</dd>
2988
     *      <dt>xMidYMid (the default)</dt><dd>Force uniform scaling with the content centered.</dd>
2989
     *      <dt>xMaxYMid</dt><dd>Force uniform scaling positioned horizontally from the right and vertically centered.</dd>
2990
     *      <dt>xMinYMax</dt><dd>Force uniform scaling positioned horizontally from the left and vertically from the bottom.</dd>
2991
     *      <dt>xMidYMax</dt><dd>Force uniform scaling horizontally centered and position vertically from the bottom.</dd>
2992
     *      <dt>xMaxYMax</dt><dd>Force uniform scaling positioned horizontally from the right and vertically from the bottom.</dd>
2993
     *  </dl>
2994
     *
2995
     * @config preserveAspectRatio
2996
     * @type String
2997
     * @default xMidYMid
2998
     */
2999
    preserveAspectRatio: {
3000
        value: "xMidYMid"
3001
    },
3002
 
3003
    /**
3004
     * The contentBounds will resize to greater values but not smaller values. (for performance)
3005
     * When resizing the contentBounds down is desirable, set the resizeDown value to true.
3006
     *
3007
     * @config resizeDown
3008
     * @type Boolean
3009
     */
3010
    resizeDown: {
3011
        value: false
3012
    },
3013
 
3014
	/**
3015
	 * Indicates the x-coordinate for the instance.
3016
	 *
3017
	 * @config x
3018
	 * @type Number
3019
	 */
3020
    x: {
3021
        getter: function()
3022
        {
3023
            return this._x;
3024
        },
3025
 
3026
        setter: function(val)
3027
        {
3028
            this._x = val;
3029
            if(this._node)
3030
            {
3031
                this._node.style.left = val + "px";
3032
            }
3033
            return val;
3034
        }
3035
    },
3036
 
3037
	/**
3038
	 * Indicates the y-coordinate for the instance.
3039
	 *
3040
	 * @config y
3041
	 * @type Number
3042
	 */
3043
    y: {
3044
        getter: function()
3045
        {
3046
            return this._y;
3047
        },
3048
 
3049
        setter: function(val)
3050
        {
3051
            this._y = val;
3052
            if(this._node)
3053
            {
3054
                this._node.style.top = val + "px";
3055
            }
3056
            return val;
3057
        }
3058
    },
3059
 
3060
    /**
3061
     * Indicates whether or not the instance will automatically redraw after a change is made to a shape.
3062
     * This property will get set to false when batching operations.
3063
     *
3064
     * @config autoDraw
3065
     * @type Boolean
3066
     * @default true
3067
     * @private
3068
     */
3069
    autoDraw: {
3070
        value: true
3071
    },
3072
 
3073
	/**
3074
	 * Indicates whether the `Graphic` and its children are visible.
3075
	 *
3076
	 * @config visible
3077
	 * @type Boolean
3078
	 */
3079
    visible: {
3080
        value: true,
3081
 
3082
        setter: function(val)
3083
        {
3084
            this._toggleVisible(val);
3085
            return val;
3086
        }
3087
    }
3088
};
3089
 
3090
Y.extend(CanvasGraphic, Y.GraphicBase, {
3091
    /**
3092
     * Sets the value of an attribute.
3093
     *
3094
     * @method set
3095
     * @param {String|Object} name The name of the attribute. Alternatively, an object of key value pairs can
3096
     * be passed in to set multiple attributes at once.
3097
     * @param {Any} value The value to set the attribute to. This value is ignored if an object is received as
3098
     * the name param.
3099
     */
3100
	set: function()
3101
	{
3102
		var host = this,
3103
            attr = arguments[0],
3104
            redrawAttrs = {
3105
                autoDraw: true,
3106
                autoSize: true,
3107
                preserveAspectRatio: true,
3108
                resizeDown: true
3109
            },
3110
            key,
3111
            forceRedraw = false;
3112
		AttributeLite.prototype.set.apply(host, arguments);
3113
        if(host._state.autoDraw === true && Y.Object.size(this._shapes) > 0)
3114
        {
3115
            if(Y_LANG.isString && redrawAttrs[attr])
3116
            {
3117
                forceRedraw = true;
3118
            }
3119
            else if(Y_LANG.isObject(attr))
3120
            {
3121
                for(key in redrawAttrs)
3122
                {
3123
                    if(redrawAttrs.hasOwnProperty(key) && attr[key])
3124
                    {
3125
                        forceRedraw = true;
3126
                        break;
3127
                    }
3128
                }
3129
            }
3130
        }
3131
        if(forceRedraw)
3132
        {
3133
            host._redraw();
3134
        }
3135
	},
3136
 
3137
    /**
3138
     * Storage for `x` attribute.
3139
     *
3140
     * @property _x
3141
     * @type Number
3142
     * @private
3143
     */
3144
    _x: 0,
3145
 
3146
    /**
3147
     * Storage for `y` attribute.
3148
     *
3149
     * @property _y
3150
     * @type Number
3151
     * @private
3152
     */
3153
    _y: 0,
3154
 
3155
    /**
3156
     * Gets the current position of the graphic instance in page coordinates.
3157
     *
3158
     * @method getXY
3159
     * @return Array The XY position of the shape.
3160
     */
3161
    getXY: function()
3162
    {
3163
        var node = this._node,
3164
            xy;
3165
        if(node)
3166
        {
3167
            xy = Y.DOM.getXY(node);
3168
        }
3169
        return xy;
3170
    },
3171
 
3172
	/**
3173
     * Initializes the class.
3174
     *
3175
     * @method initializer
3176
     * @param {Object} config Optional attributes
3177
     * @private
3178
     */
3179
    initializer: function() {
3180
        var render = this.get("render"),
3181
            visibility = this.get("visible") ? "visible" : "hidden",
3182
            w = this.get("width") || 0,
3183
            h = this.get("height") || 0;
3184
        this._shapes = {};
3185
        this._redrawQueue = {};
3186
		this._contentBounds = {
3187
            left: 0,
3188
            top: 0,
3189
            right: 0,
3190
            bottom: 0
3191
        };
3192
        this._node = DOCUMENT.createElement('div');
3193
        this._node.style.position = "absolute";
3194
        this._node.style.visibility = visibility;
3195
        this.set("width", w);
3196
        this.set("height", h);
3197
        if(render)
3198
        {
3199
            this.render(render);
3200
        }
3201
    },
3202
 
3203
    /**
3204
     * Adds the graphics node to the dom.
3205
     *
3206
     * @method render
3207
     * @param {HTMLElement} parentNode node in which to render the graphics node into.
3208
     */
3209
    render: function(render) {
3210
        var parentNode = render || DOCUMENT.body,
3211
            node = this._node,
3212
            w,
3213
            h;
3214
        if(render instanceof Y.Node)
3215
        {
3216
            parentNode = render._node;
3217
        }
3218
        else if(Y.Lang.isString(render))
3219
        {
3220
            parentNode = Y.Selector.query(render, DOCUMENT.body, true);
3221
        }
3222
        w = this.get("width") || parseInt(Y.DOM.getComputedStyle(parentNode, "width"), 10);
3223
        h = this.get("height") || parseInt(Y.DOM.getComputedStyle(parentNode, "height"), 10);
3224
        parentNode.appendChild(node);
3225
        node.style.display = "block";
3226
        node.style.position = "absolute";
3227
        node.style.left = this.get("x") + "px";
3228
        node.style.top = this.get("y") + "px";
3229
        this.set("width", w);
3230
        this.set("height", h);
3231
        this.parentNode = parentNode;
3232
        return this;
3233
    },
3234
 
3235
    /**
3236
     * Removes all nodes.
3237
     *
3238
     * @method destroy
3239
     */
3240
    destroy: function()
3241
    {
3242
        this.removeAllShapes();
3243
        if(this._node)
3244
        {
3245
            this._removeChildren(this._node);
3246
            if(this._node.parentNode)
3247
            {
3248
                this._node.parentNode.removeChild(this._node);
3249
            }
3250
            this._node = null;
3251
        }
3252
    },
3253
 
3254
    /**
3255
     * Generates a shape instance by type.
3256
     *
3257
     * @method addShape
3258
     * @param {Object} cfg attributes for the shape
3259
     * @return Shape
3260
     */
3261
    addShape: function(cfg)
3262
    {
3263
        cfg.graphic = this;
3264
        if(!this.get("visible"))
3265
        {
3266
            cfg.visible = false;
3267
        }
3268
        var ShapeClass = this._getShapeClass(cfg.type),
3269
            shape = new ShapeClass(cfg);
3270
        this._appendShape(shape);
3271
        return shape;
3272
    },
3273
 
3274
    /**
3275
     * Adds a shape instance to the graphic instance.
3276
     *
3277
     * @method _appendShape
3278
     * @param {Shape} shape The shape instance to be added to the graphic.
3279
     * @private
3280
     */
3281
    _appendShape: function(shape)
3282
    {
3283
        var node = shape.node,
3284
            parentNode = this._frag || this._node;
3285
        if(this.get("autoDraw"))
3286
        {
3287
            parentNode.appendChild(node);
3288
        }
3289
        else
3290
        {
3291
            this._getDocFrag().appendChild(node);
3292
        }
3293
    },
3294
 
3295
    /**
3296
     * Removes a shape instance from from the graphic instance.
3297
     *
3298
     * @method removeShape
3299
     * @param {Shape|String} shape The instance or id of the shape to be removed.
3300
     */
3301
    removeShape: function(shape)
3302
    {
3303
        if(!(shape instanceof CanvasShape))
3304
        {
3305
            if(Y_LANG.isString(shape))
3306
            {
3307
                shape = this._shapes[shape];
3308
            }
3309
        }
3310
        if(shape && shape instanceof CanvasShape)
3311
        {
3312
            shape._destroy();
3313
            delete this._shapes[shape.get("id")];
3314
        }
3315
        if(this.get("autoDraw"))
3316
        {
3317
            this._redraw();
3318
        }
3319
        return shape;
3320
    },
3321
 
3322
    /**
3323
     * Removes all shape instances from the dom.
3324
     *
3325
     * @method removeAllShapes
3326
     */
3327
    removeAllShapes: function()
3328
    {
3329
        var shapes = this._shapes,
3330
            i;
3331
        for(i in shapes)
3332
        {
3333
            if(shapes.hasOwnProperty(i))
3334
            {
3335
                shapes[i].destroy();
3336
            }
3337
        }
3338
        this._shapes = {};
3339
    },
3340
 
3341
    /**
3342
     * Clears the graphics object.
3343
     *
3344
     * @method clear
3345
     */
3346
    clear: function() {
3347
        this.removeAllShapes();
3348
    },
3349
 
3350
    /**
3351
     * Removes all child nodes.
3352
     *
3353
     * @method _removeChildren
3354
     * @param {HTMLElement} node
3355
     * @private
3356
     */
3357
    _removeChildren: function(node)
3358
    {
3359
        if(node && node.hasChildNodes())
3360
        {
3361
            var child;
3362
            while(node.firstChild)
3363
            {
3364
                child = node.firstChild;
3365
                this._removeChildren(child);
3366
                node.removeChild(child);
3367
            }
3368
        }
3369
    },
3370
 
3371
    /**
3372
     * Toggles visibility
3373
     *
3374
     * @method _toggleVisible
3375
     * @param {Boolean} val indicates visibilitye
3376
     * @private
3377
     */
3378
    _toggleVisible: function(val)
3379
    {
3380
        var i,
3381
            shapes = this._shapes,
3382
            visibility = val ? "visible" : "hidden";
3383
        if(shapes)
3384
        {
3385
            for(i in shapes)
3386
            {
3387
                if(shapes.hasOwnProperty(i))
3388
                {
3389
                    shapes[i].set("visible", val);
3390
                }
3391
            }
3392
        }
3393
        if(this._node)
3394
        {
3395
            this._node.style.visibility = visibility;
3396
        }
3397
    },
3398
 
3399
    /**
3400
     * Returns a shape class. Used by `addShape`.
3401
     *
3402
     * @method _getShapeClass
3403
     * @param {Shape | String} val Indicates which shape class.
3404
     * @return Function
3405
     * @private
3406
     */
3407
    _getShapeClass: function(val)
3408
    {
3409
        var shape = this._shapeClass[val];
3410
        if(shape)
3411
        {
3412
            return shape;
3413
        }
3414
        return val;
3415
    },
3416
 
3417
    /**
3418
     * Look up for shape classes. Used by `addShape` to retrieve a class for instantiation.
3419
     *
3420
     * @property _shapeClass
3421
     * @type Object
3422
     * @private
3423
     */
3424
    _shapeClass: {
3425
        circle: Y.CanvasCircle,
3426
        rect: Y.CanvasRect,
3427
        path: Y.CanvasPath,
3428
        ellipse: Y.CanvasEllipse,
3429
        pieslice: Y.CanvasPieSlice
3430
    },
3431
 
3432
    /**
3433
     * Returns a shape based on the id of its dom node.
3434
     *
3435
     * @method getShapeById
3436
     * @param {String} id Dom id of the shape's node attribute.
3437
     * @return Shape
3438
     */
3439
    getShapeById: function(id)
3440
    {
3441
        var shape = this._shapes[id];
3442
        return shape;
3443
    },
3444
 
3445
	/**
3446
	 * Allows for creating multiple shapes in order to batch appending and redraw operations.
3447
	 *
3448
	 * @method batch
3449
	 * @param {Function} method Method to execute.
3450
	 */
3451
    batch: function(method)
3452
    {
3453
        var autoDraw = this.get("autoDraw");
3454
        this.set("autoDraw", false);
3455
        method();
3456
        this.set("autoDraw", autoDraw);
3457
    },
3458
 
3459
    /**
3460
     * Returns a document fragment to for attaching shapes.
3461
     *
3462
     * @method _getDocFrag
3463
     * @return DocumentFragment
3464
     * @private
3465
     */
3466
    _getDocFrag: function()
3467
    {
3468
        if(!this._frag)
3469
        {
3470
            this._frag = DOCUMENT.createDocumentFragment();
3471
        }
3472
        return this._frag;
3473
    },
3474
 
3475
    /**
3476
     * Redraws all shapes.
3477
     *
3478
     * @method _redraw
3479
     * @private
3480
     */
3481
    _redraw: function()
3482
    {
3483
        var autoSize = this.get("autoSize"),
3484
            preserveAspectRatio = this.get("preserveAspectRatio"),
3485
            box = this.get("resizeDown") ? this._getUpdatedContentBounds() : this._contentBounds,
3486
            contentWidth,
3487
            contentHeight,
3488
            w,
3489
            h,
3490
            xScale,
3491
            yScale,
3492
            translateX = 0,
3493
            translateY = 0,
3494
            matrix,
3495
            node = this.get("node");
3496
        if(autoSize)
3497
        {
3498
            if(autoSize === "sizeContentToGraphic")
3499
            {
3500
                contentWidth = box.right - box.left;
3501
                contentHeight = box.bottom - box.top;
3502
                w = parseFloat(Y_DOM.getComputedStyle(node, "width"));
3503
                h = parseFloat(Y_DOM.getComputedStyle(node, "height"));
3504
                matrix = new Y.Matrix();
3505
                if(preserveAspectRatio === "none")
3506
                {
3507
                    xScale = w/contentWidth;
3508
                    yScale = h/contentHeight;
3509
                }
3510
                else
3511
                {
3512
                    if(contentWidth/contentHeight !== w/h)
3513
                    {
3514
                        if(contentWidth * h/contentHeight > w)
3515
                        {
3516
                            xScale = yScale = w/contentWidth;
3517
                            translateY = this._calculateTranslate(preserveAspectRatio.slice(5).toLowerCase(), contentHeight * w/contentWidth, h);
3518
                        }
3519
                        else
3520
                        {
3521
                            xScale = yScale = h/contentHeight;
3522
                            translateX = this._calculateTranslate(preserveAspectRatio.slice(1, 4).toLowerCase(), contentWidth * h/contentHeight, w);
3523
                        }
3524
                    }
3525
                }
3526
                Y_DOM.setStyle(node, "transformOrigin", "0% 0%");
3527
                translateX = translateX - (box.left * xScale);
3528
                translateY = translateY - (box.top * yScale);
3529
                matrix.translate(translateX, translateY);
3530
                matrix.scale(xScale, yScale);
3531
                Y_DOM.setStyle(node, "transform", matrix.toCSSText());
3532
            }
3533
            else
3534
            {
3535
                this.set("width", box.right);
3536
                this.set("height", box.bottom);
3537
            }
3538
        }
3539
        if(this._frag)
3540
        {
3541
            this._node.appendChild(this._frag);
3542
            this._frag = null;
3543
        }
3544
    },
3545
 
3546
    /**
3547
     * Determines the value for either an x or y value to be used for the <code>translate</code> of the Graphic.
3548
     *
3549
     * @method _calculateTranslate
3550
     * @param {String} position The position for placement. Possible values are min, mid and max.
3551
     * @param {Number} contentSize The total size of the content.
3552
     * @param {Number} boundsSize The total size of the Graphic.
3553
     * @return Number
3554
     * @private
3555
     */
3556
    _calculateTranslate: function(position, contentSize, boundsSize)
3557
    {
3558
        var ratio = boundsSize - contentSize,
3559
            coord;
3560
        switch(position)
3561
        {
3562
            case "mid" :
3563
                coord = ratio * 0.5;
3564
            break;
3565
            case "max" :
3566
                coord = ratio;
3567
            break;
3568
            default :
3569
                coord = 0;
3570
            break;
3571
        }
3572
        return coord;
3573
    },
3574
 
3575
    /**
3576
     * Adds a shape to the redraw queue and calculates the contentBounds. Used internally
3577
     * by `Shape` instances.
3578
     *
3579
     * @method addToRedrawQueue
3580
     * @param Shape shape The shape instance to add to the queue
3581
     * @protected
3582
     */
3583
    addToRedrawQueue: function(shape)
3584
    {
3585
        var shapeBox,
3586
            box;
3587
        this._shapes[shape.get("id")] = shape;
3588
        if(!this.get("resizeDown"))
3589
        {
3590
            shapeBox = shape.getBounds();
3591
            box = this._contentBounds;
3592
            box.left = box.left < shapeBox.left ? box.left : shapeBox.left;
3593
            box.top = box.top < shapeBox.top ? box.top : shapeBox.top;
3594
            box.right = box.right > shapeBox.right ? box.right : shapeBox.right;
3595
            box.bottom = box.bottom > shapeBox.bottom ? box.bottom : shapeBox.bottom;
3596
            this._contentBounds = box;
3597
        }
3598
        if(this.get("autoDraw"))
3599
        {
3600
            this._redraw();
3601
        }
3602
    },
3603
 
3604
    /**
3605
     * Recalculates and returns the `contentBounds` for the `Graphic` instance.
3606
     *
3607
     * @method _getUpdatedContentBounds
3608
     * @return {Object}
3609
     * @private
3610
     */
3611
    _getUpdatedContentBounds: function()
3612
    {
3613
        var bounds,
3614
            i,
3615
            shape,
3616
            queue = this._shapes,
3617
            box = {};
3618
        for(i in queue)
3619
        {
3620
            if(queue.hasOwnProperty(i))
3621
            {
3622
                shape = queue[i];
3623
                bounds = shape.getBounds();
3624
                box.left = Y_LANG.isNumber(box.left) ? Math.min(box.left, bounds.left) : bounds.left;
3625
                box.top = Y_LANG.isNumber(box.top) ? Math.min(box.top, bounds.top) : bounds.top;
3626
                box.right = Y_LANG.isNumber(box.right) ? Math.max(box.right, bounds.right) : bounds.right;
3627
                box.bottom = Y_LANG.isNumber(box.bottom) ? Math.max(box.bottom, bounds.bottom) : bounds.bottom;
3628
            }
3629
        }
3630
        box.left = Y_LANG.isNumber(box.left) ? box.left : 0;
3631
        box.top = Y_LANG.isNumber(box.top) ? box.top : 0;
3632
        box.right = Y_LANG.isNumber(box.right) ? box.right : 0;
3633
        box.bottom = Y_LANG.isNumber(box.bottom) ? box.bottom : 0;
3634
        this._contentBounds = box;
3635
        return box;
3636
    },
3637
 
3638
    /**
3639
     * Inserts shape on the top of the tree.
3640
     *
3641
     * @method _toFront
3642
     * @param {CanvasShape} Shape to add.
3643
     * @private
3644
     */
3645
    _toFront: function(shape)
3646
    {
3647
        var contentNode = this.get("node");
3648
        if(shape instanceof Y.CanvasShape)
3649
        {
3650
            shape = shape.get("node");
3651
        }
3652
        if(contentNode && shape)
3653
        {
3654
            contentNode.appendChild(shape);
3655
        }
3656
    },
3657
 
3658
    /**
3659
     * Inserts shape as the first child of the content node.
3660
     *
3661
     * @method _toBack
3662
     * @param {CanvasShape} Shape to add.
3663
     * @private
3664
     */
3665
    _toBack: function(shape)
3666
    {
3667
        var contentNode = this.get("node"),
3668
            targetNode;
3669
        if(shape instanceof Y.CanvasShape)
3670
        {
3671
            shape = shape.get("node");
3672
        }
3673
        if(contentNode && shape)
3674
        {
3675
            targetNode = contentNode.firstChild;
3676
            if(targetNode)
3677
            {
3678
                contentNode.insertBefore(shape, targetNode);
3679
            }
3680
            else
3681
            {
3682
                contentNode.appendChild(shape);
3683
            }
3684
        }
3685
    }
3686
});
3687
 
3688
Y.CanvasGraphic = CanvasGraphic;
3689
 
3690
 
3691
}, '3.18.1', {"requires": ["graphics", "color-base"]});