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