upbeat-dashboard
Version:
ERROR: No README.md file found!
1,257 lines (1,102 loc) • 161 kB
JavaScript
/********* Source File: src/elycharts_defaults.js*********/
/*!*********************************************************************
* ELYCHARTS v2.1.4-SNAPSHOT $Id: elycharts.js 52 2011-08-07 19:57:09Z stefano.bagnara@gmail.com $
* A Javascript library to generate interactive charts with vectorial graphics.
*
* Copyright (c) 2010 Void Labs s.n.c. (http://void.it)
* Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license.
**********************************************************************/
(function($) {
if (!$.elycharts)
$.elycharts = {};
/***********************************************************************
* DEFAULT OPTIONS
**********************************************************************/
$.elycharts.templates = {
common : {
// Tipo di grafico
// type : 'line|pie|funnel|barline'
// Permette di specificare una configurazione di default da utilizzare (definita in $.elycharts.templates.NOME)
// La configurazione completa � quindi data da tutti i valori della conf di default alla quale viene unita (con sovrascrittura) la conf corrente
// Il parametro � ricorsivo (la configurazione di default puo' a sua volta avere una configurazione di default)
// Se non specificato, la configurazione di default � quella con lo stesso nome del tipo di grafico
// template : 'NOME',
/* DATI:
// I valori associati a ogni serie del grafico. Ogni serie � associata a una chiave dell'oggetto value, il cui
// valore � l'array di dati relativi
values : {},
// Label associate ai valori del grafico
// Solo in caso di label gestite da labelmanager (quindi per pie e funnel) e per label.html = true e' possibile inserire
// degli elementi DOM/JQUERY che verranno presi e posizionati correttament.
labels : [],
// Anchor per la gestione mediante anchormanager. Possono essere stringhe e oggetti DOM/JQUERY che verranno riposizionati
anchors : {},
tooltips : {},
legend : [],
*/
// Per impostare una dimensione diversa da quella del container settare width e height
//width : x,
//height : y
// I margini del grafico rispetto al frame complessivo. Da notare che riguardano la posizione del grafico
// principale, e NON degli elementi aggiuntivi (legenda, label e titoli degli assi...). Quindi i margini devono
// essere impostati in genere proprio per lasciare lo spazio per questi elementi
// Sintassi: [top, right, bottom, left]
margins: [10, 10, 10, 10],
// style : {},
// Per gestire al meglio l'interattivita' del grafico (tooltip, highlight, anchor...) viene inserito un secondo
// layer per le parti sensibili al mouse. Se si sa che il grafico non avra' alcuna interattivita' si puo' impostare
// questo valore a false per evitare di creare il layer (ottimizzando leggermente la pagina)
interactive : true,
// Dati da applicare a tutte le serie del grafico
defaultSeries : {
// Impostare a false per disabilitare la visualizzazione della serie
visible : true,
// Impostare color qui permette di impostare velocemente plotProps.stroke+fill, tooltip.frameProps.stroke, dotProps.stroke e fillProps.fill (se non specificati)
//color: 'blue',
//plotProps : { },
// Impostazioni dei tooltip
tooltip : {
active : true,
// Se width ed height vengono impostati a 0 o ad "auto" (equivalenti) non vengono fissate dimensioni, quindi il contenuto si autodimensiona in funzione del tooltip
// Impostare a 0|auto � incompatibile con il frame SVG, quindi viene automaticamente disabilitato (come se frameProps = false)
width: 100, height: 50,
roundedCorners: 5,
padding: [6, 6] /* y, x */,
offset: [20, 0] /* y, x */,
// Se frameProps = false non disegna la cornice del tooltip (ad es. per permettere di definire la propria cornice HTML)
frameProps : { fill: "white", "stroke-width": 2 },
contentStyle : { "font-family": "Arial", "font-size": "12px", "line-height": "16px", color: "black" }
},
// Highlight feature
highlight : {
// Cambia le dimensioni dell'elemento quando deve essere evidenziato
//scale : [x, y],
// Opzioni di animazione effetto "scale"
scaleSpeed : 100, scaleEasing : '',
// Cambia gli attributi dell'elemento quando evidenziato
//newProps : { opacity : 1 },
// Inserisce un layer con gli attributi specificati sopra quello da evidenziare
//overlayProps : {"fill" : "white", "fill-opacity" : .3, "stroke-width" : 0}
// Muove l'area evidenziata. E' possibile specificare un valore X o un array [X, Y]
//move : 10,
// Opzioni di animazione effetto "move"
moveSpeed : 100, moveEasing : '',
// Opzioni di animazione da usare per riportare l'oggetto alle situazione iniziale
restoreSpeed : 0, restoreEasing : ''
},
anchor : {
// Aggiunge alle anchor esterne la classe selezionata quando il mouse passa sull'area
//addClass : "",
// Evidenzia la serie al passaggio del mouse
//highlight : "",
// Se impostato a true usa gli eventi mouseenter/mouseleave invece di mouseover/mouseout per l'highlight
//useMouseEnter : false,
},
// Opzioni per la generazione animata dei grafici
startAnimation : {
//active : true,
type : 'simple',
speed : 600,
delay : 0,
propsFrom : {}, // applicate a tutte le props di plot
propsTo : {}, // applicate a tutte le props di plot
easing : '' // easing raphael: >, <, <>, backIn, backOut, bounce, elastic
// Opzionale per alcune animazioni, permette di specificare un sotto-tipo
// subType : 0|1|2
},
// Opzioni per le transizioni dei grafici durante un cambiamento di configurazione
/* stepAnimation : {
speed : 600,
delay : 0,
easing : '' // easing raphael: >, <, <>, backIn, backOut, bounce, elastic
},*/
label : {
// Disegna o meno la label interna al grafico
active : false,
// Imposta un offset [X,Y] per la label (le coordinate sono relative al sistema di assi dello specifico settore disegnato.
// Ad es. per il piechart la X � la distanza dal centro, la Y lo spostamento ortogonale
//offset : [x, y],
html : false,
// Proprieta' della label (per HTML = false)
props : { fill: 'black', stroke: "none", "font-family": 'Arial', "font-size": "16px" },
// Stile CSS della label (per HTML = true)
style : { cursor : 'default' }
// Posizionamento della label rispetto al punto centrale (+offset) identificato
//frameAnchor : ['start|middle|end', 'top|middle|bottom']
}
/*legend : {
dotType : 'rect',
dotWidth : 10, dotHeight : 10, dotR : 4,
dotProps : { },
textProps : { font: '12px Arial', fill: "#000" }
}*/
},
series : {
// Serie specifica usata quando ci sono "dati vuoti" (ad esempio quando un piechart e' a 0)
empty : {
//plotProps : { fill : "#D0D0D0" },
label : { active : false },
tooltip : { active : false }
}
/*root : {
values : []
}*/
},
features : {
tooltip : {
// Imposta una posizione fissa per tutti i tooltip
//fixedPos : [ x, y]
// Velocita' del fade
fadeDelay : 100,
// Velocita' dello spostamento del tip da un'area all'altra
moveDelay : 300
// E' possibile specificare una funzione che filtra le coordinate del tooltip prima di mostrarlo, permettendo di modificarle
// Nota: le coordinate del mouse sono in mouseAreaData.event.pageX/pageY, e nel caso va ritornato [mouseAreaData.event.pageX, mouseAreaData.event.pageY, true] per indicare che il sistema e' relativo alla pagina)
//positionHandler : function(env, tooltipConf, mouseAreaData, suggestedX, suggestedY) { return [suggestedX, suggestedY] }
},
mousearea : {
// 'single' le aree sensibili sono relative a ogni valore di ogni serie, se 'index' il mouse attiva tutte le serie per un indice
type : 'single',
// In caso di type = 'index', indica se le aree si basano sulle barre ('bar') o sui punti di una linea ('line'). Specificare 'auto' per scegliere automaticamente
indexCenter : 'auto',
// Quanto tempo puo' passare nel passaggio da un'area all'altra per considerarlo uno spostamento di puntatore
areaMoveDelay : 500,
// Se diversi chart specificano lo stesso syncTag quando si attiva l'area di uno si disattivano quelle degli altri
syncTag: false,
// Callback for mouse actions. Parameters passed: (env, serie, index, mouseAreaData)
onMouseEnter : false,
onMouseExit : false,
onMouseChanged : false,
onMouseOver : false,
onMouseOut : false
},
highlight : {
// Evidenzia tutto l'indice con una barra ("bar"), una linea ("line") o una linea centrata sulle barre ("barline"). Se "auto" decide in autonomia tra bar e line
//indexHighlight : 'barline',
indexHighlightProps : { opacity : 1 /*fill : 'yellow', opacity : .3, scale : ".5 1"*/ }
},
animation : {
// Valore di default per la generazione animata degli elementi del grafico (anche per le non-serie: label, grid...)
startAnimation : {
//active : true,
//propsFrom : {}, // applicate a tutte le props di plot
//propsTo : {}, // applicate a tutte le props di plot
speed : 600,
delay : 0,
easing : '' // easing raphael: >, <, <>, backIn, backOut, bounce, elastic
},
// Valore di default per la transizione animata degli elementi del grafico (anche per le non-serie: label, grid...)
stepAnimation : {
speed : 600,
delay : 0,
easing : '' // easing raphael: >, <, <>, backIn, backOut, bounce, elastic
}
},
frameAnimation : {
active : false,
cssFrom : { opacity : 0},
cssTo : { opacity: 1 },
speed : 'slow',
easing : 'linear' // easing jQuery: 'linear' o 'swing'
},
pixelWorkAround : {
active : true
},
label : {},
shadows : {
active : false,
offset : [2, 2], // Per attivare l'ombra, [y, x]
props : {"stroke-width": 0, "stroke-opacity": 0, "fill": "black", "fill-opacity": .3}
},
// BALLOONS: Applicabile solo al funnel (per ora)
balloons : {
active : false,
// Width: se non specificato e' automatico
//width : 200,
// Height: se non specificato e' automatico
//height : 50,
// Lo stile CSS da applicare a ogni balloon
style : { },
// Padding
padding : [ 5, 5 ],
// La distanza dal bordo sinistro
left : 10,
// Percorso della linea: [ [ x, y iniziali (rispetto al punto di inizio standard)], ... [x, y intermedi (rispetto al punto di inizio standard)] ..., [x, y finale (rispetto all'angolo del balloon pi� vicino al punto di inizio)] ]
line : [ [ 0, 0 ], [0, 0] ],
// Propriet� della linea
lineProps : { }
},
legend : {
horizontal : false,
x : 'auto', // X | auto, (auto solo per horizontal = true)
y : 10,
width : 'auto', // X | auto, (auto solo per horizontal = true)
height : 20,
itemWidth : "fixed", // fixed | auto, solo per horizontal = true
margins : [0, 0, 0, 0],
dotMargins : [10, 5], // sx, dx
borderProps : { fill : "white", stroke : "black", "stroke-width" : 1 },
dotType : 'rect',
dotWidth : 10, dotHeight : 10, dotR : 4,
dotProps : { type : "rect", width : 10, height : 10 },
textProps : { font: '12px Arial', fill: "#000" }
},
debug : {
active : false
}
},
nop : 0
},
line : {
template : 'common',
barMargins : 0,
// Axis
defaultAxis : {
// [non per asse x] Normalizza il valore massimo dell'asse in modo che tutte le label abbiamo al massimo N cifre significative
// (Es: se il max e' 135 e normalize = 2 verra' impostato il max a 140, ma se il numero di label in y e' 3 verr� impostato 150)
normalize: 2,
// Permette di impostare i valori minimi e massimi di asse (invece di autorilevarli)
min: 0, //max: x,
// Imposta un testo da usare come prefisso e suffisso delle label
//prefix : "", suffix : "",
// Visualizza o meno le label dell'asse
labels: false,
// Distanza tra le label e l'asse relativo
labelsDistance: 8,
// [solo asse x] Rotazione (in gradi) delle label. Se specificato ignora i valori di labelsAnchor e labelsProps['text-anchor']
labelsRotate: 0,
// Proprieta' grafiche delle label
labelsProps : {font: '10px Arial', fill: "#000"},
// Compatta il numero mostrato nella label usando i suffissi specificati per migliaia, milioni...
//labelsCompactUnits : ['k', 'M'],
// Permette di specificare una funzione esterna che si occupa di formattare (o in generale trasformare) la label
//labelsFormatHandler : function (label) { return label },
// Salta le prime N label
//labelsSkip : 0,
// Force alignment for the label. Auto will automatically center it for x axis (also considering labelsRotate), "end" for l axis, "start" for the right axis.
//labelsAnchor : "auto"
// [solo asse x] Force an alternative position for the X axis labels. Auto will automatically choose the right position depending on "labelsCenter", the type of charts (bars vs lines), and labelsRotate.
//labelsPos : "auto",
// Automatically hide labels that would overlap previous labels.
//labelsHideCovered : true,
// Inserisce un margine alla label (a sinistra se in asse x, in alto se in altri assi)
//labelsMargin: 10,
// [solo asse x] If labelsHideCovered = true, make sure each label have at least this space before the next one.
//labelsMarginRight: 0,
// Distanza del titolo dall'asse
titleDistance : 25, titleDistanceIE : .75,
// Proprieta' grafiche del titolo
titleProps : {font: '12px Arial', fill: "#000", "font-weight": "bold"}
},
axis : {
x : { titleDistanceIE : 1.2 }
},
defaultSeries : {
// Tipo di serie, puo' essere 'line' o 'bar'
type : 'line',
// L'asse di riferimento della serie. Gli assi "l" ed "r" sono i 2 assi visibili destro e sinistro.
// E' possibile inserire anche un asse arbitrario (che non sar� visibile)
axis : 'l',
// Specificare cumulative = true se i valori inseriti per la serie sono cumulativi
cumulative : false,
// In caso di type="line" indica l'arrotondamento della linea
rounded : 1,
// Mette il punto di intersezione al centro dell'intervallo invece che al limite (per allineamento con bars). Se 'auto' decide autonomamente
lineCenter : 'auto',
// Permette di impilare le serie (i valori di uno iniziano dove finiscono quelli del precedente) con un altra (purche' dello stesso tipo)
// Specificare "true" per impilare con la serie visibile precedente, oppure il nome della serie sulla quale impilare
// stacked : false,
plotProps : {"stroke-width": 1, "stroke-linejoin": "round"},
barWidthPerc: 100,
//DELETED: barProps : {"width-perc" : 100, "stroke-width": 1, "fill-opacity" : .3},
// Attiva o disattiva il riempimento
fill : false,
fillProps : {stroke: "none", "stroke-width" : 0, "stroke-opacity": 0, opacity: .3},
dot : false,
dotProps : {size: 4, stroke: "#000", zindex: 5},
dotShowOnNull : false,
mouseareaShowOnNull : false,
startAnimation : {
plotPropsFrom : false,
// DELETED linePropsFrom : false,
fillPropsFrom : false,
dotPropsFrom : false,
//DELETED barPropsFrom : false,
shadowPropsFrom : false
}
},
features : {
grid : {
// N. di divisioni sull'asse X. Se "auto" si basa sulla label da visualizzare. Se "0" imposta draw[vertical] = false
// Da notare che se "auto" allora la prima e l'ultima linea (bordi) le fa vedere sempre (se ci sono le label). Se invece e' un numero si comporta come ny: fa vedere i bordi solo se forzato con forceBorder
nx : "auto",
// N. di divisione sull'asse Y. Se "0" imposta draw[horizontal] = false
ny : 4,
// Disegna o meno la griglia. Si puo' specificare un array [horizontal, vertical]
draw : false,
// Forza la visualizzazione dei bordi/assi. Se true disegna comunque i bordi (anche se draw = false o se non ci sono label),
// altrimenti si basa sulle regole standard di draw e presenza label (per asse x)
// Puo' essere un booleano singolo o un array di bordi [up, dx, down, sx]
forceBorder : false,
// Proprieta' di visualizzazione griglia
props : {stroke: '#e0e0e0', "stroke-width": 1},
// Dimensioni extra delle rette [up, dx, down, sx]
extra : [0, 0, 0, 0],
// Indica se le label (e le rispettive linee del grid) vanno centrate sulle barre (true), quindi tra 2 linee, o sui punti della serie (false), quindi su una sola linea
// Se specificato "auto" decide in autonomia
labelsCenter : "auto",
// Display a rectangular region with properties specied for every even/odd vertical/horizontal grid division
evenVProps : false,
oddVProps : false,
evenHProps : false,
oddHProps : false,
ticks : {
// Attiva le barrette sugli assi [x, l, r]
active : [false, false, false],
// Dimensioni da prima dell'asse a dopo l'asse
size : [10, 10],
// Proprieta' di visualizzazione griglia
props : {stroke: '#e0e0e0', "stroke-width": 1}
}
}
},
nop : 0
},
pie : {
template : 'common',
// Coordinate del centro, se non specificate vengono autodeterminate
//cx : 0, cy : 0,
// Raggio della torta, se non specificato viene autodeterminato
//r : 0
// Angolo dal quale iniziare a disegnare le fette, in gradi
startAngle : 0,
// Disegna la torta con le fette in senso orario (invece dell'orientamento standard per gradi, in senso antiorario)
clockwise : false,
// Soglia (rapporto sul totale) entro la quale una fetta non viene visualizzata
valueThresold : 0.006,
defaultSeries : {
// r: .5, raggio usato solo per questo spicchio, se <=1 e' in rapporto al raggio generale
// inside: X, inserisce questo spicchio dentro un altro (funziona solo inside: precedente, e non gestisce + spicchi dentro l'altro)
}
},
funnel : {
template : 'common',
rh: 0, // height of ellipsis (for top and bottom cuts)
method: 'width', // width/cutarea
topSector: 0, // height factor of top cylinder
topSectorProps : { fill: "#d0d0d0" },
bottomSector: .1, // height factor of bottom cylinder
bottomSectorProps : { fill: "#d0d0d0" },
edgeProps : { fill: "#c0c0c0", "stroke-width": 1, opacity: 1 },
nop : 0
},
barline : {
template : 'common',
// Imposta il valore massimo per la scala (altrimenti prende il valore + alto)
// max : X
// Impostare direction = rtl per creare un grafico che va da destra a sinistra
direction : 'ltr'
}
}
})(jQuery);
/********* Source File: src/elycharts_core.js*********/
/**********************************************************************
* ELYCHARTS
* A Javascript library to generate interactive charts with vectorial graphics.
*
* Copyright (c) 2010 Void Labs s.n.c. (http://void.it)
* Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license.
**********************************************************************/
(function($) {
if (!$.elycharts)
$.elycharts = {};
$.elycharts.lastId = 0;
/***********************************************************************
* INITIALIZATION / MAIN CALL
**********************************************************************/
$.fn.chart = function($options) {
if (!this.length)
return this;
var $env = this.data('elycharts_env');
if (typeof $options == "string") {
if ($options.toLowerCase() == "config")
return $env ? $env.opt : false;
if ($options.toLowerCase() == "clear") {
if ($env) {
// TODO Bisogna chiamare il destroy delle feature?
$env.paper.clear();
this.html("");
this.data('elycharts_env', false);
}
}
}
else if (!$env) {
// First call, initialization
if ($options)
$options = _extendAndNormalizeOptions($options);
if (!$options || !$options.type || !$.elycharts.templates[$options.type]) {
alert('ElyCharts ERROR: chart type is not specified');
return false;
}
$env = _initEnv(this, $options);
_processGenericConfig($env, $options);
$env.pieces = $.elycharts[$env.opt.type].draw($env);
this.data('elycharts_env', $env);
} else {
$options = _normalizeOptions($options, $env.opt);
// Already initialized
$env.oldopt = common._clone($env.opt);
$env.opt = $.extend(true, $env.opt, $options);
$env.newopt = $options;
_processGenericConfig($env, $options);
$env.pieces = $.elycharts[$env.opt.type].draw($env);
}
return this;
}
/**
* Must be called only in first call to .chart, to initialize elycharts environment.
*/
function _initEnv($container, $options) {
if (!$options.width)
$options.width = $container.width();
if (!$options.height)
$options.height = $container.height();
var $env = {
id : $.elycharts.lastId ++,
paper : common._RaphaelInstance($container.get()[0], $options.width, $options.height),
container : $container,
plots : [],
opt : $options
};
// Rendering a transparent pixel up-left. Thay way SVG area is well-covered (else the position starts at first real object, and that mess-ups everything)
$env.paper.rect(0,0,1,1).attr({opacity: 0});
$.elycharts[$options.type].init($env);
return $env;
}
function _processGenericConfig($env, $options) {
if ($options.style)
$env.container.css($options.style);
}
/**
* Must be called in first call to .chart, to build the full config structure and normalize it.
*/
function _extendAndNormalizeOptions($options) {
var k;
// Compatibility with old $.elysia_charts.default_options and $.elysia_charts.templates
if ($.elysia_charts) {
if ($.elysia_charts.default_options)
for (k in $.elysia_charts.default_options)
$.elycharts.templates[k] = $.elysia_charts.default_options[k];
if ($.elysia_charts.templates)
for (k in $.elysia_charts.templates)
$.elycharts.templates[k] = $.elysia_charts.templates[k];
}
// TODO Optimize extend cicle
while ($options.template) {
var d = $options.template;
delete $options.template;
$options = $.extend(true, {}, $.elycharts.templates[d], $options);
}
if (!$options.template && $options.type) {
$options.template = $options.type;
while ($options.template) {
d = $options.template;
delete $options.template;
$options = $.extend(true, {}, $.elycharts.templates[d], $options);
}
}
return _normalizeOptions($options, $options);
}
/**
* Normalize options passed (primarly for backward compatibility)
*/
function _normalizeOptions($options, $fullopt) {
if ($options.type == 'pie' || $options.type == 'funnel') {
if ($options.values && $.isArray($options.values) && !$.isArray($options.values[0]))
$options.values = { root : $options.values };
if ($options.tooltips && $.isArray($options.tooltips) && !$.isArray($options.tooltips[0]))
$options.tooltips = { root : $options.tooltips };
if ($options.anchors && $.isArray($options.anchors) && !$.isArray($options.anchors[0]))
$options.anchors = { root : $options.anchors };
if ($options.balloons && $.isArray($options.balloons) && !$.isArray($options.balloons[0]))
$options.balloons = { root : $options.balloons };
if ($options.legend && $.isArray($options.legend) && !$.isArray($options.legend[0]))
$options.legend = { root : $options.legend };
}
if ($options.defaultSeries) {
var deftype = $fullopt.type != 'line' ? $fullopt.type : ($options.defaultSeries.type ? $options.defaultSeries.type : ($fullopt.defaultSeries.type ? $fullopt.defaultSeries.type : 'line'));
_normalizeOptionsColor($options.defaultSeries, deftype, $fullopt);
if ($options.defaultSeries.stackedWith) {
$options.defaultSeries.stacked = $options.defaultSeries.stackedWith;
delete $options.defaultSeries.stackedWith;
}
}
if ($options.series)
for (var serie in $options.series) {
var type = $fullopt.type != 'line' ? $fullopt.type : ($options.series[serie].type ? $options.series[serie].type : ($fullopt.series[serie].type ? $fullopt.series[serie].type : (deftype ? deftype : 'line')));
_normalizeOptionsColor($options.series[serie], type, $fullopt);
if ($options.series[serie].values)
for (var value in $options.series[serie].values)
_normalizeOptionsColor($options.series[serie].values[value], type, $fullopt);
if ($options.series[serie].stackedWith) {
$options.series[serie].stacked = $options.series[serie].stackedWith;
delete $options.series[serie].stackedWith;
}
}
if ($options.type == 'line') {
if (!$options.features)
$options.features = {};
if (!$options.features.grid)
$options.features.grid = {};
if (typeof $options.gridNX != 'undefined') {
$options.features.grid.nx = $options.gridNX;
delete $options.gridNX;
}
if (typeof $options.gridNY != 'undefined') {
$options.features.grid.ny = $options.gridNY;
delete $options.gridNY;
}
if (typeof $options.gridProps != 'undefined') {
$options.features.grid.props = $options.gridProps;
delete $options.gridProps;
}
if (typeof $options.gridExtra != 'undefined') {
$options.features.grid.extra = $options.gridExtra;
delete $options.gridExtra;
}
if (typeof $options.gridForceBorder != 'undefined') {
$options.features.grid.forceBorder = $options.gridForceBorder;
delete $options.gridForceBorder;
}
if ($options.defaultAxis && $options.defaultAxis.normalize && ($options.defaultAxis.normalize == 'auto' || $options.defaultAxis.normalize == 'autony'))
$options.defaultAxis.normalize = 2;
if ($options.axis)
for (var axis in $options.axis)
if ($options.axis[axis] && $options.axis[axis].normalize && ($options.axis[axis].normalize == 'auto' || $options.axis[axis].normalize == 'autony'))
$options.axis[axis].normalize = 2;
}
return $options;
}
/**
* Manage "color" attribute.
* @param $section Section part of external conf passed
* @param $type Type of plot (for line chart can be "line" or "bar", for other types is equal to chart type)
*/
function _normalizeOptionsColor($section, $type, $fullopt) {
if ($section.color) {
var color = $section.color;
if (!$section.plotProps)
$section.plotProps = {};
if ($type == 'line') {
if ($section.plotProps && !$section.plotProps.stroke && !$fullopt.defaultSeries.plotProps.stroke)
$section.plotProps.stroke = color;
} else {
if ($section.plotProps && !$section.plotProps.fill && !$fullopt.defaultSeries.plotProps.fill)
$section.plotProps.fill = color;
}
if (!$section.tooltip)
$section.tooltip = {};
// Is disabled in defaultSetting i should not set color
if (!$section.tooltip.frameProps && $fullopt.defaultSeries.tooltip.frameProps)
$section.tooltip.frameProps = {};
if ($section.tooltip && $section.tooltip.frameProps && !$section.tooltip.frameProps.stroke && !$fullopt.defaultSeries.tooltip.frameProps.stroke)
$section.tooltip.frameProps.stroke = color;
if (!$section.legend)
$section.legend = {};
if (!$section.legend.dotProps)
$section.legend.dotProps = {};
if ($section.legend.dotProps && !$section.legend.dotProps.fill)
$section.legend.dotProps.fill = color;
if ($type == 'line') {
if (!$section.dotProps)
$section.dotProps = {};
if ($section.dotProps && !$section.dotProps.fill && !$fullopt.defaultSeries.dotProps.fill)
$section.dotProps.fill = color;
if (!$section.fillProps)
$section.fillProps = {};
if ($section.fillProps && !$section.fillProps.fill && !$fullopt.defaultSeries.fillProps.fill)
$section.fillProps.fill = color;
}
}
}
/***********************************************************************
* COMMON
**********************************************************************/
$.elycharts.common = {
_RaphaelInstance : function(c, w, h) {
var r = Raphael(c, w, h);
r.customAttributes.slice = function (cx, cy, r, rint, aa1, aa2) {
// Method body is for clockwise angles, but parameters passed are ccw
a1 = 360 - aa2; a2 = 360 - aa1;
//a1 = aa1; a2 = aa2;
var flag = (a2 - a1) > 180;
a1 = (a1 % 360) * Math.PI / 180;
a2 = (a2 % 360) * Math.PI / 180;
// a1 == a2 (but they where different before) means that there is a complete round (eg: 0-360). This should be shown
if (a1 == a2 && aa1 != aa2)
a2 += 359.99 * Math.PI / 180;
return { path : rint ? [
["M", cx + r * Math.cos(a1), cy + r * Math.sin(a1)],
["A", r, r, 0, +flag, 1, cx + r * Math.cos(a2), cy + r * Math.sin(a2)],
["L", cx + rint * Math.cos(a2), cy + rint * Math.sin(a2)],
//["L", cx + rint * Math.cos(a1), cy + rint * Math.sin(a1)],
["A", rint, rint, 0, +flag, 0, cx + rint * Math.cos(a1), cy + rint * Math.sin(a1)],
["z"]
] : [
["M", cx, cy],
["l", r * Math.cos(a1), r * Math.sin(a1)],
["A", r, r, 0, +flag, 1, cx + r * Math.cos(a2), cy + r * Math.sin(a2)],
["z"]
] };
};
return r;
},
_clone : function(obj){
if(obj == null || typeof(obj) != 'object')
return obj;
if (obj.constructor == Array)
return [].concat(obj);
var temp = new obj.constructor(); // changed (twice)
for(var key in obj)
temp[key] = this._clone(obj[key]);
return temp;
},
_mergeObjects : function(o1, o2) {
return $.extend(true, o1, o2);
/*
if (typeof o1 == 'undefined')
return o2;
if (typeof o2 == 'undefined')
return o1;
for (var idx in o2)
if (typeof o1[idx] == 'undefined')
o1[idx] = this._clone(o2[idx]);
else if (typeof o2[idx] == 'object') {
if (typeof o1[idx] == 'object')
o1[idx] = this._mergeObjects(o1[idx], o2[idx]);
else
o1[idx] = this._mergeObjects({}, o2[idx]);
}
else
o1[idx] = this._clone(o2[idx]);
return o1;*/
},
compactUnits : function(val, units) {
for (var i = units.length - 1; i >= 0; i--) {
var v = val / Math.pow(1000, i + 1);
//console.warn(i, units[i], v, v * 10 % 10);
if (v >= 1 && v * 10 % 10 == 0)
return v + units[i];
}
return val;
},
getElementOriginalAttrs : function(element) {
var attr = $(element.node).data('original-attr');
if (!attr) {
attr = element.attr();
$(element.node).data('original-attr', attr);
}
return attr;
},
findInPieces : function(pieces, section, serie, index, subsection) {
for (var i = 0; i < pieces.length; i++) {
if (
(typeof section == undefined || section == -1 || section == false || pieces[i].section == section) &&
(typeof serie == undefined || serie == -1 || serie == false || pieces[i].serie == serie) &&
(typeof index == undefined || index == -1 || index == false || pieces[i].index == index) &&
(typeof subsection == undefined || subsection == -1 || subsection == false || pieces[i].subSection == subsection)
)
return pieces[i];
}
return false;
},
samePiecePath : function(piece1, piece2) {
return (((typeof piece1.section == undefined || piece1.section == -1 || piece1.section == false) && (typeof piece2.section == undefined || piece2.section == -1 || piece2.section == false)) || piece1.section == piece2.section) &&
(((typeof piece1.serie == undefined || piece1.serie == -1 || piece1.serie == false) && (typeof piece2.serie == undefined || piece2.serie == -1 || piece2.serie == false)) || piece1.serie == piece2.serie) &&
(((typeof piece1.index == undefined || piece1.index == -1 || piece1.index == false) && (typeof piece2.index == undefined || piece2.index == -1 || piece2.index == false)) || piece1.index == piece2.index) &&
(((typeof piece1.subSection == undefined || piece1.subSection == -1 || piece1.subSection == false) && (typeof piece2.subSection == undefined || piece2.subSection == -1 || piece2.subSection == false)) || piece1.subSection == piece2.subSection);
},
executeIfChanged : function(env, changes) {
if (!env.newopt)
return true;
for (var i = 0; i < changes.length; i++) {
if (changes[i][changes[i].length - 1] == "*") {
for (var j in env.newopt)
if (j.substring(0, changes[i].length - 1) + "*" == changes[i])
return true;
}
else if (changes[i] == 'series' && (env.newopt.series || env.newopt.defaultSeries))
return true;
else if (changes[i] == 'axis' && (env.newopt.axis || env.newopt.defaultAxis))
return true;
else if (changes[i].substring(0, 9) == "features.") {
changes[i] = changes[i].substring(9);
if (env.newopt.features && env.newopt.features[changes[i]])
return true;
}
else if (typeof env.newopt[changes[i]] != 'undefined')
return true;
}
return false;
},
/**
* Ottiene le proprietà di una "Area" definita nella configurazione (options),
* identificata da section / serie / index / subsection, e facendo il merge
* di tutti i defaults innestati.
*/
areaProps : function(env, section, serie, index, subsection) {
var props;
// TODO fare una cache e fix del toLowerCase (devono solo fare la prima lettera
if (!subsection) {
if (typeof serie == 'undefined' || !serie)
props = env.opt[section.toLowerCase()];
else {
props = this._clone(env.opt['default' + section]);
if (env.opt[section .toLowerCase()] && env.opt[section.toLowerCase()][serie])
props = this._mergeObjects(props, env.opt[section.toLowerCase()][serie]);
if ((typeof index != 'undefined') && index >= 0 && props['values'] && props['values'][index])
props = this._mergeObjects(props, props['values'][index]);
}
} else {
props = this._clone(env.opt[subsection.toLowerCase()]);
if (typeof serie == 'undefined' || !serie) {
if (env.opt[section.toLowerCase()] && env.opt[section.toLowerCase()][subsection.toLowerCase()])
props = this._mergeObjects(props, env.opt[section.toLowerCase()][subsection.toLowerCase()]);
} else {
if (env.opt['default' + section] && env.opt['default' + section][subsection.toLowerCase()])
props = this._mergeObjects(props, env.opt['default' + section][subsection.toLowerCase()]);
if (env.opt[section .toLowerCase()] && env.opt[section.toLowerCase()][serie] && env.opt[section.toLowerCase()][serie][subsection.toLowerCase()])
props = this._mergeObjects(props, env.opt[section.toLowerCase()][serie][subsection.toLowerCase()]);
if (props && (typeof index != 'undefined') && index > 0 && props['values'] && props['values'][index])
props = this._mergeObjects(props, props['values'][index]);
}
}
return props;
},
absrectpath : function(x1, y1, x2, y2, r) {
// TODO Supportare r
return [['M', x1, y1], ['L', x1, y2], ['L', x2, y2], ['L', x2, y1], ['z']];
},
linepathAnchors : function(p1x, p1y, p2x, p2y, p3x, p3y, rounded) {
var method = 1;
if (rounded && rounded.length) {
method = rounded[1];
rounded = rounded[0];
}
if (!rounded)
rounded = 1;
var l1 = (p2x - p1x) / 2,
l2 = (p3x - p2x) / 2,
a = Math.atan((p2x - p1x) / Math.abs(p2y - p1y)),
b = Math.atan((p3x - p2x) / Math.abs(p2y - p3y));
a = p1y < p2y ? Math.PI - a : a;
b = p3y < p2y ? Math.PI - b : b;
if (method == 2) {
// If added by Bago to avoid curves beyond min or max
if ((a - Math.PI / 2) * (b - Math.PI / 2) > 0) {
a = 0;
b = 0;
} else {
if (Math.abs(a - Math.PI / 2) < Math.abs(b - Math.PI / 2))
b = Math.PI - a;
else
a = Math.PI - b;
}
}
var alpha = Math.PI / 2 - ((a + b) % (Math.PI * 2)) / 2,
dx1 = l1 * Math.sin(alpha + a) / 2 / rounded,
dy1 = l1 * Math.cos(alpha + a) / 2 / rounded,
dx2 = l2 * Math.sin(alpha + b) / 2 / rounded,
dy2 = l2 * Math.cos(alpha + b) / 2 / rounded;
return {
x1: p2x - dx1,
y1: p2y + dy1,
x2: p2x + dx2,
y2: p2y + dy2
};
},
linepathRevert : function(path) {
var rev = [], anc = false;
for (var i = path.length - 1; i >= 0; i--) {
switch (path[i][0]) {
case "M" : case "L" :
if (!anc)
rev.push( [ rev.length ? "L" : "M", path[i][1], path[i][2] ] );
else
rev.push( [ "C", anc[0], anc[1], anc[2], anc[3], path[i][1], path[i][2] ] );
anc = false;
break;
case "C" :
if (!anc)
rev.push( [ rev.length ? "L" : "M", path[i][5], path[i][6] ] );
else
rev.push( [ "C", anc[0], anc[1], anc[2], anc[3], path[i][5], path[i][6] ] );
anc = [ path[i][3], path[i][4], path[i][1], path[i][2] ];
}
}
return rev;
},
linepath : function ( points, rounded ) {
var path = [];
if (rounded) {
var anc = false;
for (var j = 0, jj = points.length - 1; j < jj ; j++) {
if (j) {
var a = this.linepathAnchors(points[j - 1][0], points[j - 1][1], points[j][0], points[j][1], points[j + 1][0], points[j + 1][1], rounded);
path.push([ "C", anc[0], anc[1], a.x1, a.y1, points[j][0], points[j][1] ]);
anc = [ a.x2, a.y2 ];
} else {
path.push([ "M", points[j][0], points[j][1] ]);
anc = [ points[j][0], points[j][1] ];
}
}
if (anc)
path.push([ "C", anc[0], anc[1], points[jj][0], points[jj][1], points[jj][0], points[jj][1] ]);
} else
for (var i = 0; i < points.length; i++) {
var x = points[i][0], y = points[i][1];
path.push([i == 0 ? "M" : "L", x, y]);
}
return path;
},
lineareapath : function (points1, points2, rounded) {
var path = this.linepath(points1, rounded), path2 = this.linepathRevert(this.linepath(points2, rounded));
for (var i = 0; i < path2.length; i++)
path.push( !i ? [ "L", path2[0][1], path2[0][2] ] : path2[i] );
if (path.length)
path.push(['z']);
return path;
},
/**
* Prende la coordinata X di un passo di un path
*/
getX : function(p, pos) {
switch (p[0]) {
case 'CIRCLE':
return p[1];
case 'RECT':
return p[!pos ? 1 : 3];
case 'SLICE':
return p[1];
default:
return p[p.length - 2];
}
},
/**
* Prende la coordinata Y di un passo di un path
*/
getY : function(p, pos) {
switch (p[0]) {
case 'CIRCLE':
return p[2];
case 'RECT':
return p[!pos ? 2 : 4];
case 'SLICE':
return p[2];
default:
return p[p.length - 1];
}
},
/**
* Prende il centro di un path
*
* @param offset un offset [x,y] da applicare. Da notare che gli assi potrebbero essere dipendenti dalla figura
* (ad esempio per lo SLICE x e' l'asse che passa dal centro del cerchio, y l'ortogonale).
*/
getCenter: function(path, offset) {
if (!path.path)
return false;
if (path.path.length == 0)
return false;
if (!offset)
offset = [0, 0];
if (path.center)
return [path.center[0] + offset[0], path.center[1] + offset[1]];
var p = path.path[0];
switch (p[0]) {
case 'CIRCLE':
return [p[1] + offset[0], p[2] + offset[1]];
case 'RECT':
return [(p[1] + p[2])/2 + offset[0], (p[3] + p[4])/2 + offset[1]];
case 'SLICE':
var popangle = p[5] + (p[6] - p[5]) / 2;
var rad = Math.PI / 180;
return [
p[1] + (p[4] + ((p[3] - p[4]) / 2) + offset[0]) * Math.cos(-popangle * rad) + offset[1] * Math.cos((-popangle-90) * rad),
p[2] + (p[4] + ((p[3] - p[4]) / 2) + offset[0]) * Math.sin(-popangle * rad) + offset[1] * Math.sin((-popangle-90) * rad)
];
}
// WARN Complex paths not supported
alert('ElyCharts: getCenter with complex path not supported');
return false;
},
/**
* Sposta il path passato di un offset [x,y]
* Il risultato e' il nuovo path
*
* @param offset un offset [x,y] da applicare. Da notare che gli assi potrebbero essere dipendenti dalla figura
* (ad esempio per lo SLICE x e' l'asse che passa dal centro del cerchio, y l'ortogonale).
* @param marginlimit se true non sposta oltre i margini del grafico (applicabile solo su path standard o RECT)
* @param simple se true lo spostamento e' sempre fatto sul sistema [x, y] complessivo (altrimenti alcuni elementi, come lo SLICE,
* si muovono sul proprio sistema di coordinate - la x muove lungo il raggio e la y lungo l'ortogonale)
*/
movePath : function(env, path, offset, marginlimit, simple) {
var p = [], i;
if (path.length == 1 && path[0][0] == 'RECT')
return [ [path[0][0], this._movePathX(env, path[0][1], offset[0], marginlimit), this._movePathY(env, path[0][2], offset[1], marginlimit), this._movePathX(env, path[0][3], offset[0], marginlimit), this._movePathY(env, path[0][4], offset[1], marginlimit)] ];
if (path.length == 1 && path[0][0] == 'SLICE') {
if (!simple) {
var popangle = path[0][5] + (path[0][6] - path[0][5]) / 2;
var rad = Math.PI / 180;
var x = path[0][1] + offset[0] * Math.cos(- popangle * rad) + offset[1] * Math.cos((-popangle-90) * rad);
var y = path[0][2] + offset[0] * Math.sin(- popangle * rad) + offset[1] * Math.cos((-popangle-90) * rad);
return [ [path[0][0], x, y, path[0][3], path[0][4], path[0][5], path[0][6] ] ];
}
else
return [ [ path[0][0], path[0][1] + offset[0], path[0][2] + offset[1], path[0][3], path[0][4], path[0][5], path[0][6] ] ];
}
if (path.length == 1 && path[0][0] == 'CIRCLE')
return [ [ path[0][0], path[0][1] + offset[0], path[0][2] + offset[1], path[0][3] ] ];
if (path.length == 1 && path[0][0] == 'TEXT')
return [ [ path[0][0], path[0][1], path[0][2] + offset[0], path[0][3] + offset[1] ] ];
if (path.length == 1 && path[0][0] == 'LINE') {
for (i = 0; i < path[0][1].length; i++)
p.push( [ this._movePathX(env, path[0][1][i][0], offset[0], marginlimit), this._movePathY(env, path[0][1][i][1], offset[1], marginlimit) ] );
return [ [ path[0][0], p, path[0][2] ] ];
}
if (path.length == 1 && path[0][0] == 'LINEAREA') {
for (i = 0; i < path[0][1].length; i++)
p.push( [ this._movePathX(env, path[0][1][i][0], offset[0], marginlimit), this._movePathY(env, path[0][1][i][1], offset[1], marginlimit) ] );
var pp = [];
for (i = 0; i < path[0][2].length; i++)
pp.push( [ this._movePathX(env, path[0][2][i][0], offset[0], marginlimit), this._movePathY(env, path[0][2][i][1], offset[1], marginlimit) ] );
return [ [ path[0][0], p, pp, path[0][3] ] ];
}
var newpath = [];
// http://www.w3.org/TR/SVG/paths.html#PathData
for (var j = 0; j < path.length; j++) {
var o = path[j];
switch (o[0]) {
case 'M': case 'm': case 'L': case 'l': case 'T': case 't':
// (x y)+
newpath.push([o[0], this._movePathX(env, o[1], offset[0], marginlimit), this._movePathY(env, o[2], offset[1], marginlimit)]);
break;
case 'A': case 'a':
// (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
newpath.push([o[0], o[1], o[2], o[3], o[4], o[5], this._movePathX(env, o[6], offset[0], marginlimit), this._movePathY(env, o[7], offset[1], marginlimit)]);
break;
case 'C': case 'c':
// (x1 y1 x2 y2 x y)+
newpath.push([o[0], o[1], o[2], o[3], o[4], this._movePathX(env, o[5], offset[0], marginlimit), this._movePathY(env, o[6], offset[1], marginlimit)]);
break;
case 'S': case 's': case 'Q': case 'q':
// (x1 y1 x y)+
newpath.push([o[0], o[1], o[2], this._movePathX(env, o[3], offset[0], marginlimit), this._movePathY(env, o[4], offset[1], marginlimit)]);
break;
case 'z': case 'Z':
newpath.push([o[0]]);
break;
}
}
return newpath;
},
_movePathX : function(env, x, dx, marginlimit) {
if (!marginlimit)
return x + dx;
x = x + dx;
return dx > 0 && x > env.opt.width - env.opt.margins[1] ? env.opt.width - env.opt.margins[1] : (dx < 0 && x < env.opt.margins[3] ? env.opt.margins[3] : x);
},
_movePathY : function(env, y, dy, marginlimit) {
if (!marginlimit)
return y + dy;
y = y + dy;
return dy > 0 && y > env.opt.height - env.opt.margins[2] ? env.opt.height - env.opt.margins[2] : (dy < 0 && y < env.opt.margins[0] ? env.opt.margins[0] : y);
},
/**
* Ritorna le proprieta SVG da impostare per visualizzare il path non SVG passato (se applicabile, per CIRCLE e TEXT non lo e')
*/
getSVGProps : function(path, prevprops) {
var props = prevprops ? prevprops : {};
var type = 'path', value;
if (path.length == 1 && path[0][0] == 'RECT')
value = common.absrectpath(path[0][1], path[0][2], path[0][3], path[0][4], path[0][5]);
else if (path.length == 1 && path[0][0] == 'SLICE') {
type = 'slice';
value = [ path[0][1], path[0][2], path[0][3], path[0][4], path[0][5], path[0][6] ];
} else if (path.length == 1 && path[0][0] == 'LINE')
value = common.linepath( path[0][1], path[0][2] );
else if (path.length == 1 && path[0][0] == 'LINEAREA')
value = common.lineareapath( path[0][1], path[0][2], path[0][3] );
else if (path.length == 1 && (path[0][0] == 'CIRCLE' || path[0][0] == 'TEXT' || path[0][0] == 'DOMELEMENT' || path[0][0] == 'RELEMENT'))
return prevprops ? prevprops : false;
else
value = path;
if (type != 'path' || (value && value.length > 0))
props[type] = value;
else if (!prevprops)
return false;
return props;
},
/**
* Disegna il path passato
* Gestisce la feature pixelWorkAround
*/
showPath : function(env, path, paper) {
path = this.preparePathShow(env, path);
if (!paper)
paper = env.paper;
if (path.length == 1 && path[0][0] == 'CIRCLE')
return paper.circle(path[0][1], path[0][2], path[0][3]);
if (path.length == 1 && path[0][0] == 'TEXT')
return paper.text(path[0][2], path[0][3], path[0][1]);
var props = this.getSVGProps(path);
// Props must be with some data in it
var hasdata = false;
for (var k in props) {
hasdata = true;
break;
}
return props && hasdata ? paper.path().attr(props) : false;
},
/**
* Applica al path le modifiche per poterlo visualizzare
* Per ora applica solo pixelWorkAround
*/
preparePathShow : function(env, path) {
return env.opt.features.pixelWorkAround.active ? this.movePath(env, this._clone(path), [.5, .5], false, true) : path;
},
/**
* Ritorna gli attributi Raphael completi di un piece
* Per attributi completi si intende l'insieme di attributi specificato,
* assieme a tutti gli attributi calcolati che determinano lo stato
* iniziale di un piece (e permettono di farlo ritornare a tale stato).
* In genere viene aggiunto il path SVG, per il circle vengono aggiunti
* i dati x,y,r
*/
getPieceFullAttr : function(env, piece) {
if (!piece.fullattr) {
piece.fullattr = this._clone(piece.attr);
if (piece.path)
switch (piece.path[0][0]) {
case 'CIRCLE':
var ppath = this.preparePathShow(env, piece.path);
piece.fullattr.cx = ppath[0][1];
piece.fullattr.cy = ppath[0][2];
piece.fullattr.r = ppath[0][3];
break;
case 'TEXT': case 'DOMELEMENT': case 'RELEMENT':
break;
default:
piece.fullattr = this.getSVGProps(this.preparePathShow(env, piece.path), piece.fullattr);
}
if (typeof piece.fullattr.opacity == 'undefined')
piece.fullattr.opacity = 1;
}
return piece.fullattr;
},
show : function(env, pieces) {
pieces = this.getSortedPathData(pieces);
common.animationStackStart(env);
var previousElement = false;
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];