outfielding-jqplot-rails 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +8 -4
  4. data/changes-jqplot.txt +48 -37
  5. data/copyright-jqplot.txt +17 -17
  6. data/lib/outfielding-jqplot-rails/version.rb +1 -1
  7. data/vendor/assets/javascripts/excanvas.js +1438 -1438
  8. data/vendor/assets/javascripts/jqplot-plugins/jqplot.BezierCurveRenderer.js +313 -313
  9. data/vendor/assets/javascripts/jqplot-plugins/jqplot.barRenderer.js +801 -801
  10. data/vendor/assets/javascripts/jqplot-plugins/jqplot.blockRenderer.js +234 -234
  11. data/vendor/assets/javascripts/jqplot-plugins/jqplot.bubbleRenderer.js +758 -758
  12. data/vendor/assets/javascripts/jqplot-plugins/jqplot.canvasAxisLabelRenderer.js +202 -202
  13. data/vendor/assets/javascripts/jqplot-plugins/jqplot.canvasAxisTickRenderer.js +252 -252
  14. data/vendor/assets/javascripts/jqplot-plugins/jqplot.canvasOverlay.js +1020 -1020
  15. data/vendor/assets/javascripts/jqplot-plugins/jqplot.canvasTextRenderer.js +448 -448
  16. data/vendor/assets/javascripts/jqplot-plugins/jqplot.categoryAxisRenderer.js +679 -679
  17. data/vendor/assets/javascripts/jqplot-plugins/jqplot.ciParser.js +115 -115
  18. data/vendor/assets/javascripts/jqplot-plugins/jqplot.cursor.js +1108 -1108
  19. data/vendor/assets/javascripts/jqplot-plugins/jqplot.dateAxisRenderer.js +741 -741
  20. data/vendor/assets/javascripts/jqplot-plugins/jqplot.donutRenderer.js +816 -805
  21. data/vendor/assets/javascripts/jqplot-plugins/jqplot.dragable.js +224 -224
  22. data/vendor/assets/javascripts/jqplot-plugins/jqplot.enhancedLegendRenderer.js +305 -305
  23. data/vendor/assets/javascripts/jqplot-plugins/jqplot.enhancedPieLegendRenderer.js +261 -0
  24. data/vendor/assets/javascripts/jqplot-plugins/jqplot.funnelRenderer.js +942 -942
  25. data/vendor/assets/javascripts/jqplot-plugins/jqplot.highlighter.js +464 -464
  26. data/vendor/assets/javascripts/jqplot-plugins/jqplot.json2.js +475 -475
  27. data/vendor/assets/javascripts/jqplot-plugins/jqplot.logAxisRenderer.js +533 -533
  28. data/vendor/assets/javascripts/jqplot-plugins/jqplot.mekkoAxisRenderer.js +611 -611
  29. data/vendor/assets/javascripts/jqplot-plugins/jqplot.mekkoRenderer.js +437 -437
  30. data/vendor/assets/javascripts/jqplot-plugins/jqplot.meterGaugeRenderer.js +1029 -1029
  31. data/vendor/assets/javascripts/jqplot-plugins/jqplot.mobile.js +2 -2
  32. data/vendor/assets/javascripts/jqplot-plugins/jqplot.ohlcRenderer.js +373 -373
  33. data/vendor/assets/javascripts/jqplot-plugins/jqplot.pieRenderer.js +945 -903
  34. data/vendor/assets/javascripts/jqplot-plugins/jqplot.pointLabels.js +379 -377
  35. data/vendor/assets/javascripts/jqplot-plugins/jqplot.pyramidAxisRenderer.js +728 -728
  36. data/vendor/assets/javascripts/jqplot-plugins/jqplot.pyramidGridRenderer.js +428 -428
  37. data/vendor/assets/javascripts/jqplot-plugins/jqplot.pyramidRenderer.js +513 -513
  38. data/vendor/assets/javascripts/jqplot-plugins/jqplot.trendline.js +222 -222
  39. data/vendor/assets/javascripts/jquery.jqplot.js +11477 -11411
  40. data/vendor/assets/stylesheets/jquery.jqplot.css +259 -259
  41. metadata +9 -10
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d8c0d787e7240d5724f1991fb886e70192f11fd
4
+ data.tar.gz: cfd7a5fe84574f8e41ab3950edd6a1746902c0a3
5
+ SHA512:
6
+ metadata.gz: 3d6a4c58b923d3a6e6826364f6f9cd63817768bb4f73f770de0c60850d34d50e6437e0d70d6dbae30faf71b4d61a97939a5be618252eaa639f35829931754606
7
+ data.tar.gz: 58b4437bc96259cd964c3caee0c011616e8d8573c7173c1b817c6960be142306f810941d83fbd47b1cd8041060a9210364190d2c1083b0285e69a74966072ce8
data/.gitignore CHANGED
@@ -16,4 +16,6 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  .rvmrc
19
+ .ruby-gemset
20
+ .ruby-version
19
21
  encoding_check.rb
data/CHANGELOG.md CHANGED
@@ -1,9 +1,13 @@
1
+ 2017-02-26 Jason Young <jay@outfielding.net>
2
+
3
+ * Includes jqplot 1.0.9 (d96a669)
4
+
1
5
  2013-03-30 Jason Young <jay@outfielding.net>
2
-
6
+
3
7
  * Includes jqplot 1.0.8 (r1250)
4
8
 
5
9
  2013-02-27 Jason Young <jay@outfielding.net>
6
-
10
+
7
11
  * Includes jqplot 1.0.7 (r1224)
8
12
  * adds in the changes.txt (and copyright.txt and updates the README.txt) from jqplot 1.0.7
9
13
 
@@ -14,5 +18,5 @@
14
18
  * drops the release revision from the versioning (at least for now)
15
19
 
16
20
  2012-11-04 Jason Young <jay@outfielding.net>
17
-
18
- * Initial release (includes jqplot 1.0.4r1121)
21
+
22
+ * Initial release (includes jqplot 1.0.4r1121)
data/changes-jqplot.txt CHANGED
@@ -1,5 +1,16 @@
1
1
  Title: Change Log
2
2
 
3
+ 1.0.9:
4
+ * Convert toolchain to grunt
5
+ * Add "step" chart style
6
+ * Refactor code according to JSLint rules (johanbove)
7
+ * Add enhancedPieLegendRenderer
8
+ * Pull request #17: Fix infinite loop
9
+ * Pull request #22: Update jqplot.pointLabels.js
10
+ * Pull request #23 Update jqplot.pieRenderer.js
11
+ * Pull request #25: barRenderer resizing fix
12
+ * Pull request #26: Error resizing horizontal bar charts
13
+
3
14
  1.0.8:
4
15
  * Issue #375: sortMergedLabels does not sort string labels
5
16
  * Issue #279: Groups > 3 Causes Alignment Issues
@@ -12,8 +23,8 @@ Title: Change Log
12
23
  * Issue #756: jqplot.min files contain non-UTF-8 characters
13
24
  * Issue #223: fillToZero does not color negative values when crossover point is 0
14
25
  * Pull Request #23: Adding rectangles to Canvas Overlay plugin
15
- * Pull Request #28: Cross-over points of 0 will actually change colors
16
- * Pull Request #35: Don't highlight hidden bars or show tooltips for them
26
+ * Pull Request #28: Cross-over points of 0 will actually change colors
27
+ * Pull Request #35: Don't highlight hidden bars or show tooltips for them
17
28
  * Pull Request #41: Add dutch(nl) and svenska(sv) translations for dates
18
29
  * Add tooltip support for Pie Charts
19
30
  * Update to latest YUI compressor
@@ -65,7 +76,7 @@ Title: Change Log
65
76
 
66
77
  1.0.0b2:
67
78
  * Major improvements in memory usage:
68
- ** Merged in changes from Timo Besenruether to reuse canvas elements and improve
79
+ ** Merged in changes from Timo Besenruether to reuse canvas elements and improve
69
80
  memory performance.
70
81
  ** Fixed all identifiable DOM leaks.
71
82
  ** Mergged in changes from cguillot for memory improvements in IE < 9.
@@ -73,16 +84,16 @@ Title: Change Log
73
84
  * Fixed bug where initially hidden plots would not display.
74
85
  * Fixed bug with point labels and null data points.
75
86
  * Updated to jQuery 1.6.1.
76
- * Improved pie slice margin calculation and fixed slice margin and pie positioning
87
+ * Improved pie slice margin calculation and fixed slice margin and pie positioning
77
88
  with small slices.
78
89
  * Improved bar renderer so bars always start at 0 if:
79
90
  ** The axis is a linear axis (not log/date).
80
91
  ** There are no other line types besides bars attached to the axis.
81
92
  ** The data on the axis is all >= 0.
82
93
  ** The user has not specified a pad, padMin or forceTickAt0 = true option.
83
- * Modified tick prefix behavious so prefix no added to all ticks, even if format
94
+ * Modified tick prefix behavious so prefix no added to all ticks, even if format
84
95
  string is specified.
85
- * Fix to ensure original tick formats are applied when zooming and resetting
96
+ * Fix to ensure original tick formats are applied when zooming and resetting
86
97
  zoom.
87
98
  * Updated auto tick format string so format adjusted when zooming.
88
99
  * Modified auto tick computation to put less ticks on small plots and more
@@ -90,12 +101,12 @@ Title: Change Log
90
101
  * Update bubble render to support gradients in IE 9.
91
102
 
92
103
  1.0.0b1:
93
- * Much improved tick generation algorithm to get precise rounded
104
+ * Much improved tick generation algorithm to get precise rounded
94
105
  tick values (Thanks Scott Prahl!).
95
106
  * Auto compute tick format string if none is provided.
96
107
  * Much better "slicing" of pie charts when using "sliceMargin" option to set
97
108
  a gap between the slices.
98
- * Expanded canvasOverlay plugin to create arbitrary dashed and solid
109
+ * Expanded canvasOverlay plugin to create arbitrary dashed and solid
99
110
  horizontal and vertical lines on top of plot.
100
111
  * Added defaultColors and defaultNegativeColors options to $.jqplot.config.
101
112
  * Fixed issue #318, highlighter & bar renderer incompatability.
@@ -203,23 +214,23 @@ Title: Change Log
203
214
  * Implemented vertical waterfall charts. Can create waterfall plot as
204
215
  option to bar chart. See examples folder of distribution.
205
216
  * Enhanced plot labels for waterfall style.
206
- * Enhanced bar plots so you can now color each bar of a series
217
+ * Enhanced bar plots so you can now color each bar of a series
207
218
  independently with the "varyBarColor" option.
208
219
  * Re-factored series drawing so that each series and series shadow drawn
209
220
  on its own canvas. Allows series to be redrawn independently of each other.
210
221
  * Added additional default series colors.
211
- * Added useNegativeColors option to turn off negative color array and use
222
+ * Added useNegativeColors option to turn off negative color array and use
212
223
  only seriesColors array to define all bar/filled line colors.
213
224
  * Fix css for cursor legend.
214
225
  * Modified shape renderer so rectangles can be stroked and filled.
215
- * Re-factored date methods out of dateAxisRenderer so that date formatter
226
+ * Re-factored date methods out of dateAxisRenderer so that date formatter
216
227
  and methods can be accesses outside of dateAxisRenderer plugin.
217
228
  * Fixed #132, now trigger series change event on plot target instead of drag canvas.
218
- * Fixes issue #116 where some source files had mix of tabs and spaces
229
+ * Fixes issue #116 where some source files had mix of tabs and spaces
219
230
  for indentation. Should have been all spaces.
220
231
  * Fixed issue #126, some links broken in docs section of web site.
221
232
  * Fixed issue #90, trendline plugin incompatibility with pie renderer.
222
- * Updated samples in examples folder of distribution to include navigation
233
+ * Updated samples in examples folder of distribution to include navigation
223
234
  links if web server is set up to process .html files with php.
224
235
 
225
236
 
@@ -253,7 +264,7 @@ Title: Change Log
253
264
 
254
265
  0.9.5:
255
266
 
256
- * Implemented "zoomProxy". zoomProxy allows zooming one plot from another
267
+ * Implemented "zoomProxy". zoomProxy allows zooming one plot from another
257
268
  such as an overview plot.
258
269
  * Zooming can now be constrained to just x or y axis.
259
270
  * Enhanced cursor plugin with vertical "dataTracking" line. This is a line
@@ -284,7 +295,7 @@ Title: Change Log
284
295
 
285
296
  0.9.4:
286
297
 
287
- * Implemented axis labels. Labels can be rendered in div tags or as canvas
298
+ * Implemented axis labels. Labels can be rendered in div tags or as canvas
288
299
  elements supporting rotated text.
289
300
  * Improved rotated axis label positioning so labels will start or end at a
290
301
  tick position.
@@ -295,7 +306,7 @@ Title: Change Log
295
306
  * Added option to legend to encode special HTML characters.
296
307
  * Fixed undesirable behavior where point labels for points off the plot
297
308
  were being rendered.
298
- * Added edgeTolerance option to point label renderer to control rendering of
309
+ * Added edgeTolerance option to point label renderer to control rendering of
299
310
  labels near plot edges.
300
311
 
301
312
 
@@ -303,9 +314,9 @@ Title: Change Log
303
314
 
304
315
  * Preliminary support for axis labels. Currently rendered into DIV tags,
305
316
  so no rotated label support. This feature is currently experimental.
306
- * Fixed bug #52, needed space in tick div tag between style and class declarations
317
+ * Fixed bug #52, needed space in tick div tag between style and class declarations
307
318
  or plot failed in certain application doctypes.
308
- * Fixed issue #54, miter style line join for chart lines causing spikes at steep
319
+ * Fixed issue #54, miter style line join for chart lines causing spikes at steep
309
320
  changes in slope. Changed miter style to round.
310
321
  * Added examples for new autoscaling algorithm.
311
322
  * Fixed bug #57, category axis labels disappear on redraw()
@@ -318,7 +329,7 @@ Title: Change Log
318
329
  0.9.2:
319
330
 
320
331
  * Fixed bug #45 where a plot could crash if series had different numbers of points.
321
- * Fixed issue #50, added option to turn off sorting of series data.
332
+ * Fixed issue #50, added option to turn off sorting of series data.
322
333
  * Fixed issue #31, implemented a better axis autoscaling algorithm and added an autoscale option.
323
334
 
324
335
  0.9.1:
@@ -345,7 +356,7 @@ Title: Change Log
345
356
  * Added zooming ability with double click or single click options to reset zoom.
346
357
  * Modified default tick spacing algorithm for date axes to give more space to ticks.
347
358
  * Fixed bug #2 where tickInterval wasn't working properly.
348
- * Added neighborThreshold option to control how close mouse must be to
359
+ * Added neighborThreshold option to control how close mouse must be to
349
360
  point to trigger neighbor detection.
350
361
  * Added double click event handler on plot.
351
362
 
@@ -354,9 +365,9 @@ Title: Change Log
354
365
  * Support for up to 9 y axes.
355
366
  * Added option to control padding at max/min bounds of axes separately.
356
367
  * Closed issue #21, added options to control grid line color and width.
357
- * Closed issue #20, added options to filled line charts to stoke above
368
+ * Closed issue #20, added options to filled line charts to stoke above
358
369
  fill and customize fill color and transparency.
359
- * Improved structure of on line documentation to make usage and options
370
+ * Improved structure of on line documentation to make usage and options
360
371
  docs default.
361
372
  * Added much documentation on options and css styling.
362
373
 
@@ -364,25 +375,25 @@ Title: Change Log
364
375
 
365
376
  * Bug fix release
366
377
  * Fixed bug #6, missing semi-colons messing up some javascript compressors.
367
- * Fixed bug #13 where 2D ticks array of [values, labels] would fail to
378
+ * Fixed bug #13 where 2D ticks array of [values, labels] would fail to
368
379
  renderer with DateAxisRenderer.
