shop.balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

jquery.flot.selection.js (13501B)


      1 /* Flot plugin for selecting regions of a plot.
      2 
      3 Copyright (c) 2007-2013 IOLA and Ole Laursen.
      4 Licensed under the MIT license.
      5 
      6 The plugin supports these options:
      7 
      8 selection: {
      9 	mode: null or "x" or "y" or "xy",
     10 	color: color,
     11 	shape: "round" or "miter" or "bevel",
     12 	minSize: number of pixels
     13 }
     14 
     15 Selection support is enabled by setting the mode to one of "x", "y" or "xy".
     16 In "x" mode, the user will only be able to specify the x range, similarly for
     17 "y" mode. For "xy", the selection becomes a rectangle where both ranges can be
     18 specified. "color" is color of the selection (if you need to change the color
     19 later on, you can get to it with plot.getOptions().selection.color). "shape"
     20 is the shape of the corners of the selection.
     21 
     22 "minSize" is the minimum size a selection can be in pixels. This value can
     23 be customized to determine the smallest size a selection can be and still
     24 have the selection rectangle be displayed. When customizing this value, the
     25 fact that it refers to pixels, not axis units must be taken into account.
     26 Thus, for example, if there is a bar graph in time mode with BarWidth set to 1
     27 minute, setting "minSize" to 1 will not make the minimum selection size 1
     28 minute, but rather 1 pixel. Note also that setting "minSize" to 0 will prevent
     29 "plotunselected" events from being fired when the user clicks the mouse without
     30 dragging.
     31 
     32 When selection support is enabled, a "plotselected" event will be emitted on
     33 the DOM element you passed into the plot function. The event handler gets a
     34 parameter with the ranges selected on the axes, like this:
     35 
     36 	placeholder.bind( "plotselected", function( event, ranges ) {
     37 		alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
     38 		// similar for yaxis - with multiple axes, the extra ones are in
     39 		// x2axis, x3axis, ...
     40 	});
     41 
     42 The "plotselected" event is only fired when the user has finished making the
     43 selection. A "plotselecting" event is fired during the process with the same
     44 parameters as the "plotselected" event, in case you want to know what's
     45 happening while it's happening,
     46 
     47 A "plotunselected" event with no arguments is emitted when the user clicks the
     48 mouse to remove the selection. As stated above, setting "minSize" to 0 will
     49 destroy this behavior.
     50 
     51 The plugin allso adds the following methods to the plot object:
     52 
     53 - setSelection( ranges, preventEvent )
     54 
     55   Set the selection rectangle. The passed in ranges is on the same form as
     56   returned in the "plotselected" event. If the selection mode is "x", you
     57   should put in either an xaxis range, if the mode is "y" you need to put in
     58   an yaxis range and both xaxis and yaxis if the selection mode is "xy", like
     59   this:
     60 
     61 	setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
     62 
     63   setSelection will trigger the "plotselected" event when called. If you don't
     64   want that to happen, e.g. if you're inside a "plotselected" handler, pass
     65   true as the second parameter. If you are using multiple axes, you can
     66   specify the ranges on any of those, e.g. as x2axis/x3axis/... instead of
     67   xaxis, the plugin picks the first one it sees.
     68 
     69 - clearSelection( preventEvent )
     70 
     71   Clear the selection rectangle. Pass in true to avoid getting a
     72   "plotunselected" event.
     73 
     74 - getSelection()
     75 
     76   Returns the current selection in the same format as the "plotselected"
     77   event. If there's currently no selection, the function returns null.
     78 
     79 */
     80 
     81 (function ($) {
     82     function init(plot) {
     83         var selection = {
     84                 first: { x: -1, y: -1}, second: { x: -1, y: -1},
     85                 show: false,
     86                 active: false
     87             };
     88 
     89         // FIXME: The drag handling implemented here should be
     90         // abstracted out, there's some similar code from a library in
     91         // the navigation plugin, this should be massaged a bit to fit
     92         // the Flot cases here better and reused. Doing this would
     93         // make this plugin much slimmer.
     94         var savedhandlers = {};
     95 
     96         var mouseUpHandler = null;
     97         
     98         function onMouseMove(e) {
     99             if (selection.active) {
    100                 updateSelection(e);
    101                 
    102                 plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]);
    103             }
    104         }
    105 
    106         function onMouseDown(e) {
    107             if (e.which != 1)  // only accept left-click
    108                 return;
    109             
    110             // cancel out any text selections
    111             document.body.focus();
    112 
    113             // prevent text selection and drag in old-school browsers
    114             if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) {
    115                 savedhandlers.onselectstart = document.onselectstart;
    116                 document.onselectstart = function () { return false; };
    117             }
    118             if (document.ondrag !== undefined && savedhandlers.ondrag == null) {
    119                 savedhandlers.ondrag = document.ondrag;
    120                 document.ondrag = function () { return false; };
    121             }
    122 
    123             setSelectionPos(selection.first, e);
    124 
    125             selection.active = true;
    126 
    127             // this is a bit silly, but we have to use a closure to be
    128             // able to whack the same handler again
    129             mouseUpHandler = function (e) { onMouseUp(e); };
    130             
    131             $(document).one("mouseup", mouseUpHandler);
    132         }
    133 
    134         function onMouseUp(e) {
    135             mouseUpHandler = null;
    136             
    137             // revert drag stuff for old-school browsers
    138             if (document.onselectstart !== undefined)
    139                 document.onselectstart = savedhandlers.onselectstart;
    140             if (document.ondrag !== undefined)
    141                 document.ondrag = savedhandlers.ondrag;
    142 
    143             // no more dragging
    144             selection.active = false;
    145             updateSelection(e);
    146 
    147             if (selectionIsSane())
    148                 triggerSelectedEvent();
    149             else {
    150                 // this counts as a clear
    151                 plot.getPlaceholder().trigger("plotunselected", [ ]);
    152                 plot.getPlaceholder().trigger("plotselecting", [ null ]);
    153             }
    154 
    155             return false;
    156         }
    157 
    158         function getSelection() {
    159             if (!selectionIsSane())
    160                 return null;
    161             
    162             if (!selection.show) return null;
    163 
    164             var r = {}, c1 = selection.first, c2 = selection.second;
    165             $.each(plot.getAxes(), function (name, axis) {
    166                 if (axis.used) {
    167                     var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]); 
    168                     r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };
    169                 }
    170             });
    171             return r;
    172         }
    173 
    174         function triggerSelectedEvent() {
    175             var r = getSelection();
    176 
    177             plot.getPlaceholder().trigger("plotselected", [ r ]);
    178 
    179             // backwards-compat stuff, to be removed in future
    180             if (r.xaxis && r.yaxis)
    181                 plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
    182         }
    183 
    184         function clamp(min, value, max) {
    185             return value < min ? min: (value > max ? max: value);
    186         }
    187 
    188         function setSelectionPos(pos, e) {
    189             var o = plot.getOptions();
    190             var offset = plot.getPlaceholder().offset();
    191             var plotOffset = plot.getPlotOffset();
    192             pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());
    193             pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height());
    194 
    195             if (o.selection.mode == "y")
    196                 pos.x = pos == selection.first ? 0 : plot.width();
    197 
    198             if (o.selection.mode == "x")
    199                 pos.y = pos == selection.first ? 0 : plot.height();
    200         }
    201 
    202         function updateSelection(pos) {
    203             if (pos.pageX == null)
    204                 return;
    205 
    206             setSelectionPos(selection.second, pos);
    207             if (selectionIsSane()) {
    208                 selection.show = true;
    209                 plot.triggerRedrawOverlay();
    210             }
    211             else
    212                 clearSelection(true);
    213         }
    214 
    215         function clearSelection(preventEvent) {
    216             if (selection.show) {
    217                 selection.show = false;
    218                 plot.triggerRedrawOverlay();
    219                 if (!preventEvent)
    220                     plot.getPlaceholder().trigger("plotunselected", [ ]);
    221             }
    222         }
    223 
    224         // function taken from markings support in Flot
    225         function extractRange(ranges, coord) {
    226             var axis, from, to, key, axes = plot.getAxes();
    227 
    228             for (var k in axes) {
    229                 axis = axes[k];
    230                 if (axis.direction == coord) {
    231                     key = coord + axis.n + "axis";
    232                     if (!ranges[key] && axis.n == 1)
    233                         key = coord + "axis"; // support x1axis as xaxis
    234                     if (ranges[key]) {
    235                         from = ranges[key].from;
    236                         to = ranges[key].to;
    237                         break;
    238                     }
    239                 }
    240             }
    241 
    242             // backwards-compat stuff - to be removed in future
    243             if (!ranges[key]) {
    244                 axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0];
    245                 from = ranges[coord + "1"];
    246                 to = ranges[coord + "2"];
    247             }
    248 
    249             // auto-reverse as an added bonus
    250             if (from != null && to != null && from > to) {
    251                 var tmp = from;
    252                 from = to;
    253                 to = tmp;
    254             }
    255             
    256             return { from: from, to: to, axis: axis };
    257         }
    258         
    259         function setSelection(ranges, preventEvent) {
    260             var axis, range, o = plot.getOptions();
    261 
    262             if (o.selection.mode == "y") {
    263                 selection.first.x = 0;
    264                 selection.second.x = plot.width();
    265             }
    266             else {
    267                 range = extractRange(ranges, "x");
    268 
    269                 selection.first.x = range.axis.p2c(range.from);
    270                 selection.second.x = range.axis.p2c(range.to);
    271             }
    272 
    273             if (o.selection.mode == "x") {
    274                 selection.first.y = 0;
    275                 selection.second.y = plot.height();
    276             }
    277             else {
    278                 range = extractRange(ranges, "y");
    279 
    280                 selection.first.y = range.axis.p2c(range.from);
    281                 selection.second.y = range.axis.p2c(range.to);
    282             }
    283 
    284             selection.show = true;
    285             plot.triggerRedrawOverlay();
    286             if (!preventEvent && selectionIsSane())
    287                 triggerSelectedEvent();
    288         }
    289 
    290         function selectionIsSane() {
    291             var minSize = plot.getOptions().selection.minSize;
    292             return Math.abs(selection.second.x - selection.first.x) >= minSize &&
    293                 Math.abs(selection.second.y - selection.first.y) >= minSize;
    294         }
    295 
    296         plot.clearSelection = clearSelection;
    297         plot.setSelection = setSelection;
    298         plot.getSelection = getSelection;
    299 
    300         plot.hooks.bindEvents.push(function(plot, eventHolder) {
    301             var o = plot.getOptions();
    302             if (o.selection.mode != null) {
    303                 eventHolder.mousemove(onMouseMove);
    304                 eventHolder.mousedown(onMouseDown);
    305             }
    306         });
    307 
    308 
    309         plot.hooks.drawOverlay.push(function (plot, ctx) {
    310             // draw selection
    311             if (selection.show && selectionIsSane()) {
    312                 var plotOffset = plot.getPlotOffset();
    313                 var o = plot.getOptions();
    314 
    315                 ctx.save();
    316                 ctx.translate(plotOffset.left, plotOffset.top);
    317 
    318                 var c = $.color.parse(o.selection.color);
    319 
    320                 ctx.strokeStyle = c.scale('a', 0.8).toString();
    321                 ctx.lineWidth = 1;
    322                 ctx.lineJoin = o.selection.shape;
    323                 ctx.fillStyle = c.scale('a', 0.4).toString();
    324 
    325                 var x = Math.min(selection.first.x, selection.second.x) + 0.5,
    326                     y = Math.min(selection.first.y, selection.second.y) + 0.5,
    327                     w = Math.abs(selection.second.x - selection.first.x) - 1,
    328                     h = Math.abs(selection.second.y - selection.first.y) - 1;
    329 
    330                 ctx.fillRect(x, y, w, h);
    331                 ctx.strokeRect(x, y, w, h);
    332 
    333                 ctx.restore();
    334             }
    335         });
    336         
    337         plot.hooks.shutdown.push(function (plot, eventHolder) {
    338             eventHolder.unbind("mousemove", onMouseMove);
    339             eventHolder.unbind("mousedown", onMouseDown);
    340             
    341             if (mouseUpHandler)
    342                 $(document).unbind("mouseup", mouseUpHandler);
    343         });
    344 
    345     }
    346 
    347     $.plot.plugins.push({
    348         init: init,
    349         options: {
    350             selection: {
    351                 mode: null, // one of null, "x", "y" or "xy"
    352                 color: "#e8cfac",
    353                 shape: "round", // one of "round", "miter", or "bevel"
    354                 minSize: 5 // minimum number of pixels
    355             }
    356         },
    357         name: 'selection',
    358         version: '1.1'
    359     });
    360 })(jQuery);