//if (!document.createElement('canvas').getContext) { (function() { var m = Math; var y = m.round; var z = m.sin; var A = m.cos; var Z = 10; var B = Z / 2; function getContext() { if (this.context_) { return this.context_ } return this.context_ = new CanvasRenderingContext2D_(this) } var C = Array.prototype.slice; function bind(f, b, c) { var a = C.call(arguments, 2); return function() { return f.apply(b, a.concat(C.call(arguments))) } } var D = { init: function(a) { if (/MSIE/.test(navigator.userAgent) && !window.opera) { var b = a || document; b.createElement('canvas'); b.attachEvent('onreadystatechange', bind(this.init_, this, b)) } }, init_: function(a) { if (!a.namespaces['g_vml_']) { a.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml') } if (!a.styleSheets['ex_canvas_']) { var b = a.createStyleSheet(); b.owningElement.id = 'ex_canvas_'; b.cssText = 'canvas{display:inline-block;overflow:hidden;' + 'text-align:left;width:300px;height:150px}' + 'g_vml_\\:*{behavior:url(#default#VML)}' } }, i: function(a) { if (!a.getContext) { a.getContext = getContext; a.attachEvent('onpropertychange', onPropertyChange); a.attachEvent('onresize', onResize); var b = a.attributes; if (b.width && b.width.specified) { a.style.width = b.width.nodeValue + 'px' } else { a.width = a.clientWidth } if (b.height && b.height.specified) { a.style.height = b.height.nodeValue + 'px' } else { a.height = a.clientHeight } } return a } }; function onPropertyChange(e) { var a = e.srcElement; switch (e.propertyName) { case 'width': a.style.width = a.attributes.width.nodeValue + 'px'; a.getContext().clearRect(); break; case 'height': a.style.height = a.attributes.height.nodeValue + 'px'; a.getContext().clearRect(); break } } function onResize(e) { var a = e.srcElement; if (a.firstChild) { a.firstChild.style.width = a.clientWidth + 'px'; a.firstChild.style.height = a.clientHeight + 'px' } } D.init(); var E = []; for (var i = 0; i < 16; i++) { for (var j = 0; j < 16; j++) { E[i * 16 + j] = i.toString(16) + j.toString(16) } } function createMatrixIdentity() { return [[1, 0, 0], [0, 1, 0], [0, 0, 1]] } function processStyle(a) { var b, alpha = 1; a = String(a); if (a.substring(0, 3) == 'rgb') { var c = a.indexOf('(', 3); var d = a.indexOf(')', c + 1); var e = a.substring(c + 1, d).split(','); b = '#'; for (var i = 0; i < 3; i++) { b += E[Number(e[i])] } if (e.length == 4 && a.substr(3, 1) == 'a') { alpha = e[3] } } else { b = a } return [b, alpha] } function processLineCap(a) { switch (a) { case 'butt': return 'flat'; case 'round': return 'round'; case 'square': default: return 'square' } } function CanvasRenderingContext2D_(a) { this.m_ = createMatrixIdentity(); this.mStack_ = []; this.aStack_ = []; this.currentPath_ = []; this.strokeStyle = '#000'; this.fillStyle = '#000'; this.lineWidth = 1; this.lineJoin = 'miter'; this.lineCap = 'butt'; this.miterLimit = Z * 1; this.globalAlpha = 1; this.canvas = a; var b = a.ownerDocument.createElement('div'); b.style.width = a.clientWidth + 'px'; b.style.height = a.clientHeight + 'px'; b.style.overflow = 'hidden'; b.style.position = 'absolute'; a.appendChild(b); this.element_ = b; this.arcScaleX_ = 1; this.arcScaleY_ = 1 } var F = CanvasRenderingContext2D_.prototype; F.clearRect = function() { this.element_.innerHTML = ''; this.currentPath_ = [] }; F.beginPath = function() { this.currentPath_ = [] }; F.moveTo = function(a, b) { var p = this.getCoords_(a, b); this.currentPath_.push({ type: 'moveTo', x: p.x, y: p.y }); this.currentX_ = p.x; this.currentY_ = p.y }; F.lineTo = function(a, b) { var p = this.getCoords_(a, b); this.currentPath_.push({ type: 'lineTo', x: p.x, y: p.y }); this.currentX_ = p.x; this.currentY_ = p.y }; F.bezierCurveTo = function(a, b, c, d, e, f) { var p = this.getCoords_(e, f); var g = this.getCoords_(a, b); var h = this.getCoords_(c, d); this.currentPath_.push({ type: 'bezierCurveTo', cp1x: g.x, cp1y: g.y, cp2x: h.x, cp2y: h.y, x: p.x, y: p.y }); this.currentX_ = p.x; this.currentY_ = p.y }; F.fillRect = function(a, b, c, d) { this.beginPath(); this.moveTo(a, b); this.lineTo(a + c, b); this.lineTo(a + c, b + d); this.lineTo(a, b + d); this.closePath(); this.fill(); this.currentPath_ = [] }; F.createLinearGradient = function(a, b, c, d) { return new CanvasGradient_('gradient') }; F.createRadialGradient = function(a, b, c, d, e, f) { var g = new CanvasGradient_('gradientradial'); g.radius1_ = c; g.radius2_ = f; g.focus_.x = a; g.focus_.y = b; return g }; F.stroke = function(d) { var e = []; var f = false; var a = processStyle(d ? this.fillStyle : this.strokeStyle); var g = a[0]; var h = a[1] * this.globalAlpha; var W = 10; var H = 10; e.push('<g_vml_:shape', ' fillcolor="', g, '"', ' filled="', Boolean(d), '"', ' style="position:absolute;width:', W, ';height:', H, ';"', ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"', ' stroked="', !d, '"', ' strokeweight="', this.lineWidth, '"', ' strokecolor="', g, '"', ' path="'); var j = false; var k = { x: null, y: null }; var l = { x: null, y: null }; for (var i = 0; i < this.currentPath_.length; i++) { var p = this.currentPath_[i]; var c; switch (p.type) { case 'moveTo': e.push(' m '); c = p; e.push(y(p.x), ',', y(p.y)); break; case 'lineTo': e.push(' l '); e.push(y(p.x), ',', y(p.y)); break; case 'close': e.push(' x '); p = null; break; case 'bezierCurveTo': e.push(' c '); e.push(y(p.cp1x), ',', y(p.cp1y), ',', y(p.cp2x), ',', y(p.cp2y), ',', y(p.x), ',', y(p.y)); break; case 'at': case 'wa': e.push(' ', p.type, ' '); e.push(y(p.x - this.arcScaleX_ * p.radius), ',', y(p.y - this.arcScaleY_ * p.radius), ' ', y(p.x + this.arcScaleX_ * p.radius), ',', y(p.y + this.arcScaleY_ * p.radius), ' ', y(p.xStart), ',', y(p.yStart), ' ', y(p.xEnd), ',', y(p.yEnd)); break } if (p) { if (k.x == null || p.x < k.x) { k.x = p.x } if (l.x == null || p.x > l.x) { l.x = p.x } if (k.y == null || p.y < k.y) { k.y = p.y } if (l.y == null || p.y > l.y) { l.y = p.y } } } e.push(' ">'); if (typeof this.fillStyle == 'object') { var m = { x: '50%', y: '50%' }; var n = l.x - k.x; var o = l.y - k.y; var q = n > o ? n : o; m.x = y(this.fillStyle.focus_.x / n * 100 + 50) + '%'; m.y = y(this.fillStyle.focus_.y / o * 100 + 50) + '%'; var r = []; if (this.fillStyle.type_ == 'gradientradial') { var s = this.fillStyle.radius1_ / q * 100; var t = this.fillStyle.radius2_ / q * 100 - s } else { var s = 0; var t = 100 } var u = { offset: null, color: null }; var v = { offset: null, color: null }; this.fillStyle.colors_.sort(function(a, b) { return a.offset - b.offset }); for (var i = 0; i < this.fillStyle.colors_.length; i++) { var w = this.fillStyle.colors_[i]; r.push(w.offset * t + s, '% ', w.color, ','); if (w.offset > u.offset || u.offset == null) { u.offset = w.offset; u.color = w.color } if (w.offset < v.offset || v.offset == null) { v.offset = w.offset; v.color = w.color } } r.pop(); e.push('<g_vml_:fill', ' color="', v.color, '"', ' color2="', u.color, '"', ' type="', this.fillStyle.type_, '"', ' focusposition="', m.x, ', ', m.y, '"', ' colors="', r.join(''), '"', ' opacity="', h, '" />') } else if (d) { e.push('<g_vml_:fill color="', g, '" opacity="', h, '" />') } else { var x = Math.max(this.arcScaleX_, this.arcScaleY_) * this.lineWidth; e.push('<g_vml_:stroke', ' opacity="', h, '"', ' joinstyle="', this.lineJoin, '"', ' miterlimit="', this.miterLimit, '"', ' endcap="', processLineCap(this.lineCap), '"', ' weight="', x, 'px"', ' color="', g, '" />') } e.push('</g_vml_:shape>'); this.element_.insertAdjacentHTML('beforeEnd', e.join('')) }; F.fill = function() { this.stroke(true) }; F.closePath = function() { this.currentPath_.push({ type: 'close' }) }; F.getCoords_ = function(a, b) { return { x: Z * (a * this.m_[0][0] + b * this.m_[1][0] + this.m_[2][0]) - B, y: Z * (a * this.m_[0][1] + b * this.m_[1][1] + this.m_[2][1]) - B} }; function CanvasPattern_() { } G_vmlCMjrc = D })() } if (jQuery.browser.msie) { document.execCommand("BackgroundImageCache", false, true) } (function($) { var N = $.browser.msie; var O = N && !window.XMLHttpRequest; var P = $.browser.opera; var Q = typeof document.createElement('canvas').getContext == "function"; var R = function(i) { return parseInt(i, 10) || 0 }; var S = function(a, b, c) { var x = a, y; if (x.currentStyle) { y = x.currentStyle[b] } else if (window.getComputedStyle) { if (typeof arguments[2] == "string") b = c; y = document.defaultView.getComputedStyle(x, null).getPropertyValue(b) } return y }; var T = function(a, p) { return S(a, 'border' + p + 'Color', 'border-' + p.toLowerCase() + '-color') }; var U = function(a, p) { if (a.currentStyle && !P) { w = a.currentStyle['border' + p + 'Width']; if (w == 'thin') w = 2; if (w == 'medium' && !(a.currentStyle['border' + p + 'Style'] == 'none')) w = 4; if (w == 'thick') w = 6 } else { p = p.toLowerCase(); w = document.defaultView.getComputedStyle(a, null).getPropertyValue('border-' + p + '-width') } return R(w) }; var V = function(a, i) { return a.tagName.toLowerCase() == i }; var W = function(e, a, b, c, d) { if (e == 'tl') return a; if (e == 'tr') return b; if (e == 'bl') return c; if (e == 'br') return d }; var X = function(a, b, c, d, e, f, g) { var h, curve_to; if (d.indexOf('rgba') != -1) { var i = /^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/; var j = i.exec(d); if (j) { var k = [R(j[1]), R(j[2]), R(j[3])]; d = 'rgb(' + k[0] + ', ' + k[1] + ', ' + k[2] + ')' } } var l = a.getContext('2d'); if (b == 1 || g == 'notch') { if (e > 0 && b > 1) { l.fillStyle = f; l.fillRect(0, 0, b, b); l.fillStyle = d; h = W(c, [0 - e, 0 - e], [e, 0 - e], [0 - e, e], [e, e]); l.fillRect(h[0], h[1], b, b) } else { l.fillStyle = d; l.fillRect(0, 0, b, b) } return a } else if (g == 'bevel') { h = W(c, [0, 0, 0, b, b, 0, 0, 0], [0, 0, b, b, b, 0, 0, 0], [0, 0, b, b, 0, b, 0, 0], [b, b, b, 0, 0, b, b, b]); l.fillStyle = d; l.beginPath(); l.moveTo(h[0], h[1]); l.lineTo(h[2], h[3]); l.lineTo(h[4], h[5]); l.lineTo(h[6], h[7]); l.fill(); if (e > 0 && e < b) { l.strokeStyle = f; l.lineWidth = e; l.beginPath(); h = W(c, [0, b, b, 0], [0, 0, b, b], [b, b, 0, 0], [0, b, b, 0]); l.moveTo(h[0], h[1]); l.lineTo(h[2], h[3]); l.stroke() } return a } h = W(c, [0, 0, b, 0, b, 0, 0, b, 0, 0], [b, 0, b, b, b, 0, 0, 0, 0, 0], [0, b, b, b, 0, b, 0, 0, 0, b], [b, b, b, 0, b, 0, 0, b, b, b]); l.fillStyle = d; l.beginPath(); l.moveTo(h[0], h[1]); l.lineTo(h[2], h[3]); if (c == 'br') l.bezierCurveTo(h[4], h[5], b, b, h[6], h[7]); else l.bezierCurveTo(h[4], h[5], 0, 0, h[6], h[7]); l.lineTo(h[8], h[9]); l.fill(); if (e > 0 && e < b) { var m = e / 2; var n = b - m; h = W(c, [n, m, n, m, m, n], [n, n, n, m, m, m], [n, n, m, n, m, m, m, n], [n, m, n, m, m, n, n, n]); curve_to = W(c, [0, 0], [0, 0], [0, 0], [b, b]); l.strokeStyle = f; l.lineWidth = e; l.beginPath(); l.moveTo(h[0], h[1]); l.bezierCurveTo(h[2], h[3], curve_to[0], curve_to[1], h[4], h[5]); l.stroke() } return a }; var Y = function(p, a) { var b = document.createElement('canvas'); b.setAttribute("height", a); b.setAttribute("width", a); b.style.display = "block"; b.style.position = "absolute"; b.className = "jrCorner"; Z(p, b); if (!Q && N) { if (typeof G_vmlCanvasManager == "object") { b = G_vmlCanvasManager.initElement(b) } else if (typeof G_vmlCMjrc == "object") { b = G_vmlCMjrc.i(b) } else { throw Error('Could not find excanvas'); } } return b }; var Z = function(p, a) { if (p.is("table")) { p.children("tbody").children("tr:first").children("td:first").append(a); p.css('display', 'block') } else if (p.is("td")) { if (p.children(".JrcTdContainer").length === 0) { p.html('<div class="JrcTdContainer" style="padding:0px;position:relative;margin:-1px;zoom:1;">' + p.html() + '</div>'); p.css('zoom', '1'); if (O) { p.children(".JrcTdContainer").get(0).style.setExpression("height", "this.parentNode.offsetHeight") } } p.children(".JrcTdContainer").append(a) } else { p.append(a) } }; if (N) { var ba = document.createStyleSheet(); ba.media = 'print'; ba.cssText = '.jrcIECanvasDiv { display:none !important; }' } var bb = function(D) { if (this.length == 0 || !(Q || N)) { return this } if (D == "destroy") { return this.each(function() { var p, elm = $(this); if (elm.is(".jrcRounded")) { if (typeof elm.data("ie6tmr.jrc") == 'number') window.clearInterval(elm.data("ie6tmr.jrc")); if (elm.is("table")) p = elm.children("tbody").children("tr:first").children("td:first"); else if (elm.is("td")) p = elm.children(".JrcTdContainer"); else p = elm; p.children(".jrCorner").remove(); elm.unbind('mouseleave.jrc').unbind('mouseenter.jrc').removeClass('jrcRounded').removeData('ie6tmr.jrc'); if (elm.is("td")) elm.html(elm.children(".JrcTdContainer").html()) } }) } var o = (D || "").toLowerCase(); var E = R((o.match(/(\d+)px/) || [])[1]) || "auto"; var F = ((o.match(/(#[0-9a-f]+)/) || [])[1]) || "auto"; var G = /round|bevel|notch/; var H = ((o.match(G) || ['round'])[0]); var I = /hover/.test(o); var J = /oversized/.test(o); var K = o.match("hiddenparent"); if (N) { var G = /ie6nofix|ie6fixinit|ie6fixexpr|ie6fixonload|ie6fixwidthint|ie6fixheightint|ie6fixbothint/; var L = ((o.match(G) || ['ie6fixinit'])[0]) } var M = { tl: /top|left|tl/.test(o), tr: /top|right|tr/.test(o), bl: /bottom|left|bl/.test(o), br: /bottom|right|br/.test(o) }; if (!M.tl && !M.tr && !M.bl && !M.br) M = { tl: 1, tr: 1, bl: 1, br: 1 }; this.each(function() { var d = $(this), rbg = null, bg, s, b, pr; var a = this; var e = S(this, 'display'); var f = S(this, 'position'); var g = S(this, 'lineHeight', 'line-height'); if (F == "auto") { s = d.siblings(".jrcRounded:eq(0)"); if (s.length > 0) { b = s.data("rbg.jrc"); if (typeof b == "string") { rbg = b } } } if (K || rbg === null) { var h = this.parentNode, hidden_parents = new Array(), a = 0; while ((typeof h == 'object') && !V(h, 'html')) { if (K && S(h, 'display') == 'none') { hidden_parents.push({ originalvisibility: S(h, 'visibility'), elm: h }); h.style.display = 'block'; h.style.visibility = 'hidden' } var j = S(h, 'backgroundColor', 'background-color'); if (rbg === null && j != "transparent" && j != "rgba(0, 0, 0, 0)") { rbg = j } h = h.parentNode } if (rbg === null) rbg = "#ffffff" } if (F == "auto") { bg = rbg; d.data("rbg.jrc", rbg) } else { bg = F } if (e == 'none') { var k = S(this, 'visibility'); this.style.display = 'block'; this.style.visibility = 'hidden'; var l = true } else { var m = false } var n = d.height(); var p = d.width(); if (I) { var q = o.replace(/hover|ie6nofix|ie6fixinit|ie6fixexpr|ie6fixonload|ie6fixwidthint|ie6fixheightint|ie6fixbothint/g, ""); if (L != 'ie6nofix') q = "ie6fixinit " + q; d.bind("mouseenter.jrc", function() { d.addClass('jrcHover'); d.corner(q) }); d.bind("mouseleave.jrc", function() { d.removeClass('jrcHover'); d.corner(q) }) } if (O && L != 'ie6nofix') { this.style.zoom = 1; if (L != 'ie6fixexpr') { if (d.width() % 2 != 0) d.width(d.width() + 1); if (d.height() % 2 != 0) d.height(d.height() + 1) } $(window).load(function() { if (L == 'ie6fixonload') { if (d.css('height') == 'auto') d.height(d.css('height')); if (d.width() % 2 != 0) d.width(d.width() + 1); if (d.height() % 2 != 0) d.height(d.height() + 1) } else if (L == 'ie6fixwidthint' || L == 'ie6fixheightint' || L == 'ie6fixbothint') { var c, ie6FixFunction; if (L == 'ie6fixheightint') { ie6FixFunction = function() { d.height('auto'); var a = d.height(); if (a % 2 != 0) a = a + 1; d.css({ height: a }) } } else if (L == 'ie6fixwidthint') { ie6FixFunction = function() { d.width('auto'); var a = d.width(); if (a % 2 != 0) a = a + 1; d.css({ width: a }); d.data('lastWidth.jrc', d.get(0).offsetWidth) } } else if (L == 'ie6fixbothint') { ie6FixFunction = function() { d.width('auto'); d.height('auto'); var a = d.width(); var b = d.height(); if (b % 2 != 0) b = b + 1; if (a % 2 != 0) a = a + 1; d.css({ width: a, height: b }) } } c = window.setInterval(ie6FixFunction, 100); d.data("ie6tmr.jrc", c) } }) } var r = n < p ? this.offsetHeight : this.offsetWidth; if (E == "auto") { E = r / 2; if (E > 10) E = r / 4 } if (E > r / 2 && !J) { E = r / 2 } E = Math.floor(E); var t = U(this, 'Top'); var u = U(this, 'Right'); var v = U(this, 'Bottom'); var w = U(this, 'Left'); if (f == 'static' && !V(this, 'td')) { this.style.position = 'relative' } else if (f == 'fixed' && N && !(document.compatMode == 'CSS1Compat' && !O)) { this.style.position = 'absolute' } if (t + u + v + w > 0) { this.style.overflow = 'visible' } if (l) d.css({ display: 'none', visibility: k }); if (typeof hidden_parents != "undefined") { for (var i = 0; i < hidden_parents.length; i++) { hidden_parents[i].elm.style.display = 'none'; hidden_parents[i].elm.style.visibility = hidden_parents[i].originalvisibility } } var x = 0 - t, p_right = 0 - u, p_bottom = 0 - v, p_left = 0 - w; var y = (d.find("canvas").length > 0); if (y) { if (V(this, 'table')) pr = d.children("tbody").children("tr:first").children("td:first"); else if (V(this, 'td')) pr = d.children(".JrcTdContainer"); else pr = d } if (M.tl) { bordersWidth = t < w ? t : w; if (y) pr.children("canvas.jrcTL").remove(); var z = X(Y(d, E), E, 'tl', bg, bordersWidth, T(this, 'Top'), H); $(z).css({ left: p_left, top: x }).addClass('jrcTL') } if (M.tr) { bordersWidth = t < u ? t : u; if (y) pr.children("canvas.jrcTR").remove(); var A = X(Y(d, E), E, 'tr', bg, bordersWidth, T(this, 'Top'), H); $(A).css({ right: p_right, top: x }).addClass('jrcTR') } if (M.bl) { bordersWidth = v < w ? v : w; if (y) pr.children("canvas.jrcBL").remove(); var B = X(Y(d, E), E, 'bl', bg, bordersWidth, T(this, 'Bottom'), H); $(B).css({ left: p_left, bottom: p_bottom }).addClass('jrcBL') } if (M.br) { bordersWidth = v < u ? v : u; if (y) pr.children("canvas.jrcBR").remove(); var C = X(Y(d, E), E, 'br', bg, bordersWidth, T(this, 'Bottom'), H); $(C).css({ right: p_right, bottom: p_bottom }).addClass('jrcBR') } if (N) d.children('canvas.jrCorner').children('div').addClass('jrcIECanvasDiv'); if (O && L == 'ie6fixexpr') { if (M.bl) { B.style.setExpression("bottom", "this.parentNode.offsetHeight % 2 == 0 || this.parentNode.offsetWidth % 2 == 0 ? 0-(parseInt(this.parentNode.currentStyle['borderBottomWidth'])) : 0-(parseInt(this.parentNode.currentStyle['borderBottomWidth'])+1)") } if (M.br) { C.style.setExpression("right", "this.parentNode.offsetWidth  % 2 == 0 || this.parentNode.offsetWidth % 2 == 0 ? 0-(parseInt(this.parentNode.currentStyle['borderRightWidth']))  : 0-(parseInt(this.parentNode.currentStyle['borderRightWidth'])+1)"); C.style.setExpression("bottom", "this.parentNode.offsetHeight % 2 == 0 || this.parentNode.offsetWidth % 2 == 0 ? 0-(parseInt(this.parentNode.currentStyle['borderBottomWidth'])) : 0-(parseInt(this.parentNode.currentStyle['borderBottomWidth'])+1)") } if (M.tr) { A.style.setExpression("right", "this.parentNode.offsetWidth   % 2 == 0 || this.parentNode.offsetWidth % 2 == 0 ? 0-(parseInt(this.parentNode.currentStyle['borderRightWidth']))  : 0-(parseInt(this.parentNode.currentStyle['borderRightWidth'])+1)") } } d.addClass('jrcRounded') }); if (typeof arguments[1] == "function") arguments[1](this); return this }; $.fn.corner = bb })(jQuery);

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)));
            };
        }

        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) {
                // 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}' +
            'g_vml_\\:*{behavior:url(#default#VML)}' +
            'g_o_\\:*{behavior:url(#default#VML)}';

                }

                // 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;

                    // 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.style.width = el.attributes.width.nodeValue + 'px';
                    el.getContext().clearRect();
                    break;
                case 'height':
                    el.style.height = el.attributes.height.nodeValue + 'px';
                    el.getContext().clearRect();
                    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 dec2hex = [];
        for (var i = 0; i < 16; i++) {
            for (var j = 0; j < 16; j++) {
                dec2hex[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.arcScaleX_ = o1.arcScaleX_;
            o2.arcScaleY_ = o1.arcScaleY_;
            o2.lineScale_ = o1.lineScale_;
        }

        function processStyle(styleString) {
            var str, alpha = 1;

            styleString = String(styleString);
            if (styleString.substring(0, 3) == 'rgb') {
                var start = styleString.indexOf('(', 3);
                var end = styleString.indexOf(')', start + 1);
                var guts = styleString.substring(start + 1, end).split(',');

                str = '#';
                for (var i = 0; i < 3; i++) {
                    str += dec2hex[Number(guts[i])];
                }

                if (guts.length == 4 && styleString.substr(3, 1) == 'a') {
                    alpha = guts[3];
                }
            } else {
                str = styleString;
            }

            return { color: str, alpha: alpha };
        }

        function processLineCap(lineCap) {
            switch (lineCap) {
                case 'butt':
                    return 'flat';
                case 'round':
                    return 'round';
                case 'square':
                default:
                    return 'square';
            }
        }

        /**
        * This class implements CanvasRenderingContext2D interface as described by
        * the WHATWG.
        * @param {HTMLElement} surfaceElement The element that the 2D context should
        * be associated with
        */
        function CanvasRenderingContext2D_(surfaceElement) {
            this.m_ = createMatrixIdentity();

            this.mStack_ = [];
            this.aStack_ = [];
            this.currentPath_ = [];

            // Canvas context properties
            this.strokeStyle = '#000';
            this.fillStyle = '#000';

            this.lineWidth = 1;
            this.lineJoin = 'miter';
            this.lineCap = 'butt';
            this.miterLimit = Z * 1;
            this.globalAlpha = 1;
            this.canvas = surfaceElement;

            var el = surfaceElement.ownerDocument.createElement('div');
            el.style.width = surfaceElement.clientWidth + 'px';
            el.style.height = surfaceElement.clientHeight + 'px';
            el.style.overflow = 'hidden';
            el.style.position = 'absolute';
            surfaceElement.appendChild(el);

            this.element_ = el;
            this.arcScaleX_ = 1;
            this.arcScaleY_ = 1;
            this.lineScale_ = 1;
        }

        var contextPrototype = CanvasRenderingContext2D_.prototype;
        contextPrototype.clearRect = function() {
            this.element_.innerHTML = '';
        };

        contextPrototype.beginPath = function() {
            // TODO: Branch current matrix so that save/restore has no effect
            //       as per safari docs.
            this.currentPath_ = [];
        };

        contextPrototype.moveTo = function(aX, aY) {
            var p = this.getCoords_(aX, aY);
            this.currentPath_.push({ type: 'moveTo', x: p.x, y: p.y });
            this.currentX_ = p.x;
            this.currentY_ = p.y;
        };

        contextPrototype.lineTo = function(aX, aY) {
            var p = this.getCoords_(aX, aY);
            this.currentPath_.push({ type: 'lineTo', x: p.x, y: p.y });

            this.currentX_ = p.x;
            this.currentY_ = p.y;
        };

        contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
                                            aCP2x, aCP2y,
                                            aX, aY) {
            var p = this.getCoords_(aX, aY);
            var cp1 = this.getCoords_(aCP1x, aCP1y);
            var cp2 = this.getCoords_(aCP2x, aCP2y);
            bezierCurveTo(this, cp1, cp2, p);
        };

        // Helper function that takes the already fixed cordinates.
        function bezierCurveTo(self, cp1, cp2, p) {
            self.currentPath_.push({
                type: 'bezierCurveTo',
                cp1x: cp1.x,
                cp1y: cp1.y,
                cp2x: cp2.x,
                cp2y: cp2.y,
                x: p.x,
                y: p.y
            });
            self.currentX_ = p.x;
            self.currentY_ = p.y;
        }

        contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
            // the following is lifted almost directly from
            // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes

            var cp = this.getCoords_(aCPx, aCPy);
            var p = this.getCoords_(aX, aY);

            var cp1 = {
                x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
                y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
            };
            var cp2 = {
                x: cp1.x + (p.x - this.currentX_) / 3.0,
                y: cp1.y + (p.y - this.currentY_) / 3.0
            };

            bezierCurveTo(this, cp1, cp2, p);
        };

        contextPrototype.arc = function(aX, aY, aRadius,
                                  aStartAngle, aEndAngle, aClockwise) {
            aRadius *= Z;
            var arcType = aClockwise ? 'at' : 'wa';

            var xStart = aX + mc(aStartAngle) * aRadius - Z2;
            var yStart = aY + ms(aStartAngle) * aRadius - Z2;

            var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
            var yEnd = aY + ms(aEndAngle) * aRadius - Z2;

            // IE won't render arches drawn counter clockwise if xStart == xEnd.
            if (xStart == xEnd && !aClockwise) {
                xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
                // that can be represented in binary
            }

            var p = this.getCoords_(aX, aY);
            var pStart = this.getCoords_(xStart, yStart);
            var pEnd = this.getCoords_(xEnd, yEnd);

            this.currentPath_.push({ type: arcType,
                x: p.x,
                y: p.y,
                radius: aRadius,
                xStart: pStart.x,
                yStart: pStart.y,
                xEnd: pEnd.x,
                yEnd: pEnd.y
            });

        };

        contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
            this.moveTo(aX, aY);
            this.lineTo(aX + aWidth, aY);
            this.lineTo(aX + aWidth, aY + aHeight);
            this.lineTo(aX, aY + aHeight);
            this.closePath();
        };

        contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
            var oldPath = this.currentPath_;
            this.beginPath();

            this.moveTo(aX, aY);
            this.lineTo(aX + aWidth, aY);
            this.lineTo(aX + aWidth, aY + aHeight);
            this.lineTo(aX, aY + aHeight);
            this.closePath();
            this.stroke();

            this.currentPath_ = oldPath;
        };

        contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
            var oldPath = this.currentPath_;
            this.beginPath();

            this.moveTo(aX, aY);
            this.lineTo(aX + aWidth, aY);
            this.lineTo(aX + aWidth, aY + aHeight);
            this.lineTo(aX, aY + aHeight);
            this.closePath();
            this.fill();

            this.currentPath_ = oldPath;
        };

        contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
            var gradient = new CanvasGradient_('gradient');
            gradient.x0_ = aX0;
            gradient.y0_ = aY0;
            gradient.x1_ = aX1;
            gradient.y1_ = aY1;
            return gradient;
        };

        contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
                                                   aX1, aY1, aR1) {
            var gradient = new CanvasGradient_('gradientradial');
            gradient.x0_ = aX0;
            gradient.y0_ = aY0;
            gradient.r0_ = aR0;
            gradient.x1_ = aX1;
            gradient.y1_ = aY1;
            gradient.r1_ = aR1;
            return gradient;
        };

        contextPrototype.drawImage = function(image, var_args) {
            var dx, dy, dw, dh, sx, sy, sw, sh;

            // to find the original width we overide the width and height
            var oldRuntimeWidth = image.runtimeStyle.width;
            var oldRuntimeHeight = image.runtimeStyle.height;
            image.runtimeStyle.width = 'auto';
            image.runtimeStyle.height = 'auto';

            // get the original size
            var w = image.width;
            var h = image.height;

            // and remove overides
            image.runtimeStyle.width = oldRuntimeWidth;
            image.runtimeStyle.height = oldRuntimeHeight;

            if (arguments.length == 3) {
                dx = arguments[1];
                dy = arguments[2];
                sx = sy = 0;
                sw = dw = w;
                sh = dh = h;
            } else if (arguments.length == 5) {
                dx = arguments[1];
                dy = arguments[2];
                dw = arguments[3];
                dh = arguments[4];
                sx = sy = 0;
                sw = w;
                sh = h;
            } else if (arguments.length == 9) {
                sx = arguments[1];
                sy = arguments[2];
                sw = arguments[3];
                sh = arguments[4];
                dx = arguments[5];
                dy = arguments[6];
                dw = arguments[7];
                dh = arguments[8];
            } else {
                throw Error('Invalid number of arguments');
            }

            var d = this.getCoords_(dx, dy);

            var w2 = sw / 2;
            var h2 = sh / 2;

            var vmlStr = [];

            var W = 10;
            var H = 10;

            // For some reason that I've now forgotten, using divs didn't work
            vmlStr.push(' <g_vml_:group',
                ' coordsize="', Z * W, ',', Z * H, '"',
                ' coordorigin="0,0"',
                ' style="width:', W, 'px;height:', H, 'px;position:absolute;');

            // If filters are necessary (rotation exists), create them
            // filters are bog-slow, so only create them if abbsolutely necessary
            // The following check doesn't account for skews (which don't exist
            // in the canvas spec (yet) anyway.

            if (this.m_[0][0] != 1 || this.m_[0][1]) {
                var filter = [];

                // Note the 12/21 reversal
                filter.push('M11=', this.m_[0][0], ',',
                  'M12=', this.m_[1][0], ',',
                  'M21=', this.m_[0][1], ',',
                  'M22=', this.m_[1][1], ',',
                  'Dx=', mr(d.x / Z), ',',
                  'Dy=', mr(d.y / Z), '');

                // Bounding box calculation (need to minimize displayed area so that
                // filters don't waste time on unused pixels.
                var max = d;
                var c2 = this.getCoords_(dx + dw, dy);
                var c3 = this.getCoords_(dx, dy + dh);
                var c4 = this.getCoords_(dx + dw, dy + dh);

                max.x = m.max(max.x, c2.x, c3.x, c4.x);
                max.y = m.max(max.y, c2.y, c3.y, c4.y);

                vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
                  'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
                  filter.join(''), ", sizingmethod='clip');")
            } else {
                vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
            }

            vmlStr.push(' ">',
                '<g_vml_:image src="', image.src, '"',
                ' style="width:', Z * dw, 'px;',
                ' height:', Z * dh, 'px;"',
                ' cropleft="', sx / w, '"',
                ' croptop="', sy / h, '"',
                ' cropright="', (w - sx - sw) / w, '"',
                ' cropbottom="', (h - sy - sh) / h, '"',
                ' />',
                '</g_vml_:group>');

            this.element_.insertAdjacentHTML('BeforeEnd',
                                    vmlStr.join(''));
        };

        contextPrototype.stroke = function(aFill) {
            var lineStr = [];
            var lineOpen = false;
            var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
            var color = a.color;
            var opacity = a.alpha * this.globalAlpha;

            var W = 10;
            var H = 10;

            lineStr.push('<g_vml_:shape',
                 ' filled="', !!aFill, '"',
                 ' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
                 ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
                 ' stroked="', !aFill, '"',
                 ' path="');

            var newSeq = false;
            var min = { x: null, y: null };
            var max = { x: null, y: null };

            for (var i = 0; i < this.currentPath_.length; i++) {
                var p = this.currentPath_[i];
                var c;

                switch (p.type) {
                    case 'moveTo':
                        c = p;
                        lineStr.push(' m ', mr(p.x), ',', mr(p.y));
                        break;
                    case 'lineTo':
                        lineStr.push(' l ', mr(p.x), ',', mr(p.y));
                        break;
                    case 'close':
                        lineStr.push(' x ');
                        p = null;
                        break;
                    case 'bezierCurveTo':
                        lineStr.push(' c ',
                       mr(p.cp1x), ',', mr(p.cp1y), ',',
                       mr(p.cp2x), ',', mr(p.cp2y), ',',
                       mr(p.x), ',', mr(p.y));
                        break;
                    case 'at':
                    case 'wa':
                        lineStr.push(' ', p.type, ' ',
                       mr(p.x - this.arcScaleX_ * p.radius), ',',
                       mr(p.y - this.arcScaleY_ * p.radius), ' ',
                       mr(p.x + this.arcScaleX_ * p.radius), ',',
                       mr(p.y + this.arcScaleY_ * p.radius), ' ',
                       mr(p.xStart), ',', mr(p.yStart), ' ',
                       mr(p.xEnd), ',', mr(p.yEnd));
                        break;
                }


                // TODO: Following is broken for curves due to
                //       move to proper paths.

                // Figure out dimensions so we can do gradient fills
                // properly
                if (p) {
                    if (min.x == null || p.x < min.x) {
                        min.x = p.x;
                    }
                    if (max.x == null || p.x > max.x) {
                        max.x = p.x;
                    }
                    if (min.y == null || p.y < min.y) {
                        min.y = p.y;
                    }
                    if (max.y == null || p.y > max.y) {
                        max.y = p.y;
                    }
                }
            }
            lineStr.push(' ">');

            if (!aFill) {
                var lineWidth = this.lineScale_ * this.lineWidth;

                // VML cannot correctly render a line if the width is less than 1px.
                // In that case, we dilute the color to make the line look thinner.
                if (lineWidth < 1) {
                    opacity *= lineWidth;
                }

                lineStr.push(
        '<g_vml_:stroke',
        ' opacity="', opacity, '"',
        ' joinstyle="', this.lineJoin, '"',
        ' miterlimit="', this.miterLimit, '"',
        ' endcap="', processLineCap(this.lineCap), '"',
        ' weight="', lineWidth, 'px"',
        ' color="', color, '" />'
      );
            } else if (typeof this.fillStyle == 'object') {
                var fillStyle = this.fillStyle;
                var angle = 0;
                var focus = { x: 0, y: 0 };

                // additional offset
                var shift = 0;
                // scale factor for offset
                var expansion = 1;

                if (fillStyle.type_ == 'gradient') {
                    var x0 = fillStyle.x0_ / this.arcScaleX_;
                    var y0 = fillStyle.y0_ / this.arcScaleY_;
                    var x1 = fillStyle.x1_ / this.arcScaleX_;
                    var y1 = fillStyle.y1_ / this.arcScaleY_;
                    var p0 = this.getCoords_(x0, y0);
                    var p1 = this.getCoords_(x1, y1);
                    var dx = p1.x - p0.x;
                    var dy = p1.y - p0.y;
                    angle = Math.atan2(dx, dy) * 180 / Math.PI;

                    // The angle should be a non-negative number.
                    if (angle < 0) {
                        angle += 360;
                    }

                    // Very small angles produce an unexpected result because they are
                    // converted to a scientific notation string.
                    if (angle < 1e-6) {
                        angle = 0;
                    }
                } else {
                    var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_);
                    var width = max.x - min.x;
                    var height = max.y - min.y;
                    focus = {
                        x: (p0.x - min.x) / width,
                        y: (p0.y - min.y) / height
                    };

                    width /= this.arcScaleX_ * Z;
                    height /= this.arcScaleY_ * Z;
                    var dimension = m.max(width, height);
                    shift = 2 * fillStyle.r0_ / dimension;
                    expansion = 2 * fillStyle.r1_ / dimension - shift;
                }

                // We need to sort the color stops in ascending order by offset,
                // otherwise IE won't interpret it correctly.
                var stops = fillStyle.colors_;
                stops.sort(function(cs1, cs2) {
                    return cs1.offset - cs2.offset;
                });

                var length = stops.length;
                var color1 = stops[0].color;
                var color2 = stops[length - 1].color;
                var opacity1 = stops[0].alpha * this.globalAlpha;
                var opacity2 = stops[length - 1].alpha * this.globalAlpha;

                var colors = [];
                for (var i = 0; i < length; i++) {
                    var stop = stops[i];
                    colors.push(stop.offset * expansion + shift + ' ' + stop.color);
                }

                // When colors attribute is used, the meanings of opacity and o:opacity2
                // are reversed.
                lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
                   ' method="none" focus="100%"',
                   ' color="', color1, '"',
                   ' color2="', color2, '"',
                   ' colors="', colors.join(','), '"',
                   ' opacity="', opacity2, '"',
                   ' g_o_:opacity2="', opacity1, '"',
                   ' angle="', angle, '"',
                   ' focusposition="', focus.x, ',', focus.y, '" />');
            } else {
                lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
                   '" />');
            }

            lineStr.push('</g_vml_:shape>');

            this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
        };

        contextPrototype.fill = function() {
            this.stroke(true);
        }

        contextPrototype.closePath = function() {
            this.currentPath_.push({ type: 'close' });
        };

        /**
        * @private
        */
        contextPrototype.getCoords_ = function(aX, aY) {
            var m = this.m_;
            return {
                x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
                y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
            }
        };

        contextPrototype.save = function() {
            var o = {};
            copyState(this, o);
            this.aStack_.push(o);
            this.mStack_.push(this.m_);
            this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
        };

        contextPrototype.restore = function() {
            copyState(this.aStack_.pop(), this);
            this.m_ = this.mStack_.pop();
        };

        function matrixIsFinite(m) {
            for (var j = 0; j < 3; j++) {
                for (var k = 0; k < 2; k++) {
                    if (!isFinite(m[j][k]) || isNaN(m[j][k])) {
                        return false;
                    }
                }
            }
            return true;
        }

        function setM(ctx, m, updateLineScale) {
            if (!matrixIsFinite(m)) {
                return;
            }
            ctx.m_ = m;

            if (updateLineScale) {
                // Get the line scale.
                // Determinant of this.m_ means how much the area is enlarged by the
                // transformation. So its square root can be used as a scale factor
                // for width.
                var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
                ctx.lineScale_ = sqrt(abs(det));
            }
        }

        contextPrototype.translate = function(aX, aY) {
            var m1 = [
      [1, 0, 0],
      [0, 1, 0],
      [aX, aY, 1]
    ];

            setM(this, matrixMultiply(m1, this.m_), false);
        };

        contextPrototype.rotate = function(aRot) {
            var c = mc(aRot);
            var s = ms(aRot);

            var m1 = [
      [c, s, 0],
      [-s, c, 0],
      [0, 0, 1]
    ];

            setM(this, matrixMultiply(m1, this.m_), false);
        };

        contextPrototype.scale = function(aX, aY) {
            this.arcScaleX_ *= aX;
            this.arcScaleY_ *= aY;
            var m1 = [
      [aX, 0, 0],
      [0, aY, 0],
      [0, 0, 1]
    ];

            setM(this, matrixMultiply(m1, this.m_), true);
        };

        contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
            var m1 = [
      [m11, m12, 0],
      [m21, m22, 0],
      [dx, dy, 1]
    ];

            setM(this, matrixMultiply(m1, this.m_), true);
        };

        contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
            var m = [
      [m11, m12, 0],
      [m21, m22, 0],
      [dx, dy, 1]
    ];

            setM(this, m, true);
        };

        /******** STUBS ********/
        contextPrototype.clip = function() {
            // TODO: Implement
        };

        contextPrototype.arcTo = function() {
            // TODO: Implement
        };

        contextPrototype.createPattern = function() {
            return new CanvasPattern_;
        };

        // Gradient / Pattern Stubs
        function CanvasGradient_(aType) {
            this.type_ = aType;
            this.x0_ = 0;
            this.y0_ = 0;
            this.r0_ = 0;
            this.x1_ = 0;
            this.y1_ = 0;
            this.r1_ = 0;
            this.colors_ = [];
        }

        CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
            aColor = processStyle(aColor);
            this.colors_.push({ offset: aOffset,
                color: aColor.color,
                alpha: aColor.alpha
            });
        };

        function CanvasPattern_() { }

        // set up externs
        G_vmlCanvasManager = G_vmlCanvasManager_;
        CanvasRenderingContext2D = CanvasRenderingContext2D_;
        CanvasGradient = CanvasGradient_;
        CanvasPattern = CanvasPattern_;

    })();

} // if


