├── LICENSE.md ├── README.md ├── bin └── build.sh ├── dist ├── elycharts-full.js ├── elycharts-full.min.js ├── elycharts.js └── elycharts.min.js ├── docs ├── changelog_en.txt ├── changelog_it.txt ├── demo.html ├── demo_2.html └── demo_3.html ├── index.html ├── lib ├── DP_Debug.js ├── jquery.js └── raphael.js ├── pom.xml ├── resources └── header.js └── src ├── elycharts_chart_barline.js ├── elycharts_chart_funnel.js ├── elycharts_chart_line.js ├── elycharts_chart_pie.js ├── elycharts_core.js ├── elycharts_defaults.js ├── elycharts_manager_anchor.js ├── elycharts_manager_animation.js ├── elycharts_manager_balloon.js ├── elycharts_manager_highlight.js ├── elycharts_manager_label.js ├── elycharts_manager_legend.js ├── elycharts_manager_mouse.js ├── elycharts_manager_shadow.js └── elycharts_manager_tooltip.js /LICENSE.md: -------------------------------------------------------------------------------- 1 | # elycharts 2 | 3 | Released under the [MIT License](http://en.wikipedia.org/wiki/MIT_License) 4 | 5 | Copyright (c) 2010-2014 Void Labs s.n.c. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elycharts 2 | Interactive Javascript (SVG|VML) Charting Library 3 | 4 | A Javascript library to generate interactive charts with vectorial graphics (SVG/VML) and a lot of useful features. 5 | 6 | Requires: Raphaël 1.5+, jQuery1.4 Since v2.1.4 is also compatible with: Raphael 2.0/2.1 and jQuery up to 1.10 7 | 8 | Last Stable Version v2.1.5 (2014/02/19) 9 | -------------------------------------------------------------------------------- /bin/build.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | 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 4 | yui-compressor dist/elycharts.js -o dist/elycharts.min.js 5 | cat src/elycharts_defaults.js src/elycharts_core.js src/elycharts_manager_* src/elycharts_chart_* > dist/elycharts-full.js 6 | yui-compressor dist/elycharts-full.js -o dist/elycharts-full.min.js 7 | 8 | -------------------------------------------------------------------------------- /docs/changelog_en.txt: -------------------------------------------------------------------------------- 1 | v2.1.6-SNAPSHOT 2 | - fixed round rects translation (move svg path with relative commands): it happened in rounded legend dots 3 | 4 | v2.1.5 (2014-02-19) 5 | - fixed support for animations for charts with null values 6 | - added support for null values (avgOverNulls: false) in line charts 7 | - changed stepAnimation by adding the "active" parameter to be enabled, otherwise ignored (Issue #35) 8 | - fixed stepAnimation to not be enabled if stepAnimation is declared to false (Issue #35) 9 | - rewritten the update logic to work even without animation manager and to deal with partial updates (Issue #36) 10 | - fixed labels visibility un update (they were hidden but never made visibile on update if needed) 11 | - fixed legend rewriting/updating (Issue #38) 12 | - fixed support for roundedCorners in legend "dots" (adding support for "rounded rects" svg paths and fixing move method for 'Q'). 13 | - fixed tooltip visiblity when some tooltip value is missing (compared to number of the serie values) (Issue #37) 14 | - updating values now recompute width/height and correctly adapt the paper and chart to the new size. 15 | - 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. 16 | - added support for rPerc (radius expressed in percentage of available space) for pie charts. 17 | - changed pixelWorkAround to be enabled only on SVG (to be verified) 18 | - fixed tooltip (and mouse) management in IE compatible mode (Issue #10) 19 | - fixed erros when using "legend: false" or legend without margin definitions (Issue #14) 20 | - added support for valuesPalette and seriesPalette feature to automatically assign colors starting from a cycling palette (Issue #9) 21 | - added support for single value coloring in bar charts (each column can be printed with a different color) 22 | - added caching for internal properties "inheritance" computation (enabled by default, disable by setting enableInternalCaching to false) 23 | - added "margins" support to pie chart and fixed cx/cy usage (cy was ignored in favor of cx only) (Issue #31) 24 | - rewrote "color" property inheritance to better deal with mixed line/bar charts, chart updates, have better performances and pie support (Issue #33) 25 | - rewrite "legend" properties management to simplify code (should not change the behaviour) 26 | - added "clear" method to featuresmanager so that features can be properly react to chart "clearing" (Issue #32 hint by pduval@github) 27 | - bar chart: inherit stroke color from serie color if not specified (Issue #30) 28 | - fixed width computation for mouse areas using mousearea: "index" option 29 | - added "barOverlapPerc" property to allow overlapping of columns for the same X in different series 30 | - update old copyright year references to 2010-2014 31 | 32 | v2.1.4 (2014-01-14) 33 | - fix compatibility with RaphaelJs 2.0+: .rotation attribute is no more available so axis titles were not rotated (Issue #27) 34 | - 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) 35 | - fix compatibility with Raphaeljs 2.0+: animations didn't work due to animateWith signature/behaviour changes (Issue #23) 36 | - fix for colors propagation from defaultSeries to single serie (Issue #11) 37 | - added "auto" modes for labelsAnchor and labelsPos so that most use cases it will simply set sensible values for them based on labelsCenter/labelsRotate 38 | - added support for labels hiding (for overlapping and *horizontal* overflow) also for rotated labels 39 | - various fixes for x labels. (mostly rewrote axis labels handling) 40 | - fix for "grid.nx" values when nx < ny and they are not "auto" (Issue #13) 41 | - highlight: improvement in stepAnimation management and overlayprops. 42 | - added support for function as tooltip value 43 | 44 | v2.1.3 45 | - changed default value to "false" for axis.labels 46 | - changed default value to "false" for series.dot 47 | - changed default value to "false" for features.grid.draw 48 | - changed default value to "4" for features.grid.ny 49 | - changed default value to "false" for features.grid.forceBorder 50 | - changed default value to "4" for features.legend.dotR 51 | - added support for command execution via $.chart("COMMAND"). Supported commands: config, clear 52 | - added support for null values in line charts (displayed with an average of near points) 53 | - added series.dotShowOnNull and series.mouseareaShowOnNull (default to false) to support null values and what to do with them 54 | - added features.grid.evenVProps, evenHProps, oddVProps, oddHProps to display grid bands 55 | - added offset tooltip option 56 | - added support for "style" option 57 | - highlight: added support for scale with dots, optimized scale properties to have only one numeric value (not array) 58 | - better configuration handling for templates and subsequent calls 59 | - fix in config colors normalization 60 | - better handling for values and labels (support for null/empty labels) 61 | - fix for labels overflow on X axis 62 | - pie chart: fixed support for serie.r < 0 63 | - fix for tooltip color/frameProps settings where specified with shortcut "serie.color" 64 | - line chart: fixed line chart not rounded 65 | - legend: fixed a lot (was broken in almost all configurations...) 66 | - line chart: changed the way labels are drawn and spaced on x axis (fix for labelsHideCovered) 67 | 68 | v2.1.2 69 | - support for series.empty.tooltip e series.empty.label 70 | - refactoring object visualization phase 71 | - support for "zindex" property, to set the order of the object. It should be specified in every "*Props" settings, like a SVG Attr 72 | - default zIndex for dots in line chart is "5" (so dots are shown above other objects) 73 | - fix #6: Using a simple line chart with a single serie and a single value in that serie breaks elychart. 74 | - cleanup 75 | - fix transitions 76 | 77 | v2.1.1 78 | - REFACTORING: true modular management, code split in several components 79 | - script to build library in single form and in minimized single form 80 | - fix rendering errors 81 | - fix highlight in hidden paths 82 | - fix color management in line chart 83 | - support for "valueThresold" option in pie chart 84 | - fix drawing 360 degrees pie slice 85 | - improvements in anchorManager for bind support (fix when transitions occours) 86 | - fix manager tooltip 87 | - line chart: support for rounded : Array, to set a different method of rounding ( [#, 2] is for method suggested by Bago ) 88 | - fix in number precision and sharpening for axis min/max and label calculations [#4] 89 | - fix in reg animation with only 1 value 90 | - fix in label rendering when hiding all series and re-showing one [#1] 91 | - fix in funnel highlight animation [#2] 92 | - code cleanup 93 | - fix in line generation for series with only one value 94 | - moved option labelsCenter to features.grid.labelsCenter 95 | - support for features.mousearea.indexCenter (bar|line|auto). Improvement in serie.lineCenter and features.highlight.indexHighlight, features.grid.labelsCenter (both suppors "auto" value) 96 | - fixes in reg animation 97 | - fix empty pie animations 98 | - added option serie.tooltip.active -------------------------------------------------------------------------------- /docs/changelog_it.txt: -------------------------------------------------------------------------------- 1 | This is no more updated, please look at the changelog_en.txt english changelog for newer changes. 2 | 3 | v2.1.2 4 | - supporto per series.empty.tooltip e series.empty.label 5 | - migliorie nella fase di visualizzazione degli oggetti 6 | - supporto per nuova proprietà "zindex" (da specificare in qualunque props SVG) 7 | - lo zindex di default per i "dots" nel grafico a linee è 5 (per mostrare i punti sopra gli altri oggetti) 8 | - fix line chart e problemi con serie di un solo valore [#6] 9 | - cleanup codice 10 | - fix transizioni 11 | 12 | v2.1.1 13 | - REFACTORING per gestione modulare effettiva, split del codice nei vari componenti 14 | - script per riunificazione e minimizzazione codice da distribuire 15 | - fix errore rendering 16 | - fix highlight con path nascosti 17 | - fix gestione colori in line chart 18 | - supporto opzione "valueThreshold" per pie chart 19 | - fix pie slice di 360 gradi 20 | - migliorie in anchorManager per gestione bind su elementi (fix in caso di transizioni) 21 | - fix manager tooltip 22 | - line chart: supporto per opzione "rounded" come array. [#, 2] e' per usare il metodo suggerito da Bago ( [#, 1] o solo # è il metodo standard) 23 | - fix in gestione precisione dei numeri in calcolo min/max degli assi e in view label [#4] 24 | - fix in animazione "reg" con serie di un solo valore 25 | - fix in visualizzazione label asse dopo aver nascosto tutte le serie e rivisualizzato una [#1] 26 | - fix in funnel highlight animation [#2] 27 | - cleanup codice 28 | - fix: generazione linea con un solo valore 29 | - spostata opzione labelsCenter in features.grid.labelsCenter 30 | - supporto per features.mousearea.indexCenter (bar|line|auto). Migliorie in serie.lineCenter e features.highlight.indexHighlight, features.grid.labelsCenter (entrambi supportano il valore "auto") 31 | - fix vari in animazione reg 32 | - fix animazioni con pie vuota 33 | - aggiunta opzione serie.tooltip.active 34 | 35 | v2.1.0 36 | - RENAME da elysia_charts a elycharts 37 | - fix hightlight.move 38 | - supporto per serie.anchor.userMouseEnter 39 | - fix transizioni da serie (e parti di serie) non renderizzate 40 | - line chart: cambiato supporto per linee arrotondate 41 | 42 | v2.0.10 43 | - WARN: necessita di aggiornamento a Raphael1.5 44 | - supporto transizioni di configurazione per pie 45 | - supporto pie con serie multiple 46 | - supporto per series.highlight.restoreSpeed e restoreEasing 47 | - supporto per tooltip.width="auto" (= 0) e tooltip.height="auto" (= 0) - Disabilita automaticamente frame SVG 48 | - refactoring gestione slice/archi, con gestione corretta animazione (necessita' feature "customAttribute" di Raphael1.5) 49 | - fix funnel senza dati o con dati tutti 0 50 | - fix transizioni in labelmanager e highlightmanager 51 | 52 | v2.0.9 53 | - bugfix in normalizzazione configurazioni passate 54 | - modificato valore di default di indexHighlightProps 55 | - migliorata gestione positionHandler per tooltip, in modo da gestire la posizione del mouse anche su IE 56 | - fix posizionamento label su IE (che da problemi con impostazioni opacità, e prima impostava opacity: 1 anche se non ce n'era bisogno) 57 | 58 | v2.0.8 59 | - nuova gestione del axis.normalize: ora basato sul numero di cifre significative, i vecchi valori "auto" e "autony" vengono entrambi tradotti con "2" 60 | - aggiunto startAnimation.subType usato da alcune animazioni. Supportato subType = 0,1,2 per animation.grow (subType = 2 sostituisce type = grow2) 61 | - aggiunta opzione features.debug.active per attivare debug 62 | - bugfix nomi serie con maiuscole 63 | - bugfix animazioni con serie non visibili 64 | - bugfix divisione per zero in barline 65 | - bugfix label.frameAnchor top|bottom invertiti 66 | 67 | v2.0.7 68 | - nuova gestione grid. Spostate le impostazioni in opt.features.grid (lasciata compatibilita' con vecchie opzioni), 69 | nuove opzioni grid.draw (per nascondere le linee orizzontali e/o verticali), migliorato supporto forceBorder 70 | nuova opzione grid.ticks.* per supportare le barrette sugli assi 71 | - bugfix pie 72 | - aggiunto startAnimation.type = 'grow' e 'grow2' per barline 73 | - fix animation.grow 74 | 75 | v2.0.6 76 | - supporto per "template", che permette di specificare quale configurazione di default utilizzare (permette quindi di avere dei "template" di configurazioni) 77 | WARN: $.elysia_charts.default_options e' stato rinominato in $.elysia_charts.templates (mantenuta compatibilita' all'indietro) 78 | - aggiunte le opzioni startAngle e clockwise a grafico "pie" per definire l'angolo iniziale e l'orientamento delle fette 79 | - 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 80 | - supporto di serie.labelsAnchor anche per assi l ed r (in modo da poter specificare l'allineamento delle label dell'asse) 81 | - supporto di serie.labelsFormatHandler per specificare una funzione di formattazione delle label (sia per assi l,r che per asse x) 82 | - supporto migliore axis.labelsRotate con valori negativi 83 | - gestione corretta visible=true|false in cambiamenti grafici e per gestione stackedWith 84 | - cambiamento z-index di balloon e label per renderli selezionabili 85 | - impostato { cursor : "default" } in defaultSeries.label.style e tolto da opzioni forzate 86 | - 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...) 87 | - bugfix: frameAnchor non funzionava correttamente 88 | - bugfix: frame tooltip spariva dopo un cambiamento di grafico 89 | - bugfix: Indexhighlight:bar con mousearea.type=index non funziona 90 | 91 | v2.0.5 92 | - impostato defaultSeries.axis = 'l', features.balloons.width = 0 93 | - fix stackedWith e startAnimation.grow|avg|reg 94 | - posizionamento relativo invece di assoluto per balloons 95 | - supporto linea balloons mediante features.balloons.line e features.balloons.lineProps 96 | - gestione balloon e label per ultimo indice di un funnel associato a bottom sector 97 | - supporto debug della configurazione attuale mediante libreria DP_Debug (http://www.depressedpress.com/Content/Development/JavaScript/Extensions/DP_DeBug/Index.cfm) 98 | - ripristinato supporto legenda tramite features.legend 99 | - fix titoli assi 100 | 101 | v2.0.4 102 | - migliorato supporto pixelWorkAround 103 | - fix metodi interni 104 | - spostate config fadeDelay e moveDelay in opt.features.tooltip, areaMoveDelay in opt.features.mousearea 105 | - supporto opt.features.mousearea.syncTag per collegare le parti interattive di grafici diversi (se si va su uno si disattivano le altre) 106 | - migliorie posizionamento tooltip 107 | - supporto opt.features.tooltip.positionHandler per definire la propria funzione di posizionamento tooltip 108 | - supporto per passaggio di oggetti DOM/JQUERY come label 109 | 110 | v2.0.3 111 | - ripristinato supporto opzione "color" 112 | - gestione transizioni grafici mediante chiamate successive a $(X).chart(...) 113 | - supporto series.stepAnimation (transizioni grafici), features.animation.startAnimation (generazione elementi non-serie), features.animation.stepAnimation (transizioni elementi non-serie) 114 | - supporto series.visible 115 | - grafico barline: supporto direction = 'rtl' 116 | - fix vari 117 | 118 | v2.0.2 119 | - supporto piechart vuoto e serie "empty" 120 | - fix passaggio width ed height 121 | - nuova opzione "interactive" 122 | - nuove opzioni highlight: scaleSpeed, scaleEasing, moveSpeed, moveEasing 123 | - applicabile highlight "scale" a funnel e piechart (prima implementazione) 124 | - supporto opzione "delay" in startAnimation 125 | - supporto valori cumulativi in line chart, opzione series[x].cumulative 126 | - supporto opzione axis[X].labelsCompactUnits (= ['k','m']) per accorciare le label numeriche con unita' (prima implementazione) 127 | - rename interno strutture codice 128 | - migliorie gestione sync animazioni 129 | 130 | v2.0.1 131 | - ripristinato supporto label in pie e funnel 132 | - label HTML con offset 133 | - supporto passaggio anchor come elementi dom 134 | - ottimizzazione gestione mouseover 135 | - applicabile l'highlight "move" anche al funnel e migliorie ottimizzazioni generali all'highlight 136 | - nuovo grafico barline, e supporto highlight "scale" e "move" 137 | - fix vari 138 | 139 | v2.0.0 140 | - refactoring completo 141 | -------------------------------------------------------------------------------- /docs/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 207 | 208 | 229 | 230 | 231 | 232 |
233 |
234 |
235 |
236 | 239 |
240 |
241 |
242 |
243 | 244 | 245 | -------------------------------------------------------------------------------- /docs/demo_2.html: -------------------------------------------------------------------------------- 1 | 2 | Elycharts demo 2 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 386 | 387 | 438 | 439 | 440 | 441 |
442 |
44.250
sent
443 |
444 |
445 |
41.250
delivered
446 |
447 |
2.500
bounced
448 |
449 |
450 |
15.424
opened
451 |
452 |
32.500
not opened
453 |
454 |
455 |
4.560
clicked
456 |
457 |
12.500
not clicked
458 |
459 | 460 |
461 |
AAA
462 |
463 |
464 |
465 |
BBB
466 |
467 |
468 |
469 |

