2
0
forked from Wavyzz/dolibarr

NEW Move engine to build charts from jflot to chart.js

This commit is contained in:
Laurent Destailleur
2020-03-04 22:29:21 +01:00
parent 0766097658
commit d0839350db
20 changed files with 13879 additions and 231 deletions

View File

@@ -92,31 +92,13 @@ class DolGraph
/**
* Constructor
*
* @param string $library 'jflot' (default) or 'artichow' (no more supported)
* @param string $library 'auto' (default)
*/
public function __construct($library = 'jflot')
public function __construct($library = 'auto')
{
global $conf;
global $theme_bordercolor, $theme_datacolor, $theme_bgcolor;
// To use old feature
if ($library == 'artichow')
{
$this->_library = 'artichow';
// Test if module GD present
$modules_list = get_loaded_extensions();
$isgdinstalled = 0;
foreach ($modules_list as $module)
{
if ($module == 'gd') $isgdinstalled = 1;
}
if (!$isgdinstalled)
{
$this->error = "Error: PHP GD module is not available. It is required to build graphics.";
}
}
$this->bordercolor = array(235, 235, 224);
$this->datacolor = array(array(120, 130, 150), array(160, 160, 180), array(190, 190, 220));
$this->bgcolor = array(235, 235, 224);
@@ -130,6 +112,11 @@ class DolGraph
if (isset($theme_bgcolor)) $this->bgcolor = $theme_bgcolor;
}
//print 'bgcolor: '.join(',',$this->bgcolor).'<br>';
$this->_library = $library;
if ($this->_library == 'auto') {
$this->_library = (empty($conf->global->MAIN_JS_GRAPH) ? 'jflot': $conf->global->MAIN_JS_GRAPH);
}
}
@@ -287,7 +274,7 @@ class DolGraph
/**
* Set type
*
* @param array $type Array with type for each serie. Example: array('pie'), array('lines',...,'bars')
* @param array $type Array with type for each serie. Example: array('pie', ...), array('lines', 'linesnopoint', 'bars', 'pie')
* @return void
*/
public function SetType($type)
@@ -686,188 +673,6 @@ class DolGraph
call_user_func_array(array($this, $call), array($file, $fileurl));
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Build a graph onto disk using Artichow library and return img string to it
*
* @param string $file Image file name to use if we save onto disk
* @param string $fileurl Url path to show image if saved onto disk
* @return void
*/
private function draw_artichow($file, $fileurl)
{
// phpcs:enable
global $artichow_defaultfont;
dol_syslog(get_class($this)."::draw_artichow this->type=".join(',', $this->type));
if (!defined('SHADOW_RIGHT_TOP')) define('SHADOW_RIGHT_TOP', 3);
if (!defined('LEGEND_BACKGROUND')) define('LEGEND_BACKGROUND', 2);
if (!defined('LEGEND_LINE')) define('LEGEND_LINE', 1);
// Create graph
$classname = '';
if (!isset($this->type[0]) || $this->type[0] == 'bars') $classname = 'BarPlot'; // Only one type (first one) is supported by artichow
elseif ($this->type[0] == 'lines' || $this->type[0] == 'linesnopoint') $classname = 'LinePlot';
else $classname = 'TypeUnknown';
include_once ARTICHOW_PATH.$classname.'.class.php';
// Definition de couleurs
$bgcolor = new Color($this->bgcolor[0], $this->bgcolor[1], $this->bgcolor[2]);
$bgcolorgrid = new Color($this->bgcolorgrid[0], $this->bgcolorgrid[1], $this->bgcolorgrid[2]);
$colortrans = new Color(0, 0, 0, 100);
$colorsemitrans = new Color(255, 255, 255, 60);
$colorgradient = new LinearGradient(new Color(235, 235, 235), new Color(255, 255, 255), 0);
$colorwhite = new Color(255, 255, 255);
// Graph
$graph = new Graph($this->width, $this->height);
$graph->border->hide();
$graph->setAntiAliasing(true);
if (isset($this->title))
{
$graph->title->set($this->title);
//print $artichow_defaultfont;exit;
$graph->title->setFont(new $artichow_defaultfont(10));
}
if (is_array($this->bgcolor)) $graph->setBackgroundColor($bgcolor);
else $graph->setBackgroundGradient($colorgradient);
$group = new PlotGroup;
//$group->setSpace(5, 5, 0, 0);
$paddleft = 50;
$paddright = 10;
$strl = dol_strlen(max(abs($this->MaxValue), abs($this->MinValue)));
if ($strl > 6) $paddleft += ($strl * 4);
$group->setPadding($paddleft, $paddright); // Width on left and right for Y axis values
$group->legend->setSpace(0);
$group->legend->setPadding(2, 2, 2, 2);
$group->legend->setPosition(null, 0.1);
$group->legend->setBackgroundColor($colorsemitrans);
if (is_array($this->bgcolorgrid)) $group->grid->setBackgroundColor($bgcolorgrid);
else $group->grid->setBackgroundColor($colortrans);
if ($this->hideXGrid) $group->grid->hideVertical(true);
if ($this->hideYGrid) $group->grid->hideHorizontal(true);
// On boucle sur chaque lot de donnees
$legends = array();
$i = 0;
$nblot = count($this->data[0]) - 1;
while ($i < $nblot)
{
$x = 0;
$values = array();
foreach ($this->data as $key => $valarray)
{
$legends[$x] = $valarray[0];
$values[$x] = $valarray[$i + 1];
$x++;
}
// We fix unknown values to null
$newvalues = array();
foreach ($values as $val)
{
$newvalues[] = (is_numeric($val) ? $val : null);
}
if ($this->type[0] == 'bars')
{
//print "Lot de donnees $i<br>";
//print_r($values);
//print '<br>';
$color = new Color($this->datacolor[$i][0], $this->datacolor[$i][1], $this->datacolor[$i][2], 20);
$colorbis = new Color(min($this->datacolor[$i][0] + 50, 255), min($this->datacolor[$i][1] + 50, 255), min($this->datacolor[$i][2] + 50, 255), 50);
$colorgrey = new Color(100, 100, 100);
$colorborder = new Color($this->datacolor[$i][0], $this->datacolor[$i][1], $this->datacolor[$i][2]);
if ($this->mode == 'side') $plot = new BarPlot($newvalues, $i + 1, $nblot);
if ($this->mode == 'depth') $plot = new BarPlot($newvalues, 1, 1, ($nblot - $i - 1) * 5);
$plot->barBorder->setColor($colorgrey);
//$plot->setBarColor($color);
$plot->setBarGradient(new LinearGradient($colorbis, $color, 90));
if ($this->mode == 'side') $plot->setBarPadding(0.1, 0.1);
if ($this->mode == 'depth') $plot->setBarPadding(0.1, 0.4);
if ($this->mode == 'side') $plot->setBarSpace(5);
if ($this->mode == 'depth') $plot->setBarSpace(2);
$plot->barShadow->setSize($this->SetShading);
$plot->barShadow->setPosition(SHADOW_RIGHT_TOP);
$plot->barShadow->setColor(new Color(160, 160, 160, 50));
$plot->barShadow->smooth(true);
//$plot->setSize(1, 0.96);
//$plot->setCenter(0.5, 0.52);
// Le mode automatique est plus efficace
$plot->SetYMax($this->MaxValue);
$plot->SetYMin($this->MinValue);
}
if ($this->type[0] == 'lines' || $this->type[0] == 'linesnopoint')
{
$color = new Color($this->datacolor[$i][0], $this->datacolor[$i][1], $this->datacolor[$i][2], 20);
$colorbis = new Color(min($this->datacolor[$i][0] + 20, 255), min($this->datacolor[$i][1] + 20, 255), min($this->datacolor[$i][2] + 20, 255), 60);
$colorter = new Color(min($this->datacolor[$i][0] + 50, 255), min($this->datacolor[$i][1] + 50, 255), min($this->datacolor[$i][2] + 50, 255), 90);
$plot = new LinePlot($newvalues);
//$plot->setSize(1, 0.96);
//$plot->setCenter(0.5, 0.52);
$plot->setColor($color);
$plot->setThickness(1);
// Set line background gradient
$plot->setFillGradient(new LinearGradient($colorter, $colorbis, 90));
$plot->xAxis->setLabelText($legends);
// Le mode automatique est plus efficace
$plot->SetYMax($this->MaxValue);
$plot->SetYMin($this->MinValue);
//$plot->setYAxis(0);
//$plot->hideLine(true);
}
//$plot->reduce(80); // Evite temps d'affichage trop long et nombre de ticks absisce satures
$group->legend->setTextFont(new $artichow_defaultfont(10)); // This is to force Artichow to use awFileFontDriver to
// solve a bug in Artichow with UTF8
if (count($this->Legend))
{
if ($this->type[0] == 'bars') $group->legend->add($plot, $this->Legend[$i], LEGEND_BACKGROUND);
if ($this->type[0] == 'lines' || $this->type[0] == 'linesnopoint') $group->legend->add($plot, $this->Legend[$i], LEGEND_LINE);
}
$group->add($plot);
$i++;
}
$group->axis->bottom->setLabelText($legends);
$group->axis->bottom->label->setFont(new $artichow_defaultfont(7));
//print $group->axis->bottom->getLabelNumber();
if ($this->labelInterval > 0) $group->axis->bottom->setLabelInterval($this->labelInterval);
$graph->add($group);
// Generate file
$graph->draw($file);
$this->stringtoshow = '<!-- Build using '.$this->_library.' --><img src="'.$fileurl.'" title="'.dol_escape_htmltag($this->title ? $this->title : $this->YLabel).'" alt="'.dol_escape_htmltag($this->title ? $this->title : $this->YLabel).'">';
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Build a graph using JFlot library. Input when calling this method should be:
@@ -875,7 +680,7 @@ class DolGraph
* $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
* $this->data = array(array('label'=>'labelxA','data'=>yA), array('labelxB',yB)); // Syntax deprecated
* $this->legend= array("Val1",...,"Valn"); // list of n series name
* $this->type = array('bars',...'lines'); or array('pie')
* $this->type = array('bars',...'lines','linesnopoint'); or array('pie') or array('polar')
* $this->mode = 'depth' ???
* $this->bgcolorgrid
* $this->datacolor
@@ -888,7 +693,7 @@ class DolGraph
private function draw_jflot($file, $fileurl)
{
// phpcs:enable
global $langs;
global $conf, $langs;
dol_syslog(get_class($this)."::draw_jflot this->type=".join(',', $this->type)." this->MaxValue=".$this->MaxValue);
@@ -943,7 +748,7 @@ class DolGraph
}
$tag = dol_escape_htmltag(dol_string_unaccent(dol_string_nospecial(basename($file), '_', array('-', '.'))));
$this->stringtoshow = '<!-- Build using '.$this->_library.' -->'."\n";
$this->stringtoshow = '<!-- Build using jflot -->'."\n";
if (!empty($this->title)) $this->stringtoshow .= '<div class="center dolgraphtitle'.(empty($this->cssprefix) ? '' : ' dolgraphtitle'.$this->cssprefix).'">'.$this->title.'</div>';
if (!empty($this->shownographyet))
{
@@ -962,20 +767,21 @@ class DolGraph
$i = $firstlot;
if ($nblot < 0)
{
$this->stringtoshow .= '<!-- No series of data -->';
$this->stringtoshow .= '<!-- No series of data -->'."\n";
}
else
{
while ($i < $nblot)
{
$this->stringtoshow .= $serie[$i];
$this->stringtoshow .= '<!-- Serie '.$i.' -->'."\n";
$this->stringtoshow .= $serie[$i]."\n";
$i++;
}
}
$this->stringtoshow .= "\n";
// Special case for Graph of type 'pie'
if (isset($this->type[$firstlot]) && $this->type[$firstlot] == 'pie')
if (isset($this->type[$firstlot]) && ($this->type[$firstlot] == 'pie' || $this->type[$firstlot] == 'polar'))
{
$datacolor = array();
foreach ($this->datacolor as $val) $datacolor[] = "#".sprintf("%02x%02x%02x", $val[0], $val[1], $val[2]);
@@ -1131,6 +937,238 @@ class DolGraph
$this->stringtoshow .= '</script>'."\n";
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Build a graph using Chart library. Input when calling this method should be:
* $this->data = array(array(0=>'labelxA',1=>yA), array('labelxB',yB));
* $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
* $this->data = array(array('label'=>'labelxA','data'=>yA), array('labelxB',yB)); // Syntax deprecated
* $this->legend= array("Val1",...,"Valn"); // list of n series name
* $this->type = array('bars',...'lines', 'linesnopoint'); or array('pie') or array('polar');
* $this->mode = 'depth' ???
* $this->bgcolorgrid
* $this->datacolor
* $this->shownodatagraph
*
* @param string $file Image file name to use to save onto disk (also used as javascript unique id)
* @param string $fileurl Url path to show image if saved onto disk. Never used here.
* @return void
*/
private function draw_chart($file, $fileurl)
{
// phpcs:enable
global $conf, $langs;
dol_syslog(get_class($this)."::draw_chart this->type=".join(',', $this->type)." this->MaxValue=".$this->MaxValue);
if (empty($this->width) && empty($this->height))
{
print 'Error width or height not set';
return;
}
$legends = array();
$nblot = 0;
if (is_array($this->data) && is_array($this->data[0])) {
$nblot = count($this->data[0]) - 1; // -1 to remove legend
}
if ($nblot < 0) dol_syslog('Bad value for property ->data. Must be set by mydolgraph->SetData before calling mydolgrapgh->draw', LOG_WARNING);
$firstlot = 0;
// Works with line but not with bars
//if ($nblot > 2) $firstlot = ($nblot - 2); // We limit nblot to 2 because jflot can't manage more than 2 bars on same x
$i = $firstlot;
$serie = array();
while ($i < $nblot) // Loop on each serie
{
$values = array(); // Array with horizontal y values (specific values of a serie) for each abscisse x
$serie[$i] = "";
// Fill array $values
$x = 0;
foreach ($this->data as $valarray) // Loop on each x
{
$legends[$x] = $valarray[0];
$values[$x] = (is_numeric($valarray[$i + 1]) ? $valarray[$i + 1] : null);
$x++;
}
// TODO Avoid push by adding generated long array...
if (isset($this->type[$firstlot]) && $this->type[$firstlot] == 'pie')
{
$j = 0;
foreach ($values as $x => $y) {
//if (isset($y)) $serie[$i] .= 'd'.$i.'.push({"label":"'.dol_escape_js($legends[$x]).'", "data":'.$y.'});'."\n";
if (isset($y)) {
//$serie[$i][] = $y;
$serie[$i] .= ($j > 0 ? ", " : "").$y;
$j++;
}
}
}
else
{
$j = 0;
foreach ($values as $x => $y) {
if (isset($y)) {
$serie[$i] .= ($j > 0 ? ", " : "").$y;
$j++;
}
}
}
unset($values);
$i++;
}
$tag = dol_escape_htmltag(dol_string_unaccent(dol_string_nospecial(basename($file), '_', array('-', '.'))));
$this->stringtoshow = '<!-- Build using chart -->'."\n";
if (!empty($this->title)) $this->stringtoshow .= '<div class="center dolgraphtitle'.(empty($this->cssprefix) ? '' : ' dolgraphtitle'.$this->cssprefix).'">'.$this->title.'</div>';
if (!empty($this->shownographyet))
{
$this->stringtoshow .= '<div style="width:'.$this->width.(strpos($this->width, '%') > 0 ? '': 'px').'; height:'.$this->height.'px;" class="nographyet"></div>';
$this->stringtoshow .= '<div class="nographyettext">'.$langs->trans("NotEnoughDataYet").'</div>';
return;
}
// Start the div that will contains all the graph
$dolxaxisvertical='';
if (count($this->data) > 20) $dolxaxisvertical='dol-xaxis-vertical';
// No height for the pie grah
$this->stringtoshow .= '<div id="placeholder_'.$tag.'" style="width:'.$this->width.(strpos($this->width, '%') > 0 ? '': 'px').';" class="dolgraphchart dolgraph'.(empty($dolxaxisvertical)?'':' '.$dolxaxisvertical).(empty($this->cssprefix) ? '' : ' dolgraph'.$this->cssprefix).' center"><canvas id="canvas_'.$tag.'"></canvas></div>'."\n";
$this->stringtoshow .= '<script id="'.$tag.'">'."\n";
$i = $firstlot;
if ($nblot < 0)
{
$this->stringtoshow .= '<!-- No series of data -->';
}
else
{
while ($i < $nblot)
{
//$this->stringtoshow .= '<!-- Series '.$i.' -->'."\n";
//$this->stringtoshow .= $serie[$i]."\n";
$i++;
}
}
$this->stringtoshow .= "\n";
// Special case for Graph of type 'pie'
if (isset($this->type[$firstlot]) && ($this->type[$firstlot] == 'pie' || $this->type[$firstlot] == 'polar'))
{
$type = $this->type[$firstlot]; // pie or polar
$this->stringtoshow .= 'var options = {
elements: {
arc: {
backgroundColor: [';
$i = 0;
foreach($legends as $val) // Loop on each serie
{
if ($i > 0) $this->stringtoshow .= ', '."\n";
$color = 'rgb('.$this->datacolor[$i][0].', '.$this->datacolor[$i][1].', '.$this->datacolor[$i][2].')';
$this->stringtoshow .= "'".$color."'";
$i++;
}
$this->stringtoshow .= ']
}
}
};'."\n";
$this->stringtoshow .= '
var ctx = document.getElementById("canvas_'.$tag.'").getContext("2d");
var chart = new Chart(ctx, {
// The type of chart we want to create
type: \''.($type == 'pie' ? 'doughnut' : 'polarArea').'\',
// Configuration options go here
options: options,
data: {
labels: [';
$i = 0;
foreach($legends as $val) // Loop on each serie
{
if ($i > 0) $this->stringtoshow .= ', '."\n";
$this->stringtoshow .= "'".$val."'";
$i++;
}
$this->stringtoshow .= '],
datasets: [';
$i = 0;
$i = 0;
while ($i < $nblot) // Loop on each serie
{
$color = 'rgb('.$this->datacolor[$i][0].', '.$this->datacolor[$i][1].', '.$this->datacolor[$i][2].')';
//$color = (!empty($data['seriescolor']) ? json_encode($data['seriescolor']) : json_encode($datacolor));
if ($i > 0) $this->stringtoshow .= ', '."\n";
$this->stringtoshow .= '{'."\n";
//$this->stringtoshow .= 'borderColor: \''.$color.'\', ';
//$this->stringtoshow .= 'backgroundColor: \''.$color.'\', ';
$this->stringtoshow .= ' data: ['.$serie[$i].']';
$this->stringtoshow .= '}'."\n";
$i++;
}
$this->stringtoshow .= ']'."\n";
$this->stringtoshow .= '}'."\n";
$this->stringtoshow .= '});'."\n";
}
// Other cases, graph of type 'bars', 'lines'
else
{
$type = 'bars';
if (!isset($this->type[$firstlot]) || $this->type[$firstlot] == 'bars') $type = 'bar';
if (isset($this->type[$firstlot]) && ($this->type[$firstlot] == 'lines' || $this->type[$i] == 'linesnopoint')) $type = 'line';
$this->stringtoshow .= '
var ctx = document.getElementById("canvas_'.$tag.'").getContext("2d");
var chart = new Chart(ctx, {
// The type of chart we want to create
type: \''.$type.'\',
// Configuration options go here
options: {},
data: {
labels: [';
$i = 0;
foreach($legends as $val) // Loop on each serie
{
if ($i > 0) $this->stringtoshow .= ', '."\n";
$this->stringtoshow .= "'".$val."'";
$i++;
}
$this->stringtoshow .= '],
datasets: [';
$i = 0;
while ($i < $nblot) // Loop on each serie
{
$color = 'rgb('.$this->datacolor[$i][0].', '.$this->datacolor[$i][1].', '.$this->datacolor[$i][2].')';
//$color = (!empty($data['seriescolor']) ? json_encode($data['seriescolor']) : json_encode($datacolor));
if ($i > 0) $this->stringtoshow .= ', '."\n";
$this->stringtoshow .= '{'."\n";
$this->stringtoshow .= 'label: "'.$this->Legend[$i].'",';
$this->stringtoshow .= 'pointStyle: \''.($this->type[$i] == 'linesnopoint' ? 'line' : 'circle').'\', ';
$this->stringtoshow .= 'fill: '.($type == 'bar' ? 'true' : 'false').', ';
$this->stringtoshow .= 'borderColor: \''.$color.'\', ';
$this->stringtoshow .= 'backgroundColor: \''.$color.'\', ';
$this->stringtoshow .= ' data: ['.$serie[$i].']';
$this->stringtoshow .= '}'."\n";
$i++;
}
$this->stringtoshow .= ']'."\n";
$this->stringtoshow .= '}'."\n";
$this->stringtoshow .= '});'."\n";
}
$this->stringtoshow .= '</script>'."\n";
}
/**
* Output HTML string to total value
*

View File

@@ -0,0 +1,19 @@
version: "2"
plugins:
duplication:
enabled: true
config:
languages:
- javascript
fixme:
enabled: true
exclude_patterns:
- "dist/"
- "docs/"
- "samples/"
- "scripts/"
- "test/"
- "*.js"
- "*.json"
- "*.md"
- ".*"

View File

@@ -0,0 +1,18 @@
# https://editorconfig.org
root = true
[*]
indent_style = tab
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[gulpfile.js]
indent_style = space
indent_size = 2
[*.yml]
indent_style = space
indent_size = 2

View File

@@ -0,0 +1 @@
**/*{.,-}min.js

View File

@@ -0,0 +1,7 @@
extends: chartjs
env:
browser: true
node: true
plugins: ['html']

17
htdocs/includes/chart/.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
/_book
/coverage
/custom
/dist
/docs/index.md
/gh-pages
/jsdoc
/node_modules
.DS_Store
.idea
.project
.settings
.vscode
bower.json
*.log
*.swp
*.stackdump

View File

@@ -0,0 +1,19 @@
{
"indent-style": "tabs",
"line-end-style": false,
"attr-quote-style": "double",
"spec-char-escape": false,
"attr-bans": [
"align",
"background",
"bgcolor",
"border",
"frameborder",
"longdesc",
"marginwidth",
"marginheight",
"scrolling"
],
"tag-bans": [ "b", "i" ],
"id-class-style": false
}

View File

@@ -0,0 +1,55 @@
language: node_js
node_js:
- lts/*
before_install:
- "export CHROME_BIN=/usr/bin/google-chrome"
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
script:
- gulp build
- gulp test --coverage
- gulp docs
- gulp package
- gulp bower
- cat ./coverage/lcov.info | ./node_modules/.bin/coveralls || true
sudo: required
dist: trusty
addons:
chrome: stable
firefox: latest
# IMPORTANT: scripts require GITHUB_AUTH_TOKEN and GITHUB_AUTH_EMAIL environment variables
# IMPORTANT: scripts has to be set executables in the Git repository (error 127)
# https://github.com/travis-ci/travis-ci/issues/5538#issuecomment-225025939
deploy:
- provider: script
script: ./scripts/deploy.sh
skip_cleanup: true
on:
all_branches: true
- provider: script
script: ./scripts/release.sh
skip_cleanup: true
on:
branch: release
- provider: releases
api_key: $GITHUB_AUTH_TOKEN
skip_cleanup: true
file_glob: true
file:
- ./dist/*.css
- ./dist/*.js
- ./dist/*.zip
on:
tags: true
- provider: npm
email: $NPM_AUTH_EMAIL
api_key: $NPM_AUTH_TOKEN
skip_cleanup: true
on:
tags: true

View File

@@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2018 Chart.js Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,36 @@
# Maintaining
## Release Process
Chart.js relies on [Travis CI](https://travis-ci.org/) to automate the library [releases](https://github.com/chartjs/Chart.js/releases).
### Releasing a New Version
1. draft release notes on [GitHub](https://github.com/chartjs/Chart.js/releases/new) for the upcoming tag
1. update `master` `package.json` version using [semver](https://semver.org/) semantic
1. merge `master` into the `release` branch
1. follow the build process on [Travis CI](https://travis-ci.org/chartjs/Chart.js)
> **Note:** if `master` is merged in `release` with a `package.json` version that already exists, the tag
creation fails and the release process is aborted.
### Automated Tasks
Merging into the `release` branch kicks off the automated release process:
* build of the `dist/*.js` files
* `bower.json` is generated from `package.json`
* `dist/*.js` and `bower.json` are added to a detached branch
* a tag is created from the `package.json` version
* tag (with dist files) is pushed to GitHub
Creation of this tag triggers a new build:
* `Chart.js.zip` package is generated, containing dist files and examples
* `dist/*.js` and `Chart.js.zip` are attached to the GitHub release (downloads)
* a new npm package is published on [npmjs](https://www.npmjs.com/package/chart.js)
Finally, [cdnjs](https://cdnjs.com/libraries/Chart.js) is automatically updated from the npm release.
### Further Reading
* [Travis GitHub releases](https://github.com/chartjs/Chart.js/pull/2555)
* [Bower support and dist/* files](https://github.com/chartjs/Chart.js/issues/3033)
* [cdnjs npm auto update](https://github.com/cdnjs/cdnjs/pull/8401)

View File

@@ -0,0 +1,32 @@
<p align="center">
<img src="https://www.chartjs.org/media/logo-title.svg"><br/>
Simple yet flexible JavaScript charting for designers & developers
</p>
<p align="center">
<a href="https://www.chartjs.org/docs/latest/getting-started/installation.html"><img src="https://img.shields.io/github/release/chartjs/Chart.js.svg?style=flat-square&maxAge=600" alt="Downloads"></a>
<a href="https://travis-ci.org/chartjs/Chart.js"><img src="https://img.shields.io/travis/chartjs/Chart.js.svg?style=flat-square&maxAge=600" alt="Builds"></a>
<a href="https://coveralls.io/github/chartjs/Chart.js?branch=master"><img src="https://img.shields.io/coveralls/chartjs/Chart.js.svg?style=flat-square&maxAge=600" alt="Coverage"></a>
<a href="https://github.com/chartjs/awesome"><img src="https://awesome.re/badge-flat2.svg" alt="Awesome"></a>
<a href="https://chartjs-slack.herokuapp.com/"><img src="https://img.shields.io/badge/slack-chartjs-blue.svg?style=flat-square&maxAge=3600" alt="Slack"></a>
</p>
## Documentation
- [Introduction](https://www.chartjs.org/docs/latest/)
- [Getting Started](https://www.chartjs.org/docs/latest/getting-started/)
- [General](https://www.chartjs.org/docs/latest/general/)
- [Configuration](https://www.chartjs.org/docs/latest/configuration/)
- [Charts](https://www.chartjs.org/docs/latest/charts/)
- [Axes](https://www.chartjs.org/docs/latest/axes/)
- [Developers](https://www.chartjs.org/docs/latest/developers/)
- [Popular Extensions](https://github.com/chartjs/awesome)
- [Samples](https://www.chartjs.org/samples/)
## Contributing
Instructions on building and testing Chart.js can be found in [the documentation](https://github.com/chartjs/Chart.js/blob/master/docs/developers/contributing.md#building-and-testing). Before submitting an issue or a pull request, please take a moment to look over the [contributing guidelines](https://github.com/chartjs/Chart.js/blob/master/docs/developers/contributing.md) first. For support, please post questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/chartjs) with the `chartjs` tag.
## License
Chart.js is available under the [MIT license](https://opensource.org/licenses/MIT).

View File

@@ -0,0 +1,32 @@
{
"root": "./docs",
"title": "Chart.js documentation",
"author": "chartjs",
"gitbook": "3.2.2",
"plugins": [
"-lunr",
"-search",
"search-plus",
"anchorjs",
"chartjs",
"ga",
"redirect"
],
"pluginsConfig": {
"anchorjs": {
"icon": "#",
"placement": "left",
"visible": "always"
},
"ga": {
"token": "UA-28909194-3",
"configuration": "auto"
},
"theme-default": {
"showLevel": false,
"styles": {
"website": "style.css"
}
}
}
}

View File

@@ -0,0 +1,26 @@
{
"name": "nnnick/chartjs",
"type": "library",
"description": "Simple HTML5 charts using the canvas element.",
"keywords": [
"chart",
"js"
],
"homepage": "https://www.chartjs.org/",
"license": "MIT",
"authors": [
{
"name": "NICK DOWNIE",
"email": "hello@nickdownie.com"
}
],
"require": {
"php": ">=5.3.3"
},
"minimum-stability": "stable",
"extra": {
"branch-alias": {
"release/2.0": "v2.0-dev"
}
}
}

View File

@@ -0,0 +1,172 @@
var gulp = require('gulp');
var eslint = require('gulp-eslint');
var file = require('gulp-file');
var replace = require('gulp-replace');
var size = require('gulp-size');
var streamify = require('gulp-streamify');
var terser = require('gulp-terser');
var zip = require('gulp-zip');
var exec = require('child_process').exec;
var karma = require('karma');
var merge = require('merge-stream');
var yargs = require('yargs');
var path = require('path');
var htmllint = require('gulp-htmllint');
var pkg = require('./package.json');
var argv = yargs
.option('verbose', {default: false})
.argv;
var srcDir = './src/';
var outDir = './dist/';
gulp.task('bower', bowerTask);
gulp.task('build', buildTask);
gulp.task('package', packageTask);
gulp.task('lint-html', lintHtmlTask);
gulp.task('lint-js', lintJsTask);
gulp.task('lint', gulp.parallel('lint-html', 'lint-js'));
gulp.task('docs', docsTask);
gulp.task('unittest', unittestTask);
gulp.task('test', gulp.parallel('lint', 'unittest'));
gulp.task('library-size', librarySizeTask);
gulp.task('module-sizes', moduleSizesTask);
gulp.task('size', gulp.parallel('library-size', 'module-sizes'));
gulp.task('default', gulp.parallel('build'));
function run(bin, args, done) {
return new Promise(function(resolve, reject) {
var exe = '"' + process.execPath + '"';
var src = require.resolve(bin);
var cmd = [exe, src].concat(args || []).join(' ');
var ps = exec(cmd);
ps.stdout.pipe(process.stdout);
ps.stderr.pipe(process.stderr);
ps.on('close', function(error) {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
/**
* Generates the bower.json manifest file which will be pushed along release tags.
* Specs: https://github.com/bower/spec/blob/master/json.md
*/
function bowerTask() {
var json = JSON.stringify({
name: pkg.name,
description: pkg.description,
homepage: pkg.homepage,
license: pkg.license,
version: pkg.version,
main: outDir + 'Chart.js',
ignore: [
'.github',
'.codeclimate.yml',
'.gitignore',
'.npmignore',
'.travis.yml',
'scripts'
]
}, null, 2);
return file('bower.json', json, { src: true })
.pipe(gulp.dest('./'));
}
function buildTask() {
return run('rollup/dist/bin/rollup', ['-c', argv.watch ? '--watch' : '']);
}
function packageTask() {
return merge(
// gather "regular" files landing in the package root
gulp.src([outDir + '*.js', outDir + '*.css', 'LICENSE.md']),
// since we moved the dist files one folder up (package root), we need to rewrite
// samples src="../dist/ to src="../ and then copy them in the /samples directory.
gulp.src('./samples/**/*', { base: '.' })
.pipe(streamify(replace(/src="((?:\.\.\/)+)dist\//g, 'src="$1')))
)
// finally, create the zip archive
.pipe(zip('Chart.js.zip'))
.pipe(gulp.dest(outDir));
}
function lintJsTask() {
var files = [
'samples/**/*.html',
'samples/**/*.js',
'src/**/*.js',
'test/**/*.js'
];
// NOTE(SB) codeclimate has 'complexity' and 'max-statements' eslint rules way too strict
// compare to what the current codebase can support, and since it's not straightforward
// to fix, let's turn them as warnings and rewrite code later progressively.
var options = {
rules: {
'complexity': [1, 10],
'max-statements': [1, 30]
}
};
return gulp.src(files)
.pipe(eslint(options))
.pipe(eslint.format())
.pipe(eslint.failAfterError());
}
function lintHtmlTask() {
return gulp.src('samples/**/*.html')
.pipe(htmllint({
failOnError: true,
}));
}
function docsTask() {
var bin = 'gitbook-cli/bin/gitbook.js';
var cmd = argv.watch ? 'serve' : 'build';
return run(bin, ['install', './'])
.then(() => run(bin, [cmd, './', './dist/docs']));
}
function unittestTask(done) {
new karma.Server({
configFile: path.join(__dirname, 'karma.conf.js'),
singleRun: !argv.watch,
args: {
coverage: !!argv.coverage,
inputs: (argv.inputs || 'test/specs/**/*.js').split(';'),
watch: argv.watch
}
},
// https://github.com/karma-runner/gulp-karma/issues/18
function(error) {
error = error ? new Error('Karma returned with the error code: ' + error) : undefined;
done(error);
}).start();
}
function librarySizeTask() {
return gulp.src('dist/Chart.bundle.min.js')
.pipe(size({
gzip: true
}));
}
function moduleSizesTask() {
return gulp.src(srcDir + '**/*.js')
.pipe(terser())
.pipe(size({
showFiles: true,
gzip: true
}));
}

View File

@@ -0,0 +1,108 @@
/* eslint-env es6 */
const commonjs = require('rollup-plugin-commonjs');
const istanbul = require('rollup-plugin-istanbul');
const resolve = require('rollup-plugin-node-resolve');
const builds = require('./rollup.config');
module.exports = function(karma) {
const args = karma.args || {};
// Use the same rollup config as our dist files: when debugging (--watch),
// we will prefer the unminified build which is easier to browse and works
// better with source mapping. In other cases, pick the minified build to
// make sure that the minification process (terser) doesn't break anything.
const regex = args.watch ? /Chart\.js$/ : /Chart\.min\.js$/;
const build = builds.filter(v => v.output.file.match(regex))[0];
if (args.watch) {
build.output.sourcemap = 'inline';
}
karma.set({
frameworks: ['jasmine'],
reporters: ['progress', 'kjhtml'],
browsers: ['chrome', 'firefox'],
logLevel: karma.LOG_WARN,
// Explicitly disable hardware acceleration to make image
// diff more stable when ran on Travis and dev machine.
// https://github.com/chartjs/Chart.js/pull/5629
customLaunchers: {
chrome: {
base: 'Chrome',
flags: [
'--disable-accelerated-2d-canvas'
]
},
firefox: {
base: 'Firefox',
prefs: {
'layers.acceleration.disabled': true
}
}
},
files: [
{pattern: 'test/fixtures/**/*.js', included: false},
{pattern: 'test/fixtures/**/*.json', included: false},
{pattern: 'test/fixtures/**/*.png', included: false},
'node_modules/moment/min/moment.min.js',
'test/index.js',
'src/index.js'
].concat(args.inputs),
preprocessors: {
'test/index.js': ['rollup'],
'src/index.js': ['sources']
},
rollupPreprocessor: {
plugins: [
resolve(),
commonjs()
],
output: {
name: 'test',
format: 'umd'
}
},
customPreprocessors: {
sources: {
base: 'rollup',
options: build
}
},
// These settings deal with browser disconnects. We had seen test flakiness from Firefox
// [Firefox 56.0.0 (Linux 0.0.0)]: Disconnected (1 times), because no message in 10000 ms.
// https://github.com/jasmine/jasmine/issues/1327#issuecomment-332939551
browserDisconnectTolerance: 3
});
// https://swizec.com/blog/how-to-run-javascript-tests-in-chrome-on-travis/swizec/6647
if (process.env.TRAVIS) {
karma.customLaunchers.chrome.flags.push('--no-sandbox');
}
if (args.coverage) {
karma.reporters.push('coverage');
karma.coverageReporter = {
dir: 'coverage/',
reporters: [
{type: 'html', subdir: 'html'},
{type: 'lcovonly', subdir: '.'}
]
};
[
karma.rollupPreprocessor,
karma.customPreprocessors.sources.options
].forEach(v => {
(v.plugins || (v.plugins = [])).unshift(
istanbul({
include: 'src/**/*.js'
}));
});
}
};

12752
htdocs/includes/chart/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,69 @@
{
"name": "chart.js",
"homepage": "https://www.chartjs.org",
"description": "Simple HTML5 charts using the canvas element.",
"version": "2.9.3",
"license": "MIT",
"jsdelivr": "dist/Chart.min.js",
"unpkg": "dist/Chart.min.js",
"main": "dist/Chart.js",
"keywords": [
"canvas",
"charts",
"data",
"graphs",
"html5",
"responsive"
],
"repository": {
"type": "git",
"url": "https://github.com/chartjs/Chart.js.git"
},
"bugs": {
"url": "https://github.com/chartjs/Chart.js/issues"
},
"files": [
"bower.json",
"composer.json",
"dist/*.css",
"dist/*.js"
],
"devDependencies": {
"clean-css": "^4.2.1",
"coveralls": "^3.0.0",
"eslint": "^5.9.0",
"eslint-config-chartjs": "^0.1.0",
"eslint-plugin-html": "^5.0.0",
"gitbook-cli": "^2.3.2",
"gulp": "^4.0.0",
"gulp-eslint": "^5.0.0",
"gulp-file": "^0.4.0",
"gulp-htmllint": "^0.0.16",
"gulp-replace": "^1.0.0",
"gulp-size": "^3.0.0",
"gulp-streamify": "^1.0.2",
"gulp-terser": "^1.1.6",
"gulp-zip": "^4.2.0",
"jasmine": "^3.3.0",
"jasmine-core": "^3.3.0",
"karma": "^4.0.0",
"karma-chrome-launcher": "^2.2.0",
"karma-coverage": "^1.1.1",
"karma-firefox-launcher": "^1.0.1",
"karma-jasmine": "^2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"karma-rollup-preprocessor": "^7.0.0",
"merge-stream": "^1.0.1",
"pixelmatch": "^4.0.2",
"rollup": "^1.0.0",
"rollup-plugin-commonjs": "^10.0.0",
"rollup-plugin-istanbul": "^2.0.1",
"rollup-plugin-node-resolve": "^5.0.0",
"rollup-plugin-terser": "^5.0.0",
"yargs": "^12.0.5"
},
"dependencies": {
"chartjs-color": "^2.1.0",
"moment": "^2.10.2"
}
}

View File

@@ -0,0 +1,119 @@
/* eslint-env es6 */
const commonjs = require('rollup-plugin-commonjs');
const resolve = require('rollup-plugin-node-resolve');
const terser = require('rollup-plugin-terser').terser;
const optional = require('./rollup.plugins').optional;
const stylesheet = require('./rollup.plugins').stylesheet;
const pkg = require('./package.json');
const input = 'src/index.js';
const banner = `/*!
* Chart.js v${pkg.version}
* ${pkg.homepage}
* (c) ${new Date().getFullYear()} Chart.js Contributors
* Released under the MIT License
*/`;
module.exports = [
// UMD builds (excluding moment)
// dist/Chart.min.js
// dist/Chart.js
{
input: input,
plugins: [
resolve(),
commonjs(),
stylesheet({
extract: true
}),
optional({
include: ['moment']
})
],
output: {
name: 'Chart',
file: 'dist/Chart.js',
banner: banner,
format: 'umd',
indent: false,
globals: {
moment: 'moment'
}
},
external: [
'moment'
]
},
{
input: input,
plugins: [
resolve(),
commonjs(),
optional({
include: ['moment']
}),
stylesheet({
extract: true,
minify: true
}),
terser({
output: {
preamble: banner
}
})
],
output: {
name: 'Chart',
file: 'dist/Chart.min.js',
format: 'umd',
indent: false,
globals: {
moment: 'moment'
}
},
external: [
'moment'
]
},
// UMD builds (including moment)
// dist/Chart.bundle.min.js
// dist/Chart.bundle.js
{
input: input,
plugins: [
resolve(),
commonjs(),
stylesheet()
],
output: {
name: 'Chart',
file: 'dist/Chart.bundle.js',
banner: banner,
format: 'umd',
indent: false
}
},
{
input: input,
plugins: [
resolve(),
commonjs(),
stylesheet({
minify: true
}),
terser({
output: {
preamble: banner
}
})
],
output: {
name: 'Chart',
file: 'dist/Chart.bundle.min.js',
format: 'umd',
indent: false
}
}
];

View File

@@ -0,0 +1,110 @@
/* eslint-env es6 */
const cleancss = require('clean-css');
const path = require('path');
const UMD_WRAPPER_RE = /(\(function \(global, factory\) \{)((?:\s.*?)*)(\}\(this,)/;
const CJS_FACTORY_RE = /(module.exports = )(factory\(.*?\))( :)/;
const AMD_FACTORY_RE = /(define\()(.*?, factory)(\) :)/;
function optional(config = {}) {
return {
name: 'optional',
renderChunk(code, chunk, options) {
if (options.format !== 'umd') {
this.error('only UMD format is currently supported');
}
const wrapper = UMD_WRAPPER_RE.exec(code);
const include = config.include;
if (!wrapper) {
this.error('failed to parse the UMD wrapper');
}
let content = wrapper[2];
let factory = (CJS_FACTORY_RE.exec(content) || [])[2];
let updated = false;
for (let lib of chunk.imports) {
if (!include || include.indexOf(lib) !== -1) {
const regex = new RegExp(`require\\('${lib}'\\)`);
if (!regex.test(factory)) {
this.error(`failed to parse the CJS require for ${lib}`);
}
// We need to write inline try / catch with explicit require
// in order to enable statical extraction of dependencies:
// try { return require('moment'); } catch(e) {}
const loader = `function() { try { return require('${lib}'); } catch(e) { } }()`;
factory = factory.replace(regex, loader);
updated = true;
}
}
if (!updated) {
return;
}
// Replace the CJS factory by our updated one.
content = content.replace(CJS_FACTORY_RE, `$1${factory}$3`);
// Replace the AMD factory by our updated one: we need to use the
// following AMD form in order to be able to try/catch require:
// define(['require'], function(require) { ... require(...); ... })
// https://github.com/amdjs/amdjs-api/wiki/AMD#using-require-and-exports
content = content.replace(AMD_FACTORY_RE, `$1['require'], function(require) { return ${factory}; }$3`);
return code.replace(UMD_WRAPPER_RE, `$1${content}$3`);
}
};
}
// https://github.com/chartjs/Chart.js/issues/5208
function stylesheet(config = {}) {
const minifier = new cleancss();
const styles = [];
return {
name: 'stylesheet',
transform(code, id) {
// Note that 'id' can be mapped to a CJS proxy import, in which case
// 'id' will start with 'commonjs-proxy', so let's first check if we
// are importing an existing css file (i.e. startsWith()).
if (!id.startsWith(path.resolve('.')) || !id.endsWith('.css')) {
return;
}
if (config.minify) {
code = minifier.minify(code).styles;
}
// keep track of all imported stylesheets (already minified)
styles.push(code);
return {
code: 'export default ' + JSON.stringify(code)
};
},
generateBundle(opts, bundle) {
if (!config.extract) {
return;
}
const entry = Object.keys(bundle).find(v => bundle[v].isEntry);
const name = (entry || '').replace(/\.js$/i, '.css');
if (!name) {
this.error('failed to guess the output file name');
}
this.emitFile({
type: 'asset',
source: styles.filter(v => !!v).join(''),
fileName: name
});
}
};
}
module.exports = {
optional,
stylesheet
};

View File

@@ -1382,6 +1382,8 @@ function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arr
print '<script src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jnotify/jquery.jnotify.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
}
// Flot
if (empty($conf->global->MAIN_JS_GRAPH) || $conf->global->MAIN_JS_GRAPH == 'jflot')
{
if (empty($conf->global->MAIN_DISABLE_JQUERY_FLOT) && !defined('DISABLE_JQUERY_FLOT'))
{
if (constant('JS_JQUERY_FLOT'))
@@ -1408,6 +1410,13 @@ function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arr
*/
}
}
}
// Chart
if ($conf->global->MAIN_JS_GRAPH == 'chart')
{
print '<script src="'.DOL_URL_ROOT.'/includes/chart/dist/Chart.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
}
// jQuery jeditable
if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !defined('DISABLE_JQUERY_JEDITABLE'))
{