UNPKG

upbeat-dashboard

Version:

ERROR: No README.md file found!

1,257 lines (1,102 loc) 161 kB
/********* 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];