1<?php 2/* Copyright (c) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org> 3 * Copyright (c) 2004-2015 Laurent Destailleur <eldy@users.sourceforge.net> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19/** 20 * \file htdocs/core/class/dolgraph.class.php 21 * \ingroup core 22 * \brief File for class to generate graph 23 */ 24 25 26/** 27 * Class to build graphs. 28 * Usage is: 29 * $dolgraph=new DolGraph(); 30 * $dolgraph->SetTitle($langs->transnoentities('MyTitle').'<br>'.$langs->transnoentities('MyTitlePercent').'%'); 31 * $dolgraph->SetMaxValue(50); 32 * $dolgraph->SetData($data); 33 * $dolgraph->setShowLegend(2); 34 * $dolgraph->setShowPercent(1); 35 * $dolgraph->SetType(array('pie')); 36 * $dolgraph->setHeight('200'); 37 * $dolgraph->draw('idofgraph'); 38 * print $dolgraph->show($total?0:1); 39 */ 40class DolGraph 41{ 42 public $type = array(); // Array with type of each series. Example: array('bars', 'horizontalbars', 'lines', 'pies', 'piesemicircle', 'polar'...) 43 public $mode = 'side'; // Mode bars graph: side, depth 44 private $_library = 'chart'; // Graphic library to use (jflot, chart, artichow) 45 46 //! Array of data 47 public $data; // Data of graph: array(array('abs1',valA1,valB1), array('abs2',valA2,valB2), ...) 48 public $title; // Title of graph 49 public $cssprefix = ''; // To add into css styles 50 51 /** 52 * @var int|string Width of graph. It can be a numeric for pixels or a string like '100%' 53 */ 54 public $width = 380; 55 /** 56 * @var int Height of graph 57 */ 58 public $height = 200; 59 60 public $MaxValue = 0; 61 public $MinValue = 0; 62 public $SetShading = 0; 63 64 public $horizTickIncrement = -1; 65 public $SetNumXTicks = -1; 66 public $labelInterval = -1; 67 68 public $hideXGrid = false; 69 public $hideYGrid = false; 70 71 public $Legend = array(); 72 public $LegendWidthMin = 0; 73 public $showlegend = 1; 74 public $showpointvalue = 1; 75 public $showpercent = 0; 76 public $combine = 0; // 0.05 if you want to combine records < 5% into "other" 77 public $graph; // Objet Graph (Artichow, Phplot...) 78 79 /** 80 * @var string Error code (or message) 81 */ 82 public $error = ''; 83 84 public $bordercolor; // array(R,G,B) 85 public $bgcolor; // array(R,G,B) 86 public $bgcolorgrid = array(255, 255, 255); // array(R,G,B) 87 public $datacolor; // array(array(R,G,B),...) 88 89 private $stringtoshow; // To store string to output graph into HTML page 90 91 92 /** 93 * Constructor 94 * 95 * @param string $library 'auto' (default) 96 */ 97 public function __construct($library = 'auto') 98 { 99 global $conf; 100 global $theme_bordercolor, $theme_datacolor, $theme_bgcolor; 101 102 $this->bordercolor = array(235, 235, 224); 103 $this->datacolor = array(array(120, 130, 150), array(160, 160, 180), array(190, 190, 220)); 104 $this->bgcolor = array(235, 235, 224); 105 106 $color_file = DOL_DOCUMENT_ROOT . '/theme/' . $conf->theme . '/theme_vars.inc.php'; 107 if (is_readable($color_file)) { 108 include_once $color_file; 109 if (isset($theme_bordercolor)) $this->bordercolor = $theme_bordercolor; 110 if (isset($theme_datacolor)) $this->datacolor = $theme_datacolor; 111 if (isset($theme_bgcolor)) $this->bgcolor = $theme_bgcolor; 112 } 113 //print 'bgcolor: '.join(',',$this->bgcolor).'<br>'; 114 115 $this->_library = $library; 116 if ($this->_library == 'auto') { 117 $this->_library = (empty($conf->global->MAIN_JS_GRAPH) ? 'chart' : $conf->global->MAIN_JS_GRAPH); 118 } 119 } 120 121 122 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 123 /** 124 * Utiliser SetNumTicks ou SetHorizTickIncrement mais pas les 2 125 * 126 * @param float $xi Xi 127 * @return boolean True 128 */ 129 public function SetHorizTickIncrement($xi) 130 { 131 // phpcs:enable 132 $this->horizTickIncrement = $xi; 133 return true; 134 } 135 136 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 137 /** 138 * Utiliser SetNumTicks ou SetHorizTickIncrement mais pas les 2 139 * 140 * @param float $xt Xt 141 * @return boolean True 142 */ 143 public function SetNumXTicks($xt) 144 { 145 // phpcs:enable 146 $this->SetNumXTicks = $xt; 147 return true; 148 } 149 150 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 151 /** 152 * Set label interval to reduce number of labels 153 * 154 * @param float $x Label interval 155 * @return boolean True 156 */ 157 public function SetLabelInterval($x) 158 { 159 // phpcs:enable 160 $this->labelInterval = $x; 161 return true; 162 } 163 164 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 165 /** 166 * Hide X grid 167 * 168 * @param boolean $bool XGrid or not 169 * @return boolean true 170 */ 171 public function SetHideXGrid($bool) 172 { 173 // phpcs:enable 174 $this->hideXGrid = $bool; 175 return true; 176 } 177 178 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 179 /** 180 * Hide Y grid 181 * 182 * @param boolean $bool YGrid or not 183 * @return boolean true 184 */ 185 public function SetHideYGrid($bool) 186 { 187 // phpcs:enable 188 $this->hideYGrid = $bool; 189 return true; 190 } 191 192 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 193 /** 194 * Set y label 195 * 196 * @param string $label Y label 197 * @return boolean|null True 198 */ 199 public function SetYLabel($label) 200 { 201 // phpcs:enable 202 $this->YLabel = $label; 203 } 204 205 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 206 /** 207 * Set width 208 * 209 * @param int|string $w Width (Example: 320 or '100%') 210 * @return boolean|null True 211 */ 212 public function SetWidth($w) 213 { 214 // phpcs:enable 215 $this->width = $w; 216 } 217 218 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 219 /** 220 * Set title 221 * 222 * @param string $title Title 223 * @return void 224 */ 225 public function SetTitle($title) 226 { 227 // phpcs:enable 228 $this->title = $title; 229 } 230 231 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 232 /** 233 * Set data 234 * 235 * @param array $data Data 236 * @return void 237 * @see draw_jflot() for syntax of data array 238 */ 239 public function SetData($data) 240 { 241 // phpcs:enable 242 $this->data = $data; 243 } 244 245 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 246 /** 247 * Set data 248 * 249 * @param array $datacolor Data color array(array(R,G,B),array(R,G,B)...) 250 * @return void 251 */ 252 public function SetDataColor($datacolor) 253 { 254 // phpcs:enable 255 $this->datacolor = $datacolor; 256 } 257 258 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 259 /** 260 * Set type 261 * 262 * @param array $type Array with type for each serie. Example: array('type1', 'type2', ...) where type can be: 263 * 'pie', 'piesemicircle', 'polar', 'lines', 'linesnopoint', 'bars', 'horizontalbars'... 264 * @return void 265 */ 266 public function SetType($type) 267 { 268 // phpcs:enable 269 $this->type = $type; 270 } 271 272 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 273 /** 274 * Set legend 275 * 276 * @param array $legend Legend. Example: array('seriename1','seriname2',...) 277 * @return void 278 */ 279 public function SetLegend($legend) 280 { 281 // phpcs:enable 282 $this->Legend = $legend; 283 } 284 285 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 286 /** 287 * Set min width 288 * 289 * @param int $legendwidthmin Min width 290 * @return void 291 */ 292 public function SetLegendWidthMin($legendwidthmin) 293 { 294 // phpcs:enable 295 $this->LegendWidthMin = $legendwidthmin; 296 } 297 298 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 299 /** 300 * Set max value 301 * 302 * @param int $max Max value 303 * @return void 304 */ 305 public function SetMaxValue($max) 306 { 307 // phpcs:enable 308 $this->MaxValue = $max; 309 } 310 311 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 312 /** 313 * Get max value 314 * 315 * @return int Max value 316 */ 317 public function GetMaxValue() 318 { 319 // phpcs:enable 320 return $this->MaxValue; 321 } 322 323 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 324 /** 325 * Set min value 326 * 327 * @param int $min Min value 328 * @return void 329 */ 330 public function SetMinValue($min) 331 { 332 // phpcs:enable 333 $this->MinValue = $min; 334 } 335 336 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 337 /** 338 * Get min value 339 * 340 * @return int Max value 341 */ 342 public function GetMinValue() 343 { 344 // phpcs:enable 345 return $this->MinValue; 346 } 347 348 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 349 /** 350 * Set height 351 * 352 * @param int $h Height 353 * @return void 354 */ 355 public function SetHeight($h) 356 { 357 // phpcs:enable 358 $this->height = $h; 359 } 360 361 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 362 /** 363 * Set shading 364 * 365 * @param string $s Shading 366 * @return void 367 */ 368 public function SetShading($s) 369 { 370 // phpcs:enable 371 $this->SetShading = $s; 372 } 373 374 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 375 /** 376 * Set shading 377 * 378 * @param string $s Shading 379 * @return void 380 */ 381 public function SetCssPrefix($s) 382 { 383 // phpcs:enable 384 $this->cssprefix = $s; 385 } 386 387 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 388 /** 389 * Reset bg color 390 * 391 * @return void 392 */ 393 public function ResetBgColor() 394 { 395 // phpcs:enable 396 unset($this->bgcolor); 397 } 398 399 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 400 /** 401 * Reset bgcolorgrid 402 * 403 * @return void 404 */ 405 public function ResetBgColorGrid() 406 { 407 // phpcs:enable 408 unset($this->bgcolorgrid); 409 } 410 411 /** 412 * Is graph ko 413 * 414 * @return string Error 415 */ 416 public function isGraphKo() 417 { 418 return $this->error; 419 } 420 421 /** 422 * Show legend or not 423 * 424 * @param int $showlegend 1=Show legend (default), 0=Hide legend, 2=Show legend on right 425 * @return void 426 */ 427 public function setShowLegend($showlegend) 428 { 429 $this->showlegend = $showlegend; 430 } 431 432 /** 433 * Show pointvalue or not 434 * 435 * @param int $showpointvalue 1=Show value for each point, as tooltip or inline (default), 0=Hide value 436 * @return void 437 */ 438 public function setShowPointValue($showpointvalue) 439 { 440 $this->showpointvalue = $showpointvalue; 441 } 442 443 /** 444 * Show percent or not 445 * 446 * @param int $showpercent 1=Show percent for each point, as tooltip or inline, 0=Hide percent (default) 447 * @return void 448 */ 449 public function setShowPercent($showpercent) 450 { 451 $this->showpercent = $showpercent; 452 } 453 454 455 456 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 457 /** 458 * Define background color of complete image 459 * 460 * @param array $bg_color array(R,G,B) ou 'onglet' ou 'default' 461 * @return void 462 */ 463 public function SetBgColor($bg_color = array(255, 255, 255)) 464 { 465 // phpcs:enable 466 global $theme_bgcolor, $theme_bgcoloronglet; 467 468 if (!is_array($bg_color)) { 469 if ($bg_color == 'onglet') { 470 //print 'ee'.join(',',$theme_bgcoloronglet); 471 $this->bgcolor = $theme_bgcoloronglet; 472 } else { 473 $this->bgcolor = $theme_bgcolor; 474 } 475 } else { 476 $this->bgcolor = $bg_color; 477 } 478 } 479 480 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 481 /** 482 * Define background color of grid 483 * 484 * @param array $bg_colorgrid array(R,G,B) ou 'onglet' ou 'default' 485 * @return void 486 */ 487 public function SetBgColorGrid($bg_colorgrid = array(255, 255, 255)) 488 { 489 // phpcs:enable 490 global $theme_bgcolor, $theme_bgcoloronglet; 491 492 if (!is_array($bg_colorgrid)) { 493 if ($bg_colorgrid == 'onglet') { 494 //print 'ee'.join(',',$theme_bgcoloronglet); 495 $this->bgcolorgrid = $theme_bgcoloronglet; 496 } else { 497 $this->bgcolorgrid = $theme_bgcolor; 498 } 499 } else { 500 $this->bgcolorgrid = $bg_colorgrid; 501 } 502 } 503 504 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 505 /** 506 * Reset data color 507 * 508 * @return void 509 */ 510 public function ResetDataColor() 511 { 512 // phpcs:enable 513 unset($this->datacolor); 514 } 515 516 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 517 /** 518 * Get max value 519 * 520 * @return int Max value 521 */ 522 public function GetMaxValueInData() 523 { 524 // phpcs:enable 525 if (!is_array($this->data)) return 0; 526 527 $k = 0; 528 $vals = array(); 529 530 $nblines = count($this->data); 531 $nbvalues = (empty($this->data[0]) ? 0 : count($this->data[0]) - 1); 532 533 for ($j = 0; $j < $nblines; $j++) { 534 for ($i = 0; $i < $nbvalues; $i++) { 535 $vals[$k] = $this->data[$j][$i + 1]; 536 $k++; 537 } 538 } 539 rsort($vals); 540 return $vals[0]; 541 } 542 543 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 544 /** 545 * Return min value of all data 546 * 547 * @return int Min value of all data 548 */ 549 public function GetMinValueInData() 550 { 551 // phpcs:enable 552 if (!is_array($this->data)) return 0; 553 554 $k = 0; 555 $vals = array(); 556 557 $nblines = count($this->data); 558 $nbvalues = (empty($this->data[0]) ? 0 : count($this->data[0]) - 1); 559 560 for ($j = 0; $j < $nblines; $j++) { 561 for ($i = 0; $i < $nbvalues; $i++) { 562 $vals[$k] = $this->data[$j][$i + 1]; 563 $k++; 564 } 565 } 566 sort($vals); 567 return $vals[0]; 568 } 569 570 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 571 /** 572 * Return max value of all data 573 * 574 * @return int Max value of all data 575 */ 576 public function GetCeilMaxValue() 577 { 578 // phpcs:enable 579 $max = $this->GetMaxValueInData(); 580 if ($max != 0) $max++; 581 $size = dol_strlen(abs(ceil($max))); 582 $factor = 1; 583 for ($i = 0; $i < ($size - 1); $i++) { 584 $factor *= 10; 585 } 586 587 $res = 0; 588 if (is_numeric($max)) $res = ceil($max / $factor) * $factor; 589 590 //print "max=".$max." res=".$res; 591 return $res; 592 } 593 594 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 595 /** 596 * Return min value of all data 597 * 598 * @return double Max value of all data 599 */ 600 public function GetFloorMinValue() 601 { 602 // phpcs:enable 603 $min = $this->GetMinValueInData(); 604 if ($min == '') $min = 0; 605 if ($min != 0) $min--; 606 $size = dol_strlen(abs(floor($min))); 607 $factor = 1; 608 for ($i = 0; $i < ($size - 1); $i++) { 609 $factor *= 10; 610 } 611 612 $res = floor($min / $factor) * $factor; 613 614 //print "min=".$min." res=".$res; 615 return $res; 616 } 617 618 /** 619 * Build a graph into memory using correct library (may also be wrote on disk, depending on library used) 620 * 621 * @param string $file Image file name to use to save onto disk (also used as javascript unique id) 622 * @param string $fileurl Url path to show image if saved onto disk 623 * @return integer|null 624 */ 625 public function draw($file, $fileurl = '') 626 { 627 if (empty($file)) { 628 $this->error = "Call to draw method was made with empty value for parameter file."; 629 dol_syslog(get_class($this) . "::draw " . $this->error, LOG_ERR); 630 return -2; 631 } 632 if (!is_array($this->data)) { 633 $this->error = "Call to draw method was made but SetData was not called or called with an empty dataset for parameters"; 634 dol_syslog(get_class($this) . "::draw " . $this->error, LOG_ERR); 635 return -1; 636 } 637 if (count($this->data) < 1) { 638 $this->error = "Call to draw method was made but SetData was is an empty dataset"; 639 dol_syslog(get_class($this) . "::draw " . $this->error, LOG_WARNING); 640 } 641 $call = "draw_" . $this->_library; 642 call_user_func_array(array($this, $call), array($file, $fileurl)); 643 } 644 645 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 646 /** 647 * Build a graph using JFlot library. Input when calling this method should be: 648 * $this->data = array(array(0=>'labelxA',1=>yA), array('labelxB',yB)); 649 * $this->data = array(array(0=>'labelxA',1=>yA1,...,n=>yAn), array('labelxB',yB1,...yBn)); // or when there is n series to show for each x 650 * $this->data = array(array('label'=>'labelxA','data'=>yA), array('labelxB',yB)); // Syntax deprecated 651 * $this->legend= array("Val1",...,"Valn"); // list of n series name 652 * $this->type = array('bars',...'lines','linesnopoint'); or array('pie') or array('polar') 653 * $this->mode = 'depth' ??? 654 * $this->bgcolorgrid 655 * $this->datacolor 656 * $this->shownodatagraph 657 * 658 * @param string $file Image file name to use to save onto disk (also used as javascript unique id) 659 * @param string $fileurl Url path to show image if saved onto disk. Never used here. 660 * @return void 661 */ 662 private function draw_jflot($file, $fileurl) 663 { 664 // phpcs:enable 665 global $conf, $langs; 666 667 dol_syslog(get_class($this) . "::draw_jflot this->type=" . join(',', $this->type) . " this->MaxValue=" . $this->MaxValue); 668 669 if (empty($this->width) && empty($this->height)) { 670 print 'Error width or height not set'; 671 return; 672 } 673 674 $legends = array(); 675 $nblot = 0; 676 if (is_array($this->data) && is_array($this->data[0])) { 677 $nblot = count($this->data[0]) - 1; // -1 to remove legend 678 } 679 if ($nblot < 0) dol_syslog('Bad value for property ->data. Must be set by mydolgraph->SetData before calling mydolgrapgh->draw', LOG_WARNING); 680 $firstlot = 0; 681 // Works with line but not with bars 682 //if ($nblot > 2) $firstlot = ($nblot - 2); // We limit nblot to 2 because jflot can't manage more than 2 bars on same x 683 684 $i = $firstlot; 685 $serie = array(); 686 while ($i < $nblot) // Loop on each serie 687 { 688 $values = array(); // Array with horizontal y values (specific values of a serie) for each abscisse x 689 $serie[$i] = "var d" . $i . " = [];\n"; 690 691 // Fill array $values 692 $x = 0; 693 foreach ($this->data as $valarray) // Loop on each x 694 { 695 $legends[$x] = $valarray[0]; 696 $values[$x] = (is_numeric($valarray[$i + 1]) ? $valarray[$i + 1] : null); 697 $x++; 698 } 699 700 if (isset($this->type[$firstlot]) && in_array($this->type[$firstlot], array('pie', 'piesemicircle', 'polar'))) { 701 foreach ($values as $x => $y) { 702 if (isset($y)) $serie[$i] .= 'd' . $i . '.push({"label":"' . dol_escape_js($legends[$x]) . '", "data":' . $y . '});' . "\n"; 703 } 704 } else { 705 foreach ($values as $x => $y) { 706 if (isset($y)) $serie[$i] .= 'd' . $i . '.push([' . $x . ', ' . $y . ']);' . "\n"; 707 } 708 } 709 710 unset($values); 711 $i++; 712 } 713 $tag = dol_escape_htmltag(dol_string_unaccent(dol_string_nospecial(basename($file), '_', array('-', '.')))); 714 715 $this->stringtoshow = '<!-- Build using jflot -->' . "\n"; 716 if (!empty($this->title)) $this->stringtoshow .= '<div class="center dolgraphtitle' . (empty($this->cssprefix) ? '' : ' dolgraphtitle' . $this->cssprefix) . '">' . $this->title . '</div>'; 717 if (!empty($this->shownographyet)) { 718 $this->stringtoshow .= '<div style="width:' . $this->width . 'px;height:' . $this->height . 'px;" class="nographyet"></div>'; 719 $this->stringtoshow .= '<div class="nographyettext margintoponly">' . $langs->trans("NotEnoughDataYet") . '...</div>'; 720 return; 721 } 722 723 // Start the div that will contains all the graph 724 $dolxaxisvertical = ''; 725 if (count($this->data) > 20) $dolxaxisvertical = 'dol-xaxis-vertical'; 726 $this->stringtoshow .= '<div id="placeholder_' . $tag . '" style="width:' . $this->width . 'px;height:' . $this->height . 'px;" class="dolgraph' . (empty($dolxaxisvertical) ? '' : ' ' . $dolxaxisvertical) . (empty($this->cssprefix) ? '' : ' dolgraph' . $this->cssprefix) . ' center"></div>' . "\n"; 727 728 $this->stringtoshow .= '<script id="' . $tag . '">' . "\n"; 729 $this->stringtoshow .= '$(function () {' . "\n"; 730 $i = $firstlot; 731 if ($nblot < 0) { 732 $this->stringtoshow .= '<!-- No series of data -->' . "\n"; 733 } else { 734 while ($i < $nblot) { 735 $this->stringtoshow .= '<!-- Serie ' . $i . ' -->' . "\n"; 736 $this->stringtoshow .= $serie[$i] . "\n"; 737 $i++; 738 } 739 } 740 $this->stringtoshow .= "\n"; 741 742 // Special case for Graph of type 'pie' 743 if (isset($this->type[$firstlot]) && in_array($this->type[$firstlot], array('pie', 'piesemicircle', 'polar'))) { 744 $datacolor = array(); 745 foreach ($this->datacolor as $val) { 746 if (is_array($val)) $datacolor[] = "#" . sprintf("%02x%02x%02x", $val[0], $val[1], $val[2]); // If datacolor is array(R, G, B) 747 else $datacolor[] = "#" . str_replace(array('#', '-'), '', $val); // If $val is '124' or '#124' 748 } 749 750 $urltemp = ''; // TODO Add support for url link into labels 751 $showlegend = $this->showlegend; 752 $showpointvalue = $this->showpointvalue; 753 $showpercent = $this->showpercent; 754 755 $this->stringtoshow .= ' 756 function plotWithOptions_' . $tag . '() { 757 $.plot($("#placeholder_' . $tag . '"), d0, 758 { 759 series: { 760 pie: { 761 show: true, 762 radius: 0.8, 763 ' . ($this->combine ? ' 764 combine: { 765 threshold: ' . $this->combine . ' 766 },' : '') . ' 767 label: { 768 show: true, 769 radius: 0.9, 770 formatter: function(label, series) { 771 var percent=Math.round(series.percent); 772 var number=series.data[0][1]; 773 return \''; 774 $this->stringtoshow .= '<span style="font-size:8pt;text-align:center;padding:2px;color:black;">'; 775 if ($urltemp) $this->stringtoshow .= '<a style="color: #FFFFFF;" border="0" href="' . $urltemp . '">'; 776 $this->stringtoshow .= '\'+'; 777 $this->stringtoshow .= ($showlegend ? '' : 'label+\' \'+'); // Hide label if already shown in legend 778 $this->stringtoshow .= ($showpointvalue ? 'number+' : ''); 779 $this->stringtoshow .= ($showpercent ? '\'<br/>\'+percent+\'%\'+' : ''); 780 $this->stringtoshow .= '\''; 781 if ($urltemp) $this->stringtoshow .= '</a>'; 782 $this->stringtoshow .= '</span>\'; 783 }, 784 background: { 785 opacity: 0.0, 786 color: \'#000000\' 787 } 788 } 789 } 790 }, 791 zoom: { 792 interactive: true 793 }, 794 pan: { 795 interactive: true 796 },'; 797 if (count($datacolor)) { 798 $this->stringtoshow .= 'colors: ' . (!empty($data['seriescolor']) ? json_encode($data['seriescolor']) : json_encode($datacolor)) . ','; 799 } 800 $this->stringtoshow .= 'legend: {show: ' . ($showlegend ? 'true' : 'false') . ', position: \'ne\' } 801 }); 802 }' . "\n"; 803 } 804 // Other cases, graph of type 'bars', 'lines' 805 else { 806 // Add code to support tooltips 807 // TODO: remove js css and use graph-tooltip-inner class instead by adding css in each themes 808 $this->stringtoshow .= ' 809 function showTooltip_' . $tag . '(x, y, contents) { 810 $(\'<div class="graph-tooltip-inner" id="tooltip_' . $tag . '">\' + contents + \'</div>\').css({ 811 position: \'absolute\', 812 display: \'none\', 813 top: y + 10, 814 left: x + 15, 815 border: \'1px solid #000\', 816 padding: \'5px\', 817 \'background-color\': \'#000\', 818 \'color\': \'#fff\', 819 \'font-weight\': \'bold\', 820 width: 200, 821 opacity: 0.80 822 }).appendTo("body").fadeIn(100); 823 } 824 825 var previousPoint = null; 826 $("#placeholder_' . $tag . '").bind("plothover", function (event, pos, item) { 827 $("#x").text(pos.x.toFixed(2)); 828 $("#y").text(pos.y.toFixed(2)); 829 830 if (item) { 831 if (previousPoint != item.dataIndex) { 832 previousPoint = item.dataIndex; 833 834 $("#tooltip").remove(); 835 /* console.log(item); */ 836 var x = item.datapoint[0].toFixed(2); 837 var y = item.datapoint[1].toFixed(2); 838 var z = item.series.xaxis.ticks[item.dataIndex].label; 839 '; 840 if ($this->showpointvalue > 0) $this->stringtoshow .= ' 841 showTooltip_' . $tag . '(item.pageX, item.pageY, item.series.label + "<br>" + z + " => " + y); 842 '; 843 $this->stringtoshow .= ' 844 } 845 } 846 else { 847 $("#tooltip_' . $tag . '").remove(); 848 previousPoint = null; 849 } 850 }); 851 '; 852 853 $this->stringtoshow .= 'var stack = null, steps = false;' . "\n"; 854 855 $this->stringtoshow .= 'function plotWithOptions_' . $tag . '() {' . "\n"; 856 $this->stringtoshow .= '$.plot($("#placeholder_' . $tag . '"), [ ' . "\n"; 857 $i = $firstlot; 858 while ($i < $nblot) { 859 if ($i > $firstlot) $this->stringtoshow .= ', ' . "\n"; 860 $color = sprintf("%02x%02x%02x", $this->datacolor[$i][0], $this->datacolor[$i][1], $this->datacolor[$i][2]); 861 $this->stringtoshow .= '{ '; 862 if (!isset($this->type[$i]) || $this->type[$i] == 'bars') { 863 if ($nblot == 3) { 864 if ($i == $firstlot) $align = 'right'; 865 elseif ($i == $firstlot + 1) $align = 'center'; 866 else $align = 'left'; 867 $this->stringtoshow .= 'bars: { lineWidth: 1, show: true, align: "' . $align . '", barWidth: 0.45 }, '; 868 } else $this->stringtoshow .= 'bars: { lineWidth: 1, show: true, align: "' . ($i == $firstlot ? 'center' : 'left') . '", barWidth: 0.5 }, '; 869 } 870 if (isset($this->type[$i]) && ($this->type[$i] == 'lines' || $this->type[$i] == 'linesnopoint')) $this->stringtoshow .= 'lines: { show: true, fill: false }, points: { show: ' . ($this->type[$i] == 'linesnopoint' ? 'false' : 'true') . ' }, '; 871 $this->stringtoshow .= 'color: "#' . $color . '", label: "' . (isset($this->Legend[$i]) ? dol_escape_js($this->Legend[$i]) : '') . '", data: d' . $i . ' }'; 872 $i++; 873 } 874 // shadowSize: 0 -> Drawing is faster without shadows 875 $this->stringtoshow .= "\n" . ' ], { series: { shadowSize: 0, stack: stack, lines: { fill: false, steps: steps }, bars: { barWidth: 0.6, fillColor: { colors: [{opacity: 0.9 }, {opacity: 0.85}] }} }' . "\n"; 876 877 // Xaxis 878 $this->stringtoshow .= ', xaxis: { ticks: [' . "\n"; 879 $x = 0; 880 foreach ($this->data as $key => $valarray) { 881 if ($x > 0) $this->stringtoshow .= ', ' . "\n"; 882 $this->stringtoshow .= ' [' . $x . ', "' . $valarray[0] . '"]'; 883 $x++; 884 } 885 $this->stringtoshow .= '] }' . "\n"; 886 887 // Yaxis 888 $this->stringtoshow .= ', yaxis: { min: ' . $this->MinValue . ', max: ' . ($this->MaxValue) . ' }' . "\n"; 889 890 // Background color 891 $color1 = sprintf("%02x%02x%02x", $this->bgcolorgrid[0], $this->bgcolorgrid[0], $this->bgcolorgrid[2]); 892 $color2 = sprintf("%02x%02x%02x", $this->bgcolorgrid[0], $this->bgcolorgrid[1], $this->bgcolorgrid[2]); 893 $this->stringtoshow .= ', grid: { hoverable: true, backgroundColor: { colors: ["#' . $color1 . '", "#' . $color2 . '"] }, borderWidth: 1, borderColor: \'#e6e6e6\', tickColor : \'#e6e6e6\' }' . "\n"; 894 $this->stringtoshow .= '});' . "\n"; 895 $this->stringtoshow .= '}' . "\n"; 896 } 897 898 $this->stringtoshow .= 'plotWithOptions_' . $tag . '();' . "\n"; 899 $this->stringtoshow .= '});' . "\n"; 900 $this->stringtoshow .= '</script>' . "\n"; 901 } 902 903 904 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 905 /** 906 * Build a graph using Chart library. Input when calling this method should be: 907 * $this->data = array(array(0=>'labelxA',1=>yA), array('labelxB',yB)); 908 * $this->data = array(array(0=>'labelxA',1=>yA1,...,n=>yAn), array('labelxB',yB1,...yBn)); // or when there is n series to show for each x 909 * $this->data = array(array('label'=>'labelxA','data'=>yA), array('labelxB',yB)); // Syntax deprecated 910 * $this->legend= array("Val1",...,"Valn"); // list of n series name 911 * $this->type = array('bars',...'lines', 'linesnopoint'); or array('pie') or array('polar') or array('piesemicircle'); 912 * $this->mode = 'depth' ??? 913 * $this->bgcolorgrid 914 * $this->datacolor 915 * $this->shownodatagraph 916 * 917 * @param string $file Image file name to use to save onto disk (also used as javascript unique id) 918 * @param string $fileurl Url path to show image if saved onto disk. Never used here. 919 * @return void 920 */ 921 private function draw_chart($file, $fileurl) 922 { 923 // phpcs:enable 924 global $conf, $langs; 925 926 dol_syslog(get_class($this) . "::draw_chart this->type=" . join(',', $this->type) . " this->MaxValue=" . $this->MaxValue); 927 928 if (empty($this->width) && empty($this->height)) { 929 print 'Error width or height not set'; 930 return; 931 } 932 933 $showlegend = $this->showlegend; 934 935 $legends = array(); 936 $nblot = 0; 937 if (is_array($this->data)) { 938 foreach ($this->data as $valarray) // Loop on each x 939 { 940 $nblot = max($nblot, count($valarray) - 1); // -1 to remove legend 941 } 942 } 943 //var_dump($nblot); 944 if ($nblot < 0) dol_syslog('Bad value for property ->data. Must be set by mydolgraph->SetData before calling mydolgrapgh->draw', LOG_WARNING); 945 $firstlot = 0; 946 // Works with line but not with bars 947 //if ($nblot > 2) $firstlot = ($nblot - 2); // We limit nblot to 2 because jflot can't manage more than 2 bars on same x 948 949 $serie = array(); 950 $arrayofgroupslegend = array(); 951 //var_dump($this->data); 952 953 $i = $firstlot; 954 while ($i < $nblot) // Loop on each serie 955 { 956 $values = array(); // Array with horizontal y values (specific values of a serie) for each abscisse x (with x=0,1,2,...) 957 $serie[$i] = ""; 958 959 // Fill array $values 960 $x = 0; 961 foreach ($this->data as $valarray) // Loop on each x 962 { 963 $legends[$x] = (array_key_exists('label', $valarray) ? $valarray['label'] : $valarray[0]); 964 $array_of_ykeys = array_keys($valarray); 965 $alabelexists = 1; 966 $tmpykey = explode('_', ($array_of_ykeys[$i + ($alabelexists ? 1 : 0)]), 3); 967 if (!empty($tmpykey[2]) || $tmpykey[2] == '0') { // This is a 'Group by' array 968 $tmpvalue = (array_key_exists('y_' . $tmpykey[1] . '_' . $tmpykey[2], $valarray) ? $valarray['y_' . $tmpykey[1] . '_' . $tmpykey[2]] : $valarray[$i + 1]); 969 $values[$x] = (is_numeric($tmpvalue) ? $tmpvalue : null); 970 $arrayofgroupslegend[$i] = array( 971 'stacknum' => $tmpykey[1], 972 'legend' => $this->Legend[$tmpykey[1]], 973 'legendwithgroup' => $this->Legend[$tmpykey[1]] . ' - ' . $tmpykey[2] 974 ); 975 } else { 976 $tmpvalue = (array_key_exists('y_' . $i, $valarray) ? $valarray['y_' . $i] : $valarray[$i + 1]); 977 //var_dump($i.'_'.$x.'_'.$tmpvalue); 978 $values[$x] = (is_numeric($tmpvalue) ? $tmpvalue : null); 979 } 980 $x++; 981 } 982 //var_dump($values); 983 $j = 0; 984 foreach ($values as $x => $y) { 985 if (isset($y)) { 986 $serie[$i] .= ($j > 0 ? ", " : "") . $y; 987 } else { 988 $serie[$i] .= ($j > 0 ? ", " : "") . 'null'; 989 } 990 $j++; 991 } 992 993 $values = null; // Free mem 994 $i++; 995 } 996 //var_dump($serie); 997 //var_dump($arrayofgroupslegend); 998 999 $tag = dol_escape_htmltag(dol_string_unaccent(dol_string_nospecial(basename($file), '_', array('-', '.')))); 1000 1001 $this->stringtoshow = '<!-- Build using chart -->' . "\n"; 1002 if (!empty($this->title)) $this->stringtoshow .= '<div class="center dolgraphtitle' . (empty($this->cssprefix) ? '' : ' dolgraphtitle' . $this->cssprefix) . '">' . $this->title . '</div>'; 1003 if (!empty($this->shownographyet)) { 1004 $this->stringtoshow .= '<div style="width:' . $this->width . (strpos($this->width, '%') > 0 ? '' : 'px') . '; height:' . $this->height . 'px;" class="nographyet"></div>'; 1005 $this->stringtoshow .= '<div class="nographyettext margintoponly">' . $langs->trans("NotEnoughDataYet") . '...</div>'; 1006 return; 1007 } 1008 1009 // Start the div that will contains all the graph 1010 $dolxaxisvertical = ''; 1011 if (count($this->data) > 20) $dolxaxisvertical = 'dol-xaxis-vertical'; 1012 // No height for the pie grah 1013 $cssfordiv = 'dolgraphchart'; 1014 if (isset($this->type[$firstlot])) $cssfordiv .= ' dolgraphchar' . $this->type[$firstlot]; 1015 $this->stringtoshow .= '<div id="placeholder_' . $tag . '" style="min-height: ' . $this->height . (strpos($this->height, '%') > 0 ? '' : 'px') . '; width:' . $this->width . (strpos($this->width, '%') > 0 ? '' : 'px') . ';" class="' . $cssfordiv . ' dolgraph' . (empty($dolxaxisvertical) ? '' : ' ' . $dolxaxisvertical) . (empty($this->cssprefix) ? '' : ' dolgraph' . $this->cssprefix) . ' center"><canvas id="canvas_' . $tag . '"></canvas></div>' . "\n"; 1016 1017 $this->stringtoshow .= '<script id="' . $tag . '">' . "\n"; 1018 $i = $firstlot; 1019 if ($nblot < 0) { 1020 $this->stringtoshow .= '<!-- No series of data -->'; 1021 } else { 1022 while ($i < $nblot) { 1023 //$this->stringtoshow .= '<!-- Series '.$i.' -->'."\n"; 1024 //$this->stringtoshow .= $serie[$i]."\n"; 1025 $i++; 1026 } 1027 } 1028 $this->stringtoshow .= "\n"; 1029 1030 // Special case for Graph of type 'pie', 'piesemicircle', or 'polar' 1031 if (isset($this->type[$firstlot]) && (in_array($this->type[$firstlot], array('pie', 'polar', 'piesemicircle')))) { 1032 $type = $this->type[$firstlot]; // pie or polar 1033 $this->stringtoshow .= 'var options = {' . "\n"; 1034 $legendMaxLines = 0; // Does not work 1035 if (empty($showlegend)) { 1036 $this->stringtoshow .= 'legend: { display: false }, '; 1037 } else { 1038 $this->stringtoshow .= 'legend: { position: \'' . ($showlegend == 2 ? 'right' : 'top') . '\''; 1039 if (!empty($legendMaxLines)) { 1040 $this->stringtoshow .= ', maxLines: ' . $legendMaxLines . ''; 1041 } 1042 $this->stringtoshow .= ' }, ' . "\n"; 1043 } 1044 1045 if ($this->type[$firstlot] == 'piesemicircle') { 1046 $this->stringtoshow .= 'circumference: Math.PI,' . "\n"; 1047 $this->stringtoshow .= 'rotation: -Math.PI,' . "\n"; 1048 } 1049 $this->stringtoshow .= 'elements: { arc: {' . "\n"; 1050 // Color of earch arc 1051 $this->stringtoshow .= 'backgroundColor: ['; 1052 $i = 0; 1053 $foundnegativecolor = 0; 1054 foreach ($legends as $val) // Loop on each serie 1055 { 1056 if ($i > 0) $this->stringtoshow .= ', ' . "\n"; 1057 if (is_array($this->datacolor[$i])) $color = 'rgb(' . $this->datacolor[$i][0] . ', ' . $this->datacolor[$i][1] . ', ' . $this->datacolor[$i][2] . ')'; // If datacolor is array(R, G, B) 1058 else { 1059 $tmp = str_replace('#', '', $this->datacolor[$i]); 1060 if (strpos($tmp, '-') !== false) { 1061 $foundnegativecolor++; 1062 $color = '#FFFFFF'; // If $val is '-123' 1063 } else $color = "#" . $tmp; // If $val is '123' or '#123' 1064 } 1065 $this->stringtoshow .= "'" . $color . "'"; 1066 $i++; 1067 } 1068 $this->stringtoshow .= '], ' . "\n"; 1069 // Border color 1070 if ($foundnegativecolor) { 1071 $this->stringtoshow .= 'borderColor: ['; 1072 $i = 0; 1073 foreach ($legends as $val) // Loop on each serie 1074 { 1075 if ($i > 0) $this->stringtoshow .= ', ' . "\n"; 1076 if (is_array($this->datacolor[$i])) $color = 'null'; // If datacolor is array(R, G, B) 1077 else { 1078 $tmp = str_replace('#', '', $this->datacolor[$i]); 1079 if (strpos($tmp, '-') !== false) $color = '#' . str_replace('-', '', $tmp); // If $val is '-123' 1080 else $color = 'null'; // If $val is '123' or '#123' 1081 } 1082 $this->stringtoshow .= ($color == 'null' ? "'rgba(0,0,0,0.2)'" : "'" . $color . "'"); 1083 $i++; 1084 } 1085 $this->stringtoshow .= ']'; 1086 } 1087 $this->stringtoshow .= '} } };' . "\n"; 1088 1089 $this->stringtoshow .= ' 1090 var ctx = document.getElementById("canvas_' . $tag . '").getContext("2d"); 1091 var chart = new Chart(ctx, { 1092 // The type of chart we want to create 1093 type: \'' . (in_array($type, array('pie', 'piesemicircle')) ? 'doughnut' : 'polarArea') . '\', 1094 // Configuration options go here 1095 options: options, 1096 data: { 1097 labels: ['; 1098 1099 $i = 0; 1100 foreach ($legends as $val) // Loop on each serie 1101 { 1102 if ($i > 0) $this->stringtoshow .= ', '; 1103 $this->stringtoshow .= "'" . dol_escape_js(dol_trunc($val, 32)) . "'"; 1104 $i++; 1105 } 1106 1107 $this->stringtoshow .= '], 1108 datasets: ['; 1109 $i = 0; 1110 $i = 0; 1111 while ($i < $nblot) // Loop on each serie 1112 { 1113 $color = 'rgb(' . $this->datacolor[$i][0] . ', ' . $this->datacolor[$i][1] . ', ' . $this->datacolor[$i][2] . ')'; 1114 //$color = (!empty($data['seriescolor']) ? json_encode($data['seriescolor']) : json_encode($datacolor)); 1115 1116 if ($i > 0) $this->stringtoshow .= ', ' . "\n"; 1117 $this->stringtoshow .= '{' . "\n"; 1118 //$this->stringtoshow .= 'borderColor: \''.$color.'\', '; 1119 //$this->stringtoshow .= 'backgroundColor: \''.$color.'\', '; 1120 $this->stringtoshow .= ' data: [' . $serie[$i] . ']'; 1121 $this->stringtoshow .= '}' . "\n"; 1122 $i++; 1123 } 1124 $this->stringtoshow .= ']' . "\n"; 1125 $this->stringtoshow .= '}' . "\n"; 1126 $this->stringtoshow .= '});' . "\n"; 1127 } 1128 // Other cases, graph of type 'bars', 'lines', 'linesnopoint' 1129 else { 1130 $type = 'bar'; 1131 1132 $isfunnel = false; 1133 if ($file == 'idgraphleadfunnel') $isfunnel = true; 1134 1135 if (!isset($this->type[$firstlot]) || $this->type[$firstlot] == 'bars') $type = 'bar'; 1136 if (isset($this->type[$firstlot]) && $this->type[$firstlot] == 'horizontalbars') $type = 'horizontalBar'; 1137 if (isset($this->type[$firstlot]) && ($this->type[$firstlot] == 'lines' || $this->type[$firstlot] == 'linesnopoint')) $type = 'line'; 1138 1139 $this->stringtoshow .= 'var options = { maintainAspectRatio: false, aspectRatio: 2.5, '; 1140 if (empty($showlegend)) { 1141 $this->stringtoshow .= 'legend: { display: false }, '; 1142 } 1143 $this->stringtoshow .= 'scales: { xAxes: [{ '; 1144 if ($isfunnel) { // FIXME Remove isfunnel by introducing a method hideXValues() on dolgraph 1145 $this->stringtoshow .= ' ticks: { display: false }, display: true,'; 1146 } 1147 //$this->stringtoshow .= 'type: \'time\', '; // Need Moment.js 1148 $this->stringtoshow .= 'distribution: \'linear\''; 1149 if ($type == 'bar' && count($arrayofgroupslegend) > 0) { 1150 $this->stringtoshow .= ', stacked: true'; 1151 } 1152 $this->stringtoshow .= ' }]'; 1153 $this->stringtoshow .= ', yAxes: [{ ticks: { beginAtZero: true }'; 1154 if ($type == 'bar' && count($arrayofgroupslegend) > 0) { 1155 $this->stringtoshow .= ', stacked: true'; 1156 } 1157 $this->stringtoshow .= ' }] }'; 1158 // Add a callback to change label to show only positive value 1159 if ($isfunnel) { 1160 $this->stringtoshow .= ', tooltips: { mode: \'nearest\', 1161 callbacks: { 1162 title: function(tooltipItem, data) { 1163 return data.datasets[tooltipItem[0].datasetIndex].label; 1164 }, 1165 label: function(tooltipItem, data) { 1166 return data.datasets[tooltipItem.datasetIndex].data[0][1]; 1167 } 1168 } 1169 },'; 1170 } 1171 $this->stringtoshow .= '};'; 1172 $this->stringtoshow .= ' 1173 var ctx = document.getElementById("canvas_' . $tag . '").getContext("2d"); 1174 var chart = new Chart(ctx, { 1175 // The type of chart we want to create 1176 type: \'' . $type . '\', 1177 // Configuration options go here 1178 options: options, 1179 data: { 1180 labels: ['; 1181 1182 $i = 0; 1183 if (!$isfunnel) { 1184 foreach ($legends as $val) // Loop on each serie 1185 { 1186 if ($i > 0) $this->stringtoshow .= ', '; 1187 $this->stringtoshow .= "'".dol_escape_js(dol_trunc($val, 32))."'"; 1188 $i++; 1189 } 1190 } 1191 1192 //var_dump($arrayofgroupslegend); 1193 1194 $this->stringtoshow .= '], 1195 datasets: ['; 1196 1197 global $theme_datacolor; 1198 //var_dump($arrayofgroupslegend); 1199 $i = 0; $iinstack = 0; 1200 $oldstacknum = -1; 1201 while ($i < $nblot) // Loop on each serie 1202 { 1203 $foundnegativecolor = 0; 1204 $usecolorvariantforgroupby = 0; 1205 // We used a 'group by' and we have too many colors so we generated color variants per 1206 if (is_array($arrayofgroupslegend[$i]) && count($arrayofgroupslegend[$i]) > 0) { // If we used a group by. 1207 $nbofcolorneeds = count($arrayofgroupslegend); 1208 $nbofcolorsavailable = count($theme_datacolor); 1209 if ($nbofcolorneeds > $nbofcolorsavailable) { 1210 $usecolorvariantforgroupby = 1; 1211 } 1212 1213 $textoflegend = $arrayofgroupslegend[$i]['legendwithgroup']; 1214 } else { 1215 $textoflegend = $this->Legend[$i]; 1216 } 1217 1218 if ($usecolorvariantforgroupby) { 1219 $newcolor = $this->datacolor[$arrayofgroupslegend[$i]['stacknum']]; 1220 // If we change the stack 1221 if ($oldstacknum == -1 || $arrayofgroupslegend[$i]['stacknum'] != $oldstacknum) { 1222 $iinstack = 0; 1223 } 1224 1225 //var_dump($iinstack); 1226 if ($iinstack) { 1227 // Change color with offset of $$iinstack 1228 //var_dump($newcolor); 1229 if ($iinstack % 2) { // We increase agressiveness of reference color for color 2, 4, 6, ... 1230 $ratio = min(95, 10 + 10 * $iinstack); // step of 20 1231 $brightnessratio = min(90, 5 + 5 * $iinstack); // step of 10 1232 } else { // We decrease agressiveness of reference color for color 3, 5, 7, .. 1233 $ratio = max(-100, -15 * $iinstack + 10); // step of -20 1234 $brightnessratio = min(90, 10 * $iinstack); // step of 20 1235 } 1236 //var_dump('Color '.($iinstack+1).' : '.$ratio.' '.$brightnessratio); 1237 1238 $newcolor = array_values(colorHexToRgb(colorAgressiveness(colorArrayToHex($newcolor), $ratio, $brightnessratio), false, true)); 1239 } 1240 $oldstacknum = $arrayofgroupslegend[$i]['stacknum']; 1241 1242 $color = 'rgb(' . $newcolor[0] . ', ' . $newcolor[1] . ', ' . $newcolor[2] . ', 0.9)'; 1243 $bordercolor = 'rgb(' . $newcolor[0] . ', ' . $newcolor[1] . ', ' . $newcolor[2] . ')'; 1244 } else { // We do not use a 'group by' 1245 if ($isfunnel) { 1246 $bordercolor == 'null'; 1247 if (is_array($this->datacolor[$i])) { 1248 $color = 'rgb(' . $this->datacolor[$i][0] . ', ' . $this->datacolor[$i][1] . ', ' . $this->datacolor[$i][2] . ', 0.9)'; // If datacolor is array(R, G, B) 1249 } else { 1250 // TODO FIXME This logic must be in the caller that set $this->datacolor 1251 $tmp = str_replace('#', '', $this->datacolor[$i]); 1252 if (strpos($tmp, '-') !== false) { 1253 $foundnegativecolor++; 1254 $color = '#FFFFFF'; // If $val is '-123' 1255 } else { 1256 $color = "#" . $tmp; // If $val is '123' or '#123' 1257 $bordercolor = $color; 1258 } 1259 if ($foundnegativecolor) { 1260 if (is_array($this->datacolor[$i])) $color = 'null'; // If datacolor is array(R, G, B) 1261 else { 1262 $tmp = str_replace('#', '', $this->datacolor[$i]); 1263 if (strpos($tmp, '-') !== false) $bordercolor = '#' . str_replace('-', '', $tmp); // If $val is '-123' 1264 else $bordercolor = 'null'; // If $val is '123' or '#123' 1265 } 1266 } 1267 } 1268 $bordercolor == 'null' ? "'rgba(0,0,0,0.2)'" : "'" . $bordercolor . "'"; 1269 } else { 1270 $color = 'rgb('.$this->datacolor[$i][0].', '.$this->datacolor[$i][1].', '.$this->datacolor[$i][2].', 0.9)'; 1271 $bordercolor = $color; 1272 //$color = (!empty($data['seriescolor']) ? json_encode($data['seriescolor']) : json_encode($datacolor)); 1273 } 1274 } 1275 1276 if ($i > 0) $this->stringtoshow .= ', '; 1277 $this->stringtoshow .= "\n"; 1278 $this->stringtoshow .= '{'; 1279 $this->stringtoshow .= 'dolibarrinfo: \'y_' . $i . '\', '; 1280 $this->stringtoshow .= 'label: \'' . dol_escape_js(dol_string_nohtmltag($textoflegend)) . '\', '; 1281 $this->stringtoshow .= 'pointStyle: \'' . ($this->type[$i] == 'linesnopoint' ? 'line' : 'circle') . '\', '; 1282 $this->stringtoshow .= 'fill: ' . ($type == 'bar' ? 'true' : 'false') . ', '; 1283 if ($isfunnel) { 1284 $this->stringtoshow .= 'borderWidth: \'2\', '; 1285 } elseif ($type == 'bar' || $type == 'horizontalBar') { 1286 $this->stringtoshow .= 'borderWidth: \'1\', '; 1287 } 1288 $this->stringtoshow .= 'borderColor: \'' . $bordercolor . '\', '; 1289 $this->stringtoshow .= 'backgroundColor: \'' . $color . '\', '; 1290 if ($arrayofgroupslegend[$i]) $this->stringtoshow .= 'stack: \'' . $arrayofgroupslegend[$i]['stacknum'] . '\', '; 1291 $this->stringtoshow .='data: ['; 1292 if ($isfunnel) { 1293 $this->stringtoshow .= '['.-$serie[$i].','.$serie[$i].']'; 1294 } else { 1295 $this->stringtoshow .= $serie[$i]; 1296 } 1297 $this->stringtoshow .=']'; 1298 $this->stringtoshow .= '}' . "\n"; 1299 1300 $i++; 1301 $iinstack++; 1302 } 1303 $this->stringtoshow .= ']' . "\n"; 1304 $this->stringtoshow .= '}' . "\n"; 1305 $this->stringtoshow .= '});' . "\n"; 1306 } 1307 1308 $this->stringtoshow .= '</script>' . "\n"; 1309 } 1310 1311 1312 /** 1313 * Output HTML string to total value 1314 * 1315 * @return string HTML string to total value 1316 */ 1317 public function total() 1318 { 1319 $value = 0; 1320 foreach ($this->data as $valarray) // Loop on each x 1321 { 1322 $value += $valarray[1]; 1323 } 1324 return $value; 1325 } 1326 1327 /** 1328 * Output HTML string to show graph 1329 * 1330 * @param int|string $shownographyet Show graph to say there is not enough data or the message in $shownographyet if it is a string. 1331 * @return string HTML string to show graph 1332 */ 1333 public function show($shownographyet = 0) 1334 { 1335 global $langs; 1336 1337 if ($shownographyet) { 1338 $s = '<div class="nographyet" style="width:' . (preg_match('/%/', $this->width) ? $this->width : $this->width . 'px') . '; height:' . (preg_match('/%/', $this->height) ? $this->height : $this->height . 'px') . ';"></div>'; 1339 $s .= '<div class="nographyettext margintoponly">'; 1340 if (is_numeric($shownographyet)) { 1341 $s .= $langs->trans("NotEnoughDataYet") . '...'; 1342 } else { 1343 $s .= $shownographyet . '...'; 1344 } 1345 $s .= '</div>'; 1346 return $s; 1347 } 1348 1349 return $this->stringtoshow; 1350 } 1351 1352 1353 /** 1354 * getDefaultGraphSizeForStats 1355 * 1356 * @param string $direction 'width' or 'height' 1357 * @param string $defaultsize Value we want as default size 1358 * @return int Value of width or height to use by default 1359 */ 1360 public static function getDefaultGraphSizeForStats($direction, $defaultsize = '') 1361 { 1362 global $conf; 1363 1364 if ($direction == 'width') 1365 { 1366 if (empty($conf->dol_optimize_smallscreen)) return ($defaultsize ? $defaultsize : '500'); 1367 else return (empty($_SESSION['dol_screen_width']) ? '280' : ($_SESSION['dol_screen_width'] - 40)); 1368 } 1369 if ($direction == 'height') 1370 { 1371 return (empty($conf->dol_optimize_smallscreen) ? ($defaultsize ? $defaultsize : '200') : '160'); 1372 } 1373 return 0; 1374 } 1375} 1376