nv.d3.js 443 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368
  1. (function(){
  2. var nv = window.nv || {};
  3. nv.version = '1.1.15b';
  4. nv.dev = true //set false when in production
  5. window.nv = nv;
  6. nv.tooltip = nv.tooltip || {}; // For the tooltip system
  7. nv.utils = nv.utils || {}; // Utility subsystem
  8. nv.models = nv.models || {}; //stores all the possible models/components
  9. nv.charts = {}; //stores all the ready to use charts
  10. nv.graphs = []; //stores all the graphs currently on the page
  11. nv.logs = {}; //stores some statistics and potential error messages
  12. nv.dispatch = d3.dispatch('render_start', 'render_end');
  13. // *************************************************************************
  14. // Development render timers - disabled if dev = false
  15. if (nv.dev) {
  16. nv.dispatch.on('render_start', function(e) {
  17. nv.logs.startTime = +new Date();
  18. });
  19. nv.dispatch.on('render_end', function(e) {
  20. nv.logs.endTime = +new Date();
  21. nv.logs.totalTime = nv.logs.endTime - nv.logs.startTime;
  22. nv.log('total', nv.logs.totalTime); // used for development, to keep track of graph generation times
  23. });
  24. }
  25. // ********************************************
  26. // Public Core NV functions
  27. // Logs all arguments, and returns the last so you can test things in place
  28. // Note: in IE8 console.log is an object not a function, and if modernizr is used
  29. // then calling Function.prototype.bind with with anything other than a function
  30. // causes a TypeError to be thrown.
  31. nv.log = function() {
  32. if (nv.dev && console.log && console.log.apply)
  33. console.log.apply(console, arguments)
  34. else if (nv.dev && typeof console.log == "function" && Function.prototype.bind) {
  35. var log = Function.prototype.bind.call(console.log, console);
  36. log.apply(console, arguments);
  37. }
  38. return arguments[arguments.length - 1];
  39. };
  40. nv.render = function render(step) {
  41. step = step || 1; // number of graphs to generate in each timeout loop
  42. nv.render.active = true;
  43. nv.dispatch.render_start();
  44. setTimeout(function() {
  45. var chart, graph;
  46. for (var i = 0; i < step && (graph = nv.render.queue[i]); i++) {
  47. chart = graph.generate();
  48. if (typeof graph.callback == typeof(Function)) graph.callback(chart);
  49. nv.graphs.push(chart);
  50. }
  51. nv.render.queue.splice(0, i);
  52. if (nv.render.queue.length) setTimeout(arguments.callee, 0);
  53. else {
  54. nv.dispatch.render_end();
  55. nv.render.active = false;
  56. }
  57. }, 0);
  58. };
  59. nv.render.active = false;
  60. nv.render.queue = [];
  61. nv.addGraph = function(obj) {
  62. if (typeof arguments[0] === typeof(Function))
  63. obj = {generate: arguments[0], callback: arguments[1]};
  64. nv.render.queue.push(obj);
  65. if (!nv.render.active) nv.render();
  66. };
  67. nv.identity = function(d) { return d; };
  68. nv.strip = function(s) { return s.replace(/(\s|&)/g,''); };
  69. function daysInMonth(month,year) {
  70. return (new Date(year, month+1, 0)).getDate();
  71. }
  72. function d3_time_range(floor, step, number) {
  73. return function(t0, t1, dt) {
  74. var time = floor(t0), times = [];
  75. if (time < t0) step(time);
  76. if (dt > 1) {
  77. while (time < t1) {
  78. var date = new Date(+time);
  79. if ((number(date) % dt === 0)) times.push(date);
  80. step(time);
  81. }
  82. } else {
  83. while (time < t1) { times.push(new Date(+time)); step(time); }
  84. }
  85. return times;
  86. };
  87. }
  88. d3.time.monthEnd = function(date) {
  89. return new Date(date.getFullYear(), date.getMonth(), 0);
  90. };
  91. d3.time.monthEnds = d3_time_range(d3.time.monthEnd, function(date) {
  92. date.setUTCDate(date.getUTCDate() + 1);
  93. date.setDate(daysInMonth(date.getMonth() + 1, date.getFullYear()));
  94. }, function(date) {
  95. return date.getMonth();
  96. }
  97. );
  98. /* Utility class to handle creation of an interactive layer.
  99. This places a rectangle on top of the chart. When you mouse move over it, it sends a dispatch
  100. containing the X-coordinate. It can also render a vertical line where the mouse is located.
  101. dispatch.elementMousemove is the important event to latch onto. It is fired whenever the mouse moves over
  102. the rectangle. The dispatch is given one object which contains the mouseX/Y location.
  103. It also has 'pointXValue', which is the conversion of mouseX to the x-axis scale.
  104. */
  105. nv.interactiveGuideline = function() {
  106. "use strict";
  107. var tooltip = nv.models.tooltip();
  108. //Public settings
  109. var width = null
  110. , height = null
  111. //Please pass in the bounding chart's top and left margins
  112. //This is important for calculating the correct mouseX/Y positions.
  113. , margin = {left: 0, top: 0}
  114. , xScale = d3.scale.linear()
  115. , yScale = d3.scale.linear()
  116. , dispatch = d3.dispatch('elementMousemove', 'elementMouseout','elementDblclick')
  117. , showGuideLine = true
  118. , svgContainer = null
  119. //Must pass in the bounding chart's <svg> container.
  120. //The mousemove event is attached to this container.
  121. ;
  122. //Private variables
  123. var isMSIE = navigator.userAgent.indexOf("MSIE") !== -1 //Check user-agent for Microsoft Internet Explorer.
  124. ;
  125. function layer(selection) {
  126. selection.each(function(data) {
  127. var container = d3.select(this);
  128. var availableWidth = (width || 960), availableHeight = (height || 400);
  129. var wrap = container.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([data]);
  130. var wrapEnter = wrap.enter()
  131. .append("g").attr("class", " nv-wrap nv-interactiveLineLayer");
  132. wrapEnter.append("g").attr("class","nv-interactiveGuideLine");
  133. if (!svgContainer) {
  134. return;
  135. }
  136. function mouseHandler() {
  137. var d3mouse = d3.mouse(this);
  138. var mouseX = d3mouse[0];
  139. var mouseY = d3mouse[1];
  140. var subtractMargin = true;
  141. var mouseOutAnyReason = false;
  142. if (isMSIE) {
  143. /*
  144. D3.js (or maybe SVG.getScreenCTM) has a nasty bug in Internet Explorer 10.
  145. d3.mouse() returns incorrect X,Y mouse coordinates when mouse moving
  146. over a rect in IE 10.
  147. However, d3.event.offsetX/Y also returns the mouse coordinates
  148. relative to the triggering <rect>. So we use offsetX/Y on IE.
  149. */
  150. mouseX = d3.event.offsetX;
  151. mouseY = d3.event.offsetY;
  152. /*
  153. On IE, if you attach a mouse event listener to the <svg> container,
  154. it will actually trigger it for all the child elements (like <path>, <circle>, etc).
  155. When this happens on IE, the offsetX/Y is set to where ever the child element
  156. is located.
  157. As a result, we do NOT need to subtract margins to figure out the mouse X/Y
  158. position under this scenario. Removing the line below *will* cause
  159. the interactive layer to not work right on IE.
  160. */
  161. if(d3.event.target.tagName !== "svg")
  162. subtractMargin = false;
  163. if (d3.event.target.className.baseVal.match("nv-legend"))
  164. mouseOutAnyReason = true;
  165. }
  166. if(subtractMargin) {
  167. mouseX -= margin.left;
  168. mouseY -= margin.top;
  169. }
  170. /* If mouseX/Y is outside of the chart's bounds,
  171. trigger a mouseOut event.
  172. */
  173. if (mouseX < 0 || mouseY < 0
  174. || mouseX > availableWidth || mouseY > availableHeight
  175. || (d3.event.relatedTarget && d3.event.relatedTarget.ownerSVGElement === undefined)
  176. || mouseOutAnyReason
  177. )
  178. {
  179. if (isMSIE) {
  180. if (d3.event.relatedTarget
  181. && d3.event.relatedTarget.ownerSVGElement === undefined
  182. && d3.event.relatedTarget.className.match(tooltip.nvPointerEventsClass)) {
  183. return;
  184. }
  185. }
  186. dispatch.elementMouseout({
  187. mouseX: mouseX,
  188. mouseY: mouseY
  189. });
  190. layer.renderGuideLine(null); //hide the guideline
  191. return;
  192. }
  193. var pointXValue = xScale.invert(mouseX);
  194. dispatch.elementMousemove({
  195. mouseX: mouseX,
  196. mouseY: mouseY,
  197. pointXValue: pointXValue
  198. });
  199. //If user double clicks the layer, fire a elementDblclick dispatch.
  200. if (d3.event.type === "dblclick") {
  201. dispatch.elementDblclick({
  202. mouseX: mouseX,
  203. mouseY: mouseY,
  204. pointXValue: pointXValue
  205. });
  206. }
  207. }
  208. svgContainer
  209. .on("mousemove",mouseHandler, true)
  210. .on("mouseout" ,mouseHandler,true)
  211. .on("dblclick" ,mouseHandler)
  212. ;
  213. //Draws a vertical guideline at the given X postion.
  214. layer.renderGuideLine = function(x) {
  215. if (!showGuideLine) return;
  216. var line = wrap.select(".nv-interactiveGuideLine")
  217. .selectAll("line")
  218. .data((x != null) ? [nv.utils.NaNtoZero(x)] : [], String);
  219. line.enter()
  220. .append("line")
  221. .attr("class", "nv-guideline")
  222. .attr("x1", function(d) { return d;})
  223. .attr("x2", function(d) { return d;})
  224. .attr("y1", availableHeight)
  225. .attr("y2",0)
  226. ;
  227. line.exit().remove();
  228. }
  229. });
  230. }
  231. layer.dispatch = dispatch;
  232. layer.tooltip = tooltip;
  233. layer.margin = function(_) {
  234. if (!arguments.length) return margin;
  235. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  236. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  237. return layer;
  238. };
  239. layer.width = function(_) {
  240. if (!arguments.length) return width;
  241. width = _;
  242. return layer;
  243. };
  244. layer.height = function(_) {
  245. if (!arguments.length) return height;
  246. height = _;
  247. return layer;
  248. };
  249. layer.xScale = function(_) {
  250. if (!arguments.length) return xScale;
  251. xScale = _;
  252. return layer;
  253. };
  254. layer.showGuideLine = function(_) {
  255. if (!arguments.length) return showGuideLine;
  256. showGuideLine = _;
  257. return layer;
  258. };
  259. layer.svgContainer = function(_) {
  260. if (!arguments.length) return svgContainer;
  261. svgContainer = _;
  262. return layer;
  263. };
  264. return layer;
  265. };
  266. /* Utility class that uses d3.bisect to find the index in a given array, where a search value can be inserted.
  267. This is different from normal bisectLeft; this function finds the nearest index to insert the search value.
  268. For instance, lets say your array is [1,2,3,5,10,30], and you search for 28.
  269. Normal d3.bisectLeft will return 4, because 28 is inserted after the number 10. But interactiveBisect will return 5
  270. because 28 is closer to 30 than 10.
  271. Unit tests can be found in: interactiveBisectTest.html
  272. Has the following known issues:
  273. * Will not work if the data points move backwards (ie, 10,9,8,7, etc) or if the data points are in random order.
  274. * Won't work if there are duplicate x coordinate values.
  275. */
  276. nv.interactiveBisect = function (values, searchVal, xAccessor) {
  277. "use strict";
  278. if (! values instanceof Array) return null;
  279. if (typeof xAccessor !== 'function') xAccessor = function(d,i) { return d.x;}
  280. var bisect = d3.bisector(xAccessor).left;
  281. var index = d3.max([0, bisect(values,searchVal) - 1]);
  282. var currentValue = xAccessor(values[index], index);
  283. if (typeof currentValue === 'undefined') currentValue = index;
  284. if (currentValue === searchVal) return index; //found exact match
  285. var nextIndex = d3.min([index+1, values.length - 1]);
  286. var nextValue = xAccessor(values[nextIndex], nextIndex);
  287. if (typeof nextValue === 'undefined') nextValue = nextIndex;
  288. if (Math.abs(nextValue - searchVal) >= Math.abs(currentValue - searchVal))
  289. return index;
  290. else
  291. return nextIndex
  292. };
  293. /*
  294. Returns the index in the array "values" that is closest to searchVal.
  295. Only returns an index if searchVal is within some "threshold".
  296. Otherwise, returns null.
  297. */
  298. nv.nearestValueIndex = function (values, searchVal, threshold) {
  299. "use strict";
  300. var yDistMax = Infinity, indexToHighlight = null;
  301. values.forEach(function(d,i) {
  302. var delta = Math.abs(searchVal - d);
  303. if ( delta <= yDistMax && delta < threshold) {
  304. yDistMax = delta;
  305. indexToHighlight = i;
  306. }
  307. });
  308. return indexToHighlight;
  309. };/* Tooltip rendering model for nvd3 charts.
  310. window.nv.models.tooltip is the updated,new way to render tooltips.
  311. window.nv.tooltip.show is the old tooltip code.
  312. window.nv.tooltip.* also has various helper methods.
  313. */
  314. (function() {
  315. "use strict";
  316. window.nv.tooltip = {};
  317. /* Model which can be instantiated to handle tooltip rendering.
  318. Example usage:
  319. var tip = nv.models.tooltip().gravity('w').distance(23)
  320. .data(myDataObject);
  321. tip(); //just invoke the returned function to render tooltip.
  322. */
  323. window.nv.models.tooltip = function() {
  324. var content = null //HTML contents of the tooltip. If null, the content is generated via the data variable.
  325. , data = null /* Tooltip data. If data is given in the proper format, a consistent tooltip is generated.
  326. Format of data:
  327. {
  328. key: "Date",
  329. value: "August 2009",
  330. series: [
  331. {
  332. key: "Series 1",
  333. value: "Value 1",
  334. color: "#000"
  335. },
  336. {
  337. key: "Series 2",
  338. value: "Value 2",
  339. color: "#00f"
  340. }
  341. ]
  342. }
  343. */
  344. , gravity = 'w' //Can be 'n','s','e','w'. Determines how tooltip is positioned.
  345. , distance = 50 //Distance to offset tooltip from the mouse location.
  346. , snapDistance = 25 //Tolerance allowed before tooltip is moved from its current position (creates 'snapping' effect)
  347. , fixedTop = null //If not null, this fixes the top position of the tooltip.
  348. , classes = null //Attaches additional CSS classes to the tooltip DIV that is created.
  349. , chartContainer = null //Parent DIV, of the SVG Container that holds the chart.
  350. , tooltipElem = null //actual DOM element representing the tooltip.
  351. , position = {left: null, top: null} //Relative position of the tooltip inside chartContainer.
  352. , enabled = true //True -> tooltips are rendered. False -> don't render tooltips.
  353. //Generates a unique id when you create a new tooltip() object
  354. , id = "nvtooltip-" + Math.floor(Math.random() * 100000)
  355. ;
  356. //CSS class to specify whether element should not have mouse events.
  357. var nvPointerEventsClass = "nv-pointer-events-none";
  358. //Format function for the tooltip values column
  359. var valueFormatter = function(d,i) {
  360. return d;
  361. };
  362. //Format function for the tooltip header value.
  363. var headerFormatter = function(d) {
  364. return d;
  365. };
  366. //By default, the tooltip model renders a beautiful table inside a DIV.
  367. //You can override this function if a custom tooltip is desired.
  368. var contentGenerator = function(d) {
  369. if (content != null) return content;
  370. if (d == null) return '';
  371. var table = d3.select(document.createElement("table"));
  372. var theadEnter = table.selectAll("thead")
  373. .data([d])
  374. .enter().append("thead");
  375. theadEnter.append("tr")
  376. .append("td")
  377. .attr("colspan",3)
  378. .append("strong")
  379. .classed("x-value",true)
  380. .html(headerFormatter(d.value));
  381. var tbodyEnter = table.selectAll("tbody")
  382. .data([d])
  383. .enter().append("tbody");
  384. var trowEnter = tbodyEnter.selectAll("tr")
  385. .data(function(p) { return p.series})
  386. .enter()
  387. .append("tr")
  388. .classed("highlight", function(p) { return p.highlight})
  389. ;
  390. trowEnter.append("td")
  391. .classed("legend-color-guide",true)
  392. .append("div")
  393. .style("background-color", function(p) { return p.color});
  394. trowEnter.append("td")
  395. .classed("key",true)
  396. .html(function(p) {return p.key});
  397. trowEnter.append("td")
  398. .classed("value",true)
  399. .html(function(p,i) { return valueFormatter(p.value,i) });
  400. trowEnter.selectAll("td").each(function(p) {
  401. if (p.highlight) {
  402. var opacityScale = d3.scale.linear().domain([0,1]).range(["#fff",p.color]);
  403. var opacity = 0.6;
  404. d3.select(this)
  405. .style("border-bottom-color", opacityScale(opacity))
  406. .style("border-top-color", opacityScale(opacity))
  407. ;
  408. }
  409. });
  410. var html = table.node().outerHTML;
  411. if (d.footer !== undefined)
  412. html += "<div class='footer'>" + d.footer + "</div>";
  413. return html;
  414. };
  415. var dataSeriesExists = function(d) {
  416. if (d && d.series && d.series.length > 0) return true;
  417. return false;
  418. };
  419. //In situations where the chart is in a 'viewBox', re-position the tooltip based on how far chart is zoomed.
  420. function convertViewBoxRatio() {
  421. if (chartContainer) {
  422. var svg = d3.select(chartContainer);
  423. if (svg.node().tagName !== "svg") {
  424. svg = svg.select("svg");
  425. }
  426. var viewBox = (svg.node()) ? svg.attr('viewBox') : null;
  427. if (viewBox) {
  428. viewBox = viewBox.split(' ');
  429. var ratio = parseInt(svg.style('width')) / viewBox[2];
  430. position.left = position.left * ratio;
  431. position.top = position.top * ratio;
  432. }
  433. }
  434. }
  435. //Creates new tooltip container, or uses existing one on DOM.
  436. function getTooltipContainer(newContent) {
  437. var body;
  438. if (chartContainer)
  439. body = d3.select(chartContainer);
  440. else
  441. body = d3.select("body");
  442. var container = body.select(".nvtooltip");
  443. if (container.node() === null) {
  444. //Create new tooltip div if it doesn't exist on DOM.
  445. container = body.append("div")
  446. .attr("class", "nvtooltip " + (classes? classes: "xy-tooltip"))
  447. .attr("id",id)
  448. ;
  449. }
  450. container.node().innerHTML = newContent;
  451. container.style("top",0).style("left",0).style("opacity",0);
  452. container.selectAll("div, table, td, tr").classed(nvPointerEventsClass,true)
  453. container.classed(nvPointerEventsClass,true);
  454. return container.node();
  455. }
  456. //Draw the tooltip onto the DOM.
  457. function nvtooltip() {
  458. if (!enabled) return;
  459. if (!dataSeriesExists(data)) return;
  460. convertViewBoxRatio();
  461. var left = position.left;
  462. var top = (fixedTop != null) ? fixedTop : position.top;
  463. var container = getTooltipContainer(contentGenerator(data));
  464. tooltipElem = container;
  465. if (chartContainer) {
  466. var svgComp = chartContainer.getElementsByTagName("svg")[0];
  467. var boundRect = (svgComp) ? svgComp.getBoundingClientRect() : chartContainer.getBoundingClientRect();
  468. var svgOffset = {left:0,top:0};
  469. if (svgComp) {
  470. var svgBound = svgComp.getBoundingClientRect();
  471. var chartBound = chartContainer.getBoundingClientRect();
  472. var svgBoundTop = svgBound.top;
  473. //Defensive code. Sometimes, svgBoundTop can be a really negative
  474. // number, like -134254. That's a bug.
  475. // If such a number is found, use zero instead. FireFox bug only
  476. if (svgBoundTop < 0) {
  477. var containerBound = chartContainer.getBoundingClientRect();
  478. svgBoundTop = (Math.abs(svgBoundTop) > containerBound.height) ? 0 : svgBoundTop;
  479. }
  480. svgOffset.top = Math.abs(svgBoundTop - chartBound.top);
  481. svgOffset.left = Math.abs(svgBound.left - chartBound.left);
  482. }
  483. //If the parent container is an overflow <div> with scrollbars, subtract the scroll offsets.
  484. //You need to also add any offset between the <svg> element and its containing <div>
  485. //Finally, add any offset of the containing <div> on the whole page.
  486. left += chartContainer.offsetLeft + svgOffset.left - 2*chartContainer.scrollLeft;
  487. top += chartContainer.offsetTop + svgOffset.top - 2*chartContainer.scrollTop;
  488. }
  489. if (snapDistance && snapDistance > 0) {
  490. top = Math.floor(top/snapDistance) * snapDistance;
  491. }
  492. nv.tooltip.calcTooltipPosition([left,top], gravity, distance, container);
  493. return nvtooltip;
  494. };
  495. nvtooltip.nvPointerEventsClass = nvPointerEventsClass;
  496. nvtooltip.content = function(_) {
  497. if (!arguments.length) return content;
  498. content = _;
  499. return nvtooltip;
  500. };
  501. //Returns tooltipElem...not able to set it.
  502. nvtooltip.tooltipElem = function() {
  503. return tooltipElem;
  504. };
  505. nvtooltip.contentGenerator = function(_) {
  506. if (!arguments.length) return contentGenerator;
  507. if (typeof _ === 'function') {
  508. contentGenerator = _;
  509. }
  510. return nvtooltip;
  511. };
  512. nvtooltip.data = function(_) {
  513. if (!arguments.length) return data;
  514. data = _;
  515. return nvtooltip;
  516. };
  517. nvtooltip.gravity = function(_) {
  518. if (!arguments.length) return gravity;
  519. gravity = _;
  520. return nvtooltip;
  521. };
  522. nvtooltip.distance = function(_) {
  523. if (!arguments.length) return distance;
  524. distance = _;
  525. return nvtooltip;
  526. };
  527. nvtooltip.snapDistance = function(_) {
  528. if (!arguments.length) return snapDistance;
  529. snapDistance = _;
  530. return nvtooltip;
  531. };
  532. nvtooltip.classes = function(_) {
  533. if (!arguments.length) return classes;
  534. classes = _;
  535. return nvtooltip;
  536. };
  537. nvtooltip.chartContainer = function(_) {
  538. if (!arguments.length) return chartContainer;
  539. chartContainer = _;
  540. return nvtooltip;
  541. };
  542. nvtooltip.position = function(_) {
  543. if (!arguments.length) return position;
  544. position.left = (typeof _.left !== 'undefined') ? _.left : position.left;
  545. position.top = (typeof _.top !== 'undefined') ? _.top : position.top;
  546. return nvtooltip;
  547. };
  548. nvtooltip.fixedTop = function(_) {
  549. if (!arguments.length) return fixedTop;
  550. fixedTop = _;
  551. return nvtooltip;
  552. };
  553. nvtooltip.enabled = function(_) {
  554. if (!arguments.length) return enabled;
  555. enabled = _;
  556. return nvtooltip;
  557. };
  558. nvtooltip.valueFormatter = function(_) {
  559. if (!arguments.length) return valueFormatter;
  560. if (typeof _ === 'function') {
  561. valueFormatter = _;
  562. }
  563. return nvtooltip;
  564. };
  565. nvtooltip.headerFormatter = function(_) {
  566. if (!arguments.length) return headerFormatter;
  567. if (typeof _ === 'function') {
  568. headerFormatter = _;
  569. }
  570. return nvtooltip;
  571. };
  572. //id() is a read-only function. You can't use it to set the id.
  573. nvtooltip.id = function() {
  574. return id;
  575. };
  576. return nvtooltip;
  577. };
  578. //Original tooltip.show function. Kept for backward compatibility.
  579. // pos = [left,top]
  580. nv.tooltip.show = function(pos, content, gravity, dist, parentContainer, classes) {
  581. //Create new tooltip div if it doesn't exist on DOM.
  582. var container = document.createElement('div');
  583. container.className = 'nvtooltip ' + (classes ? classes : 'xy-tooltip');
  584. var body = parentContainer;
  585. if ( !parentContainer || parentContainer.tagName.match(/g|svg/i)) {
  586. //If the parent element is an SVG element, place tooltip in the <body> element.
  587. body = document.getElementsByTagName('body')[0];
  588. }
  589. container.style.left = 0;
  590. container.style.top = 0;
  591. container.style.opacity = 0;
  592. container.innerHTML = content;
  593. body.appendChild(container);
  594. //If the parent container is an overflow <div> with scrollbars, subtract the scroll offsets.
  595. if (parentContainer) {
  596. pos[0] = pos[0] - parentContainer.scrollLeft;
  597. pos[1] = pos[1] - parentContainer.scrollTop;
  598. }
  599. nv.tooltip.calcTooltipPosition(pos, gravity, dist, container);
  600. };
  601. //Looks up the ancestry of a DOM element, and returns the first NON-svg node.
  602. nv.tooltip.findFirstNonSVGParent = function(Elem) {
  603. while(Elem.tagName.match(/^g|svg$/i) !== null) {
  604. Elem = Elem.parentNode;
  605. }
  606. return Elem;
  607. };
  608. //Finds the total offsetTop of a given DOM element.
  609. //Looks up the entire ancestry of an element, up to the first relatively positioned element.
  610. nv.tooltip.findTotalOffsetTop = function ( Elem, initialTop ) {
  611. var offsetTop = initialTop;
  612. do {
  613. if( !isNaN( Elem.offsetTop ) ) {
  614. offsetTop += (Elem.offsetTop);
  615. }
  616. } while( Elem = Elem.offsetParent );
  617. return offsetTop;
  618. };
  619. //Finds the total offsetLeft of a given DOM element.
  620. //Looks up the entire ancestry of an element, up to the first relatively positioned element.
  621. nv.tooltip.findTotalOffsetLeft = function ( Elem, initialLeft) {
  622. var offsetLeft = initialLeft;
  623. do {
  624. if( !isNaN( Elem.offsetLeft ) ) {
  625. offsetLeft += (Elem.offsetLeft);
  626. }
  627. } while( Elem = Elem.offsetParent );
  628. return offsetLeft;
  629. };
  630. //Global utility function to render a tooltip on the DOM.
  631. //pos = [left,top] coordinates of where to place the tooltip, relative to the SVG chart container.
  632. //gravity = how to orient the tooltip
  633. //dist = how far away from the mouse to place tooltip
  634. //container = tooltip DIV
  635. nv.tooltip.calcTooltipPosition = function(pos, gravity, dist, container) {
  636. var height = parseInt(container.offsetHeight),
  637. width = parseInt(container.offsetWidth),
  638. windowWidth = nv.utils.windowSize().width,
  639. windowHeight = nv.utils.windowSize().height,
  640. scrollTop = window.pageYOffset,
  641. scrollLeft = window.pageXOffset,
  642. left, top;
  643. windowHeight = window.innerWidth >= document.body.scrollWidth ? windowHeight : windowHeight - 16;
  644. windowWidth = window.innerHeight >= document.body.scrollHeight ? windowWidth : windowWidth - 16;
  645. gravity = gravity || 's';
  646. dist = dist || 20;
  647. var tooltipTop = function ( Elem ) {
  648. return nv.tooltip.findTotalOffsetTop(Elem, top);
  649. };
  650. var tooltipLeft = function ( Elem ) {
  651. return nv.tooltip.findTotalOffsetLeft(Elem,left);
  652. };
  653. switch (gravity) {
  654. case 'e':
  655. left = pos[0] - width - dist;
  656. top = pos[1] - (height / 2);
  657. var tLeft = tooltipLeft(container);
  658. var tTop = tooltipTop(container);
  659. if (tLeft < scrollLeft) left = pos[0] + dist > scrollLeft ? pos[0] + dist : scrollLeft - tLeft + left;
  660. if (tTop < scrollTop) top = scrollTop - tTop + top;
  661. if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
  662. break;
  663. case 'w':
  664. left = pos[0] + dist;
  665. top = pos[1] - (height / 2);
  666. var tLeft = tooltipLeft(container);
  667. var tTop = tooltipTop(container);
  668. if (tLeft + width > windowWidth) left = pos[0] - width - dist;
  669. if (tTop < scrollTop) top = scrollTop + 5;
  670. if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
  671. break;
  672. case 'n':
  673. left = pos[0] - (width / 2) - 5;
  674. top = pos[1] + dist;
  675. var tLeft = tooltipLeft(container);
  676. var tTop = tooltipTop(container);
  677. if (tLeft < scrollLeft) left = scrollLeft + 5;
  678. if (tLeft + width > windowWidth) left = left - width/2 + 5;
  679. if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
  680. break;
  681. case 's':
  682. left = pos[0] - (width / 2);
  683. top = pos[1] - height - dist;
  684. var tLeft = tooltipLeft(container);
  685. var tTop = tooltipTop(container);
  686. if (tLeft < scrollLeft) left = scrollLeft + 5;
  687. if (tLeft + width > windowWidth) left = left - width/2 + 5;
  688. if (scrollTop > tTop) top = scrollTop;
  689. break;
  690. case 'none':
  691. left = pos[0];
  692. top = pos[1] - dist;
  693. var tLeft = tooltipLeft(container);
  694. var tTop = tooltipTop(container);
  695. break;
  696. }
  697. container.style.left = left+'px';
  698. container.style.top = top+'px';
  699. container.style.opacity = 1;
  700. container.style.position = 'absolute';
  701. return container;
  702. };
  703. //Global utility function to remove tooltips from the DOM.
  704. nv.tooltip.cleanup = function() {
  705. // Find the tooltips, mark them for removal by this class (so others cleanups won't find it)
  706. var tooltips = document.getElementsByClassName('nvtooltip');
  707. var purging = [];
  708. while(tooltips.length) {
  709. purging.push(tooltips[0]);
  710. tooltips[0].style.transitionDelay = '0 !important';
  711. tooltips[0].style.opacity = 0;
  712. tooltips[0].className = 'nvtooltip-pending-removal';
  713. }
  714. setTimeout(function() {
  715. while (purging.length) {
  716. var removeMe = purging.pop();
  717. removeMe.parentNode.removeChild(removeMe);
  718. }
  719. }, 500);
  720. };
  721. })();
  722. nv.utils.windowSize = function() {
  723. // Sane defaults
  724. var size = {width: 640, height: 480};
  725. // Earlier IE uses Doc.body
  726. if (document.body && document.body.offsetWidth) {
  727. size.width = document.body.offsetWidth;
  728. size.height = document.body.offsetHeight;
  729. }
  730. // IE can use depending on mode it is in
  731. if (document.compatMode=='CSS1Compat' &&
  732. document.documentElement &&
  733. document.documentElement.offsetWidth ) {
  734. size.width = document.documentElement.offsetWidth;
  735. size.height = document.documentElement.offsetHeight;
  736. }
  737. // Most recent browsers use
  738. if (window.innerWidth && window.innerHeight) {
  739. size.width = window.innerWidth;
  740. size.height = window.innerHeight;
  741. }
  742. return (size);
  743. };
  744. // Easy way to bind multiple functions to window.onresize
  745. // TODO: give a way to remove a function after its bound, other than removing all of them
  746. nv.utils.windowResize = function(fun){
  747. if (fun === undefined) return;
  748. var oldresize = window.onresize;
  749. window.onresize = function(e) {
  750. if (typeof oldresize == 'function') oldresize(e);
  751. fun(e);
  752. }
  753. }
  754. // Backwards compatible way to implement more d3-like coloring of graphs.
  755. // If passed an array, wrap it in a function which implements the old default
  756. // behavior
  757. nv.utils.getColor = function(color) {
  758. if (!arguments.length) return nv.utils.defaultColor(); //if you pass in nothing, get default colors back
  759. if( Object.prototype.toString.call( color ) === '[object Array]' )
  760. return function(d, i) { return d.color || color[i % color.length]; };
  761. else
  762. return color;
  763. //can't really help it if someone passes rubbish as color
  764. }
  765. // Default color chooser uses the index of an object as before.
  766. nv.utils.defaultColor = function() {
  767. var colors = d3.scale.category20().range();
  768. return function(d, i) { return d.color || colors[i % colors.length] };
  769. }
  770. // Returns a color function that takes the result of 'getKey' for each series and
  771. // looks for a corresponding color from the dictionary,
  772. nv.utils.customTheme = function(dictionary, getKey, defaultColors) {
  773. getKey = getKey || function(series) { return series.key }; // use default series.key if getKey is undefined
  774. defaultColors = defaultColors || d3.scale.category20().range(); //default color function
  775. var defIndex = defaultColors.length; //current default color (going in reverse)
  776. return function(series, index) {
  777. var key = getKey(series);
  778. if (!defIndex) defIndex = defaultColors.length; //used all the default colors, start over
  779. if (typeof dictionary[key] !== "undefined")
  780. return (typeof dictionary[key] === "function") ? dictionary[key]() : dictionary[key];
  781. else
  782. return defaultColors[--defIndex]; // no match in dictionary, use default color
  783. }
  784. }
  785. // From the PJAX example on d3js.org, while this is not really directly needed
  786. // it's a very cool method for doing pjax, I may expand upon it a little bit,
  787. // open to suggestions on anything that may be useful
  788. nv.utils.pjax = function(links, content) {
  789. d3.selectAll(links).on("click", function() {
  790. history.pushState(this.href, this.textContent, this.href);
  791. load(this.href);
  792. d3.event.preventDefault();
  793. });
  794. function load(href) {
  795. d3.html(href, function(fragment) {
  796. var target = d3.select(content).node();
  797. target.parentNode.replaceChild(d3.select(fragment).select(content).node(), target);
  798. nv.utils.pjax(links, content);
  799. });
  800. }
  801. d3.select(window).on("popstate", function() {
  802. if (d3.event.state) load(d3.event.state);
  803. });
  804. }
  805. /* For situations where we want to approximate the width in pixels for an SVG:text element.
  806. Most common instance is when the element is in a display:none; container.
  807. Forumla is : text.length * font-size * constant_factor
  808. */
  809. nv.utils.calcApproxTextWidth = function (svgTextElem) {
  810. if (typeof svgTextElem.style === 'function'
  811. && typeof svgTextElem.text === 'function') {
  812. var fontSize = parseInt(svgTextElem.style("font-size").replace("px",""));
  813. var textLength = svgTextElem.text().length;
  814. return textLength * fontSize * 0.5;
  815. }
  816. return 0;
  817. };
  818. /* Numbers that are undefined, null or NaN, convert them to zeros.
  819. */
  820. nv.utils.NaNtoZero = function(n) {
  821. if (typeof n !== 'number'
  822. || isNaN(n)
  823. || n === null
  824. || n === Infinity) return 0;
  825. return n;
  826. };
  827. /*
  828. Snippet of code you can insert into each nv.models.* to give you the ability to
  829. do things like:
  830. chart.options({
  831. showXAxis: true,
  832. tooltips: true
  833. });
  834. To enable in the chart:
  835. chart.options = nv.utils.optionsFunc.bind(chart);
  836. */
  837. nv.utils.optionsFunc = function(args) {
  838. if (args) {
  839. d3.map(args).forEach((function(key,value) {
  840. if (typeof this[key] === "function") {
  841. this[key](value);
  842. }
  843. }).bind(this));
  844. }
  845. return this;
  846. };nv.models.axis = function() {
  847. "use strict";
  848. //============================================================
  849. // Public Variables with Default Settings
  850. //------------------------------------------------------------
  851. var axis = d3.svg.axis()
  852. ;
  853. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  854. , width = 75 //only used for tickLabel currently
  855. , height = 60 //only used for tickLabel currently
  856. , scale = d3.scale.linear()
  857. , axisLabelText = null
  858. , showMaxMin = true //TODO: showMaxMin should be disabled on all ordinal scaled axes
  859. , highlightZero = true
  860. , rotateLabels = 0
  861. , rotateYLabel = true
  862. , staggerLabels = false
  863. , isOrdinal = false
  864. , ticks = null
  865. , axisLabelDistance = 12 //The larger this number is, the closer the axis label is to the axis.
  866. ;
  867. axis
  868. .scale(scale)
  869. .orient('bottom')
  870. .tickFormat(function(d) { return d })
  871. ;
  872. //============================================================
  873. //============================================================
  874. // Private Variables
  875. //------------------------------------------------------------
  876. var scale0;
  877. //============================================================
  878. function chart(selection) {
  879. selection.each(function(data) {
  880. var container = d3.select(this);
  881. //------------------------------------------------------------
  882. // Setup containers and skeleton of chart
  883. var wrap = container.selectAll('g.nv-wrap.nv-axis').data([data]);
  884. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-axis');
  885. var gEnter = wrapEnter.append('g');
  886. var g = wrap.select('g')
  887. //------------------------------------------------------------
  888. if (ticks !== null)
  889. axis.ticks(ticks);
  890. else if (axis.orient() == 'top' || axis.orient() == 'bottom')
  891. axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100);
  892. //TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component
  893. g.transition().call(axis);
  894. scale0 = scale0 || axis.scale();
  895. var fmt = axis.tickFormat();
  896. if (fmt == null) {
  897. fmt = scale0.tickFormat();
  898. }
  899. var axisLabel = g.selectAll('text.nv-axislabel')
  900. .data([axisLabelText || null]);
  901. axisLabel.exit().remove();
  902. switch (axis.orient()) {
  903. case 'top':
  904. axisLabel.enter().append('text').attr('class', 'nv-axislabel');
  905. var w = (scale.range().length==2) ? scale.range()[1] : (scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]));
  906. axisLabel
  907. .attr('text-anchor', 'middle')
  908. .attr('y', 0)
  909. .attr('x', nv.utils.NaNtoZero(w)/2);
  910. if (showMaxMin) {
  911. var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
  912. .data(scale.domain());
  913. axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text');
  914. axisMaxMin.exit().remove();
  915. axisMaxMin
  916. .attr('transform', function(d,i) {
  917. return 'translate(' + scale(d) + ',0)'
  918. })
  919. .select('text')
  920. .attr('dy', '-0.5em')
  921. .attr('y', -axis.tickPadding())
  922. .attr('text-anchor', 'middle')
  923. .text(function(d,i) {
  924. var v = fmt(d);
  925. return ('' + v).match('NaN') ? '' : v;
  926. });
  927. axisMaxMin.transition()
  928. .attr('transform', function(d,i) {
  929. return 'translate(' + scale.range()[i] + ',0)'
  930. });
  931. }
  932. break;
  933. case 'bottom':
  934. var xLabelMargin = 36;
  935. var maxTextWidth = 30;
  936. var xTicks = g.selectAll('g').select("text");
  937. if (rotateLabels%360) {
  938. //Calculate the longest xTick width
  939. xTicks.each(function(d,i){
  940. var width = this.getBBox().width;
  941. if(width > maxTextWidth) maxTextWidth = width;
  942. });
  943. //Convert to radians before calculating sin. Add 30 to margin for healthy padding.
  944. var sin = Math.abs(Math.sin(rotateLabels*Math.PI/180));
  945. var xLabelMargin = (sin ? sin*maxTextWidth : maxTextWidth)+30;
  946. //Rotate all xTicks
  947. xTicks
  948. .attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
  949. .style('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end');
  950. }
  951. axisLabel.enter().append('text').attr('class', 'nv-axislabel');
  952. var w = (scale.range().length==2) ? scale.range()[1] : (scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]));
  953. axisLabel
  954. .attr('text-anchor', 'middle')
  955. .attr('y', xLabelMargin)
  956. .attr('x', nv.utils.NaNtoZero(w)/2);
  957. if (showMaxMin) {
  958. //if (showMaxMin && !isOrdinal) {
  959. var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
  960. //.data(scale.domain())
  961. .data([scale.domain()[0], scale.domain()[scale.domain().length - 1]]);
  962. axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text');
  963. axisMaxMin.exit().remove();
  964. axisMaxMin
  965. .attr('transform', function(d,i) {
  966. return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)'
  967. })
  968. .select('text')
  969. .attr('dy', '.71em')
  970. .attr('y', axis.tickPadding())
  971. .attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
  972. .style('text-anchor', rotateLabels ? (rotateLabels%360 > 0 ? 'start' : 'end') : 'middle')
  973. .text(function(d,i) {
  974. var v = fmt(d);
  975. return ('' + v).match('NaN') ? '' : v;
  976. });
  977. axisMaxMin.transition()
  978. .attr('transform', function(d,i) {
  979. //return 'translate(' + scale.range()[i] + ',0)'
  980. //return 'translate(' + scale(d) + ',0)'
  981. return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)'
  982. });
  983. }
  984. if (staggerLabels)
  985. xTicks
  986. .attr('transform', function(d,i) { return 'translate(0,' + (i % 2 == 0 ? '0' : '12') + ')' });
  987. break;
  988. case 'right':
  989. axisLabel.enter().append('text').attr('class', 'nv-axislabel');
  990. axisLabel
  991. .style('text-anchor', rotateYLabel ? 'middle' : 'begin')
  992. .attr('transform', rotateYLabel ? 'rotate(90)' : '')
  993. .attr('y', rotateYLabel ? (-Math.max(margin.right,width) + 12) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
  994. .attr('x', rotateYLabel ? (scale.range()[0] / 2) : axis.tickPadding());
  995. if (showMaxMin) {
  996. var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
  997. .data(scale.domain());
  998. axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text')
  999. .style('opacity', 0);
  1000. axisMaxMin.exit().remove();
  1001. axisMaxMin
  1002. .attr('transform', function(d,i) {
  1003. return 'translate(0,' + scale(d) + ')'
  1004. })
  1005. .select('text')
  1006. .attr('dy', '.32em')
  1007. .attr('y', 0)
  1008. .attr('x', axis.tickPadding())
  1009. .style('text-anchor', 'start')
  1010. .text(function(d,i) {
  1011. var v = fmt(d);
  1012. return ('' + v).match('NaN') ? '' : v;
  1013. });
  1014. axisMaxMin.transition()
  1015. .attr('transform', function(d,i) {
  1016. return 'translate(0,' + scale.range()[i] + ')'
  1017. })
  1018. .select('text')
  1019. .style('opacity', 1);
  1020. }
  1021. break;
  1022. case 'left':
  1023. /*
  1024. //For dynamically placing the label. Can be used with dynamically-sized chart axis margins
  1025. var yTicks = g.selectAll('g').select("text");
  1026. yTicks.each(function(d,i){
  1027. var labelPadding = this.getBBox().width + axis.tickPadding() + 16;
  1028. if(labelPadding > width) width = labelPadding;
  1029. });
  1030. */
  1031. axisLabel.enter().append('text').attr('class', 'nv-axislabel');
  1032. axisLabel
  1033. .style('text-anchor', rotateYLabel ? 'middle' : 'end')
  1034. .attr('transform', rotateYLabel ? 'rotate(-90)' : '')
  1035. .attr('y', rotateYLabel ? (-Math.max(margin.left,width) + axisLabelDistance) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
  1036. .attr('x', rotateYLabel ? (-scale.range()[0] / 2) : -axis.tickPadding());
  1037. if (showMaxMin) {
  1038. var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
  1039. .data(scale.domain());
  1040. axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text')
  1041. .style('opacity', 0);
  1042. axisMaxMin.exit().remove();
  1043. axisMaxMin
  1044. .attr('transform', function(d,i) {
  1045. return 'translate(0,' + scale0(d) + ')'
  1046. })
  1047. .select('text')
  1048. .attr('dy', '.32em')
  1049. .attr('y', 0)
  1050. .attr('x', -axis.tickPadding())
  1051. .attr('text-anchor', 'end')
  1052. .text(function(d,i) {
  1053. var v = fmt(d);
  1054. return ('' + v).match('NaN') ? '' : v;
  1055. });
  1056. axisMaxMin.transition()
  1057. .attr('transform', function(d,i) {
  1058. return 'translate(0,' + scale.range()[i] + ')'
  1059. })
  1060. .select('text')
  1061. .style('opacity', 1);
  1062. }
  1063. break;
  1064. }
  1065. axisLabel
  1066. .text(function(d) { return d });
  1067. if (showMaxMin && (axis.orient() === 'left' || axis.orient() === 'right')) {
  1068. //check if max and min overlap other values, if so, hide the values that overlap
  1069. g.selectAll('g') // the g's wrapping each tick
  1070. .each(function(d,i) {
  1071. d3.select(this).select('text').attr('opacity', 1);
  1072. if (scale(d) < scale.range()[1] + 10 || scale(d) > scale.range()[0] - 10) { // 10 is assuming text height is 16... if d is 0, leave it!
  1073. if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
  1074. d3.select(this).attr('opacity', 0);
  1075. d3.select(this).select('text').attr('opacity', 0); // Don't remove the ZERO line!!
  1076. }
  1077. });
  1078. //if Max and Min = 0 only show min, Issue #281
  1079. if (scale.domain()[0] == scale.domain()[1] && scale.domain()[0] == 0)
  1080. wrap.selectAll('g.nv-axisMaxMin')
  1081. .style('opacity', function(d,i) { return !i ? 1 : 0 });
  1082. }
  1083. if (showMaxMin && (axis.orient() === 'top' || axis.orient() === 'bottom')) {
  1084. var maxMinRange = [];
  1085. wrap.selectAll('g.nv-axisMaxMin')
  1086. .each(function(d,i) {
  1087. try {
  1088. if (i) // i== 1, max position
  1089. maxMinRange.push(scale(d) - this.getBBox().width - 4) //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
  1090. else // i==0, min position
  1091. maxMinRange.push(scale(d) + this.getBBox().width + 4)
  1092. }catch (err) {
  1093. if (i) // i== 1, max position
  1094. maxMinRange.push(scale(d) - 4) //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
  1095. else // i==0, min position
  1096. maxMinRange.push(scale(d) + 4)
  1097. }
  1098. });
  1099. g.selectAll('g') // the g's wrapping each tick
  1100. .each(function(d,i) {
  1101. if (scale(d) < maxMinRange[0] || scale(d) > maxMinRange[1]) {
  1102. if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
  1103. d3.select(this).remove();
  1104. else
  1105. d3.select(this).select('text').remove(); // Don't remove the ZERO line!!
  1106. }
  1107. });
  1108. }
  1109. //highlight zero line ... Maybe should not be an option and should just be in CSS?
  1110. if (highlightZero)
  1111. g.selectAll('.tick')
  1112. .filter(function(d) { return !parseFloat(Math.round(d.__data__*100000)/1000000) && (d.__data__ !== undefined) }) //this is because sometimes the 0 tick is a very small fraction, TODO: think of cleaner technique
  1113. .classed('zero', true);
  1114. //store old scales for use in transitions on update
  1115. scale0 = scale.copy();
  1116. });
  1117. return chart;
  1118. }
  1119. //============================================================
  1120. // Expose Public Variables
  1121. //------------------------------------------------------------
  1122. // expose chart's sub-components
  1123. chart.axis = axis;
  1124. d3.rebind(chart, axis, 'orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat');
  1125. d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); //these are also accessible by chart.scale(), but added common ones directly for ease of use
  1126. chart.options = nv.utils.optionsFunc.bind(chart);
  1127. chart.margin = function(_) {
  1128. if(!arguments.length) return margin;
  1129. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  1130. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  1131. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  1132. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  1133. return chart;
  1134. }
  1135. chart.width = function(_) {
  1136. if (!arguments.length) return width;
  1137. width = _;
  1138. return chart;
  1139. };
  1140. chart.ticks = function(_) {
  1141. if (!arguments.length) return ticks;
  1142. ticks = _;
  1143. return chart;
  1144. };
  1145. chart.height = function(_) {
  1146. if (!arguments.length) return height;
  1147. height = _;
  1148. return chart;
  1149. };
  1150. chart.axisLabel = function(_) {
  1151. if (!arguments.length) return axisLabelText;
  1152. axisLabelText = _;
  1153. return chart;
  1154. }
  1155. chart.showMaxMin = function(_) {
  1156. if (!arguments.length) return showMaxMin;
  1157. showMaxMin = _;
  1158. return chart;
  1159. }
  1160. chart.highlightZero = function(_) {
  1161. if (!arguments.length) return highlightZero;
  1162. highlightZero = _;
  1163. return chart;
  1164. }
  1165. chart.scale = function(_) {
  1166. if (!arguments.length) return scale;
  1167. scale = _;
  1168. axis.scale(scale);
  1169. isOrdinal = typeof scale.rangeBands === 'function';
  1170. d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands');
  1171. return chart;
  1172. }
  1173. chart.rotateYLabel = function(_) {
  1174. if(!arguments.length) return rotateYLabel;
  1175. rotateYLabel = _;
  1176. return chart;
  1177. }
  1178. chart.rotateLabels = function(_) {
  1179. if(!arguments.length) return rotateLabels;
  1180. rotateLabels = _;
  1181. return chart;
  1182. }
  1183. chart.staggerLabels = function(_) {
  1184. if (!arguments.length) return staggerLabels;
  1185. staggerLabels = _;
  1186. return chart;
  1187. };
  1188. chart.axisLabelDistance = function(_) {
  1189. if (!arguments.length) return axisLabelDistance;
  1190. axisLabelDistance = _;
  1191. return chart;
  1192. };
  1193. //============================================================
  1194. return chart;
  1195. }
  1196. //TODO: consider deprecating and using multibar with single series for this
  1197. nv.models.historicalBar = function() {
  1198. "use strict";
  1199. //============================================================
  1200. // Public Variables with Default Settings
  1201. //------------------------------------------------------------
  1202. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  1203. , width = 960
  1204. , height = 500
  1205. , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
  1206. , x = d3.scale.linear()
  1207. , y = d3.scale.linear()
  1208. , getX = function(d) { return d.x }
  1209. , getY = function(d) { return d.y }
  1210. , forceX = []
  1211. , forceY = [0]
  1212. , padData = false
  1213. , clipEdge = true
  1214. , color = nv.utils.defaultColor()
  1215. , xDomain
  1216. , yDomain
  1217. , xRange
  1218. , yRange
  1219. , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
  1220. , interactive = true
  1221. ;
  1222. //============================================================
  1223. function chart(selection) {
  1224. selection.each(function(data) {
  1225. var availableWidth = width - margin.left - margin.right,
  1226. availableHeight = height - margin.top - margin.bottom,
  1227. container = d3.select(this);
  1228. //------------------------------------------------------------
  1229. // Setup Scales
  1230. x .domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ))
  1231. if (padData)
  1232. x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
  1233. else
  1234. x.range(xRange || [0, availableWidth]);
  1235. y .domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) ))
  1236. .range(yRange || [availableHeight, 0]);
  1237. // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
  1238. if (x.domain()[0] === x.domain()[1])
  1239. x.domain()[0] ?
  1240. x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
  1241. : x.domain([-1,1]);
  1242. if (y.domain()[0] === y.domain()[1])
  1243. y.domain()[0] ?
  1244. y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
  1245. : y.domain([-1,1]);
  1246. //------------------------------------------------------------
  1247. //------------------------------------------------------------
  1248. // Setup containers and skeleton of chart
  1249. var wrap = container.selectAll('g.nv-wrap.nv-historicalBar-' + id).data([data[0].values]);
  1250. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBar-' + id);
  1251. var defsEnter = wrapEnter.append('defs');
  1252. var gEnter = wrapEnter.append('g');
  1253. var g = wrap.select('g');
  1254. gEnter.append('g').attr('class', 'nv-bars');
  1255. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  1256. //------------------------------------------------------------
  1257. container
  1258. .on('click', function(d,i) {
  1259. dispatch.chartClick({
  1260. data: d,
  1261. index: i,
  1262. pos: d3.event,
  1263. id: id
  1264. });
  1265. });
  1266. defsEnter.append('clipPath')
  1267. .attr('id', 'nv-chart-clip-path-' + id)
  1268. .append('rect');
  1269. wrap.select('#nv-chart-clip-path-' + id + ' rect')
  1270. .attr('width', availableWidth)
  1271. .attr('height', availableHeight);
  1272. g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
  1273. var bars = wrap.select('.nv-bars').selectAll('.nv-bar')
  1274. .data(function(d) { return d }, function(d,i) {return getX(d,i)});
  1275. bars.exit().remove();
  1276. var barsEnter = bars.enter().append('rect')
  1277. //.attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i })
  1278. .attr('x', 0 )
  1279. .attr('y', function(d,i) { return nv.utils.NaNtoZero(y(Math.max(0, getY(d,i)))) })
  1280. .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.abs(y(getY(d,i)) - y(0))) })
  1281. .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; })
  1282. .on('mouseover', function(d,i) {
  1283. if (!interactive) return;
  1284. d3.select(this).classed('hover', true);
  1285. dispatch.elementMouseover({
  1286. point: d,
  1287. series: data[0],
  1288. pos: [x(getX(d,i)), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
  1289. pointIndex: i,
  1290. seriesIndex: 0,
  1291. e: d3.event
  1292. });
  1293. })
  1294. .on('mouseout', function(d,i) {
  1295. if (!interactive) return;
  1296. d3.select(this).classed('hover', false);
  1297. dispatch.elementMouseout({
  1298. point: d,
  1299. series: data[0],
  1300. pointIndex: i,
  1301. seriesIndex: 0,
  1302. e: d3.event
  1303. });
  1304. })
  1305. .on('click', function(d,i) {
  1306. if (!interactive) return;
  1307. dispatch.elementClick({
  1308. //label: d[label],
  1309. value: getY(d,i),
  1310. data: d,
  1311. index: i,
  1312. pos: [x(getX(d,i)), y(getY(d,i))],
  1313. e: d3.event,
  1314. id: id
  1315. });
  1316. d3.event.stopPropagation();
  1317. })
  1318. .on('dblclick', function(d,i) {
  1319. if (!interactive) return;
  1320. dispatch.elementDblClick({
  1321. //label: d[label],
  1322. value: getY(d,i),
  1323. data: d,
  1324. index: i,
  1325. pos: [x(getX(d,i)), y(getY(d,i))],
  1326. e: d3.event,
  1327. id: id
  1328. });
  1329. d3.event.stopPropagation();
  1330. });
  1331. bars
  1332. .attr('fill', function(d,i) { return color(d, i); })
  1333. .attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i })
  1334. .transition()
  1335. .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; })
  1336. //TODO: better width calculations that don't assume always uniform data spacing;w
  1337. .attr('width', (availableWidth / data[0].values.length) * .9 );
  1338. bars.transition()
  1339. .attr('y', function(d,i) {
  1340. var rval = getY(d,i) < 0 ?
  1341. y(0) :
  1342. y(0) - y(getY(d,i)) < 1 ?
  1343. y(0) - 1 :
  1344. y(getY(d,i));
  1345. return nv.utils.NaNtoZero(rval);
  1346. })
  1347. .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.max(Math.abs(y(getY(d,i)) - y(0)),1)) });
  1348. });
  1349. return chart;
  1350. }
  1351. //Create methods to allow outside functions to highlight a specific bar.
  1352. chart.highlightPoint = function(pointIndex, isHoverOver) {
  1353. d3.select(".nv-historicalBar-" + id)
  1354. .select(".nv-bars .nv-bar-0-" + pointIndex)
  1355. .classed("hover", isHoverOver)
  1356. ;
  1357. };
  1358. chart.clearHighlights = function() {
  1359. d3.select(".nv-historicalBar-" + id)
  1360. .select(".nv-bars .nv-bar.hover")
  1361. .classed("hover", false)
  1362. ;
  1363. };
  1364. //============================================================
  1365. // Expose Public Variables
  1366. //------------------------------------------------------------
  1367. chart.dispatch = dispatch;
  1368. chart.options = nv.utils.optionsFunc.bind(chart);
  1369. chart.x = function(_) {
  1370. if (!arguments.length) return getX;
  1371. getX = _;
  1372. return chart;
  1373. };
  1374. chart.y = function(_) {
  1375. if (!arguments.length) return getY;
  1376. getY = _;
  1377. return chart;
  1378. };
  1379. chart.margin = function(_) {
  1380. if (!arguments.length) return margin;
  1381. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  1382. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  1383. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  1384. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  1385. return chart;
  1386. };
  1387. chart.width = function(_) {
  1388. if (!arguments.length) return width;
  1389. width = _;
  1390. return chart;
  1391. };
  1392. chart.height = function(_) {
  1393. if (!arguments.length) return height;
  1394. height = _;
  1395. return chart;
  1396. };
  1397. chart.xScale = function(_) {
  1398. if (!arguments.length) return x;
  1399. x = _;
  1400. return chart;
  1401. };
  1402. chart.yScale = function(_) {
  1403. if (!arguments.length) return y;
  1404. y = _;
  1405. return chart;
  1406. };
  1407. chart.xDomain = function(_) {
  1408. if (!arguments.length) return xDomain;
  1409. xDomain = _;
  1410. return chart;
  1411. };
  1412. chart.yDomain = function(_) {
  1413. if (!arguments.length) return yDomain;
  1414. yDomain = _;
  1415. return chart;
  1416. };
  1417. chart.xRange = function(_) {
  1418. if (!arguments.length) return xRange;
  1419. xRange = _;
  1420. return chart;
  1421. };
  1422. chart.yRange = function(_) {
  1423. if (!arguments.length) return yRange;
  1424. yRange = _;
  1425. return chart;
  1426. };
  1427. chart.forceX = function(_) {
  1428. if (!arguments.length) return forceX;
  1429. forceX = _;
  1430. return chart;
  1431. };
  1432. chart.forceY = function(_) {
  1433. if (!arguments.length) return forceY;
  1434. forceY = _;
  1435. return chart;
  1436. };
  1437. chart.padData = function(_) {
  1438. if (!arguments.length) return padData;
  1439. padData = _;
  1440. return chart;
  1441. };
  1442. chart.clipEdge = function(_) {
  1443. if (!arguments.length) return clipEdge;
  1444. clipEdge = _;
  1445. return chart;
  1446. };
  1447. chart.color = function(_) {
  1448. if (!arguments.length) return color;
  1449. color = nv.utils.getColor(_);
  1450. return chart;
  1451. };
  1452. chart.id = function(_) {
  1453. if (!arguments.length) return id;
  1454. id = _;
  1455. return chart;
  1456. };
  1457. chart.interactive = function(_) {
  1458. if(!arguments.length) return interactive;
  1459. interactive = false;
  1460. return chart;
  1461. };
  1462. //============================================================
  1463. return chart;
  1464. }
  1465. // Chart design based on the recommendations of Stephen Few. Implementation
  1466. // based on the work of Clint Ivy, Jamie Love, and Jason Davies.
  1467. // http://projects.instantcognition.com/protovis/bulletchart/
  1468. nv.models.bullet = function() {
  1469. "use strict";
  1470. //============================================================
  1471. // Public Variables with Default Settings
  1472. //------------------------------------------------------------
  1473. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  1474. , orient = 'left' // TODO top & bottom
  1475. , reverse = false
  1476. , ranges = function(d) { return d.ranges }
  1477. , markers = function(d) { return d.markers }
  1478. , measures = function(d) { return d.measures }
  1479. , rangeLabels = function(d) { return d.rangeLabels ? d.rangeLabels : [] }
  1480. , markerLabels = function(d) { return d.markerLabels ? d.markerLabels : [] }
  1481. , measureLabels = function(d) { return d.measureLabels ? d.measureLabels : [] }
  1482. , forceX = [0] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
  1483. , width = 380
  1484. , height = 30
  1485. , tickFormat = null
  1486. , color = nv.utils.getColor(['#1f77b4'])
  1487. , dispatch = d3.dispatch('elementMouseover', 'elementMouseout')
  1488. ;
  1489. //============================================================
  1490. function chart(selection) {
  1491. selection.each(function(d, i) {
  1492. var availableWidth = width - margin.left - margin.right,
  1493. availableHeight = height - margin.top - margin.bottom,
  1494. container = d3.select(this);
  1495. var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
  1496. markerz = markers.call(this, d, i).slice().sort(d3.descending),
  1497. measurez = measures.call(this, d, i).slice().sort(d3.descending),
  1498. rangeLabelz = rangeLabels.call(this, d, i).slice(),
  1499. markerLabelz = markerLabels.call(this, d, i).slice(),
  1500. measureLabelz = measureLabels.call(this, d, i).slice();
  1501. //------------------------------------------------------------
  1502. // Setup Scales
  1503. // Compute the new x-scale.
  1504. var x1 = d3.scale.linear()
  1505. .domain( d3.extent(d3.merge([forceX, rangez])) )
  1506. .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
  1507. // Retrieve the old x-scale, if this is an update.
  1508. var x0 = this.__chart__ || d3.scale.linear()
  1509. .domain([0, Infinity])
  1510. .range(x1.range());
  1511. // Stash the new scale.
  1512. this.__chart__ = x1;
  1513. var rangeMin = d3.min(rangez), //rangez[2]
  1514. rangeMax = d3.max(rangez), //rangez[0]
  1515. rangeAvg = rangez[1];
  1516. //------------------------------------------------------------
  1517. //------------------------------------------------------------
  1518. // Setup containers and skeleton of chart
  1519. var wrap = container.selectAll('g.nv-wrap.nv-bullet').data([d]);
  1520. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bullet');
  1521. var gEnter = wrapEnter.append('g');
  1522. var g = wrap.select('g');
  1523. gEnter.append('rect').attr('class', 'nv-range nv-rangeMax');
  1524. gEnter.append('rect').attr('class', 'nv-range nv-rangeAvg');
  1525. gEnter.append('rect').attr('class', 'nv-range nv-rangeMin');
  1526. gEnter.append('rect').attr('class', 'nv-measure');
  1527. gEnter.append('path').attr('class', 'nv-markerTriangle');
  1528. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  1529. //------------------------------------------------------------
  1530. var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
  1531. w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
  1532. var xp0 = function(d) { return d < 0 ? x0(d) : x0(0) },
  1533. xp1 = function(d) { return d < 0 ? x1(d) : x1(0) };
  1534. g.select('rect.nv-rangeMax')
  1535. .attr('height', availableHeight)
  1536. .attr('width', w1(rangeMax > 0 ? rangeMax : rangeMin))
  1537. .attr('x', xp1(rangeMax > 0 ? rangeMax : rangeMin))
  1538. .datum(rangeMax > 0 ? rangeMax : rangeMin)
  1539. /*
  1540. .attr('x', rangeMin < 0 ?
  1541. rangeMax > 0 ?
  1542. x1(rangeMin)
  1543. : x1(rangeMax)
  1544. : x1(0))
  1545. */
  1546. g.select('rect.nv-rangeAvg')
  1547. .attr('height', availableHeight)
  1548. .attr('width', w1(rangeAvg))
  1549. .attr('x', xp1(rangeAvg))
  1550. .datum(rangeAvg)
  1551. /*
  1552. .attr('width', rangeMax <= 0 ?
  1553. x1(rangeMax) - x1(rangeAvg)
  1554. : x1(rangeAvg) - x1(rangeMin))
  1555. .attr('x', rangeMax <= 0 ?
  1556. x1(rangeAvg)
  1557. : x1(rangeMin))
  1558. */
  1559. g.select('rect.nv-rangeMin')
  1560. .attr('height', availableHeight)
  1561. .attr('width', w1(rangeMax))
  1562. .attr('x', xp1(rangeMax))
  1563. .attr('width', w1(rangeMax > 0 ? rangeMin : rangeMax))
  1564. .attr('x', xp1(rangeMax > 0 ? rangeMin : rangeMax))
  1565. .datum(rangeMax > 0 ? rangeMin : rangeMax)
  1566. /*
  1567. .attr('width', rangeMax <= 0 ?
  1568. x1(rangeAvg) - x1(rangeMin)
  1569. : x1(rangeMax) - x1(rangeAvg))
  1570. .attr('x', rangeMax <= 0 ?
  1571. x1(rangeMin)
  1572. : x1(rangeAvg))
  1573. */
  1574. g.select('rect.nv-measure')
  1575. .style('fill', color)
  1576. .attr('height', availableHeight / 3)
  1577. .attr('y', availableHeight / 3)
  1578. .attr('width', measurez < 0 ?
  1579. x1(0) - x1(measurez[0])
  1580. : x1(measurez[0]) - x1(0))
  1581. .attr('x', xp1(measurez))
  1582. .on('mouseover', function() {
  1583. dispatch.elementMouseover({
  1584. value: measurez[0],
  1585. label: measureLabelz[0] || 'Current',
  1586. pos: [x1(measurez[0]), availableHeight/2]
  1587. })
  1588. })
  1589. .on('mouseout', function() {
  1590. dispatch.elementMouseout({
  1591. value: measurez[0],
  1592. label: measureLabelz[0] || 'Current'
  1593. })
  1594. })
  1595. var h3 = availableHeight / 6;
  1596. if (markerz[0]) {
  1597. g.selectAll('path.nv-markerTriangle')
  1598. .attr('transform', function(d) { return 'translate(' + x1(markerz[0]) + ',' + (availableHeight / 2) + ')' })
  1599. .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
  1600. .on('mouseover', function() {
  1601. dispatch.elementMouseover({
  1602. value: markerz[0],
  1603. label: markerLabelz[0] || 'Previous',
  1604. pos: [x1(markerz[0]), availableHeight/2]
  1605. })
  1606. })
  1607. .on('mouseout', function() {
  1608. dispatch.elementMouseout({
  1609. value: markerz[0],
  1610. label: markerLabelz[0] || 'Previous'
  1611. })
  1612. });
  1613. } else {
  1614. g.selectAll('path.nv-markerTriangle').remove();
  1615. }
  1616. wrap.selectAll('.nv-range')
  1617. .on('mouseover', function(d,i) {
  1618. var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
  1619. dispatch.elementMouseover({
  1620. value: d,
  1621. label: label,
  1622. pos: [x1(d), availableHeight/2]
  1623. })
  1624. })
  1625. .on('mouseout', function(d,i) {
  1626. var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
  1627. dispatch.elementMouseout({
  1628. value: d,
  1629. label: label
  1630. })
  1631. })
  1632. /* // THIS IS THE PREVIOUS BULLET IMPLEMENTATION, WILL REMOVE SHORTLY
  1633. // Update the range rects.
  1634. var range = g.selectAll('rect.nv-range')
  1635. .data(rangez);
  1636. range.enter().append('rect')
  1637. .attr('class', function(d, i) { return 'nv-range nv-s' + i; })
  1638. .attr('width', w0)
  1639. .attr('height', availableHeight)
  1640. .attr('x', reverse ? x0 : 0)
  1641. .on('mouseover', function(d,i) {
  1642. dispatch.elementMouseover({
  1643. value: d,
  1644. label: (i <= 0) ? 'Maximum' : (i > 1) ? 'Minimum' : 'Mean', //TODO: make these labels a variable
  1645. pos: [x1(d), availableHeight/2]
  1646. })
  1647. })
  1648. .on('mouseout', function(d,i) {
  1649. dispatch.elementMouseout({
  1650. value: d,
  1651. label: (i <= 0) ? 'Minimum' : (i >=1) ? 'Maximum' : 'Mean' //TODO: make these labels a variable
  1652. })
  1653. })
  1654. d3.transition(range)
  1655. .attr('x', reverse ? x1 : 0)
  1656. .attr('width', w1)
  1657. .attr('height', availableHeight);
  1658. // Update the measure rects.
  1659. var measure = g.selectAll('rect.nv-measure')
  1660. .data(measurez);
  1661. measure.enter().append('rect')
  1662. .attr('class', function(d, i) { return 'nv-measure nv-s' + i; })
  1663. .style('fill', function(d,i) { return color(d,i ) })
  1664. .attr('width', w0)
  1665. .attr('height', availableHeight / 3)
  1666. .attr('x', reverse ? x0 : 0)
  1667. .attr('y', availableHeight / 3)
  1668. .on('mouseover', function(d) {
  1669. dispatch.elementMouseover({
  1670. value: d,
  1671. label: 'Current', //TODO: make these labels a variable
  1672. pos: [x1(d), availableHeight/2]
  1673. })
  1674. })
  1675. .on('mouseout', function(d) {
  1676. dispatch.elementMouseout({
  1677. value: d,
  1678. label: 'Current' //TODO: make these labels a variable
  1679. })
  1680. })
  1681. d3.transition(measure)
  1682. .attr('width', w1)
  1683. .attr('height', availableHeight / 3)
  1684. .attr('x', reverse ? x1 : 0)
  1685. .attr('y', availableHeight / 3);
  1686. // Update the marker lines.
  1687. var marker = g.selectAll('path.nv-markerTriangle')
  1688. .data(markerz);
  1689. var h3 = availableHeight / 6;
  1690. marker.enter().append('path')
  1691. .attr('class', 'nv-markerTriangle')
  1692. .attr('transform', function(d) { return 'translate(' + x0(d) + ',' + (availableHeight / 2) + ')' })
  1693. .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
  1694. .on('mouseover', function(d,i) {
  1695. dispatch.elementMouseover({
  1696. value: d,
  1697. label: 'Previous',
  1698. pos: [x1(d), availableHeight/2]
  1699. })
  1700. })
  1701. .on('mouseout', function(d,i) {
  1702. dispatch.elementMouseout({
  1703. value: d,
  1704. label: 'Previous'
  1705. })
  1706. });
  1707. d3.transition(marker)
  1708. .attr('transform', function(d) { return 'translate(' + (x1(d) - x1(0)) + ',' + (availableHeight / 2) + ')' });
  1709. marker.exit().remove();
  1710. */
  1711. });
  1712. // d3.timer.flush(); // Not needed?
  1713. return chart;
  1714. }
  1715. //============================================================
  1716. // Expose Public Variables
  1717. //------------------------------------------------------------
  1718. chart.dispatch = dispatch;
  1719. chart.options = nv.utils.optionsFunc.bind(chart);
  1720. // left, right, top, bottom
  1721. chart.orient = function(_) {
  1722. if (!arguments.length) return orient;
  1723. orient = _;
  1724. reverse = orient == 'right' || orient == 'bottom';
  1725. return chart;
  1726. };
  1727. // ranges (bad, satisfactory, good)
  1728. chart.ranges = function(_) {
  1729. if (!arguments.length) return ranges;
  1730. ranges = _;
  1731. return chart;
  1732. };
  1733. // markers (previous, goal)
  1734. chart.markers = function(_) {
  1735. if (!arguments.length) return markers;
  1736. markers = _;
  1737. return chart;
  1738. };
  1739. // measures (actual, forecast)
  1740. chart.measures = function(_) {
  1741. if (!arguments.length) return measures;
  1742. measures = _;
  1743. return chart;
  1744. };
  1745. chart.forceX = function(_) {
  1746. if (!arguments.length) return forceX;
  1747. forceX = _;
  1748. return chart;
  1749. };
  1750. chart.width = function(_) {
  1751. if (!arguments.length) return width;
  1752. width = _;
  1753. return chart;
  1754. };
  1755. chart.height = function(_) {
  1756. if (!arguments.length) return height;
  1757. height = _;
  1758. return chart;
  1759. };
  1760. chart.margin = function(_) {
  1761. if (!arguments.length) return margin;
  1762. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  1763. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  1764. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  1765. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  1766. return chart;
  1767. };
  1768. chart.tickFormat = function(_) {
  1769. if (!arguments.length) return tickFormat;
  1770. tickFormat = _;
  1771. return chart;
  1772. };
  1773. chart.color = function(_) {
  1774. if (!arguments.length) return color;
  1775. color = nv.utils.getColor(_);
  1776. return chart;
  1777. };
  1778. //============================================================
  1779. return chart;
  1780. };
  1781. // Chart design based on the recommendations of Stephen Few. Implementation
  1782. // based on the work of Clint Ivy, Jamie Love, and Jason Davies.
  1783. // http://projects.instantcognition.com/protovis/bulletchart/
  1784. nv.models.bulletChart = function() {
  1785. "use strict";
  1786. //============================================================
  1787. // Public Variables with Default Settings
  1788. //------------------------------------------------------------
  1789. var bullet = nv.models.bullet()
  1790. ;
  1791. var orient = 'left' // TODO top & bottom
  1792. , reverse = false
  1793. , margin = {top: 5, right: 40, bottom: 20, left: 120}
  1794. , ranges = function(d) { return d.ranges }
  1795. , markers = function(d) { return d.markers }
  1796. , measures = function(d) { return d.measures }
  1797. , width = null
  1798. , height = 55
  1799. , tickFormat = null
  1800. , tooltips = true
  1801. , tooltip = function(key, x, y, e, graph) {
  1802. return '<h3>' + x + '</h3>' +
  1803. '<p>' + y + '</p>'
  1804. }
  1805. , noData = 'No Data Available.'
  1806. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
  1807. ;
  1808. //============================================================
  1809. //============================================================
  1810. // Private Variables
  1811. //------------------------------------------------------------
  1812. var showTooltip = function(e, offsetElement) {
  1813. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ) + margin.left,
  1814. top = e.pos[1] + ( offsetElement.offsetTop || 0) + margin.top,
  1815. content = tooltip(e.key, e.label, e.value, e, chart);
  1816. nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement);
  1817. };
  1818. //============================================================
  1819. function chart(selection) {
  1820. selection.each(function(d, i) {
  1821. var container = d3.select(this);
  1822. var availableWidth = (width || parseInt(container.style('width')) || 960)
  1823. - margin.left - margin.right,
  1824. availableHeight = height - margin.top - margin.bottom,
  1825. that = this;
  1826. chart.update = function() { chart(selection) };
  1827. chart.container = this;
  1828. //------------------------------------------------------------
  1829. // Display No Data message if there's nothing to show.
  1830. if (!d || !ranges.call(this, d, i)) {
  1831. var noDataText = container.selectAll('.nv-noData').data([noData]);
  1832. noDataText.enter().append('text')
  1833. .attr('class', 'nvd3 nv-noData')
  1834. .attr('dy', '-.7em')
  1835. .style('text-anchor', 'middle');
  1836. noDataText
  1837. .attr('x', margin.left + availableWidth / 2)
  1838. .attr('y', 18 + margin.top + availableHeight / 2)
  1839. .text(function(d) { return d });
  1840. return chart;
  1841. } else {
  1842. container.selectAll('.nv-noData').remove();
  1843. }
  1844. //------------------------------------------------------------
  1845. var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
  1846. markerz = markers.call(this, d, i).slice().sort(d3.descending),
  1847. measurez = measures.call(this, d, i).slice().sort(d3.descending);
  1848. //------------------------------------------------------------
  1849. // Setup containers and skeleton of chart
  1850. var wrap = container.selectAll('g.nv-wrap.nv-bulletChart').data([d]);
  1851. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bulletChart');
  1852. var gEnter = wrapEnter.append('g');
  1853. var g = wrap.select('g');
  1854. gEnter.append('g').attr('class', 'nv-bulletWrap');
  1855. gEnter.append('g').attr('class', 'nv-titles');
  1856. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  1857. //------------------------------------------------------------
  1858. // Compute the new x-scale.
  1859. var x1 = d3.scale.linear()
  1860. .domain([0, Math.max(rangez[0], markerz[0], measurez[0])]) // TODO: need to allow forceX and forceY, and xDomain, yDomain
  1861. .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
  1862. // Retrieve the old x-scale, if this is an update.
  1863. var x0 = this.__chart__ || d3.scale.linear()
  1864. .domain([0, Infinity])
  1865. .range(x1.range());
  1866. // Stash the new scale.
  1867. this.__chart__ = x1;
  1868. /*
  1869. // Derive width-scales from the x-scales.
  1870. var w0 = bulletWidth(x0),
  1871. w1 = bulletWidth(x1);
  1872. function bulletWidth(x) {
  1873. var x0 = x(0);
  1874. return function(d) {
  1875. return Math.abs(x(d) - x(0));
  1876. };
  1877. }
  1878. function bulletTranslate(x) {
  1879. return function(d) {
  1880. return 'translate(' + x(d) + ',0)';
  1881. };
  1882. }
  1883. */
  1884. var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
  1885. w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
  1886. var title = gEnter.select('.nv-titles').append('g')
  1887. .attr('text-anchor', 'end')
  1888. .attr('transform', 'translate(-6,' + (height - margin.top - margin.bottom) / 2 + ')');
  1889. title.append('text')
  1890. .attr('class', 'nv-title')
  1891. .text(function(d) { return d.title; });
  1892. title.append('text')
  1893. .attr('class', 'nv-subtitle')
  1894. .attr('dy', '1em')
  1895. .text(function(d) { return d.subtitle; });
  1896. bullet
  1897. .width(availableWidth)
  1898. .height(availableHeight)
  1899. var bulletWrap = g.select('.nv-bulletWrap');
  1900. d3.transition(bulletWrap).call(bullet);
  1901. // Compute the tick format.
  1902. var format = tickFormat || x1.tickFormat( availableWidth / 100 );
  1903. // Update the tick groups.
  1904. var tick = g.selectAll('g.nv-tick')
  1905. .data(x1.ticks( availableWidth / 50 ), function(d) {
  1906. return this.textContent || format(d);
  1907. });
  1908. // Initialize the ticks with the old scale, x0.
  1909. var tickEnter = tick.enter().append('g')
  1910. .attr('class', 'nv-tick')
  1911. .attr('transform', function(d) { return 'translate(' + x0(d) + ',0)' })
  1912. .style('opacity', 1e-6);
  1913. tickEnter.append('line')
  1914. .attr('y1', availableHeight)
  1915. .attr('y2', availableHeight * 7 / 6);
  1916. tickEnter.append('text')
  1917. .attr('text-anchor', 'middle')
  1918. .attr('dy', '1em')
  1919. .attr('y', availableHeight * 7 / 6)
  1920. .text(format);
  1921. // Transition the updating ticks to the new scale, x1.
  1922. var tickUpdate = d3.transition(tick)
  1923. .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
  1924. .style('opacity', 1);
  1925. tickUpdate.select('line')
  1926. .attr('y1', availableHeight)
  1927. .attr('y2', availableHeight * 7 / 6);
  1928. tickUpdate.select('text')
  1929. .attr('y', availableHeight * 7 / 6);
  1930. // Transition the exiting ticks to the new scale, x1.
  1931. d3.transition(tick.exit())
  1932. .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
  1933. .style('opacity', 1e-6)
  1934. .remove();
  1935. //============================================================
  1936. // Event Handling/Dispatching (in chart's scope)
  1937. //------------------------------------------------------------
  1938. dispatch.on('tooltipShow', function(e) {
  1939. e.key = d.title;
  1940. if (tooltips) showTooltip(e, that.parentNode);
  1941. });
  1942. //============================================================
  1943. });
  1944. d3.timer.flush();
  1945. return chart;
  1946. }
  1947. //============================================================
  1948. // Event Handling/Dispatching (out of chart's scope)
  1949. //------------------------------------------------------------
  1950. bullet.dispatch.on('elementMouseover.tooltip', function(e) {
  1951. dispatch.tooltipShow(e);
  1952. });
  1953. bullet.dispatch.on('elementMouseout.tooltip', function(e) {
  1954. dispatch.tooltipHide(e);
  1955. });
  1956. dispatch.on('tooltipHide', function() {
  1957. if (tooltips) nv.tooltip.cleanup();
  1958. });
  1959. //============================================================
  1960. //============================================================
  1961. // Expose Public Variables
  1962. //------------------------------------------------------------
  1963. chart.dispatch = dispatch;
  1964. chart.bullet = bullet;
  1965. d3.rebind(chart, bullet, 'color');
  1966. chart.options = nv.utils.optionsFunc.bind(chart);
  1967. // left, right, top, bottom
  1968. chart.orient = function(x) {
  1969. if (!arguments.length) return orient;
  1970. orient = x;
  1971. reverse = orient == 'right' || orient == 'bottom';
  1972. return chart;
  1973. };
  1974. // ranges (bad, satisfactory, good)
  1975. chart.ranges = function(x) {
  1976. if (!arguments.length) return ranges;
  1977. ranges = x;
  1978. return chart;
  1979. };
  1980. // markers (previous, goal)
  1981. chart.markers = function(x) {
  1982. if (!arguments.length) return markers;
  1983. markers = x;
  1984. return chart;
  1985. };
  1986. // measures (actual, forecast)
  1987. chart.measures = function(x) {
  1988. if (!arguments.length) return measures;
  1989. measures = x;
  1990. return chart;
  1991. };
  1992. chart.width = function(x) {
  1993. if (!arguments.length) return width;
  1994. width = x;
  1995. return chart;
  1996. };
  1997. chart.height = function(x) {
  1998. if (!arguments.length) return height;
  1999. height = x;
  2000. return chart;
  2001. };
  2002. chart.margin = function(_) {
  2003. if (!arguments.length) return margin;
  2004. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  2005. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  2006. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  2007. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  2008. return chart;
  2009. };
  2010. chart.tickFormat = function(x) {
  2011. if (!arguments.length) return tickFormat;
  2012. tickFormat = x;
  2013. return chart;
  2014. };
  2015. chart.tooltips = function(_) {
  2016. if (!arguments.length) return tooltips;
  2017. tooltips = _;
  2018. return chart;
  2019. };
  2020. chart.tooltipContent = function(_) {
  2021. if (!arguments.length) return tooltip;
  2022. tooltip = _;
  2023. return chart;
  2024. };
  2025. chart.noData = function(_) {
  2026. if (!arguments.length) return noData;
  2027. noData = _;
  2028. return chart;
  2029. };
  2030. //============================================================
  2031. return chart;
  2032. };
  2033. nv.models.cumulativeLineChart = function() {
  2034. "use strict";
  2035. //============================================================
  2036. // Public Variables with Default Settings
  2037. //------------------------------------------------------------
  2038. var lines = nv.models.line()
  2039. , xAxis = nv.models.axis()
  2040. , yAxis = nv.models.axis()
  2041. , legend = nv.models.legend()
  2042. , controls = nv.models.legend()
  2043. , interactiveLayer = nv.interactiveGuideline()
  2044. ;
  2045. var margin = {top: 30, right: 30, bottom: 50, left: 60}
  2046. , color = nv.utils.defaultColor()
  2047. , width = null
  2048. , height = null
  2049. , showLegend = true
  2050. , showXAxis = true
  2051. , showYAxis = true
  2052. , rightAlignYAxis = false
  2053. , tooltips = true
  2054. , showControls = true
  2055. , useInteractiveGuideline = false
  2056. , rescaleY = true
  2057. , tooltip = function(key, x, y, e, graph) {
  2058. return '<h3>' + key + '</h3>' +
  2059. '<p>' + y + ' at ' + x + '</p>'
  2060. }
  2061. , x //can be accessed via chart.xScale()
  2062. , y //can be accessed via chart.yScale()
  2063. , id = lines.id()
  2064. , state = { index: 0, rescaleY: rescaleY }
  2065. , defaultState = null
  2066. , noData = 'No Data Available.'
  2067. , average = function(d) { return d.average }
  2068. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
  2069. , transitionDuration = 250
  2070. , noErrorCheck = false //if set to TRUE, will bypass an error check in the indexify function.
  2071. ;
  2072. xAxis
  2073. .orient('bottom')
  2074. .tickPadding(7)
  2075. ;
  2076. yAxis
  2077. .orient((rightAlignYAxis) ? 'right' : 'left')
  2078. ;
  2079. //============================================================
  2080. controls.updateState(false);
  2081. //============================================================
  2082. // Private Variables
  2083. //------------------------------------------------------------
  2084. var dx = d3.scale.linear()
  2085. , index = {i: 0, x: 0}
  2086. ;
  2087. var showTooltip = function(e, offsetElement) {
  2088. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  2089. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  2090. x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
  2091. y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
  2092. content = tooltip(e.series.key, x, y, e, chart);
  2093. nv.tooltip.show([left, top], content, null, null, offsetElement);
  2094. };
  2095. //============================================================
  2096. function chart(selection) {
  2097. selection.each(function(data) {
  2098. var container = d3.select(this).classed('nv-chart-' + id, true),
  2099. that = this;
  2100. var availableWidth = (width || parseInt(container.style('width')) || 960)
  2101. - margin.left - margin.right,
  2102. availableHeight = (height || parseInt(container.style('height')) || 400)
  2103. - margin.top - margin.bottom;
  2104. chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
  2105. chart.container = this;
  2106. //set state.disabled
  2107. state.disabled = data.map(function(d) { return !!d.disabled });
  2108. if (!defaultState) {
  2109. var key;
  2110. defaultState = {};
  2111. for (key in state) {
  2112. if (state[key] instanceof Array)
  2113. defaultState[key] = state[key].slice(0);
  2114. else
  2115. defaultState[key] = state[key];
  2116. }
  2117. }
  2118. var indexDrag = d3.behavior.drag()
  2119. .on('dragstart', dragStart)
  2120. .on('drag', dragMove)
  2121. .on('dragend', dragEnd);
  2122. function dragStart(d,i) {
  2123. d3.select(chart.container)
  2124. .style('cursor', 'ew-resize');
  2125. }
  2126. function dragMove(d,i) {
  2127. index.x = d3.event.x;
  2128. index.i = Math.round(dx.invert(index.x));
  2129. updateZero();
  2130. }
  2131. function dragEnd(d,i) {
  2132. d3.select(chart.container)
  2133. .style('cursor', 'auto');
  2134. // update state and send stateChange with new index
  2135. state.index = index.i;
  2136. dispatch.stateChange(state);
  2137. }
  2138. //------------------------------------------------------------
  2139. // Display No Data message if there's nothing to show.
  2140. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  2141. var noDataText = container.selectAll('.nv-noData').data([noData]);
  2142. noDataText.enter().append('text')
  2143. .attr('class', 'nvd3 nv-noData')
  2144. .attr('dy', '-.7em')
  2145. .style('text-anchor', 'middle');
  2146. noDataText
  2147. .attr('x', margin.left + availableWidth / 2)
  2148. .attr('y', margin.top + availableHeight / 2)
  2149. .text(function(d) { return d });
  2150. return chart;
  2151. } else {
  2152. container.selectAll('.nv-noData').remove();
  2153. }
  2154. //------------------------------------------------------------
  2155. //------------------------------------------------------------
  2156. // Setup Scales
  2157. x = lines.xScale();
  2158. y = lines.yScale();
  2159. if (!rescaleY) {
  2160. var seriesDomains = data
  2161. .filter(function(series) { return !series.disabled })
  2162. .map(function(series,i) {
  2163. var initialDomain = d3.extent(series.values, lines.y());
  2164. //account for series being disabled when losing 95% or more
  2165. if (initialDomain[0] < -.95) initialDomain[0] = -.95;
  2166. return [
  2167. (initialDomain[0] - initialDomain[1]) / (1 + initialDomain[1]),
  2168. (initialDomain[1] - initialDomain[0]) / (1 + initialDomain[0])
  2169. ];
  2170. });
  2171. var completeDomain = [
  2172. d3.min(seriesDomains, function(d) { return d[0] }),
  2173. d3.max(seriesDomains, function(d) { return d[1] })
  2174. ]
  2175. lines.yDomain(completeDomain);
  2176. } else {
  2177. lines.yDomain(null);
  2178. }
  2179. dx .domain([0, data[0].values.length - 1]) //Assumes all series have same length
  2180. .range([0, availableWidth])
  2181. .clamp(true);
  2182. //------------------------------------------------------------
  2183. var data = indexify(index.i, data);
  2184. //------------------------------------------------------------
  2185. // Setup containers and skeleton of chart
  2186. var interactivePointerEvents = (useInteractiveGuideline) ? "none" : "all";
  2187. var wrap = container.selectAll('g.nv-wrap.nv-cumulativeLine').data([data]);
  2188. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-cumulativeLine').append('g');
  2189. var g = wrap.select('g');
  2190. gEnter.append('g').attr('class', 'nv-interactive');
  2191. gEnter.append('g').attr('class', 'nv-x nv-axis').style("pointer-events","none");
  2192. gEnter.append('g').attr('class', 'nv-y nv-axis');
  2193. gEnter.append('g').attr('class', 'nv-background');
  2194. gEnter.append('g').attr('class', 'nv-linesWrap').style("pointer-events",interactivePointerEvents);
  2195. gEnter.append('g').attr('class', 'nv-avgLinesWrap').style("pointer-events","none");
  2196. gEnter.append('g').attr('class', 'nv-legendWrap');
  2197. gEnter.append('g').attr('class', 'nv-controlsWrap');
  2198. //------------------------------------------------------------
  2199. // Legend
  2200. if (showLegend) {
  2201. legend.width(availableWidth);
  2202. g.select('.nv-legendWrap')
  2203. .datum(data)
  2204. .call(legend);
  2205. if ( margin.top != legend.height()) {
  2206. margin.top = legend.height();
  2207. availableHeight = (height || parseInt(container.style('height')) || 400)
  2208. - margin.top - margin.bottom;
  2209. }
  2210. g.select('.nv-legendWrap')
  2211. .attr('transform', 'translate(0,' + (-margin.top) +')')
  2212. }
  2213. //------------------------------------------------------------
  2214. //------------------------------------------------------------
  2215. // Controls
  2216. if (showControls) {
  2217. var controlsData = [
  2218. { key: 'Re-scale y-axis', disabled: !rescaleY }
  2219. ];
  2220. controls
  2221. .width(140)
  2222. .color(['#444', '#444', '#444'])
  2223. .rightAlign(false)
  2224. .margin({top: 5, right: 0, bottom: 5, left: 20})
  2225. ;
  2226. g.select('.nv-controlsWrap')
  2227. .datum(controlsData)
  2228. .attr('transform', 'translate(0,' + (-margin.top) +')')
  2229. .call(controls);
  2230. }
  2231. //------------------------------------------------------------
  2232. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  2233. if (rightAlignYAxis) {
  2234. g.select(".nv-y.nv-axis")
  2235. .attr("transform", "translate(" + availableWidth + ",0)");
  2236. }
  2237. // Show error if series goes below 100%
  2238. var tempDisabled = data.filter(function(d) { return d.tempDisabled });
  2239. wrap.select('.tempDisabled').remove(); //clean-up and prevent duplicates
  2240. if (tempDisabled.length) {
  2241. wrap.append('text').attr('class', 'tempDisabled')
  2242. .attr('x', availableWidth / 2)
  2243. .attr('y', '-.71em')
  2244. .style('text-anchor', 'end')
  2245. .text(tempDisabled.map(function(d) { return d.key }).join(', ') + ' values cannot be calculated for this time period.');
  2246. }
  2247. //------------------------------------------------------------
  2248. // Main Chart Component(s)
  2249. //------------------------------------------------------------
  2250. //Set up interactive layer
  2251. if (useInteractiveGuideline) {
  2252. interactiveLayer
  2253. .width(availableWidth)
  2254. .height(availableHeight)
  2255. .margin({left:margin.left,top:margin.top})
  2256. .svgContainer(container)
  2257. .xScale(x);
  2258. wrap.select(".nv-interactive").call(interactiveLayer);
  2259. }
  2260. gEnter.select('.nv-background')
  2261. .append('rect');
  2262. g.select('.nv-background rect')
  2263. .attr('width', availableWidth)
  2264. .attr('height', availableHeight);
  2265. lines
  2266. //.x(function(d) { return d.x })
  2267. .y(function(d) { return d.display.y })
  2268. .width(availableWidth)
  2269. .height(availableHeight)
  2270. .color(data.map(function(d,i) {
  2271. return d.color || color(d, i);
  2272. }).filter(function(d,i) { return !data[i].disabled && !data[i].tempDisabled; }));
  2273. var linesWrap = g.select('.nv-linesWrap')
  2274. .datum(data.filter(function(d) { return !d.disabled && !d.tempDisabled }));
  2275. //d3.transition(linesWrap).call(lines);
  2276. linesWrap.call(lines);
  2277. /*Handle average lines [AN-612] ----------------------------*/
  2278. //Store a series index number in the data array.
  2279. data.forEach(function(d,i) {
  2280. d.seriesIndex = i;
  2281. });
  2282. var avgLineData = data.filter(function(d) {
  2283. return !d.disabled && !!average(d);
  2284. });
  2285. var avgLines = g.select(".nv-avgLinesWrap").selectAll("line")
  2286. .data(avgLineData, function(d) { return d.key; });
  2287. var getAvgLineY = function(d) {
  2288. //If average lines go off the svg element, clamp them to the svg bounds.
  2289. var yVal = y(average(d));
  2290. if (yVal < 0) return 0;
  2291. if (yVal > availableHeight) return availableHeight;
  2292. return yVal;
  2293. };
  2294. avgLines.enter()
  2295. .append('line')
  2296. .style('stroke-width',2)
  2297. .style('stroke-dasharray','10,10')
  2298. .style('stroke',function (d,i) {
  2299. return lines.color()(d,d.seriesIndex);
  2300. })
  2301. .attr('x1',0)
  2302. .attr('x2',availableWidth)
  2303. .attr('y1', getAvgLineY)
  2304. .attr('y2', getAvgLineY);
  2305. avgLines
  2306. .style('stroke-opacity',function(d){
  2307. //If average lines go offscreen, make them transparent
  2308. var yVal = y(average(d));
  2309. if (yVal < 0 || yVal > availableHeight) return 0;
  2310. return 1;
  2311. })
  2312. .attr('x1',0)
  2313. .attr('x2',availableWidth)
  2314. .attr('y1', getAvgLineY)
  2315. .attr('y2', getAvgLineY);
  2316. avgLines.exit().remove();
  2317. //Create index line -----------------------------------------
  2318. var indexLine = linesWrap.selectAll('.nv-indexLine')
  2319. .data([index]);
  2320. indexLine.enter().append('rect').attr('class', 'nv-indexLine')
  2321. .attr('width', 3)
  2322. .attr('x', -2)
  2323. .attr('fill', 'red')
  2324. .attr('fill-opacity', .5)
  2325. .style("pointer-events","all")
  2326. .call(indexDrag)
  2327. indexLine
  2328. .attr('transform', function(d) { return 'translate(' + dx(d.i) + ',0)' })
  2329. .attr('height', availableHeight)
  2330. //------------------------------------------------------------
  2331. //------------------------------------------------------------
  2332. // Setup Axes
  2333. if (showXAxis) {
  2334. xAxis
  2335. .scale(x)
  2336. //Suggest how many ticks based on the chart width and D3 should listen (70 is the optimal number for MM/DD/YY dates)
  2337. .ticks( Math.min(data[0].values.length,availableWidth/70) )
  2338. .tickSize(-availableHeight, 0);
  2339. g.select('.nv-x.nv-axis')
  2340. .attr('transform', 'translate(0,' + y.range()[0] + ')');
  2341. d3.transition(g.select('.nv-x.nv-axis'))
  2342. .call(xAxis);
  2343. }
  2344. if (showYAxis) {
  2345. yAxis
  2346. .scale(y)
  2347. .ticks( availableHeight / 36 )
  2348. .tickSize( -availableWidth, 0);
  2349. d3.transition(g.select('.nv-y.nv-axis'))
  2350. .call(yAxis);
  2351. }
  2352. //------------------------------------------------------------
  2353. //============================================================
  2354. // Event Handling/Dispatching (in chart's scope)
  2355. //------------------------------------------------------------
  2356. function updateZero() {
  2357. indexLine
  2358. .data([index]);
  2359. //When dragging the index line, turn off line transitions.
  2360. // Then turn them back on when done dragging.
  2361. var oldDuration = chart.transitionDuration();
  2362. chart.transitionDuration(0);
  2363. chart.update();
  2364. chart.transitionDuration(oldDuration);
  2365. }
  2366. g.select('.nv-background rect')
  2367. .on('click', function() {
  2368. index.x = d3.mouse(this)[0];
  2369. index.i = Math.round(dx.invert(index.x));
  2370. // update state and send stateChange with new index
  2371. state.index = index.i;
  2372. dispatch.stateChange(state);
  2373. updateZero();
  2374. });
  2375. lines.dispatch.on('elementClick', function(e) {
  2376. index.i = e.pointIndex;
  2377. index.x = dx(index.i);
  2378. // update state and send stateChange with new index
  2379. state.index = index.i;
  2380. dispatch.stateChange(state);
  2381. updateZero();
  2382. });
  2383. controls.dispatch.on('legendClick', function(d,i) {
  2384. d.disabled = !d.disabled;
  2385. rescaleY = !d.disabled;
  2386. state.rescaleY = rescaleY;
  2387. dispatch.stateChange(state);
  2388. chart.update();
  2389. });
  2390. legend.dispatch.on('stateChange', function(newState) {
  2391. state.disabled = newState.disabled;
  2392. dispatch.stateChange(state);
  2393. chart.update();
  2394. });
  2395. interactiveLayer.dispatch.on('elementMousemove', function(e) {
  2396. lines.clearHighlights();
  2397. var singlePoint, pointIndex, pointXLocation, allData = [];
  2398. data
  2399. .filter(function(series, i) {
  2400. series.seriesIndex = i;
  2401. return !series.disabled;
  2402. })
  2403. .forEach(function(series,i) {
  2404. pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
  2405. lines.highlightPoint(i, pointIndex, true);
  2406. var point = series.values[pointIndex];
  2407. if (typeof point === 'undefined') return;
  2408. if (typeof singlePoint === 'undefined') singlePoint = point;
  2409. if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
  2410. allData.push({
  2411. key: series.key,
  2412. value: chart.y()(point, pointIndex),
  2413. color: color(series,series.seriesIndex)
  2414. });
  2415. });
  2416. //Highlight the tooltip entry based on which point the mouse is closest to.
  2417. if (allData.length > 2) {
  2418. var yValue = chart.yScale().invert(e.mouseY);
  2419. var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
  2420. var threshold = 0.03 * domainExtent;
  2421. var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold);
  2422. if (indexToHighlight !== null)
  2423. allData[indexToHighlight].highlight = true;
  2424. }
  2425. var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex), pointIndex);
  2426. interactiveLayer.tooltip
  2427. .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
  2428. .chartContainer(that.parentNode)
  2429. .enabled(tooltips)
  2430. .valueFormatter(function(d,i) {
  2431. return yAxis.tickFormat()(d);
  2432. })
  2433. .data(
  2434. {
  2435. value: xValue,
  2436. series: allData
  2437. }
  2438. )();
  2439. interactiveLayer.renderGuideLine(pointXLocation);
  2440. });
  2441. interactiveLayer.dispatch.on("elementMouseout",function(e) {
  2442. dispatch.tooltipHide();
  2443. lines.clearHighlights();
  2444. });
  2445. dispatch.on('tooltipShow', function(e) {
  2446. if (tooltips) showTooltip(e, that.parentNode);
  2447. });
  2448. // Update chart from a state object passed to event handler
  2449. dispatch.on('changeState', function(e) {
  2450. if (typeof e.disabled !== 'undefined') {
  2451. data.forEach(function(series,i) {
  2452. series.disabled = e.disabled[i];
  2453. });
  2454. state.disabled = e.disabled;
  2455. }
  2456. if (typeof e.index !== 'undefined') {
  2457. index.i = e.index;
  2458. index.x = dx(index.i);
  2459. state.index = e.index;
  2460. indexLine
  2461. .data([index]);
  2462. }
  2463. if (typeof e.rescaleY !== 'undefined') {
  2464. rescaleY = e.rescaleY;
  2465. }
  2466. chart.update();
  2467. });
  2468. //============================================================
  2469. });
  2470. return chart;
  2471. }
  2472. //============================================================
  2473. // Event Handling/Dispatching (out of chart's scope)
  2474. //------------------------------------------------------------
  2475. lines.dispatch.on('elementMouseover.tooltip', function(e) {
  2476. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  2477. dispatch.tooltipShow(e);
  2478. });
  2479. lines.dispatch.on('elementMouseout.tooltip', function(e) {
  2480. dispatch.tooltipHide(e);
  2481. });
  2482. dispatch.on('tooltipHide', function() {
  2483. if (tooltips) nv.tooltip.cleanup();
  2484. });
  2485. //============================================================
  2486. //============================================================
  2487. // Expose Public Variables
  2488. //------------------------------------------------------------
  2489. // expose chart's sub-components
  2490. chart.dispatch = dispatch;
  2491. chart.lines = lines;
  2492. chart.legend = legend;
  2493. chart.xAxis = xAxis;
  2494. chart.yAxis = yAxis;
  2495. chart.interactiveLayer = interactiveLayer;
  2496. d3.rebind(chart, lines, 'defined', 'isArea', 'x', 'y', 'xScale','yScale', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi','useVoronoi', 'id');
  2497. chart.options = nv.utils.optionsFunc.bind(chart);
  2498. chart.margin = function(_) {
  2499. if (!arguments.length) return margin;
  2500. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  2501. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  2502. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  2503. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  2504. return chart;
  2505. };
  2506. chart.width = function(_) {
  2507. if (!arguments.length) return width;
  2508. width = _;
  2509. return chart;
  2510. };
  2511. chart.height = function(_) {
  2512. if (!arguments.length) return height;
  2513. height = _;
  2514. return chart;
  2515. };
  2516. chart.color = function(_) {
  2517. if (!arguments.length) return color;
  2518. color = nv.utils.getColor(_);
  2519. legend.color(color);
  2520. return chart;
  2521. };
  2522. chart.rescaleY = function(_) {
  2523. if (!arguments.length) return rescaleY;
  2524. rescaleY = _;
  2525. return chart;
  2526. };
  2527. chart.showControls = function(_) {
  2528. if (!arguments.length) return showControls;
  2529. showControls = _;
  2530. return chart;
  2531. };
  2532. chart.useInteractiveGuideline = function(_) {
  2533. if(!arguments.length) return useInteractiveGuideline;
  2534. useInteractiveGuideline = _;
  2535. if (_ === true) {
  2536. chart.interactive(false);
  2537. chart.useVoronoi(false);
  2538. }
  2539. return chart;
  2540. };
  2541. chart.showLegend = function(_) {
  2542. if (!arguments.length) return showLegend;
  2543. showLegend = _;
  2544. return chart;
  2545. };
  2546. chart.showXAxis = function(_) {
  2547. if (!arguments.length) return showXAxis;
  2548. showXAxis = _;
  2549. return chart;
  2550. };
  2551. chart.showYAxis = function(_) {
  2552. if (!arguments.length) return showYAxis;
  2553. showYAxis = _;
  2554. return chart;
  2555. };
  2556. chart.rightAlignYAxis = function(_) {
  2557. if(!arguments.length) return rightAlignYAxis;
  2558. rightAlignYAxis = _;
  2559. yAxis.orient( (_) ? 'right' : 'left');
  2560. return chart;
  2561. };
  2562. chart.tooltips = function(_) {
  2563. if (!arguments.length) return tooltips;
  2564. tooltips = _;
  2565. return chart;
  2566. };
  2567. chart.tooltipContent = function(_) {
  2568. if (!arguments.length) return tooltip;
  2569. tooltip = _;
  2570. return chart;
  2571. };
  2572. chart.state = function(_) {
  2573. if (!arguments.length) return state;
  2574. state = _;
  2575. return chart;
  2576. };
  2577. chart.defaultState = function(_) {
  2578. if (!arguments.length) return defaultState;
  2579. defaultState = _;
  2580. return chart;
  2581. };
  2582. chart.noData = function(_) {
  2583. if (!arguments.length) return noData;
  2584. noData = _;
  2585. return chart;
  2586. };
  2587. chart.average = function(_) {
  2588. if(!arguments.length) return average;
  2589. average = _;
  2590. return chart;
  2591. };
  2592. chart.transitionDuration = function(_) {
  2593. if (!arguments.length) return transitionDuration;
  2594. transitionDuration = _;
  2595. return chart;
  2596. };
  2597. chart.noErrorCheck = function(_) {
  2598. if (!arguments.length) return noErrorCheck;
  2599. noErrorCheck = _;
  2600. return chart;
  2601. };
  2602. //============================================================
  2603. //============================================================
  2604. // Functions
  2605. //------------------------------------------------------------
  2606. /* Normalize the data according to an index point. */
  2607. function indexify(idx, data) {
  2608. return data.map(function(line, i) {
  2609. if (!line.values) {
  2610. return line;
  2611. }
  2612. var indexValue = line.values[idx];
  2613. if (indexValue == null) {
  2614. return line;
  2615. }
  2616. var v = lines.y()(indexValue, idx);
  2617. //TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue
  2618. if (v < -.95 && !noErrorCheck) {
  2619. //if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically correct till it hits 100)
  2620. line.tempDisabled = true;
  2621. return line;
  2622. }
  2623. line.tempDisabled = false;
  2624. line.values = line.values.map(function(point, pointIndex) {
  2625. point.display = {'y': (lines.y()(point, pointIndex) - v) / (1 + v) };
  2626. return point;
  2627. })
  2628. return line;
  2629. })
  2630. }
  2631. //============================================================
  2632. return chart;
  2633. }
  2634. //TODO: consider deprecating by adding necessary features to multiBar model
  2635. nv.models.discreteBar = function() {
  2636. "use strict";
  2637. //============================================================
  2638. // Public Variables with Default Settings
  2639. //------------------------------------------------------------
  2640. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  2641. , width = 960
  2642. , height = 500
  2643. , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
  2644. , x = d3.scale.ordinal()
  2645. , y = d3.scale.linear()
  2646. , getX = function(d) { return d.x }
  2647. , getY = function(d) { return d.y }
  2648. , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
  2649. , color = nv.utils.defaultColor()
  2650. , showValues = false
  2651. , valueFormat = d3.format(',.2f')
  2652. , xDomain
  2653. , yDomain
  2654. , xRange
  2655. , yRange
  2656. , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
  2657. , rectClass = 'discreteBar'
  2658. ;
  2659. //============================================================
  2660. //============================================================
  2661. // Private Variables
  2662. //------------------------------------------------------------
  2663. var x0, y0;
  2664. //============================================================
  2665. function chart(selection) {
  2666. selection.each(function(data) {
  2667. var availableWidth = width - margin.left - margin.right,
  2668. availableHeight = height - margin.top - margin.bottom,
  2669. container = d3.select(this);
  2670. //add series index to each data point for reference
  2671. data.forEach(function(series, i) {
  2672. series.values.forEach(function(point) {
  2673. point.series = i;
  2674. });
  2675. });
  2676. //------------------------------------------------------------
  2677. // Setup Scales
  2678. // remap and flatten the data for use in calculating the scales' domains
  2679. var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
  2680. data.map(function(d) {
  2681. return d.values.map(function(d,i) {
  2682. return { x: getX(d,i), y: getY(d,i), y0: d.y0 }
  2683. })
  2684. });
  2685. x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
  2686. .rangeBands(xRange || [0, availableWidth], .1);
  2687. y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y }).concat(forceY)));
  2688. // If showValues, pad the Y axis range to account for label height
  2689. if (showValues) y.range(yRange || [availableHeight - (y.domain()[0] < 0 ? 12 : 0), y.domain()[1] > 0 ? 12 : 0]);
  2690. else y.range(yRange || [availableHeight, 0]);
  2691. //store old scales if they exist
  2692. x0 = x0 || x;
  2693. y0 = y0 || y.copy().range([y(0),y(0)]);
  2694. //------------------------------------------------------------
  2695. //------------------------------------------------------------
  2696. // Setup containers and skeleton of chart
  2697. var wrap = container.selectAll('g.nv-wrap.nv-discretebar').data([data]);
  2698. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discretebar');
  2699. var gEnter = wrapEnter.append('g');
  2700. var g = wrap.select('g');
  2701. gEnter.append('g').attr('class', 'nv-groups');
  2702. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  2703. //------------------------------------------------------------
  2704. //TODO: by definition, the discrete bar should not have multiple groups, will modify/remove later
  2705. var groups = wrap.select('.nv-groups').selectAll('.nv-group')
  2706. .data(function(d) { return d }, function(d) { return d.key });
  2707. groups.enter().append('g')
  2708. .style('stroke-opacity', 1e-6)
  2709. .style('fill-opacity', 1e-6);
  2710. groups.exit()
  2711. .transition()
  2712. .style('stroke-opacity', 1e-6)
  2713. .style('fill-opacity', 1e-6)
  2714. .remove();
  2715. groups
  2716. .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
  2717. .classed('hover', function(d) { return d.hover });
  2718. groups
  2719. .transition()
  2720. .style('stroke-opacity', 1)
  2721. .style('fill-opacity', .75);
  2722. var bars = groups.selectAll('g.nv-bar')
  2723. .data(function(d) { return d.values });
  2724. bars.exit().remove();
  2725. var barsEnter = bars.enter().append('g')
  2726. .attr('transform', function(d,i,j) {
  2727. return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05 ) + ', ' + y(0) + ')'
  2728. })
  2729. .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
  2730. d3.select(this).classed('hover', true);
  2731. dispatch.elementMouseover({
  2732. value: getY(d,i),
  2733. point: d,
  2734. series: data[d.series],
  2735. pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
  2736. pointIndex: i,
  2737. seriesIndex: d.series,
  2738. e: d3.event
  2739. });
  2740. })
  2741. .on('mouseout', function(d,i) {
  2742. d3.select(this).classed('hover', false);
  2743. dispatch.elementMouseout({
  2744. value: getY(d,i),
  2745. point: d,
  2746. series: data[d.series],
  2747. pointIndex: i,
  2748. seriesIndex: d.series,
  2749. e: d3.event
  2750. });
  2751. })
  2752. .on('click', function(d,i) {
  2753. dispatch.elementClick({
  2754. value: getY(d,i),
  2755. point: d,
  2756. series: data[d.series],
  2757. pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
  2758. pointIndex: i,
  2759. seriesIndex: d.series,
  2760. e: d3.event
  2761. });
  2762. d3.event.stopPropagation();
  2763. })
  2764. .on('dblclick', function(d,i) {
  2765. dispatch.elementDblClick({
  2766. value: getY(d,i),
  2767. point: d,
  2768. series: data[d.series],
  2769. pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
  2770. pointIndex: i,
  2771. seriesIndex: d.series,
  2772. e: d3.event
  2773. });
  2774. d3.event.stopPropagation();
  2775. });
  2776. barsEnter.append('rect')
  2777. .attr('height', 0)
  2778. .attr('width', x.rangeBand() * .9 / data.length )
  2779. if (showValues) {
  2780. barsEnter.append('text')
  2781. .attr('text-anchor', 'middle')
  2782. ;
  2783. bars.select('text')
  2784. .text(function(d,i) { return valueFormat(getY(d,i)) })
  2785. .transition()
  2786. .attr('x', x.rangeBand() * .9 / 2)
  2787. .attr('y', function(d,i) { return getY(d,i) < 0 ? y(getY(d,i)) - y(0) + 12 : -4 })
  2788. ;
  2789. } else {
  2790. bars.selectAll('text').remove();
  2791. }
  2792. bars
  2793. .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive' })
  2794. .style('fill', function(d,i) { return d.color || color(d,i) })
  2795. .style('stroke', function(d,i) { return d.color || color(d,i) })
  2796. .select('rect')
  2797. .attr('class', rectClass)
  2798. .transition()
  2799. .attr('width', x.rangeBand() * .9 / data.length);
  2800. bars.transition()
  2801. //.delay(function(d,i) { return i * 1200 / data[0].values.length })
  2802. .attr('transform', function(d,i) {
  2803. var left = x(getX(d,i)) + x.rangeBand() * .05,
  2804. top = getY(d,i) < 0 ?
  2805. y(0) :
  2806. y(0) - y(getY(d,i)) < 1 ?
  2807. y(0) - 1 : //make 1 px positive bars show up above y=0
  2808. y(getY(d,i));
  2809. return 'translate(' + left + ', ' + top + ')'
  2810. })
  2811. .select('rect')
  2812. .attr('height', function(d,i) {
  2813. return Math.max(Math.abs(y(getY(d,i)) - y((yDomain && yDomain[0]) || 0)) || 1)
  2814. });
  2815. //store old scales for use in transitions on update
  2816. x0 = x.copy();
  2817. y0 = y.copy();
  2818. });
  2819. return chart;
  2820. }
  2821. //============================================================
  2822. // Expose Public Variables
  2823. //------------------------------------------------------------
  2824. chart.dispatch = dispatch;
  2825. chart.options = nv.utils.optionsFunc.bind(chart);
  2826. chart.x = function(_) {
  2827. if (!arguments.length) return getX;
  2828. getX = _;
  2829. return chart;
  2830. };
  2831. chart.y = function(_) {
  2832. if (!arguments.length) return getY;
  2833. getY = _;
  2834. return chart;
  2835. };
  2836. chart.margin = function(_) {
  2837. if (!arguments.length) return margin;
  2838. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  2839. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  2840. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  2841. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  2842. return chart;
  2843. };
  2844. chart.width = function(_) {
  2845. if (!arguments.length) return width;
  2846. width = _;
  2847. return chart;
  2848. };
  2849. chart.height = function(_) {
  2850. if (!arguments.length) return height;
  2851. height = _;
  2852. return chart;
  2853. };
  2854. chart.xScale = function(_) {
  2855. if (!arguments.length) return x;
  2856. x = _;
  2857. return chart;
  2858. };
  2859. chart.yScale = function(_) {
  2860. if (!arguments.length) return y;
  2861. y = _;
  2862. return chart;
  2863. };
  2864. chart.xDomain = function(_) {
  2865. if (!arguments.length) return xDomain;
  2866. xDomain = _;
  2867. return chart;
  2868. };
  2869. chart.yDomain = function(_) {
  2870. if (!arguments.length) return yDomain;
  2871. yDomain = _;
  2872. return chart;
  2873. };
  2874. chart.xRange = function(_) {
  2875. if (!arguments.length) return xRange;
  2876. xRange = _;
  2877. return chart;
  2878. };
  2879. chart.yRange = function(_) {
  2880. if (!arguments.length) return yRange;
  2881. yRange = _;
  2882. return chart;
  2883. };
  2884. chart.forceY = function(_) {
  2885. if (!arguments.length) return forceY;
  2886. forceY = _;
  2887. return chart;
  2888. };
  2889. chart.color = function(_) {
  2890. if (!arguments.length) return color;
  2891. color = nv.utils.getColor(_);
  2892. return chart;
  2893. };
  2894. chart.id = function(_) {
  2895. if (!arguments.length) return id;
  2896. id = _;
  2897. return chart;
  2898. };
  2899. chart.showValues = function(_) {
  2900. if (!arguments.length) return showValues;
  2901. showValues = _;
  2902. return chart;
  2903. };
  2904. chart.valueFormat= function(_) {
  2905. if (!arguments.length) return valueFormat;
  2906. valueFormat = _;
  2907. return chart;
  2908. };
  2909. chart.rectClass= function(_) {
  2910. if (!arguments.length) return rectClass;
  2911. rectClass = _;
  2912. return chart;
  2913. };
  2914. //============================================================
  2915. return chart;
  2916. }
  2917. nv.models.discreteBarChart = function() {
  2918. "use strict";
  2919. //============================================================
  2920. // Public Variables with Default Settings
  2921. //------------------------------------------------------------
  2922. var discretebar = nv.models.discreteBar()
  2923. , xAxis = nv.models.axis()
  2924. , yAxis = nv.models.axis()
  2925. ;
  2926. var margin = {top: 15, right: 10, bottom: 50, left: 60}
  2927. , width = null
  2928. , height = null
  2929. , color = nv.utils.getColor()
  2930. , showXAxis = true
  2931. , showYAxis = true
  2932. , rightAlignYAxis = false
  2933. , staggerLabels = false
  2934. , tooltips = true
  2935. , tooltip = function(key, x, y, e, graph) {
  2936. return '<h3>' + x + '</h3>' +
  2937. '<p>' + y + '</p>'
  2938. }
  2939. , x
  2940. , y
  2941. , noData = "No Data Available."
  2942. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'beforeUpdate')
  2943. , transitionDuration = 250
  2944. ;
  2945. xAxis
  2946. .orient('bottom')
  2947. .highlightZero(false)
  2948. .showMaxMin(false)
  2949. .tickFormat(function(d) { return d })
  2950. ;
  2951. yAxis
  2952. .orient((rightAlignYAxis) ? 'right' : 'left')
  2953. .tickFormat(d3.format(',.1f'))
  2954. ;
  2955. //============================================================
  2956. //============================================================
  2957. // Private Variables
  2958. //------------------------------------------------------------
  2959. var showTooltip = function(e, offsetElement) {
  2960. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  2961. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  2962. x = xAxis.tickFormat()(discretebar.x()(e.point, e.pointIndex)),
  2963. y = yAxis.tickFormat()(discretebar.y()(e.point, e.pointIndex)),
  2964. content = tooltip(e.series.key, x, y, e, chart);
  2965. nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
  2966. };
  2967. //============================================================
  2968. function chart(selection) {
  2969. selection.each(function(data) {
  2970. var container = d3.select(this),
  2971. that = this;
  2972. var availableWidth = (width || parseInt(container.style('width')) || 960)
  2973. - margin.left - margin.right,
  2974. availableHeight = (height || parseInt(container.style('height')) || 400)
  2975. - margin.top - margin.bottom;
  2976. chart.update = function() {
  2977. dispatch.beforeUpdate();
  2978. container.transition().duration(transitionDuration).call(chart);
  2979. };
  2980. chart.container = this;
  2981. //------------------------------------------------------------
  2982. // Display No Data message if there's nothing to show.
  2983. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  2984. var noDataText = container.selectAll('.nv-noData').data([noData]);
  2985. noDataText.enter().append('text')
  2986. .attr('class', 'nvd3 nv-noData')
  2987. .attr('dy', '-.7em')
  2988. .style('text-anchor', 'middle');
  2989. noDataText
  2990. .attr('x', margin.left + availableWidth / 2)
  2991. .attr('y', margin.top + availableHeight / 2)
  2992. .text(function(d) { return d });
  2993. return chart;
  2994. } else {
  2995. container.selectAll('.nv-noData').remove();
  2996. }
  2997. //------------------------------------------------------------
  2998. //------------------------------------------------------------
  2999. // Setup Scales
  3000. x = discretebar.xScale();
  3001. y = discretebar.yScale().clamp(true);
  3002. //------------------------------------------------------------
  3003. //------------------------------------------------------------
  3004. // Setup containers and skeleton of chart
  3005. var wrap = container.selectAll('g.nv-wrap.nv-discreteBarWithAxes').data([data]);
  3006. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discreteBarWithAxes').append('g');
  3007. var defsEnter = gEnter.append('defs');
  3008. var g = wrap.select('g');
  3009. gEnter.append('g').attr('class', 'nv-x nv-axis');
  3010. gEnter.append('g').attr('class', 'nv-y nv-axis')
  3011. .append('g').attr('class', 'nv-zeroLine')
  3012. .append('line');
  3013. gEnter.append('g').attr('class', 'nv-barsWrap');
  3014. g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  3015. if (rightAlignYAxis) {
  3016. g.select(".nv-y.nv-axis")
  3017. .attr("transform", "translate(" + availableWidth + ",0)");
  3018. }
  3019. //------------------------------------------------------------
  3020. //------------------------------------------------------------
  3021. // Main Chart Component(s)
  3022. discretebar
  3023. .width(availableWidth)
  3024. .height(availableHeight);
  3025. var barsWrap = g.select('.nv-barsWrap')
  3026. .datum(data.filter(function(d) { return !d.disabled }))
  3027. barsWrap.transition().call(discretebar);
  3028. //------------------------------------------------------------
  3029. defsEnter.append('clipPath')
  3030. .attr('id', 'nv-x-label-clip-' + discretebar.id())
  3031. .append('rect');
  3032. g.select('#nv-x-label-clip-' + discretebar.id() + ' rect')
  3033. .attr('width', x.rangeBand() * (staggerLabels ? 2 : 1))
  3034. .attr('height', 16)
  3035. .attr('x', -x.rangeBand() / (staggerLabels ? 1 : 2 ));
  3036. //------------------------------------------------------------
  3037. // Setup Axes
  3038. if (showXAxis) {
  3039. xAxis
  3040. .scale(x)
  3041. .ticks( availableWidth / 100 )
  3042. .tickSize(-availableHeight, 0);
  3043. g.select('.nv-x.nv-axis')
  3044. .attr('transform', 'translate(0,' + (y.range()[0] + ((discretebar.showValues() && y.domain()[0] < 0) ? 16 : 0)) + ')');
  3045. //d3.transition(g.select('.nv-x.nv-axis'))
  3046. g.select('.nv-x.nv-axis').transition()
  3047. .call(xAxis);
  3048. var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
  3049. if (staggerLabels) {
  3050. xTicks
  3051. .selectAll('text')
  3052. .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '5' : '17') + ')' })
  3053. }
  3054. }
  3055. if (showYAxis) {
  3056. yAxis
  3057. .scale(y)
  3058. .ticks( availableHeight / 36 )
  3059. .tickSize( -availableWidth, 0);
  3060. g.select('.nv-y.nv-axis').transition()
  3061. .call(yAxis);
  3062. }
  3063. // Zero line
  3064. g.select(".nv-zeroLine line")
  3065. .attr("x1",0)
  3066. .attr("x2",availableWidth)
  3067. .attr("y1", y(0))
  3068. .attr("y2", y(0))
  3069. ;
  3070. //------------------------------------------------------------
  3071. //============================================================
  3072. // Event Handling/Dispatching (in chart's scope)
  3073. //------------------------------------------------------------
  3074. dispatch.on('tooltipShow', function(e) {
  3075. if (tooltips) showTooltip(e, that.parentNode);
  3076. });
  3077. //============================================================
  3078. });
  3079. return chart;
  3080. }
  3081. //============================================================
  3082. // Event Handling/Dispatching (out of chart's scope)
  3083. //------------------------------------------------------------
  3084. discretebar.dispatch.on('elementMouseover.tooltip', function(e) {
  3085. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  3086. dispatch.tooltipShow(e);
  3087. });
  3088. discretebar.dispatch.on('elementMouseout.tooltip', function(e) {
  3089. dispatch.tooltipHide(e);
  3090. });
  3091. dispatch.on('tooltipHide', function() {
  3092. if (tooltips) nv.tooltip.cleanup();
  3093. });
  3094. //============================================================
  3095. //============================================================
  3096. // Expose Public Variables
  3097. //------------------------------------------------------------
  3098. // expose chart's sub-components
  3099. chart.dispatch = dispatch;
  3100. chart.discretebar = discretebar;
  3101. chart.xAxis = xAxis;
  3102. chart.yAxis = yAxis;
  3103. d3.rebind(chart, discretebar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'id', 'showValues', 'valueFormat');
  3104. chart.options = nv.utils.optionsFunc.bind(chart);
  3105. chart.margin = function(_) {
  3106. if (!arguments.length) return margin;
  3107. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  3108. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  3109. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  3110. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  3111. return chart;
  3112. };
  3113. chart.width = function(_) {
  3114. if (!arguments.length) return width;
  3115. width = _;
  3116. return chart;
  3117. };
  3118. chart.height = function(_) {
  3119. if (!arguments.length) return height;
  3120. height = _;
  3121. return chart;
  3122. };
  3123. chart.color = function(_) {
  3124. if (!arguments.length) return color;
  3125. color = nv.utils.getColor(_);
  3126. discretebar.color(color);
  3127. return chart;
  3128. };
  3129. chart.showXAxis = function(_) {
  3130. if (!arguments.length) return showXAxis;
  3131. showXAxis = _;
  3132. return chart;
  3133. };
  3134. chart.showYAxis = function(_) {
  3135. if (!arguments.length) return showYAxis;
  3136. showYAxis = _;
  3137. return chart;
  3138. };
  3139. chart.rightAlignYAxis = function(_) {
  3140. if(!arguments.length) return rightAlignYAxis;
  3141. rightAlignYAxis = _;
  3142. yAxis.orient( (_) ? 'right' : 'left');
  3143. return chart;
  3144. };
  3145. chart.staggerLabels = function(_) {
  3146. if (!arguments.length) return staggerLabels;
  3147. staggerLabels = _;
  3148. return chart;
  3149. };
  3150. chart.tooltips = function(_) {
  3151. if (!arguments.length) return tooltips;
  3152. tooltips = _;
  3153. return chart;
  3154. };
  3155. chart.tooltipContent = function(_) {
  3156. if (!arguments.length) return tooltip;
  3157. tooltip = _;
  3158. return chart;
  3159. };
  3160. chart.noData = function(_) {
  3161. if (!arguments.length) return noData;
  3162. noData = _;
  3163. return chart;
  3164. };
  3165. chart.transitionDuration = function(_) {
  3166. if (!arguments.length) return transitionDuration;
  3167. transitionDuration = _;
  3168. return chart;
  3169. };
  3170. //============================================================
  3171. return chart;
  3172. }
  3173. nv.models.distribution = function() {
  3174. "use strict";
  3175. //============================================================
  3176. // Public Variables with Default Settings
  3177. //------------------------------------------------------------
  3178. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  3179. , width = 400 //technically width or height depending on x or y....
  3180. , size = 8
  3181. , axis = 'x' // 'x' or 'y'... horizontal or vertical
  3182. , getData = function(d) { return d[axis] } // defaults d.x or d.y
  3183. , color = nv.utils.defaultColor()
  3184. , scale = d3.scale.linear()
  3185. , domain
  3186. ;
  3187. //============================================================
  3188. //============================================================
  3189. // Private Variables
  3190. //------------------------------------------------------------
  3191. var scale0;
  3192. //============================================================
  3193. function chart(selection) {
  3194. selection.each(function(data) {
  3195. var availableLength = width - (axis === 'x' ? margin.left + margin.right : margin.top + margin.bottom),
  3196. naxis = axis == 'x' ? 'y' : 'x',
  3197. container = d3.select(this);
  3198. //------------------------------------------------------------
  3199. // Setup Scales
  3200. scale0 = scale0 || scale;
  3201. //------------------------------------------------------------
  3202. //------------------------------------------------------------
  3203. // Setup containers and skeleton of chart
  3204. var wrap = container.selectAll('g.nv-distribution').data([data]);
  3205. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-distribution');
  3206. var gEnter = wrapEnter.append('g');
  3207. var g = wrap.select('g');
  3208. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
  3209. //------------------------------------------------------------
  3210. var distWrap = g.selectAll('g.nv-dist')
  3211. .data(function(d) { return d }, function(d) { return d.key });
  3212. distWrap.enter().append('g');
  3213. distWrap
  3214. .attr('class', function(d,i) { return 'nv-dist nv-series-' + i })
  3215. .style('stroke', function(d,i) { return color(d, i) });
  3216. var dist = distWrap.selectAll('line.nv-dist' + axis)
  3217. .data(function(d) { return d.values })
  3218. dist.enter().append('line')
  3219. .attr(axis + '1', function(d,i) { return scale0(getData(d,i)) })
  3220. .attr(axis + '2', function(d,i) { return scale0(getData(d,i)) })
  3221. distWrap.exit().selectAll('line.nv-dist' + axis)
  3222. .transition()
  3223. .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
  3224. .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
  3225. .style('stroke-opacity', 0)
  3226. .remove();
  3227. dist
  3228. .attr('class', function(d,i) { return 'nv-dist' + axis + ' nv-dist' + axis + '-' + i })
  3229. .attr(naxis + '1', 0)
  3230. .attr(naxis + '2', size);
  3231. dist
  3232. .transition()
  3233. .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
  3234. .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
  3235. scale0 = scale.copy();
  3236. });
  3237. return chart;
  3238. }
  3239. //============================================================
  3240. // Expose Public Variables
  3241. //------------------------------------------------------------
  3242. chart.options = nv.utils.optionsFunc.bind(chart);
  3243. chart.margin = function(_) {
  3244. if (!arguments.length) return margin;
  3245. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  3246. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  3247. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  3248. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  3249. return chart;
  3250. };
  3251. chart.width = function(_) {
  3252. if (!arguments.length) return width;
  3253. width = _;
  3254. return chart;
  3255. };
  3256. chart.axis = function(_) {
  3257. if (!arguments.length) return axis;
  3258. axis = _;
  3259. return chart;
  3260. };
  3261. chart.size = function(_) {
  3262. if (!arguments.length) return size;
  3263. size = _;
  3264. return chart;
  3265. };
  3266. chart.getData = function(_) {
  3267. if (!arguments.length) return getData;
  3268. getData = d3.functor(_);
  3269. return chart;
  3270. };
  3271. chart.scale = function(_) {
  3272. if (!arguments.length) return scale;
  3273. scale = _;
  3274. return chart;
  3275. };
  3276. chart.color = function(_) {
  3277. if (!arguments.length) return color;
  3278. color = nv.utils.getColor(_);
  3279. return chart;
  3280. };
  3281. //============================================================
  3282. return chart;
  3283. }
  3284. nv.models.historicalBarChart = function() {
  3285. "use strict";
  3286. //============================================================
  3287. // Public Variables with Default Settings
  3288. //------------------------------------------------------------
  3289. var bars = nv.models.historicalBar()
  3290. , xAxis = nv.models.axis()
  3291. , yAxis = nv.models.axis()
  3292. , legend = nv.models.legend()
  3293. ;
  3294. var margin = {top: 30, right: 90, bottom: 50, left: 90}
  3295. , color = nv.utils.defaultColor()
  3296. , width = null
  3297. , height = null
  3298. , showLegend = false
  3299. , showXAxis = true
  3300. , showYAxis = true
  3301. , rightAlignYAxis = false
  3302. , tooltips = true
  3303. , tooltip = function(key, x, y, e, graph) {
  3304. return '<h3>' + key + '</h3>' +
  3305. '<p>' + y + ' at ' + x + '</p>'
  3306. }
  3307. , x
  3308. , y
  3309. , state = {}
  3310. , defaultState = null
  3311. , noData = 'No Data Available.'
  3312. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
  3313. , transitionDuration = 250
  3314. ;
  3315. xAxis
  3316. .orient('bottom')
  3317. .tickPadding(7)
  3318. ;
  3319. yAxis
  3320. .orient( (rightAlignYAxis) ? 'right' : 'left')
  3321. ;
  3322. //============================================================
  3323. //============================================================
  3324. // Private Variables
  3325. //------------------------------------------------------------
  3326. var showTooltip = function(e, offsetElement) {
  3327. // New addition to calculate position if SVG is scaled with viewBox, may move TODO: consider implementing everywhere else
  3328. if (offsetElement) {
  3329. var svg = d3.select(offsetElement).select('svg');
  3330. var viewBox = (svg.node()) ? svg.attr('viewBox') : null;
  3331. if (viewBox) {
  3332. viewBox = viewBox.split(' ');
  3333. var ratio = parseInt(svg.style('width')) / viewBox[2];
  3334. e.pos[0] = e.pos[0] * ratio;
  3335. e.pos[1] = e.pos[1] * ratio;
  3336. }
  3337. }
  3338. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  3339. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  3340. x = xAxis.tickFormat()(bars.x()(e.point, e.pointIndex)),
  3341. y = yAxis.tickFormat()(bars.y()(e.point, e.pointIndex)),
  3342. content = tooltip(e.series.key, x, y, e, chart);
  3343. nv.tooltip.show([left, top], content, null, null, offsetElement);
  3344. };
  3345. //============================================================
  3346. function chart(selection) {
  3347. selection.each(function(data) {
  3348. var container = d3.select(this),
  3349. that = this;
  3350. var availableWidth = (width || parseInt(container.style('width')) || 960)
  3351. - margin.left - margin.right,
  3352. availableHeight = (height || parseInt(container.style('height')) || 400)
  3353. - margin.top - margin.bottom;
  3354. chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
  3355. chart.container = this;
  3356. //set state.disabled
  3357. state.disabled = data.map(function(d) { return !!d.disabled });
  3358. if (!defaultState) {
  3359. var key;
  3360. defaultState = {};
  3361. for (key in state) {
  3362. if (state[key] instanceof Array)
  3363. defaultState[key] = state[key].slice(0);
  3364. else
  3365. defaultState[key] = state[key];
  3366. }
  3367. }
  3368. //------------------------------------------------------------
  3369. // Display noData message if there's nothing to show.
  3370. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  3371. var noDataText = container.selectAll('.nv-noData').data([noData]);
  3372. noDataText.enter().append('text')
  3373. .attr('class', 'nvd3 nv-noData')
  3374. .attr('dy', '-.7em')
  3375. .style('text-anchor', 'middle');
  3376. noDataText
  3377. .attr('x', margin.left + availableWidth / 2)
  3378. .attr('y', margin.top + availableHeight / 2)
  3379. .text(function(d) { return d });
  3380. return chart;
  3381. } else {
  3382. container.selectAll('.nv-noData').remove();
  3383. }
  3384. //------------------------------------------------------------
  3385. //------------------------------------------------------------
  3386. // Setup Scales
  3387. x = bars.xScale();
  3388. y = bars.yScale();
  3389. //------------------------------------------------------------
  3390. //------------------------------------------------------------
  3391. // Setup containers and skeleton of chart
  3392. var wrap = container.selectAll('g.nv-wrap.nv-historicalBarChart').data([data]);
  3393. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBarChart').append('g');
  3394. var g = wrap.select('g');
  3395. gEnter.append('g').attr('class', 'nv-x nv-axis');
  3396. gEnter.append('g').attr('class', 'nv-y nv-axis');
  3397. gEnter.append('g').attr('class', 'nv-barsWrap');
  3398. gEnter.append('g').attr('class', 'nv-legendWrap');
  3399. //------------------------------------------------------------
  3400. //------------------------------------------------------------
  3401. // Legend
  3402. if (showLegend) {
  3403. legend.width(availableWidth);
  3404. g.select('.nv-legendWrap')
  3405. .datum(data)
  3406. .call(legend);
  3407. if ( margin.top != legend.height()) {
  3408. margin.top = legend.height();
  3409. availableHeight = (height || parseInt(container.style('height')) || 400)
  3410. - margin.top - margin.bottom;
  3411. }
  3412. wrap.select('.nv-legendWrap')
  3413. .attr('transform', 'translate(0,' + (-margin.top) +')')
  3414. }
  3415. //------------------------------------------------------------
  3416. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  3417. if (rightAlignYAxis) {
  3418. g.select(".nv-y.nv-axis")
  3419. .attr("transform", "translate(" + availableWidth + ",0)");
  3420. }
  3421. //------------------------------------------------------------
  3422. // Main Chart Component(s)
  3423. bars
  3424. .width(availableWidth)
  3425. .height(availableHeight)
  3426. .color(data.map(function(d,i) {
  3427. return d.color || color(d, i);
  3428. }).filter(function(d,i) { return !data[i].disabled }));
  3429. var barsWrap = g.select('.nv-barsWrap')
  3430. .datum(data.filter(function(d) { return !d.disabled }))
  3431. barsWrap.transition().call(bars);
  3432. //------------------------------------------------------------
  3433. //------------------------------------------------------------
  3434. // Setup Axes
  3435. if (showXAxis) {
  3436. xAxis
  3437. .scale(x)
  3438. .tickSize(-availableHeight, 0);
  3439. g.select('.nv-x.nv-axis')
  3440. .attr('transform', 'translate(0,' + y.range()[0] + ')');
  3441. g.select('.nv-x.nv-axis')
  3442. .transition()
  3443. .call(xAxis);
  3444. }
  3445. if (showYAxis) {
  3446. yAxis
  3447. .scale(y)
  3448. .ticks( availableHeight / 36 )
  3449. .tickSize( -availableWidth, 0);
  3450. g.select('.nv-y.nv-axis')
  3451. .transition()
  3452. .call(yAxis);
  3453. }
  3454. //------------------------------------------------------------
  3455. //============================================================
  3456. // Event Handling/Dispatching (in chart's scope)
  3457. //------------------------------------------------------------
  3458. legend.dispatch.on('legendClick', function(d,i) {
  3459. d.disabled = !d.disabled;
  3460. if (!data.filter(function(d) { return !d.disabled }).length) {
  3461. data.map(function(d) {
  3462. d.disabled = false;
  3463. wrap.selectAll('.nv-series').classed('disabled', false);
  3464. return d;
  3465. });
  3466. }
  3467. state.disabled = data.map(function(d) { return !!d.disabled });
  3468. dispatch.stateChange(state);
  3469. selection.transition().call(chart);
  3470. });
  3471. legend.dispatch.on('legendDblclick', function(d) {
  3472. //Double clicking should always enable current series, and disabled all others.
  3473. data.forEach(function(d) {
  3474. d.disabled = true;
  3475. });
  3476. d.disabled = false;
  3477. state.disabled = data.map(function(d) { return !!d.disabled });
  3478. dispatch.stateChange(state);
  3479. chart.update();
  3480. });
  3481. dispatch.on('tooltipShow', function(e) {
  3482. if (tooltips) showTooltip(e, that.parentNode);
  3483. });
  3484. dispatch.on('changeState', function(e) {
  3485. if (typeof e.disabled !== 'undefined') {
  3486. data.forEach(function(series,i) {
  3487. series.disabled = e.disabled[i];
  3488. });
  3489. state.disabled = e.disabled;
  3490. }
  3491. chart.update();
  3492. });
  3493. //============================================================
  3494. });
  3495. return chart;
  3496. }
  3497. //============================================================
  3498. // Event Handling/Dispatching (out of chart's scope)
  3499. //------------------------------------------------------------
  3500. bars.dispatch.on('elementMouseover.tooltip', function(e) {
  3501. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  3502. dispatch.tooltipShow(e);
  3503. });
  3504. bars.dispatch.on('elementMouseout.tooltip', function(e) {
  3505. dispatch.tooltipHide(e);
  3506. });
  3507. dispatch.on('tooltipHide', function() {
  3508. if (tooltips) nv.tooltip.cleanup();
  3509. });
  3510. //============================================================
  3511. //============================================================
  3512. // Expose Public Variables
  3513. //------------------------------------------------------------
  3514. // expose chart's sub-components
  3515. chart.dispatch = dispatch;
  3516. chart.bars = bars;
  3517. chart.legend = legend;
  3518. chart.xAxis = xAxis;
  3519. chart.yAxis = yAxis;
  3520. d3.rebind(chart, bars, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale',
  3521. 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id', 'interpolate','highlightPoint','clearHighlights', 'interactive');
  3522. chart.options = nv.utils.optionsFunc.bind(chart);
  3523. chart.margin = function(_) {
  3524. if (!arguments.length) return margin;
  3525. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  3526. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  3527. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  3528. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  3529. return chart;
  3530. };
  3531. chart.width = function(_) {
  3532. if (!arguments.length) return width;
  3533. width = _;
  3534. return chart;
  3535. };
  3536. chart.height = function(_) {
  3537. if (!arguments.length) return height;
  3538. height = _;
  3539. return chart;
  3540. };
  3541. chart.color = function(_) {
  3542. if (!arguments.length) return color;
  3543. color = nv.utils.getColor(_);
  3544. legend.color(color);
  3545. return chart;
  3546. };
  3547. chart.showLegend = function(_) {
  3548. if (!arguments.length) return showLegend;
  3549. showLegend = _;
  3550. return chart;
  3551. };
  3552. chart.showXAxis = function(_) {
  3553. if (!arguments.length) return showXAxis;
  3554. showXAxis = _;
  3555. return chart;
  3556. };
  3557. chart.showYAxis = function(_) {
  3558. if (!arguments.length) return showYAxis;
  3559. showYAxis = _;
  3560. return chart;
  3561. };
  3562. chart.rightAlignYAxis = function(_) {
  3563. if(!arguments.length) return rightAlignYAxis;
  3564. rightAlignYAxis = _;
  3565. yAxis.orient( (_) ? 'right' : 'left');
  3566. return chart;
  3567. };
  3568. chart.tooltips = function(_) {
  3569. if (!arguments.length) return tooltips;
  3570. tooltips = _;
  3571. return chart;
  3572. };
  3573. chart.tooltipContent = function(_) {
  3574. if (!arguments.length) return tooltip;
  3575. tooltip = _;
  3576. return chart;
  3577. };
  3578. chart.state = function(_) {
  3579. if (!arguments.length) return state;
  3580. state = _;
  3581. return chart;
  3582. };
  3583. chart.defaultState = function(_) {
  3584. if (!arguments.length) return defaultState;
  3585. defaultState = _;
  3586. return chart;
  3587. };
  3588. chart.noData = function(_) {
  3589. if (!arguments.length) return noData;
  3590. noData = _;
  3591. return chart;
  3592. };
  3593. chart.transitionDuration = function(_) {
  3594. if (!arguments.length) return transitionDuration;
  3595. transitionDuration = _;
  3596. return chart;
  3597. };
  3598. //============================================================
  3599. return chart;
  3600. }
  3601. nv.models.indentedTree = function() {
  3602. "use strict";
  3603. //============================================================
  3604. // Public Variables with Default Settings
  3605. //------------------------------------------------------------
  3606. var margin = {top: 0, right: 0, bottom: 0, left: 0} //TODO: implement, maybe as margin on the containing div
  3607. , width = 960
  3608. , height = 500
  3609. , color = nv.utils.defaultColor()
  3610. , id = Math.floor(Math.random() * 10000)
  3611. , header = true
  3612. , filterZero = false
  3613. , noData = "No Data Available."
  3614. , childIndent = 20
  3615. , columns = [{key:'key', label: 'Name', type:'text'}] //TODO: consider functions like chart.addColumn, chart.removeColumn, instead of a block like this
  3616. , tableClass = null
  3617. , iconOpen = 'images/grey-plus.png' //TODO: consider removing this and replacing with a '+' or '-' unless user defines images
  3618. , iconClose = 'images/grey-minus.png'
  3619. , dispatch = d3.dispatch('elementClick', 'elementDblclick', 'elementMouseover', 'elementMouseout')
  3620. , getUrl = function(d) { return d.url }
  3621. ;
  3622. //============================================================
  3623. var idx = 0;
  3624. function chart(selection) {
  3625. selection.each(function(data) {
  3626. var depth = 1,
  3627. container = d3.select(this);
  3628. var tree = d3.layout.tree()
  3629. .children(function(d) { return d.values })
  3630. .size([height, childIndent]); //Not sure if this is needed now that the result is HTML
  3631. chart.update = function() { container.transition().duration(600).call(chart) };
  3632. //------------------------------------------------------------
  3633. // Display No Data message if there's nothing to show.
  3634. if (!data[0]) data[0] = {key: noData};
  3635. //------------------------------------------------------------
  3636. var nodes = tree.nodes(data[0]);
  3637. // nodes.map(function(d) {
  3638. // d.id = i++;
  3639. // })
  3640. //------------------------------------------------------------
  3641. // Setup containers and skeleton of chart
  3642. var wrap = d3.select(this).selectAll('div').data([[nodes]]);
  3643. var wrapEnter = wrap.enter().append('div').attr('class', 'nvd3 nv-wrap nv-indentedtree');
  3644. var tableEnter = wrapEnter.append('table');
  3645. var table = wrap.select('table').attr('width', '100%').attr('class', tableClass);
  3646. //------------------------------------------------------------
  3647. if (header) {
  3648. var thead = tableEnter.append('thead');
  3649. var theadRow1 = thead.append('tr');
  3650. columns.forEach(function(column) {
  3651. theadRow1
  3652. .append('th')
  3653. .attr('width', column.width ? column.width : '10%')
  3654. .style('text-align', column.type == 'numeric' ? 'right' : 'left')
  3655. .append('span')
  3656. .text(column.label);
  3657. });
  3658. }
  3659. var tbody = table.selectAll('tbody')
  3660. .data(function(d) { return d });
  3661. tbody.enter().append('tbody');
  3662. //compute max generations
  3663. depth = d3.max(nodes, function(node) { return node.depth });
  3664. tree.size([height, depth * childIndent]); //TODO: see if this is necessary at all
  3665. // Update the nodes…
  3666. var node = tbody.selectAll('tr')
  3667. // .data(function(d) { return d; }, function(d) { return d.id || (d.id == ++i)});
  3668. .data(function(d) { return d.filter(function(d) { return (filterZero && !d.children) ? filterZero(d) : true; } )}, function(d,i) { return d.id || (d.id || ++idx)});
  3669. //.style('display', 'table-row'); //TODO: see if this does anything
  3670. node.exit().remove();
  3671. node.select('img.nv-treeicon')
  3672. .attr('src', icon)
  3673. .classed('folded', folded);
  3674. var nodeEnter = node.enter().append('tr');
  3675. columns.forEach(function(column, index) {
  3676. var nodeName = nodeEnter.append('td')
  3677. .style('padding-left', function(d) { return (index ? 0 : d.depth * childIndent + 12 + (icon(d) ? 0 : 16)) + 'px' }, 'important') //TODO: check why I did the ternary here
  3678. .style('text-align', column.type == 'numeric' ? 'right' : 'left');
  3679. if (index == 0) {
  3680. nodeName.append('img')
  3681. .classed('nv-treeicon', true)
  3682. .classed('nv-folded', folded)
  3683. .attr('src', icon)
  3684. .style('width', '14px')
  3685. .style('height', '14px')
  3686. .style('padding', '0 1px')
  3687. .style('display', function(d) { return icon(d) ? 'inline-block' : 'none'; })
  3688. .on('click', click);
  3689. }
  3690. nodeName.each(function(d) {
  3691. if (!index && getUrl(d))
  3692. d3.select(this)
  3693. .append('a')
  3694. .attr('href',getUrl)
  3695. .attr('class', d3.functor(column.classes))
  3696. .append('span')
  3697. else
  3698. d3.select(this)
  3699. .append('span')
  3700. d3.select(this).select('span')
  3701. .attr('class', d3.functor(column.classes) )
  3702. .text(function(d) { return column.format ? (d[column.key] ? column.format(d[column.key]) : '-') : (d[column.key] || '-'); });
  3703. });
  3704. if (column.showCount) {
  3705. nodeName.append('span')
  3706. .attr('class', 'nv-childrenCount');
  3707. node.selectAll('span.nv-childrenCount').text(function(d) {
  3708. return ((d.values && d.values.length) || (d._values && d._values.length)) ? //If this is a parent
  3709. '(' + ((d.values && (d.values.filter(function(d) { return filterZero ? filterZero(d) : true; }).length)) //If children are in values check its children and filter
  3710. || (d._values && d._values.filter(function(d) { return filterZero ? filterZero(d) : true; }).length) //Otherwise, do the same, but with the other name, _values...
  3711. || 0) + ')' //This is the catch-all in case there are no children after a filter
  3712. : '' //If this is not a parent, just give an empty string
  3713. });
  3714. }
  3715. // if (column.click)
  3716. // nodeName.select('span').on('click', column.click);
  3717. });
  3718. node
  3719. .order()
  3720. .on('click', function(d) {
  3721. dispatch.elementClick({
  3722. row: this, //TODO: decide whether or not this should be consistent with scatter/line events or should be an html link (a href)
  3723. data: d,
  3724. pos: [d.x, d.y]
  3725. });
  3726. })
  3727. .on('dblclick', function(d) {
  3728. dispatch.elementDblclick({
  3729. row: this,
  3730. data: d,
  3731. pos: [d.x, d.y]
  3732. });
  3733. })
  3734. .on('mouseover', function(d) {
  3735. dispatch.elementMouseover({
  3736. row: this,
  3737. data: d,
  3738. pos: [d.x, d.y]
  3739. });
  3740. })
  3741. .on('mouseout', function(d) {
  3742. dispatch.elementMouseout({
  3743. row: this,
  3744. data: d,
  3745. pos: [d.x, d.y]
  3746. });
  3747. });
  3748. // Toggle children on click.
  3749. function click(d, _, unshift) {
  3750. d3.event.stopPropagation();
  3751. if(d3.event.shiftKey && !unshift) {
  3752. //If you shift-click, it'll toggle fold all the children, instead of itself
  3753. d3.event.shiftKey = false;
  3754. d.values && d.values.forEach(function(node){
  3755. if (node.values || node._values) {
  3756. click(node, 0, true);
  3757. }
  3758. });
  3759. return true;
  3760. }
  3761. if(!hasChildren(d)) {
  3762. //download file
  3763. //window.location.href = d.url;
  3764. return true;
  3765. }
  3766. if (d.values) {
  3767. d._values = d.values;
  3768. d.values = null;
  3769. } else {
  3770. d.values = d._values;
  3771. d._values = null;
  3772. }
  3773. chart.update();
  3774. }
  3775. function icon(d) {
  3776. return (d._values && d._values.length) ? iconOpen : (d.values && d.values.length) ? iconClose : '';
  3777. }
  3778. function folded(d) {
  3779. return (d._values && d._values.length);
  3780. }
  3781. function hasChildren(d) {
  3782. var values = d.values || d._values;
  3783. return (values && values.length);
  3784. }
  3785. });
  3786. return chart;
  3787. }
  3788. //============================================================
  3789. // Expose Public Variables
  3790. //------------------------------------------------------------
  3791. chart.options = nv.utils.optionsFunc.bind(chart);
  3792. chart.margin = function(_) {
  3793. if (!arguments.length) return margin;
  3794. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  3795. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  3796. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  3797. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  3798. return chart;
  3799. };
  3800. chart.width = function(_) {
  3801. if (!arguments.length) return width;
  3802. width = _;
  3803. return chart;
  3804. };
  3805. chart.height = function(_) {
  3806. if (!arguments.length) return height;
  3807. height = _;
  3808. return chart;
  3809. };
  3810. chart.color = function(_) {
  3811. if (!arguments.length) return color;
  3812. color = nv.utils.getColor(_);
  3813. scatter.color(color);
  3814. return chart;
  3815. };
  3816. chart.id = function(_) {
  3817. if (!arguments.length) return id;
  3818. id = _;
  3819. return chart;
  3820. };
  3821. chart.header = function(_) {
  3822. if (!arguments.length) return header;
  3823. header = _;
  3824. return chart;
  3825. };
  3826. chart.noData = function(_) {
  3827. if (!arguments.length) return noData;
  3828. noData = _;
  3829. return chart;
  3830. };
  3831. chart.filterZero = function(_) {
  3832. if (!arguments.length) return filterZero;
  3833. filterZero = _;
  3834. return chart;
  3835. };
  3836. chart.columns = function(_) {
  3837. if (!arguments.length) return columns;
  3838. columns = _;
  3839. return chart;
  3840. };
  3841. chart.tableClass = function(_) {
  3842. if (!arguments.length) return tableClass;
  3843. tableClass = _;
  3844. return chart;
  3845. };
  3846. chart.iconOpen = function(_){
  3847. if (!arguments.length) return iconOpen;
  3848. iconOpen = _;
  3849. return chart;
  3850. }
  3851. chart.iconClose = function(_){
  3852. if (!arguments.length) return iconClose;
  3853. iconClose = _;
  3854. return chart;
  3855. }
  3856. chart.getUrl = function(_){
  3857. if (!arguments.length) return getUrl;
  3858. getUrl = _;
  3859. return chart;
  3860. }
  3861. //============================================================
  3862. return chart;
  3863. };nv.models.legend = function() {
  3864. "use strict";
  3865. //============================================================
  3866. // Public Variables with Default Settings
  3867. //------------------------------------------------------------
  3868. var margin = {top: 5, right: 0, bottom: 5, left: 0}
  3869. , width = 400
  3870. , height = 20
  3871. , getKey = function(d) { return d.key }
  3872. , color = nv.utils.defaultColor()
  3873. , align = true
  3874. , rightAlign = true
  3875. , updateState = true //If true, legend will update data.disabled and trigger a 'stateChange' dispatch.
  3876. , radioButtonMode = false //If true, clicking legend items will cause it to behave like a radio button. (only one can be selected at a time)
  3877. , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange')
  3878. ;
  3879. //============================================================
  3880. function chart(selection) {
  3881. selection.each(function(data) {
  3882. var availableWidth = width - margin.left - margin.right,
  3883. container = d3.select(this);
  3884. //------------------------------------------------------------
  3885. // Setup containers and skeleton of chart
  3886. var wrap = container.selectAll('g.nv-legend').data([data]);
  3887. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g');
  3888. var g = wrap.select('g');
  3889. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  3890. //------------------------------------------------------------
  3891. var series = g.selectAll('.nv-series')
  3892. .data(function(d) { return d });
  3893. var seriesEnter = series.enter().append('g').attr('class', 'nv-series')
  3894. .on('mouseover', function(d,i) {
  3895. dispatch.legendMouseover(d,i); //TODO: Make consistent with other event objects
  3896. })
  3897. .on('mouseout', function(d,i) {
  3898. dispatch.legendMouseout(d,i);
  3899. })
  3900. .on('click', function(d,i) {
  3901. dispatch.legendClick(d,i);
  3902. if (updateState) {
  3903. if (radioButtonMode) {
  3904. //Radio button mode: set every series to disabled,
  3905. // and enable the clicked series.
  3906. data.forEach(function(series) { series.disabled = true});
  3907. d.disabled = false;
  3908. }
  3909. else {
  3910. d.disabled = !d.disabled;
  3911. if (data.every(function(series) { return series.disabled})) {
  3912. //the default behavior of NVD3 legends is, if every single series
  3913. // is disabled, turn all series' back on.
  3914. data.forEach(function(series) { series.disabled = false});
  3915. }
  3916. }
  3917. dispatch.stateChange({
  3918. disabled: data.map(function(d) { return !!d.disabled })
  3919. });
  3920. }
  3921. })
  3922. .on('dblclick', function(d,i) {
  3923. dispatch.legendDblclick(d,i);
  3924. if (updateState) {
  3925. //the default behavior of NVD3 legends, when double clicking one,
  3926. // is to set all other series' to false, and make the double clicked series enabled.
  3927. data.forEach(function(series) {
  3928. series.disabled = true;
  3929. });
  3930. d.disabled = false;
  3931. dispatch.stateChange({
  3932. disabled: data.map(function(d) { return !!d.disabled })
  3933. });
  3934. }
  3935. });
  3936. seriesEnter.append('circle')
  3937. .style('stroke-width', 2)
  3938. .attr('class','nv-legend-symbol')
  3939. .attr('r', 5);
  3940. seriesEnter.append('text')
  3941. .attr('text-anchor', 'start')
  3942. .attr('class','nv-legend-text')
  3943. .attr('dy', '.32em')
  3944. .attr('dx', '8');
  3945. series.classed('disabled', function(d) { return d.disabled });
  3946. series.exit().remove();
  3947. series.select('circle')
  3948. .style('fill', function(d,i) { return d.color || color(d,i)})
  3949. .style('stroke', function(d,i) { return d.color || color(d, i) });
  3950. series.select('text').text(getKey);
  3951. //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option)
  3952. // NEW ALIGNING CODE, TODO: clean up
  3953. if (align) {
  3954. var seriesWidths = [];
  3955. series.each(function(d,i) {
  3956. var legendText = d3.select(this).select('text');
  3957. var nodeTextLength;
  3958. try {
  3959. nodeTextLength = legendText.getComputedTextLength();
  3960. // If the legendText is display:none'd (nodeTextLength == 0), simulate an error so we approximate, instead
  3961. if(nodeTextLength <= 0) throw Error();
  3962. }
  3963. catch(e) {
  3964. nodeTextLength = nv.utils.calcApproxTextWidth(legendText);
  3965. }
  3966. seriesWidths.push(nodeTextLength + 28); // 28 is ~ the width of the circle plus some padding
  3967. });
  3968. var seriesPerRow = 0;
  3969. var legendWidth = 0;
  3970. var columnWidths = [];
  3971. while ( legendWidth < availableWidth && seriesPerRow < seriesWidths.length) {
  3972. columnWidths[seriesPerRow] = seriesWidths[seriesPerRow];
  3973. legendWidth += seriesWidths[seriesPerRow++];
  3974. }
  3975. if (seriesPerRow === 0) seriesPerRow = 1; //minimum of one series per row
  3976. while ( legendWidth > availableWidth && seriesPerRow > 1 ) {
  3977. columnWidths = [];
  3978. seriesPerRow--;
  3979. for (var k = 0; k < seriesWidths.length; k++) {
  3980. if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) )
  3981. columnWidths[k % seriesPerRow] = seriesWidths[k];
  3982. }
  3983. legendWidth = columnWidths.reduce(function(prev, cur, index, array) {
  3984. return prev + cur;
  3985. });
  3986. }
  3987. var xPositions = [];
  3988. for (var i = 0, curX = 0; i < seriesPerRow; i++) {
  3989. xPositions[i] = curX;
  3990. curX += columnWidths[i];
  3991. }
  3992. series
  3993. .attr('transform', function(d, i) {
  3994. return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * 20) + ')';
  3995. });
  3996. //position legend as far right as possible within the total width
  3997. if (rightAlign) {
  3998. g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')');
  3999. }
  4000. else {
  4001. g.attr('transform', 'translate(0' + ',' + margin.top + ')');
  4002. }
  4003. height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * 20);
  4004. } else {
  4005. var ypos = 5,
  4006. newxpos = 5,
  4007. maxwidth = 0,
  4008. xpos;
  4009. series
  4010. .attr('transform', function(d, i) {
  4011. var length = d3.select(this).select('text').node().getComputedTextLength() + 28;
  4012. xpos = newxpos;
  4013. if (width < margin.left + margin.right + xpos + length) {
  4014. newxpos = xpos = 5;
  4015. ypos += 20;
  4016. }
  4017. newxpos += length;
  4018. if (newxpos > maxwidth) maxwidth = newxpos;
  4019. return 'translate(' + xpos + ',' + ypos + ')';
  4020. });
  4021. //position legend as far right as possible within the total width
  4022. g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')');
  4023. height = margin.top + margin.bottom + ypos + 15;
  4024. }
  4025. });
  4026. return chart;
  4027. }
  4028. //============================================================
  4029. // Expose Public Variables
  4030. //------------------------------------------------------------
  4031. chart.dispatch = dispatch;
  4032. chart.options = nv.utils.optionsFunc.bind(chart);
  4033. chart.margin = function(_) {
  4034. if (!arguments.length) return margin;
  4035. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  4036. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  4037. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  4038. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  4039. return chart;
  4040. };
  4041. chart.width = function(_) {
  4042. if (!arguments.length) return width;
  4043. width = _;
  4044. return chart;
  4045. };
  4046. chart.height = function(_) {
  4047. if (!arguments.length) return height;
  4048. height = _;
  4049. return chart;
  4050. };
  4051. chart.key = function(_) {
  4052. if (!arguments.length) return getKey;
  4053. getKey = _;
  4054. return chart;
  4055. };
  4056. chart.color = function(_) {
  4057. if (!arguments.length) return color;
  4058. color = nv.utils.getColor(_);
  4059. return chart;
  4060. };
  4061. chart.align = function(_) {
  4062. if (!arguments.length) return align;
  4063. align = _;
  4064. return chart;
  4065. };
  4066. chart.rightAlign = function(_) {
  4067. if (!arguments.length) return rightAlign;
  4068. rightAlign = _;
  4069. return chart;
  4070. };
  4071. chart.updateState = function(_) {
  4072. if (!arguments.length) return updateState;
  4073. updateState = _;
  4074. return chart;
  4075. };
  4076. chart.radioButtonMode = function(_) {
  4077. if (!arguments.length) return radioButtonMode;
  4078. radioButtonMode = _;
  4079. return chart;
  4080. };
  4081. //============================================================
  4082. return chart;
  4083. }
  4084. nv.models.line = function() {
  4085. "use strict";
  4086. //============================================================
  4087. // Public Variables with Default Settings
  4088. //------------------------------------------------------------
  4089. var scatter = nv.models.scatter()
  4090. ;
  4091. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  4092. , width = 960
  4093. , height = 500
  4094. , color = nv.utils.defaultColor() // a function that returns a color
  4095. , getX = function(d) { return d.x } // accessor to get the x value from a data point
  4096. , getY = function(d) { return d.y } // accessor to get the y value from a data point
  4097. , defined = function(d,i) { return !isNaN(getY(d,i)) && getY(d,i) !== null } // allows a line to be not continuous when it is not defined
  4098. , isArea = function(d) { return d.area } // decides if a line is an area or just a line
  4099. , clipEdge = false // if true, masks lines within x and y scale
  4100. , x //can be accessed via chart.xScale()
  4101. , y //can be accessed via chart.yScale()
  4102. , interpolate = "linear" // controls the line interpolation
  4103. ;
  4104. scatter
  4105. .size(16) // default size
  4106. .sizeDomain([16,256]) //set to speed up calculation, needs to be unset if there is a custom size accessor
  4107. ;
  4108. //============================================================
  4109. //============================================================
  4110. // Private Variables
  4111. //------------------------------------------------------------
  4112. var x0, y0 //used to store previous scales
  4113. ;
  4114. //============================================================
  4115. function chart(selection) {
  4116. selection.each(function(data) {
  4117. var availableWidth = width - margin.left - margin.right,
  4118. availableHeight = height - margin.top - margin.bottom,
  4119. container = d3.select(this);
  4120. //------------------------------------------------------------
  4121. // Setup Scales
  4122. x = scatter.xScale();
  4123. y = scatter.yScale();
  4124. x0 = x0 || x;
  4125. y0 = y0 || y;
  4126. //------------------------------------------------------------
  4127. //------------------------------------------------------------
  4128. // Setup containers and skeleton of chart
  4129. var wrap = container.selectAll('g.nv-wrap.nv-line').data([data]);
  4130. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-line');
  4131. var defsEnter = wrapEnter.append('defs');
  4132. var gEnter = wrapEnter.append('g');
  4133. var g = wrap.select('g')
  4134. gEnter.append('g').attr('class', 'nv-groups');
  4135. gEnter.append('g').attr('class', 'nv-scatterWrap');
  4136. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  4137. //------------------------------------------------------------
  4138. scatter
  4139. .width(availableWidth)
  4140. .height(availableHeight)
  4141. var scatterWrap = wrap.select('.nv-scatterWrap');
  4142. //.datum(data); // Data automatically trickles down from the wrap
  4143. scatterWrap.transition().call(scatter);
  4144. defsEnter.append('clipPath')
  4145. .attr('id', 'nv-edge-clip-' + scatter.id())
  4146. .append('rect');
  4147. wrap.select('#nv-edge-clip-' + scatter.id() + ' rect')
  4148. .attr('width', availableWidth)
  4149. .attr('height', (availableHeight > 0) ? availableHeight : 0);
  4150. g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
  4151. scatterWrap
  4152. .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
  4153. var groups = wrap.select('.nv-groups').selectAll('.nv-group')
  4154. .data(function(d) { return d }, function(d) { return d.key });
  4155. groups.enter().append('g')
  4156. .style('stroke-opacity', 1e-6)
  4157. .style('fill-opacity', 1e-6);
  4158. groups.exit().remove();
  4159. groups
  4160. .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
  4161. .classed('hover', function(d) { return d.hover })
  4162. .style('fill', function(d,i){ return color(d, i) })
  4163. .style('stroke', function(d,i){ return color(d, i)});
  4164. groups
  4165. .transition()
  4166. .style('stroke-opacity', 1)
  4167. .style('fill-opacity', .5);
  4168. var areaPaths = groups.selectAll('path.nv-area')
  4169. .data(function(d) { return isArea(d) ? [d] : [] }); // this is done differently than lines because I need to check if series is an area
  4170. areaPaths.enter().append('path')
  4171. .attr('class', 'nv-area')
  4172. .attr('d', function(d) {
  4173. return d3.svg.area()
  4174. .interpolate(interpolate)
  4175. .defined(defined)
  4176. .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
  4177. .y0(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
  4178. .y1(function(d,i) { return y0( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) })
  4179. //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
  4180. .apply(this, [d.values])
  4181. });
  4182. groups.exit().selectAll('path.nv-area')
  4183. .remove();
  4184. areaPaths
  4185. .transition()
  4186. .attr('d', function(d) {
  4187. return d3.svg.area()
  4188. .interpolate(interpolate)
  4189. .defined(defined)
  4190. .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
  4191. .y0(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
  4192. .y1(function(d,i) { return y( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) })
  4193. //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
  4194. .apply(this, [d.values])
  4195. });
  4196. var linePaths = groups.selectAll('path.nv-line')
  4197. .data(function(d) { return [d.values] });
  4198. linePaths.enter().append('path')
  4199. .attr('class', 'nv-line')
  4200. .attr('d',
  4201. d3.svg.line()
  4202. .interpolate(interpolate)
  4203. .defined(defined)
  4204. .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
  4205. .y(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
  4206. );
  4207. linePaths
  4208. .transition()
  4209. .attr('d',
  4210. d3.svg.line()
  4211. .interpolate(interpolate)
  4212. .defined(defined)
  4213. .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
  4214. .y(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
  4215. );
  4216. //store old scales for use in transitions on update
  4217. x0 = x.copy();
  4218. y0 = y.copy();
  4219. });
  4220. return chart;
  4221. }
  4222. //============================================================
  4223. // Expose Public Variables
  4224. //------------------------------------------------------------
  4225. chart.dispatch = scatter.dispatch;
  4226. chart.scatter = scatter;
  4227. d3.rebind(chart, scatter, 'id', 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange',
  4228. 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi', 'clipRadius', 'padData','highlightPoint','clearHighlights');
  4229. chart.options = nv.utils.optionsFunc.bind(chart);
  4230. chart.margin = function(_) {
  4231. if (!arguments.length) return margin;
  4232. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  4233. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  4234. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  4235. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  4236. return chart;
  4237. };
  4238. chart.width = function(_) {
  4239. if (!arguments.length) return width;
  4240. width = _;
  4241. return chart;
  4242. };
  4243. chart.height = function(_) {
  4244. if (!arguments.length) return height;
  4245. height = _;
  4246. return chart;
  4247. };
  4248. chart.x = function(_) {
  4249. if (!arguments.length) return getX;
  4250. getX = _;
  4251. scatter.x(_);
  4252. return chart;
  4253. };
  4254. chart.y = function(_) {
  4255. if (!arguments.length) return getY;
  4256. getY = _;
  4257. scatter.y(_);
  4258. return chart;
  4259. };
  4260. chart.clipEdge = function(_) {
  4261. if (!arguments.length) return clipEdge;
  4262. clipEdge = _;
  4263. return chart;
  4264. };
  4265. chart.color = function(_) {
  4266. if (!arguments.length) return color;
  4267. color = nv.utils.getColor(_);
  4268. scatter.color(color);
  4269. return chart;
  4270. };
  4271. chart.interpolate = function(_) {
  4272. if (!arguments.length) return interpolate;
  4273. interpolate = _;
  4274. return chart;
  4275. };
  4276. chart.defined = function(_) {
  4277. if (!arguments.length) return defined;
  4278. defined = _;
  4279. return chart;
  4280. };
  4281. chart.isArea = function(_) {
  4282. if (!arguments.length) return isArea;
  4283. isArea = d3.functor(_);
  4284. return chart;
  4285. };
  4286. //============================================================
  4287. return chart;
  4288. }
  4289. nv.models.lineChart = function() {
  4290. "use strict";
  4291. //============================================================
  4292. // Public Variables with Default Settings
  4293. //------------------------------------------------------------
  4294. var lines = nv.models.line()
  4295. , xAxis = nv.models.axis()
  4296. , yAxis = nv.models.axis()
  4297. , legend = nv.models.legend()
  4298. , interactiveLayer = nv.interactiveGuideline()
  4299. ;
  4300. var margin = {top: 30, right: 20, bottom: 50, left: 60}
  4301. , color = nv.utils.defaultColor()
  4302. , width = null
  4303. , height = null
  4304. , showLegend = true
  4305. , showXAxis = true
  4306. , showYAxis = true
  4307. , rightAlignYAxis = false
  4308. , useInteractiveGuideline = false
  4309. , tooltips = true
  4310. , tooltip = function(key, x, y, e, graph) {
  4311. return '<h3>' + key + '</h3>' +
  4312. '<p>' + y + ' at ' + x + '</p>'
  4313. }
  4314. , x
  4315. , y
  4316. , state = {}
  4317. , defaultState = null
  4318. , noData = 'No Data Available.'
  4319. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
  4320. , transitionDuration = 250
  4321. ;
  4322. xAxis
  4323. .orient('bottom')
  4324. .tickPadding(7)
  4325. ;
  4326. yAxis
  4327. .orient((rightAlignYAxis) ? 'right' : 'left')
  4328. ;
  4329. //============================================================
  4330. //============================================================
  4331. // Private Variables
  4332. //------------------------------------------------------------
  4333. var showTooltip = function(e, offsetElement) {
  4334. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  4335. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  4336. x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
  4337. y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
  4338. content = tooltip(e.series.key, x, y, e, chart);
  4339. nv.tooltip.show([left, top], content, null, null, offsetElement);
  4340. };
  4341. //============================================================
  4342. function chart(selection) {
  4343. selection.each(function(data) {
  4344. var container = d3.select(this),
  4345. that = this;
  4346. var availableWidth = (width || parseInt(container.style('width')) || 960)
  4347. - margin.left - margin.right,
  4348. availableHeight = (height || parseInt(container.style('height')) || 400)
  4349. - margin.top - margin.bottom;
  4350. chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
  4351. chart.container = this;
  4352. //set state.disabled
  4353. state.disabled = data.map(function(d) { return !!d.disabled });
  4354. if (!defaultState) {
  4355. var key;
  4356. defaultState = {};
  4357. for (key in state) {
  4358. if (state[key] instanceof Array)
  4359. defaultState[key] = state[key].slice(0);
  4360. else
  4361. defaultState[key] = state[key];
  4362. }
  4363. }
  4364. //------------------------------------------------------------
  4365. // Display noData message if there's nothing to show.
  4366. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  4367. var noDataText = container.selectAll('.nv-noData').data([noData]);
  4368. noDataText.enter().append('text')
  4369. .attr('class', 'nvd3 nv-noData')
  4370. .attr('dy', '-.7em')
  4371. .style('text-anchor', 'middle');
  4372. noDataText
  4373. .attr('x', margin.left + availableWidth / 2)
  4374. .attr('y', margin.top + availableHeight / 2)
  4375. .text(function(d) { return d });
  4376. return chart;
  4377. } else {
  4378. container.selectAll('.nv-noData').remove();
  4379. }
  4380. //------------------------------------------------------------
  4381. //------------------------------------------------------------
  4382. // Setup Scales
  4383. x = lines.xScale();
  4384. y = lines.yScale();
  4385. //------------------------------------------------------------
  4386. //------------------------------------------------------------
  4387. // Setup containers and skeleton of chart
  4388. var wrap = container.selectAll('g.nv-wrap.nv-lineChart').data([data]);
  4389. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineChart').append('g');
  4390. var g = wrap.select('g');
  4391. gEnter.append("rect").style("opacity",0);
  4392. gEnter.append('g').attr('class', 'nv-x nv-axis');
  4393. gEnter.append('g').attr('class', 'nv-y nv-axis');
  4394. gEnter.append('g').attr('class', 'nv-linesWrap');
  4395. gEnter.append('g').attr('class', 'nv-legendWrap');
  4396. gEnter.append('g').attr('class', 'nv-interactive');
  4397. g.select("rect")
  4398. .attr("width",availableWidth)
  4399. .attr("height",(availableHeight > 0) ? availableHeight : 0);
  4400. //------------------------------------------------------------
  4401. // Legend
  4402. if (showLegend) {
  4403. legend.width(availableWidth);
  4404. g.select('.nv-legendWrap')
  4405. .datum(data)
  4406. .call(legend);
  4407. if ( margin.top != legend.height()) {
  4408. margin.top = legend.height();
  4409. availableHeight = (height || parseInt(container.style('height')) || 400)
  4410. - margin.top - margin.bottom;
  4411. }
  4412. wrap.select('.nv-legendWrap')
  4413. .attr('transform', 'translate(0,' + (-margin.top) +')')
  4414. }
  4415. //------------------------------------------------------------
  4416. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  4417. if (rightAlignYAxis) {
  4418. g.select(".nv-y.nv-axis")
  4419. .attr("transform", "translate(" + availableWidth + ",0)");
  4420. }
  4421. //------------------------------------------------------------
  4422. // Main Chart Component(s)
  4423. //------------------------------------------------------------
  4424. //Set up interactive layer
  4425. if (useInteractiveGuideline) {
  4426. interactiveLayer
  4427. .width(availableWidth)
  4428. .height(availableHeight)
  4429. .margin({left:margin.left, top:margin.top})
  4430. .svgContainer(container)
  4431. .xScale(x);
  4432. wrap.select(".nv-interactive").call(interactiveLayer);
  4433. }
  4434. lines
  4435. .width(availableWidth)
  4436. .height(availableHeight)
  4437. .color(data.map(function(d,i) {
  4438. return d.color || color(d, i);
  4439. }).filter(function(d,i) { return !data[i].disabled }));
  4440. var linesWrap = g.select('.nv-linesWrap')
  4441. .datum(data.filter(function(d) { return !d.disabled }))
  4442. linesWrap.transition().call(lines);
  4443. //------------------------------------------------------------
  4444. //------------------------------------------------------------
  4445. // Setup Axes
  4446. if (showXAxis) {
  4447. xAxis
  4448. .scale(x)
  4449. .ticks( availableWidth / 100 )
  4450. .tickSize(-availableHeight, 0);
  4451. g.select('.nv-x.nv-axis')
  4452. .attr('transform', 'translate(0,' + y.range()[0] + ')');
  4453. g.select('.nv-x.nv-axis')
  4454. .transition()
  4455. .call(xAxis);
  4456. }
  4457. if (showYAxis) {
  4458. yAxis
  4459. .scale(y)
  4460. .ticks( availableHeight / 36 )
  4461. .tickSize( -availableWidth, 0);
  4462. g.select('.nv-y.nv-axis')
  4463. .transition()
  4464. .call(yAxis);
  4465. }
  4466. //------------------------------------------------------------
  4467. //============================================================
  4468. // Event Handling/Dispatching (in chart's scope)
  4469. //------------------------------------------------------------
  4470. legend.dispatch.on('stateChange', function(newState) {
  4471. state = newState;
  4472. dispatch.stateChange(state);
  4473. chart.update();
  4474. });
  4475. interactiveLayer.dispatch.on('elementMousemove', function(e) {
  4476. lines.clearHighlights();
  4477. var singlePoint, pointIndex, pointXLocation, allData = [];
  4478. data
  4479. .filter(function(series, i) {
  4480. series.seriesIndex = i;
  4481. return !series.disabled;
  4482. })
  4483. .forEach(function(series,i) {
  4484. pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
  4485. lines.highlightPoint(i, pointIndex, true);
  4486. var point = series.values[pointIndex];
  4487. if (typeof point === 'undefined') return;
  4488. if (typeof singlePoint === 'undefined') singlePoint = point;
  4489. if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
  4490. allData.push({
  4491. key: series.key,
  4492. value: chart.y()(point, pointIndex),
  4493. color: color(series,series.seriesIndex)
  4494. });
  4495. });
  4496. //Highlight the tooltip entry based on which point the mouse is closest to.
  4497. if (allData.length > 2) {
  4498. var yValue = chart.yScale().invert(e.mouseY);
  4499. var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
  4500. var threshold = 0.03 * domainExtent;
  4501. var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold);
  4502. if (indexToHighlight !== null)
  4503. allData[indexToHighlight].highlight = true;
  4504. }
  4505. var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
  4506. interactiveLayer.tooltip
  4507. .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
  4508. .chartContainer(that.parentNode)
  4509. .enabled(tooltips)
  4510. .valueFormatter(function(d,i) {
  4511. return yAxis.tickFormat()(d);
  4512. })
  4513. .data(
  4514. {
  4515. value: xValue,
  4516. series: allData
  4517. }
  4518. )();
  4519. interactiveLayer.renderGuideLine(pointXLocation);
  4520. });
  4521. interactiveLayer.dispatch.on("elementMouseout",function(e) {
  4522. dispatch.tooltipHide();
  4523. lines.clearHighlights();
  4524. });
  4525. dispatch.on('tooltipShow', function(e) {
  4526. if (tooltips) showTooltip(e, that.parentNode);
  4527. });
  4528. dispatch.on('changeState', function(e) {
  4529. if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
  4530. data.forEach(function(series,i) {
  4531. series.disabled = e.disabled[i];
  4532. });
  4533. state.disabled = e.disabled;
  4534. }
  4535. chart.update();
  4536. });
  4537. //============================================================
  4538. });
  4539. return chart;
  4540. }
  4541. //============================================================
  4542. // Event Handling/Dispatching (out of chart's scope)
  4543. //------------------------------------------------------------
  4544. lines.dispatch.on('elementMouseover.tooltip', function(e) {
  4545. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  4546. dispatch.tooltipShow(e);
  4547. });
  4548. lines.dispatch.on('elementMouseout.tooltip', function(e) {
  4549. dispatch.tooltipHide(e);
  4550. });
  4551. dispatch.on('tooltipHide', function() {
  4552. if (tooltips) nv.tooltip.cleanup();
  4553. });
  4554. //============================================================
  4555. //============================================================
  4556. // Expose Public Variables
  4557. //------------------------------------------------------------
  4558. // expose chart's sub-components
  4559. chart.dispatch = dispatch;
  4560. chart.lines = lines;
  4561. chart.legend = legend;
  4562. chart.xAxis = xAxis;
  4563. chart.yAxis = yAxis;
  4564. chart.interactiveLayer = interactiveLayer;
  4565. d3.rebind(chart, lines, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'xRange', 'yRange'
  4566. , 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'useVoronoi','id', 'interpolate');
  4567. chart.options = nv.utils.optionsFunc.bind(chart);
  4568. chart.margin = function(_) {
  4569. if (!arguments.length) return margin;
  4570. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  4571. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  4572. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  4573. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  4574. return chart;
  4575. };
  4576. chart.width = function(_) {
  4577. if (!arguments.length) return width;
  4578. width = _;
  4579. return chart;
  4580. };
  4581. chart.height = function(_) {
  4582. if (!arguments.length) return height;
  4583. height = _;
  4584. return chart;
  4585. };
  4586. chart.color = function(_) {
  4587. if (!arguments.length) return color;
  4588. color = nv.utils.getColor(_);
  4589. legend.color(color);
  4590. return chart;
  4591. };
  4592. chart.showLegend = function(_) {
  4593. if (!arguments.length) return showLegend;
  4594. showLegend = _;
  4595. return chart;
  4596. };
  4597. chart.showXAxis = function(_) {
  4598. if (!arguments.length) return showXAxis;
  4599. showXAxis = _;
  4600. return chart;
  4601. };
  4602. chart.showYAxis = function(_) {
  4603. if (!arguments.length) return showYAxis;
  4604. showYAxis = _;
  4605. return chart;
  4606. };
  4607. chart.rightAlignYAxis = function(_) {
  4608. if(!arguments.length) return rightAlignYAxis;
  4609. rightAlignYAxis = _;
  4610. yAxis.orient( (_) ? 'right' : 'left');
  4611. return chart;
  4612. };
  4613. chart.useInteractiveGuideline = function(_) {
  4614. if(!arguments.length) return useInteractiveGuideline;
  4615. useInteractiveGuideline = _;
  4616. if (_ === true) {
  4617. chart.interactive(false);
  4618. chart.useVoronoi(false);
  4619. }
  4620. return chart;
  4621. };
  4622. chart.tooltips = function(_) {
  4623. if (!arguments.length) return tooltips;
  4624. tooltips = _;
  4625. return chart;
  4626. };
  4627. chart.tooltipContent = function(_) {
  4628. if (!arguments.length) return tooltip;
  4629. tooltip = _;
  4630. return chart;
  4631. };
  4632. chart.state = function(_) {
  4633. if (!arguments.length) return state;
  4634. state = _;
  4635. return chart;
  4636. };
  4637. chart.defaultState = function(_) {
  4638. if (!arguments.length) return defaultState;
  4639. defaultState = _;
  4640. return chart;
  4641. };
  4642. chart.noData = function(_) {
  4643. if (!arguments.length) return noData;
  4644. noData = _;
  4645. return chart;
  4646. };
  4647. chart.transitionDuration = function(_) {
  4648. if (!arguments.length) return transitionDuration;
  4649. transitionDuration = _;
  4650. return chart;
  4651. };
  4652. //============================================================
  4653. return chart;
  4654. }
  4655. nv.models.linePlusBarChart = function() {
  4656. "use strict";
  4657. //============================================================
  4658. // Public Variables with Default Settings
  4659. //------------------------------------------------------------
  4660. var lines = nv.models.line()
  4661. , bars = nv.models.historicalBar()
  4662. , xAxis = nv.models.axis()
  4663. , y1Axis = nv.models.axis()
  4664. , y2Axis = nv.models.axis()
  4665. , legend = nv.models.legend()
  4666. ;
  4667. var margin = {top: 30, right: 60, bottom: 50, left: 60}
  4668. , width = null
  4669. , height = null
  4670. , getX = function(d) { return d.x }
  4671. , getY = function(d) { return d.y }
  4672. , color = nv.utils.defaultColor()
  4673. , showLegend = true
  4674. , tooltips = true
  4675. , tooltip = function(key, x, y, e, graph) {
  4676. return '<h3>' + key + '</h3>' +
  4677. '<p>' + y + ' at ' + x + '</p>';
  4678. }
  4679. , x
  4680. , y1
  4681. , y2
  4682. , state = {}
  4683. , defaultState = null
  4684. , noData = "No Data Available."
  4685. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
  4686. ;
  4687. bars
  4688. .padData(true)
  4689. ;
  4690. lines
  4691. .clipEdge(false)
  4692. .padData(true)
  4693. ;
  4694. xAxis
  4695. .orient('bottom')
  4696. .tickPadding(7)
  4697. .highlightZero(false)
  4698. ;
  4699. y1Axis
  4700. .orient('left')
  4701. ;
  4702. y2Axis
  4703. .orient('right')
  4704. ;
  4705. //============================================================
  4706. //============================================================
  4707. // Private Variables
  4708. //------------------------------------------------------------
  4709. var showTooltip = function(e, offsetElement) {
  4710. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  4711. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  4712. x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
  4713. y = (e.series.bar ? y1Axis : y2Axis).tickFormat()(lines.y()(e.point, e.pointIndex)),
  4714. content = tooltip(e.series.key, x, y, e, chart);
  4715. nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
  4716. }
  4717. ;
  4718. //------------------------------------------------------------
  4719. function chart(selection) {
  4720. selection.each(function(data) {
  4721. var container = d3.select(this),
  4722. that = this;
  4723. var availableWidth = (width || parseInt(container.style('width')) || 960)
  4724. - margin.left - margin.right,
  4725. availableHeight = (height || parseInt(container.style('height')) || 400)
  4726. - margin.top - margin.bottom;
  4727. chart.update = function() { container.transition().call(chart); };
  4728. // chart.container = this;
  4729. //set state.disabled
  4730. state.disabled = data.map(function(d) { return !!d.disabled });
  4731. if (!defaultState) {
  4732. var key;
  4733. defaultState = {};
  4734. for (key in state) {
  4735. if (state[key] instanceof Array)
  4736. defaultState[key] = state[key].slice(0);
  4737. else
  4738. defaultState[key] = state[key];
  4739. }
  4740. }
  4741. //------------------------------------------------------------
  4742. // Display No Data message if there's nothing to show.
  4743. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  4744. var noDataText = container.selectAll('.nv-noData').data([noData]);
  4745. noDataText.enter().append('text')
  4746. .attr('class', 'nvd3 nv-noData')
  4747. .attr('dy', '-.7em')
  4748. .style('text-anchor', 'middle');
  4749. noDataText
  4750. .attr('x', margin.left + availableWidth / 2)
  4751. .attr('y', margin.top + availableHeight / 2)
  4752. .text(function(d) { return d });
  4753. return chart;
  4754. } else {
  4755. container.selectAll('.nv-noData').remove();
  4756. }
  4757. //------------------------------------------------------------
  4758. //------------------------------------------------------------
  4759. // Setup Scales
  4760. var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
  4761. var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
  4762. //x = xAxis.scale();
  4763. x = dataLines.filter(function(d) { return !d.disabled; }).length && dataLines.filter(function(d) { return !d.disabled; })[0].values.length ? lines.xScale() : bars.xScale();
  4764. //x = dataLines.filter(function(d) { return !d.disabled; }).length ? lines.xScale() : bars.xScale(); //old code before change above
  4765. y1 = bars.yScale();
  4766. y2 = lines.yScale();
  4767. //------------------------------------------------------------
  4768. //------------------------------------------------------------
  4769. // Setup containers and skeleton of chart
  4770. var wrap = d3.select(this).selectAll('g.nv-wrap.nv-linePlusBar').data([data]);
  4771. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-linePlusBar').append('g');
  4772. var g = wrap.select('g');
  4773. gEnter.append('g').attr('class', 'nv-x nv-axis');
  4774. gEnter.append('g').attr('class', 'nv-y1 nv-axis');
  4775. gEnter.append('g').attr('class', 'nv-y2 nv-axis');
  4776. gEnter.append('g').attr('class', 'nv-barsWrap');
  4777. gEnter.append('g').attr('class', 'nv-linesWrap');
  4778. gEnter.append('g').attr('class', 'nv-legendWrap');
  4779. //------------------------------------------------------------
  4780. //------------------------------------------------------------
  4781. // Legend
  4782. if (showLegend) {
  4783. legend.width( availableWidth / 2 );
  4784. g.select('.nv-legendWrap')
  4785. .datum(data.map(function(series) {
  4786. series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
  4787. series.key = series.originalKey + (series.bar ? ' (left axis)' : ' (right axis)');
  4788. return series;
  4789. }))
  4790. .call(legend);
  4791. if ( margin.top != legend.height()) {
  4792. margin.top = legend.height();
  4793. availableHeight = (height || parseInt(container.style('height')) || 400)
  4794. - margin.top - margin.bottom;
  4795. }
  4796. g.select('.nv-legendWrap')
  4797. .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
  4798. }
  4799. //------------------------------------------------------------
  4800. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  4801. //------------------------------------------------------------
  4802. // Main Chart Component(s)
  4803. lines
  4804. .width(availableWidth)
  4805. .height(availableHeight)
  4806. .color(data.map(function(d,i) {
  4807. return d.color || color(d, i);
  4808. }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }))
  4809. bars
  4810. .width(availableWidth)
  4811. .height(availableHeight)
  4812. .color(data.map(function(d,i) {
  4813. return d.color || color(d, i);
  4814. }).filter(function(d,i) { return !data[i].disabled && data[i].bar }))
  4815. var barsWrap = g.select('.nv-barsWrap')
  4816. .datum(dataBars.length ? dataBars : [{values:[]}])
  4817. var linesWrap = g.select('.nv-linesWrap')
  4818. .datum(dataLines[0] && !dataLines[0].disabled ? dataLines : [{values:[]}] );
  4819. //.datum(!dataLines[0].disabled ? dataLines : [{values:dataLines[0].values.map(function(d) { return [d[0], null] }) }] );
  4820. d3.transition(barsWrap).call(bars);
  4821. d3.transition(linesWrap).call(lines);
  4822. //------------------------------------------------------------
  4823. //------------------------------------------------------------
  4824. // Setup Axes
  4825. xAxis
  4826. .scale(x)
  4827. .ticks( availableWidth / 100 )
  4828. .tickSize(-availableHeight, 0);
  4829. g.select('.nv-x.nv-axis')
  4830. .attr('transform', 'translate(0,' + y1.range()[0] + ')');
  4831. d3.transition(g.select('.nv-x.nv-axis'))
  4832. .call(xAxis);
  4833. y1Axis
  4834. .scale(y1)
  4835. .ticks( availableHeight / 36 )
  4836. .tickSize(-availableWidth, 0);
  4837. d3.transition(g.select('.nv-y1.nv-axis'))
  4838. .style('opacity', dataBars.length ? 1 : 0)
  4839. .call(y1Axis);
  4840. y2Axis
  4841. .scale(y2)
  4842. .ticks( availableHeight / 36 )
  4843. .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
  4844. g.select('.nv-y2.nv-axis')
  4845. .style('opacity', dataLines.length ? 1 : 0)
  4846. .attr('transform', 'translate(' + availableWidth + ',0)');
  4847. //.attr('transform', 'translate(' + x.range()[1] + ',0)');
  4848. d3.transition(g.select('.nv-y2.nv-axis'))
  4849. .call(y2Axis);
  4850. //------------------------------------------------------------
  4851. //============================================================
  4852. // Event Handling/Dispatching (in chart's scope)
  4853. //------------------------------------------------------------
  4854. legend.dispatch.on('stateChange', function(newState) {
  4855. state = newState;
  4856. dispatch.stateChange(state);
  4857. chart.update();
  4858. });
  4859. dispatch.on('tooltipShow', function(e) {
  4860. if (tooltips) showTooltip(e, that.parentNode);
  4861. });
  4862. // Update chart from a state object passed to event handler
  4863. dispatch.on('changeState', function(e) {
  4864. if (typeof e.disabled !== 'undefined') {
  4865. data.forEach(function(series,i) {
  4866. series.disabled = e.disabled[i];
  4867. });
  4868. state.disabled = e.disabled;
  4869. }
  4870. chart.update();
  4871. });
  4872. //============================================================
  4873. });
  4874. return chart;
  4875. }
  4876. //============================================================
  4877. // Event Handling/Dispatching (out of chart's scope)
  4878. //------------------------------------------------------------
  4879. lines.dispatch.on('elementMouseover.tooltip', function(e) {
  4880. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  4881. dispatch.tooltipShow(e);
  4882. });
  4883. lines.dispatch.on('elementMouseout.tooltip', function(e) {
  4884. dispatch.tooltipHide(e);
  4885. });
  4886. bars.dispatch.on('elementMouseover.tooltip', function(e) {
  4887. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  4888. dispatch.tooltipShow(e);
  4889. });
  4890. bars.dispatch.on('elementMouseout.tooltip', function(e) {
  4891. dispatch.tooltipHide(e);
  4892. });
  4893. dispatch.on('tooltipHide', function() {
  4894. if (tooltips) nv.tooltip.cleanup();
  4895. });
  4896. //============================================================
  4897. //============================================================
  4898. // Expose Public Variables
  4899. //------------------------------------------------------------
  4900. // expose chart's sub-components
  4901. chart.dispatch = dispatch;
  4902. chart.legend = legend;
  4903. chart.lines = lines;
  4904. chart.bars = bars;
  4905. chart.xAxis = xAxis;
  4906. chart.y1Axis = y1Axis;
  4907. chart.y2Axis = y2Axis;
  4908. d3.rebind(chart, lines, 'defined', 'size', 'clipVoronoi', 'interpolate');
  4909. //TODO: consider rebinding x, y and some other stuff, and simply do soemthign lile bars.x(lines.x()), etc.
  4910. //d3.rebind(chart, lines, 'x', 'y', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id');
  4911. chart.options = nv.utils.optionsFunc.bind(chart);
  4912. chart.x = function(_) {
  4913. if (!arguments.length) return getX;
  4914. getX = _;
  4915. lines.x(_);
  4916. bars.x(_);
  4917. return chart;
  4918. };
  4919. chart.y = function(_) {
  4920. if (!arguments.length) return getY;
  4921. getY = _;
  4922. lines.y(_);
  4923. bars.y(_);
  4924. return chart;
  4925. };
  4926. chart.margin = function(_) {
  4927. if (!arguments.length) return margin;
  4928. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  4929. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  4930. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  4931. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  4932. return chart;
  4933. };
  4934. chart.width = function(_) {
  4935. if (!arguments.length) return width;
  4936. width = _;
  4937. return chart;
  4938. };
  4939. chart.height = function(_) {
  4940. if (!arguments.length) return height;
  4941. height = _;
  4942. return chart;
  4943. };
  4944. chart.color = function(_) {
  4945. if (!arguments.length) return color;
  4946. color = nv.utils.getColor(_);
  4947. legend.color(color);
  4948. return chart;
  4949. };
  4950. chart.showLegend = function(_) {
  4951. if (!arguments.length) return showLegend;
  4952. showLegend = _;
  4953. return chart;
  4954. };
  4955. chart.tooltips = function(_) {
  4956. if (!arguments.length) return tooltips;
  4957. tooltips = _;
  4958. return chart;
  4959. };
  4960. chart.tooltipContent = function(_) {
  4961. if (!arguments.length) return tooltip;
  4962. tooltip = _;
  4963. return chart;
  4964. };
  4965. chart.state = function(_) {
  4966. if (!arguments.length) return state;
  4967. state = _;
  4968. return chart;
  4969. };
  4970. chart.defaultState = function(_) {
  4971. if (!arguments.length) return defaultState;
  4972. defaultState = _;
  4973. return chart;
  4974. };
  4975. chart.noData = function(_) {
  4976. if (!arguments.length) return noData;
  4977. noData = _;
  4978. return chart;
  4979. };
  4980. //============================================================
  4981. return chart;
  4982. }
  4983. nv.models.lineWithFocusChart = function() {
  4984. "use strict";
  4985. //============================================================
  4986. // Public Variables with Default Settings
  4987. //------------------------------------------------------------
  4988. var lines = nv.models.line()
  4989. , lines2 = nv.models.line()
  4990. , xAxis = nv.models.axis()
  4991. , yAxis = nv.models.axis()
  4992. , x2Axis = nv.models.axis()
  4993. , y2Axis = nv.models.axis()
  4994. , legend = nv.models.legend()
  4995. , brush = d3.svg.brush()
  4996. ;
  4997. var margin = {top: 30, right: 30, bottom: 30, left: 60}
  4998. , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
  4999. , color = nv.utils.defaultColor()
  5000. , width = null
  5001. , height = null
  5002. , height2 = 100
  5003. , x
  5004. , y
  5005. , x2
  5006. , y2
  5007. , showLegend = true
  5008. , brushExtent = null
  5009. , tooltips = true
  5010. , tooltip = function(key, x, y, e, graph) {
  5011. return '<h3>' + key + '</h3>' +
  5012. '<p>' + y + ' at ' + x + '</p>'
  5013. }
  5014. , noData = "No Data Available."
  5015. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush')
  5016. , transitionDuration = 250
  5017. ;
  5018. lines
  5019. .clipEdge(true)
  5020. ;
  5021. lines2
  5022. .interactive(false)
  5023. ;
  5024. xAxis
  5025. .orient('bottom')
  5026. .tickPadding(5)
  5027. ;
  5028. yAxis
  5029. .orient('left')
  5030. ;
  5031. x2Axis
  5032. .orient('bottom')
  5033. .tickPadding(5)
  5034. ;
  5035. y2Axis
  5036. .orient('left')
  5037. ;
  5038. //============================================================
  5039. //============================================================
  5040. // Private Variables
  5041. //------------------------------------------------------------
  5042. var showTooltip = function(e, offsetElement) {
  5043. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  5044. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  5045. x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
  5046. y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
  5047. content = tooltip(e.series.key, x, y, e, chart);
  5048. nv.tooltip.show([left, top], content, null, null, offsetElement);
  5049. };
  5050. //============================================================
  5051. function chart(selection) {
  5052. selection.each(function(data) {
  5053. var container = d3.select(this),
  5054. that = this;
  5055. var availableWidth = (width || parseInt(container.style('width')) || 960)
  5056. - margin.left - margin.right,
  5057. availableHeight1 = (height || parseInt(container.style('height')) || 400)
  5058. - margin.top - margin.bottom - height2,
  5059. availableHeight2 = height2 - margin2.top - margin2.bottom;
  5060. chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
  5061. chart.container = this;
  5062. //------------------------------------------------------------
  5063. // Display No Data message if there's nothing to show.
  5064. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  5065. var noDataText = container.selectAll('.nv-noData').data([noData]);
  5066. noDataText.enter().append('text')
  5067. .attr('class', 'nvd3 nv-noData')
  5068. .attr('dy', '-.7em')
  5069. .style('text-anchor', 'middle');
  5070. noDataText
  5071. .attr('x', margin.left + availableWidth / 2)
  5072. .attr('y', margin.top + availableHeight1 / 2)
  5073. .text(function(d) { return d });
  5074. return chart;
  5075. } else {
  5076. container.selectAll('.nv-noData').remove();
  5077. }
  5078. //------------------------------------------------------------
  5079. //------------------------------------------------------------
  5080. // Setup Scales
  5081. x = lines.xScale();
  5082. y = lines.yScale();
  5083. x2 = lines2.xScale();
  5084. y2 = lines2.yScale();
  5085. //------------------------------------------------------------
  5086. //------------------------------------------------------------
  5087. // Setup containers and skeleton of chart
  5088. var wrap = container.selectAll('g.nv-wrap.nv-lineWithFocusChart').data([data]);
  5089. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineWithFocusChart').append('g');
  5090. var g = wrap.select('g');
  5091. gEnter.append('g').attr('class', 'nv-legendWrap');
  5092. var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
  5093. focusEnter.append('g').attr('class', 'nv-x nv-axis');
  5094. focusEnter.append('g').attr('class', 'nv-y nv-axis');
  5095. focusEnter.append('g').attr('class', 'nv-linesWrap');
  5096. var contextEnter = gEnter.append('g').attr('class', 'nv-context');
  5097. contextEnter.append('g').attr('class', 'nv-x nv-axis');
  5098. contextEnter.append('g').attr('class', 'nv-y nv-axis');
  5099. contextEnter.append('g').attr('class', 'nv-linesWrap');
  5100. contextEnter.append('g').attr('class', 'nv-brushBackground');
  5101. contextEnter.append('g').attr('class', 'nv-x nv-brush');
  5102. //------------------------------------------------------------
  5103. //------------------------------------------------------------
  5104. // Legend
  5105. if (showLegend) {
  5106. legend.width(availableWidth);
  5107. g.select('.nv-legendWrap')
  5108. .datum(data)
  5109. .call(legend);
  5110. if ( margin.top != legend.height()) {
  5111. margin.top = legend.height();
  5112. availableHeight1 = (height || parseInt(container.style('height')) || 400)
  5113. - margin.top - margin.bottom - height2;
  5114. }
  5115. g.select('.nv-legendWrap')
  5116. .attr('transform', 'translate(0,' + (-margin.top) +')')
  5117. }
  5118. //------------------------------------------------------------
  5119. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  5120. //------------------------------------------------------------
  5121. // Main Chart Component(s)
  5122. lines
  5123. .width(availableWidth)
  5124. .height(availableHeight1)
  5125. .color(
  5126. data
  5127. .map(function(d,i) {
  5128. return d.color || color(d, i);
  5129. })
  5130. .filter(function(d,i) {
  5131. return !data[i].disabled;
  5132. })
  5133. );
  5134. lines2
  5135. .defined(lines.defined())
  5136. .width(availableWidth)
  5137. .height(availableHeight2)
  5138. .color(
  5139. data
  5140. .map(function(d,i) {
  5141. return d.color || color(d, i);
  5142. })
  5143. .filter(function(d,i) {
  5144. return !data[i].disabled;
  5145. })
  5146. );
  5147. g.select('.nv-context')
  5148. .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')')
  5149. var contextLinesWrap = g.select('.nv-context .nv-linesWrap')
  5150. .datum(data.filter(function(d) { return !d.disabled }))
  5151. d3.transition(contextLinesWrap).call(lines2);
  5152. //------------------------------------------------------------
  5153. /*
  5154. var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
  5155. .datum(data.filter(function(d) { return !d.disabled }))
  5156. d3.transition(focusLinesWrap).call(lines);
  5157. */
  5158. //------------------------------------------------------------
  5159. // Setup Main (Focus) Axes
  5160. xAxis
  5161. .scale(x)
  5162. .ticks( availableWidth / 100 )
  5163. .tickSize(-availableHeight1, 0);
  5164. yAxis
  5165. .scale(y)
  5166. .ticks( availableHeight1 / 36 )
  5167. .tickSize( -availableWidth, 0);
  5168. g.select('.nv-focus .nv-x.nv-axis')
  5169. .attr('transform', 'translate(0,' + availableHeight1 + ')');
  5170. //------------------------------------------------------------
  5171. //------------------------------------------------------------
  5172. // Setup Brush
  5173. brush
  5174. .x(x2)
  5175. .on('brush', function() {
  5176. //When brushing, turn off transitions because chart needs to change immediately.
  5177. var oldTransition = chart.transitionDuration();
  5178. chart.transitionDuration(0);
  5179. onBrush();
  5180. chart.transitionDuration(oldTransition);
  5181. });
  5182. if (brushExtent) brush.extent(brushExtent);
  5183. var brushBG = g.select('.nv-brushBackground').selectAll('g')
  5184. .data([brushExtent || brush.extent()])
  5185. var brushBGenter = brushBG.enter()
  5186. .append('g');
  5187. brushBGenter.append('rect')
  5188. .attr('class', 'left')
  5189. .attr('x', 0)
  5190. .attr('y', 0)
  5191. .attr('height', availableHeight2);
  5192. brushBGenter.append('rect')
  5193. .attr('class', 'right')
  5194. .attr('x', 0)
  5195. .attr('y', 0)
  5196. .attr('height', availableHeight2);
  5197. var gBrush = g.select('.nv-x.nv-brush')
  5198. .call(brush);
  5199. gBrush.selectAll('rect')
  5200. //.attr('y', -5)
  5201. .attr('height', availableHeight2);
  5202. gBrush.selectAll('.resize').append('path').attr('d', resizePath);
  5203. onBrush();
  5204. //------------------------------------------------------------
  5205. //------------------------------------------------------------
  5206. // Setup Secondary (Context) Axes
  5207. x2Axis
  5208. .scale(x2)
  5209. .ticks( availableWidth / 100 )
  5210. .tickSize(-availableHeight2, 0);
  5211. g.select('.nv-context .nv-x.nv-axis')
  5212. .attr('transform', 'translate(0,' + y2.range()[0] + ')');
  5213. d3.transition(g.select('.nv-context .nv-x.nv-axis'))
  5214. .call(x2Axis);
  5215. y2Axis
  5216. .scale(y2)
  5217. .ticks( availableHeight2 / 36 )
  5218. .tickSize( -availableWidth, 0);
  5219. d3.transition(g.select('.nv-context .nv-y.nv-axis'))
  5220. .call(y2Axis);
  5221. g.select('.nv-context .nv-x.nv-axis')
  5222. .attr('transform', 'translate(0,' + y2.range()[0] + ')');
  5223. //------------------------------------------------------------
  5224. //============================================================
  5225. // Event Handling/Dispatching (in chart's scope)
  5226. //------------------------------------------------------------
  5227. legend.dispatch.on('stateChange', function(newState) {
  5228. chart.update();
  5229. });
  5230. dispatch.on('tooltipShow', function(e) {
  5231. if (tooltips) showTooltip(e, that.parentNode);
  5232. });
  5233. //============================================================
  5234. //============================================================
  5235. // Functions
  5236. //------------------------------------------------------------
  5237. // Taken from crossfilter (http://square.github.com/crossfilter/)
  5238. function resizePath(d) {
  5239. var e = +(d == 'e'),
  5240. x = e ? 1 : -1,
  5241. y = availableHeight2 / 3;
  5242. return 'M' + (.5 * x) + ',' + y
  5243. + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
  5244. + 'V' + (2 * y - 6)
  5245. + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y)
  5246. + 'Z'
  5247. + 'M' + (2.5 * x) + ',' + (y + 8)
  5248. + 'V' + (2 * y - 8)
  5249. + 'M' + (4.5 * x) + ',' + (y + 8)
  5250. + 'V' + (2 * y - 8);
  5251. }
  5252. function updateBrushBG() {
  5253. if (!brush.empty()) brush.extent(brushExtent);
  5254. brushBG
  5255. .data([brush.empty() ? x2.domain() : brushExtent])
  5256. .each(function(d,i) {
  5257. var leftWidth = x2(d[0]) - x.range()[0],
  5258. rightWidth = x.range()[1] - x2(d[1]);
  5259. d3.select(this).select('.left')
  5260. .attr('width', leftWidth < 0 ? 0 : leftWidth);
  5261. d3.select(this).select('.right')
  5262. .attr('x', x2(d[1]))
  5263. .attr('width', rightWidth < 0 ? 0 : rightWidth);
  5264. });
  5265. }
  5266. function onBrush() {
  5267. brushExtent = brush.empty() ? null : brush.extent();
  5268. var extent = brush.empty() ? x2.domain() : brush.extent();
  5269. //The brush extent cannot be less than one. If it is, don't update the line chart.
  5270. if (Math.abs(extent[0] - extent[1]) <= 1) {
  5271. return;
  5272. }
  5273. dispatch.brush({extent: extent, brush: brush});
  5274. updateBrushBG();
  5275. // Update Main (Focus)
  5276. var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
  5277. .datum(
  5278. data
  5279. .filter(function(d) { return !d.disabled })
  5280. .map(function(d,i) {
  5281. return {
  5282. key: d.key,
  5283. values: d.values.filter(function(d,i) {
  5284. return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
  5285. })
  5286. }
  5287. })
  5288. );
  5289. focusLinesWrap.transition().duration(transitionDuration).call(lines);
  5290. // Update Main (Focus) Axes
  5291. g.select('.nv-focus .nv-x.nv-axis').transition().duration(transitionDuration)
  5292. .call(xAxis);
  5293. g.select('.nv-focus .nv-y.nv-axis').transition().duration(transitionDuration)
  5294. .call(yAxis);
  5295. }
  5296. //============================================================
  5297. });
  5298. return chart;
  5299. }
  5300. //============================================================
  5301. // Event Handling/Dispatching (out of chart's scope)
  5302. //------------------------------------------------------------
  5303. lines.dispatch.on('elementMouseover.tooltip', function(e) {
  5304. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  5305. dispatch.tooltipShow(e);
  5306. });
  5307. lines.dispatch.on('elementMouseout.tooltip', function(e) {
  5308. dispatch.tooltipHide(e);
  5309. });
  5310. dispatch.on('tooltipHide', function() {
  5311. if (tooltips) nv.tooltip.cleanup();
  5312. });
  5313. //============================================================
  5314. //============================================================
  5315. // Expose Public Variables
  5316. //------------------------------------------------------------
  5317. // expose chart's sub-components
  5318. chart.dispatch = dispatch;
  5319. chart.legend = legend;
  5320. chart.lines = lines;
  5321. chart.lines2 = lines2;
  5322. chart.xAxis = xAxis;
  5323. chart.yAxis = yAxis;
  5324. chart.x2Axis = x2Axis;
  5325. chart.y2Axis = y2Axis;
  5326. d3.rebind(chart, lines, 'defined', 'isArea', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id');
  5327. chart.options = nv.utils.optionsFunc.bind(chart);
  5328. chart.x = function(_) {
  5329. if (!arguments.length) return lines.x;
  5330. lines.x(_);
  5331. lines2.x(_);
  5332. return chart;
  5333. };
  5334. chart.y = function(_) {
  5335. if (!arguments.length) return lines.y;
  5336. lines.y(_);
  5337. lines2.y(_);
  5338. return chart;
  5339. };
  5340. chart.margin = function(_) {
  5341. if (!arguments.length) return margin;
  5342. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  5343. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  5344. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  5345. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  5346. return chart;
  5347. };
  5348. chart.margin2 = function(_) {
  5349. if (!arguments.length) return margin2;
  5350. margin2 = _;
  5351. return chart;
  5352. };
  5353. chart.width = function(_) {
  5354. if (!arguments.length) return width;
  5355. width = _;
  5356. return chart;
  5357. };
  5358. chart.height = function(_) {
  5359. if (!arguments.length) return height;
  5360. height = _;
  5361. return chart;
  5362. };
  5363. chart.height2 = function(_) {
  5364. if (!arguments.length) return height2;
  5365. height2 = _;
  5366. return chart;
  5367. };
  5368. chart.color = function(_) {
  5369. if (!arguments.length) return color;
  5370. color =nv.utils.getColor(_);
  5371. legend.color(color);
  5372. return chart;
  5373. };
  5374. chart.showLegend = function(_) {
  5375. if (!arguments.length) return showLegend;
  5376. showLegend = _;
  5377. return chart;
  5378. };
  5379. chart.tooltips = function(_) {
  5380. if (!arguments.length) return tooltips;
  5381. tooltips = _;
  5382. return chart;
  5383. };
  5384. chart.tooltipContent = function(_) {
  5385. if (!arguments.length) return tooltip;
  5386. tooltip = _;
  5387. return chart;
  5388. };
  5389. chart.interpolate = function(_) {
  5390. if (!arguments.length) return lines.interpolate();
  5391. lines.interpolate(_);
  5392. lines2.interpolate(_);
  5393. return chart;
  5394. };
  5395. chart.noData = function(_) {
  5396. if (!arguments.length) return noData;
  5397. noData = _;
  5398. return chart;
  5399. };
  5400. // Chart has multiple similar Axes, to prevent code duplication, probably need to link all axis functions manually like below
  5401. chart.xTickFormat = function(_) {
  5402. if (!arguments.length) return xAxis.tickFormat();
  5403. xAxis.tickFormat(_);
  5404. x2Axis.tickFormat(_);
  5405. return chart;
  5406. };
  5407. chart.yTickFormat = function(_) {
  5408. if (!arguments.length) return yAxis.tickFormat();
  5409. yAxis.tickFormat(_);
  5410. y2Axis.tickFormat(_);
  5411. return chart;
  5412. };
  5413. chart.brushExtent = function(_) {
  5414. if (!arguments.length) return brushExtent;
  5415. brushExtent = _;
  5416. return chart;
  5417. };
  5418. chart.transitionDuration = function(_) {
  5419. if (!arguments.length) return transitionDuration;
  5420. transitionDuration = _;
  5421. return chart;
  5422. };
  5423. //============================================================
  5424. return chart;
  5425. }
  5426. nv.models.linePlusBarWithFocusChart = function() {
  5427. "use strict";
  5428. //============================================================
  5429. // Public Variables with Default Settings
  5430. //------------------------------------------------------------
  5431. var lines = nv.models.line()
  5432. , lines2 = nv.models.line()
  5433. , bars = nv.models.historicalBar()
  5434. , bars2 = nv.models.historicalBar()
  5435. , xAxis = nv.models.axis()
  5436. , x2Axis = nv.models.axis()
  5437. , y1Axis = nv.models.axis()
  5438. , y2Axis = nv.models.axis()
  5439. , y3Axis = nv.models.axis()
  5440. , y4Axis = nv.models.axis()
  5441. , legend = nv.models.legend()
  5442. , brush = d3.svg.brush()
  5443. ;
  5444. var margin = {top: 30, right: 30, bottom: 30, left: 60}
  5445. , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
  5446. , width = null
  5447. , height = null
  5448. , height2 = 100
  5449. , getX = function(d) { return d.x }
  5450. , getY = function(d) { return d.y }
  5451. , color = nv.utils.defaultColor()
  5452. , showLegend = true
  5453. , extent
  5454. , brushExtent = null
  5455. , tooltips = true
  5456. , tooltip = function(key, x, y, e, graph) {
  5457. return '<h3>' + key + '</h3>' +
  5458. '<p>' + y + ' at ' + x + '</p>';
  5459. }
  5460. , x
  5461. , x2
  5462. , y1
  5463. , y2
  5464. , y3
  5465. , y4
  5466. , noData = "No Data Available."
  5467. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush')
  5468. , transitionDuration = 0
  5469. ;
  5470. lines
  5471. .clipEdge(true)
  5472. ;
  5473. lines2
  5474. .interactive(false)
  5475. ;
  5476. xAxis
  5477. .orient('bottom')
  5478. .tickPadding(5)
  5479. ;
  5480. y1Axis
  5481. .orient('left')
  5482. ;
  5483. y2Axis
  5484. .orient('right')
  5485. ;
  5486. x2Axis
  5487. .orient('bottom')
  5488. .tickPadding(5)
  5489. ;
  5490. y3Axis
  5491. .orient('left')
  5492. ;
  5493. y4Axis
  5494. .orient('right')
  5495. ;
  5496. //============================================================
  5497. //============================================================
  5498. // Private Variables
  5499. //------------------------------------------------------------
  5500. var showTooltip = function(e, offsetElement) {
  5501. if (extent) {
  5502. e.pointIndex += Math.ceil(extent[0]);
  5503. }
  5504. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  5505. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  5506. x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
  5507. y = (e.series.bar ? y1Axis : y2Axis).tickFormat()(lines.y()(e.point, e.pointIndex)),
  5508. content = tooltip(e.series.key, x, y, e, chart);
  5509. nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
  5510. };
  5511. //------------------------------------------------------------
  5512. function chart(selection) {
  5513. selection.each(function(data) {
  5514. var container = d3.select(this),
  5515. that = this;
  5516. var availableWidth = (width || parseInt(container.style('width')) || 960)
  5517. - margin.left - margin.right,
  5518. availableHeight1 = (height || parseInt(container.style('height')) || 400)
  5519. - margin.top - margin.bottom - height2,
  5520. availableHeight2 = height2 - margin2.top - margin2.bottom;
  5521. chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
  5522. chart.container = this;
  5523. //------------------------------------------------------------
  5524. // Display No Data message if there's nothing to show.
  5525. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  5526. var noDataText = container.selectAll('.nv-noData').data([noData]);
  5527. noDataText.enter().append('text')
  5528. .attr('class', 'nvd3 nv-noData')
  5529. .attr('dy', '-.7em')
  5530. .style('text-anchor', 'middle');
  5531. noDataText
  5532. .attr('x', margin.left + availableWidth / 2)
  5533. .attr('y', margin.top + availableHeight1 / 2)
  5534. .text(function(d) { return d });
  5535. return chart;
  5536. } else {
  5537. container.selectAll('.nv-noData').remove();
  5538. }
  5539. //------------------------------------------------------------
  5540. //------------------------------------------------------------
  5541. // Setup Scales
  5542. var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
  5543. var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
  5544. x = bars.xScale();
  5545. x2 = x2Axis.scale();
  5546. y1 = bars.yScale();
  5547. y2 = lines.yScale();
  5548. y3 = bars2.yScale();
  5549. y4 = lines2.yScale();
  5550. var series1 = data
  5551. .filter(function(d) { return !d.disabled && d.bar })
  5552. .map(function(d) {
  5553. return d.values.map(function(d,i) {
  5554. return { x: getX(d,i), y: getY(d,i) }
  5555. })
  5556. });
  5557. var series2 = data
  5558. .filter(function(d) { return !d.disabled && !d.bar })
  5559. .map(function(d) {
  5560. return d.values.map(function(d,i) {
  5561. return { x: getX(d,i), y: getY(d,i) }
  5562. })
  5563. });
  5564. x .range([0, availableWidth]);
  5565. x2 .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
  5566. .range([0, availableWidth]);
  5567. //------------------------------------------------------------
  5568. //------------------------------------------------------------
  5569. // Setup containers and skeleton of chart
  5570. var wrap = container.selectAll('g.nv-wrap.nv-linePlusBar').data([data]);
  5571. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-linePlusBar').append('g');
  5572. var g = wrap.select('g');
  5573. gEnter.append('g').attr('class', 'nv-legendWrap');
  5574. var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
  5575. focusEnter.append('g').attr('class', 'nv-x nv-axis');
  5576. focusEnter.append('g').attr('class', 'nv-y1 nv-axis');
  5577. focusEnter.append('g').attr('class', 'nv-y2 nv-axis');
  5578. focusEnter.append('g').attr('class', 'nv-barsWrap');
  5579. focusEnter.append('g').attr('class', 'nv-linesWrap');
  5580. var contextEnter = gEnter.append('g').attr('class', 'nv-context');
  5581. contextEnter.append('g').attr('class', 'nv-x nv-axis');
  5582. contextEnter.append('g').attr('class', 'nv-y1 nv-axis');
  5583. contextEnter.append('g').attr('class', 'nv-y2 nv-axis');
  5584. contextEnter.append('g').attr('class', 'nv-barsWrap');
  5585. contextEnter.append('g').attr('class', 'nv-linesWrap');
  5586. contextEnter.append('g').attr('class', 'nv-brushBackground');
  5587. contextEnter.append('g').attr('class', 'nv-x nv-brush');
  5588. //------------------------------------------------------------
  5589. //------------------------------------------------------------
  5590. // Legend
  5591. if (showLegend) {
  5592. legend.width( availableWidth / 2 );
  5593. g.select('.nv-legendWrap')
  5594. .datum(data.map(function(series) {
  5595. series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
  5596. series.key = series.originalKey + (series.bar ? ' (left axis)' : ' (right axis)');
  5597. return series;
  5598. }))
  5599. .call(legend);
  5600. if ( margin.top != legend.height()) {
  5601. margin.top = legend.height();
  5602. availableHeight1 = (height || parseInt(container.style('height')) || 400)
  5603. - margin.top - margin.bottom - height2;
  5604. }
  5605. g.select('.nv-legendWrap')
  5606. .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
  5607. }
  5608. //------------------------------------------------------------
  5609. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  5610. //------------------------------------------------------------
  5611. // Context Components
  5612. bars2
  5613. .width(availableWidth)
  5614. .height(availableHeight2)
  5615. .color(data.map(function(d,i) {
  5616. return d.color || color(d, i);
  5617. }).filter(function(d,i) { return !data[i].disabled && data[i].bar }));
  5618. lines2
  5619. .width(availableWidth)
  5620. .height(availableHeight2)
  5621. .color(data.map(function(d,i) {
  5622. return d.color || color(d, i);
  5623. }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }));
  5624. var bars2Wrap = g.select('.nv-context .nv-barsWrap')
  5625. .datum(dataBars.length ? dataBars : [{values:[]}]);
  5626. var lines2Wrap = g.select('.nv-context .nv-linesWrap')
  5627. .datum(!dataLines[0].disabled ? dataLines : [{values:[]}]);
  5628. g.select('.nv-context')
  5629. .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')')
  5630. bars2Wrap.transition().call(bars2);
  5631. lines2Wrap.transition().call(lines2);
  5632. //------------------------------------------------------------
  5633. //------------------------------------------------------------
  5634. // Setup Brush
  5635. brush
  5636. .x(x2)
  5637. .on('brush', onBrush);
  5638. if (brushExtent) brush.extent(brushExtent);
  5639. var brushBG = g.select('.nv-brushBackground').selectAll('g')
  5640. .data([brushExtent || brush.extent()])
  5641. var brushBGenter = brushBG.enter()
  5642. .append('g');
  5643. brushBGenter.append('rect')
  5644. .attr('class', 'left')
  5645. .attr('x', 0)
  5646. .attr('y', 0)
  5647. .attr('height', availableHeight2);
  5648. brushBGenter.append('rect')
  5649. .attr('class', 'right')
  5650. .attr('x', 0)
  5651. .attr('y', 0)
  5652. .attr('height', availableHeight2);
  5653. var gBrush = g.select('.nv-x.nv-brush')
  5654. .call(brush);
  5655. gBrush.selectAll('rect')
  5656. //.attr('y', -5)
  5657. .attr('height', availableHeight2);
  5658. gBrush.selectAll('.resize').append('path').attr('d', resizePath);
  5659. //------------------------------------------------------------
  5660. //------------------------------------------------------------
  5661. // Setup Secondary (Context) Axes
  5662. x2Axis
  5663. .ticks( availableWidth / 100 )
  5664. .tickSize(-availableHeight2, 0);
  5665. g.select('.nv-context .nv-x.nv-axis')
  5666. .attr('transform', 'translate(0,' + y3.range()[0] + ')');
  5667. g.select('.nv-context .nv-x.nv-axis').transition()
  5668. .call(x2Axis);
  5669. y3Axis
  5670. .scale(y3)
  5671. .ticks( availableHeight2 / 36 )
  5672. .tickSize( -availableWidth, 0);
  5673. g.select('.nv-context .nv-y1.nv-axis')
  5674. .style('opacity', dataBars.length ? 1 : 0)
  5675. .attr('transform', 'translate(0,' + x2.range()[0] + ')');
  5676. g.select('.nv-context .nv-y1.nv-axis').transition()
  5677. .call(y3Axis);
  5678. y4Axis
  5679. .scale(y4)
  5680. .ticks( availableHeight2 / 36 )
  5681. .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
  5682. g.select('.nv-context .nv-y2.nv-axis')
  5683. .style('opacity', dataLines.length ? 1 : 0)
  5684. .attr('transform', 'translate(' + x2.range()[1] + ',0)');
  5685. g.select('.nv-context .nv-y2.nv-axis').transition()
  5686. .call(y4Axis);
  5687. //------------------------------------------------------------
  5688. //============================================================
  5689. // Event Handling/Dispatching (in chart's scope)
  5690. //------------------------------------------------------------
  5691. legend.dispatch.on('stateChange', function(newState) {
  5692. chart.update();
  5693. });
  5694. dispatch.on('tooltipShow', function(e) {
  5695. if (tooltips) showTooltip(e, that.parentNode);
  5696. });
  5697. //============================================================
  5698. //============================================================
  5699. // Functions
  5700. //------------------------------------------------------------
  5701. // Taken from crossfilter (http://square.github.com/crossfilter/)
  5702. function resizePath(d) {
  5703. var e = +(d == 'e'),
  5704. x = e ? 1 : -1,
  5705. y = availableHeight2 / 3;
  5706. return 'M' + (.5 * x) + ',' + y
  5707. + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
  5708. + 'V' + (2 * y - 6)
  5709. + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y)
  5710. + 'Z'
  5711. + 'M' + (2.5 * x) + ',' + (y + 8)
  5712. + 'V' + (2 * y - 8)
  5713. + 'M' + (4.5 * x) + ',' + (y + 8)
  5714. + 'V' + (2 * y - 8);
  5715. }
  5716. function updateBrushBG() {
  5717. if (!brush.empty()) brush.extent(brushExtent);
  5718. brushBG
  5719. .data([brush.empty() ? x2.domain() : brushExtent])
  5720. .each(function(d,i) {
  5721. var leftWidth = x2(d[0]) - x2.range()[0],
  5722. rightWidth = x2.range()[1] - x2(d[1]);
  5723. d3.select(this).select('.left')
  5724. .attr('width', leftWidth < 0 ? 0 : leftWidth);
  5725. d3.select(this).select('.right')
  5726. .attr('x', x2(d[1]))
  5727. .attr('width', rightWidth < 0 ? 0 : rightWidth);
  5728. });
  5729. }
  5730. function onBrush() {
  5731. brushExtent = brush.empty() ? null : brush.extent();
  5732. extent = brush.empty() ? x2.domain() : brush.extent();
  5733. dispatch.brush({extent: extent, brush: brush});
  5734. updateBrushBG();
  5735. //------------------------------------------------------------
  5736. // Prepare Main (Focus) Bars and Lines
  5737. bars
  5738. .width(availableWidth)
  5739. .height(availableHeight1)
  5740. .color(data.map(function(d,i) {
  5741. return d.color || color(d, i);
  5742. }).filter(function(d,i) { return !data[i].disabled && data[i].bar }));
  5743. lines
  5744. .width(availableWidth)
  5745. .height(availableHeight1)
  5746. .color(data.map(function(d,i) {
  5747. return d.color || color(d, i);
  5748. }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }));
  5749. var focusBarsWrap = g.select('.nv-focus .nv-barsWrap')
  5750. .datum(!dataBars.length ? [{values:[]}] :
  5751. dataBars
  5752. .map(function(d,i) {
  5753. return {
  5754. key: d.key,
  5755. values: d.values.filter(function(d,i) {
  5756. return bars.x()(d,i) >= extent[0] && bars.x()(d,i) <= extent[1];
  5757. })
  5758. }
  5759. })
  5760. );
  5761. var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
  5762. .datum(dataLines[0].disabled ? [{values:[]}] :
  5763. dataLines
  5764. .map(function(d,i) {
  5765. return {
  5766. key: d.key,
  5767. values: d.values.filter(function(d,i) {
  5768. return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
  5769. })
  5770. }
  5771. })
  5772. );
  5773. //------------------------------------------------------------
  5774. //------------------------------------------------------------
  5775. // Update Main (Focus) X Axis
  5776. if (dataBars.length) {
  5777. x = bars.xScale();
  5778. } else {
  5779. x = lines.xScale();
  5780. }
  5781. xAxis
  5782. .scale(x)
  5783. .ticks( availableWidth / 100 )
  5784. .tickSize(-availableHeight1, 0);
  5785. xAxis.domain([Math.ceil(extent[0]), Math.floor(extent[1])]);
  5786. g.select('.nv-x.nv-axis').transition().duration(transitionDuration)
  5787. .call(xAxis);
  5788. //------------------------------------------------------------
  5789. //------------------------------------------------------------
  5790. // Update Main (Focus) Bars and Lines
  5791. focusBarsWrap.transition().duration(transitionDuration).call(bars);
  5792. focusLinesWrap.transition().duration(transitionDuration).call(lines);
  5793. //------------------------------------------------------------
  5794. //------------------------------------------------------------
  5795. // Setup and Update Main (Focus) Y Axes
  5796. g.select('.nv-focus .nv-x.nv-axis')
  5797. .attr('transform', 'translate(0,' + y1.range()[0] + ')');
  5798. y1Axis
  5799. .scale(y1)
  5800. .ticks( availableHeight1 / 36 )
  5801. .tickSize(-availableWidth, 0);
  5802. g.select('.nv-focus .nv-y1.nv-axis')
  5803. .style('opacity', dataBars.length ? 1 : 0);
  5804. y2Axis
  5805. .scale(y2)
  5806. .ticks( availableHeight1 / 36 )
  5807. .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
  5808. g.select('.nv-focus .nv-y2.nv-axis')
  5809. .style('opacity', dataLines.length ? 1 : 0)
  5810. .attr('transform', 'translate(' + x.range()[1] + ',0)');
  5811. g.select('.nv-focus .nv-y1.nv-axis').transition().duration(transitionDuration)
  5812. .call(y1Axis);
  5813. g.select('.nv-focus .nv-y2.nv-axis').transition().duration(transitionDuration)
  5814. .call(y2Axis);
  5815. }
  5816. //============================================================
  5817. onBrush();
  5818. });
  5819. return chart;
  5820. }
  5821. //============================================================
  5822. // Event Handling/Dispatching (out of chart's scope)
  5823. //------------------------------------------------------------
  5824. lines.dispatch.on('elementMouseover.tooltip', function(e) {
  5825. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  5826. dispatch.tooltipShow(e);
  5827. });
  5828. lines.dispatch.on('elementMouseout.tooltip', function(e) {
  5829. dispatch.tooltipHide(e);
  5830. });
  5831. bars.dispatch.on('elementMouseover.tooltip', function(e) {
  5832. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  5833. dispatch.tooltipShow(e);
  5834. });
  5835. bars.dispatch.on('elementMouseout.tooltip', function(e) {
  5836. dispatch.tooltipHide(e);
  5837. });
  5838. dispatch.on('tooltipHide', function() {
  5839. if (tooltips) nv.tooltip.cleanup();
  5840. });
  5841. //============================================================
  5842. //============================================================
  5843. // Expose Public Variables
  5844. //------------------------------------------------------------
  5845. // expose chart's sub-components
  5846. chart.dispatch = dispatch;
  5847. chart.legend = legend;
  5848. chart.lines = lines;
  5849. chart.lines2 = lines2;
  5850. chart.bars = bars;
  5851. chart.bars2 = bars2;
  5852. chart.xAxis = xAxis;
  5853. chart.x2Axis = x2Axis;
  5854. chart.y1Axis = y1Axis;
  5855. chart.y2Axis = y2Axis;
  5856. chart.y3Axis = y3Axis;
  5857. chart.y4Axis = y4Axis;
  5858. d3.rebind(chart, lines, 'defined', 'size', 'clipVoronoi', 'interpolate');
  5859. //TODO: consider rebinding x, y and some other stuff, and simply do soemthign lile bars.x(lines.x()), etc.
  5860. //d3.rebind(chart, lines, 'x', 'y', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id');
  5861. chart.options = nv.utils.optionsFunc.bind(chart);
  5862. chart.x = function(_) {
  5863. if (!arguments.length) return getX;
  5864. getX = _;
  5865. lines.x(_);
  5866. bars.x(_);
  5867. return chart;
  5868. };
  5869. chart.y = function(_) {
  5870. if (!arguments.length) return getY;
  5871. getY = _;
  5872. lines.y(_);
  5873. bars.y(_);
  5874. return chart;
  5875. };
  5876. chart.margin = function(_) {
  5877. if (!arguments.length) return margin;
  5878. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  5879. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  5880. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  5881. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  5882. return chart;
  5883. };
  5884. chart.width = function(_) {
  5885. if (!arguments.length) return width;
  5886. width = _;
  5887. return chart;
  5888. };
  5889. chart.height = function(_) {
  5890. if (!arguments.length) return height;
  5891. height = _;
  5892. return chart;
  5893. };
  5894. chart.color = function(_) {
  5895. if (!arguments.length) return color;
  5896. color = nv.utils.getColor(_);
  5897. legend.color(color);
  5898. return chart;
  5899. };
  5900. chart.showLegend = function(_) {
  5901. if (!arguments.length) return showLegend;
  5902. showLegend = _;
  5903. return chart;
  5904. };
  5905. chart.tooltips = function(_) {
  5906. if (!arguments.length) return tooltips;
  5907. tooltips = _;
  5908. return chart;
  5909. };
  5910. chart.tooltipContent = function(_) {
  5911. if (!arguments.length) return tooltip;
  5912. tooltip = _;
  5913. return chart;
  5914. };
  5915. chart.noData = function(_) {
  5916. if (!arguments.length) return noData;
  5917. noData = _;
  5918. return chart;
  5919. };
  5920. chart.brushExtent = function(_) {
  5921. if (!arguments.length) return brushExtent;
  5922. brushExtent = _;
  5923. return chart;
  5924. };
  5925. //============================================================
  5926. return chart;
  5927. }
  5928. nv.models.multiBar = function() {
  5929. "use strict";
  5930. //============================================================
  5931. // Public Variables with Default Settings
  5932. //------------------------------------------------------------
  5933. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  5934. , width = 960
  5935. , height = 500
  5936. , x = d3.scale.ordinal()
  5937. , y = d3.scale.linear()
  5938. , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
  5939. , getX = function(d) { return d.x }
  5940. , getY = function(d) { return d.y }
  5941. , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
  5942. , clipEdge = true
  5943. , stacked = false
  5944. , stackOffset = 'zero' // options include 'silhouette', 'wiggle', 'expand', 'zero', or a custom function
  5945. , color = nv.utils.defaultColor()
  5946. , hideable = false
  5947. , barColor = null // adding the ability to set the color for each rather than the whole group
  5948. , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
  5949. , delay = 1200
  5950. , xDomain
  5951. , yDomain
  5952. , xRange
  5953. , yRange
  5954. , groupSpacing = 0.1
  5955. , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
  5956. ;
  5957. //============================================================
  5958. //============================================================
  5959. // Private Variables
  5960. //------------------------------------------------------------
  5961. var x0, y0 //used to store previous scales
  5962. ;
  5963. //============================================================
  5964. function chart(selection) {
  5965. selection.each(function(data) {
  5966. var availableWidth = width - margin.left - margin.right,
  5967. availableHeight = height - margin.top - margin.bottom,
  5968. container = d3.select(this);
  5969. if(hideable && data.length) hideable = [{
  5970. values: data[0].values.map(function(d) {
  5971. return {
  5972. x: d.x,
  5973. y: 0,
  5974. series: d.series,
  5975. size: 0.01
  5976. };}
  5977. )}];
  5978. if (stacked)
  5979. data = d3.layout.stack()
  5980. .offset(stackOffset)
  5981. .values(function(d){ return d.values })
  5982. .y(getY)
  5983. (!data.length && hideable ? hideable : data);
  5984. //add series index to each data point for reference
  5985. data.forEach(function(series, i) {
  5986. series.values.forEach(function(point) {
  5987. point.series = i;
  5988. });
  5989. });
  5990. //------------------------------------------------------------
  5991. // HACK for negative value stacking
  5992. if (stacked)
  5993. data[0].values.map(function(d,i) {
  5994. var posBase = 0, negBase = 0;
  5995. data.map(function(d) {
  5996. var f = d.values[i]
  5997. f.size = Math.abs(f.y);
  5998. if (f.y<0) {
  5999. f.y1 = negBase;
  6000. negBase = negBase - f.size;
  6001. } else
  6002. {
  6003. f.y1 = f.size + posBase;
  6004. posBase = posBase + f.size;
  6005. }
  6006. });
  6007. });
  6008. //------------------------------------------------------------
  6009. // Setup Scales
  6010. // remap and flatten the data for use in calculating the scales' domains
  6011. var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
  6012. data.map(function(d) {
  6013. return d.values.map(function(d,i) {
  6014. return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 }
  6015. })
  6016. });
  6017. x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
  6018. .rangeBands(xRange || [0, availableWidth], groupSpacing);
  6019. //y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y1 : 0) }).concat(forceY)))
  6020. y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return stacked ? (d.y > 0 ? d.y1 : d.y1 + d.y ) : d.y }).concat(forceY)))
  6021. .range(yRange || [availableHeight, 0]);
  6022. // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
  6023. if (x.domain()[0] === x.domain()[1])
  6024. x.domain()[0] ?
  6025. x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
  6026. : x.domain([-1,1]);
  6027. if (y.domain()[0] === y.domain()[1])
  6028. y.domain()[0] ?
  6029. y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
  6030. : y.domain([-1,1]);
  6031. x0 = x0 || x;
  6032. y0 = y0 || y;
  6033. //------------------------------------------------------------
  6034. //------------------------------------------------------------
  6035. // Setup containers and skeleton of chart
  6036. var wrap = container.selectAll('g.nv-wrap.nv-multibar').data([data]);
  6037. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibar');
  6038. var defsEnter = wrapEnter.append('defs');
  6039. var gEnter = wrapEnter.append('g');
  6040. var g = wrap.select('g')
  6041. gEnter.append('g').attr('class', 'nv-groups');
  6042. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  6043. //------------------------------------------------------------
  6044. defsEnter.append('clipPath')
  6045. .attr('id', 'nv-edge-clip-' + id)
  6046. .append('rect');
  6047. wrap.select('#nv-edge-clip-' + id + ' rect')
  6048. .attr('width', availableWidth)
  6049. .attr('height', availableHeight);
  6050. g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
  6051. var groups = wrap.select('.nv-groups').selectAll('.nv-group')
  6052. .data(function(d) { return d }, function(d,i) { return i });
  6053. groups.enter().append('g')
  6054. .style('stroke-opacity', 1e-6)
  6055. .style('fill-opacity', 1e-6);
  6056. groups.exit()
  6057. .transition()
  6058. .selectAll('rect.nv-bar')
  6059. .delay(function(d,i) {
  6060. return i * delay/ data[0].values.length;
  6061. })
  6062. .attr('y', function(d) { return stacked ? y0(d.y0) : y0(0) })
  6063. .attr('height', 0)
  6064. .remove();
  6065. groups
  6066. .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
  6067. .classed('hover', function(d) { return d.hover })
  6068. .style('fill', function(d,i){ return color(d, i) })
  6069. .style('stroke', function(d,i){ return color(d, i) });
  6070. groups
  6071. .transition()
  6072. .style('stroke-opacity', 1)
  6073. .style('fill-opacity', .75);
  6074. var bars = groups.selectAll('rect.nv-bar')
  6075. .data(function(d) { return (hideable && !data.length) ? hideable.values : d.values });
  6076. bars.exit().remove();
  6077. var barsEnter = bars.enter().append('rect')
  6078. .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
  6079. .attr('x', function(d,i,j) {
  6080. return stacked ? 0 : (j * x.rangeBand() / data.length )
  6081. })
  6082. .attr('y', function(d) { return y0(stacked ? d.y0 : 0) })
  6083. .attr('height', 0)
  6084. .attr('width', x.rangeBand() / (stacked ? 1 : data.length) )
  6085. .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
  6086. ;
  6087. bars
  6088. .style('fill', function(d,i,j){ return color(d, j, i); })
  6089. .style('stroke', function(d,i,j){ return color(d, j, i); })
  6090. .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
  6091. d3.select(this).classed('hover', true);
  6092. dispatch.elementMouseover({
  6093. value: getY(d,i),
  6094. point: d,
  6095. series: data[d.series],
  6096. pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
  6097. pointIndex: i,
  6098. seriesIndex: d.series,
  6099. e: d3.event
  6100. });
  6101. })
  6102. .on('mouseout', function(d,i) {
  6103. d3.select(this).classed('hover', false);
  6104. dispatch.elementMouseout({
  6105. value: getY(d,i),
  6106. point: d,
  6107. series: data[d.series],
  6108. pointIndex: i,
  6109. seriesIndex: d.series,
  6110. e: d3.event
  6111. });
  6112. })
  6113. .on('click', function(d,i) {
  6114. dispatch.elementClick({
  6115. value: getY(d,i),
  6116. point: d,
  6117. series: data[d.series],
  6118. pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
  6119. pointIndex: i,
  6120. seriesIndex: d.series,
  6121. e: d3.event
  6122. });
  6123. d3.event.stopPropagation();
  6124. })
  6125. .on('dblclick', function(d,i) {
  6126. dispatch.elementDblClick({
  6127. value: getY(d,i),
  6128. point: d,
  6129. series: data[d.series],
  6130. pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
  6131. pointIndex: i,
  6132. seriesIndex: d.series,
  6133. e: d3.event
  6134. });
  6135. d3.event.stopPropagation();
  6136. });
  6137. bars
  6138. .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
  6139. .transition()
  6140. .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
  6141. if (barColor) {
  6142. if (!disabled) disabled = data.map(function() { return true });
  6143. bars
  6144. .style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); })
  6145. .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
  6146. }
  6147. if (stacked)
  6148. bars.transition()
  6149. .delay(function(d,i) {
  6150. return i * delay / data[0].values.length;
  6151. })
  6152. .attr('y', function(d,i) {
  6153. return y((stacked ? d.y1 : 0));
  6154. })
  6155. .attr('height', function(d,i) {
  6156. return Math.max(Math.abs(y(d.y + (stacked ? d.y0 : 0)) - y((stacked ? d.y0 : 0))),1);
  6157. })
  6158. .attr('x', function(d,i) {
  6159. return stacked ? 0 : (d.series * x.rangeBand() / data.length )
  6160. })
  6161. .attr('width', x.rangeBand() / (stacked ? 1 : data.length) );
  6162. else
  6163. bars.transition()
  6164. .delay(function(d,i) {
  6165. return i * delay/ data[0].values.length;
  6166. })
  6167. .attr('x', function(d,i) {
  6168. return d.series * x.rangeBand() / data.length
  6169. })
  6170. .attr('width', x.rangeBand() / data.length)
  6171. .attr('y', function(d,i) {
  6172. return getY(d,i) < 0 ?
  6173. y(0) :
  6174. y(0) - y(getY(d,i)) < 1 ?
  6175. y(0) - 1 :
  6176. y(getY(d,i)) || 0;
  6177. })
  6178. .attr('height', function(d,i) {
  6179. return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0;
  6180. });
  6181. //store old scales for use in transitions on update
  6182. x0 = x.copy();
  6183. y0 = y.copy();
  6184. });
  6185. return chart;
  6186. }
  6187. //============================================================
  6188. // Expose Public Variables
  6189. //------------------------------------------------------------
  6190. chart.dispatch = dispatch;
  6191. chart.options = nv.utils.optionsFunc.bind(chart);
  6192. chart.x = function(_) {
  6193. if (!arguments.length) return getX;
  6194. getX = _;
  6195. return chart;
  6196. };
  6197. chart.y = function(_) {
  6198. if (!arguments.length) return getY;
  6199. getY = _;
  6200. return chart;
  6201. };
  6202. chart.margin = function(_) {
  6203. if (!arguments.length) return margin;
  6204. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  6205. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  6206. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  6207. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  6208. return chart;
  6209. };
  6210. chart.width = function(_) {
  6211. if (!arguments.length) return width;
  6212. width = _;
  6213. return chart;
  6214. };
  6215. chart.height = function(_) {
  6216. if (!arguments.length) return height;
  6217. height = _;
  6218. return chart;
  6219. };
  6220. chart.xScale = function(_) {
  6221. if (!arguments.length) return x;
  6222. x = _;
  6223. return chart;
  6224. };
  6225. chart.yScale = function(_) {
  6226. if (!arguments.length) return y;
  6227. y = _;
  6228. return chart;
  6229. };
  6230. chart.xDomain = function(_) {
  6231. if (!arguments.length) return xDomain;
  6232. xDomain = _;
  6233. return chart;
  6234. };
  6235. chart.yDomain = function(_) {
  6236. if (!arguments.length) return yDomain;
  6237. yDomain = _;
  6238. return chart;
  6239. };
  6240. chart.xRange = function(_) {
  6241. if (!arguments.length) return xRange;
  6242. xRange = _;
  6243. return chart;
  6244. };
  6245. chart.yRange = function(_) {
  6246. if (!arguments.length) return yRange;
  6247. yRange = _;
  6248. return chart;
  6249. };
  6250. chart.forceY = function(_) {
  6251. if (!arguments.length) return forceY;
  6252. forceY = _;
  6253. return chart;
  6254. };
  6255. chart.stacked = function(_) {
  6256. if (!arguments.length) return stacked;
  6257. stacked = _;
  6258. return chart;
  6259. };
  6260. chart.stackOffset = function(_) {
  6261. if (!arguments.length) return stackOffset;
  6262. stackOffset = _;
  6263. return chart;
  6264. };
  6265. chart.clipEdge = function(_) {
  6266. if (!arguments.length) return clipEdge;
  6267. clipEdge = _;
  6268. return chart;
  6269. };
  6270. chart.color = function(_) {
  6271. if (!arguments.length) return color;
  6272. color = nv.utils.getColor(_);
  6273. return chart;
  6274. };
  6275. chart.barColor = function(_) {
  6276. if (!arguments.length) return barColor;
  6277. barColor = nv.utils.getColor(_);
  6278. return chart;
  6279. };
  6280. chart.disabled = function(_) {
  6281. if (!arguments.length) return disabled;
  6282. disabled = _;
  6283. return chart;
  6284. };
  6285. chart.id = function(_) {
  6286. if (!arguments.length) return id;
  6287. id = _;
  6288. return chart;
  6289. };
  6290. chart.hideable = function(_) {
  6291. if (!arguments.length) return hideable;
  6292. hideable = _;
  6293. return chart;
  6294. };
  6295. chart.delay = function(_) {
  6296. if (!arguments.length) return delay;
  6297. delay = _;
  6298. return chart;
  6299. };
  6300. chart.groupSpacing = function(_) {
  6301. if (!arguments.length) return groupSpacing;
  6302. groupSpacing = _;
  6303. return chart;
  6304. };
  6305. //============================================================
  6306. return chart;
  6307. }
  6308. nv.models.multiBarChart = function() {
  6309. "use strict";
  6310. //============================================================
  6311. // Public Variables with Default Settings
  6312. //------------------------------------------------------------
  6313. var multibar = nv.models.multiBar()
  6314. , xAxis = nv.models.axis()
  6315. , yAxis = nv.models.axis()
  6316. , legend = nv.models.legend()
  6317. , controls = nv.models.legend()
  6318. ;
  6319. var margin = {top: 30, right: 20, bottom: 50, left: 60}
  6320. , width = null
  6321. , height = null
  6322. , color = nv.utils.defaultColor()
  6323. , showControls = true
  6324. , showLegend = true
  6325. , showXAxis = true
  6326. , showYAxis = true
  6327. , rightAlignYAxis = false
  6328. , reduceXTicks = true // if false a tick will show for every data point
  6329. , staggerLabels = false
  6330. , rotateLabels = 0
  6331. , tooltips = true
  6332. , tooltip = function(key, x, y, e, graph) {
  6333. return '<h3>' + key + '</h3>' +
  6334. '<p>' + y + ' on ' + x + '</p>'
  6335. }
  6336. , x //can be accessed via chart.xScale()
  6337. , y //can be accessed via chart.yScale()
  6338. , state = { stacked: false }
  6339. , defaultState = null
  6340. , noData = "No Data Available."
  6341. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
  6342. , controlWidth = function() { return showControls ? 180 : 0 }
  6343. , transitionDuration = 250
  6344. ;
  6345. multibar
  6346. .stacked(false)
  6347. ;
  6348. xAxis
  6349. .orient('bottom')
  6350. .tickPadding(7)
  6351. .highlightZero(true)
  6352. .showMaxMin(false)
  6353. .tickFormat(function(d) { return d })
  6354. ;
  6355. yAxis
  6356. .orient((rightAlignYAxis) ? 'right' : 'left')
  6357. .tickFormat(d3.format(',.1f'))
  6358. ;
  6359. controls.updateState(false);
  6360. //============================================================
  6361. //============================================================
  6362. // Private Variables
  6363. //------------------------------------------------------------
  6364. var showTooltip = function(e, offsetElement) {
  6365. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  6366. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  6367. x = xAxis.tickFormat()(multibar.x()(e.point, e.pointIndex)),
  6368. y = yAxis.tickFormat()(multibar.y()(e.point, e.pointIndex)),
  6369. content = tooltip(e.series.key, x, y, e, chart);
  6370. nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
  6371. };
  6372. //============================================================
  6373. function chart(selection) {
  6374. selection.each(function(data) {
  6375. var container = d3.select(this),
  6376. that = this;
  6377. var availableWidth = (width || parseInt(container.style('width')) || 960)
  6378. - margin.left - margin.right,
  6379. availableHeight = (height || parseInt(container.style('height')) || 400)
  6380. - margin.top - margin.bottom;
  6381. chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
  6382. chart.container = this;
  6383. //set state.disabled
  6384. state.disabled = data.map(function(d) { return !!d.disabled });
  6385. if (!defaultState) {
  6386. var key;
  6387. defaultState = {};
  6388. for (key in state) {
  6389. if (state[key] instanceof Array)
  6390. defaultState[key] = state[key].slice(0);
  6391. else
  6392. defaultState[key] = state[key];
  6393. }
  6394. }
  6395. //------------------------------------------------------------
  6396. // Display noData message if there's nothing to show.
  6397. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  6398. var noDataText = container.selectAll('.nv-noData').data([noData]);
  6399. noDataText.enter().append('text')
  6400. .attr('class', 'nvd3 nv-noData')
  6401. .attr('dy', '-.7em')
  6402. .style('text-anchor', 'middle');
  6403. noDataText
  6404. .attr('x', margin.left + availableWidth / 2)
  6405. .attr('y', margin.top + availableHeight / 2)
  6406. .text(function(d) { return d });
  6407. return chart;
  6408. } else {
  6409. container.selectAll('.nv-noData').remove();
  6410. }
  6411. //------------------------------------------------------------
  6412. //------------------------------------------------------------
  6413. // Setup Scales
  6414. x = multibar.xScale();
  6415. y = multibar.yScale();
  6416. //------------------------------------------------------------
  6417. //------------------------------------------------------------
  6418. // Setup containers and skeleton of chart
  6419. var wrap = container.selectAll('g.nv-wrap.nv-multiBarWithLegend').data([data]);
  6420. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarWithLegend').append('g');
  6421. var g = wrap.select('g');
  6422. gEnter.append('g').attr('class', 'nv-x nv-axis');
  6423. gEnter.append('g').attr('class', 'nv-y nv-axis');
  6424. gEnter.append('g').attr('class', 'nv-barsWrap');
  6425. gEnter.append('g').attr('class', 'nv-legendWrap');
  6426. gEnter.append('g').attr('class', 'nv-controlsWrap');
  6427. //------------------------------------------------------------
  6428. //------------------------------------------------------------
  6429. // Legend
  6430. if (showLegend) {
  6431. legend.width(availableWidth - controlWidth());
  6432. if (multibar.barColor())
  6433. data.forEach(function(series,i) {
  6434. series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
  6435. })
  6436. g.select('.nv-legendWrap')
  6437. .datum(data)
  6438. .call(legend);
  6439. if ( margin.top != legend.height()) {
  6440. margin.top = legend.height();
  6441. availableHeight = (height || parseInt(container.style('height')) || 400)
  6442. - margin.top - margin.bottom;
  6443. }
  6444. g.select('.nv-legendWrap')
  6445. .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
  6446. }
  6447. //------------------------------------------------------------
  6448. //------------------------------------------------------------
  6449. // Controls
  6450. if (showControls) {
  6451. var controlsData = [
  6452. { key: 'Grouped', disabled: multibar.stacked() },
  6453. { key: 'Stacked', disabled: !multibar.stacked() }
  6454. ];
  6455. controls.width(controlWidth()).color(['#444', '#444', '#444']);
  6456. g.select('.nv-controlsWrap')
  6457. .datum(controlsData)
  6458. .attr('transform', 'translate(0,' + (-margin.top) +')')
  6459. .call(controls);
  6460. }
  6461. //------------------------------------------------------------
  6462. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  6463. if (rightAlignYAxis) {
  6464. g.select(".nv-y.nv-axis")
  6465. .attr("transform", "translate(" + availableWidth + ",0)");
  6466. }
  6467. //------------------------------------------------------------
  6468. // Main Chart Component(s)
  6469. multibar
  6470. .disabled(data.map(function(series) { return series.disabled }))
  6471. .width(availableWidth)
  6472. .height(availableHeight)
  6473. .color(data.map(function(d,i) {
  6474. return d.color || color(d, i);
  6475. }).filter(function(d,i) { return !data[i].disabled }))
  6476. var barsWrap = g.select('.nv-barsWrap')
  6477. .datum(data.filter(function(d) { return !d.disabled }))
  6478. barsWrap.transition().call(multibar);
  6479. //------------------------------------------------------------
  6480. //------------------------------------------------------------
  6481. // Setup Axes
  6482. if (showXAxis) {
  6483. xAxis
  6484. .scale(x)
  6485. .ticks( availableWidth / 100 )
  6486. .tickSize(-availableHeight, 0);
  6487. g.select('.nv-x.nv-axis')
  6488. .attr('transform', 'translate(0,' + y.range()[0] + ')');
  6489. g.select('.nv-x.nv-axis').transition()
  6490. .call(xAxis);
  6491. var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g');
  6492. xTicks
  6493. .selectAll('line, text')
  6494. .style('opacity', 1)
  6495. if (staggerLabels) {
  6496. var getTranslate = function(x,y) {
  6497. return "translate(" + x + "," + y + ")";
  6498. };
  6499. var staggerUp = 5, staggerDown = 17; //pixels to stagger by
  6500. // Issue #140
  6501. xTicks
  6502. .selectAll("text")
  6503. .attr('transform', function(d,i,j) {
  6504. return getTranslate(0, (j % 2 == 0 ? staggerUp : staggerDown));
  6505. });
  6506. var totalInBetweenTicks = d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;
  6507. g.selectAll(".nv-x.nv-axis .nv-axisMaxMin text")
  6508. .attr("transform", function(d,i) {
  6509. return getTranslate(0, (i === 0 || totalInBetweenTicks % 2 !== 0) ? staggerDown : staggerUp);
  6510. });
  6511. }
  6512. if (reduceXTicks)
  6513. xTicks
  6514. .filter(function(d,i) {
  6515. return i % Math.ceil(data[0].values.length / (availableWidth / 100)) !== 0;
  6516. })
  6517. .selectAll('text, line')
  6518. .style('opacity', 0);
  6519. if(rotateLabels)
  6520. xTicks
  6521. .selectAll('.tick text')
  6522. .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
  6523. .style('text-anchor', rotateLabels > 0 ? 'start' : 'end');
  6524. g.select('.nv-x.nv-axis').selectAll('g.nv-axisMaxMin text')
  6525. .style('opacity', 1);
  6526. }
  6527. if (showYAxis) {
  6528. yAxis
  6529. .scale(y)
  6530. .ticks( availableHeight / 36 )
  6531. .tickSize( -availableWidth, 0);
  6532. g.select('.nv-y.nv-axis').transition()
  6533. .call(yAxis);
  6534. }
  6535. //------------------------------------------------------------
  6536. //============================================================
  6537. // Event Handling/Dispatching (in chart's scope)
  6538. //------------------------------------------------------------
  6539. legend.dispatch.on('stateChange', function(newState) {
  6540. state = newState;
  6541. dispatch.stateChange(state);
  6542. chart.update();
  6543. });
  6544. controls.dispatch.on('legendClick', function(d,i) {
  6545. if (!d.disabled) return;
  6546. controlsData = controlsData.map(function(s) {
  6547. s.disabled = true;
  6548. return s;
  6549. });
  6550. d.disabled = false;
  6551. switch (d.key) {
  6552. case 'Grouped':
  6553. multibar.stacked(false);
  6554. break;
  6555. case 'Stacked':
  6556. multibar.stacked(true);
  6557. break;
  6558. }
  6559. state.stacked = multibar.stacked();
  6560. dispatch.stateChange(state);
  6561. chart.update();
  6562. });
  6563. dispatch.on('tooltipShow', function(e) {
  6564. if (tooltips) showTooltip(e, that.parentNode)
  6565. });
  6566. // Update chart from a state object passed to event handler
  6567. dispatch.on('changeState', function(e) {
  6568. if (typeof e.disabled !== 'undefined') {
  6569. data.forEach(function(series,i) {
  6570. series.disabled = e.disabled[i];
  6571. });
  6572. state.disabled = e.disabled;
  6573. }
  6574. if (typeof e.stacked !== 'undefined') {
  6575. multibar.stacked(e.stacked);
  6576. state.stacked = e.stacked;
  6577. }
  6578. chart.update();
  6579. });
  6580. //============================================================
  6581. });
  6582. return chart;
  6583. }
  6584. //============================================================
  6585. // Event Handling/Dispatching (out of chart's scope)
  6586. //------------------------------------------------------------
  6587. multibar.dispatch.on('elementMouseover.tooltip', function(e) {
  6588. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  6589. dispatch.tooltipShow(e);
  6590. });
  6591. multibar.dispatch.on('elementMouseout.tooltip', function(e) {
  6592. dispatch.tooltipHide(e);
  6593. });
  6594. dispatch.on('tooltipHide', function() {
  6595. if (tooltips) nv.tooltip.cleanup();
  6596. });
  6597. //============================================================
  6598. //============================================================
  6599. // Expose Public Variables
  6600. //------------------------------------------------------------
  6601. // expose chart's sub-components
  6602. chart.dispatch = dispatch;
  6603. chart.multibar = multibar;
  6604. chart.legend = legend;
  6605. chart.xAxis = xAxis;
  6606. chart.yAxis = yAxis;
  6607. d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'clipEdge',
  6608. 'id', 'stacked', 'stackOffset', 'delay', 'barColor','groupSpacing');
  6609. chart.options = nv.utils.optionsFunc.bind(chart);
  6610. chart.margin = function(_) {
  6611. if (!arguments.length) return margin;
  6612. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  6613. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  6614. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  6615. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  6616. return chart;
  6617. };
  6618. chart.width = function(_) {
  6619. if (!arguments.length) return width;
  6620. width = _;
  6621. return chart;
  6622. };
  6623. chart.height = function(_) {
  6624. if (!arguments.length) return height;
  6625. height = _;
  6626. return chart;
  6627. };
  6628. chart.color = function(_) {
  6629. if (!arguments.length) return color;
  6630. color = nv.utils.getColor(_);
  6631. legend.color(color);
  6632. return chart;
  6633. };
  6634. chart.showControls = function(_) {
  6635. if (!arguments.length) return showControls;
  6636. showControls = _;
  6637. return chart;
  6638. };
  6639. chart.showLegend = function(_) {
  6640. if (!arguments.length) return showLegend;
  6641. showLegend = _;
  6642. return chart;
  6643. };
  6644. chart.showXAxis = function(_) {
  6645. if (!arguments.length) return showXAxis;
  6646. showXAxis = _;
  6647. return chart;
  6648. };
  6649. chart.showYAxis = function(_) {
  6650. if (!arguments.length) return showYAxis;
  6651. showYAxis = _;
  6652. return chart;
  6653. };
  6654. chart.rightAlignYAxis = function(_) {
  6655. if(!arguments.length) return rightAlignYAxis;
  6656. rightAlignYAxis = _;
  6657. yAxis.orient( (_) ? 'right' : 'left');
  6658. return chart;
  6659. };
  6660. chart.reduceXTicks= function(_) {
  6661. if (!arguments.length) return reduceXTicks;
  6662. reduceXTicks = _;
  6663. return chart;
  6664. };
  6665. chart.rotateLabels = function(_) {
  6666. if (!arguments.length) return rotateLabels;
  6667. rotateLabels = _;
  6668. return chart;
  6669. }
  6670. chart.staggerLabels = function(_) {
  6671. if (!arguments.length) return staggerLabels;
  6672. staggerLabels = _;
  6673. return chart;
  6674. };
  6675. chart.tooltip = function(_) {
  6676. if (!arguments.length) return tooltip;
  6677. tooltip = _;
  6678. return chart;
  6679. };
  6680. chart.tooltips = function(_) {
  6681. if (!arguments.length) return tooltips;
  6682. tooltips = _;
  6683. return chart;
  6684. };
  6685. chart.tooltipContent = function(_) {
  6686. if (!arguments.length) return tooltip;
  6687. tooltip = _;
  6688. return chart;
  6689. };
  6690. chart.state = function(_) {
  6691. if (!arguments.length) return state;
  6692. state = _;
  6693. return chart;
  6694. };
  6695. chart.defaultState = function(_) {
  6696. if (!arguments.length) return defaultState;
  6697. defaultState = _;
  6698. return chart;
  6699. };
  6700. chart.noData = function(_) {
  6701. if (!arguments.length) return noData;
  6702. noData = _;
  6703. return chart;
  6704. };
  6705. chart.transitionDuration = function(_) {
  6706. if (!arguments.length) return transitionDuration;
  6707. transitionDuration = _;
  6708. return chart;
  6709. };
  6710. //============================================================
  6711. return chart;
  6712. }
  6713. nv.models.multiBarHorizontal = function() {
  6714. "use strict";
  6715. //============================================================
  6716. // Public Variables with Default Settings
  6717. //------------------------------------------------------------
  6718. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  6719. , width = 960
  6720. , height = 500
  6721. , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
  6722. , x = d3.scale.ordinal()
  6723. , y = d3.scale.linear()
  6724. , getX = function(d) { return d.x }
  6725. , getY = function(d) { return d.y }
  6726. , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
  6727. , color = nv.utils.defaultColor()
  6728. , barColor = null // adding the ability to set the color for each rather than the whole group
  6729. , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
  6730. , stacked = false
  6731. , showValues = false
  6732. , showBarLabels = false
  6733. , valuePadding = 60
  6734. , valueFormat = d3.format(',.2f')
  6735. , delay = 1200
  6736. , xDomain
  6737. , yDomain
  6738. , xRange
  6739. , yRange
  6740. , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
  6741. ;
  6742. //============================================================
  6743. //============================================================
  6744. // Private Variables
  6745. //------------------------------------------------------------
  6746. var x0, y0 //used to store previous scales
  6747. ;
  6748. //============================================================
  6749. function chart(selection) {
  6750. selection.each(function(data) {
  6751. var availableWidth = width - margin.left - margin.right,
  6752. availableHeight = height - margin.top - margin.bottom,
  6753. container = d3.select(this);
  6754. if (stacked)
  6755. data = d3.layout.stack()
  6756. .offset('zero')
  6757. .values(function(d){ return d.values })
  6758. .y(getY)
  6759. (data);
  6760. //add series index to each data point for reference
  6761. data.forEach(function(series, i) {
  6762. series.values.forEach(function(point) {
  6763. point.series = i;
  6764. });
  6765. });
  6766. //------------------------------------------------------------
  6767. // HACK for negative value stacking
  6768. if (stacked)
  6769. data[0].values.map(function(d,i) {
  6770. var posBase = 0, negBase = 0;
  6771. data.map(function(d) {
  6772. var f = d.values[i]
  6773. f.size = Math.abs(f.y);
  6774. if (f.y<0) {
  6775. f.y1 = negBase - f.size;
  6776. negBase = negBase - f.size;
  6777. } else
  6778. {
  6779. f.y1 = posBase;
  6780. posBase = posBase + f.size;
  6781. }
  6782. });
  6783. });
  6784. //------------------------------------------------------------
  6785. // Setup Scales
  6786. // remap and flatten the data for use in calculating the scales' domains
  6787. var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
  6788. data.map(function(d) {
  6789. return d.values.map(function(d,i) {
  6790. return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 }
  6791. })
  6792. });
  6793. x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
  6794. .rangeBands(xRange || [0, availableHeight], .1);
  6795. //y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y0 : 0) }).concat(forceY)))
  6796. y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return stacked ? (d.y > 0 ? d.y1 + d.y : d.y1 ) : d.y }).concat(forceY)))
  6797. if (showValues && !stacked)
  6798. y.range(yRange || [(y.domain()[0] < 0 ? valuePadding : 0), availableWidth - (y.domain()[1] > 0 ? valuePadding : 0) ]);
  6799. else
  6800. y.range(yRange || [0, availableWidth]);
  6801. x0 = x0 || x;
  6802. y0 = y0 || d3.scale.linear().domain(y.domain()).range([y(0),y(0)]);
  6803. //------------------------------------------------------------
  6804. //------------------------------------------------------------
  6805. // Setup containers and skeleton of chart
  6806. var wrap = d3.select(this).selectAll('g.nv-wrap.nv-multibarHorizontal').data([data]);
  6807. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibarHorizontal');
  6808. var defsEnter = wrapEnter.append('defs');
  6809. var gEnter = wrapEnter.append('g');
  6810. var g = wrap.select('g');
  6811. gEnter.append('g').attr('class', 'nv-groups');
  6812. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  6813. //------------------------------------------------------------
  6814. var groups = wrap.select('.nv-groups').selectAll('.nv-group')
  6815. .data(function(d) { return d }, function(d,i) { return i });
  6816. groups.enter().append('g')
  6817. .style('stroke-opacity', 1e-6)
  6818. .style('fill-opacity', 1e-6);
  6819. groups.exit().transition()
  6820. .style('stroke-opacity', 1e-6)
  6821. .style('fill-opacity', 1e-6)
  6822. .remove();
  6823. groups
  6824. .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
  6825. .classed('hover', function(d) { return d.hover })
  6826. .style('fill', function(d,i){ return color(d, i) })
  6827. .style('stroke', function(d,i){ return color(d, i) });
  6828. groups.transition()
  6829. .style('stroke-opacity', 1)
  6830. .style('fill-opacity', .75);
  6831. var bars = groups.selectAll('g.nv-bar')
  6832. .data(function(d) { return d.values });
  6833. bars.exit().remove();
  6834. var barsEnter = bars.enter().append('g')
  6835. .attr('transform', function(d,i,j) {
  6836. return 'translate(' + y0(stacked ? d.y0 : 0) + ',' + (stacked ? 0 : (j * x.rangeBand() / data.length ) + x(getX(d,i))) + ')'
  6837. });
  6838. barsEnter.append('rect')
  6839. .attr('width', 0)
  6840. .attr('height', x.rangeBand() / (stacked ? 1 : data.length) )
  6841. bars
  6842. .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
  6843. d3.select(this).classed('hover', true);
  6844. dispatch.elementMouseover({
  6845. value: getY(d,i),
  6846. point: d,
  6847. series: data[d.series],
  6848. pos: [ y(getY(d,i) + (stacked ? d.y0 : 0)), x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length) ],
  6849. pointIndex: i,
  6850. seriesIndex: d.series,
  6851. e: d3.event
  6852. });
  6853. })
  6854. .on('mouseout', function(d,i) {
  6855. d3.select(this).classed('hover', false);
  6856. dispatch.elementMouseout({
  6857. value: getY(d,i),
  6858. point: d,
  6859. series: data[d.series],
  6860. pointIndex: i,
  6861. seriesIndex: d.series,
  6862. e: d3.event
  6863. });
  6864. })
  6865. .on('click', function(d,i) {
  6866. dispatch.elementClick({
  6867. value: getY(d,i),
  6868. point: d,
  6869. series: data[d.series],
  6870. pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
  6871. pointIndex: i,
  6872. seriesIndex: d.series,
  6873. e: d3.event
  6874. });
  6875. d3.event.stopPropagation();
  6876. })
  6877. .on('dblclick', function(d,i) {
  6878. dispatch.elementDblClick({
  6879. value: getY(d,i),
  6880. point: d,
  6881. series: data[d.series],
  6882. pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
  6883. pointIndex: i,
  6884. seriesIndex: d.series,
  6885. e: d3.event
  6886. });
  6887. d3.event.stopPropagation();
  6888. });
  6889. barsEnter.append('text');
  6890. if (showValues && !stacked) {
  6891. bars.select('text')
  6892. .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'end' : 'start' })
  6893. .attr('y', x.rangeBand() / (data.length * 2))
  6894. .attr('dy', '.32em')
  6895. .text(function(d,i) { return valueFormat(getY(d,i)) })
  6896. bars.transition()
  6897. .select('text')
  6898. .attr('x', function(d,i) { return getY(d,i) < 0 ? -4 : y(getY(d,i)) - y(0) + 4 })
  6899. } else {
  6900. bars.selectAll('text').text('');
  6901. }
  6902. if (showBarLabels && !stacked) {
  6903. barsEnter.append('text').classed('nv-bar-label',true);
  6904. bars.select('text.nv-bar-label')
  6905. .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'start' : 'end' })
  6906. .attr('y', x.rangeBand() / (data.length * 2))
  6907. .attr('dy', '.32em')
  6908. .text(function(d,i) { return getX(d,i) });
  6909. bars.transition()
  6910. .select('text.nv-bar-label')
  6911. .attr('x', function(d,i) { return getY(d,i) < 0 ? y(0) - y(getY(d,i)) + 4 : -4 });
  6912. }
  6913. else {
  6914. bars.selectAll('text.nv-bar-label').text('');
  6915. }
  6916. bars
  6917. .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
  6918. if (barColor) {
  6919. if (!disabled) disabled = data.map(function() { return true });
  6920. bars
  6921. .style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); })
  6922. .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
  6923. }
  6924. if (stacked)
  6925. bars.transition()
  6926. .attr('transform', function(d,i) {
  6927. return 'translate(' + y(d.y1) + ',' + x(getX(d,i)) + ')'
  6928. })
  6929. .select('rect')
  6930. .attr('width', function(d,i) {
  6931. return Math.abs(y(getY(d,i) + d.y0) - y(d.y0))
  6932. })
  6933. .attr('height', x.rangeBand() );
  6934. else
  6935. bars.transition()
  6936. .attr('transform', function(d,i) {
  6937. //TODO: stacked must be all positive or all negative, not both?
  6938. return 'translate(' +
  6939. (getY(d,i) < 0 ? y(getY(d,i)) : y(0))
  6940. + ',' +
  6941. (d.series * x.rangeBand() / data.length
  6942. +
  6943. x(getX(d,i)) )
  6944. + ')'
  6945. })
  6946. .select('rect')
  6947. .attr('height', x.rangeBand() / data.length )
  6948. .attr('width', function(d,i) {
  6949. return Math.max(Math.abs(y(getY(d,i)) - y(0)),1)
  6950. });
  6951. //store old scales for use in transitions on update
  6952. x0 = x.copy();
  6953. y0 = y.copy();
  6954. });
  6955. return chart;
  6956. }
  6957. //============================================================
  6958. // Expose Public Variables
  6959. //------------------------------------------------------------
  6960. chart.dispatch = dispatch;
  6961. chart.options = nv.utils.optionsFunc.bind(chart);
  6962. chart.x = function(_) {
  6963. if (!arguments.length) return getX;
  6964. getX = _;
  6965. return chart;
  6966. };
  6967. chart.y = function(_) {
  6968. if (!arguments.length) return getY;
  6969. getY = _;
  6970. return chart;
  6971. };
  6972. chart.margin = function(_) {
  6973. if (!arguments.length) return margin;
  6974. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  6975. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  6976. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  6977. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  6978. return chart;
  6979. };
  6980. chart.width = function(_) {
  6981. if (!arguments.length) return width;
  6982. width = _;
  6983. return chart;
  6984. };
  6985. chart.height = function(_) {
  6986. if (!arguments.length) return height;
  6987. height = _;
  6988. return chart;
  6989. };
  6990. chart.xScale = function(_) {
  6991. if (!arguments.length) return x;
  6992. x = _;
  6993. return chart;
  6994. };
  6995. chart.yScale = function(_) {
  6996. if (!arguments.length) return y;
  6997. y = _;
  6998. return chart;
  6999. };
  7000. chart.xDomain = function(_) {
  7001. if (!arguments.length) return xDomain;
  7002. xDomain = _;
  7003. return chart;
  7004. };
  7005. chart.yDomain = function(_) {
  7006. if (!arguments.length) return yDomain;
  7007. yDomain = _;
  7008. return chart;
  7009. };
  7010. chart.xRange = function(_) {
  7011. if (!arguments.length) return xRange;
  7012. xRange = _;
  7013. return chart;
  7014. };
  7015. chart.yRange = function(_) {
  7016. if (!arguments.length) return yRange;
  7017. yRange = _;
  7018. return chart;
  7019. };
  7020. chart.forceY = function(_) {
  7021. if (!arguments.length) return forceY;
  7022. forceY = _;
  7023. return chart;
  7024. };
  7025. chart.stacked = function(_) {
  7026. if (!arguments.length) return stacked;
  7027. stacked = _;
  7028. return chart;
  7029. };
  7030. chart.color = function(_) {
  7031. if (!arguments.length) return color;
  7032. color = nv.utils.getColor(_);
  7033. return chart;
  7034. };
  7035. chart.barColor = function(_) {
  7036. if (!arguments.length) return barColor;
  7037. barColor = nv.utils.getColor(_);
  7038. return chart;
  7039. };
  7040. chart.disabled = function(_) {
  7041. if (!arguments.length) return disabled;
  7042. disabled = _;
  7043. return chart;
  7044. };
  7045. chart.id = function(_) {
  7046. if (!arguments.length) return id;
  7047. id = _;
  7048. return chart;
  7049. };
  7050. chart.delay = function(_) {
  7051. if (!arguments.length) return delay;
  7052. delay = _;
  7053. return chart;
  7054. };
  7055. chart.showValues = function(_) {
  7056. if (!arguments.length) return showValues;
  7057. showValues = _;
  7058. return chart;
  7059. };
  7060. chart.showBarLabels = function(_) {
  7061. if (!arguments.length) return showBarLabels;
  7062. showBarLabels = _;
  7063. return chart;
  7064. };
  7065. chart.valueFormat= function(_) {
  7066. if (!arguments.length) return valueFormat;
  7067. valueFormat = _;
  7068. return chart;
  7069. };
  7070. chart.valuePadding = function(_) {
  7071. if (!arguments.length) return valuePadding;
  7072. valuePadding = _;
  7073. return chart;
  7074. };
  7075. //============================================================
  7076. return chart;
  7077. }
  7078. nv.models.multiBarHorizontalChart = function() {
  7079. "use strict";
  7080. //============================================================
  7081. // Public Variables with Default Settings
  7082. //------------------------------------------------------------
  7083. var multibar = nv.models.multiBarHorizontal()
  7084. , xAxis = nv.models.axis()
  7085. , yAxis = nv.models.axis()
  7086. , legend = nv.models.legend().height(30)
  7087. , controls = nv.models.legend().height(30)
  7088. ;
  7089. var margin = {top: 30, right: 20, bottom: 50, left: 60}
  7090. , width = null
  7091. , height = null
  7092. , color = nv.utils.defaultColor()
  7093. , showControls = true
  7094. , showLegend = true
  7095. , showXAxis = true
  7096. , showYAxis = true
  7097. , stacked = false
  7098. , tooltips = true
  7099. , tooltip = function(key, x, y, e, graph) {
  7100. return '<h3>' + key + ' - ' + x + '</h3>' +
  7101. '<p>' + y + '</p>'
  7102. }
  7103. , x //can be accessed via chart.xScale()
  7104. , y //can be accessed via chart.yScale()
  7105. , state = { stacked: stacked }
  7106. , defaultState = null
  7107. , noData = 'No Data Available.'
  7108. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
  7109. , controlWidth = function() { return showControls ? 180 : 0 }
  7110. , transitionDuration = 250
  7111. ;
  7112. multibar
  7113. .stacked(stacked)
  7114. ;
  7115. xAxis
  7116. .orient('left')
  7117. .tickPadding(5)
  7118. .highlightZero(false)
  7119. .showMaxMin(false)
  7120. .tickFormat(function(d) { return d })
  7121. ;
  7122. yAxis
  7123. .orient('bottom')
  7124. .tickFormat(d3.format(',.1f'))
  7125. ;
  7126. controls.updateState(false);
  7127. //============================================================
  7128. //============================================================
  7129. // Private Variables
  7130. //------------------------------------------------------------
  7131. var showTooltip = function(e, offsetElement) {
  7132. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  7133. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  7134. x = xAxis.tickFormat()(multibar.x()(e.point, e.pointIndex)),
  7135. y = yAxis.tickFormat()(multibar.y()(e.point, e.pointIndex)),
  7136. content = tooltip(e.series.key, x, y, e, chart);
  7137. nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement);
  7138. };
  7139. //============================================================
  7140. function chart(selection) {
  7141. selection.each(function(data) {
  7142. var container = d3.select(this),
  7143. that = this;
  7144. var availableWidth = (width || parseInt(container.style('width')) || 960)
  7145. - margin.left - margin.right,
  7146. availableHeight = (height || parseInt(container.style('height')) || 400)
  7147. - margin.top - margin.bottom;
  7148. chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
  7149. chart.container = this;
  7150. //set state.disabled
  7151. state.disabled = data.map(function(d) { return !!d.disabled });
  7152. if (!defaultState) {
  7153. var key;
  7154. defaultState = {};
  7155. for (key in state) {
  7156. if (state[key] instanceof Array)
  7157. defaultState[key] = state[key].slice(0);
  7158. else
  7159. defaultState[key] = state[key];
  7160. }
  7161. }
  7162. //------------------------------------------------------------
  7163. // Display No Data message if there's nothing to show.
  7164. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  7165. var noDataText = container.selectAll('.nv-noData').data([noData]);
  7166. noDataText.enter().append('text')
  7167. .attr('class', 'nvd3 nv-noData')
  7168. .attr('dy', '-.7em')
  7169. .style('text-anchor', 'middle');
  7170. noDataText
  7171. .attr('x', margin.left + availableWidth / 2)
  7172. .attr('y', margin.top + availableHeight / 2)
  7173. .text(function(d) { return d });
  7174. return chart;
  7175. } else {
  7176. container.selectAll('.nv-noData').remove();
  7177. }
  7178. //------------------------------------------------------------
  7179. //------------------------------------------------------------
  7180. // Setup Scales
  7181. x = multibar.xScale();
  7182. y = multibar.yScale();
  7183. //------------------------------------------------------------
  7184. //------------------------------------------------------------
  7185. // Setup containers and skeleton of chart
  7186. var wrap = container.selectAll('g.nv-wrap.nv-multiBarHorizontalChart').data([data]);
  7187. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarHorizontalChart').append('g');
  7188. var g = wrap.select('g');
  7189. gEnter.append('g').attr('class', 'nv-x nv-axis');
  7190. gEnter.append('g').attr('class', 'nv-y nv-axis')
  7191. .append('g').attr('class', 'nv-zeroLine')
  7192. .append('line');
  7193. gEnter.append('g').attr('class', 'nv-barsWrap');
  7194. gEnter.append('g').attr('class', 'nv-legendWrap');
  7195. gEnter.append('g').attr('class', 'nv-controlsWrap');
  7196. //------------------------------------------------------------
  7197. //------------------------------------------------------------
  7198. // Legend
  7199. if (showLegend) {
  7200. legend.width(availableWidth - controlWidth());
  7201. if (multibar.barColor())
  7202. data.forEach(function(series,i) {
  7203. series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
  7204. })
  7205. g.select('.nv-legendWrap')
  7206. .datum(data)
  7207. .call(legend);
  7208. if ( margin.top != legend.height()) {
  7209. margin.top = legend.height();
  7210. availableHeight = (height || parseInt(container.style('height')) || 400)
  7211. - margin.top - margin.bottom;
  7212. }
  7213. g.select('.nv-legendWrap')
  7214. .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
  7215. }
  7216. //------------------------------------------------------------
  7217. //------------------------------------------------------------
  7218. // Controls
  7219. if (showControls) {
  7220. var controlsData = [
  7221. { key: 'Grouped', disabled: multibar.stacked() },
  7222. { key: 'Stacked', disabled: !multibar.stacked() }
  7223. ];
  7224. controls.width(controlWidth()).color(['#444', '#444', '#444']);
  7225. g.select('.nv-controlsWrap')
  7226. .datum(controlsData)
  7227. .attr('transform', 'translate(0,' + (-margin.top) +')')
  7228. .call(controls);
  7229. }
  7230. //------------------------------------------------------------
  7231. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  7232. //------------------------------------------------------------
  7233. // Main Chart Component(s)
  7234. multibar
  7235. .disabled(data.map(function(series) { return series.disabled }))
  7236. .width(availableWidth)
  7237. .height(availableHeight)
  7238. .color(data.map(function(d,i) {
  7239. return d.color || color(d, i);
  7240. }).filter(function(d,i) { return !data[i].disabled }))
  7241. var barsWrap = g.select('.nv-barsWrap')
  7242. .datum(data.filter(function(d) { return !d.disabled }))
  7243. barsWrap.transition().call(multibar);
  7244. //------------------------------------------------------------
  7245. //------------------------------------------------------------
  7246. // Setup Axes
  7247. if (showXAxis) {
  7248. xAxis
  7249. .scale(x)
  7250. .ticks( availableHeight / 24 )
  7251. .tickSize(-availableWidth, 0);
  7252. g.select('.nv-x.nv-axis').transition()
  7253. .call(xAxis);
  7254. var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
  7255. xTicks
  7256. .selectAll('line, text');
  7257. }
  7258. if (showYAxis) {
  7259. yAxis
  7260. .scale(y)
  7261. .ticks( availableWidth / 100 )
  7262. .tickSize( -availableHeight, 0);
  7263. g.select('.nv-y.nv-axis')
  7264. .attr('transform', 'translate(0,' + availableHeight + ')');
  7265. g.select('.nv-y.nv-axis').transition()
  7266. .call(yAxis);
  7267. }
  7268. // Zero line
  7269. g.select(".nv-zeroLine line")
  7270. .attr("x1", y(0))
  7271. .attr("x2", y(0))
  7272. .attr("y1", 0)
  7273. .attr("y2", -availableHeight)
  7274. ;
  7275. //------------------------------------------------------------
  7276. //============================================================
  7277. // Event Handling/Dispatching (in chart's scope)
  7278. //------------------------------------------------------------
  7279. legend.dispatch.on('stateChange', function(newState) {
  7280. state = newState;
  7281. dispatch.stateChange(state);
  7282. chart.update();
  7283. });
  7284. controls.dispatch.on('legendClick', function(d,i) {
  7285. if (!d.disabled) return;
  7286. controlsData = controlsData.map(function(s) {
  7287. s.disabled = true;
  7288. return s;
  7289. });
  7290. d.disabled = false;
  7291. switch (d.key) {
  7292. case 'Grouped':
  7293. multibar.stacked(false);
  7294. break;
  7295. case 'Stacked':
  7296. multibar.stacked(true);
  7297. break;
  7298. }
  7299. state.stacked = multibar.stacked();
  7300. dispatch.stateChange(state);
  7301. chart.update();
  7302. });
  7303. dispatch.on('tooltipShow', function(e) {
  7304. if (tooltips) showTooltip(e, that.parentNode);
  7305. });
  7306. // Update chart from a state object passed to event handler
  7307. dispatch.on('changeState', function(e) {
  7308. if (typeof e.disabled !== 'undefined') {
  7309. data.forEach(function(series,i) {
  7310. series.disabled = e.disabled[i];
  7311. });
  7312. state.disabled = e.disabled;
  7313. }
  7314. if (typeof e.stacked !== 'undefined') {
  7315. multibar.stacked(e.stacked);
  7316. state.stacked = e.stacked;
  7317. }
  7318. chart.update();
  7319. });
  7320. //============================================================
  7321. });
  7322. return chart;
  7323. }
  7324. //============================================================
  7325. // Event Handling/Dispatching (out of chart's scope)
  7326. //------------------------------------------------------------
  7327. multibar.dispatch.on('elementMouseover.tooltip', function(e) {
  7328. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  7329. dispatch.tooltipShow(e);
  7330. });
  7331. multibar.dispatch.on('elementMouseout.tooltip', function(e) {
  7332. dispatch.tooltipHide(e);
  7333. });
  7334. dispatch.on('tooltipHide', function() {
  7335. if (tooltips) nv.tooltip.cleanup();
  7336. });
  7337. //============================================================
  7338. //============================================================
  7339. // Expose Public Variables
  7340. //------------------------------------------------------------
  7341. // expose chart's sub-components
  7342. chart.dispatch = dispatch;
  7343. chart.multibar = multibar;
  7344. chart.legend = legend;
  7345. chart.xAxis = xAxis;
  7346. chart.yAxis = yAxis;
  7347. d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY',
  7348. 'clipEdge', 'id', 'delay', 'showValues','showBarLabels', 'valueFormat', 'stacked', 'barColor');
  7349. chart.options = nv.utils.optionsFunc.bind(chart);
  7350. chart.margin = function(_) {
  7351. if (!arguments.length) return margin;
  7352. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  7353. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  7354. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  7355. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  7356. return chart;
  7357. };
  7358. chart.width = function(_) {
  7359. if (!arguments.length) return width;
  7360. width = _;
  7361. return chart;
  7362. };
  7363. chart.height = function(_) {
  7364. if (!arguments.length) return height;
  7365. height = _;
  7366. return chart;
  7367. };
  7368. chart.color = function(_) {
  7369. if (!arguments.length) return color;
  7370. color = nv.utils.getColor(_);
  7371. legend.color(color);
  7372. return chart;
  7373. };
  7374. chart.showControls = function(_) {
  7375. if (!arguments.length) return showControls;
  7376. showControls = _;
  7377. return chart;
  7378. };
  7379. chart.showLegend = function(_) {
  7380. if (!arguments.length) return showLegend;
  7381. showLegend = _;
  7382. return chart;
  7383. };
  7384. chart.showXAxis = function(_) {
  7385. if (!arguments.length) return showXAxis;
  7386. showXAxis = _;
  7387. return chart;
  7388. };
  7389. chart.showYAxis = function(_) {
  7390. if (!arguments.length) return showYAxis;
  7391. showYAxis = _;
  7392. return chart;
  7393. };
  7394. chart.tooltip = function(_) {
  7395. if (!arguments.length) return tooltip;
  7396. tooltip = _;
  7397. return chart;
  7398. };
  7399. chart.tooltips = function(_) {
  7400. if (!arguments.length) return tooltips;
  7401. tooltips = _;
  7402. return chart;
  7403. };
  7404. chart.tooltipContent = function(_) {
  7405. if (!arguments.length) return tooltip;
  7406. tooltip = _;
  7407. return chart;
  7408. };
  7409. chart.state = function(_) {
  7410. if (!arguments.length) return state;
  7411. state = _;
  7412. return chart;
  7413. };
  7414. chart.defaultState = function(_) {
  7415. if (!arguments.length) return defaultState;
  7416. defaultState = _;
  7417. return chart;
  7418. };
  7419. chart.noData = function(_) {
  7420. if (!arguments.length) return noData;
  7421. noData = _;
  7422. return chart;
  7423. };
  7424. chart.transitionDuration = function(_) {
  7425. if (!arguments.length) return transitionDuration;
  7426. transitionDuration = _;
  7427. return chart;
  7428. };
  7429. //============================================================
  7430. return chart;
  7431. }
  7432. nv.models.multiChart = function() {
  7433. "use strict";
  7434. //============================================================
  7435. // Public Variables with Default Settings
  7436. //------------------------------------------------------------
  7437. var margin = {top: 30, right: 20, bottom: 50, left: 60},
  7438. color = d3.scale.category20().range(),
  7439. width = null,
  7440. height = null,
  7441. showLegend = true,
  7442. tooltips = true,
  7443. tooltip = function(key, x, y, e, graph) {
  7444. return '<h3>' + key + '</h3>' +
  7445. '<p>' + y + ' at ' + x + '</p>'
  7446. },
  7447. x,
  7448. y,
  7449. yDomain1,
  7450. yDomain2
  7451. ; //can be accessed via chart.lines.[x/y]Scale()
  7452. //============================================================
  7453. // Private Variables
  7454. //------------------------------------------------------------
  7455. var x = d3.scale.linear(),
  7456. yScale1 = d3.scale.linear(),
  7457. yScale2 = d3.scale.linear(),
  7458. lines1 = nv.models.line().yScale(yScale1),
  7459. lines2 = nv.models.line().yScale(yScale2),
  7460. bars1 = nv.models.multiBar().stacked(false).yScale(yScale1),
  7461. bars2 = nv.models.multiBar().stacked(false).yScale(yScale2),
  7462. stack1 = nv.models.stackedArea().yScale(yScale1),
  7463. stack2 = nv.models.stackedArea().yScale(yScale2),
  7464. xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5),
  7465. yAxis1 = nv.models.axis().scale(yScale1).orient('left'),
  7466. yAxis2 = nv.models.axis().scale(yScale2).orient('right'),
  7467. legend = nv.models.legend().height(30),
  7468. dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
  7469. var showTooltip = function(e, offsetElement) {
  7470. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  7471. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  7472. x = xAxis.tickFormat()(lines1.x()(e.point, e.pointIndex)),
  7473. y = ((e.series.yAxis == 2) ? yAxis2 : yAxis1).tickFormat()(lines1.y()(e.point, e.pointIndex)),
  7474. content = tooltip(e.series.key, x, y, e, chart);
  7475. nv.tooltip.show([left, top], content, undefined, undefined, offsetElement.offsetParent);
  7476. };
  7477. function chart(selection) {
  7478. selection.each(function(data) {
  7479. var container = d3.select(this),
  7480. that = this;
  7481. chart.update = function() { container.transition().call(chart); };
  7482. chart.container = this;
  7483. var availableWidth = (width || parseInt(container.style('width')) || 960)
  7484. - margin.left - margin.right,
  7485. availableHeight = (height || parseInt(container.style('height')) || 400)
  7486. - margin.top - margin.bottom;
  7487. var dataLines1 = data.filter(function(d) {return !d.disabled && d.type == 'line' && d.yAxis == 1})
  7488. var dataLines2 = data.filter(function(d) {return !d.disabled && d.type == 'line' && d.yAxis == 2})
  7489. var dataBars1 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 1})
  7490. var dataBars2 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 2})
  7491. var dataStack1 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 1})
  7492. var dataStack2 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 2})
  7493. var series1 = data.filter(function(d) {return !d.disabled && d.yAxis == 1})
  7494. .map(function(d) {
  7495. return d.values.map(function(d,i) {
  7496. return { x: d.x, y: d.y }
  7497. })
  7498. })
  7499. var series2 = data.filter(function(d) {return !d.disabled && d.yAxis == 2})
  7500. .map(function(d) {
  7501. return d.values.map(function(d,i) {
  7502. return { x: d.x, y: d.y }
  7503. })
  7504. })
  7505. x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
  7506. .range([0, availableWidth]);
  7507. var wrap = container.selectAll('g.wrap.multiChart').data([data]);
  7508. var gEnter = wrap.enter().append('g').attr('class', 'wrap nvd3 multiChart').append('g');
  7509. gEnter.append('g').attr('class', 'x axis');
  7510. gEnter.append('g').attr('class', 'y1 axis');
  7511. gEnter.append('g').attr('class', 'y2 axis');
  7512. gEnter.append('g').attr('class', 'lines1Wrap');
  7513. gEnter.append('g').attr('class', 'lines2Wrap');
  7514. gEnter.append('g').attr('class', 'bars1Wrap');
  7515. gEnter.append('g').attr('class', 'bars2Wrap');
  7516. gEnter.append('g').attr('class', 'stack1Wrap');
  7517. gEnter.append('g').attr('class', 'stack2Wrap');
  7518. gEnter.append('g').attr('class', 'legendWrap');
  7519. var g = wrap.select('g');
  7520. if (showLegend) {
  7521. legend.width( availableWidth / 2 );
  7522. g.select('.legendWrap')
  7523. .datum(data.map(function(series) {
  7524. series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
  7525. series.key = series.originalKey + (series.yAxis == 1 ? '' : ' (right axis)');
  7526. return series;
  7527. }))
  7528. .call(legend);
  7529. if ( margin.top != legend.height()) {
  7530. margin.top = legend.height();
  7531. availableHeight = (height || parseInt(container.style('height')) || 400)
  7532. - margin.top - margin.bottom;
  7533. }
  7534. g.select('.legendWrap')
  7535. .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
  7536. }
  7537. lines1
  7538. .width(availableWidth)
  7539. .height(availableHeight)
  7540. .interpolate("monotone")
  7541. .color(data.map(function(d,i) {
  7542. return d.color || color[i % color.length];
  7543. }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'line'}));
  7544. lines2
  7545. .width(availableWidth)
  7546. .height(availableHeight)
  7547. .interpolate("monotone")
  7548. .color(data.map(function(d,i) {
  7549. return d.color || color[i % color.length];
  7550. }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'line'}));
  7551. bars1
  7552. .width(availableWidth)
  7553. .height(availableHeight)
  7554. .color(data.map(function(d,i) {
  7555. return d.color || color[i % color.length];
  7556. }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'bar'}));
  7557. bars2
  7558. .width(availableWidth)
  7559. .height(availableHeight)
  7560. .color(data.map(function(d,i) {
  7561. return d.color || color[i % color.length];
  7562. }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'bar'}));
  7563. stack1
  7564. .width(availableWidth)
  7565. .height(availableHeight)
  7566. .color(data.map(function(d,i) {
  7567. return d.color || color[i % color.length];
  7568. }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'}));
  7569. stack2
  7570. .width(availableWidth)
  7571. .height(availableHeight)
  7572. .color(data.map(function(d,i) {
  7573. return d.color || color[i % color.length];
  7574. }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'}));
  7575. g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  7576. var lines1Wrap = g.select('.lines1Wrap')
  7577. .datum(dataLines1)
  7578. var bars1Wrap = g.select('.bars1Wrap')
  7579. .datum(dataBars1)
  7580. var stack1Wrap = g.select('.stack1Wrap')
  7581. .datum(dataStack1)
  7582. var lines2Wrap = g.select('.lines2Wrap')
  7583. .datum(dataLines2)
  7584. var bars2Wrap = g.select('.bars2Wrap')
  7585. .datum(dataBars2)
  7586. var stack2Wrap = g.select('.stack2Wrap')
  7587. .datum(dataStack2)
  7588. var extraValue1 = dataStack1.length ? dataStack1.map(function(a){return a.values}).reduce(function(a,b){
  7589. return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
  7590. }).concat([{x:0, y:0}]) : []
  7591. var extraValue2 = dataStack2.length ? dataStack2.map(function(a){return a.values}).reduce(function(a,b){
  7592. return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
  7593. }).concat([{x:0, y:0}]) : []
  7594. yScale1 .domain(yDomain1 || d3.extent(d3.merge(series1).concat(extraValue1), function(d) { return d.y } ))
  7595. .range([0, availableHeight])
  7596. yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } ))
  7597. .range([0, availableHeight])
  7598. lines1.yDomain(yScale1.domain())
  7599. bars1.yDomain(yScale1.domain())
  7600. stack1.yDomain(yScale1.domain())
  7601. lines2.yDomain(yScale2.domain())
  7602. bars2.yDomain(yScale2.domain())
  7603. stack2.yDomain(yScale2.domain())
  7604. if(dataStack1.length){d3.transition(stack1Wrap).call(stack1);}
  7605. if(dataStack2.length){d3.transition(stack2Wrap).call(stack2);}
  7606. if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);}
  7607. if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);}
  7608. if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);}
  7609. if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);}
  7610. xAxis
  7611. .ticks( availableWidth / 100 )
  7612. .tickSize(-availableHeight, 0);
  7613. g.select('.x.axis')
  7614. .attr('transform', 'translate(0,' + availableHeight + ')');
  7615. d3.transition(g.select('.x.axis'))
  7616. .call(xAxis);
  7617. yAxis1
  7618. .ticks( availableHeight / 36 )
  7619. .tickSize( -availableWidth, 0);
  7620. d3.transition(g.select('.y1.axis'))
  7621. .call(yAxis1);
  7622. yAxis2
  7623. .ticks( availableHeight / 36 )
  7624. .tickSize( -availableWidth, 0);
  7625. d3.transition(g.select('.y2.axis'))
  7626. .call(yAxis2);
  7627. g.select('.y2.axis')
  7628. .style('opacity', series2.length ? 1 : 0)
  7629. .attr('transform', 'translate(' + x.range()[1] + ',0)');
  7630. legend.dispatch.on('stateChange', function(newState) {
  7631. chart.update();
  7632. });
  7633. dispatch.on('tooltipShow', function(e) {
  7634. if (tooltips) showTooltip(e, that.parentNode);
  7635. });
  7636. });
  7637. return chart;
  7638. }
  7639. //============================================================
  7640. // Event Handling/Dispatching (out of chart's scope)
  7641. //------------------------------------------------------------
  7642. lines1.dispatch.on('elementMouseover.tooltip', function(e) {
  7643. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  7644. dispatch.tooltipShow(e);
  7645. });
  7646. lines1.dispatch.on('elementMouseout.tooltip', function(e) {
  7647. dispatch.tooltipHide(e);
  7648. });
  7649. lines2.dispatch.on('elementMouseover.tooltip', function(e) {
  7650. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  7651. dispatch.tooltipShow(e);
  7652. });
  7653. lines2.dispatch.on('elementMouseout.tooltip', function(e) {
  7654. dispatch.tooltipHide(e);
  7655. });
  7656. bars1.dispatch.on('elementMouseover.tooltip', function(e) {
  7657. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  7658. dispatch.tooltipShow(e);
  7659. });
  7660. bars1.dispatch.on('elementMouseout.tooltip', function(e) {
  7661. dispatch.tooltipHide(e);
  7662. });
  7663. bars2.dispatch.on('elementMouseover.tooltip', function(e) {
  7664. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  7665. dispatch.tooltipShow(e);
  7666. });
  7667. bars2.dispatch.on('elementMouseout.tooltip', function(e) {
  7668. dispatch.tooltipHide(e);
  7669. });
  7670. stack1.dispatch.on('tooltipShow', function(e) {
  7671. //disable tooltips when value ~= 0
  7672. //// TODO: consider removing points from voronoi that have 0 value instead of this hack
  7673. if (!Math.round(stack1.y()(e.point) * 100)) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
  7674. setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
  7675. return false;
  7676. }
  7677. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
  7678. dispatch.tooltipShow(e);
  7679. });
  7680. stack1.dispatch.on('tooltipHide', function(e) {
  7681. dispatch.tooltipHide(e);
  7682. });
  7683. stack2.dispatch.on('tooltipShow', function(e) {
  7684. //disable tooltips when value ~= 0
  7685. //// TODO: consider removing points from voronoi that have 0 value instead of this hack
  7686. if (!Math.round(stack2.y()(e.point) * 100)) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
  7687. setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
  7688. return false;
  7689. }
  7690. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
  7691. dispatch.tooltipShow(e);
  7692. });
  7693. stack2.dispatch.on('tooltipHide', function(e) {
  7694. dispatch.tooltipHide(e);
  7695. });
  7696. lines1.dispatch.on('elementMouseover.tooltip', function(e) {
  7697. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  7698. dispatch.tooltipShow(e);
  7699. });
  7700. lines1.dispatch.on('elementMouseout.tooltip', function(e) {
  7701. dispatch.tooltipHide(e);
  7702. });
  7703. lines2.dispatch.on('elementMouseover.tooltip', function(e) {
  7704. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  7705. dispatch.tooltipShow(e);
  7706. });
  7707. lines2.dispatch.on('elementMouseout.tooltip', function(e) {
  7708. dispatch.tooltipHide(e);
  7709. });
  7710. dispatch.on('tooltipHide', function() {
  7711. if (tooltips) nv.tooltip.cleanup();
  7712. });
  7713. //============================================================
  7714. // Global getters and setters
  7715. //------------------------------------------------------------
  7716. chart.dispatch = dispatch;
  7717. chart.lines1 = lines1;
  7718. chart.lines2 = lines2;
  7719. chart.bars1 = bars1;
  7720. chart.bars2 = bars2;
  7721. chart.stack1 = stack1;
  7722. chart.stack2 = stack2;
  7723. chart.xAxis = xAxis;
  7724. chart.yAxis1 = yAxis1;
  7725. chart.yAxis2 = yAxis2;
  7726. chart.options = nv.utils.optionsFunc.bind(chart);
  7727. chart.x = function(_) {
  7728. if (!arguments.length) return getX;
  7729. getX = _;
  7730. lines1.x(_);
  7731. bars1.x(_);
  7732. return chart;
  7733. };
  7734. chart.y = function(_) {
  7735. if (!arguments.length) return getY;
  7736. getY = _;
  7737. lines1.y(_);
  7738. bars1.y(_);
  7739. return chart;
  7740. };
  7741. chart.yDomain1 = function(_) {
  7742. if (!arguments.length) return yDomain1;
  7743. yDomain1 = _;
  7744. return chart;
  7745. };
  7746. chart.yDomain2 = function(_) {
  7747. if (!arguments.length) return yDomain2;
  7748. yDomain2 = _;
  7749. return chart;
  7750. };
  7751. chart.margin = function(_) {
  7752. if (!arguments.length) return margin;
  7753. margin = _;
  7754. return chart;
  7755. };
  7756. chart.width = function(_) {
  7757. if (!arguments.length) return width;
  7758. width = _;
  7759. return chart;
  7760. };
  7761. chart.height = function(_) {
  7762. if (!arguments.length) return height;
  7763. height = _;
  7764. return chart;
  7765. };
  7766. chart.color = function(_) {
  7767. if (!arguments.length) return color;
  7768. color = _;
  7769. legend.color(_);
  7770. return chart;
  7771. };
  7772. chart.showLegend = function(_) {
  7773. if (!arguments.length) return showLegend;
  7774. showLegend = _;
  7775. return chart;
  7776. };
  7777. chart.tooltips = function(_) {
  7778. if (!arguments.length) return tooltips;
  7779. tooltips = _;
  7780. return chart;
  7781. };
  7782. chart.tooltipContent = function(_) {
  7783. if (!arguments.length) return tooltip;
  7784. tooltip = _;
  7785. return chart;
  7786. };
  7787. return chart;
  7788. }
  7789. nv.models.ohlcBar = function() {
  7790. "use strict";
  7791. //============================================================
  7792. // Public Variables with Default Settings
  7793. //------------------------------------------------------------
  7794. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  7795. , width = 960
  7796. , height = 500
  7797. , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
  7798. , x = d3.scale.linear()
  7799. , y = d3.scale.linear()
  7800. , getX = function(d) { return d.x }
  7801. , getY = function(d) { return d.y }
  7802. , getOpen = function(d) { return d.open }
  7803. , getClose = function(d) { return d.close }
  7804. , getHigh = function(d) { return d.high }
  7805. , getLow = function(d) { return d.low }
  7806. , forceX = []
  7807. , forceY = []
  7808. , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
  7809. , clipEdge = true
  7810. , color = nv.utils.defaultColor()
  7811. , xDomain
  7812. , yDomain
  7813. , xRange
  7814. , yRange
  7815. , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
  7816. ;
  7817. //============================================================
  7818. //============================================================
  7819. // Private Variables
  7820. //------------------------------------------------------------
  7821. //TODO: store old scales for transitions
  7822. //============================================================
  7823. function chart(selection) {
  7824. selection.each(function(data) {
  7825. var availableWidth = width - margin.left - margin.right,
  7826. availableHeight = height - margin.top - margin.bottom,
  7827. container = d3.select(this);
  7828. //------------------------------------------------------------
  7829. // Setup Scales
  7830. x .domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
  7831. if (padData)
  7832. x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
  7833. else
  7834. x.range(xRange || [0, availableWidth]);
  7835. y .domain(yDomain || [
  7836. d3.min(data[0].values.map(getLow).concat(forceY)),
  7837. d3.max(data[0].values.map(getHigh).concat(forceY))
  7838. ])
  7839. .range(yRange || [availableHeight, 0]);
  7840. // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
  7841. if (x.domain()[0] === x.domain()[1])
  7842. x.domain()[0] ?
  7843. x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
  7844. : x.domain([-1,1]);
  7845. if (y.domain()[0] === y.domain()[1])
  7846. y.domain()[0] ?
  7847. y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
  7848. : y.domain([-1,1]);
  7849. //------------------------------------------------------------
  7850. //------------------------------------------------------------
  7851. // Setup containers and skeleton of chart
  7852. var wrap = d3.select(this).selectAll('g.nv-wrap.nv-ohlcBar').data([data[0].values]);
  7853. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-ohlcBar');
  7854. var defsEnter = wrapEnter.append('defs');
  7855. var gEnter = wrapEnter.append('g');
  7856. var g = wrap.select('g');
  7857. gEnter.append('g').attr('class', 'nv-ticks');
  7858. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  7859. //------------------------------------------------------------
  7860. container
  7861. .on('click', function(d,i) {
  7862. dispatch.chartClick({
  7863. data: d,
  7864. index: i,
  7865. pos: d3.event,
  7866. id: id
  7867. });
  7868. });
  7869. defsEnter.append('clipPath')
  7870. .attr('id', 'nv-chart-clip-path-' + id)
  7871. .append('rect');
  7872. wrap.select('#nv-chart-clip-path-' + id + ' rect')
  7873. .attr('width', availableWidth)
  7874. .attr('height', availableHeight);
  7875. g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
  7876. var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick')
  7877. .data(function(d) { return d });
  7878. ticks.exit().remove();
  7879. var ticksEnter = ticks.enter().append('path')
  7880. .attr('class', function(d,i,j) { return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i })
  7881. .attr('d', function(d,i) {
  7882. var w = (availableWidth / data[0].values.length) * .9;
  7883. return 'm0,0l0,'
  7884. + (y(getOpen(d,i))
  7885. - y(getHigh(d,i)))
  7886. + 'l'
  7887. + (-w/2)
  7888. + ',0l'
  7889. + (w/2)
  7890. + ',0l0,'
  7891. + (y(getLow(d,i)) - y(getOpen(d,i)))
  7892. + 'l0,'
  7893. + (y(getClose(d,i))
  7894. - y(getLow(d,i)))
  7895. + 'l'
  7896. + (w/2)
  7897. + ',0l'
  7898. + (-w/2)
  7899. + ',0z';
  7900. })
  7901. .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; })
  7902. //.attr('fill', function(d,i) { return color[0]; })
  7903. //.attr('stroke', function(d,i) { return color[0]; })
  7904. //.attr('x', 0 )
  7905. //.attr('y', function(d,i) { return y(Math.max(0, getY(d,i))) })
  7906. //.attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) })
  7907. .on('mouseover', function(d,i) {
  7908. d3.select(this).classed('hover', true);
  7909. dispatch.elementMouseover({
  7910. point: d,
  7911. series: data[0],
  7912. pos: [x(getX(d,i)), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
  7913. pointIndex: i,
  7914. seriesIndex: 0,
  7915. e: d3.event
  7916. });
  7917. })
  7918. .on('mouseout', function(d,i) {
  7919. d3.select(this).classed('hover', false);
  7920. dispatch.elementMouseout({
  7921. point: d,
  7922. series: data[0],
  7923. pointIndex: i,
  7924. seriesIndex: 0,
  7925. e: d3.event
  7926. });
  7927. })
  7928. .on('click', function(d,i) {
  7929. dispatch.elementClick({
  7930. //label: d[label],
  7931. value: getY(d,i),
  7932. data: d,
  7933. index: i,
  7934. pos: [x(getX(d,i)), y(getY(d,i))],
  7935. e: d3.event,
  7936. id: id
  7937. });
  7938. d3.event.stopPropagation();
  7939. })
  7940. .on('dblclick', function(d,i) {
  7941. dispatch.elementDblClick({
  7942. //label: d[label],
  7943. value: getY(d,i),
  7944. data: d,
  7945. index: i,
  7946. pos: [x(getX(d,i)), y(getY(d,i))],
  7947. e: d3.event,
  7948. id: id
  7949. });
  7950. d3.event.stopPropagation();
  7951. });
  7952. ticks
  7953. .attr('class', function(d,i,j) { return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i })
  7954. d3.transition(ticks)
  7955. .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; })
  7956. .attr('d', function(d,i) {
  7957. var w = (availableWidth / data[0].values.length) * .9;
  7958. return 'm0,0l0,'
  7959. + (y(getOpen(d,i))
  7960. - y(getHigh(d,i)))
  7961. + 'l'
  7962. + (-w/2)
  7963. + ',0l'
  7964. + (w/2)
  7965. + ',0l0,'
  7966. + (y(getLow(d,i))
  7967. - y(getOpen(d,i)))
  7968. + 'l0,'
  7969. + (y(getClose(d,i))
  7970. - y(getLow(d,i)))
  7971. + 'l'
  7972. + (w/2)
  7973. + ',0l'
  7974. + (-w/2)
  7975. + ',0z';
  7976. })
  7977. //.attr('width', (availableWidth / data[0].values.length) * .9 )
  7978. //d3.transition(ticks)
  7979. //.attr('y', function(d,i) { return y(Math.max(0, getY(d,i))) })
  7980. //.attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) });
  7981. //.order(); // not sure if this makes any sense for this model
  7982. });
  7983. return chart;
  7984. }
  7985. //============================================================
  7986. // Expose Public Variables
  7987. //------------------------------------------------------------
  7988. chart.dispatch = dispatch;
  7989. chart.options = nv.utils.optionsFunc.bind(chart);
  7990. chart.x = function(_) {
  7991. if (!arguments.length) return getX;
  7992. getX = _;
  7993. return chart;
  7994. };
  7995. chart.y = function(_) {
  7996. if (!arguments.length) return getY;
  7997. getY = _;
  7998. return chart;
  7999. };
  8000. chart.open = function(_) {
  8001. if (!arguments.length) return getOpen;
  8002. getOpen = _;
  8003. return chart;
  8004. };
  8005. chart.close = function(_) {
  8006. if (!arguments.length) return getClose;
  8007. getClose = _;
  8008. return chart;
  8009. };
  8010. chart.high = function(_) {
  8011. if (!arguments.length) return getHigh;
  8012. getHigh = _;
  8013. return chart;
  8014. };
  8015. chart.low = function(_) {
  8016. if (!arguments.length) return getLow;
  8017. getLow = _;
  8018. return chart;
  8019. };
  8020. chart.margin = function(_) {
  8021. if (!arguments.length) return margin;
  8022. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  8023. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  8024. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  8025. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  8026. return chart;
  8027. };
  8028. chart.width = function(_) {
  8029. if (!arguments.length) return width;
  8030. width = _;
  8031. return chart;
  8032. };
  8033. chart.height = function(_) {
  8034. if (!arguments.length) return height;
  8035. height = _;
  8036. return chart;
  8037. };
  8038. chart.xScale = function(_) {
  8039. if (!arguments.length) return x;
  8040. x = _;
  8041. return chart;
  8042. };
  8043. chart.yScale = function(_) {
  8044. if (!arguments.length) return y;
  8045. y = _;
  8046. return chart;
  8047. };
  8048. chart.xDomain = function(_) {
  8049. if (!arguments.length) return xDomain;
  8050. xDomain = _;
  8051. return chart;
  8052. };
  8053. chart.yDomain = function(_) {
  8054. if (!arguments.length) return yDomain;
  8055. yDomain = _;
  8056. return chart;
  8057. };
  8058. chart.xRange = function(_) {
  8059. if (!arguments.length) return xRange;
  8060. xRange = _;
  8061. return chart;
  8062. };
  8063. chart.yRange = function(_) {
  8064. if (!arguments.length) return yRange;
  8065. yRange = _;
  8066. return chart;
  8067. };
  8068. chart.forceX = function(_) {
  8069. if (!arguments.length) return forceX;
  8070. forceX = _;
  8071. return chart;
  8072. };
  8073. chart.forceY = function(_) {
  8074. if (!arguments.length) return forceY;
  8075. forceY = _;
  8076. return chart;
  8077. };
  8078. chart.padData = function(_) {
  8079. if (!arguments.length) return padData;
  8080. padData = _;
  8081. return chart;
  8082. };
  8083. chart.clipEdge = function(_) {
  8084. if (!arguments.length) return clipEdge;
  8085. clipEdge = _;
  8086. return chart;
  8087. };
  8088. chart.color = function(_) {
  8089. if (!arguments.length) return color;
  8090. color = nv.utils.getColor(_);
  8091. return chart;
  8092. };
  8093. chart.id = function(_) {
  8094. if (!arguments.length) return id;
  8095. id = _;
  8096. return chart;
  8097. };
  8098. //============================================================
  8099. return chart;
  8100. }
  8101. nv.models.pie = function() {
  8102. "use strict";
  8103. //============================================================
  8104. // Public Variables with Default Settings
  8105. //------------------------------------------------------------
  8106. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  8107. , width = 500
  8108. , height = 500
  8109. , getX = function(d) { return d.x }
  8110. , getY = function(d) { return d.y }
  8111. , getDescription = function(d) { return d.description }
  8112. , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
  8113. , color = nv.utils.defaultColor()
  8114. , valueFormat = d3.format(',.2f')
  8115. , showLabels = true
  8116. , pieLabelsOutside = true
  8117. , donutLabelsOutside = false
  8118. , labelType = "key"
  8119. , labelThreshold = .02 //if slice percentage is under this, don't show label
  8120. , donut = false
  8121. , labelSunbeamLayout = false
  8122. , startAngle = false
  8123. , endAngle = false
  8124. , donutRatio = 0.5
  8125. , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
  8126. ;
  8127. //============================================================
  8128. function chart(selection) {
  8129. selection.each(function(data) {
  8130. var availableWidth = width - margin.left - margin.right,
  8131. availableHeight = height - margin.top - margin.bottom,
  8132. radius = Math.min(availableWidth, availableHeight) / 2,
  8133. arcRadius = radius-(radius / 5),
  8134. container = d3.select(this);
  8135. //------------------------------------------------------------
  8136. // Setup containers and skeleton of chart
  8137. //var wrap = container.selectAll('.nv-wrap.nv-pie').data([data]);
  8138. var wrap = container.selectAll('.nv-wrap.nv-pie').data(data);
  8139. var wrapEnter = wrap.enter().append('g').attr('class','nvd3 nv-wrap nv-pie nv-chart-' + id);
  8140. var gEnter = wrapEnter.append('g');
  8141. var g = wrap.select('g');
  8142. gEnter.append('g').attr('class', 'nv-pie');
  8143. gEnter.append('g').attr('class', 'nv-pieLabels');
  8144. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  8145. g.select('.nv-pie').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
  8146. g.select('.nv-pieLabels').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
  8147. //------------------------------------------------------------
  8148. container
  8149. .on('click', function(d,i) {
  8150. dispatch.chartClick({
  8151. data: d,
  8152. index: i,
  8153. pos: d3.event,
  8154. id: id
  8155. });
  8156. });
  8157. var arc = d3.svg.arc()
  8158. .outerRadius(arcRadius);
  8159. if (startAngle) arc.startAngle(startAngle)
  8160. if (endAngle) arc.endAngle(endAngle);
  8161. if (donut) arc.innerRadius(radius * donutRatio);
  8162. // Setup the Pie chart and choose the data element
  8163. var pie = d3.layout.pie()
  8164. .sort(null)
  8165. .value(function(d) { return d.disabled ? 0 : getY(d) });
  8166. var slices = wrap.select('.nv-pie').selectAll('.nv-slice')
  8167. .data(pie);
  8168. var pieLabels = wrap.select('.nv-pieLabels').selectAll('.nv-label')
  8169. .data(pie);
  8170. slices.exit().remove();
  8171. pieLabels.exit().remove();
  8172. var ae = slices.enter().append('g')
  8173. .attr('class', 'nv-slice')
  8174. .on('mouseover', function(d,i){
  8175. d3.select(this).classed('hover', true);
  8176. dispatch.elementMouseover({
  8177. label: getX(d.data),
  8178. value: getY(d.data),
  8179. point: d.data,
  8180. pointIndex: i,
  8181. pos: [d3.event.pageX, d3.event.pageY],
  8182. id: id
  8183. });
  8184. })
  8185. .on('mouseout', function(d,i){
  8186. d3.select(this).classed('hover', false);
  8187. dispatch.elementMouseout({
  8188. label: getX(d.data),
  8189. value: getY(d.data),
  8190. point: d.data,
  8191. index: i,
  8192. id: id
  8193. });
  8194. })
  8195. .on('click', function(d,i) {
  8196. dispatch.elementClick({
  8197. label: getX(d.data),
  8198. value: getY(d.data),
  8199. point: d.data,
  8200. index: i,
  8201. pos: d3.event,
  8202. id: id
  8203. });
  8204. d3.event.stopPropagation();
  8205. })
  8206. .on('dblclick', function(d,i) {
  8207. dispatch.elementDblClick({
  8208. label: getX(d.data),
  8209. value: getY(d.data),
  8210. point: d.data,
  8211. index: i,
  8212. pos: d3.event,
  8213. id: id
  8214. });
  8215. d3.event.stopPropagation();
  8216. });
  8217. slices
  8218. .attr('fill', function(d,i) { return color(d, i); })
  8219. .attr('stroke', function(d,i) { return color(d, i); });
  8220. var paths = ae.append('path')
  8221. .each(function(d) { this._current = d; });
  8222. //.attr('d', arc);
  8223. slices.select('path')
  8224. .transition()
  8225. .attr('d', arc)
  8226. .attrTween('d', arcTween);
  8227. if (showLabels) {
  8228. // This does the normal label
  8229. var labelsArc = d3.svg.arc().innerRadius(0);
  8230. if (pieLabelsOutside){ labelsArc = arc; }
  8231. if (donutLabelsOutside) { labelsArc = d3.svg.arc().outerRadius(arc.outerRadius()); }
  8232. pieLabels.enter().append("g").classed("nv-label",true)
  8233. .each(function(d,i) {
  8234. var group = d3.select(this);
  8235. group
  8236. .attr('transform', function(d) {
  8237. if (labelSunbeamLayout) {
  8238. d.outerRadius = arcRadius + 10; // Set Outer Coordinate
  8239. d.innerRadius = arcRadius + 15; // Set Inner Coordinate
  8240. var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
  8241. if ((d.startAngle+d.endAngle)/2 < Math.PI) {
  8242. rotateAngle -= 90;
  8243. } else {
  8244. rotateAngle += 90;
  8245. }
  8246. return 'translate(' + labelsArc.centroid(d) + ') rotate(' + rotateAngle + ')';
  8247. } else {
  8248. d.outerRadius = radius + 10; // Set Outer Coordinate
  8249. d.innerRadius = radius + 15; // Set Inner Coordinate
  8250. return 'translate(' + labelsArc.centroid(d) + ')'
  8251. }
  8252. });
  8253. group.append('rect')
  8254. .style('stroke', '#fff')
  8255. .style('fill', '#fff')
  8256. .attr("rx", 3)
  8257. .attr("ry", 3);
  8258. group.append('text')
  8259. .style('text-anchor', labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle') //center the text on it's origin or begin/end if orthogonal aligned
  8260. .style('fill', '#000')
  8261. });
  8262. var labelLocationHash = {};
  8263. var avgHeight = 14;
  8264. var avgWidth = 140;
  8265. var createHashKey = function(coordinates) {
  8266. return Math.floor(coordinates[0]/avgWidth) * avgWidth + ',' + Math.floor(coordinates[1]/avgHeight) * avgHeight;
  8267. };
  8268. pieLabels.transition()
  8269. .attr('transform', function(d) {
  8270. if (labelSunbeamLayout) {
  8271. d.outerRadius = arcRadius + 10; // Set Outer Coordinate
  8272. d.innerRadius = arcRadius + 15; // Set Inner Coordinate
  8273. var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
  8274. if ((d.startAngle+d.endAngle)/2 < Math.PI) {
  8275. rotateAngle -= 90;
  8276. } else {
  8277. rotateAngle += 90;
  8278. }
  8279. return 'translate(' + labelsArc.centroid(d) + ') rotate(' + rotateAngle + ')';
  8280. } else {
  8281. d.outerRadius = radius + 10; // Set Outer Coordinate
  8282. d.innerRadius = radius + 15; // Set Inner Coordinate
  8283. /*
  8284. Overlapping pie labels are not good. What this attempts to do is, prevent overlapping.
  8285. Each label location is hashed, and if a hash collision occurs, we assume an overlap.
  8286. Adjust the label's y-position to remove the overlap.
  8287. */
  8288. var center = labelsArc.centroid(d);
  8289. var hashKey = createHashKey(center);
  8290. if (labelLocationHash[hashKey]) {
  8291. center[1] -= avgHeight;
  8292. }
  8293. labelLocationHash[createHashKey(center)] = true;
  8294. return 'translate(' + center + ')'
  8295. }
  8296. });
  8297. pieLabels.select(".nv-label text")
  8298. .style('text-anchor', labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle') //center the text on it's origin or begin/end if orthogonal aligned
  8299. .text(function(d, i) {
  8300. var percent = (d.endAngle - d.startAngle) / (2 * Math.PI);
  8301. var labelTypes = {
  8302. "key" : getX(d.data),
  8303. "value": getY(d.data),
  8304. "percent": d3.format('%')(percent)
  8305. };
  8306. return (d.value && percent > labelThreshold) ? labelTypes[labelType] : '';
  8307. });
  8308. }
  8309. // Computes the angle of an arc, converting from radians to degrees.
  8310. function angle(d) {
  8311. var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
  8312. return a > 90 ? a - 180 : a;
  8313. }
  8314. function arcTween(a) {
  8315. a.endAngle = isNaN(a.endAngle) ? 0 : a.endAngle;
  8316. a.startAngle = isNaN(a.startAngle) ? 0 : a.startAngle;
  8317. if (!donut) a.innerRadius = 0;
  8318. var i = d3.interpolate(this._current, a);
  8319. this._current = i(0);
  8320. return function(t) {
  8321. return arc(i(t));
  8322. };
  8323. }
  8324. function tweenPie(b) {
  8325. b.innerRadius = 0;
  8326. var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
  8327. return function(t) {
  8328. return arc(i(t));
  8329. };
  8330. }
  8331. });
  8332. return chart;
  8333. }
  8334. //============================================================
  8335. // Expose Public Variables
  8336. //------------------------------------------------------------
  8337. chart.dispatch = dispatch;
  8338. chart.options = nv.utils.optionsFunc.bind(chart);
  8339. chart.margin = function(_) {
  8340. if (!arguments.length) return margin;
  8341. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  8342. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  8343. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  8344. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  8345. return chart;
  8346. };
  8347. chart.width = function(_) {
  8348. if (!arguments.length) return width;
  8349. width = _;
  8350. return chart;
  8351. };
  8352. chart.height = function(_) {
  8353. if (!arguments.length) return height;
  8354. height = _;
  8355. return chart;
  8356. };
  8357. chart.values = function(_) {
  8358. nv.log("pie.values() is no longer supported.");
  8359. return chart;
  8360. };
  8361. chart.x = function(_) {
  8362. if (!arguments.length) return getX;
  8363. getX = _;
  8364. return chart;
  8365. };
  8366. chart.y = function(_) {
  8367. if (!arguments.length) return getY;
  8368. getY = d3.functor(_);
  8369. return chart;
  8370. };
  8371. chart.description = function(_) {
  8372. if (!arguments.length) return getDescription;
  8373. getDescription = _;
  8374. return chart;
  8375. };
  8376. chart.showLabels = function(_) {
  8377. if (!arguments.length) return showLabels;
  8378. showLabels = _;
  8379. return chart;
  8380. };
  8381. chart.labelSunbeamLayout = function(_) {
  8382. if (!arguments.length) return labelSunbeamLayout;
  8383. labelSunbeamLayout = _;
  8384. return chart;
  8385. };
  8386. chart.donutLabelsOutside = function(_) {
  8387. if (!arguments.length) return donutLabelsOutside;
  8388. donutLabelsOutside = _;
  8389. return chart;
  8390. };
  8391. chart.pieLabelsOutside = function(_) {
  8392. if (!arguments.length) return pieLabelsOutside;
  8393. pieLabelsOutside = _;
  8394. return chart;
  8395. };
  8396. chart.labelType = function(_) {
  8397. if (!arguments.length) return labelType;
  8398. labelType = _;
  8399. labelType = labelType || "key";
  8400. return chart;
  8401. };
  8402. chart.donut = function(_) {
  8403. if (!arguments.length) return donut;
  8404. donut = _;
  8405. return chart;
  8406. };
  8407. chart.donutRatio = function(_) {
  8408. if (!arguments.length) return donutRatio;
  8409. donutRatio = _;
  8410. return chart;
  8411. };
  8412. chart.startAngle = function(_) {
  8413. if (!arguments.length) return startAngle;
  8414. startAngle = _;
  8415. return chart;
  8416. };
  8417. chart.endAngle = function(_) {
  8418. if (!arguments.length) return endAngle;
  8419. endAngle = _;
  8420. return chart;
  8421. };
  8422. chart.id = function(_) {
  8423. if (!arguments.length) return id;
  8424. id = _;
  8425. return chart;
  8426. };
  8427. chart.color = function(_) {
  8428. if (!arguments.length) return color;
  8429. color = nv.utils.getColor(_);
  8430. return chart;
  8431. };
  8432. chart.valueFormat = function(_) {
  8433. if (!arguments.length) return valueFormat;
  8434. valueFormat = _;
  8435. return chart;
  8436. };
  8437. chart.labelThreshold = function(_) {
  8438. if (!arguments.length) return labelThreshold;
  8439. labelThreshold = _;
  8440. return chart;
  8441. };
  8442. //============================================================
  8443. return chart;
  8444. }
  8445. nv.models.pieChart = function() {
  8446. "use strict";
  8447. //============================================================
  8448. // Public Variables with Default Settings
  8449. //------------------------------------------------------------
  8450. var pie = nv.models.pie()
  8451. , legend = nv.models.legend()
  8452. ;
  8453. var margin = {top: 30, right: 20, bottom: 20, left: 20}
  8454. , width = null
  8455. , height = null
  8456. , showLegend = true
  8457. , color = nv.utils.defaultColor()
  8458. , tooltips = true
  8459. , tooltip = function(key, y, e, graph) {
  8460. return '<h3>' + key + '</h3>' +
  8461. '<p>' + y + '</p>'
  8462. }
  8463. , state = {}
  8464. , defaultState = null
  8465. , noData = "No Data Available."
  8466. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
  8467. ;
  8468. //============================================================
  8469. //============================================================
  8470. // Private Variables
  8471. //------------------------------------------------------------
  8472. var showTooltip = function(e, offsetElement) {
  8473. var tooltipLabel = pie.description()(e.point) || pie.x()(e.point)
  8474. var left = e.pos[0] + ( (offsetElement && offsetElement.offsetLeft) || 0 ),
  8475. top = e.pos[1] + ( (offsetElement && offsetElement.offsetTop) || 0),
  8476. y = pie.valueFormat()(pie.y()(e.point)),
  8477. content = tooltip(tooltipLabel, y, e, chart);
  8478. nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
  8479. };
  8480. //============================================================
  8481. function chart(selection) {
  8482. selection.each(function(data) {
  8483. var container = d3.select(this),
  8484. that = this;
  8485. var availableWidth = (width || parseInt(container.style('width')) || 960)
  8486. - margin.left - margin.right,
  8487. availableHeight = (height || parseInt(container.style('height')) || 400)
  8488. - margin.top - margin.bottom;
  8489. chart.update = function() { container.transition().call(chart); };
  8490. chart.container = this;
  8491. //set state.disabled
  8492. state.disabled = data.map(function(d) { return !!d.disabled });
  8493. if (!defaultState) {
  8494. var key;
  8495. defaultState = {};
  8496. for (key in state) {
  8497. if (state[key] instanceof Array)
  8498. defaultState[key] = state[key].slice(0);
  8499. else
  8500. defaultState[key] = state[key];
  8501. }
  8502. }
  8503. //------------------------------------------------------------
  8504. // Display No Data message if there's nothing to show.
  8505. if (!data || !data.length) {
  8506. var noDataText = container.selectAll('.nv-noData').data([noData]);
  8507. noDataText.enter().append('text')
  8508. .attr('class', 'nvd3 nv-noData')
  8509. .attr('dy', '-.7em')
  8510. .style('text-anchor', 'middle');
  8511. noDataText
  8512. .attr('x', margin.left + availableWidth / 2)
  8513. .attr('y', margin.top + availableHeight / 2)
  8514. .text(function(d) { return d });
  8515. return chart;
  8516. } else {
  8517. container.selectAll('.nv-noData').remove();
  8518. }
  8519. //------------------------------------------------------------
  8520. //------------------------------------------------------------
  8521. // Setup containers and skeleton of chart
  8522. var wrap = container.selectAll('g.nv-wrap.nv-pieChart').data([data]);
  8523. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-pieChart').append('g');
  8524. var g = wrap.select('g');
  8525. gEnter.append('g').attr('class', 'nv-pieWrap');
  8526. gEnter.append('g').attr('class', 'nv-legendWrap');
  8527. //------------------------------------------------------------
  8528. //------------------------------------------------------------
  8529. // Legend
  8530. if (showLegend) {
  8531. legend
  8532. .width( availableWidth )
  8533. .key(pie.x());
  8534. wrap.select('.nv-legendWrap')
  8535. .datum(data)
  8536. .call(legend);
  8537. if ( margin.top != legend.height()) {
  8538. margin.top = legend.height();
  8539. availableHeight = (height || parseInt(container.style('height')) || 400)
  8540. - margin.top - margin.bottom;
  8541. }
  8542. wrap.select('.nv-legendWrap')
  8543. .attr('transform', 'translate(0,' + (-margin.top) +')');
  8544. }
  8545. //------------------------------------------------------------
  8546. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  8547. //------------------------------------------------------------
  8548. // Main Chart Component(s)
  8549. pie
  8550. .width(availableWidth)
  8551. .height(availableHeight);
  8552. var pieWrap = g.select('.nv-pieWrap')
  8553. .datum([data]);
  8554. d3.transition(pieWrap).call(pie);
  8555. //------------------------------------------------------------
  8556. //============================================================
  8557. // Event Handling/Dispatching (in chart's scope)
  8558. //------------------------------------------------------------
  8559. legend.dispatch.on('stateChange', function(newState) {
  8560. state = newState;
  8561. dispatch.stateChange(state);
  8562. chart.update();
  8563. });
  8564. pie.dispatch.on('elementMouseout.tooltip', function(e) {
  8565. dispatch.tooltipHide(e);
  8566. });
  8567. // Update chart from a state object passed to event handler
  8568. dispatch.on('changeState', function(e) {
  8569. if (typeof e.disabled !== 'undefined') {
  8570. data.forEach(function(series,i) {
  8571. series.disabled = e.disabled[i];
  8572. });
  8573. state.disabled = e.disabled;
  8574. }
  8575. chart.update();
  8576. });
  8577. //============================================================
  8578. });
  8579. return chart;
  8580. }
  8581. //============================================================
  8582. // Event Handling/Dispatching (out of chart's scope)
  8583. //------------------------------------------------------------
  8584. pie.dispatch.on('elementMouseover.tooltip', function(e) {
  8585. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  8586. dispatch.tooltipShow(e);
  8587. });
  8588. dispatch.on('tooltipShow', function(e) {
  8589. if (tooltips) showTooltip(e);
  8590. });
  8591. dispatch.on('tooltipHide', function() {
  8592. if (tooltips) nv.tooltip.cleanup();
  8593. });
  8594. //============================================================
  8595. //============================================================
  8596. // Expose Public Variables
  8597. //------------------------------------------------------------
  8598. // expose chart's sub-components
  8599. chart.legend = legend;
  8600. chart.dispatch = dispatch;
  8601. chart.pie = pie;
  8602. d3.rebind(chart, pie, 'valueFormat', 'values', 'x', 'y', 'description', 'id', 'showLabels', 'donutLabelsOutside', 'pieLabelsOutside', 'labelType', 'donut', 'donutRatio', 'labelThreshold');
  8603. chart.options = nv.utils.optionsFunc.bind(chart);
  8604. chart.margin = function(_) {
  8605. if (!arguments.length) return margin;
  8606. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  8607. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  8608. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  8609. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  8610. return chart;
  8611. };
  8612. chart.width = function(_) {
  8613. if (!arguments.length) return width;
  8614. width = _;
  8615. return chart;
  8616. };
  8617. chart.height = function(_) {
  8618. if (!arguments.length) return height;
  8619. height = _;
  8620. return chart;
  8621. };
  8622. chart.color = function(_) {
  8623. if (!arguments.length) return color;
  8624. color = nv.utils.getColor(_);
  8625. legend.color(color);
  8626. pie.color(color);
  8627. return chart;
  8628. };
  8629. chart.showLegend = function(_) {
  8630. if (!arguments.length) return showLegend;
  8631. showLegend = _;
  8632. return chart;
  8633. };
  8634. chart.tooltips = function(_) {
  8635. if (!arguments.length) return tooltips;
  8636. tooltips = _;
  8637. return chart;
  8638. };
  8639. chart.tooltipContent = function(_) {
  8640. if (!arguments.length) return tooltip;
  8641. tooltip = _;
  8642. return chart;
  8643. };
  8644. chart.state = function(_) {
  8645. if (!arguments.length) return state;
  8646. state = _;
  8647. return chart;
  8648. };
  8649. chart.defaultState = function(_) {
  8650. if (!arguments.length) return defaultState;
  8651. defaultState = _;
  8652. return chart;
  8653. };
  8654. chart.noData = function(_) {
  8655. if (!arguments.length) return noData;
  8656. noData = _;
  8657. return chart;
  8658. };
  8659. //============================================================
  8660. return chart;
  8661. }
  8662. nv.models.scatter = function() {
  8663. "use strict";
  8664. //============================================================
  8665. // Public Variables with Default Settings
  8666. //------------------------------------------------------------
  8667. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  8668. , width = 960
  8669. , height = 500
  8670. , color = nv.utils.defaultColor() // chooses color
  8671. , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't select one
  8672. , x = d3.scale.linear()
  8673. , y = d3.scale.linear()
  8674. , z = d3.scale.linear() //linear because d3.svg.shape.size is treated as area
  8675. , getX = function(d) { return d.x } // accessor to get the x value
  8676. , getY = function(d) { return d.y } // accessor to get the y value
  8677. , getSize = function(d) { return d.size || 1} // accessor to get the point size
  8678. , getShape = function(d) { return d.shape || 'circle' } // accessor to get point shape
  8679. , onlyCircles = true // Set to false to use shapes
  8680. , forceX = [] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
  8681. , forceY = [] // List of numbers to Force into the Y scale
  8682. , forceSize = [] // List of numbers to Force into the Size scale
  8683. , interactive = true // If true, plots a voronoi overlay for advanced point intersection
  8684. , pointKey = null
  8685. , pointActive = function(d) { return !d.notActive } // any points that return false will be filtered out
  8686. , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
  8687. , padDataOuter = .1 //outerPadding to imitate ordinal scale outer padding
  8688. , clipEdge = false // if true, masks points within x and y scale
  8689. , clipVoronoi = true // if true, masks each point with a circle... can turn off to slightly increase performance
  8690. , clipRadius = function() { return 25 } // function to get the radius for voronoi point clips
  8691. , xDomain = null // Override x domain (skips the calculation from data)
  8692. , yDomain = null // Override y domain
  8693. , xRange = null // Override x range
  8694. , yRange = null // Override y range
  8695. , sizeDomain = null // Override point size domain
  8696. , sizeRange = null
  8697. , singlePoint = false
  8698. , dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout')
  8699. , useVoronoi = true
  8700. ;
  8701. //============================================================
  8702. //============================================================
  8703. // Private Variables
  8704. //------------------------------------------------------------
  8705. var x0, y0, z0 // used to store previous scales
  8706. , timeoutID
  8707. , needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips
  8708. ;
  8709. //============================================================
  8710. function chart(selection) {
  8711. selection.each(function(data) {
  8712. var availableWidth = width - margin.left - margin.right,
  8713. availableHeight = height - margin.top - margin.bottom,
  8714. container = d3.select(this);
  8715. //add series index to each data point for reference
  8716. data.forEach(function(series, i) {
  8717. series.values.forEach(function(point) {
  8718. point.series = i;
  8719. });
  8720. });
  8721. //------------------------------------------------------------
  8722. // Setup Scales
  8723. // remap and flatten the data for use in calculating the scales' domains
  8724. var seriesData = (xDomain && yDomain && sizeDomain) ? [] : // if we know xDomain and yDomain and sizeDomain, no need to calculate.... if Size is constant remember to set sizeDomain to speed up performance
  8725. d3.merge(
  8726. data.map(function(d) {
  8727. return d.values.map(function(d,i) {
  8728. return { x: getX(d,i), y: getY(d,i), size: getSize(d,i) }
  8729. })
  8730. })
  8731. );
  8732. x .domain(xDomain || d3.extent(seriesData.map(function(d) { return d.x; }).concat(forceX)))
  8733. if (padData && data[0])
  8734. x.range(xRange || [(availableWidth * padDataOuter + availableWidth) / (2 *data[0].values.length), availableWidth - availableWidth * (1 + padDataOuter) / (2 * data[0].values.length) ]);
  8735. //x.range([availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
  8736. else
  8737. x.range(xRange || [0, availableWidth]);
  8738. y .domain(yDomain || d3.extent(seriesData.map(function(d) { return d.y }).concat(forceY)))
  8739. .range(yRange || [availableHeight, 0]);
  8740. z .domain(sizeDomain || d3.extent(seriesData.map(function(d) { return d.size }).concat(forceSize)))
  8741. .range(sizeRange || [16, 256]);
  8742. // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
  8743. if (x.domain()[0] === x.domain()[1] || y.domain()[0] === y.domain()[1]) singlePoint = true;
  8744. if (x.domain()[0] === x.domain()[1])
  8745. x.domain()[0] ?
  8746. x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
  8747. : x.domain([-1,1]);
  8748. if (y.domain()[0] === y.domain()[1])
  8749. y.domain()[0] ?
  8750. y.domain([y.domain()[0] - y.domain()[0] * 0.01, y.domain()[1] + y.domain()[1] * 0.01])
  8751. : y.domain([-1,1]);
  8752. if ( isNaN(x.domain()[0])) {
  8753. x.domain([-1,1]);
  8754. }
  8755. if ( isNaN(y.domain()[0])) {
  8756. y.domain([-1,1]);
  8757. }
  8758. x0 = x0 || x;
  8759. y0 = y0 || y;
  8760. z0 = z0 || z;
  8761. //------------------------------------------------------------
  8762. //------------------------------------------------------------
  8763. // Setup containers and skeleton of chart
  8764. var wrap = container.selectAll('g.nv-wrap.nv-scatter').data([data]);
  8765. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatter nv-chart-' + id + (singlePoint ? ' nv-single-point' : ''));
  8766. var defsEnter = wrapEnter.append('defs');
  8767. var gEnter = wrapEnter.append('g');
  8768. var g = wrap.select('g');
  8769. gEnter.append('g').attr('class', 'nv-groups');
  8770. gEnter.append('g').attr('class', 'nv-point-paths');
  8771. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  8772. //------------------------------------------------------------
  8773. defsEnter.append('clipPath')
  8774. .attr('id', 'nv-edge-clip-' + id)
  8775. .append('rect');
  8776. wrap.select('#nv-edge-clip-' + id + ' rect')
  8777. .attr('width', availableWidth)
  8778. .attr('height', (availableHeight > 0) ? availableHeight : 0);
  8779. g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
  8780. function updateInteractiveLayer() {
  8781. if (!interactive) return false;
  8782. var eventElements;
  8783. var vertices = d3.merge(data.map(function(group, groupIndex) {
  8784. return group.values
  8785. .map(function(point, pointIndex) {
  8786. // *Adding noise to make duplicates very unlikely
  8787. // *Injecting series and point index for reference
  8788. /* *Adding a 'jitter' to the points, because there's an issue in d3.geom.voronoi.
  8789. */
  8790. var pX = getX(point,pointIndex);
  8791. var pY = getY(point,pointIndex);
  8792. return [x(pX)+ Math.random() * 1e-7,
  8793. y(pY)+ Math.random() * 1e-7,
  8794. groupIndex,
  8795. pointIndex, point]; //temp hack to add noise untill I think of a better way so there are no duplicates
  8796. })
  8797. .filter(function(pointArray, pointIndex) {
  8798. return pointActive(pointArray[4], pointIndex); // Issue #237.. move filter to after map, so pointIndex is correct!
  8799. })
  8800. })
  8801. );
  8802. //inject series and point index for reference into voronoi
  8803. if (useVoronoi === true) {
  8804. if (clipVoronoi) {
  8805. var pointClipsEnter = wrap.select('defs').selectAll('.nv-point-clips')
  8806. .data([id])
  8807. .enter();
  8808. pointClipsEnter.append('clipPath')
  8809. .attr('class', 'nv-point-clips')
  8810. .attr('id', 'nv-points-clip-' + id);
  8811. var pointClips = wrap.select('#nv-points-clip-' + id).selectAll('circle')
  8812. .data(vertices);
  8813. pointClips.enter().append('circle')
  8814. .attr('r', clipRadius);
  8815. pointClips.exit().remove();
  8816. pointClips
  8817. .attr('cx', function(d) { return d[0] })
  8818. .attr('cy', function(d) { return d[1] });
  8819. wrap.select('.nv-point-paths')
  8820. .attr('clip-path', 'url(#nv-points-clip-' + id + ')');
  8821. }
  8822. if(vertices.length) {
  8823. // Issue #283 - Adding 2 dummy points to the voronoi b/c voronoi requires min 3 points to work
  8824. vertices.push([x.range()[0] - 20, y.range()[0] - 20, null, null]);
  8825. vertices.push([x.range()[1] + 20, y.range()[1] + 20, null, null]);
  8826. vertices.push([x.range()[0] - 20, y.range()[0] + 20, null, null]);
  8827. vertices.push([x.range()[1] + 20, y.range()[1] - 20, null, null]);
  8828. }
  8829. var bounds = d3.geom.polygon([
  8830. [-10,-10],
  8831. [-10,height + 10],
  8832. [width + 10,height + 10],
  8833. [width + 10,-10]
  8834. ]);
  8835. var voronoi = d3.geom.voronoi(vertices).map(function(d, i) {
  8836. return {
  8837. 'data': bounds.clip(d),
  8838. 'series': vertices[i][2],
  8839. 'point': vertices[i][3]
  8840. }
  8841. });
  8842. var pointPaths = wrap.select('.nv-point-paths').selectAll('path')
  8843. .data(voronoi);
  8844. pointPaths.enter().append('path')
  8845. .attr('class', function(d,i) { return 'nv-path-'+i; });
  8846. pointPaths.exit().remove();
  8847. pointPaths
  8848. .attr('d', function(d) {
  8849. if (d.data.length === 0)
  8850. return 'M 0 0'
  8851. else
  8852. return 'M' + d.data.join('L') + 'Z';
  8853. });
  8854. var mouseEventCallback = function(d,mDispatch) {
  8855. if (needsUpdate) return 0;
  8856. var series = data[d.series];
  8857. if (typeof series === 'undefined') return;
  8858. var point = series.values[d.point];
  8859. mDispatch({
  8860. point: point,
  8861. series: series,
  8862. pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
  8863. seriesIndex: d.series,
  8864. pointIndex: d.point
  8865. });
  8866. };
  8867. pointPaths
  8868. .on('click', function(d) {
  8869. mouseEventCallback(d, dispatch.elementClick);
  8870. })
  8871. .on('mouseover', function(d) {
  8872. mouseEventCallback(d, dispatch.elementMouseover);
  8873. })
  8874. .on('mouseout', function(d, i) {
  8875. mouseEventCallback(d, dispatch.elementMouseout);
  8876. });
  8877. } else {
  8878. /*
  8879. // bring data in form needed for click handlers
  8880. var dataWithPoints = vertices.map(function(d, i) {
  8881. return {
  8882. 'data': d,
  8883. 'series': vertices[i][2],
  8884. 'point': vertices[i][3]
  8885. }
  8886. });
  8887. */
  8888. // add event handlers to points instead voronoi paths
  8889. wrap.select('.nv-groups').selectAll('.nv-group')
  8890. .selectAll('.nv-point')
  8891. //.data(dataWithPoints)
  8892. //.style('pointer-events', 'auto') // recativate events, disabled by css
  8893. .on('click', function(d,i) {
  8894. //nv.log('test', d, i);
  8895. if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
  8896. var series = data[d.series],
  8897. point = series.values[i];
  8898. dispatch.elementClick({
  8899. point: point,
  8900. series: series,
  8901. pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
  8902. seriesIndex: d.series,
  8903. pointIndex: i
  8904. });
  8905. })
  8906. .on('mouseover', function(d,i) {
  8907. if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
  8908. var series = data[d.series],
  8909. point = series.values[i];
  8910. dispatch.elementMouseover({
  8911. point: point,
  8912. series: series,
  8913. pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
  8914. seriesIndex: d.series,
  8915. pointIndex: i
  8916. });
  8917. })
  8918. .on('mouseout', function(d,i) {
  8919. if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
  8920. var series = data[d.series],
  8921. point = series.values[i];
  8922. dispatch.elementMouseout({
  8923. point: point,
  8924. series: series,
  8925. seriesIndex: d.series,
  8926. pointIndex: i
  8927. });
  8928. });
  8929. }
  8930. needsUpdate = false;
  8931. }
  8932. needsUpdate = true;
  8933. var groups = wrap.select('.nv-groups').selectAll('.nv-group')
  8934. .data(function(d) { return d }, function(d) { return d.key });
  8935. groups.enter().append('g')
  8936. .style('stroke-opacity', 1e-6)
  8937. .style('fill-opacity', 1e-6);
  8938. groups.exit()
  8939. .remove();
  8940. groups
  8941. .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
  8942. .classed('hover', function(d) { return d.hover });
  8943. groups
  8944. .transition()
  8945. .style('fill', function(d,i) { return color(d, i) })
  8946. .style('stroke', function(d,i) { return color(d, i) })
  8947. .style('stroke-opacity', 1)
  8948. .style('fill-opacity', .5);
  8949. if (onlyCircles) {
  8950. var points = groups.selectAll('circle.nv-point')
  8951. .data(function(d) { return d.values }, pointKey);
  8952. points.enter().append('circle')
  8953. .style('fill', function (d,i) { return d.color })
  8954. .style('stroke', function (d,i) { return d.color })
  8955. .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
  8956. .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
  8957. .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) });
  8958. points.exit().remove();
  8959. groups.exit().selectAll('path.nv-point').transition()
  8960. .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
  8961. .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
  8962. .remove();
  8963. points.each(function(d,i) {
  8964. d3.select(this)
  8965. .classed('nv-point', true)
  8966. .classed('nv-point-' + i, true)
  8967. .classed('hover',false)
  8968. ;
  8969. });
  8970. points.transition()
  8971. .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
  8972. .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
  8973. .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) });
  8974. } else {
  8975. var points = groups.selectAll('path.nv-point')
  8976. .data(function(d) { return d.values });
  8977. points.enter().append('path')
  8978. .style('fill', function (d,i) { return d.color })
  8979. .style('stroke', function (d,i) { return d.color })
  8980. .attr('transform', function(d,i) {
  8981. return 'translate(' + x0(getX(d,i)) + ',' + y0(getY(d,i)) + ')'
  8982. })
  8983. .attr('d',
  8984. d3.svg.symbol()
  8985. .type(getShape)
  8986. .size(function(d,i) { return z(getSize(d,i)) })
  8987. );
  8988. points.exit().remove();
  8989. groups.exit().selectAll('path.nv-point')
  8990. .transition()
  8991. .attr('transform', function(d,i) {
  8992. return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
  8993. })
  8994. .remove();
  8995. points.each(function(d,i) {
  8996. d3.select(this)
  8997. .classed('nv-point', true)
  8998. .classed('nv-point-' + i, true)
  8999. .classed('hover',false)
  9000. ;
  9001. });
  9002. points.transition()
  9003. .attr('transform', function(d,i) {
  9004. //nv.log(d,i,getX(d,i), x(getX(d,i)));
  9005. return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
  9006. })
  9007. .attr('d',
  9008. d3.svg.symbol()
  9009. .type(getShape)
  9010. .size(function(d,i) { return z(getSize(d,i)) })
  9011. );
  9012. }
  9013. // Delay updating the invisible interactive layer for smoother animation
  9014. clearTimeout(timeoutID); // stop repeat calls to updateInteractiveLayer
  9015. timeoutID = setTimeout(updateInteractiveLayer, 300);
  9016. //updateInteractiveLayer();
  9017. //store old scales for use in transitions on update
  9018. x0 = x.copy();
  9019. y0 = y.copy();
  9020. z0 = z.copy();
  9021. });
  9022. return chart;
  9023. }
  9024. //============================================================
  9025. // Event Handling/Dispatching (out of chart's scope)
  9026. //------------------------------------------------------------
  9027. chart.clearHighlights = function() {
  9028. //Remove the 'hover' class from all highlighted points.
  9029. d3.selectAll(".nv-chart-" + id + " .nv-point.hover").classed("hover",false);
  9030. };
  9031. chart.highlightPoint = function(seriesIndex,pointIndex,isHoverOver) {
  9032. d3.select(".nv-chart-" + id + " .nv-series-" + seriesIndex + " .nv-point-" + pointIndex)
  9033. .classed("hover",isHoverOver);
  9034. };
  9035. dispatch.on('elementMouseover.point', function(d) {
  9036. if (interactive) chart.highlightPoint(d.seriesIndex,d.pointIndex,true);
  9037. });
  9038. dispatch.on('elementMouseout.point', function(d) {
  9039. if (interactive) chart.highlightPoint(d.seriesIndex,d.pointIndex,false);
  9040. });
  9041. //============================================================
  9042. //============================================================
  9043. // Expose Public Variables
  9044. //------------------------------------------------------------
  9045. chart.dispatch = dispatch;
  9046. chart.options = nv.utils.optionsFunc.bind(chart);
  9047. chart.x = function(_) {
  9048. if (!arguments.length) return getX;
  9049. getX = d3.functor(_);
  9050. return chart;
  9051. };
  9052. chart.y = function(_) {
  9053. if (!arguments.length) return getY;
  9054. getY = d3.functor(_);
  9055. return chart;
  9056. };
  9057. chart.size = function(_) {
  9058. if (!arguments.length) return getSize;
  9059. getSize = d3.functor(_);
  9060. return chart;
  9061. };
  9062. chart.margin = function(_) {
  9063. if (!arguments.length) return margin;
  9064. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  9065. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  9066. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  9067. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  9068. return chart;
  9069. };
  9070. chart.width = function(_) {
  9071. if (!arguments.length) return width;
  9072. width = _;
  9073. return chart;
  9074. };
  9075. chart.height = function(_) {
  9076. if (!arguments.length) return height;
  9077. height = _;
  9078. return chart;
  9079. };
  9080. chart.xScale = function(_) {
  9081. if (!arguments.length) return x;
  9082. x = _;
  9083. return chart;
  9084. };
  9085. chart.yScale = function(_) {
  9086. if (!arguments.length) return y;
  9087. y = _;
  9088. return chart;
  9089. };
  9090. chart.zScale = function(_) {
  9091. if (!arguments.length) return z;
  9092. z = _;
  9093. return chart;
  9094. };
  9095. chart.xDomain = function(_) {
  9096. if (!arguments.length) return xDomain;
  9097. xDomain = _;
  9098. return chart;
  9099. };
  9100. chart.yDomain = function(_) {
  9101. if (!arguments.length) return yDomain;
  9102. yDomain = _;
  9103. return chart;
  9104. };
  9105. chart.sizeDomain = function(_) {
  9106. if (!arguments.length) return sizeDomain;
  9107. sizeDomain = _;
  9108. return chart;
  9109. };
  9110. chart.xRange = function(_) {
  9111. if (!arguments.length) return xRange;
  9112. xRange = _;
  9113. return chart;
  9114. };
  9115. chart.yRange = function(_) {
  9116. if (!arguments.length) return yRange;
  9117. yRange = _;
  9118. return chart;
  9119. };
  9120. chart.sizeRange = function(_) {
  9121. if (!arguments.length) return sizeRange;
  9122. sizeRange = _;
  9123. return chart;
  9124. };
  9125. chart.forceX = function(_) {
  9126. if (!arguments.length) return forceX;
  9127. forceX = _;
  9128. return chart;
  9129. };
  9130. chart.forceY = function(_) {
  9131. if (!arguments.length) return forceY;
  9132. forceY = _;
  9133. return chart;
  9134. };
  9135. chart.forceSize = function(_) {
  9136. if (!arguments.length) return forceSize;
  9137. forceSize = _;
  9138. return chart;
  9139. };
  9140. chart.interactive = function(_) {
  9141. if (!arguments.length) return interactive;
  9142. interactive = _;
  9143. return chart;
  9144. };
  9145. chart.pointKey = function(_) {
  9146. if (!arguments.length) return pointKey;
  9147. pointKey = _;
  9148. return chart;
  9149. };
  9150. chart.pointActive = function(_) {
  9151. if (!arguments.length) return pointActive;
  9152. pointActive = _;
  9153. return chart;
  9154. };
  9155. chart.padData = function(_) {
  9156. if (!arguments.length) return padData;
  9157. padData = _;
  9158. return chart;
  9159. };
  9160. chart.padDataOuter = function(_) {
  9161. if (!arguments.length) return padDataOuter;
  9162. padDataOuter = _;
  9163. return chart;
  9164. };
  9165. chart.clipEdge = function(_) {
  9166. if (!arguments.length) return clipEdge;
  9167. clipEdge = _;
  9168. return chart;
  9169. };
  9170. chart.clipVoronoi= function(_) {
  9171. if (!arguments.length) return clipVoronoi;
  9172. clipVoronoi = _;
  9173. return chart;
  9174. };
  9175. chart.useVoronoi= function(_) {
  9176. if (!arguments.length) return useVoronoi;
  9177. useVoronoi = _;
  9178. if (useVoronoi === false) {
  9179. clipVoronoi = false;
  9180. }
  9181. return chart;
  9182. };
  9183. chart.clipRadius = function(_) {
  9184. if (!arguments.length) return clipRadius;
  9185. clipRadius = _;
  9186. return chart;
  9187. };
  9188. chart.color = function(_) {
  9189. if (!arguments.length) return color;
  9190. color = nv.utils.getColor(_);
  9191. return chart;
  9192. };
  9193. chart.shape = function(_) {
  9194. if (!arguments.length) return getShape;
  9195. getShape = _;
  9196. return chart;
  9197. };
  9198. chart.onlyCircles = function(_) {
  9199. if (!arguments.length) return onlyCircles;
  9200. onlyCircles = _;
  9201. return chart;
  9202. };
  9203. chart.id = function(_) {
  9204. if (!arguments.length) return id;
  9205. id = _;
  9206. return chart;
  9207. };
  9208. chart.singlePoint = function(_) {
  9209. if (!arguments.length) return singlePoint;
  9210. singlePoint = _;
  9211. return chart;
  9212. };
  9213. //============================================================
  9214. return chart;
  9215. }
  9216. nv.models.scatterChart = function() {
  9217. "use strict";
  9218. //============================================================
  9219. // Public Variables with Default Settings
  9220. //------------------------------------------------------------
  9221. var scatter = nv.models.scatter()
  9222. , xAxis = nv.models.axis()
  9223. , yAxis = nv.models.axis()
  9224. , legend = nv.models.legend()
  9225. , controls = nv.models.legend()
  9226. , distX = nv.models.distribution()
  9227. , distY = nv.models.distribution()
  9228. ;
  9229. var margin = {top: 30, right: 20, bottom: 50, left: 75}
  9230. , width = null
  9231. , height = null
  9232. , color = nv.utils.defaultColor()
  9233. , x = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.xScale()
  9234. , y = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.yScale()
  9235. , xPadding = 0
  9236. , yPadding = 0
  9237. , showDistX = false
  9238. , showDistY = false
  9239. , showLegend = true
  9240. , showXAxis = true
  9241. , showYAxis = true
  9242. , rightAlignYAxis = false
  9243. , showControls = !!d3.fisheye
  9244. , fisheye = 0
  9245. , pauseFisheye = false
  9246. , tooltips = true
  9247. , tooltipX = function(key, x, y) { return '<strong>' + x + '</strong>' }
  9248. , tooltipY = function(key, x, y) { return '<strong>' + y + '</strong>' }
  9249. , tooltip = null
  9250. , state = {}
  9251. , defaultState = null
  9252. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
  9253. , noData = "No Data Available."
  9254. , transitionDuration = 250
  9255. ;
  9256. scatter
  9257. .xScale(x)
  9258. .yScale(y)
  9259. ;
  9260. xAxis
  9261. .orient('bottom')
  9262. .tickPadding(10)
  9263. ;
  9264. yAxis
  9265. .orient((rightAlignYAxis) ? 'right' : 'left')
  9266. .tickPadding(10)
  9267. ;
  9268. distX
  9269. .axis('x')
  9270. ;
  9271. distY
  9272. .axis('y')
  9273. ;
  9274. controls.updateState(false);
  9275. //============================================================
  9276. //============================================================
  9277. // Private Variables
  9278. //------------------------------------------------------------
  9279. var x0, y0;
  9280. var showTooltip = function(e, offsetElement) {
  9281. //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?)
  9282. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  9283. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  9284. leftX = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  9285. topX = y.range()[0] + margin.top + ( offsetElement.offsetTop || 0),
  9286. leftY = x.range()[0] + margin.left + ( offsetElement.offsetLeft || 0 ),
  9287. topY = e.pos[1] + ( offsetElement.offsetTop || 0),
  9288. xVal = xAxis.tickFormat()(scatter.x()(e.point, e.pointIndex)),
  9289. yVal = yAxis.tickFormat()(scatter.y()(e.point, e.pointIndex));
  9290. if( tooltipX != null )
  9291. nv.tooltip.show([leftX, topX], tooltipX(e.series.key, xVal, yVal, e, chart), 'n', 1, offsetElement, 'x-nvtooltip');
  9292. if( tooltipY != null )
  9293. nv.tooltip.show([leftY, topY], tooltipY(e.series.key, xVal, yVal, e, chart), 'e', 1, offsetElement, 'y-nvtooltip');
  9294. if( tooltip != null )
  9295. nv.tooltip.show([left, top], tooltip(e.series.key, xVal, yVal, e, chart), e.value < 0 ? 'n' : 's', null, offsetElement);
  9296. };
  9297. var controlsData = [
  9298. { key: 'Magnify', disabled: true }
  9299. ];
  9300. //============================================================
  9301. function chart(selection) {
  9302. selection.each(function(data) {
  9303. var container = d3.select(this),
  9304. that = this;
  9305. var availableWidth = (width || parseInt(container.style('width')) || 960)
  9306. - margin.left - margin.right,
  9307. availableHeight = (height || parseInt(container.style('height')) || 400)
  9308. - margin.top - margin.bottom;
  9309. chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
  9310. chart.container = this;
  9311. //set state.disabled
  9312. state.disabled = data.map(function(d) { return !!d.disabled });
  9313. if (!defaultState) {
  9314. var key;
  9315. defaultState = {};
  9316. for (key in state) {
  9317. if (state[key] instanceof Array)
  9318. defaultState[key] = state[key].slice(0);
  9319. else
  9320. defaultState[key] = state[key];
  9321. }
  9322. }
  9323. //------------------------------------------------------------
  9324. // Display noData message if there's nothing to show.
  9325. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  9326. var noDataText = container.selectAll('.nv-noData').data([noData]);
  9327. noDataText.enter().append('text')
  9328. .attr('class', 'nvd3 nv-noData')
  9329. .attr('dy', '-.7em')
  9330. .style('text-anchor', 'middle');
  9331. noDataText
  9332. .attr('x', margin.left + availableWidth / 2)
  9333. .attr('y', margin.top + availableHeight / 2)
  9334. .text(function(d) { return d });
  9335. return chart;
  9336. } else {
  9337. container.selectAll('.nv-noData').remove();
  9338. }
  9339. //------------------------------------------------------------
  9340. //------------------------------------------------------------
  9341. // Setup Scales
  9342. x0 = x0 || x;
  9343. y0 = y0 || y;
  9344. //------------------------------------------------------------
  9345. //------------------------------------------------------------
  9346. // Setup containers and skeleton of chart
  9347. var wrap = container.selectAll('g.nv-wrap.nv-scatterChart').data([data]);
  9348. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter.id());
  9349. var gEnter = wrapEnter.append('g');
  9350. var g = wrap.select('g');
  9351. // background for pointer events
  9352. gEnter.append('rect').attr('class', 'nvd3 nv-background');
  9353. gEnter.append('g').attr('class', 'nv-x nv-axis');
  9354. gEnter.append('g').attr('class', 'nv-y nv-axis');
  9355. gEnter.append('g').attr('class', 'nv-scatterWrap');
  9356. gEnter.append('g').attr('class', 'nv-distWrap');
  9357. gEnter.append('g').attr('class', 'nv-legendWrap');
  9358. gEnter.append('g').attr('class', 'nv-controlsWrap');
  9359. //------------------------------------------------------------
  9360. //------------------------------------------------------------
  9361. // Legend
  9362. if (showLegend) {
  9363. var legendWidth = (showControls) ? availableWidth / 2 : availableWidth;
  9364. legend.width(legendWidth);
  9365. wrap.select('.nv-legendWrap')
  9366. .datum(data)
  9367. .call(legend);
  9368. if ( margin.top != legend.height()) {
  9369. margin.top = legend.height();
  9370. availableHeight = (height || parseInt(container.style('height')) || 400)
  9371. - margin.top - margin.bottom;
  9372. }
  9373. wrap.select('.nv-legendWrap')
  9374. .attr('transform', 'translate(' + (availableWidth - legendWidth) + ',' + (-margin.top) +')');
  9375. }
  9376. //------------------------------------------------------------
  9377. //------------------------------------------------------------
  9378. // Controls
  9379. if (showControls) {
  9380. controls.width(180).color(['#444']);
  9381. g.select('.nv-controlsWrap')
  9382. .datum(controlsData)
  9383. .attr('transform', 'translate(0,' + (-margin.top) +')')
  9384. .call(controls);
  9385. }
  9386. //------------------------------------------------------------
  9387. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  9388. if (rightAlignYAxis) {
  9389. g.select(".nv-y.nv-axis")
  9390. .attr("transform", "translate(" + availableWidth + ",0)");
  9391. }
  9392. //------------------------------------------------------------
  9393. // Main Chart Component(s)
  9394. scatter
  9395. .width(availableWidth)
  9396. .height(availableHeight)
  9397. .color(data.map(function(d,i) {
  9398. return d.color || color(d, i);
  9399. }).filter(function(d,i) { return !data[i].disabled }));
  9400. if (xPadding !== 0)
  9401. scatter.xDomain(null);
  9402. if (yPadding !== 0)
  9403. scatter.yDomain(null);
  9404. wrap.select('.nv-scatterWrap')
  9405. .datum(data.filter(function(d) { return !d.disabled }))
  9406. .call(scatter);
  9407. //Adjust for x and y padding
  9408. if (xPadding !== 0) {
  9409. var xRange = x.domain()[1] - x.domain()[0];
  9410. scatter.xDomain([x.domain()[0] - (xPadding * xRange), x.domain()[1] + (xPadding * xRange)]);
  9411. }
  9412. if (yPadding !== 0) {
  9413. var yRange = y.domain()[1] - y.domain()[0];
  9414. scatter.yDomain([y.domain()[0] - (yPadding * yRange), y.domain()[1] + (yPadding * yRange)]);
  9415. }
  9416. //Only need to update the scatter again if x/yPadding changed the domain.
  9417. if (yPadding !== 0 || xPadding !== 0) {
  9418. wrap.select('.nv-scatterWrap')
  9419. .datum(data.filter(function(d) { return !d.disabled }))
  9420. .call(scatter);
  9421. }
  9422. //------------------------------------------------------------
  9423. //------------------------------------------------------------
  9424. // Setup Axes
  9425. if (showXAxis) {
  9426. xAxis
  9427. .scale(x)
  9428. .ticks( xAxis.ticks() && xAxis.ticks().length ? xAxis.ticks() : availableWidth / 100 )
  9429. .tickSize( -availableHeight , 0);
  9430. g.select('.nv-x.nv-axis')
  9431. .attr('transform', 'translate(0,' + y.range()[0] + ')')
  9432. .call(xAxis);
  9433. }
  9434. if (showYAxis) {
  9435. yAxis
  9436. .scale(y)
  9437. .ticks( yAxis.ticks() && yAxis.ticks().length ? yAxis.ticks() : availableHeight / 36 )
  9438. .tickSize( -availableWidth, 0);
  9439. g.select('.nv-y.nv-axis')
  9440. .call(yAxis);
  9441. }
  9442. if (showDistX) {
  9443. distX
  9444. .getData(scatter.x())
  9445. .scale(x)
  9446. .width(availableWidth)
  9447. .color(data.map(function(d,i) {
  9448. return d.color || color(d, i);
  9449. }).filter(function(d,i) { return !data[i].disabled }));
  9450. gEnter.select('.nv-distWrap').append('g')
  9451. .attr('class', 'nv-distributionX');
  9452. g.select('.nv-distributionX')
  9453. .attr('transform', 'translate(0,' + y.range()[0] + ')')
  9454. .datum(data.filter(function(d) { return !d.disabled }))
  9455. .call(distX);
  9456. }
  9457. if (showDistY) {
  9458. distY
  9459. .getData(scatter.y())
  9460. .scale(y)
  9461. .width(availableHeight)
  9462. .color(data.map(function(d,i) {
  9463. return d.color || color(d, i);
  9464. }).filter(function(d,i) { return !data[i].disabled }));
  9465. gEnter.select('.nv-distWrap').append('g')
  9466. .attr('class', 'nv-distributionY');
  9467. g.select('.nv-distributionY')
  9468. .attr('transform',
  9469. 'translate(' + (rightAlignYAxis ? availableWidth : -distY.size() ) + ',0)')
  9470. .datum(data.filter(function(d) { return !d.disabled }))
  9471. .call(distY);
  9472. }
  9473. //------------------------------------------------------------
  9474. if (d3.fisheye) {
  9475. g.select('.nv-background')
  9476. .attr('width', availableWidth)
  9477. .attr('height', availableHeight);
  9478. g.select('.nv-background').on('mousemove', updateFisheye);
  9479. g.select('.nv-background').on('click', function() { pauseFisheye = !pauseFisheye;});
  9480. scatter.dispatch.on('elementClick.freezeFisheye', function() {
  9481. pauseFisheye = !pauseFisheye;
  9482. });
  9483. }
  9484. function updateFisheye() {
  9485. if (pauseFisheye) {
  9486. g.select('.nv-point-paths').style('pointer-events', 'all');
  9487. return false;
  9488. }
  9489. g.select('.nv-point-paths').style('pointer-events', 'none' );
  9490. var mouse = d3.mouse(this);
  9491. x.distortion(fisheye).focus(mouse[0]);
  9492. y.distortion(fisheye).focus(mouse[1]);
  9493. g.select('.nv-scatterWrap')
  9494. .call(scatter);
  9495. if (showXAxis)
  9496. g.select('.nv-x.nv-axis').call(xAxis);
  9497. if (showYAxis)
  9498. g.select('.nv-y.nv-axis').call(yAxis);
  9499. g.select('.nv-distributionX')
  9500. .datum(data.filter(function(d) { return !d.disabled }))
  9501. .call(distX);
  9502. g.select('.nv-distributionY')
  9503. .datum(data.filter(function(d) { return !d.disabled }))
  9504. .call(distY);
  9505. }
  9506. //============================================================
  9507. // Event Handling/Dispatching (in chart's scope)
  9508. //------------------------------------------------------------
  9509. controls.dispatch.on('legendClick', function(d,i) {
  9510. d.disabled = !d.disabled;
  9511. fisheye = d.disabled ? 0 : 2.5;
  9512. g.select('.nv-background') .style('pointer-events', d.disabled ? 'none' : 'all');
  9513. g.select('.nv-point-paths').style('pointer-events', d.disabled ? 'all' : 'none' );
  9514. if (d.disabled) {
  9515. x.distortion(fisheye).focus(0);
  9516. y.distortion(fisheye).focus(0);
  9517. g.select('.nv-scatterWrap').call(scatter);
  9518. g.select('.nv-x.nv-axis').call(xAxis);
  9519. g.select('.nv-y.nv-axis').call(yAxis);
  9520. } else {
  9521. pauseFisheye = false;
  9522. }
  9523. chart.update();
  9524. });
  9525. legend.dispatch.on('stateChange', function(newState) {
  9526. state.disabled = newState.disabled;
  9527. dispatch.stateChange(state);
  9528. chart.update();
  9529. });
  9530. scatter.dispatch.on('elementMouseover.tooltip', function(e) {
  9531. d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
  9532. .attr('y1', function(d,i) { return e.pos[1] - availableHeight;});
  9533. d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
  9534. .attr('x2', e.pos[0] + distX.size());
  9535. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  9536. dispatch.tooltipShow(e);
  9537. });
  9538. dispatch.on('tooltipShow', function(e) {
  9539. if (tooltips) showTooltip(e, that.parentNode);
  9540. });
  9541. // Update chart from a state object passed to event handler
  9542. dispatch.on('changeState', function(e) {
  9543. if (typeof e.disabled !== 'undefined') {
  9544. data.forEach(function(series,i) {
  9545. series.disabled = e.disabled[i];
  9546. });
  9547. state.disabled = e.disabled;
  9548. }
  9549. chart.update();
  9550. });
  9551. //============================================================
  9552. //store old scales for use in transitions on update
  9553. x0 = x.copy();
  9554. y0 = y.copy();
  9555. });
  9556. return chart;
  9557. }
  9558. //============================================================
  9559. // Event Handling/Dispatching (out of chart's scope)
  9560. //------------------------------------------------------------
  9561. scatter.dispatch.on('elementMouseout.tooltip', function(e) {
  9562. dispatch.tooltipHide(e);
  9563. d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
  9564. .attr('y1', 0);
  9565. d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
  9566. .attr('x2', distY.size());
  9567. });
  9568. dispatch.on('tooltipHide', function() {
  9569. if (tooltips) nv.tooltip.cleanup();
  9570. });
  9571. //============================================================
  9572. //============================================================
  9573. // Expose Public Variables
  9574. //------------------------------------------------------------
  9575. // expose chart's sub-components
  9576. chart.dispatch = dispatch;
  9577. chart.scatter = scatter;
  9578. chart.legend = legend;
  9579. chart.controls = controls;
  9580. chart.xAxis = xAxis;
  9581. chart.yAxis = yAxis;
  9582. chart.distX = distX;
  9583. chart.distY = distY;
  9584. d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi');
  9585. chart.options = nv.utils.optionsFunc.bind(chart);
  9586. chart.margin = function(_) {
  9587. if (!arguments.length) return margin;
  9588. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  9589. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  9590. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  9591. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  9592. return chart;
  9593. };
  9594. chart.width = function(_) {
  9595. if (!arguments.length) return width;
  9596. width = _;
  9597. return chart;
  9598. };
  9599. chart.height = function(_) {
  9600. if (!arguments.length) return height;
  9601. height = _;
  9602. return chart;
  9603. };
  9604. chart.color = function(_) {
  9605. if (!arguments.length) return color;
  9606. color = nv.utils.getColor(_);
  9607. legend.color(color);
  9608. distX.color(color);
  9609. distY.color(color);
  9610. return chart;
  9611. };
  9612. chart.showDistX = function(_) {
  9613. if (!arguments.length) return showDistX;
  9614. showDistX = _;
  9615. return chart;
  9616. };
  9617. chart.showDistY = function(_) {
  9618. if (!arguments.length) return showDistY;
  9619. showDistY = _;
  9620. return chart;
  9621. };
  9622. chart.showControls = function(_) {
  9623. if (!arguments.length) return showControls;
  9624. showControls = _;
  9625. return chart;
  9626. };
  9627. chart.showLegend = function(_) {
  9628. if (!arguments.length) return showLegend;
  9629. showLegend = _;
  9630. return chart;
  9631. };
  9632. chart.showXAxis = function(_) {
  9633. if (!arguments.length) return showXAxis;
  9634. showXAxis = _;
  9635. return chart;
  9636. };
  9637. chart.showYAxis = function(_) {
  9638. if (!arguments.length) return showYAxis;
  9639. showYAxis = _;
  9640. return chart;
  9641. };
  9642. chart.rightAlignYAxis = function(_) {
  9643. if(!arguments.length) return rightAlignYAxis;
  9644. rightAlignYAxis = _;
  9645. yAxis.orient( (_) ? 'right' : 'left');
  9646. return chart;
  9647. };
  9648. chart.fisheye = function(_) {
  9649. if (!arguments.length) return fisheye;
  9650. fisheye = _;
  9651. return chart;
  9652. };
  9653. chart.xPadding = function(_) {
  9654. if (!arguments.length) return xPadding;
  9655. xPadding = _;
  9656. return chart;
  9657. };
  9658. chart.yPadding = function(_) {
  9659. if (!arguments.length) return yPadding;
  9660. yPadding = _;
  9661. return chart;
  9662. };
  9663. chart.tooltips = function(_) {
  9664. if (!arguments.length) return tooltips;
  9665. tooltips = _;
  9666. return chart;
  9667. };
  9668. chart.tooltipContent = function(_) {
  9669. if (!arguments.length) return tooltip;
  9670. tooltip = _;
  9671. return chart;
  9672. };
  9673. chart.tooltipXContent = function(_) {
  9674. if (!arguments.length) return tooltipX;
  9675. tooltipX = _;
  9676. return chart;
  9677. };
  9678. chart.tooltipYContent = function(_) {
  9679. if (!arguments.length) return tooltipY;
  9680. tooltipY = _;
  9681. return chart;
  9682. };
  9683. chart.state = function(_) {
  9684. if (!arguments.length) return state;
  9685. state = _;
  9686. return chart;
  9687. };
  9688. chart.defaultState = function(_) {
  9689. if (!arguments.length) return defaultState;
  9690. defaultState = _;
  9691. return chart;
  9692. };
  9693. chart.noData = function(_) {
  9694. if (!arguments.length) return noData;
  9695. noData = _;
  9696. return chart;
  9697. };
  9698. chart.transitionDuration = function(_) {
  9699. if (!arguments.length) return transitionDuration;
  9700. transitionDuration = _;
  9701. return chart;
  9702. };
  9703. //============================================================
  9704. return chart;
  9705. }
  9706. nv.models.scatterPlusLineChart = function() {
  9707. "use strict";
  9708. //============================================================
  9709. // Public Variables with Default Settings
  9710. //------------------------------------------------------------
  9711. var scatter = nv.models.scatter()
  9712. , xAxis = nv.models.axis()
  9713. , yAxis = nv.models.axis()
  9714. , legend = nv.models.legend()
  9715. , controls = nv.models.legend()
  9716. , distX = nv.models.distribution()
  9717. , distY = nv.models.distribution()
  9718. ;
  9719. var margin = {top: 30, right: 20, bottom: 50, left: 75}
  9720. , width = null
  9721. , height = null
  9722. , color = nv.utils.defaultColor()
  9723. , x = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.xScale()
  9724. , y = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.yScale()
  9725. , showDistX = false
  9726. , showDistY = false
  9727. , showLegend = true
  9728. , showXAxis = true
  9729. , showYAxis = true
  9730. , rightAlignYAxis = false
  9731. , showControls = !!d3.fisheye
  9732. , fisheye = 0
  9733. , pauseFisheye = false
  9734. , tooltips = true
  9735. , tooltipX = function(key, x, y) { return '<strong>' + x + '</strong>' }
  9736. , tooltipY = function(key, x, y) { return '<strong>' + y + '</strong>' }
  9737. , tooltip = function(key, x, y, date) { return '<h3>' + key + '</h3>'
  9738. + '<p>' + date + '</p>' }
  9739. , state = {}
  9740. , defaultState = null
  9741. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
  9742. , noData = "No Data Available."
  9743. , transitionDuration = 250
  9744. ;
  9745. scatter
  9746. .xScale(x)
  9747. .yScale(y)
  9748. ;
  9749. xAxis
  9750. .orient('bottom')
  9751. .tickPadding(10)
  9752. ;
  9753. yAxis
  9754. .orient((rightAlignYAxis) ? 'right' : 'left')
  9755. .tickPadding(10)
  9756. ;
  9757. distX
  9758. .axis('x')
  9759. ;
  9760. distY
  9761. .axis('y')
  9762. ;
  9763. controls.updateState(false);
  9764. //============================================================
  9765. //============================================================
  9766. // Private Variables
  9767. //------------------------------------------------------------
  9768. var x0, y0;
  9769. var showTooltip = function(e, offsetElement) {
  9770. //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?)
  9771. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  9772. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  9773. leftX = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  9774. topX = y.range()[0] + margin.top + ( offsetElement.offsetTop || 0),
  9775. leftY = x.range()[0] + margin.left + ( offsetElement.offsetLeft || 0 ),
  9776. topY = e.pos[1] + ( offsetElement.offsetTop || 0),
  9777. xVal = xAxis.tickFormat()(scatter.x()(e.point, e.pointIndex)),
  9778. yVal = yAxis.tickFormat()(scatter.y()(e.point, e.pointIndex));
  9779. if( tooltipX != null )
  9780. nv.tooltip.show([leftX, topX], tooltipX(e.series.key, xVal, yVal, e, chart), 'n', 1, offsetElement, 'x-nvtooltip');
  9781. if( tooltipY != null )
  9782. nv.tooltip.show([leftY, topY], tooltipY(e.series.key, xVal, yVal, e, chart), 'e', 1, offsetElement, 'y-nvtooltip');
  9783. if( tooltip != null )
  9784. nv.tooltip.show([left, top], tooltip(e.series.key, xVal, yVal, e.point.tooltip, e, chart), e.value < 0 ? 'n' : 's', null, offsetElement);
  9785. };
  9786. var controlsData = [
  9787. { key: 'Magnify', disabled: true }
  9788. ];
  9789. //============================================================
  9790. function chart(selection) {
  9791. selection.each(function(data) {
  9792. var container = d3.select(this),
  9793. that = this;
  9794. var availableWidth = (width || parseInt(container.style('width')) || 960)
  9795. - margin.left - margin.right,
  9796. availableHeight = (height || parseInt(container.style('height')) || 400)
  9797. - margin.top - margin.bottom;
  9798. chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
  9799. chart.container = this;
  9800. //set state.disabled
  9801. state.disabled = data.map(function(d) { return !!d.disabled });
  9802. if (!defaultState) {
  9803. var key;
  9804. defaultState = {};
  9805. for (key in state) {
  9806. if (state[key] instanceof Array)
  9807. defaultState[key] = state[key].slice(0);
  9808. else
  9809. defaultState[key] = state[key];
  9810. }
  9811. }
  9812. //------------------------------------------------------------
  9813. // Display noData message if there's nothing to show.
  9814. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  9815. var noDataText = container.selectAll('.nv-noData').data([noData]);
  9816. noDataText.enter().append('text')
  9817. .attr('class', 'nvd3 nv-noData')
  9818. .attr('dy', '-.7em')
  9819. .style('text-anchor', 'middle');
  9820. noDataText
  9821. .attr('x', margin.left + availableWidth / 2)
  9822. .attr('y', margin.top + availableHeight / 2)
  9823. .text(function(d) { return d });
  9824. return chart;
  9825. } else {
  9826. container.selectAll('.nv-noData').remove();
  9827. }
  9828. //------------------------------------------------------------
  9829. //------------------------------------------------------------
  9830. // Setup Scales
  9831. x = scatter.xScale();
  9832. y = scatter.yScale();
  9833. x0 = x0 || x;
  9834. y0 = y0 || y;
  9835. //------------------------------------------------------------
  9836. //------------------------------------------------------------
  9837. // Setup containers and skeleton of chart
  9838. var wrap = container.selectAll('g.nv-wrap.nv-scatterChart').data([data]);
  9839. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter.id());
  9840. var gEnter = wrapEnter.append('g');
  9841. var g = wrap.select('g')
  9842. // background for pointer events
  9843. gEnter.append('rect').attr('class', 'nvd3 nv-background').style("pointer-events","none");
  9844. gEnter.append('g').attr('class', 'nv-x nv-axis');
  9845. gEnter.append('g').attr('class', 'nv-y nv-axis');
  9846. gEnter.append('g').attr('class', 'nv-scatterWrap');
  9847. gEnter.append('g').attr('class', 'nv-regressionLinesWrap');
  9848. gEnter.append('g').attr('class', 'nv-distWrap');
  9849. gEnter.append('g').attr('class', 'nv-legendWrap');
  9850. gEnter.append('g').attr('class', 'nv-controlsWrap');
  9851. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  9852. if (rightAlignYAxis) {
  9853. g.select(".nv-y.nv-axis")
  9854. .attr("transform", "translate(" + availableWidth + ",0)");
  9855. }
  9856. //------------------------------------------------------------
  9857. //------------------------------------------------------------
  9858. // Legend
  9859. if (showLegend) {
  9860. legend.width( availableWidth / 2 );
  9861. wrap.select('.nv-legendWrap')
  9862. .datum(data)
  9863. .call(legend);
  9864. if ( margin.top != legend.height()) {
  9865. margin.top = legend.height();
  9866. availableHeight = (height || parseInt(container.style('height')) || 400)
  9867. - margin.top - margin.bottom;
  9868. }
  9869. wrap.select('.nv-legendWrap')
  9870. .attr('transform', 'translate(' + (availableWidth / 2) + ',' + (-margin.top) +')');
  9871. }
  9872. //------------------------------------------------------------
  9873. //------------------------------------------------------------
  9874. // Controls
  9875. if (showControls) {
  9876. controls.width(180).color(['#444']);
  9877. g.select('.nv-controlsWrap')
  9878. .datum(controlsData)
  9879. .attr('transform', 'translate(0,' + (-margin.top) +')')
  9880. .call(controls);
  9881. }
  9882. //------------------------------------------------------------
  9883. //------------------------------------------------------------
  9884. // Main Chart Component(s)
  9885. scatter
  9886. .width(availableWidth)
  9887. .height(availableHeight)
  9888. .color(data.map(function(d,i) {
  9889. return d.color || color(d, i);
  9890. }).filter(function(d,i) { return !data[i].disabled }))
  9891. wrap.select('.nv-scatterWrap')
  9892. .datum(data.filter(function(d) { return !d.disabled }))
  9893. .call(scatter);
  9894. wrap.select('.nv-regressionLinesWrap')
  9895. .attr('clip-path', 'url(#nv-edge-clip-' + scatter.id() + ')');
  9896. var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines')
  9897. .data(function(d) {return d });
  9898. regWrap.enter().append('g').attr('class', 'nv-regLines');
  9899. var regLine = regWrap.selectAll('.nv-regLine').data(function(d){return [d]});
  9900. var regLineEnter = regLine.enter()
  9901. .append('line').attr('class', 'nv-regLine')
  9902. .style('stroke-opacity', 0);
  9903. regLine
  9904. .transition()
  9905. .attr('x1', x.range()[0])
  9906. .attr('x2', x.range()[1])
  9907. .attr('y1', function(d,i) {return y(x.domain()[0] * d.slope + d.intercept) })
  9908. .attr('y2', function(d,i) { return y(x.domain()[1] * d.slope + d.intercept) })
  9909. .style('stroke', function(d,i,j) { return color(d,j) })
  9910. .style('stroke-opacity', function(d,i) {
  9911. return (d.disabled || typeof d.slope === 'undefined' || typeof d.intercept === 'undefined') ? 0 : 1
  9912. });
  9913. //------------------------------------------------------------
  9914. //------------------------------------------------------------
  9915. // Setup Axes
  9916. if (showXAxis) {
  9917. xAxis
  9918. .scale(x)
  9919. .ticks( xAxis.ticks() ? xAxis.ticks() : availableWidth / 100 )
  9920. .tickSize( -availableHeight , 0);
  9921. g.select('.nv-x.nv-axis')
  9922. .attr('transform', 'translate(0,' + y.range()[0] + ')')
  9923. .call(xAxis);
  9924. }
  9925. if (showYAxis) {
  9926. yAxis
  9927. .scale(y)
  9928. .ticks( yAxis.ticks() ? yAxis.ticks() : availableHeight / 36 )
  9929. .tickSize( -availableWidth, 0);
  9930. g.select('.nv-y.nv-axis')
  9931. .call(yAxis);
  9932. }
  9933. if (showDistX) {
  9934. distX
  9935. .getData(scatter.x())
  9936. .scale(x)
  9937. .width(availableWidth)
  9938. .color(data.map(function(d,i) {
  9939. return d.color || color(d, i);
  9940. }).filter(function(d,i) { return !data[i].disabled }));
  9941. gEnter.select('.nv-distWrap').append('g')
  9942. .attr('class', 'nv-distributionX');
  9943. g.select('.nv-distributionX')
  9944. .attr('transform', 'translate(0,' + y.range()[0] + ')')
  9945. .datum(data.filter(function(d) { return !d.disabled }))
  9946. .call(distX);
  9947. }
  9948. if (showDistY) {
  9949. distY
  9950. .getData(scatter.y())
  9951. .scale(y)
  9952. .width(availableHeight)
  9953. .color(data.map(function(d,i) {
  9954. return d.color || color(d, i);
  9955. }).filter(function(d,i) { return !data[i].disabled }));
  9956. gEnter.select('.nv-distWrap').append('g')
  9957. .attr('class', 'nv-distributionY');
  9958. g.select('.nv-distributionY')
  9959. .attr('transform', 'translate(' + (rightAlignYAxis ? availableWidth : -distY.size() ) + ',0)')
  9960. .datum(data.filter(function(d) { return !d.disabled }))
  9961. .call(distY);
  9962. }
  9963. //------------------------------------------------------------
  9964. if (d3.fisheye) {
  9965. g.select('.nv-background')
  9966. .attr('width', availableWidth)
  9967. .attr('height', availableHeight)
  9968. ;
  9969. g.select('.nv-background').on('mousemove', updateFisheye);
  9970. g.select('.nv-background').on('click', function() { pauseFisheye = !pauseFisheye;});
  9971. scatter.dispatch.on('elementClick.freezeFisheye', function() {
  9972. pauseFisheye = !pauseFisheye;
  9973. });
  9974. }
  9975. function updateFisheye() {
  9976. if (pauseFisheye) {
  9977. g.select('.nv-point-paths').style('pointer-events', 'all');
  9978. return false;
  9979. }
  9980. g.select('.nv-point-paths').style('pointer-events', 'none' );
  9981. var mouse = d3.mouse(this);
  9982. x.distortion(fisheye).focus(mouse[0]);
  9983. y.distortion(fisheye).focus(mouse[1]);
  9984. g.select('.nv-scatterWrap')
  9985. .datum(data.filter(function(d) { return !d.disabled }))
  9986. .call(scatter);
  9987. if (showXAxis)
  9988. g.select('.nv-x.nv-axis').call(xAxis);
  9989. if (showYAxis)
  9990. g.select('.nv-y.nv-axis').call(yAxis);
  9991. g.select('.nv-distributionX')
  9992. .datum(data.filter(function(d) { return !d.disabled }))
  9993. .call(distX);
  9994. g.select('.nv-distributionY')
  9995. .datum(data.filter(function(d) { return !d.disabled }))
  9996. .call(distY);
  9997. }
  9998. //============================================================
  9999. // Event Handling/Dispatching (in chart's scope)
  10000. //------------------------------------------------------------
  10001. controls.dispatch.on('legendClick', function(d,i) {
  10002. d.disabled = !d.disabled;
  10003. fisheye = d.disabled ? 0 : 2.5;
  10004. g.select('.nv-background') .style('pointer-events', d.disabled ? 'none' : 'all');
  10005. g.select('.nv-point-paths').style('pointer-events', d.disabled ? 'all' : 'none' );
  10006. if (d.disabled) {
  10007. x.distortion(fisheye).focus(0);
  10008. y.distortion(fisheye).focus(0);
  10009. g.select('.nv-scatterWrap').call(scatter);
  10010. g.select('.nv-x.nv-axis').call(xAxis);
  10011. g.select('.nv-y.nv-axis').call(yAxis);
  10012. } else {
  10013. pauseFisheye = false;
  10014. }
  10015. chart.update();
  10016. });
  10017. legend.dispatch.on('stateChange', function(newState) {
  10018. state = newState;
  10019. dispatch.stateChange(state);
  10020. chart.update();
  10021. });
  10022. scatter.dispatch.on('elementMouseover.tooltip', function(e) {
  10023. d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
  10024. .attr('y1', e.pos[1] - availableHeight);
  10025. d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
  10026. .attr('x2', e.pos[0] + distX.size());
  10027. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
  10028. dispatch.tooltipShow(e);
  10029. });
  10030. dispatch.on('tooltipShow', function(e) {
  10031. if (tooltips) showTooltip(e, that.parentNode);
  10032. });
  10033. // Update chart from a state object passed to event handler
  10034. dispatch.on('changeState', function(e) {
  10035. if (typeof e.disabled !== 'undefined') {
  10036. data.forEach(function(series,i) {
  10037. series.disabled = e.disabled[i];
  10038. });
  10039. state.disabled = e.disabled;
  10040. }
  10041. chart.update();
  10042. });
  10043. //============================================================
  10044. //store old scales for use in transitions on update
  10045. x0 = x.copy();
  10046. y0 = y.copy();
  10047. });
  10048. return chart;
  10049. }
  10050. //============================================================
  10051. // Event Handling/Dispatching (out of chart's scope)
  10052. //------------------------------------------------------------
  10053. scatter.dispatch.on('elementMouseout.tooltip', function(e) {
  10054. dispatch.tooltipHide(e);
  10055. d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
  10056. .attr('y1', 0);
  10057. d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
  10058. .attr('x2', distY.size());
  10059. });
  10060. dispatch.on('tooltipHide', function() {
  10061. if (tooltips) nv.tooltip.cleanup();
  10062. });
  10063. //============================================================
  10064. //============================================================
  10065. // Expose Public Variables
  10066. //------------------------------------------------------------
  10067. // expose chart's sub-components
  10068. chart.dispatch = dispatch;
  10069. chart.scatter = scatter;
  10070. chart.legend = legend;
  10071. chart.controls = controls;
  10072. chart.xAxis = xAxis;
  10073. chart.yAxis = yAxis;
  10074. chart.distX = distX;
  10075. chart.distY = distY;
  10076. d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi');
  10077. chart.options = nv.utils.optionsFunc.bind(chart);
  10078. chart.margin = function(_) {
  10079. if (!arguments.length) return margin;
  10080. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  10081. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  10082. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  10083. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  10084. return chart;
  10085. };
  10086. chart.width = function(_) {
  10087. if (!arguments.length) return width;
  10088. width = _;
  10089. return chart;
  10090. };
  10091. chart.height = function(_) {
  10092. if (!arguments.length) return height;
  10093. height = _;
  10094. return chart;
  10095. };
  10096. chart.color = function(_) {
  10097. if (!arguments.length) return color;
  10098. color = nv.utils.getColor(_);
  10099. legend.color(color);
  10100. distX.color(color);
  10101. distY.color(color);
  10102. return chart;
  10103. };
  10104. chart.showDistX = function(_) {
  10105. if (!arguments.length) return showDistX;
  10106. showDistX = _;
  10107. return chart;
  10108. };
  10109. chart.showDistY = function(_) {
  10110. if (!arguments.length) return showDistY;
  10111. showDistY = _;
  10112. return chart;
  10113. };
  10114. chart.showControls = function(_) {
  10115. if (!arguments.length) return showControls;
  10116. showControls = _;
  10117. return chart;
  10118. };
  10119. chart.showLegend = function(_) {
  10120. if (!arguments.length) return showLegend;
  10121. showLegend = _;
  10122. return chart;
  10123. };
  10124. chart.showXAxis = function(_) {
  10125. if (!arguments.length) return showXAxis;
  10126. showXAxis = _;
  10127. return chart;
  10128. };
  10129. chart.showYAxis = function(_) {
  10130. if (!arguments.length) return showYAxis;
  10131. showYAxis = _;
  10132. return chart;
  10133. };
  10134. chart.rightAlignYAxis = function(_) {
  10135. if(!arguments.length) return rightAlignYAxis;
  10136. rightAlignYAxis = _;
  10137. yAxis.orient( (_) ? 'right' : 'left');
  10138. return chart;
  10139. };
  10140. chart.fisheye = function(_) {
  10141. if (!arguments.length) return fisheye;
  10142. fisheye = _;
  10143. return chart;
  10144. };
  10145. chart.tooltips = function(_) {
  10146. if (!arguments.length) return tooltips;
  10147. tooltips = _;
  10148. return chart;
  10149. };
  10150. chart.tooltipContent = function(_) {
  10151. if (!arguments.length) return tooltip;
  10152. tooltip = _;
  10153. return chart;
  10154. };
  10155. chart.tooltipXContent = function(_) {
  10156. if (!arguments.length) return tooltipX;
  10157. tooltipX = _;
  10158. return chart;
  10159. };
  10160. chart.tooltipYContent = function(_) {
  10161. if (!arguments.length) return tooltipY;
  10162. tooltipY = _;
  10163. return chart;
  10164. };
  10165. chart.state = function(_) {
  10166. if (!arguments.length) return state;
  10167. state = _;
  10168. return chart;
  10169. };
  10170. chart.defaultState = function(_) {
  10171. if (!arguments.length) return defaultState;
  10172. defaultState = _;
  10173. return chart;
  10174. };
  10175. chart.noData = function(_) {
  10176. if (!arguments.length) return noData;
  10177. noData = _;
  10178. return chart;
  10179. };
  10180. chart.transitionDuration = function(_) {
  10181. if (!arguments.length) return transitionDuration;
  10182. transitionDuration = _;
  10183. return chart;
  10184. };
  10185. //============================================================
  10186. return chart;
  10187. }
  10188. nv.models.sparkline = function() {
  10189. "use strict";
  10190. //============================================================
  10191. // Public Variables with Default Settings
  10192. //------------------------------------------------------------
  10193. var margin = {top: 2, right: 0, bottom: 2, left: 0}
  10194. , width = 400
  10195. , height = 32
  10196. , animate = true
  10197. , x = d3.scale.linear()
  10198. , y = d3.scale.linear()
  10199. , getX = function(d) { return d.x }
  10200. , getY = function(d) { return d.y }
  10201. , color = nv.utils.getColor(['#000'])
  10202. , xDomain
  10203. , yDomain
  10204. , xRange
  10205. , yRange
  10206. ;
  10207. //============================================================
  10208. function chart(selection) {
  10209. selection.each(function(data) {
  10210. var availableWidth = width - margin.left - margin.right,
  10211. availableHeight = height - margin.top - margin.bottom,
  10212. container = d3.select(this);
  10213. //------------------------------------------------------------
  10214. // Setup Scales
  10215. x .domain(xDomain || d3.extent(data, getX ))
  10216. .range(xRange || [0, availableWidth]);
  10217. y .domain(yDomain || d3.extent(data, getY ))
  10218. .range(yRange || [availableHeight, 0]);
  10219. //------------------------------------------------------------
  10220. //------------------------------------------------------------
  10221. // Setup containers and skeleton of chart
  10222. var wrap = container.selectAll('g.nv-wrap.nv-sparkline').data([data]);
  10223. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparkline');
  10224. var gEnter = wrapEnter.append('g');
  10225. var g = wrap.select('g');
  10226. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
  10227. //------------------------------------------------------------
  10228. var paths = wrap.selectAll('path')
  10229. .data(function(d) { return [d] });
  10230. paths.enter().append('path');
  10231. paths.exit().remove();
  10232. paths
  10233. .style('stroke', function(d,i) { return d.color || color(d, i) })
  10234. .attr('d', d3.svg.line()
  10235. .x(function(d,i) { return x(getX(d,i)) })
  10236. .y(function(d,i) { return y(getY(d,i)) })
  10237. );
  10238. // TODO: Add CURRENT data point (Need Min, Mac, Current / Most recent)
  10239. var points = wrap.selectAll('circle.nv-point')
  10240. .data(function(data) {
  10241. var yValues = data.map(function(d, i) { return getY(d,i); });
  10242. function pointIndex(index) {
  10243. if (index != -1) {
  10244. var result = data[index];
  10245. result.pointIndex = index;
  10246. return result;
  10247. } else {
  10248. return null;
  10249. }
  10250. }
  10251. var maxPoint = pointIndex(yValues.lastIndexOf(y.domain()[1])),
  10252. minPoint = pointIndex(yValues.indexOf(y.domain()[0])),
  10253. currentPoint = pointIndex(yValues.length - 1);
  10254. return [minPoint, maxPoint, currentPoint].filter(function (d) {return d != null;});
  10255. });
  10256. points.enter().append('circle');
  10257. points.exit().remove();
  10258. points
  10259. .attr('cx', function(d,i) { return x(getX(d,d.pointIndex)) })
  10260. .attr('cy', function(d,i) { return y(getY(d,d.pointIndex)) })
  10261. .attr('r', 2)
  10262. .attr('class', function(d,i) {
  10263. return getX(d, d.pointIndex) == x.domain()[1] ? 'nv-point nv-currentValue' :
  10264. getY(d, d.pointIndex) == y.domain()[0] ? 'nv-point nv-minValue' : 'nv-point nv-maxValue'
  10265. });
  10266. });
  10267. return chart;
  10268. }
  10269. //============================================================
  10270. // Expose Public Variables
  10271. //------------------------------------------------------------
  10272. chart.options = nv.utils.optionsFunc.bind(chart);
  10273. chart.margin = function(_) {
  10274. if (!arguments.length) return margin;
  10275. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  10276. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  10277. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  10278. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  10279. return chart;
  10280. };
  10281. chart.width = function(_) {
  10282. if (!arguments.length) return width;
  10283. width = _;
  10284. return chart;
  10285. };
  10286. chart.height = function(_) {
  10287. if (!arguments.length) return height;
  10288. height = _;
  10289. return chart;
  10290. };
  10291. chart.x = function(_) {
  10292. if (!arguments.length) return getX;
  10293. getX = d3.functor(_);
  10294. return chart;
  10295. };
  10296. chart.y = function(_) {
  10297. if (!arguments.length) return getY;
  10298. getY = d3.functor(_);
  10299. return chart;
  10300. };
  10301. chart.xScale = function(_) {
  10302. if (!arguments.length) return x;
  10303. x = _;
  10304. return chart;
  10305. };
  10306. chart.yScale = function(_) {
  10307. if (!arguments.length) return y;
  10308. y = _;
  10309. return chart;
  10310. };
  10311. chart.xDomain = function(_) {
  10312. if (!arguments.length) return xDomain;
  10313. xDomain = _;
  10314. return chart;
  10315. };
  10316. chart.yDomain = function(_) {
  10317. if (!arguments.length) return yDomain;
  10318. yDomain = _;
  10319. return chart;
  10320. };
  10321. chart.xRange = function(_) {
  10322. if (!arguments.length) return xRange;
  10323. xRange = _;
  10324. return chart;
  10325. };
  10326. chart.yRange = function(_) {
  10327. if (!arguments.length) return yRange;
  10328. yRange = _;
  10329. return chart;
  10330. };
  10331. chart.animate = function(_) {
  10332. if (!arguments.length) return animate;
  10333. animate = _;
  10334. return chart;
  10335. };
  10336. chart.color = function(_) {
  10337. if (!arguments.length) return color;
  10338. color = nv.utils.getColor(_);
  10339. return chart;
  10340. };
  10341. //============================================================
  10342. return chart;
  10343. }
  10344. nv.models.sparklinePlus = function() {
  10345. "use strict";
  10346. //============================================================
  10347. // Public Variables with Default Settings
  10348. //------------------------------------------------------------
  10349. var sparkline = nv.models.sparkline();
  10350. var margin = {top: 15, right: 100, bottom: 10, left: 50}
  10351. , width = null
  10352. , height = null
  10353. , x
  10354. , y
  10355. , index = []
  10356. , paused = false
  10357. , xTickFormat = d3.format(',r')
  10358. , yTickFormat = d3.format(',.2f')
  10359. , showValue = true
  10360. , alignValue = true
  10361. , rightAlignValue = false
  10362. , noData = "No Data Available."
  10363. ;
  10364. //============================================================
  10365. function chart(selection) {
  10366. selection.each(function(data) {
  10367. var container = d3.select(this);
  10368. var availableWidth = (width || parseInt(container.style('width')) || 960)
  10369. - margin.left - margin.right,
  10370. availableHeight = (height || parseInt(container.style('height')) || 400)
  10371. - margin.top - margin.bottom;
  10372. chart.update = function() { chart(selection) };
  10373. chart.container = this;
  10374. //------------------------------------------------------------
  10375. // Display No Data message if there's nothing to show.
  10376. if (!data || !data.length) {
  10377. var noDataText = container.selectAll('.nv-noData').data([noData]);
  10378. noDataText.enter().append('text')
  10379. .attr('class', 'nvd3 nv-noData')
  10380. .attr('dy', '-.7em')
  10381. .style('text-anchor', 'middle');
  10382. noDataText
  10383. .attr('x', margin.left + availableWidth / 2)
  10384. .attr('y', margin.top + availableHeight / 2)
  10385. .text(function(d) { return d });
  10386. return chart;
  10387. } else {
  10388. container.selectAll('.nv-noData').remove();
  10389. }
  10390. var currentValue = sparkline.y()(data[data.length-1], data.length-1);
  10391. //------------------------------------------------------------
  10392. //------------------------------------------------------------
  10393. // Setup Scales
  10394. x = sparkline.xScale();
  10395. y = sparkline.yScale();
  10396. //------------------------------------------------------------
  10397. //------------------------------------------------------------
  10398. // Setup containers and skeleton of chart
  10399. var wrap = container.selectAll('g.nv-wrap.nv-sparklineplus').data([data]);
  10400. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparklineplus');
  10401. var gEnter = wrapEnter.append('g');
  10402. var g = wrap.select('g');
  10403. gEnter.append('g').attr('class', 'nv-sparklineWrap');
  10404. gEnter.append('g').attr('class', 'nv-valueWrap');
  10405. gEnter.append('g').attr('class', 'nv-hoverArea');
  10406. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  10407. //------------------------------------------------------------
  10408. //------------------------------------------------------------
  10409. // Main Chart Component(s)
  10410. var sparklineWrap = g.select('.nv-sparklineWrap');
  10411. sparkline
  10412. .width(availableWidth)
  10413. .height(availableHeight);
  10414. sparklineWrap
  10415. .call(sparkline);
  10416. //------------------------------------------------------------
  10417. var valueWrap = g.select('.nv-valueWrap');
  10418. var value = valueWrap.selectAll('.nv-currentValue')
  10419. .data([currentValue]);
  10420. value.enter().append('text').attr('class', 'nv-currentValue')
  10421. .attr('dx', rightAlignValue ? -8 : 8)
  10422. .attr('dy', '.9em')
  10423. .style('text-anchor', rightAlignValue ? 'end' : 'start');
  10424. value
  10425. .attr('x', availableWidth + (rightAlignValue ? margin.right : 0))
  10426. .attr('y', alignValue ? function(d) { return y(d) } : 0)
  10427. .style('fill', sparkline.color()(data[data.length-1], data.length-1))
  10428. .text(yTickFormat(currentValue));
  10429. gEnter.select('.nv-hoverArea').append('rect')
  10430. .on('mousemove', sparklineHover)
  10431. .on('click', function() { paused = !paused })
  10432. .on('mouseout', function() { index = []; updateValueLine(); });
  10433. //.on('mouseout', function() { index = null; updateValueLine(); });
  10434. g.select('.nv-hoverArea rect')
  10435. .attr('transform', function(d) { return 'translate(' + -margin.left + ',' + -margin.top + ')' })
  10436. .attr('width', availableWidth + margin.left + margin.right)
  10437. .attr('height', availableHeight + margin.top);
  10438. function updateValueLine() { //index is currently global (within the chart), may or may not keep it that way
  10439. if (paused) return;
  10440. var hoverValue = g.selectAll('.nv-hoverValue').data(index)
  10441. var hoverEnter = hoverValue.enter()
  10442. .append('g').attr('class', 'nv-hoverValue')
  10443. .style('stroke-opacity', 0)
  10444. .style('fill-opacity', 0);
  10445. hoverValue.exit()
  10446. .transition().duration(250)
  10447. .style('stroke-opacity', 0)
  10448. .style('fill-opacity', 0)
  10449. .remove();
  10450. hoverValue
  10451. .attr('transform', function(d) { return 'translate(' + x(sparkline.x()(data[d],d)) + ',0)' })
  10452. .transition().duration(250)
  10453. .style('stroke-opacity', 1)
  10454. .style('fill-opacity', 1);
  10455. if (!index.length) return;
  10456. hoverEnter.append('line')
  10457. .attr('x1', 0)
  10458. .attr('y1', -margin.top)
  10459. .attr('x2', 0)
  10460. .attr('y2', availableHeight);
  10461. hoverEnter.append('text').attr('class', 'nv-xValue')
  10462. .attr('x', -6)
  10463. .attr('y', -margin.top)
  10464. .attr('text-anchor', 'end')
  10465. .attr('dy', '.9em')
  10466. g.select('.nv-hoverValue .nv-xValue')
  10467. .text(xTickFormat(sparkline.x()(data[index[0]], index[0])));
  10468. hoverEnter.append('text').attr('class', 'nv-yValue')
  10469. .attr('x', 6)
  10470. .attr('y', -margin.top)
  10471. .attr('text-anchor', 'start')
  10472. .attr('dy', '.9em')
  10473. g.select('.nv-hoverValue .nv-yValue')
  10474. .text(yTickFormat(sparkline.y()(data[index[0]], index[0])));
  10475. }
  10476. function sparklineHover() {
  10477. if (paused) return;
  10478. var pos = d3.mouse(this)[0] - margin.left;
  10479. function getClosestIndex(data, x) {
  10480. var distance = Math.abs(sparkline.x()(data[0], 0) - x);
  10481. var closestIndex = 0;
  10482. for (var i = 0; i < data.length; i++){
  10483. if (Math.abs(sparkline.x()(data[i], i) - x) < distance) {
  10484. distance = Math.abs(sparkline.x()(data[i], i) - x);
  10485. closestIndex = i;
  10486. }
  10487. }
  10488. return closestIndex;
  10489. }
  10490. index = [getClosestIndex(data, Math.round(x.invert(pos)))];
  10491. updateValueLine();
  10492. }
  10493. });
  10494. return chart;
  10495. }
  10496. //============================================================
  10497. // Expose Public Variables
  10498. //------------------------------------------------------------
  10499. // expose chart's sub-components
  10500. chart.sparkline = sparkline;
  10501. d3.rebind(chart, sparkline, 'x', 'y', 'xScale', 'yScale', 'color');
  10502. chart.options = nv.utils.optionsFunc.bind(chart);
  10503. chart.margin = function(_) {
  10504. if (!arguments.length) return margin;
  10505. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  10506. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  10507. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  10508. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  10509. return chart;
  10510. };
  10511. chart.width = function(_) {
  10512. if (!arguments.length) return width;
  10513. width = _;
  10514. return chart;
  10515. };
  10516. chart.height = function(_) {
  10517. if (!arguments.length) return height;
  10518. height = _;
  10519. return chart;
  10520. };
  10521. chart.xTickFormat = function(_) {
  10522. if (!arguments.length) return xTickFormat;
  10523. xTickFormat = _;
  10524. return chart;
  10525. };
  10526. chart.yTickFormat = function(_) {
  10527. if (!arguments.length) return yTickFormat;
  10528. yTickFormat = _;
  10529. return chart;
  10530. };
  10531. chart.showValue = function(_) {
  10532. if (!arguments.length) return showValue;
  10533. showValue = _;
  10534. return chart;
  10535. };
  10536. chart.alignValue = function(_) {
  10537. if (!arguments.length) return alignValue;
  10538. alignValue = _;
  10539. return chart;
  10540. };
  10541. chart.rightAlignValue = function(_) {
  10542. if (!arguments.length) return rightAlignValue;
  10543. rightAlignValue = _;
  10544. return chart;
  10545. };
  10546. chart.noData = function(_) {
  10547. if (!arguments.length) return noData;
  10548. noData = _;
  10549. return chart;
  10550. };
  10551. //============================================================
  10552. return chart;
  10553. }
  10554. nv.models.stackedArea = function() {
  10555. "use strict";
  10556. //============================================================
  10557. // Public Variables with Default Settings
  10558. //------------------------------------------------------------
  10559. var margin = {top: 0, right: 0, bottom: 0, left: 0}
  10560. , width = 960
  10561. , height = 500
  10562. , color = nv.utils.defaultColor() // a function that computes the color
  10563. , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't selet one
  10564. , getX = function(d) { return d.x } // accessor to get the x value from a data point
  10565. , getY = function(d) { return d.y } // accessor to get the y value from a data point
  10566. , style = 'stack'
  10567. , offset = 'zero'
  10568. , order = 'default'
  10569. , interpolate = 'linear' // controls the line interpolation
  10570. , clipEdge = false // if true, masks lines within x and y scale
  10571. , x //can be accessed via chart.xScale()
  10572. , y //can be accessed via chart.yScale()
  10573. , scatter = nv.models.scatter()
  10574. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'areaClick', 'areaMouseover', 'areaMouseout')
  10575. ;
  10576. scatter
  10577. .size(2.2) // default size
  10578. .sizeDomain([2.2,2.2]) // all the same size by default
  10579. ;
  10580. /************************************
  10581. * offset:
  10582. * 'wiggle' (stream)
  10583. * 'zero' (stacked)
  10584. * 'expand' (normalize to 100%)
  10585. * 'silhouette' (simple centered)
  10586. *
  10587. * order:
  10588. * 'inside-out' (stream)
  10589. * 'default' (input order)
  10590. ************************************/
  10591. //============================================================
  10592. function chart(selection) {
  10593. selection.each(function(data) {
  10594. var availableWidth = width - margin.left - margin.right,
  10595. availableHeight = height - margin.top - margin.bottom,
  10596. container = d3.select(this);
  10597. //------------------------------------------------------------
  10598. // Setup Scales
  10599. x = scatter.xScale();
  10600. y = scatter.yScale();
  10601. //------------------------------------------------------------
  10602. var dataRaw = data;
  10603. // Injecting point index into each point because d3.layout.stack().out does not give index
  10604. data.forEach(function(aseries, i) {
  10605. aseries.seriesIndex = i;
  10606. aseries.values = aseries.values.map(function(d, j) {
  10607. d.index = j;
  10608. d.seriesIndex = i;
  10609. return d;
  10610. });
  10611. });
  10612. var dataFiltered = data.filter(function(series) {
  10613. return !series.disabled;
  10614. });
  10615. data = d3.layout.stack()
  10616. .order(order)
  10617. .offset(offset)
  10618. .values(function(d) { return d.values }) //TODO: make values customizeable in EVERY model in this fashion
  10619. .x(getX)
  10620. .y(getY)
  10621. .out(function(d, y0, y) {
  10622. var yHeight = (getY(d) === 0) ? 0 : y;
  10623. d.display = {
  10624. y: yHeight,
  10625. y0: y0
  10626. };
  10627. })
  10628. (dataFiltered);
  10629. //------------------------------------------------------------
  10630. // Setup containers and skeleton of chart
  10631. var wrap = container.selectAll('g.nv-wrap.nv-stackedarea').data([data]);
  10632. var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedarea');
  10633. var defsEnter = wrapEnter.append('defs');
  10634. var gEnter = wrapEnter.append('g');
  10635. var g = wrap.select('g');
  10636. gEnter.append('g').attr('class', 'nv-areaWrap');
  10637. gEnter.append('g').attr('class', 'nv-scatterWrap');
  10638. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  10639. //------------------------------------------------------------
  10640. scatter
  10641. .width(availableWidth)
  10642. .height(availableHeight)
  10643. .x(getX)
  10644. .y(function(d) { return d.display.y + d.display.y0 })
  10645. .forceY([0])
  10646. .color(data.map(function(d,i) {
  10647. return d.color || color(d, d.seriesIndex);
  10648. }));
  10649. var scatterWrap = g.select('.nv-scatterWrap')
  10650. .datum(data);
  10651. scatterWrap.call(scatter);
  10652. defsEnter.append('clipPath')
  10653. .attr('id', 'nv-edge-clip-' + id)
  10654. .append('rect');
  10655. wrap.select('#nv-edge-clip-' + id + ' rect')
  10656. .attr('width', availableWidth)
  10657. .attr('height', availableHeight);
  10658. g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
  10659. var area = d3.svg.area()
  10660. .x(function(d,i) { return x(getX(d,i)) })
  10661. .y0(function(d) {
  10662. return y(d.display.y0)
  10663. })
  10664. .y1(function(d) {
  10665. return y(d.display.y + d.display.y0)
  10666. })
  10667. .interpolate(interpolate);
  10668. var zeroArea = d3.svg.area()
  10669. .x(function(d,i) { return x(getX(d,i)) })
  10670. .y0(function(d) { return y(d.display.y0) })
  10671. .y1(function(d) { return y(d.display.y0) });
  10672. var path = g.select('.nv-areaWrap').selectAll('path.nv-area')
  10673. .data(function(d) { return d });
  10674. path.enter().append('path').attr('class', function(d,i) { return 'nv-area nv-area-' + i })
  10675. .attr('d', function(d,i){
  10676. return zeroArea(d.values, d.seriesIndex);
  10677. })
  10678. .on('mouseover', function(d,i) {
  10679. d3.select(this).classed('hover', true);
  10680. dispatch.areaMouseover({
  10681. point: d,
  10682. series: d.key,
  10683. pos: [d3.event.pageX, d3.event.pageY],
  10684. seriesIndex: d.seriesIndex
  10685. });
  10686. })
  10687. .on('mouseout', function(d,i) {
  10688. d3.select(this).classed('hover', false);
  10689. dispatch.areaMouseout({
  10690. point: d,
  10691. series: d.key,
  10692. pos: [d3.event.pageX, d3.event.pageY],
  10693. seriesIndex: d.seriesIndex
  10694. });
  10695. })
  10696. .on('click', function(d,i) {
  10697. d3.select(this).classed('hover', false);
  10698. dispatch.areaClick({
  10699. point: d,
  10700. series: d.key,
  10701. pos: [d3.event.pageX, d3.event.pageY],
  10702. seriesIndex: d.seriesIndex
  10703. });
  10704. })
  10705. path.exit().remove();
  10706. path
  10707. .style('fill', function(d,i){
  10708. return d.color || color(d, d.seriesIndex)
  10709. })
  10710. .style('stroke', function(d,i){ return d.color || color(d, d.seriesIndex) });
  10711. path.transition()
  10712. .attr('d', function(d,i) {
  10713. return area(d.values,i)
  10714. });
  10715. //============================================================
  10716. // Event Handling/Dispatching (in chart's scope)
  10717. //------------------------------------------------------------
  10718. scatter.dispatch.on('elementMouseover.area', function(e) {
  10719. g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', true);
  10720. });
  10721. scatter.dispatch.on('elementMouseout.area', function(e) {
  10722. g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', false);
  10723. });
  10724. //============================================================
  10725. //Special offset functions
  10726. chart.d3_stackedOffset_stackPercent = function(stackData) {
  10727. var n = stackData.length, //How many series
  10728. m = stackData[0].length, //how many points per series
  10729. k = 1 / n,
  10730. i,
  10731. j,
  10732. o,
  10733. y0 = [];
  10734. for (j = 0; j < m; ++j) { //Looping through all points
  10735. for (i = 0, o = 0; i < dataRaw.length; i++) //looping through series'
  10736. o += getY(dataRaw[i].values[j]) //total value of all points at a certian point in time.
  10737. if (o) for (i = 0; i < n; i++)
  10738. stackData[i][j][1] /= o;
  10739. else
  10740. for (i = 0; i < n; i++)
  10741. stackData[i][j][1] = k;
  10742. }
  10743. for (j = 0; j < m; ++j) y0[j] = 0;
  10744. return y0;
  10745. };
  10746. });
  10747. return chart;
  10748. }
  10749. //============================================================
  10750. // Event Handling/Dispatching (out of chart's scope)
  10751. //------------------------------------------------------------
  10752. scatter.dispatch.on('elementClick.area', function(e) {
  10753. dispatch.areaClick(e);
  10754. })
  10755. scatter.dispatch.on('elementMouseover.tooltip', function(e) {
  10756. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
  10757. dispatch.tooltipShow(e);
  10758. });
  10759. scatter.dispatch.on('elementMouseout.tooltip', function(e) {
  10760. dispatch.tooltipHide(e);
  10761. });
  10762. //============================================================
  10763. //============================================================
  10764. // Global getters and setters
  10765. //------------------------------------------------------------
  10766. chart.dispatch = dispatch;
  10767. chart.scatter = scatter;
  10768. d3.rebind(chart, scatter, 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange',
  10769. 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi','clipRadius','highlightPoint','clearHighlights');
  10770. chart.options = nv.utils.optionsFunc.bind(chart);
  10771. chart.x = function(_) {
  10772. if (!arguments.length) return getX;
  10773. getX = d3.functor(_);
  10774. return chart;
  10775. };
  10776. chart.y = function(_) {
  10777. if (!arguments.length) return getY;
  10778. getY = d3.functor(_);
  10779. return chart;
  10780. }
  10781. chart.margin = function(_) {
  10782. if (!arguments.length) return margin;
  10783. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  10784. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  10785. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  10786. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  10787. return chart;
  10788. };
  10789. chart.width = function(_) {
  10790. if (!arguments.length) return width;
  10791. width = _;
  10792. return chart;
  10793. };
  10794. chart.height = function(_) {
  10795. if (!arguments.length) return height;
  10796. height = _;
  10797. return chart;
  10798. };
  10799. chart.clipEdge = function(_) {
  10800. if (!arguments.length) return clipEdge;
  10801. clipEdge = _;
  10802. return chart;
  10803. };
  10804. chart.color = function(_) {
  10805. if (!arguments.length) return color;
  10806. color = nv.utils.getColor(_);
  10807. return chart;
  10808. };
  10809. chart.offset = function(_) {
  10810. if (!arguments.length) return offset;
  10811. offset = _;
  10812. return chart;
  10813. };
  10814. chart.order = function(_) {
  10815. if (!arguments.length) return order;
  10816. order = _;
  10817. return chart;
  10818. };
  10819. //shortcut for offset + order
  10820. chart.style = function(_) {
  10821. if (!arguments.length) return style;
  10822. style = _;
  10823. switch (style) {
  10824. case 'stack':
  10825. chart.offset('zero');
  10826. chart.order('default');
  10827. break;
  10828. case 'stream':
  10829. chart.offset('wiggle');
  10830. chart.order('inside-out');
  10831. break;
  10832. case 'stream-center':
  10833. chart.offset('silhouette');
  10834. chart.order('inside-out');
  10835. break;
  10836. case 'expand':
  10837. chart.offset('expand');
  10838. chart.order('default');
  10839. break;
  10840. case 'stack_percent':
  10841. chart.offset(chart.d3_stackedOffset_stackPercent);
  10842. chart.order('default');
  10843. break;
  10844. }
  10845. return chart;
  10846. };
  10847. chart.interpolate = function(_) {
  10848. if (!arguments.length) return interpolate;
  10849. interpolate = _;
  10850. return chart;
  10851. };
  10852. //============================================================
  10853. return chart;
  10854. }
  10855. nv.models.stackedAreaChart = function() {
  10856. "use strict";
  10857. //============================================================
  10858. // Public Variables with Default Settings
  10859. //------------------------------------------------------------
  10860. var stacked = nv.models.stackedArea()
  10861. , xAxis = nv.models.axis()
  10862. , yAxis = nv.models.axis()
  10863. , legend = nv.models.legend()
  10864. , controls = nv.models.legend()
  10865. , interactiveLayer = nv.interactiveGuideline()
  10866. ;
  10867. var margin = {top: 30, right: 25, bottom: 50, left: 60}
  10868. , width = null
  10869. , height = null
  10870. , color = nv.utils.defaultColor() // a function that takes in d, i and returns color
  10871. , showControls = true
  10872. , showLegend = true
  10873. , showXAxis = true
  10874. , showYAxis = true
  10875. , rightAlignYAxis = false
  10876. , useInteractiveGuideline = false
  10877. , tooltips = true
  10878. , tooltip = function(key, x, y, e, graph) {
  10879. return '<h3>' + key + '</h3>' +
  10880. '<p>' + y + ' on ' + x + '</p>'
  10881. }
  10882. , x //can be accessed via chart.xScale()
  10883. , y //can be accessed via chart.yScale()
  10884. , yAxisTickFormat = d3.format(',.2f')
  10885. , state = { style: stacked.style() }
  10886. , defaultState = null
  10887. , noData = 'No Data Available.'
  10888. , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
  10889. , controlWidth = 250
  10890. , cData = ['Stacked','Stream','Expanded']
  10891. , controlLabels = {}
  10892. , transitionDuration = 250
  10893. ;
  10894. xAxis
  10895. .orient('bottom')
  10896. .tickPadding(7)
  10897. ;
  10898. yAxis
  10899. .orient((rightAlignYAxis) ? 'right' : 'left')
  10900. ;
  10901. controls.updateState(false);
  10902. //============================================================
  10903. //============================================================
  10904. // Private Variables
  10905. //------------------------------------------------------------
  10906. var showTooltip = function(e, offsetElement) {
  10907. var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
  10908. top = e.pos[1] + ( offsetElement.offsetTop || 0),
  10909. x = xAxis.tickFormat()(stacked.x()(e.point, e.pointIndex)),
  10910. y = yAxis.tickFormat()(stacked.y()(e.point, e.pointIndex)),
  10911. content = tooltip(e.series.key, x, y, e, chart);
  10912. nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
  10913. };
  10914. //============================================================
  10915. function chart(selection) {
  10916. selection.each(function(data) {
  10917. var container = d3.select(this),
  10918. that = this;
  10919. var availableWidth = (width || parseInt(container.style('width')) || 960)
  10920. - margin.left - margin.right,
  10921. availableHeight = (height || parseInt(container.style('height')) || 400)
  10922. - margin.top - margin.bottom;
  10923. chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
  10924. chart.container = this;
  10925. //set state.disabled
  10926. state.disabled = data.map(function(d) { return !!d.disabled });
  10927. if (!defaultState) {
  10928. var key;
  10929. defaultState = {};
  10930. for (key in state) {
  10931. if (state[key] instanceof Array)
  10932. defaultState[key] = state[key].slice(0);
  10933. else
  10934. defaultState[key] = state[key];
  10935. }
  10936. }
  10937. //------------------------------------------------------------
  10938. // Display No Data message if there's nothing to show.
  10939. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
  10940. var noDataText = container.selectAll('.nv-noData').data([noData]);
  10941. noDataText.enter().append('text')
  10942. .attr('class', 'nvd3 nv-noData')
  10943. .attr('dy', '-.7em')
  10944. .style('text-anchor', 'middle');
  10945. noDataText
  10946. .attr('x', margin.left + availableWidth / 2)
  10947. .attr('y', margin.top + availableHeight / 2)
  10948. .text(function(d) { return d });
  10949. return chart;
  10950. } else {
  10951. container.selectAll('.nv-noData').remove();
  10952. }
  10953. //------------------------------------------------------------
  10954. //------------------------------------------------------------
  10955. // Setup Scales
  10956. x = stacked.xScale();
  10957. y = stacked.yScale();
  10958. //------------------------------------------------------------
  10959. //------------------------------------------------------------
  10960. // Setup containers and skeleton of chart
  10961. var wrap = container.selectAll('g.nv-wrap.nv-stackedAreaChart').data([data]);
  10962. var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedAreaChart').append('g');
  10963. var g = wrap.select('g');
  10964. gEnter.append("rect").style("opacity",0);
  10965. gEnter.append('g').attr('class', 'nv-x nv-axis');
  10966. gEnter.append('g').attr('class', 'nv-y nv-axis');
  10967. gEnter.append('g').attr('class', 'nv-stackedWrap');
  10968. gEnter.append('g').attr('class', 'nv-legendWrap');
  10969. gEnter.append('g').attr('class', 'nv-controlsWrap');
  10970. gEnter.append('g').attr('class', 'nv-interactive');
  10971. g.select("rect").attr("width",availableWidth).attr("height",availableHeight);
  10972. //------------------------------------------------------------
  10973. // Legend
  10974. if (showLegend) {
  10975. var legendWidth = (showControls) ? availableWidth - controlWidth : availableWidth;
  10976. legend
  10977. .width(legendWidth);
  10978. g.select('.nv-legendWrap')
  10979. .datum(data)
  10980. .call(legend);
  10981. if ( margin.top != legend.height()) {
  10982. margin.top = legend.height();
  10983. availableHeight = (height || parseInt(container.style('height')) || 400)
  10984. - margin.top - margin.bottom;
  10985. }
  10986. g.select('.nv-legendWrap')
  10987. .attr('transform', 'translate(' + (availableWidth-legendWidth) + ',' + (-margin.top) +')');
  10988. }
  10989. //------------------------------------------------------------
  10990. //------------------------------------------------------------
  10991. // Controls
  10992. if (showControls) {
  10993. var controlsData = [
  10994. {
  10995. key: controlLabels.stacked || 'Stacked',
  10996. metaKey: 'Stacked',
  10997. disabled: stacked.style() != 'stack',
  10998. style: 'stack'
  10999. },
  11000. {
  11001. key: controlLabels.stream || 'Stream',
  11002. metaKey: 'Stream',
  11003. disabled: stacked.style() != 'stream',
  11004. style: 'stream'
  11005. },
  11006. {
  11007. key: controlLabels.expanded || 'Expanded',
  11008. metaKey: 'Expanded',
  11009. disabled: stacked.style() != 'expand',
  11010. style: 'expand'
  11011. },
  11012. {
  11013. key: controlLabels.stack_percent || 'Stack %',
  11014. metaKey: 'Stack_Percent',
  11015. disabled: stacked.style() != 'stack_percent',
  11016. style: 'stack_percent'
  11017. }
  11018. ];
  11019. controlWidth = (cData.length/3) * 260;
  11020. controlsData = controlsData.filter(function(d) {
  11021. return cData.indexOf(d.metaKey) !== -1;
  11022. })
  11023. controls
  11024. .width( controlWidth )
  11025. .color(['#444', '#444', '#444']);
  11026. g.select('.nv-controlsWrap')
  11027. .datum(controlsData)
  11028. .call(controls);
  11029. if ( margin.top != Math.max(controls.height(), legend.height()) ) {
  11030. margin.top = Math.max(controls.height(), legend.height());
  11031. availableHeight = (height || parseInt(container.style('height')) || 400)
  11032. - margin.top - margin.bottom;
  11033. }
  11034. g.select('.nv-controlsWrap')
  11035. .attr('transform', 'translate(0,' + (-margin.top) +')');
  11036. }
  11037. //------------------------------------------------------------
  11038. wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  11039. if (rightAlignYAxis) {
  11040. g.select(".nv-y.nv-axis")
  11041. .attr("transform", "translate(" + availableWidth + ",0)");
  11042. }
  11043. //------------------------------------------------------------
  11044. // Main Chart Component(s)
  11045. //------------------------------------------------------------
  11046. //Set up interactive layer
  11047. if (useInteractiveGuideline) {
  11048. interactiveLayer
  11049. .width(availableWidth)
  11050. .height(availableHeight)
  11051. .margin({left: margin.left, top: margin.top})
  11052. .svgContainer(container)
  11053. .xScale(x);
  11054. wrap.select(".nv-interactive").call(interactiveLayer);
  11055. }
  11056. stacked
  11057. .width(availableWidth)
  11058. .height(availableHeight)
  11059. var stackedWrap = g.select('.nv-stackedWrap')
  11060. .datum(data);
  11061. stackedWrap.transition().call(stacked);
  11062. //------------------------------------------------------------
  11063. //------------------------------------------------------------
  11064. // Setup Axes
  11065. if (showXAxis) {
  11066. xAxis
  11067. .scale(x)
  11068. .ticks( availableWidth / 100 )
  11069. .tickSize( -availableHeight, 0);
  11070. g.select('.nv-x.nv-axis')
  11071. .attr('transform', 'translate(0,' + availableHeight + ')');
  11072. g.select('.nv-x.nv-axis')
  11073. .transition().duration(0)
  11074. .call(xAxis);
  11075. }
  11076. if (showYAxis) {
  11077. yAxis
  11078. .scale(y)
  11079. .ticks(stacked.offset() == 'wiggle' ? 0 : availableHeight / 36)
  11080. .tickSize(-availableWidth, 0)
  11081. .setTickFormat( (stacked.style() == 'expand' || stacked.style() == 'stack_percent')
  11082. ? d3.format('%') : yAxisTickFormat);
  11083. g.select('.nv-y.nv-axis')
  11084. .transition().duration(0)
  11085. .call(yAxis);
  11086. }
  11087. //------------------------------------------------------------
  11088. //============================================================
  11089. // Event Handling/Dispatching (in chart's scope)
  11090. //------------------------------------------------------------
  11091. stacked.dispatch.on('areaClick.toggle', function(e) {
  11092. if (data.filter(function(d) { return !d.disabled }).length === 1)
  11093. data.forEach(function(d) {
  11094. d.disabled = false;
  11095. });
  11096. else
  11097. data.forEach(function(d,i) {
  11098. d.disabled = (i != e.seriesIndex);
  11099. });
  11100. state.disabled = data.map(function(d) { return !!d.disabled });
  11101. dispatch.stateChange(state);
  11102. chart.update();
  11103. });
  11104. legend.dispatch.on('stateChange', function(newState) {
  11105. state.disabled = newState.disabled;
  11106. dispatch.stateChange(state);
  11107. chart.update();
  11108. });
  11109. controls.dispatch.on('legendClick', function(d,i) {
  11110. if (!d.disabled) return;
  11111. controlsData = controlsData.map(function(s) {
  11112. s.disabled = true;
  11113. return s;
  11114. });
  11115. d.disabled = false;
  11116. stacked.style(d.style);
  11117. state.style = stacked.style();
  11118. dispatch.stateChange(state);
  11119. chart.update();
  11120. });
  11121. interactiveLayer.dispatch.on('elementMousemove', function(e) {
  11122. stacked.clearHighlights();
  11123. var singlePoint, pointIndex, pointXLocation, allData = [];
  11124. data
  11125. .filter(function(series, i) {
  11126. series.seriesIndex = i;
  11127. return !series.disabled;
  11128. })
  11129. .forEach(function(series,i) {
  11130. pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
  11131. stacked.highlightPoint(i, pointIndex, true);
  11132. var point = series.values[pointIndex];
  11133. if (typeof point === 'undefined') return;
  11134. if (typeof singlePoint === 'undefined') singlePoint = point;
  11135. if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
  11136. //If we are in 'expand' mode, use the stacked percent value instead of raw value.
  11137. var tooltipValue = (stacked.style() == 'expand') ? point.display.y : chart.y()(point,pointIndex);
  11138. allData.push({
  11139. key: series.key,
  11140. value: tooltipValue,
  11141. color: color(series,series.seriesIndex),
  11142. stackedValue: point.display
  11143. });
  11144. });
  11145. allData.reverse();
  11146. //Highlight the tooltip entry based on which stack the mouse is closest to.
  11147. if (allData.length > 2) {
  11148. var yValue = chart.yScale().invert(e.mouseY);
  11149. var yDistMax = Infinity, indexToHighlight = null;
  11150. allData.forEach(function(series,i) {
  11151. //To handle situation where the stacked area chart is negative, we need to use absolute values
  11152. //when checking if the mouse Y value is within the stack area.
  11153. yValue = Math.abs(yValue);
  11154. var stackedY0 = Math.abs(series.stackedValue.y0);
  11155. var stackedY = Math.abs(series.stackedValue.y);
  11156. if ( yValue >= stackedY0 && yValue <= (stackedY + stackedY0))
  11157. {
  11158. indexToHighlight = i;
  11159. return;
  11160. }
  11161. });
  11162. if (indexToHighlight != null)
  11163. allData[indexToHighlight].highlight = true;
  11164. }
  11165. var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
  11166. //If we are in 'expand' mode, force the format to be a percentage.
  11167. var valueFormatter = (stacked.style() == 'expand') ?
  11168. function(d,i) {return d3.format(".1%")(d);} :
  11169. function(d,i) {return yAxis.tickFormat()(d); };
  11170. interactiveLayer.tooltip
  11171. .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
  11172. .chartContainer(that.parentNode)
  11173. .enabled(tooltips)
  11174. .valueFormatter(valueFormatter)
  11175. .data(
  11176. {
  11177. value: xValue,
  11178. series: allData
  11179. }
  11180. )();
  11181. interactiveLayer.renderGuideLine(pointXLocation);
  11182. });
  11183. interactiveLayer.dispatch.on("elementMouseout",function(e) {
  11184. dispatch.tooltipHide();
  11185. stacked.clearHighlights();
  11186. });
  11187. dispatch.on('tooltipShow', function(e) {
  11188. if (tooltips) showTooltip(e, that.parentNode);
  11189. });
  11190. // Update chart from a state object passed to event handler
  11191. dispatch.on('changeState', function(e) {
  11192. if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
  11193. data.forEach(function(series,i) {
  11194. series.disabled = e.disabled[i];
  11195. });
  11196. state.disabled = e.disabled;
  11197. }
  11198. if (typeof e.style !== 'undefined') {
  11199. stacked.style(e.style);
  11200. }
  11201. chart.update();
  11202. });
  11203. });
  11204. return chart;
  11205. }
  11206. //============================================================
  11207. // Event Handling/Dispatching (out of chart's scope)
  11208. //------------------------------------------------------------
  11209. stacked.dispatch.on('tooltipShow', function(e) {
  11210. //disable tooltips when value ~= 0
  11211. //// TODO: consider removing points from voronoi that have 0 value instead of this hack
  11212. /*
  11213. if (!Math.round(stacked.y()(e.point) * 100)) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
  11214. setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
  11215. return false;
  11216. }
  11217. */
  11218. e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
  11219. dispatch.tooltipShow(e);
  11220. });
  11221. stacked.dispatch.on('tooltipHide', function(e) {
  11222. dispatch.tooltipHide(e);
  11223. });
  11224. dispatch.on('tooltipHide', function() {
  11225. if (tooltips) nv.tooltip.cleanup();
  11226. });
  11227. //============================================================
  11228. //============================================================
  11229. // Expose Public Variables
  11230. //------------------------------------------------------------
  11231. // expose chart's sub-components
  11232. chart.dispatch = dispatch;
  11233. chart.stacked = stacked;
  11234. chart.legend = legend;
  11235. chart.controls = controls;
  11236. chart.xAxis = xAxis;
  11237. chart.yAxis = yAxis;
  11238. chart.interactiveLayer = interactiveLayer;
  11239. d3.rebind(chart, stacked, 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'interactive', 'useVoronoi', 'offset', 'order', 'style', 'clipEdge', 'forceX', 'forceY', 'forceSize', 'interpolate');
  11240. chart.options = nv.utils.optionsFunc.bind(chart);
  11241. chart.margin = function(_) {
  11242. if (!arguments.length) return margin;
  11243. margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
  11244. margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
  11245. margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
  11246. margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
  11247. return chart;
  11248. };
  11249. chart.width = function(_) {
  11250. if (!arguments.length) return width;
  11251. width = _;
  11252. return chart;
  11253. };
  11254. chart.height = function(_) {
  11255. if (!arguments.length) return height;
  11256. height = _;
  11257. return chart;
  11258. };
  11259. chart.color = function(_) {
  11260. if (!arguments.length) return color;
  11261. color = nv.utils.getColor(_);
  11262. legend.color(color);
  11263. stacked.color(color);
  11264. return chart;
  11265. };
  11266. chart.showControls = function(_) {
  11267. if (!arguments.length) return showControls;
  11268. showControls = _;
  11269. return chart;
  11270. };
  11271. chart.showLegend = function(_) {
  11272. if (!arguments.length) return showLegend;
  11273. showLegend = _;
  11274. return chart;
  11275. };
  11276. chart.showXAxis = function(_) {
  11277. if (!arguments.length) return showXAxis;
  11278. showXAxis = _;
  11279. return chart;
  11280. };
  11281. chart.showYAxis = function(_) {
  11282. if (!arguments.length) return showYAxis;
  11283. showYAxis = _;
  11284. return chart;
  11285. };
  11286. chart.rightAlignYAxis = function(_) {
  11287. if(!arguments.length) return rightAlignYAxis;
  11288. rightAlignYAxis = _;
  11289. yAxis.orient( (_) ? 'right' : 'left');
  11290. return chart;
  11291. };
  11292. chart.useInteractiveGuideline = function(_) {
  11293. if(!arguments.length) return useInteractiveGuideline;
  11294. useInteractiveGuideline = _;
  11295. if (_ === true) {
  11296. chart.interactive(false);
  11297. chart.useVoronoi(false);
  11298. }
  11299. return chart;
  11300. };
  11301. chart.tooltip = function(_) {
  11302. if (!arguments.length) return tooltip;
  11303. tooltip = _;
  11304. return chart;
  11305. };
  11306. chart.tooltips = function(_) {
  11307. if (!arguments.length) return tooltips;
  11308. tooltips = _;
  11309. return chart;
  11310. };
  11311. chart.tooltipContent = function(_) {
  11312. if (!arguments.length) return tooltip;
  11313. tooltip = _;
  11314. return chart;
  11315. };
  11316. chart.state = function(_) {
  11317. if (!arguments.length) return state;
  11318. state = _;
  11319. return chart;
  11320. };
  11321. chart.defaultState = function(_) {
  11322. if (!arguments.length) return defaultState;
  11323. defaultState = _;
  11324. return chart;
  11325. };
  11326. chart.noData = function(_) {
  11327. if (!arguments.length) return noData;
  11328. noData = _;
  11329. return chart;
  11330. };
  11331. chart.transitionDuration = function(_) {
  11332. if (!arguments.length) return transitionDuration;
  11333. transitionDuration = _;
  11334. return chart;
  11335. };
  11336. chart.controlsData = function(_) {
  11337. if (!arguments.length) return cData;
  11338. cData = _;
  11339. return chart;
  11340. };
  11341. chart.controlLabels = function(_) {
  11342. if (!arguments.length) return controlLabels;
  11343. if (typeof _ !== 'object') return controlLabels;
  11344. controlLabels = _;
  11345. return chart;
  11346. };
  11347. yAxis.setTickFormat = yAxis.tickFormat;
  11348. yAxis.tickFormat = function(_) {
  11349. if (!arguments.length) return yAxisTickFormat;
  11350. yAxisTickFormat = _;
  11351. return yAxis;
  11352. };
  11353. //============================================================
  11354. return chart;
  11355. }
  11356. })();