Replacing OpenFlashCharts by d3js and c3js: Farewell Flash ! (still an alpha version !)

SVN:trunk[4001]
This commit is contained in:
Denis Flaven
2016-04-18 14:56:02 +00:00
parent e27d61a525
commit 7abb048b7c
10 changed files with 17101 additions and 303 deletions

View File

@@ -596,7 +596,7 @@ abstract class DashletGroupBy extends Dashlet
switch($sStyle)
{
case 'bars':
$sType = 'open_flash_chart';
$sType = 'chart';
$aExtraParams = array(
'chart_type' => 'bars',
'chart_title' => $sTitle,
@@ -607,7 +607,7 @@ abstract class DashletGroupBy extends Dashlet
break;
case 'pie':
$sType = 'open_flash_chart';
$sType = 'chart';
$aExtraParams = array(
'chart_type' => 'pie',
'chart_title' => $sTitle,

View File

@@ -872,33 +872,42 @@ EOF
}
break;
case 'open_flash_chart':
case 'chart':
static $iChartCounter = 0;
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (!empty($sContext))
{
$sContext = '&'.$sContext;
}
$iChartCounter++;
$sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie';
$sTitle = isset($aExtraParams['chart_title']) ? $aExtraParams['chart_title'] : '';
$sTitle = isset($aExtraParams['chart_title']) ? '<h1 style="text-align:center">'.htmlentities(Dict::S($aExtraParams['chart_title']), ENT_QUOTES, 'UTF-8').'</h1>' : '';
$sHtml = "$sTitle<div style=\"height:200px;width:100%\" id=\"my_chart_$sId{$iChartCounter}\"><div style=\"height:200px;line-height:200px;vertical-align:center;text-align:center;width:100%\"><img src=\"../images/indicator.gif\"></div></div>\n";
$sGroupBy = isset($aExtraParams['group_by']) ? $aExtraParams['group_by'] : '';
$sGroupByExpr = isset($aExtraParams['group_by_expr']) ? '&params[group_by_expr]='.$aExtraParams['group_by_expr'] : '';
$sFilter = $this->m_oFilter->serialize();
$sHtml .= "<div id=\"my_chart_$sId{$iChartCounter}\">If the chart does not display, <a href=\"http://get.adobe.com/flash/\" target=\"_blank\">install Flash</a></div>\n";
$oPage->add_script("function ofc_resize(left, width, top, height) { /* do nothing special */ }");
if (isset($aExtraParams['group_by_label']))
{
$sUrl = urlencode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=open_flash_chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[chart_title]=$sTitle&params[currentId]=$sId&id=$sId&filter=".urlencode($sFilter));
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[chart_title]=$sTitle&params[currentId]=$sId{$iChartCounter}&id=$sId{$iChartCounter}&filter=".urlencode($sFilter));
}
else
{
$sUrl = urlencode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=open_flash_chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[chart_type]=$sChartType&params[chart_title]=$sTitle&params[currentId]=$sId&id=$sId&filter=".urlencode($sFilter));
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[chart_type]=$sChartType&params[chart_title]=$sTitle&params[currentId]=$sId{$iChartCounter}&id=$sId{$iChartCounter}&filter=".urlencode($sFilter));
}
$sType = ($sChartType == 'pie') ? 'pie' : 'bar';
$oPage->add_ready_script(
<<<EOF
$.post($sUrl, {}, function(data) {
$('body').append(data);
});
EOF
);
break;
case 'chart_ajax':
$sHtml = '';
$sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie';
$sId = utils::ReadParam('id', '');
$oPage->add_ready_script("swfobject.embedSWF(\"../images/open-flash-chart.swf\", \"my_chart_$sId{$iChartCounter}\", \"100%\", \"300\",\"9.0.0\", \"expressInstall.swf\",
{\"data-file\":\"".$sUrl."\"}, {wmode: 'transparent'} );\n");
$iChartCounter++;
if (isset($aExtraParams['group_by']))
{
if (isset($aExtraParams['group_by_label']))
@@ -921,208 +930,122 @@ EOF
$aGroupBy = array();
$aLabels = array();
$aValues = array();
$iTotalCount = 0;
$aValues = array();
$aURLS = array();
foreach ($aRes as $iRow => $aRow)
{
$sValue = $aRow['grouped_by_1'];
$aValues[$iRow] = $sValue;
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
$aLabels[$iRow] = $sHtmlValue;
$aGroupBy[$iRow] = (int) $aRow['_itop_count_'];
$aLabels[$iRow] = strip_tags($sHtmlValue);
$aGroupBy[(int)$iRow] = (int) $aRow['_itop_count_'];
$iTotalCount += $aRow['_itop_count_'];
}
$aData = array();
$idx = 0;
$aURLs = array();
foreach($aGroupBy as $iRow => $iCount)
{
$aValues[] = array('label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'), 'label_html' => $sHtmlValue, 'value' => (int) $aRow['_itop_count_']);
// Build the search for this subset
$oSubsetSearch = $this->m_oFilter->DeepClone();
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($aValues[$iRow]));
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
$oSubsetSearch->AddConditionExpression($oCondition);
$aURLs[$idx] = $oSubsetSearch->serialize();
$idx++;
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".urlencode($oSubsetSearch->serialize());
}
$sURLList = '';
foreach($aURLs as $index => $sURL)
{
$sURLList .= "\taURLs[$index] = '".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html{$sContext}&filter=".urlencode($sURL)."';\n";
}
$oPage->add_script(
<<<EOF
function ofc_drill_down_{$sId}(index)
{
var aURLs = new Array();
{$sURLList}
window.location.href=aURLs[index];
}
EOF
);
$sJSURLs = json_encode($aURLs);
}
break;
case 'open_flash_chart_ajax':
require_once(APPROOT.'/pages/php-ofc-library/open-flash-chart.php');
$sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie';
$sId = utils::ReadParam('id', '');
$oChart = new open_flash_chart();
switch($sChartType)
{
case 'bars':
$oChartElement = new bar_glass();
if (isset($aExtraParams['group_by']))
$aNames = array();
foreach($aValues as $idx => $aValue)
{
if (isset($aExtraParams['group_by_label']))
{
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
$sGroupByLabel = $aExtraParams['group_by_label'];
}
else
{
// Backward compatibility: group_by is simply a field id
$sAlias = $this->m_oFilter->GetClassAlias();
$oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias);
$sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']);
}
$aGroupBy = array();
$aGroupBy['grouped_by_1'] = $oGroupByExp;
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
$aRes = CMDBSource::QueryToArray($sSql);
$aGroupBy = array();
$aLabels = array();
$iTotalCount = 0;
foreach ($aRes as $iRow => $aRow)
{
$sValue = $aRow['grouped_by_1'];
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
$aLabels[$iRow] = strip_tags($sHtmlValue);
$aGroupBy[$iRow] = (int) $aRow['_itop_count_'];
$iTotalCount += $aRow['_itop_count_'];
}
$aData = array();
$aChartLabels = array();
$maxValue = 0;
foreach($aGroupBy as $iRow => $iCount)
{
$oBarValue = new bar_value($iCount);
$oBarValue->on_click("ofc_drill_down_$sId");
$aData[] = $oBarValue;
if ($iCount > $maxValue) $maxValue = $iCount;
$aChartLabels[] = html_entity_decode($aLabels[$iRow], ENT_QUOTES, 'UTF-8');
}
$oYAxis = new y_axis();
$aMagicValues = array(1,2,5,10);
$iMultiplier = 1;
$index = 0;
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
while($maxValue > $iTop)
{
$index++;
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
if (($index % count($aMagicValues)) == 0)
{
$iMultiplier = $iMultiplier * 10;
}
}
//echo "oYAxis->set_range(0, $iTop, $iMultiplier);\n";
$oYAxis->set_range(0, $iTop, $iMultiplier);
$oChart->set_y_axis( $oYAxis );
$oChartElement->set_values( $aData );
$oXAxis = new x_axis();
$oXLabels = new x_axis_labels();
// set them vertical
$oXLabels->set_vertical();
// set the label text
$oXLabels->set_labels($aChartLabels);
// Add the X Axis Labels to the X Axis
$oXAxis->set_labels( $oXLabels );
$oChart->set_x_axis( $oXAxis );
$aNames[$idx] = $aValue['label_html'];
}
break;
$sJSNames = json_encode($aNames);
$sJson = json_encode($aValues);
$sJSCount = json_encode(Dict::S('UI:GroupBy:Count'));
$oPage->add_ready_script(
<<<EOF
var chart = c3.generate({
bindto: d3.select('#my_chart_$sId'),
data: {
json: $sJson,
keys: {
x: 'label',
value: ["value"]
},
onclick: function (d, element) {
var aURLs = $sJSURLs;
window.location.href = aURLs[d.index];
},
selection: {
enabled: true
},
type: 'bar'
},
axis: {
x: {
type: 'category' // this needed to load string x value
}
},
grid: {
y: {
show: true
}
},
legend: {
show: false,
},
tooltip: {
grouped: false,
format: {
title: function() { return '' },
name: function (name, ratio, id, index) {
var aNames = $sJSNames;
return aNames[index];
}
}
}
});
EOF
);
break;
case 'pie':
default:
$oChartElement = new pie();
$oChartElement->set_start_angle( 35 );
$oChartElement->set_animate( true );
$oChartElement->set_tooltip( '#label# - #val# (#percent#)' );
$oChartElement->set_colours( array('#FF8A00', '#909980', '#2C2B33', '#CCC08D', '#596664') );
if (isset($aExtraParams['group_by']))
$aColumns = array();
$aNames = array();
foreach($aValues as $idx => $aValue)
{
if (isset($aExtraParams['group_by_label']))
{
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
$sGroupByLabel = $aExtraParams['group_by_label'];
}
else
{
// Backward compatibility: group_by is simply a field id
$sAlias = $this->m_oFilter->GetClassAlias();
$oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias);
$sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']);
}
$aGroupBy = array();
$aGroupBy['grouped_by_1'] = $oGroupByExp;
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
$aRes = CMDBSource::QueryToArray($sSql);
$aGroupBy = array();
$aLabels = array();
$iTotalCount = 0;
foreach ($aRes as $iRow => $aRow)
{
$sValue = $aRow['grouped_by_1'];
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
$aLabels[$iRow] = strip_tags($sHtmlValue);
$aGroupBy[$iRow] = (int) $aRow['_itop_count_'];
$iTotalCount += $aRow['_itop_count_'];
}
$aData = array();
foreach($aGroupBy as $iRow => $iCount)
{
$sFlashLabel = html_entity_decode($aLabels[$iRow], ENT_QUOTES, 'UTF-8');
$PieValue = new pie_value($iCount, $sFlashLabel); //@@ BUG: not passed via ajax !!!
$PieValue->on_click("ofc_drill_down_$sId");
$aData[] = $PieValue;
}
$oChartElement->set_values( $aData );
$oChart->x_axis = null;
$aColumns[] = array('series_'.$idx, (int)$aValue['value']);
$aNames['series_'.$idx] = $aValue['label'];
}
}
if (isset($aExtraParams['chart_title']))
{
// The title has been given in an url, and urlencoded...
// and urlencode transforms utf-8 into something similar to ISO-8859-1
// Example: é (C3A9 becomes %E9)
// As a consequence, json_encode (called within open-flash-chart.php)
// was returning 'null' and the graph was not displayed at all
// To make sure that the graph is displayed AND to get a correct title
// (at least for european characters) let's transform back into utf-8 !
$sTitle = iconv("ISO-8859-1", "UTF-8//IGNORE", $aExtraParams['chart_title']);
// If the title is a dictionnary entry, fetch it
$sTitle = Dict::S($sTitle);
$oTitle = new title($sTitle);
$oChart->set_title( $oTitle );
$oTitle->set_style("{font-size: 16px; font-family: Tahoma; font-weight: bold; text-align: center;}");
$sJSColumns = json_encode($aColumns);
$sJSNames = json_encode($aNames);
$oPage->add_ready_script(
<<<EOF
var chart = c3.generate({
bindto: d3.select('#my_chart_$sId'),
data: {
columns: $sJSColumns,
type: 'pie',
names: $sJSNames,
onclick: function (d, element) {
var aURLs = $sJSURLs;
window.location.href= aURLs[d.index];
},
},
legend: {
show: true,
},
tooltip: {
format: {
value: function (value, ratio, id) { return value; }
}
}
});
EOF
);
break;
}
$oChart->set_bg_colour('#FFFFFF');
$oChart->add_element( $oChartElement );
$sHtml = $oChart->toPrettyString();
break;
default:

