Skip to content

Commit

Permalink
Update excanvas and remove previous eventholder work-around to fix ho…
Browse files Browse the repository at this point in the history
…ver/click bug in IE 8 (test case by Ara Anjargolian); add support for setting the redraw overlay period and set it to 60 frames/s
  • Loading branch information
OleLaursen committed May 13, 2011
1 parent 9f060ec commit f6cebd3
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 70 deletions.
9 changes: 9 additions & 0 deletions API.txt
Expand Up @@ -679,6 +679,10 @@ Customizing the grid
mouseActiveRadius: number mouseActiveRadius: number
} }


interaction: {
redrawOverlayInterval: number or -1
}

The grid is the thing with the axes and a number of ticks. Many of the The grid is the thing with the axes and a number of ticks. Many of the
things in the grid are configured under the individual axes, but not things in the grid are configured under the individual axes, but not
all. "color" is the color of the grid itself whereas "backgroundColor" all. "color" is the color of the grid itself whereas "backgroundColor"
Expand Down Expand Up @@ -798,6 +802,11 @@ If you want to disable interactivity for a specific data series, you
can set "hoverable" and "clickable" to false in the options for that can set "hoverable" and "clickable" to false in the options for that
series, like this { data: [...], label: "Foo", clickable: false }. series, like this { data: [...], label: "Foo", clickable: false }.


"redrawOverlayInterval" specifies the maximum time to delay a redraw
of interactive things (this works as a rate limiting device). The
default is capped to 60 frames per second. You can set it to -1 to
disable the rate limiting.



Specifying gradients Specifying gradients
==================== ====================
Expand Down
131 changes: 66 additions & 65 deletions excanvas.js
Expand Up @@ -49,6 +49,8 @@ if (!document.createElement('canvas').getContext) {
var Z = 10; var Z = 10;
var Z2 = Z / 2; var Z2 = Z / 2;


var IE_VERSION = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];

