PluginProbe
Contact Forms by Cimatti / 1.1
Contact Forms by Cimatti v1.1
2.3.6 2.3.5 2.3.0 2.2.32 2.2.4 2.2.0 2.1.2 2.1.1 trunk 1.0 1.1 1.2 1.2.1 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 All 62 releases
contact-forms / flot / excanvas.js

excanvas.js in Contact Forms by Cimatti 1.1, at flot/excanvas.js

1,428 lines 40.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // Copyright 2006 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15
16 // Known Issues:
17 //
18 // * Patterns only support repeat.
19 // * Radial gradient are not implemented. The VML version of these look very
20 // different from the canvas one.
21 // * Clipping paths are not implemented.
22 // * Coordsize. The width and height attribute have higher priority than the
23 // width and height style values which isn't correct.
24 // * Painting mode isn't implemented.
25 // * Canvas width/height should is using content-box by default. IE in
26 // Quirks mode will draw the canvas using border-box. Either change your
27 // doctype to HTML5
28 // (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
29 // or use Box Sizing Behavior from WebFX
30 // (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
31 // * Non uniform scaling does not correctly scale strokes.
32 // * Filling very large shapes (above 5000 points) is buggy.
33 // * Optimize. There is always room for speed improvements.
34
35 // Only add this code if we do not already have a canvas implementation
36 if (!document.createElement('canvas').getContext) {
37
38 (function() {
39
40 // alias some functions to make (compiled) code shorter
41 var m = Math;
42 var mr = m.round;
43 var ms = m.sin;
44 var mc = m.cos;
45 var abs = m.abs;
46 var sqrt = m.sqrt;
47
48 // this is used for sub pixel precision
49 var Z = 10;
50 var Z2 = Z / 2;
51
52 /**
53 * This funtion is assigned to the <canvas> elements as element.getContext().
54 * @this {HTMLElement}
55 * @return {CanvasRenderingContext2D_}
56 */
57 function getContext() {
58 return this.context_ ||
59 (this.context_ = new CanvasRenderingContext2D_(this));
60 }
61
62 var slice = Array.prototype.slice;
63
64 /**
65 * Binds a function to an object. The returned function will always use the
66 * passed in {@code obj} as {@code this}.
67 *
68 * Example:
69 *
70 * g = bind(f, obj, a, b)
71 * g(c, d) // will do f.call(obj, a, b, c, d)
72 *
73 * @param {Function} f The function to bind the object to
74 * @param {Object} obj The object that should act as this when the function
75 * is called
76 * @param {*} var_args Rest arguments that will be used as the initial
77 * arguments when the function is called
78 * @return {Function} A new function that has bound this
79 */
80 function bind(f, obj, var_args) {
81 var a = slice.call(arguments, 2);
82 return function() {
83 return f.apply(obj, a.concat(slice.call(arguments)));
84 };
85 }
86
87 function encodeHtmlAttribute(s) {
88 return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
89 }
90
91 function addNamespacesAndStylesheet(doc) {
92 // create xmlns
93 if (!doc.namespaces['g_vml_']) {
94 doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml',
95 '#default#VML');
96
97 }
98 if (!doc.namespaces['g_o_']) {
99 doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office',
100 '#default#VML');
101 }
102
103 // Setup default CSS. Only add one style sheet per document
104 if (!doc.styleSheets['ex_canvas_']) {
105 var ss = doc.createStyleSheet();
106 ss.owningElement.id = 'ex_canvas_';
107 ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
108 // default size is 300x150 in Gecko and Opera
109 'text-align:left;width:300px;height:150px}';
110 }
111 }
112
113 // Add namespaces and stylesheet at startup.
114 addNamespacesAndStylesheet(document);
115
116 var G_vmlCanvasManager_ = {
117 init: function(opt_doc) {
118 if (/MSIE/.test(navigator.userAgent) && !window.opera) {
119 var doc = opt_doc || document;
120 // Create a dummy element so that IE will allow canvas elements to be
121 // recognized.
122 doc.createElement('canvas');
123 doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
124 }
125 },
126
127 init_: function(doc) {
128 // find all canvas elements
129 var els = doc.getElementsByTagName('canvas');
130 for (var i = 0; i < els.length; i++) {
131 this.initElement(els[i]);
132 }
133 },
134
135 /**
136 * Public initializes a canvas element so that it can be used as canvas
137 * element from now on. This is called automatically before the page is
138 * loaded but if you are creating elements using createElement you need to
139 * make sure this is called on the element.
140 * @param {HTMLElement} el The canvas element to initialize.
141 * @return {HTMLElement} the element that was created.
142 */
143 initElement: function(el) {
144 if (!el.getContext) {
145 el.getContext = getContext;
146
147 // Add namespaces and stylesheet to document of the element.
148 addNamespacesAndStylesheet(el.ownerDocument);
149
150 // Remove fallback content. There is no way to hide text nodes so we
151 // just remove all childNodes. We could hide all elements and remove
152 // text nodes but who really cares about the fallback content.
153 el.innerHTML = '';
154
155 // do not use inline function because that will leak memory
156 el.attachEvent('onpropertychange', onPropertyChange);
157 el.attachEvent('onresize', onResize);
158
159 var attrs = el.attributes;
160 if (attrs.width && attrs.width.specified) {
161 // TODO: use runtimeStyle and coordsize
162 // el.getContext().setWidth_(attrs.width.nodeValue);
163 el.style.width = attrs.width.nodeValue + 'px';
164 } else {
165 el.width = el.clientWidth;
166 }
167 if (attrs.height && attrs.height.specified) {
168 // TODO: use runtimeStyle and coordsize
169 // el.getContext().setHeight_(attrs.height.nodeValue);
170 el.style.height = attrs.height.nodeValue + 'px';
171 } else {
172 el.height = el.clientHeight;
173 }
174 //el.getContext().setCoordsize_()
175 }
176 return el;
177 }
178 };
179
180 function onPropertyChange(e) {
181 var el = e.srcElement;
182
183 switch (e.propertyName) {
184 case 'width':
185 el.getContext().clearRect();
186 el.style.width = el.attributes.width.nodeValue + 'px';
187 // In IE8 this does not trigger onresize.
188 el.firstChild.style.width = el.clientWidth + 'px';
189 break;
190 case 'height':
191 el.getContext().clearRect();
192 el.style.height = el.attributes.height.nodeValue + 'px';
193 el.firstChild.style.height = el.clientHeight + 'px';
194 break;
195 }
196 }
197
198 function onResize(e) {
199 var el = e.srcElement;
200 if (el.firstChild) {
201 el.firstChild.style.width = el.clientWidth + 'px';
202 el.firstChild.style.height = el.clientHeight + 'px';
203 }
204 }
205
206 G_vmlCanvasManager_.init();
207
208 // precompute "00" to "FF"
209 var decToHex = [];
210 for (var i = 0; i < 16; i++) {
211 for (var j = 0; j < 16; j++) {
212 decToHex[i * 16 + j] = i.toString(16) + j.toString(16);
213 }
214 }
215
216 function createMatrixIdentity() {
217 return [
218 [1, 0, 0],
219 [0, 1, 0],
220 [0, 0, 1]
221 ];
222 }
223
224 function matrixMultiply(m1, m2) {
225 var result = createMatrixIdentity();
226
227 for (var x = 0; x < 3; x++) {
228 for (var y = 0; y < 3; y++) {
229 var sum = 0;
230
231 for (var z = 0; z < 3; z++) {
232 sum += m1[x][z] * m2[z][y];
233 }
234
235 result[x][y] = sum;
236 }
237 }
238 return result;
239 }
240
241 function copyState(o1, o2) {
242 o2.fillStyle = o1.fillStyle;
243 o2.lineCap = o1.lineCap;
244 o2.lineJoin = o1.lineJoin;
245 o2.lineWidth = o1.lineWidth;
246 o2.miterLimit = o1.miterLimit;
247 o2.shadowBlur = o1.shadowBlur;
248 o2.shadowColor = o1.shadowColor;
249 o2.shadowOffsetX = o1.shadowOffsetX;
250 o2.shadowOffsetY = o1.shadowOffsetY;
251 o2.strokeStyle = o1.strokeStyle;
252 o2.globalAlpha = o1.globalAlpha;
253 o2.font = o1.font;
254 o2.textAlign = o1.textAlign;
255 o2.textBaseline = o1.textBaseline;
256 o2.arcScaleX_ = o1.arcScaleX_;
257 o2.arcScaleY_ = o1.arcScaleY_;
258 o2.lineScale_ = o1.lineScale_;
259 }
260
261 var colorData = {
262 aliceblue: '#F0F8FF',
263 antiquewhite: '#FAEBD7',
264 aquamarine: '#7FFFD4',
265 azure: '#F0FFFF',
266 beige: '#F5F5DC',
267 bisque: '#FFE4C4',
268 black: '#000000',
269 blanchedalmond: '#FFEBCD',
270 blueviolet: '#8A2BE2',
271 brown: '#A52A2A',
272 burlywood: '#DEB887',
273 cadetblue: '#5F9EA0',
274 chartreuse: '#7FFF00',
275 chocolate: '#D2691E',
276 coral: '#FF7F50',
277 cornflowerblue: '#6495ED',
278 cornsilk: '#FFF8DC',
279 crimson: '#DC143C',
280 cyan: '#00FFFF',
281 darkblue: '#00008B',
282 darkcyan: '#008B8B',
283 darkgoldenrod: '#B8860B',
284 darkgray: '#A9A9A9',
285 darkgreen: '#006400',
286 darkgrey: '#A9A9A9',
287 darkkhaki: '#BDB76B',
288 darkmagenta: '#8B008B',
289 darkolivegreen: '#556B2F',
290 darkorange: '#FF8C00',
291 darkorchid: '#9932CC',
292 darkred: '#8B0000',
293 darksalmon: '#E9967A',
294 darkseagreen: '#8FBC8F',
295 darkslateblue: '#483D8B',
296 darkslategray: '#2F4F4F',
297 darkslategrey: '#2F4F4F',
298 darkturquoise: '#00CED1',
299 darkviolet: '#9400D3',
300 deeppink: '#FF1493',
301 deepskyblue: '#00BFFF',
302 dimgray: '#696969',
303 dimgrey: '#696969',
304 dodgerblue: '#1E90FF',
305 firebrick: '#B22222',
306 floralwhite: '#FFFAF0',
307 forestgreen: '#228B22',
308 gainsboro: '#DCDCDC',
309 ghostwhite: '#F8F8FF',
310 gold: '#FFD700',
311 goldenrod: '#DAA520',
312 grey: '#808080',
313 greenyellow: '#ADFF2F',
314 honeydew: '#F0FFF0',
315 hotpink: '#FF69B4',
316 indianred: '#CD5C5C',
317 indigo: '#4B0082',
318 ivory: '#FFFFF0',
319 khaki: '#F0E68C',
320 lavender: '#E6E6FA',
321 lavenderblush: '#FFF0F5',
322 lawngreen: '#7CFC00',
323 lemonchiffon: '#FFFACD',
324 lightblue: '#ADD8E6',
325 lightcoral: '#F08080',
326 lightcyan: '#E0FFFF',
327 lightgoldenrodyellow: '#FAFAD2',
328 lightgreen: '#90EE90',
329 lightgrey: '#D3D3D3',
330 lightpink: '#FFB6C1',
331 lightsalmon: '#FFA07A',
332 lightseagreen: '#20B2AA',
333 lightskyblue: '#87CEFA',
334 lightslategray: '#778899',
335 lightslategrey: '#778899',
336 lightsteelblue: '#B0C4DE',
337 lightyellow: '#FFFFE0',
338 limegreen: '#32CD32',
339 linen: '#FAF0E6',
340 magenta: '#FF00FF',
341 mediumaquamarine: '#66CDAA',
342 mediumblue: '#0000CD',
343 mediumorchid: '#BA55D3',
344 mediumpurple: '#9370DB',
345 mediumseagreen: '#3CB371',
346 mediumslateblue: '#7B68EE',
347 mediumspringgreen: '#00FA9A',
348 mediumturquoise: '#48D1CC',
349 mediumvioletred: '#C71585',
350 midnightblue: '#191970',
351 mintcream: '#F5FFFA',
352 mistyrose: '#FFE4E1',
353 moccasin: '#FFE4B5',
354 navajowhite: '#FFDEAD',
355 oldlace: '#FDF5E6',
356 olivedrab: '#6B8E23',
357 orange: '#FFA500',
358 orangered: '#FF4500',
359 orchid: '#DA70D6',
360 palegoldenrod: '#EEE8AA',
361 palegreen: '#98FB98',
362 paleturquoise: '#AFEEEE',
363 palevioletred: '#DB7093',
364 papayawhip: '#FFEFD5',
365 peachpuff: '#FFDAB9',
366 peru: '#CD853F',
367 pink: '#FFC0CB',
368 plum: '#DDA0DD',
369 powderblue: '#B0E0E6',
370 rosybrown: '#BC8F8F',
371 royalblue: '#4169E1',
372 saddlebrown: '#8B4513',
373 salmon: '#FA8072',
374 sandybrown: '#F4A460',
375 seagreen: '#2E8B57',
376 seashell: '#FFF5EE',
377 sienna: '#A0522D',
378 skyblue: '#87CEEB',
379 slateblue: '#6A5ACD',
380 slategray: '#708090',
381 slategrey: '#708090',
382 snow: '#FFFAFA',
383 springgreen: '#00FF7F',
384 steelblue: '#4682B4',
385 tan: '#D2B48C',
386 thistle: '#D8BFD8',
387 tomato: '#FF6347',
388 turquoise: '#40E0D0',
389 violet: '#EE82EE',
390 wheat: '#F5DEB3',
391 whitesmoke: '#F5F5F5',
392 yellowgreen: '#9ACD32'
393 };
394
395
396 function getRgbHslContent(styleString) {
397 var start = styleString.indexOf('(', 3);
398 var end = styleString.indexOf(')', start + 1);
399 var parts = styleString.substring(start + 1, end).split(',');
400 // add alpha if needed
401 if (parts.length == 4 && styleString.substr(3, 1) == 'a') {
402 alpha = Number(parts[3]);
403 } else {
404 parts[3] = 1;
405 }
406 return parts;
407 }
408
409 function percent(s) {
410 return parseFloat(s) / 100;
411 }
412
413 function clamp(v, min, max) {
414 return Math.min(max, Math.max(min, v));
415 }
416
417 function hslToRgb(parts){
418 var r, g, b;
419 h = parseFloat(parts[0]) / 360 % 360;
420 if (h < 0)
421 h++;
422 s = clamp(percent(parts[1]), 0, 1);
423 l = clamp(percent(parts[2]), 0, 1);
424 if (s == 0) {
425 r = g = b = l; // achromatic
426 } else {
427 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
428 var p = 2 * l - q;
429 r = hueToRgb(p, q, h + 1 / 3);
430 g = hueToRgb(p, q, h);
431 b = hueToRgb(p, q, h - 1 / 3);
432 }
433
434 return '#' + decToHex[Math.floor(r * 255)] +
435 decToHex[Math.floor(g * 255)] +
436 decToHex[Math.floor(b * 255)];
437 }
438
439 function hueToRgb(m1, m2, h) {
440 if (h < 0)
441 h++;
442 if (h > 1)
443 h--;
444
445 if (6 * h < 1)
446 return m1 + (m2 - m1) * 6 * h;
447 else if (2 * h < 1)
448 return m2;
449 else if (3 * h < 2)
450 return m1 + (m2 - m1) * (2 / 3 - h) * 6;
451 else
452 return m1;
453 }
454
455 function processStyle(styleString) {
456 var str, alpha = 1;
457
458 styleString = String(styleString);
459 if (styleString.charAt(0) == '#') {
460 str = styleString;
461 } else if (/^rgb/.test(styleString)) {
462 var parts = getRgbHslContent(styleString);
463 var str = '#', n;
464 for (var i = 0; i < 3; i++) {
465 if (parts[i].indexOf('%') != -1) {
466 n = Math.floor(percent(parts[i]) * 255);
467 } else {
468 n = Number(parts[i]);
469 }
470 str += decToHex[clamp(n, 0, 255)];
471 }
472 alpha = parts[3];
473 } else if (/^hsl/.test(styleString)) {
474 var parts = getRgbHslContent(styleString);
475 str = hslToRgb(parts);
476 alpha = parts[3];
477 } else {
478 str = colorData[styleString] || styleString;
479 }
480 return {color: str, alpha: alpha};
481 }
482
483 var DEFAULT_STYLE = {
484 style: 'normal',
485 variant: 'normal',
486 weight: 'normal',
487 size: 10,
488 family: 'sans-serif'
489 };
490
491 // Internal text style cache
492 var fontStyleCache = {};
493
494 function processFontStyle(styleString) {
495 if (fontStyleCache[styleString]) {
496 return fontStyleCache[styleString];
497 }
498
499 var el = document.createElement('div');
500 var style = el.style;
501 try {
502 style.font = styleString;
503 } catch (ex) {
504 // Ignore failures to set to invalid font.
505 }
506
507 return fontStyleCache[styleString] = {
508 style: style.fontStyle || DEFAULT_STYLE.style,
509 variant: style.fontVariant || DEFAULT_STYLE.variant,
510 weight: style.fontWeight || DEFAULT_STYLE.weight,
511 size: style.fontSize || DEFAULT_STYLE.size,
512 family: style.fontFamily || DEFAULT_STYLE.family
513 };
514 }
515
516 function getComputedStyle(style, element) {
517 var computedStyle = {};
518
519 for (var p in style) {
520 computedStyle[p] = style[p];
521 }
522
523 // Compute the size
524 var canvasFontSize = parseFloat(element.currentStyle.fontSize),
525 fontSize = parseFloat(style.size);
526
527 if (typeof style.size == 'number') {
528 computedStyle.size = style.size;
529 } else if (style.size.indexOf('px') != -1) {
530 computedStyle.size = fontSize;
531 } else if (style.size.indexOf('em') != -1) {
532 computedStyle.size = canvasFontSize * fontSize;
533 } else if(style.size.indexOf('%') != -1) {
534 computedStyle.size = (canvasFontSize / 100) * fontSize;
535 } else if (style.size.indexOf('pt') != -1) {
536 computedStyle.size = fontSize / .75;
537 } else {
538 computedStyle.size = canvasFontSize;
539 }
540
541 // Different scaling between normal text and VML text. This was found using
542 // trial and error to get the same size as non VML text.
543 computedStyle.size *= 0.981;
544
545 return computedStyle;
546 }
547
548 function buildStyle(style) {
549 return style.style + ' ' + style.variant + ' ' + style.weight + ' ' +
550 style.size + 'px ' + style.family;
551 }
552
553 function processLineCap(lineCap) {
554 switch (lineCap) {
555 case 'butt':
556 return 'flat';
557 case 'round':
558 return 'round';
559 case 'square':
560 default:
561 return 'square';
562 }
563 }
564
565 /**
566 * This class implements CanvasRenderingContext2D interface as described by
567 * the WHATWG.
568 * @param {HTMLElement} surfaceElement The element that the 2D context should
569 * be associated with
570 */
571 function CanvasRenderingContext2D_(surfaceElement) {
572 this.m_ = createMatrixIdentity();
573
574 this.mStack_ = [];
575 this.aStack_ = [];
576 this.currentPath_ = [];
577
578 // Canvas context properties
579 this.strokeStyle = '#000';
580 this.fillStyle = '#000';
581
582 this.lineWidth = 1;
583 this.lineJoin = 'miter';
584 this.lineCap = 'butt';
585 this.miterLimit = Z * 1;
586 this.globalAlpha = 1;
587 this.font = '10px sans-serif';
588 this.textAlign = 'left';
589 this.textBaseline = 'alphabetic';
590 this.canvas = surfaceElement;
591
592 var el = surfaceElement.ownerDocument.createElement('div');
593 el.style.width = surfaceElement.clientWidth + 'px';
594 el.style.height = surfaceElement.clientHeight + 'px';
595 el.style.overflow = 'hidden';
596 el.style.position = 'absolute';
597 surfaceElement.appendChild(el);
598
599 this.element_ = el;
600 this.arcScaleX_ = 1;
601 this.arcScaleY_ = 1;
602 this.lineScale_ = 1;
603 }
604
605 var contextPrototype = CanvasRenderingContext2D_.prototype;
606 contextPrototype.clearRect = function() {
607 if (this.textMeasureEl_) {
608 this.textMeasureEl_.removeNode(true);
609 this.textMeasureEl_ = null;
610 }
611 this.element_.innerHTML = '';
612 };
613
614 contextPrototype.beginPath = function() {
615 // TODO: Branch current matrix so that save/restore has no effect
616 // as per safari docs.
617 this.currentPath_ = [];
618 };
619
620 contextPrototype.moveTo = function(aX, aY) {
621 var p = this.getCoords_(aX, aY);
622 this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
623 this.currentX_ = p.x;
624 this.currentY_ = p.y;
625 };
626
627 contextPrototype.lineTo = function(aX, aY) {
628 var p = this.getCoords_(aX, aY);
629 this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});
630
631 this.currentX_ = p.x;
632 this.currentY_ = p.y;
633 };
634
635 contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
636 aCP2x, aCP2y,
637 aX, aY) {
638 var p = this.getCoords_(aX, aY);
639 var cp1 = this.getCoords_(aCP1x, aCP1y);
640 var cp2 = this.getCoords_(aCP2x, aCP2y);
641 bezierCurveTo(this, cp1, cp2, p);
642 };
643
644 // Helper function that takes the already fixed cordinates.
645 function bezierCurveTo(self, cp1, cp2, p) {
646 self.currentPath_.push({
647 type: 'bezierCurveTo',
648 cp1x: cp1.x,
649 cp1y: cp1.y,
650 cp2x: cp2.x,
651 cp2y: cp2.y,
652 x: p.x,
653 y: p.y
654 });
655 self.currentX_ = p.x;
656 self.currentY_ = p.y;
657 }
658
659 contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
660 // the following is lifted almost directly from
661 // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
662
663 var cp = this.getCoords_(aCPx, aCPy);
664 var p = this.getCoords_(aX, aY);
665
666 var cp1 = {
667 x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
668 y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
669 };
670 var cp2 = {
671 x: cp1.x + (p.x - this.currentX_) / 3.0,
672 y: cp1.y + (p.y - this.currentY_) / 3.0
673 };
674
675 bezierCurveTo(this, cp1, cp2, p);
676 };
677
678 contextPrototype.arc = function(aX, aY, aRadius,
679 aStartAngle, aEndAngle, aClockwise) {
680 aRadius *= Z;
681 var arcType = aClockwise ? 'at' : 'wa';
682
683 var xStart = aX + mc(aStartAngle) * aRadius - Z2;
684 var yStart = aY + ms(aStartAngle) * aRadius - Z2;
685
686 var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
687 var yEnd = aY + ms(aEndAngle) * aRadius - Z2;
688
689 // IE won't render arches drawn counter clockwise if xStart == xEnd.
690 if (xStart == xEnd && !aClockwise) {
691 xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
692 // that can be represented in binary
693 }
694
695 var p = this.getCoords_(aX, aY);
696 var pStart = this.getCoords_(xStart, yStart);
697 var pEnd = this.getCoords_(xEnd, yEnd);
698
699 this.currentPath_.push({type: arcType,
700 x: p.x,
701 y: p.y,
702 radius: aRadius,
703 xStart: pStart.x,
704 yStart: pStart.y,
705 xEnd: pEnd.x,
706 yEnd: pEnd.y});
707
708 };
709
710 contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
711 this.moveTo(aX, aY);
712 this.lineTo(aX + aWidth, aY);
713 this.lineTo(aX + aWidth, aY + aHeight);
714 this.lineTo(aX, aY + aHeight);
715 this.closePath();
716 };
717
718 contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
719 var oldPath = this.currentPath_;
720 this.beginPath();
721
722 this.moveTo(aX, aY);
723 this.lineTo(aX + aWidth, aY);
724 this.lineTo(aX + aWidth, aY + aHeight);
725 this.lineTo(aX, aY + aHeight);
726 this.closePath();
727 this.stroke();
728
729 this.currentPath_ = oldPath;
730 };
731
732 contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
733 var oldPath = this.currentPath_;
734 this.beginPath();
735
736 this.moveTo(aX, aY);
737 this.lineTo(aX + aWidth, aY);
738 this.lineTo(aX + aWidth, aY + aHeight);
739 this.lineTo(aX, aY + aHeight);
740 this.closePath();
741 this.fill();
742
743 this.currentPath_ = oldPath;
744 };
745
746 contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
747 var gradient = new CanvasGradient_('gradient');
748 gradient.x0_ = aX0;
749 gradient.y0_ = aY0;
750 gradient.x1_ = aX1;
751 gradient.y1_ = aY1;
752 return gradient;
753 };
754
755 contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
756 aX1, aY1, aR1) {
757 var gradient = new CanvasGradient_('gradientradial');
758 gradient.x0_ = aX0;
759 gradient.y0_ = aY0;
760 gradient.r0_ = aR0;
761 gradient.x1_ = aX1;
762 gradient.y1_ = aY1;
763 gradient.r1_ = aR1;
764 return gradient;
765 };
766
767 contextPrototype.drawImage = function(image, var_args) {
768 var dx, dy, dw, dh, sx, sy, sw, sh;
769
770 // to find the original width we overide the width and height
771 var oldRuntimeWidth = image.runtimeStyle.width;
772 var oldRuntimeHeight = image.runtimeStyle.height;
773 image.runtimeStyle.width = 'auto';
774 image.runtimeStyle.height = 'auto';
775
776 // get the original size
777 var w = image.width;
778 var h = image.height;
779
780 // and remove overides
781 image.runtimeStyle.width = oldRuntimeWidth;
782 image.runtimeStyle.height = oldRuntimeHeight;
783
784 if (arguments.length == 3) {
785 dx = arguments[1];
786 dy = arguments[2];
787 sx = sy = 0;
788 sw = dw = w;
789 sh = dh = h;
790 } else if (arguments.length == 5) {
791 dx = arguments[1];
792 dy = arguments[2];
793 dw = arguments[3];
794 dh = arguments[4];
795 sx = sy = 0;
796 sw = w;
797 sh = h;
798 } else if (arguments.length == 9) {
799 sx = arguments[1];
800 sy = arguments[2];
801 sw = arguments[3];
802 sh = arguments[4];
803 dx = arguments[5];
804 dy = arguments[6];
805 dw = arguments[7];
806 dh = arguments[8];
807 } else {
808 throw Error('Invalid number of arguments');
809 }
810
811 var d = this.getCoords_(dx, dy);
812
813 var w2 = sw / 2;
814 var h2 = sh / 2;
815
816 var vmlStr = [];
817
818 var W = 10;
819 var H = 10;
820
821 // For some reason that I've now forgotten, using divs didn't work
822 vmlStr.push(' <g_vml_:group',
823 ' coordsize="', Z * W, ',', Z * H, '"',
824 ' coordorigin="0,0"' ,
825 ' style="width:', W, 'px;height:', H, 'px;position:absolute;');
826
827 // If filters are necessary (rotation exists), create them
828 // filters are bog-slow, so only create them if abbsolutely necessary
829 // The following check doesn't account for skews (which don't exist
830 // in the canvas spec (yet) anyway.
831
832 if (this.m_[0][0] != 1 || this.m_[0][1] ||
833 this.m_[1][1] != 1 || this.m_[1][0]) {
834 var filter = [];
835
836 // Note the 12/21 reversal
837 filter.push('M11=', this.m_[0][0], ',',
838 'M12=', this.m_[1][0], ',',
839 'M21=', this.m_[0][1], ',',
840 'M22=', this.m_[1][1], ',',
841 'Dx=', mr(d.x / Z), ',',
842 'Dy=', mr(d.y / Z), '');
843
844 // Bounding box calculation (need to minimize displayed area so that
845 // filters don't waste time on unused pixels.
846 var max = d;
847 var c2 = this.getCoords_(dx + dw, dy);
848 var c3 = this.getCoords_(dx, dy + dh);
849 var c4 = this.getCoords_(dx + dw, dy + dh);
850
851 max.x = m.max(max.x, c2.x, c3.x, c4.x);
852 max.y = m.max(max.y, c2.y, c3.y, c4.y);
853
854 vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
855 'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
856 filter.join(''), ", sizingmethod='clip');");
857
858 } else {
859 vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
860 }
861
862 vmlStr.push(' ">' ,
863 '<g_vml_:image src="', image.src, '"',
864 ' style="width:', Z * dw, 'px;',
865 ' height:', Z * dh, 'px"',
866 ' cropleft="', sx / w, '"',
867 ' croptop="', sy / h, '"',
868 ' cropright="', (w - sx - sw) / w, '"',
869 ' cropbottom="', (h - sy - sh) / h, '"',
870 ' />',
871 '</g_vml_:group>');
872
873 this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join(''));
874 };
875
876 contextPrototype.stroke = function(aFill) {
877 var W = 10;
878 var H = 10;
879 // Divide the shape into chunks if it's too long because IE has a limit
880 // somewhere for how long a VML shape can be. This simple division does
881 // not work with fills, only strokes, unfortunately.
882 var chunkSize = 5000;
883
884 var min = {x: null, y: null};
885 var max = {x: null, y: null};
886
887 for (var j = 0; j < this.currentPath_.length; j += chunkSize) {
888 var lineStr = [];
889 var lineOpen = false;
890
891 lineStr.push('<g_vml_:shape',
892 ' filled="', !!aFill, '"',
893 ' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
894 ' coordorigin="0,0"',
895 ' coordsize="', Z * W, ',', Z * H, '"',
896 ' stroked="', !aFill, '"',
897 ' path="');
898
899 var newSeq = false;
900
901 for (var i = j; i < Math.min(j + chunkSize, this.currentPath_.length); i++) {
902 if (i % chunkSize == 0 && i > 0) { // move into position for next chunk
903 lineStr.push(' m ', mr(this.currentPath_[i-1].x), ',', mr(this.currentPath_[i-1].y));
904 }
905
906 var p = this.currentPath_[i];
907 var c;
908
909 switch (p.type) {
910 case 'moveTo':
911 c = p;
912 lineStr.push(' m ', mr(p.x), ',', mr(p.y));
913 break;
914 case 'lineTo':
915 lineStr.push(' l ', mr(p.x), ',', mr(p.y));
916 break;
917 case 'close':
918 lineStr.push(' x ');
919 p = null;
920 break;
921 case 'bezierCurveTo':
922 lineStr.push(' c ',
923 mr(p.cp1x), ',', mr(p.cp1y), ',',
924 mr(p.cp2x), ',', mr(p.cp2y), ',',
925 mr(p.x), ',', mr(p.y));
926 break;
927 case 'at':
928 case 'wa':
929 lineStr.push(' ', p.type, ' ',
930 mr(p.x - this.arcScaleX_ * p.radius), ',',
931 mr(p.y - this.arcScaleY_ * p.radius), ' ',
932 mr(p.x + this.arcScaleX_ * p.radius), ',',
933 mr(p.y + this.arcScaleY_ * p.radius), ' ',
934 mr(p.xStart), ',', mr(p.yStart), ' ',
935 mr(p.xEnd), ',', mr(p.yEnd));
936 break;
937 }
938
939
940 // TODO: Following is broken for curves due to
941 // move to proper paths.
942
943 // Figure out dimensions so we can do gradient fills
944 // properly
945 if (p) {
946 if (min.x == null || p.x < min.x) {
947 min.x = p.x;
948 }
949 if (max.x == null || p.x > max.x) {
950 max.x = p.x;
951 }
952 if (min.y == null || p.y < min.y) {
953 min.y = p.y;
954 }
955 if (max.y == null || p.y > max.y) {
956 max.y = p.y;
957 }
958 }
959 }
960 lineStr.push(' ">');
961
962 if (!aFill) {
963 appendStroke(this, lineStr);
964 } else {
965 appendFill(this, lineStr, min, max);
966 }
967
968 lineStr.push('</g_vml_:shape>');
969
970 this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
971 }
972 };
973
974 function appendStroke(ctx, lineStr) {
975 var a = processStyle(ctx.strokeStyle);
976 var color = a.color;
977 var opacity = a.alpha * ctx.globalAlpha;
978 var lineWidth = ctx.lineScale_ * ctx.lineWidth;
979
980 // VML cannot correctly render a line if the width is less than 1px.
981 // In that case, we dilute the color to make the line look thinner.
982 if (lineWidth < 1) {
983 opacity *= lineWidth;
984 }
985
986 lineStr.push(
987 '<g_vml_:stroke',
988 ' opacity="', opacity, '"',
989 ' joinstyle="', ctx.lineJoin, '"',
990 ' miterlimit="', ctx.miterLimit, '"',
991 ' endcap="', processLineCap(ctx.lineCap), '"',
992 ' weight="', lineWidth, 'px"',
993 ' color="', color, '" />'
994 );
995 }
996
997 function appendFill(ctx, lineStr, min, max) {
998 var fillStyle = ctx.fillStyle;
999 var arcScaleX = ctx.arcScaleX_;
1000 var arcScaleY = ctx.arcScaleY_;
1001 var width = max.x - min.x;
1002 var height = max.y - min.y;
1003 if (fillStyle instanceof CanvasGradient_) {
1004 // TODO: Gradients transformed with the transformation matrix.
1005 var angle = 0;
1006 var focus = {x: 0, y: 0};
1007
1008 // additional offset
1009 var shift = 0;
1010 // scale factor for offset
1011 var expansion = 1;
1012
1013 if (fillStyle.type_ == 'gradient') {
1014 var x0 = fillStyle.x0_ / arcScaleX;
1015 var y0 = fillStyle.y0_ / arcScaleY;
1016 var x1 = fillStyle.x1_ / arcScaleX;
1017 var y1 = fillStyle.y1_ / arcScaleY;
1018 var p0 = ctx.getCoords_(x0, y0);
1019 var p1 = ctx.getCoords_(x1, y1);
1020 var dx = p1.x - p0.x;
1021 var dy = p1.y - p0.y;
1022 angle = Math.atan2(dx, dy) * 180 / Math.PI;
1023
1024 // The angle should be a non-negative number.
1025 if (angle < 0) {
1026 angle += 360;
1027 }
1028
1029 // Very small angles produce an unexpected result because they are
1030 // converted to a scientific notation string.
1031 if (angle < 1e-6) {
1032 angle = 0;
1033 }
1034 } else {
1035 var p0 = ctx.getCoords_(fillStyle.x0_, fillStyle.y0_);
1036 focus = {
1037 x: (p0.x - min.x) / width,
1038 y: (p0.y - min.y) / height
1039 };
1040
1041 width /= arcScaleX * Z;
1042 height /= arcScaleY * Z;
1043 var dimension = m.max(width, height);
1044 shift = 2 * fillStyle.r0_ / dimension;
1045 expansion = 2 * fillStyle.r1_ / dimension - shift;
1046 }
1047
1048 // We need to sort the color stops in ascending order by offset,
1049 // otherwise IE won't interpret it correctly.
1050 var stops = fillStyle.colors_;
1051 stops.sort(function(cs1, cs2) {
1052 return cs1.offset - cs2.offset;
1053 });
1054
1055 var length = stops.length;
1056 var color1 = stops[0].color;
1057 var color2 = stops[length - 1].color;
1058 var opacity1 = stops[0].alpha * ctx.globalAlpha;
1059 var opacity2 = stops[length - 1].alpha * ctx.globalAlpha;
1060
1061 var colors = [];
1062 for (var i = 0; i < length; i++) {
1063 var stop = stops[i];
1064 colors.push(stop.offset * expansion + shift + ' ' + stop.color);
1065 }
1066
1067 // When colors attribute is used, the meanings of opacity and o:opacity2
1068 // are reversed.
1069 lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
1070 ' method="none" focus="100%"',
1071 ' color="', color1, '"',
1072 ' color2="', color2, '"',
1073 ' colors="', colors.join(','), '"',
1074 ' opacity="', opacity2, '"',
1075 ' g_o_:opacity2="', opacity1, '"',
1076 ' angle="', angle, '"',
1077 ' focusposition="', focus.x, ',', focus.y, '" />');
1078 } else if (fillStyle instanceof CanvasPattern_) {
1079 if (width && height) {
1080 var deltaLeft = -min.x;
1081 var deltaTop = -min.y;
1082 lineStr.push('<g_vml_:fill',
1083 ' position="',
1084 deltaLeft / width * arcScaleX * arcScaleX, ',',
1085 deltaTop / height * arcScaleY * arcScaleY, '"',
1086 ' type="tile"',
1087 // TODO: Figure out the correct size to fit the scale.
1088 //' size="', w, 'px ', h, 'px"',
1089 ' src="', fillStyle.src_, '" />');
1090 }
1091 } else {
1092 var a = processStyle(ctx.fillStyle);
1093 var color = a.color;
1094 var opacity = a.alpha * ctx.globalAlpha;
1095 lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
1096 '" />');
1097 }
1098 }
1099
1100 contextPrototype.fill = function() {
1101 this.stroke(true);
1102 };
1103
1104 contextPrototype.closePath = function() {
1105 this.currentPath_.push({type: 'close'});
1106 };
1107
1108 /**
1109 * @private
1110 */
1111 contextPrototype.getCoords_ = function(aX, aY) {
1112 var m = this.m_;
1113 return {
1114 x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
1115 y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
1116 };
1117 };
1118
1119 contextPrototype.save = function() {
1120 var o = {};
1121 copyState(this, o);
1122 this.aStack_.push(o);
1123 this.mStack_.push(this.m_);
1124 this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
1125 };
1126
1127 contextPrototype.restore = function() {
1128 if (this.aStack_.length) {
1129 copyState(this.aStack_.pop(), this);
1130 this.m_ = this.mStack_.pop();
1131 }
1132 };
1133
1134 function matrixIsFinite(m) {
1135 return isFinite(m[0][0]) && isFinite(m[0][1]) &&
1136 isFinite(m[1][0]) && isFinite(m[1][1]) &&
1137 isFinite(m[2][0]) && isFinite(m[2][1]);
1138 }
1139
1140 function setM(ctx, m, updateLineScale) {
1141 if (!matrixIsFinite(m)) {
1142 return;
1143 }
1144 ctx.m_ = m;
1145
1146 if (updateLineScale) {
1147 // Get the line scale.
1148 // Determinant of this.m_ means how much the area is enlarged by the
1149 // transformation. So its square root can be used as a scale factor
1150 // for width.
1151 var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
1152 ctx.lineScale_ = sqrt(abs(det));
1153 }
1154 }
1155
1156 contextPrototype.translate = function(aX, aY) {
1157 var m1 = [
1158 [1, 0, 0],
1159 [0, 1, 0],
1160 [aX, aY, 1]
1161 ];
1162
1163 setM(this, matrixMultiply(m1, this.m_), false);
1164 };
1165
1166 contextPrototype.rotate = function(aRot) {
1167 var c = mc(aRot);
1168 var s = ms(aRot);
1169
1170 var m1 = [
1171 [c, s, 0],
1172 [-s, c, 0],
1173 [0, 0, 1]
1174 ];
1175
1176 setM(this, matrixMultiply(m1, this.m_), false);
1177 };
1178
1179 contextPrototype.scale = function(aX, aY) {
1180 this.arcScaleX_ *= aX;
1181 this.arcScaleY_ *= aY;
1182 var m1 = [
1183 [aX, 0, 0],
1184 [0, aY, 0],
1185 [0, 0, 1]
1186 ];
1187
1188 setM(this, matrixMultiply(m1, this.m_), true);
1189 };
1190
1191 contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
1192 var m1 = [
1193 [m11, m12, 0],
1194 [m21, m22, 0],
1195 [dx, dy, 1]
1196 ];
1197
1198 setM(this, matrixMultiply(m1, this.m_), true);
1199 };
1200
1201 contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
1202 var m = [
1203 [m11, m12, 0],
1204 [m21, m22, 0],
1205 [dx, dy, 1]
1206 ];
1207
1208 setM(this, m, true);
1209 };
1210
1211 /**
1212 * The text drawing function.
1213 * The maxWidth argument isn't taken in account, since no browser supports
1214 * it yet.
1215 */
1216 contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) {
1217 var m = this.m_,
1218 delta = 1000,
1219 left = 0,
1220 right = delta,
1221 offset = {x: 0, y: 0},
1222 lineStr = [];
1223
1224 var fontStyle = getComputedStyle(processFontStyle(this.font),
1225 this.element_);
1226
1227 var fontStyleString = buildStyle(fontStyle);
1228
1229 var elementStyle = this.element_.currentStyle;
1230 var textAlign = this.textAlign.toLowerCase();
1231 switch (textAlign) {
1232 case 'left':
1233 case 'center':
1234 case 'right':
1235 break;
1236 case 'end':
1237 textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left';
1238 break;
1239 case 'start':
1240 textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left';
1241 break;
1242 default:
1243 textAlign = 'left';
1244 }
1245
1246 // 1.75 is an arbitrary number, as there is no info about the text baseline
1247 switch (this.textBaseline) {
1248 case 'hanging':
1249 case 'top':
1250 offset.y = fontStyle.size / 1.75;
1251 break;
1252 case 'middle':
1253 break;
1254 default:
1255 case null:
1256 case 'alphabetic':
1257 case 'ideographic':
1258 case 'bottom':
1259 offset.y = -fontStyle.size / 2.25;
1260 break;
1261 }
1262
1263 switch(textAlign) {
1264 case 'right':
1265 left = delta;
1266 right = 0.05;
1267 break;
1268 case 'center':
1269 left = right = delta / 2;
1270 break;
1271 }
1272
1273 var d = this.getCoords_(x + offset.x, y + offset.y);
1274
1275 lineStr.push('<g_vml_:line from="', -left ,' 0" to="', right ,' 0.05" ',
1276 ' coordsize="100 100" coordorigin="0 0"',
1277 ' filled="', !stroke, '" stroked="', !!stroke,
1278 '" style="position:absolute;width:1px;height:1px;">');
1279
1280 if (stroke) {
1281 appendStroke(this, lineStr);
1282 } else {
1283 // TODO: Fix the min and max params.
1284 appendFill(this, lineStr, {x: -left, y: 0},
1285 {x: right, y: fontStyle.size});
1286 }
1287
1288 var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' +
1289 m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0';
1290
1291 var skewOffset = mr(d.x / Z) + ',' + mr(d.y / Z);
1292
1293 lineStr.push('<g_vml_:skew on="t" matrix="', skewM ,'" ',
1294 ' offset="', skewOffset, '" origin="', left ,' 0" />',
1295 '<g_vml_:path textpathok="true" />',
1296 '<g_vml_:textpath on="true" string="',
1297 encodeHtmlAttribute(text),
1298 '" style="v-text-align:', textAlign,
1299 ';font:', encodeHtmlAttribute(fontStyleString),
1300 '" /></g_vml_:line>');
1301
1302 this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
1303 };
1304
1305 contextPrototype.fillText = function(text, x, y, maxWidth) {
1306 this.drawText_(text, x, y, maxWidth, false);
1307 };
1308
1309 contextPrototype.strokeText = function(text, x, y, maxWidth) {
1310 this.drawText_(text, x, y, maxWidth, true);
1311 };
1312
1313 contextPrototype.measureText = function(text) {
1314 if (!this.textMeasureEl_) {
1315 var s = '<span style="position:absolute;' +
1316 'top:-20000px;left:0;padding:0;margin:0;border:none;' +
1317 'white-space:pre;"></span>';
1318 this.element_.insertAdjacentHTML('beforeEnd', s);
1319 this.textMeasureEl_ = this.element_.lastChild;
1320 }
1321 var doc = this.element_.ownerDocument;
1322 this.textMeasureEl_.innerHTML = '';
1323 this.textMeasureEl_.style.font = this.font;
1324 // Don't use innerHTML or innerText because they allow markup/whitespace.
1325 this.textMeasureEl_.appendChild(doc.createTextNode(text));
1326 return {width: this.textMeasureEl_.offsetWidth};
1327 };
1328
1329 /******** STUBS ********/
1330 contextPrototype.clip = function() {
1331 // TODO: Implement
1332 };
1333
1334 contextPrototype.arcTo = function() {
1335 // TODO: Implement
1336 };
1337
1338 contextPrototype.createPattern = function(image, repetition) {
1339 return new CanvasPattern_(image, repetition);
1340 };
1341
1342 // Gradient / Pattern Stubs
1343 function CanvasGradient_(aType) {
1344 this.type_ = aType;
1345 this.x0_ = 0;
1346 this.y0_ = 0;
1347 this.r0_ = 0;
1348 this.x1_ = 0;
1349 this.y1_ = 0;
1350 this.r1_ = 0;
1351 this.colors_ = [];
1352 }
1353
1354 CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
1355 aColor = processStyle(aColor);
1356 this.colors_.push({offset: aOffset,
1357 color: aColor.color,
1358 alpha: aColor.alpha});
1359 };
1360
1361 function CanvasPattern_(image, repetition) {
1362 assertImageIsValid(image);
1363 switch (repetition) {
1364 case 'repeat':
1365 case null:
1366 case '':
1367 this.repetition_ = 'repeat';
1368 break
1369 case 'repeat-x':
1370 case 'repeat-y':
1371 case 'no-repeat':
1372 this.repetition_ = repetition;
1373 break;
1374 default:
1375 throwException('SYNTAX_ERR');
1376 }
1377
1378 this.src_ = image.src;
1379 this.width_ = image.width;
1380 this.height_ = image.height;
1381 }
1382
1383 function throwException(s) {
1384 throw new DOMException_(s);
1385 }
1386
1387 function assertImageIsValid(img) {
1388 if (!img || img.nodeType != 1 || img.tagName != 'IMG') {
1389 throwException('TYPE_MISMATCH_ERR');
1390 }
1391 if (img.readyState != 'complete') {
1392 throwException('INVALID_STATE_ERR');
1393 }
1394 }
1395
1396 function DOMException_(s) {
1397 this.code = this[s];
1398 this.message = s +': DOM Exception ' + this.code;
1399 }
1400 var p = DOMException_.prototype = new Error;
1401 p.INDEX_SIZE_ERR = 1;
1402 p.DOMSTRING_SIZE_ERR = 2;
1403 p.HIERARCHY_REQUEST_ERR = 3;
1404 p.WRONG_DOCUMENT_ERR = 4;
1405 p.INVALID_CHARACTER_ERR = 5;
1406 p.NO_DATA_ALLOWED_ERR = 6;
1407 p.NO_MODIFICATION_ALLOWED_ERR = 7;
1408 p.NOT_FOUND_ERR = 8;
1409 p.NOT_SUPPORTED_ERR = 9;
1410 p.INUSE_ATTRIBUTE_ERR = 10;
1411 p.INVALID_STATE_ERR = 11;
1412 p.SYNTAX_ERR = 12;
1413 p.INVALID_MODIFICATION_ERR = 13;
1414 p.NAMESPACE_ERR = 14;
1415 p.INVALID_ACCESS_ERR = 15;
1416 p.VALIDATION_ERR = 16;
1417 p.TYPE_MISMATCH_ERR = 17;
1418
1419 // set up externs
1420 G_vmlCanvasManager = G_vmlCanvasManager_;
1421 CanvasRenderingContext2D = CanvasRenderingContext2D_;
1422 CanvasGradient = CanvasGradient_;
1423 CanvasPattern = CanvasPattern_;
1424 DOMException = DOMException_;
1425 })();
1426
1427 } // if
1428