dolgraph.class.php 48 KB

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