/** /**
* This funtion is assigned to the <canvas> elements as element.getContext(). * This funtion is assigned to the <canvas> elements as element.getContext().
* @this {HTMLElement} * @this {HTMLElement}
Expand Down Expand Up @@ -88,17 +90,15 @@ if (!document.createElement('canvas').getContext) {
return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;'); return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
} }


function addNamespacesAndStylesheet(doc) { function addNamespace(doc, prefix, urn) {
// create xmlns if (!doc.namespaces[prefix]) {
if (!doc.namespaces['g_vml_']) { doc.namespaces.add(prefix, urn, '#default#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');
} }
}

function addNamespacesAndStylesheet(doc) {
addNamespace(doc, 'g_vml_', 'urn:schemas-microsoft-com:vml');
addNamespace(doc, 'g_o_', 'urn:schemas-microsoft-com:office:office');


// Setup default CSS. Only add one style sheet per document // Setup default CSS. Only add one style sheet per document
if (!doc.styleSheets['ex_canvas_']) { if (!doc.styleSheets['ex_canvas_']) {
Expand All @@ -115,13 +115,11 @@ if (!document.createElement('canvas').getContext) {


var G_vmlCanvasManager_ = { var G_vmlCanvasManager_ = {
init: function(opt_doc) { init: function(opt_doc) {
if (/MSIE/.test(navigator.userAgent) && !window.opera) { var doc = opt_doc || document;
var doc = opt_doc || document; // Create a dummy element so that IE will allow canvas elements to be
// Create a dummy element so that IE will allow canvas elements to be // recognized.
// recognized. doc.createElement('canvas');
doc.createElement('canvas'); doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
}
}, },


init_: function(doc) { init_: function(doc) {
Expand Down Expand Up @@ -398,9 +396,7 @@ if (!document.createElement('canvas').getContext) {
var end = styleString.indexOf(')', start + 1); var end = styleString.indexOf(')', start + 1);
var parts = styleString.substring(start + 1, end).split(','); var parts = styleString.substring(start + 1, end).split(',');
// add alpha if needed // add alpha if needed
if (parts.length == 4 && styleString.substr(3, 1) == 'a') { if (parts.length != 4 || styleString.charAt(3) != 'a') {
alpha = Number(parts[3]);
} else {
parts[3] = 1; parts[3] = 1;
} }
return parts; return parts;
Expand All @@ -415,7 +411,7 @@ if (!document.createElement('canvas').getContext) {
} }


function hslToRgb(parts){ function hslToRgb(parts){
var r, g, b; var r, g, b, h, s, l;
h = parseFloat(parts[0]) / 360 % 360; h = parseFloat(parts[0]) / 360 % 360;
if (h < 0) if (h < 0)
h++; h++;
Expand Down Expand Up @@ -452,7 +448,13 @@ if (!document.createElement('canvas').getContext) {
return m1; return m1;
} }


var processStyleCache = {};

function processStyle(styleString) { function processStyle(styleString) {
if (styleString in processStyleCache) {
return processStyleCache[styleString];
}

var str, alpha = 1; var str, alpha = 1;


styleString = String(styleString); styleString = String(styleString);
Expand All @@ -465,19 +467,19 @@ if (!document.createElement('canvas').getContext) {
if (parts[i].indexOf('%') != -1) { if (parts[i].indexOf('%') != -1) {
n = Math.floor(percent(parts[i]) * 255); n = Math.floor(percent(parts[i]) * 255);
} else { } else {
n = Number(parts[i]); n = +parts[i];
} }
str += decToHex[clamp(n, 0, 255)]; str += decToHex[clamp(n, 0, 255)];
} }
alpha = parts[3]; alpha = +parts[3];
} else if (/^hsl/.test(styleString)) { } else if (/^hsl/.test(styleString)) {
var parts = getRgbHslContent(styleString); var parts = getRgbHslContent(styleString);
str = hslToRgb(parts); str = hslToRgb(parts);
alpha = parts[3]; alpha = parts[3];
} else { } else {
str = colorData[styleString] || styleString; str = colorData[styleString] || styleString;
} }
return {color: str, alpha: alpha}; return processStyleCache[styleString] = {color: str, alpha: alpha};
} }


var DEFAULT_STYLE = { var DEFAULT_STYLE = {
Expand Down Expand Up @@ -550,25 +552,22 @@ if (!document.createElement('canvas').getContext) {
style.size + 'px ' + style.family; style.size + 'px ' + style.family;
} }


var lineCapMap = {
'butt': 'flat',
'round': 'round'
};

function processLineCap(lineCap) { function processLineCap(lineCap) {
switch (lineCap) { return lineCapMap[lineCap] || 'square';
case 'butt':
return 'flat';
case 'round':
return 'round';
case 'square':
default:
return 'square';
}
} }


/** /**
* This class implements CanvasRenderingContext2D interface as described by * This class implements CanvasRenderingContext2D interface as described by
* the WHATWG. * the WHATWG.
* @param {HTMLElement} surfaceElement The element that the 2D context should * @param {HTMLElement} canvasElement The element that the 2D context should
* be associated with * be associated with
*/ */
function CanvasRenderingContext2D_(surfaceElement) { function CanvasRenderingContext2D_(canvasElement) {
this.m_ = createMatrixIdentity(); this.m_ = createMatrixIdentity();


this.mStack_ = []; this.mStack_ = [];
Expand All @@ -587,14 +586,19 @@ if (!document.createElement('canvas').getContext) {
this.font = '10px sans-serif'; this.font = '10px sans-serif';
this.textAlign = 'left'; this.textAlign = 'left';
this.textBaseline = 'alphabetic'; this.textBaseline = 'alphabetic';
this.canvas = surfaceElement; this.canvas = canvasElement;

var cssText = 'width:' + canvasElement.clientWidth + 'px;height:' +
canvasElement.clientHeight + 'px;overflow:hidden;position:absolute';
var el = canvasElement.ownerDocument.createElement('div');
el.style.cssText = cssText;
canvasElement.appendChild(el);


var el = surfaceElement.ownerDocument.createElement('div'); var overlayEl = el.cloneNode(false);
el.style.width = surfaceElement.clientWidth + 'px'; // Use a non transparent background.
el.style.height = surfaceElement.clientHeight + 'px'; overlayEl.style.backgroundColor = 'red';
el.style.overflow = 'hidden'; overlayEl.style.filter = 'alpha(opacity=0)';
el.style.position = 'absolute'; canvasElement.appendChild(overlayEl);
surfaceElement.appendChild(el);


this.element_ = el; this.element_ = el;
this.arcScaleX_ = 1; this.arcScaleX_ = 1;
Expand All @@ -618,14 +622,14 @@ if (!document.createElement('canvas').getContext) {
}; };


contextPrototype.moveTo = function(aX, aY) { contextPrototype.moveTo = function(aX, aY) {
var p = this.getCoords_(aX, aY); var p = getCoords(this, aX, aY);
this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y}); this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
this.currentX_ = p.x; this.currentX_ = p.x;
this.currentY_ = p.y; this.currentY_ = p.y;
}; };


contextPrototype.lineTo = function(aX, aY) { contextPrototype.lineTo = function(aX, aY) {
var p = this.getCoords_(aX, aY); var p = getCoords(this, aX, aY);
this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y}); this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});


this.currentX_ = p.x; this.currentX_ = p.x;
Expand All @@ -635,9 +639,9 @@ if (!document.createElement('canvas').getContext) {
contextPrototype.bezierCurveTo = function(aCP1x, aCP1y, contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
aCP2x, aCP2y, aCP2x, aCP2y,
aX, aY) { aX, aY) {
var p = this.getCoords_(aX, aY); var p = getCoords(this, aX, aY);
var cp1 = this.getCoords_(aCP1x, aCP1y); var cp1 = getCoords(this, aCP1x, aCP1y);
var cp2 = this.getCoords_(aCP2x, aCP2y); var cp2 = getCoords(this, aCP2x, aCP2y);
bezierCurveTo(this, cp1, cp2, p); bezierCurveTo(this, cp1, cp2, p);
}; };


