|
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 }, '@VERSION@', {"requires": ["graphics"]}); |