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