pax_global_header00006660000000000000000000000064124031171050014504gustar00rootroot0000000000000052 comment=e82847f6dc02e2e14a6e847ba85d47eef8dd7d2a elycharts.js-2.1.5+ds/000077500000000000000000000000001240311710500145245ustar00rootroot00000000000000elycharts.js-2.1.5+ds/bin/000077500000000000000000000000001240311710500152745ustar00rootroot00000000000000elycharts.js-2.1.5+ds/bin/build.sh000066400000000000000000000011501240311710500167240ustar00rootroot00000000000000#/bin/sh cat src/elycharts_defaults.js src/elycharts_core.js src/elycharts_manager_anchor.js src/elycharts_manager_animation.js src/elycharts_manager_highlight.js src/elycharts_manager_label.js src/elycharts_manager_legend.js src/elycharts_manager_mouse.js src/elycharts_manager_tooltip.js src/elycharts_chart_line.js src/elycharts_chart_pie.js > dist/elycharts.js yui-compressor dist/elycharts.js -o dist/elycharts.min.js cat src/elycharts_defaults.js src/elycharts_core.js src/elycharts_manager_* src/elycharts_chart_* > dist/elycharts-full.js yui-compressor dist/elycharts-full.js -o dist/elycharts-full.min.js elycharts.js-2.1.5+ds/docs/000077500000000000000000000000001240311710500154545ustar00rootroot00000000000000elycharts.js-2.1.5+ds/docs/changelog_en.txt000066400000000000000000000151341240311710500206320ustar00rootroot00000000000000v2.1.5 (2014-02-19) - fixed support for animations for charts with null values - added support for null values (avgOverNulls: false) in line charts - changed stepAnimation by adding the "active" parameter to be enabled, otherwise ignored (Issue #35) - fixed stepAnimation to not be enabled if stepAnimation is declared to false (Issue #35) - rewritten the update logic to work even without animation manager and to deal with partial updates (Issue #36) - fixed labels visibility un update (they were hidden but never made visibile on update if needed) - fixed legend rewriting/updating (Issue #38) - fixed support for roundedCorners in legend "dots" (adding support for "rounded rects" svg paths and fixing move method for 'Q'). - fixed tooltip visiblity when some tooltip value is missing (compared to number of the serie values) (Issue #37) - updating values now recompute width/height and correctly adapt the paper and chart to the new size. - separate computed width from option.width (so empty width is correctly applied on update): plugins should now use env.width instead of env.opt.width. - added support for rPerc (radius expressed in percentage of available space) for pie charts. - changed pixelWorkAround to be enabled only on SVG (to be verified) - fixed tooltip (and mouse) management in IE compatible mode (Issue #10) - fixed erros when using "legend: false" or legend without margin definitions (Issue #14) - added support for valuesPalette and seriesPalette feature to automatically assign colors starting from a cycling palette (Issue #9) - added support for single value coloring in bar charts (each column can be printed with a different color) - added caching for internal properties "inheritance" computation (enabled by default, disable by setting enableInternalCaching to false) - added "margins" support to pie chart and fixed cx/cy usage (cy was ignored in favor of cx only) (Issue #31) - rewrote "color" property inheritance to better deal with mixed line/bar charts, chart updates, have better performances and pie support (Issue #33) - rewrite "legend" properties management to simplify code (should not change the behaviour) - added "clear" method to featuresmanager so that features can be properly react to chart "clearing" (Issue #32 hint by pduval@github) - bar chart: inherit stroke color from serie color if not specified (Issue #30) - fixed width computation for mouse areas using mousearea: "index" option - added "barOverlapPerc" property to allow overlapping of columns for the same X in different series - update old copyright year references to 2010-2014 v2.1.4 (2014-01-14) - fix compatibility with RaphaelJs 2.0+: .rotation attribute is no more available so axis titles were not rotated (Issue #27) - fix compatibility with jQuery 1.9+: $.browser.msie no more available. Switched to Raphael.VML, with the hope this was the intended check (Issue #28) - fix compatibility with Raphaeljs 2.0+: animations didn't work due to animateWith signature/behaviour changes (Issue #23) - fix for colors propagation from defaultSeries to single serie (Issue #11) - added "auto" modes for labelsAnchor and labelsPos so that most use cases it will simply set sensible values for them based on labelsCenter/labelsRotate - added support for labels hiding (for overlapping and *horizontal* overflow) also for rotated labels - various fixes for x labels. (mostly rewrote axis labels handling) - fix for "grid.nx" values when nx < ny and they are not "auto" (Issue #13) - highlight: improvement in stepAnimation management and overlayprops. - added support for function as tooltip value v2.1.3 - changed default value to "false" for axis.labels - changed default value to "false" for series.dot - changed default value to "false" for features.grid.draw - changed default value to "4" for features.grid.ny - changed default value to "false" for features.grid.forceBorder - changed default value to "4" for features.legend.dotR - added support for command execution via $.chart("COMMAND"). Supported commands: config, clear - added support for null values in line charts (displayed with an average of near points) - added series.dotShowOnNull and series.mouseareaShowOnNull (default to false) to support null values and what to do with them - added features.grid.evenVProps, evenHProps, oddVProps, oddHProps to display grid bands - added offset tooltip option - added support for "style" option - highlight: added support for scale with dots, optimized scale properties to have only one numeric value (not array) - better configuration handling for templates and subsequent calls - fix in config colors normalization - better handling for values and labels (support for null/empty labels) - fix for labels overflow on X axis - pie chart: fixed support for serie.r < 0 - fix for tooltip color/frameProps settings where specified with shortcut "serie.color" - line chart: fixed line chart not rounded - legend: fixed a lot (was broken in almost all configurations...) - line chart: changed the way labels are drawn and spaced on x axis (fix for labelsHideCovered) v2.1.2 - support for series.empty.tooltip e series.empty.label - refactoring object visualization phase - support for "zindex" property, to set the order of the object. It should be specified in every "*Props" settings, like a SVG Attr - default zIndex for dots in line chart is "5" (so dots are shown above other objects) - fix #6: Using a simple line chart with a single serie and a single value in that serie breaks elychart. - cleanup - fix transitions v2.1.1 - REFACTORING: true modular management, code split in several components - script to build library in single form and in minimized single form - fix rendering errors - fix highlight in hidden paths - fix color management in line chart - support for "valueThresold" option in pie chart - fix drawing 360 degrees pie slice - improvements in anchorManager for bind support (fix when transitions occours) - fix manager tooltip - line chart: support for rounded : Array, to set a different method of rounding ( [#, 2] is for method suggested by Bago ) - fix in number precision and sharpening for axis min/max and label calculations [#4] - fix in reg animation with only 1 value - fix in label rendering when hiding all series and re-showing one [#1] - fix in funnel highlight animation [#2] - code cleanup - fix in line generation for series with only one value - moved option labelsCenter to features.grid.labelsCenter - support for features.mousearea.indexCenter (bar|line|auto). Improvement in serie.lineCenter and features.highlight.indexHighlight, features.grid.labelsCenter (both suppors "auto" value) - fixes in reg animation - fix empty pie animations - added option serie.tooltip.activeelycharts.js-2.1.5+ds/docs/changelog_it.txt000066400000000000000000000172411240311710500206450ustar00rootroot00000000000000This is no more updated, please look at the changelog_en.txt english changelog for newer changes. v2.1.2 - supporto per series.empty.tooltip e series.empty.label - migliorie nella fase di visualizzazione degli oggetti - supporto per nuova proprietà "zindex" (da specificare in qualunque props SVG) - lo zindex di default per i "dots" nel grafico a linee è 5 (per mostrare i punti sopra gli altri oggetti) - fix line chart e problemi con serie di un solo valore [#6] - cleanup codice - fix transizioni v2.1.1 - REFACTORING per gestione modulare effettiva, split del codice nei vari componenti - script per riunificazione e minimizzazione codice da distribuire - fix errore rendering - fix highlight con path nascosti - fix gestione colori in line chart - supporto opzione "valueThreshold" per pie chart - fix pie slice di 360 gradi - migliorie in anchorManager per gestione bind su elementi (fix in caso di transizioni) - fix manager tooltip - line chart: supporto per opzione "rounded" come array. [#, 2] e' per usare il metodo suggerito da Bago ( [#, 1] o solo # è il metodo standard) - fix in gestione precisione dei numeri in calcolo min/max degli assi e in view label [#4] - fix in animazione "reg" con serie di un solo valore - fix in visualizzazione label asse dopo aver nascosto tutte le serie e rivisualizzato una [#1] - fix in funnel highlight animation [#2] - cleanup codice - fix: generazione linea con un solo valore - spostata opzione labelsCenter in features.grid.labelsCenter - supporto per features.mousearea.indexCenter (bar|line|auto). Migliorie in serie.lineCenter e features.highlight.indexHighlight, features.grid.labelsCenter (entrambi supportano il valore "auto") - fix vari in animazione reg - fix animazioni con pie vuota - aggiunta opzione serie.tooltip.active v2.1.0 - RENAME da elysia_charts a elycharts - fix hightlight.move - supporto per serie.anchor.userMouseEnter - fix transizioni da serie (e parti di serie) non renderizzate - line chart: cambiato supporto per linee arrotondate v2.0.10 - WARN: necessita di aggiornamento a Raphael1.5 - supporto transizioni di configurazione per pie - supporto pie con serie multiple - supporto per series.highlight.restoreSpeed e restoreEasing - supporto per tooltip.width="auto" (= 0) e tooltip.height="auto" (= 0) - Disabilita automaticamente frame SVG - refactoring gestione slice/archi, con gestione corretta animazione (necessita' feature "customAttribute" di Raphael1.5) - fix funnel senza dati o con dati tutti 0 - fix transizioni in labelmanager e highlightmanager v2.0.9 - bugfix in normalizzazione configurazioni passate - modificato valore di default di indexHighlightProps - migliorata gestione positionHandler per tooltip, in modo da gestire la posizione del mouse anche su IE - fix posizionamento label su IE (che da problemi con impostazioni opacità, e prima impostava opacity: 1 anche se non ce n'era bisogno) v2.0.8 - nuova gestione del axis.normalize: ora basato sul numero di cifre significative, i vecchi valori "auto" e "autony" vengono entrambi tradotti con "2" - aggiunto startAnimation.subType usato da alcune animazioni. Supportato subType = 0,1,2 per animation.grow (subType = 2 sostituisce type = grow2) - aggiunta opzione features.debug.active per attivare debug - bugfix nomi serie con maiuscole - bugfix animazioni con serie non visibili - bugfix divisione per zero in barline - bugfix label.frameAnchor top|bottom invertiti v2.0.7 - nuova gestione grid. Spostate le impostazioni in opt.features.grid (lasciata compatibilita' con vecchie opzioni), nuove opzioni grid.draw (per nascondere le linee orizzontali e/o verticali), migliorato supporto forceBorder nuova opzione grid.ticks.* per supportare le barrette sugli assi - bugfix pie - aggiunto startAnimation.type = 'grow' e 'grow2' per barline - fix animation.grow v2.0.6 - supporto per "template", che permette di specificare quale configurazione di default utilizzare (permette quindi di avere dei "template" di configurazioni) WARN: $.elysia_charts.default_options e' stato rinominato in $.elysia_charts.templates (mantenuta compatibilita' all'indietro) - aggiunte le opzioni startAngle e clockwise a grafico "pie" per definire l'angolo iniziale e l'orientamento delle fette - semplificato serie.stackedWith: ora si chiama solo "stacked" (tenuta compatibilita' all'indietro) ed e' possibile specificare solo "true" per impilare automaticamente con la precedente serie visibile - supporto di serie.labelsAnchor anche per assi l ed r (in modo da poter specificare l'allineamento delle label dell'asse) - supporto di serie.labelsFormatHandler per specificare una funzione di formattazione delle label (sia per assi l,r che per asse x) - supporto migliore axis.labelsRotate con valori negativi - gestione corretta visible=true|false in cambiamenti grafici e per gestione stackedWith - cambiamento z-index di balloon e label per renderli selezionabili - impostato { cursor : "default" } in defaultSeries.label.style e tolto da opzioni forzate - bugfix (major): risolte problematiche in interruzione di animazioni (che provocava problemi in caso di interazione col grafico in vari momenti: animazione iniziale, animazioni intermedie, highlight...) - bugfix: frameAnchor non funzionava correttamente - bugfix: frame tooltip spariva dopo un cambiamento di grafico - bugfix: Indexhighlight:bar con mousearea.type=index non funziona v2.0.5 - impostato defaultSeries.axis = 'l', features.balloons.width = 0 - fix stackedWith e startAnimation.grow|avg|reg - posizionamento relativo invece di assoluto per balloons - supporto linea balloons mediante features.balloons.line e features.balloons.lineProps - gestione balloon e label per ultimo indice di un funnel associato a bottom sector - supporto debug della configurazione attuale mediante libreria DP_Debug (http://www.depressedpress.com/Content/Development/JavaScript/Extensions/DP_DeBug/Index.cfm) - ripristinato supporto legenda tramite features.legend - fix titoli assi v2.0.4 - migliorato supporto pixelWorkAround - fix metodi interni - spostate config fadeDelay e moveDelay in opt.features.tooltip, areaMoveDelay in opt.features.mousearea - supporto opt.features.mousearea.syncTag per collegare le parti interattive di grafici diversi (se si va su uno si disattivano le altre) - migliorie posizionamento tooltip - supporto opt.features.tooltip.positionHandler per definire la propria funzione di posizionamento tooltip - supporto per passaggio di oggetti DOM/JQUERY come label v2.0.3 - ripristinato supporto opzione "color" - gestione transizioni grafici mediante chiamate successive a $(X).chart(...) - supporto series.stepAnimation (transizioni grafici), features.animation.startAnimation (generazione elementi non-serie), features.animation.stepAnimation (transizioni elementi non-serie) - supporto series.visible - grafico barline: supporto direction = 'rtl' - fix vari v2.0.2 - supporto piechart vuoto e serie "empty" - fix passaggio width ed height - nuova opzione "interactive" - nuove opzioni highlight: scaleSpeed, scaleEasing, moveSpeed, moveEasing - applicabile highlight "scale" a funnel e piechart (prima implementazione) - supporto opzione "delay" in startAnimation - supporto valori cumulativi in line chart, opzione series[x].cumulative - supporto opzione axis[X].labelsCompactUnits (= ['k','m']) per accorciare le label numeriche con unita' (prima implementazione) - rename interno strutture codice - migliorie gestione sync animazioni v2.0.1 - ripristinato supporto label in pie e funnel - label HTML con offset - supporto passaggio anchor come elementi dom - ottimizzazione gestione mouseover - applicabile l'highlight "move" anche al funnel e migliorie ottimizzazioni generali all'highlight - nuovo grafico barline, e supporto highlight "scale" e "move" - fix vari v2.0.0 - refactoring completo elycharts.js-2.1.5+ds/pom.xml000066400000000000000000000120651240311710500160450ustar00rootroot00000000000000 4.0.0 elycharts elycharts 2.1.5 Elycharts pom croche-releases http://croche.googlecode.com/svn/repository/releases oss.sonatype.org http://oss.sonatype.org/content/groups/public target/ target/test-scripts maven-resources-plugin 2.6 copy-resources process-resources copy-resources ${basedir}/target/extra-resources resources true croche.maven maven-merge-file-plugin 0.4 merge-release-notes process-resources merge dist/elycharts-full.js src ${basedir}/target/extra-resources header elycharts_default elycharts_core elycharts_manager elycharts_chart *.js /********* Source File: #{parent.name}/#{file.name} *********/\n UTF-8 dist/elycharts.js src ${basedir}/target/extra-resources header elycharts_default elycharts_core elycharts_manager elycharts_chart header.js elycharts_defaults.js elycharts_core.js elycharts_manager_anchor.js elycharts_manager_animation.js elycharts_manager_highlight.js elycharts_manager_label.js elycharts_manager_legend.js elycharts_manager_mouse.js elycharts_manager_tooltip.js elycharts_chart_line.js elycharts_chart_pie.js /********* Source File: #{parent.name}/#{file.name} *********/\n UTF-8 net.alchim31.maven yuicompressor-maven-plugin 1.2 compress-js package compress dist dist .min -1 true *-min.js *.min.js *header.js elycharts.js-2.1.5+ds/resources/000077500000000000000000000000001240311710500165365ustar00rootroot00000000000000elycharts.js-2.1.5+ds/resources/header.js000066400000000000000000000006231240311710500203250ustar00rootroot00000000000000/*!********************************************************************* * ELYCHARTS v${pom.version} $Id$ * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ elycharts.js-2.1.5+ds/src/000077500000000000000000000000001240311710500153135ustar00rootroot00000000000000elycharts.js-2.1.5+ds/src/elycharts_chart_barline.js000066400000000000000000000051311240311710500225240ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * CHART: BARLINE * * Singola barra orizzontale contenente vari valori. * * L'idea è che possa essere vista come una linechart orizzontale * invece di verticale, con solo serie di tipo bar e con un solo valore. * In futuro (quando sarà possibile far linechart orizzontali) potrebbe * proprio essere renderizzata in questo modo. **********************************************************************/ $.elycharts.barline = { init : function($env) { }, draw : function(env) { var paper = env.paper; var opt = env.opt; env.xmin = opt.margins[3]; env.xmax = env.width - opt.margins[1]; env.ymin = opt.margins[0]; env.ymax = env.height - opt.margins[2]; var maxvalue = 0; for (var serie in opt.values) { var values = opt.values[serie]; var value = values[0]; var plot = { props : common.areaProps(env, 'Series', serie) }; env.plots[serie] = plot; if (!plot.props.stacked || !env.plots[plot.props.stacked]) { plot.from = 0; } else { plot.from = env.plots[plot.props.stacked].to; } plot.to = plot.from + value; if (plot.to > maxvalue) maxvalue = plot.to; } // TODO opt.max dovrebbe essere opt.axis[?].max ? if (typeof opt.max != 'undefined') maxvalue = opt.max; if (!maxvalue) maxvalue = 1; var pieces = []; for (serie in opt.values) { plot = env.plots[serie]; var d = (env.xmax - env.xmin) / maxvalue; if (opt.direction != 'rtl') pieces.push({ paths : [ { path : [ [ 'RECT', env.xmin + d * plot.from, env.ymin, env.xmin + d * plot.to, env.ymax] ], attr : plot.props.plotProps } ], section: 'Series', serie: serie, subSection : 'Plot', mousearea : 'paths' }); else pieces.push({ paths : [ { path : [ [ 'RECT', env.xmax - d * plot.from, env.ymin, env.xmax - d * plot.to, env.ymax] ], attr : plot.props.plotProps } ], section: 'Series', serie: serie, subSection : 'Plot', mousearea : 'paths' }); } return pieces; } }; })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_chart_funnel.js000066400000000000000000000116361240311710500224060ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * CHART: FUNNEL **********************************************************************/ $.elycharts.funnel = { init : function($env) { }, draw : function(env) { var paper = env.paper; var opt = env.opt; env.xmin = opt.margins[3]; env.xmax = env.width - opt.margins[1]; env.ymin = opt.margins[0] + Math.abs(opt.rh); for (var serie in opt.values) { var values = opt.values[serie]; var lastwidthratio = opt.method == 'width' ? values[values.length - 1] / (values[0] ? values[0] : 1) : Math.sqrt(values[values.length - 1] / (values[0] ? values[0] : 1) * Math.pow(1 / 2, 2)) * 2; env.ymax = env.height - opt.margins[2] - lastwidthratio * Math.abs(opt.rh); var pieces = this.pieces(env, serie, 0, 1, 1, values); } return pieces; }, pieces : function(env, serie, hstart, hend, wratio, values) { var path, pieces = [], opt = env.opt; var v0 = values[0] ? values[0] : 1; var h = hstart; // Starting height var hslices = (hend - hstart - opt.topSector - opt.bottomSector) / (values.length > 1 ? values.length - 1 : 1); var w = wratio; // Starting width if ((path = this.edge(env, h, w, true))) pieces.push({path : path, section: 'Edge', attr : env.opt.edgeProps}); if (opt.topSector > 0 && (path = this.section(env, h, h = h + opt.topSector, w, w))) pieces.push({path : path.path, center: path.center, rect: path.rect, section: 'Sector', serie: 'top', attr : env.opt.topSectorProps}); var paths = []; for (var i = 1; i < values.length; i++) { var v = values[0] ? values[i] : 1; // METODO "cutarea" // area taglio attuale / area taglio iniziale = valore attuale / valore iniziare // => larghezza attuale = sqrt(values[i] / values[0] * pow(larghezza iniziale / 2, 2)) * 2 if ((path = this.section(env, h, h = h + hslices, w, opt.method == 'width' ? w = v / v0 * wratio : w = Math.sqrt(v / v0 * Math.pow(wratio / 2, 2)) * 2))) var props = common.areaProps(env, 'Series', serie, i - 1); paths.push({path : path.path, center: path.center, rect: path.rect, attr : props.plotProps}); } pieces.push({section: 'Series', serie: serie, paths : paths, subSection : 'Plot', mousearea : 'paths' }); if (opt.bottomSector > 0 && (path = this.section(env, h, h = h + opt.bottomSector, w, w))) pieces.push({path : path.path, center: path.center, rect: path.rect, section: 'Sector', serie: 'bottom', attr : env.opt.bottomSectorProps}); if ((path = this.edge(env, h, w, false))) pieces.push({path : path, section : 'Edge', attr : env.opt.edgeProps}); return pieces; }, section : function(env, hfrom, hto, wfrom, wto) { x1a = env.xmin + (env.xmax - env.xmin) * (wfrom / -2 + 1/2); x2a = env.xmin + (env.xmax - env.xmin) * (wfrom / 2 + 1/2); x1b = env.xmin + (env.xmax - env.xmin) * (wto / -2 + 1/2); x2b = env.xmin + (env.xmax - env.xmin) * (wto / 2 + 1/2); y1 = env.ymin + (env.ymax - env.ymin) * hfrom; y2 = env.ymin + (env.ymax - env.ymin) * hto; var rwa = (x2a - x1a) / 2; var rha = rwa / (env.xmax - env.xmin) * 2 * Math.abs(env.opt.rh); var rwb = (x2b - x1b) / 2; var rhb = rwb / (env.xmax - env.xmin) * 2 * Math.abs(env.opt.rh); var pathLn = []; pathLn.push(['M', x1a, y1]); if (env.opt.rh != 0) pathLn.push(['A', rwa, rha, 0, 0, env.opt.rh > 0 ? 1 : 0, x2a, y1]); else pathLn.push(['L', x2a, y1]); pathLn.push(['L', x2b, y2]); if (env.opt.rh != 0) pathLn.push(['A', rwb, rhb, 0, 0, env.opt.rh > 0 ? 0 : 1, x1b, y2]); else pathLn.push(['L', x1b, y2]); pathLn.push(['z']); return {path: pathLn, center: [(x2a + x1a) / 2, (y2 + y1) / 2 + (env.opt.rh > 0 ? -1 : +1) * (rha + rhb) / 2], rect: [x1a, y1, x2a, y2]}; }, edge : function(env, h, w, isTop) { if ((isTop && env.opt.rh >= 0) || (!isTop && env.opt.rh <= 0)) return false; x1 = env.xmin + (env.xmax - env.xmin) * (w / -2 + 1/2); x2 = env.xmin + (env.xmax - env.xmin) * (w / 2 + 1/2); y = env.ymin + (env.ymax - env.ymin) * h; var rw = (x2 - x1) / 2; var rh = rw / (env.xmax - env.xmin) * 2 * Math.abs(env.opt.rh); var pathLn = []; pathLn.push(['M', x1, y]); pathLn.push(['A', rw, rh, 0, 0, env.opt.rh < 0 ? 1 : 0, x2, y]); pathLn.push(['A', rw, rh, 0, 0, env.opt.rh < 0 ? 1 : 0, x1, y]); pathLn.push(['z']); return pathLn; } }; })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_chart_line.js000066400000000000000000000777041240311710500220560ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * CHART: LINE/BAR **********************************************************************/ $.elycharts.line = { init : function($env) { }, _getColorizationKey : function($type) { if ($type == 'line') return [ ['plotProps', 'stroke'], ['dotProps', 'fill'], ['fillProps', 'fill'] ]; else return [ ['plotProps', 'stroke'], ['plotProps', 'fill'] ]; }, draw : function(env) { if (common.executeIfChanged(env, ['values', 'series'])) { env.plots = {}; env.axis = { x : {} }; env.barno = 0; env.indexCenter = 'line'; } var opt = env.opt; var plots = env.plots; var axis = env.axis; var paper = env.paper; var values = env.opt.values; var labels = env.opt.labels; var i, cum, props, serie, plot, labelsCount; // Valorizzazione di tutte le opzioni utili e le impostazioni interne di ogni grafico e dell'ambiente di lavoro if (common.executeIfChanged(env, ['values', 'series'])) { var idx = 0; var prevVisibleSerie = false; for (serie in values) { plot = { index : idx, type : false, visible : false }; plots[serie] = plot; if (values[serie]) { props = common.areaProps(env, 'Series', serie); plot.type = props.type; if (props.type == 'bar') env.indexCenter = 'bar'; if (props.visible) { plot.visible = true; if (!labelsCount || labelsCount < values[serie].length) labelsCount = values[serie].length; // Values // showValues: manage NULL elements (doing an avg of near points) for line serie var showValues = [] for (i = 0; i < values[serie].length; i++) { var val = values[serie][i]; if (props.avgOverNulls && val == null) { if (props.type == 'bar') val = 0; else { for (var j = i + 1; j < values[serie].length && values[serie][j] == null; j++) {} var next = j < values[serie].length ? values[serie][j] : null; for (var k = i -1; k >= 0 && values[serie][k] == null; k--) {} var prev = k >= 0 ? values[serie][k] : null; val = next != null ? (prev != null ? (next * (i - k) + prev * (j - i)) / (j - k) : next) : prev; } } showValues.push(val); } if (props.stacked && !(typeof props.stacked == 'string')) props.stacked = prevVisibleSerie; if (typeof props.stacked == 'undefined' || props.stacked == serie || props.stacked < 0 || !plots[props.stacked] || !plots[props.stacked].visible || plots[props.stacked].type != plot.type) { // NOT Stacked plot.ref = serie; if (props.type == 'bar') plot.barno = env.barno ++; plot.from = []; if (!props.cumulative) plot.to = showValues; else { plot.to = []; cum = 0; for (i = 0; i < showValues.length; i++) plot.to.push(cum += showValues[i] != null ? showValues[i] : 0); } for (i = 0; i < showValues.length; i++) plot.from.push(plot.to[i] != null ? 0 : null); } else { // Stacked plot.ref = props.stacked; if (props.type == 'bar') plot.barno = plots[props.stacked].barno; plot.from = plots[props.stacked].stack; plot.to = []; cum = 0; if (!props.cumulative) for (i = 0; i < showValues.length; i++) plot.to.push(plot.from[i] + (showValues[i] != null ? showValues[i] : 0)); else for (i = 0; i < showValues.length; i++) plot.to.push(plot.from[i] + (cum += (showValues[i] != null ? showValues[i] : 0))); plots[props.stacked].stack = plot.to; } plot.stack = plot.to; plot.max = Math.max.apply(Math, plot.from.concat(plot.to)); plot.min = Math.min.apply(Math, plot.from.concat(plot.to)); // Assi (DEP: values, series) if (props.axis) { if (!axis[props.axis]) axis[props.axis] = { plots : [] }; axis[props.axis].plots.push(serie); if (typeof axis[props.axis].max == 'undefined') axis[props.axis].max = plot.max; else axis[props.axis].max = Math.max(axis[props.axis].max, plot.max); if (typeof axis[props.axis].min == 'undefined') axis[props.axis].min = plot.min; else axis[props.axis].min = Math.min(axis[props.axis].min, plot.min); } prevVisibleSerie = serie; } } } } // Labels normalization (if not set or less than values) if (!labels) labels = []; while (labelsCount > labels.length) labels.push(null); labelsCount = labels.length; env.opt.labels = labels; // Prepare axis scale (values, series, axis) if (common.executeIfChanged(env, ['values', 'series', 'axis'])) { for (var lidx in axis) { props = common.areaProps(env, 'Axis', lidx); axis[lidx].props = props; if (typeof props.max != 'undefined') axis[lidx].max = props.max; if (typeof props.min != 'undefined') axis[lidx].min = props.min; if (axis[lidx].min == axis[lidx].max) axis[lidx].max = axis[lidx].min + 1; if (props.normalize && props.normalize > 0) { var v = Math.abs(axis[lidx].max); if (axis[lidx].min && Math.abs(axis[lidx].min) > v) v = Math.abs(axis[lidx].min); if (v) { var basev = Math.floor(Math.log(v)/Math.LN10) - (props.normalize - 1); // NOTE: On firefox Math.pow(10, -X) sometimes results in number noise (0.89999...), it's better to do 1/Math.pow(10,X) basev = basev >= 0 ? Math.pow(10, basev) : 1 / Math.pow(10, -basev); v = Math.ceil(v / basev / (opt.features.grid.ny ? opt.features.grid.ny : 1)) * basev * (opt.features.grid.ny ? opt.features.grid.ny : 1); // Calculation above, with decimal number sometimes insert some noise in numbers (eg: 8.899999... instead of 0.9), so i need to round result with proper precision v = Math.round(v / basev) * basev; // I need to store the normalization base for further roundin (eg: in axis label, sometimes calculation results in "number noise", so i need to round them with proper precision) axis[lidx].normalizationBase = basev; if (axis[lidx].max) axis[lidx].max = Math.ceil(axis[lidx].max / v) * v; if (axis[lidx].min) axis[lidx].min = Math.floor(axis[lidx].min / v) * v; } } if (axis[lidx].plots) for (var ii = 0; ii < axis[lidx].plots.length; ii++) { plots[axis[lidx].plots[ii]].max = axis[lidx].max; plots[axis[lidx].plots[ii]].min = axis[lidx].min; } } } var pieces = []; this.grid(env, pieces); // DEP: * var deltaX = (env.width - opt.margins[3] - opt.margins[1]) / (labels.length > 1 ? labels.length - 1 : 1); var deltaBarX = (env.width - opt.margins[3] - opt.margins[1]) / (labels.length > 0 ? labels.length : 1); for (serie in values) { props = common.areaProps(env, 'Series', serie); plot = plots[serie]; common.colorize(env, props, this._getColorizationKey(props.type), common.getItemColor(env, serie)); // TODO Settare una props in questo modo potrebbe incasinare la gestione degli update parziali (se iso "lineCenter: auto" e passo da un grafico con indexCenter = bar a uno con indexCenter = line) if (props.lineCenter && props.lineCenter == 'auto') props.lineCenter = (env.indexCenter == 'bar'); else if (props.lineCenter && env.indexCenter == 'line') env.indexCenter = 'bar'; if (values[serie] && props.visible) { var deltaY = (env.height - opt.margins[2] - opt.margins[0]) / (plot.max - plot.min); if (props.type == 'line') { // LINE CHART var linePath = [ 'LINE', [], props.rounded ]; var fillPath = [ 'LINEAREA', [], [], props.rounded ]; var dotPieces = []; for (i = 0, ii = labels.length; i < ii; i++) if (plot.to.length > i) { var indexProps = common.areaProps(env, 'Series', serie, i); common.colorize(env, indexProps, this._getColorizationKey(props.type), common.getItemColor(env, serie, i)); var x = Math.round((props.lineCenter ? deltaBarX / 2 : 0) + opt.margins[3] + i * (props.lineCenter ? deltaBarX : deltaX)); var y = null; if (plot.to[i] != null) { var d = plot.to[i] > plot.max ? plot.max : (plot.to[i] < plot.min ? plot.min : plot.to[i]); y = Math.round(env.height - opt.margins[2] - deltaY * (d - plot.min)); } var yy = null; if (plot.from[i] != null) { var dd = plot.from[i] > plot.max ? plot.max : (plot.from[i] < plot.min ? plot.min : plot.from[i]); yy = Math.round(env.height - opt.margins[2] - deltaY * (dd - plot.min)) + (Raphael.VML ? 1 : 0); } linePath[1].push([x, y]); if (props.fill) { fillPath[1].push([x, y]); fillPath[2].push([x, yy]); } if (indexProps.dot) { if (values[serie][i] == null && !indexProps.dotShowOnNull) dotPieces.push({path : false, attr : false}); else dotPieces.push({path : [ [ 'CIRCLE', x, y, indexProps.dotProps.size ] ], attr : indexProps.dotProps}); // TODO Size should not be in dotProps (not an svg props) } } if (props.fill) pieces.push({ section : 'Series', serie : serie, subSection : 'Fill', path : [ fillPath ], attr : props.fillProps }); else pieces.push({ section : 'Series', serie : serie, subSection : 'Fill', path : false, attr : false }); pieces.push({ section : 'Series', serie : serie, subSection : 'Plot', path : [ linePath ], attr : props.plotProps , mousearea : 'pathsteps'}); if (dotPieces.length) pieces.push({ section : 'Series', serie : serie, subSection : 'Dot', paths : dotPieces }); else pieces.push({ section : 'Series', serie : serie, subSection : 'Dot', path : false, attr : false }); } else { pieceBar = []; // BAR CHART for (i = 0, ii = labels.length; i < ii; i++) if (plot.to.length > i) { if (plot.from[i] != plot.to[i]) { var indexProps = common.areaProps(env, 'Series', serie, i); common.colorize(env, indexProps, this._getColorizationKey(props.type), common.getItemColor(env, serie, i)); var bwid = Math.floor((deltaBarX - opt.barMargins) / (1 + (env.barno - 1) * (100 - opt.barOverlapPerc) / 100)); var bpad = bwid * (100 - props.barWidthPerc) / 200; var boff = opt.barMargins / 2 + plot.barno * (bwid * (100 - opt.barOverlapPerc) / 100); var x1 = Math.floor(opt.margins[3] + i * deltaBarX + boff + bpad); var y1 = Math.round(env.height - opt.margins[2] - deltaY * (plot.to[i] - plot.min)); var y2 = Math.round(env.height - opt.margins[2] - deltaY * (plot.from[i] - plot.min)); pieceBar.push({path : [ [ 'RECT', x1, y1, x1 + bwid - bpad * 2, y2 ] ], attr : indexProps.plotProps }); } else pieceBar.push({path : false, attr : false }); } if (pieceBar.length) pieces.push({ section : 'Series', serie : serie, subSection : 'Plot', paths: pieceBar, mousearea : 'paths' }); else pieces.push({ section : 'Series', serie : serie, subSection : 'Plot', path: false, attr: false, mousearea : 'paths' }); } } else { // Grafico non visibile / senza dati, deve comunque inserire i piece vuoti (NELLO STESSO ORDINE SOPRA!) if (props.type == 'line') pieces.push({ section : 'Series', serie : serie, subSection : 'Fill', path : false, attr : false }); pieces.push({ section : 'Series', serie : serie, subSection : 'Plot', path: false, attr: false, mousearea : 'paths' }); if (props.type == 'line') pieces.push({ section : 'Series', serie : serie, subSection : 'Dot', path : false, attr : false }); } } return pieces; }, grid : function(env, pieces) { // DEP: axis, [=> series, values], labels, margins, width, height, grid* if (common.executeIfChanged(env, ['values', 'series', 'axis', 'labels', 'margins', 'width', 'height', 'features.grid'])) { var opt = env.opt; var props = env.opt.features.grid; var paper = env.paper; var axis = env.axis; var labels = env.opt.labels; var deltaX = (env.width - opt.margins[3] - opt.margins[1]) / (labels.length > 1 ? labels.length - 1 : 1); var deltaBarX = (env.width - opt.margins[3] - opt.margins[1]) / (labels.length > 0 ? labels.length : 1); var i, j, x, y, lw, labx, laby, labe, val, txt; // Label X axis var paths = []; var labelsCenter = props.labelsCenter; if (labelsCenter == 'auto') labelsCenter = (env.indexCenter == 'bar'); if (axis.x && axis.x.props.labels) { // used in case of labelsHideCovered, contains a "rotated" representation of the rect coordinates occupied by the last shown label var lastShownLabelRect = false; // labelsAnchor is "auto" by default. Can be "start","middle" or "end". If "auto" then it is automatically set depending on labelsRotate. var labelsAnchor = axis.x.props.labelsAnchor || 'auto'; // Automatic labelsAnchor is "middle" on no rotation, otherwise the anchor is the higher side of the label. if (labelsAnchor == 'auto') labelsAnchor = axis.x.props.labelsRotate > 0 ? "start" : (axis.x.props.labelsRotate == 0 ? "middle" : "end"); // labelsPos is "auto" by default. Can be "start", "middle" or "end". If "auto" then it is automatically set depending on labelsCenter and labelsRotate and labelsAnchor. var labelsPos = axis.x.props.labelsPos || 'auto'; // in labelsCenter (bar) it is middle when there is no rotation, equals to labelsAnchor on rotation. // in !labelsCenter (line) is is always 'start'; if (labelsPos == 'auto') labelsPos = labelsCenter ? (axis.x.props.labelsRotate == 0 ? labelsAnchor : 'middle') : 'start'; for (i = 0; i < labels.length; i++) if ((typeof labels[i] != 'boolean' && labels[i] != null) || labels[i]) { if (!axis.x.props.labelsSkip || i >= axis.x.props.labelsSkip) { val = labels[i]; if (axis.x.props.labelsFormatHandler) val = axis.x.props.labelsFormatHandler(val); txt = (axis.x.props.prefix ? axis.x.props.prefix : "") + val + (axis.x.props.suffix ? axis.x.props.suffix : ""); labx = opt.margins[3] + i * (labelsCenter ? deltaBarX : deltaX) + (axis.x.props.labelsMargin ? axis.x.props.labelsMargin : 0); if (labelsPos == 'middle') labx += (labelsCenter ? deltaBarX : deltaX) / 2; if (labelsPos == 'end') labx += (labelsCenter ? deltaBarX : deltaX); laby = env.height - opt.margins[2] + axis.x.props.labelsDistance; labe = paper.text(labx, laby, txt).attr(axis.x.props.labelsProps).toBack(); labe.attr({"text-anchor" : labelsAnchor}); // will contain the boundingbox size, or false if it is hidden. var boundingbox = false; var bbox = labe.getBBox(); var p1 = {x: bbox.x, y: bbox.y}; var p2 = {x: bbox.x+bbox.width, y: bbox.y+bbox.height}; var o1 = {x: labx, y: laby}; rotate = function (p, rad) { var X = p.x * Math.cos(rad) - p.y * Math.sin(rad), Y = p.x * Math.sin(rad) + p.y * Math.cos(rad); return {x: X, y: Y}; }; // calculate collision between non rotated rects with vertext p1-p2 and t1-t2 // this algorythm works only for horizontal rects (alpha = 0) // "dist" is the length added as a margin to the rects before collision detection collide = function(r1,r2,dist) { xor = function(a,b) { return ( a || b ) && !( a && b ); } if (r1.alpha != r2.alpha) throw "collide doens't support rects with different rotations"; var r1p1r = rotate({x: r1.p1.x-dist, y:r1.p1.y-dist}, -r1.alpha); var r1p2r = rotate({x: r1.p2.x+dist, y:r1.p2.y+dist}, -r1.alpha); var r2p1r = rotate({x: r2.p1.x-dist, y:r2.p1.y-dist}, -r2.alpha); var r2p2r = rotate({x: r2.p2.x+dist, y:r2.p2.y+dist}, -r2.alpha); return !xor(Math.min(r1p1r.x,r1p2r.x) > Math.max(r2p1r.x,r2p2r.x), Math.max(r1p1r.x,r1p2r.x) < Math.min(r2p1r.x,r2p2r.x)) && !xor(Math.min(r1p1r.y,r1p2r.y) > Math.max(r2p1r.y,r2p2r.y), Math.max(r1p1r.y,r1p2r.y) < Math.min(r2p1r.y,r2p2r.y)); } // compute equivalent orizontal rotated rect rotated = function(rect, origin, alpha) { translate = function (p1, p2) { return {x: p1.x+p2.x, y: p1.y+p2.y}; }; negate = function(p1) { return {x: -p1.x, y: -p1.y}; }; var p1trt = translate(rotate(translate(rect.p1,negate(origin)), alpha),origin); var p2trt = translate(rotate(translate(rect.p2,negate(origin)), alpha),origin); return { p1: p1trt, p2: p2trt, alpha: rect.alpha+alpha }; } bbox = function(rect) { if (rect.alpha == 0) { return { x: rect.p1.x, y: rect.p1.y, width: rect.p2.x-rect.p1.x, height: rect.p2.y-rect.p1.y }; } else { var points = []; points.push({ x: 0, y: 0 }); points.push({ x: rect.p2.x-rect.p1.x, y: 0 }); points.push({ x: 0, y: rect.p2.y-rect.p1.y }); points.push({ x: rect.p2.x-rect.p1.x, y: rect.p2.y-rect.p1.y }); var bb = []; bb['left'] = 0; bb['right'] = 0; bb['top'] = 0; bb['bottom'] = 0; for (_px = 0; _px < points.length; _px++) { var p = points[_px]; var newX = parseInt((p.x * Math.cos(rect.alpha)) + (p.y * Math.sin(rect.alpha))); var newY = parseInt((p.x * Math.sin(rect.alpha)) + (p.y * Math.cos(rect.alpha))); bb['left'] = Math.min(bb['left'], newX); bb['right'] = Math.max(bb['right'], newX); bb['top'] = Math.min(bb['top'], newY); bb['bottom'] = Math.max(bb['bottom'], newY); } var newWidth = parseInt(Math.abs(bb['right'] - bb['left'])); var newHeight = parseInt(Math.abs(bb['bottom'] - bb['top'])); var newX = ((rect.p1.x + rect.p2.x) / 2) - newWidth / 2; var newY = ((rect.p1.y + rect.p2.y) / 2) - newHeight / 2; return { x: newX, y: newY, width: newWidth, height: newHeight }; } } var alpha = Raphael.rad(axis.x.props.labelsRotate); // compute used "rect" so to be able to check if there is overlapping with previous ones. var rect = rotated({p1: p1, p2: p2, alpha: 0}, o1, alpha); //console.log('bbox ',p1, p2, rect, props.nx, val, rect.p1, rect.p2, rect.alpha, boundingbox, env.width); // se collide con l'ultimo mostrato non lo mostro. var dist = axis.x.props.labelsMarginRight ? axis.x.props.labelsMarginRight / 2 : 0; if (axis.x.props.labelsHideCovered && lastShownLabelRect && collide(rect, lastShownLabelRect, dist)) { labe.hide(); // labels[i] = false; } else { boundingbox = bbox(rect); // Manage label overflow if (props.nx == 'auto' && (boundingbox.x < 0 || boundingbox.x+boundingbox.width > env.width)) { labe.hide(); // labels[i] = false; } else { lastShownLabelRect = rect; } } // Apply rotation to the element. if (axis.x.props.labelsRotate) { if (Raphael.animation) { labe.transform(Raphael.format('r{0},{1},{2}', axis.x.props.labelsRotate, labx, laby)).toBack(); } else { labe.rotate(axis.x.props.labelsRotate, labx, laby).toBack(); } } paths.push({ path : [ [ 'RELEMENT', labe ] ], attr : false }); } } } pieces.push({ section : 'Axis', serie : 'x', subSection : 'Label', paths : paths }); // Title X Axis if (axis.x && axis.x.props.title) { x = opt.margins[3] + Math.floor((env.width - opt.margins[1] - opt.margins[3]) / 2); y = env.height - opt.margins[2] + axis.x.props.titleDistance * (Raphael.VML ? axis.x.props.titleDistanceIE : 1); //paper.text(x, y, axis.x.props.title).attr(axis.x.props.titleProps); pieces.push({ section : 'Axis', serie : 'x', subSection : 'Title', path : [ [ 'TEXT', axis.x.props.title, x, y ] ], attr : axis.x.props.titleProps }); } else pieces.push({ section : 'Axis', serie : 'x', subSection : 'Title', path : false, attr : false }); // Label + Title L/R Axis for (var jj in ['l', 'r']) { j = ['l', 'r'][jj]; if (axis[j] && axis[j].props.labels && props.ny) { paths = []; for (i = axis[j].props.labelsSkip ? axis[j].props.labelsSkip : 0; i <= props.ny; i++) { var deltaY = (env.height - opt.margins[2] - opt.margins[0]) / props.ny; // TODO we should never set "props". We should use local variables for derived value (so to correctly deal with updates) if (j == 'r') { labx = env.width - opt.margins[1] + axis[j].props.labelsDistance; if (!axis[j].props.labelsProps["text-anchor"]) axis[j].props.labelsProps["text-anchor"] = "start"; } else { labx = opt.margins[3] - axis[j].props.labelsDistance; if (!axis[j].props.labelsProps["text-anchor"]) axis[j].props.labelsProps["text-anchor"] = "end"; } if (axis[j].props.labelsAnchor && axis[j].props.labelsAnchor != 'auto') axis[j].props.labelsProps["text-anchor"] = axis[j].props.labelsAnchor; // NOTE: Parenthesis () around division are useful to keep right number precision val = (axis[j].min + (i * ((axis[j].max - axis[j].min) / props.ny))); // Rounding with proper precision for "number sharpening" if (axis[j].normalizationBase) // I use (1 / ( 1 / norm ) ) to avoid some noise val = Math.round(val / axis[j].normalizationBase) / ( 1 / axis[j].normalizationBase ); if (axis[j].props.labelsFormatHandler) val = axis[j].props.labelsFormatHandler(val); if (axis[j].props.labelsCompactUnits) val = common.compactUnits(val, axis[j].props.labelsCompactUnits); txt = (axis[j].props.prefix ? axis[j].props.prefix : "") + val + (axis[j].props.suffix ? axis[j].props.suffix : ""); laby = env.height - opt.margins[2] - i * deltaY; //var labe = paper.text(labx, laby + (axis[j].props.labelsMargin ? axis[j].props.labelsMargin : 0), txt).attr(axis[j].props.labelsProps).toBack(); paths.push( { path : [ [ 'TEXT', txt, labx, laby + (axis[j].props.labelsMargin ? axis[j].props.labelsMargin : 0) ] ], attr : axis[j].props.labelsProps }); } pieces.push({ section : 'Axis', serie : j, subSection : 'Label', paths : paths }); } else pieces.push({ section : 'Axis', serie : j, subSection : 'Label', paths : [] }); if (axis[j] && axis[j].props.title) { if (j == 'r') x = env.width - opt.margins[1] + axis[j].props.titleDistance * (Raphael.VML ? axis[j].props.titleDistanceIE : 1); else x = opt.margins[3] - axis[j].props.titleDistance * (Raphael.VML ? axis[j].props.titleDistanceIE : 1); //paper.text(x, opt.margins[0] + Math.floor((env.height - opt.margins[0] - opt.margins[2]) / 2), axis[j].props.title).attr(axis[j].props.titleProps).attr({rotation : j == 'l' ? 270 : 90}); var attr = common._clone(axis[j].props.titleProps); var rotation = j == 'l' ? 270 : 90; var y = opt.margins[0] + Math.floor((env.height - opt.margins[0] - opt.margins[2]) / 2); // Raphael 2 does not support .rotation if (Raphael.animation) { var labe = paper.text(x, y, axis[j].props.title).attr(attr).transform(Raphael.format('r{0}', rotation)).toBack(); pieces.push({ section : 'Axis', serie : j, subSection : 'Title', path : [ [ 'RELEMENT', labe ] ], attr : false }); } else { attr.rotation = rotation; pieces.push({ section : 'Axis', serie : j, subSection : 'Title', path : [ [ 'TEXT', axis[j].props.title, x, y ] ], attr : attr }); } } else pieces.push({ section : 'Axis', serie : j, subSection : 'Title', path : false, attr : false }); } // Grid if (props.nx || props.ny) { var path = [], bandsH = [], bandsV = [], nx = props.nx == 'auto' ? (labelsCenter ? labels.length : labels.length - 1) : props.nx, ny = props.ny, rowHeight = (env.height - opt.margins[2] - opt.margins[0]) / (ny ? ny : 1), columnWidth = (env.width - opt.margins[1] - opt.margins[3]) / (nx ? nx : 1), forceBorderX1 = typeof props.forceBorder == 'object' ? props.forceBorder[3] : props.forceBorder, forceBorderX2 = typeof props.forceBorder == 'object' ? props.forceBorder[1] : props.forceBorder, forceBorderY1 = typeof props.forceBorder == 'object' ? props.forceBorder[0] : props.forceBorder, forceBorderY2 = typeof props.forceBorder == 'object' ? props.forceBorder[2] : props.forceBorder, drawH = ny > 0 ? (typeof props.draw == 'object' ? props.draw[0] : props.draw) : false, drawV = nx > 0 ? typeof props.draw == 'object' ? props.draw[1] : props.draw : false; if (ny > 0) for (i = 0; i < ny + 1; i++) { if ( forceBorderY1 && i == 0 || // Show top line only if forced forceBorderY2 && i == ny || // Show bottom line only if forced drawH && i > 0 && i < ny // Show other lines if draw = true ) { path.push(["M", opt.margins[3] - props.extra[3], opt.margins[0] + Math.round(i * rowHeight) ]); path.push(["L", env.width - opt.margins[1] + props.extra[1], opt.margins[0] + Math.round(i * rowHeight)]); } if (i < ny) { if (i % 2 == 0 && props.evenHProps || i % 2 == 1 && props.oddHProps) bandsH.push({path : [ [ 'RECT', opt.margins[3] - props.extra[3], opt.margins[0] + Math.round(i * rowHeight), // x1, y1 env.width - opt.margins[1] + props.extra[1], opt.margins[0] + Math.round((i + 1) * rowHeight) // x2, y2 ] ], attr : i % 2 == 0 ? props.evenHProps : props.oddHProps }); else bandsH.push({ path : false, attr: false}) } } for (i = 0; i < nx + 1; i++) { if ( forceBorderX1 && i == 0 || // Always show first line if forced forceBorderX2 && i == nx || // Always show last line if forced drawV && ( // To show other lines draw must be true (props.nx != 'auto' && i > 0 && i < nx) || // If nx = [number] show other lines (first and last are managed above with forceBorder) (props.nx == 'auto' && (typeof labels[i] != 'boolean' || labels[i])) // if nx = 'auto' show all lines if a label is associated ) // Show all lines if props.nx is a number, or if label != false, AND draw must be true ) { path.push(["M", opt.margins[3] + Math.round(i * columnWidth), opt.margins[0] - props.extra[0] ]); //(t ? props.extra[0] : 0)]); path.push(["L", opt.margins[3] + Math.round(i * columnWidth), env.height - opt.margins[2] + props.extra[2] ]); //(t ? props.extra[2] : 0)]); } if (i < nx) { if (i % 2 == 0 && props.evenVProps || i % 2 == 1 && props.oddVProps) bandsV.push({path : [ [ 'RECT', opt.margins[3] + Math.round(i * columnWidth), opt.margins[0] - props.extra[0], // x1, y1 opt.margins[3] + Math.round((i + 1) * columnWidth), env.height - opt.margins[2] + props.extra[2], // x2, y2 ] ], attr : i % 2 == 0 ? props.evenVProps : props.oddVProps }); else bandsV.push({ path : false, attr: false}) } } pieces.push({ section : 'Grid', path : path.length ? path : false, attr : path.length ? props.props : false }); pieces.push({ section : 'GridBandH', paths : bandsH }); pieces.push({ section : 'GridBandV', paths : bandsV }); var tpath = []; // Ticks asse X if (props.ticks.active && (typeof props.ticks.active != 'object' || props.ticks.active[0])) { for (i = 0; i < nx + 1; i++) { if (props.nx != 'auto' || typeof labels[i] != 'boolean' || labels[i]) { tpath.push(["M", opt.margins[3] + Math.round(i * columnWidth), env.height - opt.margins[2] - props.ticks.size[1] ]); tpath.push(["L", opt.margins[3] + Math.round(i * columnWidth), env.height - opt.margins[2] + props.ticks.size[0] ]); } } } // Ticks asse L if (props.ticks.active && (typeof props.ticks.active != 'object' || props.ticks.active[1])) for (i = 0; i < ny + 1; i++) { tpath.push(["M", opt.margins[3] - props.ticks.size[0], opt.margins[0] + Math.round(i * rowHeight) ]); tpath.push(["L", opt.margins[3] + props.ticks.size[1], opt.margins[0] + Math.round(i * rowHeight)]); } // Ticks asse R if (props.ticks.active && (typeof props.ticks.active != 'object' || props.ticks.active[2])) for (i = 0; i < ny + 1; i++) { tpath.push(["M", env.width - opt.margins[1] - props.ticks.size[1], opt.margins[0] + Math.round(i * rowHeight) ]); tpath.push(["L", env.width - opt.margins[1] + props.ticks.size[0], opt.margins[0] + Math.round(i * rowHeight)]); } pieces.push({ section : 'Ticks', path : tpath.length ? tpath : false, attr : tpath.length ? props.ticks.props : false }); } } } } })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_chart_pie.js000066400000000000000000000117021240311710500216660ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * CHART: PIE **********************************************************************/ $.elycharts.pie = { init : function($env) { }, draw : function(env) { //var paper = env.paper; var opt = env.opt; var w = env.width - env.opt.margins[1] - env.opt.margins[3]; var h = env.height - env.opt.margins[0] - env.opt.margins[2]; var r = env.opt.r ? env.opt.r : Math.floor((w < h ? w : h) / 2 * (env.opt.rPerc ? env.opt.rPerc / 100 : 0.8)); var cx = (env.opt.cx ? env.opt.cx : Math.floor(w / 2)) + env.opt.margins[3]; var cy = (env.opt.cy ? env.opt.cy : Math.floor(h / 2)) + env.opt.margins[0]; var cnt = 0, i, ii, serie, plot, props; for (serie in opt.values) { plot = { visible : false, total : 0, values : [] }; env.plots[serie] = plot; var serieProps = common.areaProps(env, 'Series', serie); common.colorize(env, serieProps, [['plotProps','stroke'],['plotProps','fill']], common.getItemColor(env, serie)); if (serieProps.visible) { plot.visible = true; cnt ++; plot.values = opt.values[serie]; for (i = 0, ii = plot.values.length; i < ii; i++) if (plot.values[i] > 0) { props = common.areaProps(env, 'Series', serie, i); common.colorize(env, props, [['plotProps','stroke'],['plotProps','fill']], common.getItemColor(env, serie, i)); if (typeof props.inside == 'undefined' || props.inside < 0) plot.total += plot.values[i]; } for (i = 0; i < ii; i++) if (plot.values[i] < plot.total * opt.valueThresold) { plot.total = plot.total - plot.values[i]; plot.values[i] = 0; } } } var rstep = r / cnt; var rstart = -rstep, rend = 0; var pieces = []; for (serie in opt.values) { plot = env.plots[serie]; var paths = []; if (plot.visible) { rstart += rstep; rend += rstep; var angle = env.opt.startAngle, angleplus = 0, anglelimit = 0; if (plot.total == 0) { env.emptySeries = true; props = common.areaProps(env, 'Series', 'empty'); common.colorize(env, props, [['plotProps','stroke'],['plotProps','fill']], common.getItemColor(env, serie)); paths.push({ path : [ [ 'CIRCLE', cx, cy, r ] ], attr : props.plotProps }); } else { env.emptySeries = false; for (i = 0, ii = plot.values.length; i < ii; i++) { var value = plot.values[i]; if (value > 0) { props = common.areaProps(env, 'Series', serie, i); common.colorize(env, props, [['plotProps','stroke'],['plotProps','fill']], common.getItemColor(env, serie, i)); if (typeof props.inside == 'undefined' || props.inside < 0) { angle += anglelimit; angleplus = 360 * value / plot.total; anglelimit = angleplus; } else { angleplus = 360 * values[props.inside] / plot.total * value / values[props.inside]; } var rrstart = rstart, rrend = rend; if (props.r) { if (props.r > 0) { if (props.r <= 1) rrend = rstart + rstep * props.r; else rrend = rstart + props.r; } else { if (props.r >= -1) rrstart = rstart + rstep * (-props.r); else rrstart = rstart - props.r; } } if (!env.opt.clockwise) paths.push({ path : [ [ 'SLICE', cx, cy, rrend, rrstart, angle, angle + angleplus ] ], attr : props.plotProps }); else paths.push({ path : [ [ 'SLICE', cx, cy, rrend, rrstart, - angle - angleplus, - angle ] ], attr : props.plotProps }); } else paths.push({ path : false, attr : false }); } } } else { // Even if serie is not visible it's better to put some empty path (for better transitions). It's not mandatory, just better if (opt.values[serie] && opt.values[serie].length) for (i = 0, ii = opt.values[serie].length; i < ii; i++) paths.push({ path : false, attr : false }); } pieces.push({ section : 'Series', serie : serie, subSection : 'Plot', paths : paths , mousearea : 'paths'}); } return pieces; } } })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_core.js000066400000000000000000001377301240311710500206720ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 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) { if ($.elycharts.featuresmanager) $.elycharts.featuresmanager.clear($env); $env.paper.clear(); $env.cache = false; if ($env.autoresize) $(window).unbind('resize', $env.autoresize); this.html(""); this.data('elycharts_env', false); } } return this; } 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); this.data('elycharts_env', $env); } else { if (!$options) $options = {}; $options = _normalizeOptions($options, $env.opt); // Already initialized $env.oldopt = common._clone($env.opt); $env.opt = $.extend(true, $env.opt, $options); $env.newopt = $options; $env.oldwidth = $env.width; $env.oldheight = $env.height; } $env.cache = $options['enableInternalCaching'] ? {} : false; _processGenericConfig($env, $options); if ($env.opt.autoresize) { if (!$env.autoresize) { var that = this; $env.autoresize = _debounce(function() { that.chart(); }); $(window).bind('resize', $env.autoresize); } } else { if ($env.autoresize) { $(window).unbind('resize', $env.autoresize); $env.autoresize = false; } } var pieces = $.elycharts[$env.opt.type].draw($env); if ($env.pieces) { pieces = _updatePieces($env, $env.pieces, pieces); } common._show($env, pieces); $env.pieces = pieces; return this; } function _updatePieces(env, pieces1, pieces2, section, serie, internal) { // Se pieces2 == null deve essere nascosto tutto pieces1 var newpieces = [], newpiece; var j = 0; for (var i = 0; i < pieces1.length; i ++) { // Se il piece attuale c'e' solo in pieces2 lo riporto nei nuovi, impostando come gia' mostrato // A meno che internal = true (siamo in un multipath, nel caso se una cosa non c'e' va considerata da togliere) if (pieces2 && (j >= pieces2.length || !common.samePiecePath(pieces1[i], pieces2[j]))) { if (!internal) { pieces1[i].show = false; newpieces.push(pieces1[i]); } else { newpiece = { path : false, attr : false }; newpiece.show = true; newpiece.animation = { element : pieces1[i].element ? pieces1[i].element : false, speed : 0, easing : '', delay : 0 } newpieces.push(newpiece); } } // Bisogna gestire la transizione dal vecchio piece al nuovo else { newpiece = pieces2 ? pieces2[j] : { path : false, attr : false }; newpiece.show = true; if (typeof pieces1[i].paths == 'undefined') { newpiece.animation = { element : pieces1[i].element ? pieces1[i].element : false, speed : 0, easing : '', delay : 0 } } else { // Multiple path piece newpiece.paths = _updatePieces(env, pieces1[i].paths, pieces2[j].paths, pieces1[i].section, pieces1[i].serie, true); } newpieces.push(newpiece); j++; } } // If there are pieces left in pieces2 i must add them unchanged if (pieces2) for (; j < pieces2.length; j++) newpieces.push(pieces2[j]); return newpieces; }; // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/ function _debounce(func, threshold, execAsap) { var timeout; return function debounced () { var obj = this, args = arguments; function delayed () { if (!execAsap) func.apply(obj, args); timeout = null; }; if (timeout) clearTimeout(timeout); else if (execAsap) func.apply(obj, args); timeout = setTimeout(delayed, threshold || 100); }; } /** * Must be called only in first call to .chart, to initialize elycharts environment. */ function _initEnv($container, $options) { var $env = { id : $.elycharts.lastId ++, paper : common._RaphaelInstance($container.get()[0], 0, 0), 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); $env.width = $options.width ? $options.width : $env.container.width(); $env.height = $options.height ? $options.height : $env.container.height(); $env.paper.setSize($env.width, $env.height); } /** * 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 cycle 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 plotType = $options.defaultSeries.type ? $options.defaultSeries.type : ($fullopt.defaultSeries.type ? $fullopt.defaultSeries.type : $fullopt.type); _normalizeOptionsSerie($options.defaultSeries, $fullopt.type, plotType, $fullopt); } if ($options.series) for (var serie in $options.series) { var seriePlotType = $options.series[serie].type ? $options.series[serie].type : ($fullopt.series[serie].type ? $fullopt.series[serie].type : (plotType ? plotType : $fullopt.type)); _normalizeOptionsSerie($options.series[serie], $fullopt.type, seriePlotType, $fullopt); } 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, the stackedWith legacy and values "color" properties. * @param $section Section part of external conf passed * @param $type Chart type * @param $plotType for line chart can be "line" or "bar", for other types is equal to chart type. */ function _normalizeOptionsSerie($section, $type, $plotType, $fullopt) { if ($section.stackedWith) { $section.stacked = $section.stackedWith; delete $section.stackedWith; } } /*********************************************************************** * 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; }, 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] == 'width' && (env.oldwidth != env.width)) return true; else if (changes[i] == 'height' && (env.oldheight != env.height)) 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; }, /** * Can be called for a whole serie or for a given index of the serie. * returns the color for that item considering valuesPalette, seriesPalette and inheritance */ getItemColor : function(env, serie, index) { var props = this.areaProps(env, 'Series', serie, index); if (props.color) return props.color; if (index !== false && props.valuesPalette) return props.valuesPalette[index % props.valuesPalette.length]; if (env.opt.seriesPalette) { var serieIndex = 0; for(seriekey in env.opt.values) { if (serie == seriekey) return env.opt.seriesPalette[serieIndex % env.opt.seriesPalette.length]; else serieIndex++; } } }, /** * Given an expandKey as array of array it sets the color to the nested tree unless it is already defined. * So [ [ 'parent', 'child' ], [ 'item' ] ] will try to put color in props.parent.child and props.item unless * they already exists. */ colorize : function(env, props, expandKeys, color) { if (color) { for (k in expandKeys) { var p = props; var i = 0; for (i = 0; i < expandKeys[k].length - 1; i++) { if (!p[expandKeys[k][i]]) p[expandKeys[k][i]] = {}; p = p[expandKeys[k][i]]; } if (!p[expandKeys[k][expandKeys[k].length-1]]) p[expandKeys[k][expandKeys[k].length-1]] = color; } } }, /** * 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; var sectionProps = env.opt[section.toLowerCase()]; // TODO fare una cache e fix del toLowerCase (devono solo fare la prima lettera if (!subsection) { if (typeof serie == 'undefined' || !serie) props = sectionProps; else { var cacheKey = section+'/'+serie+'/'+index; if (env.cache && env.cache.areaPropsCache && env.cache.areaPropsCache[cacheKey]) { props = env.cache.areaPropsCache[cacheKey]; } else { props = this._clone(env.opt['default' + section]); if (sectionProps && sectionProps[serie]) props = $.extend(true, props, sectionProps[serie]); if ((typeof index != 'undefined') && index >= 0 && props['values'] && props['values'][index]) props = $.extend(true, props, props['values'][index]); if (env.cache) { if (!env.cache.areaPropsCache) env.cache.areaPropsCache = {}; env.cache.areaPropsCache[cacheKey] = props; } } } } else { var subsectionKey = subsection.toLowerCase(); props = this._clone(env.opt[subsectionKey]); if (typeof serie == 'undefined' || !serie) { if (sectionProps && sectionProps[subsectionKey]) props = $.extend(true, props, sectionProps[subsectionKey]); } else { if (env.opt['default' + section] && env.opt['default' + section][subsectionKey]) props = $.extend(true, props, env.opt['default' + section][subsectionKey]); if (sectionProps && sectionProps[serie] && sectionProps[serie][subsectionKey]) props = $.extend(true, props, sectionProps[serie][subsectionKey]); if ((typeof index != 'undefined') && index > 0 && props['values'] && props['values'][index]) props = $.extend(true, props, props['values'][index]); } } return props; }, _absrectpath : function(x1, y1, x2, y2, r) { if (r) { // we can use 'a' or 'Q' for the same result. var res = [ ['M',x1,y1+r], ['a', r, r, 0, 0, 1, r, -r], //['Q',x1,y1, x1+r,y1], ['L',x2-r,y1], ['a', r, r, 0, 0, 1, r, r], //['Q',x2,y1, x2,y1+r], ['L',x2,y2-r], ['a', r, r, 0, 0, 1, -r, r], // ['Q',x2,y2, x2-r,y2], ['L',x1+r,y2], ['a', r, r, 0, 0, 1, -r, -r], // ['Q',x1,y2, x1,y2-r], ['z'] ]; return res; } else 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(Math.abs(p2x - p1x) / Math.abs(p2y - p1y)), b = Math.atan(Math.abs(p3x - p2x) / Math.abs(p2y - p3y)); a = (p1y < p2y && p2x > p1x) || (p1y > p2y && p2x < p1x) ? Math.PI - a : a; b = (p3y < p2y && p3x > p2x) || (p3y > p2y && p3x < p2x) ? 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 }; }, _linepath : function ( points, rounded ) { var path = []; if (rounded) { var anc = false; for (var j = 0, jj = points.length; j < jj ; j++) { var x = points[j][0], y = points[j][1]; if (x != null && y != null) { if (anc) { if (j + 1 != jj && points[j + 1][0] != null && points[j + 1][1] != null) { 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] ]); // path.push([ "M", anc[0], anc[1] ]); // path.push([ "L", a.x1, a.y1 ]); // path.push([ "M", points[j][0], points[j][1] ]); anc = [ a.x2, a.y2 ]; } else { path.push([ "C", anc[0], anc[1], points[j][0], points[j][1], points[j][0], points[j][1] ]); anc = [ points[j][0], points[j][1] ]; } } else { path.push([ "M", points[j][0], points[j][1] ]); anc = [ points[j][0], points[j][1] ]; } } else anc = false; } } else { var prevx = null; var prevy = null; for (var i = 0; i < points.length; i++) { var x = points[i][0], y = points[i][1]; if (x != null && y != null) { path.push([prevx == null || prevy == null ? "M" : "L", x, y]); } prevx = x; prevy = y; } } return path; }, _lineareapath : function (points1, points2, rounded) { var path = this._linepath(points1, rounded); var path2 = this._linepath(points2.reverse(), rounded); var finalPath = []; var firstPushed = null; for (var i = 0; i <= path.length; i++) { if (i == path.length || path[i][0] == "M") { if (firstPushed != null) { for (var j = path.length - i; j <= path.length - firstPushed; j++) { if (path2[j][0] == "M") finalPath.push([ "L", path2[j][1], path2[j][2] ]); else finalPath.push(path2[j]); } finalPath.push(['z']); firstPushed = null; } if (i != path.length) finalPath.push(path[i]); } else { finalPath.push(path[i]); if (firstPushed == null) firstPushed = i; } } return finalPath; }, /** * 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), path[0][5]] ]; 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]) { // TODO the translation for lowercase actions are all wrong! // relative movements do not need to be adjusted for moving (or at most, only the first one have to). // TODO relative movements this way cannot be forced to stay in marginlimit! 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': // Fixed for uppercase C in 2.1.5 // (x1 y1 x2 y2 x y)+ newpath.push([o[0], this._movePathX(env, o[1], offset[0], marginlimit), this._movePathY(env, o[2], offset[1], marginlimit), this._movePathX(env, o[3], offset[0], marginlimit), this._movePathY(env, o[4], offset[1], marginlimit), 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': // Fixed for uppercase Q in 2.1.5 // (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)]); newpath.push([o[0], this._movePathX(env, o[1], offset[0], marginlimit), this._movePathY(env, o[2], offset[1], marginlimit), 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 (x == null) return null; if (!marginlimit) return x + dx; x = x + dx; return dx > 0 && x > env.width - env.opt.margins[1] ? env.width - env.opt.margins[1] : (dx < 0 && x < env.opt.margins[3] ? env.opt.margins[3] : x); }, _movePathY : function(env, y, dy, marginlimit) { if (y == null) return null; if (!marginlimit) return y + dy; y = y + dy; return dy > 0 && y > env.height - env.opt.margins[2] ? env.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(env, origPath, prevprops) { var path = this._preparePathShow(env, origPath); 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) { if (!paper) paper = env.paper; if (path.length == 1 && path[0][0] == 'CIRCLE') { path = this._preparePathShow(env, path); return paper.circle(path[0][1], path[0][2], path[0][3]); } if (path.length == 1 && path[0][0] == 'TEXT') { path = this._preparePathShow(env, path); return paper.text(path[0][2], path[0][3], path[0][1]); } var props = this.getSVGProps(env, 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(env, piece.path, piece.fullattr); } if (typeof piece.fullattr.opacity == 'undefined') piece.fullattr.opacity = 1; } return piece.fullattr; }, _show : function(env, origPieces) { if ($.elycharts.featuresmanager) $.elycharts.featuresmanager.beforeShow(env, origPieces); pieces = this._getSortedPathData(origPieces); this._animationStackStart(env); var previousElement = false; for (var i = 0; i < pieces.length; i++) { var piece = pieces[i]; if ((typeof piece.show == 'undefined' || piece.show) && (typeof piece.parent == 'undefined' || typeof piece.parent.show == 'undefined' || piece.parent.show)) { // If there is piece.animation.element, this is the old element that must be transformed to the new one piece.element = piece.animation && piece.animation.element ? piece.animation.element : false; piece.hide = false; if (!piece.path) { // Element should not be shown or must be hidden: nothing to prepare piece.hide = true; } else if (piece.path.length == 1 && piece.path[0][0] == 'TEXT') { // TEXT // Animation is not supported, so if there's an old element i must hide it (with force = true to hide it for sure, even if there's a new version of same element) if (piece.element) { common.animationStackPush(env, piece, piece.element, false, piece.animation.speed, piece.animation.easing, piece.animation.delay, true); piece.animation.element = false; } piece.element = this.showPath(env, piece.path); // If this is a transition i must position new element if (piece.element && env.newopt && previousElement) piece.element.insertAfter(previousElement); } else if (piece.path.length == 1 && piece.path[0][0] == 'DOMELEMENT') { // DOMELEMENT // Already shown // Animation not supported } else if (piece.path.length == 1 && piece.path[0][0] == 'RELEMENT') { // RAPHAEL ELEMENT // Already shown // Animation is not supported, so if there's an old element i must hide it (with force = true to hide it for sure, even if there's a new version of same element) if (piece.element) { common.animationStackPush(env, piece, piece.element, false, piece.animation.speed, piece.animation.easing, piece.animation.delay, true); piece.animation.element = false; } piece.element = piece.path[0][1]; if (piece.element && previousElement) piece.element.insertAfter(previousElement); piece.attr = false; } else { // OTHERS if (!piece.element) { if (piece.animation && piece.animation.startPath && piece.animation.startPath.length) piece.element = this.showPath(env, piece.animation.startPath); else piece.element = this.showPath(env, piece.path); // If this is a transition i must position new element if (piece.element && env.newopt && previousElement) piece.element.insertAfter(previousElement); } } if (piece.element) { if (piece.attr) { if (!piece.animation) { // Standard piece visualization if (typeof piece.attr.opacity == 'undefined') piece.attr.opacity = 1; piece.element.attr(piece.attr); } else { // Piece animation if (!piece.animation.element) piece.element.attr(piece.animation.startAttr ? piece.animation.startAttr : piece.attr); //if (typeof animationAttr.opacity == 'undefined') // animationAttr.opacity = 1; common.animationStackPush(env, piece, piece.element, this.getPieceFullAttr(env, piece), piece.animation.speed, piece.animation.easing, piece.animation.delay); } } else if (piece.hide) // Hide the piece common.animationStackPush(env, piece, piece.element, false, piece.animation.speed, piece.animation.easing, piece.animation.delay); previousElement = piece.element; } } } this._animationStackEnd(env); if ($.elycharts.featuresmanager) $.elycharts.featuresmanager.afterShow(env, origPieces); }, /** * Given an array of pieces, return an array of single pathdata contained in pieces, sorted by zindex */ _getSortedPathData : function(pieces) { res = []; for (var i = 0; i < pieces.length; i++) { var piece = pieces[i]; if (piece.paths) { for (var j = 0; j < piece.paths.length; j++) { piece.paths[j].pos = res.length; piece.paths[j].parent = piece; res.push(piece.paths[j]); } } else { piece.pos = res.length; piece.parent = false; res.push(piece); } } return res.sort(function (a, b) { var za = typeof a.attr == 'undefined' || typeof a.attr.zindex == 'undefined' ? ( !a.parent || typeof a.parent.attr == 'undefined' || typeof a.parent.attr.zindex == 'undefined' ? 0 : a.parent.attr.zindex ) : a.attr.zindex; var zb = typeof b.attr == 'undefined' || typeof b.attr.zindex == 'undefined' ? ( !b.parent || typeof b.parent.attr == 'undefined' || typeof b.parent.attr.zindex == 'undefined' ? 0 : b.parent.attr.zindex ) : b.attr.zindex; return za < zb ? -1 : (za > zb ? 1 : (a.pos < b.pos ? -1 : (a.pos > b.pos ? 1 : 0))); }); }, _animationStackStart : function(env) { if (!env.animationStackDepth || env.animationStackDepth == 0) { env.animationStackDepth = 0; env.animationStack = {}; } env.animationStackDepth ++; }, _animationStackEnd : function(env) { env.animationStackDepth --; if (env.animationStackDepth == 0) { for (var delay in env.animationStack) { this._animationStackAnimate(env.animationStack[delay], delay); delete env.animationStack[delay]; } env.animationStack = {}; } }, /** * Inserisce l'animazione richiesta nello stack di animazioni. * Nel caso lo stack non sia inizializzato esegue subito l'animazione. */ animationStackPush : function(env, piece, element, newattr, speed, easing, delay, force) { if (typeof delay == 'undefined') delay = 0; if (!env.animationStackDepth || env.animationStackDepth == 0) { this._animationStackAnimate([{piece : piece, object : element, props : newattr, speed: speed, easing : easing, force : force}], delay); } else { if (!env.animationStack[delay]) env.animationStack[delay] = []; env.animationStack[delay].push({piece : piece, object : element, props : newattr, speed: speed, easing : easing, force : force}); } }, _animationStackAnimate : function(stack, delay) { var caller = this; var func = function() { var a = stack.pop(); var anim = caller._animationStackAnimateElement(a); while (stack.length > 0) { var b = stack.pop(); caller._animationStackAnimateElement(b, a, anim); } } if (delay > 0) setTimeout(func, delay); else func(); }, _animationStackAnimateElement : function (a, awith, awithanim) { //console.warn('call', a.piece.animationInProgress, a.force, a.piece.path, a.piece); if (a.force || !a.piece.animationInProgress) { // Metodo non documentato per bloccare l'animazione corrente a.object.stop(); if (!a.props) a.props = { opacity : 0 }; // TODO Sarebbe da rimuovere l'elemento alla fine if (!a.speed || a.speed <= 0) { //console.warn('direct'); a.object.attr(a.props); a.piece.animationInProgress = false; return; } a.piece.animationInProgress = true; //console.warn('START', a.piece.animationInProgress, a.piece.path, a.piece); // NOTA onEnd non viene chiamato se l'animazione viene bloccata con stop var onEnd = function() { //console.warn('END', a.piece.animationInProgress, a.piece); a.piece.animationInProgress = false } if (Raphael.animation) { var anim = Raphael.animation(a.props, a.speed, a.easing ? a.easing : 'linear', onEnd); if (awith) { // console.warn('animateWith', awith, awithanim, anim); a.object.animateWith(awith, awithanim, anim); } else { // console.warn('animate', anim); a.object.animate(anim); } return anim; } else { if (awith) { // console.warn('animateWith', awith, awithanim, anim); a.object.animateWith(awith, a.props, a.speed, a.easing ? a.easing : 'linear', onEnd); } else { // console.warn('animate', anim); a.object.animate(a.props, a.speed, a.easing ? a.easing : 'linear', onEnd); } return null; } } //else console.warn('SKIP', a.piece.animationInProgress, a.piece.path, a.piece); return null; } } var common = $.elycharts.common; /*********************************************************************** * FEATURESMANAGER **********************************************************************/ $.elycharts.featuresmanager = { managers : [], initialized : false, register : function(manager, priority) { $.elycharts.featuresmanager.managers.push([priority, manager]); $.elycharts.featuresmanager.initialized = false; }, init : function() { $.elycharts.featuresmanager.managers.sort(function(a, b) { return a[0] < b[0] ? -1 : (a[0] == b[0] ? 0 : 1) }); $.elycharts.featuresmanager.initialized = true; }, clear : function(env) { if (!$.elycharts.featuresmanager.initialized) this.init(); // reverse cycle over manager for (var i = $.elycharts.featuresmanager.managers.length - 1; i >= 0; i--) if ($.elycharts.featuresmanager.managers[i][1].clear) $.elycharts.featuresmanager.managers[i][1].clear(env); }, beforeShow : function(env, pieces) { if (!$.elycharts.featuresmanager.initialized) this.init(); for (var i = 0; i < $.elycharts.featuresmanager.managers.length; i++) if ($.elycharts.featuresmanager.managers[i][1].beforeShow) $.elycharts.featuresmanager.managers[i][1].beforeShow(env, pieces); }, afterShow : function(env, pieces) { if (!$.elycharts.featuresmanager.initialized) this.init(); for (var i = 0; i < $.elycharts.featuresmanager.managers.length; i++) if ($.elycharts.featuresmanager.managers[i][1].afterShow) $.elycharts.featuresmanager.managers[i][1].afterShow(env, pieces); }, onMouseOver : function(env, serie, index, mouseAreaData) { if (!$.elycharts.featuresmanager.initialized) this.init(); for (var i = 0; i < $.elycharts.featuresmanager.managers.length; i++) if ($.elycharts.featuresmanager.managers[i][1].onMouseOver) $.elycharts.featuresmanager.managers[i][1].onMouseOver(env, serie, index, mouseAreaData); }, onMouseOut : function(env, serie, index, mouseAreaData) { if (!$.elycharts.featuresmanager.initialized) this.init(); for (var i = 0; i < $.elycharts.featuresmanager.managers.length; i++) if ($.elycharts.featuresmanager.managers[i][1].onMouseOut) $.elycharts.featuresmanager.managers[i][1].onMouseOut(env, serie, index, mouseAreaData); }, onMouseEnter : function(env, serie, index, mouseAreaData) { if (!$.elycharts.featuresmanager.initialized) this.init(); for (var i = 0; i < $.elycharts.featuresmanager.managers.length; i++) if ($.elycharts.featuresmanager.managers[i][1].onMouseEnter) $.elycharts.featuresmanager.managers[i][1].onMouseEnter(env, serie, index, mouseAreaData); }, onMouseChanged : function(env, serie, index, mouseAreaData) { if (!$.elycharts.featuresmanager.initialized) this.init(); for (var i = 0; i < $.elycharts.featuresmanager.managers.length; i++) if ($.elycharts.featuresmanager.managers[i][1].onMouseChanged) $.elycharts.featuresmanager.managers[i][1].onMouseChanged(env, serie, index, mouseAreaData); }, onMouseExit : function(env, serie, index, mouseAreaData) { if (!$.elycharts.featuresmanager.initialized) this.init(); for (var i = 0; i < $.elycharts.featuresmanager.managers.length; i++) if ($.elycharts.featuresmanager.managers[i][1].onMouseExit) $.elycharts.featuresmanager.managers[i][1].onMouseExit(env, serie, index, mouseAreaData); } } })(jQuery); /*********************************************** * OGGETTI USATI: PIECE: Contiene un elemento da visualizzare nel grafico. E' un oggetto con queste proprietà: - section,[serie],[index],[subsection]: Dati che permettono di identificare che tipo di elemento è e a quale blocco della configurazione appartiene. Ad esempio gli elementi principali del chart hanno section="Series", serie=nome della serie, subSection = 'Plot' - [paths]: Contiene un array di pathdata, nel caso questo piece è costituito da piu' sottoelementi (ad esempio i Dots, o gli elementi di un Pie o Funnel) - [PATHDATA.*]: Se questo piece e' costituito da un solo elemento, i suoi dati sono memorizzati direttamente nella root di PIECE. - show: Proprieta' usata internamente per decidere se questo piece dovrà essere visualizzato o meno (in genere nel caso di una transizione che non ha variato questo piece, che quindi puo' essere lasciato allo stato precedente) - hide: Proprieta' usata internamente per decidere se l'elemento va nascosto, usato in caso di transizione se l'elemento non è piu' presente. PATHDATA: I dati utili per visualizzare un path nel canvas: - PATH: Il path che permette di disegnare l'elemento. Se NULL l'elemento è vuoto/ da non visualizzare (instanziato solo come placeholder) - attr: gli attributi Raphael dell'elemento. NULL se path è NULL. - [center]: centro del path - [rect]: rettangolo che include il path PATH: Un array in cui ogni elemento determina un passo del percorso per disegnare il grafico. E' una astrazione sul PATH SVG effettivo, e puo' avere alcuni valori speciali: [ [ 'TEXT', testo, x, y ] ] [ [ 'CIRCLE', x, y, raggio ] ] [ [ 'RECT', x1, y1, x2, y2, rounded ] ] (x1,y1 dovrebbero essere sempre le coordinate in alto a sx) [ [ 'SLICE', x, y, raggio, raggio int, angolo1, angolo2 ] ] (gli angoli sono in gradi) [ [ 'RELEMENT', element ] ] (elemento Raphael gia' disegnato) [ [ 'DOMELEMENT', element ] ] (elemento DOM - in genere un DIV html - già disegnato) [ ... Path SVG ... ] ------------------------------------------------------------------------ Z-INDEX: 0 : base 10 : tooltip 20 : interactive area (tutti gli elementi innescati dalla interactive area dovrebbero essere < 20) 25 : label / balloons (potrebbero essere resi cliccabili dall'esterno, quindi > 20) ------------------------------------------------------------------------ USEFUL RESOURCES: http://docs.jquery.com/Plugins/Authoring http://www.learningjquery.com/2007/10/a-plugin-development-pattern http://dean.edwards.name/packer/2/usage/#special-chars http://raphaeljs.com/reference.html#attr TODO * ottimizzare common.areaProps * rifare la posizione del tooltip del pie * ripristinare shadow *********************************************/ elycharts.js-2.1.5+ds/src/elycharts_defaults.js000066400000000000000000000505051240311710500215430ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 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 : [], */ // Autoresize uses jQuery resize event to automatically resize the chart to the container // autoresize makes sense only when width or height is not defined. // autoresize: false, // 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' }, // used to be true pixelWorkAround : { active : Raphael.svg }, 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, // radius for the dots (used to be 4 but there also was a bug preventing radius support, so moved to 0) dotR : 0, dotProps : { type : "rect", width : 10, height : 10 }, textProps : { font: '12px Arial', fill: "#000" } }, debug : { active : false } }, enableInternalCaching : true, nop : 0 }, line : { template : 'common', // absolute margin left to both sides of each column / column group. barMargins : 0, // overlap between additional columns over the previous one (ignored for the first serie) barOverlapPerc : 0, // disable this if you want to use null values and want the lines/area to be broken over null values avgOverNulls: true, // 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 // Radius in percentage of the available space //rPerc : 80 // 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, // @since elycharts 2.1.5 (previously there was no margins support so when we implemented it we had to add a 0 margin // here to not start adding the common margin to every pie user margins : [0, 0, 0, 0], 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); elycharts.js-2.1.5+ds/src/elycharts_manager_anchor.js000066400000000000000000000072341240311710500227010ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * FEATURE: ANCHOR * * Permette di collegare i dati del grafico con delle aree esterne, * identificate dal loro selettore CSS, e di interagire con esse. **********************************************************************/ $.elycharts.anchormanager = { afterShow : function(env, pieces) { // Prendo le aree gestite da mouseAreas, e metto i miei listener // Non c'e' bisogno di gestire il clean per una chiamata successiva, lo fa gia' il mouseareamanager // Tranne per i bind degli eventi jquery if (!env.opt.anchors) return; if (!env.anchorBinds) env.anchorBinds = []; while (env.anchorBinds.length) { var b = env.anchorBinds.pop(); $(b[0]).unbind(b[1], b[2]); } for (var i = 0; i < env.mouseAreas.length; i++) { var serie = env.mouseAreas[i].piece ? env.mouseAreas[i].piece.serie : false; var anc; if (serie) anc = env.opt.anchors[serie][env.mouseAreas[i].index]; else anc = env.opt.anchors[env.mouseAreas[i].index]; if (anc && env.mouseAreas[i].props.anchor && env.mouseAreas[i].props.anchor.highlight) { (function(env, mouseAreaData, anc, caller) { var f1 = function() { caller.anchorMouseOver(env, mouseAreaData); }; var f2 = function() { caller.anchorMouseOut(env, mouseAreaData); }; if (!env.mouseAreas[i].props.anchor.useMouseEnter) { env.anchorBinds.push([anc, 'mouseover', f1]); env.anchorBinds.push([anc, 'mouseout', f2]); $(anc).mouseover(f1); $(anc).mouseout(f2); } else { env.anchorBinds.push([anc, 'mouseenter', f1]); env.anchorBinds.push([anc, 'mouseleave', f2]); $(anc).mouseenter(f1); $(anc).mouseleave(f2); } })(env, env.mouseAreas[i], anc, this); } } env.onAnchors = []; }, anchorMouseOver : function(env, mouseAreaData) { $.elycharts.highlightmanager.onMouseOver(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); }, anchorMouseOut : function(env, mouseAreaData) { $.elycharts.highlightmanager.onMouseOut(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); }, onMouseOver : function(env, serie, index, mouseAreaData) { if (!env.opt.anchors) return; if (mouseAreaData.props.anchor && mouseAreaData.props.anchor.addClass) { //var serie = mouseAreaData.piece ? mouseAreaData.piece.serie : false; var anc; if (serie) anc = env.opt.anchors[serie][mouseAreaData.index]; else anc = env.opt.anchors[mouseAreaData.index]; if (anc) { $(anc).addClass(mouseAreaData.props.anchor.addClass); env.onAnchors.push([anc, mouseAreaData.props.anchor.addClass]); } } }, onMouseOut : function(env, serie, index, mouseAreaData) { if (!env.opt.anchors) return; while (env.onAnchors.length > 0) { var o = env.onAnchors.pop(); $(o[0]).removeClass(o[1]); } } } $.elycharts.featuresmanager.register($.elycharts.anchormanager, 30); })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_manager_animation.js000066400000000000000000000330571240311710500234100ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * ANIMATIONMANAGER **********************************************************************/ $.elycharts.animationmanager = { beforeShow : function(env, pieces) { if (!env.newopt) this.startAnimation(env, pieces); else this.stepAnimation(env, pieces); }, stepAnimation : function(env, pieces) { pieces = this._stepAnimationInt(env, pieces); }, _stepAnimationInt : function(env, pieces, section, serie, internal) { for (var i = 0; i < pieces.length; i++) { var animationProps = common.areaProps(env, section ? section : pieces[i].section, serie ? serie : pieces[i].serie); if (animationProps && animationProps.stepAnimation) animationProps = animationProps.stepAnimation; else animationProps = env.opt.features.animation.stepAnimation; if (typeof pieces[i].paths == 'undefined') { if (animationProps && animationProps.active && pieces[i].animation) { pieces[i].animation.speed = animationProps && animationProps.speed ? animationProps.speed : 300; pieces[i].animation.easing = animationProps && animationProps.easing ? animationProps.easing : ''; pieces[i].animation.delay = animationProps && animationProps.delay ? animationProps.delay : 0; if (!pieces[i].animation.element) pieces[i].animation.startAttr = {opacity : 0}; } } else { this._stepAnimationInt(env, pieces[i].paths, pieces[i].section, pieces[i].serie, true); } } }, startAnimation : function(env, pieces) { for (var i = 0; i < pieces.length; i++) if (pieces[i].paths || pieces[i].path) { var props = common.areaProps(env, pieces[i].section, pieces[i].serie); if (props && props.startAnimation) props = props.startAnimation; else props = env.opt.features.animation.startAnimation; if (props && props.active) { if (props.type == 'simple' || pieces[i].section != 'Series') this.animationSimple(env, props, pieces[i]); if (props.type == 'grow') this.animationGrow(env, props, pieces[i]); if (props.type == 'avg') this.animationAvg(env, props, pieces[i]); if (props.type == 'reg') this.animationReg(env, props, pieces[i]); } } }, /** * Inserisce i dati base di animazione del piece e la transizione di attributi */ _animationPiece : function(piece, animationProps, subSection) { if (piece.paths) { for (var i = 0; i < piece.paths.length; i++) this._animationPiece(piece.paths[i], animationProps, subSection); } else if (piece.path) { piece.animation = { speed : animationProps.speed, easing : animationProps.easing, delay : animationProps.delay, startPath : [], startAttr : common._clone(piece.attr) }; if (animationProps.propsTo) piece.attr = $.extend(true, piece.attr, animationProps.propsTo); if (animationProps.propsFrom) piece.animation.startAttr = $.extend(true, piece.animation.startAttr, animationProps.propsFrom); if (subSection && animationProps[subSection.toLowerCase() + 'PropsFrom']) piece.animation.startAttr = $.extend(true, piece.animation.startAttr, animationProps[subSection.toLowerCase() + 'PropsFrom']); if (typeof piece.animation.startAttr.opacity != 'undefined' && typeof piece.attr.opacity == 'undefined') piece.attr.opacity = 1; } }, animationSimple : function(env, props, piece) { this._animationPiece(piece, props, piece.subSection); }, animationGrow : function(env, props, piece) { this._animationPiece(piece, props, piece.subSection); var i, npath, y; switch (env.opt.type) { case 'line': y = env.height - env.opt.margins[2]; switch (piece.subSection) { case 'Plot': if (!piece.paths) { npath = [ 'LINE', [], piece.path[0][2]]; for (i = 0; i < piece.path[0][1].length; i++) npath[1].push([ piece.path[0][1][i][0], piece.path[0][1][i][1] == null ? null : y ]); piece.animation.startPath.push(npath); } else { for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path) piece.paths[i].animation.startPath.push([ 'RECT', piece.paths[i].path[0][1], y, piece.paths[i].path[0][3], y ]); } break; case 'Fill': npath = [ 'LINEAREA', [], [], piece.path[0][3]]; for (i = 0; i < piece.path[0][1].length; i++) { npath[1].push([ piece.path[0][1][i][0], piece.path[0][1][i][1] == null ? null : y ]); npath[2].push([ piece.path[0][2][i][0], piece.path[0][2][i][1] == null ? null : y ]); } piece.animation.startPath.push(npath); break; case 'Dot': for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path) piece.paths[i].animation.startPath.push(['CIRCLE', piece.paths[i].path[0][1], y, piece.paths[i].path[0][3]]); break; } break; case 'pie': if (piece.subSection == 'Plot') for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path && piece.paths[i].path[0][0] == 'SLICE') piece.paths[i].animation.startPath.push([ 'SLICE', piece.paths[i].path[0][1], piece.paths[i].path[0][2], piece.paths[i].path[0][4] + piece.paths[i].path[0][3] * 0.1, piece.paths[i].path[0][4], piece.paths[i].path[0][5], piece.paths[i].path[0][6] ]); break; case 'funnel': alert('Unsupported animation GROW for funnel'); break; case 'barline': var x; if (piece.section == 'Series' && piece.subSection == 'Plot') { if (!props.subType) x = env.opt.direction != 'rtl' ? env.opt.margins[3] : env.width - env.opt.margins[1]; else if (props.subType == 1) x = env.opt.direction != 'rtl' ? env.width - env.opt.margins[1] : env.opt.margins[3]; for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path) { if (!props.subType || props.subType == 1) piece.paths[i].animation.startPath.push([ 'RECT', x, piece.paths[i].path[0][2], x, piece.paths[i].path[0][4], piece.paths[i].path[0][5] ]); else { y = (piece.paths[i].path[0][2] + piece.paths[i].path[0][4]) / 2; piece.paths[i].animation.startPath.push([ 'RECT', piece.paths[i].path[0][1], y, piece.paths[i].path[0][3], y, piece.paths[i].path[0][5] ]); } } } break; } }, _animationAvgXYArray : function(arr) { var res = [], avg = 0, i; var count = 0; for (i = 0; i < arr.length; i++) if (arr[i][1] != null) { avg += arr[i][1]; count++; } avg = avg / count; for (i = 0; i < arr.length; i++) res.push([ arr[i][0], arr[i][1] == null ? null : avg ]); return res; }, animationAvg : function(env, props, piece) { this._animationPiece(piece, props, piece.subSection); var avg = 0, i, l; switch (env.opt.type) { case 'line': switch (piece.subSection) { case 'Plot': if (!piece.paths) { // LINE piece.animation.startPath.push([ 'LINE', this._animationAvgXYArray(piece.path[0][1]), piece.path[0][2] ]); } else { // BAR l = 0; for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path) { l ++; avg += piece.paths[i].path[0][2]; } avg = avg / l; for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path) piece.paths[i].animation.startPath.push([ "RECT", piece.paths[i].path[0][1], avg, piece.paths[i].path[0][3], piece.paths[i].path[0][4] ]); } break; case 'Fill': piece.animation.startPath.push([ 'LINEAREA', this._animationAvgXYArray(piece.path[0][1]), this._animationAvgXYArray(piece.path[0][2]), piece.path[0][3] ]); break; case 'Dot': l = 0; for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path) { l ++; avg += piece.paths[i].path[0][2]; } avg = avg / l; for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path) piece.paths[i].animation.startPath.push(['CIRCLE', piece.paths[i].path[0][1], avg, piece.paths[i].path[0][3]]); break; } break; case 'pie': var delta = 360 / piece.paths.length; if (piece.subSection == 'Plot') for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path && piece.paths[i].path[0][0] == 'SLICE') piece.paths[i].animation.startPath.push([ 'SLICE', piece.paths[i].path[0][1], piece.paths[i].path[0][2], piece.paths[i].path[0][3], piece.paths[i].path[0][4], i * delta, (i + 1) * delta ]); break; case 'funnel': alert('Unsupported animation AVG for funnel'); break; case 'barline': alert('Unsupported animation AVG for barline'); break; } }, _animationRegXYArray : function(arr) { var res = []; var c = arr.length; var start = 0; var end = c - 1; while (arr[start][1] == null) start++; while (arr[end][1] == null) end--; var y1 = arr[0][1]; var y2 = arr[c - 1][1]; for (var i = 0; i < arr.length; i++) { if (arr[i][1] == null) res.push([ arr[i][0], null ]); else { res.push([ arr[i][0], arr[start][1] + (arr[end][1] - arr[start][1]) / (end - start) * ( i - start) ]); } } return res; }, animationReg : function(env, props, piece) { this._animationPiece(piece, props, piece.subSection); var i, c, y1, y2; switch (env.opt.type) { case 'line': switch (piece.subSection) { case 'Plot': if (!piece.paths) { // LINE piece.animation.startPath.push([ 'LINE', this._animationRegXYArray(piece.path[0][1]), piece.path[0][2] ]); } else { // BAR c = piece.paths.length; if (c > 1) { for (i = 0; !piece.paths[i].path && i < piece.paths.length; i++) {} y1 = piece.paths[i].path ? common.getY(piece.paths[i].path[0]) : 0; for (i = piece.paths.length - 1; !piece.paths[i].path && i >= 0; i--) {} y2 = piece.paths[i].path ? common.getY(piece.paths[i].path[0]) : 0; for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path) piece.paths[i].animation.startPath.push([ "RECT", piece.paths[i].path[0][1], y1 + (y2 - y1) / (c - 1) * i, piece.paths[i].path[0][3], piece.paths[i].path[0][4] ]); } } break; case 'Fill': piece.animation.startPath.push([ 'LINEAREA', this._animationRegXYArray(piece.path[0][1]), this._animationRegXYArray(piece.path[0][2]), piece.path[0][3] ]); break; case 'Dot': c = piece.paths.length; if (c > 1) { for (i = 0; !piece.paths[i].path && i < piece.paths.length; i++) {} y1 = piece.paths[i].path ? common.getY(piece.paths[i].path[0]) : 0; for (i = piece.paths.length - 1; !piece.paths[i].path && i >= 0; i--) {} y2 = piece.paths[i].path ? common.getY(piece.paths[i].path[0]) : 0; for (i = 0; i < piece.paths.length; i++) if (piece.paths[i].path) piece.paths[i].animation.startPath.push(['CIRCLE', piece.paths[i].path[0][1], y1 + (y2 - y1) / (c - 1) * i, piece.paths[i].path[0][3]]); } break; } break; case 'pie': alert('Unsupported animation REG for pie'); break; case 'funnel': alert('Unsupported animation REG for funnel'); break; case 'barline': alert('Unsupported animation REG for barline'); break; } } } $.elycharts.featuresmanager.register($.elycharts.animationmanager, 10); /*********************************************************************** * FRAMEANIMATIONMANAGER **********************************************************************/ $.elycharts.frameanimationmanager = { beforeShow : function(env, pieces) { if (env.opt.features.frameAnimation.active) $(env.container.get(0)).css(env.opt.features.frameAnimation.cssFrom); }, afterShow : function(env, pieces) { if (env.opt.features.frameAnimation.active) env.container.animate(env.opt.features.frameAnimation.cssTo, env.opt.features.frameAnimation.speed, env.opt.features.frameAnimation.easing); } }; $.elycharts.featuresmanager.register($.elycharts.frameanimationmanager, 90); })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_manager_balloon.js000066400000000000000000000060451240311710500230540ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * FEATURE: BALLOON **********************************************************************/ $.elycharts.balloonmanager = { afterShow : function(env, pieces) { // TODO transizioni if (env.opt.features.balloons.active && env.opt.balloons && env.opt.type == 'funnel') { var conf = env.opt.features.balloons; var lastSerie = false, lastIndex = false; for (var i = 0; i < pieces.length; i++) { if (pieces[i].section == 'Series' && pieces[i].subSection == 'Plot') { for (var index = 0; index < pieces[i].paths.length; index ++) if (env.opt.balloons[pieces[i].serie] && env.opt.balloons[pieces[i].serie][index]) { lastSerie = pieces[i].serie; lastIndex = index; this.drawBalloon(env, lastSerie, index, pieces[i].paths[index].rect); } } else if (lastSerie && pieces[i].section == 'Sector' && pieces[i].serie == 'bottom' && !pieces[i].subSection && lastIndex < env.opt.balloons[lastSerie].length - 1) { this.drawBalloon(env, lastSerie, env.opt.balloons[lastSerie].length - 1, pieces[i].rect); } } } }, drawBalloon : function(env, serie, index, rect) { var conf = env.opt.features.balloons; var balloon = env.opt.balloons[serie][index]; //var offset = $(env.container).offset(); var style = { position : 'absolute', 'z-index' : 25, //top : offset.top + rect[1] , left: offset.left + conf.left, margin : rect[1] + "px 0 0 " + conf.left + "px", height : (conf.height ? conf.height : rect[3] - rect[1]) - conf.padding[0] * 2 , width : conf.width ? conf.width : rect[0], padding : conf.padding[0] + 'px ' + conf.padding[1] + 'px' }; if (typeof balloon == 'string') $("
").css(style).css(conf.style).html(balloon).prependTo(env.container); else $(balloon).css(style).css(conf.style).prependTo(env.container); // Disegna la linea if (conf.line) { var path = []; for (var j = 0; j < conf.line.length; j++) { if (j == 0) path.push([ 'M', rect[0] - conf.line[j][0], rect[1] + conf.line[j][1]]); else if (j == conf.line.length - 1) path.push([ 'L', conf.left + (conf.width ? conf.width : rect[0]) - conf.line[j][0], rect[1] + conf.line[j][1] ]); else path.push([ 'L', rect[0] - conf.line[j][0], rect[1] + conf.line[j][1]]); } common.showPath(env, path).attr(conf.lineProps); } } } $.elycharts.featuresmanager.register($.elycharts.balloonmanager, 30); })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_manager_highlight.js000066400000000000000000000250441240311710500233750ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * FEATURE: HIGHLIGHT * * Permette di evidenziare in vari modi l'area in cui si passa con il * mouse. **********************************************************************/ $.elycharts.highlightmanager = { removeHighlighted : function(env, full) { if (env.highlighted) while (env.highlighted.length > 0) { var o = env.highlighted.pop(); if (o.piece) { if (full) common.animationStackPush(env, o.piece, o.piece.element, common.getPieceFullAttr(env, o.piece), o.cfg.restoreSpeed, o.cfg.restoreEasing, 0, true); } else o.element.remove(); } }, afterShow : function(env, pieces) { if (env.highlighted && env.highlighted.length > 0) this.removeHighlighted(env, false); env.highlighted = []; }, onMouseOver : function(env, serie, index, mouseAreaData) { var path, element; // TODO Se non e' attivo l'overlay (per la serie o per tutto) e' inutile fare il resto // Cerco i piece da evidenziare (tutti quelli che sono costituiti da path multipli) for (var i = 0; i < mouseAreaData.pieces.length; i++) // Il loop sotto estrae solo i pieces con array di path (quindi non i line o i fill del linechart ... ma il resto si) if (mouseAreaData.pieces[i].section == 'Series' && mouseAreaData.pieces[i].paths && (!serie || mouseAreaData.pieces[i].serie == serie) && mouseAreaData.pieces[i].paths[index] && mouseAreaData.pieces[i].paths[index].element) { var piece = mouseAreaData.pieces[i].paths[index]; element = piece.element; path = piece.path; var attr = common.getElementOriginalAttrs(element); var newattr = false; // In caso la geometria dell'oggetto è modificata mediante attr (es: per circle) qui memorizza i nuovi attributi var props = serie ? mouseAreaData.props : common.areaProps(env, mouseAreaData.pieces[i].section, mouseAreaData.pieces[i].serie); var pelement, ppiece, ppath; if (path && props.highlight) { if (props.highlight.scale) { var scale = props.highlight.scale; if (typeof scale == 'number') scale = [scale, scale]; if (path[0][0] == 'RECT') { var w = path[0][3] - path[0][1]; var h = path[0][4] - path[0][2]; path = [ [ 'RECT', path[0][1], path[0][2] - h * (scale[1] - 1), path[0][3] + w * (scale[0] - 1), path[0][4] ] ]; common.animationStackPush(env, piece, element, common.getSVGProps(env, path), props.highlight.scaleSpeed, props.highlight.scaleEasing); } else if (path[0][0] == 'CIRCLE') { // I pass directly new radius newattr = {r : path[0][3] * scale[0]}; common.animationStackPush(env, piece, element, newattr, props.highlight.scaleSpeed, props.highlight.scaleEasing); } else if (path[0][0] == 'SLICE') { // Per lo slice x e' il raggio, y e' l'angolo var d = (path[0][6] - path[0][5]) * (scale[1] - 1) / 2; if (d > 90) d = 90; path = [ [ 'SLICE', path[0][1], path[0][2], path[0][3] * scale[0], path[0][4], path[0][5] - d, path[0][6] + d ] ]; common.animationStackPush(env, piece, element, common.getSVGProps(env, path), props.highlight.scaleSpeed, props.highlight.scaleEasing); } else if (env.opt.type == 'funnel') { var dx = (piece.rect[2] - piece.rect[0]) * (scale[0] - 1) / 2; var dy = (piece.rect[3] - piece.rect[1]) * (scale[1] - 1) / 2; // Specifico di un settore del funnel // SHOULD ALREADY BE DONE BY core common.animationStackStart(env); path = [ common.movePath(env, [ path[0]], [-dx, -dy])[0], common.movePath(env, [ path[1]], [+dx, -dy])[0], common.movePath(env, [ path[2]], [+dx, +dy])[0], common.movePath(env, [ path[3]], [-dx, +dy])[0], path[4] ]; common.animationStackPush(env, piece, element, common.getSVGProps(env, path), props.highlight.scaleSpeed, props.highlight.scaleEasing, 0, true); // Se c'e' un piece precedente lo usa, altrimenti cerca un topSector per la riduzione pelement = false; if (index > 0) { ppiece = mouseAreaData.pieces[i].paths[index - 1]; pelement = ppiece.element; ppath = ppiece.path; } else { ppiece = common.findInPieces(mouseAreaData.pieces, 'Sector', 'top'); if (ppiece) { pelement = ppiece.element; ppath = ppiece.path; } } if (pelement) { //pattr = common.getElementOriginalAttrs(pelement); ppath = [ ppath[0], ppath[1], common.movePath(env, [ ppath[2]], [+dx, -dy])[0], common.movePath(env, [ ppath[3]], [-dx, -dy])[0], ppath[4] ]; common.animationStackPush(env, ppiece, pelement, common.getSVGProps(env, ppath), props.highlight.scaleSpeed, props.highlight.scaleEasing, 0, true); env.highlighted.push({piece : ppiece, cfg : props.highlight}); } // Se c'e' un piece successivo lo usa, altrimenti cerca un bottomSector per la riduzione pelement = false; if (index < mouseAreaData.pieces[i].paths.length - 1) { ppiece = mouseAreaData.pieces[i].paths[index + 1]; pelement = ppiece.element; ppath = ppiece.path; } else { ppiece = common.findInPieces(mouseAreaData.pieces, 'Sector', 'bottom'); if (ppiece) { pelement = ppiece.element; ppath = ppiece.path; } } if (pelement) { //var pattr = common.getElementOriginalAttrs(pelement); ppath = [ common.movePath(env, [ ppath[0]], [-dx, +dy])[0], common.movePath(env, [ ppath[1]], [+dx, +dy])[0], ppath[2], ppath[3], ppath[4] ]; common.animationStackPush(env, ppiece, pelement, common.getSVGProps(env, ppath), props.highlight.scaleSpeed, props.highlight.scaleEasing, 0, true); env.highlighted.push({piece : ppiece, cfg : props.highlight}); } // SHOULD ALREADY BE DONE BY core: common.animationStackEnd(env); } /* Con scale non va bene if (!attr.scale) attr.scale = [1, 1]; element.attr({scale : [scale[0], scale[1]]}); */ } if (props.highlight.newProps) { for (var a in props.highlight.newProps) if (typeof attr[a] == 'undefined') attr[a] = false; common.animationStackPush(env, piece, element, props.highlight.newProps); } if (props.highlight.move) { var offset = $.isArray(props.highlight.move) ? props.highlight.move : [props.highlight.move, 0]; path = common.movePath(env, path, offset); common.animationStackPush(env, piece, element, common.getSVGProps(env, path), props.highlight.moveSpeed, props.highlight.moveEasing); } //env.highlighted.push({element : element, attr : attr}); env.highlighted.push({piece : piece, cfg : props.highlight}); if (props.highlight.overlayProps) { // NOTA: path e' il path modificato dai precedenti (cosi' l'overlay tiene conto della cosa), deve guardare anche a newattr //BIND: mouseAreaData.listenerDisabled = true; element = common.showPath(env, path); if (newattr) element.attr(newattr); element.attr(props.highlight.overlayProps); //BIND: $(element.node).unbind().mouseover(mouseAreaData.mouseover).mouseout(mouseAreaData.mouseout); // Se metto immediatamente il mouseAreaData.listenerDisabled poi va comunque un mouseout dalla vecchia area e va // in loop. TODO Rivedere e sistemare anche per tooltip //BIND: setTimeout(function() { mouseAreaData.listenerDisabled = false; }, 10); attr = false; env.highlighted.push({element : element, attr : attr, cfg : props.highlight}); } } } if (env.opt.features.highlight.indexHighlight && env.opt.type == 'line') { var t = env.opt.features.highlight.indexHighlight; if (t == 'auto') t = (env.indexCenter == 'bar' ? 'bar' : 'line'); var delta1 = (env.width - env.opt.margins[3] - env.opt.margins[1]) / (env.opt.labels.length > 0 ? env.opt.labels.length : 1); var delta2 = (env.width - env.opt.margins[3] - env.opt.margins[1]) / (env.opt.labels.length > 1 ? env.opt.labels.length - 1 : 1); var lineCenter = true; switch (t) { case 'bar': path = [ ['RECT', env.opt.margins[3] + index * delta1, env.opt.margins[0] , env.opt.margins[3] + (index + 1) * delta1, env.height - env.opt.margins[2] ] ]; break; case 'line': lineCenter = false; case 'barline': var x = Math.round((lineCenter ? delta1 / 2 : 0) + env.opt.margins[3] + index * (lineCenter ? delta1 : delta2)); path = [[ 'M', x, env.opt.margins[0]], ['L', x, env.height - env.opt.margins[2]]]; } if (path) { //BIND: mouseAreaData.listenerDisabled = true; element = common.showPath(env, path).attr(env.opt.features.highlight.indexHighlightProps); //BIND: $(element.node).unbind().mouseover(mouseAreaData.mouseover).mouseout(mouseAreaData.mouseout); //BIND: setTimeout(function() { mouseAreaData.listenerDisabled = false; }, 10); env.highlighted.push({element : element, attr : false, cfg : env.opt.features.highlight}); } } }, onMouseOut : function(env, serie, index, mouseAreaData) { this.removeHighlighted(env, true); } }; $.elycharts.featuresmanager.register($.elycharts.highlightmanager, 21); })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_manager_label.js000066400000000000000000000124761240311710500225120ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * FEATURE: LABELS * * Permette di visualizzare in vari modi le label del grafico. * In particolare per pie e funnel permette la visualizzazione all'interno * delle fette. * Per i line chart le label sono visualizzate già nella gestione assi. * * TODO: * - Comunque per i line chart si potrebbe gestire la visualizzazione * all'interno delle barre, o sopra i punti. **********************************************************************/ $.elycharts.labelmanager = { beforeShow : function(env, pieces) { if (!common.executeIfChanged(env, ['labels', 'values', 'series'])) return; if (env.opt.labels && (env.opt.type == 'pie' || env.opt.type == 'funnel')) { var /*lastSerie = false, */lastIndex = false; var paths; for (var i = 0; i < pieces.length; i++) { if (pieces[i].section == 'Series' && pieces[i].subSection == 'Plot') { var props = common.areaProps(env, 'Series', pieces[i].serie); if (env.emptySeries && env.opt.series.empty) props.label = $.extend(true, props.label, env.opt.series.empty.label); if (props && props.label && props.label.active) { paths = []; for (var index = 0; index < pieces[i].paths.length; index++) if (pieces[i].paths[index].path) { //lastSerie = pieces[i].serie; lastIndex = index; paths.push(this.showLabel(env, pieces[i], pieces[i].paths[index], pieces[i].serie, index, pieces)); } else paths.push({ path : false, attr : false }); pieces.push({ section : pieces[i].section, serie : pieces[i].serie, subSection : 'Label', paths: paths }); } } else if (pieces[i].section == 'Sector' && pieces[i].serie == 'bottom' && !pieces[i].subSection && lastIndex < env.opt.labels.length - 1) { paths = []; paths.push(this.showLabel(env, pieces[i], pieces[i], 'Series', env.opt.labels.length - 1, pieces)); pieces.push({ section : pieces[i].section, serie : pieces[i].serie, subSection : 'Label', paths: paths }); } } } }, showLabel : function(env, piece, path, serie, index, pieces) { var pp = common.areaProps(env, 'Series', serie, index); if (env.opt.labels[index] || pp.label.label) { var p = path; var label = pp.label.label ? pp.label.label : env.opt.labels[index]; var center = common.getCenter(p, pp.label.offset); if (!pp.label.html) { var attr = pp.label.props; if (pp.label.frameAnchor) { attr = common._clone(pp.label.props); attr['text-anchor'] = pp.label.frameAnchor[0]; attr['alignment-baseline'] = pp.label.frameAnchor[1]; } /*pieces.push({ path : [ [ 'TEXT', label, center[0], center[1] ] ], attr : attr, section: 'Series', serie : serie, index : index, subSection : 'Label' });*/ return { path : [ [ 'TEXT', label, center[0], center[1] ] ], attr : attr }; } else { var opacity = 1; var style = common._clone(pp.label.style); var set_opacity = (typeof style.opacity != 'undefined') if (set_opacity) { opacity = style.opacity; style.opacity = 0; } style.position = 'absolute'; style['z-index'] = 25; var el; if (typeof label == 'string') el = $('
' + label + '
').css(style).prependTo(env.container); else el = $(label).css(style).prependTo(env.container); // Centramento corretto label if (env.opt.features.debug.active && el.height() == 0) alert('DEBUG: Al gestore label e\' stata passata una label ancora senza dimensioni, quindi ancora non disegnata. Per questo motivo il posizionamento potrebbe non essere correto.'); var posX = center[0]; var posY = center[1]; if (!pp.label.frameAnchor || pp.label.frameAnchor[0] == 'middle') posX -= el.width() / 2; else if (pp.label.frameAnchor && pp.label.frameAnchor[0] == 'end') posX -= el.width(); if (!pp.label.frameAnchor || pp.label.frameAnchor[1] == 'middle') posY -= el.height() / 2; else if (pp.label.frameAnchor && pp.label.frameAnchor[1] == 'top') posY -= el.height(); if (set_opacity) el.css({ margin: posY + 'px 0 0 ' + posX + 'px', opacity : opacity}); else el.css({ margin: posY + 'px 0 0 ' + posX + 'px'}); /*pieces.push({ path : [ [ 'DOMELEMENT', el ] ], attr : false, section: 'Series', serie : serie, index : index, subSection : 'Label' });*/ return { path : [ [ 'DOMELEMENT', el ] ], attr : false }; } } return false; } } $.elycharts.featuresmanager.register($.elycharts.labelmanager, 5); })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_manager_legend.js000066400000000000000000000141361240311710500226640ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * FEATURE: LEGEND **********************************************************************/ $.elycharts.legendmanager = { afterShow : function(env, pieces) { // TODO the whole thing should simply return "pieces" whose visibility is handled by core, so to enable animations and // make things more generic. if (env.legenditems) { for (item in env.legenditems) { env.legenditems[item].remove(); } env.legenditems = false; } if (!env.opt.legend || env.opt.legend.length == 0) return; var props = env.opt.features.legend; if (props === false) return; var propsx = props.x; if (propsx == 'auto') { var autox = 1; propsx = 0; } var propswidth = props.width; if (propswidth == 'auto') { var autowidth = 1; propswidth = env.width; } var wauto = 0; var items = []; // env.opt.legend normalmente è { serie : 'Legend', ... }, per i pie invece { serie : ['Legend', ...], ... } var legendCount = 0; var serie, data, h, w, x, y, xd; for (serie in env.opt.legend) { if (env.opt.type != 'pie') legendCount ++; else legendCount += env.opt.legend[serie].length; } var i = 0; for (serie in env.opt.legend) { if (env.opt.type != 'pie') data = [ env.opt.legend[serie] ]; else data = env.opt.legend[serie]; for (var j = 0; j < data.length; j++) { var sprops = common.areaProps(env, 'Series', serie, env.opt.type == 'pie' ? j : false); var computedProps = $.extend(true, {}, props); if (sprops.legend) computedProps = $.extend(true, computedProps, sprops.legend); var color = common.getItemColor(env, serie, env.opt.type == 'pie' ? j : false); if (color) { common.colorize(env, computedProps, [['dotProps', 'fill']], color); } // legacy support for legend dot color inherited from pie "fill" // TODO maybe we should simply remove this and leave the "color" support only if (!computedProps.dotProps.fill && env.opt.type == 'pie') { if (sprops.plotProps && sprops.plotProps.fill) computedProps.dotProps.fill = sprops.plotProps.fill; } var hMargin = props.margins ? props.margins[0] + props.margins[2] : 0; var wMargin = props.margins ? props.margins[1] + props.margins[3] : 0; var tMargin = props.margins ? props.margins[0] : 0; var lMargin = props.margins ? props.margins[3] : 0; if (!props.horizontal) { // Posizione dell'angolo in alto a sinistra h = (props.height - hMargin) / legendCount; w = propswidth - wMargin; x = Math.floor(propsx + lMargin); y = Math.floor(props.y + tMargin + h * i); } else { h = props.height - hMargin; if (!props.itemWidth || props.itemWidth == 'fixed') { w = (propswidth - wMargin) / legendCount; x = Math.floor(propsx + lMargin + w * i); } else { w = (propswidth - wMargin) - wauto; x = propsx + lMargin + wauto; } y = Math.floor(props.y + tMargin); } if (computedProps.dotType == "rect") { items.push(common.showPath(env, [ [ 'RECT', props.dotMargins[0] + x, y + Math.floor((h - computedProps.dotHeight) / 2), props.dotMargins[0] + x + computedProps.dotWidth, y + Math.floor((h - computedProps.dotHeight) / 2) + computedProps.dotHeight, computedProps.dotR ] ]).attr(computedProps.dotProps)); xd = props.dotMargins[0] + computedProps.dotWidth + props.dotMargins[1]; } else if (computedProps.dotType == "circle") { items.push(common.showPath(env, [ [ 'CIRCLE', props.dotMargins[0] + x + computedProps.dotR, y + (h / 2), computedProps.dotR ] ]).attr(computedProps.dotProps)); xd = props.dotMargins[0] + computedProps.dotR * 2 + props.dotMargins[1]; } var text = data[j]; var t = common.showPath(env, [ [ 'TEXT', text, x + xd, y + Math.ceil(h / 2) + (Raphael.VML ? 2 : 0) ] ]).attr({"text-anchor" : "start"}).attr(computedProps.textProps); //.hide(); items.push(t); while (t.getBBox().width > (w - xd) && t.getBBox().width > 10) { text = text.substring(0, text.length - 1); t.attr({text : text}); } t.show(); if (props.horizontal && props.itemWidth == 'auto') wauto += xd + t.getBBox().width + 4; else if (!props.horizontal && autowidth) wauto = t.getBBox().width + xd > wauto ? t.getBBox().width + xd : wauto; else wauto += w; i++; } } if (autowidth) propswidth = wauto + props.margins[3] + props.margins[1] - 1; if (autox) { propsx = Math.floor((env.width - propswidth) / 2); for (i in items) { if (items[i].attrs.x) items[i].attr('x', items[i].attrs.x + propsx); else items[i].attr('path', common.movePath(env, items[i].attrs.path, [propsx, 0])); } } var borderPath = [ [ 'RECT', propsx, props.y, propsx + propswidth, props.y + props.height, props.r ] ]; var border = common.showPath(env, borderPath).attr(props.borderProps); // The legend rectangle is written as the last one because it depends on the sizes of the contents but it should // be drawn behind the others, so at the end we bring to front all items but the border for(i in items) items[i].toFront(); items.unshift(border); env.legenditems = items; } } $.elycharts.featuresmanager.register($.elycharts.legendmanager, 90); })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_manager_mouse.js000066400000000000000000000242211240311710500225520ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var featuresmanager = $.elycharts.featuresmanager; var common = $.elycharts.common; /*********************************************************************** * MOUSEMANAGER **********************************************************************/ $.elycharts.mousemanager = { clear : function(env) { if (env.mouseLayer) { env.mouseLayer.remove(); env.mouseLayer = null; env.mousePaper.clear(); env.mousePaper.remove(); env.mousePaper = null; env.mouseTimer = null; env.mouseAreas = null; // NOTE: do we also need to unbind mouseover/mouseout from areas or is this handled automatically by Raphael? } }, afterShow : function(env, pieces) { if (!env.opt.interactive) return; this.clear(env); env.mouseLayer = $('
').css({position : 'absolute', 'z-index' : 20, opacity : 1}).prependTo(env.container); env.mousePaper = common._RaphaelInstance(env.mouseLayer.get(0), env.width, env.height); var paper = env.mousePaper; if (env.opt.features.debug.active && typeof DP_Debug != 'undefined') { env.paper.text(env.width, env.height - 5, 'DEBUG').attr({ 'text-anchor' : 'end', stroke: 'red', opacity: .1 }); paper.text(env.width, env.height - 5, 'DEBUG').attr({ 'text-anchor' : 'end', stroke: 'red', opacity: .1 }).click(function() { DP_Debug.dump(env.opt, '', false, 4); }); } var i, j; // Adding mouseover only in right area, based on pieces env.mouseAreas = []; if (env.opt.features.mousearea.type == 'single') { // SINGLE: Every serie's index is an area for (i = 0; i < pieces.length; i++) { if (pieces[i].mousearea) { // pathstep if (!pieces[i].paths) { // path standard, generating an area for each point if (pieces[i].path.length >= 1 && (pieces[i].path[0][0] == 'LINE' || pieces[i].path[0][0] == 'LINEAREA')) for (j = 0; j < pieces[i].path[0][1].length; j++) { var props = common.areaProps(env, pieces[i].section, pieces[i].serie); if (props.mouseareaShowOnNull || pieces[i].section != 'Series' || env.opt.values[pieces[i].serie][j] != null) env.mouseAreas.push({ path : [ [ 'CIRCLE', pieces[i].path[0][1][j][0], pieces[i].path[0][1][j][1], 10 ] ], piece : pieces[i], pieces : pieces, index : j, props : props }); } else // Code below is only for standard path - it should be useless now (now there are only LINE and LINEAREA) // TODO DELETE for (j = 0; j < pieces[i].path.length; j++) { env.mouseAreas.push({ path : [ [ 'CIRCLE', common.getX(pieces[i].path[j]), common.getY(pieces[i].path[j]), 10 ] ], piece : pieces[i], pieces : pieces, index : j, props : common.areaProps(env, pieces[i].section, pieces[i].serie) }); } // paths } else if (pieces[i].paths) { // Set of paths (bar graph?), generating overlapped areas for (j = 0; j < pieces[i].paths.length; j++) if (pieces[i].paths[j].path) env.mouseAreas.push({ path : pieces[i].paths[j].path, piece : pieces[i], pieces : pieces, index : j, props : common.areaProps(env, pieces[i].section, pieces[i].serie) }); } } } } else { // INDEX: Each index (in every serie) is an area var indexCenter = env.opt.features.mousearea.indexCenter; if (indexCenter == 'auto') indexCenter = env.indexCenter; var start, delta; if (indexCenter == 'bar') { delta = (env.width - env.opt.margins[3] - env.opt.margins[1]) / (env.opt.labels.length > 0 ? env.opt.labels.length : 1); start = env.opt.margins[3]; } else { delta = (env.width - env.opt.margins[3] - env.opt.margins[1]) / (env.opt.labels.length > 1 ? env.opt.labels.length - 1 : 1); start = env.opt.margins[3] - delta / 2; } for (var idx in env.opt.labels) { // idx can be a string and concatenation results in bad sums. var index = parseInt(idx); env.mouseAreas.push({ path : [ [ 'RECT', start + index * delta, env.height - env.opt.margins[2], start + (index + 1) * delta, env.opt.margins[0] ] ], piece : false, pieces : pieces, index : parseInt(index), props : env.opt.defaultSeries // TODO common.areaProps(env, 'Plot') }); } } var syncenv = false; if (!env.opt.features.mousearea.syncTag) { env.mouseareaenv = { chartEnv : false, mouseObj : false, caller : false, inArea : -1, timer : false }; syncenv = env.mouseareaenv; } else { if (!$.elycharts.mouseareaenv) $.elycharts.mouseareaenv = {}; if (!$.elycharts.mouseareaenv[env.opt.features.mousearea.syncTag]) $.elycharts.mouseareaenv[env.opt.features.mousearea.syncTag] = { chartEnv : false, mouseObj : false, caller : false, inArea : -1, timer : false }; syncenv = $.elycharts.mouseareaenv[env.opt.features.mousearea.syncTag]; } for (i = 0; i < env.mouseAreas.length; i++) { env.mouseAreas[i].area = common.showPath(env, env.mouseAreas[i].path, paper).attr({stroke: "#000", fill: "#fff", opacity: 0}); (function(env, obj, objidx, caller, syncenv) { var piece = obj.piece; var index = obj.index; obj.mouseover = function(e) { //BIND: if (obj.listenerDisabled) return; obj.event = e; clearTimeout(syncenv.timer); caller.onMouseOverArea(env, piece, index, obj); if (syncenv.chartEnv && syncenv.chartEnv.id != env.id) { // Chart changed, removing old one syncenv.caller.onMouseExitArea(syncenv.chartEnv, syncenv.mouseObj.piece, syncenv.mouseObj.index, syncenv.mouseObj); caller.onMouseEnterArea(env, piece, index, obj); } else if (syncenv.inArea != objidx) { if (syncenv.inArea < 0) caller.onMouseEnterArea(env, piece, index, obj); else caller.onMouseChangedArea(env, piece, index, obj); } syncenv.chartEnv = env; syncenv.mouseObj = obj; syncenv.caller = caller; syncenv.inArea = objidx; }; obj.mouseout = function(e) { //BIND: if (obj.listenerDisabled) return; obj.event = e; clearTimeout(syncenv.timer); caller.onMouseOutArea(env, piece, index, obj); syncenv.timer = setTimeout(function() { syncenv.timer = false; caller.onMouseExitArea(env, piece, index, obj); syncenv.chartEnv = false; syncenv.inArea = -1; }, env.opt.features.mousearea.areaMoveDelay); }; $(obj.area.node).mouseover(obj.mouseover); $(obj.area.node).mouseout(obj.mouseout); })(env, env.mouseAreas[i], i, this, syncenv); } }, // Called when mouse enter an area onMouseOverArea : function(env, piece, index, mouseAreaData) { //console.warn('over', piece.serie, index); if (env.opt.features.mousearea.onMouseOver) env.opt.features.mousearea.onMouseOver(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); featuresmanager.onMouseOver(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); }, // Called when mouse exit from an area onMouseOutArea : function(env, piece, index, mouseAreaData) { //console.warn('out', piece.serie, index); if (env.opt.features.mousearea.onMouseOut) env.opt.features.mousearea.onMouseOut(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); featuresmanager.onMouseOut(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); }, // Called when mouse enter an area from empty space (= it was in no area before) onMouseEnterArea : function(env, piece, index, mouseAreaData) { //console.warn('enter', piece.serie, index); if (env.opt.features.mousearea.onMouseEnter) env.opt.features.mousearea.onMouseEnter(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); featuresmanager.onMouseEnter(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); }, // Called when mouse enter an area and it was on another area onMouseChangedArea : function(env, piece, index, mouseAreaData) { //console.warn('changed', piece.serie, index); if (env.opt.features.mousearea.onMouseChanged) env.opt.features.mousearea.onMouseChanged(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); featuresmanager.onMouseChanged(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); }, // Called when mouse leaves an area and does not enter in another one (timeout check) onMouseExitArea : function(env, piece, index, mouseAreaData) { //console.warn('exit', piece.serie, index); if (env.opt.features.mousearea.onMouseExit) env.opt.features.mousearea.onMouseExit(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); featuresmanager.onMouseExit(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); } } $.elycharts.featuresmanager.register($.elycharts.mousemanager, 0); })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_manager_shadow.js000066400000000000000000000032461240311710500227130ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { /*********************************************************************** * FEATURE: SHADOW **********************************************************************/ $.elycharts.shadowmanager = { beforeShow : function(env, pieces) { if (!env.opt.features.shadows || !env.opt.features.shadows.active) return; // TODO if (!common.changed(env, ['labels', 'series'])) // TODO FIX var shadowOffset = env.opt.features.shadows.offset; var shadows = []; for (var i = 0; i < pieces.length; i++) { var path = []; for (var j = 0; j < pieces[i].path.length; j++) { var o = pieces[i].path[j]; switch (o[0]) { case 'M': case 'L': path.push([o[0], o[1] + shadowOffset[0], o[2] + shadowOffset[1]]); break; case 'A': case 'C': path.push([o[0], o[1], o[2], o[3], o[4], o[5], o[6] + shadowOffset[0], o[7] + shadowOffset[1]]); break; case 'z': case 'Z': path.push([o[0]]); break; } } shadows.push({path: path, attr: env.opt.features.shadows.props}); } for (var i = shadows.length - 1; i >= 0; i--) pieces.unshift(shadows[i]); } } $.elycharts.featuresmanager.register($.elycharts.shadowmanager, 5); })(jQuery); elycharts.js-2.1.5+ds/src/elycharts_manager_tooltip.js000066400000000000000000000226261240311710500231230ustar00rootroot00000000000000/********************************************************************** * ELYCHARTS * A Javascript library to generate interactive charts with vectorial graphics. * * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. **********************************************************************/ (function($) { var common = $.elycharts.common; /*********************************************************************** * FEATURE: TOOLTIP **********************************************************************/ $.elycharts.tooltipmanager = { clear : function(env) { if (env.tooltipContainer) { env.tooltipFrame.clear(); env.tooltipFrame.remove(); env.tooltipFrame = null; env.tooltipFrameElement = null; env.tooltipContent.remove(); env.tooltipContent = null; env.tooltipContainer.remove(); env.tooltipContainer = null; } }, afterShow : function(env, pieces) { this.clear(env); if (!$.elycharts.tooltipid) $.elycharts.tooltipid = 0; $.elycharts.tooltipid ++; // Preparo il tooltip env.tooltipContainer = $('').appendTo(document.body); env.tooltipFrame = common._RaphaelInstance('elycharts_tooltip_' + $.elycharts.tooltipid + '_frame', 500, 500); env.tooltipContent = $('#elycharts_tooltip_' + $.elycharts.tooltipid + '_content'); }, _prepareShow : function(env, props, mouseAreaData, tip) { // Il dimensionamento del tooltip e la view del frame SVG, lo fa solo se width ed height sono specificati if (props.width && props.width != 'auto' && props.height && props.height != 'auto') { var delta = props.frameProps && props.frameProps['stroke-width'] ? props.frameProps['stroke-width'] : 0; env.tooltipContainer.width(props.width + delta + 1).height(props.height + delta + 1); if (!env.tooltipFrameElement && props.frameProps) { var framePath = [ [ 'RECT', delta / 2, delta / 2, props.width, props.height, props.roundedCorners ] ]; env.tooltipFrameElement = common.showPath(env, framePath, env.tooltipFrame).attr(props.frameProps); // env.tooltipFrameElement = env.tooltipFrame.rect(delta / 2, delta / 2, props.width, props.height, props.roundedCorners); } } if (env.tooltipFrameElement) { env.tooltipFrameElement.attr(props.frameProps); } if (props.padding) env.tooltipContent.css({ padding : props.padding[0] + 'px ' + props.padding[1] + 'px' }); env.tooltipContent.css(props.contentStyle); env.tooltipContent.html(tip); //BIND: env.tooltipContainer.unbind().mouseover(mouseAreaData.mouseover).mouseout(mouseAreaData.mouseout); // WARN: Prendendo env.paper.canvas non va bene... //var offset = $(env.paper.canvas).offset(); var offset = $(env.container).offset(); if (env.opt.features.tooltip.fixedPos) { offset.top += env.opt.features.tooltip.fixedPos[1]; offset.left += env.opt.features.tooltip.fixedPos[0]; } else { var coord = this.getXY(env, props, mouseAreaData); if (!coord[2]) { offset.left += coord[0]; while (offset.top + coord[1] < 0) coord[1] += 20; offset.top += coord[1]; } else { offset.left = coord[0]; offset.top = coord[1]; } } return { top : offset.top, left : offset.left }; }, /** * Ritorna [x, y] oppure [x, y, true] se le coordinate sono relative alla pagina (e non al grafico) */ getXY : function(env, props, mouseAreaData) { // NOTA Posizione mouse: mouseAreaData.event.pageX/pageY var x = 0, y = 0; if (mouseAreaData.path[0][0] == 'RECT') { // L'area e' su un rettangolo (un bar o un indice completo), il tooltip lo faccio subito sopra // Nota: per capire se e' sull'indice completo basta guardare mouseAreaData.piece == null x = common.getX(mouseAreaData.path[0]) - props.offset[1]; y = common.getY(mouseAreaData.path[0]) - props.height - props.offset[0]; } else if (mouseAreaData.path[0][0] == 'CIRCLE') { // L'area e' su un cerchio (punto di un line) x = common.getX(mouseAreaData.path[0]) - props.offset[1]; y = common.getY(mouseAreaData.path[0]) - props.height - props.offset[0]; } else if (mouseAreaData.path[0][0] == 'SLICE') { // L'area è su una fetta di torta (pie) var path = mouseAreaData.path[0]; // Genera la posizione del tip considerando che deve stare all'interno di un cerchio che è sempre dalla parte opposta dell'area // e deve essere il piu' vicino possibile all'area var w = props.width && props.width != 'auto' ? props.width : 100; var h = props.height && props.height != 'auto' ? props.height : 100; // Raggio del cerchio che contiene il tip var cr = Math.sqrt(Math.pow(w,2) + Math.pow(h,2)) / 2; if (cr > env.opt.r) cr = env.opt.r; var tipangle = path[5] + (path[6] - path[5]) / 2 + 180; var rad = Math.PI / 180; x = path[1] + cr * Math.cos(- tipangle * rad) - w / 2; y = path[2] + cr * Math.sin(- tipangle * rad) - h / 2; } else if (mouseAreaData.piece && mouseAreaData.piece.paths && mouseAreaData.index >= 0 && mouseAreaData.piece.paths[mouseAreaData.index] && mouseAreaData.piece.paths[mouseAreaData.index].rect) { // L'area ha una forma complessa, ma abbiamo il rettangolo di contenimento (funnel) var rect = mouseAreaData.piece.paths[mouseAreaData.index].rect; x = rect[0] - props.offset[1]; y = rect[1] - props.height - props.offset[0]; } if (env.opt.features.tooltip.positionHandler) return env.opt.features.tooltip.positionHandler(env, props, mouseAreaData, x, y); else return [x, y]; }, getTip : function(env, serie, index) { var tip = false; if (env.opt.tooltips) { if (typeof env.opt.tooltips == 'function') tip = env.opt.tooltips(env, serie, index, serie && env.opt.values[serie] && env.opt.values[serie][index] ? env.opt.values[serie][index] : false, env.opt.labels && env.opt.labels[index] ? env.opt.labels[index] : false); else { if (serie && env.opt.tooltips[serie] && env.opt.tooltips[serie][index]) tip = env.opt.tooltips[serie][index]; else if (!serie && env.opt.tooltips[index]) tip = env.opt.tooltips[index]; } } return tip; }, _getProps : function(env, serie, index, mouseAreaData) { var props = mouseAreaData.props.tooltip; if (env.emptySeries && env.opt.series.empty) props = $.extend(true, props, env.opt.series.empty.tooltip); if (!props || !props.active) return false; if (props.frameProps) { var color = common.getItemColor(env, serie, index); if (color) { props = common._clone(props); common.colorize(env, props, [['frameProps', 'stroke']], color); } } return props; }, _fadeOut : function(env) { env.tooltipContainer.fadeOut(env.opt.features.tooltip.fadeDelay); }, onMouseEnter : function(env, serie, index, mouseAreaData) { var props = this._getProps(env, serie, index, mouseAreaData); if (!props) return false; var tip = this.getTip(env, serie, index); if (!tip) { this._fadeOut(env); return true; } //if (!env.opt.tooltips || (serie && (!env.opt.tooltips[serie] || !env.opt.tooltips[serie][index])) || (!serie && !env.opt.tooltips[index])) // return this.onMouseExit(env, serie, index, mouseAreaData); //var tip = serie ? env.opt.tooltips[serie][index] : env.opt.tooltips[index]; env.tooltipContainer.css(this._prepareShow(env, props, mouseAreaData, tip)).fadeIn(env.opt.features.tooltip.fadeDelay); return true; }, onMouseChanged : function(env, serie, index, mouseAreaData) { var props = this._getProps(env, serie, index, mouseAreaData); if (!props) return false; var tip = this.getTip(env, serie, index); if (!tip) { this._fadeOut(env); return true; } /*if (!env.opt.tooltips || (serie && (!env.opt.tooltips[serie] || !env.opt.tooltips[serie][index])) || (!serie && !env.opt.tooltips[index])) return this.onMouseExit(env, serie, index, mouseAreaData); var tip = serie ? env.opt.tooltips[serie][index] : env.opt.tooltips[index];*/ env.tooltipContainer.clearQueue(); // NOTE: this is needed because sometimes we "fadeOut" during mouseChanged so we also have to fadeIn in that cases. // For simplicity we always fadeIn every time. env.tooltipContainer.fadeIn(env.opt.features.tooltip.fadeDelay); // Nota: Non passo da animationStackPush, i tooltip non sono legati a piece env.tooltipContainer.animate(this._prepareShow(env, props, mouseAreaData, tip), env.opt.features.tooltip.moveDelay, 'linear' /*swing*/); return true; }, onMouseExit : function(env, serie, index, mouseAreaData) { var props = this._getProps(env, serie, index, mouseAreaData); if (!props) return false; this._fadeOut(env); return true; } } $.elycharts.featuresmanager.register($.elycharts.tooltipmanager, 20); })(jQuery);