/** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: @VERSION * * Copyright (c) 2009-2011 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.Dragable * Plugin to make plotted points dragable by the user. */ $.jqplot.Dragable = function(options) { // Group: Properties this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false}); this.shapeRenderer = new $.jqplot.ShapeRenderer(); this.isDragging = false; this.isOver = false; this._ctx; this._elem; this._point; this._gridData; // prop: color // CSS color spec for the dragged point (and adjacent line segment or bar). this.color; // prop: constrainTo // Constrain dragging motion to an axis or to none. // Allowable values are 'none', 'x', 'y' this.constrainTo = 'none'; // 'x', 'y', or 'none'; $.extend(true, this, options); }; function DragCanvas() { $.jqplot.GenericCanvas.call(this); this.isDragging = false; this.isOver = false; this._neighbor; this._cursors = []; } DragCanvas.prototype = new $.jqplot.GenericCanvas(); DragCanvas.prototype.constructor = DragCanvas; // called within scope of series $.jqplot.Dragable.parseOptions = function (defaults, opts) { var options = opts || {}; this.plugins.dragable = new $.jqplot.Dragable(options.dragable); // since this function is called before series options are parsed, // we can set this here and it will be overridden if needed. this.isDragable = $.jqplot.config.enablePlugins; }; // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. // add a new DragCanvas object to the plot plugins to handle drawing on this new canvas. $.jqplot.Dragable.postPlotDraw = function() { // Memory Leaks patch if (this.plugins.dragable && this.plugins.dragable.highlightCanvas) { this.plugins.dragable.highlightCanvas.resetCanvas(); this.plugins.dragable.highlightCanvas = null; } this.plugins.dragable = {previousCursor:'auto', isOver:false}; this.plugins.dragable.dragCanvas = new DragCanvas(); this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding, 'jqplot-dragable-canvas', this._plotDimensions, this)); var dctx = this.plugins.dragable.dragCanvas.setContext(); }; //$.jqplot.preInitHooks.push($.jqplot.Dragable.init); $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Dragable.parseOptions); $.jqplot.postDrawHooks.push($.jqplot.Dragable.postPlotDraw); $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]); $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleDown]); $.jqplot.eventListenerHooks.push(['jqplotMouseUp', handleUp]); function initDragPoint(plot, neighbor) { var s = plot.series[neighbor.seriesIndex]; var drag = s.plugins.dragable; // first, init the mark renderer for the dragged point var smr = s.markerRenderer; var mr = drag.markerRenderer; mr.style = smr.style; mr.lineWidth = smr.lineWidth + 2.5; mr.size = smr.size + 5; if (!drag.color) { var rgba = $.jqplot.getColorComponents(smr.color); var newrgb = [rgba[0], rgba[1], rgba[2]]; var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]); drag.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')'; } mr.color = drag.color; mr.init(); var start = (neighbor.pointIndex > 0) ? neighbor.pointIndex - 1 : 0; var end = neighbor.pointIndex+2; drag._gridData = s.gridData.slice(start, end); } function handleMove(ev, gridpos, datapos, neighbor, plot) { if (plot.plugins.dragable.dragCanvas.isDragging) { var dc = plot.plugins.dragable.dragCanvas; var dp = dc._neighbor; var s = plot.series[dp.seriesIndex]; var drag = s.plugins.dragable; var gd = s.gridData; // compute the new grid position with any constraints. var x = (drag.constrainTo == 'y') ? dp.gridData[0] : gridpos.x; var y = (drag.constrainTo == 'x') ? dp.gridData[1] : gridpos.y; // compute data values for any listeners. var xu = s._xaxis.series_p2u(x); var yu = s._yaxis.series_p2u(y); // clear the canvas then redraw effect at new position. var ctx = dc._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // adjust our gridData for the new mouse position if (dp.pointIndex > 0) { drag._gridData[1] = [x, y]; } else { drag._gridData[0] = [x, y]; } plot.series[dp.seriesIndex].draw(dc._ctx, {gridData:drag._gridData, shadow:false, preventJqPlotSeriesDrawTrigger:true, color:drag.color, markerOptions:{color:drag.color, shadow:false}, trendline:{show:false}}); plot.target.trigger('jqplotSeriesPointChange', [dp.seriesIndex, dp.pointIndex, [xu,yu], [x,y]]); } else if (neighbor != null) { var series = plot.series[neighbor.seriesIndex]; if (series.isDragable) { var dc = plot.plugins.dragable.dragCanvas; if (!dc.isOver) { dc._cursors.push(ev.target.style.cursor); ev.target.style.cursor = "pointer"; } dc.isOver = true; } } else if (neighbor == null) { var dc = plot.plugins.dragable.dragCanvas; if (dc.isOver) { ev.target.style.cursor = dc._cursors.pop(); dc.isOver = false; } } } function handleDown(ev, gridpos, datapos, neighbor, plot) { var dc = plot.plugins.dragable.dragCanvas; dc._cursors.push(ev.target.style.cursor); if (neighbor != null) { var s = plot.series[neighbor.seriesIndex]; var drag = s.plugins.dragable; if (s.isDragable && !dc.isDragging) { dc._neighbor = neighbor; dc.isDragging = true; initDragPoint(plot, neighbor); drag.markerRenderer.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], dc._ctx); ev.target.style.cursor = "move"; plot.target.trigger('jqplotDragStart', [neighbor.seriesIndex, neighbor.pointIndex, gridpos, datapos]); } } // Just in case of a hickup, we'll clear the drag canvas and reset. else { var ctx = dc._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); dc.isDragging = false; } } function handleUp(ev, gridpos, datapos, neighbor, plot) { if (plot.plugins.dragable.dragCanvas.isDragging) { var dc = plot.plugins.dragable.dragCanvas; // clear the canvas var ctx = dc._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); dc.isDragging = false; // redraw the series canvas at the new point. var dp = dc._neighbor; var s = plot.series[dp.seriesIndex]; var drag = s.plugins.dragable; // compute the new grid position with any constraints. var x = (drag.constrainTo == 'y') ? dp.data[0] : datapos[s.xaxis]; var y = (drag.constrainTo == 'x') ? dp.data[1] : datapos[s.yaxis]; // var x = datapos[s.xaxis]; // var y = datapos[s.yaxis]; s.data[dp.pointIndex][0] = x; s.data[dp.pointIndex][1] = y; plot.drawSeries({preventJqPlotSeriesDrawTrigger:true}, dp.seriesIndex); dc._neighbor = null; ev.target.style.cursor = dc._cursors.pop(); plot.target.trigger('jqplotDragStop', [gridpos, datapos]); } } })(jQuery);