369
- * Fixes bug #16 where pie renderer overwriting options for all plot types
380
+ * Fixes bug #16 where pie renderer overwriting options for all plot types
370
381
  and crashing non pie plots.
371
- * Fixes bug #17 constrainTo dragable option mispelled as "contstrainTo".
382
+ * Fixes bug #17 constrainTo dragable option mispelled as "contstrainTo".
372
383
  Fixed dragable color issue when used with trend lines.
373
384
 
374
385
  0.7.0:
375
386
 
376
387
  * Pie chart support
377
- * Enabled tooltipLocation option in highlighter.
378
- * Highlighter Tooltip will account for mark size and highlight size when
379
- positioning itself.
388
+ * Enabled tooltipLocation option in highlighter.
389
+ * Highlighter Tooltip will account for mark size and highlight size when
390
+ positioning itself.
380
391
  * Added ability to show just x, y or both axes in highlighter tooltip.
381
392
  * Added customization of separator between axes values in highlighter tooltip.
382
- * Modified how shadows are drawn for lines, bars and markers. Now drawn first,
393
+ * Modified how shadows are drawn for lines, bars and markers. Now drawn first,
383
394
  so they are always behind the object.
384
395
  * Adjustments to shadow parameters on lines to account for new shadow positioning.
385
- * Added a ColorGenerator class to robustly return next available color
396
+ * Added a ColorGenerator class to robustly return next available color
386
397
  for a plot with wrap around to first color at end.
387
398
  * Udates to docs about css file.
388
399
  * Fixed bug with String x values in series and IE error on sorting (Category Axis).
@@ -391,35 +402,35 @@ Title: Change Log
391
402
  0.6.6b:
392
403
 
393
404
  * Added excanvas.js and excanvas.min.js to compressed distributions.
394
- * Added example/test html pages I had locally into repository and to
405
+ * Added example/test html pages I had locally into repository and to
395
406
  compressed distributions.
396
407
 
397
408
  0.6.6a:
398
409
 
399
410
  * Removed absolute positioning from dom element and put back into css file.
400
- * Duplicate of 0.6.6 with a suffix to unambiguously differentiate between
411
+ * Duplicate of 0.6.6 with a suffix to unambiguously differentiate between
401
412
  previously posted 0.6.6 release.
402
413
 
403
414
  0.6.6:
404
415
 
405
416
  * Fixed bug #5, trend line plugin failing when no trend line options specified.
406
417
  * Added absolute position css spec to axis tick dom element.
407
- * Enhancement to category axes, more intuitive handling of series with
418
+ * Enhancement to category axes, more intuitive handling of series with
408
419
  missing data values.
409
420
 
410
421
  0.6.5:
411
422
 
412
- * Fixed bug #4, series of unequal data length not rendering correctly.
423
+ * Fixed bug #4, series of unequal data length not rendering correctly.
413
424
  This is a bugfix release only.
414
425
 
415
426
  0.6.4:
416
427
 