if (jQuery.browser.msie) {
    document.execCommand("BackgroundImageCache", false, true);
}


(function($) {

    var isMSIE = $.browser.msie;
    var isltMSIE7 = isMSIE && !window.XMLHttpRequest;
    var isOpera = $.browser.opera;
    var canvasSupport = typeof document.createElement('canvas').getContext == "function";
    // get number as integer
    var Num = function(i) { return parseInt(i, 10) || 0; };

    // get lowest number from array
    /*
    var asNum = function(a, b) { return a-b; };
    var getMin = function(a) {
    var b = a.concat();
    return b.sort(asNum)[0];
    };*/

    // a basic replacement for jquery .css()
    // getStyle(elm,BorderTopWidth,border-top-width)
    var getStyle = function(el, styleProp, styleProp2) {
        var x = el, y;
        if (x.currentStyle) {
            y = x.currentStyle[styleProp];
        } else if (window.getComputedStyle) {
            if (typeof arguments[2] == "string") styleProp = styleProp2;
            y = document.defaultView.getComputedStyle(x, null).getPropertyValue(styleProp);
        }
        return y;
    };

    var getBorderColor = function(elm, p) {
        return getStyle(elm, 'border' + p + 'Color', 'border-' + p.toLowerCase() + '-color')
    };

    var getBorderWidth = function(elm, p) {

        if (elm.currentStyle && !isOpera) {
            w = elm.currentStyle['border' + p + 'Width'];
            if (w == 'thin') w = 2;
            if (w == 'medium' && !(elm.currentStyle['border' + p + 'Style'] == 'none')) w = 4;
            if (w == 'thick') w = 6;
        } else {
            p = p.toLowerCase();
            w = document.defaultView.getComputedStyle(elm, null).getPropertyValue('border-' + p + '-width');
        }
        return Num(w);
    };

    var isElm = function(elm, i) {
        return elm.tagName.toLowerCase() == i;
    };

    var rotationSteps = function(r_type, a, b, c, d) {
        if (r_type == 'tl') return a;
        if (r_type == 'tr') return b;
        if (r_type == 'bl') return c;
        if (r_type == 'br') return d;
    };

    // draw the round corner in Canvas object
    var drawCorner = function(canvas, radius, r_type, bg_color, border_width, border_color, corner_effect) {

        var steps, curve_to;

        // change rgba(1,2,3,0.9) to rgb(1,2,3)
        if (bg_color.indexOf('rgba') != -1) {
            var reg = /^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/;
            var bits = reg.exec(bg_color);
            if (bits) {
                var channels = [Num(bits[1]), Num(bits[2]), Num(bits[3])];
                bg_color = 'rgb(' + channels[0] + ', ' + channels[1] + ', ' + channels[2] + ')';
            }
        }

        var ctx = canvas.getContext('2d');

        if (radius == 1 || corner_effect == 'notch') {

            if (border_width > 0 && radius > 1) {
                ctx.fillStyle = border_color;
                ctx.fillRect(0, 0, radius, radius);
                ctx.fillStyle = bg_color;
                steps = rotationSteps(r_type, [0 - border_width, 0 - border_width], [border_width, 0 - border_width], [0 - border_width, border_width], [border_width, border_width]);
                ctx.fillRect(steps[0], steps[1], radius, radius);
            } else {
                ctx.fillStyle = bg_color;
                ctx.fillRect(0, 0, radius, radius);
            }
            return canvas;
        } else if (corner_effect == 'bevel') {
            steps = rotationSteps(r_type, [0, 0, 0, radius, radius, 0, 0, 0], [0, 0, radius, radius, radius, 0, 0, 0], [0, 0, radius, radius, 0, radius, 0, 0], [radius, radius, radius, 0, 0, radius, radius, radius]);
            ctx.fillStyle = bg_color;
            ctx.beginPath();
            ctx.moveTo(steps[0], steps[1]);
            ctx.lineTo(steps[2], steps[3]);
            ctx.lineTo(steps[4], steps[5]);
            ctx.lineTo(steps[6], steps[7]);
            ctx.fill();
            if (border_width > 0 && border_width < radius) {
                ctx.strokeStyle = border_color;
                ctx.lineWidth = border_width;
                ctx.beginPath();
                steps = rotationSteps(r_type, [0, radius, radius, 0], [0, 0, radius, radius], [radius, radius, 0, 0], [0, radius, radius, 0]);
                ctx.moveTo(steps[0], steps[1]);
                ctx.lineTo(steps[2], steps[3]);
                ctx.stroke();
            }
            return canvas;
        }

        steps = rotationSteps(r_type,
					[0, 0, radius, 0, radius, 0, 0, radius, 0, 0],
					[radius, 0, radius, radius, radius, 0, 0, 0, 0, 0],
					[0, radius, radius, radius, 0, radius, 0, 0, 0, radius],
					[radius, radius, radius, 0, radius, 0, 0, radius, radius, radius]);

        ctx.fillStyle = bg_color;
        ctx.beginPath();
        ctx.moveTo(steps[0], steps[1]);
        ctx.lineTo(steps[2], steps[3]);
        if (r_type == 'br') ctx.bezierCurveTo(steps[4], steps[5], radius, radius, steps[6], steps[7]);
        else ctx.bezierCurveTo(steps[4], steps[5], 0, 0, steps[6], steps[7]);
        ctx.lineTo(steps[8], steps[9]);
        ctx.fill();

        // draw border
        if (border_width > 0 && border_width < radius) {

            // offset caused by border
            var offset = border_width / 2;
            var ro = radius - offset;
            steps = rotationSteps(r_type,
				[ro, offset, ro, offset, offset, ro],
				[ro, ro, ro, offset, offset, offset],
				[ro, ro, offset, ro, offset, offset, offset, ro],
				[ro, offset, ro, offset, offset, ro, ro, ro]
			);

            curve_to = rotationSteps(r_type, [0, 0], [0, 0], [0, 0], [radius, radius]);

            ctx.strokeStyle = border_color;
            ctx.lineWidth = border_width;
            ctx.beginPath();
            // go to corner to begin curve
            ctx.moveTo(steps[0], steps[1]);
            // curve from righttop to leftbottom (for the tl canvas)
            ctx.bezierCurveTo(steps[2], steps[3], curve_to[0], curve_to[1], steps[4], steps[5]);
            ctx.stroke();

        }

        return canvas;

    };

    // create and append canvas element to parent
    var createCanvas = function(p, radius) {

        var elm = document.createElement('canvas');
        elm.setAttribute("height", radius);
        elm.setAttribute("width", radius);
        elm.style.display = "block";
        elm.style.position = "absolute";
        elm.className = "jrCorner";

        appendToParent(p, elm);

        if (!canvasSupport && isMSIE) { // no native canvas support
            if (typeof G_vmlCanvasManager == "object") { // use excanvas
                elm = G_vmlCanvasManager.initElement(elm);
            } else if (typeof G_vmlCMjrc == "object") { // use the stipped down version of excanvas
                elm = G_vmlCMjrc.i(elm);
            } else {
                throw Error('Could not find excanvas');
            }
        }

        return elm;
    };

    var appendToParent = function(p, elm) {
        if (p.is("table")) {
            p.children("tbody").children("tr:first").children("td:first").append(elm);
            p.css('display', 'block'); // only firefox seems to need this
        } else if (p.is("td")) {
            if (p.children(".JrcTdContainer").length === 0) {
                // only is msie you can absolute position a element inside a table cell, so we need a wrapper
                p.html('<div class="JrcTdContainer" style="padding:0px;position:relative;margin:-1px;zoom:1;">' + p.html() + '</div>');
                p.css('zoom', '1');
                if (isltMSIE7) { //  msie6 only
                    p.children(".JrcTdContainer").get(0).style.setExpression("height", "this.parentNode.offsetHeight");
                }

            }
            p.children(".JrcTdContainer").append(elm);

        } else {
            p.append(elm);
        }

    };

    // hide corners in ie print
    // (using canvas {display:none} doesnt work)
    if (isMSIE) {
        var ss = document.createStyleSheet();
        ss.media = 'print';
        ss.cssText = '.jrcIECanvasDiv { display:none !important; }';
    }

    // $.corner function
    var _corner = function(options) {

        // nothing to do || no support for native canvas or excanvas
        if (this.length == 0 || !(canvasSupport || isMSIE)) {
            return this;
        }

        if (options == "destroy") {
            return this.each(function() {
                var p, elm = $(this);
                if (elm.is(".jrcRounded")) {
                    if (typeof elm.data("ie6tmr.jrc") == 'number') window.clearInterval(elm.data("ie6tmr.jrc"));
                    if (elm.is("table")) p = elm.children("tbody").children("tr:first").children("td:first");
                    else if (elm.is("td")) p = elm.children(".JrcTdContainer");
                    else p = elm;
                    p.children(".jrCorner").remove();
                    elm.unbind('mouseleave.jrc').unbind('mouseenter.jrc').removeClass('jrcRounded').removeData('ie6tmr.jrc');
                    if (elm.is("td")) elm.html(elm.children(".JrcTdContainer").html());
                }
            });
        }

        // interpret the (string) argument
        var o = (options || "").toLowerCase();
        var radius = Num((o.match(/(\d+)px/) || [])[1]) || "auto"; // corner width
        var bg_arg = ((o.match(/(#[0-9a-f]+)/) || [])[1]) || "auto";  // strip color
        var re = /round|bevel|notch/; // Corner Effects
        var fx = ((o.match(re) || ['round'])[0]);
        var hover = /hover/.test(o);
        var overSized = /oversized/.test(o);
        var hiddenparent_arg = o.match("hiddenparent");
        if (isMSIE) {
            var re = /ie6nofix|ie6fixinit|ie6fixexpr|ie6fixonload|ie6fixwidthint|ie6fixheightint|ie6fixbothint/; // Type of iefix
            var ie6Fix = ((o.match(re) || ['ie6fixinit'])[0]);
        }

        //var edges = { T:0, B:1 };
        var opts = {
            tl: /top|left|tl/.test(o),
            tr: /top|right|tr/.test(o),
            bl: /bottom|left|bl/.test(o),
            br: /bottom|right|br/.test(o)
        };

        // round all corners if nothing is set
        if (!opts.tl && !opts.tr && !opts.bl && !opts.br) opts = { tl: 1, tr: 1, bl: 1, br: 1 };

        this.each(function() {

            var elm = $(this), rbg = null, bg, s, b, pr;
            var a = this;
            var elm_display = getStyle(this, 'display');
            var elm_position = getStyle(this, 'position');
            var elm_lineheight = getStyle(this, 'lineHeight', 'line-height');

            if (bg_arg == "auto") { // no background color of the parent is set ...
                s = elm.siblings(".jrcRounded:eq(0)");
                if (s.length > 0) { // sibling already has the parent background color stored?
                    b = s.data("rbg.jrc");
                    if (typeof b == "string") {
                        rbg = b;
                    }
                }
            }

            if (hiddenparent_arg || rbg === null) {
                // temporary show hidden parent (wm.morgun) + search for background color
                var current_p = this.parentNode, hidden_parents = new Array(), a = 0;
                while ((typeof current_p == 'object') && !isElm(current_p, 'html')) {

                    if (hiddenparent_arg && getStyle(current_p, 'display') == 'none') {
                        hidden_parents.push({
                            originalvisibility: getStyle(current_p, 'visibility'),
                            elm: current_p
                        });
                        current_p.style.display = 'block';
                        current_p.style.visibility = 'hidden';
                    }
                    var pbg = getStyle(current_p, 'backgroundColor', 'background-color');
                    if (rbg === null && pbg != "transparent" && pbg != "rgba(0, 0, 0, 0)") {
                        rbg = pbg;
                    }

                    current_p = current_p.parentNode;

                }

                if (rbg === null) rbg = "#ffffff";
            }

            // store the parent background color
            if (bg_arg == "auto") {
                bg = rbg;
                elm.data("rbg.jrc", rbg);
            } else {
                bg = bg_arg;
            }

            // if element is hidden we cant get the size..
            if (elm_display == 'none') {
                var originalvisibility = getStyle(this, 'visibility');
                this.style.display = 'block';
                this.style.visibility = 'hidden';
                var ishidden = true;
            } else {
                var ishiddden = false;
            }

            // save width/height
            var elm_height = elm.height();
            var elm_width = elm.width();

            // hover (optional argument - for a alterative to #roundedelement:hover)
            if (hover) {

                var newOptions = o.replace(/hover|ie6nofix|ie6fixinit|ie6fixexpr|ie6fixonload|ie6fixwidthint|ie6fixheightint|ie6fixbothint/g, "");
                if (ie6Fix != 'ie6nofix') newOptions = "ie6fixinit " + newOptions;

                elm.bind("mouseenter.jrc", function() {
                    elm.addClass('jrcHover');
                    elm.corner(newOptions);
                });
                elm.bind("mouseleave.jrc", function() {
                    elm.removeClass('jrcHover');
                    elm.corner(newOptions);
                });

            }

            // msie6 rendering bugs 
            if (isltMSIE7 && ie6Fix != 'ie6nofix') {
                this.style.zoom = 1;

                //if (this.currentStyle['height'] == 'auto') {
                //	elm.height(elm_height); 
                //}

                // http://www.pmob.co.uk/temp/onepxgap.htm
                if (ie6Fix != 'ie6fixexpr') {
                    if (elm.width() % 2 != 0) elm.width(elm.width() + 1);
                    if (elm.height() % 2 != 0) elm.height(elm.height() + 1);
                }

                $(window).load(function() {
                    if (ie6Fix == 'ie6fixonload') {
                        if (elm.css('height') == 'auto') elm.height(elm.css('height'));
                        if (elm.width() % 2 != 0) elm.width(elm.width() + 1);
                        if (elm.height() % 2 != 0) elm.height(elm.height() + 1);
                    } else if (ie6Fix == 'ie6fixwidthint' || ie6Fix == 'ie6fixheightint' || ie6Fix == 'ie6fixbothint') {
                        var myInterval, ie6FixFunction;
                        if (ie6Fix == 'ie6fixheightint') {
                            ie6FixFunction = function() {
                                elm.height('auto');
                                var elm_height = elm.height();
                                if (elm_height % 2 != 0) elm_height = elm_height + 1;
                                elm.css({ height: elm_height });
                            };
                        } else if (ie6Fix == 'ie6fixwidthint') {
                            ie6FixFunction = function() {
                                elm.width('auto');
                                var elm_width = elm.width();
                                if (elm_width % 2 != 0) elm_width = elm_width + 1;
                                elm.css({ width: elm_width });
                                elm.data('lastWidth.jrc', elm.get(0).offsetWidth);
                            };
                        } else if (ie6Fix == 'ie6fixbothint') {
                            ie6FixFunction = function() {
                                elm.width('auto');
                                elm.height('auto');
                                var elm_width = elm.width();
                                var elm_height = elm.height();
                                if (elm_height % 2 != 0) elm_height = elm_height + 1;
                                if (elm_width % 2 != 0) elm_width = elm_width + 1;
                                elm.css({ width: elm_width, height: elm_height });
                            };
                        }
                        myInterval = window.setInterval(ie6FixFunction, 100);
                        elm.data("ie6tmr.jrc", myInterval);
                    }
                });

                // ie6fixwidthint|ie6fixheightint

                //this.style.setExpression("height","parseInt(this.clientHeight) % 2 != 0 ? parseInt(this.style.height)-1 : parseInt(this.style.height)"); 
                //alert(this.style.height);
                //if (elm_lineheight != 'normal' && elm_height < elm_lineheight) {
                // 	elm.css('lineHeight', elm_height);
                //}
            }

            // get lowest height/width
            /*
            var arr = [this.offsetHeight,this.offsetWidth];
            if (elm_height != 0) arr[arr.length] = elm_height;
            if (elm_width != 0) arr[arr.length] = elm_width;
            var widthHeightSmallest = getMin(arr);*/

            var widthHeightSmallest = elm_height < elm_width ? this.offsetHeight : this.offsetWidth;


            // the size of the corner is not defined...
            if (radius == "auto") {
                radius = widthHeightSmallest / 2;
                if (radius > 10) radius = widthHeightSmallest / 4;
            }

            // the size of the corner can't be to high
            if (radius > widthHeightSmallest / 2 && !overSized) {
                radius = widthHeightSmallest / 2;
            }

            radius = Math.floor(radius);

            // get border width
            var border_t = getBorderWidth(this, 'Top');
            var border_r = getBorderWidth(this, 'Right');
            var border_b = getBorderWidth(this, 'Bottom');
            var border_l = getBorderWidth(this, 'Left');

            // some css thats required in order to position the canvas elements
            if (elm_position == 'static' && !isElm(this, 'td')) {
                //elm.css('position','relative'); 
                this.style.position = 'relative';
                // only needed for ie6 and (ie7 in Quirks mode) , CSS1Compat == Strict mode
            } else if (elm_position == 'fixed' && isMSIE && !(document.compatMode == 'CSS1Compat' && !isltMSIE7)) {
                //elm.css('position','absolute');
                this.style.position = 'absolute';
            }

            // overflow hidden + border makes the real borders at the corners visible
            // so we set overflow to visible when it has borders
            if (border_t + border_r + border_b + border_l > 0) {
                //elm.css('overflow','visible');
                this.style.overflow = 'visible';
            }

            // restore css
            if (ishidden) elm.css({ display: 'none', visibility: originalvisibility });

            //  restore css of hidden parents
            if (typeof hidden_parents != "undefined") {
                for (var i = 0; i < hidden_parents.length; i++) {
                    hidden_parents[i].elm.style.display = 'none';
                    hidden_parents[i].elm.style.visibility = hidden_parents[i].originalvisibility;
                }
            }

            var p_top = 0 - border_t,
				p_right = 0 - border_r,
				p_bottom = 0 - border_b,
				p_left = 0 - border_l;

            var mhc = (elm.find("canvas").length > 0);

            if (mhc) {
                // pr is the parent of the canvas elements
                if (isElm(this, 'table')) pr = elm.children("tbody").children("tr:first").children("td:first");
                else if (isElm(this, 'td')) pr = elm.children(".JrcTdContainer");
                else pr = elm;
            }

            // draw Corners in canvas elements (createCanvas also appends it to parent)
            if (opts.tl) {
                // use lowest border size
                bordersWidth = border_t < border_l ? border_t : border_l;
                // remove old corner
                if (mhc) pr.children("canvas.jrcTL").remove();
                // create,append and draw corner
                var tl = drawCorner(createCanvas(elm, radius), radius, 'tl', bg, bordersWidth, getBorderColor(this, 'Top'), fx);
                // position corner
                $(tl).css({ left: p_left, top: p_top }).addClass('jrcTL');
            }
            if (opts.tr) {
                bordersWidth = border_t < border_r ? border_t : border_r;
                if (mhc) pr.children("canvas.jrcTR").remove();
                var tr = drawCorner(createCanvas(elm, radius), radius, 'tr', bg, bordersWidth, getBorderColor(this, 'Top'), fx);
                $(tr).css({ right: p_right, top: p_top }).addClass('jrcTR');
            }
            if (opts.bl) {
                bordersWidth = border_b < border_l ? border_b : border_l;
                if (mhc) pr.children("canvas.jrcBL").remove();
                var bl = drawCorner(createCanvas(elm, radius), radius, 'bl', bg, bordersWidth, getBorderColor(this, 'Bottom'), fx);
                $(bl).css({ left: p_left, bottom: p_bottom }).addClass('jrcBL');
            }
            if (opts.br) {
                bordersWidth = border_b < border_r ? border_b : border_r;
                if (mhc) pr.children("canvas.jrcBR").remove();
                var br = drawCorner(createCanvas(elm, radius), radius, 'br', bg, bordersWidth, getBorderColor(this, 'Bottom'), fx);
                $(br).css({ right: p_right, bottom: p_bottom }).addClass('jrcBR');
            }

            // we need this to hide it in ie print
            if (isMSIE) elm.children('canvas.jrCorner').children('div').addClass('jrcIECanvasDiv');

            // based on fix: http://www.ilikespam.com/blog/the-odd-pixel-bug
            if (isltMSIE7 && ie6Fix == 'ie6fixexpr') {
                if (opts.bl) {
                    bl.style.setExpression("bottom", "this.parentNode.offsetHeight % 2 == 0 || this.parentNode.offsetWidth % 2 == 0 ? 0-(parseInt(this.parentNode.currentStyle['borderBottomWidth'])) : 0-(parseInt(this.parentNode.currentStyle['borderBottomWidth'])+1)");
                }
                if (opts.br) {
                    br.style.setExpression("right", "this.parentNode.offsetWidth  % 2 == 0 || this.parentNode.offsetWidth % 2 == 0 ? 0-(parseInt(this.parentNode.currentStyle['borderRightWidth']))  : 0-(parseInt(this.parentNode.currentStyle['borderRightWidth'])+1)");
                    br.style.setExpression("bottom", "this.parentNode.offsetHeight % 2 == 0 || this.parentNode.offsetWidth % 2 == 0 ? 0-(parseInt(this.parentNode.currentStyle['borderBottomWidth'])) : 0-(parseInt(this.parentNode.currentStyle['borderBottomWidth'])+1)");
                }
                if (opts.tr) {
                    tr.style.setExpression("right", "this.parentNode.offsetWidth   % 2 == 0 || this.parentNode.offsetWidth % 2 == 0 ? 0-(parseInt(this.parentNode.currentStyle['borderRightWidth']))  : 0-(parseInt(this.parentNode.currentStyle['borderRightWidth'])+1)");
                }
            }

            elm.addClass('jrcRounded');


        });

        // callback function (is called when the last element is rounded)
        if (typeof arguments[1] == "function") arguments[1](this);
        return this;

    };

    $.fn.corner = _corner;

})(jQuery);

