merged cont.
[opensuse:yast-rest-service.git] / webyast / public / javascripts / plugin / jqplot.highlighter.js
1 /**
2  * Copyright (c) 2009 Chris Leonello
3  * jqPlot is currently available for use in all personal or commercial projects 
4  * under both the MIT and GPL version 2.0 licenses. This means that you can 
5  * choose the license that best suits your project and use it accordingly. 
6  *
7  * The author would appreciate an email letting him know of any substantial
8  * use of jqPlot.  You can reach the author at: chris dot leonello at gmail 
9  * dot com or see http://www.jqplot.com/info.php .  This is, of course, 
10  * not required.
11  *
12  * If you are feeling kind and generous, consider supporting the project by
13  * making a donation at: http://www.jqplot.com/donate.php .
14  *
15  * Thanks for using jqPlot!
16  * 
17  */
18 (function($) {
19     $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);
20     
21     /**
22      * Class: $.jqplot.Highlighter
23      * Plugin which will highlight data points when they are moused over.
24      * 
25      * To use this plugin, include the js
26      * file in your source:
27      * 
28      * > <script type="text/javascript" src="plugins/jqplot.highlighter.js"></script>
29      * 
30      * A tooltip providing information about the data point is enabled by default.
31      * To disable the tooltip, set "showTooltip" to false.
32      * 
33      * You can control what data is displayed in the tooltip with various
34      * options.  The "tooltipAxes" option controls wether the x, y or both
35      * data values are displayed.
36      * 
37      * Some chart types (e.g. hi-low-close) have more than one y value per
38      * data point. To display the additional values in the tooltip, set the
39      * "yvalues" option to the desired number of y values present (3 for a hlc chart).
40      * 
41      * By default, data values will be formatted with the same formatting
42      * specifiers as used to format the axis ticks.  A custom format code
43      * can be supplied with the tooltipFormatString option.  This will apply 
44      * to all values in the tooltip.  
45      * 
46      * For more complete control, the "formatString" option can be set.  This
47      * Allows conplete control over tooltip formatting.  Values are passed to
48      * the format string in an order determined by the "tooltipAxes" and "yvalues"
49      * options.  So, if you have a hi-low-close chart and you just want to display 
50      * the hi-low-close values in the tooltip, you could set a formatString like:
51      * 
52      * > highlighter: {
53      * >     tooltipAxes: 'y',
54      * >     yvalues: 3,
55      * >     formatString:'<table class="jqplot-highlighter">
56      * >         <tr><td>hi:</td><td>%s</td></tr>
57      * >         <tr><td>low:</td><td>%s</td></tr>
58      * >         <tr><td>close:</td><td>%s</td></tr></table>'
59      * > }
60      * 
61      */
62     $.jqplot.Highlighter = function(options) {
63         // Group: Properties
64         //
65         //prop: show
66         // true to show the highlight.
67         this.show = $.jqplot.config.enablePlugins;
68         // prop: markerRenderer
69         // Renderer used to draw the marker of the highlighted point.
70         // Renderer will assimilate attributes from the data point being highlighted,
71         // so no attributes need set on the renderer directly.
72         // Default is to turn off shadow drawing on the highlighted point.
73         this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false});
74         // prop: showMarker
75         // true to show the marker
76         this.showMarker  = true;
77         // prop: lineWidthAdjust
78         // Pixels to add to the lineWidth of the highlight.
79         this.lineWidthAdjust = 2.5;
80         // prop: sizeAdjust
81         // Pixels to add to the overall size of the highlight.
82         this.sizeAdjust = 5;
83         // prop: showTooltip
84         // Show a tooltip with data point values.
85         this.showTooltip = true;
86         // prop: tooltipLocation
87         // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
88         this.tooltipLocation = 'nw';
89         // prop: tooltipFade
90         // true = fade in/out tooltip, flase = show/hide tooltip
91         this.fadeTooltip = true;
92         // prop: tooltipFadeSpeed
93         // 'slow', 'def', 'fast', or number of milliseconds.
94         this.tooltipFadeSpeed = "fast";
95         // prop: tooltipOffset
96         // Pixel offset of tooltip from the highlight.
97         this.tooltipOffset = 2;
98         // prop: tooltipAxes
99         // Which axes to display in tooltip, 'x', 'y' or 'both', 'xy' or 'yx'
100         // 'both' and 'xy' are equivalent, 'yx' reverses order of labels.
101         this.tooltipAxes = 'both';
102         // prop; tooltipSeparator
103         // String to use to separate x and y axes in tooltip.
104         this.tooltipSeparator = ', ';
105         // prop: useAxesFormatters
106         // Use the x and y axes formatters to format the text in the tooltip.
107         this.useAxesFormatters = true;
108         // prop: tooltipFormatString
109         // sprintf format string for the tooltip.
110         // Uses Ash Searle's javascript sprintf implementation
111         // found here: http://hexmen.com/blog/2007/03/printf-sprintf/
112         // See http://perldoc.perl.org/functions/sprintf.html for reference.
113         // Additional "p" and "P" format specifiers added by Chris Leonello.
114         this.tooltipFormatString = '%.5P';
115         // prop: formatString
116         // alternative to tooltipFormatString
117         // will format the whole tooltip text, populating with x, y values as
118         // indicated by tooltipAxes option.  So, you could have a tooltip like:
119         // 'Date: %s, number of cats: %d' to format the whole tooltip at one go.
120         // If useAxesFormatters is true, values will be formatted according to
121         // Axes formatters and you can populate your tooltip string with 
122         // %s placeholders.
123         this.formatString = null;
124         // prop: yvalues
125         // Number of y values to expect in the data point array.
126         // Typically this is 1.  Certain plots, like OHLC, will
127         // have more y values in each data point array.
128         this.yvalues = 1;
129         this._tooltipElem;
130         this.isHighlighting = false;
131
132         $.extend(true, this, options);
133     };
134     
135     // axis.renderer.tickrenderer.formatter
136     
137     // called with scope of plot
138     $.jqplot.Highlighter.init = function (target, data, opts){
139         var options = opts || {};
140         // add a highlighter attribute to the plot
141         this.plugins.highlighter = new $.jqplot.Highlighter(options.highlighter);
142     };
143     
144     // called within scope of series
145     $.jqplot.Highlighter.parseOptions = function (defaults, options) {
146         this.showHighlight = true;
147     };
148     
149     // called within context of plot
150     // create a canvas which we can draw on.
151     // insert it before the eventCanvas, so eventCanvas will still capture events.
152     $.jqplot.Highlighter.postPlotDraw = function() {
153         this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas();
154         
155         this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions));
156         var hctx = this.plugins.highlighter.highlightCanvas.setContext();
157         
158         var p = this.plugins.highlighter;
159         p._tooltipElem = $('<div class="jqplot-highlighter-tooltip" style="position:absolute;display:none"></div>');
160         this.target.append(p._tooltipElem);
161     };
162     
163     $.jqplot.preInitHooks.push($.jqplot.Highlighter.init);
164     $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions);
165     $.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw);
166     
167     function draw(plot, neighbor) {
168         var hl = plot.plugins.highlighter;
169         var s = plot.series[neighbor.seriesIndex];
170         var smr = s.markerRenderer;
171         var mr = hl.markerRenderer;
172         mr.style = smr.style;
173         mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust;
174         mr.size = smr.size + hl.sizeAdjust;
175         var rgba = $.jqplot.getColorComponents(smr.color);
176         var newrgb = [rgba[0], rgba[1], rgba[2]];
177         var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
178         mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
179         mr.init();
180         mr.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], hl.highlightCanvas._ctx);
181     }
182     
183     function showTooltip(plot, series, neighbor) {
184         // neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}
185         // gridData should be x,y pixel coords on the grid.
186         // add the plot._gridPadding to that to get x,y in the target.
187         var hl = plot.plugins.highlighter;
188         var elem = hl._tooltipElem;
189         if (hl.useAxesFormatters) {
190             var xf = series._xaxis._ticks[0].formatter;
191             var yf = series._yaxis._ticks[0].formatter;
192             var xfstr = series._xaxis._ticks[0].formatString;
193             var yfstr = series._yaxis._ticks[0].formatString;
194             var str;
195             var xstr = xf(xfstr, neighbor.data[0]);
196             var ystrs = [];
197             for (var i=1; i<hl.yvalues+1; i++) {
198                 ystrs.push(yf(yfstr, neighbor.data[i]));
199             }
200             if (hl.formatString) {
201                 switch (hl.tooltipAxes) {
202                     case 'both':
203                     case 'xy':
204                         ystrs.unshift(xstr);
205                         ystrs.unshift(hl.formatString);
206                         str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
207                         break;
208                     case 'yx':
209                         ystrs.push(xstr);
210                         ystrs.unshift(hl.formatString);
211                         str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
212                         break;
213                     case 'x':
214                         str = $.jqplot.sprintf.apply($.jqplot.sprintf, [hl.formatString, xstr]);
215                         break;
216                     case 'y':
217                         ystrs.unshift(hl.formatString);
218                         str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
219                         break;
220                     default: // same as xy
221                         ystrs.unshift(xstr);
222                         ystrs.unshift(hl.formatString);
223                         str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
224                         break;
225                 } 
226             }
227             else {
228                 switch (hl.tooltipAxes) {
229                     case 'both':
230                     case 'xy':
231                         str = xstr;
232                         for (var i=0; i<ystrs.length; i++) {
233                             str += hl.tooltipSeparator + ystrs[i];
234                         }
235                         break;
236                     case 'yx':
237                         str = '';
238                         for (var i=0; i<ystrs.length; i++) {
239                             str += ystrs[i] + hl.tooltipSeparator;
240                         }
241                         str += xstr;
242                         break;
243                     case 'x':
244                         str = xstr;
245                         break;
246                     case 'y':
247                         str = '';
248                         for (var i=0; i<ystrs.length; i++) {
249                             str += ystrs[i] + hl.tooltipSeparator;
250                         }
251                         break;
252                     default: // same as 'xy'
253                         str = xstr;
254                         for (var i=0; i<ystrs.length; i++) {
255                             str += hl.tooltipSeparator + ystrs[i];
256                         }
257                         break;
258                     
259                 }                
260             }
261         }
262         else {
263             var str;
264             if (hl.tooltipAxes == 'both' || hl.tooltipAxes == 'xy') {
265                 str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[0]) + hl.tooltipSeparator + $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]);
266             }
267             else if (hl.tooltipAxes == 'yx') {
268                 str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]) + hl.tooltipSeparator + $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[0]);
269             }
270             else if (hl.tooltipAxes == 'x') {
271                 str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[0]);
272             }
273             else if (hl.tooltipAxes == 'y') {
274                 str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]);
275             } 
276         }
277         elem.html(str);
278         var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]};
279         var ms = 0;
280         var fact = 0.707;
281         if (series.markerRenderer.show == true) { 
282             ms = (series.markerRenderer.size + hl.sizeAdjust)/2;
283         }
284         switch (hl.tooltipLocation) {
285             case 'nw':
286                 var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms;
287                 var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms;
288                 break;
289             case 'n':
290                 var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
291                 var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - ms;
292                 break;
293             case 'ne':
294                 var x = gridpos.x + plot._gridPadding.left + hl.tooltipOffset + fact * ms;
295                 var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms;
296                 break;
297             case 'e':
298                 var x = gridpos.x + plot._gridPadding.left + hl.tooltipOffset + ms;
299                 var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
300                 break;
301             case 'se':
302                 var x = gridpos.x + plot._gridPadding.left + hl.tooltipOffset + fact * ms;
303                 var y = gridpos.y + plot._gridPadding.top + hl.tooltipOffset + fact * ms;
304                 break;
305             case 's':
306                 var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
307                 var y = gridpos.y + plot._gridPadding.top + hl.tooltipOffset + ms;
308                 break;
309             case 'sw':
310                 var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms;
311                 var y = gridpos.y + plot._gridPadding.top + hl.tooltipOffset + fact * ms;
312                 break;
313             case 'w':
314                 var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - ms;
315                 var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
316                 break;
317             default: // same as 'nw'
318                 var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms;
319                 var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms;
320                 break;
321         }
322         elem.css('left', x);
323         elem.css('top', y);
324         if (hl.fadeTooltip) {
325             elem.fadeIn(hl.tooltipFadeSpeed);
326         }
327         else {
328             elem.show();
329         }
330         
331     }
332     
333     function handleMove(ev, gridpos, datapos, neighbor, plot) {
334         var hl = plot.plugins.highlighter;
335         if (hl.show) {
336             if (neighbor == null && hl.isHighlighting) {
337                var ctx = hl.highlightCanvas._ctx;
338                ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
339                 if (hl.fadeTooltip) {
340                     hl._tooltipElem.fadeOut(hl.tooltipFadeSpeed);
341                 }
342                 else {
343                     hl._tooltipElem.hide();
344                 }
345                hl.isHighlighting = false;
346             
347             }
348             if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) {
349                 hl.isHighlighting = true;
350                 if (hl.showMarker) {
351                     draw(plot, neighbor);
352                 }
353                 if (hl.showTooltip) {
354                     showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor);
355                 }
356             }
357         }
358     }
359 })(jQuery);