initial commit
[home-automation.git] / libraries / RGraph.vprogress.js
1     /**
2     * o------------------------------------------------------------------------------o
3     * | This file is part of the RGraph package - you can learn more at:             |
4     * |                                                                              |
5     * |                          http://www.rgraph.net                               |
6     * |                                                                              |
7     * | This package is licensed under the RGraph license. For all kinds of business |
8     * | purposes there is a small one-time licensing fee to pay and for non          |
9     * | commercial  purposes it is free to use. You can read the full license here:  |
10     * |                                                                              |
11     * |                      http://www.rgraph.net/LICENSE.txt                       |
12     * o------------------------------------------------------------------------------o
13     */
14
15     if (typeof(RGraph) == 'undefined') RGraph = {};
16
17     /**
18     * The progress bar constructor
19     * 
20     * @param int id    The ID of the canvas tag
21     * @param int value The indicated value of the meter.
22     * @param int max   The end value (the upper most) of the meter
23     */
24     RGraph.VProgress = function (id, value, max)
25     {
26         this.id                = id;
27         this.max               = max;
28         this.value             = value;
29         this.canvas            = document.getElementById(id);
30         this.context           = this.canvas.getContext('2d');
31         this.canvas.__object__ = this;
32         this.type              = 'vprogress';
33         this.coords            = [];
34         this.isRGraph          = true;
35
36
37         /**
38         * Compatibility with older browsers
39         */
40         RGraph.OldBrowserCompat(this.context);
41
42         this.properties = {
43             'chart.colors':             ['#0c0'],
44             'chart.tickmarks':          true,
45             'chart.tickmarks.color':    'black',
46             'chart.tickmarks.inner':    false,
47             'chart.gutter':             25,
48             'chart.numticks':           10,
49             'chart.numticks.inner':     50,
50             'chart.background.color':   '#eee',
51             'chart.shadow':             false,
52             'chart.shadow.color':       'rgba(0,0,0,0.5)',
53             'chart.shadow.blur':        3,
54             'chart.shadow.offsetx':     3,
55             'chart.shadow.offsety':     3,
56             'chart.title':              '',
57             'chart.title.background':   null,
58             'chart.title.hpos':         null,
59             'chart.title.vpos':         null,
60             'chart.width':              0,
61             'chart.height':             0,
62             'chart.text.size':          10,
63             'chart.text.color':         'black',
64             'chart.text.font':          'Verdana',
65             'chart.contextmenu':        null,
66             'chart.units.pre':          '',
67             'chart.units.post':         '',
68             'chart.tooltips':           [],
69             'chart.tooltips.effect':    'fade',
70             'chart.tooltips.css.class': 'RGraph_tooltip',
71             'chart.tooltips.highlight': true,
72             'chart.tooltips.coords.adjust': [0,0],
73             'chart.annotatable':        false,
74             'chart.annotate.color':     'black',
75             'chart.zoom.mode':          'canvas',
76             'chart.zoom.factor':        1.5,
77             'chart.zoom.fade.in':       true,
78             'chart.zoom.fade.out':      true,
79             'chart.zoom.hdir':          'right',
80             'chart.zoom.vdir':          'down',
81             'chart.zoom.frames':        10,
82             'chart.zoom.delay':         50,
83             'chart.zoom.shadow':        true,
84             'chart.zoom.background':    true,
85             'chart.zoom.action':        'zoom',
86             'chart.arrows':             false,
87             'chart.margin':             0,
88             'chart.resizable':          false,
89             'chart.label.inner':        false,
90             'chart.labels.count':       10,
91             'chart.labels.position':    'right',
92             'chart.adjustable':         false,
93             'chart.min':                0,
94             'chart.scale.decimals':     0
95         }
96
97         // Check for support
98         if (!this.canvas) {
99             alert('[PROGRESS] No canvas support');
100             return;
101         }
102
103         // Check the common library has been included
104         if (typeof(RGraph) == 'undefined') {
105             alert('[PROGRESS] Fatal error: The common library does not appear to have been included');
106         }
107     }
108
109
110     /**
111     * A generic setter
112     * 
113     * @param string name  The name of the property to set
114     * @param string value The value of the poperty
115     */
116     RGraph.VProgress.prototype.Set = function (name, value)
117     {
118         this.properties[name.toLowerCase()] = value;
119     }
120
121
122     /**
123     * A generic getter
124     * 
125     * @param string name  The name of the property to get
126     */
127     RGraph.VProgress.prototype.Get = function (name)
128     {
129         return this.properties[name.toLowerCase()];
130     }
131
132
133     /**
134     * Draws the progress bar
135     */
136     RGraph.VProgress.prototype.Draw = function ()
137     {
138         /**
139         * Fire the onbeforedraw event
140         */
141         RGraph.FireCustomEvent(this, 'onbeforedraw');
142
143         /**
144         * Clear all of this canvases event handlers (the ones installed by RGraph)
145         */
146         RGraph.ClearEventListeners(this.id);
147
148         // Figure out the width and height
149         this.width  = this.canvas.width - (2 * this.Get('chart.gutter'));
150         this.height = this.canvas.height - (2 * this.Get('chart.gutter'));
151         this.coords = [];
152
153         this.Drawbar();
154         this.DrawTickMarks();
155         this.DrawLabels();
156
157         this.context.stroke();
158         this.context.fill();
159
160         /**
161         * Setup the context menu if required
162         */
163         if (this.Get('chart.contextmenu')) {
164             RGraph.ShowContext(this);
165         }
166         
167         /**
168         * Alternatively, show the tooltip if requested
169         */
170         if (typeof(this.Get('chart.tooltips')) == 'function' || this.Get('chart.tooltips').length) {
171
172             // Need to register this object for redrawing
173             RGraph.Register(this);
174
175             /**
176             * Install the window onclick handler
177             */
178             window.onclick = function ()
179             {
180                 RGraph.Redraw();
181             }
182
183
184             /**
185             * Install the onclick event handler for the tooltips
186             */
187             //this.canvas.onclick = function (e)
188             var canvas_onclick_func = function (e)
189             {
190                 e = RGraph.FixEventObject(e);
191
192                 var canvas = document.getElementById(this.id);
193                 var obj = canvas.__object__;
194
195                 /**
196                 * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
197                 * This "deselects" any already selected bar
198                 */
199                 RGraph.Redraw();
200     
201                 /**
202                 * Get the mouse X/Y coordinates
203                 */
204                 var mouseCoords = RGraph.getMouseXY(e);
205
206                 /**
207                 * Loop through the bars determining if the mouse is over a bar
208                 */
209                 for (var i=0; i<obj.coords.length; i++) {
210
211                     var mouseX = mouseCoords[0] - obj.Get('chart.tooltips.coords.adjust')[0];
212                     var mouseY = mouseCoords[1] - obj.Get('chart.tooltips.coords.adjust')[1];
213                     var left   = obj.coords[i][0];
214                     var top    = obj.coords[i][1];
215                     var width  = obj.coords[i][2];
216                     var height = obj.coords[i][3];
217                     var idx    = i;
218
219                     if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
220     
221                         /**
222                         * Get the tooltip text
223                         */
224                         if (typeof(obj.Get('chart.tooltips')) == 'function') {
225                             var text = obj.Get('chart.tooltips')(idx);
226                         
227                         } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[idx]) == 'function') {
228                             var text = obj.Get('chart.tooltips')[idx](idx);
229                         
230                         } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
231                             var text = obj.Get('chart.tooltips')[idx];
232
233                         } else {
234                             var text = null;
235                         }
236
237                         /**
238                         * Show a tooltip if it's defined
239                         */
240                         if (text) {
241
242                             obj.context.beginPath();
243                             obj.context.strokeStyle = 'black';
244                             obj.context.fillStyle   = 'rgba(255,255,255,0.5)';
245                             obj.context.strokeRect(left, top, width, height);
246                             obj.context.fillRect(left, top, width, height);
247         
248                             obj.context.stroke();
249                             obj.context.fill();
250
251                             RGraph.Tooltip(canvas, text, e.pageX, e.pageY, i);
252                         }
253                     }
254                 }
255
256                 /**
257                 * Stop the event bubbling
258                 */
259                 e.stopPropagation();
260             }
261             this.canvas.addEventListener('click', canvas_onclick_func, false);
262             RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
263
264
265             /**
266             * If the cursor is over a hotspot, change the cursor to a hand
267             */
268             //this.canvas.onmousemove = function (e)
269             var canvas_onmousemove_func = function (e)
270             {
271                 e = RGraph.FixEventObject(e);
272
273                 var canvas = document.getElementById(this.id);
274                 var obj = canvas.__object__;
275
276                 /**
277                 * Get the mouse X/Y coordinates
278                 */
279                 var mouseCoords = RGraph.getMouseXY(e);
280
281                 /**
282                 * Loop through the bars determining if the mouse is over a bar
283                 */
284                 for (var i=0; i<obj.coords.length; i++) {
285
286                     var mouseX = mouseCoords[0] - obj.Get('chart.tooltips.coords.adjust')[0];  // In relation to the canvas
287                     var mouseY = mouseCoords[1] - obj.Get('chart.tooltips.coords.adjust')[1];  // In relation to the canvas
288                     var left   = obj.coords[i][0];
289                     var top    = obj.coords[i][1];
290                     var width  = obj.coords[i][2];
291                     var height = obj.coords[i][3];
292
293                     if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
294                         canvas.style.cursor = 'pointer';
295                         break;
296                     }
297                     
298                     canvas.style.cursor = 'default';
299                 }
300             }
301             this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
302             RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
303         }
304         
305         /**
306         * If the canvas is annotatable, do install the event handlers
307         */
308         if (this.Get('chart.annotatable')) {
309             RGraph.Annotate(this);
310         }
311         
312         /**
313         * This bit shows the mini zoom window if requested
314         */
315         if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
316             RGraph.ShowZoomWindow(this);
317         }
318
319         
320         /**
321         * This function enables resizing
322         */
323         if (this.Get('chart.resizable')) {
324             RGraph.AllowResizing(this);
325         }
326         
327         /**
328         * Instead of using RGraph.common.adjusting.js, handle them here
329         */
330         if (this.Get('chart.adjustable')) {
331             RGraph.AllowAdjusting(this);
332         }
333         
334         /**
335         * Fire the RGraph ondraw event
336         */
337         RGraph.FireCustomEvent(this, 'ondraw');
338     }
339
340
341     /**
342     * Draw the bar itself
343     */
344     RGraph.VProgress.prototype.Drawbar = function ()
345     {
346         // Set a shadow if requested
347         if (this.Get('chart.shadow')) {
348             RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur'));
349         }
350
351         // Draw the shadow for MSIE
352         if (document.all && this.Get('chart.shadow')) {
353             this.context.fillStyle = this.Get('chart.shadow.color');
354             this.context.fillRect(this.Get('chart.gutter') + this.Get('chart.shadow.offsetx'), this.Get('chart.gutter') + this.Get('chart.shadow.offsety'), this.width, this.height);
355         }
356
357         // Draw the outline
358         this.context.fillStyle   = this.Get('chart.background.color');
359         this.context.strokeStyle = 'black';
360         this.context.strokeRect(this.Get('chart.gutter'), this.Get('chart.gutter'), this.width, this.height);
361         this.context.fillRect(this.Get('chart.gutter'), this.Get('chart.gutter'), this.width, this.height);
362
363         // Turn off any shadow
364         RGraph.NoShadow(this);
365
366         this.context.strokeStyle = 'black';
367         this.context.fillStyle   = this.Get('chart.colors')[0];
368         var margin = this.Get('chart.margin');
369         var barHeight = this.canvas.height - this.Get('chart.gutter') - this.Get('chart.gutter');
370
371         // Draw the actual bar itself
372         if (typeof(this.value) == 'number') {
373
374             this.context.lineWidth   = 1;
375             this.context.strokeStyle = '#999';
376
377         } else if (typeof(this.value) == 'object') {
378
379             this.context.beginPath();
380             this.context.strokeStyle = '#999';
381
382             var startPoint = this.canvas.height - this.Get('chart.gutter');
383             
384             for (var i=0; i<this.value.length; ++i) {
385
386                 var segmentHeight = ( (this.value[i] - this.Get('chart.min')) / (this.max - this.Get('chart.min')) ) * barHeight;
387
388                 this.context.fillStyle = this.Get('chart.colors')[i];
389
390                 this.context.fillRect(this.Get('chart.gutter') + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight);
391                 this.context.strokeRect(this.Get('chart.gutter') + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight);
392
393
394                 // Store the coords
395                 this.coords.push([this.Get('chart.gutter') + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight]);
396
397                 startPoint -= segmentHeight;
398             }
399
400         }
401
402         /**
403         * Inner tickmarks
404         */
405         if (this.Get('chart.tickmarks.inner')) {
406         
407             var spacing = (this.canvas.height - this.Get('chart.gutter') - this.Get('chart.gutter')) / this.Get('chart.numticks.inner');
408
409             this.context.lineWidth   = 1;
410             this.context.strokeStyle = '#999';
411
412             this.context.beginPath();
413
414             for (var y = this.Get('chart.gutter'); y<this.canvas.height - this.Get('chart.gutter'); y+=spacing) {
415                 this.context.moveTo(this.Get('chart.gutter'), y);
416                 this.context.lineTo(this.Get('chart.gutter') + 3, y);
417
418                 this.context.moveTo(this.canvas.width - this.Get('chart.gutter'), y);
419                 this.context.lineTo(this.canvas.width - this.Get('chart.gutter') - 3, y);
420             }
421
422             this.context.stroke();
423         }
424
425         /**
426         * Draw the actual bar
427         */
428         var barHeight = Math.min(this.height, ( (this.value - this.Get('chart.min')) / (this.max - this.Get('chart.min')) ) * this.height);
429
430         this.context.beginPath();
431         this.context.strokeStyle = 'black';
432
433         if (typeof(this.value) == 'number') {
434             this.context.strokeRect(this.Get('chart.gutter') + margin, this.Get('chart.gutter') + this.height - barHeight, this.width - margin - margin, barHeight);
435             this.context.fillRect(this.Get('chart.gutter') + margin, this.Get('chart.gutter') + this.height - barHeight, this.width - margin - margin, barHeight);
436         }
437
438
439         /**
440         * Draw the arrows indicating the level if requested
441         */
442         if (this.Get('chart.arrows')) {
443             var x = this.Get('chart.gutter') - 4;
444             var y = this.canvas.height - this.Get('chart.gutter') - barHeight;
445             
446             this.context.lineWidth = 1;
447             this.context.fillStyle = 'black';
448             this.context.strokeStyle = 'black';
449
450             this.context.beginPath();
451                 this.context.moveTo(x, y);
452                 this.context.lineTo(x - 4, y - 2);
453                 this.context.lineTo(x - 4, y + 2);
454             this.context.closePath();
455
456             this.context.stroke();
457             this.context.fill();
458
459             x +=  this.width + 8;
460
461             this.context.beginPath();
462                 this.context.moveTo(x, y);
463                 this.context.lineTo(x + 4, y - 2);
464                 this.context.lineTo(x + 4, y + 2);
465             this.context.closePath();
466
467             this.context.stroke();
468             this.context.fill();
469         }
470
471
472
473
474         /**
475         * Draw the "in-bar" label
476         */
477         if (this.Get('chart.label.inner')) {
478             this.context.beginPath();
479             this.context.fillStyle = 'black';
480             RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size') + 2, this.canvas.width / 2, this.canvas.height - this.Get('chart.gutter') - barHeight - 5, String(this.Get('chart.units.pre') + this.value + this.Get('chart.units.post')), 'bottom', 'center');
481             this.context.fill();
482         }
483
484
485         // Store the coords
486         this.coords.push([this.Get('chart.gutter') + margin, this.Get('chart.gutter') + this.height - barHeight, this.width - margin - margin, barHeight]);
487     }
488
489     /**
490     * The function that draws the tick marks. Apt name...
491     */
492     RGraph.VProgress.prototype.DrawTickMarks = function ()
493     {
494         var gutter = this.Get('chart.gutter');
495
496         this.context.strokeStyle = this.Get('chart.tickmarks.color');
497
498         if (this.Get('chart.tickmarks')) {
499         
500             var startX = this.Get('chart.labels.position') == 'left' ? (gutter - 4) : (this.canvas.width - gutter);
501             var endX   = this.Get('chart.labels.position') == 'left' ? gutter : this.canvas.width - gutter + 4;
502
503             this.tickInterval = this.height / this.Get('chart.numticks');
504
505             this.context.beginPath();
506                 for (var i=gutter; i<=(this.canvas.height - gutter - this.tickInterval); i+=this.tickInterval) {
507                     this.context.moveTo(startX, i);
508                     this.context.lineTo(endX, i);
509                 }
510         }
511
512         // Not necessary ???
513         //this.context.stroke();
514     }
515
516
517     /**
518     * The function that draws the labels
519     */
520     RGraph.VProgress.prototype.DrawLabels = function ()
521     {
522         this.context.fillStyle = this.Get('chart.text.color');
523
524         var context    = this.context;
525         var gutter     = this.Get('chart.gutter');
526         var position   = this.Get('chart.labels.position');
527         var xAlignment = position == 'left' ? 'right' : 'left';
528         var yAlignment = 'center';
529         var count      = this.Get('chart.labels.count');
530         var units_pre  = this.Get('chart.units.pre');
531         var units_post = this.Get('chart.units.post');
532         var text_size  = this.Get('chart.text.size');
533         var text_font  = this.Get('chart.text.font');
534         
535         if (this.Get('chart.tickmarks')) {
536             for (var i=0; i<count ; ++i) {
537     
538                 var text = String(
539                                   ((( (this.max - this.Get('chart.min')) / count) * (count - i)) + this.Get('chart.min')).toFixed(this.Get('chart.scale.decimals'))
540                                  );
541
542                 RGraph.Text(context, text_font, text_size, position == 'left' ? (gutter - 5) : (this.canvas.width - gutter + 5), (((this.canvas.height - (2 * gutter)) / count) * i) + gutter, units_pre + text + units_post, yAlignment, xAlignment);
543             }
544             
545             if (this.Get('chart.min') != 0) {
546                 RGraph.Text(context, text_font, text_size, position == 'left' ? (gutter - 5) : (this.canvas.width - gutter + 5), this.canvas.height - gutter, units_pre + String(this.Get('chart.min').toFixed(this.Get('chart.scale.decimals'))) + units_post, yAlignment, xAlignment);
547             }
548         }
549
550         // Draw the title text
551         if (this.Get('chart.title')) {
552             RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.Get('chart.gutter'), 0, this.Get('chart.text.size') + 2);
553         }
554     }