View File

@@ -66,6 +66,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$this->add_linked_stylesheet("../css/fg.menu.css");
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
$this->add_linked_stylesheet("../css/magnific-popup.css");
$this->add_linked_stylesheet("../css/c3.min.css");
$this->add_linked_script('../js/jquery.layout.min.js');
$this->add_linked_script('../js/jquery.ba-bbq.min.js');
@@ -82,10 +83,8 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$this->add_linked_script('../js/fg.menu.js');
$this->add_linked_script('../js/icon_select.js');
$this->add_linked_script('../js/raphael-min.js');
$this->add_linked_script('../js/g.raphael.js');
$this->add_linked_script('../js/g.pie.js');
$this->add_linked_script('../js/g.dot.js');
$this->add_linked_script('../js/charts.js');
$this->add_linked_script('../js/d3.min.js');
$this->add_linked_script('../js/c3.min.js');
$this->add_linked_script('../js/jquery.multiselect.js');
$this->add_linked_script('../js/ajaxfileupload.js');
$this->add_linked_script('../js/jquery.mousewheel.js');
@@ -455,6 +454,7 @@ EOF
EOF
);
$this->add_ready_script(InlineImage::FixImagesWidth());
$sUserPrefs = appUserPreferences::GetAsJSON();
$this->add_script(
<<<EOF
@@ -731,7 +731,7 @@ EOF
}
// special stylesheet for printing, hides the navigation gadgets
$sHtml .= "<link rel=\"stylesheet\" media=\"print\" type=\"text/css\" href=\"../css/print.css?itopversion=".ITOP_VERSION."\" />\n";
if ($this->GetOutputFormat() == 'html')
{
$sHtml .= $this->output_dict_entries(true); // before any script so that they can benefit from the translations

View File

@@ -29,4 +29,47 @@ body { margin:none; }
.printable-version legend {
padding-left: 8px;
background-image: none;
}
}
/**
* PRINT Stylesheet
*
* First 'neutralize' all the positioning/overflow CSS added by Layout
* Then change or add cosmetic styles (borders, padding) for printing
*
* MUST use "!important" for all size, position, margin & overflow rules,
* so these will 'override' styles applied to the elements by Layout
*/
html, body {
/* NEUTRALIZE 'layout container' styles */
overflow: visible !important;
width: auto !important;
height: auto !important;
position: static !important;
}
.ui-layout-pane ,
.ui-layout-resizer ,
.ui-layout-toggler {
/* NEUTRALIZE 'layout element' styles */
display: none !important; /* hide ALL by default */
position: relative !important;
top: auto !important;
bottom: auto !important;
left: auto !important;
right: auto !important;
width: auto !important;
height: auto !important;
overflow: visible !important;
}
/* SHOW ONLY the panes you want */
.ui-layout-pane-center {
display: block !important;
left: 0 !important;
right: 0 !important;
/* OPTIONAL: change cosmetic styles as desired
border: 0 !important;
padding: 0 !important;
background: transparent !important;
*/
}

