reservation plugin - unbound request (unclean
[unfold.git] / portal / static / unbound_reservation_static / lib / thirdparty / excanvas / excanvas.js
1 // Copyright 2006 Google Inc.\r
2 //\r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //   http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 \r
16 // Known Issues:\r
17 //\r
18 // * Patterns are not implemented.\r
19 // * Radial gradient are not implemented. The VML version of these look very\r
20 //   different from the canvas one.\r
21 // * Clipping paths are not implemented.\r
22 // * Coordsize. The width and height attribute have higher priority than the\r
23 //   width and height style values which isn't correct.\r
24 // * Painting mode isn't implemented.\r
25 // * Canvas width/height should is using content-box by default. IE in\r
26 //   Quirks mode will draw the canvas using border-box. Either change your\r
27 //   doctype to HTML5\r
28 //   (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)\r
29 //   or use Box Sizing Behavior from WebFX\r
30 //   (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)\r
31 // * Non uniform scaling does not correctly scale strokes.\r
32 // * Optimize. There is always room for speed improvements.\r
33 \r
34 // Only add this code if we do not already have a canvas implementation\r
35 if (!document.createElement('canvas').getContext) {\r
36 \r
37 (function() {\r
38 \r
39   // alias some functions to make (compiled) code shorter\r
40   var m = Math;\r
41   var mr = m.round;\r
42   var ms = m.sin;\r
43   var mc = m.cos;\r
44   var abs = m.abs;\r
45   var sqrt = m.sqrt;\r
46 \r
47   // this is used for sub pixel precision\r
48   var Z = 10;\r
49   var Z2 = Z / 2;\r
50 \r
51   /**\r
52    * This funtion is assigned to the <canvas> elements as element.getContext().\r
53    * @this {HTMLElement}\r
54    * @return {CanvasRenderingContext2D_}\r
55    */\r
56   function getContext() {\r
57     return this.context_ ||\r
58         (this.context_ = new CanvasRenderingContext2D_(this));\r
59   }\r
60 \r
61   var slice = Array.prototype.slice;\r
62 \r
63   /**\r
64    * Binds a function to an object. The returned function will always use the\r
65    * passed in {@code obj} as {@code this}.\r
66    *\r
67    * Example:\r
68    *\r
69    *   g = bind(f, obj, a, b)\r
70    *   g(c, d) // will do f.call(obj, a, b, c, d)\r
71    *\r
72    * @param {Function} f The function to bind the object to\r
73    * @param {Object} obj The object that should act as this when the function\r
74    *     is called\r
75    * @param {*} var_args Rest arguments that will be used as the initial\r
76    *     arguments when the function is called\r
77    * @return {Function} A new function that has bound this\r
78    */\r
79   function bind(f, obj, var_args) {\r
80     var a = slice.call(arguments, 2);\r
81     return function() {\r
82       return f.apply(obj, a.concat(slice.call(arguments)));\r
83     };\r
84   }\r
85 \r
86   var G_vmlCanvasManager_ = {\r
87     init: function(opt_doc) {\r
88       if (/MSIE/.test(navigator.userAgent) && !window.opera) {\r
89         var doc = opt_doc || document;\r
90         // Create a dummy element so that IE will allow canvas elements to be\r
91         // recognized.\r
92         doc.createElement('canvas');\r
93         doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));\r
94       }\r
95     },\r
96 \r
97     init_: function(doc) {\r
98       // create xmlns\r
99       if (!doc.namespaces['g_vml_']) {\r
100         doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml',\r
101                            '#default#VML');\r
102 \r
103       }\r
104       if (!doc.namespaces['g_o_']) {\r
105         doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office',\r
106                            '#default#VML');\r
107       }\r
108 \r
109       // Setup default CSS.  Only add one style sheet per document\r
110       if (!doc.styleSheets['ex_canvas_']) {\r
111         var ss = doc.createStyleSheet();\r
112         ss.owningElement.id = 'ex_canvas_';\r
113         ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +\r
114             // default size is 300x150 in Gecko and Opera\r
115             'text-align:left;width:300px;height:150px}' +\r
116             'g_vml_\\:*{behavior:url(#default#VML)}' +\r
117             'g_o_\\:*{behavior:url(#default#VML)}';\r
118 \r
119       }\r
120 \r
121       // find all canvas elements\r
122       var els = doc.getElementsByTagName('canvas');\r
123       for (var i = 0; i < els.length; i++) {\r
124         this.initElement(els[i]);\r
125       }\r
126     },\r
127 \r
128     /**\r
129      * Public initializes a canvas element so that it can be used as canvas\r
130      * element from now on. This is called automatically before the page is\r
131      * loaded but if you are creating elements using createElement you need to\r
132      * make sure this is called on the element.\r
133      * @param {HTMLElement} el The canvas element to initialize.\r
134      * @return {HTMLElement} the element that was created.\r
135      */\r
136     initElement: function(el) {\r
137       if (!el.getContext) {\r
138 \r
139         el.getContext = getContext;\r
140 \r
141         // Remove fallback content. There is no way to hide text nodes so we\r
142         // just remove all childNodes. We could hide all elements and remove\r
143         // text nodes but who really cares about the fallback content.\r
144         el.innerHTML = '';\r
145 \r
146         // do not use inline function because that will leak memory\r
147         el.attachEvent('onpropertychange', onPropertyChange);\r
148         el.attachEvent('onresize', onResize);\r
149 \r
150         var attrs = el.attributes;\r
151         if (attrs.width && attrs.width.specified) {\r
152           // TODO: use runtimeStyle and coordsize\r
153           // el.getContext().setWidth_(attrs.width.nodeValue);\r
154           el.style.width = attrs.width.nodeValue + 'px';\r
155         } else {\r
156           el.width = el.clientWidth;\r
157         }\r
158         if (attrs.height && attrs.height.specified) {\r
159           // TODO: use runtimeStyle and coordsize\r
160           // el.getContext().setHeight_(attrs.height.nodeValue);\r
161           el.style.height = attrs.height.nodeValue + 'px';\r
162         } else {\r
163           el.height = el.clientHeight;\r
164         }\r
165         //el.getContext().setCoordsize_()\r
166       }\r
167       return el;\r
168     }\r
169   };\r
170 \r
171   function onPropertyChange(e) {\r
172     var el = e.srcElement;\r
173 \r
174     switch (e.propertyName) {\r
175       case 'width':\r
176         el.style.width = el.attributes.width.nodeValue + 'px';\r
177         el.getContext().clearRect();\r
178         break;\r
179       case 'height':\r
180         el.style.height = el.attributes.height.nodeValue + 'px';\r
181         el.getContext().clearRect();\r
182         break;\r
183     }\r
184   }\r
185 \r
186   function onResize(e) {\r
187     var el = e.srcElement;\r
188     if (el.firstChild) {\r
189       el.firstChild.style.width =  el.clientWidth + 'px';\r
190       el.firstChild.style.height = el.clientHeight + 'px';\r
191     }\r
192   }\r
193 \r
194   G_vmlCanvasManager_.init();\r
195 \r
196   // precompute "00" to "FF"\r
197   var dec2hex = [];\r
198   for (var i = 0; i < 16; i++) {\r
199     for (var j = 0; j < 16; j++) {\r
200       dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);\r
201     }\r
202   }\r
203 \r
204   function createMatrixIdentity() {\r
205     return [\r
206       [1, 0, 0],\r
207       [0, 1, 0],\r
208       [0, 0, 1]\r
209     ];\r
210   }\r
211 \r
212   function matrixMultiply(m1, m2) {\r
213     var result = createMatrixIdentity();\r
214 \r
215     for (var x = 0; x < 3; x++) {\r
216       for (var y = 0; y < 3; y++) {\r
217         var sum = 0;\r
218 \r
219         for (var z = 0; z < 3; z++) {\r
220           sum += m1[x][z] * m2[z][y];\r
221         }\r
222 \r
223         result[x][y] = sum;\r
224       }\r
225     }\r
226     return result;\r
227   }\r
228 \r
229   function copyState(o1, o2) {\r
230     o2.fillStyle     = o1.fillStyle;\r
231     o2.lineCap       = o1.lineCap;\r
232     o2.lineJoin      = o1.lineJoin;\r
233     o2.lineWidth     = o1.lineWidth;\r
234     o2.miterLimit    = o1.miterLimit;\r
235     o2.shadowBlur    = o1.shadowBlur;\r
236     o2.shadowColor   = o1.shadowColor;\r
237     o2.shadowOffsetX = o1.shadowOffsetX;\r
238     o2.shadowOffsetY = o1.shadowOffsetY;\r
239     o2.strokeStyle   = o1.strokeStyle;\r
240     o2.globalAlpha   = o1.globalAlpha;\r
241     o2.arcScaleX_    = o1.arcScaleX_;\r
242     o2.arcScaleY_    = o1.arcScaleY_;\r
243     o2.lineScale_    = o1.lineScale_;\r
244   }\r
245 \r
246   function processStyle(styleString) {\r
247     var str, alpha = 1;\r
248 \r
249     styleString = String(styleString);\r
250     if (styleString.substring(0, 3) == 'rgb') {\r
251       var start = styleString.indexOf('(', 3);\r
252       var end = styleString.indexOf(')', start + 1);\r
253       var guts = styleString.substring(start + 1, end).split(',');\r
254 \r
255       str = '#';\r
256       for (var i = 0; i < 3; i++) {\r
257         str += dec2hex[Number(guts[i])];\r
258       }\r
259 \r
260       if (guts.length == 4 && styleString.substr(3, 1) == 'a') {\r
261         alpha = guts[3];\r
262       }\r
263     } else {\r
264       str = styleString;\r
265     }\r
266 \r
267     return {color: str, alpha: alpha};\r
268   }\r
269 \r
270   function processLineCap(lineCap) {\r
271     switch (lineCap) {\r
272       case 'butt':\r
273         return 'flat';\r
274       case 'round':\r
275         return 'round';\r
276       case 'square':\r
277       default:\r
278         return 'square';\r
279     }\r
280   }\r
281 \r
282   /**\r
283    * This class implements CanvasRenderingContext2D interface as described by\r
284    * the WHATWG.\r
285    * @param {HTMLElement} surfaceElement The element that the 2D context should\r
286    * be associated with\r
287    */\r
288   function CanvasRenderingContext2D_(surfaceElement) {\r
289     this.m_ = createMatrixIdentity();\r
290 \r
291     this.mStack_ = [];\r
292     this.aStack_ = [];\r
293     this.currentPath_ = [];\r
294 \r
295     // Canvas context properties\r
296     this.strokeStyle = '#000';\r
297     this.fillStyle = '#000';\r
298 \r
299     this.lineWidth = 1;\r
300     this.lineJoin = 'miter';\r
301     this.lineCap = 'butt';\r
302     this.miterLimit = Z * 1;\r
303     this.globalAlpha = 1;\r
304     this.canvas = surfaceElement;\r
305 \r
306     var el = surfaceElement.ownerDocument.createElement('div');\r
307     el.style.width =  surfaceElement.clientWidth + 'px';\r
308     el.style.height = surfaceElement.clientHeight + 'px';\r
309     el.style.overflow = 'hidden';\r
310     el.style.position = 'absolute';\r
311     surfaceElement.appendChild(el);\r
312 \r
313     this.element_ = el;\r
314     this.arcScaleX_ = 1;\r
315     this.arcScaleY_ = 1;\r
316     this.lineScale_ = 1;\r
317   }\r
318 \r
319   var contextPrototype = CanvasRenderingContext2D_.prototype;\r
320   contextPrototype.clearRect = function() {\r
321     this.element_.innerHTML = '';\r
322   };\r
323 \r
324   contextPrototype.beginPath = function() {\r
325     // TODO: Branch current matrix so that save/restore has no effect\r
326     //       as per safari docs.\r
327     this.currentPath_ = [];\r
328   };\r
329 \r
330   contextPrototype.moveTo = function(aX, aY) {\r
331     var p = this.getCoords_(aX, aY);\r
332     this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});\r
333     this.currentX_ = p.x;\r
334     this.currentY_ = p.y;\r
335   };\r
336 \r
337   contextPrototype.lineTo = function(aX, aY) {\r
338     var p = this.getCoords_(aX, aY);\r
339     this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});\r
340 \r
341     this.currentX_ = p.x;\r
342     this.currentY_ = p.y;\r
343   };\r
344 \r
345   contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,\r
346                                             aCP2x, aCP2y,\r
347                                             aX, aY) {\r
348     var p = this.getCoords_(aX, aY);\r
349     var cp1 = this.getCoords_(aCP1x, aCP1y);\r
350     var cp2 = this.getCoords_(aCP2x, aCP2y);\r
351     bezierCurveTo(this, cp1, cp2, p);\r
352   };\r
353 \r
354   // Helper function that takes the already fixed cordinates.\r
355   function bezierCurveTo(self, cp1, cp2, p) {\r
356     self.currentPath_.push({\r
357       type: 'bezierCurveTo',\r
358       cp1x: cp1.x,\r
359       cp1y: cp1.y,\r
360       cp2x: cp2.x,\r
361       cp2y: cp2.y,\r
362       x: p.x,\r
363       y: p.y\r
364     });\r
365     self.currentX_ = p.x;\r
366     self.currentY_ = p.y;\r
367   }\r
368 \r
369   contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {\r
370     // the following is lifted almost directly from\r
371     // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes\r
372 \r
373     var cp = this.getCoords_(aCPx, aCPy);\r
374     var p = this.getCoords_(aX, aY);\r
375 \r
376     var cp1 = {\r
377       x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),\r
378       y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)\r
379     };\r
380     var cp2 = {\r
381       x: cp1.x + (p.x - this.currentX_) / 3.0,\r
382       y: cp1.y + (p.y - this.currentY_) / 3.0\r
383     };\r
384 \r
385     bezierCurveTo(this, cp1, cp2, p);\r
386   };\r
387 \r
388   contextPrototype.arc = function(aX, aY, aRadius,\r
389                                   aStartAngle, aEndAngle, aClockwise) {\r
390     aRadius *= Z;\r
391     var arcType = aClockwise ? 'at' : 'wa';\r
392 \r
393     var xStart = aX + mc(aStartAngle) * aRadius - Z2;\r
394     var yStart = aY + ms(aStartAngle) * aRadius - Z2;\r
395 \r
396     var xEnd = aX + mc(aEndAngle) * aRadius - Z2;\r
397     var yEnd = aY + ms(aEndAngle) * aRadius - Z2;\r
398 \r
399     // IE won't render arches drawn counter clockwise if xStart == xEnd.\r
400     if (xStart == xEnd && !aClockwise) {\r
401       xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something\r
402                        // that can be represented in binary\r
403     }\r
404 \r
405     var p = this.getCoords_(aX, aY);\r
406     var pStart = this.getCoords_(xStart, yStart);\r
407     var pEnd = this.getCoords_(xEnd, yEnd);\r
408 \r
409     this.currentPath_.push({type: arcType,\r
410                            x: p.x,\r
411                            y: p.y,\r
412                            radius: aRadius,\r
413                            xStart: pStart.x,\r
414                            yStart: pStart.y,\r
415                            xEnd: pEnd.x,\r
416                            yEnd: pEnd.y});\r
417 \r
418   };\r
419 \r
420   contextPrototype.rect = function(aX, aY, aWidth, aHeight) {\r
421     this.moveTo(aX, aY);\r
422     this.lineTo(aX + aWidth, aY);\r
423     this.lineTo(aX + aWidth, aY + aHeight);\r
424     this.lineTo(aX, aY + aHeight);\r
425     this.closePath();\r
426   };\r
427 \r
428   contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {\r
429     var oldPath = this.currentPath_;\r
430     this.beginPath();\r
431 \r
432     this.moveTo(aX, aY);\r
433     this.lineTo(aX + aWidth, aY);\r
434     this.lineTo(aX + aWidth, aY + aHeight);\r
435     this.lineTo(aX, aY + aHeight);\r
436     this.closePath();\r
437     this.stroke();\r
438 \r
439     this.currentPath_ = oldPath;\r
440   };\r
441 \r
442   contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {\r
443     var oldPath = this.currentPath_;\r
444     this.beginPath();\r
445 \r
446     this.moveTo(aX, aY);\r
447     this.lineTo(aX + aWidth, aY);\r
448     this.lineTo(aX + aWidth, aY + aHeight);\r
449     this.lineTo(aX, aY + aHeight);\r
450     this.closePath();\r
451     this.fill();\r
452 \r
453     this.currentPath_ = oldPath;\r
454   };\r
455 \r
456   contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {\r
457     var gradient = new CanvasGradient_('gradient');\r
458     gradient.x0_ = aX0;\r
459     gradient.y0_ = aY0;\r
460     gradient.x1_ = aX1;\r
461     gradient.y1_ = aY1;\r
462     return gradient;\r
463   };\r
464 \r
465   contextPrototype.createRadialGradient = function(aX0, aY0, aR0,\r
466                                                    aX1, aY1, aR1) {\r
467     var gradient = new CanvasGradient_('gradientradial');\r
468     gradient.x0_ = aX0;\r
469     gradient.y0_ = aY0;\r
470     gradient.r0_ = aR0;\r
471     gradient.x1_ = aX1;\r
472     gradient.y1_ = aY1;\r
473     gradient.r1_ = aR1;\r
474     return gradient;\r
475   };\r
476 \r
477   contextPrototype.drawImage = function(image, var_args) {\r
478     var dx, dy, dw, dh, sx, sy, sw, sh;\r
479 \r
480     // to find the original width we overide the width and height\r
481     var oldRuntimeWidth = image.runtimeStyle.width;\r
482     var oldRuntimeHeight = image.runtimeStyle.height;\r
483     image.runtimeStyle.width = 'auto';\r
484     image.runtimeStyle.height = 'auto';\r
485 \r
486     // get the original size\r
487     var w = image.width;\r
488     var h = image.height;\r
489 \r
490     // and remove overides\r
491     image.runtimeStyle.width = oldRuntimeWidth;\r
492     image.runtimeStyle.height = oldRuntimeHeight;\r
493 \r
494     if (arguments.length == 3) {\r
495       dx = arguments[1];\r
496       dy = arguments[2];\r
497       sx = sy = 0;\r
498       sw = dw = w;\r
499       sh = dh = h;\r
500     } else if (arguments.length == 5) {\r
501       dx = arguments[1];\r
502       dy = arguments[2];\r
503       dw = arguments[3];\r
504       dh = arguments[4];\r
505       sx = sy = 0;\r
506       sw = w;\r
507       sh = h;\r
508     } else if (arguments.length == 9) {\r
509       sx = arguments[1];\r
510       sy = arguments[2];\r
511       sw = arguments[3];\r
512       sh = arguments[4];\r
513       dx = arguments[5];\r
514       dy = arguments[6];\r
515       dw = arguments[7];\r
516       dh = arguments[8];\r
517     } else {\r
518       throw Error('Invalid number of arguments');\r
519     }\r
520 \r
521     var d = this.getCoords_(dx, dy);\r
522 \r
523     var w2 = sw / 2;\r
524     var h2 = sh / 2;\r
525 \r
526     var vmlStr = [];\r
527 \r
528     var W = 10;\r
529     var H = 10;\r
530 \r
531     // For some reason that I've now forgotten, using divs didn't work\r
532     vmlStr.push(' <g_vml_:group',\r
533                 ' coordsize="', Z * W, ',', Z * H, '"',\r
534                 ' coordorigin="0,0"' ,\r
535                 ' style="width:', W, 'px;height:', H, 'px;position:absolute;');\r
536 \r
537     // If filters are necessary (rotation exists), create them\r
538     // filters are bog-slow, so only create them if abbsolutely necessary\r
539     // The following check doesn't account for skews (which don't exist\r
540     // in the canvas spec (yet) anyway.\r
541 \r
542     if (this.m_[0][0] != 1 || this.m_[0][1]) {\r
543       var filter = [];\r
544 \r
545       // Note the 12/21 reversal\r
546       filter.push('M11=', this.m_[0][0], ',',\r
547                   'M12=', this.m_[1][0], ',',\r
548                   'M21=', this.m_[0][1], ',',\r
549                   'M22=', this.m_[1][1], ',',\r
550                   'Dx=', mr(d.x / Z), ',',\r
551                   'Dy=', mr(d.y / Z), '');\r
552 \r
553       // Bounding box calculation (need to minimize displayed area so that\r
554       // filters don't waste time on unused pixels.\r
555       var max = d;\r
556       var c2 = this.getCoords_(dx + dw, dy);\r
557       var c3 = this.getCoords_(dx, dy + dh);\r
558       var c4 = this.getCoords_(dx + dw, dy + dh);\r
559 \r
560       max.x = m.max(max.x, c2.x, c3.x, c4.x);\r
561       max.y = m.max(max.y, c2.y, c3.y, c4.y);\r
562 \r
563       vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),\r
564                   'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',\r
565                   filter.join(''), ", sizingmethod='clip');")\r
566     } else {\r
567       vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');\r
568     }\r
569 \r
570     vmlStr.push(' ">' ,\r
571                 '<g_vml_:image src="', image.src, '"',\r
572                 ' style="width:', Z * dw, 'px;',\r
573                 ' height:', Z * dh, 'px;"',\r
574                 ' cropleft="', sx / w, '"',\r
575                 ' croptop="', sy / h, '"',\r
576                 ' cropright="', (w - sx - sw) / w, '"',\r
577                 ' cropbottom="', (h - sy - sh) / h, '"',\r
578                 ' />',\r
579                 '</g_vml_:group>');\r
580 \r
581     this.element_.insertAdjacentHTML('BeforeEnd',\r
582                                     vmlStr.join(''));\r
583   };\r
584 \r
585   contextPrototype.stroke = function(aFill) {\r
586     var lineStr = [];\r
587     var lineOpen = false;\r
588     var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);\r
589     var color = a.color;\r
590     var opacity = a.alpha * this.globalAlpha;\r
591 \r
592     var W = 10;\r
593     var H = 10;\r
594 \r
595     lineStr.push('<g_vml_:shape',\r
596                  ' filled="', !!aFill, '"',\r
597                  ' style="position:absolute;width:', W, 'px;height:', H, 'px;"',\r
598                  ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',\r
599                  ' stroked="', !aFill, '"',\r
600                  ' path="');\r
601 \r
602     var newSeq = false;\r
603     var min = {x: null, y: null};\r
604     var max = {x: null, y: null};\r
605 \r
606     for (var i = 0; i < this.currentPath_.length; i++) {\r
607       var p = this.currentPath_[i];\r
608       var c;\r
609 \r
610       switch (p.type) {\r
611         case 'moveTo':\r
612           c = p;\r
613           lineStr.push(' m ', mr(p.x), ',', mr(p.y));\r
614           break;\r
615         case 'lineTo':\r
616           lineStr.push(' l ', mr(p.x), ',', mr(p.y));\r
617           break;\r
618         case 'close':\r
619           lineStr.push(' x ');\r
620           p = null;\r
621           break;\r
622         case 'bezierCurveTo':\r
623           lineStr.push(' c ',\r
624                        mr(p.cp1x), ',', mr(p.cp1y), ',',\r
625                        mr(p.cp2x), ',', mr(p.cp2y), ',',\r
626                        mr(p.x), ',', mr(p.y));\r
627           break;\r
628         case 'at':\r
629         case 'wa':\r
630           lineStr.push(' ', p.type, ' ',\r
631                        mr(p.x - this.arcScaleX_ * p.radius), ',',\r
632                        mr(p.y - this.arcScaleY_ * p.radius), ' ',\r
633                        mr(p.x + this.arcScaleX_ * p.radius), ',',\r
634                        mr(p.y + this.arcScaleY_ * p.radius), ' ',\r
635                        mr(p.xStart), ',', mr(p.yStart), ' ',\r
636                        mr(p.xEnd), ',', mr(p.yEnd));\r
637           break;\r
638       }\r
639 \r
640 \r
641       // TODO: Following is broken for curves due to\r
642       //       move to proper paths.\r
643 \r
644       // Figure out dimensions so we can do gradient fills\r
645       // properly\r
646       if (p) {\r
647         if (min.x == null || p.x < min.x) {\r
648           min.x = p.x;\r
649         }\r
650         if (max.x == null || p.x > max.x) {\r
651           max.x = p.x;\r
652         }\r
653         if (min.y == null || p.y < min.y) {\r
654           min.y = p.y;\r
655         }\r
656         if (max.y == null || p.y > max.y) {\r
657           max.y = p.y;\r
658         }\r
659       }\r
660     }\r
661     lineStr.push(' ">');\r
662 \r
663     if (!aFill) {\r
664       var lineWidth = this.lineScale_ * this.lineWidth;\r
665 \r
666       // VML cannot correctly render a line if the width is less than 1px.\r
667       // In that case, we dilute the color to make the line look thinner.\r
668       if (lineWidth < 1) {\r
669         opacity *= lineWidth;\r
670       }\r
671 \r
672       lineStr.push(\r
673         '<g_vml_:stroke',\r
674         ' opacity="', opacity, '"',\r
675         ' joinstyle="', this.lineJoin, '"',\r
676         ' miterlimit="', this.miterLimit, '"',\r
677         ' endcap="', processLineCap(this.lineCap), '"',\r
678         ' weight="', lineWidth, 'px"',\r
679         ' color="', color, '" />'\r
680       );\r
681     } else if (typeof this.fillStyle == 'object') {\r
682       var fillStyle = this.fillStyle;\r
683       var angle = 0;\r
684       var focus = {x: 0, y: 0};\r
685 \r
686       // additional offset\r
687       var shift = 0;\r
688       // scale factor for offset\r
689       var expansion = 1;\r
690 \r
691       if (fillStyle.type_ == 'gradient') {\r
692         var x0 = fillStyle.x0_ / this.arcScaleX_;\r
693         var y0 = fillStyle.y0_ / this.arcScaleY_;\r
694         var x1 = fillStyle.x1_ / this.arcScaleX_;\r
695         var y1 = fillStyle.y1_ / this.arcScaleY_;\r
696         var p0 = this.getCoords_(x0, y0);\r
697         var p1 = this.getCoords_(x1, y1);\r
698         var dx = p1.x - p0.x;\r
699         var dy = p1.y - p0.y;\r
700         angle = Math.atan2(dx, dy) * 180 / Math.PI;\r
701 \r
702         // The angle should be a non-negative number.\r
703         if (angle < 0) {\r
704           angle += 360;\r
705         }\r
706 \r
707         // Very small angles produce an unexpected result because they are\r
708         // converted to a scientific notation string.\r
709         if (angle < 1e-6) {\r
710           angle = 0;\r
711         }\r
712       } else {\r
713         var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_);\r
714         var width  = max.x - min.x;\r
715         var height = max.y - min.y;\r
716         focus = {\r
717           x: (p0.x - min.x) / width,\r
718           y: (p0.y - min.y) / height\r
719         };\r
720 \r
721         width  /= this.arcScaleX_ * Z;\r
722         height /= this.arcScaleY_ * Z;\r
723         var dimension = m.max(width, height);\r
724         shift = 2 * fillStyle.r0_ / dimension;\r
725         expansion = 2 * fillStyle.r1_ / dimension - shift;\r
726       }\r
727 \r
728       // We need to sort the color stops in ascending order by offset,\r
729       // otherwise IE won't interpret it correctly.\r
730       var stops = fillStyle.colors_;\r
731       stops.sort(function(cs1, cs2) {\r
732         return cs1.offset - cs2.offset;\r
733       });\r
734 \r
735       var length = stops.length;\r
736       var color1 = stops[0].color;\r
737       var color2 = stops[length - 1].color;\r
738       var opacity1 = stops[0].alpha * this.globalAlpha;\r
739       var opacity2 = stops[length - 1].alpha * this.globalAlpha;\r
740 \r
741       var colors = [];\r
742       for (var i = 0; i < length; i++) {\r
743         var stop = stops[i];\r
744         colors.push(stop.offset * expansion + shift + ' ' + stop.color);\r
745       }\r
746 \r
747       // When colors attribute is used, the meanings of opacity and o:opacity2\r
748       // are reversed.\r
749       lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',\r
750                    ' method="none" focus="100%"',\r
751                    ' color="', color1, '"',\r
752                    ' color2="', color2, '"',\r
753                    ' colors="', colors.join(','), '"',\r
754                    ' opacity="', opacity2, '"',\r
755                    ' g_o_:opacity2="', opacity1, '"',\r
756                    ' angle="', angle, '"',\r
757                    ' focusposition="', focus.x, ',', focus.y, '" />');\r
758     } else {\r
759       lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,\r
760                    '" />');\r
761     }\r
762 \r
763     lineStr.push('</g_vml_:shape>');\r
764 \r
765     this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));\r
766   };\r
767 \r
768   contextPrototype.fill = function() {\r
769     this.stroke(true);\r
770   }\r
771 \r
772   contextPrototype.closePath = function() {\r
773     this.currentPath_.push({type: 'close'});\r
774   };\r
775 \r
776   /**\r
777    * @private\r
778    */\r
779   contextPrototype.getCoords_ = function(aX, aY) {\r
780     var m = this.m_;\r
781     return {\r
782       x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,\r
783       y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2\r
784     }\r
785   };\r
786 \r
787   contextPrototype.save = function() {\r
788     var o = {};\r
789     copyState(this, o);\r
790     this.aStack_.push(o);\r
791     this.mStack_.push(this.m_);\r
792     this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);\r
793   };\r
794 \r
795   contextPrototype.restore = function() {\r
796     copyState(this.aStack_.pop(), this);\r
797     this.m_ = this.mStack_.pop();\r
798   };\r
799 \r
800   function matrixIsFinite(m) {\r
801     for (var j = 0; j < 3; j++) {\r
802       for (var k = 0; k < 2; k++) {\r
803         if (!isFinite(m[j][k]) || isNaN(m[j][k])) {\r
804           return false;\r
805         }\r
806       }\r
807     }\r
808     return true;\r
809   }\r
810 \r
811   function setM(ctx, m, updateLineScale) {\r
812     if (!matrixIsFinite(m)) {\r
813       return;\r
814     }\r
815     ctx.m_ = m;\r
816 \r
817     if (updateLineScale) {\r
818       // Get the line scale.\r
819       // Determinant of this.m_ means how much the area is enlarged by the\r
820       // transformation. So its square root can be used as a scale factor\r
821       // for width.\r
822       var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];\r
823       ctx.lineScale_ = sqrt(abs(det));\r
824     }\r
825   }\r
826 \r
827   contextPrototype.translate = function(aX, aY) {\r
828     var m1 = [\r
829       [1,  0,  0],\r
830       [0,  1,  0],\r
831       [aX, aY, 1]\r
832     ];\r
833 \r
834     setM(this, matrixMultiply(m1, this.m_), false);\r
835   };\r
836 \r
837   contextPrototype.rotate = function(aRot) {\r
838     var c = mc(aRot);\r
839     var s = ms(aRot);\r
840 \r
841     var m1 = [\r
842       [c,  s, 0],\r
843       [-s, c, 0],\r
844       [0,  0, 1]\r
845     ];\r
846 \r
847     setM(this, matrixMultiply(m1, this.m_), false);\r
848   };\r
849 \r
850   contextPrototype.scale = function(aX, aY) {\r
851     this.arcScaleX_ *= aX;\r
852     this.arcScaleY_ *= aY;\r
853     var m1 = [\r
854       [aX, 0,  0],\r
855       [0,  aY, 0],\r
856       [0,  0,  1]\r
857     ];\r
858 \r
859     setM(this, matrixMultiply(m1, this.m_), true);\r
860   };\r
861 \r
862   contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {\r
863     var m1 = [\r
864       [m11, m12, 0],\r
865       [m21, m22, 0],\r
866       [dx,  dy,  1]\r
867     ];\r
868 \r
869     setM(this, matrixMultiply(m1, this.m_), true);\r
870   };\r
871 \r
872   contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {\r
873     var m = [\r
874       [m11, m12, 0],\r
875       [m21, m22, 0],\r
876       [dx,  dy,  1]\r
877     ];\r
878 \r
879     setM(this, m, true);\r
880   };\r
881 \r
882   /******** STUBS ********/\r
883   contextPrototype.clip = function() {\r
884     // TODO: Implement\r
885   };\r
886 \r
887   contextPrototype.arcTo = function() {\r
888     // TODO: Implement\r
889   };\r
890 \r
891   contextPrototype.createPattern = function() {\r
892     return new CanvasPattern_;\r
893   };\r
894 \r
895   // Gradient / Pattern Stubs\r
896   function CanvasGradient_(aType) {\r
897     this.type_ = aType;\r
898     this.x0_ = 0;\r
899     this.y0_ = 0;\r
900     this.r0_ = 0;\r
901     this.x1_ = 0;\r
902     this.y1_ = 0;\r
903     this.r1_ = 0;\r
904     this.colors_ = [];\r
905   }\r
906 \r
907   CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {\r
908     aColor = processStyle(aColor);\r
909     this.colors_.push({offset: aOffset,\r
910                        color: aColor.color,\r
911                        alpha: aColor.alpha});\r
912   };\r
913 \r
914   function CanvasPattern_() {}\r
915 \r
916   // set up externs\r
917   G_vmlCanvasManager = G_vmlCanvasManager_;\r
918   CanvasRenderingContext2D = CanvasRenderingContext2D_;\r
919   CanvasGradient = CanvasGradient_;\r
920   CanvasPattern = CanvasPattern_;\r
921 \r
922 })();\r
923 \r
924 } // if\r
925 \r
926 if (dhtmlx && dhtmlx._modules)\r
927         dhtmlx._modules["thirdparty/excanvas/excanvas.js"] = true;