// Copyright 2006 Google Inc. |
|
// |
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
// you may not use this file except in compliance with the License. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
|
|
// Known Issues: |
|
// |
|
// * Patterns only support repeat. |
|
// * Radial gradient are not implemented. The VML version of these look very |
|
// different from the canvas one. |
|
// * Clipping paths are not implemented. |
|
// * Coordsize. The width and height attribute have higher priority than the |
|
// width and height style values which isn't correct. |
|
// * Painting mode isn't implemented. |
|
// * Canvas width/height should is using content-box by default. IE in |
|
// Quirks mode will draw the canvas using border-box. Either change your |
|
// doctype to HTML5 |
|
// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype) |
|
// or use Box Sizing Behavior from WebFX |
|
// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html) |
|
// * Non uniform scaling does not correctly scale strokes. |
|
// * Filling very large shapes (above 5000 points) is buggy. |
|
// * Optimize. There is always room for speed improvements. |
|
|
|
// Only add this code if we do not already have a canvas implementation |
|
if (!document.createElement('canvas').getContext) { |
|
|
|
(function() { |
|
|
|
// alias some functions to make (compiled) code shorter |
|
var m = Math; |
|
var mr = m.round; |
|
var ms = m.sin; |
|
var mc = m.cos; |
|
var abs = m.abs; |
|
var sqrt = m.sqrt; |
|
|
|
// this is used for sub pixel precision |
|
var Z = 10; |
|
var Z2 = Z / 2; |
|
|
|
/** |
|
* This funtion is assigned to the <canvas> elements as element.getContext(). |
|
* @this {HTMLElement} |
|
* @return {CanvasRenderingContext2D_} |
|
*/ |
|
function getContext() { |
|
return this.context_ || |
|
(this.context_ = new CanvasRenderingContext2D_(this)); |
|
} |
|
|
|
var slice = Array.prototype.slice; |
|
|
|
/** |
|
* Binds a function to an object. The returned function will always use the |
|
* passed in {@code obj} as {@code this}. |
|
* |
|
* Example: |
|
* |
|
* g = bind(f, obj, a, b) |
|
* g(c, d) // will do f.call(obj, a, b, c, d) |
|
* |
|
* @param {Function} f The function to bind the object to |
|
* @param {Object} obj The object that should act as this when the function |
|
* is called |
|
* @param {*} var_args Rest arguments that will be used as the initial |
|
* arguments when the function is called |
|
* @return {Function} A new function that has bound this |
|
*/ |
|
function bind(f, obj, var_args) { |
|
var a = slice.call(arguments, 2); |
|
return function() { |
|
return f.apply(obj, a.concat(slice.call(arguments))); |
|
}; |
|
} |
|
|
|
function encodeHtmlAttribute(s) { |
|
return String(s).replace(/&/g, '&').replace(/"/g, '"'); |
|
} |
|
|
|
function addNamespacesAndStylesheet(doc) { |
|
// create xmlns |
|
if (!doc.namespaces['g_vml_']) { |
|
doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml', |
|
'#default#VML'); |
|
|
|
} |
|
if (!doc.namespaces['g_o_']) { |
|
doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office', |
|
'#default#VML'); |
|
} |
|
|
|
// Setup default CSS. Only add one style sheet per document |
|
if (!doc.styleSheets['ex_canvas_']) { |
|
var ss = doc.createStyleSheet(); |
|
ss.owningElement.id = 'ex_canvas_'; |
|
ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + |
|
// default size is 300x150 in Gecko and Opera |
|
'text-align:left;width:300px;height:150px}'; |
|
} |
|
} |
|
|
|
// Add namespaces and stylesheet at startup. |
|
addNamespacesAndStylesheet(document); |
|
|
|
var G_vmlCanvasManager_ = { |
|
init: function(opt_doc) { |
|
if (/MSIE/.test(navigator.userAgent) && !window.opera) { |
|
var doc = opt_doc || document; |
|
// Create a dummy element so that IE will allow canvas elements to be |
|
// recognized. |
|
doc.createElement('canvas'); |
|
doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); |
|
} |
|
}, |
|
|
|
init_: function(doc) { |
|
// find all canvas elements |
|
var els = doc.getElementsByTagName('canvas'); |
|
for (var i = 0; i < els.length; i++) { |
|
this.initElement(els[i]); |
|
} |
|
}, |
|
|
|
/** |
|
* Public initializes a canvas element so that it can be used as canvas |
|
* element from now on. This is called automatically before the page is |
|
* loaded but if you are creating elements using createElement you need to |
|
* make sure this is called on the element. |
|
* @param {HTMLElement} el The canvas element to initialize. |
|
* @return {HTMLElement} the element that was created. |
|
*/ |
|
initElement: function(el) { |
|
if (!el.getContext) { |
|
el.getContext = getContext; |
|
|
|
// Add namespaces and stylesheet to document of the element. |
|
addNamespacesAndStylesheet(el.ownerDocument); |
|
|
|
// Remove fallback content. There is no way to hide text nodes so we |
|
// just remove all childNodes. We could hide all elements and remove |
|
// text nodes but who really cares about the fallback content. |
|
el.innerHTML = ''; |
|
|
|
// do not use inline function because that will leak memory |
|
el.attachEvent('onpropertychange', onPropertyChange); |
|
el.attachEvent('onresize', onResize); |
|
|
|
var attrs = el.attributes; |
|
if (attrs.width && attrs.width.specified) { |
|
// TODO: use runtimeStyle and coordsize |
|
// el.getContext().setWidth_(attrs.width.nodeValue); |
|
el.style.width = attrs.width.nodeValue + 'px'; |
|
} else { |
|
el.width = el.clientWidth; |
|
} |
|
if (attrs.height && attrs.height.specified) { |
|
// TODO: use runtimeStyle and coordsize |
|
// el.getContext().setHeight_(attrs.height.nodeValue); |
|
el.style.height = attrs.height.nodeValue + 'px'; |
|
} else { |
|
el.height = el.clientHeight; |
|
} |
|
//el.getContext().setCoordsize_() |
|
} |
|
return el; |
|
} |
|
}; |
|
|
|
function onPropertyChange(e) { |
|
var el = e.srcElement; |
|
|
|
switch (e.propertyName) { |
|
case 'width': |
|
el.getContext().clearRect(); |
|
el.style.width = el.attributes.width.nodeValue + 'px'; |
|
// In IE8 this does not trigger onresize. |
|
el.firstChild.style.width = el.clientWidth + 'px'; |
|
break; |
|
case 'height': |
|
el.getContext().clearRect(); |
|
el.style.height = el.attributes.height.nodeValue + 'px'; |
|
el.firstChild.style.height = el.clientHeight + 'px'; |
|
break; |
|
} |
|
} |
|
|
|
function onResize(e) { |
|
var el = e.srcElement; |
|
if (el.firstChild) { |
|
el.firstChild.style.width = el.clientWidth + 'px'; |
|
el.firstChild.style.height = el.clientHeight + 'px'; |
|
} |
|
} |
|
|
|
G_vmlCanvasManager_.init(); |
|
|
|
// precompute "00" to "FF" |
|
var decToHex = []; |
|
for (var i = 0; i < 16; i++) { |
|
for (var j = 0; j < 16; j++) { |
|
decToHex[i * 16 + j] = i.toString(16) + j.toString(16); |
|
} |
|
} |
|
|
|
function createMatrixIdentity() { |
|
return [ |
|
[1, 0, 0], |
|
[0, 1, 0], |
|
[0, 0, 1] |
|
]; |
|
} |
|
|
|
function matrixMultiply(m1, m2) { |
|
var result = createMatrixIdentity(); |
|
|
|
for (var x = 0; x < 3; x++) { |
|
for (var y = 0; y < 3; y++) { |
|
var sum = 0; |
|
|
|
for (var z = 0; z < 3; z++) { |
|
sum += m1[x][z] * m2[z][y]; |
|
} |
|
|
|
result[x][y] = sum; |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
function copyState(o1, o2) { |
|
o2.fillStyle = o1.fillStyle; |
|
o2.lineCap = o1.lineCap; |
|
o2.lineJoin = o1.lineJoin; |
|
o2.lineWidth = o1.lineWidth; |
|
o2.miterLimit = o1.miterLimit; |
|
o2.shadowBlur = o1.shadowBlur; |
|
o2.shadowColor = o1.shadowColor; |
|
o2.shadowOffsetX = o1.shadowOffsetX; |
|
o2.shadowOffsetY = o1.shadowOffsetY; |
|
o2.strokeStyle = o1.strokeStyle; |
|
o2.globalAlpha = o1.globalAlpha; |
|
o2.font = o1.font; |
|
o2.textAlign = o1.textAlign; |
|
o2.textBaseline = o1.textBaseline; |
|
o2.arcScaleX_ = o1.arcScaleX_; |
|
o2.arcScaleY_ = o1.arcScaleY_; |
|
o2.lineScale_ = o1.lineScale_; |
|
} |
|
|
|
var colorData = { |
|
aliceblue: '#F0F8FF', |
|
antiquewhite: '#FAEBD7', |
|
aquamarine: '#7FFFD4', |
|
azure: '#F0FFFF', |
|
beige: '#F5F5DC', |
|
bisque: '#FFE4C4', |
|
black: '#000000', |
|
blanchedalmond: '#FFEBCD', |
|
blueviolet: '#8A2BE2', |
|
brown: '#A52A2A', |
|
burlywood: '#DEB887', |
|
cadetblue: '#5F9EA0', |
|
chartreuse: '#7FFF00', |
|
chocolate: '#D2691E', |
|
coral: '#FF7F50', |
|
cornflowerblue: '#6495ED', |
|
cornsilk: '#FFF8DC', |
|
crimson: '#DC143C', |
|
cyan: '#00FFFF', |
|
darkblue: '#00008B', |
|
darkcyan: '#008B8B', |
|
darkgoldenrod: '#B8860B', |
|
darkgray: '#A9A9A9', |
|
darkgreen: '#006400', |
|
darkgrey: '#A9A9A9', |
|
darkkhaki: '#BDB76B', |
|
darkmagenta: '#8B008B', |
|
darkolivegreen: '#556B2F', |
|
darkorange: '#FF8C00', |
|
darkorchid: '#9932CC', |
|
darkred: '#8B0000', |
|
darksalmon: '#E9967A', |
|
darkseagreen: '#8FBC8F', |
|
darkslateblue: '#483D8B', |
|
darkslategray: '#2F4F4F', |
|
darkslategrey: '#2F4F4F', |
|
darkturquoise: '#00CED1', |
|
darkviolet: '#9400D3', |
|
deeppink: '#FF1493', |
|
deepskyblue: '#00BFFF', |
|
dimgray: '#696969', |
|
dimgrey: '#696969', |
|
dodgerblue: '#1E90FF', |
|
firebrick: '#B22222', |
|
floralwhite: '#FFFAF0', |
|
forestgreen: '#228B22', |
|
gainsboro: '#DCDCDC', |
|
ghostwhite: '#F8F8FF', |
|
gold: '#FFD700', |
|
goldenrod: '#DAA520', |
|
grey: '#808080', |
|
greenyellow: '#ADFF2F', |
|
honeydew: '#F0FFF0', |
|
hotpink: '#FF69B4', |
|
indianred: '#CD5C5C', |
|
indigo: '#4B0082', |
|
ivory: '#FFFFF0', |
|
khaki: '#F0E68C', |
|
lavender: '#E6E6FA', |
|
lavenderblush: '#FFF0F5', |
|
lawngreen: '#7CFC00', |
|
lemonchiffon: '#FFFACD', |
|
lightblue: '#ADD8E6', |
|
lightcoral: '#F08080', |
|
lightcyan: '#E0FFFF', |
|
lightgoldenrodyellow: '#FAFAD2', |
|
lightgreen: '#90EE90', |
|
lightgrey: '#D3D3D3', |
|
lightpink: '#FFB6C1', |
|
lightsalmon: '#FFA07A', |
|
lightseagreen: '#20B2AA', |
|
lightskyblue: '#87CEFA', |
|
lightslategray: '#778899', |
|
lightslategrey: '#778899', |
|
lightsteelblue: '#B0C4DE', |
|
lightyellow: '#FFFFE0', |
|
limegreen: '#32CD32', |
|
linen: '#FAF0E6', |
|
magenta: '#FF00FF', |
|
mediumaquamarine: '#66CDAA', |
|
mediumblue: '#0000CD', |
|
mediumorchid: '#BA55D3', |
|
mediumpurple: '#9370DB', |
|
mediumseagreen: '#3CB371', |
|
mediumslateblue: '#7B68EE', |
|
mediumspringgreen: '#00FA9A', |
|
mediumturquoise: '#48D1CC', |
|
mediumvioletred: '#C71585', |
|
midnightblue: '#191970', |
|
mintcream: '#F5FFFA', |
|
mistyrose: '#FFE4E1', |
|
moccasin: '#FFE4B5', |
|
navajowhite: '#FFDEAD', |
|
oldlace: '#FDF5E6', |
|
olivedrab: '#6B8E23', |
|
orange: '#FFA500', |
|
orangered: '#FF4500', |
|
orchid: '#DA70D6', |
|
palegoldenrod: '#EEE8AA', |
|
palegreen: '#98FB98', |
|
paleturquoise: '#AFEEEE', |
|
palevioletred: '#DB7093', |
|
papayawhip: '#FFEFD5', |
|
peachpuff: '#FFDAB9', |
|
peru: '#CD853F', |
|
pink: '#FFC0CB', |
|
plum: '#DDA0DD', |
|
powderblue: '#B0E0E6', |
|
rosybrown: '#BC8F8F', |
<