7332
js/c3.js Normal file

File diff suppressed because it is too large Load Diff

5
js/c3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

9554
js/d3.js vendored Normal file

File diff suppressed because it is too large Load Diff

5
js/d3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -706,19 +706,18 @@ try
}
break;
case 'open_flash_chart':
case 'chart':
// Workaround for IE8 + IIS + HTTPS
// See TRAC #363, fix described here: http://forums.codecharge.com/posts.php?post_id=97771
$oPage->add_header("Expires: Fri, 17 Jul 1970 05:00:00 GMT");
$oPage->add_header("Cache-Control: cache, must-revalidate");
$oPage->add_header("Pragma: public");
$oPage->SetContentType('application/json');
$aParams = utils::ReadParam('params', array(), false, 'raw_data');
if ($sFilter != '')
{
$oFilter = DBSearch::unserialize($sFilter);
$oDisplayBlock = new DisplayBlock($oFilter, 'open_flash_chart_ajax', false);
$oDisplayBlock = new DisplayBlock($oFilter, 'chart_ajax', false);
$oDisplayBlock->RenderContent($oPage, $aParams);
}
else

View File

@@ -580,7 +580,6 @@ try
$oPage->add('</form>');
$oPage->add('</div> <!-- end of wizForm -->');
if ($bShouldConfirm)
{
$sYesButton = Dict::S('UI:Button:Ok');
@@ -588,7 +587,7 @@ try
$oPage->add('<div id="dlg_confirmation" title="'.htmlentities(Dict::S('UI:CSVImportConfirmTitle'), ENT_QUOTES, 'UTF-8').'">');
$oPage->add('<p style="text-align:center"><b>'.$sMessage.'</b></p>');
$oPage->add('<p style="text-align:center">'.htmlentities(Dict::S('UI:CSVImportConfirmMessage'), ENT_QUOTES, 'UTF-8').'</p>');
$oPage->add('<div id="confirmation_chart"></div>');
$oPage->add('<div id="confirmation_chart" style="width:100%;height:300px;overflow:hidden"></div>');
$oPage->add('</div> <!-- end of dlg_confirmation -->');
$oPage->add_ready_script(
<<<EOF
@@ -604,21 +603,14 @@ try
'$sNoButton': CancelImport
}
});
swfobject.embedSWF( "../images/open-flash-chart.swf",
"confirmation_chart",
"100%", "300","9.0.0",
"expressInstall.swf",
{},
{'wmode': 'transparent'}
);
EOF
);
);
}
$sErrors = addslashes(Dict::Format('UI:CSVImportError_items', $iErrors));
$sCreated = addslashes(Dict::Format('UI:CSVImportCreated_items', $iCreated));
$sModified = addslashes(Dict::Format('UI:CSVImportModified_items', $iModified));
$sUnchanged = addslashes(Dict::Format('UI:CSVImportUnchanged_items', $iUnchanged));
$sErrors = json_encode(Dict::Format('UI:CSVImportError_items', $iErrors));
$sCreated = json_encode(Dict::Format('UI:CSVImportCreated_items', $iCreated));
$sModified = json_encode(Dict::Format('UI:CSVImportModified_items', $iModified));
$sUnchanged = json_encode(Dict::Format('UI:CSVImportUnchanged_items', $iUnchanged));
$oPage->add_script(
<<< EOF
function CSVGoBack()
@@ -645,6 +637,34 @@ function DoSubmit(bConfirm)
if (bConfirm) //Ask for a confirmation
{
$('#dlg_confirmation').dialog('open');
var chart = c3.generate({
bindto: '#confirmation_chart',
data: {
columns: [
['errors', $iErrors],
['created', $iCreated],
['modified', $iModified],
['unchanged', $iUnchanged]
],
colors: {
errors: '#FF6666',
created: '#66FF66',
modified: '#6666FF',
unchanged: '#666666'
},
names: {
errors: $sErrors,
created: $sCreated,
modified: $sModified,
unchanged: $sUnchanged
}
type: 'donut'
},
legend: {
show: true,
}
});
}
else
{
@@ -667,89 +687,6 @@ function RunImport()
$('#wizForm').block();
$('#wizForm').submit();
}
function open_flash_chart_data()
{
var iErrors = $iErrors;
var iModified = $iModified;
var iCreated = $iCreated;
var iUnchanged = $iUnchanged;
var fAlpha = 0.9;
var oResult = {
"elements": [
{
"type": "pie",
"tip": "#label# (#percent#)",
"gradient-fill": true,
"font-size": 14,
"colours":[],
"values": [],
"animate":[
{
"type": "fade"
}
]
}
],
"x_axis": null,
"font-size": 14,
"bg_colour": "#EEEEEE"
};
if (iErrors > 0)
{
var oErrors =
{
"value": iErrors,
"label": "$sErrors",
"alpha": fAlpha,
"label-colour": "#CC3333",
};
oResult.elements[0].values.push(oErrors);
oResult.elements[0].colours.push('#FF6666');
}
if (iModified > 0)
{
var oModified =
{
"value": iModified,
"label": "$sModified",
"alpha": fAlpha,
"label-colour": "#3333CC",
};
oResult.elements[0].values.push(oModified);
oResult.elements[0].colours.push('#6666FF');
}
if (iCreated > 0)
{
var oCreated =
{
"value": iCreated,
"label": "$sCreated",
"alpha": fAlpha,
"label-colour": "#33CC33",
};
oResult.elements[0].values.push(oCreated);
oResult.elements[0].colours.push('#66FF66');
}
if (iUnchanged > 0)
{
var oUnchanged =
{
"value": iUnchanged,
"label": "$sUnchanged",
"alpha": fAlpha,
"label-colour": "#333333",
};
oResult.elements[0].values.push(oUnchanged);
oResult.elements[0].colours.push('#666666');
}
return JSON.stringify(oResult);
}
EOF
);
if ($iErrors > 0)