Expand All @@ -660,8 +664,8 @@ if (!document.createElement('canvas').getContext) {
// the following is lifted almost directly from // the following is lifted almost directly from
// http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes


var cp = this.getCoords_(aCPx, aCPy); var cp = getCoords(this, aCPx, aCPy);
var p = this.getCoords_(aX, aY); var p = getCoords(this, aX, aY);


var cp1 = { var cp1 = {
x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_), x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
Expand Down Expand Up @@ -692,9 +696,9 @@ if (!document.createElement('canvas').getContext) {
// that can be represented in binary // that can be represented in binary
} }


var p = this.getCoords_(aX, aY); var p = getCoords(this, aX, aY);
var pStart = this.getCoords_(xStart, yStart); var pStart = getCoords(this, xStart, yStart);
var pEnd = this.getCoords_(xEnd, yEnd); var pEnd = getCoords(this, xEnd, yEnd);


this.currentPath_.push({type: arcType, this.currentPath_.push({type: arcType,
x: p.x, x: p.x,
Expand Down Expand Up @@ -808,7 +812,7 @@ if (!document.createElement('canvas').getContext) {
throw Error('Invalid number of arguments'); throw Error('Invalid number of arguments');
} }


var d = this.getCoords_(dx, dy); var d = getCoords(this, dx, dy);


var w2 = sw / 2; var w2 = sw / 2;
var h2 = sh / 2; var h2 = sh / 2;
Expand Down Expand Up @@ -844,9 +848,9 @@ if (!document.createElement('canvas').getContext) {
// Bounding box calculation (need to minimize displayed area so that // Bounding box calculation (need to minimize displayed area so that
// filters don't waste time on unused pixels. // filters don't waste time on unused pixels.
var max = d; var max = d;
var c2 = this.getCoords_(dx + dw, dy); var c2 = getCoords(this, dx + dw, dy);
var c3 = this.getCoords_(dx, dy + dh); var c3 = getCoords(this, dx, dy + dh);
var c4 = this.getCoords_(dx + dw, dy + dh); var c4 = getCoords(this, dx + dw, dy + dh);


max.x = m.max(max.x, c2.x, c3.x, c4.x); max.x = m.max(max.x, c2.x, c3.x, c4.x);
max.y = m.max(max.y, c2.y, c3.y, c4.y); max.y = m.max(max.y, c2.y, c3.y, c4.y);
Expand Down Expand Up @@ -1015,8 +1019,8 @@ if (!document.createElement('canvas').getContext) {
var y0 = fillStyle.y0_ / arcScaleY; var y0 = fillStyle.y0_ / arcScaleY;
var x1 = fillStyle.x1_ / arcScaleX; var x1 = fillStyle.x1_ / arcScaleX;
var y1 = fillStyle.y1_ / arcScaleY; var y1 = fillStyle.y1_ / arcScaleY;
var p0 = ctx.getCoords_(x0, y0); var p0 = getCoords(ctx, x0, y0);
var p1 = ctx.getCoords_(x1, y1); var p1 = getCoords(ctx, x1, y1);
var dx = p1.x - p0.x; var dx = p1.x - p0.x;
var dy = p1.y - p0.y; var dy = p1.y - p0.y;
angle = Math.atan2(dx, dy) * 180 / Math.PI; angle = Math.atan2(dx, dy) * 180 / Math.PI;
Expand All @@ -1032,7 +1036,7 @@ if (!document.createElement('canvas').getContext) {
angle = 0; angle = 0;
} }
} else { } else {
var p0 = ctx.getCoords_(fillStyle.x0_, fillStyle.y0_); var p0 = getCoords(ctx, fillStyle.x0_, fillStyle.y0_);
focus = { focus = {
x: (p0.x - min.x) / width, x: (p0.x - min.x) / width,
y: (p0.y - min.y) / height y: (p0.y - min.y) / height
Expand Down Expand Up @@ -1105,11 +1109,8 @@ if (!document.createElement('canvas').getContext) {
this.currentPath_.push({type: 'close'}); this.currentPath_.push({type: 'close'});
}; };


/** function getCoords(ctx, aX, aY) {
* @private var m = ctx.m_;
*/
contextPrototype.getCoords_ = function(aX, aY) {
var m = this.m_;
return { return {
x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2, 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 y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
Expand Down Expand Up @@ -1270,7 +1271,7 @@ if (!document.createElement('canvas').getContext) {
break; break;
} }


var d = this.getCoords_(x + offset.x, y + offset.y); var d = getCoords(this, x + offset.x, y + offset.y);


lineStr.push('<g_vml_:line from="', -left ,' 0" to="', right ,' 0.05" ', lineStr.push('<g_vml_:line from="', -left ,' 0" to="', right ,' 0.05" ',
' coordsize="100 100" coordorigin="0 0"', ' coordsize="100 100" coordorigin="0 0"',
Expand Down

0 comments on commit f6cebd3

Please sign in to comment.