/** * @license * File: sigplot.slider.js * * Copyright (c) 2012-2014, Michael Ihde, All rights reserved. * Copyright (c) 2012-2014, Axios Inc., All rights reserved. * * This file is part of SigPlot. * * SigPlot is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation; either version 3.0 of the License, or * (at your option) any later version. This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the * GNU Lesser General Public License along with SigPlot. */ /* global mx */ /* global m */ (function( sigplot, mx, m, undefined ) { /** * @constructor * @param options * @returns {sigplot.SliderPlugin} */ sigplot.SliderPlugin = function(options) { this.options = (options !== undefined) ? options : {}; if (this.options.display === undefined) { this.options.display = true; } if (this.options.style === undefined) { this.options.style = {}; } if (this.options.direction === undefined) { this.options.direction = "vertical"; } this.position = undefined; this.location = undefined; }; sigplot.SliderPlugin.prototype = { init: function(plot) { this.plot = plot; var Mx = plot._Mx; // Register for mouse events var self = this; this.onmousemove = function(evt) { // Ignore if the slider isn't even visible if (self.location === undefined) { return; } // Or if the user wants to prevent a drag operation if (self.options.prevent_drag) { return; } // Ignore if the mouse is outside of the plot area if ((evt.xpos < Mx.l) || (evt.xpos > Mx.r)) { self.set_highlight(false); return; } if ((evt.ypos > Mx.b) || (evt.ypos < Mx.t)) { self.set_highlight(false); return; } // If the mouse is close, "highlight" the line var lineWidth = (self.options.style.lineWidth !== undefined) ? self.options.style.lineWidth : 1; // If we aren't dragging, then there is nothing else to do if (!self.dragging) { if (Mx.warpbox) { return; } // Don't highlight if a warpbox is being drawn if (self.options.direction === "vertical") { if (Math.abs(self.location - evt.xpos) < (lineWidth + 5)) { self.set_highlight(true); } else { self.set_highlight(false); } } else if (self.options.direction === "horizontal") { if (Math.abs(self.location - evt.ypos) < (lineWidth + 5)) { self.set_highlight(true); } else { self.set_highlight(false); } } else if (self.options.direction === "both") { if (Math.abs(self.location.x - evt.xpos) < (lineWidth + 5) && Math.abs(self.location.y - evt.ypos) < (lineWidth + 5)) { self.set_highlight(true); } else { self.set_highlight(false); } } return; } // If we are dragging, update the slider location var pos = mx.pixel_to_real(Mx, evt.xpos, evt.ypos); if (self.options.direction === "vertical") { self.location = evt.xpos; self.position = pos.x; } else if (self.options.direction === "horizontal") { self.location = evt.ypos; self.position = pos.y; } else if (self.options.direction === "both") { self.location.x = evt.xpos; self.position.x = pos.x; self.location.y = evt.ypos; self.position.y = pos.y; } // Refresh the plugin self.plot.redraw(); // Prevent any other plot default action at this point evt.preventDefault(); }; this.plot.addListener("mmove", this.onmousemove); this.onmousedown = function(evt) { if (self.location === undefined) { return; } // Or if the user wants to prevent a drag operation if (self.options.prevent_drag) { return; } if ((evt.xpos < Mx.l) || (evt.xpos > Mx.r)) { return; } if ((evt.ypos > Mx.b) || (evt.ypos < Mx.t)) { return; } var lineWidth = (self.options.style.lineWidth !== undefined) ? self.options.style.lineWidth : 1; if (self.options.direction === "vertical") { if (Math.abs(self.location - evt.xpos) < (lineWidth + 5)) { self.dragging = true; evt.preventDefault(); } } else if (self.options.direction === "horizontal") { if (Math.abs(self.location - evt.ypos) < (lineWidth + 5)) { self.dragging = true; evt.preventDefault(); } } else if (self.options.direction === "both") { if (Math.abs(self.location.x - evt.xpos) < (lineWidth + 5) && Math.abs(self.location.y - evt.ypos) < (lineWidth + 5)) { self.dragging = true; evt.preventDefault(); } } }; this.plot.addListener("mdown", this.onmousedown); this.onmouseup = function(evt) { if (!self.dragging) { return; } // We are no longer dragging self.dragging = false; // Issue a slider tag event var evt = document.createEvent('Event'); evt.source = self; evt.initEvent('slidertag', true, true); if (self.options.direction === "both") { evt.location = self.location ? JSON.parse(JSON.stringify(self.location)) : undefined; evt.position = self.position ? JSON.parse(JSON.stringify(self.position)) : undefined; } else { evt.location = self.location; evt.position = self.position; } var canceled = !mx.dispatchEvent(Mx, evt); // Issue a slider tag event var evt = document.createEvent('Event'); evt.initEvent('sliderdrag', true, true); if (self.options.direction === "both") { evt.location = self.location ? JSON.parse(JSON.stringify(self.location)) : undefined; evt.position = self.position ? JSON.parse(JSON.stringify(self.position)) : undefined; } else { evt.location = self.location; evt.position = self.position; } var canceled = !mx.dispatchEvent(Mx, evt); }; document.addEventListener("mouseup", this.onmouseup, false); }, addListener: function (what, callback) { var Mx = this.plot._Mx; var self = this; var wrapped_cb = function(evt) { if (evt.source === self) { return callback(evt); } }; mx.addEventListener(Mx, what, wrapped_cb, false); }, removeListener: function (what, callback) { var Mx = this.plot._Mx; mx.removeEventListener(Mx, what, callback, false); }, set_highlight: function(ishighlight) { if (ishighlight !== this.highlight) { this.highlight = ishighlight; this.plot.redraw(); } }, set_position: function(position) { if (this.dragging) { return; } if (this.options.direction === "both") { // Object comparison if (this.position !== undefined && this.position.x === position.x && this.position.y === position.y) { return; } } else { if (this.position === position) { return; } } this.set_highlight(false); // cheat any set position clears the highlight var Mx = this.plot._Mx; if (this.options.direction === "both") { this.position = position ? JSON.parse(JSON.stringify(position)) : undefined; } else { this.position = position; } var pxl; if (this.options.direction === "both") { pxl = mx.real_to_pixel(Mx, this.position.x, this.position.y); } else { pxl = mx.real_to_pixel(Mx, this.position, this.position); } if (this.options.direction === "vertical") { this.location = pxl.x; } else if (this.options.direction === "horizontal") { this.location = pxl.y; } else if (this.options.direction === "both") { this.location = {x : pxl.x, y: pxl.y}; } // Issue a slider tag event var evt = document.createEvent('Event'); evt.initEvent('slidertag', true, true); if (this.options.direction === "both") { // If both, expecting position to be an object evt.location = this.location ? JSON.parse(JSON.stringify(this.location)) : undefined; evt.position = this.position ? JSON.parse(JSON.stringify(this.position)) : undefined; } else { // vertical or horizontal evt.location = this.location; evt.position = this.position; } var canceled = !mx.dispatchEvent(Mx, evt); if (canceled) { return; } this.plot.redraw(); }, set_location: function(location) { if (this.dragging) { return; } if (this.options.direction === "both") { if (this.location !== undefined && this.location.x === location.x && this.location.y === location.y) { return; } } else { if (this.location === location) { return; } } this.set_highlight(false); // cheat any set location clears the highlight var Mx = this.plot._Mx; if (this.options.direction === "both") { this.location = location ? JSON.parse(JSON.stringify(location)) : undefined; } else { this.location = location; } var pos; if (this.options.direction === "both") { pos = mx.pixel_to_real(Mx, location.x, location.y); } else { pos = mx.pixel_to_real(Mx, location, location); } if (this.options.direction === "vertical") { this.position = pos.x; } else if (this.options.direction === "horizontal") { this.position = pos.y; } else if (this.options.direction === "both") { this.position = {x: pos.x, y: pos.y}; } // Issue a slider tag event var evt = document.createEvent('Event'); evt.initEvent('slidertag', true, true); if (this.options.direction === "both") { evt.location = this.location ? JSON.parse(JSON.stringify(this.location)) : undefined; evt.position = this.position ? JSON.parse(JSON.stringify(this.position)) : undefined; } else { evt.location = this.location; evt.position = this.position; } var canceled = !mx.dispatchEvent(Mx, evt); if (canceled) { return; } this.plot.redraw(); }, get_position: function() { // In real units return this.position; }, get_location: function() { // Pixels return this.location; }, refresh: function(canvas) { if (!this.options.display) { return; } if (this.position === undefined) { return; } var Mx = this.plot._Mx; var ctx = canvas.getContext("2d"); ctx.lineWidth = (this.options.style.lineWidth !== undefined) ? this.options.style.lineWidth : 1; ctx.lineCap = (this.options.style.lineCap !== undefined) ? this.options.style.lineCap : "square"; ctx.strokeStyle = (this.options.style.strokeStyle !== undefined) ? this.options.style.strokeStyle : Mx.fg; if (this.dragging || this.highlight) { ctx.lineWidth = Math.ceil(ctx.lineWidth * 1.2); } var pxl; if (this.options.direction === "both") { pxl = mx.real_to_pixel(Mx, this.position.x, this.position.y); } else { pxl = mx.real_to_pixel(Mx, this.position, this.position); } if (this.options.direction === "vertical") { if ((pxl.x < Mx.l) || (pxl.x > Mx.r)) { return; } this.location = pxl.x; } else if (this.options.direction === "horizontal") { if ((pxl.y < Mx.t) || (pxl.y > Mx.b)) { return; } this.location = pxl.y; } else if (this.options.direction === "both") { if ((pxl.x < Mx.l) || (pxl.x > Mx.r) || ((pxl.y < Mx.t) || (pxl.y > Mx.b))) { return; } this.location.x = pxl.x; this.location.y = pxl.y; } if (this.options.direction === "vertical") { ctx.beginPath(); ctx.moveTo(this.location+0.5, Mx.t); ctx.lineTo(this.location+0.5, Mx.b); ctx.stroke(); } else if (this.options.direction === "horizontal") { ctx.beginPath(); ctx.moveTo(Mx.l, this.location+0.5); ctx.lineTo(Mx.r, this.location+0.5); ctx.stroke(); } else if (this.options.direction === "both") { // Horizontal portion ctx.beginPath(); ctx.moveTo(Mx.l, this.location.y+0.5); ctx.lineTo(Mx.r, this.location.y+0.5); ctx.closePath(); // Vertical portion ctx.moveTo(this.location.x+0.5, Mx.t); ctx.lineTo(this.location.x+0.5, Mx.b); ctx.stroke(); } }, dispose: function() { this.plot.removeListener("mmove", this.onmousemove); document.removeEventListener("mouseup", this.onmouseup, false); this.plot = undefined; this.position = undefined; } }; }( window.sigplot = window.sigplot || {}, mx, m));