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