470 |
471 |
472 |
473 |

474 | 475 | 476 | -------------------------------------------------------------------------------- /docs/demo_3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 34 | 35 | 36 | 37 | 213 | 214 | 215 | 216 | 217 | 218 |
219 | 240 | update data 241 | refresh chart 242 | update data 2 243 | update data 3 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | ElyCharts 3 | 4 |

ElyCharts

5 | 6 |

elycharts.js

7 |

elycharts.min.js

8 |

Demo

9 |

Demo 2

10 |

Changelog

11 |

Google Code Project

12 | 13 | 14 | -------------------------------------------------------------------------------- /lib/DP_Debug.js: -------------------------------------------------------------------------------- 1 | /* DepressedPress.com DP_Debug 2 | 3 | Author: Jim Davis, the Depressed Press of Boston 4 | Date: September 20, 2006 5 | Contact: webmaster@depressedpress.com 6 | Website: www.depressedpress.com 7 | 8 | Full documentation can be found at: 9 | http://www.depressedpress.com/Content/Development/JavaScript/Extensions/DP_Debug/Index.cfm 10 | 11 | DP_Debug provides extensions to JavaScript to assist with debugging. 12 | 13 | + The DP_Debug.dump() method allows you to dump an HTML representation of any JavaScript object to the DP_Debug console. It supports nested objects, recursive references and chained method calls. 14 | + The methods DP_Debug.dumpCookies() and DP_Debug.dumpQueryString dump important browser information to the console. 15 | + The DP_Debug.log() method creates a log entry in the DP_Debug console. 16 | + The methods DP_Debug.logInfo(), DP_Debug.logWarning() and DP_Debug.logError() provide shortcuts for logging specific types of messages. 17 | + The DP_Debug.timer() method provides a simple way to time specific blocks of code. 18 | + The DP_Debug.dpGetType() method provides a more specific answer as to an object type than the native typeof operator. 19 | 20 | Copyright (c) 1996-2006, The Depressed Press of Boston (depressedpress.com) 21 | 22 | All rights reserved. 23 | 24 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 25 | 26 | +) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 27 | 28 | +) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 29 | 30 | +) Neither the name of the DEPRESSED PRESS OF BOSTON (DEPRESSEDPRESS.COM) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 31 | 32 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | 34 | */ 35 | 36 | // Add a container for utility methods 37 | DP_Debug = new Object(); 38 | 39 | // dumpWindow Management 40 | // 41 | // Opens the window, if it's not already open 42 | DP_Debug.openDebugWindow = function() { 43 | 44 | // Create the Dump Window 45 | DPDebugWin = window.open("","dumpWindow","scrollbars=yes,resizable=yes,width=500,height=400"); 46 | var DPW = DPDebugWin.document; 47 | 48 | if ( DPW.getElementById("dpBox") == null ) { 49 | 50 | // Write the HTML 51 | DPW.write( "", 52 | "", 53 | "", 54 | " dpDebug by the DepressedPress", 55 | " ", 56 | " ", 185 | " ", 334 | "", 335 | "
", 336 | "
", 337 | "
DP_Debug by the DepressedPress
", 338 | "
Show Quick Ref
", 339 | "
", 340 | "
", 341 | " ", 387 | "
", 388 | "
dump Output
", 389 | " ", 390 | "
", 391 | "
", 392 | "
", 393 | "
", 394 | "
log Output
", 395 | "
", 396 | " Show: ", 397 | " Clear", 398 | "
", 399 | "
", 400 | "
", 401 | "
", 402 | ""); 403 | // Close the stream 404 | DPW.close(); 405 | }; 406 | 407 | }; 408 | 409 | // Default Enabled status to "false" 410 | DP_Debug.Enabled = false; 411 | // Methods to indicate enabled/disabled status 412 | DP_Debug.enable = function() { 413 | DP_Debug.Enabled = true; 414 | }; 415 | DP_Debug.disable = function() { 416 | DP_Debug.Enabled = false; 417 | }; 418 | DP_Debug.isEnabled = function() { 419 | return DP_Debug.Enabled; 420 | }; 421 | 422 | // Add convienence methods for common log operations 423 | DP_Debug.logInfo = function(Content) { 424 | DP_Debug.logger(Content, "Info"); 425 | }; 426 | DP_Debug.logWarning = function(Content) { 427 | DP_Debug.logger(Content, "Warning"); 428 | }; 429 | DP_Debug.logError = function(Content) { 430 | DP_Debug.logger(Content, "Error"); 431 | }; 432 | // "log" method 433 | DP_Debug.logger = function(Content, Type) { 434 | 435 | // Manage input params 436 | if ( typeof Content != "string" || Content == "" ) { 437 | var Content = ""; 438 | }; 439 | if ( typeof Type != "string" || Type == "" ) { 440 | var Type = "Info"; 441 | }; 442 | 443 | // Open the dumpWindow (if it's not already open) 444 | DP_Debug.openDebugWindow() 445 | 446 | // Get the Current Entries 447 | var CurEntries = DPDebugWin.dpLgEntries; 448 | // Generate an Entry Object 449 | var NewEntry = new Object(); 450 | NewEntry.Content = Content; 451 | NewEntry.Type = Type; 452 | NewEntry.Time = new Date(); 453 | 454 | // Add the new Entry to the List 455 | CurEntries[CurEntries.length] = NewEntry; 456 | 457 | // Write the Log Entry 458 | DPDebugWin.writeLog(); 459 | 460 | // Return 461 | return; 462 | 463 | }; 464 | 465 | 466 | // Extend Object with the "log" method 467 | DP_Debug.timer = function(Name) { 468 | 469 | // Manage input params 470 | if ( typeof Name != "string" || Name == "" ) { 471 | var Name = "timer"; 472 | }; 473 | 474 | // Open the dumpWindow (if it's not already open) 475 | DP_Debug.openDebugWindow(); 476 | 477 | // Get the Current Entries 478 | var CurTimers = DPDebugWin.timers; 479 | 480 | // See if this is a new timer 481 | if ( typeof CurTimers[Name] == "undefined" ) { 482 | // Set the Timer Start Point 483 | CurTimers[Name] = new Date(); 484 | // Output the log entry 485 | DP_Debug.logger("Timer \"" + Name + "\" started.", "Timer"); 486 | } else { 487 | // Get the Timer Start Point 488 | var CurStart = CurTimers[Name]; 489 | var CurEnd = new Date(); 490 | // Reset the Timer 491 | CurTimers[Name] = undefined; 492 | // Get the duration 493 | var CurTime = CurEnd.getTime() - CurStart.getTime() 494 | // Output the log entry 495 | DP_Debug.logger("Timer \"" + Name + "\" ended. Duration " + CurTime + " ms", "Timer"); 496 | }; 497 | 498 | // Return 499 | return; 500 | 501 | }; 502 | 503 | 504 | // Cookie Dumper 505 | DP_Debug.dumpCookies = function() { 506 | 507 | var AllCookies = new Object; 508 | var Cookies = document.cookie.split(";"); 509 | 510 | for ( var Cnt=0; Cnt < Cookies.length; Cnt++ ) { 511 | var CurCookie = Cookies[Cnt].split("="); 512 | if ( CurCookie[0] ) { 513 | if ( CurCookie[1] ) { 514 | AllCookies[CurCookie[0].replace(/^\s*|\s*$/g,"")] = CurCookie[1]; 515 | } else { 516 | AllCookies[CurCookie[0].replace(/^\s*|\s*$/g,"")] = ""; 517 | }; 518 | }; 519 | }; 520 | 521 | DP_Debug.dump(AllCookies, "Accessible Browser Cookies"); 522 | 523 | }; 524 | 525 | 526 | // Query String Dumper 527 | DP_Debug.dumpQueryString = function() { 528 | 529 | var QS, AllElements, CurElement, CurName, CurVal 530 | 531 | QS = new Object(); 532 | QueryString = location.search; 533 | 534 | // Split the query string on the ampersand (the substring removes the question mark) 535 | AllElements = QueryString.substring(1).split('&'); 536 | 537 | // Loop over the elements 538 | for( var Cnt = 0; Cnt < AllElements.length; Cnt++) { 539 | // Split the current element on the equals sign 540 | CurElement = AllElements[Cnt].split('='); 541 | CurName = unescape(CurElement[0]).replace(/^\s*|\s*$/g,""); 542 | // Call the get method to obtain the value 543 | if ( CurName.length > 0 ) { 544 | 545 | if ( !QS[CurName] ) { QS[CurName] = new Array() }; 546 | QS[CurName][QS[CurName].length] = CurElement[1]; 547 | 548 | }; 549 | }; 550 | 551 | // Dump the object 552 | DP_Debug.dump(QS, "Accessible Query String Values"); 553 | 554 | }; 555 | 556 | 557 | // Extend Object with the "dump" method 558 | DP_Debug.dump = function(Ob, DevLabel, ShowFunctions, MaxRecurseLevel) { 559 | 560 | // Grab the Start time 561 | var DumpStart = new Date(); 562 | 563 | // Open the dumpWindow (if it's not already open) 564 | DP_Debug.openDebugWindow() 565 | 566 | // Set up the Object Checker 567 | var ParsedObs = new Array(); 568 | // Define the current Dump InstanceID 569 | var InstID = DPDebugWin.dumpInstances++; 570 | var ObID = 0; 571 | var ObRefID = 0; 572 | 573 | // Manage input params 574 | if ( typeof DevLabel != "string" || DevLabel == "" ) { 575 | var DevLabel = ""; 576 | }; 577 | // Only IE can handle local links in popups generated by JavaScript 578 | if ( navigator.appName == "Microsoft Internet Explorer" ) { 579 | LogLabel = "[Dump " + (InstID + 1) + "] " + DevLabel; 580 | } else { 581 | LogLabel = "[Dump " + (InstID + 1) + "] " + DevLabel; 582 | }; 583 | // Set the full Dev Label 584 | DevLabel = "[Dump " + (InstID + 1) + "] " + DevLabel; 585 | // Set whether or not to show functions 586 | if ( typeof ShowFunctions != "boolean" ) { 587 | var ShowFunctions = false; 588 | }; 589 | // Set the maximum recursive level 590 | if ( typeof MaxRecurseLevel != "number" ) { 591 | var MaxRecurseLevel = -1; 592 | }; 593 | 594 | // Set the encoded character dictionary for the conversion to HTML 595 | var EncodedChars = new Array(); 596 | // Add simple chars (& must come first!) 597 | EncodedChars["&"] = "&"; 598 | EncodedChars["<"] = "<"; 599 | EncodedChars[">"] = ">"; 600 | EncodedChars["\""] = """; 601 | 602 | // Output the current dump 603 | DPDebugWin.document.getElementById("dpDpDsp").innerHTML += parseToHTML(Ob, DevLabel); 604 | 605 | // Grab the End Time 606 | var DumpEnd = new Date(); 607 | // Determine the total time 608 | var DumpTime = DumpEnd.getTime() - DumpStart.getTime() 609 | // Output the log entry 610 | DP_Debug.logger(LogLabel + " (Completed in " + DumpTime + " ms)", "Dump"); 611 | 612 | // Return a reference to the object 613 | return Ob; 614 | 615 | // Escape characters using the Encoded Chars tables 616 | function escapeString(CurString) { 617 | var CurRegEx; 618 | for ( var CurChar in EncodedChars ) { 619 | if (typeof EncodedChars[CurChar] != "function") {; 620 | if ( CurChar != "\\" ) { 621 | CurRegEx = new RegExp(CurChar, "g"); 622 | } else { 623 | CurRegEx = /\\/g; 624 | }; 625 | CurString = CurString.replace(CurRegEx, EncodedChars[CurChar]); 626 | }; 627 | }; 628 | return CurString; 629 | }; 630 | 631 | // Has the object been parsed already? 632 | function checkIfParsedOB(Ob) { 633 | // Check the parsed array 634 | for ( var Cnt = 0; Cnt < ParsedObs.length; Cnt++ ) { 635 | if ( ParsedObs[Cnt] === Ob ) { 636 | return true; 637 | }; 638 | }; 639 | // Add the passed object to the parsed array 640 | ParsedObs[ParsedObs.length] = Ob; 641 | return false; 642 | }; 643 | 644 | // Get the Ref ID 645 | function getObRefID(Ob) { 646 | // Check the parsed array 647 | for ( var Cnt = 0; Cnt < ParsedObs.length; Cnt++ ) { 648 | if ( ParsedObs[Cnt] === Ob ) { 649 | return Cnt; 650 | }; 651 | }; 652 | return ""; 653 | }; 654 | 655 | // Parse objects to HTML 656 | function parseToHTML(Ob, ObLabel, RecurseLevel, ForceUnknown) { 657 | 658 | // Manage Arugments 659 | if ( typeof RecurseLevel != "number" ) { 660 | RecurseLevel = 0; 661 | }; 662 | if ( typeof ForceUnknown != "boolean" ) { 663 | ForceUnknown = false; 664 | }; 665 | 666 | // Update the object ID 667 | ObID = ObID + 1; 668 | 669 | // Initialize results 670 | var Results = ""; 671 | 672 | // Set options based on whether this is the first pass or a recursive pass 673 | if ( RecurseLevel == 0 ) { 674 | var StylePrefix = "Inst"; 675 | Results += ""; 676 | } else { 677 | var StylePrefix = "Ob"; 678 | }; 679 | 680 | // Get some information about the passed Object and set some display fragments 681 | if ( ForceUnknown ) { 682 | var ObType = "unknown"; 683 | } else { 684 | var ObType = DP_Debug.getType(Ob); 685 | }; 686 | var ObLabelID = "ObLabel_" + InstID + "_" + ObID; 687 | var ObValueID = "ObValue_" + InstID + "_" + ObID; 688 | var ObIDLink = ""; 689 | var ObDisplayed = false; 690 | switch ( ObType ) { 691 | case "object": case "array": 692 | // Check to see if the object has already been displayed 693 | if ( checkIfParsedOB(Ob) ) { 694 | ObDisplayed = true; 695 | }; 696 | var CurObRefID = getObRefID(Ob); 697 | if ( ObDisplayed ) { 698 | // Only IE can handle local links in popups generated by JavaScript 699 | if ( navigator.appName == "Microsoft Internet Explorer" ) { 700 | ObIDLink = " (id: " + CurObRefID + ")"; 701 | } else { 702 | ObIDLink = " (id: " + CurObRefID + ")"; 703 | }; 704 | } else { 705 | ObIDLink = " (id: " + CurObRefID + ")"; 706 | }; 707 | }; 708 | 709 | // Display Element Header 710 | Results += ""; 711 | Results += ""; 775 | if ( RecurseLevel == 0 ) { 776 | Results += "
" + ObLabel + "" + ObType + ObIDLink + "
"; 712 | 713 | // Display Element Content 714 | switch ( ObType ) { 715 | case "object": case "array": 716 | if ( ObDisplayed ) { 717 | Results += "( Previously Displayed )"; 718 | } else if ( RecurseLevel == MaxRecurseLevel ) { 719 | Results += "( Maximum Recursion Depth Reached )"; 720 | } else { 721 | // Determine if the object is enumerable 722 | var ObEnumerable = true; 723 | try { for ( var Prop in Ob ) { break; } } catch (CurError) { ObEnumerable = false; }; 724 | // Loop over object 725 | if ( ObEnumerable ) { 726 | Results += ""; 727 | for ( var Prop in Ob ) { 728 | PropEnumerable = true; 729 | try { typeof Ob[Prop]; Ob[Prop]; } catch (CurError) { 730 | PropEnumerable = false; 731 | Results += parseToHTML(null, Prop, RecurseLevel + 1, true); 732 | }; 733 | if ( PropEnumerable && ( typeof Ob[Prop] != "function" || ShowFunctions ) ) { 734 | Results += parseToHTML(Ob[Prop], Prop, RecurseLevel + 1); 735 | }; 736 | }; 737 | Results += "
"; 738 | } else { 739 | Results += "( Object is not Enumerable )"; 740 | }; 741 | }; 742 | break; 743 | case "function": 744 | Results += escapeString(Ob.toString()); 745 | break; 746 | case "null": 747 | Results += "( null )"; 748 | break; 749 | case "date": 750 | Results += Ob.toString(); 751 | break; 752 | case "number": 753 | Results += Ob.toString(); 754 | break; 755 | case "string": 756 | if ( Ob.length == 0 ) { 757 | Results += "( Empty String )"; 758 | } else { 759 | Results += escapeString(Ob); 760 | }; 761 | break; 762 | case "boolean": 763 | Results += Ob.toString(); 764 | break; 765 | case "undefined": 766 | Results += "( Undefined Entity )"; 767 | break; 768 | case "unknown": 769 | Results += "( Entity is not Enumerable )"; 770 | break; 771 | }; 772 | 773 | // Display Element Footer 774 | Results += "

" 777 | }; 778 | 779 | // Return Results 780 | return Results; 781 | 782 | }; 783 | 784 | }; 785 | 786 | 787 | // Extend Object with the "dpGetType" method 788 | DP_Debug.getType = function(Ob) { 789 | 790 | try { 791 | switch (typeof Ob) { 792 | case "object": 793 | if ( Ob == null ) { 794 | return "null"; 795 | } else if ( Ob.constructor == Date ) { 796 | return "date"; 797 | } else if ( Ob.constructor == Array ) { 798 | return "array"; 799 | } else if ( Ob.constructor == String ) { 800 | return "string"; 801 | } else if ( Ob.constructor == Number ) { 802 | return "number"; 803 | } else if ( Ob.constructor == Boolean ) { 804 | return "boolean"; 805 | } else if ( Ob == undefined ) { 806 | return "undefined"; 807 | } else { 808 | return "object"; 809 | }; 810 | case "function": 811 | return "function"; 812 | case "number": 813 | return "number"; 814 | case "string": 815 | return "string"; 816 | case "boolean": 817 | return "boolean"; 818 | case "undefined": 819 | return "undefined"; 820 | default: 821 | return "unknown"; 822 | }; 823 | } catch (CurError) { 824 | return "unknown"; 825 | }; 826 | 827 | }; 828 | -------------------------------------------------------------------------------- /lib/raphael.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidlabs/elycharts/2e17b827d714076e72f066d3cee76534e4a0f7bf/lib/raphael.js -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | elycharts 5 | elycharts 6 | 2.1.6-SNAPSHOT 7 | Elycharts 8 | pom 9 | 10 | 11 | 12 | croche-releases 13 | http://croche.googlecode.com/svn/repository/releases 14 | 15 | 16 | oss.sonatype.org 17 | http://oss.sonatype.org/content/groups/public 18 | 19 | 20 | 21 | 22 | target/ 23 | target/test-scripts 24 | 25 | 26 | maven-resources-plugin 27 | 2.6 28 | 29 | 30 | copy-resources 31 | process-resources 32 | 33 | copy-resources 34 | 35 | 36 | ${basedir}/target/extra-resources 37 | 38 | 39 | resources 40 | true 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | croche.maven 49 | maven-merge-file-plugin 50 | 0.4 51 | 52 | 53 | merge-release-notes 54 | process-resources 55 | 56 | merge 57 | 58 | 59 | 60 | 61 | dist/elycharts-full.js 62 | 63 | src 64 | ${basedir}/target/extra-resources 65 | 66 | 67 | header 68 | elycharts_default 69 | elycharts_core 70 | elycharts_manager 71 | elycharts_chart 72 | 73 | 74 | *.js 75 | 76 | /********* Source File: #{parent.name}/#{file.name} 77 | *********/\n 78 | UTF-8 79 | 80 | 81 | dist/elycharts.js 82 | 83 | src 84 | ${basedir}/target/extra-resources 85 | 86 | 87 | header 88 | elycharts_default 89 | elycharts_core 90 | elycharts_manager 91 | elycharts_chart 92 | 93 | 94 | header.js 95 | elycharts_defaults.js 96 | elycharts_core.js 97 | elycharts_manager_anchor.js 98 | elycharts_manager_animation.js 99 | elycharts_manager_highlight.js 100 | elycharts_manager_label.js 101 | elycharts_manager_legend.js 102 | elycharts_manager_mouse.js 103 | elycharts_manager_tooltip.js 104 | elycharts_chart_line.js 105 | elycharts_chart_pie.js 106 | 107 | /********* Source File: #{parent.name}/#{file.name} 108 | *********/\n 109 | UTF-8 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | net.alchim31.maven 118 | yuicompressor-maven-plugin 119 | 1.2 120 | 121 | 122 | compress-js 123 | package 124 | 125 | compress 126 | 127 | 128 | dist 129 | dist 130 | .min 131 | -1 132 | true 133 | 134 | *-min.js 135 | *.min.js 136 | *header.js 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /resources/header.js: -------------------------------------------------------------------------------- 1 | /*!********************************************************************* 2 | * ELYCHARTS v${pom.version} $Id$ 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | -------------------------------------------------------------------------------- /src/elycharts_chart_barline.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var common = $.elycharts.common; 12 | 13 | /*********************************************************************** 14 | * CHART: BARLINE 15 | * 16 | * Singola barra orizzontale contenente vari valori. 17 | * 18 | * L'idea è che possa essere vista come una linechart orizzontale 19 | * invece di verticale, con solo serie di tipo bar e con un solo valore. 20 | * In futuro (quando sarà possibile far linechart orizzontali) potrebbe 21 | * proprio essere renderizzata in questo modo. 22 | **********************************************************************/ 23 | 24 | $.elycharts.barline = { 25 | 26 | init : function($env) { 27 | }, 28 | 29 | draw : function(env) { 30 | var paper = env.paper; 31 | var opt = env.opt; 32 | 33 | env.xmin = opt.margins[3]; 34 | env.xmax = env.width - opt.margins[1]; 35 | env.ymin = opt.margins[0]; 36 | env.ymax = env.height - opt.margins[2]; 37 | 38 | var maxvalue = 0; 39 | for (var serie in opt.values) { 40 | var values = opt.values[serie]; 41 | var value = values[0]; 42 | var plot = { 43 | props : common.areaProps(env, 'Series', serie) 44 | }; 45 | env.plots[serie] = plot; 46 | 47 | if (!plot.props.stacked || !env.plots[plot.props.stacked]) { 48 | plot.from = 0; 49 | } else { 50 | plot.from = env.plots[plot.props.stacked].to; 51 | } 52 | plot.to = plot.from + value; 53 | if (plot.to > maxvalue) 54 | maxvalue = plot.to; 55 | } 56 | // TODO opt.max dovrebbe essere opt.axis[?].max ? 57 | if (typeof opt.max != 'undefined') 58 | maxvalue = opt.max; 59 | if (!maxvalue) 60 | maxvalue = 1; 61 | 62 | var pieces = []; 63 | for (serie in opt.values) { 64 | plot = env.plots[serie]; 65 | var d = (env.xmax - env.xmin) / maxvalue; 66 | if (opt.direction != 'rtl') 67 | pieces.push({ 68 | paths : [ { path : [ [ 'RECT', env.xmin + d * plot.from, env.ymin, env.xmin + d * plot.to, env.ymax] ], attr : plot.props.plotProps } ], 69 | section: 'Series', serie: serie, subSection : 'Plot', mousearea : 'paths' 70 | }); 71 | else 72 | pieces.push({ 73 | paths : [ { path : [ [ 'RECT', env.xmax - d * plot.from, env.ymin, env.xmax - d * plot.to, env.ymax] ], attr : plot.props.plotProps } ], 74 | section: 'Series', serie: serie, subSection : 'Plot', mousearea : 'paths' 75 | }); 76 | } 77 | 78 | return pieces; 79 | } 80 | }; 81 | 82 | })(jQuery); 83 | -------------------------------------------------------------------------------- /src/elycharts_chart_funnel.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var common = $.elycharts.common; 12 | 13 | /*********************************************************************** 14 | * CHART: FUNNEL 15 | **********************************************************************/ 16 | 17 | $.elycharts.funnel = { 18 | 19 | init : function($env) { 20 | }, 21 | 22 | draw : function(env) { 23 | var paper = env.paper; 24 | var opt = env.opt; 25 | 26 | env.xmin = opt.margins[3]; 27 | env.xmax = env.width - opt.margins[1]; 28 | env.ymin = opt.margins[0] + Math.abs(opt.rh); 29 | 30 | for (var serie in opt.values) { 31 | var values = opt.values[serie]; 32 | 33 | var lastwidthratio = opt.method == 'width' ? values[values.length - 1] / (values[0] ? values[0] : 1) : 34 | Math.sqrt(values[values.length - 1] / (values[0] ? values[0] : 1) * Math.pow(1 / 2, 2)) * 2; 35 | 36 | env.ymax = env.height - opt.margins[2] - lastwidthratio * Math.abs(opt.rh); 37 | 38 | var pieces = this.pieces(env, serie, 0, 1, 1, values); 39 | } 40 | 41 | return pieces; 42 | }, 43 | 44 | pieces : function(env, serie, hstart, hend, wratio, values) { 45 | var path, pieces = [], opt = env.opt; 46 | 47 | var v0 = values[0] ? values[0] : 1; 48 | var h = hstart; // Starting height 49 | var hslices = (hend - hstart - opt.topSector - opt.bottomSector) / (values.length > 1 ? values.length - 1 : 1); 50 | var w = wratio; // Starting width 51 | if ((path = this.edge(env, h, w, true))) 52 | pieces.push({path : path, section: 'Edge', attr : env.opt.edgeProps}); 53 | if (opt.topSector > 0 && (path = this.section(env, h, h = h + opt.topSector, w, w))) 54 | pieces.push({path : path.path, center: path.center, rect: path.rect, section: 'Sector', serie: 'top', attr : env.opt.topSectorProps}); 55 | var paths = []; 56 | for (var i = 1; i < values.length; i++) { 57 | var v = values[0] ? values[i] : 1; 58 | // METODO "cutarea" 59 | // area taglio attuale / area taglio iniziale = valore attuale / valore iniziare 60 | // => larghezza attuale = sqrt(values[i] / values[0] * pow(larghezza iniziale / 2, 2)) * 2 61 | if ((path = this.section(env, h, h = h + hslices, w, 62 | opt.method == 'width' ? w = v / v0 * wratio : w = Math.sqrt(v / v0 * Math.pow(wratio / 2, 2)) * 2))) 63 | var props = common.areaProps(env, 'Series', serie, i - 1); 64 | paths.push({path : path.path, center: path.center, rect: path.rect, attr : props.plotProps}); 65 | } 66 | pieces.push({section: 'Series', serie: serie, paths : paths, subSection : 'Plot', mousearea : 'paths' }); 67 | 68 | if (opt.bottomSector > 0 && (path = this.section(env, h, h = h + opt.bottomSector, w, w))) 69 | pieces.push({path : path.path, center: path.center, rect: path.rect, section: 'Sector', serie: 'bottom', attr : env.opt.bottomSectorProps}); 70 | if ((path = this.edge(env, h, w, false))) 71 | pieces.push({path : path, section : 'Edge', attr : env.opt.edgeProps}); 72 | 73 | return pieces; 74 | }, 75 | 76 | section : function(env, hfrom, hto, wfrom, wto) { 77 | x1a = env.xmin + (env.xmax - env.xmin) * (wfrom / -2 + 1/2); 78 | x2a = env.xmin + (env.xmax - env.xmin) * (wfrom / 2 + 1/2); 79 | x1b = env.xmin + (env.xmax - env.xmin) * (wto / -2 + 1/2); 80 | x2b = env.xmin + (env.xmax - env.xmin) * (wto / 2 + 1/2); 81 | y1 = env.ymin + (env.ymax - env.ymin) * hfrom; 82 | y2 = env.ymin + (env.ymax - env.ymin) * hto; 83 | var rwa = (x2a - x1a) / 2; 84 | var rha = rwa / (env.xmax - env.xmin) * 2 * Math.abs(env.opt.rh); 85 | var rwb = (x2b - x1b) / 2; 86 | var rhb = rwb / (env.xmax - env.xmin) * 2 * Math.abs(env.opt.rh); 87 | 88 | var pathLn = []; 89 | 90 | pathLn.push(['M', x1a, y1]); 91 | if (env.opt.rh != 0) 92 | pathLn.push(['A', rwa, rha, 0, 0, env.opt.rh > 0 ? 1 : 0, x2a, y1]); 93 | else 94 | pathLn.push(['L', x2a, y1]); 95 | pathLn.push(['L', x2b, y2]); 96 | if (env.opt.rh != 0) 97 | pathLn.push(['A', rwb, rhb, 0, 0, env.opt.rh > 0 ? 0 : 1, x1b, y2]); 98 | else 99 | pathLn.push(['L', x1b, y2]); 100 | pathLn.push(['z']); 101 | 102 | return {path: pathLn, center: [(x2a + x1a) / 2, (y2 + y1) / 2 + (env.opt.rh > 0 ? -1 : +1) * (rha + rhb) / 2], rect: [x1a, y1, x2a, y2]}; 103 | }, 104 | 105 | edge : function(env, h, w, isTop) { 106 | if ((isTop && env.opt.rh >= 0) || (!isTop && env.opt.rh <= 0)) 107 | return false; 108 | 109 | x1 = env.xmin + (env.xmax - env.xmin) * (w / -2 + 1/2); 110 | x2 = env.xmin + (env.xmax - env.xmin) * (w / 2 + 1/2); 111 | y = env.ymin + (env.ymax - env.ymin) * h; 112 | var rw = (x2 - x1) / 2; 113 | var rh = rw / (env.xmax - env.xmin) * 2 * Math.abs(env.opt.rh); 114 | 115 | var pathLn = []; 116 | pathLn.push(['M', x1, y]); 117 | pathLn.push(['A', rw, rh, 0, 0, env.opt.rh < 0 ? 1 : 0, x2, y]); 118 | pathLn.push(['A', rw, rh, 0, 0, env.opt.rh < 0 ? 1 : 0, x1, y]); 119 | pathLn.push(['z']); 120 | return pathLn; 121 | } 122 | }; 123 | 124 | })(jQuery); 125 | -------------------------------------------------------------------------------- /src/elycharts_chart_pie.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var common = $.elycharts.common; 12 | 13 | /*********************************************************************** 14 | * CHART: PIE 15 | **********************************************************************/ 16 | 17 | $.elycharts.pie = { 18 | init : function($env) { 19 | }, 20 | 21 | draw : function(env) { 22 | //var paper = env.paper; 23 | var opt = env.opt; 24 | 25 | var w = env.width - env.opt.margins[1] - env.opt.margins[3]; 26 | var h = env.height - env.opt.margins[0] - env.opt.margins[2]; 27 | var r = env.opt.r ? env.opt.r : Math.floor((w < h ? w : h) / 2 * (env.opt.rPerc ? env.opt.rPerc / 100 : 0.8)); 28 | var cx = (env.opt.cx ? env.opt.cx : Math.floor(w / 2)) + env.opt.margins[3]; 29 | var cy = (env.opt.cy ? env.opt.cy : Math.floor(h / 2)) + env.opt.margins[0]; 30 | 31 | var cnt = 0, i, ii, serie, plot, props; 32 | for (serie in opt.values) { 33 | plot = { 34 | visible : false, 35 | total : 0, 36 | values : [] 37 | }; 38 | env.plots[serie] = plot; 39 | var serieProps = common.areaProps(env, 'Series', serie); 40 | common.colorize(env, serieProps, [['plotProps','stroke'],['plotProps','fill']], common.getItemColor(env, serie)); 41 | if (serieProps.visible) { 42 | plot.visible = true; 43 | cnt ++; 44 | plot.values = opt.values[serie]; 45 | for (i = 0, ii = plot.values.length; i < ii; i++) 46 | if (plot.values[i] > 0) { 47 | props = common.areaProps(env, 'Series', serie, i); 48 | common.colorize(env, props, [['plotProps','stroke'],['plotProps','fill']], common.getItemColor(env, serie, i)); 49 | if (typeof props.inside == 'undefined' || props.inside < 0) 50 | plot.total += plot.values[i]; 51 | } 52 | for (i = 0; i < ii; i++) 53 | if (plot.values[i] < plot.total * opt.valueThresold) { 54 | plot.total = plot.total - plot.values[i]; 55 | plot.values[i] = 0; 56 | } 57 | } 58 | } 59 | 60 | var rstep = r / cnt; 61 | var rstart = -rstep, rend = 0; 62 | 63 | var pieces = []; 64 | for (serie in opt.values) { 65 | plot = env.plots[serie]; 66 | var paths = []; 67 | if (plot.visible) { 68 | rstart += rstep; 69 | rend += rstep; 70 | var angle = env.opt.startAngle, angleplus = 0, anglelimit = 0; 71 | 72 | if (plot.total == 0) { 73 | env.emptySeries = true; 74 | props = common.areaProps(env, 'Series', 'empty'); 75 | common.colorize(env, props, [['plotProps','stroke'],['plotProps','fill']], common.getItemColor(env, serie)); 76 | paths.push({ path : [ [ 'CIRCLE', cx, cy, r ] ], attr : props.plotProps }); 77 | 78 | } else { 79 | env.emptySeries = false; 80 | for (i = 0, ii = plot.values.length; i < ii; i++) { 81 | var value = plot.values[i]; 82 | if (value > 0) { 83 | props = common.areaProps(env, 'Series', serie, i); 84 | common.colorize(env, props, [['plotProps','stroke'],['plotProps','fill']], common.getItemColor(env, serie, i)); 85 | if (typeof props.inside == 'undefined' || props.inside < 0) { 86 | angle += anglelimit; 87 | angleplus = 360 * value / plot.total; 88 | anglelimit = angleplus; 89 | } else { 90 | angleplus = 360 * values[props.inside] / plot.total * value / values[props.inside]; 91 | } 92 | var rrstart = rstart, rrend = rend; 93 | if (props.r) { 94 | if (props.r > 0) { 95 | if (props.r <= 1) 96 | rrend = rstart + rstep * props.r; 97 | else 98 | rrend = rstart + props.r; 99 | } else { 100 | if (props.r >= -1) 101 | rrstart = rstart + rstep * (-props.r); 102 | else 103 | rrstart = rstart - props.r; 104 | } 105 | } 106 | if (!env.opt.clockwise) 107 | paths.push({ path : [ [ 'SLICE', cx, cy, rrend, rrstart, angle, angle + angleplus ] ], attr : props.plotProps }); 108 | else 109 | paths.push({ path : [ [ 'SLICE', cx, cy, rrend, rrstart, - angle - angleplus, - angle ] ], attr : props.plotProps }); 110 | } else 111 | paths.push({ path : false, attr : false }); 112 | } 113 | } 114 | } else { 115 | // Even if serie is not visible it's better to put some empty path (for better transitions). It's not mandatory, just better 116 | if (opt.values[serie] && opt.values[serie].length) 117 | for (i = 0, ii = opt.values[serie].length; i < ii; i++) 118 | paths.push({ path : false, attr : false }); 119 | } 120 | 121 | pieces.push({ section : 'Series', serie : serie, subSection : 'Plot', paths : paths , mousearea : 'paths'}); 122 | } 123 | 124 | return pieces; 125 | } 126 | } 127 | 128 | })(jQuery); 129 | -------------------------------------------------------------------------------- /src/elycharts_defaults.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | if (!$.elycharts) 11 | $.elycharts = {}; 12 | 13 | /*********************************************************************** 14 | * DEFAULT OPTIONS 15 | **********************************************************************/ 16 | 17 | $.elycharts.templates = { 18 | 19 | common : { 20 | // Tipo di grafico 21 | // type : 'line|pie|funnel|barline' 22 | 23 | // Permette di specificare una configurazione di default da utilizzare (definita in $.elycharts.templates.NOME) 24 | // La configurazione completa � quindi data da tutti i valori della conf di default alla quale viene unita (con sovrascrittura) la conf corrente 25 | // Il parametro � ricorsivo (la configurazione di default puo' a sua volta avere una configurazione di default) 26 | // Se non specificato, la configurazione di default � quella con lo stesso nome del tipo di grafico 27 | // template : 'NOME', 28 | 29 | /* DATI: 30 | // I valori associati a ogni serie del grafico. Ogni serie � associata a una chiave dell'oggetto value, il cui 31 | // valore � l'array di dati relativi 32 | values : {}, 33 | 34 | // Label associate ai valori del grafico 35 | // Solo in caso di label gestite da labelmanager (quindi per pie e funnel) e per label.html = true e' possibile inserire 36 | // degli elementi DOM/JQUERY che verranno presi e posizionati correttament. 37 | labels : [], 38 | 39 | // Anchor per la gestione mediante anchormanager. Possono essere stringhe e oggetti DOM/JQUERY che verranno riposizionati 40 | anchors : {}, 41 | 42 | tooltips : {}, 43 | 44 | legend : [], 45 | */ 46 | 47 | // Autoresize uses jQuery resize event to automatically resize the chart to the container 48 | // autoresize makes sense only when width or height is not defined. 49 | // autoresize: false, 50 | 51 | // Per impostare una dimensione diversa da quella del container settare width e height 52 | //width : x, 53 | //height : y 54 | 55 | // I margini del grafico rispetto al frame complessivo. Da notare che riguardano la posizione del grafico 56 | // principale, e NON degli elementi aggiuntivi (legenda, label e titoli degli assi...). Quindi i margini devono 57 | // essere impostati in genere proprio per lasciare lo spazio per questi elementi 58 | // Sintassi: [top, right, bottom, left] 59 | margins: [10, 10, 10, 10], 60 | 61 | // style : {}, 62 | 63 | // Per gestire al meglio l'interattivita' del grafico (tooltip, highlight, anchor...) viene inserito un secondo 64 | // layer per le parti sensibili al mouse. Se si sa che il grafico non avra' alcuna interattivita' si puo' impostare 65 | // questo valore a false per evitare di creare il layer (ottimizzando leggermente la pagina) 66 | interactive : true, 67 | 68 | // Dati da applicare a tutte le serie del grafico 69 | defaultSeries : { 70 | // Impostare a false per disabilitare la visualizzazione della serie 71 | visible : true, 72 | 73 | // Impostare color qui permette di impostare velocemente plotProps.stroke+fill, tooltip.frameProps.stroke, dotProps.stroke e fillProps.fill (se non specificati) 74 | //color: 'blue', 75 | 76 | //plotProps : { }, 77 | 78 | // Impostazioni dei tooltip 79 | tooltip : { 80 | active : true, 81 | // 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 82 | // Impostare a 0|auto � incompatibile con il frame SVG, quindi viene automaticamente disabilitato (come se frameProps = false) 83 | width: 100, height: 50, 84 | roundedCorners: 5, 85 | padding: [6, 6] /* y, x */, 86 | offset: [20, 0] /* y, x */, 87 | // Se frameProps = false non disegna la cornice del tooltip (ad es. per permettere di definire la propria cornice HTML) 88 | frameProps : { fill: "white", "stroke-width": 2 }, 89 | contentStyle : { "font-family": "Arial", "font-size": "12px", "line-height": "16px", color: "black" } 90 | }, 91 | 92 | // Highlight feature 93 | highlight : { 94 | // Cambia le dimensioni dell'elemento quando deve essere evidenziato 95 | //scale : [x, y], 96 | // Opzioni di animazione effetto "scale" 97 | scaleSpeed : 100, scaleEasing : '', 98 | // Cambia gli attributi dell'elemento quando evidenziato 99 | //newProps : { opacity : 1 }, 100 | // Inserisce un layer con gli attributi specificati sopra quello da evidenziare 101 | //overlayProps : {"fill" : "white", "fill-opacity" : .3, "stroke-width" : 0} 102 | // Muove l'area evidenziata. E' possibile specificare un valore X o un array [X, Y] 103 | //move : 10, 104 | // Opzioni di animazione effetto "move" 105 | moveSpeed : 100, moveEasing : '', 106 | // Opzioni di animazione da usare per riportare l'oggetto alle situazione iniziale 107 | restoreSpeed : 0, restoreEasing : '' 108 | }, 109 | 110 | anchor : { 111 | // Aggiunge alle anchor esterne la classe selezionata quando il mouse passa sull'area 112 | //addClass : "", 113 | // Evidenzia la serie al passaggio del mouse 114 | //highlight : "", 115 | // Se impostato a true usa gli eventi mouseenter/mouseleave invece di mouseover/mouseout per l'highlight 116 | //useMouseEnter : false, 117 | }, 118 | 119 | // Opzioni per la generazione animata dei grafici 120 | startAnimation : { 121 | //active : true, 122 | type : 'simple', 123 | speed : 600, 124 | delay : 0, 125 | propsFrom : {}, // applicate a tutte le props di plot 126 | propsTo : {}, // applicate a tutte le props di plot 127 | easing : '' // easing raphael: >, <, <>, backIn, backOut, bounce, elastic 128 | 129 | // Opzionale per alcune animazioni, permette di specificare un sotto-tipo 130 | // subType : 0|1|2 131 | }, 132 | 133 | // Opzioni per le transizioni dei grafici durante un cambiamento di configurazione 134 | /* stepAnimation : { 135 | speed : 600, 136 | delay : 0, 137 | easing : '' // easing raphael: >, <, <>, backIn, backOut, bounce, elastic 138 | },*/ 139 | 140 | label : { 141 | // Disegna o meno la label interna al grafico 142 | active : false, 143 | // Imposta un offset [X,Y] per la label (le coordinate sono relative al sistema di assi dello specifico settore disegnato. 144 | // Ad es. per il piechart la X � la distanza dal centro, la Y lo spostamento ortogonale 145 | //offset : [x, y], 146 | html : false, 147 | // Proprieta' della label (per HTML = false) 148 | props : { fill: 'black', stroke: "none", "font-family": 'Arial', "font-size": "16px" }, 149 | // Stile CSS della label (per HTML = true) 150 | style : { cursor : 'default' } 151 | // Posizionamento della label rispetto al punto centrale (+offset) identificato 152 | //frameAnchor : ['start|middle|end', 'top|middle|bottom'] 153 | } 154 | 155 | /*legend : { 156 | dotType : 'rect', 157 | dotWidth : 10, dotHeight : 10, dotR : 4, 158 | dotProps : { }, 159 | textProps : { font: '12px Arial', fill: "#000" } 160 | }*/ 161 | }, 162 | 163 | series : { 164 | // Serie specifica usata quando ci sono "dati vuoti" (ad esempio quando un piechart e' a 0) 165 | empty : { 166 | //plotProps : { fill : "#D0D0D0" }, 167 | label : { active : false }, 168 | tooltip : { active : false } 169 | } 170 | /*root : { 171 | values : [] 172 | }*/ 173 | }, 174 | 175 | features : { 176 | tooltip : { 177 | // Imposta una posizione fissa per tutti i tooltip 178 | //fixedPos : [ x, y] 179 | // Velocita' del fade 180 | fadeDelay : 100, 181 | // Velocita' dello spostamento del tip da un'area all'altra 182 | moveDelay : 300 183 | // E' possibile specificare una funzione che filtra le coordinate del tooltip prima di mostrarlo, permettendo di modificarle 184 | // 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) 185 | //positionHandler : function(env, tooltipConf, mouseAreaData, suggestedX, suggestedY) { return [suggestedX, suggestedY] } 186 | }, 187 | mousearea : { 188 | // 'single' le aree sensibili sono relative a ogni valore di ogni serie, se 'index' il mouse attiva tutte le serie per un indice 189 | type : 'single', 190 | // 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 191 | indexCenter : 'auto', 192 | // Quanto tempo puo' passare nel passaggio da un'area all'altra per considerarlo uno spostamento di puntatore 193 | areaMoveDelay : 500, 194 | // Se diversi chart specificano lo stesso syncTag quando si attiva l'area di uno si disattivano quelle degli altri 195 | syncTag: false, 196 | // Callback for mouse actions. Parameters passed: (env, serie, index, mouseAreaData) 197 | onMouseEnter : false, 198 | onMouseExit : false, 199 | onMouseChanged : false, 200 | onMouseOver : false, 201 | onMouseOut : false 202 | }, 203 | highlight : { 204 | // 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 205 | //indexHighlight : 'barline', 206 | indexHighlightProps : { opacity : 1 /*fill : 'yellow', opacity : .3, scale : ".5 1"*/ } 207 | }, 208 | animation : { 209 | // Valore di default per la generazione animata degli elementi del grafico (anche per le non-serie: label, grid...) 210 | startAnimation : { 211 | //active : true, 212 | //propsFrom : {}, // applicate a tutte le props di plot 213 | //propsTo : {}, // applicate a tutte le props di plot 214 | speed : 600, 215 | delay : 0, 216 | easing : '' // easing raphael: >, <, <>, backIn, backOut, bounce, elastic 217 | }, 218 | // Valore di default per la transizione animata degli elementi del grafico (anche per le non-serie: label, grid...) 219 | stepAnimation : { 220 | speed : 600, 221 | delay : 0, 222 | easing : '' // easing raphael: >, <, <>, backIn, backOut, bounce, elastic 223 | } 224 | }, 225 | frameAnimation : { 226 | active : false, 227 | cssFrom : { opacity : 0}, 228 | cssTo : { opacity: 1 }, 229 | speed : 'slow', 230 | easing : 'linear' // easing jQuery: 'linear' o 'swing' 231 | }, 232 | // used to be true 233 | pixelWorkAround : { 234 | active : Raphael.svg 235 | }, 236 | label : {}, 237 | shadows : { 238 | active : false, 239 | offset : [2, 2], // Per attivare l'ombra, [y, x] 240 | props : {"stroke-width": 0, "stroke-opacity": 0, "fill": "black", "fill-opacity": .3} 241 | }, 242 | // BALLOONS: Applicabile solo al funnel (per ora) 243 | balloons : { 244 | active : false, 245 | // Width: se non specificato e' automatico 246 | //width : 200, 247 | // Height: se non specificato e' automatico 248 | //height : 50, 249 | // Lo stile CSS da applicare a ogni balloon 250 | style : { }, 251 | // Padding 252 | padding : [ 5, 5 ], 253 | // La distanza dal bordo sinistro 254 | left : 10, 255 | // 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)] ] 256 | line : [ [ 0, 0 ], [0, 0] ], 257 | // Propriet� della linea 258 | lineProps : { } 259 | }, 260 | legend : { 261 | horizontal : false, 262 | x : 'auto', // X | auto, (auto solo per horizontal = true) 263 | y : 10, 264 | width : 'auto', // X | auto, (auto solo per horizontal = true) 265 | height : 20, 266 | itemWidth : "fixed", // fixed | auto, solo per horizontal = true 267 | margins : [0, 0, 0, 0], 268 | dotMargins : [10, 5], // sx, dx 269 | borderProps : { fill : "white", stroke : "black", "stroke-width" : 1 }, 270 | dotType : 'rect', 271 | dotWidth : 10, dotHeight : 10, 272 | // radius for the dots (used to be 4 but there also was a bug preventing radius support, so moved to 0) 273 | dotR : 0, 274 | dotProps : { type : "rect", width : 10, height : 10 }, 275 | textProps : { font: '12px Arial', fill: "#000" } 276 | }, 277 | debug : { 278 | active : false 279 | } 280 | }, 281 | 282 | enableInternalCaching : true, 283 | 284 | nop : 0 285 | }, 286 | 287 | line : { 288 | template : 'common', 289 | 290 | // absolute margin left to both sides of each column / column group. 291 | barMargins : 0, 292 | // overlap between additional columns over the previous one (ignored for the first serie) 293 | barOverlapPerc : 0, 294 | 295 | // disable this if you want to use null values and want the lines/area to be broken over null values 296 | avgOverNulls: true, 297 | 298 | // Axis 299 | defaultAxis : { 300 | // [non per asse x] Normalizza il valore massimo dell'asse in modo che tutte le label abbiamo al massimo N cifre significative 301 | // (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) 302 | normalize: 2, 303 | // Permette di impostare i valori minimi e massimi di asse (invece di autorilevarli) 304 | min: 0, //max: x, 305 | // Imposta un testo da usare come prefisso e suffisso delle label 306 | //prefix : "", suffix : "", 307 | // Visualizza o meno le label dell'asse 308 | labels: false, 309 | // Distanza tra le label e l'asse relativo 310 | labelsDistance: 8, 311 | // [solo asse x] Rotazione (in gradi) delle label. Se specificato ignora i valori di labelsAnchor e labelsProps['text-anchor'] 312 | labelsRotate: 0, 313 | // Proprieta' grafiche delle label 314 | labelsProps : {font: '10px Arial', fill: "#000"}, 315 | // Compatta il numero mostrato nella label usando i suffissi specificati per migliaia, milioni... 316 | //labelsCompactUnits : ['k', 'M'], 317 | // Permette di specificare una funzione esterna che si occupa di formattare (o in generale trasformare) la label 318 | //labelsFormatHandler : function (label) { return label }, 319 | // Salta le prime N label 320 | //labelsSkip : 0, 321 | // 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. 322 | //labelsAnchor : "auto" 323 | // [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. 324 | //labelsPos : "auto", 325 | // Automatically hide labels that would overlap previous labels. 326 | //labelsHideCovered : true, 327 | // Inserisce un margine alla label (a sinistra se in asse x, in alto se in altri assi) 328 | //labelsMargin: 10, 329 | // [solo asse x] If labelsHideCovered = true, make sure each label have at least this space before the next one. 330 | //labelsMarginRight: 0, 331 | // Distanza del titolo dall'asse 332 | titleDistance : 25, titleDistanceIE : .75, 333 | // Proprieta' grafiche del titolo 334 | titleProps : {font: '12px Arial', fill: "#000", "font-weight": "bold"} 335 | }, 336 | axis : { 337 | x : { titleDistanceIE : 1.2 } 338 | }, 339 | 340 | defaultSeries : { 341 | // Tipo di serie, puo' essere 'line' o 'bar' 342 | type : 'line', 343 | // L'asse di riferimento della serie. Gli assi "l" ed "r" sono i 2 assi visibili destro e sinistro. 344 | // E' possibile inserire anche un asse arbitrario (che non sar� visibile) 345 | axis : 'l', 346 | // Specificare cumulative = true se i valori inseriti per la serie sono cumulativi 347 | cumulative : false, 348 | // In caso di type="line" indica l'arrotondamento della linea 349 | rounded : 1, 350 | // Mette il punto di intersezione al centro dell'intervallo invece che al limite (per allineamento con bars). Se 'auto' decide autonomamente 351 | lineCenter : 'auto', 352 | // Permette di impilare le serie (i valori di uno iniziano dove finiscono quelli del precedente) con un altra (purche' dello stesso tipo) 353 | // Specificare "true" per impilare con la serie visibile precedente, oppure il nome della serie sulla quale impilare 354 | // stacked : false, 355 | 356 | plotProps : {"stroke-width": 1, "stroke-linejoin": "round"}, 357 | 358 | barWidthPerc: 100, 359 | //DELETED: barProps : {"width-perc" : 100, "stroke-width": 1, "fill-opacity" : .3}, 360 | 361 | // Attiva o disattiva il riempimento 362 | fill : false, 363 | fillProps : {stroke: "none", "stroke-width" : 0, "stroke-opacity": 0, opacity: .3}, 364 | 365 | dot : false, 366 | dotProps : {size: 4, stroke: "#000", zindex: 5}, 367 | dotShowOnNull : false, 368 | 369 | mouseareaShowOnNull : false, 370 | 371 | startAnimation : { 372 | plotPropsFrom : false, 373 | // DELETED linePropsFrom : false, 374 | fillPropsFrom : false, 375 | dotPropsFrom : false, 376 | //DELETED barPropsFrom : false, 377 | shadowPropsFrom : false 378 | } 379 | 380 | }, 381 | 382 | features : { 383 | grid : { 384 | // N. di divisioni sull'asse X. Se "auto" si basa sulla label da visualizzare. Se "0" imposta draw[vertical] = false 385 | // 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 386 | nx : "auto", 387 | // N. di divisione sull'asse Y. Se "0" imposta draw[horizontal] = false 388 | ny : 4, 389 | // Disegna o meno la griglia. Si puo' specificare un array [horizontal, vertical] 390 | draw : false, 391 | // Forza la visualizzazione dei bordi/assi. Se true disegna comunque i bordi (anche se draw = false o se non ci sono label), 392 | // altrimenti si basa sulle regole standard di draw e presenza label (per asse x) 393 | // Puo' essere un booleano singolo o un array di bordi [up, dx, down, sx] 394 | forceBorder : false, 395 | // Proprieta' di visualizzazione griglia 396 | props : {stroke: '#e0e0e0', "stroke-width": 1}, 397 | // Dimensioni extra delle rette [up, dx, down, sx] 398 | extra : [0, 0, 0, 0], 399 | // 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 400 | // Se specificato "auto" decide in autonomia 401 | labelsCenter : "auto", 402 | 403 | // Display a rectangular region with properties specied for every even/odd vertical/horizontal grid division 404 | evenVProps : false, 405 | oddVProps : false, 406 | evenHProps : false, 407 | oddHProps : false, 408 | 409 | ticks : { 410 | // Attiva le barrette sugli assi [x, l, r] 411 | active : [false, false, false], 412 | // Dimensioni da prima dell'asse a dopo l'asse 413 | size : [10, 10], 414 | // Proprieta' di visualizzazione griglia 415 | props : {stroke: '#e0e0e0', "stroke-width": 1} 416 | } 417 | } 418 | }, 419 | 420 | nop : 0 421 | }, 422 | 423 | pie : { 424 | template : 'common', 425 | 426 | // Coordinate del centro, se non specificate vengono autodeterminate 427 | //cx : 0, cy : 0, 428 | // Raggio della torta, se non specificato viene autodeterminato 429 | //r : 0 430 | // Radius in percentage of the available space 431 | //rPerc : 80 432 | // Angolo dal quale iniziare a disegnare le fette, in gradi 433 | startAngle : 0, 434 | // Disegna la torta con le fette in senso orario (invece dell'orientamento standard per gradi, in senso antiorario) 435 | clockwise : false, 436 | // Soglia (rapporto sul totale) entro la quale una fetta non viene visualizzata 437 | valueThresold : 0.006, 438 | 439 | // @since elycharts 2.1.5 (previously there was no margins support so when we implemented it we had to add a 0 margin 440 | // here to not start adding the common margin to every pie user 441 | margins : [0, 0, 0, 0], 442 | 443 | defaultSeries : { 444 | // r: .5, raggio usato solo per questo spicchio, se <=1 e' in rapporto al raggio generale 445 | // inside: X, inserisce questo spicchio dentro un altro (funziona solo inside: precedente, e non gestisce + spicchi dentro l'altro) 446 | } 447 | }, 448 | 449 | funnel : { 450 | template : 'common', 451 | 452 | rh: 0, // height of ellipsis (for top and bottom cuts) 453 | method: 'width', // width/cutarea 454 | topSector: 0, // height factor of top cylinder 455 | topSectorProps : { fill: "#d0d0d0" }, 456 | bottomSector: .1, // height factor of bottom cylinder 457 | bottomSectorProps : { fill: "#d0d0d0" }, 458 | edgeProps : { fill: "#c0c0c0", "stroke-width": 1, opacity: 1 }, 459 | 460 | nop : 0 461 | }, 462 | 463 | barline : { 464 | template : 'common', 465 | 466 | // Imposta il valore massimo per la scala (altrimenti prende il valore + alto) 467 | // max : X 468 | 469 | // Impostare direction = rtl per creare un grafico che va da destra a sinistra 470 | direction : 'ltr' 471 | } 472 | } 473 | 474 | })(jQuery); 475 | -------------------------------------------------------------------------------- /src/elycharts_manager_anchor.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var common = $.elycharts.common; 12 | 13 | /*********************************************************************** 14 | * FEATURE: ANCHOR 15 | * 16 | * Permette di collegare i dati del grafico con delle aree esterne, 17 | * identificate dal loro selettore CSS, e di interagire con esse. 18 | **********************************************************************/ 19 | 20 | $.elycharts.anchormanager = { 21 | 22 | afterShow : function(env, pieces) { 23 | // Prendo le aree gestite da mouseAreas, e metto i miei listener 24 | // Non c'e' bisogno di gestire il clean per una chiamata successiva, lo fa gia' il mouseareamanager 25 | // Tranne per i bind degli eventi jquery 26 | 27 | if (!env.opt.anchors) 28 | return; 29 | 30 | if (!env.anchorBinds) 31 | env.anchorBinds = []; 32 | 33 | while (env.anchorBinds.length) { 34 | var b = env.anchorBinds.pop(); 35 | $(b[0]).unbind(b[1], b[2]); 36 | } 37 | 38 | for (var i = 0; i < env.mouseAreas.length; i++) { 39 | var serie = env.mouseAreas[i].piece ? env.mouseAreas[i].piece.serie : false; 40 | var anc; 41 | if (serie) 42 | anc = env.opt.anchors[serie][env.mouseAreas[i].index]; 43 | else 44 | anc = env.opt.anchors[env.mouseAreas[i].index]; 45 | 46 | if (anc && env.mouseAreas[i].props.anchor && env.mouseAreas[i].props.anchor.highlight) { 47 | 48 | (function(env, mouseAreaData, anc, caller) { 49 | 50 | var f1 = function() { caller.anchorMouseOver(env, mouseAreaData); }; 51 | var f2 = function() { caller.anchorMouseOut(env, mouseAreaData); }; 52 | if (!env.mouseAreas[i].props.anchor.useMouseEnter) { 53 | env.anchorBinds.push([anc, 'mouseover', f1]); 54 | env.anchorBinds.push([anc, 'mouseout', f2]); 55 | $(anc).mouseover(f1); 56 | $(anc).mouseout(f2); 57 | } else { 58 | env.anchorBinds.push([anc, 'mouseenter', f1]); 59 | env.anchorBinds.push([anc, 'mouseleave', f2]); 60 | $(anc).mouseenter(f1); 61 | $(anc).mouseleave(f2); 62 | } 63 | })(env, env.mouseAreas[i], anc, this); 64 | } 65 | } 66 | 67 | env.onAnchors = []; 68 | }, 69 | 70 | anchorMouseOver : function(env, mouseAreaData) { 71 | $.elycharts.highlightmanager.onMouseOver(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 72 | }, 73 | 74 | anchorMouseOut : function(env, mouseAreaData) { 75 | $.elycharts.highlightmanager.onMouseOut(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 76 | }, 77 | 78 | onMouseOver : function(env, serie, index, mouseAreaData) { 79 | if (!env.opt.anchors) 80 | return; 81 | 82 | if (mouseAreaData.props.anchor && mouseAreaData.props.anchor.addClass) { 83 | //var serie = mouseAreaData.piece ? mouseAreaData.piece.serie : false; 84 | var anc; 85 | if (serie) 86 | anc = env.opt.anchors[serie][mouseAreaData.index]; 87 | else 88 | anc = env.opt.anchors[mouseAreaData.index]; 89 | if (anc) { 90 | $(anc).addClass(mouseAreaData.props.anchor.addClass); 91 | env.onAnchors.push([anc, mouseAreaData.props.anchor.addClass]); 92 | } 93 | } 94 | }, 95 | 96 | onMouseOut : function(env, serie, index, mouseAreaData) { 97 | if (!env.opt.anchors) 98 | return; 99 | 100 | while (env.onAnchors.length > 0) { 101 | var o = env.onAnchors.pop(); 102 | $(o[0]).removeClass(o[1]); 103 | } 104 | } 105 | } 106 | 107 | $.elycharts.featuresmanager.register($.elycharts.anchormanager, 30); 108 | 109 | })(jQuery); 110 | -------------------------------------------------------------------------------- /src/elycharts_manager_animation.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var common = $.elycharts.common; 12 | 13 | /*********************************************************************** 14 | * ANIMATIONMANAGER 15 | **********************************************************************/ 16 | 17 | $.elycharts.animationmanager = { 18 | 19 | beforeShow : function(env, pieces) { 20 | if (!env.newopt) 21 | this.startAnimation(env, pieces); 22 | else 23 | this.stepAnimation(env, pieces); 24 | }, 25 | 26 | stepAnimation : function(env, pieces) { 27 | pieces = this._stepAnimationInt(env, pieces); 28 | }, 29 | 30 | _stepAnimationInt : function(env, pieces, section, serie, internal) { 31 | for (var i = 0; i < pieces.length; i++) { 32 | var animationProps = common.areaProps(env, section ? section : pieces[i].section, serie ? serie : pieces[i].serie); 33 | if (animationProps && animationProps.stepAnimation) 34 | animationProps = animationProps.stepAnimation; 35 | else 36 | animationProps = env.opt.features.animation.stepAnimation; 37 | 38 | if (typeof pieces[i].paths == 'undefined') { 39 | if (animationProps && animationProps.active && pieces[i].animation) { 40 | pieces[i].animation.speed = animationProps && animationProps.speed ? animationProps.speed : 300; 41 | pieces[i].animation.easing = animationProps && animationProps.easing ? animationProps.easing : ''; 42 | pieces[i].animation.delay = animationProps && animationProps.delay ? animationProps.delay : 0; 43 | if (!pieces[i].animation.element) 44 | pieces[i].animation.startAttr = {opacity : 0}; 45 | } 46 | } else { 47 | this._stepAnimationInt(env, pieces[i].paths, pieces[i].section, pieces[i].serie, true); 48 | } 49 | } 50 | }, 51 | 52 | startAnimation : function(env, pieces) { 53 | for (var i = 0; i < pieces.length; i++) 54 | if (pieces[i].paths || pieces[i].path) { 55 | var props = common.areaProps(env, pieces[i].section, pieces[i].serie); 56 | if (props && props.startAnimation) 57 | props = props.startAnimation; 58 | else 59 | props = env.opt.features.animation.startAnimation; 60 | 61 | if (props && props.active) { 62 | if (props.type == 'simple' || pieces[i].section != 'Series') 63 | this.animationSimple(env, props, pieces[i]); 64 | if (props.type == 'grow') 65 | this.animationGrow(env, props, pieces[i]); 66 | if (props.type == 'avg') 67 | this.animationAvg(env, props, pieces[i]); 68 | if (props.type == 'reg') 69 | this.animationReg(env, props, pieces[i]); 70 | } 71 | } 72 | }, 73 | 74 | /** 75 | * Inserisce i dati base di animazione del piece e la transizione di attributi 76 | */ 77 | _animationPiece : function(piece, animationProps, subSection) { 78 | if (piece.paths) { 79 | for (var i = 0; i < piece.paths.length; i++) 80 | this._animationPiece(piece.paths[i], animationProps, subSection); 81 | } else if (piece.path) { 82 | piece.animation = { 83 | speed : animationProps.speed, 84 | easing : animationProps.easing, 85 | delay : animationProps.delay, 86 | startPath : [], 87 | startAttr : common._clone(piece.attr) 88 | }; 89 | if (animationProps.propsTo) 90 | piece.attr = $.extend(true, piece.attr, animationProps.propsTo); 91 | if (animationProps.propsFrom) 92 | piece.animation.startAttr = $.extend(true, piece.animation.startAttr, animationProps.propsFrom); 93 | if (subSection && animationProps[subSection.toLowerCase() + 'PropsFrom']) 94 | piece.animation.startAttr = $.extend(true, piece.animation.startAttr, animationProps[subSection.toLowerCase() + 'PropsFrom']); 95 | 96 | if (typeof piece.animation.startAttr.opacity != 'undefined' && typeof piece.attr.opacity == 'undefined') 97 | piece.attr.opacity = 1; 98 | } 99 | }, 100 | 101 | animationSimple : function(env, props, piece) { 102 | this._animationPiece(piece, props, piece.subSection); 103 | }, 104 | 105 | animationGrow : function(env, props, piece) { 106 | this._animationPiece(piece, props, piece.subSection); 107 | var i, npath, y; 108 | 109 | switch (env.opt.type) { 110 | case 'line': 111 | y = env.height - env.opt.margins[2]; 112 | switch (piece.subSection) { 113 | case 'Plot': 114 | if (!piece.paths) { 115 | npath = [ 'LINE', [], piece.path[0][2]]; 116 | for (i = 0; i < piece.path[0][1].length; i++) 117 | npath[1].push([ piece.path[0][1][i][0], piece.path[0][1][i][1] == null ? null : y ]); 118 | piece.animation.startPath.push(npath); 119 | 120 | } else { 121 | for (i = 0; i < piece.paths.length; i++) 122 | if (piece.paths[i].path) 123 | piece.paths[i].animation.startPath.push([ 'RECT', piece.paths[i].path[0][1], y, piece.paths[i].path[0][3], y ]); 124 | } 125 | break; 126 | case 'Fill': 127 | npath = [ 'LINEAREA', [], [], piece.path[0][3]]; 128 | for (i = 0; i < piece.path[0][1].length; i++) { 129 | npath[1].push([ piece.path[0][1][i][0], piece.path[0][1][i][1] == null ? null : y ]); 130 | npath[2].push([ piece.path[0][2][i][0], piece.path[0][2][i][1] == null ? null : y ]); 131 | } 132 | piece.animation.startPath.push(npath); 133 | 134 | break; 135 | case 'Dot': 136 | for (i = 0; i < piece.paths.length; i++) 137 | if (piece.paths[i].path) 138 | piece.paths[i].animation.startPath.push(['CIRCLE', piece.paths[i].path[0][1], y, piece.paths[i].path[0][3]]); 139 | break; 140 | } 141 | break; 142 | 143 | case 'pie': 144 | if (piece.subSection == 'Plot') 145 | for (i = 0; i < piece.paths.length; i++) 146 | if (piece.paths[i].path && piece.paths[i].path[0][0] == 'SLICE') 147 | 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] ]); 148 | 149 | break; 150 | 151 | case 'funnel': 152 | alert('Unsupported animation GROW for funnel'); 153 | break; 154 | 155 | case 'barline': 156 | var x; 157 | if (piece.section == 'Series' && piece.subSection == 'Plot') { 158 | if (!props.subType) 159 | x = env.opt.direction != 'rtl' ? env.opt.margins[3] : env.width - env.opt.margins[1]; 160 | else if (props.subType == 1) 161 | x = env.opt.direction != 'rtl' ? env.width - env.opt.margins[1] : env.opt.margins[3]; 162 | for (i = 0; i < piece.paths.length; i++) 163 | if (piece.paths[i].path) { 164 | if (!props.subType || props.subType == 1) 165 | 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] ]); 166 | else { 167 | y = (piece.paths[i].path[0][2] + piece.paths[i].path[0][4]) / 2; 168 | 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] ]); 169 | } 170 | } 171 | } 172 | 173 | break; 174 | } 175 | }, 176 | 177 | _animationAvgXYArray : function(arr) { 178 | var res = [], avg = 0, i; 179 | var count = 0; 180 | for (i = 0; i < arr.length; i++) if (arr[i][1] != null) { 181 | avg += arr[i][1]; 182 | count++; 183 | } 184 | avg = avg / count; 185 | for (i = 0; i < arr.length; i++) 186 | res.push([ arr[i][0], arr[i][1] == null ? null : avg ]); 187 | return res; 188 | }, 189 | 190 | animationAvg : function(env, props, piece) { 191 | this._animationPiece(piece, props, piece.subSection); 192 | 193 | var avg = 0, i, l; 194 | switch (env.opt.type) { 195 | case 'line': 196 | switch (piece.subSection) { 197 | case 'Plot': 198 | if (!piece.paths) { 199 | // LINE 200 | piece.animation.startPath.push([ 'LINE', this._animationAvgXYArray(piece.path[0][1]), piece.path[0][2] ]); 201 | 202 | } else { 203 | // BAR 204 | l = 0; 205 | for (i = 0; i < piece.paths.length; i++) 206 | if (piece.paths[i].path) { 207 | l ++; 208 | avg += piece.paths[i].path[0][2]; 209 | } 210 | avg = avg / l; 211 | for (i = 0; i < piece.paths.length; i++) 212 | if (piece.paths[i].path) 213 | 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] ]); 214 | } 215 | break; 216 | 217 | case 'Fill': 218 | piece.animation.startPath.push([ 'LINEAREA', this._animationAvgXYArray(piece.path[0][1]), this._animationAvgXYArray(piece.path[0][2]), piece.path[0][3] ]); 219 | 220 | break; 221 | 222 | case 'Dot': 223 | l = 0; 224 | for (i = 0; i < piece.paths.length; i++) 225 | if (piece.paths[i].path) { 226 | l ++; 227 | avg += piece.paths[i].path[0][2]; 228 | } 229 | avg = avg / l; 230 | for (i = 0; i < piece.paths.length; i++) 231 | if (piece.paths[i].path) 232 | piece.paths[i].animation.startPath.push(['CIRCLE', piece.paths[i].path[0][1], avg, piece.paths[i].path[0][3]]); 233 | break; 234 | } 235 | break; 236 | 237 | case 'pie': 238 | var delta = 360 / piece.paths.length; 239 | 240 | if (piece.subSection == 'Plot') 241 | for (i = 0; i < piece.paths.length; i++) 242 | if (piece.paths[i].path && piece.paths[i].path[0][0] == 'SLICE') 243 | 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 ]); 244 | 245 | break; 246 | 247 | case 'funnel': 248 | alert('Unsupported animation AVG for funnel'); 249 | break; 250 | 251 | case 'barline': 252 | alert('Unsupported animation AVG for barline'); 253 | break; 254 | } 255 | }, 256 | 257 | _animationRegXYArray : function(arr) { 258 | var res = []; 259 | var c = arr.length; 260 | 261 | var start = 0; 262 | var end = c - 1; 263 | 264 | while (arr[start][1] == null) start++; 265 | while (arr[end][1] == null) end--; 266 | 267 | var y1 = arr[0][1]; 268 | var y2 = arr[c - 1][1]; 269 | 270 | for (var i = 0; i < arr.length; i++) { 271 | if (arr[i][1] == null) res.push([ arr[i][0], null ]); 272 | else { 273 | res.push([ arr[i][0], arr[start][1] + (arr[end][1] - arr[start][1]) / (end - start) * ( i - start) ]); 274 | } 275 | } 276 | 277 | return res; 278 | }, 279 | 280 | animationReg : function(env, props, piece) { 281 | this._animationPiece(piece, props, piece.subSection); 282 | var i, c, y1, y2; 283 | 284 | switch (env.opt.type) { 285 | case 'line': 286 | switch (piece.subSection) { 287 | case 'Plot': 288 | if (!piece.paths) { 289 | // LINE 290 | piece.animation.startPath.push([ 'LINE', this._animationRegXYArray(piece.path[0][1]), piece.path[0][2] ]); 291 | 292 | } else { 293 | // BAR 294 | c = piece.paths.length; 295 | if (c > 1) { 296 | for (i = 0; !piece.paths[i].path && i < piece.paths.length; i++) {} 297 | y1 = piece.paths[i].path ? common.getY(piece.paths[i].path[0]) : 0; 298 | for (i = piece.paths.length - 1; !piece.paths[i].path && i >= 0; i--) {} 299 | y2 = piece.paths[i].path ? common.getY(piece.paths[i].path[0]) : 0; 300 | 301 | for (i = 0; i < piece.paths.length; i++) 302 | if (piece.paths[i].path) 303 | 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] ]); 304 | } 305 | } 306 | break; 307 | 308 | case 'Fill': 309 | piece.animation.startPath.push([ 'LINEAREA', this._animationRegXYArray(piece.path[0][1]), this._animationRegXYArray(piece.path[0][2]), piece.path[0][3] ]); 310 | break; 311 | 312 | case 'Dot': 313 | c = piece.paths.length; 314 | if (c > 1) { 315 | for (i = 0; !piece.paths[i].path && i < piece.paths.length; i++) {} 316 | y1 = piece.paths[i].path ? common.getY(piece.paths[i].path[0]) : 0; 317 | for (i = piece.paths.length - 1; !piece.paths[i].path && i >= 0; i--) {} 318 | y2 = piece.paths[i].path ? common.getY(piece.paths[i].path[0]) : 0; 319 | 320 | for (i = 0; i < piece.paths.length; i++) 321 | if (piece.paths[i].path) 322 | 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]]); 323 | } 324 | break; 325 | } 326 | break; 327 | 328 | case 'pie': 329 | alert('Unsupported animation REG for pie'); 330 | break; 331 | 332 | case 'funnel': 333 | alert('Unsupported animation REG for funnel'); 334 | break; 335 | 336 | case 'barline': 337 | alert('Unsupported animation REG for barline'); 338 | break; 339 | } 340 | } 341 | } 342 | 343 | $.elycharts.featuresmanager.register($.elycharts.animationmanager, 10); 344 | 345 | /*********************************************************************** 346 | * FRAMEANIMATIONMANAGER 347 | **********************************************************************/ 348 | 349 | $.elycharts.frameanimationmanager = { 350 | 351 | beforeShow : function(env, pieces) { 352 | if (env.opt.features.frameAnimation.active) 353 | $(env.container.get(0)).css(env.opt.features.frameAnimation.cssFrom); 354 | }, 355 | 356 | afterShow : function(env, pieces) { 357 | if (env.opt.features.frameAnimation.active) 358 | env.container.animate(env.opt.features.frameAnimation.cssTo, env.opt.features.frameAnimation.speed, env.opt.features.frameAnimation.easing); 359 | } 360 | }; 361 | 362 | $.elycharts.featuresmanager.register($.elycharts.frameanimationmanager, 90); 363 | 364 | })(jQuery); 365 | -------------------------------------------------------------------------------- /src/elycharts_manager_balloon.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var common = $.elycharts.common; 12 | 13 | /*********************************************************************** 14 | * FEATURE: BALLOON 15 | **********************************************************************/ 16 | 17 | $.elycharts.balloonmanager = { 18 | 19 | afterShow : function(env, pieces) { 20 | // TODO transizioni 21 | 22 | if (env.opt.features.balloons.active && env.opt.balloons && env.opt.type == 'funnel') { 23 | var conf = env.opt.features.balloons; 24 | var lastSerie = false, lastIndex = false; 25 | for (var i = 0; i < pieces.length; i++) { 26 | if (pieces[i].section == 'Series' && pieces[i].subSection == 'Plot') { 27 | for (var index = 0; index < pieces[i].paths.length; index ++) 28 | if (env.opt.balloons[pieces[i].serie] && env.opt.balloons[pieces[i].serie][index]) { 29 | lastSerie = pieces[i].serie; 30 | lastIndex = index; 31 | this.drawBalloon(env, lastSerie, index, pieces[i].paths[index].rect); 32 | } 33 | } else if (lastSerie && pieces[i].section == 'Sector' && pieces[i].serie == 'bottom' && !pieces[i].subSection && lastIndex < env.opt.balloons[lastSerie].length - 1) { 34 | this.drawBalloon(env, lastSerie, env.opt.balloons[lastSerie].length - 1, pieces[i].rect); 35 | } 36 | } 37 | } 38 | }, 39 | 40 | drawBalloon : function(env, serie, index, rect) { 41 | var conf = env.opt.features.balloons; 42 | var balloon = env.opt.balloons[serie][index]; 43 | 44 | //var offset = $(env.container).offset(); 45 | var style = { 46 | position : 'absolute', 'z-index' : 25, 47 | //top : offset.top + rect[1] , left: offset.left + conf.left, 48 | margin : rect[1] + "px 0 0 " + conf.left + "px", 49 | height : (conf.height ? conf.height : rect[3] - rect[1]) - conf.padding[0] * 2 , 50 | width : conf.width ? conf.width : rect[0], 51 | padding : conf.padding[0] + 'px ' + conf.padding[1] + 'px' 52 | }; 53 | 54 | if (typeof balloon == 'string') 55 | $("
").css(style).css(conf.style).html(balloon).prependTo(env.container); 56 | else 57 | $(balloon).css(style).css(conf.style).prependTo(env.container); 58 | 59 | // Disegna la linea 60 | if (conf.line) { 61 | var path = []; 62 | for (var j = 0; j < conf.line.length; j++) { 63 | if (j == 0) 64 | path.push([ 'M', rect[0] - conf.line[j][0], rect[1] + conf.line[j][1]]); 65 | else if (j == conf.line.length - 1) 66 | path.push([ 'L', conf.left + (conf.width ? conf.width : rect[0]) - conf.line[j][0], rect[1] + conf.line[j][1] ]); 67 | else 68 | path.push([ 'L', rect[0] - conf.line[j][0], rect[1] + conf.line[j][1]]); 69 | } 70 | common.showPath(env, path).attr(conf.lineProps); 71 | } 72 | } 73 | } 74 | 75 | $.elycharts.featuresmanager.register($.elycharts.balloonmanager, 30); 76 | 77 | })(jQuery); 78 | -------------------------------------------------------------------------------- /src/elycharts_manager_highlight.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var common = $.elycharts.common; 12 | 13 | /*********************************************************************** 14 | * FEATURE: HIGHLIGHT 15 | * 16 | * Permette di evidenziare in vari modi l'area in cui si passa con il 17 | * mouse. 18 | **********************************************************************/ 19 | 20 | $.elycharts.highlightmanager = { 21 | 22 | removeHighlighted : function(env, full) { 23 | if (env.highlighted) 24 | while (env.highlighted.length > 0) { 25 | var o = env.highlighted.pop(); 26 | if (o.piece) { 27 | if (full) 28 | common.animationStackPush(env, o.piece, o.piece.element, common.getPieceFullAttr(env, o.piece), o.cfg.restoreSpeed, o.cfg.restoreEasing, 0, true); 29 | } else 30 | o.element.remove(); 31 | } 32 | }, 33 | 34 | afterShow : function(env, pieces) { 35 | if (env.highlighted && env.highlighted.length > 0) 36 | this.removeHighlighted(env, false); 37 | env.highlighted = []; 38 | }, 39 | 40 | onMouseOver : function(env, serie, index, mouseAreaData) { 41 | var path, element; 42 | // TODO Se non e' attivo l'overlay (per la serie o per tutto) e' inutile fare il resto 43 | 44 | // Cerco i piece da evidenziare (tutti quelli che sono costituiti da path multipli) 45 | for (var i = 0; i < mouseAreaData.pieces.length; i++) 46 | 47 | // Il loop sotto estrae solo i pieces con array di path (quindi non i line o i fill del linechart ... ma il resto si) 48 | if (mouseAreaData.pieces[i].section == 'Series' && mouseAreaData.pieces[i].paths 49 | && (!serie || mouseAreaData.pieces[i].serie == serie) 50 | && mouseAreaData.pieces[i].paths[index] && mouseAreaData.pieces[i].paths[index].element) { 51 | var piece = mouseAreaData.pieces[i].paths[index]; 52 | element = piece.element; 53 | path = piece.path; 54 | var attr = common.getElementOriginalAttrs(element); 55 | var newattr = false; // In caso la geometria dell'oggetto è modificata mediante attr (es: per circle) qui memorizza i nuovi attributi 56 | var props = serie ? mouseAreaData.props : common.areaProps(env, mouseAreaData.pieces[i].section, mouseAreaData.pieces[i].serie); 57 | var pelement, ppiece, ppath; 58 | if (path && props.highlight) { 59 | if (props.highlight.scale) { 60 | var scale = props.highlight.scale; 61 | if (typeof scale == 'number') 62 | scale = [scale, scale]; 63 | 64 | if (path[0][0] == 'RECT') { 65 | var w = path[0][3] - path[0][1]; 66 | var h = path[0][4] - path[0][2]; 67 | path = [ [ 'RECT', path[0][1], path[0][2] - h * (scale[1] - 1), path[0][3] + w * (scale[0] - 1), path[0][4] ] ]; 68 | common.animationStackPush(env, piece, element, common.getSVGProps(env, path), props.highlight.scaleSpeed, props.highlight.scaleEasing); 69 | } 70 | else if (path[0][0] == 'CIRCLE') { 71 | // I pass directly new radius 72 | newattr = {r : path[0][3] * scale[0]}; 73 | common.animationStackPush(env, piece, element, newattr, props.highlight.scaleSpeed, props.highlight.scaleEasing); 74 | } 75 | else if (path[0][0] == 'SLICE') { 76 | // Per lo slice x e' il raggio, y e' l'angolo 77 | var d = (path[0][6] - path[0][5]) * (scale[1] - 1) / 2; 78 | if (d > 90) 79 | d = 90; 80 | path = [ [ 'SLICE', path[0][1], path[0][2], path[0][3] * scale[0], path[0][4], path[0][5] - d, path[0][6] + d ] ]; 81 | common.animationStackPush(env, piece, element, common.getSVGProps(env, path), props.highlight.scaleSpeed, props.highlight.scaleEasing); 82 | 83 | } else if (env.opt.type == 'funnel') { 84 | var dx = (piece.rect[2] - piece.rect[0]) * (scale[0] - 1) / 2; 85 | var dy = (piece.rect[3] - piece.rect[1]) * (scale[1] - 1) / 2; 86 | 87 | // Specifico di un settore del funnel 88 | // SHOULD ALREADY BE DONE BY core common.animationStackStart(env); 89 | path = [ common.movePath(env, [ path[0]], [-dx, -dy])[0], 90 | common.movePath(env, [ path[1]], [+dx, -dy])[0], 91 | common.movePath(env, [ path[2]], [+dx, +dy])[0], 92 | common.movePath(env, [ path[3]], [-dx, +dy])[0], 93 | path[4] ]; 94 | common.animationStackPush(env, piece, element, common.getSVGProps(env, path), props.highlight.scaleSpeed, props.highlight.scaleEasing, 0, true); 95 | 96 | // Se c'e' un piece precedente lo usa, altrimenti cerca un topSector per la riduzione 97 | pelement = false; 98 | if (index > 0) { 99 | ppiece = mouseAreaData.pieces[i].paths[index - 1]; 100 | pelement = ppiece.element; 101 | ppath = ppiece.path; 102 | } else { 103 | ppiece = common.findInPieces(mouseAreaData.pieces, 'Sector', 'top'); 104 | if (ppiece) { 105 | pelement = ppiece.element; 106 | ppath = ppiece.path; 107 | } 108 | } 109 | if (pelement) { 110 | //pattr = common.getElementOriginalAttrs(pelement); 111 | ppath = [ 112 | ppath[0], ppath[1], 113 | common.movePath(env, [ ppath[2]], [+dx, -dy])[0], 114 | common.movePath(env, [ ppath[3]], [-dx, -dy])[0], 115 | ppath[4] ]; 116 | common.animationStackPush(env, ppiece, pelement, common.getSVGProps(env, ppath), props.highlight.scaleSpeed, props.highlight.scaleEasing, 0, true); 117 | env.highlighted.push({piece : ppiece, cfg : props.highlight}); 118 | } 119 | 120 | // Se c'e' un piece successivo lo usa, altrimenti cerca un bottomSector per la riduzione 121 | pelement = false; 122 | if (index < mouseAreaData.pieces[i].paths.length - 1) { 123 | ppiece = mouseAreaData.pieces[i].paths[index + 1]; 124 | pelement = ppiece.element; 125 | ppath = ppiece.path; 126 | } else { 127 | ppiece = common.findInPieces(mouseAreaData.pieces, 'Sector', 'bottom'); 128 | if (ppiece) { 129 | pelement = ppiece.element; 130 | ppath = ppiece.path; 131 | } 132 | } 133 | if (pelement) { 134 | //var pattr = common.getElementOriginalAttrs(pelement); 135 | ppath = [ 136 | common.movePath(env, [ ppath[0]], [-dx, +dy])[0], 137 | common.movePath(env, [ ppath[1]], [+dx, +dy])[0], 138 | ppath[2], ppath[3], 139 | ppath[4] ]; 140 | common.animationStackPush(env, ppiece, pelement, common.getSVGProps(env, ppath), props.highlight.scaleSpeed, props.highlight.scaleEasing, 0, true); 141 | env.highlighted.push({piece : ppiece, cfg : props.highlight}); 142 | } 143 | // SHOULD ALREADY BE DONE BY core: common.animationStackEnd(env); 144 | } 145 | /* Con scale non va bene 146 | if (!attr.scale) 147 | attr.scale = [1, 1]; 148 | element.attr({scale : [scale[0], scale[1]]}); */ 149 | } 150 | if (props.highlight.newProps) { 151 | for (var a in props.highlight.newProps) 152 | if (typeof attr[a] == 'undefined') 153 | attr[a] = false; 154 | common.animationStackPush(env, piece, element, props.highlight.newProps); 155 | } 156 | if (props.highlight.move) { 157 | var offset = $.isArray(props.highlight.move) ? props.highlight.move : [props.highlight.move, 0]; 158 | path = common.movePath(env, path, offset); 159 | common.animationStackPush(env, piece, element, common.getSVGProps(env, path), props.highlight.moveSpeed, props.highlight.moveEasing); 160 | } 161 | 162 | //env.highlighted.push({element : element, attr : attr}); 163 | env.highlighted.push({piece : piece, cfg : props.highlight}); 164 | 165 | if (props.highlight.overlayProps) { 166 | // NOTA: path e' il path modificato dai precedenti (cosi' l'overlay tiene conto della cosa), deve guardare anche a newattr 167 | //BIND: mouseAreaData.listenerDisabled = true; 168 | element = common.showPath(env, path); 169 | if (newattr) 170 | element.attr(newattr); 171 | element.attr(props.highlight.overlayProps); 172 | //BIND: $(element.node).unbind().mouseover(mouseAreaData.mouseover).mouseout(mouseAreaData.mouseout); 173 | // Se metto immediatamente il mouseAreaData.listenerDisabled poi va comunque un mouseout dalla vecchia area e va 174 | // in loop. TODO Rivedere e sistemare anche per tooltip 175 | //BIND: setTimeout(function() { mouseAreaData.listenerDisabled = false; }, 10); 176 | attr = false; 177 | env.highlighted.push({element : element, attr : attr, cfg : props.highlight}); 178 | } 179 | } 180 | } 181 | 182 | if (env.opt.features.highlight.indexHighlight && env.opt.type == 'line') { 183 | var t = env.opt.features.highlight.indexHighlight; 184 | if (t == 'auto') 185 | t = (env.indexCenter == 'bar' ? 'bar' : 'line'); 186 | 187 | var delta1 = (env.width - env.opt.margins[3] - env.opt.margins[1]) / (env.opt.labels.length > 0 ? env.opt.labels.length : 1); 188 | var delta2 = (env.width - env.opt.margins[3] - env.opt.margins[1]) / (env.opt.labels.length > 1 ? env.opt.labels.length - 1 : 1); 189 | var lineCenter = true; 190 | 191 | switch (t) { 192 | case 'bar': 193 | path = [ ['RECT', env.opt.margins[3] + index * delta1, env.opt.margins[0] , 194 | env.opt.margins[3] + (index + 1) * delta1, env.height - env.opt.margins[2] ] ]; 195 | break; 196 | 197 | case 'line': 198 | lineCenter = false; 199 | case 'barline': 200 | var x = Math.round((lineCenter ? delta1 / 2 : 0) + env.opt.margins[3] + index * (lineCenter ? delta1 : delta2)); 201 | path = [[ 'M', x, env.opt.margins[0]], ['L', x, env.height - env.opt.margins[2]]]; 202 | } 203 | if (path) { 204 | //BIND: mouseAreaData.listenerDisabled = true; 205 | element = common.showPath(env, path).attr(env.opt.features.highlight.indexHighlightProps); 206 | //BIND: $(element.node).unbind().mouseover(mouseAreaData.mouseover).mouseout(mouseAreaData.mouseout); 207 | //BIND: setTimeout(function() { mouseAreaData.listenerDisabled = false; }, 10); 208 | env.highlighted.push({element : element, attr : false, cfg : env.opt.features.highlight}); 209 | } 210 | } 211 | }, 212 | 213 | onMouseOut : function(env, serie, index, mouseAreaData) { 214 | this.removeHighlighted(env, true); 215 | } 216 | 217 | }; 218 | 219 | $.elycharts.featuresmanager.register($.elycharts.highlightmanager, 21); 220 | 221 | })(jQuery); 222 | -------------------------------------------------------------------------------- /src/elycharts_manager_label.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var common = $.elycharts.common; 12 | 13 | /*********************************************************************** 14 | * FEATURE: LABELS 15 | * 16 | * Permette di visualizzare in vari modi le label del grafico. 17 | * In particolare per pie e funnel permette la visualizzazione all'interno 18 | * delle fette. 19 | * Per i line chart le label sono visualizzate già nella gestione assi. 20 | * 21 | * TODO: 22 | * - Comunque per i line chart si potrebbe gestire la visualizzazione 23 | * all'interno delle barre, o sopra i punti. 24 | **********************************************************************/ 25 | 26 | $.elycharts.labelmanager = { 27 | 28 | beforeShow : function(env, pieces) { 29 | 30 | if (!common.executeIfChanged(env, ['labels', 'values', 'series'])) 31 | return; 32 | 33 | if (env.opt.labels && (env.opt.type == 'pie' || env.opt.type == 'funnel')) { 34 | var /*lastSerie = false, */lastIndex = false; 35 | var paths; 36 | 37 | for (var i = 0; i < pieces.length; i++) { 38 | if (pieces[i].section == 'Series' && pieces[i].subSection == 'Plot') { 39 | var props = common.areaProps(env, 'Series', pieces[i].serie); 40 | if (env.emptySeries && env.opt.series.empty) 41 | props.label = $.extend(true, props.label, env.opt.series.empty.label); 42 | if (props && props.label && props.label.active) { 43 | paths = []; 44 | for (var index = 0; index < pieces[i].paths.length; index++) 45 | if (pieces[i].paths[index].path) { 46 | //lastSerie = pieces[i].serie; 47 | lastIndex = index; 48 | paths.push(this.showLabel(env, pieces[i], pieces[i].paths[index], pieces[i].serie, index, pieces)); 49 | } else 50 | paths.push({ path : false, attr : false }); 51 | pieces.push({ section : pieces[i].section, serie : pieces[i].serie, subSection : 'Label', paths: paths }); 52 | } 53 | } 54 | else if (pieces[i].section == 'Sector' && pieces[i].serie == 'bottom' && !pieces[i].subSection && lastIndex < env.opt.labels.length - 1) { 55 | paths = []; 56 | paths.push(this.showLabel(env, pieces[i], pieces[i], 'Series', env.opt.labels.length - 1, pieces)); 57 | pieces.push({ section : pieces[i].section, serie : pieces[i].serie, subSection : 'Label', paths: paths }); 58 | } 59 | } 60 | 61 | } 62 | }, 63 | 64 | showLabel : function(env, piece, path, serie, index, pieces) { 65 | var pp = common.areaProps(env, 'Series', serie, index); 66 | if (env.opt.labels[index] || pp.label.label) { 67 | var p = path; 68 | var label = pp.label.label ? pp.label.label : env.opt.labels[index]; 69 | var center = common.getCenter(p, pp.label.offset); 70 | if (!pp.label.html) { 71 | var attr = pp.label.props; 72 | if (pp.label.frameAnchor) { 73 | attr = common._clone(pp.label.props); 74 | attr['text-anchor'] = pp.label.frameAnchor[0]; 75 | attr['alignment-baseline'] = pp.label.frameAnchor[1]; 76 | } 77 | /*pieces.push({ 78 | path : [ [ 'TEXT', label, center[0], center[1] ] ], attr : attr, 79 | section: 'Series', serie : serie, index : index, subSection : 'Label' 80 | });*/ 81 | return { path : [ [ 'TEXT', label, center[0], center[1] ] ], attr : attr }; 82 | 83 | } else { 84 | var opacity = 1; 85 | var style = common._clone(pp.label.style); 86 | var set_opacity = (typeof style.opacity != 'undefined') 87 | if (set_opacity) { 88 | opacity = style.opacity; 89 | style.opacity = 0; 90 | } 91 | style.position = 'absolute'; 92 | style['z-index'] = 25; 93 | 94 | var el; 95 | if (typeof label == 'string') 96 | el = $('
' + label + '
').css(style).prependTo(env.container); 97 | else 98 | el = $(label).css(style).prependTo(env.container); 99 | 100 | // Centramento corretto label 101 | if (env.opt.features.debug.active && el.height() == 0) 102 | 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.'); 103 | var posX = center[0]; 104 | var posY = center[1]; 105 | if (!pp.label.frameAnchor || pp.label.frameAnchor[0] == 'middle') 106 | posX -= el.width() / 2; 107 | else if (pp.label.frameAnchor && pp.label.frameAnchor[0] == 'end') 108 | posX -= el.width(); 109 | if (!pp.label.frameAnchor || pp.label.frameAnchor[1] == 'middle') 110 | posY -= el.height() / 2; 111 | else if (pp.label.frameAnchor && pp.label.frameAnchor[1] == 'top') 112 | posY -= el.height(); 113 | if (set_opacity) 114 | el.css({ margin: posY + 'px 0 0 ' + posX + 'px', opacity : opacity}); 115 | else 116 | el.css({ margin: posY + 'px 0 0 ' + posX + 'px'}); 117 | 118 | /*pieces.push({ 119 | path : [ [ 'DOMELEMENT', el ] ], attr : false, 120 | section: 'Series', serie : serie, index : index, subSection : 'Label' 121 | });*/ 122 | return { path : [ [ 'DOMELEMENT', el ] ], attr : false }; 123 | 124 | } 125 | } 126 | return false; 127 | } 128 | } 129 | 130 | $.elycharts.featuresmanager.register($.elycharts.labelmanager, 5); 131 | 132 | })(jQuery); 133 | -------------------------------------------------------------------------------- /src/elycharts_manager_legend.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var common = $.elycharts.common; 12 | 13 | /*********************************************************************** 14 | * FEATURE: LEGEND 15 | **********************************************************************/ 16 | 17 | $.elycharts.legendmanager = { 18 | 19 | afterShow : function(env, pieces) { 20 | // TODO the whole thing should simply return "pieces" whose visibility is handled by core, so to enable animations and 21 | // make things more generic. 22 | if (env.legenditems) { 23 | for (item in env.legenditems) { 24 | env.legenditems[item].remove(); 25 | } 26 | env.legenditems = false; 27 | } 28 | if (!env.opt.legend || env.opt.legend.length == 0) 29 | return; 30 | 31 | var props = env.opt.features.legend; 32 | 33 | if (props === false) return; 34 | 35 | var propsx = props.x; 36 | if (propsx == 'auto') { 37 | var autox = 1; 38 | propsx = 0; 39 | } 40 | var propswidth = props.width; 41 | if (propswidth == 'auto') { 42 | var autowidth = 1; 43 | propswidth = env.width; 44 | } 45 | 46 | var wauto = 0; 47 | var items = []; 48 | // env.opt.legend normalmente è { serie : 'Legend', ... }, per i pie invece { serie : ['Legend', ...], ... } 49 | var legendCount = 0; 50 | var serie, data, h, w, x, y, xd; 51 | for (serie in env.opt.legend) { 52 | if (env.opt.type != 'pie') 53 | legendCount ++; 54 | else 55 | legendCount += env.opt.legend[serie].length; 56 | } 57 | var i = 0; 58 | for (serie in env.opt.legend) { 59 | if (env.opt.type != 'pie') 60 | data = [ env.opt.legend[serie] ]; 61 | else 62 | data = env.opt.legend[serie]; 63 | 64 | for (var j = 0; j < data.length; j++) { 65 | var sprops = common.areaProps(env, 'Series', serie, env.opt.type == 'pie' ? j : false); 66 | var computedProps = $.extend(true, {}, props); 67 | 68 | if (sprops.legend) 69 | computedProps = $.extend(true, computedProps, sprops.legend); 70 | 71 | var color = common.getItemColor(env, serie, env.opt.type == 'pie' ? j : false); 72 | if (color) { 73 | common.colorize(env, computedProps, [['dotProps', 'fill']], color); 74 | } 75 | 76 | // legacy support for legend dot color inherited from pie "fill" 77 | // TODO maybe we should simply remove this and leave the "color" support only 78 | if (!computedProps.dotProps.fill && env.opt.type == 'pie') { 79 | if (sprops.plotProps && sprops.plotProps.fill) 80 | computedProps.dotProps.fill = sprops.plotProps.fill; 81 | } 82 | 83 | var hMargin = props.margins ? props.margins[0] + props.margins[2] : 0; 84 | var wMargin = props.margins ? props.margins[1] + props.margins[3] : 0; 85 | var tMargin = props.margins ? props.margins[0] : 0; 86 | var lMargin = props.margins ? props.margins[3] : 0; 87 | 88 | if (!props.horizontal) { 89 | // Posizione dell'angolo in alto a sinistra 90 | h = (props.height - hMargin) / legendCount; 91 | w = propswidth - wMargin; 92 | x = Math.floor(propsx + lMargin); 93 | y = Math.floor(props.y + tMargin + h * i); 94 | } else { 95 | h = props.height - hMargin; 96 | if (!props.itemWidth || props.itemWidth == 'fixed') { 97 | w = (propswidth - wMargin) / legendCount; 98 | x = Math.floor(propsx + lMargin + w * i); 99 | } else { 100 | w = (propswidth - wMargin) - wauto; 101 | x = Math.floor(propsx + lMargin + wauto); 102 | } 103 | y = Math.floor(props.y + tMargin); 104 | } 105 | 106 | if (computedProps.dotType == "rect") { 107 | 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)); 108 | xd = props.dotMargins[0] + computedProps.dotWidth + props.dotMargins[1]; 109 | } else if (computedProps.dotType == "circle") { 110 | items.push(common.showPath(env, [ [ 'CIRCLE', props.dotMargins[0] + x + computedProps.dotR, y + (h / 2), computedProps.dotR ] ]).attr(computedProps.dotProps)); 111 | xd = props.dotMargins[0] + computedProps.dotR * 2 + props.dotMargins[1]; 112 | } 113 | 114 | var text = data[j]; 115 | 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(); 116 | items.push(t); 117 | while (t.getBBox().width > (w - xd) && t.getBBox().width > 10) { 118 | text = text.substring(0, text.length - 1); 119 | t.attr({text : text}); 120 | } 121 | t.show(); 122 | 123 | if (props.horizontal && props.itemWidth == 'auto') 124 | wauto += xd + t.getBBox().width + 4; 125 | else if (!props.horizontal && autowidth) 126 | wauto = t.getBBox().width + xd > wauto ? t.getBBox().width + xd : wauto; 127 | else 128 | wauto += w; 129 | 130 | i++; 131 | } 132 | } 133 | 134 | if (autowidth) 135 | propswidth = wauto + props.margins[3] + props.margins[1] - 1; 136 | if (autox) { 137 | propsx = Math.floor((env.width - propswidth) / 2); 138 | for (i in items) { 139 | if (items[i].attrs.x) 140 | items[i].attr('x', items[i].attrs.x + propsx); 141 | else 142 | items[i].attr('path', common.movePath(env, items[i].attrs.path, [propsx, 0])); 143 | } 144 | } 145 | var borderPath = [ [ 'RECT', propsx, props.y, propsx + propswidth, props.y + props.height, props.r ] ]; 146 | var border = common.showPath(env, borderPath).attr(props.borderProps); 147 | 148 | // The legend rectangle is written as the last one because it depends on the sizes of the contents but it should 149 | // be drawn behind the others, so at the end we bring to front all items but the border 150 | for(i in items) items[i].toFront(); 151 | 152 | items.unshift(border); 153 | 154 | env.legenditems = items; 155 | } 156 | } 157 | 158 | $.elycharts.featuresmanager.register($.elycharts.legendmanager, 90); 159 | 160 | })(jQuery); 161 | -------------------------------------------------------------------------------- /src/elycharts_manager_mouse.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var featuresmanager = $.elycharts.featuresmanager; 12 | var common = $.elycharts.common; 13 | 14 | /*********************************************************************** 15 | * MOUSEMANAGER 16 | **********************************************************************/ 17 | 18 | $.elycharts.mousemanager = { 19 | 20 | clear : function(env) { 21 | if (env.mouseLayer) { 22 | env.mouseLayer.remove(); 23 | env.mouseLayer = null; 24 | env.mousePaper.clear(); 25 | env.mousePaper.remove(); 26 | env.mousePaper = null; 27 | env.mouseTimer = null; 28 | env.mouseAreas = null; 29 | // NOTE: do we also need to unbind mouseover/mouseout from areas or is this handled automatically by Raphael? 30 | } 31 | }, 32 | 33 | afterShow : function(env, pieces) { 34 | if (!env.opt.interactive) 35 | return; 36 | 37 | this.clear(env); 38 | 39 | env.mouseLayer = $('
').css({position : 'absolute', 'z-index' : 20, opacity : 1}).prependTo(env.container); 40 | env.mousePaper = common._RaphaelInstance(env.mouseLayer.get(0), env.width, env.height); 41 | var paper = env.mousePaper; 42 | 43 | if (env.opt.features.debug.active && typeof DP_Debug != 'undefined') { 44 | env.paper.text(env.width, env.height - 5, 'DEBUG').attr({ 'text-anchor' : 'end', stroke: 'red', opacity: .1 }); 45 | paper.text(env.width, env.height - 5, 'DEBUG').attr({ 'text-anchor' : 'end', stroke: 'red', opacity: .1 }).click(function() { 46 | DP_Debug.dump(env.opt, '', false, 4); 47 | }); 48 | } 49 | 50 | var i, j; 51 | 52 | // Adding mouseover only in right area, based on pieces 53 | env.mouseAreas = []; 54 | if (env.opt.features.mousearea.type == 'single') { 55 | // SINGLE: Every serie's index is an area 56 | for (i = 0; i < pieces.length; i++) { 57 | if (pieces[i].mousearea) { 58 | // pathstep 59 | if (!pieces[i].paths) { 60 | // path standard, generating an area for each point 61 | if (pieces[i].path.length >= 1 && (pieces[i].path[0][0] == 'LINE' || pieces[i].path[0][0] == 'LINEAREA')) 62 | for (j = 0; j < pieces[i].path[0][1].length; j++) { 63 | var props = common.areaProps(env, pieces[i].section, pieces[i].serie); 64 | if (props.mouseareaShowOnNull || pieces[i].section != 'Series' || env.opt.values[pieces[i].serie][j] != null) 65 | env.mouseAreas.push({ 66 | path : [ [ 'CIRCLE', pieces[i].path[0][1][j][0], pieces[i].path[0][1][j][1], 10 ] ], 67 | piece : pieces[i], 68 | pieces : pieces, 69 | index : j, 70 | props : props 71 | }); 72 | } 73 | 74 | else // Code below is only for standard path - it should be useless now (now there are only LINE and LINEAREA) 75 | // TODO DELETE 76 | for (j = 0; j < pieces[i].path.length; j++) { 77 | env.mouseAreas.push({ 78 | path : [ [ 'CIRCLE', common.getX(pieces[i].path[j]), common.getY(pieces[i].path[j]), 10 ] ], 79 | piece : pieces[i], 80 | pieces : pieces, 81 | index : j, 82 | props : common.areaProps(env, pieces[i].section, pieces[i].serie) 83 | }); 84 | } 85 | 86 | // paths 87 | } else if (pieces[i].paths) { 88 | // Set of paths (bar graph?), generating overlapped areas 89 | for (j = 0; j < pieces[i].paths.length; j++) 90 | if (pieces[i].paths[j].path) 91 | env.mouseAreas.push({ 92 | path : pieces[i].paths[j].path, 93 | piece : pieces[i], 94 | pieces : pieces, 95 | index : j, 96 | props : common.areaProps(env, pieces[i].section, pieces[i].serie) 97 | }); 98 | } 99 | } 100 | } 101 | } else { 102 | // INDEX: Each index (in every serie) is an area 103 | var indexCenter = env.opt.features.mousearea.indexCenter; 104 | if (indexCenter == 'auto') 105 | indexCenter = env.indexCenter; 106 | var start, delta; 107 | if (indexCenter == 'bar') { 108 | delta = (env.width - env.opt.margins[3] - env.opt.margins[1]) / (env.opt.labels.length > 0 ? env.opt.labels.length : 1); 109 | start = env.opt.margins[3]; 110 | } else { 111 | delta = (env.width - env.opt.margins[3] - env.opt.margins[1]) / (env.opt.labels.length > 1 ? env.opt.labels.length - 1 : 1); 112 | start = env.opt.margins[3] - delta / 2; 113 | } 114 | 115 | for (var idx in env.opt.labels) { 116 | // idx can be a string and concatenation results in bad sums. 117 | var index = parseInt(idx); 118 | env.mouseAreas.push({ 119 | path : [ [ 'RECT', start + index * delta, env.height - env.opt.margins[2], start + (index + 1) * delta, env.opt.margins[0] ] ], 120 | piece : false, 121 | pieces : pieces, 122 | index : parseInt(index), 123 | props : env.opt.defaultSeries // TODO common.areaProps(env, 'Plot') 124 | }); 125 | } 126 | } 127 | 128 | var syncenv = false; 129 | if (!env.opt.features.mousearea.syncTag) { 130 | env.mouseareaenv = { chartEnv : false, mouseObj : false, caller : false, inArea : -1, timer : false }; 131 | syncenv = env.mouseareaenv; 132 | } else { 133 | if (!$.elycharts.mouseareaenv) 134 | $.elycharts.mouseareaenv = {}; 135 | if (!$.elycharts.mouseareaenv[env.opt.features.mousearea.syncTag]) 136 | $.elycharts.mouseareaenv[env.opt.features.mousearea.syncTag] = { chartEnv : false, mouseObj : false, caller : false, inArea : -1, timer : false }; 137 | syncenv = $.elycharts.mouseareaenv[env.opt.features.mousearea.syncTag]; 138 | } 139 | for (i = 0; i < env.mouseAreas.length; i++) { 140 | env.mouseAreas[i].area = common.showPath(env, env.mouseAreas[i].path, paper).attr({stroke: "#000", fill: "#fff", opacity: 0}); 141 | 142 | (function(env, obj, objidx, caller, syncenv) { 143 | var piece = obj.piece; 144 | var index = obj.index; 145 | 146 | obj.mouseover = function(e) { 147 | //BIND: if (obj.listenerDisabled) return; 148 | obj.event = e; 149 | clearTimeout(syncenv.timer); 150 | caller.onMouseOverArea(env, piece, index, obj); 151 | 152 | if (syncenv.chartEnv && syncenv.chartEnv.id != env.id) { 153 | // Chart changed, removing old one 154 | syncenv.caller.onMouseExitArea(syncenv.chartEnv, syncenv.mouseObj.piece, syncenv.mouseObj.index, syncenv.mouseObj); 155 | caller.onMouseEnterArea(env, piece, index, obj); 156 | } 157 | else if (syncenv.inArea != objidx) { 158 | if (syncenv.inArea < 0) 159 | caller.onMouseEnterArea(env, piece, index, obj); 160 | else 161 | caller.onMouseChangedArea(env, piece, index, obj); 162 | } 163 | syncenv.chartEnv = env; 164 | syncenv.mouseObj = obj; 165 | syncenv.caller = caller; 166 | syncenv.inArea = objidx; 167 | }; 168 | obj.mouseout = function(e) { 169 | //BIND: if (obj.listenerDisabled) return; 170 | obj.event = e; 171 | clearTimeout(syncenv.timer); 172 | caller.onMouseOutArea(env, piece, index, obj); 173 | syncenv.timer = setTimeout(function() { 174 | syncenv.timer = false; 175 | caller.onMouseExitArea(env, piece, index, obj); 176 | syncenv.chartEnv = false; 177 | syncenv.inArea = -1; 178 | }, env.opt.features.mousearea.areaMoveDelay); 179 | }; 180 | 181 | $(obj.area.node).mouseover(obj.mouseover); 182 | $(obj.area.node).mouseout(obj.mouseout); 183 | })(env, env.mouseAreas[i], i, this, syncenv); 184 | } 185 | }, 186 | 187 | // Called when mouse enter an area 188 | onMouseOverArea : function(env, piece, index, mouseAreaData) { 189 | //console.warn('over', piece.serie, index); 190 | if (env.opt.features.mousearea.onMouseOver) 191 | env.opt.features.mousearea.onMouseOver(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 192 | featuresmanager.onMouseOver(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 193 | }, 194 | 195 | // Called when mouse exit from an area 196 | onMouseOutArea : function(env, piece, index, mouseAreaData) { 197 | //console.warn('out', piece.serie, index); 198 | if (env.opt.features.mousearea.onMouseOut) 199 | env.opt.features.mousearea.onMouseOut(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 200 | featuresmanager.onMouseOut(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 201 | }, 202 | 203 | // Called when mouse enter an area from empty space (= it was in no area before) 204 | onMouseEnterArea : function(env, piece, index, mouseAreaData) { 205 | //console.warn('enter', piece.serie, index); 206 | if (env.opt.features.mousearea.onMouseEnter) 207 | env.opt.features.mousearea.onMouseEnter(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 208 | featuresmanager.onMouseEnter(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 209 | }, 210 | 211 | // Called when mouse enter an area and it was on another area 212 | onMouseChangedArea : function(env, piece, index, mouseAreaData) { 213 | //console.warn('changed', piece.serie, index); 214 | if (env.opt.features.mousearea.onMouseChanged) 215 | env.opt.features.mousearea.onMouseChanged(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 216 | featuresmanager.onMouseChanged(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 217 | }, 218 | 219 | // Called when mouse leaves an area and does not enter in another one (timeout check) 220 | onMouseExitArea : function(env, piece, index, mouseAreaData) { 221 | //console.warn('exit', piece.serie, index); 222 | if (env.opt.features.mousearea.onMouseExit) 223 | env.opt.features.mousearea.onMouseExit(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 224 | featuresmanager.onMouseExit(env, mouseAreaData.piece ? mouseAreaData.piece.serie : false, mouseAreaData.index, mouseAreaData); 225 | } 226 | 227 | } 228 | 229 | $.elycharts.featuresmanager.register($.elycharts.mousemanager, 0); 230 | 231 | })(jQuery); 232 | -------------------------------------------------------------------------------- /src/elycharts_manager_shadow.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | /*********************************************************************** 12 | * FEATURE: SHADOW 13 | **********************************************************************/ 14 | 15 | $.elycharts.shadowmanager = { 16 | 17 | beforeShow : function(env, pieces) { 18 | if (!env.opt.features.shadows || !env.opt.features.shadows.active) 19 | return; 20 | 21 | // TODO if (!common.changed(env, ['labels', 'series'])) 22 | // TODO FIX 23 | var shadowOffset = env.opt.features.shadows.offset; 24 | 25 | var shadows = []; 26 | for (var i = 0; i < pieces.length; i++) { 27 | var path = []; 28 | for (var j = 0; j < pieces[i].path.length; j++) { 29 | var o = pieces[i].path[j]; 30 | switch (o[0]) { 31 | case 'M': case 'L': 32 | path.push([o[0], o[1] + shadowOffset[0], o[2] + shadowOffset[1]]); 33 | break; 34 | case 'A': case 'C': 35 | path.push([o[0], o[1], o[2], o[3], o[4], o[5], o[6] + shadowOffset[0], o[7] + shadowOffset[1]]); 36 | break; 37 | case 'z': case 'Z': 38 | path.push([o[0]]); 39 | break; 40 | } 41 | } 42 | shadows.push({path: path, attr: env.opt.features.shadows.props}); 43 | } 44 | for (var i = shadows.length - 1; i >= 0; i--) 45 | pieces.unshift(shadows[i]); 46 | } 47 | } 48 | 49 | $.elycharts.featuresmanager.register($.elycharts.shadowmanager, 5); 50 | 51 | })(jQuery); 52 | -------------------------------------------------------------------------------- /src/elycharts_manager_tooltip.js: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * ELYCHARTS 3 | * A Javascript library to generate interactive charts with vectorial graphics. 4 | * 5 | * Copyright (c) 2010-2014 Void Labs s.n.c. (http://void.it) 6 | * Licensed under the MIT (http://creativecommons.org/licenses/MIT/) license. 7 | **********************************************************************/ 8 | 9 | (function($) { 10 | 11 | var common = $.elycharts.common; 12 | 13 | /*********************************************************************** 14 | * FEATURE: TOOLTIP 15 | **********************************************************************/ 16 | 17 | $.elycharts.tooltipmanager = { 18 | 19 | clear : function(env) { 20 | if (env.tooltipContainer) { 21 | env.tooltipFrame.clear(); 22 | env.tooltipFrame.remove(); 23 | env.tooltipFrame = null; 24 | env.tooltipFrameElement = null; 25 | env.tooltipContent.remove(); 26 | env.tooltipContent = null; 27 | env.tooltipContainer.remove(); 28 | env.tooltipContainer = null; 29 | } 30 | }, 31 | 32 | afterShow : function(env, pieces) { 33 | this.clear(env); 34 | 35 | if (!$.elycharts.tooltipid) 36 | $.elycharts.tooltipid = 0; 37 | $.elycharts.tooltipid ++; 38 | 39 | // Preparo il tooltip 40 | env.tooltipContainer = $('').appendTo(document.body); 41 | env.tooltipFrame = common._RaphaelInstance('elycharts_tooltip_' + $.elycharts.tooltipid + '_frame', 500, 500); 42 | env.tooltipContent = $('#elycharts_tooltip_' + $.elycharts.tooltipid + '_content'); 43 | }, 44 | 45 | _prepareShow : function(env, props, mouseAreaData, tip) { 46 | 47 | // Il dimensionamento del tooltip e la view del frame SVG, lo fa solo se width ed height sono specificati 48 | if (props.width && props.width != 'auto' && props.height && props.height != 'auto') { 49 | var delta = props.frameProps && props.frameProps['stroke-width'] ? props.frameProps['stroke-width'] : 0; 50 | env.tooltipContainer.width(props.width + delta + 1).height(props.height + delta + 1); 51 | if (!env.tooltipFrameElement && props.frameProps) { 52 | var framePath = [ [ 'RECT', delta / 2, delta / 2, props.width, props.height, props.roundedCorners ] ]; 53 | env.tooltipFrameElement = common.showPath(env, framePath, env.tooltipFrame).attr(props.frameProps); 54 | // env.tooltipFrameElement = env.tooltipFrame.rect(delta / 2, delta / 2, props.width, props.height, props.roundedCorners); 55 | } 56 | } 57 | 58 | if (env.tooltipFrameElement) { 59 | env.tooltipFrameElement.attr(props.frameProps); 60 | } 61 | 62 | if (props.padding) 63 | env.tooltipContent.css({ padding : props.padding[0] + 'px ' + props.padding[1] + 'px' }); 64 | env.tooltipContent.css(props.contentStyle); 65 | env.tooltipContent.html(tip); 66 | 67 | //BIND: env.tooltipContainer.unbind().mouseover(mouseAreaData.mouseover).mouseout(mouseAreaData.mouseout); 68 | 69 | // WARN: Prendendo env.paper.canvas non va bene... 70 | //var offset = $(env.paper.canvas).offset(); 71 | var offset = $(env.container).offset(); 72 | 73 | if (env.opt.features.tooltip.fixedPos) { 74 | offset.top += env.opt.features.tooltip.fixedPos[1]; 75 | offset.left += env.opt.features.tooltip.fixedPos[0]; 76 | 77 | } else { 78 | var coord = this.getXY(env, props, mouseAreaData); 79 | if (!coord[2]) { 80 | offset.left += coord[0]; 81 | while (offset.top + coord[1] < 0) 82 | coord[1] += 20; 83 | offset.top += coord[1]; 84 | } else { 85 | offset.left = coord[0]; 86 | offset.top = coord[1]; 87 | } 88 | } 89 | 90 | return { top : offset.top, left : offset.left }; 91 | }, 92 | 93 | /** 94 | * Ritorna [x, y] oppure [x, y, true] se le coordinate sono relative alla pagina (e non al grafico) 95 | */ 96 | getXY : function(env, props, mouseAreaData) { 97 | // NOTA Posizione mouse: mouseAreaData.event.pageX/pageY 98 | var x = 0, y = 0; 99 | if (mouseAreaData.path[0][0] == 'RECT') { 100 | // L'area e' su un rettangolo (un bar o un indice completo), il tooltip lo faccio subito sopra 101 | // Nota: per capire se e' sull'indice completo basta guardare mouseAreaData.piece == null 102 | x = common.getX(mouseAreaData.path[0]) - props.offset[1]; 103 | y = common.getY(mouseAreaData.path[0]) - props.height - props.offset[0]; 104 | } 105 | else if (mouseAreaData.path[0][0] == 'CIRCLE') { 106 | // L'area e' su un cerchio (punto di un line) 107 | x = common.getX(mouseAreaData.path[0]) - props.offset[1]; 108 | y = common.getY(mouseAreaData.path[0]) - props.height - props.offset[0]; 109 | } 110 | else if (mouseAreaData.path[0][0] == 'SLICE') { 111 | // L'area è su una fetta di torta (pie) 112 | var path = mouseAreaData.path[0]; 113 | 114 | // Genera la posizione del tip considerando che deve stare all'interno di un cerchio che è sempre dalla parte opposta dell'area 115 | // e deve essere il piu' vicino possibile all'area 116 | var w = props.width && props.width != 'auto' ? props.width : 100; 117 | var h = props.height && props.height != 'auto' ? props.height : 100; 118 | // Raggio del cerchio che contiene il tip 119 | var cr = Math.sqrt(Math.pow(w,2) + Math.pow(h,2)) / 2; 120 | if (cr > env.opt.r) 121 | cr = env.opt.r; 122 | 123 | var tipangle = path[5] + (path[6] - path[5]) / 2 + 180; 124 | var rad = Math.PI / 180; 125 | x = path[1] + cr * Math.cos(- tipangle * rad) - w / 2; 126 | y = path[2] + cr * Math.sin(- tipangle * rad) - h / 2; 127 | } 128 | else if (mouseAreaData.piece && mouseAreaData.piece.paths && mouseAreaData.index >= 0 && mouseAreaData.piece.paths[mouseAreaData.index] && mouseAreaData.piece.paths[mouseAreaData.index].rect) { 129 | // L'area ha una forma complessa, ma abbiamo il rettangolo di contenimento (funnel) 130 | var rect = mouseAreaData.piece.paths[mouseAreaData.index].rect; 131 | x = rect[0] - props.offset[1]; 132 | y = rect[1] - props.height - props.offset[0]; 133 | } 134 | 135 | if (env.opt.features.tooltip.positionHandler) 136 | return env.opt.features.tooltip.positionHandler(env, props, mouseAreaData, x, y); 137 | else 138 | return [x, y]; 139 | }, 140 | 141 | getTip : function(env, serie, index) { 142 | var tip = false; 143 | if (env.opt.tooltips) { 144 | if (typeof env.opt.tooltips == 'function') 145 | 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); 146 | else { 147 | if (serie && env.opt.tooltips[serie] && env.opt.tooltips[serie][index]) 148 | tip = env.opt.tooltips[serie][index]; 149 | else if (!serie && env.opt.tooltips[index]) 150 | tip = env.opt.tooltips[index]; 151 | } 152 | } 153 | return tip; 154 | }, 155 | 156 | _getProps : function(env, serie, index, mouseAreaData) { 157 | var props = mouseAreaData.props.tooltip; 158 | if (env.emptySeries && env.opt.series.empty) 159 | props = $.extend(true, props, env.opt.series.empty.tooltip); 160 | if (!props || !props.active) 161 | return false; 162 | 163 | if (props.frameProps) { 164 | var color = common.getItemColor(env, serie, index); 165 | if (color) { 166 | props = common._clone(props); 167 | common.colorize(env, props, [['frameProps', 'stroke']], color); 168 | } 169 | } 170 | return props; 171 | }, 172 | 173 | _fadeOut : function(env) { 174 | env.tooltipContainer.fadeOut(env.opt.features.tooltip.fadeDelay); 175 | }, 176 | 177 | onMouseEnter : function(env, serie, index, mouseAreaData) { 178 | var props = this._getProps(env, serie, index, mouseAreaData); 179 | if (!props) return false; 180 | 181 | var tip = this.getTip(env, serie, index); 182 | if (!tip) { 183 | this._fadeOut(env); 184 | return true; 185 | } 186 | 187 | //if (!env.opt.tooltips || (serie && (!env.opt.tooltips[serie] || !env.opt.tooltips[serie][index])) || (!serie && !env.opt.tooltips[index])) 188 | // return this.onMouseExit(env, serie, index, mouseAreaData); 189 | //var tip = serie ? env.opt.tooltips[serie][index] : env.opt.tooltips[index]; 190 | 191 | env.tooltipContainer.css(this._prepareShow(env, props, mouseAreaData, tip)).fadeIn(env.opt.features.tooltip.fadeDelay); 192 | 193 | return true; 194 | }, 195 | 196 | onMouseChanged : function(env, serie, index, mouseAreaData) { 197 | var props = this._getProps(env, serie, index, mouseAreaData); 198 | if (!props) return false; 199 | 200 | var tip = this.getTip(env, serie, index); 201 | if (!tip) { 202 | this._fadeOut(env); 203 | return true; 204 | } 205 | 206 | /*if (!env.opt.tooltips || (serie && (!env.opt.tooltips[serie] || !env.opt.tooltips[serie][index])) || (!serie && !env.opt.tooltips[index])) 207 | return this.onMouseExit(env, serie, index, mouseAreaData); 208 | var tip = serie ? env.opt.tooltips[serie][index] : env.opt.tooltips[index];*/ 209 | 210 | env.tooltipContainer.clearQueue(); 211 | 212 | // NOTE: this is needed because sometimes we "fadeOut" during mouseChanged so we also have to fadeIn in that cases. 213 | // For simplicity we always fadeIn every time. 214 | env.tooltipContainer.fadeIn(env.opt.features.tooltip.fadeDelay); 215 | // Nota: Non passo da animationStackPush, i tooltip non sono legati a piece 216 | env.tooltipContainer.animate(this._prepareShow(env, props, mouseAreaData, tip), env.opt.features.tooltip.moveDelay, 'linear' /*swing*/); 217 | 218 | return true; 219 | }, 220 | 221 | onMouseExit : function(env, serie, index, mouseAreaData) { 222 | var props = this._getProps(env, serie, index, mouseAreaData); 223 | if (!props) return false; 224 | 225 | this._fadeOut(env); 226 | 227 | return true; 228 | } 229 | } 230 | 231 | $.elycharts.featuresmanager.register($.elycharts.tooltipmanager, 20); 232 | 233 | })(jQuery); 234 | --------------------------------------------------------------------------------