PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
← All changes | admin/resources/donutChart.js +545 -0 1.6.1 → 1.8.16 View file →
@@ -1,0 +1,545 @@
1 +/*
2 +The MIT License (MIT)
3 +
4 +Copyright (c) 2014 Valerio Neri
5 +
6 +Permission is hereby granted, free of charge, to any person obtaining a copy
7 +of this software and associated documentation files (the "Software"), to deal
8 +in the Software without restriction, including without limitation the rights
9 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 +copies of the Software, and to permit persons to whom the Software is
11 +furnished to do so, subject to the following conditions:
12 +
13 +The above copyright notice and this permission notice shall be included in all
14 +copies or substantial portions of the Software.
15 +*/
16 +
17 +
18 +var donutChart = function(chartElementID){
19 + var centerx = 0;
20 + var centery = 0;
21 + var sizex = 0;
22 + var sizey = 0;
23 + var scaling = 0;
24 + var radius = 0;
25 + var startx = 0;
26 + var starty = 0;
27 + var endx = 0;
28 + var endy = 0;
29 + var animationSpeed = 1;
30 + var firedAnimation = false;
31 + var innerCircleColor = "#666666";
32 + var outerCircleColor = "#aade87";
33 + var textColor = "#ffffff";
34 + var unitText = "%";
35 + var innerCircleDOM = undefined;
36 + var outerCircleDOM = undefined;
37 + var tNumberDOM = undefined;
38 + var textDOM = undefined;
39 + var tUnitDOM = undefined;
40 + var titleDOM = undefined;
41 + var svg = undefined;
42 + var chartID = chartElementID;
43 + var chart = undefined;
44 + var lastValue = -1;
45 + var startValue = 0;
46 + var endValue = 0;
47 + var loaded = false;
48 + var that=this;
49 + var maxValue = 100;
50 + var titleText ="";
51 + var titlePosition = "top";
52 + var titleColor = "#ffffff";
53 + var textShift = 20; // this is required for moving the chart
54 + var textScaling = 1;
55 +
56 + // update: unfortunately in some cases the event is fired, and nobody tells us - that's why we just wait for the element to be available
57 + // wait for the DOM, otherwise you won't find the reference to elements
58 + //document.addEventListener("DOMContentLoaded", onLoad);
59 + onLoad();
60 +
61 + // this is used for refreshing the animation
62 + (function() {
63 + var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
64 + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
65 + window.requestAnimationFrame = requestAnimationFrame;
66 + })();
67 +
68 + this.getValue = function(){
69 + if (lastValue>-1)
70 + return Math.round(lastValue/360*maxValue*10)/10;
71 + else
72 + return 0;
73 + }
74 +
75 + this.setValue = function(value){
76 + that.draw({end:value, animationSpeed:0});
77 + }
78 +
79 + function setAnimate(fromD, toD, duration){
80 + dAct = fromD;
81 + // determine direction (up or down)
82 + if (dAct<toD)
83 + doIt(dAct, toD, duration, "up");
84 + else
85 + doIt(dAct, toD, duration, "down");
86 + }
87 +
88 + // this function is used for the animation - it uses an exponential acceleration
89 + function calculateAccValue(xx, m){
90 + var mxValue = m;
91 + var x=Math.round(xx);
92 + if (m==0){
93 + mxValue=1;
94 + }
95 + if (x==0){
96 + x=1;
97 + }
98 + if (Math.abs(x)>=mxValue){
99 + return mxValue;
100 + }else{
101 + //return ( Math.pow( (-2),( (-1*x) + (Math.log(maxValue)/Math.log(2)) ) ) + maxValue);
102 + var num1 = -2;
103 + var num2 = Math.round( ((-1*x)+(Math.log(mxValue)/Math.log(2))) );
104 + return Math.pow(num1 , num2 )+mxValue;
105 + }
106 + }
107 +
108 + function textSetter(DOM, text){
109 + // text scaling
110 + if ((text>=1000)&&(textScaling=1)){
111 + textScaling=1-((Math.floor( (Math.log(text)/Math.log(10))-2))/10)-0.1;
112 + tNumberDOM.setAttribute('font-size', 50*scaling*textScaling);
113 + tUnitDOM.setAttribute('font-size', 30*scaling*textScaling);
114 + } else{
115 + textScaling = 1;
116 + tNumberDOM.setAttribute('font-size', 50*scaling*textScaling);
117 + tUnitDOM.setAttribute('font-size', 30*scaling*textScaling);
118 + }
119 + DOM.textContent = text;
120 + }
121 +
122 + function doIt(dAct, toD, duration, direction){
123 + var toString = "";
124 + // -0.0001 is used for preventing IE reach 360 and close the circle
125 + toString = getD(dAct-0.0001);
126 + outerCircleDOM.setAttribute('d',toString);
127 +
128 + //force the value to be an integer
129 + var anim_cur_val = Math.round(dAct/360*maxValue*10)/10;
130 + //textSetter(tNumberDOM, Math.round(dAct/360*maxValue*10)/10);
131 + textSetter(tNumberDOM, anim_cur_val.toFixed(0));
132 +
133 + //console.log('dAct '+dAct+ ' toD'+toD);
134 + // determine direction (up or down)
135 + if (direction=="up")
136 + dAct = dAct + 5.5;
137 + else
138 + dAct = dAct - 5.5;
139 +
140 + duration = duration - calculateAccValue(duration, 500);
141 + if (direction=="up"){
142 + if (dAct < toD){
143 + setTimeout(function(){
144 + // animate it according to refresh rate
145 + requestAnimationFrame(function(){
146 + doIt(dAct, toD, duration, direction);
147 + })
148 + },duration);
149 + } else {
150 +
151 + outerCircleDOM.setAttribute('d',getD((toD-0.0001)));
152 + textSetter(tNumberDOM, Math.round(toD/360*maxValue*10)/10);
153 + }
154 + } else {
155 + if (dAct >= toD){
156 + setTimeout(function(){
157 + // animate it according to refresh rate
158 + requestAnimationFrame(function(){
159 + doIt(dAct, toD, duration, direction);
160 + })
161 + },duration);
162 + } else {
163 + outerCircleDOM.setAttribute('d',getD(toD-0.0001));
164 + textSetter(tNumberDOM ,Math.round(toD/360*maxValue*10)/10);
165 + }
166 + }
167 +
168 + };
169 +
170 + // this function gives us the circle coordinates
171 + function getCoordinates(radius,offset,degrees){
172 + var radians = degreesToRadians(degrees);
173 + var x = offset.x + radius * Math.cos(radians)
174 + var y = offset.y + radius * Math.sin(radians)
175 + return {x:x,y:y};
176 + };
177 +
178 + // small converstion beween degrees and radians
179 + function degreesToRadians(degrees){
180 + var radians = (degrees * Math.PI) / 180;
181 + return radians
182 + };
183 +
184 + // this function returns the "d" string for the path, according to the degrees
185 + function getD(degree){
186 + radius = 100*scaling;
187 + startx = centerx+radius;
188 + starty = centery;
189 + var coor = getCoordinates(radius, {x:startx,y:starty},degree);
190 + endx = coor.x;
191 + endy = coor.y;
192 + var largearc = 0;
193 + if (degree>180){
194 + largearc=1;
195 + } else {
196 + largearc=0;
197 + }
198 + // don't ask me how I did it folks
199 + d="M "+startx+" "+starty+" a "+radius+" "+radius+" 0 "+largearc+" 1 "+(endx-startx-radius)+" "+(endy-starty)+" L "+(centerx)+" "+(centery)+" Z";
200 + //console.log('GETd '+degree+ " "+d);
201 + return d;
202 + }
203 +
204 + // this function checks, if an element is fully visible (according to the scrolling)
205 + function checkVisible(){
206 + if (firedAnimation){
207 + // animation has already fired, detach the event listeners
208 + document.removeEventListener("scroll", checkVisible, false);
209 + window.removeEventListener("resize", checkVisible, false);
210 + return;
211 + }
212 + var rect = chart.getBoundingClientRect();
213 + var top = window.pageYOffset || document.documentElement.scrollTop;
214 + var left = window.pageXOffset || document.documentElement.scrollLeft;
215 + if ((rect.top>=0)&&(rect.top+rect.height<window.innerHeight) &&
216 + (rect.left>=0)&&(rect.left+rect.width<window.innerWidth)){
217 + // this means that we fully see the chart
218 + // fire the animation (only once)
219 + firedAnimation = true;
220 + setAnimate(startValue, endValue, (500/animationSpeed));
221 + }
222 + }
223 +
224 + var waitingLoad =0;
225 +
226 + function onLoad(){
227 + // get the container for the chart
228 + chart = document.getElementById(chartID);
229 +
230 + // if the container is not ready in the DOM, then retry
231 + // try for around 20secs, then throw an exception
232 + if (chart==null){
233 +
234 + // after 10 seconds, pause a little bit
235 + if ((waitingLoad > 10000)&&(waitingLoad<20000)){
236 + setTimeout(onLoad, 1000);
237 + waitingLoad = waitingLoad +1000;
238 + return;
239 + }
240 + if (waitingLoad > 20000){
241 + throw('donutChart: onLoad() - The chart element "'+chartID+'" could not be loaded in the last 20 seconds');
242 + }
243 + setTimeout(onLoad, 500);
244 + waitingLoad = waitingLoad +500;
245 + return;
246 + }
247 + // create an svg object and all other things - thanks to Thoka
248 + svg = document.createElementNS('http://www.w3.org/2000/svg','svg');
249 + innerCircleDOM = document.createElementNS('http://www.w3.org/2000/svg','circle');
250 + outerCircleDOM = document.createElementNS('http://www.w3.org/2000/svg','path');
251 + textDOM = document.createElementNS('http://www.w3.org/2000/svg','text');
252 + tNumberDOM = document.createElementNS('http://www.w3.org/2000/svg','tspan');
253 + tUnitDOM = document.createElementNS('http://www.w3.org/2000/svg','tspan');
254 + titleDOM = document.createElementNS('http://www.w3.org/2000/svg','text');
255 +
256 + // append to the chart
257 + chart.appendChild(svg);
258 + svg.appendChild(outerCircleDOM);
259 + svg.appendChild(innerCircleDOM);
260 + svg.appendChild(textDOM);
261 + svg.appendChild(titleDOM);
262 +
263 + textDOM.appendChild(tNumberDOM);
264 + textDOM.appendChild(tUnitDOM);
265 + loaded = true;
266 + }
267 +
268 + this.reload = function(){
269 + if (!loaded)
270 + onLoad();
271 + }
272 +
273 + this.clear = function(){
274 + // reinitialize some parameters
275 + firedAnimation = false;
276 + animationSpeed = 1;
277 + maxValue = 100;
278 + unitText = "%";
279 + lastValue = -1;
280 + this.setValue(0);
281 + }
282 +
283 + this.delete = function(){
284 + // remove everything
285 + this.clear();
286 + textDOM.removeChild(tNumberDOM);
287 + textDOM.removeChild(tUnitDOM);
288 + svg.removeChild(textDOM);
289 + svg.removeChild(titleDOM);
290 + svg.removeChild(outerCircleDOM);
291 + svg.removeChild(innerCircleDOM);
292 + chart.removeChild(svg);
293 + loaded = false;
294 + // yes but we leave chart, because it was provided
295 + }
296 +
297 + this.draw = function(options){
298 + // check if main function has already loaded, if not, reiterate
299 + if (!loaded){
300 + setTimeout(function(){
301 + that.draw(options);
302 + }, 500);
303 + return;
304 + }
305 +
306 + // OPTION CHECK - contains the options
307 + // check if all options are set
308 + var oTester = [];
309 + oTester.options = typeof(options)!="undefined";
310 +
311 + if (! (oTester.options)){
312 + throw('donutChart: draw() - Not enough parameters or no parameters set in object');
313 + return;
314 + }
315 +
316 + oTester.end = typeof(options.end)!="undefined";
317 + oTester.start = typeof(options.start)!="undefined";
318 + oTester.scaling = typeof(options.scaling)!="undefined";
319 + oTester.size = typeof(options.size)!="undefined";
320 + oTester.animationSpeed = typeof(options.animationSpeed)!="undefined";
321 + oTester.textColor = typeof(options.textColor)!="undefined";
322 + oTester.innerCircleColor = typeof(options.innerCircleColor)!="undefined";
323 + oTester.outerCircleColor = typeof(options.outerCircleColor)!="undefined";
324 + oTester.unitText = typeof(options.unitText)!="undefined";
325 + oTester.maxValue = typeof(options.maxValue)!="undefined";
326 + oTester.titleText = typeof(options.titleText)!="undefined";
327 + oTester.titleColor = typeof(options.titleColor)!="undefined";
328 + oTester.titlePosition = typeof(options.titlePosition)!="undefined";
329 +
330 + if (! (oTester.end)){
331 + throw('donutChart: draw() - No "end" value specified');
332 + return;
333 + }
334 +
335 + // reinitialize some parameters
336 + firedAnimation = false;
337 + animationSpeed = 1;
338 + maxValue = 100;
339 + unitText = "%";
340 + textShift = 20;
341 + textScaling = 1;
342 +
343 + // if the values are not set, set the standard
344 + if (!oTester.size){
345 + options.size = 200;
346 + }
347 +
348 + if (!oTester.scaling){
349 + options.scaling = 1;
350 + }
351 +
352 + // if size ist not set, then take the options.scaling and the standard size of 200x200
353 + if (!oTester.size){
354 + svg.setAttribute('width', 200*options.scaling);
355 + svg.setAttribute('height', 200*options.scaling);
356 + options.size = 200*options.scaling;
357 + }
358 + // if scaling not set, then take the size anc calculate the options.scaling
359 + if (!oTester.scaling){
360 + svg.setAttribute('width', options.size);
361 + svg.setAttribute('height', options.size);
362 + if (svg.getAttribute('width')<svg.getAttribute('height')){
363 + options.scaling = svg.getAttribute('width') / 200;
364 + } else{
365 + options.scaling = svg.getAttribute('height') / 200;
366 + }
367 + }
368 +
369 + // set the colors, if set
370 + if (oTester.outerCircleColor){
371 + outerCircleColor = options.outerCircleColor;
372 + }
373 + if (oTester.innerCircleColor){
374 + innerCircleColor = options.innerCircleColor;
375 + }
376 + if (oTester.textColor){
377 + textColor = options.textColor;
378 + }
379 +
380 + // set the unitText, if set
381 + if (oTester.unitText){
382 + unitText = options.unitText;
383 + }
384 +
385 + // set the title, if set
386 + if (oTester.titleText){
387 + titleText = options.titleText;
388 + }
389 + if (oTester.titleColor){
390 + titleColor = options.titleColor;
391 + }
392 + if (oTester.titlePosition){
393 + titlePosition = options.titlePosition;
394 + }
395 +
396 +
397 + // set the maxValue, if set
398 + if (oTester.maxValue){
399 + maxValue = options.maxValue;
400 + }
401 +
402 + // set the starting position, if set
403 + if (oTester.start){
404 + options.start = Math.round(parseFloat(options.start)*10)/10;
405 + if (options.start>maxValue)
406 + options.start = maxValue;
407 + if (options.start<0)
408 + options.start = 0;
409 + if (isNaN(options.start)){
410 + // not a number and not parseable, ignore
411 + options.start = 0;
412 + oTester.start = false;
413 + }
414 + startValue = options.start/maxValue*360;
415 + }
416 +
417 + options.end = Math.round(parseFloat(options.end)*10)/10;
418 +
419 + if (options.end>maxValue)
420 + options.end = maxValue;
421 + if (options.end<0)
422 + options.end = 0;
423 +
424 + if (isNaN(options.end)){
425 + // not a number and not parseable, ignore
426 + return;
427 + }
428 +
429 + options.end = options.end/maxValue*360;
430 + endValue = options.end;
431 +
432 + textShift = textShift*options.scaling;
433 +
434 + // set the size and the center
435 + sizex = options.size;
436 + sizey = options.size+2*textShift;
437 + centerx = sizex/2;
438 + centery = (sizey/2);
439 + scaling = options.scaling;
440 +
441 + // set animation speed (0 = no animation), standard is one
442 + if(oTester.animationSpeed){
443 + animationSpeed = options.animationSpeed;
444 + }
445 +
446 + //e4j
447 + var innerCircleStroke = typeof(options.innerCircleStroke)!="undefined" ? options.innerCircleStroke : 'none';
448 + var outerCircleStroke = typeof(options.outerCircleStroke)!="undefined" ? options.outerCircleStroke : 'none';
449 + //
450 +
451 + // initialise with start position
452 + outerCircleDOM.setAttribute('d', getD(startValue));
453 +
454 + chart.style.width = sizex;
455 + chart.style.height = sizey;
456 +
457 + svg.setAttribute('height', sizey);
458 + svg.setAttribute('class', 'donutChart');
459 + svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
460 + svg.setAttribute('version', '1.1');
461 +
462 + innerCircleDOM.setAttribute('cx', centerx);
463 + innerCircleDOM.setAttribute('cy', centery);
464 + innerCircleDOM.setAttribute('r', 80*options.scaling);
465 + innerCircleDOM.setAttribute('stroke', innerCircleStroke);
466 + innerCircleDOM.setAttribute('stroke-width', '2');
467 + innerCircleDOM.setAttribute('fill', innerCircleColor);
468 +
469 + outerCircleDOM.setAttribute('fill', outerCircleColor);
470 + outerCircleDOM.setAttribute('stroke', outerCircleStroke);
471 + outerCircleDOM.setAttribute('stroke-width', '36');
472 +
473 + tNumberDOM.setAttribute('x', centerx);
474 + tNumberDOM.setAttribute('y', centery+Math.round(18.75*options.scaling));
475 + tNumberDOM.setAttribute('font-size', 50*options.scaling);
476 + tNumberDOM.setAttribute('fill', textColor);
477 + tNumberDOM.setAttribute('font-family', 'arial');
478 + tNumberDOM.setAttribute('font-weight', 'normal');
479 + tNumberDOM.setAttribute('text-anchor', 'middle');
480 +
481 + tUnitDOM.setAttribute('font-size', 30*options.scaling);
482 + tUnitDOM.textContent = unitText;
483 + tUnitDOM.setAttribute('fill', textColor);
484 + tUnitDOM.setAttribute('text-anchor', 'middle');
485 + tUnitDOM.setAttribute('font-family', 'arial');
486 + tUnitDOM.setAttribute('font-weight', 'normal');
487 +
488 + titleDOM.setAttribute('x', centerx);
489 + // set the position of the title
490 + switch(titlePosition){
491 + case "inner-bottom":
492 + if (!oTester.titleColor)
493 + titleColor = "#ffffff";
494 + titleDOM.setAttribute('y', centery+sizey/5+textShift/2);
495 + break;
496 +
497 + case "outer-bottom":
498 + if (!oTester.titleColor)
499 + titleColor = "#666666";
500 +
501 + titleDOM.setAttribute('y', sizey-textShift/2);
502 + break;
503 +
504 + case "inner-top":
505 + if (!oTester.titleColor)
506 + titleColor = "#ffffff";
507 + titleDOM.setAttribute('y', centery-sizey/5-textShift/2);
508 + break;
509 +
510 + default:
511 + case "outer-top":
512 + if (!oTester.titleColor)
513 + titleColor = "#666666";
514 +
515 + titleDOM.setAttribute('y', textShift/2);
516 + break;
517 + }
518 +
519 + titleDOM.setAttribute('font-size', 12*options.scaling);
520 + titleDOM.setAttribute('fill', titleColor);
521 + titleDOM.setAttribute('font-family', 'arial');
522 + titleDOM.setAttribute('font-weight', 'normal');
523 + titleDOM.setAttribute('text-anchor', 'middle');
524 + titleDOM.textContent = titleText;
525 +
526 +
527 + // if the animation has been chosen
528 + if (animationSpeed>0){
529 + // ignore start value if lastvalue is already set
530 + if (lastValue>-1){
531 + startValue = lastValue;
532 + }
533 + window.addEventListener("resize", checkVisible, false);
534 + document.addEventListener("scroll", checkVisible, false);
535 + tNumberDOM.textContent = Math.round(startValue/360*maxValue*10)/10;
536 + checkVisible();
537 + } else {
538 + // set the circle to the end position, without animation
539 + // the 0.0001 is for avoiding the circle to disappear, due to some browser
540 + outerCircleDOM.setAttribute('d', getD(options.end-0.0001));
541 + tNumberDOM.textContent = Math.round(options.end/360*maxValue*10)/10;
542 + }
543 + lastValue = endValue;
544 + }
545 +}