From 6f5f0b2b3e71c12920220e9d323cb19c48b2a052 Mon Sep 17 00:00:00 2001 From: Dan Vanderkam Date: Sun, 31 Mar 2013 23:46:54 -0400 Subject: [PATCH] Merge in hairlines and super-annotations work. --- dygraph-interaction-model.js | 79 ++++++++++++++++++++++++++++++-------------- dygraph.js | 34 ++++++++++++++++--- plugins/legend.js | 8 ++--- push-to-web.sh | 2 +- tests/plugins.html | 2 +- 5 files changed, 90 insertions(+), 35 deletions(-) diff --git a/dygraph-interaction-model.js b/dygraph-interaction-model.js index 27c18b7..6f27968 100644 --- a/dygraph-interaction-model.js +++ b/dygraph-interaction-model.js @@ -281,6 +281,7 @@ Dygraph.Interaction.moveZoom = function(event, g, context) { }; /** + * TODO(danvk): move this logic into dygraph.js * @param {Dygraph} g * @param {Event} event * @param {Object} context @@ -291,38 +292,56 @@ Dygraph.Interaction.treatMouseOpAsClick = function(g, event, context) { var selectedPoint = null; - // Find out if the click occurs on a point. This only matters if there's a - // pointClickCallback. - if (pointClickCallback) { - var closestIdx = -1; - var closestDistance = Number.MAX_VALUE; - - // check if the click was on a particular point. - for (var i = 0; i < g.selPoints_.length; i++) { - var p = g.selPoints_[i]; - var distance = Math.pow(p.canvasx - context.dragEndX, 2) + - Math.pow(p.canvasy - context.dragEndY, 2); - if (!isNaN(distance) && - (closestIdx == -1 || distance < closestDistance)) { - closestDistance = distance; - closestIdx = i; - } + // Find out if the click occurs on a point. + var closestIdx = -1; + var closestDistance = Number.MAX_VALUE; + + // check if the click was on a particular point. + for (var i = 0; i < g.selPoints_.length; i++) { + var p = g.selPoints_[i]; + var distance = Math.pow(p.canvasx - context.dragEndX, 2) + + Math.pow(p.canvasy - context.dragEndY, 2); + if (!isNaN(distance) && + (closestIdx == -1 || distance < closestDistance)) { + closestDistance = distance; + closestIdx = i; } + } - // Allow any click within two pixels of the dot. - var radius = g.getNumericOption('highlightCircleSize') + 2; - if (closestDistance <= radius * radius) { - selectedPoint = g.selPoints_[closestIdx]; - } + // Allow any click within two pixels of the dot. + var radius = g.getNumericOption('highlightCircleSize') + 2; + if (closestDistance <= radius * radius) { + selectedPoint = g.selPoints_[closestIdx]; } if (selectedPoint) { - pointClickCallback.call(g, event, selectedPoint); + var e = { + cancelable: true, + point: selectedPoint, + canvasx: context.dragEndX, + canvasy: context.dragEndY + }; + var defaultPrevented = g.cascadeEvents_('pointClick', e); + if (defaultPrevented) { + // Note: this also prevents click / clickCallback from firing. + return; + } + if (pointClickCallback) { + pointClickCallback.call(g, event, selectedPoint); + } } - // TODO(danvk): pass along more info about the points, e.g. 'x' - if (clickCallback) { - clickCallback.call(g, event, g.lastx_, g.selPoints_); + var e = { + xval: g.lastx_, // closest point by x value + pts: g.selPoints_, + canvasx: context.dragEndX, + canvasy: context.dragEndY + }; + if (!g.cascadeEvents_('click', e)) { + if (clickCallback) { + // TODO(danvk): pass along more info about the points, e.g. 'x' + clickCallback.call(g, event, g.lastx_, g.selPoints_); + } } }; @@ -631,6 +650,16 @@ Dygraph.Interaction.defaultModel = { context.cancelNextDblclick = false; return; } + + // Give plugins a chance to grab this event. + var e = { + canvasx: context.dragEndX, + canvasy: context.dragEndY + }; + if (g.cascadeEvents_('dblclick', e)) { + return; + } + if (event.altKey || event.shiftKey) { return; } diff --git a/dygraph.js b/dygraph.js index 27b1b36..e704dde 100644 --- a/dygraph.js +++ b/dygraph.js @@ -537,7 +537,15 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { var plugins = Dygraph.PLUGINS.concat(this.getOption('plugins')); for (var i = 0; i < plugins.length; i++) { var Plugin = plugins[i]; - var pluginInstance = new Plugin(); + + // the plugins option may contain either plugin classes or instances. + var pluginInstance; + if (typeof(Plugin.activate) !== 'undefined') { + pluginInstance = Plugin; + } else { + pluginInstance = new Plugin(); + } + var pluginDict = { plugin: pluginInstance, events: {}, @@ -578,12 +586,12 @@ Dygraph.prototype.__init__ = function(div, file, attrs) { /** * Triggers a cascade of events to the various plugins which are interested in them. - * Returns true if the "default behavior" should be performed, i.e. if none of - * the event listeners called event.preventDefault(). + * Returns true if the "default behavior" should be prevented, i.e. if one + * of the event listeners called event.preventDefault(). * @private */ Dygraph.prototype.cascadeEvents_ = function(name, extra_props) { - if (!(name in this.eventListeners_)) return true; + if (!(name in this.eventListeners_)) return false; // QUESTION: can we use objects & prototypes to speed this up? var e = { @@ -2278,6 +2286,7 @@ Dygraph.prototype.isSeriesLocked = function() { */ Dygraph.prototype.loadedEvent_ = function(data) { this.rawData_ = this.parseCSV_(data); + this.cascadeDataDidUpdateEvent_(); this.predraw_(); }; @@ -3393,6 +3402,17 @@ Dygraph.prototype.parseDataTable_ = function(data) { }; /** + * Signals to plugins that the chart data has updated. + * This happens after the data has updated but before the chart has redrawn. + */ +Dygraph.prototype.cascadeDataDidUpdateEvent_ = function() { + // TODO(danvk): there are some issues checking xAxisRange() and using + // toDomCoords from handlers of this event. The visible range should be set + // when the chart is drawn, not derived from the data. + this.cascadeEvents_('dataDidUpdate', {}); +}; + +/** * Get the CSV data. If it's in a function, call that function. If it's in a * file, do an XMLHttpRequest to get it. * @private @@ -3407,11 +3427,13 @@ Dygraph.prototype.start_ = function() { if (Dygraph.isArrayLike(data)) { this.rawData_ = this.parseArray_(data); + this.cascadeDataDidUpdateEvent_(); this.predraw_(); } else if (typeof data == 'object' && typeof data.getColumnRange == 'function') { // must be a DataTable from gviz. this.parseDataTable_(data); + this.cascadeDataDidUpdateEvent_(); this.predraw_(); } else if (typeof data == 'string') { // Heuristic: a newline means it's CSV data. Otherwise it's an URL. @@ -3501,6 +3523,10 @@ Dygraph.prototype.updateOptions = function(input_attrs, block_redraw) { this.attributes_.reparseSeries(); if (file) { + // This event indicates that the data is about to change, but hasn't yet. + // TODO(danvk): support cancelation of the update via this event. + this.cascadeEvents_('dataWillUpdate', {}); + this.file_ = file; if (!block_redraw) this.start_(); } else { diff --git a/plugins/legend.js b/plugins/legend.js index d0a6fc8..c88727a 100644 --- a/plugins/legend.js +++ b/plugins/legend.js @@ -38,7 +38,7 @@ legend.prototype.toString = function() { }; // (defined below) -var generateLegendHTML, generateLegendDashHTML; +var generateLegendDashHTML; /** * This is called during the dygraph constructor, after options have been set @@ -128,7 +128,7 @@ legend.prototype.select = function(e) { var xValue = e.selectedX; var points = e.selectedPoints; - var html = generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_); + var html = legend.generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_); this.legend_div_.innerHTML = html; }; @@ -137,7 +137,7 @@ legend.prototype.deselect = function(e) { var oneEmWidth = calculateEmWidthInDiv(this.legend_div_); this.one_em_width_ = oneEmWidth; - var html = generateLegendHTML(e.dygraph, undefined, undefined, oneEmWidth); + var html = legend.generateLegendHTML(e.dygraph, undefined, undefined, oneEmWidth); this.legend_div_.innerHTML = html; }; @@ -187,7 +187,7 @@ legend.prototype.destroy = function() { * relevant when displaying a legend with no selection (i.e. {legend: * 'always'}) and with dashed lines. */ -generateLegendHTML = function(g, x, sel_points, oneEmWidth) { +legend.generateLegendHTML = function(g, x, sel_points, oneEmWidth) { // TODO(danvk): deprecate this option in place of {legend: 'never'} if (g.getOption('showLabelsOnHighlight') !== true) return ''; diff --git a/push-to-web.sh b/push-to-web.sh index 785c4a1..a2811d0 100755 --- a/push-to-web.sh +++ b/push-to-web.sh @@ -30,7 +30,7 @@ if [ -s docs/options.html ] ; then find . -path ./.git -prune -o -print | xargs chmod a+rX # Copy everything to the site. - rsync -avzr gallery common tests jsdoc experimental plugins datahandler polyfills $site \ + rsync -avzr gallery common tests jsdoc experimental plugins datahandler polyfills extras $site \ && \ rsync -avzr --copy-links dashed-canvas.js dygraph*.js gadget.xml excanvas.js thumbnail.png screenshot.png $temp_dir/* $site/ else diff --git a/tests/plugins.html b/tests/plugins.html index 2d084d0..49a11cd 100644 --- a/tests/plugins.html +++ b/tests/plugins.html @@ -9,7 +9,7 @@ - +

Plugins Demo

-- 2.7.4