source: Dev/branches/jQueryUI/client/RGraph/excanvas/excanvas.original.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

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