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