417
- * Fixed bug (issue #1 in tracker) where flat line data series (all x and/or y
428
+ * Fixed bug (issue #1 in tracker) where flat line data series (all x and/or y
418
429
  values are euqal) or single value data series would crash.
419
430
 
420
431
  0.6.3:
421
432
 
422
- * Support for stacked line (a.k.a. area) and stacked bar (horizontal and
433
+ * Support for stacked line (a.k.a. area) and stacked bar (horizontal and
423
434
  vertical) charts.
424
435
  * Refactored barRenderer to use default shape and shadow renderers.
425
436
  * Added info (contacts & support information) page to web site.
@@ -446,7 +457,7 @@ Title: Change Log
446
457
 
447
458
  0.6.0:
448
459
 
449
- * Added rotated text support. Uses native canvas text functionality in
460
+ * Added rotated text support. Uses native canvas text functionality in
450
461
  browsers that support it or draws text on canvas with Hershey font
451
462
  * metrics for non-supporting browsers.
452
463
  * Removed lots of lint in js code.
data/copyright-jqplot.txt CHANGED
@@ -4,14 +4,14 @@
4
4
  *
5
5
  * Version: @VERSION
6
6
  *
7
- * Copyright (c) 2009-2013 Chris Leonello
8
- * jqPlot is currently available for use in all personal or commercial projects
9
- * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
10
- * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
11
- * choose the license that best suits your project and use it accordingly.
12
- *
13
- * Although not required, the author would appreciate an email letting him
14
- * know of any substantial use of jqPlot. You can reach the author at:
7
+ * Copyright (c) 2009-2015 Chris Leonello
8
+ * jqPlot is currently available for use in all personal or commercial projects
9
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
10
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
11
+ * choose the license that best suits your project and use it accordingly.
12
+ *
13
+ * Although not required, the author would appreciate an email letting him
14
+ * know of any substantial use of jqPlot. You can reach the author at:
15
15
  * chris at jqplot dot com or see http://www.jqplot.com/info.php .
16
16
  *
17
17
  * If you are feeling kind and generous, consider supporting the project by
@@ -28,20 +28,20 @@
28
28
  *
29
29
  * included jsDate library by Chris Leonello:
30
30
  *
31
- * Copyright (c) 2010-2013 Chris Leonello
31
+ * Copyright (c) 2010-2015 Chris Leonello
32
32
  *
33
- * jsDate is currently available for use in all personal or commercial projects
34
- * under both the MIT and GPL version 2.0 licenses. This means that you can
33
+ * jsDate is currently available for use in all personal or commercial projects
34
+ * under both the MIT and GPL version 2.0 licenses. This means that you can
35
35
  * choose the license that best suits your project and use it accordingly.
36
36
  *
37
- * jsDate borrows many concepts and ideas from the Date Instance
37
+ * jsDate borrows many concepts and ideas from the Date Instance
38
38
  * Methods by Ken Snyder along with some parts of Ken's actual code.
39
- *
39
+ *
40
40
  * Ken's origianl Date Instance Methods and copyright notice:
41
- *
41
+ *
42
42
  * Ken Snyder (ken d snyder at gmail dot com)
43
43
  * 2008-09-10
44
- * version 2.0.2 (http://kendsnyder.com/sandbox/date/)
44
+ * version 2.0.2 (http://kendsnyder.com/sandbox/date/)
45
45
  * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
46
46
  *
47
47
  * jqplotToImage function based on Larry Siden's export-jqplot-to-png.js.
@@ -51,6 +51,6 @@
51
51
  * Larry's original code can be found here:
52
52
  *
53
53
  * https://github.com/lsiden/export-jqplot-to-png
54
- *
55
- *
54
+ *
55
+ *
56
56
  */
@@ -1,7 +1,7 @@
1
1
  module Outfielding
2
2
  module Jqplot
3
3
  module Rails
4
- VERSION = "1.0.8"
4
+ VERSION = "1.0.9"
5
5
  end
6
6
  end
7
7
  end
@@ -1,1438 +1,1438 @@
1
- // Memory Leaks patch from http://explorercanvas.googlecode.com/svn/trunk/
2
- // svn : r73
3
- // ------------------------------------------------------------------
4
- // Copyright 2006 Google Inc.
5
- //
6
- // Licensed under the Apache License, Version 2.0 (the "License");
7
- // you may not use this file except in compliance with the License.
8
- // You may obtain a copy of the License at
9
- //
10
- // http://www.apache.org/licenses/LICENSE-2.0
11
- //
12
- // Unless required by applicable law or agreed to in writing, software
13
- // distributed under the License is distributed on an "AS IS" BASIS,
14
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- // See the License for the specific language governing permissions and
16
- // limitations under the License.
17
-
18
-
19
- // Known Issues:
20
- //
21
- // * Patterns only support repeat.
22
- // * Radial gradient are not implemented. The VML version of these look very
23
- // different from the canvas one.
24
- // * Clipping paths are not implemented.
25
- // * Coordsize. The width and height attribute have higher priority than the
26
- // width and height style values which isn't correct.
27
- // * Painting mode isn't implemented.
28
- // * Canvas width/height should is using content-box by default. IE in
29
- // Quirks mode will draw the canvas using border-box. Either change your
30
- // doctype to HTML5
31
- // (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
32
- // or use Box Sizing Behavior from WebFX
33
- // (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
34
- // * Non uniform scaling does not correctly scale strokes.
35
- // * Optimize. There is always room for speed improvements.
36
-
37
- // Only add this code if we do not already have a canvas implementation
38
- if (!document.createElement('canvas').getContext) {
39
-
40
- (function() {
41
-
42
- // alias some functions to make (compiled) code shorter
43
- var m = Math;
44
- var mr = m.round;
45
- var ms = m.sin;
46
- var mc = m.cos;
47
- var abs = m.abs;
48
- var sqrt = m.sqrt;
49
-
50
- // this is used for sub pixel precision
51
- var Z = 10;
52
- var Z2 = Z / 2;
53
-
54
- var IE_VERSION = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];
55
-
56
- /**
57
- * This funtion is assigned to the <canvas> elements as element.getContext().
58
- * @this {HTMLElement}
59
- * @return {CanvasRenderingContext2D_}
60
- */
61
- function getContext() {
62
- return this.context_ ||
63
- (this.context_ = new CanvasRenderingContext2D_(this));
64
- }
65
-
66
- var slice = Array.prototype.slice;
67
-
68
- /**
69
- * Binds a function to an object. The returned function will always use the
70
- * passed in {@code obj} as {@code this}.
71
- *
72
- * Example:
73
- *
74
- * g = bind(f, obj, a, b)
75
- * g(c, d) // will do f.call(obj, a, b, c, d)
76
- *
77
- * @param {Function} f The function to bind the object to
78
- * @param {Object} obj The object that should act as this when the function
79
- * is called
80
- * @param {*} var_args Rest arguments that will be used as the initial
81
- * arguments when the function is called
82
- * @return {Function} A new function that has bound this
83
- */
84
- function bind(f, obj, var_args) {
85
- var a = slice.call(arguments, 2);
86
- return function() {
87
- return f.apply(obj, a.concat(slice.call(arguments)));
88
- };
89
- }
90
-
91
- function encodeHtmlAttribute(s) {
92
- return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
93
- }
94
-
95
- function addNamespace(doc, prefix, urn) {
96
- if (!doc.namespaces[prefix]) {
97
- doc.namespaces.add(prefix, urn, '#default#VML');
98
- }
99
- }
100
-
101
- function addNamespacesAndStylesheet(doc) {
102
- addNamespace(doc, 'g_vml_', 'urn:schemas-microsoft-com:vml');
103
- addNamespace(doc, 'g_o_', 'urn:schemas-microsoft-com:office:office');
104
-
105
- // Setup default CSS. Only add one style sheet per document
106
- if (!doc.styleSheets['ex_canvas_']) {
107
- var ss = doc.createStyleSheet();
108
- ss.owningElement.id = 'ex_canvas_';
109
- ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
110
- // default size is 300x150 in Gecko and Opera
111
- 'text-align:left;width:300px;height:150px}';
112
- }
113
- }
114
-
115
- // Add namespaces and stylesheet at startup.
116
- addNamespacesAndStylesheet(document);
117
-
118
- var G_vmlCanvasManager_ = {
119
- init: function(opt_doc) {
120
- var doc = opt_doc || document;
121
- // Create a dummy element so that IE will allow canvas elements to be
122
- // recognized.
123
- doc.createElement('canvas');
124
- doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
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
- // Memory Leaks patch : see http://code.google.com/p/explorercanvas/issues/detail?id=82
180
- uninitElement: function(el){
181
- if (el.getContext) {
182
- var ctx = el.getContext();
183
- delete ctx.element_;
184
- delete ctx.canvas;
185
- el.innerHTML = "";
186
- //el.outerHTML = "";
187
- el.context_ = null;
188
- el.getContext = null;
189
- el.detachEvent("onpropertychange", onPropertyChange);
190
- el.detachEvent("onresize", onResize);
191
- }
192
- }
193
- };
194
-
195
- function onPropertyChange(e) {
196
- var el = e.srcElement;
197
-
198
- switch (e.propertyName) {
199
- case 'width':
200
- el.getContext().clearRect();
201
- el.style.width = el.attributes.width.nodeValue + 'px';
202
- // In IE8 this does not trigger onresize.
203
- el.firstChild.style.width = el.clientWidth + 'px';
204
- break;
205
- case 'height':
206
- el.getContext().clearRect();
207
- el.style.height = el.attributes.height.nodeValue + 'px';
208
- el.firstChild.style.height = el.clientHeight + 'px';
209
- break;
210
- }
211
- }
212
-
213
- function onResize(e) {
214
- var el = e.srcElement;
215
- if (el.firstChild) {
216
- el.firstChild.style.width = el.clientWidth + 'px';
217
- el.firstChild.style.height = el.clientHeight + 'px';
218
- }
219
- }
220
-
221
- G_vmlCanvasManager_.init();
222
-
223
- // precompute "00" to "FF"
224
- var decToHex = [];
225
- for (var i = 0; i < 16; i++) {
226
- for (var j = 0; j < 16; j++) {
227
- decToHex[i * 16 + j] = i.toString(16) + j.toString(16);
228
- }
229
- }
230
-
231
- function createMatrixIdentity() {
232
- return [
233
- [1, 0, 0],
234
- [0, 1, 0],
235
- [0, 0, 1]
236
- ];
237
- }
238
-
239
- function matrixMultiply(m1, m2) {
240
- var result = createMatrixIdentity();
241
-
242
- for (var x = 0; x < 3; x++) {
243
- for (var y = 0; y < 3; y++) {
244
- var sum = 0;
245
-
246
- for (var z = 0; z < 3; z++) {
247
- sum += m1[x][z] * m2[z][y];
248
- }
249
-
250
- result[x][y] = sum;
251
- }
252
- }
253
- return result;
254
- }
255
-
256
- function copyState(o1, o2) {
257
- o2.fillStyle = o1.fillStyle;
258
- o2.lineCap = o1.lineCap;
259
- o2.lineJoin = o1.lineJoin;
260
- o2.lineWidth = o1.lineWidth;
261
- o2.miterLimit = o1.miterLimit;
262
- o2.shadowBlur = o1.shadowBlur;
263
- o2.shadowColor = o1.shadowColor;
264
- o2.shadowOffsetX = o1.shadowOffsetX;
265
- o2.shadowOffsetY = o1.shadowOffsetY;
266
- o2.strokeStyle = o1.strokeStyle;
267
- o2.globalAlpha = o1.globalAlpha;
268
- o2.font = o1.font;
269
- o2.textAlign = o1.textAlign;
270
- o2.textBaseline = o1.textBaseline;
271
- o2.arcScaleX_ = o1.arcScaleX_;
272
- o2.arcScaleY_ = o1.arcScaleY_;
273
- o2.lineScale_ = o1.lineScale_;
274
- }
275
-
276
- var colorData = {
277
- aliceblue: '#F0F8FF',
278
- antiquewhite: '#FAEBD7',
279
- aquamarine: '#7FFFD4',
280
- azure: '#F0FFFF',
281
- beige: '#F5F5DC',
282
- bisque: '#FFE4C4',
283
- black: '#000000',
284
- blanchedalmond: '#FFEBCD',
285
- blueviolet: '#8A2BE2',
286
- brown: '#A52A2A',
287
- burlywood: '#DEB887',
288
- cadetblue: '#5F9EA0',
289
- chartreuse: '#7FFF00',
290
- chocolate: '#D2691E',
291
- coral: '#FF7F50',
292
- cornflowerblue: '#6495ED',
293
- cornsilk: '#FFF8DC',
294
- crimson: '#DC143C',
295
- cyan: '#00FFFF',
296
- darkblue: '#00008B',
297
- darkcyan: '#008B8B',
298
- darkgoldenrod: '#B8860B',
299
- darkgray: '#A9A9A9',
300
- darkgreen: '#006400',
301
- darkgrey: '#A9A9A9',
302
- darkkhaki: '#BDB76B',
303
- darkmagenta: '#8B008B',
304
- darkolivegreen: '#556B2F',
305
- darkorange: '#FF8C00',
306
- darkorchid: '#9932CC',
307
- darkred: '#8B0000',
308
- darksalmon: '#E9967A',
309
- darkseagreen: '#8FBC8F',
310
- darkslateblue: '#483D8B',
311
- darkslategray: '#2F4F4F',
312
- darkslategrey: '#2F4F4F',
313
- darkturquoise: '#00CED1',
314
- darkviolet: '#9400D3',
315
- deeppink: '#FF1493',
316
- deepskyblue: '#00BFFF',
317
- dimgray: '#696969',
318
- dimgrey: '#696969',
319
- dodgerblue: '#1E90FF',
320
- firebrick: '#B22222',
321
- floralwhite: '#FFFAF0',
322
- forestgreen: '#228B22',
323
- gainsboro: '#DCDCDC',
324
- ghostwhite: '#F8F8FF',
325
- gold: '#FFD700',
326
- goldenrod: '#DAA520',
327
- grey: '#808080',
328
- greenyellow: '#ADFF2F',
329
- honeydew: '#F0FFF0',
330
- hotpink: '#FF69B4',
331
- indianred: '#CD5C5C',
332
- indigo: '#4B0082',
333
- ivory: '#FFFFF0',
334
- khaki: '#F0E68C',
335
- lavender: '#E6E6FA',
336
- lavenderblush: '#FFF0F5',
337
- lawngreen: '#7CFC00',
338
- lemonchiffon: '#FFFACD',
339
- lightblue: '#ADD8E6',
340
- lightcoral: '#F08080',
341
- lightcyan: '#E0FFFF',
342
- lightgoldenrodyellow: '#FAFAD2',
343
- lightgreen: '#90EE90',
344
- lightgrey: '#D3D3D3',
345
- lightpink: '#FFB6C1',
346
- lightsalmon: '#FFA07A',
347
- lightseagreen: '#20B2AA',
348
- lightskyblue: '#87CEFA',
349
- lightslategray: '#778899',
350
- lightslategrey: '#778899',
351
- lightsteelblue: '#B0C4DE',
352
- lightyellow: '#FFFFE0',
353
- limegreen: '#32CD32',
354
- linen: '#FAF0E6',
355
- magenta: '#FF00FF',
356
- mediumaquamarine: '#66CDAA',
357
- mediumblue: '#0000CD',
358
- mediumorchid: '#BA55D3',
359
- mediumpurple: '#9370DB',
360
- mediumseagreen: '#3CB371',
361
- mediumslateblue: '#7B68EE',
362
- mediumspringgreen: '#00FA9A',
363
- mediumturquoise: '#48D1CC',
364
- mediumvioletred: '#C71585',
365
- midnightblue: '#191970',
366
- mintcream: '#F5FFFA',
367
- mistyrose: '#FFE4E1',
368
- moccasin: '#FFE4B5',
369
- navajowhite: '#FFDEAD',
370
- oldlace: '#FDF5E6',
371
- olivedrab: '#6B8E23',
372
- orange: '#FFA500',
373
- orangered: '#FF4500',
374
- orchid: '#DA70D6',
375
- palegoldenrod: '#EEE8AA',
376
- palegreen: '#98FB98',
377
- paleturquoise: '#AFEEEE',
378
- palevioletred: '#DB7093',
379
- papayawhip: '#FFEFD5',
380
- peachpuff: '#FFDAB9',
381
- peru: '#CD853F',
382
- pink: '#FFC0CB',
383
- plum: '#DDA0DD',
384
- powderblue: '#B0E0E6',
385
- rosybrown: '#BC8F8F',
386
- royalblue: '#4169E1',
387
- saddlebrown: '#8B4513',
388
- salmon: '#FA8072',
389
- sandybrown: '#F4A460',
390
- seagreen: '#2E8B57',
391
- seashell: '#FFF5EE',
392
- sienna: '#A0522D',
393
- skyblue: '#87CEEB',
394
- slateblue: '#6A5ACD',
395
- slategray: '#708090',
396
- slategrey: '#708090',
397
- snow: '#FFFAFA',
398
- springgreen: '#00FF7F',
399
- steelblue: '#4682B4',
400
- tan: '#D2B48C',
401
- thistle: '#D8BFD8',
402
- tomato: '#FF6347',
403
- turquoise: '#40E0D0',
404
- violet: '#EE82EE',
405
- wheat: '#F5DEB3',
406
- whitesmoke: '#F5F5F5',
407
- yellowgreen: '#9ACD32'
408
- };
409
-
410
-
411
- function getRgbHslContent(styleString) {
412
- var start = styleString.indexOf('(', 3);
413
- var end = styleString.indexOf(')', start + 1);
414
- var parts = styleString.substring(start + 1, end).split(',');
415
- // add alpha if needed
416
- if (parts.length != 4 || styleString.charAt(3) != 'a') {
417
- parts[3] = 1;
418
- }
419
- return parts;
420
- }
421
-
422
- function percent(s) {
423
- return parseFloat(s) / 100;
424
- }
425
-
426
- function clamp(v, min, max) {
427
- return Math.min(max, Math.max(min, v));
428
- }
429
-
430
- function hslToRgb(parts){
431
- var r, g, b, h, s, l;
432
- h = parseFloat(parts[0]) / 360 % 360;
433
- if (h < 0)
434
- h++;
435
- s = clamp(percent(parts[1]), 0, 1);
436
- l = clamp(percent(parts[2]), 0, 1);
437
- if (s == 0) {
438
- r = g = b = l; // achromatic
439
- } else {
440
- var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
441
- var p = 2 * l - q;
442
- r = hueToRgb(p, q, h + 1 / 3);
443
- g = hueToRgb(p, q, h);
444
- b = hueToRgb(p, q, h - 1 / 3);
445
- }
446
-
447
- return '#' + decToHex[Math.floor(r * 255)] +
448
- decToHex[Math.floor(g * 255)] +
449
- decToHex[Math.floor(b * 255)];
450
- }
451
-
452
- function hueToRgb(m1, m2, h) {
453
- if (h < 0)
454
- h++;
455
- if (h > 1)
456
- h--;
457
-
458
- if (6 * h < 1)
459
- return m1 + (m2 - m1) * 6 * h;
460
- else if (2 * h < 1)
461
- return m2;
462
- else if (3 * h < 2)
463
- return m1 + (m2 - m1) * (2 / 3 - h) * 6;
464
- else
465
- return m1;
466
- }
467
-
468
- var processStyleCache = {};
469
-
470
- function processStyle(styleString) {
471
- if (styleString in processStyleCache) {
472
- return processStyleCache[styleString];
473
- }
474
-
475
- var str, alpha = 1;
476
-
477
- styleString = String(styleString);
478
- if (styleString.charAt(0) == '#') {
479
- str = styleString;
480
- } else if (/^rgb/.test(styleString)) {
481
- var parts = getRgbHslContent(styleString);
482
- var str = '#', n;
483
- for (var i = 0; i < 3; i++) {
484
- if (parts[i].indexOf('%') != -1) {
485
- n = Math.floor(percent(parts[i]) * 255);
486
- } else {
487
- n = +parts[i];
488
- }
489
- str += decToHex[clamp(n, 0, 255)];
490
- }
491
- alpha = +parts[3];
492
- } else if (/^hsl/.test(styleString)) {
493
- var parts = getRgbHslContent(styleString);
494
- str = hslToRgb(parts);
495
- alpha = parts[3];
496
- } else {
497
- str = colorData[styleString] || styleString;
498
- }
499
- return processStyleCache[styleString] = {color: str, alpha: alpha};
500
- }
501
-
502
- var DEFAULT_STYLE = {
503
- style: 'normal',
504
- variant: 'normal',
505
- weight: 'normal',
506
- size: 10,
507
- family: 'sans-serif'
508
- };
509
-
510
- // Internal text style cache
511
- var fontStyleCache = {};
512
-
513
- function processFontStyle(styleString) {
514
- if (fontStyleCache[styleString]) {
515
- return fontStyleCache[styleString];
516
- }
517
-
518
- var el = document.createElement('div');
519
- var style = el.style;
520
- try {
521
- style.font = styleString;
522
- } catch (ex) {
523
- // Ignore failures to set to invalid font.
524
- }
525
-
526
- return fontStyleCache[styleString] = {
527
- style: style.fontStyle || DEFAULT_STYLE.style,
528
- variant: style.fontVariant || DEFAULT_STYLE.variant,
529
- weight: style.fontWeight || DEFAULT_STYLE.weight,
530
- size: style.fontSize || DEFAULT_STYLE.size,
531
- family: style.fontFamily || DEFAULT_STYLE.family
532
- };
533
- }
534
-
535
- function getComputedStyle(style, element) {
536
- var computedStyle = {};
537
-
538
- for (var p in style) {
539
- computedStyle[p] = style[p];
540
- }
541
-
542
- // Compute the size
543
- var canvasFontSize = parseFloat(element.currentStyle.fontSize),
544
- fontSize = parseFloat(style.size);
545
-
546
- if (typeof style.size == 'number') {
547
- computedStyle.size = style.size;
548
- } else if (style.size.indexOf('px') != -1) {
549
- computedStyle.size = fontSize;
550
- } else if (style.size.indexOf('em') != -1) {
551
- computedStyle.size = canvasFontSize * fontSize;
552
- } else if(style.size.indexOf('%') != -1) {
553
- computedStyle.size = (canvasFontSize / 100) * fontSize;
554
- } else if (style.size.indexOf('pt') != -1) {
555
- computedStyle.size = fontSize / .75;
556
- } else {
557
- computedStyle.size = canvasFontSize;
558
- }
559
-
560
- // Different scaling between normal text and VML text. This was found using
561
- // trial and error to get the same size as non VML text.
562
- computedStyle.size *= 0.981;
563
-
564
- // Fix for VML handling of bare font family names. Add a '' around font family names.
565
- computedStyle.family = "'" + computedStyle.family.replace(/(\'|\")/g,'').replace(/\s*,\s*/g, "', '") + "'";
566
-
567
- return computedStyle;
568
- }
569
-
570
- function buildStyle(style) {
571
- return style.style + ' ' + style.variant + ' ' + style.weight + ' ' +
572
- style.size + 'px ' + style.family;
573
- }
574
-
575
- var lineCapMap = {
576
- 'butt': 'flat',
577
- 'round': 'round'
578
- };
579
-
580
- function processLineCap(lineCap) {
581
- return lineCapMap[lineCap] || 'square';
582
- }
583
-
584
- /**
585
- * This class implements CanvasRenderingContext2D interface as described by
586
- * the WHATWG.
587
- * @param {HTMLElement} canvasElement The element that the 2D context should
588
- * be associated with
589
- */
590
- function CanvasRenderingContext2D_(canvasElement) {
591
- this.m_ = createMatrixIdentity();
592
-
593
- this.mStack_ = [];
594
- this.aStack_ = [];
595
- this.currentPath_ = [];
596
-
597
- // Canvas context properties
598
- this.strokeStyle = '#000';
599
- this.fillStyle = '#000';
600
-
601
- this.lineWidth = 1;
602
- this.lineJoin = 'miter';
603
- this.lineCap = 'butt';
604
- this.miterLimit = Z * 1;
605
- this.globalAlpha = 1;
606
- this.font = '10px sans-serif';
607
- this.textAlign = 'left';
608
- this.textBaseline = 'alphabetic';
609
- this.canvas = canvasElement;
610
-
611
- var cssText = 'width:' + canvasElement.clientWidth + 'px;height:' +
612
- canvasElement.clientHeight + 'px;overflow:hidden;position:absolute';
613
- var el = canvasElement.ownerDocument.createElement('div');
614
- el.style.cssText = cssText;
615
- canvasElement.appendChild(el);
616
-
617
- var overlayEl = el.cloneNode(false);
618
- // Use a non transparent background.
619
- overlayEl.style.backgroundColor = 'red';
620
- overlayEl.style.filter = 'alpha(opacity=0)';
621
- canvasElement.appendChild(overlayEl);
622
-
623
- this.element_ = el;
624
- this.arcScaleX_ = 1;
625
- this.arcScaleY_ = 1;
626
- this.lineScale_ = 1;
627
- }
628
-
629
- var contextPrototype = CanvasRenderingContext2D_.prototype;
630
- contextPrototype.clearRect = function() {
631
- if (this.textMeasureEl_) {
632
- this.textMeasureEl_.removeNode(true);
633
- this.textMeasureEl_ = null;
634
- }
635
- this.element_.innerHTML = '';
636
- };
637
-
638
- contextPrototype.beginPath = function() {
639
- // TODO: Branch current matrix so that save/restore has no effect
640
- // as per safari docs.
641
- this.currentPath_ = [];
642
- };
643
-
644
- contextPrototype.moveTo = function(aX, aY) {
645
- var p = getCoords(this, aX, aY);
646
- this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
647
- this.currentX_ = p.x;
648
- this.currentY_ = p.y;
649
- };
650
-
651
- contextPrototype.lineTo = function(aX, aY) {
652
- var p = getCoords(this, aX, aY);
653
- this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});
654
-
655
- this.currentX_ = p.x;
656
- this.currentY_ = p.y;
657
- };
658
-
659
- contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
660
- aCP2x, aCP2y,
661
- aX, aY) {
662
- var p = getCoords(this, aX, aY);
663
- var cp1 = getCoords(this, aCP1x, aCP1y);
664
- var cp2 = getCoords(this, aCP2x, aCP2y);
665
- bezierCurveTo(this, cp1, cp2, p);
666
- };
667
-
668
- // Helper function that takes the already fixed cordinates.
669
- function bezierCurveTo(self, cp1, cp2, p) {
670
- self.currentPath_.push({
671
- type: 'bezierCurveTo',
672
- cp1x: cp1.x,
673
- cp1y: cp1.y,
674
- cp2x: cp2.x,
675
- cp2y: cp2.y,
676
- x: p.x,
677
- y: p.y
678
- });
679
- self.currentX_ = p.x;
680
- self.currentY_ = p.y;
681
- }
682
-
683
- contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
684
- // the following is lifted almost directly from
685
- // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
686
-
687
- var cp = getCoords(this, aCPx, aCPy);
688
- var p = getCoords(this, aX, aY);
689
-
690
- var cp1 = {
691
- x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
692
- y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
693
- };
694
- var cp2 = {
695
- x: cp1.x + (p.x - this.currentX_) / 3.0,
696
- y: cp1.y + (p.y - this.currentY_) / 3.0
697
- };
698
-
699
- bezierCurveTo(this, cp1, cp2, p);
700
- };
701
-
702
- contextPrototype.arc = function(aX, aY, aRadius,
703
- aStartAngle, aEndAngle, aClockwise) {
704
- aRadius *= Z;
705
- var arcType = aClockwise ? 'at' : 'wa';
706
-
707
- var xStart = aX + mc(aStartAngle) * aRadius - Z2;
708
- var yStart = aY + ms(aStartAngle) * aRadius - Z2;
709
-
710
- var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
711
- var yEnd = aY + ms(aEndAngle) * aRadius - Z2;
712
-
713
- // IE won't render arches drawn counter clockwise if xStart == xEnd.
714
- if (xStart == xEnd && !aClockwise) {
715
- xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
716
- // that can be represented in binary
717
- }
718
-
719
- var p = getCoords(this, aX, aY);
720
- var pStart = getCoords(this, xStart, yStart);
721
- var pEnd = getCoords(this, xEnd, yEnd);
722
-
723
- this.currentPath_.push({type: arcType,
724
- x: p.x,
725
- y: p.y,
726
- radius: aRadius,
727
- xStart: pStart.x,
728
- yStart: pStart.y,
729
- xEnd: pEnd.x,
730
- yEnd: pEnd.y});
731
-
732
- };
733
-
734
- contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
735
- this.moveTo(aX, aY);
736
- this.lineTo(aX + aWidth, aY);
737
- this.lineTo(aX + aWidth, aY + aHeight);
738
- this.lineTo(aX, aY + aHeight);
739
- this.closePath();
740
- };
741
-
742
- contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
743
- var oldPath = this.currentPath_;
744
- this.beginPath();
745
-
746
- this.moveTo(aX, aY);
747
- this.lineTo(aX + aWidth, aY);
748
- this.lineTo(aX + aWidth, aY + aHeight);
749
- this.lineTo(aX, aY + aHeight);
750
- this.closePath();
751
- this.stroke();
752
-
753
- this.currentPath_ = oldPath;
754
- };
755
-
756
- contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
757
- var oldPath = this.currentPath_;
758
- this.beginPath();
759
-
760
- this.moveTo(aX, aY);
761
- this.lineTo(aX + aWidth, aY);
762
- this.lineTo(aX + aWidth, aY + aHeight);
763
- this.lineTo(aX, aY + aHeight);
764
- this.closePath();
765
- this.fill();
766
-
767
- this.currentPath_ = oldPath;
768
- };
769
-
770
- contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
771
- var gradient = new CanvasGradient_('gradient');
772
- gradient.x0_ = aX0;
773
- gradient.y0_ = aY0;
774
- gradient.x1_ = aX1;
775
- gradient.y1_ = aY1;
776
- return gradient;
777
- };
778
-
779
- contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
780
- aX1, aY1, aR1) {
781
- var gradient = new CanvasGradient_('gradientradial');
782
- gradient.x0_ = aX0;
783
- gradient.y0_ = aY0;
784
- gradient.r0_ = aR0;
785
- gradient.x1_ = aX1;
786
- gradient.y1_ = aY1;
787
- gradient.r1_ = aR1;
788
- return gradient;
789
- };
790
-
791
- contextPrototype.drawImage = function(image, var_args) {
792
- var dx, dy, dw, dh, sx, sy, sw, sh;
793
-
794
- // to find the original width we overide the width and height
795
- var oldRuntimeWidth = image.runtimeStyle.width;
796
- var oldRuntimeHeight = image.runtimeStyle.height;
797
- image.runtimeStyle.width = 'auto';
798
- image.runtimeStyle.height = 'auto';
799
-
800
- // get the original size
801
- var w = image.width;
802
- var h = image.height;
803
-
804
- // and remove overides
805
- image.runtimeStyle.width = oldRuntimeWidth;
806
- image.runtimeStyle.height = oldRuntimeHeight;
807
-
808
- if (arguments.length == 3) {
809
- dx = arguments[1];
810
- dy = arguments[2];
811
- sx = sy = 0;
812
- sw = dw = w;
813
- sh = dh = h;
814
- } else if (arguments.length == 5) {
815
- dx = arguments[1];
816
- dy = arguments[2];
817
- dw = arguments[3];
818
- dh = arguments[4];
819
- sx = sy = 0;
820
- sw = w;
821
- sh = h;
822
- } else if (arguments.length == 9) {
823
- sx = arguments[1];
824
- sy = arguments[2];
825
- sw = arguments[3];
826
- sh = arguments[4];
827
- dx = arguments[5];
828
- dy = arguments[6];
829
- dw = arguments[7];
830
- dh = arguments[8];
831
- } else {
832
- throw Error('Invalid number of arguments');
833
- }
834
-
835
- var d = getCoords(this, dx, dy);
836
-
837
- var w2 = sw / 2;
838
- var h2 = sh / 2;
839
-
840
- var vmlStr = [];
841
-
842
- var W = 10;
843
- var H = 10;
844
-
845
- // For some reason that I've now forgotten, using divs didn't work
846
- vmlStr.push(' <g_vml_:group',
847
- ' coordsize="', Z * W, ',', Z * H, '"',
848
- ' coordorigin="0,0"' ,
849
- ' style="width:', W, 'px;height:', H, 'px;position:absolute;');
850
-
851
- // If filters are necessary (rotation exists), create them
852
- // filters are bog-slow, so only create them if abbsolutely necessary
853
- // The following check doesn't account for skews (which don't exist
854
- // in the canvas spec (yet) anyway.
855
-
856
- if (this.m_[0][0] != 1 || this.m_[0][1] ||
857
- this.m_[1][1] != 1 || this.m_[1][0]) {
858
- var filter = [];
859
-
860
- // Note the 12/21 reversal
861
- filter.push('M11=', this.m_[0][0], ',',
862
- 'M12=', this.m_[1][0], ',',
863
- 'M21=', this.m_[0][1], ',',
864
- 'M22=', this.m_[1][1], ',',
865
- 'Dx=', mr(d.x / Z), ',',
866
- 'Dy=', mr(d.y / Z), '');
867
-
868
- // Bounding box calculation (need to minimize displayed area so that
869
- // filters don't waste time on unused pixels.
870
- var max = d;
871
- var c2 = getCoords(this, dx + dw, dy);
872
- var c3 = getCoords(this, dx, dy + dh);
873
- var c4 = getCoords(this, dx + dw, dy + dh);
874
-
875
- max.x = m.max(max.x, c2.x, c3.x, c4.x);
876
- max.y = m.max(max.y, c2.y, c3.y, c4.y);
877
-
878
- vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
879
- 'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
880
- filter.join(''), ", sizingmethod='clip');");
881
-
882
- } else {
883
- vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
884
- }
885
-
886
- vmlStr.push(' ">' ,
887
- '<g_vml_:image src="', image.src, '"',
888
- ' style="width:', Z * dw, 'px;',
889
- ' height:', Z * dh, 'px"',
890
- ' cropleft="', sx / w, '"',
891
- ' croptop="', sy / h, '"',
892
- ' cropright="', (w - sx - sw) / w, '"',
893
- ' cropbottom="', (h - sy - sh) / h, '"',
894
- ' />',
895
- '</g_vml_:group>');
896
-
897
- this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join(''));
898
- };
899
-
900
- contextPrototype.stroke = function(aFill) {
901
- var lineStr = [];
902
- var lineOpen = false;
903
-
904
- var W = 10;
905
- var H = 10;
906
-
907
- lineStr.push('<g_vml_:shape',
908
- ' filled="', !!aFill, '"',
909
- ' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
910
- ' coordorigin="0,0"',
911
- ' coordsize="', Z * W, ',', Z * H, '"',
912
- ' stroked="', !aFill, '"',
913
- ' path="');
914
-
915
- var newSeq = false;
916
- var min = {x: null, y: null};
917
- var max = {x: null, y: null};
918
-
919
- for (var i = 0; i < this.currentPath_.length; i++) {
920
- var p = this.currentPath_[i];
921
- var c;
922
-
923
- switch (p.type) {
924
- case 'moveTo':
925
- c = p;
926
- lineStr.push(' m ', mr(p.x), ',', mr(p.y));
927
- break;
928
- case 'lineTo':
929
- lineStr.push(' l ', mr(p.x), ',', mr(p.y));
930
- break;
931
- case 'close':
932
- lineStr.push(' x ');
933
- p = null;
934
- break;
935
- case 'bezierCurveTo':
936
- lineStr.push(' c ',
937
- mr(p.cp1x), ',', mr(p.cp1y), ',',
938
- mr(p.cp2x), ',', mr(p.cp2y), ',',
939
- mr(p.x), ',', mr(p.y));
940
- break;
941
- case 'at':
942
- case 'wa':
943
- lineStr.push(' ', p.type, ' ',
944
- mr(p.x - this.arcScaleX_ * p.radius), ',',
945
- mr(p.y - this.arcScaleY_ * p.radius), ' ',
946
- mr(p.x + this.arcScaleX_ * p.radius), ',',
947
- mr(p.y + this.arcScaleY_ * p.radius), ' ',
948
- mr(p.xStart), ',', mr(p.yStart), ' ',
949
- mr(p.xEnd), ',', mr(p.yEnd));
950
- break;
951
- }
952
-
953
-
954
- // TODO: Following is broken for curves due to
955
- // move to proper paths.
956
-
957
- // Figure out dimensions so we can do gradient fills
958
- // properly
959
- if (p) {
960
- if (min.x == null || p.x < min.x) {
961
- min.x = p.x;
962
- }
963
- if (max.x == null || p.x > max.x) {
964
- max.x = p.x;
965
- }
966
- if (min.y == null || p.y < min.y) {
967
- min.y = p.y;
968
- }
969
- if (max.y == null || p.y > max.y) {
970
- max.y = p.y;
971
- }
972
- }
973
- }
974
- lineStr.push(' ">');
975
-
976
- if (!aFill) {
977
- appendStroke(this, lineStr);
978
- } else {
979
- appendFill(this, lineStr, min, max);
980
- }
981
-
982
- lineStr.push('</g_vml_:shape>');
983
-
984
- this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
985
- };
986
-
987
- function appendStroke(ctx, lineStr) {
988
- var a = processStyle(ctx.strokeStyle);
989
- var color = a.color;
990
- var opacity = a.alpha * ctx.globalAlpha;
991
- var lineWidth = ctx.lineScale_ * ctx.lineWidth;
992
-
993
- // VML cannot correctly render a line if the width is less than 1px.
994
- // In that case, we dilute the color to make the line look thinner.
995
- if (lineWidth < 1) {
996
- opacity *= lineWidth;
997
- }
998
-
999
- lineStr.push(
1000
- '<g_vml_:stroke',
1001
- ' opacity="', opacity, '"',
1002
- ' joinstyle="', ctx.lineJoin, '"',
1003
- ' miterlimit="', ctx.miterLimit, '"',
1004
- ' endcap="', processLineCap(ctx.lineCap), '"',
1005
- ' weight="', lineWidth, 'px"',
1006
- ' color="', color, '" />'
1007
- );
1008
- }
1009
-
1010
- function appendFill(ctx, lineStr, min, max) {
1011
- var fillStyle = ctx.fillStyle;
1012
- var arcScaleX = ctx.arcScaleX_;
1013
- var arcScaleY = ctx.arcScaleY_;
1014
- var width = max.x - min.x;
1015
- var height = max.y - min.y;
1016
- if (fillStyle instanceof CanvasGradient_) {
1017
- // TODO: Gradients transformed with the transformation matrix.
1018
- var angle = 0;
1019
- var focus = {x: 0, y: 0};
1020
-
1021
- // additional offset
1022
- var shift = 0;
1023
- // scale factor for offset
1024
- var expansion = 1;
1025
-
1026
- if (fillStyle.type_ == 'gradient') {
1027
- var x0 = fillStyle.x0_ / arcScaleX;
1028
- var y0 = fillStyle.y0_ / arcScaleY;
1029
- var x1 = fillStyle.x1_ / arcScaleX;
1030
- var y1 = fillStyle.y1_ / arcScaleY;
1031
- var p0 = getCoords(ctx, x0, y0);
1032
- var p1 = getCoords(ctx, x1, y1);
1033
- var dx = p1.x - p0.x;
1034
- var dy = p1.y - p0.y;
1035
- angle = Math.atan2(dx, dy) * 180 / Math.PI;
1036
-
1037
- // The angle should be a non-negative number.
1038
- if (angle < 0) {
1039
- angle += 360;
1040
- }
1041
-
1042
- // Very small angles produce an unexpected result because they are
1043
- // converted to a scientific notation string.
1044
- if (angle < 1e-6) {
1045
- angle = 0;
1046
- }
1047
- } else {
1048
- var p0 = getCoords(ctx, fillStyle.x0_, fillStyle.y0_);
1049
- focus = {
1050
- x: (p0.x - min.x) / width,
1051
- y: (p0.y - min.y) / height
1052
- };
1053
-
1054
- width /= arcScaleX * Z;
1055
- height /= arcScaleY * Z;
1056
- var dimension = m.max(width, height);
1057
- shift = 2 * fillStyle.r0_ / dimension;
1058
- expansion = 2 * fillStyle.r1_ / dimension - shift;
1059
- }
1060
-
1061
- // We need to sort the color stops in ascending order by offset,
1062
- // otherwise IE won't interpret it correctly.
1063
- var stops = fillStyle.colors_;
1064
- stops.sort(function(cs1, cs2) {
1065
- return cs1.offset - cs2.offset;
1066
- });
1067
-
1068
- var length = stops.length;
1069
- var color1 = stops[0].color;
1070
- var color2 = stops[length - 1].color;
1071
- var opacity1 = stops[0].alpha * ctx.globalAlpha;
1072
- var opacity2 = stops[length - 1].alpha * ctx.globalAlpha;
1073
-
1074
- var colors = [];
1075
- for (var i = 0; i < length; i++) {
1076
- var stop = stops[i];
1077
- colors.push(stop.offset * expansion + shift + ' ' + stop.color);
1078
- }
1079
-
1080
- // When colors attribute is used, the meanings of opacity and o:opacity2
1081
- // are reversed.
1082
- lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
1083
- ' method="none" focus="100%"',
1084
- ' color="', color1, '"',
1085
- ' color2="', color2, '"',
1086
- ' colors="', colors.join(','), '"',
1087
- ' opacity="', opacity2, '"',
1088
- ' g_o_:opacity2="', opacity1, '"',
1089
- ' angle="', angle, '"',
1090
- ' focusposition="', focus.x, ',', focus.y, '" />');
1091
- } else if (fillStyle instanceof CanvasPattern_) {
1092
- if (width && height) {
1093
- var deltaLeft = -min.x;
1094
- var deltaTop = -min.y;
1095
- lineStr.push('<g_vml_:fill',
1096
- ' position="',
1097
- deltaLeft / width * arcScaleX * arcScaleX, ',',
1098
- deltaTop / height * arcScaleY * arcScaleY, '"',
1099
- ' type="tile"',
1100
- // TODO: Figure out the correct size to fit the scale.
1101
- //' size="', w, 'px ', h, 'px"',
1102
- ' src="', fillStyle.src_, '" />');
1103
- }
1104
- } else {
1105
- var a = processStyle(ctx.fillStyle);
1106
- var color = a.color;
1107
- var opacity = a.alpha * ctx.globalAlpha;
1108
- lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
1109
- '" />');
1110
- }
1111
- }
1112
-
1113
- contextPrototype.fill = function() {
1114
- this.stroke(true);
1115
- };
1116
-
1117
- contextPrototype.closePath = function() {
1118
- this.currentPath_.push({type: 'close'});
1119
- };
1120
-
1121
- function getCoords(ctx, aX, aY) {
1122
- var m = ctx.m_;
1123
- return {
1124
- x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
1125
- y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
1126
- };
1127
- };
1128
-
1129
- contextPrototype.save = function() {
1130
- var o = {};
1131
- copyState(this, o);
1132
- this.aStack_.push(o);
1133
- this.mStack_.push(this.m_);
1134
- this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
1135
- };
1136
-
1137
- contextPrototype.restore = function() {
1138
- if (this.aStack_.length) {
1139
- copyState(this.aStack_.pop(), this);
1140
- this.m_ = this.mStack_.pop();
1141
- }
1142
- };
1143
-
1144
- function matrixIsFinite(m) {
1145
- return isFinite(m[0][0]) && isFinite(m[0][1]) &&
1146
- isFinite(m[1][0]) && isFinite(m[1][1]) &&
1147
- isFinite(m[2][0]) && isFinite(m[2][1]);
1148
- }
1149
-
1150
- function setM(ctx, m, updateLineScale) {
1151
- if (!matrixIsFinite(m)) {
1152
- return;
1153
- }
1154
- ctx.m_ = m;
1155
-
1156
- if (updateLineScale) {
1157
- // Get the line scale.
1158
- // Determinant of this.m_ means how much the area is enlarged by the
1159
- // transformation. So its square root can be used as a scale factor
1160
- // for width.
1161
- var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
1162
- ctx.lineScale_ = sqrt(abs(det));
1163
- }
1164
- }
1165
-
1166
- contextPrototype.translate = function(aX, aY) {
1167
- var m1 = [
1168
- [1, 0, 0],
1169
- [0, 1, 0],
1170
- [aX, aY, 1]
1171
- ];
1172
-
1173
- setM(this, matrixMultiply(m1, this.m_), false);
1174
- };
1175
-
1176
- contextPrototype.rotate = function(aRot) {
1177
- var c = mc(aRot);
1178
- var s = ms(aRot);
1179
-
1180
- var m1 = [
1181
- [c, s, 0],
1182
- [-s, c, 0],
1183
- [0, 0, 1]
1184
- ];
1185
-
1186
- setM(this, matrixMultiply(m1, this.m_), false);
1187
- };
1188
-
1189
- contextPrototype.scale = function(aX, aY) {
1190
- this.arcScaleX_ *= aX;
1191
- this.arcScaleY_ *= aY;
1192
- var m1 = [
1193
- [aX, 0, 0],
1194
- [0, aY, 0],
1195
- [0, 0, 1]
1196
- ];
1197
-
1198
- setM(this, matrixMultiply(m1, this.m_), true);
1199
- };
1200
-
1201
- contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
1202
- var m1 = [
1203
- [m11, m12, 0],
1204
- [m21, m22, 0],
1205
- [dx, dy, 1]
1206
- ];
1207
-
1208
- setM(this, matrixMultiply(m1, this.m_), true);
1209
- };
1210
-
1211
- contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
1212
- var m = [
1213
- [m11, m12, 0],
1214
- [m21, m22, 0],
1215
- [dx, dy, 1]
1216
- ];
1217
-
1218
- setM(this, m, true);
1219
- };
1220
-
1221
- /**
1222
- * The text drawing function.
1223
- * The maxWidth argument isn't taken in account, since no browser supports
1224
- * it yet.
1225
- */
1226
- contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) {
1227
- var m = this.m_,
1228
- delta = 1000,
1229
- left = 0,
1230
- right = delta,
1231
- offset = {x: 0, y: 0},
1232
- lineStr = [];
1233
-
1234
- var fontStyle = getComputedStyle(processFontStyle(this.font), this.element_);
1235
-
1236
- var fontStyleString = buildStyle(fontStyle);
1237
-
1238
- var elementStyle = this.element_.currentStyle;
1239
- var textAlign = this.textAlign.toLowerCase();
1240
- switch (textAlign) {
1241
- case 'left':
1242
- case 'center':
1243
- case 'right':
1244
- break;
1245
- case 'end':
1246
- textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left';
1247
- break;
1248
- case 'start':
1249
- textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left';
1250
- break;
1251
- default:
1252
- textAlign = 'left';
1253
- }
1254
-
1255
- // 1.75 is an arbitrary number, as there is no info about the text baseline
1256
- switch (this.textBaseline) {
1257
- case 'hanging':
1258
- case 'top':
1259
- offset.y = fontStyle.size / 1.75;
1260
- break;
1261
- case 'middle':
1262
- break;
1263
- default:
1264
- case null:
1265
- case 'alphabetic':
1266
- case 'ideographic':
1267
- case 'bottom':
1268
- offset.y = -fontStyle.size / 2.25;
1269
- break;
1270
- }
1271
-
1272
- switch(textAlign) {
1273
- case 'right':
1274
- left = delta;
1275
- right = 0.05;
1276
- break;
1277
- case 'center':
1278
- left = right = delta / 2;
1279
- break;
1280
- }
1281
-
1282
- var d = getCoords(this, x + offset.x, y + offset.y);
1283
-
1284
- lineStr.push('<g_vml_:line from="', -left ,' 0" to="', right ,' 0.05" ',
1285
- ' coordsize="100 100" coordorigin="0 0"',
1286
- ' filled="', !stroke, '" stroked="', !!stroke,
1287
- '" style="position:absolute;width:1px;height:1px;">');
1288
-
1289
- if (stroke) {
1290
- appendStroke(this, lineStr);
1291
- } else {
1292
- // TODO: Fix the min and max params.
1293
- appendFill(this, lineStr, {x: -left, y: 0},
1294
- {x: right, y: fontStyle.size});
1295
- }
1296
-
1297
- var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' +
1298
- m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0';
1299
-
1300
- var skewOffset = mr(d.x / Z + 1 - m[0][0]) + ',' + mr(d.y / Z - 2 * m[1][0]);
1301
-
1302
-
1303
- lineStr.push('<g_vml_:skew on="t" matrix="', skewM ,'" ',
1304
- ' offset="', skewOffset, '" origin="', left ,' 0" />',
1305
- '<g_vml_:path textpathok="true" />',
1306
- '<g_vml_:textpath on="true" string="',
1307
- encodeHtmlAttribute(text),
1308
- '" style="v-text-align:', textAlign,
1309
- ';font:', encodeHtmlAttribute(fontStyleString),
1310
- '" /></g_vml_:line>');
1311
-
1312
- this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
1313
- };
1314
-
1315
- contextPrototype.fillText = function(text, x, y, maxWidth) {
1316
- this.drawText_(text, x, y, maxWidth, false);
1317
- };
1318
-
1319
- contextPrototype.strokeText = function(text, x, y, maxWidth) {
1320
- this.drawText_(text, x, y, maxWidth, true);
1321
- };
1322
-
1323
- contextPrototype.measureText = function(text) {
1324
- if (!this.textMeasureEl_) {
1325
- var s = '<span style="position:absolute;' +
1326
- 'top:-20000px;left:0;padding:0;margin:0;border:none;' +
1327
- 'white-space:pre;"></span>';
1328
- this.element_.insertAdjacentHTML('beforeEnd', s);
1329
- this.textMeasureEl_ = this.element_.lastChild;
1330
- }
1331
- var doc = this.element_.ownerDocument;
1332
- this.textMeasureEl_.innerHTML = '';
1333
- this.textMeasureEl_.style.font = this.font;
1334
- // Don't use innerHTML or innerText because they allow markup/whitespace.
1335
- this.textMeasureEl_.appendChild(doc.createTextNode(text));
1336
- return {width: this.textMeasureEl_.offsetWidth};
1337
- };
1338
-
1339
- /******** STUBS ********/
1340
- contextPrototype.clip = function() {
1341
- // TODO: Implement
1342
- };
1343
-
1344
- contextPrototype.arcTo = function() {
1345
- // TODO: Implement
1346
- };
1347
-
1348
- contextPrototype.createPattern = function(image, repetition) {
1349
- return new CanvasPattern_(image, repetition);
1350
- };
1351
-
1352
- // Gradient / Pattern Stubs
1353
- function CanvasGradient_(aType) {
1354
- this.type_ = aType;
1355
- this.x0_ = 0;
1356
- this.y0_ = 0;
1357
- this.r0_ = 0;
1358
- this.x1_ = 0;
1359
- this.y1_ = 0;
1360
- this.r1_ = 0;
1361
- this.colors_ = [];
1362
- }
1363
-
1364
- CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
1365
- aColor = processStyle(aColor);
1366
- this.colors_.push({offset: aOffset,
1367
- color: aColor.color,
1368
- alpha: aColor.alpha});
1369
- };
1370
-
1371
- function CanvasPattern_(image, repetition) {
1372
- assertImageIsValid(image);
1373
- switch (repetition) {
1374
- case 'repeat':
1375
- case null:
1376
- case '':
1377
- this.repetition_ = 'repeat';
1378
- break;
1379
- case 'repeat-x':
1380
- case 'repeat-y':
1381
- case 'no-repeat':
1382
- this.repetition_ = repetition;
1383
- break;
1384
- default:
1385
- throwException('SYNTAX_ERR');
1386
- }
1387
-
1388
- this.src_ = image.src;
1389
- this.width_ = image.width;
1390
- this.height_ = image.height;
1391
- }
1392
-
1393
- function throwException(s) {
1394
- throw new DOMException_(s);
1395
- }
1396
-
1397
- function assertImageIsValid(img) {
1398
- if (!img || img.nodeType != 1 || img.tagName != 'IMG') {
1399
- throwException('TYPE_MISMATCH_ERR');
1400
- }
1401
- if (img.readyState != 'complete') {
1402
- throwException('INVALID_STATE_ERR');
1403
- }
1404
- }
1405
-
1406
- function DOMException_(s) {
1407
- this.code = this[s];
1408
- this.message = s +': DOM Exception ' + this.code;
1409
- }
1410
- var p = DOMException_.prototype = new Error;
1411
- p.INDEX_SIZE_ERR = 1;
1412
- p.DOMSTRING_SIZE_ERR = 2;
1413
- p.HIERARCHY_REQUEST_ERR = 3;
1414
- p.WRONG_DOCUMENT_ERR = 4;
1415
- p.INVALID_CHARACTER_ERR = 5;
1416
- p.NO_DATA_ALLOWED_ERR = 6;
1417
- p.NO_MODIFICATION_ALLOWED_ERR = 7;
1418
- p.NOT_FOUND_ERR = 8;
1419
- p.NOT_SUPPORTED_ERR = 9;
1420
- p.INUSE_ATTRIBUTE_ERR = 10;
1421
- p.INVALID_STATE_ERR = 11;
1422
- p.SYNTAX_ERR = 12;
1423
- p.INVALID_MODIFICATION_ERR = 13;
1424
- p.NAMESPACE_ERR = 14;
1425
- p.INVALID_ACCESS_ERR = 15;
1426
- p.VALIDATION_ERR = 16;
1427
- p.TYPE_MISMATCH_ERR = 17;
1428
-
1429
- // set up externs
1430
- G_vmlCanvasManager = G_vmlCanvasManager_;
1431
- CanvasRenderingContext2D = CanvasRenderingContext2D_;
1432
- CanvasGradient = CanvasGradient_;
1433
- CanvasPattern = CanvasPattern_;
1434
- DOMException = DOMException_;
1435
- G_vmlCanvasManager._version = 888;
1436
- })();
1437
-
1438
- } // if
1
+ // Memory Leaks patch from http://explorercanvas.googlecode.com/svn/trunk/
2
+ // svn : r73
3
+ // ------------------------------------------------------------------
4
+ // Copyright 2006 Google Inc.
5
+ //
6
+ // Licensed under the Apache License, Version 2.0 (the "License");
7
+ // you may not use this file except in compliance with the License.
8
+ // You may obtain a copy of the License at
9
+ //
10
+ // http://www.apache.org/licenses/LICENSE-2.0
11
+ //
12
+ // Unless required by applicable law or agreed to in writing, software
13
+ // distributed under the License is distributed on an "AS IS" BASIS,
14
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ // See the License for the specific language governing permissions and
16
+ // limitations under the License.
17
+
18
+
19
+ // Known Issues:
20
+ //
21
+ // * Patterns only support repeat.
22
+ // * Radial gradient are not implemented. The VML version of these look very
23
+ // different from the canvas one.
24
+ // * Clipping paths are not implemented.
25
+ // * Coordsize. The width and height attribute have higher priority than the
26
+ // width and height style values which isn't correct.
27
+ // * Painting mode isn't implemented.
28
+ // * Canvas width/height should is using content-box by default. IE in
29
+ // Quirks mode will draw the canvas using border-box. Either change your
30
+ // doctype to HTML5
31
+ // (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
32
+ // or use Box Sizing Behavior from WebFX
33
+ // (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
34
+ // * Non uniform scaling does not correctly scale strokes.
35
+ // * Optimize. There is always room for speed improvements.
36
+
37
+ // Only add this code if we do not already have a canvas implementation
38
+ if (!document.createElement('canvas').getContext) {
39
+
40
+ (function() {
41
+
42
+ // alias some functions to make (compiled) code shorter
43
+ var m = Math;
44
+ var mr = m.round;
45
+ var ms = m.sin;
46
+ var mc = m.cos;
47
+ var abs = m.abs;
48
+ var sqrt = m.sqrt;
49
+
50
+ // this is used for sub pixel precision
51
+ var Z = 10;
52
+ var Z2 = Z / 2;
53
+
54
+ var IE_VERSION = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];
55
+
56
+ /**
57
+ * This funtion is assigned to the <canvas> elements as element.getContext().
58
+ * @this {HTMLElement}
59
+ * @return {CanvasRenderingContext2D_}
60
+ */
61
+ function getContext() {
62
+ return this.context_ ||
63
+ (this.context_ = new CanvasRenderingContext2D_(this));
64
+ }
65
+
66
+ var slice = Array.prototype.slice;
67
+
68
+ /**
69
+ * Binds a function to an object. The returned function will always use the
70
+ * passed in {@code obj} as {@code this}.
71
+ *
72
+ * Example:
73
+ *
74
+ * g = bind(f, obj, a, b)
75
+ * g(c, d) // will do f.call(obj, a, b, c, d)
76
+ *
77
+ * @param {Function} f The function to bind the object to
78
+ * @param {Object} obj The object that should act as this when the function
79
+ * is called
80
+ * @param {*} var_args Rest arguments that will be used as the initial
81
+ * arguments when the function is called
82
+ * @return {Function} A new function that has bound this
83
+ */
84
+ function bind(f, obj, var_args) {
85
+ var a = slice.call(arguments, 2);
86
+ return function() {
87
+ return f.apply(obj, a.concat(slice.call(arguments)));
88
+ };
89
+ }
90
+
91
+ function encodeHtmlAttribute(s) {
92
+ return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
93
+ }
94
+
95
+ function addNamespace(doc, prefix, urn) {
96
+ if (!doc.namespaces[prefix]) {
97
+ doc.namespaces.add(prefix, urn, '#default#VML');
98
+ }
99
+ }
100
+
101
+ function addNamespacesAndStylesheet(doc) {
102
+ addNamespace(doc, 'g_vml_', 'urn:schemas-microsoft-com:vml');
103
+ addNamespace(doc, 'g_o_', 'urn:schemas-microsoft-com:office:office');
104
+
105
+ // Setup default CSS. Only add one style sheet per document
106
+ if (!doc.styleSheets['ex_canvas_']) {
107
+ var ss = doc.createStyleSheet();
108
+ ss.owningElement.id = 'ex_canvas_';
109
+ ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
110
+ // default size is 300x150 in Gecko and Opera
111
+ 'text-align:left;width:300px;height:150px}';
112
+ }
113
+ }
114
+
115
+ // Add namespaces and stylesheet at startup.
116
+ addNamespacesAndStylesheet(document);
117
+
118
+ var G_vmlCanvasManager_ = {
119
+ init: function(opt_doc) {
120
+ var doc = opt_doc || document;
121
+ // Create a dummy element so that IE will allow canvas elements to be
122
+ // recognized.
123
+ doc.createElement('canvas');
124
+ doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
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
+ // Memory Leaks patch : see http://code.google.com/p/explorercanvas/issues/detail?id=82
180
+ uninitElement: function(el){
181
+ if (el.getContext) {
182
+ var ctx = el.getContext();
183
+ delete ctx.element_;
184
+ delete ctx.canvas;
185
+ el.innerHTML = "";
186
+ //el.outerHTML = "";
187
+ el.context_ = null;
188
+ el.getContext = null;
189
+ el.detachEvent("onpropertychange", onPropertyChange);
190
+ el.detachEvent("onresize", onResize);
191
+ }
192
+ }
193
+ };
194
+
195
+ function onPropertyChange(e) {
196
+ var el = e.srcElement;
197
+
198
+ switch (e.propertyName) {
199
+ case 'width':
200
+ el.getContext().clearRect();
201
+ el.style.width = el.attributes.width.nodeValue + 'px';
202
+ // In IE8 this does not trigger onresize.
203
+ el.firstChild.style.width = el.clientWidth + 'px';
204
+ break;
205
+ case 'height':
206
+ el.getContext().clearRect();
207
+ el.style.height = el.attributes.height.nodeValue + 'px';
208
+ el.firstChild.style.height = el.clientHeight + 'px';
209
+ break;
210
+ }
211
+ }
212
+
213
+ function onResize(e) {
214
+ var el = e.srcElement;
215
+ if (el.firstChild) {
216
+ el.firstChild.style.width = el.clientWidth + 'px';
217
+ el.firstChild.style.height = el.clientHeight + 'px';
218
+ }
219
+ }
220
+
221
+ G_vmlCanvasManager_.init();
222
+
223
+ // precompute "00" to "FF"
224
+ var decToHex = [];
225
+ for (var i = 0; i < 16; i++) {
226
+ for (var j = 0; j < 16; j++) {
227
+ decToHex[i * 16 + j] = i.toString(16) + j.toString(16);
228
+ }
229
+ }
230
+
231
+ function createMatrixIdentity() {
232
+ return [
233
+ [1, 0, 0],
234
+ [0, 1, 0],
235
+ [0, 0, 1]
236
+ ];
237
+ }
238
+
239
+ function matrixMultiply(m1, m2) {
240
+ var result = createMatrixIdentity();
241
+
242
+ for (var x = 0; x < 3; x++) {
243
+ for (var y = 0; y < 3; y++) {
244
+ var sum = 0;
245
+
246
+ for (var z = 0; z < 3; z++) {
247
+ sum += m1[x][z] * m2[z][y];
248
+ }
249
+
250
+ result[x][y] = sum;
251
+ }
252
+ }
253
+ return result;
254
+ }
255
+
256
+ function copyState(o1, o2) {
257
+ o2.fillStyle = o1.fillStyle;
258
+ o2.lineCap = o1.lineCap;
259
+ o2.lineJoin = o1.lineJoin;
260
+ o2.lineWidth = o1.lineWidth;
261
+ o2.miterLimit = o1.miterLimit;
262
+ o2.shadowBlur = o1.shadowBlur;
263
+ o2.shadowColor = o1.shadowColor;
264
+ o2.shadowOffsetX = o1.shadowOffsetX;
265
+ o2.shadowOffsetY = o1.shadowOffsetY;
266
+ o2.strokeStyle = o1.strokeStyle;
267
+ o2.globalAlpha = o1.globalAlpha;
268
+ o2.font = o1.font;
269
+ o2.textAlign = o1.textAlign;
270
+ o2.textBaseline = o1.textBaseline;
271
+ o2.arcScaleX_ = o1.arcScaleX_;
272
+ o2.arcScaleY_ = o1.arcScaleY_;
273
+ o2.lineScale_ = o1.lineScale_;
274
+ }
275
+
276
+ var colorData = {
277
+ aliceblue: '#F0F8FF',
278
+ antiquewhite: '#FAEBD7',
279
+ aquamarine: '#7FFFD4',
280
+ azure: '#F0FFFF',
281
+ beige: '#F5F5DC',
282
+ bisque: '#FFE4C4',
283
+ black: '#000000',
284
+ blanchedalmond: '#FFEBCD',
285
+ blueviolet: '#8A2BE2',
286
+ brown: '#A52A2A',
287
+ burlywood: '#DEB887',
288
+ cadetblue: '#5F9EA0',
289
+ chartreuse: '#7FFF00',
290
+ chocolate: '#D2691E',
291
+ coral: '#FF7F50',
292
+ cornflowerblue: '#6495ED',
293
+ cornsilk: '#FFF8DC',
294
+ crimson: '#DC143C',
295
+ cyan: '#00FFFF',
296
+ darkblue: '#00008B',
297
+ darkcyan: '#008B8B',
298
+ darkgoldenrod: '#B8860B',
299
+ darkgray: '#A9A9A9',
300
+ darkgreen: '#006400',
301
+ darkgrey: '#A9A9A9',
302
+ darkkhaki: '#BDB76B',
303
+ darkmagenta: '#8B008B',
304
+ darkolivegreen: '#556B2F',
305
+ darkorange: '#FF8C00',
306
+ darkorchid: '#9932CC',
307
+ darkred: '#8B0000',
308
+ darksalmon: '#E9967A',
309
+ darkseagreen: '#8FBC8F',
310
+ darkslateblue: '#483D8B',
311
+ darkslategray: '#2F4F4F',
312
+ darkslategrey: '#2F4F4F',
313
+ darkturquoise: '#00CED1',
314
+ darkviolet: '#9400D3',
315
+ deeppink: '#FF1493',
316
+ deepskyblue: '#00BFFF',
317
+ dimgray: '#696969',
318
+ dimgrey: '#696969',
319
+ dodgerblue: '#1E90FF',
320
+ firebrick: '#B22222',
321
+ floralwhite: '#FFFAF0',
322
+ forestgreen: '#228B22',
323
+ gainsboro: '#DCDCDC',
324
+ ghostwhite: '#F8F8FF',
325
+ gold: '#FFD700',
326
+ goldenrod: '#DAA520',
327
+ grey: '#808080',
328
+ greenyellow: '#ADFF2F',
329
+ honeydew: '#F0FFF0',
330
+ hotpink: '#FF69B4',
331
+ indianred: '#CD5C5C',
332
+ indigo: '#4B0082',
333
+ ivory: '#FFFFF0',
334
+ khaki: '#F0E68C',
335
+ lavender: '#E6E6FA',
336
+ lavenderblush: '#FFF0F5',
337
+ lawngreen: '#7CFC00',
338
+ lemonchiffon: '#FFFACD',
339
+ lightblue: '#ADD8E6',
340
+ lightcoral: '#F08080',
341
+ lightcyan: '#E0FFFF',
342
+ lightgoldenrodyellow: '#FAFAD2',
343
+ lightgreen: '#90EE90',
344
+ lightgrey: '#D3D3D3',
345
+ lightpink: '#FFB6C1',
346
+ lightsalmon: '#FFA07A',
347
+ lightseagreen: '#20B2AA',
348
+ lightskyblue: '#87CEFA',
349
+ lightslategray: '#778899',
350
+ lightslategrey: '#778899',
351
+ lightsteelblue: '#B0C4DE',
352
+ lightyellow: '#FFFFE0',
353
+ limegreen: '#32CD32',
354
+ linen: '#FAF0E6',
355
+ magenta: '#FF00FF',
356
+ mediumaquamarine: '#66CDAA',
357
+ mediumblue: '#0000CD',
358
+ mediumorchid: '#BA55D3',
359
+ mediumpurple: '#9370DB',
360
+ mediumseagreen: '#3CB371',
361
+ mediumslateblue: '#7B68EE',
362
+ mediumspringgreen: '#00FA9A',
363
+ mediumturquoise: '#48D1CC',
364
+ mediumvioletred: '#C71585',
365
+ midnightblue: '#191970',
366
+ mintcream: '#F5FFFA',
367
+ mistyrose: '#FFE4E1',
368
+ moccasin: '#FFE4B5',
369
+ navajowhite: '#FFDEAD',
370
+ oldlace: '#FDF5E6',
371
+ olivedrab: '#6B8E23',
372
+ orange: '#FFA500',
373
+ orangered: '#FF4500',
374
+ orchid: '#DA70D6',
375
+ palegoldenrod: '#EEE8AA',
376
+ palegreen: '#98FB98',
377
+ paleturquoise: '#AFEEEE',
378
+ palevioletred: '#DB7093',
379
+ papayawhip: '#FFEFD5',
380
+ peachpuff: '#FFDAB9',
381
+ peru: '#CD853F',
382
+ pink: '#FFC0CB',
383
+ plum: '#DDA0DD',
384
+ powderblue: '#B0E0E6',
385
+ rosybrown: '#BC8F8F',
386
+ royalblue: '#4169E1',
387
+ saddlebrown: '#8B4513',
388
+ salmon: '#FA8072',
389
+ sandybrown: '#F4A460',
390
+ seagreen: '#2E8B57',
391
+ seashell: '#FFF5EE',
392
+ sienna: '#A0522D',
393
+ skyblue: '#87CEEB',
394
+ slateblue: '#6A5ACD',
395
+ slategray: '#708090',
396
+ slategrey: '#708090',
397
+ snow: '#FFFAFA',
398
+ springgreen: '#00FF7F',
399
+ steelblue: '#4682B4',
400
+ tan: '#D2B48C',
401
+ thistle: '#D8BFD8',
402
+ tomato: '#FF6347',
403
+ turquoise: '#40E0D0',
404
+ violet: '#EE82EE',
405
+ wheat: '#F5DEB3',
406
+ whitesmoke: '#F5F5F5',
407
+ yellowgreen: '#9ACD32'
408
+ };
409
+
410
+
411
+ function getRgbHslContent(styleString) {
412
+ var start = styleString.indexOf('(', 3);
413
+ var end = styleString.indexOf(')', start + 1);
414
+ var parts = styleString.substring(start + 1, end).split(',');
415
+ // add alpha if needed
416
+ if (parts.length != 4 || styleString.charAt(3) != 'a') {
417
+ parts[3] = 1;
418
+ }
419
+ return parts;
420
+ }
421
+
422
+ function percent(s) {
423
+ return parseFloat(s) / 100;
424
+ }
425
+
426
+ function clamp(v, min, max) {
427
+ return Math.min(max, Math.max(min, v));
428
+ }
429
+
430
+ function hslToRgb(parts){
431
+ var r, g, b, h, s, l;
432
+ h = parseFloat(parts[0]) / 360 % 360;
433
+ if (h < 0)
434
+ h++;
435
+ s = clamp(percent(parts[1]), 0, 1);
436
+ l = clamp(percent(parts[2]), 0, 1);
437
+ if (s == 0) {
438
+ r = g = b = l; // achromatic
439
+ } else {
440
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
441
+ var p = 2 * l - q;
442
+ r = hueToRgb(p, q, h + 1 / 3);
443
+ g = hueToRgb(p, q, h);
444
+ b = hueToRgb(p, q, h - 1 / 3);
445
+ }
446
+
447
+ return '#' + decToHex[Math.floor(r * 255)] +
448
+ decToHex[Math.floor(g * 255)] +
449
+ decToHex[Math.floor(b * 255)];
450
+ }
451
+
452
+ function hueToRgb(m1, m2, h) {
453
+ if (h < 0)
454
+ h++;
455
+ if (h > 1)
456
+ h--;
457
+
458
+ if (6 * h < 1)
459
+ return m1 + (m2 - m1) * 6 * h;
460
+ else if (2 * h < 1)
461
+ return m2;
462
+ else if (3 * h < 2)
463
+ return m1 + (m2 - m1) * (2 / 3 - h) * 6;
464
+ else
465
+ return m1;
466
+ }
467
+
468
+ var processStyleCache = {};
469
+
470
+ function processStyle(styleString) {
471
+ if (styleString in processStyleCache) {
472
+ return processStyleCache[styleString];
473
+ }
474
+
475
+ var str, alpha = 1;
476
+
477
+ styleString = String(styleString);
478
+ if (styleString.charAt(0) == '#') {
479
+ str = styleString;
480
+ } else if (/^rgb/.test(styleString)) {
481
+ var parts = getRgbHslContent(styleString);
482
+ var str = '#', n;
483
+ for (var i = 0; i < 3; i++) {
484
+ if (parts[i].indexOf('%') != -1) {
485
+ n = Math.floor(percent(parts[i]) * 255);
486
+ } else {
487
+ n = +parts[i];
488
+ }
489
+ str += decToHex[clamp(n, 0, 255)];
490
+ }
491
+ alpha = +parts[3];
492
+ } else if (/^hsl/.test(styleString)) {
493
+ var parts = getRgbHslContent(styleString);
494
+ str = hslToRgb(parts);
495
+ alpha = parts[3];
496
+ } else {
497
+ str = colorData[styleString] || styleString;
498
+ }
499
+ return processStyleCache[styleString] = {color: str, alpha: alpha};
500
+ }
501
+
502
+ var DEFAULT_STYLE = {
503
+ style: 'normal',
504
+ variant: 'normal',
505
+ weight: 'normal',
506
+ size: 10,
507
+ family: 'sans-serif'
508
+ };
509
+
510
+ // Internal text style cache
511
+ var fontStyleCache = {};
512
+
513
+ function processFontStyle(styleString) {
514
+ if (fontStyleCache[styleString]) {
515
+ return fontStyleCache[styleString];
516
+ }
517
+
518
+ var el = document.createElement('div');
519
+ var style = el.style;
520
+ try {
521
+ style.font = styleString;
522
+ } catch (ex) {
523
+ // Ignore failures to set to invalid font.
524
+ }
525
+
526
+ return fontStyleCache[styleString] = {
527
+ style: style.fontStyle || DEFAULT_STYLE.style,
528
+ variant: style.fontVariant || DEFAULT_STYLE.variant,
529
+ weight: style.fontWeight || DEFAULT_STYLE.weight,
530
+ size: style.fontSize || DEFAULT_STYLE.size,
531
+ family: style.fontFamily || DEFAULT_STYLE.family
532
+ };
533
+ }
534
+
535
+ function getComputedStyle(style, element) {
536
+ var computedStyle = {};
537
+
538
+ for (var p in style) {
539
+ computedStyle[p] = style[p];
540
+ }
541
+
542
+ // Compute the size
543
+ var canvasFontSize = parseFloat(element.currentStyle.fontSize),
544
+ fontSize = parseFloat(style.size);
545
+
546
+ if (typeof style.size == 'number') {
547
+ computedStyle.size = style.size;
548
+ } else if (style.size.indexOf('px') != -1) {
549
+ computedStyle.size = fontSize;
550
+ } else if (style.size.indexOf('em') != -1) {
551
+ computedStyle.size = canvasFontSize * fontSize;
552
+ } else if(style.size.indexOf('%') != -1) {
553
+ computedStyle.size = (canvasFontSize / 100) * fontSize;
554
+ } else if (style.size.indexOf('pt') != -1) {
555
+ computedStyle.size = fontSize / .75;
556
+ } else {
557
+ computedStyle.size = canvasFontSize;
558
+ }
559
+
560
+ // Different scaling between normal text and VML text. This was found using
561
+ // trial and error to get the same size as non VML text.
562
+ computedStyle.size *= 0.981;
563
+
564
+ // Fix for VML handling of bare font family names. Add a '' around font family names.
565
+ computedStyle.family = "'" + computedStyle.family.replace(/(\'|\")/g,'').replace(/\s*,\s*/g, "', '") + "'";
566
+
567
+ return computedStyle;
568
+ }
569
+
570
+ function buildStyle(style) {
571
+ return style.style + ' ' + style.variant + ' ' + style.weight + ' ' +
572
+ style.size + 'px ' + style.family;
573
+ }
574
+
575
+ var lineCapMap = {
576
+ 'butt': 'flat',
577
+ 'round': 'round'
578
+ };
579
+
580
+ function processLineCap(lineCap) {
581
+ return lineCapMap[lineCap] || 'square';
582
+ }
583
+
584
+ /**
585
+ * This class implements CanvasRenderingContext2D interface as described by
586
+ * the WHATWG.
587
+ * @param {HTMLElement} canvasElement The element that the 2D context should
588
+ * be associated with
589
+ */
590
+ function CanvasRenderingContext2D_(canvasElement) {
591
+ this.m_ = createMatrixIdentity();
592
+
593
+ this.mStack_ = [];
594
+ this.aStack_ = [];
595
+ this.currentPath_ = [];
596
+
597
+ // Canvas context properties
598
+ this.strokeStyle = '#000';
599
+ this.fillStyle = '#000';
600
+
601
+ this.lineWidth = 1;
602
+ this.lineJoin = 'miter';
603
+ this.lineCap = 'butt';
604
+ this.miterLimit = Z * 1;
605
+ this.globalAlpha = 1;
606
+ this.font = '10px sans-serif';
607
+ this.textAlign = 'left';
608
+ this.textBaseline = 'alphabetic';
609
+ this.canvas = canvasElement;
610
+
611
+ var cssText = 'width:' + canvasElement.clientWidth + 'px;height:' +
612
+ canvasElement.clientHeight + 'px;overflow:hidden;position:absolute';
613
+ var el = canvasElement.ownerDocument.createElement('div');
614
+ el.style.cssText = cssText;
615
+ canvasElement.appendChild(el);
616
+
617
+ var overlayEl = el.cloneNode(false);
618
+ // Use a non transparent background.
619
+ overlayEl.style.backgroundColor = 'red';
620
+ overlayEl.style.filter = 'alpha(opacity=0)';
621
+ canvasElement.appendChild(overlayEl);
622
+
623
+ this.element_ = el;
624
+ this.arcScaleX_ = 1;
625
+ this.arcScaleY_ = 1;
626
+ this.lineScale_ = 1;
627
+ }
628
+
629
+ var contextPrototype = CanvasRenderingContext2D_.prototype;
630
+ contextPrototype.clearRect = function() {
631
+ if (this.textMeasureEl_) {
632
+ this.textMeasureEl_.removeNode(true);
633
+ this.textMeasureEl_ = null;
634
+ }
635
+ this.element_.innerHTML = '';
636
+ };
637
+
638
+ contextPrototype.beginPath = function() {
639
+ // TODO: Branch current matrix so that save/restore has no effect
640
+ // as per safari docs.
641
+ this.currentPath_ = [];
642
+ };
643
+
644
+ contextPrototype.moveTo = function(aX, aY) {
645
+ var p = getCoords(this, aX, aY);
646
+ this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
647
+ this.currentX_ = p.x;
648
+ this.currentY_ = p.y;
649
+ };
650
+
651
+ contextPrototype.lineTo = function(aX, aY) {
652
+ var p = getCoords(this, aX, aY);
653
+ this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});
654
+
655
+ this.currentX_ = p.x;
656
+ this.currentY_ = p.y;
657
+ };
658
+
659
+ contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
660
+ aCP2x, aCP2y,
661
+ aX, aY) {
662
+ var p = getCoords(this, aX, aY);
663
+ var cp1 = getCoords(this, aCP1x, aCP1y);
664
+ var cp2 = getCoords(this, aCP2x, aCP2y);
665
+ bezierCurveTo(this, cp1, cp2, p);
666
+ };
667
+
668
+ // Helper function that takes the already fixed cordinates.
669
+ function bezierCurveTo(self, cp1, cp2, p) {
670
+ self.currentPath_.push({
671
+ type: 'bezierCurveTo',
672
+ cp1x: cp1.x,
673
+ cp1y: cp1.y,
674
+ cp2x: cp2.x,
675
+ cp2y: cp2.y,
676
+ x: p.x,
677
+ y: p.y
678
+ });
679
+ self.currentX_ = p.x;
680
+ self.currentY_ = p.y;
681
+ }
682
+
683
+ contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
684
+ // the following is lifted almost directly from
685
+ // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
686
+
687
+ var cp = getCoords(this, aCPx, aCPy);
688
+ var p = getCoords(this, aX, aY);
689
+
690
+ var cp1 = {
691
+ x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
692
+ y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
693
+ };
694
+ var cp2 = {
695
+ x: cp1.x + (p.x - this.currentX_) / 3.0,
696
+ y: cp1.y + (p.y - this.currentY_) / 3.0
697
+ };
698
+
699
+ bezierCurveTo(this, cp1, cp2, p);
700
+ };
701
+
702
+ contextPrototype.arc = function(aX, aY, aRadius,
703
+ aStartAngle, aEndAngle, aClockwise) {
704
+ aRadius *= Z;
705
+ var arcType = aClockwise ? 'at' : 'wa';
706
+
707
+ var xStart = aX + mc(aStartAngle) * aRadius - Z2;
708
+ var yStart = aY + ms(aStartAngle) * aRadius - Z2;
709
+
710
+ var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
711
+ var yEnd = aY + ms(aEndAngle) * aRadius - Z2;
712
+
713
+ // IE won't render arches drawn counter clockwise if xStart == xEnd.
714
+ if (xStart == xEnd && !aClockwise) {
715
+ xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
716
+ // that can be represented in binary
717
+ }
718
+
719
+ var p = getCoords(this, aX, aY);
720
+ var pStart = getCoords(this, xStart, yStart);
721
+ var pEnd = getCoords(this, xEnd, yEnd);
722
+
723
+ this.currentPath_.push({type: arcType,
724
+ x: p.x,
725
+ y: p.y,
726
+ radius: aRadius,
727
+ xStart: pStart.x,
728
+ yStart: pStart.y,
729
+ xEnd: pEnd.x,
730
+ yEnd: pEnd.y});
731
+
732
+ };
733
+
734
+ contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
735
+ this.moveTo(aX, aY);
736
+ this.lineTo(aX + aWidth, aY);
737
+ this.lineTo(aX + aWidth, aY + aHeight);
738
+ this.lineTo(aX, aY + aHeight);
739
+ this.closePath();
740
+ };
741
+
742
+ contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
743
+ var oldPath = this.currentPath_;
744
+ this.beginPath();
745
+
746
+ this.moveTo(aX, aY);
747
+ this.lineTo(aX + aWidth, aY);
748
+ this.lineTo(aX + aWidth, aY + aHeight);
749
+ this.lineTo(aX, aY + aHeight);
750
+ this.closePath();
751
+ this.stroke();
752
+
753
+ this.currentPath_ = oldPath;
754
+ };
755
+
756
+ contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
757
+ var oldPath = this.currentPath_;
758
+ this.beginPath();
759
+
760
+ this.moveTo(aX, aY);
761
+ this.lineTo(aX + aWidth, aY);
762
+ this.lineTo(aX + aWidth, aY + aHeight);
763
+ this.lineTo(aX, aY + aHeight);
764
+ this.closePath();
765
+ this.fill();
766
+
767
+ this.currentPath_ = oldPath;
768
+ };
769
+
770
+ contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
771
+ var gradient = new CanvasGradient_('gradient');
772
+ gradient.x0_ = aX0;
773
+ gradient.y0_ = aY0;
774
+ gradient.x1_ = aX1;
775
+ gradient.y1_ = aY1;
776
+ return gradient;
777
+ };
778
+
779
+ contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
780
+ aX1, aY1, aR1) {
781
+ var gradient = new CanvasGradient_('gradientradial');
782
+ gradient.x0_ = aX0;
783
+ gradient.y0_ = aY0;
784
+ gradient.r0_ = aR0;
785
+ gradient.x1_ = aX1;
786
+ gradient.y1_ = aY1;
787
+ gradient.r1_ = aR1;
788
+ return gradient;
789
+ };
790
+
791
+ contextPrototype.drawImage = function(image, var_args) {
792
+ var dx, dy, dw, dh, sx, sy, sw, sh;
793
+
794
+ // to find the original width we overide the width and height
795
+ var oldRuntimeWidth = image.runtimeStyle.width;
796
+ var oldRuntimeHeight = image.runtimeStyle.height;
797
+ image.runtimeStyle.width = 'auto';
798
+ image.runtimeStyle.height = 'auto';
799
+
800
+ // get the original size
801
+ var w = image.width;
802
+ var h = image.height;
803
+
804
+ // and remove overides
805
+ image.runtimeStyle.width = oldRuntimeWidth;
806
+ image.runtimeStyle.height = oldRuntimeHeight;
807
+
808
+ if (arguments.length == 3) {
809
+ dx = arguments[1];
810
+ dy = arguments[2];
811
+ sx = sy = 0;
812
+ sw = dw = w;
813
+ sh = dh = h;
814
+ } else if (arguments.length == 5) {
815
+ dx = arguments[1];
816
+ dy = arguments[2];
817
+ dw = arguments[3];
818
+ dh = arguments[4];
819
+ sx = sy = 0;
820
+ sw = w;
821
+ sh = h;
822
+ } else if (arguments.length == 9) {
823
+ sx = arguments[1];
824
+ sy = arguments[2];
825
+ sw = arguments[3];
826
+ sh = arguments[4];
827
+ dx = arguments[5];
828
+ dy = arguments[6];
829
+ dw = arguments[7];
830
+ dh = arguments[8];
831
+ } else {
832
+ throw Error('Invalid number of arguments');
833
+ }
834
+
835
+ var d = getCoords(this, dx, dy);
836
+
837
+ var w2 = sw / 2;
838
+ var h2 = sh / 2;
839
+
840
+ var vmlStr = [];
841
+
842
+ var W = 10;
843
+ var H = 10;
844
+
845
+ // For some reason that I've now forgotten, using divs didn't work
846
+ vmlStr.push(' <g_vml_:group',
847
+ ' coordsize="', Z * W, ',', Z * H, '"',
848
+ ' coordorigin="0,0"' ,
849
+ ' style="width:', W, 'px;height:', H, 'px;position:absolute;');
850
+
851
+ // If filters are necessary (rotation exists), create them
852
+ // filters are bog-slow, so only create them if abbsolutely necessary
853
+ // The following check doesn't account for skews (which don't exist
854
+ // in the canvas spec (yet) anyway.
855
+
856
+ if (this.m_[0][0] != 1 || this.m_[0][1] ||
857
+ this.m_[1][1] != 1 || this.m_[1][0]) {
858
+ var filter = [];
859
+
860
+ // Note the 12/21 reversal
861
+ filter.push('M11=', this.m_[0][0], ',',
862
+ 'M12=', this.m_[1][0], ',',
863
+ 'M21=', this.m_[0][1], ',',
864
+ 'M22=', this.m_[1][1], ',',
865
+ 'Dx=', mr(d.x / Z), ',',
866
+ 'Dy=', mr(d.y / Z), '');
867
+
868
+ // Bounding box calculation (need to minimize displayed area so that
869
+ // filters don't waste time on unused pixels.
870
+ var max = d;
871
+ var c2 = getCoords(this, dx + dw, dy);
872
+ var c3 = getCoords(this, dx, dy + dh);
873
+ var c4 = getCoords(this, dx + dw, dy + dh);
874
+
875
+ max.x = m.max(max.x, c2.x, c3.x, c4.x);
876
+ max.y = m.max(max.y, c2.y, c3.y, c4.y);
877
+
878
+ vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
879
+ 'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
880
+ filter.join(''), ", sizingmethod='clip');");
881
+
882
+ } else {
883
+ vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
884
+ }
885
+
886
+ vmlStr.push(' ">' ,
887
+ '<g_vml_:image src="', image.src, '"',
888
+ ' style="width:', Z * dw, 'px;',
889
+ ' height:', Z * dh, 'px"',
890
+ ' cropleft="', sx / w, '"',
891
+ ' croptop="', sy / h, '"',
892
+ ' cropright="', (w - sx - sw) / w, '"',
893
+ ' cropbottom="', (h - sy - sh) / h, '"',
894
+ ' />',
895
+ '</g_vml_:group>');
896
+
897
+ this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join(''));
898
+ };
899
+
900
+ contextPrototype.stroke = function(aFill) {
901
+ var lineStr = [];
902
+ var lineOpen = false;
903
+
904
+ var W = 10;
905
+ var H = 10;
906
+
907
+ lineStr.push('<g_vml_:shape',
908
+ ' filled="', !!aFill, '"',
909
+ ' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
910
+ ' coordorigin="0,0"',
911
+ ' coordsize="', Z * W, ',', Z * H, '"',
912
+ ' stroked="', !aFill, '"',
913
+ ' path="');
914
+
915
+ var newSeq = false;
916
+ var min = {x: null, y: null};
917
+ var max = {x: null, y: null};
918
+
919
+ for (var i = 0; i < this.currentPath_.length; i++) {
920
+ var p = this.currentPath_[i];
921
+ var c;
922
+
923
+ switch (p.type) {
924
+ case 'moveTo':
925
+ c = p;
926
+ lineStr.push(' m ', mr(p.x), ',', mr(p.y));
927
+ break;
928
+ case 'lineTo':
929
+ lineStr.push(' l ', mr(p.x), ',', mr(p.y));
930
+ break;
931
+ case 'close':
932
+ lineStr.push(' x ');
933
+ p = null;
934
+ break;
935
+ case 'bezierCurveTo':
936
+ lineStr.push(' c ',
937
+ mr(p.cp1x), ',', mr(p.cp1y), ',',
938
+ mr(p.cp2x), ',', mr(p.cp2y), ',',
939
+ mr(p.x), ',', mr(p.y));
940
+ break;
941
+ case 'at':
942
+ case 'wa':
943
+ lineStr.push(' ', p.type, ' ',
944
+ mr(p.x - this.arcScaleX_ * p.radius), ',',
945
+ mr(p.y - this.arcScaleY_ * p.radius), ' ',
946
+ mr(p.x + this.arcScaleX_ * p.radius), ',',
947
+ mr(p.y + this.arcScaleY_ * p.radius), ' ',
948
+ mr(p.xStart), ',', mr(p.yStart), ' ',
949
+ mr(p.xEnd), ',', mr(p.yEnd));
950
+ break;
951
+ }
952
+
953
+
954
+ // TODO: Following is broken for curves due to
955
+ // move to proper paths.
956
+
957
+ // Figure out dimensions so we can do gradient fills
958
+ // properly
959
+ if (p) {
960
+ if (min.x == null || p.x < min.x) {
961
+ min.x = p.x;
962
+ }
963
+ if (max.x == null || p.x > max.x) {
964
+ max.x = p.x;
965
+ }
966
+ if (min.y == null || p.y < min.y) {
967
+ min.y = p.y;
968
+ }
969
+ if (max.y == null || p.y > max.y) {
970
+ max.y = p.y;
971
+ }
972
+ }
973
+ }
974
+ lineStr.push(' ">');
975
+
976
+ if (!aFill) {
977
+ appendStroke(this, lineStr);
978
+ } else {
979
+ appendFill(this, lineStr, min, max);
980
+ }
981
+
982
+ lineStr.push('</g_vml_:shape>');
983
+
984
+ this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
985
+ };
986
+
987
+ function appendStroke(ctx, lineStr) {
988
+ var a = processStyle(ctx.strokeStyle);
989
+ var color = a.color;
990
+ var opacity = a.alpha * ctx.globalAlpha;
991
+ var lineWidth = ctx.lineScale_ * ctx.lineWidth;
992
+
993
+ // VML cannot correctly render a line if the width is less than 1px.
994
+ // In that case, we dilute the color to make the line look thinner.
995
+ if (lineWidth < 1) {
996
+ opacity *= lineWidth;
997
+ }
998
+
999
+ lineStr.push(
1000
+ '<g_vml_:stroke',
1001
+ ' opacity="', opacity, '"',
1002
+ ' joinstyle="', ctx.lineJoin, '"',
1003
+ ' miterlimit="', ctx.miterLimit, '"',
1004
+ ' endcap="', processLineCap(ctx.lineCap), '"',
1005
+ ' weight="', lineWidth, 'px"',
1006
+ ' color="', color, '" />'
1007
+ );
1008
+ }
1009
+
1010
+ function appendFill(ctx, lineStr, min, max) {
1011
+ var fillStyle = ctx.fillStyle;
1012
+ var arcScaleX = ctx.arcScaleX_;
1013
+ var arcScaleY = ctx.arcScaleY_;
1014
+ var width = max.x - min.x;
1015
+ var height = max.y - min.y;
1016
+ if (fillStyle instanceof CanvasGradient_) {
1017
+ // TODO: Gradients transformed with the transformation matrix.
1018
+ var angle = 0;
1019
+ var focus = {x: 0, y: 0};
1020
+
1021
+ // additional offset
1022
+ var shift = 0;
1023
+ // scale factor for offset
1024
+ var expansion = 1;
1025
+
1026
+ if (fillStyle.type_ == 'gradient') {
1027
+ var x0 = fillStyle.x0_ / arcScaleX;
1028
+ var y0 = fillStyle.y0_ / arcScaleY;
1029
+ var x1 = fillStyle.x1_ / arcScaleX;
1030
+ var y1 = fillStyle.y1_ / arcScaleY;
1031
+ var p0 = getCoords(ctx, x0, y0);
1032
+ var p1 = getCoords(ctx, x1, y1);
1033
+ var dx = p1.x - p0.x;
1034
+ var dy = p1.y - p0.y;
1035
+ angle = Math.atan2(dx, dy) * 180 / Math.PI;
1036
+
1037
+ // The angle should be a non-negative number.
1038
+ if (angle < 0) {
1039
+ angle += 360;
1040
+ }
1041
+
1042
+ // Very small angles produce an unexpected result because they are
1043
+ // converted to a scientific notation string.
1044
+ if (angle < 1e-6) {
1045
+ angle = 0;
1046
+ }
1047
+ } else {
1048
+ var p0 = getCoords(ctx, fillStyle.x0_, fillStyle.y0_);
1049
+ focus = {
1050
+ x: (p0.x - min.x) / width,
1051
+ y: (p0.y - min.y) / height
1052
+ };
1053
+
1054
+ width /= arcScaleX * Z;
1055
+ height /= arcScaleY * Z;
1056
+ var dimension = m.max(width, height);
1057
+ shift = 2 * fillStyle.r0_ / dimension;
1058
+ expansion = 2 * fillStyle.r1_ / dimension - shift;
1059
+ }
1060
+
1061
+ // We need to sort the color stops in ascending order by offset,
1062
+ // otherwise IE won't interpret it correctly.
1063
+ var stops = fillStyle.colors_;
1064
+ stops.sort(function(cs1, cs2) {
1065
+ return cs1.offset - cs2.offset;
1066
+ });
1067
+
1068
+ var length = stops.length;
1069
+ var color1 = stops[0].color;
1070
+ var color2 = stops[length - 1].color;
1071
+ var opacity1 = stops[0].alpha * ctx.globalAlpha;
1072
+ var opacity2 = stops[length - 1].alpha * ctx.globalAlpha;
1073
+
1074
+ var colors = [];
1075
+ for (var i = 0; i < length; i++) {
1076
+ var stop = stops[i];
1077
+ colors.push(stop.offset * expansion + shift + ' ' + stop.color);
1078
+ }
1079
+
1080
+ // When colors attribute is used, the meanings of opacity and o:opacity2
1081
+ // are reversed.
1082
+ lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
1083
+ ' method="none" focus="100%"',
1084
+ ' color="', color1, '"',
1085
+ ' color2="', color2, '"',
1086
+ ' colors="', colors.join(','), '"',
1087
+ ' opacity="', opacity2, '"',
1088
+ ' g_o_:opacity2="', opacity1, '"',
1089
+ ' angle="', angle, '"',
1090
+ ' focusposition="', focus.x, ',', focus.y, '" />');
1091
+ } else if (fillStyle instanceof CanvasPattern_) {
1092
+ if (width && height) {
1093
+ var deltaLeft = -min.x;
1094
+ var deltaTop = -min.y;
1095
+ lineStr.push('<g_vml_:fill',
1096
+ ' position="',
1097
+ deltaLeft / width * arcScaleX * arcScaleX, ',',
1098
+ deltaTop / height * arcScaleY * arcScaleY, '"',
1099
+ ' type="tile"',
1100
+ // TODO: Figure out the correct size to fit the scale.
1101
+ //' size="', w, 'px ', h, 'px"',
1102
+ ' src="', fillStyle.src_, '" />');
1103
+ }
1104
+ } else {
1105
+ var a = processStyle(ctx.fillStyle);
1106
+ var color = a.color;
1107
+ var opacity = a.alpha * ctx.globalAlpha;
1108
+ lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
1109
+ '" />');
1110
+ }
1111
+ }
1112
+
1113
+ contextPrototype.fill = function() {
1114
+ this.stroke(true);
1115
+ };
1116
+
1117
+ contextPrototype.closePath = function() {
1118
+ this.currentPath_.push({type: 'close'});
1119
+ };
1120
+
1121
+ function getCoords(ctx, aX, aY) {
1122
+ var m = ctx.m_;
1123
+ return {
1124
+ x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
1125
+ y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
1126
+ };
1127
+ };
1128
+
1129
+ contextPrototype.save = function() {
1130
+ var o = {};
1131
+ copyState(this, o);
1132
+ this.aStack_.push(o);
1133
+ this.mStack_.push(this.m_);
1134
+ this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
1135
+ };
1136
+
1137
+ contextPrototype.restore = function() {
1138
+ if (this.aStack_.length) {
1139
+ copyState(this.aStack_.pop(), this);
1140
+ this.m_ = this.mStack_.pop();
1141
+ }
1142
+ };
1143
+
1144
+ function matrixIsFinite(m) {
1145
+ return isFinite(m[0][0]) && isFinite(m[0][1]) &&
1146
+ isFinite(m[1][0]) && isFinite(m[1][1]) &&
1147
+ isFinite(m[2][0]) && isFinite(m[2][1]);
1148
+ }
1149
+
1150
+ function setM(ctx, m, updateLineScale) {
1151
+ if (!matrixIsFinite(m)) {
1152
+ return;
1153
+ }
1154
+ ctx.m_ = m;
1155
+
1156
+ if (updateLineScale) {
1157
+ // Get the line scale.
1158
+ // Determinant of this.m_ means how much the area is enlarged by the
1159
+ // transformation. So its square root can be used as a scale factor
1160
+ // for width.
1161
+ var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
1162
+ ctx.lineScale_ = sqrt(abs(det));
1163
+ }
1164
+ }
1165
+
1166
+ contextPrototype.translate = function(aX, aY) {
1167
+ var m1 = [
1168
+ [1, 0, 0],
1169
+ [0, 1, 0],
1170
+ [aX, aY, 1]
1171
+ ];
1172
+
1173
+ setM(this, matrixMultiply(m1, this.m_), false);
1174
+ };
1175
+
1176
+ contextPrototype.rotate = function(aRot) {
1177
+ var c = mc(aRot);
1178
+ var s = ms(aRot);
1179
+
1180
+ var m1 = [
1181
+ [c, s, 0],
1182
+ [-s, c, 0],
1183
+ [0, 0, 1]
1184
+ ];
1185
+
1186
+ setM(this, matrixMultiply(m1, this.m_), false);
1187
+ };
1188
+
1189
+ contextPrototype.scale = function(aX, aY) {
1190
+ this.arcScaleX_ *= aX;
1191
+ this.arcScaleY_ *= aY;
1192
+ var m1 = [
1193
+ [aX, 0, 0],
1194
+ [0, aY, 0],
1195
+ [0, 0, 1]
1196
+ ];
1197
+
1198
+ setM(this, matrixMultiply(m1, this.m_), true);
1199
+ };
1200
+
1201
+ contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
1202
+ var m1 = [
1203
+ [m11, m12, 0],
1204
+ [m21, m22, 0],
1205
+ [dx, dy, 1]
1206
+ ];
1207
+
1208
+ setM(this, matrixMultiply(m1, this.m_), true);
1209
+ };
1210
+
1211
+ contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
1212
+ var m = [
1213
+ [m11, m12, 0],
1214
+ [m21, m22, 0],
1215
+ [dx, dy, 1]
1216
+ ];
1217
+
1218
+ setM(this, m, true);
1219
+ };
1220
+
1221
+ /**
1222
+ * The text drawing function.
1223
+ * The maxWidth argument isn't taken in account, since no browser supports
1224
+ * it yet.
1225
+ */
1226
+ contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) {
1227
+ var m = this.m_,
1228
+ delta = 1000,
1229
+ left = 0,
1230
+ right = delta,
1231
+ offset = {x: 0, y: 0},
1232
+ lineStr = [];
1233
+
1234
+ var fontStyle = getComputedStyle(processFontStyle(this.font), this.element_);
1235
+
1236
+ var fontStyleString = buildStyle(fontStyle);
1237
+
1238
+ var elementStyle = this.element_.currentStyle;
1239
+ var textAlign = this.textAlign.toLowerCase();
1240
+ switch (textAlign) {
1241
+ case 'left':
1242
+ case 'center':
1243
+ case 'right':
1244
+ break;
1245
+ case 'end':
1246
+ textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left';
1247
+ break;
1248
+ case 'start':
1249
+ textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left';
1250
+ break;
1251
+ default:
1252
+ textAlign = 'left';
1253
+ }
1254
+
1255
+ // 1.75 is an arbitrary number, as there is no info about the text baseline
1256
+ switch (this.textBaseline) {
1257
+ case 'hanging':
1258
+ case 'top':
1259
+ offset.y = fontStyle.size / 1.75;
1260
+ break;
1261
+ case 'middle':
1262
+ break;
1263
+ default:
1264
+ case null:
1265
+ case 'alphabetic':
1266
+ case 'ideographic':
1267
+ case 'bottom':
1268
+ offset.y = -fontStyle.size / 2.25;
1269
+ break;
1270
+ }
1271
+
1272
+ switch(textAlign) {
1273
+ case 'right':
1274
+ left = delta;
1275
+ right = 0.05;
1276
+ break;
1277
+ case 'center':
1278
+ left = right = delta / 2;
1279
+ break;
1280
+ }
1281
+
1282
+ var d = getCoords(this, x + offset.x, y + offset.y);
1283
+
1284
+ lineStr.push('<g_vml_:line from="', -left ,' 0" to="', right ,' 0.05" ',
1285
+ ' coordsize="100 100" coordorigin="0 0"',
1286
+ ' filled="', !stroke, '" stroked="', !!stroke,
1287
+ '" style="position:absolute;width:1px;height:1px;">');
1288
+
1289
+ if (stroke) {
1290
+ appendStroke(this, lineStr);
1291
+ } else {
1292
+ // TODO: Fix the min and max params.
1293
+ appendFill(this, lineStr, {x: -left, y: 0},
1294
+ {x: right, y: fontStyle.size});
1295
+ }
1296
+
1297
+ var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' +
1298
+ m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0';
1299
+
1300
+ var skewOffset = mr(d.x / Z + 1 - m[0][0]) + ',' + mr(d.y / Z - 2 * m[1][0]);
1301
+
1302
+
1303
+ lineStr.push('<g_vml_:skew on="t" matrix="', skewM ,'" ',
1304
+ ' offset="', skewOffset, '" origin="', left ,' 0" />',
1305
+ '<g_vml_:path textpathok="true" />',
1306
+ '<g_vml_:textpath on="true" string="',
1307
+ encodeHtmlAttribute(text),
1308
+ '" style="v-text-align:', textAlign,
1309
+ ';font:', encodeHtmlAttribute(fontStyleString),
1310
+ '" /></g_vml_:line>');
1311
+
1312
+ this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
1313
+ };
1314
+
1315
+ contextPrototype.fillText = function(text, x, y, maxWidth) {
1316
+ this.drawText_(text, x, y, maxWidth, false);
1317
+ };
1318
+
1319
+ contextPrototype.strokeText = function(text, x, y, maxWidth) {
1320
+ this.drawText_(text, x, y, maxWidth, true);
1321
+ };
1322
+
1323
+ contextPrototype.measureText = function(text) {
1324
+ if (!this.textMeasureEl_) {
1325
+ var s = '<span style="position:absolute;' +
1326
+ 'top:-20000px;left:0;padding:0;margin:0;border:none;' +
1327
+ 'white-space:pre;"></span>';
1328
+ this.element_.insertAdjacentHTML('beforeEnd', s);
1329
+ this.textMeasureEl_ = this.element_.lastChild;
1330
+ }
1331
+ var doc = this.element_.ownerDocument;
1332
+ this.textMeasureEl_.innerHTML = '';
1333
+ this.textMeasureEl_.style.font = this.font;
1334
+ // Don't use innerHTML or innerText because they allow markup/whitespace.
1335
+ this.textMeasureEl_.appendChild(doc.createTextNode(text));
1336
+ return {width: this.textMeasureEl_.offsetWidth};
1337
+ };
1338
+
1339
+ /******** STUBS ********/
1340
+ contextPrototype.clip = function() {
1341
+ // TODO: Implement
1342
+ };
1343
+
1344
+ contextPrototype.arcTo = function() {
1345
+ // TODO: Implement
1346
+ };
1347
+
1348
+ contextPrototype.createPattern = function(image, repetition) {
1349
+ return new CanvasPattern_(image, repetition);
1350
+ };
1351
+
1352
+ // Gradient / Pattern Stubs
1353
+ function CanvasGradient_(aType) {
1354
+ this.type_ = aType;
1355
+ this.x0_ = 0;
1356
+ this.y0_ = 0;
1357
+ this.r0_ = 0;
1358
+ this.x1_ = 0;
1359
+ this.y1_ = 0;
1360
+ this.r1_ = 0;
1361
+ this.colors_ = [];
1362
+ }
1363
+
1364
+ CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
1365
+ aColor = processStyle(aColor);
1366
+ this.colors_.push({offset: aOffset,
1367
+ color: aColor.color,
1368
+ alpha: aColor.alpha});
1369
+ };
1370
+
1371
+ function CanvasPattern_(image, repetition) {
1372
+ assertImageIsValid(image);
1373
+ switch (repetition) {
1374
+ case 'repeat':
1375
+ case null:
1376
+ case '':
1377
+ this.repetition_ = 'repeat';
1378
+ break;
1379
+ case 'repeat-x':
1380
+ case 'repeat-y':
1381
+ case 'no-repeat':
1382
+ this.repetition_ = repetition;
1383
+ break;
1384
+ default:
1385
+ throwException('SYNTAX_ERR');
1386
+ }
1387
+
1388
+ this.src_ = image.src;
1389
+ this.width_ = image.width;
1390
+ this.height_ = image.height;
1391
+ }
1392
+
1393
+ function throwException(s) {
1394
+ throw new DOMException_(s);
1395
+ }
1396
+
1397
+ function assertImageIsValid(img) {
1398
+ if (!img || img.nodeType != 1 || img.tagName != 'IMG') {
1399
+ throwException('TYPE_MISMATCH_ERR');
1400
+ }
1401
+ if (img.readyState != 'complete') {
1402
+ throwException('INVALID_STATE_ERR');
1403
+ }
1404
+ }
1405
+
1406
+ function DOMException_(s) {
1407
+ this.code = this[s];
1408
+ this.message = s +': DOM Exception ' + this.code;
1409
+ }
1410
+ var p = DOMException_.prototype = new Error;
1411
+ p.INDEX_SIZE_ERR = 1;
1412
+ p.DOMSTRING_SIZE_ERR = 2;
1413
+ p.HIERARCHY_REQUEST_ERR = 3;
1414
+ p.WRONG_DOCUMENT_ERR = 4;
1415
+ p.INVALID_CHARACTER_ERR = 5;
1416
+ p.NO_DATA_ALLOWED_ERR = 6;
1417
+ p.NO_MODIFICATION_ALLOWED_ERR = 7;
1418
+ p.NOT_FOUND_ERR = 8;
1419
+ p.NOT_SUPPORTED_ERR = 9;
1420
+ p.INUSE_ATTRIBUTE_ERR = 10;
1421
+ p.INVALID_STATE_ERR = 11;
1422
+ p.SYNTAX_ERR = 12;
1423
+ p.INVALID_MODIFICATION_ERR = 13;
1424
+ p.NAMESPACE_ERR = 14;
1425
+ p.INVALID_ACCESS_ERR = 15;
1426
+ p.VALIDATION_ERR = 16;
1427
+ p.TYPE_MISMATCH_ERR = 17;
1428
+
1429
+ // set up externs
1430
+ G_vmlCanvasManager = G_vmlCanvasManager_;
1431
+ CanvasRenderingContext2D = CanvasRenderingContext2D_;
1432
+ CanvasGradient = CanvasGradient_;
1433
+ CanvasPattern = CanvasPattern_;
1434
+ DOMException = DOMException_;
1435
+ G_vmlCanvasManager._version = 888;
1436
+ })();
1437
+
1438
+ } // if