Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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