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