IDEMPIERE-5402 Replace Jfree Chart with Billboard (#1463)
- replace jfreechart with https://github.com/naver/billboard.js
This commit is contained in:
parent
8748f11ddf
commit
92cdb06129
|
@ -183,11 +183,11 @@
|
||||||
<setEntry value="org.apache.felix.webconsole@default:true"/>
|
<setEntry value="org.apache.felix.webconsole@default:true"/>
|
||||||
<setEntry value="org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default"/>
|
<setEntry value="org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default"/>
|
||||||
<setEntry value="org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default"/>
|
<setEntry value="org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default"/>
|
||||||
|
<setEntry value="org.apache.httpcomponents.client5.httpclient5@default:default"/>
|
||||||
|
<setEntry value="org.apache.httpcomponents.core5.httpcore5-h2@default:default"/>
|
||||||
|
<setEntry value="org.apache.httpcomponents.core5.httpcore5@default:default"/>
|
||||||
<setEntry value="org.apache.httpcomponents.httpclient@default:default"/>
|
<setEntry value="org.apache.httpcomponents.httpclient@default:default"/>
|
||||||
<setEntry value="org.apache.httpcomponents.httpcore@default:default"/>
|
<setEntry value="org.apache.httpcomponents.httpcore@default:default"/>
|
||||||
<setEntry value="org.apache.httpcomponents.client5.httpclient5@default:default"/>
|
|
||||||
<setEntry value="org.apache.httpcomponents.core5.httpcore5@default:default"/>
|
|
||||||
<setEntry value="org.apache.httpcomponents.core5.httpcore5-h2@default:default"/>
|
|
||||||
<setEntry value="org.apache.neethi@default:default"/>
|
<setEntry value="org.apache.neethi@default:default"/>
|
||||||
<setEntry value="org.apache.poi.ooxml-schemas@default:default"/>
|
<setEntry value="org.apache.poi.ooxml-schemas@default:default"/>
|
||||||
<setEntry value="org.apache.servicemix.bundles.cglib@default:default"/>
|
<setEntry value="org.apache.servicemix.bundles.cglib@default:default"/>
|
||||||
|
@ -393,6 +393,8 @@
|
||||||
<setEntry value="org.idempiere.felix.webconsole@default:true"/>
|
<setEntry value="org.idempiere.felix.webconsole@default:true"/>
|
||||||
<setEntry value="org.idempiere.hazelcast.service@default:default"/>
|
<setEntry value="org.idempiere.hazelcast.service@default:default"/>
|
||||||
<setEntry value="org.idempiere.webservices@default:default"/>
|
<setEntry value="org.idempiere.webservices@default:default"/>
|
||||||
|
<setEntry value="org.idempiere.zk.billboard.chart@default:default"/>
|
||||||
|
<setEntry value="org.idempiere.zk.billboard@default:default"/>
|
||||||
<setEntry value="org.idempiere.zk.extra@default:default"/>
|
<setEntry value="org.idempiere.zk.extra@default:default"/>
|
||||||
</setAttribute>
|
</setAttribute>
|
||||||
<booleanAttribute key="show_selected_only" value="false"/>
|
<booleanAttribute key="show_selected_only" value="false"/>
|
||||||
|
|
|
@ -187,11 +187,11 @@
|
||||||
<setEntry value="org.apache.felix.webconsole@default:true"/>
|
<setEntry value="org.apache.felix.webconsole@default:true"/>
|
||||||
<setEntry value="org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default"/>
|
<setEntry value="org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default"/>
|
||||||
<setEntry value="org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default"/>
|
<setEntry value="org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default"/>
|
||||||
|
<setEntry value="org.apache.httpcomponents.client5.httpclient5@default:default"/>
|
||||||
|
<setEntry value="org.apache.httpcomponents.core5.httpcore5-h2@default:default"/>
|
||||||
|
<setEntry value="org.apache.httpcomponents.core5.httpcore5@default:default"/>
|
||||||
<setEntry value="org.apache.httpcomponents.httpclient@default:default"/>
|
<setEntry value="org.apache.httpcomponents.httpclient@default:default"/>
|
||||||
<setEntry value="org.apache.httpcomponents.httpcore@default:default"/>
|
<setEntry value="org.apache.httpcomponents.httpcore@default:default"/>
|
||||||
<setEntry value="org.apache.httpcomponents.client5.httpclient5@default:default"/>
|
|
||||||
<setEntry value="org.apache.httpcomponents.core5.httpcore5@default:default"/>
|
|
||||||
<setEntry value="org.apache.httpcomponents.core5.httpcore5-h2@default:default"/>
|
|
||||||
<setEntry value="org.apache.neethi@default:default"/>
|
<setEntry value="org.apache.neethi@default:default"/>
|
||||||
<setEntry value="org.apache.poi.ooxml-schemas@default:default"/>
|
<setEntry value="org.apache.poi.ooxml-schemas@default:default"/>
|
||||||
<setEntry value="org.apache.servicemix.bundles.batik@default:default"/>
|
<setEntry value="org.apache.servicemix.bundles.batik@default:default"/>
|
||||||
|
@ -415,6 +415,8 @@
|
||||||
<setEntry value="org.idempiere.keikai@default:false"/>
|
<setEntry value="org.idempiere.keikai@default:false"/>
|
||||||
<setEntry value="org.idempiere.printformat.editor@default:default"/>
|
<setEntry value="org.idempiere.printformat.editor@default:default"/>
|
||||||
<setEntry value="org.idempiere.webservices@default:default"/>
|
<setEntry value="org.idempiere.webservices@default:default"/>
|
||||||
|
<setEntry value="org.idempiere.zk.billboard.chart@default:default"/>
|
||||||
|
<setEntry value="org.idempiere.zk.billboard@default:false"/>
|
||||||
<setEntry value="org.idempiere.zk.extra@default:default"/>
|
<setEntry value="org.idempiere.zk.extra@default:default"/>
|
||||||
</setAttribute>
|
</setAttribute>
|
||||||
<booleanAttribute key="show_selected_only" value="false"/>
|
<booleanAttribute key="show_selected_only" value="false"/>
|
||||||
|
|
|
@ -57,4 +57,18 @@
|
||||||
version="0.0.0"
|
version="0.0.0"
|
||||||
unpack="false"/>
|
unpack="false"/>
|
||||||
|
|
||||||
|
<plugin
|
||||||
|
id="org.idempiere.zk.billboard"
|
||||||
|
download-size="0"
|
||||||
|
install-size="0"
|
||||||
|
version="0.0.0"
|
||||||
|
unpack="false"/>
|
||||||
|
|
||||||
|
<plugin
|
||||||
|
id="org.idempiere.zk.billboard.chart"
|
||||||
|
download-size="0"
|
||||||
|
install-size="0"
|
||||||
|
version="0.0.0"
|
||||||
|
unpack="false"/>
|
||||||
|
|
||||||
</feature>
|
</feature>
|
||||||
|
|
|
@ -213,7 +213,8 @@ Require-Bundle: org.adempiere.base;bundle-version="0.0.0",
|
||||||
com.sun.activation.jakarta.activation;bundle-version="1.2.1",
|
com.sun.activation.jakarta.activation;bundle-version="1.2.1",
|
||||||
org.adempiere.base.process,
|
org.adempiere.base.process,
|
||||||
com.github.librepdf.openpdf;bundle-version="1.3.26",
|
com.github.librepdf.openpdf;bundle-version="1.3.26",
|
||||||
com.github.librepdf.openpdf-fonts-extra;bundle-version="1.3.26"
|
com.github.librepdf.openpdf-fonts-extra;bundle-version="1.3.26",
|
||||||
|
org.idempiere.zk.billboard
|
||||||
Bundle-Activator: org.adempiere.webui.WebUIActivator
|
Bundle-Activator: org.adempiere.webui.WebUIActivator
|
||||||
Eclipse-ExtensibleAPI: true
|
Eclipse-ExtensibleAPI: true
|
||||||
Web-ContextPath: webui
|
Web-ContextPath: webui
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<!DOCTYPE scr:component>
|
<!DOCTYPE scr:component>
|
||||||
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.adempiere.webui.apps.graph.jfreegraph.ChartRendererServiceImpl">
|
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.adempiere.webui.apps.graph.jfreegraph.ChartRendererServiceImpl">
|
||||||
<implementation class="org.adempiere.webui.apps.graph.jfreegraph.ChartRendererServiceImpl"/>
|
<implementation class="org.adempiere.webui.apps.graph.jfreegraph.ChartRendererServiceImpl"/>
|
||||||
<property name="service.ranking" type="Integer" value="0"/>
|
<property name="service.ranking" type="Integer" value="-1"/>
|
||||||
<service>
|
<service>
|
||||||
<provide interface="org.adempiere.webui.apps.graph.IChartRendererService"/>
|
<provide interface="org.adempiere.webui.apps.graph.IChartRendererService"/>
|
||||||
</service>
|
</service>
|
||||||
|
|
|
@ -172,11 +172,11 @@
|
||||||
<setEntry value="org.apache.felix.scr@1:true"/>
|
<setEntry value="org.apache.felix.scr@1:true"/>
|
||||||
<setEntry value="org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default"/>
|
<setEntry value="org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default"/>
|
||||||
<setEntry value="org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default"/>
|
<setEntry value="org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default"/>
|
||||||
|
<setEntry value="org.apache.httpcomponents.client5.httpclient5@default:default"/>
|
||||||
|
<setEntry value="org.apache.httpcomponents.core5.httpcore5-h2@default:default"/>
|
||||||
|
<setEntry value="org.apache.httpcomponents.core5.httpcore5@default:default"/>
|
||||||
<setEntry value="org.apache.httpcomponents.httpclient@default:default"/>
|
<setEntry value="org.apache.httpcomponents.httpclient@default:default"/>
|
||||||
<setEntry value="org.apache.httpcomponents.httpcore@default:default"/>
|
<setEntry value="org.apache.httpcomponents.httpcore@default:default"/>
|
||||||
<setEntry value="org.apache.httpcomponents.client5.httpclient5@default:default"/>
|
|
||||||
<setEntry value="org.apache.httpcomponents.core5.httpcore5@default:default"/>
|
|
||||||
<setEntry value="org.apache.httpcomponents.core5.httpcore5-h2@default:default"/>
|
|
||||||
<setEntry value="org.apache.neethi@default:default"/>
|
<setEntry value="org.apache.neethi@default:default"/>
|
||||||
<setEntry value="org.apache.poi.ooxml-schemas@default:default"/>
|
<setEntry value="org.apache.poi.ooxml-schemas@default:default"/>
|
||||||
<setEntry value="org.apache.servicemix.bundles.batik@default:default"/>
|
<setEntry value="org.apache.servicemix.bundles.batik@default:default"/>
|
||||||
|
@ -393,6 +393,8 @@
|
||||||
<setEntry value="org.idempiere.hazelcast.service@default:default"/>
|
<setEntry value="org.idempiere.hazelcast.service@default:default"/>
|
||||||
<setEntry value="org.idempiere.test@default:default"/>
|
<setEntry value="org.idempiere.test@default:default"/>
|
||||||
<setEntry value="org.idempiere.webservices@default:default"/>
|
<setEntry value="org.idempiere.webservices@default:default"/>
|
||||||
|
<setEntry value="org.idempiere.zk.billboard.chart@default:default"/>
|
||||||
|
<setEntry value="org.idempiere.zk.billboard@default:default"/>
|
||||||
<setEntry value="org.idempiere.zk.extra@default:default"/>
|
<setEntry value="org.idempiere.zk.extra@default:default"/>
|
||||||
</setAttribute>
|
</setAttribute>
|
||||||
<booleanAttribute key="show_selected_only" value="false"/>
|
<booleanAttribute key="show_selected_only" value="false"/>
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>org.idempiere.zk.billboard.chart</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.pde.ds.core.builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
|
<nature>org.eclipse.pde.PluginNature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
|
@ -0,0 +1,2 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding/<project>=UTF-8
|
|
@ -0,0 +1,4 @@
|
||||||
|
activeProfiles=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
resolveWorkspaceProjects=true
|
||||||
|
version=1
|
|
@ -0,0 +1,3 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
pluginProject.extensions=false
|
||||||
|
resolve.requirebundle=false
|
|
@ -0,0 +1,27 @@
|
||||||
|
Manifest-Version: 1.0
|
||||||
|
Bundle-ManifestVersion: 2
|
||||||
|
Bundle-Name: Billboard.js Chart
|
||||||
|
Bundle-SymbolicName: org.idempiere.zk.billboard.chart
|
||||||
|
Bundle-Version: 10.0.0.qualifier
|
||||||
|
Bundle-Activator: org.idempiere.zk.billboard.chart.Activator
|
||||||
|
Bundle-Vendor: iDempiere
|
||||||
|
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||||
|
Automatic-Module-Name: org.idempiere.zk.billboard.chart
|
||||||
|
Import-Package: org.osgi.framework,
|
||||||
|
org.osgi.service.component,
|
||||||
|
org.osgi.service.component.annotations
|
||||||
|
Bundle-ActivationPolicy: lazy
|
||||||
|
Require-Bundle: org.adempiere.ui.zk,
|
||||||
|
org.adempiere.base,
|
||||||
|
zul,
|
||||||
|
zcommon,
|
||||||
|
zel,
|
||||||
|
zhtml,
|
||||||
|
zjavassist,
|
||||||
|
zk,
|
||||||
|
zkbind,
|
||||||
|
zkplus,
|
||||||
|
zkwebfragment,
|
||||||
|
zweb,
|
||||||
|
org.idempiere.zk.billboard
|
||||||
|
Service-Component: OSGI-INF/org.idempiere.zk.billboard.chart.ChartRendererServiceImpl.xml
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.idempiere.zk.billboard.chart.ChartRendererServiceImpl">
|
||||||
|
<property name="service.ranking" type="Integer" value="0"/>
|
||||||
|
<service>
|
||||||
|
<provide interface="org.adempiere.webui.apps.graph.IChartRendererService"/>
|
||||||
|
</service>
|
||||||
|
<implementation class="org.idempiere.zk.billboard.chart.ChartRendererServiceImpl"/>
|
||||||
|
</scr:component>
|
|
@ -0,0 +1,5 @@
|
||||||
|
source.. = src/
|
||||||
|
output.. = target/classes/
|
||||||
|
bin.includes = META-INF/,\
|
||||||
|
.,\
|
||||||
|
OSGI-INF/
|
|
@ -0,0 +1,12 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.idempiere</groupId>
|
||||||
|
<artifactId>org.idempiere.parent</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../org.idempiere.parent/pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<artifactId>org.idempiere.zk.billboard.chart</artifactId>
|
||||||
|
<packaging>eclipse-plugin</packaging>
|
||||||
|
</project>
|
|
@ -0,0 +1,51 @@
|
||||||
|
/***********************************************************************
|
||||||
|
* This file is part of iDempiere ERP Open Source *
|
||||||
|
* http://www.idempiere.org *
|
||||||
|
* *
|
||||||
|
* Copyright (C) Contributors *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software *
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||||
|
* MA 02110-1301, USA. *
|
||||||
|
* *
|
||||||
|
* Contributors: *
|
||||||
|
* - hengsin *
|
||||||
|
**********************************************************************/
|
||||||
|
package org.idempiere.zk.billboard.chart;
|
||||||
|
|
||||||
|
import org.osgi.framework.BundleActivator;
|
||||||
|
import org.osgi.framework.BundleContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author hengsin
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Activator implements BundleActivator {
|
||||||
|
|
||||||
|
private static BundleContext context;
|
||||||
|
|
||||||
|
static BundleContext getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(BundleContext bundleContext) throws Exception {
|
||||||
|
Activator.context = bundleContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(BundleContext bundleContext) throws Exception {
|
||||||
|
Activator.context = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,545 @@
|
||||||
|
/***********************************************************************
|
||||||
|
* This file is part of iDempiere ERP Open Source *
|
||||||
|
* http://www.idempiere.org *
|
||||||
|
* *
|
||||||
|
* Copyright (C) Contributors *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software *
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||||
|
* MA 02110-1301, USA. *
|
||||||
|
* *
|
||||||
|
* Contributors: *
|
||||||
|
* - hengsin *
|
||||||
|
**********************************************************************/
|
||||||
|
package org.idempiere.zk.billboard.chart;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import org.adempiere.exceptions.DBException;
|
||||||
|
import org.compiere.model.MChart;
|
||||||
|
import org.compiere.model.MChartDatasource;
|
||||||
|
import org.compiere.model.MQuery;
|
||||||
|
import org.compiere.model.MRole;
|
||||||
|
import org.compiere.model.MTable;
|
||||||
|
import org.compiere.util.CLogger;
|
||||||
|
import org.compiere.util.DB;
|
||||||
|
import org.compiere.util.Env;
|
||||||
|
import org.compiere.util.Util;
|
||||||
|
import org.idempiere.zk.billboard.Billboard;
|
||||||
|
import org.zkoss.zul.CategoryModel;
|
||||||
|
import org.zkoss.zul.ChartModel;
|
||||||
|
import org.zkoss.zul.PieModel;
|
||||||
|
import org.zkoss.zul.SimpleCategoryModel;
|
||||||
|
import org.zkoss.zul.SimplePieModel;
|
||||||
|
import org.zkoss.zul.SimpleXYModel;
|
||||||
|
import org.zkoss.zul.XYModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render AD_Chart using zk-billboard.
|
||||||
|
* Note: 3d chart not supported by zk-billboard
|
||||||
|
* @author hengsin
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ChartBuilder {
|
||||||
|
|
||||||
|
private final static CLogger log = CLogger.getCLogger(ChartBuilder.class);
|
||||||
|
protected final SimpleDateFormat tsDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
|
|
||||||
|
private MChart mChart;
|
||||||
|
private HashMap<String,MQuery> queries;
|
||||||
|
private ChartModel chartModel;
|
||||||
|
private Date minDate;
|
||||||
|
private Date maxDate;
|
||||||
|
|
||||||
|
public ChartBuilder(MChart chart) {
|
||||||
|
this.mChart = chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @return Billboard
|
||||||
|
*/
|
||||||
|
public Billboard createChart() {
|
||||||
|
String type = mChart.getChartType();
|
||||||
|
|
||||||
|
if (MChart.CHARTTYPE_BarChart.equals(type))
|
||||||
|
{
|
||||||
|
if (mChart.isTimeSeries())
|
||||||
|
return createXYBarChart();
|
||||||
|
return createBarChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_3DBarChart.equals(type))
|
||||||
|
{
|
||||||
|
return create3DBarChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_StackedBarChart.equals(type))
|
||||||
|
{
|
||||||
|
if (mChart.isTimeSeries())
|
||||||
|
return createXYBarChart();
|
||||||
|
return createStackedBarChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_3DStackedBarChart.equals(type))
|
||||||
|
{
|
||||||
|
return create3DStackedBarChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_3DPieChart.equals(type))
|
||||||
|
{
|
||||||
|
return create3DPieChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_PieChart.equals(type))
|
||||||
|
{
|
||||||
|
return createPieChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_3DLineChart.equals(type))
|
||||||
|
{
|
||||||
|
return create3DLineChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_AreaChart.equals(type))
|
||||||
|
{
|
||||||
|
return createAreaChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_StackedAreaChart.equals(type))
|
||||||
|
{
|
||||||
|
return createStackedAreaChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_LineChart.equals(type))
|
||||||
|
{
|
||||||
|
if (mChart.isTimeSeries())
|
||||||
|
return createTimeSeriesChart();
|
||||||
|
return createLineChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_RingChart.equals(type))
|
||||||
|
{
|
||||||
|
return createRingChart();
|
||||||
|
}
|
||||||
|
else if (MChart.CHARTTYPE_WaterfallChart.equals(type))
|
||||||
|
{
|
||||||
|
return createWaterfallChart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("unknown chart type=" + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadData() {
|
||||||
|
queries = new HashMap<String,MQuery>();
|
||||||
|
for ( MChartDatasource ds : mChart.getDatasources() )
|
||||||
|
{
|
||||||
|
addData(ds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addData(MChartDatasource ds) {
|
||||||
|
|
||||||
|
String value = ds.getValueColumn();
|
||||||
|
String category;
|
||||||
|
String unit = "D";
|
||||||
|
|
||||||
|
if ( !mChart.isTimeSeries() )
|
||||||
|
category = ds.getCategoryColumn();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( mChart.getTimeUnit().equals(MChart.TIMEUNIT_Week))
|
||||||
|
{
|
||||||
|
unit = "W";
|
||||||
|
}
|
||||||
|
else if ( mChart.getTimeUnit().equals(MChart.TIMEUNIT_Month))
|
||||||
|
{
|
||||||
|
unit = "MM";
|
||||||
|
}
|
||||||
|
else if ( mChart.getTimeUnit().equals(MChart.TIMEUNIT_Quarter))
|
||||||
|
{
|
||||||
|
unit = "Q";
|
||||||
|
}
|
||||||
|
else if ( mChart.getTimeUnit().equals(MChart.TIMEUNIT_Year))
|
||||||
|
{
|
||||||
|
unit = "Y";
|
||||||
|
}
|
||||||
|
|
||||||
|
category = " TRUNC(" + ds.getDateColumn() + ", '" + unit + "') ";
|
||||||
|
}
|
||||||
|
|
||||||
|
String series = DB.TO_STRING(ds.getName());
|
||||||
|
boolean hasSeries = false;
|
||||||
|
if (ds.getSeriesColumn() != null)
|
||||||
|
{
|
||||||
|
series = ds.getSeriesColumn();
|
||||||
|
hasSeries = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String where = ds.getWhereClause();
|
||||||
|
if ( !Util.isEmpty(where))
|
||||||
|
{
|
||||||
|
where = Env.parseContext(Env.getCtx(), mChart.getWindowNo(), where, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasWhere = false;
|
||||||
|
|
||||||
|
String sql = "SELECT " + value + ", " + category + ", " + series
|
||||||
|
+ " FROM " + ds.getFromClause();
|
||||||
|
if ( !Util.isEmpty(where))
|
||||||
|
{
|
||||||
|
sql += " WHERE " + where;
|
||||||
|
hasWhere = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Date currentDate = Env.getContextAsDate(Env.getCtx(), "#Date");
|
||||||
|
Date startDate = null;
|
||||||
|
Date endDate = null;
|
||||||
|
|
||||||
|
int scope = mChart.getTimeScope();
|
||||||
|
int offset = ds.getTimeOffset();
|
||||||
|
|
||||||
|
if ( mChart.isTimeSeries() && scope != 0 )
|
||||||
|
{
|
||||||
|
offset += -scope;
|
||||||
|
startDate = increment(currentDate, mChart.getTimeUnit(), offset);
|
||||||
|
endDate = increment(startDate, mChart.getTimeUnit(), scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( startDate != null && endDate != null )
|
||||||
|
{
|
||||||
|
sql += hasWhere ? " AND " : " WHERE ";
|
||||||
|
sql += category + ">=TRUNC(" + DB.TO_DATE(new Timestamp(startDate.getTime())) + ", '" + unit + "') AND ";
|
||||||
|
sql += category + "<=TRUNC(" + DB.TO_DATE(new Timestamp(endDate.getTime())) + ", '" + unit + "') ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sql.indexOf('@') >= 0) {
|
||||||
|
sql = Env.parseContext(Env.getCtx(), 0, sql, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
MRole role = MRole.getDefault(Env.getCtx(), false);
|
||||||
|
sql = role.addAccessSQL(sql, null, true, false);
|
||||||
|
|
||||||
|
if (hasSeries)
|
||||||
|
sql += " GROUP BY " + series + ", " + category + " ORDER BY " + series + ", " + category;
|
||||||
|
else
|
||||||
|
sql += " GROUP BY " + category + " ORDER BY " + category;
|
||||||
|
|
||||||
|
log.log(Level.FINE, sql);
|
||||||
|
|
||||||
|
PreparedStatement pstmt = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
ChartModel dataset = getChartModel();
|
||||||
|
minDate = null;
|
||||||
|
maxDate = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pstmt = DB.prepareStatement(sql, null);
|
||||||
|
rs = pstmt.executeQuery();
|
||||||
|
while(rs.next())
|
||||||
|
{
|
||||||
|
String key = rs.getString(2);
|
||||||
|
String seriesName = rs.getString(3);
|
||||||
|
if (seriesName == null)
|
||||||
|
seriesName = ds.getName();
|
||||||
|
String queryWhere = "";
|
||||||
|
if ( hasWhere )
|
||||||
|
queryWhere += where + " AND ";
|
||||||
|
|
||||||
|
queryWhere += series + " = " + DB.TO_STRING(seriesName) + " AND " + category + " = " ;
|
||||||
|
|
||||||
|
if (mChart.isTimeSeries())
|
||||||
|
{
|
||||||
|
Date date = rs.getDate(2);
|
||||||
|
if (minDate == null || minDate.compareTo(date) > 0)
|
||||||
|
minDate = date;
|
||||||
|
if (maxDate == null || maxDate.compareTo(date) < 0)
|
||||||
|
maxDate = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( mChart.isTimeSeries() && dataset instanceof XYModel )
|
||||||
|
{
|
||||||
|
XYModel xy = (XYModel) dataset;
|
||||||
|
|
||||||
|
Date date = rs.getDate(2);
|
||||||
|
BigDecimal tsvalue = rs.getBigDecimal(1);
|
||||||
|
xy.addValue(seriesName, date.getTime(), tsvalue);
|
||||||
|
key = tsDateFormat.format(date);
|
||||||
|
queryWhere += DB.TO_DATE(new Timestamp(date.getTime()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
queryWhere += DB.TO_STRING(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
MQuery query = new MQuery(ds.getAD_Table_ID());
|
||||||
|
String keyCol = MTable.get(Env.getCtx(), ds.getAD_Table_ID()).getKeyColumns()[0];
|
||||||
|
String whereClause = keyCol + " IN (SELECT " + ds.getKeyColumn() + " FROM "
|
||||||
|
+ ds.getFromClause() + " WHERE " + queryWhere + " )";
|
||||||
|
query.addRestriction(whereClause.toString());
|
||||||
|
query.setRecordCount(1);
|
||||||
|
|
||||||
|
HashMap<String, MQuery> map = getQueries();
|
||||||
|
|
||||||
|
if (dataset instanceof PieModel) {
|
||||||
|
((PieModel) dataset).setValue(key, rs.getBigDecimal(1));
|
||||||
|
map.put(key, query);
|
||||||
|
}
|
||||||
|
else if ( dataset instanceof CategoryModel ) {
|
||||||
|
((CategoryModel) dataset).setValue(seriesName, key, rs.getBigDecimal(1));
|
||||||
|
map.put(seriesName + "__" + key, query);
|
||||||
|
}
|
||||||
|
else if (dataset instanceof XYModel ) {
|
||||||
|
map.put(seriesName + "__" + key, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SQLException e)
|
||||||
|
{
|
||||||
|
throw new DBException(e, sql);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
DB.close(rs, pstmt);
|
||||||
|
rs = null; pstmt = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Date increment(Date lastDate, String timeUnit, int qty) {
|
||||||
|
|
||||||
|
if ( lastDate == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(lastDate);
|
||||||
|
|
||||||
|
if ( timeUnit.equals(MChart.TIMEUNIT_Day))
|
||||||
|
cal.add(Calendar.DAY_OF_YEAR, qty);
|
||||||
|
else if ( timeUnit.equals(MChart.TIMEUNIT_Week))
|
||||||
|
cal.add(Calendar.WEEK_OF_YEAR, qty);
|
||||||
|
else if ( timeUnit.equals(MChart.TIMEUNIT_Month))
|
||||||
|
cal.add(Calendar.MONTH, qty);
|
||||||
|
else if ( timeUnit.equals(MChart.TIMEUNIT_Quarter))
|
||||||
|
cal.add(Calendar.MONTH, 3*qty);
|
||||||
|
else if ( timeUnit.equals(MChart.TIMEUNIT_Year))
|
||||||
|
cal.add(Calendar.YEAR, qty);
|
||||||
|
|
||||||
|
return cal.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CategoryModel getCategoryModel() {
|
||||||
|
chartModel = new SimpleCategoryModel();
|
||||||
|
loadData();
|
||||||
|
return (CategoryModel) chartModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XYModel getXYModel() {
|
||||||
|
chartModel = new SimpleXYModel();
|
||||||
|
loadData();
|
||||||
|
return (XYModel) chartModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PieModel getPieModel() {
|
||||||
|
chartModel = new SimplePieModel();
|
||||||
|
loadData();
|
||||||
|
return (PieModel) chartModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChartModel getChartModel() {
|
||||||
|
return chartModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, MQuery> getQueries() {
|
||||||
|
return queries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MQuery getQuery(String key) {
|
||||||
|
|
||||||
|
|
||||||
|
if ( queries.containsKey(key) )
|
||||||
|
{
|
||||||
|
return queries.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createXYBarChart() {
|
||||||
|
Billboard billboard = newBillboard("bar");
|
||||||
|
XYModel xymodel = getXYModel();
|
||||||
|
CategoryModel model = new SimpleCategoryModel();
|
||||||
|
Collection<Comparable<?>> seriesList = xymodel.getSeries();
|
||||||
|
for(Comparable<?> series : seriesList) {
|
||||||
|
int count = xymodel.getDataCount(series);
|
||||||
|
for(int i = 0; i < count; i++) {
|
||||||
|
Number value = xymodel.getY(series, i);
|
||||||
|
Number category = xymodel.getX(series, i);
|
||||||
|
Date date = new Date(category.longValue());
|
||||||
|
String categoryLabel = null;
|
||||||
|
categoryLabel = tsDateFormat.format(date);
|
||||||
|
Number oldValue = model.getValue(series, categoryLabel);
|
||||||
|
if (oldValue != null)
|
||||||
|
value = oldValue.doubleValue() + value.doubleValue();
|
||||||
|
model.setValue(series, categoryLabel, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
billboard.setModel(model);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createTimeSeriesChart() {
|
||||||
|
Billboard billboard = newBillboard("line");
|
||||||
|
XYModel xymodel = getXYModel();
|
||||||
|
CategoryModel model = new SimpleCategoryModel();
|
||||||
|
Collection<Comparable<?>> seriesList = xymodel.getSeries();
|
||||||
|
for(Comparable<?> series : seriesList) {
|
||||||
|
int count = xymodel.getDataCount(series);
|
||||||
|
for(int i = 0; i < count; i++) {
|
||||||
|
Number value = xymodel.getY(series, i);
|
||||||
|
Number category = xymodel.getX(series, i);
|
||||||
|
Date date = new Date(category.longValue());
|
||||||
|
String categoryLabel = null;
|
||||||
|
categoryLabel = tsDateFormat.format(date);
|
||||||
|
Number oldValue = model.getValue(series, categoryLabel);
|
||||||
|
if (oldValue != null)
|
||||||
|
value = oldValue.doubleValue() + value.doubleValue();
|
||||||
|
model.setValue(series, categoryLabel, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
billboard.setModel(model);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createWaterfallChart() {
|
||||||
|
Billboard billboard = newBillboard("waterfall");
|
||||||
|
CategoryModel model = getCategoryModel();
|
||||||
|
CategoryModel waterfallModel = new SimpleCategoryModel();
|
||||||
|
Collection<Comparable<?>> seriesList = model.getSeries();
|
||||||
|
Map<Comparable<?>, BigDecimal> valueMap = new HashMap<Comparable<?>, BigDecimal>();
|
||||||
|
Collection<Comparable<?>> categories = model.getCategories();
|
||||||
|
for(Comparable<?> series : seriesList) {
|
||||||
|
for(Comparable<?> category : categories) {
|
||||||
|
BigDecimal value = (BigDecimal) model.getValue(series, category);
|
||||||
|
BigDecimal diff = value;
|
||||||
|
BigDecimal oldValue = valueMap.get(series);
|
||||||
|
if (oldValue != null) {
|
||||||
|
diff = diff.subtract(oldValue);
|
||||||
|
}
|
||||||
|
valueMap.put(series, value);
|
||||||
|
waterfallModel.setValue(series, category, diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
billboard.setModel(waterfallModel);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard newBillboard(String type) {
|
||||||
|
Billboard billboard = new Billboard();
|
||||||
|
if (mChart.isDisplayLegend()) {
|
||||||
|
billboard.setLegend(true, false);
|
||||||
|
billboard.addLegendOptions("location", "bottom"); //bottom, right
|
||||||
|
}
|
||||||
|
billboard.setTickAxisLabel(mChart.getDomainLabel());
|
||||||
|
billboard.setValueAxisLabel(mChart.getRangeLabel());
|
||||||
|
billboard.setTitle(mChart.getName());
|
||||||
|
billboard.setType(type);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createRingChart() {
|
||||||
|
Billboard billboard = newBillboard("donut");
|
||||||
|
billboard.setModel(getPieModel());
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createPieChart() {
|
||||||
|
Billboard billboard = newBillboard("pie");
|
||||||
|
billboard.setModel(getPieModel());
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard create3DPieChart() {
|
||||||
|
return createPieChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createBarChart() {
|
||||||
|
Billboard billboard = newBillboard("bar");
|
||||||
|
billboard.setModel(getCategoryModel());
|
||||||
|
if (MChart.CHARTORIENTATION_Vertical.equals(mChart.getChartOrientation()))
|
||||||
|
billboard.setOrient(Billboard.VERTICAL_ORIENTATION);
|
||||||
|
else if (MChart.CHARTORIENTATION_Horizontal.equals(mChart.getChartOrientation()))
|
||||||
|
billboard.setOrient(Billboard.HORIZONTAL_ORIENTATION);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard create3DBarChart() {
|
||||||
|
return createBarChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createStackedBarChart() {
|
||||||
|
Billboard billboard = newBillboard("stacked_bar");
|
||||||
|
billboard.setModel(getCategoryModel());
|
||||||
|
if (MChart.CHARTORIENTATION_Vertical.equals(mChart.getChartOrientation()))
|
||||||
|
billboard.setOrient(Billboard.VERTICAL_ORIENTATION);
|
||||||
|
else if (MChart.CHARTORIENTATION_Horizontal.equals(mChart.getChartOrientation()))
|
||||||
|
billboard.setOrient(Billboard.HORIZONTAL_ORIENTATION);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard create3DStackedBarChart() {
|
||||||
|
return createStackedBarChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createAreaChart() {
|
||||||
|
Billboard billboard = newBillboard("area");
|
||||||
|
billboard.setModel(getCategoryModel());
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createStackedAreaChart() {
|
||||||
|
Billboard billboard = newBillboard("stacked_area");
|
||||||
|
billboard.setModel(getCategoryModel());
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createLineChart() {
|
||||||
|
Billboard billboard = newBillboard("line");
|
||||||
|
billboard.setModel(getCategoryModel());
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard create3DLineChart() {
|
||||||
|
return createLineChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getMinDate() {
|
||||||
|
return minDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinDate(Date minDate) {
|
||||||
|
this.minDate = minDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getMaxDate() {
|
||||||
|
return maxDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxDate(Date maxDate) {
|
||||||
|
this.maxDate = maxDate;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
/***********************************************************************
|
||||||
|
* This file is part of iDempiere ERP Open Source *
|
||||||
|
* http://www.idempiere.org *
|
||||||
|
* *
|
||||||
|
* Copyright (C) Contributors *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software *
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||||
|
* MA 02110-1301, USA. *
|
||||||
|
* *
|
||||||
|
* Contributors: *
|
||||||
|
* - hengsin *
|
||||||
|
**********************************************************************/
|
||||||
|
package org.idempiere.zk.billboard.chart;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.adempiere.webui.apps.AEnv;
|
||||||
|
import org.adempiere.webui.apps.graph.IChartRendererService;
|
||||||
|
import org.adempiere.webui.apps.graph.model.ChartModel;
|
||||||
|
import org.adempiere.webui.apps.graph.model.GoalModel;
|
||||||
|
import org.adempiere.webui.apps.graph.model.IndicatorModel;
|
||||||
|
import org.adempiere.webui.component.Label;
|
||||||
|
import org.compiere.model.MChart;
|
||||||
|
import org.compiere.model.MQuery;
|
||||||
|
import org.compiere.model.MSysConfig;
|
||||||
|
import org.compiere.util.Msg;
|
||||||
|
import org.idempiere.zk.billboard.Billboard;
|
||||||
|
import org.zkoss.json.JSONObject;
|
||||||
|
import org.zkoss.zk.au.out.AuScript;
|
||||||
|
import org.zkoss.zk.ui.Component;
|
||||||
|
import org.zkoss.zk.ui.Executions;
|
||||||
|
import org.zkoss.zk.ui.event.Event;
|
||||||
|
import org.zkoss.zk.ui.event.EventListener;
|
||||||
|
import org.zkoss.zk.ui.util.Clients;
|
||||||
|
import org.zkoss.zul.CategoryModel;
|
||||||
|
import org.zkoss.zul.Div;
|
||||||
|
import org.zkoss.zul.PieModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hengsin
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@org.osgi.service.component.annotations.Component(name="org.idempiere.zk.billboard.chart.ChartRendererServiceImpl", immediate = true,
|
||||||
|
service = IChartRendererService.class, property = {"service.ranking:Integer=0"})
|
||||||
|
public class ChartRendererServiceImpl implements IChartRendererService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public ChartRendererServiceImpl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean renderPerformanceIndicator(Component parent, int chartWidth, int chartHeight, IndicatorModel model) {
|
||||||
|
PerformanceGraphBuilder builder = new PerformanceGraphBuilder();
|
||||||
|
Billboard billboard = builder.createIndicatorChart(model);
|
||||||
|
billboard.setStyle("width: "+chartWidth+"px;height: "+chartHeight+"px;");
|
||||||
|
parent.appendChild(billboard);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean renderPerformanceGraph(Component parent, int chartWidth, int chartHeight,
|
||||||
|
GoalModel goalModel) {
|
||||||
|
PerformanceGraphBuilder builder = new PerformanceGraphBuilder();
|
||||||
|
Billboard billboard = builder.createPerformanceChart(goalModel, chartWidth, chartHeight);
|
||||||
|
parent.appendChild(billboard);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean renderChart(Component parent, int width, int height,
|
||||||
|
ChartModel chartModel, boolean showTitle) {
|
||||||
|
ChartBuilder builder = new ChartBuilder(chartModel.chart);
|
||||||
|
Billboard billboard = builder.createChart();
|
||||||
|
billboard.setStyle("width: " + width + "px;" + " height: " + height + "px;");
|
||||||
|
if (!showTitle)
|
||||||
|
billboard.setTitle("");
|
||||||
|
updateUI(parent, chartModel, builder, billboard, width, height);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUI(Component parent, ChartModel chartModel, ChartBuilder builder, Billboard billboard, int width, int height) {
|
||||||
|
// set billboard time series properties
|
||||||
|
Date minDate = builder.getMinDate();
|
||||||
|
Date maxDate = builder.getMaxDate();
|
||||||
|
if (chartModel.chart.isTimeSeries() && minDate != null && maxDate != null)
|
||||||
|
{
|
||||||
|
billboard.setTimeSeries(true);
|
||||||
|
|
||||||
|
int noOfPeriod = 0;
|
||||||
|
if (width < MSysConfig.getIntValue("CHART_MIN_WIDTH_3_PERIOD", 230, chartModel.chart.getAD_Client_ID()))
|
||||||
|
noOfPeriod = 3;
|
||||||
|
else if (width < MSysConfig.getIntValue("CHART_MIN_WIDTH_6_PERIOD", 320, chartModel.chart.getAD_Client_ID()))
|
||||||
|
noOfPeriod = 6;
|
||||||
|
|
||||||
|
Calendar c = Calendar.getInstance();
|
||||||
|
c.setTime(maxDate);
|
||||||
|
|
||||||
|
String timeUnit = chartModel.chart.getTimeUnit();
|
||||||
|
if (chartModel.chart.getTimeScope() == 1)
|
||||||
|
{
|
||||||
|
if (timeUnit.equals(MChart.TIMEUNIT_Week))
|
||||||
|
timeUnit = MChart.TIMEUNIT_Day;
|
||||||
|
else if (timeUnit.equals(MChart.TIMEUNIT_Month))
|
||||||
|
timeUnit = MChart.TIMEUNIT_Week;
|
||||||
|
else if (timeUnit.equals(MChart.TIMEUNIT_Quarter))
|
||||||
|
timeUnit = MChart.TIMEUNIT_Month;
|
||||||
|
else if (timeUnit.equals(MChart.TIMEUNIT_Year))
|
||||||
|
timeUnit = MChart.TIMEUNIT_Quarter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeUnit.equals(MChart.TIMEUNIT_Day))
|
||||||
|
{
|
||||||
|
billboard.setTimeSeriesInterval("1 days");
|
||||||
|
billboard.setTimeSeriesFormat("%D"); // e.g. 03/26/08 %m/%d/%y
|
||||||
|
if (noOfPeriod != 0)
|
||||||
|
c.add(Calendar.DAY_OF_MONTH, -1 * (noOfPeriod - 1));
|
||||||
|
}
|
||||||
|
else if (timeUnit.equals(MChart.TIMEUNIT_Week))
|
||||||
|
{
|
||||||
|
billboard.setTimeSeriesInterval("1 weeks");
|
||||||
|
billboard.setTimeSeriesFormat("%D"); // e.g. 03/26/08 %m/%d/%y
|
||||||
|
if (noOfPeriod != 0)
|
||||||
|
c.add(Calendar.WEEK_OF_YEAR, -1 * (noOfPeriod - 1));
|
||||||
|
}
|
||||||
|
else if (timeUnit.equals(MChart.TIMEUNIT_Month))
|
||||||
|
{
|
||||||
|
billboard.setTimeSeriesInterval("1 months");
|
||||||
|
billboard.setTimeSeriesFormat("%b %Y"); // e.g. Sep 2008
|
||||||
|
if (noOfPeriod != 0)
|
||||||
|
c.add(Calendar.MONTH, -1 * (noOfPeriod - 1));
|
||||||
|
}
|
||||||
|
else if (timeUnit.equals(MChart.TIMEUNIT_Quarter))
|
||||||
|
{
|
||||||
|
billboard.setTimeSeriesInterval("3 months");
|
||||||
|
billboard.setTimeSeriesFormat("%b %Y"); // e.g. Sep 2008
|
||||||
|
if (noOfPeriod != 0)
|
||||||
|
c.add(Calendar.MONTH, -1 * (noOfPeriod - 1) * 3);
|
||||||
|
}
|
||||||
|
else if (timeUnit.equals(MChart.TIMEUNIT_Year))
|
||||||
|
{
|
||||||
|
billboard.setTimeSeriesInterval("1 years");
|
||||||
|
billboard.setTimeSeriesFormat("%Y"); // e.g. 2008
|
||||||
|
if (noOfPeriod != 0)
|
||||||
|
c.add(Calendar.YEAR, -1 * (noOfPeriod - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noOfPeriod != 0)
|
||||||
|
{
|
||||||
|
Date startDate = c.getTime();
|
||||||
|
if (minDate.before(startDate))
|
||||||
|
minDate = startDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.getChildren().clear();
|
||||||
|
parent.appendChild(billboard);
|
||||||
|
|
||||||
|
Label label = new Label(Msg.translate(chartModel.chart.getCtx(), "NoDataAvailable"));
|
||||||
|
Div labelDiv = new Div();
|
||||||
|
labelDiv.setStyle("padding: 10px;");
|
||||||
|
labelDiv.appendChild(label);
|
||||||
|
Div div = new Div();
|
||||||
|
div.appendChild(labelDiv);
|
||||||
|
parent.appendChild(div);
|
||||||
|
div.setVisible(false);
|
||||||
|
|
||||||
|
if (Executions.getCurrent() != null)
|
||||||
|
{
|
||||||
|
String script = "var parent = jq('#" + parent.getUuid() + "');";
|
||||||
|
script += "var billboard = parent.children().first(); ";
|
||||||
|
script += "var div = parent.children().eq(1); ";
|
||||||
|
script += "if (billboard.children().length == 0) {";
|
||||||
|
script += "div.show(); ";
|
||||||
|
script += "billboard.hide(); ";
|
||||||
|
script += "parent.height(div.css('height')); }";
|
||||||
|
script += "else {";
|
||||||
|
script += "div.hide(); ";
|
||||||
|
script += "billboard.show(); ";
|
||||||
|
script += "}";
|
||||||
|
Clients.response(new AuScript(script));
|
||||||
|
}
|
||||||
|
|
||||||
|
ZoomListener listener = new ZoomListener(builder.getQueries(), billboard.getModel());
|
||||||
|
billboard.addEventListener("onDataClick", listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ZoomListener implements EventListener<Event> {
|
||||||
|
private Map<String, MQuery> queries;
|
||||||
|
private org.zkoss.zul.ChartModel model;
|
||||||
|
|
||||||
|
private ZoomListener(Map<String,MQuery> queries, org.zkoss.zul.ChartModel model) {
|
||||||
|
this.queries = queries;
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(Event event) throws Exception {
|
||||||
|
JSONObject json = (JSONObject) event.getData();
|
||||||
|
Number seriesIndex = (Number) json.get("seriesIndex");
|
||||||
|
Number pointIndex = (Number) json.get("pointIndex");
|
||||||
|
if (pointIndex == null)
|
||||||
|
pointIndex = Integer.valueOf(0);
|
||||||
|
|
||||||
|
MQuery query = null;
|
||||||
|
if (model instanceof PieModel) {
|
||||||
|
PieModel pieModel = (PieModel) model;
|
||||||
|
Object category = pieModel.getCategory(pointIndex.intValue());
|
||||||
|
if (category != null)
|
||||||
|
query = queries.get(category.toString());
|
||||||
|
} else if (model instanceof CategoryModel) {
|
||||||
|
CategoryModel categoryModel = (CategoryModel) model;
|
||||||
|
Object series = categoryModel.getSeries(seriesIndex.intValue());
|
||||||
|
Object category = categoryModel.getCategory(pointIndex.intValue());
|
||||||
|
query = queries.get(series.toString()+"__"+category.toString());
|
||||||
|
}
|
||||||
|
if (query != null)
|
||||||
|
AEnv.zoom(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,363 @@
|
||||||
|
/***********************************************************************
|
||||||
|
* This file is part of iDempiere ERP Open Source *
|
||||||
|
* http://www.idempiere.org *
|
||||||
|
* *
|
||||||
|
* Copyright (C) Contributors *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software *
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||||
|
* MA 02110-1301, USA. *
|
||||||
|
* *
|
||||||
|
* Contributors: *
|
||||||
|
* - hengsin *
|
||||||
|
**********************************************************************/
|
||||||
|
package org.idempiere.zk.billboard.chart;
|
||||||
|
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.adempiere.apps.graph.GraphColumn;
|
||||||
|
import org.adempiere.webui.apps.AEnv;
|
||||||
|
import org.adempiere.webui.apps.graph.model.GoalModel;
|
||||||
|
import org.adempiere.webui.apps.graph.model.IndicatorModel;
|
||||||
|
import org.adempiere.webui.component.ZkCssHelper;
|
||||||
|
import org.compiere.model.MColorSchema;
|
||||||
|
import org.compiere.model.MGoal;
|
||||||
|
import org.compiere.model.MQuery;
|
||||||
|
import org.compiere.model.X_PA_Goal;
|
||||||
|
import org.idempiere.zk.billboard.Billboard;
|
||||||
|
import org.zkoss.json.JSONObject;
|
||||||
|
import org.zkoss.zk.ui.event.Event;
|
||||||
|
import org.zkoss.zk.ui.event.EventListener;
|
||||||
|
import org.zkoss.zul.CategoryModel;
|
||||||
|
import org.zkoss.zul.DialModel;
|
||||||
|
import org.zkoss.zul.DialModelRange;
|
||||||
|
import org.zkoss.zul.DialModelScale;
|
||||||
|
import org.zkoss.zul.PieModel;
|
||||||
|
import org.zkoss.zul.SimpleCategoryModel;
|
||||||
|
import org.zkoss.zul.SimplePieModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author hengsin
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class PerformanceGraphBuilder {
|
||||||
|
|
||||||
|
public Billboard createIndicatorChart(IndicatorModel model) {
|
||||||
|
Billboard billboard = new Billboard();
|
||||||
|
billboard.setType("gauge");
|
||||||
|
DialModel dialModel = createDialModel(model);
|
||||||
|
buildDialRendererOptions(billboard, dialModel);
|
||||||
|
billboard.setModel(dialModel);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildDialRendererOptions(Billboard billboard, DialModel dialModel) {
|
||||||
|
DialModelScale dialScale = dialModel.getScale(0);
|
||||||
|
billboard.addRendererOptions("min", 0);
|
||||||
|
billboard.addRendererOptions("max", dialScale.getScaleUpperBound());
|
||||||
|
List<Double> intervals = new ArrayList<Double>();
|
||||||
|
List<String> intervalColors = new ArrayList<String>();
|
||||||
|
for(int i = 0; i < dialScale.rangeSize(); i++) {
|
||||||
|
DialModelRange dialRange = dialScale.getRange(i);
|
||||||
|
double upperBound = dialRange.getUpperBound();
|
||||||
|
intervals.add(upperBound);
|
||||||
|
intervalColors.add(dialRange.getRangeColor());
|
||||||
|
}
|
||||||
|
List<Double> ticks = new ArrayList<Double>(intervals);
|
||||||
|
ticks.add(0, 0d);
|
||||||
|
billboard.addRendererOptions("ticks", ticks.toArray(new Double[0]));
|
||||||
|
billboard.addRendererOptions("intervals", intervals.toArray(new Double[0]));
|
||||||
|
billboard.addRendererOptions("intervalColors", intervalColors.toArray(new String[0]));
|
||||||
|
billboard.addRendererOptions("tickColor", dialScale.getTickColor());
|
||||||
|
billboard.addRendererOptions("background", dialModel.getFrameBgColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
private DialModel createDialModel(IndicatorModel model)
|
||||||
|
{
|
||||||
|
DialModel dialModel = new DialModel();
|
||||||
|
|
||||||
|
MColorSchema colorSchema = model.goalModel.getColorSchema();
|
||||||
|
int upperBound = 0;
|
||||||
|
int [] rangeBounds = new int[]{colorSchema.getMark1Percent(), colorSchema.getMark2Percent(), colorSchema.getMark3Percent(), colorSchema.getMark4Percent()};
|
||||||
|
for (int rangeBound : rangeBounds)
|
||||||
|
{
|
||||||
|
if (rangeBound > upperBound)
|
||||||
|
{
|
||||||
|
if (rangeBound == 9999)
|
||||||
|
{
|
||||||
|
upperBound = (int) Math.floor(upperBound*1.5);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upperBound = rangeBound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DialModelScale dialScale = dialModel.newScale(0, upperBound, 180, -180, colorSchema.getMark2Percent() - colorSchema.getMark1Percent(), 5);
|
||||||
|
int rangeLo = 0;
|
||||||
|
for (int rangeHi : rangeBounds){
|
||||||
|
if (rangeHi==9999)
|
||||||
|
rangeHi = (int) Math.floor(rangeLo*1.5);
|
||||||
|
if (rangeLo < rangeHi) {
|
||||||
|
dialScale.newRange(rangeLo, rangeHi, "#"+ZkCssHelper.createHexColorString(colorSchema.getColor(rangeHi)), 0.5, 0.5);
|
||||||
|
rangeLo = rangeHi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialModel.setFrameBgColor("#"+ZkCssHelper.createHexColorString(model.dialBackground));
|
||||||
|
dialScale.setTickFont(new Font("SansSerif", Font.BOLD, 8));
|
||||||
|
dialScale.setValueFont(new Font("SansSerif", Font.BOLD, 8));
|
||||||
|
dialModel.setFrameFgColor("#000000");
|
||||||
|
dialScale.setTickColor("#"+ZkCssHelper.createHexColorString(model.tickColor));
|
||||||
|
//
|
||||||
|
dialScale.setValue(model.goalModel.getPercent());
|
||||||
|
return dialModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Billboard createPerformanceChart(GoalModel goalModel, int chartWidth, int chartHeight) {
|
||||||
|
if(X_PA_Goal.CHARTTYPE_BarChart.equals(goalModel.chartType))
|
||||||
|
{
|
||||||
|
return createBarChart(goalModel, chartWidth, chartHeight);
|
||||||
|
}
|
||||||
|
else if (X_PA_Goal.CHARTTYPE_PieChart.equals(goalModel.chartType))
|
||||||
|
{
|
||||||
|
return createPieChart(goalModel, chartWidth, chartHeight);
|
||||||
|
}
|
||||||
|
else if (X_PA_Goal.CHARTTYPE_AreaChart.equals(goalModel.chartType))
|
||||||
|
{
|
||||||
|
return createAreaChart(goalModel, chartWidth, chartHeight);
|
||||||
|
}
|
||||||
|
else if (X_PA_Goal.CHARTTYPE_LineChart.equals(goalModel.chartType))
|
||||||
|
{
|
||||||
|
return createLineChart(goalModel, chartWidth, chartHeight);
|
||||||
|
}
|
||||||
|
else if (X_PA_Goal.CHARTTYPE_RingChart.equals(goalModel.chartType))
|
||||||
|
{
|
||||||
|
return createDonutChart(goalModel, chartWidth, chartHeight);
|
||||||
|
}
|
||||||
|
else if (X_PA_Goal.CHARTTYPE_WaterfallChart.equals(goalModel.chartType))
|
||||||
|
{
|
||||||
|
return createWaterfallChart(goalModel, chartWidth, chartHeight);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("unknown chart type=" + goalModel.chartType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createWaterfallChart(GoalModel goalModel, int chartWidth,
|
||||||
|
int chartHeight) {
|
||||||
|
Billboard billboard = new Billboard();
|
||||||
|
billboard.setType("waterfall");
|
||||||
|
CategoryModel chartModel = createWaterfallModel(goalModel);
|
||||||
|
billboard.setModel(chartModel);
|
||||||
|
billboard.setStyle("width: " + chartWidth + "px" +
|
||||||
|
"; height: "+chartHeight+"px");
|
||||||
|
billboard.addEventListener("onDataClick", new ZoomListener(goalModel, chartModel.getCategories().toArray(new Comparable<?>[0])));
|
||||||
|
if (goalModel.showTitle)
|
||||||
|
billboard.setTitle(goalModel.goal.getMeasure().getName());
|
||||||
|
billboard.setTickAxisLabel(goalModel.xAxisLabel);
|
||||||
|
billboard.setValueAxisLabel(goalModel.yAxisLabel);
|
||||||
|
billboard.setLegend(false, false);
|
||||||
|
buildRendererOptions(billboard, goalModel);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createDonutChart(GoalModel goalModel, int chartWidth,
|
||||||
|
int chartHeight) {
|
||||||
|
Billboard billboard = new Billboard();
|
||||||
|
billboard.setType("donut");
|
||||||
|
PieModel chartModel = new SimplePieModel();
|
||||||
|
List<GraphColumn> list = goalModel.columnList;
|
||||||
|
for (int i = 0; i < list.size(); i++){
|
||||||
|
chartModel.setValue(list.get(i).getLabel(), list.get(i).getValue());
|
||||||
|
}
|
||||||
|
billboard.setModel(chartModel);
|
||||||
|
billboard.setStyle("width: " + chartWidth + "px" +
|
||||||
|
"; height: "+chartHeight+"px");
|
||||||
|
billboard.setLegend(true, true);
|
||||||
|
billboard.addEventListener("onDataClick", new ZoomListener(goalModel, chartModel.getCategories().toArray(new Comparable<?>[0])));
|
||||||
|
if (goalModel.showTitle)
|
||||||
|
billboard.setTitle(goalModel.goal.getMeasure().getName());
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createLineChart(GoalModel goalModel, int chartWidth,
|
||||||
|
int chartHeight) {
|
||||||
|
Billboard billboard = new Billboard();
|
||||||
|
billboard.setType("line");
|
||||||
|
CategoryModel chartModel = createCategoryModel(goalModel, true);
|
||||||
|
billboard.setModel(chartModel);
|
||||||
|
billboard.setStyle("width: " + chartWidth + "px" +
|
||||||
|
"; height: "+chartHeight+"px");
|
||||||
|
billboard.addEventListener("onDataClick", new ZoomListener(goalModel, chartModel.getCategories().toArray(new Comparable<?>[0])));
|
||||||
|
if (goalModel.showTitle)
|
||||||
|
billboard.setTitle(goalModel.goal.getMeasure().getName());
|
||||||
|
billboard.setTickAxisLabel(goalModel.xAxisLabel);
|
||||||
|
billboard.setValueAxisLabel(goalModel.yAxisLabel);
|
||||||
|
billboard.setLegend(false, false);
|
||||||
|
buildRendererOptions(billboard, goalModel);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createAreaChart(GoalModel goalModel, int chartWidth,
|
||||||
|
int chartHeight) {
|
||||||
|
Billboard billboard = new Billboard();
|
||||||
|
billboard.setType("area");
|
||||||
|
CategoryModel chartModel = createCategoryModel(goalModel, true);
|
||||||
|
billboard.setModel(chartModel);
|
||||||
|
billboard.setStyle("width: " + chartWidth + "px" +
|
||||||
|
"; height: "+chartHeight+"px");
|
||||||
|
billboard.addEventListener("onDataClick", new ZoomListener(goalModel, chartModel.getCategories().toArray(new Comparable<?>[0])));
|
||||||
|
billboard.setLegend(false, false);
|
||||||
|
if (goalModel.showTitle)
|
||||||
|
billboard.setTitle(goalModel.goal.getMeasure().getName());
|
||||||
|
billboard.setTickAxisLabel(goalModel.xAxisLabel);
|
||||||
|
billboard.setValueAxisLabel(goalModel.yAxisLabel);
|
||||||
|
buildRendererOptions(billboard, goalModel);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createPieChart(GoalModel goalModel, int chartWidth,
|
||||||
|
int chartHeight) {
|
||||||
|
Billboard billboard = new Billboard();
|
||||||
|
billboard.setType("pie");
|
||||||
|
PieModel chartModel = new SimplePieModel();
|
||||||
|
List<GraphColumn> list = goalModel.columnList;
|
||||||
|
for (int i = 0; i < list.size(); i++){
|
||||||
|
chartModel.setValue(list.get(i).getLabel(), list.get(i).getValue());
|
||||||
|
}
|
||||||
|
billboard.setModel(chartModel);
|
||||||
|
billboard.setStyle("width: " + chartWidth + "px" +
|
||||||
|
"; height: "+chartHeight+"px");
|
||||||
|
billboard.addEventListener("onDataClick", new ZoomListener(goalModel, chartModel.getCategories().toArray(new Comparable<?>[0])));
|
||||||
|
billboard.setLegend(true, true);
|
||||||
|
if (goalModel.showTitle)
|
||||||
|
billboard.setTitle(goalModel.goal.getMeasure().getName());
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Billboard createBarChart(final GoalModel goalModel, int chartWidth, int chartHeight) {
|
||||||
|
|
||||||
|
Billboard billboard = new Billboard();
|
||||||
|
billboard.setRenderdefer(500);
|
||||||
|
billboard.setType("bar");
|
||||||
|
CategoryModel chartModel = createCategoryModel(goalModel, true);
|
||||||
|
billboard.setModel(chartModel);
|
||||||
|
billboard.setStyle("width: " + chartWidth + "px" +
|
||||||
|
"; height: "+chartHeight+"px");
|
||||||
|
billboard.addEventListener("onDataClick", new ZoomListener(goalModel, chartModel.getCategories().toArray(new Comparable<?>[0])));
|
||||||
|
if (goalModel.showTitle)
|
||||||
|
billboard.setTitle(goalModel.goal.getMeasure().getName());
|
||||||
|
billboard.setTickAxisLabel(goalModel.xAxisLabel);
|
||||||
|
billboard.setValueAxisLabel(goalModel.yAxisLabel);
|
||||||
|
billboard.setLegend(false, false);
|
||||||
|
buildRendererOptions(billboard, goalModel);
|
||||||
|
return billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CategoryModel createCategoryModel(GoalModel goalModel, boolean linear) {
|
||||||
|
CategoryModel chartModel = new SimpleCategoryModel();
|
||||||
|
List<GraphColumn> list = goalModel.columnList;
|
||||||
|
for (int i = 0; i < list.size(); i++){
|
||||||
|
String series = goalModel.xAxisLabel;
|
||||||
|
if (!linear) {
|
||||||
|
if (list.get(i).getDate() != null) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(list.get(i).getDate());
|
||||||
|
series = Integer.toString(cal.get(Calendar.YEAR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chartModel.setValue(series, list.get(i).getLabel(), list.get(i).getValue());
|
||||||
|
}
|
||||||
|
return chartModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CategoryModel createWaterfallModel(GoalModel goalModel) {
|
||||||
|
return createCategoryModel(goalModel, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildRendererOptions(Billboard billboard, GoalModel goalModel) {
|
||||||
|
List<Double> intervals = new ArrayList<Double>();
|
||||||
|
List<String> intervalColors = new ArrayList<String>();
|
||||||
|
MColorSchema colorSchema = goalModel.goal.getColorSchema();
|
||||||
|
int upperBound = 0;
|
||||||
|
int [] rangeBounds = new int[]{colorSchema.getMark1Percent(), colorSchema.getMark2Percent(), colorSchema.getMark3Percent(), colorSchema.getMark4Percent()};
|
||||||
|
for (int rangeBound : rangeBounds)
|
||||||
|
{
|
||||||
|
if (rangeBound > upperBound)
|
||||||
|
{
|
||||||
|
if (rangeBound == 9999)
|
||||||
|
{
|
||||||
|
upperBound = (int) Math.floor(upperBound*1.5);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upperBound = rangeBound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int rangeLo = 0;
|
||||||
|
for (int rangeHi : rangeBounds){
|
||||||
|
if (rangeHi==9999)
|
||||||
|
rangeHi = (int) Math.floor(rangeLo*1.5);
|
||||||
|
if (rangeLo < rangeHi) {
|
||||||
|
intervals.add(Double.valueOf(rangeHi));
|
||||||
|
intervalColors.add("#"+ZkCssHelper.createHexColorString(colorSchema.getColor(rangeHi)));
|
||||||
|
rangeLo = rangeHi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
billboard.addRendererOptions("intervals", intervals.toArray(new Double[0]));
|
||||||
|
billboard.addRendererOptions("intervalColors", intervalColors.toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ZoomListener implements EventListener<Event> {
|
||||||
|
private GoalModel goalModel;
|
||||||
|
private Comparable<?>[] categories;
|
||||||
|
|
||||||
|
private ZoomListener(GoalModel goalModel, Comparable<?>[] comparables) {
|
||||||
|
this.goalModel = goalModel;
|
||||||
|
this.categories = comparables;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(Event event) throws Exception {
|
||||||
|
JSONObject json = (JSONObject) event.getData();
|
||||||
|
Number pointIndex = (Number) json.get("pointIndex");
|
||||||
|
if (pointIndex == null)
|
||||||
|
pointIndex = Integer.valueOf(0);
|
||||||
|
Comparable<?> categoryLabel = categories[pointIndex.intValue()];
|
||||||
|
List<GraphColumn> list = goalModel.columnList;
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
if (list.get(i).getLabel().equals(categoryLabel)) {
|
||||||
|
zoom(goalModel.goal, list.get(i));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void zoom(MGoal goal, GraphColumn graphColumn) {
|
||||||
|
MQuery query = graphColumn.getMQuery(goal);
|
||||||
|
if (query != null)
|
||||||
|
AEnv.zoom(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>org.idempiere.zk.billboard</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
|
<nature>org.eclipse.pde.PluginNature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
|
@ -0,0 +1,2 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding/<project>=UTF-8
|
|
@ -0,0 +1,4 @@
|
||||||
|
activeProfiles=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
resolveWorkspaceProjects=true
|
||||||
|
version=1
|
|
@ -0,0 +1,3 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
pluginProject.extensions=false
|
||||||
|
resolve.requirebundle=false
|
|
@ -0,0 +1,23 @@
|
||||||
|
Manifest-Version: 1.0
|
||||||
|
Bundle-ManifestVersion: 2
|
||||||
|
Bundle-Name: Billboard
|
||||||
|
Bundle-SymbolicName: org.idempiere.zk.billboard
|
||||||
|
Bundle-Version: 10.0.0.qualifier
|
||||||
|
Bundle-Vendor: iDempiere
|
||||||
|
Automatic-Module-Name: org.idempiere.zk.billboard
|
||||||
|
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||||
|
Export-Package: metainfo.zk,
|
||||||
|
org.idempiere.zk.billboard,
|
||||||
|
web.js.zul.billboard,
|
||||||
|
web.js.zul.billboard.css,
|
||||||
|
web.js.zul.billboard.ext,
|
||||||
|
web.js.zul.billboard.mold
|
||||||
|
Require-Bundle: zcommon,
|
||||||
|
zel,
|
||||||
|
zhtml,
|
||||||
|
zjavassist,
|
||||||
|
zk,
|
||||||
|
zkbind,
|
||||||
|
zkplus,
|
||||||
|
zul,
|
||||||
|
zweb
|
|
@ -0,0 +1,7 @@
|
||||||
|
# org.idempiere.zk.billboard
|
||||||
|
|
||||||
|
1. Wrap https://github.com/naver/billboard.js as zk component.
|
||||||
|
|
||||||
|
2. To update, replace billboard.pkgd.js and billboard.pkgd.src.js with latest billboard.pkgd.js and billboard.pkgd.min.js from https://github.com/naver/billboard.js (Note that due to naming convention of zk, billboard.pkgd.js=billboard.pkgd.min.js and billboard.pkgd.src.js=billboard.pkgd.js ).
|
||||||
|
|
||||||
|
3. To update, replace billboard.css with latest billboard.css from https://github.com/naver/billboard.js. Add !important to padding and text-align of .bb-tooltip th and padding of .bb-tooltip td to fix conflict with zk css
|
|
@ -0,0 +1,4 @@
|
||||||
|
source.. = src/
|
||||||
|
output.. = target/classes/
|
||||||
|
bin.includes = META-INF/,\
|
||||||
|
.
|
|
@ -0,0 +1,12 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.idempiere</groupId>
|
||||||
|
<artifactId>org.idempiere.parent</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../org.idempiere.parent/pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<artifactId>org.idempiere.zk.billboard</artifactId>
|
||||||
|
<packaging>eclipse-plugin</packaging>
|
||||||
|
</project>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<language-addon>
|
||||||
|
<addon-name>billboard</addon-name>
|
||||||
|
<depends>zul</depends>
|
||||||
|
<language-name>xul/html</language-name>
|
||||||
|
<version>
|
||||||
|
<version-class>org.idempiere.zk.billboard.Version</version-class>
|
||||||
|
<version-uid>3.5.1.20220905</version-uid>
|
||||||
|
</version>
|
||||||
|
<component>
|
||||||
|
<component-name>billboard</component-name>
|
||||||
|
<component-class>org.idempiere.zk.billboard.Billboard</component-class>
|
||||||
|
<mold>
|
||||||
|
<mold-name>default</mold-name>
|
||||||
|
<widget-class>zul.billboard.Billboard</widget-class>
|
||||||
|
<mold-uri>mold/billboard.js</mold-uri>
|
||||||
|
</mold>
|
||||||
|
</component>
|
||||||
|
|
||||||
|
<stylesheet href="~./js/zul/billboard/css/billboard.css" type="text/css"/>
|
||||||
|
<javascript-module name="zul.billboard" version="3.5.1.20220905"/>
|
||||||
|
<!-- this js module doesn't actually exists and it is here for billboard.css version -->
|
||||||
|
<javascript-module name="zul.billboard.css" version="3.5.1.20220905"/>
|
||||||
|
</language-addon>
|
|
@ -0,0 +1,469 @@
|
||||||
|
/***********************************************************************
|
||||||
|
* This file is part of iDempiere ERP Open Source *
|
||||||
|
* http://www.idempiere.org *
|
||||||
|
* *
|
||||||
|
* Copyright (C) Contributors *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software *
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||||
|
* MA 02110-1301, USA. *
|
||||||
|
* *
|
||||||
|
* Contributors: *
|
||||||
|
* - hengsin *
|
||||||
|
**********************************************************************/
|
||||||
|
package org.idempiere.zk.billboard;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.zkoss.json.JSONArray;
|
||||||
|
import org.zkoss.json.JSONObject;
|
||||||
|
import org.zkoss.zk.au.AuRequest;
|
||||||
|
import org.zkoss.zk.ui.event.Events;
|
||||||
|
import org.zkoss.zul.CategoryModel;
|
||||||
|
import org.zkoss.zul.ChartModel;
|
||||||
|
import org.zkoss.zul.DialModel;
|
||||||
|
import org.zkoss.zul.PieModel;
|
||||||
|
import org.zkoss.zul.event.ChartDataEvent;
|
||||||
|
import org.zkoss.zul.event.ChartDataListener;
|
||||||
|
import org.zkoss.zul.impl.XulElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author hengsin
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Billboard extends XulElement {
|
||||||
|
/**
|
||||||
|
* generated serial id
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -3888636406033151303L;
|
||||||
|
|
||||||
|
// Must
|
||||||
|
private ChartModel _model;
|
||||||
|
|
||||||
|
private ChartDataListener _dataListener;
|
||||||
|
|
||||||
|
// Optional
|
||||||
|
private String _title = "";
|
||||||
|
private String _type = "line";
|
||||||
|
private String _orient = "vertical";
|
||||||
|
private Map<String, Object> _rendererOptions;
|
||||||
|
private Map<String, Object> _legend;
|
||||||
|
private boolean timeSeries = false;
|
||||||
|
private String timeSeriesInterval = "1 months"; //"1 days", "1 year", "1 weeks"
|
||||||
|
private String timeSeriesFormat = "%b %Y"; //%Y - year, %m - month, %#d - day
|
||||||
|
private String tickAxisLabel = null;
|
||||||
|
private String valueAxisLabel = null;
|
||||||
|
private String[] seriesColors = null;
|
||||||
|
private int xAxisAngle = 0;
|
||||||
|
|
||||||
|
public static final String ON_DATA_CLICK_EVENT = "onDataClick";
|
||||||
|
|
||||||
|
public static final String VERTICAL_ORIENTATION = "vertical";
|
||||||
|
public static final String HORIZONTAL_ORIENTATION = "horizontal";
|
||||||
|
|
||||||
|
// Event Listener
|
||||||
|
static {
|
||||||
|
addClientEvent(Billboard.class, Events.ON_CLICK, CE_IMPORTANT);
|
||||||
|
addClientEvent(Billboard.class, ON_DATA_CLICK_EVENT, CE_IMPORTANT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
|
||||||
|
throws java.io.IOException {
|
||||||
|
super.renderProperties(renderer);
|
||||||
|
|
||||||
|
render(renderer, "type", _type);
|
||||||
|
render(renderer, "title", _title);
|
||||||
|
render(renderer, "orient", _orient);
|
||||||
|
render(renderer, "timeSeries", timeSeries);
|
||||||
|
render(renderer, "xAxisAngle", xAxisAngle);
|
||||||
|
if (timeSeries) {
|
||||||
|
if (timeSeriesInterval != null)
|
||||||
|
render(renderer, "timeSeriesInterval", timeSeriesInterval);
|
||||||
|
if (timeSeriesFormat != null)
|
||||||
|
render(renderer, "timeSeriesFormat", timeSeriesFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
String model = toJSONArray(transferToJSONObject(getModel()));
|
||||||
|
render(renderer, "model", model);
|
||||||
|
|
||||||
|
if (_rendererOptions != null && !_rendererOptions.isEmpty()) {
|
||||||
|
JSONObject jData = mapToJSON(_rendererOptions);
|
||||||
|
render(renderer, "rendererOptions", jData.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_legend != null && !_legend.isEmpty()) {
|
||||||
|
JSONObject jData = mapToJSON(_legend);
|
||||||
|
render(renderer, "legend", jData.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tickAxisLabel != null)
|
||||||
|
render(renderer, "tickAxisLabel", tickAxisLabel);
|
||||||
|
if (valueAxisLabel != null)
|
||||||
|
render(renderer, "valueAxisLabel", valueAxisLabel);
|
||||||
|
|
||||||
|
if (seriesColors != null && seriesColors.length > 0) {
|
||||||
|
JSONArray jData = new JSONArray();
|
||||||
|
for(String s : seriesColors) {
|
||||||
|
jData.add(s);
|
||||||
|
}
|
||||||
|
render(renderer, "seriesColors", jData.toString());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* JSON String Content
|
||||||
|
* "values": "X axis", "Line1":value1, "Line2": value2}
|
||||||
|
* [
|
||||||
|
* {"values":"Q1","'2001'":20,"'2002'":40},
|
||||||
|
* {"values":"Q2","'2001'":35,"'2002'":60},
|
||||||
|
* {"values":"Q3","'2001'":40,"'2002'":70},
|
||||||
|
* {"values":"Q4","'2001'":55,"'2002'":90}
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject mapToJSON(Map<String, Object> map) {
|
||||||
|
JSONObject jData = new JSONObject();
|
||||||
|
for(String key : map.keySet()) {
|
||||||
|
Object value = map.get(key);
|
||||||
|
jData.put(key, value);
|
||||||
|
}
|
||||||
|
return jData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void service(AuRequest request, boolean everError) {
|
||||||
|
if (Events.ON_CLICK.equals(request.getCommand())) {
|
||||||
|
Events.postEvent(Events.ON_CLICK, this, request.getData());
|
||||||
|
} else if (ON_DATA_CLICK_EVENT.equals(request.getCommand())) {
|
||||||
|
Events.postEvent(ON_DATA_CLICK_EVENT, this, request.getData());
|
||||||
|
} else {
|
||||||
|
super.service(request, everError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DefaultChartDataListener implements ChartDataListener, Serializable {
|
||||||
|
private static final long serialVersionUID = 20091125153002L;
|
||||||
|
|
||||||
|
public void onChange(ChartDataEvent event) {
|
||||||
|
invalidate(); // Force redraw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return {@link ChartModel}
|
||||||
|
*/
|
||||||
|
public ChartModel getModel() {
|
||||||
|
return _model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
public void setModel(ChartModel model) {
|
||||||
|
if (_model != model) {
|
||||||
|
if (_model != null)
|
||||||
|
_model.removeChartDataListener(_dataListener);
|
||||||
|
|
||||||
|
_model = model;
|
||||||
|
|
||||||
|
if (_dataListener == null) {
|
||||||
|
_dataListener = new DefaultChartDataListener();
|
||||||
|
_model.addChartDataListener(_dataListener);
|
||||||
|
}
|
||||||
|
invalidate(); // Always redraw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return chart title
|
||||||
|
*/
|
||||||
|
public String getTitle() {
|
||||||
|
return _title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set chart title
|
||||||
|
* @param title
|
||||||
|
*/
|
||||||
|
public void setTitle(String title) {
|
||||||
|
if(!title.equals(this._title)) {
|
||||||
|
this._title = title;
|
||||||
|
smartUpdate("title", _title);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return chart type
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set chart type
|
||||||
|
* @param type
|
||||||
|
*/
|
||||||
|
public void setType(String type) {
|
||||||
|
if(!type.equals(this._type)) {
|
||||||
|
if(isValid(type)) {
|
||||||
|
this._type = type;
|
||||||
|
smartUpdate("type", _type);
|
||||||
|
invalidate(); // Always redraw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return chart orientation (horizontal or vertical)
|
||||||
|
*/
|
||||||
|
public String getOrient() {
|
||||||
|
return _orient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set chart orientation
|
||||||
|
* @param orient
|
||||||
|
*/
|
||||||
|
public void setOrient(String orient) {
|
||||||
|
if(!orient.equals(this._orient)) {
|
||||||
|
this._orient = orient;
|
||||||
|
smartUpdate("orient", _orient);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public void addRendererOptions(String key, Object value) {
|
||||||
|
if (_rendererOptions == null)
|
||||||
|
_rendererOptions = new HashMap<String, Object>();
|
||||||
|
_rendererOptions.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public void addLegendOptions(String key, Object value) {
|
||||||
|
if (_legend == null)
|
||||||
|
_legend = new HashMap<String, Object>();
|
||||||
|
_legend.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private List<JSONObject> transferToJSONObject(ChartModel model) {
|
||||||
|
LinkedList<JSONObject> list = new LinkedList<JSONObject>();
|
||||||
|
|
||||||
|
if (model == null || _type == null)
|
||||||
|
return list;
|
||||||
|
|
||||||
|
if ("gauge".equals(_type)) {
|
||||||
|
DialModel dialModel = (DialModel) model;
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put("value", new double[]{dialModel.getValue(0)});
|
||||||
|
list.add(json);
|
||||||
|
}
|
||||||
|
else if ("pie".equals(_type) || "donut".equals(_type)) {
|
||||||
|
PieModel tempModel = (PieModel) model;
|
||||||
|
for (int i = 0; i < tempModel.getCategories().size(); i++) {
|
||||||
|
Comparable category = tempModel.getCategory(i);
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put("category", category);
|
||||||
|
json.put("value", tempModel.getValue(category));
|
||||||
|
list.add(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
CategoryModel tempModel = (CategoryModel) model;
|
||||||
|
int seriesLength = tempModel.getSeries().size();
|
||||||
|
for (int j = 0; j < seriesLength; j++) {
|
||||||
|
Comparable series = tempModel.getSeries(j);
|
||||||
|
for (int i = 0; i < tempModel.getCategories().size(); i++) {
|
||||||
|
Comparable category = tempModel.getCategory(i);
|
||||||
|
Number value = tempModel.getValue(series, category);
|
||||||
|
if (value != null) {
|
||||||
|
JSONObject jData = new JSONObject();
|
||||||
|
jData.put("category", category);
|
||||||
|
jData.put("series", series);
|
||||||
|
jData.put("value", value != null ? value : 0.00d);
|
||||||
|
list.add(jData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper
|
||||||
|
private static String toJSONArray(List<JSONObject> list) {
|
||||||
|
// list may be null.
|
||||||
|
if (list == null || list.isEmpty())
|
||||||
|
return "";
|
||||||
|
|
||||||
|
final StringBuffer sb = new StringBuffer().append('[');
|
||||||
|
for (Iterator<JSONObject> it = list.iterator(); it.hasNext();) {
|
||||||
|
String s = String.valueOf(it.next());
|
||||||
|
sb.append(s).append(',');
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.length() - 1);
|
||||||
|
sb.append(']');
|
||||||
|
return sb.toString().replaceAll("\\\\", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
//supported chart type
|
||||||
|
private static final List<Object> _VALID_TYPES = Arrays.asList(new Object[] {
|
||||||
|
"pie", "line", "bar", "area", "stacked_bar", "stacked_area", "gauge", "donut", "waterfall"
|
||||||
|
});
|
||||||
|
|
||||||
|
private static boolean isValid(String type) {
|
||||||
|
return _VALID_TYPES.contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default zclass is "z-billboard"
|
||||||
|
*/
|
||||||
|
public String getZclass() {
|
||||||
|
return (this._zclass != null ? this._zclass : "z-billboard");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param show
|
||||||
|
* @param insideGrid
|
||||||
|
*/
|
||||||
|
public void setLegend(boolean show, boolean insideGrid) {
|
||||||
|
if (show) {
|
||||||
|
addLegendOptions("show", Boolean.TRUE);
|
||||||
|
if (insideGrid) {
|
||||||
|
addLegendOptions("placement", "insideGrid");
|
||||||
|
} else {
|
||||||
|
addLegendOptions("placement", "outsideGrid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return x axis label
|
||||||
|
*/
|
||||||
|
public String getTickAxisLabel() {
|
||||||
|
return tickAxisLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set x axis label
|
||||||
|
* @param tickAxisLabel
|
||||||
|
*/
|
||||||
|
public void setTickAxisLabel(String tickAxisLabel) {
|
||||||
|
this.tickAxisLabel = tickAxisLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return y axis label
|
||||||
|
*/
|
||||||
|
public String getValueAxisLabel() {
|
||||||
|
return valueAxisLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set y axis label
|
||||||
|
* @param valueAxisLabel
|
||||||
|
*/
|
||||||
|
public void setValueAxisLabel(String valueAxisLabel) {
|
||||||
|
this.valueAxisLabel = valueAxisLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return true if it is time series chart
|
||||||
|
*/
|
||||||
|
public boolean isTimeSeries() {
|
||||||
|
return timeSeries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param _timeSeries
|
||||||
|
*/
|
||||||
|
public void setTimeSeries(boolean _timeSeries) {
|
||||||
|
this.timeSeries = _timeSeries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return time series interval
|
||||||
|
*/
|
||||||
|
public String getTimeSeriesInterval() {
|
||||||
|
return timeSeriesInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param _timeSeriesInterval
|
||||||
|
*/
|
||||||
|
public void setTimeSeriesInterval(String _timeSeriesInterval) {
|
||||||
|
this.timeSeriesInterval = _timeSeriesInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return time series format
|
||||||
|
*/
|
||||||
|
public String getTimeSeriesFormat() {
|
||||||
|
return timeSeriesFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set time series format
|
||||||
|
* @param timeSeriesFormat
|
||||||
|
*/
|
||||||
|
public void setTimeSeriesFormat(String timeSeriesFormat) {
|
||||||
|
this.timeSeriesFormat = timeSeriesFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getSeriesColors() {
|
||||||
|
return seriesColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSeriesColors(String[] seriesColors) {
|
||||||
|
this.seriesColors = seriesColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getXAxisAngle() {
|
||||||
|
return xAxisAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setXAxisAngle(int xAxisAngle) {
|
||||||
|
this.xAxisAngle = xAxisAngle;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/***********************************************************************
|
||||||
|
* This file is part of iDempiere ERP Open Source *
|
||||||
|
* http://www.idempiere.org *
|
||||||
|
* *
|
||||||
|
* Copyright (C) Contributors *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software *
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||||
|
* MA 02110-1301, USA. *
|
||||||
|
* *
|
||||||
|
* Contributors: *
|
||||||
|
* - hengsin *
|
||||||
|
**********************************************************************/
|
||||||
|
package org.idempiere.zk.billboard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author hengsin
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Version {
|
||||||
|
/** Returns the version UID.
|
||||||
|
*/
|
||||||
|
public static final String UID = "3.5.1.20220905";
|
||||||
|
}
|
|
@ -0,0 +1,270 @@
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
var Billboard =
|
||||||
|
zul.billboard.Billboard = zk.$extends(zk.Widget,
|
||||||
|
{
|
||||||
|
|
||||||
|
_title : '',
|
||||||
|
_type : 'line',
|
||||||
|
|
||||||
|
_cursor : false,
|
||||||
|
_highlighter : true,
|
||||||
|
_dataClickTS : 0,
|
||||||
|
|
||||||
|
$define : {
|
||||||
|
title: null,
|
||||||
|
type: null,
|
||||||
|
|
||||||
|
width: null,
|
||||||
|
height: null,
|
||||||
|
|
||||||
|
model : null,
|
||||||
|
series : null,
|
||||||
|
seriesData : null,
|
||||||
|
seriesDefaults : null,
|
||||||
|
seriesColors: null,
|
||||||
|
axes : null,
|
||||||
|
ticks : null,
|
||||||
|
tickAxisLabel: null,
|
||||||
|
valueAxisLabel: null,
|
||||||
|
|
||||||
|
orient : null,
|
||||||
|
rendererOptions: null,
|
||||||
|
legend: null,
|
||||||
|
timeSeries: null,
|
||||||
|
timeSeriesInterval: null,
|
||||||
|
timeSeriesFormat: null,
|
||||||
|
xAxisAngle: null
|
||||||
|
},
|
||||||
|
|
||||||
|
_dataPrepare : function() {
|
||||||
|
var dataModel = this.getModel();
|
||||||
|
var data = [];
|
||||||
|
try {
|
||||||
|
data = jq.evalJSON(dataModel);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
if (typeof data == "undefined") {
|
||||||
|
data = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this phase, we need to decide following variables
|
||||||
|
var seriesData = [];
|
||||||
|
var ticks = [];
|
||||||
|
|
||||||
|
// Start data prepare
|
||||||
|
if( this.getType() == 'gauge') {
|
||||||
|
seriesData.push("data");
|
||||||
|
for ( var i = 0, len = data.length; i < len; i++) {
|
||||||
|
seriesData.push(data[i]['value']);
|
||||||
|
}
|
||||||
|
} else if( this.getType() == 'pie' || this.getType() == 'donut') {
|
||||||
|
for ( var i = 0, len = data.length; i < len; i++) {
|
||||||
|
seriesData.push([data[i]['category'], data[i]['value']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
seriesData = [ seriesData ];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var seriesMap = new Array();
|
||||||
|
for ( var i = 0, len = data.length; i < len; i++) {
|
||||||
|
|
||||||
|
var current = data[i];
|
||||||
|
var seriesIndex = -1;
|
||||||
|
var seriesLabel = current['series'];
|
||||||
|
var categoryLabel = current['category'];
|
||||||
|
var categoryValue = current['value'];
|
||||||
|
var seriesIndex = seriesMap.indexOf(seriesLabel);
|
||||||
|
if (seriesIndex < 0) {
|
||||||
|
seriesMap.push(seriesLabel);
|
||||||
|
seriesIndex = seriesMap.length-1;
|
||||||
|
}
|
||||||
|
// Initial Array
|
||||||
|
if(!seriesData[seriesIndex]) {
|
||||||
|
seriesData[seriesIndex] = new Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seriesData[seriesIndex].length == 0)
|
||||||
|
seriesData[seriesIndex].push(seriesLabel);
|
||||||
|
seriesData[seriesIndex].push({category: categoryLabel, value: categoryValue});
|
||||||
|
}
|
||||||
|
|
||||||
|
var seriesLabel = new Array();
|
||||||
|
for (var i=0; i<seriesMap.length; i++) {
|
||||||
|
var label = seriesMap[i];
|
||||||
|
seriesLabel.push({label: label});
|
||||||
|
}
|
||||||
|
this.setSeries(seriesLabel);
|
||||||
|
}
|
||||||
|
// End data prepare
|
||||||
|
|
||||||
|
this.setSeriesData(seriesData);
|
||||||
|
this.setTicks(ticks);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_chartPrepare : function() {
|
||||||
|
|
||||||
|
var wgt = this;
|
||||||
|
// In this phase, we need to decide following variables
|
||||||
|
var axes = {};
|
||||||
|
var seriesDefaults = {};
|
||||||
|
|
||||||
|
seriesDefaults.rendererOptions = {};
|
||||||
|
// Start chart prepare
|
||||||
|
if (this.getRendererOptions()) {
|
||||||
|
var options = jq.evalJSON(this.getRendererOptions());
|
||||||
|
if (seriesDefaults.rendererOptions)
|
||||||
|
seriesDefaults.rendererOptions = jQuery.extend({}, seriesDefaults.rendererOptions, options);
|
||||||
|
else
|
||||||
|
seriesDefaults.rendererOptions = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal or Vertical ?
|
||||||
|
if(this.getType() != 'pie' && this.getType() != 'gauge' && this.getType() != 'donut') {
|
||||||
|
var axisRenderer = this.getTimeSeries() ? "timeseries" : "category";
|
||||||
|
|
||||||
|
// Vertical
|
||||||
|
axes.xaxis = {
|
||||||
|
renderer : axisRenderer ,
|
||||||
|
ticks: wgt.getTicks()
|
||||||
|
};
|
||||||
|
if (this.getTimeSeries()) {
|
||||||
|
axes.xaxis.tickInterval = this.getTimeSeriesInterval();
|
||||||
|
axes.xaxis.tickOptions = {formatString: this.getTimeSeriesFormat()};
|
||||||
|
}
|
||||||
|
if (this.getXAxisAngle() != 0) {
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
axes.xaxis.tickOptions.angle = this.getXAxisAngle();
|
||||||
|
} else {
|
||||||
|
axes.xaxis.tickOptions = {
|
||||||
|
angle: this.getXAxisAngle()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
axes.yaxis = {};
|
||||||
|
if (this.getTickAxisLabel()) {
|
||||||
|
axes.xaxis.label = this.getTickAxisLabel();
|
||||||
|
}
|
||||||
|
if (this.getValueAxisLabel()) {
|
||||||
|
axes.yaxis.label = this.getValueAxisLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.getOrient() == 'horizontal') {
|
||||||
|
axes.rotated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// End chart prepare
|
||||||
|
|
||||||
|
this.setAxes(axes);
|
||||||
|
this.setSeriesDefaults(seriesDefaults);
|
||||||
|
},
|
||||||
|
|
||||||
|
_chartPlot : function() {
|
||||||
|
var wgt = this;
|
||||||
|
var legend = this.getLegend() ? jq.evalJSON(this.getLegend()) : null;
|
||||||
|
var seriesColors = this.getSeriesColors() ? jq.evalJSON(this.getSeriesColors()) : null;
|
||||||
|
var nodata = false;
|
||||||
|
if (typeof wgt.getSeriesData() == "undefined" || wgt.getSeriesData() == null) {
|
||||||
|
nodata = true;
|
||||||
|
} else if (wgt.getSeriesData().length == 0){
|
||||||
|
nodata = true;
|
||||||
|
} else if (Object.prototype.toString.call(wgt.getSeriesData()[0]) === '[object Array]') {
|
||||||
|
var count = 0;
|
||||||
|
for(var i = 0; i < wgt.getSeriesData().length; i++) {
|
||||||
|
count = count + wgt.getSeriesData()[i].length;
|
||||||
|
}
|
||||||
|
nodata = (count == 0);
|
||||||
|
}
|
||||||
|
if (!nodata) {
|
||||||
|
var c = zk.$import('zul.billboard.Billboard');
|
||||||
|
if (c._renderers[wgt._type]) {
|
||||||
|
var model = c._renderers[wgt._type].render(wgt);
|
||||||
|
if (legend) {
|
||||||
|
if (legend.show == true) {
|
||||||
|
model.legend.show = true;
|
||||||
|
} else if (legend.show == false) {
|
||||||
|
model.legend.show = true;
|
||||||
|
}
|
||||||
|
if (legend.placement) {
|
||||||
|
if (legend.placement == "insideGrid") {
|
||||||
|
model.legend.position = 'inset';
|
||||||
|
} else if (legend.placement == "outsideGrid") {
|
||||||
|
if (legend.location)
|
||||||
|
model.legend.position = legend.location;
|
||||||
|
else
|
||||||
|
model.legend.position = 'bottom';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (seriesColors) {
|
||||||
|
if (model.data.groups) {
|
||||||
|
var grparr = model.data.groups[0];
|
||||||
|
var colors = {};
|
||||||
|
for(var i = 0; i < seriesColors.length && i < grparr.length; i++) {
|
||||||
|
colors[grparr[i]] = seriesColors[i];
|
||||||
|
}
|
||||||
|
model.data["colors"] = colors;
|
||||||
|
} else if (wgt.getSeries()) {
|
||||||
|
var colors = {};
|
||||||
|
var seriesarr = wgt.getSeries();
|
||||||
|
for(var i = 0; i < seriesColors.length && i < seriesarr.length; i++) {
|
||||||
|
colors[seriesarr[i]] = seriesColors[i];
|
||||||
|
}
|
||||||
|
model.data["colors"] = colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var chart = bb.generate(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
bind_ : function() {
|
||||||
|
|
||||||
|
this.$supers(Billboard, 'bind_', arguments);
|
||||||
|
|
||||||
|
// Step 1
|
||||||
|
this._dataPrepare();
|
||||||
|
|
||||||
|
// Step 2
|
||||||
|
this._chartPrepare();
|
||||||
|
|
||||||
|
// Step 3
|
||||||
|
this._chartPlot();
|
||||||
|
},
|
||||||
|
|
||||||
|
unbind_ : function() {
|
||||||
|
this.$supers(Billboard, 'unbind_', arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
doClick_ : function(event) {
|
||||||
|
var ts = new Date().getTime();
|
||||||
|
if ((ts - this._dataClickTS) > 500)
|
||||||
|
this.$supers(Billboard, 'doClick_', arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
isBarType : function() {
|
||||||
|
return this._type == 'bar' || this._type == 'stacked_bar' || this._type == 'waterfall';
|
||||||
|
},
|
||||||
|
|
||||||
|
getCursor : function() {
|
||||||
|
if(this._cursor) {
|
||||||
|
return { show: true, followMouse: true,
|
||||||
|
showTooltip: true, tooltipLocation:'sw', style: 'pointer'};
|
||||||
|
} else {
|
||||||
|
return { show: false };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setCursor : function(val) {
|
||||||
|
this._cursor = val;
|
||||||
|
},
|
||||||
|
|
||||||
|
getZclass : function() {
|
||||||
|
return this._zclass != null ? this._zclass : "z-billboard";
|
||||||
|
}
|
||||||
|
|
||||||
|
}, {_renderers : {}});
|
||||||
|
})();
|
|
@ -0,0 +1,235 @@
|
||||||
|
/*!
|
||||||
|
* Copyright (c) 2017 ~ present NAVER Corp.
|
||||||
|
* billboard.js project is licensed under the MIT license
|
||||||
|
*
|
||||||
|
* billboard.js, JavaScript chart library
|
||||||
|
* https://naver.github.io/billboard.js/
|
||||||
|
*
|
||||||
|
* @version 3.5.1
|
||||||
|
*/
|
||||||
|
/*-- Chart --*/
|
||||||
|
.bb svg {
|
||||||
|
font: 10px sans-serif;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
|
||||||
|
|
||||||
|
.bb path, .bb line {
|
||||||
|
fill: none;
|
||||||
|
stroke: #000; }
|
||||||
|
|
||||||
|
.bb text, .bb .bb-button {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none; }
|
||||||
|
|
||||||
|
.bb-legend-item-tile,
|
||||||
|
.bb-xgrid-focus,
|
||||||
|
.bb-ygrid-focus,
|
||||||
|
.bb-ygrid,
|
||||||
|
.bb-event-rect,
|
||||||
|
.bb-bars path {
|
||||||
|
shape-rendering: crispEdges; }
|
||||||
|
|
||||||
|
.bb-chart-arc .bb-gauge-value {
|
||||||
|
fill: #000; }
|
||||||
|
|
||||||
|
.bb-chart-arc path {
|
||||||
|
stroke: #fff; }
|
||||||
|
|
||||||
|
.bb-chart-arc rect {
|
||||||
|
stroke: #fff;
|
||||||
|
stroke-width: 1; }
|
||||||
|
|
||||||
|
.bb-chart-arc text {
|
||||||
|
fill: #fff;
|
||||||
|
font-size: 13px; }
|
||||||
|
|
||||||
|
/*-- Axis --*/
|
||||||
|
.bb-axis {
|
||||||
|
shape-rendering: crispEdges; }
|
||||||
|
|
||||||
|
/*-- Grid --*/
|
||||||
|
.bb-grid {
|
||||||
|
pointer-events: none; }
|
||||||
|
.bb-grid line {
|
||||||
|
stroke: #aaa; }
|
||||||
|
.bb-grid text {
|
||||||
|
fill: #aaa; }
|
||||||
|
|
||||||
|
.bb-xgrid, .bb-ygrid {
|
||||||
|
stroke-dasharray: 3 3; }
|
||||||
|
|
||||||
|
/*-- Text on Chart --*/
|
||||||
|
.bb-text.bb-empty {
|
||||||
|
fill: #808080;
|
||||||
|
font-size: 2em; }
|
||||||
|
|
||||||
|
/*-- Line --*/
|
||||||
|
.bb-line {
|
||||||
|
stroke-width: 1px; }
|
||||||
|
|
||||||
|
/*-- Point --*/
|
||||||
|
.bb-circle._expanded_ {
|
||||||
|
stroke-width: 1px;
|
||||||
|
stroke: white; }
|
||||||
|
|
||||||
|
.bb-selected-circle {
|
||||||
|
fill: white;
|
||||||
|
stroke-width: 2px; }
|
||||||
|
|
||||||
|
/*-- Bar --*/
|
||||||
|
.bb-bar {
|
||||||
|
stroke-width: 0; }
|
||||||
|
.bb-bar._expanded_ {
|
||||||
|
fill-opacity: 0.75; }
|
||||||
|
|
||||||
|
/*-- Candlestick --*/
|
||||||
|
.bb-candlestick {
|
||||||
|
stroke-width: 1px; }
|
||||||
|
.bb-candlestick._expanded_ {
|
||||||
|
fill-opacity: 0.75; }
|
||||||
|
|
||||||
|
/*-- Focus --*/
|
||||||
|
.bb-target.bb-focused, .bb-circles.bb-focused {
|
||||||
|
opacity: 1; }
|
||||||
|
|
||||||
|
.bb-target.bb-focused path.bb-line, .bb-target.bb-focused path.bb-step, .bb-circles.bb-focused path.bb-line, .bb-circles.bb-focused path.bb-step {
|
||||||
|
stroke-width: 2px; }
|
||||||
|
|
||||||
|
.bb-target.bb-defocused, .bb-circles.bb-defocused {
|
||||||
|
opacity: 0.3 !important; }
|
||||||
|
.bb-target.bb-defocused .text-overlapping, .bb-circles.bb-defocused .text-overlapping {
|
||||||
|
opacity: .05 !important; }
|
||||||
|
|
||||||
|
/*-- Region --*/
|
||||||
|
.bb-region {
|
||||||
|
fill: steelblue;
|
||||||
|
fill-opacity: .1; }
|
||||||
|
|
||||||
|
/*-- Zoom region --*/
|
||||||
|
.bb-zoom-brush {
|
||||||
|
fill-opacity: .1; }
|
||||||
|
|
||||||
|
/*-- Brush --*/
|
||||||
|
.bb-brush .extent {
|
||||||
|
fill-opacity: .1; }
|
||||||
|
|
||||||
|
/*-- Legend --*/
|
||||||
|
.bb-legend-item {
|
||||||
|
font-size: 12px;
|
||||||
|
user-select: none; }
|
||||||
|
|
||||||
|
.bb-legend-item-hidden {
|
||||||
|
opacity: 0.15; }
|
||||||
|
|
||||||
|
.bb-legend-background {
|
||||||
|
opacity: 0.75;
|
||||||
|
fill: white;
|
||||||
|
stroke: lightgray;
|
||||||
|
stroke-width: 1; }
|
||||||
|
|
||||||
|
/*-- Title --*/
|
||||||
|
.bb-title {
|
||||||
|
font: 14px sans-serif; }
|
||||||
|
|
||||||
|
/*-- Tooltip --*/
|
||||||
|
.bb-tooltip-container {
|
||||||
|
z-index: 10;
|
||||||
|
user-select: none; }
|
||||||
|
|
||||||
|
.bb-tooltip {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
empty-cells: show;
|
||||||
|
opacity: 0.9;
|
||||||
|
-webkit-box-shadow: 7px 7px 12px -9px #777777;
|
||||||
|
-moz-box-shadow: 7px 7px 12px -9px #777777;
|
||||||
|
box-shadow: 7px 7px 12px -9px #777777; }
|
||||||
|
.bb-tooltip tr {
|
||||||
|
border: 1px solid #CCC; }
|
||||||
|
.bb-tooltip th {
|
||||||
|
background-color: #aaa;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 2px 5px !important;
|
||||||
|
text-align: left !important;
|
||||||
|
color: #FFF; }
|
||||||
|
.bb-tooltip td {
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 3px 6px !important;
|
||||||
|
background-color: #fff;
|
||||||
|
border-left: 1px dotted #999; }
|
||||||
|
.bb-tooltip td > span, .bb-tooltip td > svg {
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
margin-right: 6px; }
|
||||||
|
.bb-tooltip.value {
|
||||||
|
text-align: right; }
|
||||||
|
|
||||||
|
/*-- Area --*/
|
||||||
|
.bb-area {
|
||||||
|
stroke-width: 0;
|
||||||
|
opacity: 0.2; }
|
||||||
|
|
||||||
|
/*-- Arc --*/
|
||||||
|
.bb-chart-arcs-title {
|
||||||
|
dominant-baseline: middle;
|
||||||
|
font-size: 1.3em; }
|
||||||
|
|
||||||
|
text.bb-chart-arcs-gauge-title {
|
||||||
|
dominant-baseline: middle;
|
||||||
|
font-size: 2.7em; }
|
||||||
|
|
||||||
|
.bb-chart-arcs {
|
||||||
|
/*-- Polar --*/ }
|
||||||
|
.bb-chart-arcs .bb-chart-arcs-background {
|
||||||
|
fill: #e0e0e0;
|
||||||
|
stroke: #fff; }
|
||||||
|
.bb-chart-arcs .bb-chart-arcs-gauge-unit {
|
||||||
|
fill: #000;
|
||||||
|
font-size: 16px; }
|
||||||
|
.bb-chart-arcs .bb-chart-arcs-gauge-max {
|
||||||
|
fill: #777; }
|
||||||
|
.bb-chart-arcs .bb-chart-arcs-gauge-min {
|
||||||
|
fill: #777; }
|
||||||
|
.bb-chart-arcs .bb-levels circle {
|
||||||
|
fill: none;
|
||||||
|
stroke: #848282;
|
||||||
|
stroke-width: .5px; }
|
||||||
|
.bb-chart-arcs .bb-levels text {
|
||||||
|
fill: #848282; }
|
||||||
|
|
||||||
|
/*-- Radar --*/
|
||||||
|
.bb-chart-radars .bb-levels polygon {
|
||||||
|
fill: none;
|
||||||
|
stroke: #848282;
|
||||||
|
stroke-width: .5px; }
|
||||||
|
|
||||||
|
.bb-chart-radars .bb-levels text {
|
||||||
|
fill: #848282; }
|
||||||
|
|
||||||
|
.bb-chart-radars .bb-axis line {
|
||||||
|
stroke: #848282;
|
||||||
|
stroke-width: .5px; }
|
||||||
|
|
||||||
|
.bb-chart-radars .bb-axis text {
|
||||||
|
font-size: 1.15em;
|
||||||
|
cursor: default; }
|
||||||
|
|
||||||
|
.bb-chart-radars .bb-shapes polygon {
|
||||||
|
fill-opacity: .2;
|
||||||
|
stroke-width: 1px; }
|
||||||
|
|
||||||
|
/*-- Button --*/
|
||||||
|
.bb-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px; }
|
||||||
|
.bb-button .bb-zoom-reset {
|
||||||
|
font-size: 11px;
|
||||||
|
border: solid 1px #ccc;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer; }
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
var billboard = billboard || {};
|
||||||
|
|
||||||
|
billboard.AreaRenderer = function() {};
|
||||||
|
|
||||||
|
billboard.AreaRenderer.prototype.render = function(wgt) {
|
||||||
|
var columns = [];
|
||||||
|
var categories = new Array();
|
||||||
|
wgt.getSeriesData().forEach((x, i) => {
|
||||||
|
x.forEach((y, j) => {
|
||||||
|
if (y.category) {
|
||||||
|
columns[columns.length-1].push(y.value);
|
||||||
|
if (!categories.indexOf[y.category])
|
||||||
|
categories.push(y.category);
|
||||||
|
} else {
|
||||||
|
var values = new Array();
|
||||||
|
columns.push(values);
|
||||||
|
values.push(y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var color = {};
|
||||||
|
var area = {};
|
||||||
|
var background = {};
|
||||||
|
var rendererOptions = wgt._rendererOptions ? jq.evalJSON(wgt._rendererOptions) : null;
|
||||||
|
if (rendererOptions) {
|
||||||
|
if (rendererOptions["intervalColors"]) {
|
||||||
|
color["pattern"] = new Array();
|
||||||
|
rendererOptions["intervalColors"].forEach((x, i) => color["pattern"].push(x));
|
||||||
|
}
|
||||||
|
if (rendererOptions["intervals"]) {
|
||||||
|
color["threshold"] = {values: []};
|
||||||
|
rendererOptions["intervals"].forEach((x, i) => color["threshold"]["values"].push(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendererOptions["background"]) {
|
||||||
|
background["color"] = rendererOptions["background"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var x = {tick: {}};
|
||||||
|
var rotated = false;
|
||||||
|
var axes = wgt.getAxes();
|
||||||
|
if (axes.rotated)
|
||||||
|
rotated = axes.rotated;
|
||||||
|
if (axes.xaxis.renderer == "timeseries") {
|
||||||
|
x["type"] = "timeseries";
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
x["tick"]["format"] = axes.xaxis.tickOptions.formatString;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x["type"] = "category";
|
||||||
|
}
|
||||||
|
if (x["type"] == "category") {
|
||||||
|
if (categories.length > 0) {
|
||||||
|
x["categories"] = [];
|
||||||
|
categories.forEach((v, i) => x["categories"].push(v));
|
||||||
|
}
|
||||||
|
} else if (x["type"] == "timeseries") {
|
||||||
|
var ts = new Array();
|
||||||
|
ts.push("x");
|
||||||
|
categories.forEach((s, i) => ts.push(s));
|
||||||
|
columns.push(ts);
|
||||||
|
x["tick"]["fit"] = true;
|
||||||
|
}
|
||||||
|
x["clipPath"] = false;
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
if (axes.xaxis.tickOptions.angle) {
|
||||||
|
if (axes.xaxis.tickOptions.angle != 0) {
|
||||||
|
x["tick"]["rotate"] = axes.xaxis.tickOptions.angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = {
|
||||||
|
bindto: "#"+wgt.$n().id,
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type: wgt._type,
|
||||||
|
onclick: function(d, e) {
|
||||||
|
var i = 0;
|
||||||
|
var si = 0;
|
||||||
|
for(var s in wgt.getSeries()) {
|
||||||
|
if (s == d.id) {
|
||||||
|
si = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
wgt._dataClickTS = new Date().getTime();
|
||||||
|
wgt.fire("onDataClick", {
|
||||||
|
seriesIndex : si,
|
||||||
|
pointIndex : d.index,
|
||||||
|
data : d.value,
|
||||||
|
ticks : wgt.getTicks()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: color,
|
||||||
|
area: area,
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
doNotHide: false,
|
||||||
|
grouped: false,
|
||||||
|
contents: function(d, defaultTitleFormat, defaultValueFormat, color) {
|
||||||
|
var c = d[0];
|
||||||
|
var h = '<table class="bb-tooltip"><tbody><tr><th>';
|
||||||
|
h = h + defaultTitleFormat(c.x);
|
||||||
|
h = h + '</th></tr><tr class="bb-tooltip-name-data"><td class="value">';
|
||||||
|
h = h + c.value + '</td></tr></tbody></table>';
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {show: false},
|
||||||
|
axis: {
|
||||||
|
x: x,
|
||||||
|
rotated: rotated
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
x: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
front: false,
|
||||||
|
focus: {
|
||||||
|
show: false,
|
||||||
|
|
||||||
|
// Below options are available when 'tooltip.grouped=false' option is set
|
||||||
|
edge: true,
|
||||||
|
y: true
|
||||||
|
},
|
||||||
|
lines: {
|
||||||
|
front: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
right: 30
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (x["type"] == "timeseries") {
|
||||||
|
model["data"]["x"] = "x";
|
||||||
|
}
|
||||||
|
if (wgt.getTitle())
|
||||||
|
model["title"] = {text: wgt.getTitle()};
|
||||||
|
if (wgt.getTickAxisLabel())
|
||||||
|
model["axis"]["x"]["label"] = {text: wgt.getTickAxisLabel(), position: "outer-right"};
|
||||||
|
if (wgt.getValueAxisLabel())
|
||||||
|
model["axis"]["y"] = { label: {text: wgt.getValueAxisLabel(), position: "outer-top"} };
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
zul.billboard.Billboard._renderers["area"] = new billboard.AreaRenderer();
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
var billboard = billboard || {};
|
||||||
|
|
||||||
|
billboard.BarRenderer = function() {};
|
||||||
|
|
||||||
|
billboard.BarRenderer.prototype.render = function(wgt) {
|
||||||
|
var columns = [];
|
||||||
|
var categories = new Array();
|
||||||
|
wgt.getSeriesData().forEach((x, i) => {
|
||||||
|
x.forEach((y, j) => {
|
||||||
|
if (y.category) {
|
||||||
|
columns[columns.length-1].push(y.value);
|
||||||
|
if (!categories.indexOf[y.category])
|
||||||
|
categories.push(y.category);
|
||||||
|
} else {
|
||||||
|
var values = new Array();
|
||||||
|
columns.push(values);
|
||||||
|
values.push(y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var color = {};
|
||||||
|
var bar = {};
|
||||||
|
var background = {};
|
||||||
|
var rendererOptions = wgt._rendererOptions ? jq.evalJSON(wgt._rendererOptions) : null;
|
||||||
|
if (rendererOptions) {
|
||||||
|
if (rendererOptions["intervalColors"]) {
|
||||||
|
color["pattern"] = new Array();
|
||||||
|
rendererOptions["intervalColors"].forEach((x, i) => color["pattern"].push(x));
|
||||||
|
}
|
||||||
|
if (rendererOptions["intervals"]) {
|
||||||
|
color["threshold"] = {values: []};
|
||||||
|
rendererOptions["intervals"].forEach((x, i) => color["threshold"]["values"].push(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendererOptions["background"]) {
|
||||||
|
background["color"] = rendererOptions["background"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var x = {tick: {}};
|
||||||
|
var rotated = false;
|
||||||
|
var axes = wgt.getAxes();
|
||||||
|
if (axes.rotated)
|
||||||
|
rotated = axes.rotated;
|
||||||
|
if (axes.xaxis.renderer == "timeseries") {
|
||||||
|
x["type"] = "timeseries";
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
x["tick"]["format"] = axes.xaxis.tickOptions.formatString;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x["type"] = "category";
|
||||||
|
}
|
||||||
|
if (x["type"] == "category") {
|
||||||
|
if (categories.length > 0) {
|
||||||
|
x["categories"] = [];
|
||||||
|
categories.forEach((v, i) => x["categories"].push(v));
|
||||||
|
}
|
||||||
|
} else if (x["type"] == "timeseries") {
|
||||||
|
var ts = new Array();
|
||||||
|
ts.push("x");
|
||||||
|
categories.forEach((s, i) => ts.push(s));
|
||||||
|
columns.push(ts);
|
||||||
|
x["tick"]["fit"] = true;
|
||||||
|
}
|
||||||
|
x["clipPath"] = false;
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
if (axes.xaxis.tickOptions.angle) {
|
||||||
|
if (axes.xaxis.tickOptions.angle != 0) {
|
||||||
|
x["tick"]["rotate"] = axes.xaxis.tickOptions.angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = {
|
||||||
|
bindto: "#"+wgt.$n().id,
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type: wgt._type,
|
||||||
|
onclick: function(d, e) {
|
||||||
|
var i = 0;
|
||||||
|
var si = 0;
|
||||||
|
for(var s in wgt.getSeries()) {
|
||||||
|
if (s == d.id) {
|
||||||
|
si = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
wgt._dataClickTS = new Date().getTime();
|
||||||
|
wgt.fire("onDataClick", {
|
||||||
|
seriesIndex : si,
|
||||||
|
pointIndex : d.index,
|
||||||
|
data : d.value,
|
||||||
|
ticks : wgt.getTicks()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: color,
|
||||||
|
bar: bar,
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
doNotHide: false,
|
||||||
|
grouped: false,
|
||||||
|
contents: function(d, defaultTitleFormat, defaultValueFormat, color) {
|
||||||
|
var c = d[0];
|
||||||
|
var h = '<table class="bb-tooltip"><tbody><tr><th>';
|
||||||
|
h = h + defaultTitleFormat(c.x);
|
||||||
|
h = h + '</th></tr><tr class="bb-tooltip-name-data"><td class="value">';
|
||||||
|
h = h + c.value + '</td></tr></tbody></table>';
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {show: false},
|
||||||
|
axis: {
|
||||||
|
x: x,
|
||||||
|
rotated: rotated
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
x: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
front: false,
|
||||||
|
focus: {
|
||||||
|
show: false,
|
||||||
|
|
||||||
|
// Below options are available when 'tooltip.grouped=false' option is set
|
||||||
|
edge: true,
|
||||||
|
y: true
|
||||||
|
},
|
||||||
|
lines: {
|
||||||
|
front: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
right: 30
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (x["type"] == "timeseries") {
|
||||||
|
model["data"]["x"] = "x";
|
||||||
|
}
|
||||||
|
if (wgt.getTitle())
|
||||||
|
model["title"] = {text: wgt.getTitle()};
|
||||||
|
if (wgt.getTickAxisLabel())
|
||||||
|
model["axis"]["x"]["label"] = {text: wgt.getTickAxisLabel(), position: "outer-right"};
|
||||||
|
if (wgt.getValueAxisLabel())
|
||||||
|
model["axis"]["y"] = { label: {text: wgt.getValueAxisLabel(), position: "outer-top"} };
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
zul.billboard.Billboard._renderers["bar"] = new billboard.BarRenderer();
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
var billboard = billboard || {};
|
||||||
|
|
||||||
|
billboard.DonutRenderer = function() {};
|
||||||
|
|
||||||
|
billboard.DonutRenderer.prototype.render = function(wgt) {
|
||||||
|
var columns = wgt.getSeriesData()[0];
|
||||||
|
var color = {};
|
||||||
|
var donut = {};
|
||||||
|
var background = {};
|
||||||
|
var rendererOptions = wgt._rendererOptions ? jq.evalJSON(wgt._rendererOptions) : null;
|
||||||
|
if (rendererOptions) {
|
||||||
|
if (rendererOptions["intervalColors"]) {
|
||||||
|
color["pattern"] = new Array();
|
||||||
|
rendererOptions["intervalColors"].forEach((x, i) => color["pattern"].push(x));
|
||||||
|
}
|
||||||
|
if (rendererOptions["intervals"]) {
|
||||||
|
color["threshold"] = {values: []};
|
||||||
|
rendererOptions["intervals"].forEach((x, i) => color["threshold"]["values"].push(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendererOptions["background"]) {
|
||||||
|
background["color"] = rendererOptions["background"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = {
|
||||||
|
bindto: "#"+wgt.$n().id,
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type: wgt._type,
|
||||||
|
onclick: function(d, e) {
|
||||||
|
wgt._dataClickTS = new Date().getTime();
|
||||||
|
wgt.fire("onDataClick", {
|
||||||
|
seriesIndex : 0,
|
||||||
|
pointIndex : d.index,
|
||||||
|
data : d.value,
|
||||||
|
ticks : wgt.getTicks()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: color,
|
||||||
|
donut: donut,
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
doNotHide: false,
|
||||||
|
grouped: false,
|
||||||
|
format: {
|
||||||
|
title: function(x) { return ""; },
|
||||||
|
name: function(name, ratio, id, index) { return name; },
|
||||||
|
value: function(value, ratio, id, index) { return value; }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {show: true}
|
||||||
|
};
|
||||||
|
if (wgt.getTitle())
|
||||||
|
model["donut"]["title"] = wgt.getTitle();
|
||||||
|
//model["title"] = {text: wgt.getTitle()};
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
zul.billboard.Billboard._renderers["donut"] = new billboard.DonutRenderer();
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
var billboard = billboard || {};
|
||||||
|
|
||||||
|
billboard.GaugeRenderer = function() {};
|
||||||
|
|
||||||
|
billboard.GaugeRenderer.prototype.render = function(wgt) {
|
||||||
|
var columns = [wgt.getSeriesData()[0], wgt.getSeriesData()[1]];
|
||||||
|
var color = {};
|
||||||
|
var gauge = {units: "%"};
|
||||||
|
var rendererOptions = wgt._rendererOptions ? jq.evalJSON(wgt._rendererOptions) : null;
|
||||||
|
if (rendererOptions) {
|
||||||
|
if (rendererOptions["intervalColors"]) {
|
||||||
|
color["pattern"] = new Array();
|
||||||
|
rendererOptions["intervalColors"].forEach((x, i) => color["pattern"].push(x));
|
||||||
|
}
|
||||||
|
if (rendererOptions["intervals"]) {
|
||||||
|
color["threshold"] = {values: []};
|
||||||
|
rendererOptions["intervals"].forEach((x, i) => color["threshold"]["values"].push(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (rendererOptions["min"]) {
|
||||||
|
gauge["min"] = rendererOptions["min"];
|
||||||
|
}
|
||||||
|
if (rendererOptions["max"]) {
|
||||||
|
gauge["max"] = rendererOptions["max"];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (rendererOptions["background"]) {
|
||||||
|
gauge["background"] = rendererOptions["background"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var model = {
|
||||||
|
bindto: "#"+wgt.$n().id,
|
||||||
|
data: {
|
||||||
|
columns: [columns],
|
||||||
|
type: wgt._type
|
||||||
|
},
|
||||||
|
color: color,
|
||||||
|
gauge: gauge,
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
doNotHide: false,
|
||||||
|
grouped: false,
|
||||||
|
format: {
|
||||||
|
title: function(x) { return ""; },
|
||||||
|
name: function(name, ratio, id, index) { return ""; },
|
||||||
|
value: function(value, ratio, id, index) { return value; }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {show: false}
|
||||||
|
};
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
zul.billboard.Billboard._renderers["gauge"] = new billboard.GaugeRenderer();
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
var billboard = billboard || {};
|
||||||
|
|
||||||
|
billboard.LineRenderer = function() {};
|
||||||
|
|
||||||
|
billboard.LineRenderer.prototype.render = function(wgt) {
|
||||||
|
var columns = [];
|
||||||
|
var categories = new Array();
|
||||||
|
wgt.getSeriesData().forEach((x, i) => {
|
||||||
|
x.forEach((y, j) => {
|
||||||
|
if (y.category) {
|
||||||
|
columns[columns.length-1].push(y.value);
|
||||||
|
if (!categories.indexOf[y.category])
|
||||||
|
categories.push(y.category);
|
||||||
|
} else {
|
||||||
|
var values = new Array();
|
||||||
|
columns.push(values);
|
||||||
|
values.push(y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var color = {};
|
||||||
|
var line = {};
|
||||||
|
var background = {};
|
||||||
|
var rendererOptions = wgt._rendererOptions ? jq.evalJSON(wgt._rendererOptions) : null;
|
||||||
|
if (rendererOptions) {
|
||||||
|
if (rendererOptions["intervalColors"]) {
|
||||||
|
color["pattern"] = new Array();
|
||||||
|
rendererOptions["intervalColors"].forEach((x, i) => color["pattern"].push(x));
|
||||||
|
}
|
||||||
|
if (rendererOptions["intervals"]) {
|
||||||
|
color["threshold"] = {values: []};
|
||||||
|
rendererOptions["intervals"].forEach((x, i) => color["threshold"]["values"].push(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendererOptions["background"]) {
|
||||||
|
background["color"] = rendererOptions["background"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var x = {tick: {}};
|
||||||
|
var rotated = false;
|
||||||
|
var axes = wgt.getAxes();
|
||||||
|
if (axes.rotated)
|
||||||
|
rotated = axes.rotated;
|
||||||
|
if (axes.xaxis.renderer == "timeseries") {
|
||||||
|
x["type"] = "timeseries";
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
x["tick"]["format"] = axes.xaxis.tickOptions.formatString;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x["type"] = "category";
|
||||||
|
}
|
||||||
|
if (x["type"] == "category") {
|
||||||
|
if (categories.length > 0) {
|
||||||
|
x["categories"] = [];
|
||||||
|
categories.forEach((v, i) => x["categories"].push(v));
|
||||||
|
}
|
||||||
|
} else if (x["type"] == "timeseries") {
|
||||||
|
var ts = new Array();
|
||||||
|
ts.push("x");
|
||||||
|
categories.forEach((s, i) => ts.push(s));
|
||||||
|
columns.push(ts);
|
||||||
|
x["tick"]["fit"] = true;
|
||||||
|
}
|
||||||
|
x["clipPath"] = false;
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
if (axes.xaxis.tickOptions.angle) {
|
||||||
|
if (axes.xaxis.tickOptions.angle != 0) {
|
||||||
|
x["tick"]["rotate"] = axes.xaxis.tickOptions.angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = {
|
||||||
|
bindto: "#"+wgt.$n().id,
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type: wgt._type,
|
||||||
|
onclick: function(d, e) {
|
||||||
|
var i = 0;
|
||||||
|
var si = 0;
|
||||||
|
for(var s in wgt.getSeries()) {
|
||||||
|
if (s == d.id) {
|
||||||
|
si = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
wgt._dataClickTS = new Date().getTime();
|
||||||
|
wgt.fire("onDataClick", {
|
||||||
|
seriesIndex : si,
|
||||||
|
pointIndex : d.index,
|
||||||
|
data : d.value,
|
||||||
|
ticks : wgt.getTicks()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: color,
|
||||||
|
line: line,
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
doNotHide: false,
|
||||||
|
grouped: false,
|
||||||
|
contents: function(d, defaultTitleFormat, defaultValueFormat, color) {
|
||||||
|
var c = d[0];
|
||||||
|
var h = '<table class="bb-tooltip"><tbody><tr><th>';
|
||||||
|
h = h + defaultTitleFormat(c.x);
|
||||||
|
h = h + '</th></tr><tr class="bb-tooltip-name-data"><td class="value">';
|
||||||
|
h = h + c.value + '</td></tr></tbody></table>';
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {show: false},
|
||||||
|
axis: {
|
||||||
|
x: x,
|
||||||
|
rotated: rotated
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
x: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
front: false,
|
||||||
|
focus: {
|
||||||
|
show: false,
|
||||||
|
|
||||||
|
// Below options are available when 'tooltip.grouped=false' option is set
|
||||||
|
edge: true,
|
||||||
|
y: true
|
||||||
|
},
|
||||||
|
lines: {
|
||||||
|
front: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
right: 30
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (x["type"] == "timeseries") {
|
||||||
|
model["data"]["x"] = "x";
|
||||||
|
}
|
||||||
|
if (wgt.getTitle())
|
||||||
|
model["title"] = {text: wgt.getTitle()};
|
||||||
|
if (wgt.getTickAxisLabel())
|
||||||
|
model["axis"]["x"]["label"] = {text: wgt.getTickAxisLabel(), position: "outer-right"};
|
||||||
|
if (wgt.getValueAxisLabel())
|
||||||
|
model["axis"]["y"] = { label: {text: wgt.getValueAxisLabel(), position: "outer-top"} };
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
zul.billboard.Billboard._renderers["line"] = new billboard.LineRenderer();
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
var billboard = billboard || {};
|
||||||
|
|
||||||
|
billboard.PieRenderer = function() {};
|
||||||
|
|
||||||
|
billboard.PieRenderer.prototype.render = function(wgt) {
|
||||||
|
var columns = wgt.getSeriesData()[0];
|
||||||
|
var color = {};
|
||||||
|
var pie = {};
|
||||||
|
var background = {};
|
||||||
|
var rendererOptions = wgt._rendererOptions ? jq.evalJSON(wgt._rendererOptions) : null;
|
||||||
|
if (rendererOptions) {
|
||||||
|
if (rendererOptions["intervalColors"]) {
|
||||||
|
color["pattern"] = new Array();
|
||||||
|
rendererOptions["intervalColors"].forEach((x, i) => color["pattern"].push(x));
|
||||||
|
}
|
||||||
|
if (rendererOptions["intervals"]) {
|
||||||
|
color["threshold"] = {values: []};
|
||||||
|
rendererOptions["intervals"].forEach((x, i) => color["threshold"]["values"].push(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendererOptions["background"]) {
|
||||||
|
background["color"] = rendererOptions["background"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = {
|
||||||
|
bindto: "#"+wgt.$n().id,
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type: wgt._type,
|
||||||
|
onclick: function(d, e) {
|
||||||
|
wgt._dataClickTS = new Date().getTime();
|
||||||
|
wgt.fire("onDataClick", {
|
||||||
|
seriesIndex : 0,
|
||||||
|
pointIndex : d.index,
|
||||||
|
data : d.value,
|
||||||
|
ticks : wgt.getTicks()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: color,
|
||||||
|
pie: pie,
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
doNotHide: false,
|
||||||
|
grouped: false,
|
||||||
|
format: {
|
||||||
|
title: function(x) { return ""; },
|
||||||
|
name: function(name, ratio, id, index) { return name; },
|
||||||
|
value: function(value, ratio, id, index) { return value; }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {show: true}
|
||||||
|
};
|
||||||
|
if (wgt.getTitle())
|
||||||
|
model["title"] = {text: wgt.getTitle()};
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
zul.billboard.Billboard._renderers["pie"] = new billboard.PieRenderer();
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,174 @@
|
||||||
|
var billboard = billboard || {};
|
||||||
|
|
||||||
|
billboard.StackedAreaRenderer = function() {};
|
||||||
|
|
||||||
|
billboard.StackedAreaRenderer.prototype.render = function(wgt) {
|
||||||
|
var columns = [];
|
||||||
|
var categories = new Array();
|
||||||
|
var seriesMap = {};
|
||||||
|
var currentSeries = "";
|
||||||
|
wgt.getSeriesData().forEach((x, i) => {
|
||||||
|
x.forEach((y, j) => {
|
||||||
|
if (y.category) {
|
||||||
|
if (!seriesMap[currentSeries])
|
||||||
|
seriesMap[currentSeries] = {};
|
||||||
|
var categoryMap = seriesMap[currentSeries];
|
||||||
|
if (!categoryMap[y.category])
|
||||||
|
categoryMap[y.category] = y.value;
|
||||||
|
else
|
||||||
|
categoryMap[y.category] += y.value;
|
||||||
|
if (categories.indexOf(y.category) < 0)
|
||||||
|
categories.push(y.category);
|
||||||
|
} else {
|
||||||
|
currentSeries = y;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
categories.forEach((c, i) => {
|
||||||
|
for(var s in seriesMap) {
|
||||||
|
var categoryMap = seriesMap[s];
|
||||||
|
if (!categoryMap[c]) {
|
||||||
|
categoryMap[c] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for(var s in seriesMap) {
|
||||||
|
var row = new Array();
|
||||||
|
row.push(s);
|
||||||
|
var categoryMap = seriesMap[s];
|
||||||
|
categories.forEach((c, i) => {
|
||||||
|
row.push(categoryMap[c]);
|
||||||
|
});
|
||||||
|
columns.push(row);
|
||||||
|
}
|
||||||
|
var groups = new Array();
|
||||||
|
groups[0] = new Array();
|
||||||
|
for(var s in seriesMap) {
|
||||||
|
groups[0].push(s);
|
||||||
|
}
|
||||||
|
var color = {};
|
||||||
|
var area = {};
|
||||||
|
var background = {};
|
||||||
|
var rendererOptions = wgt._rendererOptions ? jq.evalJSON(wgt._rendererOptions) : null;
|
||||||
|
if (rendererOptions) {
|
||||||
|
if (rendererOptions["intervalColors"]) {
|
||||||
|
color["pattern"] = new Array();
|
||||||
|
rendererOptions["intervalColors"].forEach((x, i) => color["pattern"].push(x));
|
||||||
|
}
|
||||||
|
if (rendererOptions["intervals"]) {
|
||||||
|
color["threshold"] = {values: []};
|
||||||
|
rendererOptions["intervals"].forEach((x, i) => color["threshold"]["values"].push(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendererOptions["background"]) {
|
||||||
|
background["color"] = rendererOptions["background"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var x = {tick: {}};
|
||||||
|
var rotated = false;
|
||||||
|
var axes = wgt.getAxes();
|
||||||
|
if (axes.rotated)
|
||||||
|
rotated = axes.rotated;
|
||||||
|
if (axes.xaxis.renderer == "timeseries") {
|
||||||
|
x["type"] = "timeseries";
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
x["tick"]["format"] = axes.xaxis.tickOptions.formatString;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x["type"] = "category";
|
||||||
|
}
|
||||||
|
if (x["type"] == "category") {
|
||||||
|
if (categories.length > 0) {
|
||||||
|
x["categories"] = [];
|
||||||
|
categories.forEach((v, i) => x["categories"].push(v));
|
||||||
|
}
|
||||||
|
} else if (x["type"] == "timeseries") {
|
||||||
|
var ts = new Array();
|
||||||
|
ts.push("x");
|
||||||
|
categories.forEach((s, i) => ts.push(s));
|
||||||
|
columns.push(ts);
|
||||||
|
x["tick"]["fit"] = true;
|
||||||
|
}
|
||||||
|
x["clipPath"] = false;
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
if (axes.xaxis.tickOptions.angle) {
|
||||||
|
if (axes.xaxis.tickOptions.angle != 0) {
|
||||||
|
x["tick"]["rotate"] = axes.xaxis.tickOptions.angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = {
|
||||||
|
bindto: "#"+wgt.$n().id,
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type: "area",
|
||||||
|
groups: groups,
|
||||||
|
onclick: function(d, e) {
|
||||||
|
var i = 0;
|
||||||
|
var si = 0;
|
||||||
|
for(var s in seriesMap) {
|
||||||
|
if (s == d.id) {
|
||||||
|
si = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
wgt._dataClickTS = new Date().getTime();
|
||||||
|
wgt.fire("onDataClick", {
|
||||||
|
seriesIndex : si,
|
||||||
|
pointIndex : d.index,
|
||||||
|
data : d.value,
|
||||||
|
ticks : wgt.getTicks()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: color,
|
||||||
|
area: area,
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
doNotHide: false,
|
||||||
|
grouped: true
|
||||||
|
},
|
||||||
|
legend: {show: true},
|
||||||
|
axis: {
|
||||||
|
x: x,
|
||||||
|
rotated: rotated
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
x: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
front: false,
|
||||||
|
focus: {
|
||||||
|
show: false,
|
||||||
|
|
||||||
|
// Below options are available when 'tooltip.grouped=false' option is set
|
||||||
|
edge: true,
|
||||||
|
y: true
|
||||||
|
},
|
||||||
|
lines: {
|
||||||
|
front: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
right: 30
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (x["type"] == "timeseries") {
|
||||||
|
model["data"]["x"] = "x";
|
||||||
|
}
|
||||||
|
if (wgt.getTitle())
|
||||||
|
model["title"] = {text: wgt.getTitle()};
|
||||||
|
if (wgt.getTickAxisLabel())
|
||||||
|
model["axis"]["x"]["label"] = {text: wgt.getTickAxisLabel(), position: "outer-right"};
|
||||||
|
if (wgt.getValueAxisLabel())
|
||||||
|
model["axis"]["y"] = { label: {text: wgt.getValueAxisLabel(), position: "outer-top"} };
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
zul.billboard.Billboard._renderers["stacked_area"] = new billboard.StackedAreaRenderer();
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
var billboard = billboard || {};
|
||||||
|
|
||||||
|
billboard.StackedBarRenderer = function() {};
|
||||||
|
|
||||||
|
billboard.StackedBarRenderer.prototype.render = function(wgt) {
|
||||||
|
var columns = [];
|
||||||
|
var categories = new Array();
|
||||||
|
var seriesMap = {};
|
||||||
|
var currentSeries = "";
|
||||||
|
wgt.getSeriesData().forEach((x, i) => {
|
||||||
|
x.forEach((y, j) => {
|
||||||
|
if (y.category) {
|
||||||
|
if (!seriesMap[currentSeries])
|
||||||
|
seriesMap[currentSeries] = {};
|
||||||
|
var categoryMap = seriesMap[currentSeries];
|
||||||
|
if (!categoryMap[y.category])
|
||||||
|
categoryMap[y.category] = y.value;
|
||||||
|
else
|
||||||
|
categoryMap[y.category] += y.value;
|
||||||
|
if (categories.indexOf(y.category) < 0)
|
||||||
|
categories.push(y.category);
|
||||||
|
} else {
|
||||||
|
currentSeries = y;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
categories.forEach((c, i) => {
|
||||||
|
for(var s in seriesMap) {
|
||||||
|
var categoryMap = seriesMap[s];
|
||||||
|
if (!categoryMap[c]) {
|
||||||
|
categoryMap[c] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for(var s in seriesMap) {
|
||||||
|
var row = new Array();
|
||||||
|
row.push(s);
|
||||||
|
var categoryMap = seriesMap[s];
|
||||||
|
categories.forEach((c, i) => {
|
||||||
|
row.push(categoryMap[c]);
|
||||||
|
});
|
||||||
|
columns.push(row);
|
||||||
|
}
|
||||||
|
var groups = new Array();
|
||||||
|
groups[0] = new Array();
|
||||||
|
for(var s in seriesMap) {
|
||||||
|
groups[0].push(s);
|
||||||
|
}
|
||||||
|
var color = {};
|
||||||
|
var bar = {};
|
||||||
|
var background = {};
|
||||||
|
var rendererOptions = wgt._rendererOptions ? jq.evalJSON(wgt._rendererOptions) : null;
|
||||||
|
if (rendererOptions) {
|
||||||
|
if (rendererOptions["intervalColors"]) {
|
||||||
|
color["pattern"] = new Array();
|
||||||
|
rendererOptions["intervalColors"].forEach((x, i) => color["pattern"].push(x));
|
||||||
|
}
|
||||||
|
if (rendererOptions["intervals"]) {
|
||||||
|
color["threshold"] = {values: []};
|
||||||
|
rendererOptions["intervals"].forEach((x, i) => color["threshold"]["values"].push(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendererOptions["background"]) {
|
||||||
|
background["color"] = rendererOptions["background"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var x = {tick: {}};
|
||||||
|
var rotated = false;
|
||||||
|
var axes = wgt.getAxes();
|
||||||
|
if (axes.rotated)
|
||||||
|
rotated = axes.rotated;
|
||||||
|
if (axes.xaxis.renderer == "timeseries") {
|
||||||
|
x["type"] = "timeseries";
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
x["tick"]["format"] = axes.xaxis.tickOptions.formatString;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x["type"] = "category";
|
||||||
|
}
|
||||||
|
if (x["type"] == "category") {
|
||||||
|
if (categories.length > 0) {
|
||||||
|
x["categories"] = [];
|
||||||
|
categories.forEach((v, i) => x["categories"].push(v));
|
||||||
|
}
|
||||||
|
} else if (x["type"] == "timeseries") {
|
||||||
|
var ts = new Array();
|
||||||
|
ts.push("x");
|
||||||
|
categories.forEach((s, i) => ts.push(s));
|
||||||
|
columns.push(ts);
|
||||||
|
x["tick"]["fit"] = true;
|
||||||
|
}
|
||||||
|
x["clipPath"] = false;
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
if (axes.xaxis.tickOptions.angle) {
|
||||||
|
if (axes.xaxis.tickOptions.angle != 0) {
|
||||||
|
x["tick"]["rotate"] = axes.xaxis.tickOptions.angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = {
|
||||||
|
bindto: "#"+wgt.$n().id,
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type: "bar",
|
||||||
|
groups: groups,
|
||||||
|
onclick: function(d, e) {
|
||||||
|
var i = 0;
|
||||||
|
var si = 0;
|
||||||
|
for(var s in seriesMap) {
|
||||||
|
if (s == d.id) {
|
||||||
|
si = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
wgt._dataClickTS = new Date().getTime();
|
||||||
|
wgt.fire("onDataClick", {
|
||||||
|
seriesIndex : si,
|
||||||
|
pointIndex : d.index,
|
||||||
|
data : d.value,
|
||||||
|
ticks : wgt.getTicks()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: color,
|
||||||
|
bar: bar,
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
doNotHide: false,
|
||||||
|
grouped: true
|
||||||
|
},
|
||||||
|
legend: {show: true},
|
||||||
|
axis: {
|
||||||
|
x: x,
|
||||||
|
rotated: rotated
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
x: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
front: false,
|
||||||
|
focus: {
|
||||||
|
show: false,
|
||||||
|
|
||||||
|
// Below options are available when 'tooltip.grouped=false' option is set
|
||||||
|
edge: true,
|
||||||
|
y: true
|
||||||
|
},
|
||||||
|
lines: {
|
||||||
|
front: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
right: 30
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (x["type"] == "timeseries") {
|
||||||
|
model["data"]["x"] = "x";
|
||||||
|
}
|
||||||
|
if (wgt.getTitle())
|
||||||
|
model["title"] = {text: wgt.getTitle()};
|
||||||
|
if (wgt.getTickAxisLabel())
|
||||||
|
model["axis"]["x"]["label"] = {text: wgt.getTickAxisLabel(), position: "outer-right"};
|
||||||
|
if (wgt.getValueAxisLabel())
|
||||||
|
model["axis"]["y"] = { label: {text: wgt.getValueAxisLabel(), position: "outer-top"} };
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
zul.billboard.Billboard._renderers["stacked_bar"] = new billboard.StackedBarRenderer();
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
var billboard = billboard || {};
|
||||||
|
|
||||||
|
billboard.WaterfallRenderer = function() {};
|
||||||
|
|
||||||
|
billboard.WaterfallRenderer.prototype.render = function(wgt) {
|
||||||
|
var columns = [];
|
||||||
|
var categories = new Array();
|
||||||
|
var prev = 0;
|
||||||
|
wgt.getSeriesData().forEach((x, i) => {
|
||||||
|
x.forEach((y, j) => {
|
||||||
|
if (y.category) {
|
||||||
|
columns[columns.length-1].push([prev, y.value, prev, y.value]);
|
||||||
|
prev = y.value;
|
||||||
|
if (!categories.indexOf[y.category])
|
||||||
|
categories.push(y.category);
|
||||||
|
} else {
|
||||||
|
var values = new Array();
|
||||||
|
columns.push(values);
|
||||||
|
values.push(y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var color = {};
|
||||||
|
var candlestick = {};
|
||||||
|
var background = {};
|
||||||
|
var rendererOptions = wgt._rendererOptions ? jq.evalJSON(wgt._rendererOptions) : null;
|
||||||
|
if (rendererOptions) {
|
||||||
|
if (rendererOptions["intervalColors"]) {
|
||||||
|
color["pattern"] = new Array();
|
||||||
|
rendererOptions["intervalColors"].forEach((x, i) => color["pattern"].push(x));
|
||||||
|
}
|
||||||
|
if (rendererOptions["intervals"]) {
|
||||||
|
color["threshold"] = {values: []};
|
||||||
|
rendererOptions["intervals"].forEach((x, i) => color["threshold"]["values"].push(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendererOptions["background"]) {
|
||||||
|
background["color"] = rendererOptions["background"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var x = {tick: {}};
|
||||||
|
var rotated = false;
|
||||||
|
var axes = wgt.getAxes();
|
||||||
|
if (axes.rotated)
|
||||||
|
rotated = axes.rotated;
|
||||||
|
if (axes.xaxis.renderer == "timeseries") {
|
||||||
|
x["type"] = "timeseries";
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
x["tick"]["format"] = axes.xaxis.tickOptions.formatString;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x["type"] = "category";
|
||||||
|
}
|
||||||
|
if (x["type"] == "category") {
|
||||||
|
if (categories.length > 0) {
|
||||||
|
x["categories"] = [];
|
||||||
|
categories.forEach((v, i) => x["categories"].push(v));
|
||||||
|
}
|
||||||
|
} else if (x["type"] == "timeseries") {
|
||||||
|
var ts = new Array();
|
||||||
|
ts.push("x");
|
||||||
|
categories.forEach((s, i) => ts.push(s));
|
||||||
|
columns.push(ts);
|
||||||
|
x["tick"]["fit"] = true;
|
||||||
|
}
|
||||||
|
x["clipPath"] = false;
|
||||||
|
if (axes.xaxis.tickOptions) {
|
||||||
|
if (axes.xaxis.tickOptions.angle) {
|
||||||
|
if (axes.xaxis.tickOptions.angle != 0) {
|
||||||
|
x["tick"]["rotate"] = axes.xaxis.tickOptions.angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = {
|
||||||
|
bindto: "#"+wgt.$n().id,
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type: "candlestick",
|
||||||
|
selection: {
|
||||||
|
enabled: true,
|
||||||
|
grouped: true
|
||||||
|
},
|
||||||
|
onselected: function(d, e) {
|
||||||
|
var i = 0;
|
||||||
|
var si = 0;
|
||||||
|
for(var s in wgt.getSeries()) {
|
||||||
|
if (s == d.id) {
|
||||||
|
si = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
wgt._dataClickTS = new Date().getTime();
|
||||||
|
wgt.fire("onDataClick", {
|
||||||
|
seriesIndex : si,
|
||||||
|
pointIndex : d.index,
|
||||||
|
data : d.value,
|
||||||
|
ticks : wgt.getTicks()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: color,
|
||||||
|
candlestick: candlestick,
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
doNotHide: false,
|
||||||
|
contents: function(d, defaultTitleFormat, defaultValueFormat, color) {
|
||||||
|
var c = d[0];
|
||||||
|
var h = '<table class="bb-tooltip"><tbody><tr><th>';
|
||||||
|
h = h + defaultTitleFormat(c.x);
|
||||||
|
h = h + '</th></tr><tr class="bb-tooltip-name-data"><td class="value">';
|
||||||
|
h = h + c.value[1] + '</td></tr></tbody></table>';
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {show: false},
|
||||||
|
axis: {
|
||||||
|
x: x,
|
||||||
|
rotated: rotated
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
x: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
front: false,
|
||||||
|
focus: {
|
||||||
|
show: false,
|
||||||
|
|
||||||
|
// Below options are available when 'tooltip.grouped=false' option is set
|
||||||
|
edge: true,
|
||||||
|
y: true
|
||||||
|
},
|
||||||
|
lines: {
|
||||||
|
front: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
right: 30
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (x["type"] == "timeseries") {
|
||||||
|
model["data"]["x"] = "x";
|
||||||
|
}
|
||||||
|
if (wgt.getTitle())
|
||||||
|
model["title"] = {text: wgt.getTitle()};
|
||||||
|
if (wgt.getTickAxisLabel())
|
||||||
|
model["axis"]["x"]["label"] = {text: wgt.getTickAxisLabel(), position: "outer-right"};
|
||||||
|
if (wgt.getValueAxisLabel())
|
||||||
|
model["axis"]["y"] = { label: {text: wgt.getValueAxisLabel(), position: "outer-top"} };
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
zul.billboard.Billboard._renderers["waterfall"] = new billboard.WaterfallRenderer();
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
function (out) {
|
||||||
|
out.push('<div', this.domAttrs_(), '></div>');
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<package name="zul.billboard" language="xul/html" depends="zul">
|
||||||
|
<widget name="Billboard" />
|
||||||
|
|
||||||
|
<script src="ext/billboard.pkgd.js" />
|
||||||
|
<script src="ext/billboard.gauge.js" />
|
||||||
|
<script src="ext/billboard.bar.js" />
|
||||||
|
<script src="ext/billboard.line.js" />
|
||||||
|
<script src="ext/billboard.pie.js" />
|
||||||
|
<script src="ext/billboard.donut.js" />
|
||||||
|
<script src="ext/billboard.area.js" />
|
||||||
|
<script src="ext/billboard.waterfall.js" />
|
||||||
|
<script src="ext/billboard.stacked_bar.js" />
|
||||||
|
<script src="ext/billboard.stacked_area.js" />
|
||||||
|
</package>
|
2
pom.xml
2
pom.xml
|
@ -38,6 +38,8 @@
|
||||||
<module>org.idempiere.zk.extra</module>
|
<module>org.idempiere.zk.extra</module>
|
||||||
<module>org.idempiere.keikai</module>
|
<module>org.idempiere.keikai</module>
|
||||||
<module>org.idempiere.printformat.editor</module>
|
<module>org.idempiere.printformat.editor</module>
|
||||||
|
<module>org.idempiere.zk.billboard</module>
|
||||||
|
<module>org.idempiere.zk.billboard.chart</module>
|
||||||
<module>org.adempiere.report.jasper-feature</module>
|
<module>org.adempiere.report.jasper-feature</module>
|
||||||
<module>org.adempiere.base-feature</module>
|
<module>org.adempiere.base-feature</module>
|
||||||
<module>org.adempiere.replication-feature</module>
|
<module>org.adempiere.replication-feature</module>
|
||||||
|
|
Loading…
Reference in New Issue