| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187371883718937190371913719237193371943719537196371973719837199372003720137202372033720437205372063720737208372093721037211372123721337214372153721637217372183721937220372213722237223372243722537226372273722837229372303723137232372333723437235372363723737238372393724037241372423724337244372453724637247372483724937250372513725237253372543725537256372573725837259372603726137262372633726437265372663726737268372693727037271372723727337274372753727637277372783727937280372813728237283372843728537286372873728837289372903729137292372933729437295372963729737298372993730037301373023730337304373053730637307373083730937310373113731237313373143731537316373173731837319373203732137322373233732437325373263732737328373293733037331373323733337334373353733637337373383733937340373413734237343373443734537346373473734837349373503735137352373533735437355373563735737358373593736037361373623736337364373653736637367373683736937370373713737237373373743737537376373773737837379373803738137382373833738437385373863738737388373893739037391373923739337394373953739637397373983739937400374013740237403374043740537406374073740837409374103741137412374133741437415374163741737418374193742037421374223742337424374253742637427374283742937430374313743237433374343743537436374373743837439374403744137442374433744437445374463744737448374493745037451374523745337454374553745637457374583745937460374613746237463374643746537466374673746837469374703747137472374733747437475374763747737478374793748037481374823748337484374853748637487374883748937490374913749237493374943749537496374973749837499375003750137502375033750437505375063750737508375093751037511375123751337514375153751637517375183751937520375213752237523375243752537526375273752837529375303753137532375333753437535375363753737538375393754037541375423754337544375453754637547375483754937550375513755237553375543755537556375573755837559375603756137562375633756437565375663756737568375693757037571375723757337574375753757637577375783757937580375813758237583375843758537586375873758837589375903759137592375933759437595375963759737598375993760037601376023760337604376053760637607376083760937610376113761237613376143761537616376173761837619376203762137622376233762437625376263762737628376293763037631376323763337634376353763637637376383763937640376413764237643376443764537646376473764837649376503765137652376533765437655376563765737658376593766037661376623766337664376653766637667376683766937670376713767237673376743767537676376773767837679376803768137682376833768437685376863768737688376893769037691376923769337694376953769637697376983769937700377013770237703377043770537706377073770837709377103771137712377133771437715377163771737718377193772037721377223772337724377253772637727377283772937730377313773237733377343773537736377373773837739377403774137742377433774437745377463774737748377493775037751377523775337754377553775637757377583775937760377613776237763377643776537766377673776837769377703777137772377733777437775377763777737778377793778037781377823778337784377853778637787377883778937790377913779237793377943779537796377973779837799378003780137802378033780437805378063780737808378093781037811378123781337814378153781637817378183781937820378213782237823378243782537826378273782837829378303783137832378333783437835378363783737838378393784037841378423784337844378453784637847378483784937850378513785237853378543785537856378573785837859378603786137862378633786437865378663786737868378693787037871378723787337874378753787637877378783787937880378813788237883378843788537886378873788837889378903789137892378933789437895378963789737898378993790037901379023790337904379053790637907379083790937910379113791237913379143791537916379173791837919379203792137922379233792437925379263792737928379293793037931379323793337934379353793637937379383793937940379413794237943379443794537946379473794837949379503795137952379533795437955379563795737958379593796037961379623796337964379653796637967379683796937970379713797237973379743797537976379773797837979379803798137982379833798437985379863798737988379893799037991379923799337994379953799637997379983799938000380013800238003380043800538006380073800838009380103801138012380133801438015380163801738018380193802038021380223802338024380253802638027380283802938030380313803238033380343803538036380373803838039380403804138042380433804438045380463804738048380493805038051380523805338054380553805638057380583805938060380613806238063380643806538066380673806838069380703807138072380733807438075380763807738078380793808038081380823808338084380853808638087380883808938090380913809238093380943809538096380973809838099381003810138102381033810438105381063810738108381093811038111381123811338114381153811638117381183811938120381213812238123381243812538126381273812838129381303813138132381333813438135381363813738138381393814038141381423814338144381453814638147381483814938150381513815238153381543815538156381573815838159381603816138162381633816438165381663816738168381693817038171381723817338174381753817638177381783817938180381813818238183381843818538186381873818838189381903819138192381933819438195381963819738198381993820038201382023820338204382053820638207382083820938210382113821238213382143821538216382173821838219382203822138222382233822438225382263822738228382293823038231382323823338234382353823638237382383823938240382413824238243382443824538246382473824838249382503825138252382533825438255382563825738258382593826038261382623826338264382653826638267382683826938270382713827238273382743827538276382773827838279382803828138282382833828438285382863828738288382893829038291382923829338294382953829638297382983829938300383013830238303383043830538306383073830838309383103831138312383133831438315383163831738318383193832038321383223832338324383253832638327383283832938330383313833238333383343833538336383373833838339383403834138342383433834438345383463834738348383493835038351383523835338354383553835638357383583835938360383613836238363383643836538366383673836838369383703837138372383733837438375383763837738378383793838038381383823838338384383853838638387383883838938390383913839238393383943839538396383973839838399384003840138402384033840438405384063840738408384093841038411384123841338414384153841638417384183841938420384213842238423384243842538426384273842838429384303843138432384333843438435384363843738438384393844038441384423844338444384453844638447384483844938450384513845238453384543845538456384573845838459384603846138462384633846438465384663846738468384693847038471384723847338474384753847638477384783847938480384813848238483384843848538486384873848838489384903849138492384933849438495384963849738498384993850038501385023850338504385053850638507385083850938510385113851238513385143851538516385173851838519385203852138522385233852438525385263852738528385293853038531385323853338534385353853638537385383853938540385413854238543385443854538546385473854838549385503855138552385533855438555385563855738558385593856038561385623856338564385653856638567385683856938570385713857238573385743857538576385773857838579385803858138582385833858438585385863858738588385893859038591385923859338594385953859638597385983859938600386013860238603386043860538606386073860838609386103861138612386133861438615386163861738618386193862038621386223862338624386253862638627386283862938630386313863238633386343863538636386373863838639386403864138642386433864438645386463864738648386493865038651386523865338654386553865638657386583865938660386613866238663386643866538666386673866838669386703867138672386733867438675386763867738678386793868038681386823868338684386853868638687386883868938690386913869238693386943869538696386973869838699387003870138702387033870438705387063870738708387093871038711387123871338714387153871638717387183871938720387213872238723387243872538726387273872838729387303873138732387333873438735387363873738738387393874038741387423874338744387453874638747387483874938750387513875238753387543875538756387573875838759387603876138762387633876438765387663876738768387693877038771387723877338774387753877638777387783877938780387813878238783387843878538786387873878838789387903879138792387933879438795387963879738798387993880038801388023880338804388053880638807388083880938810388113881238813388143881538816388173881838819388203882138822388233882438825388263882738828388293883038831388323883338834388353883638837388383883938840388413884238843388443884538846388473884838849388503885138852388533885438855388563885738858388593886038861388623886338864388653886638867388683886938870388713887238873388743887538876388773887838879388803888138882388833888438885388863888738888388893889038891388923889338894388953889638897388983889938900389013890238903389043890538906389073890838909389103891138912389133891438915389163891738918389193892038921389223892338924389253892638927389283892938930389313893238933389343893538936389373893838939389403894138942389433894438945389463894738948389493895038951389523895338954389553895638957389583895938960389613896238963389643896538966389673896838969389703897138972389733897438975389763897738978389793898038981389823898338984389853898638987389883898938990389913899238993389943899538996389973899838999390003900139002390033900439005390063900739008390093901039011390123901339014390153901639017390183901939020390213902239023390243902539026390273902839029390303903139032390333903439035390363903739038390393904039041390423904339044390453904639047390483904939050390513905239053390543905539056390573905839059390603906139062390633906439065390663906739068390693907039071390723907339074390753907639077390783907939080390813908239083390843908539086390873908839089390903909139092390933909439095390963909739098390993910039101391023910339104391053910639107391083910939110391113911239113391143911539116391173911839119391203912139122391233912439125391263912739128391293913039131391323913339134391353913639137391383913939140391413914239143391443914539146391473914839149391503915139152391533915439155391563915739158391593916039161391623916339164391653916639167391683916939170391713917239173391743917539176391773917839179391803918139182391833918439185391863918739188391893919039191391923919339194391953919639197391983919939200392013920239203392043920539206392073920839209392103921139212392133921439215392163921739218392193922039221392223922339224392253922639227392283922939230392313923239233392343923539236392373923839239392403924139242392433924439245392463924739248392493925039251392523925339254392553925639257392583925939260392613926239263392643926539266392673926839269392703927139272392733927439275392763927739278392793928039281392823928339284392853928639287392883928939290392913929239293392943929539296392973929839299393003930139302393033930439305393063930739308393093931039311393123931339314393153931639317393183931939320393213932239323393243932539326393273932839329393303933139332393333933439335393363933739338393393934039341393423934339344393453934639347393483934939350393513935239353393543935539356393573935839359393603936139362393633936439365393663936739368393693937039371393723937339374393753937639377393783937939380393813938239383393843938539386393873938839389393903939139392393933939439395393963939739398393993940039401394023940339404394053940639407394083940939410394113941239413394143941539416394173941839419394203942139422394233942439425394263942739428394293943039431394323943339434394353943639437394383943939440394413944239443394443944539446394473944839449394503945139452394533945439455394563945739458394593946039461394623946339464394653946639467394683946939470394713947239473394743947539476394773947839479394803948139482394833948439485394863948739488394893949039491394923949339494394953949639497394983949939500395013950239503395043950539506395073950839509395103951139512395133951439515395163951739518395193952039521395223952339524395253952639527395283952939530395313953239533395343953539536395373953839539395403954139542395433954439545395463954739548395493955039551395523955339554395553955639557395583955939560395613956239563395643956539566395673956839569395703957139572395733957439575395763957739578395793958039581395823958339584395853958639587395883958939590395913959239593395943959539596395973959839599396003960139602396033960439605396063960739608396093961039611396123961339614396153961639617396183961939620396213962239623396243962539626396273962839629396303963139632396333963439635396363963739638396393964039641396423964339644396453964639647396483964939650396513965239653396543965539656396573965839659396603966139662396633966439665396663966739668396693967039671396723967339674396753967639677396783967939680396813968239683396843968539686396873968839689396903969139692396933969439695396963969739698396993970039701397023970339704397053970639707397083970939710397113971239713397143971539716397173971839719397203972139722397233972439725397263972739728397293973039731397323973339734397353973639737397383973939740397413974239743397443974539746397473974839749397503975139752397533975439755397563975739758397593976039761397623976339764397653976639767397683976939770397713977239773397743977539776397773977839779397803978139782397833978439785397863978739788397893979039791397923979339794397953979639797397983979939800398013980239803398043980539806398073980839809398103981139812398133981439815398163981739818398193982039821398223982339824398253982639827398283982939830398313983239833398343983539836398373983839839398403984139842398433984439845398463984739848398493985039851398523985339854398553985639857398583985939860398613986239863398643986539866398673986839869398703987139872398733987439875398763987739878398793988039881398823988339884398853988639887398883988939890398913989239893398943989539896398973989839899399003990139902399033990439905399063990739908399093991039911399123991339914399153991639917399183991939920399213992239923399243992539926399273992839929399303993139932399333993439935399363993739938399393994039941399423994339944399453994639947399483994939950399513995239953399543995539956399573995839959399603996139962399633996439965399663996739968399693997039971399723997339974399753997639977399783997939980399813998239983399843998539986399873998839989399903999139992399933999439995399963999739998399994000040001400024000340004400054000640007400084000940010400114001240013400144001540016400174001840019400204002140022400234002440025400264002740028400294003040031400324003340034400354003640037400384003940040400414004240043400444004540046400474004840049400504005140052400534005440055400564005740058400594006040061400624006340064400654006640067400684006940070400714007240073400744007540076400774007840079400804008140082400834008440085400864008740088400894009040091400924009340094400954009640097400984009940100401014010240103401044010540106401074010840109401104011140112401134011440115401164011740118401194012040121401224012340124401254012640127401284012940130401314013240133401344013540136401374013840139401404014140142401434014440145401464014740148401494015040151401524015340154401554015640157401584015940160401614016240163401644016540166401674016840169401704017140172401734017440175401764017740178401794018040181401824018340184401854018640187401884018940190401914019240193401944019540196401974019840199402004020140202402034020440205402064020740208402094021040211402124021340214402154021640217402184021940220402214022240223402244022540226402274022840229402304023140232402334023440235402364023740238402394024040241402424024340244402454024640247402484024940250402514025240253402544025540256402574025840259402604026140262402634026440265402664026740268402694027040271402724027340274402754027640277402784027940280402814028240283402844028540286402874028840289402904029140292402934029440295402964029740298402994030040301403024030340304403054030640307403084030940310403114031240313403144031540316403174031840319403204032140322403234032440325403264032740328403294033040331403324033340334403354033640337403384033940340403414034240343403444034540346403474034840349403504035140352403534035440355403564035740358403594036040361403624036340364403654036640367403684036940370403714037240373403744037540376403774037840379403804038140382403834038440385403864038740388403894039040391403924039340394403954039640397403984039940400404014040240403404044040540406404074040840409404104041140412404134041440415404164041740418404194042040421404224042340424404254042640427404284042940430404314043240433404344043540436404374043840439404404044140442404434044440445404464044740448404494045040451404524045340454404554045640457404584045940460404614046240463404644046540466404674046840469404704047140472404734047440475404764047740478404794048040481404824048340484404854048640487404884048940490404914049240493404944049540496404974049840499405004050140502405034050440505405064050740508405094051040511405124051340514405154051640517405184051940520405214052240523405244052540526405274052840529405304053140532405334053440535405364053740538405394054040541405424054340544405454054640547405484054940550405514055240553405544055540556405574055840559405604056140562405634056440565405664056740568405694057040571405724057340574405754057640577405784057940580405814058240583405844058540586405874058840589405904059140592405934059440595405964059740598405994060040601406024060340604406054060640607406084060940610406114061240613406144061540616406174061840619406204062140622406234062440625406264062740628406294063040631406324063340634406354063640637406384063940640406414064240643406444064540646406474064840649406504065140652406534065440655406564065740658406594066040661406624066340664406654066640667406684066940670406714067240673406744067540676406774067840679406804068140682406834068440685406864068740688406894069040691406924069340694406954069640697406984069940700407014070240703407044070540706407074070840709407104071140712407134071440715407164071740718407194072040721407224072340724407254072640727407284072940730407314073240733407344073540736407374073840739407404074140742407434074440745407464074740748407494075040751407524075340754407554075640757407584075940760407614076240763407644076540766407674076840769407704077140772407734077440775407764077740778407794078040781407824078340784407854078640787407884078940790407914079240793407944079540796407974079840799408004080140802408034080440805408064080740808408094081040811408124081340814408154081640817408184081940820408214082240823408244082540826408274082840829408304083140832408334083440835408364083740838408394084040841408424084340844408454084640847408484084940850408514085240853408544085540856408574085840859408604086140862408634086440865408664086740868408694087040871408724087340874408754087640877408784087940880408814088240883408844088540886408874088840889408904089140892408934089440895408964089740898408994090040901409024090340904409054090640907409084090940910409114091240913409144091540916409174091840919409204092140922409234092440925409264092740928409294093040931409324093340934409354093640937409384093940940409414094240943409444094540946409474094840949409504095140952409534095440955409564095740958409594096040961409624096340964409654096640967409684096940970409714097240973409744097540976409774097840979409804098140982409834098440985409864098740988409894099040991409924099340994409954099640997409984099941000410014100241003410044100541006410074100841009410104101141012410134101441015410164101741018410194102041021410224102341024410254102641027410284102941030410314103241033410344103541036410374103841039410404104141042410434104441045410464104741048410494105041051410524105341054410554105641057410584105941060410614106241063410644106541066410674106841069410704107141072410734107441075410764107741078410794108041081410824108341084410854108641087410884108941090410914109241093410944109541096410974109841099411004110141102411034110441105411064110741108411094111041111411124111341114411154111641117411184111941120411214112241123411244112541126411274112841129411304113141132411334113441135411364113741138411394114041141411424114341144411454114641147411484114941150411514115241153411544115541156411574115841159411604116141162411634116441165411664116741168411694117041171411724117341174411754117641177411784117941180411814118241183411844118541186411874118841189411904119141192411934119441195411964119741198411994120041201412024120341204412054120641207412084120941210412114121241213412144121541216412174121841219412204122141222412234122441225412264122741228412294123041231412324123341234412354123641237412384123941240412414124241243412444124541246412474124841249412504125141252412534125441255412564125741258412594126041261412624126341264412654126641267412684126941270412714127241273412744127541276412774127841279412804128141282412834128441285412864128741288412894129041291412924129341294412954129641297412984129941300413014130241303413044130541306413074130841309413104131141312413134131441315413164131741318413194132041321413224132341324413254132641327413284132941330413314133241333413344133541336413374133841339413404134141342413434134441345413464134741348413494135041351413524135341354413554135641357413584135941360413614136241363413644136541366413674136841369413704137141372413734137441375413764137741378413794138041381413824138341384413854138641387413884138941390413914139241393413944139541396413974139841399414004140141402414034140441405414064140741408414094141041411414124141341414414154141641417414184141941420414214142241423414244142541426414274142841429414304143141432414334143441435414364143741438414394144041441414424144341444414454144641447414484144941450414514145241453414544145541456414574145841459414604146141462414634146441465414664146741468414694147041471414724147341474414754147641477414784147941480414814148241483414844148541486414874148841489414904149141492414934149441495414964149741498414994150041501415024150341504415054150641507415084150941510415114151241513415144151541516415174151841519415204152141522415234152441525415264152741528415294153041531415324153341534415354153641537415384153941540415414154241543415444154541546415474154841549415504155141552415534155441555415564155741558415594156041561415624156341564415654156641567415684156941570415714157241573415744157541576415774157841579415804158141582415834158441585415864158741588415894159041591415924159341594415954159641597415984159941600416014160241603416044160541606416074160841609416104161141612416134161441615416164161741618416194162041621416224162341624416254162641627416284162941630416314163241633416344163541636416374163841639416404164141642416434164441645416464164741648416494165041651416524165341654416554165641657416584165941660416614166241663416644166541666416674166841669416704167141672416734167441675416764167741678416794168041681416824168341684416854168641687416884168941690416914169241693416944169541696416974169841699417004170141702417034170441705417064170741708417094171041711417124171341714417154171641717417184171941720417214172241723417244172541726417274172841729417304173141732417334173441735417364173741738417394174041741417424174341744417454174641747417484174941750417514175241753417544175541756417574175841759417604176141762417634176441765417664176741768417694177041771417724177341774417754177641777417784177941780417814178241783417844178541786417874178841789417904179141792417934179441795417964179741798417994180041801418024180341804418054180641807418084180941810418114181241813418144181541816418174181841819418204182141822418234182441825418264182741828418294183041831418324183341834418354183641837418384183941840418414184241843418444184541846418474184841849418504185141852418534185441855418564185741858418594186041861418624186341864418654186641867418684186941870418714187241873418744187541876418774187841879418804188141882418834188441885418864188741888418894189041891418924189341894418954189641897418984189941900419014190241903419044190541906419074190841909419104191141912419134191441915419164191741918419194192041921419224192341924419254192641927419284192941930419314193241933419344193541936419374193841939419404194141942419434194441945419464194741948419494195041951419524195341954419554195641957419584195941960419614196241963419644196541966419674196841969419704197141972419734197441975419764197741978419794198041981419824198341984419854198641987419884198941990419914199241993419944199541996419974199841999420004200142002420034200442005420064200742008420094201042011420124201342014420154201642017420184201942020420214202242023420244202542026420274202842029420304203142032420334203442035420364203742038420394204042041420424204342044420454204642047420484204942050420514205242053420544205542056420574205842059420604206142062420634206442065420664206742068420694207042071420724207342074420754207642077420784207942080420814208242083420844208542086420874208842089420904209142092420934209442095420964209742098420994210042101421024210342104421054210642107421084210942110421114211242113421144211542116421174211842119421204212142122421234212442125421264212742128421294213042131421324213342134421354213642137421384213942140421414214242143421444214542146421474214842149421504215142152421534215442155421564215742158421594216042161421624216342164421654216642167421684216942170421714217242173421744217542176421774217842179421804218142182421834218442185421864218742188421894219042191421924219342194421954219642197421984219942200422014220242203422044220542206422074220842209422104221142212422134221442215422164221742218422194222042221422224222342224422254222642227422284222942230422314223242233422344223542236422374223842239422404224142242422434224442245422464224742248422494225042251422524225342254422554225642257422584225942260422614226242263422644226542266422674226842269422704227142272422734227442275422764227742278422794228042281422824228342284422854228642287422884228942290422914229242293422944229542296422974229842299423004230142302423034230442305423064230742308423094231042311423124231342314423154231642317423184231942320423214232242323423244232542326423274232842329423304233142332423334233442335423364233742338423394234042341423424234342344423454234642347423484234942350423514235242353423544235542356423574235842359423604236142362423634236442365423664236742368423694237042371423724237342374423754237642377423784237942380423814238242383423844238542386423874238842389423904239142392423934239442395423964239742398423994240042401424024240342404424054240642407424084240942410424114241242413424144241542416424174241842419424204242142422424234242442425424264242742428424294243042431424324243342434424354243642437424384243942440424414244242443424444244542446424474244842449424504245142452424534245442455424564245742458424594246042461424624246342464424654246642467424684246942470424714247242473424744247542476424774247842479424804248142482424834248442485424864248742488424894249042491424924249342494424954249642497424984249942500425014250242503425044250542506425074250842509425104251142512425134251442515425164251742518425194252042521425224252342524425254252642527425284252942530425314253242533425344253542536425374253842539425404254142542425434254442545425464254742548425494255042551425524255342554425554255642557425584255942560425614256242563425644256542566425674256842569425704257142572425734257442575425764257742578425794258042581425824258342584425854258642587425884258942590425914259242593425944259542596425974259842599426004260142602426034260442605426064260742608426094261042611426124261342614426154261642617426184261942620426214262242623426244262542626426274262842629426304263142632426334263442635426364263742638426394264042641426424264342644426454264642647426484264942650426514265242653426544265542656426574265842659426604266142662426634266442665426664266742668426694267042671426724267342674426754267642677426784267942680426814268242683426844268542686426874268842689426904269142692426934269442695426964269742698426994270042701427024270342704427054270642707427084270942710427114271242713427144271542716427174271842719427204272142722427234272442725427264272742728427294273042731427324273342734427354273642737427384273942740427414274242743427444274542746427474274842749427504275142752427534275442755427564275742758427594276042761427624276342764427654276642767427684276942770427714277242773427744277542776427774277842779427804278142782427834278442785427864278742788427894279042791427924279342794427954279642797427984279942800428014280242803428044280542806428074280842809428104281142812428134281442815428164281742818428194282042821428224282342824428254282642827428284282942830428314283242833428344283542836428374283842839428404284142842428434284442845428464284742848428494285042851428524285342854428554285642857428584285942860428614286242863428644286542866428674286842869428704287142872428734287442875428764287742878428794288042881428824288342884428854288642887428884288942890428914289242893428944289542896428974289842899429004290142902429034290442905429064290742908429094291042911429124291342914429154291642917429184291942920429214292242923429244292542926429274292842929429304293142932429334293442935429364293742938429394294042941429424294342944429454294642947429484294942950429514295242953429544295542956429574295842959429604296142962429634296442965429664296742968429694297042971429724297342974429754297642977429784297942980429814298242983429844298542986429874298842989429904299142992429934299442995429964299742998429994300043001430024300343004430054300643007430084300943010430114301243013430144301543016430174301843019430204302143022430234302443025430264302743028430294303043031430324303343034430354303643037430384303943040430414304243043430444304543046430474304843049430504305143052430534305443055430564305743058430594306043061430624306343064430654306643067430684306943070430714307243073430744307543076430774307843079430804308143082430834308443085430864308743088430894309043091430924309343094430954309643097430984309943100431014310243103431044310543106431074310843109431104311143112431134311443115431164311743118431194312043121431224312343124431254312643127431284312943130431314313243133431344313543136431374313843139431404314143142431434314443145431464314743148431494315043151431524315343154431554315643157431584315943160431614316243163431644316543166431674316843169431704317143172431734317443175431764317743178431794318043181431824318343184431854318643187431884318943190431914319243193431944319543196431974319843199432004320143202432034320443205432064320743208432094321043211432124321343214432154321643217432184321943220432214322243223432244322543226432274322843229432304323143232432334323443235432364323743238432394324043241432424324343244432454324643247432484324943250432514325243253432544325543256432574325843259432604326143262432634326443265432664326743268432694327043271432724327343274432754327643277432784327943280432814328243283432844328543286432874328843289432904329143292432934329443295432964329743298432994330043301433024330343304433054330643307433084330943310433114331243313433144331543316433174331843319433204332143322433234332443325433264332743328433294333043331433324333343334433354333643337433384333943340433414334243343433444334543346433474334843349433504335143352433534335443355433564335743358433594336043361433624336343364433654336643367433684336943370433714337243373433744337543376433774337843379433804338143382433834338443385433864338743388433894339043391433924339343394433954339643397433984339943400434014340243403434044340543406434074340843409434104341143412434134341443415434164341743418434194342043421434224342343424434254342643427434284342943430434314343243433434344343543436434374343843439434404344143442434434344443445434464344743448434494345043451434524345343454434554345643457434584345943460434614346243463434644346543466434674346843469434704347143472434734347443475434764347743478434794348043481434824348343484434854348643487434884348943490434914349243493434944349543496434974349843499435004350143502435034350443505435064350743508435094351043511435124351343514435154351643517435184351943520435214352243523435244352543526435274352843529435304353143532435334353443535435364353743538435394354043541435424354343544435454354643547435484354943550435514355243553435544355543556435574355843559435604356143562435634356443565435664356743568435694357043571435724357343574435754357643577435784357943580435814358243583435844358543586435874358843589435904359143592435934359443595435964359743598435994360043601436024360343604436054360643607436084360943610436114361243613436144361543616436174361843619436204362143622436234362443625436264362743628436294363043631436324363343634436354363643637436384363943640436414364243643436444364543646436474364843649436504365143652436534365443655436564365743658436594366043661436624366343664436654366643667436684366943670436714367243673436744367543676436774367843679436804368143682436834368443685436864368743688436894369043691436924369343694436954369643697436984369943700437014370243703437044370543706437074370843709437104371143712437134371443715437164371743718437194372043721437224372343724437254372643727437284372943730437314373243733437344373543736437374373843739437404374143742437434374443745437464374743748437494375043751437524375343754437554375643757437584375943760437614376243763437644376543766437674376843769437704377143772437734377443775437764377743778437794378043781437824378343784437854378643787437884378943790437914379243793437944379543796437974379843799438004380143802438034380443805438064380743808438094381043811438124381343814438154381643817438184381943820438214382243823438244382543826438274382843829438304383143832438334383443835438364383743838438394384043841438424384343844438454384643847438484384943850438514385243853438544385543856438574385843859438604386143862438634386443865438664386743868438694387043871438724387343874438754387643877438784387943880438814388243883438844388543886438874388843889438904389143892438934389443895438964389743898438994390043901439024390343904439054390643907439084390943910439114391243913439144391543916439174391843919439204392143922439234392443925439264392743928439294393043931439324393343934439354393643937439384393943940439414394243943439444394543946439474394843949439504395143952439534395443955439564395743958439594396043961439624396343964439654396643967439684396943970439714397243973439744397543976439774397843979439804398143982439834398443985439864398743988439894399043991439924399343994439954399643997439984399944000440014400244003440044400544006440074400844009440104401144012440134401444015440164401744018440194402044021440224402344024440254402644027440284402944030440314403244033440344403544036440374403844039440404404144042440434404444045440464404744048440494405044051440524405344054440554405644057440584405944060440614406244063440644406544066440674406844069440704407144072440734407444075440764407744078440794408044081440824408344084440854408644087440884408944090440914409244093440944409544096440974409844099441004410144102441034410444105441064410744108441094411044111441124411344114441154411644117441184411944120441214412244123441244412544126441274412844129441304413144132441334413444135441364413744138441394414044141441424414344144441454414644147441484414944150441514415244153441544415544156441574415844159441604416144162441634416444165441664416744168441694417044171441724417344174441754417644177441784417944180441814418244183441844418544186441874418844189441904419144192441934419444195441964419744198441994420044201442024420344204442054420644207442084420944210442114421244213442144421544216442174421844219442204422144222442234422444225442264422744228442294423044231442324423344234442354423644237442384423944240442414424244243442444424544246442474424844249442504425144252442534425444255442564425744258442594426044261442624426344264442654426644267442684426944270442714427244273442744427544276442774427844279442804428144282442834428444285442864428744288442894429044291442924429344294442954429644297442984429944300443014430244303443044430544306443074430844309443104431144312443134431444315443164431744318443194432044321443224432344324443254432644327443284432944330443314433244333443344433544336443374433844339443404434144342443434434444345443464434744348443494435044351443524435344354443554435644357443584435944360443614436244363443644436544366443674436844369443704437144372443734437444375443764437744378443794438044381443824438344384443854438644387443884438944390443914439244393443944439544396443974439844399444004440144402444034440444405444064440744408444094441044411444124441344414444154441644417444184441944420444214442244423444244442544426444274442844429444304443144432444334443444435444364443744438444394444044441444424444344444444454444644447444484444944450444514445244453444544445544456444574445844459444604446144462444634446444465444664446744468444694447044471444724447344474444754447644477444784447944480444814448244483444844448544486444874448844489444904449144492444934449444495444964449744498444994450044501445024450344504445054450644507445084450944510445114451244513445144451544516445174451844519445204452144522445234452444525445264452744528445294453044531445324453344534445354453644537445384453944540445414454244543445444454544546445474454844549445504455144552445534455444555445564455744558445594456044561445624456344564445654456644567445684456944570445714457244573445744457544576445774457844579445804458144582445834458444585445864458744588445894459044591445924459344594445954459644597445984459944600446014460244603446044460544606446074460844609446104461144612446134461444615446164461744618446194462044621446224462344624446254462644627446284462944630446314463244633446344463544636446374463844639446404464144642446434464444645446464464744648446494465044651446524465344654446554465644657446584465944660446614466244663446644466544666446674466844669446704467144672446734467444675446764467744678446794468044681446824468344684446854468644687446884468944690446914469244693446944469544696446974469844699447004470144702447034470444705447064470744708447094471044711447124471344714447154471644717447184471944720447214472244723447244472544726447274472844729447304473144732447334473444735447364473744738447394474044741447424474344744447454474644747447484474944750447514475244753447544475544756447574475844759447604476144762447634476444765447664476744768447694477044771447724477344774447754477644777447784477944780447814478244783447844478544786447874478844789447904479144792447934479444795447964479744798447994480044801448024480344804448054480644807448084480944810448114481244813448144481544816448174481844819448204482144822448234482444825448264482744828448294483044831448324483344834448354483644837448384483944840448414484244843448444484544846448474484844849448504485144852448534485444855448564485744858448594486044861448624486344864448654486644867448684486944870448714487244873448744487544876448774487844879448804488144882448834488444885448864488744888448894489044891448924489344894448954489644897448984489944900449014490244903449044490544906449074490844909449104491144912449134491444915449164491744918449194492044921449224492344924449254492644927449284492944930449314493244933449344493544936449374493844939449404494144942449434494444945449464494744948449494495044951449524495344954449554495644957449584495944960449614496244963449644496544966449674496844969449704497144972449734497444975449764497744978449794498044981449824498344984449854498644987449884498944990449914499244993449944499544996449974499844999450004500145002450034500445005450064500745008450094501045011450124501345014450154501645017450184501945020450214502245023450244502545026450274502845029450304503145032450334503445035450364503745038450394504045041450424504345044450454504645047450484504945050450514505245053450544505545056450574505845059450604506145062450634506445065450664506745068450694507045071450724507345074450754507645077450784507945080450814508245083450844508545086450874508845089450904509145092450934509445095450964509745098450994510045101451024510345104451054510645107451084510945110451114511245113451144511545116451174511845119451204512145122451234512445125451264512745128451294513045131451324513345134451354513645137451384513945140451414514245143451444514545146451474514845149451504515145152451534515445155451564515745158451594516045161451624516345164451654516645167451684516945170451714517245173451744517545176451774517845179451804518145182451834518445185451864518745188451894519045191451924519345194451954519645197451984519945200452014520245203452044520545206452074520845209452104521145212452134521445215452164521745218452194522045221452224522345224452254522645227452284522945230452314523245233452344523545236452374523845239452404524145242452434524445245452464524745248452494525045251452524525345254452554525645257452584525945260452614526245263452644526545266452674526845269452704527145272452734527445275452764527745278452794528045281452824528345284452854528645287452884528945290452914529245293452944529545296452974529845299453004530145302453034530445305453064530745308453094531045311453124531345314453154531645317453184531945320453214532245323453244532545326453274532845329453304533145332453334533445335453364533745338453394534045341453424534345344453454534645347453484534945350453514535245353453544535545356453574535845359453604536145362453634536445365453664536745368453694537045371453724537345374453754537645377453784537945380453814538245383453844538545386453874538845389453904539145392453934539445395453964539745398453994540045401454024540345404454054540645407454084540945410454114541245413454144541545416454174541845419454204542145422454234542445425454264542745428454294543045431454324543345434454354543645437454384543945440454414544245443454444544545446454474544845449454504545145452454534545445455454564545745458454594546045461454624546345464454654546645467454684546945470454714547245473454744547545476454774547845479454804548145482454834548445485454864548745488454894549045491454924549345494454954549645497454984549945500455014550245503455044550545506455074550845509455104551145512455134551445515455164551745518455194552045521455224552345524455254552645527455284552945530455314553245533455344553545536455374553845539455404554145542455434554445545455464554745548455494555045551455524555345554455554555645557455584555945560455614556245563455644556545566455674556845569455704557145572455734557445575455764557745578455794558045581455824558345584455854558645587455884558945590455914559245593455944559545596455974559845599456004560145602456034560445605456064560745608456094561045611456124561345614456154561645617456184561945620456214562245623456244562545626456274562845629456304563145632456334563445635456364563745638456394564045641456424564345644456454564645647456484564945650456514565245653456544565545656456574565845659456604566145662456634566445665456664566745668456694567045671456724567345674456754567645677456784567945680456814568245683456844568545686456874568845689456904569145692456934569445695456964569745698456994570045701457024570345704457054570645707457084570945710457114571245713457144571545716457174571845719457204572145722457234572445725457264572745728457294573045731457324573345734457354573645737457384573945740457414574245743457444574545746457474574845749457504575145752457534575445755457564575745758457594576045761457624576345764457654576645767457684576945770457714577245773457744577545776457774577845779457804578145782457834578445785457864578745788457894579045791457924579345794457954579645797457984579945800458014580245803458044580545806458074580845809458104581145812458134581445815458164581745818458194582045821458224582345824458254582645827458284582945830458314583245833458344583545836458374583845839458404584145842458434584445845458464584745848458494585045851458524585345854458554585645857458584585945860458614586245863458644586545866458674586845869458704587145872458734587445875458764587745878458794588045881458824588345884458854588645887458884588945890458914589245893458944589545896458974589845899459004590145902459034590445905459064590745908459094591045911459124591345914459154591645917459184591945920459214592245923459244592545926459274592845929459304593145932459334593445935459364593745938459394594045941459424594345944459454594645947459484594945950459514595245953459544595545956459574595845959459604596145962459634596445965459664596745968459694597045971459724597345974459754597645977459784597945980459814598245983459844598545986459874598845989459904599145992459934599445995459964599745998459994600046001460024600346004460054600646007460084600946010460114601246013460144601546016460174601846019460204602146022460234602446025460264602746028460294603046031460324603346034460354603646037460384603946040460414604246043460444604546046460474604846049460504605146052460534605446055460564605746058460594606046061460624606346064460654606646067460684606946070460714607246073460744607546076460774607846079460804608146082460834608446085460864608746088460894609046091460924609346094460954609646097460984609946100461014610246103461044610546106461074610846109461104611146112461134611446115461164611746118461194612046121461224612346124461254612646127461284612946130461314613246133461344613546136461374613846139461404614146142461434614446145461464614746148461494615046151461524615346154461554615646157461584615946160461614616246163461644616546166461674616846169461704617146172461734617446175461764617746178461794618046181461824618346184461854618646187461884618946190461914619246193461944619546196461974619846199462004620146202462034620446205462064620746208462094621046211462124621346214462154621646217462184621946220462214622246223462244622546226462274622846229462304623146232462334623446235462364623746238462394624046241462424624346244462454624646247462484624946250462514625246253462544625546256462574625846259462604626146262462634626446265462664626746268462694627046271462724627346274462754627646277462784627946280462814628246283462844628546286462874628846289462904629146292462934629446295462964629746298462994630046301463024630346304463054630646307463084630946310463114631246313463144631546316463174631846319463204632146322463234632446325463264632746328463294633046331463324633346334463354633646337463384633946340463414634246343463444634546346463474634846349463504635146352463534635446355463564635746358463594636046361463624636346364463654636646367463684636946370463714637246373463744637546376463774637846379463804638146382463834638446385463864638746388463894639046391463924639346394463954639646397463984639946400464014640246403464044640546406464074640846409464104641146412464134641446415464164641746418464194642046421464224642346424464254642646427464284642946430464314643246433464344643546436464374643846439464404644146442464434644446445464464644746448464494645046451464524645346454464554645646457464584645946460464614646246463464644646546466464674646846469464704647146472464734647446475464764647746478464794648046481464824648346484464854648646487464884648946490464914649246493464944649546496464974649846499465004650146502465034650446505465064650746508465094651046511465124651346514465154651646517465184651946520465214652246523465244652546526465274652846529465304653146532465334653446535465364653746538465394654046541465424654346544465454654646547465484654946550465514655246553465544655546556465574655846559465604656146562465634656446565465664656746568465694657046571465724657346574465754657646577465784657946580465814658246583465844658546586465874658846589465904659146592465934659446595465964659746598465994660046601466024660346604466054660646607466084660946610466114661246613466144661546616466174661846619466204662146622466234662446625466264662746628466294663046631466324663346634466354663646637466384663946640466414664246643466444664546646466474664846649466504665146652466534665446655466564665746658466594666046661466624666346664466654666646667466684666946670466714667246673466744667546676466774667846679466804668146682466834668446685466864668746688466894669046691466924669346694466954669646697466984669946700467014670246703467044670546706467074670846709467104671146712467134671446715467164671746718467194672046721467224672346724467254672646727467284672946730467314673246733467344673546736467374673846739467404674146742467434674446745467464674746748467494675046751467524675346754467554675646757467584675946760467614676246763467644676546766467674676846769467704677146772467734677446775467764677746778467794678046781467824678346784467854678646787467884678946790467914679246793467944679546796467974679846799468004680146802468034680446805468064680746808468094681046811468124681346814468154681646817468184681946820468214682246823468244682546826468274682846829468304683146832468334683446835468364683746838468394684046841468424684346844468454684646847468484684946850468514685246853468544685546856468574685846859468604686146862468634686446865468664686746868468694687046871468724687346874468754687646877468784687946880468814688246883468844688546886468874688846889468904689146892468934689446895468964689746898468994690046901469024690346904469054690646907469084690946910469114691246913469144691546916469174691846919469204692146922469234692446925469264692746928469294693046931469324693346934469354693646937469384693946940469414694246943469444694546946469474694846949469504695146952469534695446955469564695746958469594696046961469624696346964469654696646967469684696946970469714697246973469744697546976469774697846979469804698146982469834698446985469864698746988469894699046991469924699346994469954699646997469984699947000470014700247003470044700547006470074700847009470104701147012470134701447015470164701747018470194702047021470224702347024470254702647027470284702947030470314703247033470344703547036470374703847039470404704147042470434704447045470464704747048470494705047051470524705347054470554705647057470584705947060470614706247063470644706547066470674706847069470704707147072470734707447075470764707747078470794708047081470824708347084470854708647087470884708947090470914709247093470944709547096470974709847099471004710147102471034710447105471064710747108471094711047111471124711347114471154711647117471184711947120471214712247123471244712547126471274712847129471304713147132471334713447135471364713747138471394714047141471424714347144471454714647147471484714947150471514715247153471544715547156471574715847159471604716147162471634716447165471664716747168471694717047171471724717347174471754717647177471784717947180471814718247183471844718547186471874718847189471904719147192471934719447195471964719747198471994720047201472024720347204472054720647207472084720947210472114721247213472144721547216472174721847219472204722147222472234722447225472264722747228472294723047231472324723347234472354723647237472384723947240472414724247243472444724547246472474724847249472504725147252472534725447255472564725747258472594726047261472624726347264472654726647267472684726947270472714727247273472744727547276472774727847279472804728147282472834728447285472864728747288472894729047291472924729347294472954729647297472984729947300473014730247303473044730547306473074730847309473104731147312473134731447315473164731747318473194732047321473224732347324473254732647327473284732947330473314733247333473344733547336473374733847339473404734147342473434734447345473464734747348473494735047351473524735347354473554735647357473584735947360473614736247363473644736547366473674736847369473704737147372473734737447375473764737747378473794738047381473824738347384473854738647387473884738947390473914739247393473944739547396473974739847399474004740147402474034740447405474064740747408474094741047411474124741347414474154741647417474184741947420474214742247423474244742547426474274742847429474304743147432474334743447435474364743747438474394744047441474424744347444474454744647447474484744947450474514745247453474544745547456474574745847459474604746147462474634746447465474664746747468474694747047471474724747347474474754747647477474784747947480474814748247483474844748547486474874748847489474904749147492474934749447495474964749747498474994750047501475024750347504475054750647507475084750947510475114751247513475144751547516475174751847519475204752147522475234752447525475264752747528475294753047531475324753347534475354753647537475384753947540475414754247543475444754547546475474754847549475504755147552475534755447555475564755747558475594756047561475624756347564475654756647567475684756947570475714757247573475744757547576475774757847579475804758147582475834758447585475864758747588475894759047591475924759347594475954759647597475984759947600476014760247603476044760547606476074760847609476104761147612476134761447615476164761747618476194762047621476224762347624476254762647627476284762947630476314763247633476344763547636476374763847639476404764147642476434764447645476464764747648476494765047651476524765347654476554765647657476584765947660476614766247663476644766547666476674766847669476704767147672476734767447675476764767747678476794768047681476824768347684476854768647687476884768947690476914769247693476944769547696476974769847699477004770147702477034770447705477064770747708477094771047711477124771347714477154771647717477184771947720477214772247723477244772547726477274772847729477304773147732477334773447735477364773747738477394774047741477424774347744477454774647747477484774947750477514775247753477544775547756477574775847759477604776147762477634776447765477664776747768477694777047771477724777347774477754777647777477784777947780477814778247783477844778547786477874778847789477904779147792477934779447795477964779747798477994780047801478024780347804478054780647807478084780947810478114781247813478144781547816478174781847819478204782147822478234782447825478264782747828478294783047831478324783347834478354783647837478384783947840478414784247843478444784547846478474784847849478504785147852478534785447855478564785747858478594786047861478624786347864478654786647867478684786947870478714787247873478744787547876478774787847879478804788147882478834788447885478864788747888478894789047891478924789347894478954789647897478984789947900479014790247903479044790547906479074790847909479104791147912479134791447915479164791747918479194792047921479224792347924479254792647927479284792947930479314793247933479344793547936479374793847939479404794147942479434794447945479464794747948479494795047951479524795347954479554795647957479584795947960479614796247963479644796547966479674796847969479704797147972479734797447975479764797747978479794798047981479824798347984479854798647987479884798947990479914799247993479944799547996479974799847999480004800148002480034800448005480064800748008480094801048011480124801348014480154801648017480184801948020480214802248023480244802548026480274802848029480304803148032480334803448035480364803748038480394804048041480424804348044480454804648047480484804948050480514805248053480544805548056480574805848059480604806148062480634806448065480664806748068480694807048071480724807348074480754807648077480784807948080480814808248083480844808548086480874808848089480904809148092480934809448095480964809748098480994810048101481024810348104481054810648107481084810948110481114811248113481144811548116481174811848119481204812148122481234812448125481264812748128481294813048131481324813348134481354813648137481384813948140481414814248143481444814548146481474814848149481504815148152481534815448155481564815748158481594816048161481624816348164481654816648167481684816948170481714817248173481744817548176481774817848179481804818148182481834818448185481864818748188481894819048191481924819348194481954819648197481984819948200482014820248203482044820548206482074820848209482104821148212482134821448215482164821748218482194822048221482224822348224482254822648227482284822948230482314823248233482344823548236482374823848239482404824148242482434824448245482464824748248482494825048251482524825348254482554825648257482584825948260482614826248263482644826548266482674826848269482704827148272482734827448275482764827748278482794828048281482824828348284482854828648287482884828948290482914829248293482944829548296482974829848299483004830148302483034830448305483064830748308483094831048311483124831348314483154831648317483184831948320483214832248323483244832548326483274832848329483304833148332483334833448335483364833748338483394834048341483424834348344483454834648347483484834948350483514835248353483544835548356483574835848359483604836148362483634836448365483664836748368483694837048371483724837348374483754837648377483784837948380483814838248383483844838548386483874838848389483904839148392483934839448395483964839748398483994840048401484024840348404484054840648407484084840948410484114841248413484144841548416484174841848419484204842148422484234842448425484264842748428484294843048431484324843348434484354843648437484384843948440484414844248443484444844548446484474844848449484504845148452484534845448455484564845748458484594846048461484624846348464484654846648467484684846948470484714847248473484744847548476484774847848479484804848148482484834848448485484864848748488484894849048491484924849348494484954849648497484984849948500485014850248503485044850548506485074850848509485104851148512485134851448515485164851748518485194852048521485224852348524485254852648527485284852948530485314853248533485344853548536485374853848539485404854148542485434854448545485464854748548485494855048551485524855348554485554855648557485584855948560485614856248563485644856548566485674856848569485704857148572485734857448575485764857748578485794858048581485824858348584485854858648587485884858948590485914859248593485944859548596485974859848599486004860148602486034860448605486064860748608486094861048611486124861348614486154861648617486184861948620486214862248623486244862548626486274862848629486304863148632486334863448635486364863748638486394864048641486424864348644486454864648647486484864948650486514865248653486544865548656486574865848659486604866148662486634866448665486664866748668486694867048671486724867348674486754867648677486784867948680486814868248683486844868548686486874868848689486904869148692486934869448695486964869748698486994870048701487024870348704487054870648707487084870948710487114871248713487144871548716487174871848719487204872148722487234872448725487264872748728487294873048731487324873348734487354873648737487384873948740487414874248743487444874548746487474874848749487504875148752487534875448755487564875748758487594876048761487624876348764487654876648767487684876948770487714877248773487744877548776487774877848779487804878148782487834878448785487864878748788487894879048791487924879348794487954879648797487984879948800488014880248803488044880548806488074880848809488104881148812488134881448815488164881748818488194882048821488224882348824488254882648827488284882948830488314883248833488344883548836488374883848839488404884148842488434884448845488464884748848488494885048851488524885348854488554885648857488584885948860488614886248863488644886548866488674886848869488704887148872488734887448875488764887748878488794888048881488824888348884488854888648887488884888948890488914889248893488944889548896488974889848899489004890148902489034890448905489064890748908489094891048911489124891348914489154891648917489184891948920489214892248923489244892548926489274892848929489304893148932489334893448935489364893748938489394894048941489424894348944489454894648947489484894948950489514895248953489544895548956489574895848959489604896148962489634896448965489664896748968489694897048971489724897348974489754897648977489784897948980489814898248983489844898548986489874898848989489904899148992489934899448995489964899748998489994900049001490024900349004490054900649007490084900949010490114901249013490144901549016490174901849019490204902149022490234902449025490264902749028490294903049031490324903349034490354903649037490384903949040490414904249043490444904549046490474904849049490504905149052490534905449055490564905749058490594906049061490624906349064490654906649067490684906949070490714907249073490744907549076490774907849079490804908149082490834908449085490864908749088490894909049091490924909349094490954909649097490984909949100491014910249103491044910549106491074910849109491104911149112491134911449115491164911749118491194912049121491224912349124491254912649127491284912949130491314913249133491344913549136491374913849139491404914149142491434914449145491464914749148491494915049151491524915349154491554915649157491584915949160491614916249163491644916549166491674916849169491704917149172491734917449175491764917749178491794918049181491824918349184491854918649187491884918949190491914919249193491944919549196491974919849199492004920149202492034920449205492064920749208492094921049211492124921349214492154921649217492184921949220492214922249223492244922549226492274922849229492304923149232492334923449235492364923749238492394924049241492424924349244492454924649247492484924949250492514925249253492544925549256492574925849259492604926149262492634926449265492664926749268492694927049271492724927349274492754927649277492784927949280492814928249283492844928549286492874928849289492904929149292492934929449295492964929749298492994930049301493024930349304493054930649307493084930949310493114931249313493144931549316493174931849319493204932149322493234932449325493264932749328493294933049331493324933349334493354933649337493384933949340493414934249343493444934549346493474934849349493504935149352493534935449355493564935749358493594936049361493624936349364493654936649367493684936949370493714937249373493744937549376493774937849379493804938149382493834938449385493864938749388493894939049391493924939349394493954939649397493984939949400494014940249403494044940549406494074940849409494104941149412494134941449415494164941749418494194942049421494224942349424494254942649427494284942949430494314943249433494344943549436494374943849439494404944149442494434944449445494464944749448494494945049451494524945349454494554945649457494584945949460494614946249463494644946549466494674946849469494704947149472494734947449475494764947749478494794948049481494824948349484494854948649487494884948949490494914949249493494944949549496494974949849499495004950149502495034950449505495064950749508495094951049511495124951349514495154951649517495184951949520495214952249523495244952549526495274952849529495304953149532495334953449535495364953749538495394954049541495424954349544495454954649547495484954949550495514955249553495544955549556495574955849559495604956149562495634956449565495664956749568495694957049571495724957349574495754957649577495784957949580495814958249583495844958549586495874958849589495904959149592495934959449595495964959749598495994960049601496024960349604496054960649607496084960949610496114961249613496144961549616496174961849619496204962149622496234962449625496264962749628496294963049631496324963349634496354963649637496384963949640496414964249643496444964549646496474964849649496504965149652496534965449655496564965749658496594966049661496624966349664496654966649667496684966949670496714967249673496744967549676496774967849679496804968149682496834968449685496864968749688496894969049691496924969349694496954969649697496984969949700497014970249703497044970549706497074970849709497104971149712497134971449715497164971749718497194972049721497224972349724497254972649727497284972949730497314973249733497344973549736497374973849739497404974149742497434974449745497464974749748497494975049751497524975349754497554975649757497584975949760497614976249763497644976549766497674976849769497704977149772497734977449775497764977749778497794978049781497824978349784497854978649787497884978949790497914979249793497944979549796497974979849799498004980149802498034980449805498064980749808498094981049811498124981349814498154981649817498184981949820498214982249823498244982549826498274982849829498304983149832498334983449835498364983749838498394984049841498424984349844498454984649847498484984949850498514985249853498544985549856498574985849859498604986149862498634986449865498664986749868498694987049871498724987349874498754987649877498784987949880498814988249883498844988549886498874988849889498904989149892498934989449895498964989749898498994990049901499024990349904499054990649907499084990949910499114991249913499144991549916499174991849919499204992149922499234992449925499264992749928499294993049931499324993349934499354993649937499384993949940499414994249943499444994549946499474994849949499504995149952499534995449955499564995749958499594996049961499624996349964499654996649967499684996949970499714997249973499744997549976499774997849979499804998149982499834998449985499864998749988499894999049991499924999349994499954999649997499984999950000500015000250003500045000550006500075000850009500105001150012500135001450015500165001750018500195002050021500225002350024500255002650027500285002950030500315003250033500345003550036500375003850039500405004150042500435004450045500465004750048500495005050051500525005350054500555005650057500585005950060500615006250063500645006550066500675006850069500705007150072500735007450075500765007750078500795008050081500825008350084500855008650087500885008950090500915009250093500945009550096500975009850099501005010150102501035010450105501065010750108501095011050111501125011350114501155011650117501185011950120501215012250123501245012550126501275012850129501305013150132501335013450135501365013750138501395014050141501425014350144501455014650147501485014950150501515015250153501545015550156501575015850159501605016150162501635016450165501665016750168501695017050171501725017350174501755017650177501785017950180501815018250183501845018550186501875018850189501905019150192501935019450195501965019750198501995020050201502025020350204502055020650207502085020950210502115021250213502145021550216502175021850219502205022150222502235022450225502265022750228502295023050231502325023350234502355023650237502385023950240502415024250243502445024550246502475024850249502505025150252502535025450255502565025750258502595026050261502625026350264502655026650267502685026950270502715027250273502745027550276502775027850279502805028150282502835028450285502865028750288502895029050291502925029350294502955029650297502985029950300503015030250303503045030550306503075030850309503105031150312503135031450315503165031750318503195032050321503225032350324503255032650327503285032950330503315033250333503345033550336503375033850339503405034150342503435034450345503465034750348503495035050351503525035350354503555035650357503585035950360503615036250363503645036550366503675036850369503705037150372503735037450375503765037750378503795038050381503825038350384503855038650387503885038950390503915039250393503945039550396503975039850399504005040150402504035040450405504065040750408504095041050411504125041350414504155041650417504185041950420504215042250423504245042550426504275042850429504305043150432504335043450435504365043750438504395044050441504425044350444504455044650447504485044950450504515045250453504545045550456504575045850459504605046150462504635046450465504665046750468504695047050471504725047350474504755047650477504785047950480504815048250483504845048550486504875048850489504905049150492504935049450495504965049750498504995050050501505025050350504505055050650507505085050950510505115051250513505145051550516505175051850519505205052150522505235052450525505265052750528505295053050531505325053350534505355053650537505385053950540505415054250543505445054550546505475054850549505505055150552505535055450555505565055750558505595056050561505625056350564505655056650567505685056950570505715057250573505745057550576505775057850579505805058150582505835058450585505865058750588505895059050591505925059350594505955059650597505985059950600506015060250603506045060550606506075060850609506105061150612506135061450615506165061750618506195062050621506225062350624506255062650627506285062950630506315063250633506345063550636506375063850639506405064150642506435064450645506465064750648506495065050651506525065350654506555065650657506585065950660506615066250663506645066550666506675066850669506705067150672506735067450675506765067750678506795068050681506825068350684506855068650687506885068950690506915069250693506945069550696506975069850699507005070150702507035070450705507065070750708507095071050711507125071350714507155071650717507185071950720507215072250723507245072550726507275072850729507305073150732507335073450735507365073750738507395074050741507425074350744507455074650747507485074950750507515075250753507545075550756507575075850759507605076150762507635076450765507665076750768507695077050771507725077350774507755077650777507785077950780507815078250783507845078550786507875078850789507905079150792507935079450795507965079750798507995080050801508025080350804508055080650807508085080950810508115081250813508145081550816508175081850819508205082150822508235082450825508265082750828508295083050831508325083350834508355083650837508385083950840508415084250843508445084550846508475084850849508505085150852508535085450855508565085750858508595086050861508625086350864508655086650867508685086950870508715087250873508745087550876508775087850879508805088150882508835088450885508865088750888508895089050891508925089350894508955089650897508985089950900509015090250903509045090550906509075090850909509105091150912509135091450915509165091750918509195092050921509225092350924509255092650927509285092950930509315093250933509345093550936509375093850939509405094150942509435094450945509465094750948509495095050951509525095350954509555095650957509585095950960509615096250963509645096550966509675096850969509705097150972509735097450975509765097750978509795098050981509825098350984509855098650987509885098950990509915099250993509945099550996509975099850999510005100151002510035100451005510065100751008510095101051011510125101351014510155101651017510185101951020510215102251023510245102551026510275102851029510305103151032510335103451035510365103751038510395104051041510425104351044510455104651047510485104951050510515105251053510545105551056510575105851059510605106151062510635106451065510665106751068510695107051071510725107351074510755107651077510785107951080510815108251083510845108551086510875108851089510905109151092510935109451095510965109751098510995110051101511025110351104511055110651107511085110951110511115111251113511145111551116511175111851119511205112151122511235112451125511265112751128511295113051131511325113351134511355113651137511385113951140511415114251143511445114551146511475114851149511505115151152511535115451155511565115751158511595116051161511625116351164511655116651167511685116951170511715117251173511745117551176511775117851179511805118151182511835118451185511865118751188511895119051191511925119351194511955119651197511985119951200512015120251203512045120551206512075120851209512105121151212512135121451215512165121751218512195122051221512225122351224512255122651227512285122951230512315123251233512345123551236512375123851239512405124151242512435124451245512465124751248512495125051251512525125351254512555125651257512585125951260512615126251263512645126551266512675126851269512705127151272512735127451275512765127751278512795128051281512825128351284512855128651287512885128951290512915129251293512945129551296512975129851299513005130151302513035130451305513065130751308513095131051311513125131351314513155131651317513185131951320513215132251323513245132551326513275132851329513305133151332513335133451335513365133751338513395134051341513425134351344513455134651347513485134951350513515135251353513545135551356513575135851359513605136151362513635136451365513665136751368513695137051371513725137351374513755137651377513785137951380513815138251383513845138551386513875138851389513905139151392513935139451395513965139751398513995140051401514025140351404514055140651407514085140951410514115141251413514145141551416514175141851419514205142151422514235142451425514265142751428514295143051431514325143351434514355143651437514385143951440514415144251443514445144551446514475144851449514505145151452514535145451455514565145751458514595146051461514625146351464514655146651467514685146951470514715147251473514745147551476514775147851479514805148151482514835148451485514865148751488514895149051491514925149351494514955149651497514985149951500515015150251503515045150551506515075150851509515105151151512515135151451515515165151751518515195152051521515225152351524515255152651527515285152951530515315153251533515345153551536515375153851539515405154151542515435154451545515465154751548515495155051551515525155351554515555155651557515585155951560515615156251563515645156551566515675156851569515705157151572515735157451575515765157751578515795158051581515825158351584515855158651587515885158951590515915159251593515945159551596515975159851599516005160151602516035160451605516065160751608516095161051611516125161351614516155161651617516185161951620516215162251623516245162551626516275162851629516305163151632516335163451635516365163751638516395164051641516425164351644516455164651647516485164951650516515165251653516545165551656516575165851659516605166151662516635166451665516665166751668516695167051671516725167351674516755167651677516785167951680516815168251683516845168551686516875168851689516905169151692516935169451695516965169751698516995170051701517025170351704517055170651707517085170951710517115171251713517145171551716517175171851719517205172151722517235172451725517265172751728517295173051731517325173351734517355173651737517385173951740517415174251743517445174551746517475174851749517505175151752517535175451755517565175751758517595176051761517625176351764517655176651767517685176951770517715177251773517745177551776517775177851779517805178151782517835178451785517865178751788517895179051791517925179351794517955179651797517985179951800518015180251803518045180551806518075180851809518105181151812518135181451815518165181751818518195182051821518225182351824518255182651827518285182951830518315183251833518345183551836518375183851839518405184151842518435184451845518465184751848518495185051851518525185351854518555185651857518585185951860518615186251863518645186551866518675186851869518705187151872518735187451875518765187751878518795188051881518825188351884518855188651887518885188951890518915189251893518945189551896518975189851899519005190151902519035190451905519065190751908519095191051911519125191351914519155191651917519185191951920519215192251923519245192551926519275192851929519305193151932519335193451935519365193751938519395194051941519425194351944519455194651947519485194951950519515195251953519545195551956519575195851959519605196151962519635196451965519665196751968519695197051971519725197351974519755197651977519785197951980519815198251983519845198551986519875198851989519905199151992519935199451995519965199751998519995200052001520025200352004520055200652007520085200952010520115201252013520145201552016520175201852019520205202152022520235202452025520265202752028520295203052031520325203352034520355203652037520385203952040520415204252043520445204552046520475204852049520505205152052520535205452055520565205752058520595206052061520625206352064520655206652067520685206952070520715207252073520745207552076520775207852079520805208152082520835208452085520865208752088520895209052091520925209352094520955209652097520985209952100521015210252103521045210552106521075210852109521105211152112521135211452115521165211752118521195212052121521225212352124521255212652127521285212952130521315213252133521345213552136521375213852139521405214152142521435214452145521465214752148521495215052151521525215352154521555215652157521585215952160521615216252163521645216552166521675216852169521705217152172521735217452175521765217752178521795218052181521825218352184521855218652187521885218952190521915219252193521945219552196521975219852199522005220152202522035220452205522065220752208522095221052211522125221352214522155221652217522185221952220522215222252223522245222552226522275222852229522305223152232522335223452235522365223752238522395224052241522425224352244522455224652247522485224952250522515225252253522545225552256522575225852259522605226152262522635226452265522665226752268522695227052271522725227352274522755227652277522785227952280522815228252283522845228552286522875228852289522905229152292522935229452295522965229752298522995230052301523025230352304523055230652307523085230952310523115231252313523145231552316523175231852319523205232152322523235232452325523265232752328523295233052331523325233352334523355233652337523385233952340523415234252343523445234552346523475234852349523505235152352523535235452355523565235752358523595236052361523625236352364523655236652367523685236952370523715237252373523745237552376523775237852379523805238152382523835238452385523865238752388523895239052391523925239352394523955239652397523985239952400524015240252403524045240552406524075240852409524105241152412524135241452415524165241752418524195242052421524225242352424524255242652427524285242952430524315243252433524345243552436524375243852439524405244152442524435244452445524465244752448524495245052451524525245352454524555245652457524585245952460524615246252463524645246552466524675246852469524705247152472524735247452475524765247752478524795248052481524825248352484524855248652487524885248952490524915249252493524945249552496524975249852499525005250152502525035250452505525065250752508525095251052511525125251352514525155251652517525185251952520525215252252523525245252552526525275252852529525305253152532525335253452535525365253752538525395254052541525425254352544525455254652547525485254952550525515255252553525545255552556525575255852559525605256152562525635256452565525665256752568525695257052571525725257352574525755257652577525785257952580525815258252583525845258552586525875258852589525905259152592525935259452595525965259752598525995260052601526025260352604526055260652607526085260952610526115261252613526145261552616526175261852619526205262152622526235262452625526265262752628526295263052631526325263352634526355263652637526385263952640526415264252643526445264552646526475264852649526505265152652526535265452655526565265752658526595266052661526625266352664526655266652667526685266952670526715267252673526745267552676526775267852679526805268152682526835268452685526865268752688526895269052691526925269352694526955269652697526985269952700527015270252703527045270552706527075270852709527105271152712527135271452715527165271752718527195272052721527225272352724527255272652727527285272952730527315273252733527345273552736527375273852739527405274152742527435274452745527465274752748527495275052751527525275352754527555275652757527585275952760527615276252763527645276552766527675276852769527705277152772527735277452775527765277752778527795278052781527825278352784527855278652787527885278952790527915279252793527945279552796527975279852799528005280152802528035280452805528065280752808528095281052811528125281352814528155281652817528185281952820528215282252823528245282552826528275282852829528305283152832528335283452835528365283752838528395284052841528425284352844528455284652847528485284952850528515285252853528545285552856528575285852859528605286152862528635286452865528665286752868528695287052871528725287352874528755287652877528785287952880528815288252883528845288552886528875288852889528905289152892528935289452895528965289752898528995290052901529025290352904529055290652907529085290952910529115291252913529145291552916529175291852919529205292152922529235292452925529265292752928529295293052931529325293352934529355293652937529385293952940529415294252943529445294552946529475294852949529505295152952529535295452955529565295752958529595296052961529625296352964529655296652967529685296952970529715297252973529745297552976529775297852979529805298152982529835298452985529865298752988529895299052991529925299352994529955299652997529985299953000530015300253003530045300553006530075300853009530105301153012530135301453015530165301753018530195302053021530225302353024530255302653027530285302953030530315303253033530345303553036530375303853039530405304153042530435304453045530465304753048530495305053051530525305353054530555305653057530585305953060530615306253063530645306553066530675306853069530705307153072530735307453075530765307753078530795308053081530825308353084530855308653087530885308953090530915309253093530945309553096530975309853099531005310153102531035310453105531065310753108531095311053111531125311353114531155311653117531185311953120531215312253123531245312553126531275312853129531305313153132531335313453135531365313753138531395314053141531425314353144531455314653147531485314953150531515315253153531545315553156531575315853159531605316153162531635316453165531665316753168531695317053171531725317353174531755317653177531785317953180531815318253183531845318553186531875318853189531905319153192531935319453195531965319753198531995320053201532025320353204532055320653207532085320953210532115321253213532145321553216532175321853219532205322153222532235322453225532265322753228532295323053231532325323353234532355323653237532385323953240532415324253243532445324553246532475324853249532505325153252532535325453255532565325753258532595326053261532625326353264532655326653267532685326953270532715327253273532745327553276532775327853279532805328153282532835328453285532865328753288532895329053291532925329353294532955329653297532985329953300533015330253303533045330553306533075330853309533105331153312533135331453315533165331753318533195332053321533225332353324533255332653327533285332953330533315333253333533345333553336533375333853339533405334153342533435334453345533465334753348533495335053351533525335353354533555335653357533585335953360533615336253363533645336553366533675336853369533705337153372533735337453375533765337753378533795338053381533825338353384533855338653387533885338953390533915339253393533945339553396533975339853399534005340153402534035340453405534065340753408534095341053411534125341353414534155341653417534185341953420534215342253423534245342553426534275342853429534305343153432534335343453435534365343753438534395344053441534425344353444534455344653447534485344953450534515345253453534545345553456534575345853459534605346153462534635346453465534665346753468534695347053471534725347353474534755347653477534785347953480534815348253483534845348553486534875348853489534905349153492534935349453495534965349753498534995350053501535025350353504535055350653507535085350953510535115351253513535145351553516535175351853519535205352153522535235352453525535265352753528535295353053531535325353353534535355353653537535385353953540535415354253543535445354553546535475354853549535505355153552535535355453555535565355753558535595356053561535625356353564535655356653567535685356953570535715357253573535745357553576535775357853579535805358153582535835358453585535865358753588535895359053591535925359353594535955359653597535985359953600536015360253603536045360553606536075360853609536105361153612536135361453615536165361753618536195362053621536225362353624536255362653627536285362953630536315363253633536345363553636536375363853639536405364153642536435364453645536465364753648536495365053651536525365353654536555365653657536585365953660536615366253663536645366553666536675366853669536705367153672536735367453675536765367753678536795368053681536825368353684536855368653687536885368953690536915369253693536945369553696536975369853699537005370153702537035370453705537065370753708537095371053711537125371353714537155371653717537185371953720537215372253723537245372553726537275372853729537305373153732537335373453735537365373753738537395374053741537425374353744537455374653747537485374953750537515375253753537545375553756537575375853759537605376153762537635376453765537665376753768537695377053771537725377353774537755377653777537785377953780537815378253783537845378553786537875378853789537905379153792537935379453795537965379753798537995380053801538025380353804538055380653807538085380953810538115381253813538145381553816538175381853819538205382153822538235382453825538265382753828538295383053831538325383353834538355383653837538385383953840538415384253843538445384553846538475384853849538505385153852538535385453855538565385753858538595386053861538625386353864538655386653867538685386953870538715387253873538745387553876538775387853879538805388153882538835388453885538865388753888538895389053891538925389353894538955389653897538985389953900539015390253903539045390553906539075390853909539105391153912539135391453915539165391753918539195392053921539225392353924539255392653927539285392953930539315393253933539345393553936539375393853939539405394153942539435394453945539465394753948539495395053951539525395353954539555395653957539585395953960539615396253963539645396553966539675396853969539705397153972539735397453975539765397753978539795398053981539825398353984539855398653987539885398953990539915399253993539945399553996539975399853999540005400154002540035400454005540065400754008540095401054011540125401354014540155401654017540185401954020540215402254023540245402554026540275402854029540305403154032540335403454035540365403754038540395404054041540425404354044540455404654047540485404954050540515405254053540545405554056540575405854059540605406154062540635406454065540665406754068540695407054071540725407354074540755407654077540785407954080540815408254083540845408554086540875408854089540905409154092540935409454095540965409754098540995410054101541025410354104541055410654107541085410954110541115411254113541145411554116541175411854119541205412154122541235412454125541265412754128541295413054131541325413354134541355413654137541385413954140541415414254143541445414554146541475414854149541505415154152541535415454155541565415754158541595416054161541625416354164541655416654167541685416954170541715417254173541745417554176541775417854179541805418154182541835418454185541865418754188541895419054191541925419354194541955419654197541985419954200542015420254203542045420554206542075420854209542105421154212542135421454215542165421754218542195422054221542225422354224542255422654227542285422954230542315423254233542345423554236542375423854239542405424154242542435424454245542465424754248542495425054251542525425354254542555425654257542585425954260542615426254263542645426554266542675426854269542705427154272542735427454275542765427754278542795428054281542825428354284542855428654287542885428954290542915429254293542945429554296542975429854299543005430154302543035430454305543065430754308543095431054311543125431354314543155431654317543185431954320543215432254323543245432554326543275432854329543305433154332543335433454335543365433754338543395434054341543425434354344543455434654347543485434954350543515435254353543545435554356543575435854359543605436154362543635436454365543665436754368543695437054371543725437354374543755437654377543785437954380543815438254383543845438554386543875438854389543905439154392543935439454395543965439754398543995440054401544025440354404544055440654407544085440954410544115441254413544145441554416544175441854419544205442154422544235442454425544265442754428544295443054431544325443354434544355443654437544385443954440544415444254443544445444554446544475444854449544505445154452544535445454455544565445754458544595446054461544625446354464544655446654467544685446954470544715447254473544745447554476544775447854479544805448154482544835448454485544865448754488544895449054491544925449354494544955449654497544985449954500545015450254503545045450554506545075450854509545105451154512545135451454515545165451754518545195452054521545225452354524545255452654527545285452954530545315453254533545345453554536545375453854539545405454154542545435454454545545465454754548545495455054551545525455354554545555455654557545585455954560545615456254563545645456554566545675456854569545705457154572545735457454575545765457754578545795458054581545825458354584545855458654587545885458954590545915459254593545945459554596545975459854599546005460154602546035460454605546065460754608546095461054611546125461354614546155461654617546185461954620546215462254623546245462554626546275462854629546305463154632546335463454635546365463754638546395464054641546425464354644546455464654647546485464954650546515465254653546545465554656546575465854659546605466154662546635466454665546665466754668546695467054671546725467354674546755467654677546785467954680546815468254683546845468554686546875468854689546905469154692546935469454695546965469754698546995470054701547025470354704547055470654707547085470954710547115471254713547145471554716547175471854719547205472154722547235472454725547265472754728547295473054731547325473354734547355473654737547385473954740547415474254743547445474554746547475474854749547505475154752547535475454755547565475754758547595476054761547625476354764547655476654767547685476954770547715477254773547745477554776547775477854779547805478154782547835478454785547865478754788547895479054791547925479354794547955479654797547985479954800548015480254803548045480554806548075480854809548105481154812548135481454815548165481754818548195482054821548225482354824548255482654827548285482954830548315483254833548345483554836548375483854839548405484154842548435484454845548465484754848548495485054851548525485354854548555485654857548585485954860548615486254863548645486554866548675486854869548705487154872548735487454875548765487754878548795488054881548825488354884548855488654887548885488954890548915489254893548945489554896548975489854899549005490154902549035490454905549065490754908549095491054911549125491354914549155491654917549185491954920549215492254923549245492554926549275492854929549305493154932549335493454935549365493754938549395494054941549425494354944549455494654947549485494954950549515495254953549545495554956549575495854959549605496154962549635496454965549665496754968549695497054971549725497354974549755497654977549785497954980549815498254983549845498554986549875498854989549905499154992549935499454995549965499754998549995500055001550025500355004550055500655007550085500955010550115501255013550145501555016550175501855019550205502155022550235502455025550265502755028550295503055031550325503355034550355503655037550385503955040550415504255043550445504555046550475504855049550505505155052550535505455055550565505755058550595506055061550625506355064550655506655067550685506955070550715507255073550745507555076550775507855079550805508155082550835508455085550865508755088550895509055091550925509355094550955509655097550985509955100551015510255103551045510555106551075510855109551105511155112551135511455115551165511755118551195512055121551225512355124551255512655127551285512955130551315513255133551345513555136551375513855139551405514155142551435514455145551465514755148551495515055151551525515355154551555515655157551585515955160551615516255163551645516555166551675516855169551705517155172551735517455175551765517755178551795518055181551825518355184551855518655187551885518955190551915519255193551945519555196551975519855199552005520155202552035520455205552065520755208552095521055211552125521355214552155521655217552185521955220552215522255223552245522555226552275522855229552305523155232552335523455235552365523755238552395524055241552425524355244552455524655247552485524955250552515525255253552545525555256552575525855259552605526155262552635526455265552665526755268552695527055271552725527355274552755527655277552785527955280552815528255283552845528555286552875528855289552905529155292552935529455295552965529755298552995530055301553025530355304553055530655307553085530955310553115531255313553145531555316553175531855319553205532155322553235532455325553265532755328553295533055331553325533355334553355533655337553385533955340553415534255343553445534555346553475534855349553505535155352553535535455355553565535755358553595536055361553625536355364553655536655367553685536955370553715537255373553745537555376553775537855379553805538155382553835538455385553865538755388553895539055391553925539355394553955539655397553985539955400554015540255403554045540555406554075540855409554105541155412554135541455415554165541755418554195542055421554225542355424554255542655427554285542955430554315543255433554345543555436554375543855439554405544155442554435544455445554465544755448554495545055451554525545355454554555545655457554585545955460554615546255463554645546555466554675546855469554705547155472554735547455475554765547755478554795548055481554825548355484554855548655487554885548955490554915549255493554945549555496554975549855499555005550155502555035550455505555065550755508555095551055511555125551355514555155551655517555185551955520555215552255523555245552555526555275552855529555305553155532555335553455535555365553755538555395554055541555425554355544555455554655547555485554955550555515555255553555545555555556555575555855559555605556155562555635556455565555665556755568555695557055571555725557355574555755557655577555785557955580555815558255583555845558555586555875558855589555905559155592555935559455595555965559755598555995560055601556025560355604556055560655607556085560955610556115561255613556145561555616556175561855619556205562155622556235562455625556265562755628556295563055631556325563355634556355563655637556385563955640556415564255643556445564555646556475564855649556505565155652556535565455655556565565755658556595566055661556625566355664556655566655667556685566955670556715567255673556745567555676556775567855679556805568155682556835568455685556865568755688556895569055691556925569355694556955569655697556985569955700557015570255703557045570555706557075570855709557105571155712557135571455715557165571755718557195572055721557225572355724557255572655727557285572955730557315573255733557345573555736557375573855739557405574155742557435574455745557465574755748557495575055751557525575355754557555575655757557585575955760557615576255763557645576555766557675576855769557705577155772557735577455775557765577755778557795578055781557825578355784557855578655787557885578955790557915579255793557945579555796557975579855799558005580155802558035580455805558065580755808558095581055811558125581355814558155581655817558185581955820558215582255823558245582555826558275582855829558305583155832558335583455835558365583755838558395584055841558425584355844558455584655847558485584955850558515585255853558545585555856558575585855859558605586155862558635586455865558665586755868558695587055871558725587355874558755587655877558785587955880558815588255883558845588555886558875588855889558905589155892558935589455895558965589755898558995590055901559025590355904559055590655907559085590955910559115591255913559145591555916559175591855919559205592155922559235592455925559265592755928559295593055931559325593355934559355593655937559385593955940559415594255943559445594555946559475594855949559505595155952559535595455955559565595755958559595596055961559625596355964559655596655967559685596955970559715597255973559745597555976559775597855979559805598155982559835598455985559865598755988559895599055991559925599355994559955599655997559985599956000560015600256003560045600556006560075600856009560105601156012560135601456015560165601756018560195602056021560225602356024560255602656027560285602956030560315603256033560345603556036560375603856039560405604156042560435604456045560465604756048560495605056051560525605356054560555605656057560585605956060560615606256063560645606556066560675606856069560705607156072560735607456075560765607756078560795608056081560825608356084560855608656087560885608956090560915609256093560945609556096560975609856099561005610156102561035610456105561065610756108561095611056111561125611356114561155611656117561185611956120561215612256123561245612556126561275612856129561305613156132561335613456135561365613756138561395614056141561425614356144561455614656147561485614956150561515615256153561545615556156561575615856159561605616156162561635616456165561665616756168561695617056171561725617356174561755617656177561785617956180561815618256183561845618556186561875618856189561905619156192561935619456195561965619756198561995620056201562025620356204562055620656207562085620956210562115621256213562145621556216562175621856219562205622156222562235622456225562265622756228562295623056231562325623356234562355623656237562385623956240562415624256243562445624556246562475624856249562505625156252562535625456255562565625756258562595626056261562625626356264562655626656267562685626956270562715627256273562745627556276562775627856279562805628156282562835628456285562865628756288562895629056291562925629356294562955629656297562985629956300563015630256303563045630556306563075630856309563105631156312563135631456315563165631756318563195632056321563225632356324563255632656327563285632956330563315633256333563345633556336563375633856339563405634156342563435634456345563465634756348563495635056351563525635356354563555635656357563585635956360563615636256363563645636556366563675636856369563705637156372563735637456375563765637756378563795638056381563825638356384563855638656387563885638956390563915639256393563945639556396563975639856399564005640156402564035640456405564065640756408564095641056411564125641356414564155641656417564185641956420564215642256423564245642556426564275642856429564305643156432564335643456435564365643756438564395644056441564425644356444564455644656447564485644956450564515645256453564545645556456564575645856459564605646156462564635646456465564665646756468564695647056471564725647356474564755647656477564785647956480564815648256483564845648556486564875648856489564905649156492564935649456495564965649756498564995650056501565025650356504565055650656507565085650956510565115651256513565145651556516565175651856519565205652156522565235652456525565265652756528565295653056531565325653356534565355653656537565385653956540565415654256543565445654556546565475654856549565505655156552565535655456555565565655756558565595656056561565625656356564565655656656567565685656956570565715657256573565745657556576565775657856579565805658156582565835658456585565865658756588565895659056591565925659356594565955659656597565985659956600566015660256603566045660556606566075660856609566105661156612566135661456615566165661756618566195662056621566225662356624566255662656627566285662956630566315663256633566345663556636566375663856639566405664156642566435664456645566465664756648566495665056651566525665356654566555665656657566585665956660566615666256663566645666556666566675666856669566705667156672566735667456675566765667756678566795668056681566825668356684566855668656687566885668956690566915669256693566945669556696566975669856699567005670156702567035670456705567065670756708567095671056711567125671356714567155671656717567185671956720567215672256723567245672556726567275672856729567305673156732567335673456735567365673756738567395674056741567425674356744567455674656747567485674956750567515675256753567545675556756567575675856759567605676156762567635676456765567665676756768567695677056771567725677356774567755677656777567785677956780567815678256783567845678556786567875678856789567905679156792567935679456795567965679756798567995680056801568025680356804568055680656807568085680956810568115681256813568145681556816568175681856819568205682156822568235682456825568265682756828568295683056831568325683356834568355683656837568385683956840568415684256843568445684556846568475684856849568505685156852568535685456855568565685756858568595686056861568625686356864568655686656867568685686956870568715687256873568745687556876568775687856879568805688156882568835688456885568865688756888568895689056891568925689356894568955689656897568985689956900569015690256903569045690556906569075690856909569105691156912569135691456915569165691756918569195692056921569225692356924569255692656927569285692956930569315693256933569345693556936569375693856939569405694156942569435694456945569465694756948569495695056951569525695356954569555695656957569585695956960569615696256963569645696556966569675696856969569705697156972569735697456975569765697756978569795698056981569825698356984569855698656987569885698956990569915699256993569945699556996569975699856999570005700157002570035700457005570065700757008570095701057011570125701357014570155701657017570185701957020570215702257023570245702557026570275702857029570305703157032570335703457035570365703757038570395704057041570425704357044570455704657047570485704957050570515705257053570545705557056570575705857059570605706157062570635706457065570665706757068570695707057071570725707357074570755707657077570785707957080570815708257083570845708557086570875708857089570905709157092570935709457095570965709757098570995710057101571025710357104571055710657107571085710957110571115711257113571145711557116571175711857119571205712157122571235712457125571265712757128571295713057131571325713357134571355713657137571385713957140571415714257143571445714557146571475714857149571505715157152571535715457155571565715757158571595716057161571625716357164571655716657167571685716957170571715717257173571745717557176571775717857179571805718157182571835718457185571865718757188571895719057191571925719357194571955719657197571985719957200572015720257203572045720557206572075720857209572105721157212572135721457215572165721757218572195722057221572225722357224572255722657227572285722957230572315723257233572345723557236572375723857239572405724157242572435724457245572465724757248572495725057251572525725357254572555725657257572585725957260572615726257263572645726557266572675726857269572705727157272572735727457275572765727757278572795728057281572825728357284572855728657287572885728957290572915729257293572945729557296572975729857299573005730157302573035730457305573065730757308573095731057311573125731357314573155731657317573185731957320573215732257323573245732557326573275732857329573305733157332573335733457335573365733757338573395734057341573425734357344573455734657347573485734957350573515735257353573545735557356573575735857359573605736157362573635736457365573665736757368573695737057371573725737357374573755737657377573785737957380573815738257383573845738557386573875738857389573905739157392573935739457395573965739757398573995740057401574025740357404574055740657407574085740957410574115741257413574145741557416574175741857419574205742157422574235742457425574265742757428574295743057431574325743357434574355743657437574385743957440574415744257443574445744557446574475744857449574505745157452574535745457455574565745757458574595746057461574625746357464574655746657467574685746957470574715747257473574745747557476574775747857479574805748157482574835748457485574865748757488574895749057491574925749357494574955749657497574985749957500575015750257503575045750557506575075750857509575105751157512575135751457515575165751757518575195752057521575225752357524575255752657527575285752957530575315753257533575345753557536575375753857539575405754157542575435754457545575465754757548575495755057551575525755357554575555755657557575585755957560575615756257563575645756557566575675756857569575705757157572575735757457575575765757757578575795758057581575825758357584575855758657587575885758957590575915759257593575945759557596575975759857599576005760157602576035760457605576065760757608576095761057611576125761357614576155761657617576185761957620576215762257623576245762557626576275762857629576305763157632576335763457635576365763757638576395764057641576425764357644576455764657647576485764957650576515765257653576545765557656576575765857659576605766157662576635766457665576665766757668576695767057671576725767357674576755767657677576785767957680576815768257683576845768557686576875768857689576905769157692576935769457695576965769757698576995770057701577025770357704577055770657707577085770957710577115771257713577145771557716577175771857719577205772157722577235772457725577265772757728577295773057731577325773357734577355773657737577385773957740577415774257743577445774557746577475774857749577505775157752577535775457755577565775757758577595776057761577625776357764577655776657767577685776957770577715777257773577745777557776577775777857779577805778157782577835778457785577865778757788577895779057791577925779357794577955779657797577985779957800578015780257803578045780557806578075780857809578105781157812578135781457815578165781757818578195782057821578225782357824578255782657827578285782957830578315783257833578345783557836578375783857839578405784157842578435784457845578465784757848578495785057851578525785357854578555785657857578585785957860578615786257863578645786557866578675786857869578705787157872578735787457875578765787757878578795788057881578825788357884578855788657887578885788957890578915789257893578945789557896578975789857899579005790157902579035790457905579065790757908579095791057911579125791357914579155791657917579185791957920579215792257923579245792557926579275792857929579305793157932579335793457935579365793757938579395794057941579425794357944579455794657947579485794957950579515795257953579545795557956579575795857959579605796157962579635796457965579665796757968579695797057971579725797357974579755797657977579785797957980579815798257983579845798557986579875798857989579905799157992579935799457995579965799757998579995800058001580025800358004580055800658007580085800958010580115801258013580145801558016580175801858019580205802158022580235802458025580265802758028580295803058031580325803358034580355803658037580385803958040580415804258043580445804558046580475804858049580505805158052580535805458055580565805758058580595806058061580625806358064580655806658067580685806958070580715807258073580745807558076580775807858079580805808158082580835808458085580865808758088580895809058091580925809358094580955809658097580985809958100581015810258103581045810558106581075810858109581105811158112581135811458115581165811758118581195812058121581225812358124581255812658127581285812958130581315813258133581345813558136581375813858139581405814158142581435814458145581465814758148581495815058151581525815358154581555815658157581585815958160581615816258163581645816558166581675816858169581705817158172581735817458175581765817758178581795818058181581825818358184581855818658187581885818958190581915819258193581945819558196581975819858199582005820158202582035820458205582065820758208582095821058211582125821358214582155821658217582185821958220582215822258223582245822558226582275822858229582305823158232582335823458235582365823758238582395824058241582425824358244582455824658247582485824958250582515825258253582545825558256582575825858259582605826158262582635826458265582665826758268582695827058271582725827358274582755827658277582785827958280582815828258283582845828558286582875828858289582905829158292582935829458295582965829758298582995830058301583025830358304583055830658307583085830958310583115831258313583145831558316583175831858319583205832158322583235832458325583265832758328583295833058331583325833358334583355833658337583385833958340583415834258343583445834558346583475834858349583505835158352583535835458355583565835758358583595836058361583625836358364583655836658367583685836958370583715837258373583745837558376583775837858379583805838158382583835838458385583865838758388583895839058391583925839358394583955839658397583985839958400584015840258403584045840558406584075840858409584105841158412584135841458415584165841758418584195842058421584225842358424584255842658427584285842958430584315843258433584345843558436584375843858439584405844158442584435844458445584465844758448584495845058451584525845358454584555845658457584585845958460584615846258463584645846558466584675846858469584705847158472584735847458475584765847758478584795848058481584825848358484584855848658487584885848958490584915849258493584945849558496584975849858499585005850158502585035850458505585065850758508585095851058511585125851358514585155851658517585185851958520585215852258523585245852558526585275852858529585305853158532585335853458535585365853758538585395854058541585425854358544585455854658547585485854958550585515855258553585545855558556585575855858559585605856158562585635856458565585665856758568585695857058571585725857358574585755857658577585785857958580585815858258583585845858558586585875858858589585905859158592585935859458595585965859758598585995860058601586025860358604586055860658607586085860958610586115861258613586145861558616586175861858619586205862158622586235862458625586265862758628586295863058631586325863358634586355863658637586385863958640586415864258643586445864558646586475864858649586505865158652586535865458655586565865758658586595866058661586625866358664586655866658667586685866958670586715867258673586745867558676586775867858679586805868158682586835868458685586865868758688586895869058691586925869358694586955869658697586985869958700587015870258703587045870558706587075870858709587105871158712587135871458715587165871758718587195872058721587225872358724587255872658727587285872958730587315873258733587345873558736587375873858739587405874158742587435874458745587465874758748587495875058751587525875358754587555875658757587585875958760587615876258763587645876558766587675876858769587705877158772587735877458775587765877758778587795878058781587825878358784587855878658787587885878958790587915879258793587945879558796587975879858799588005880158802588035880458805588065880758808588095881058811588125881358814588155881658817588185881958820588215882258823588245882558826588275882858829588305883158832588335883458835588365883758838588395884058841588425884358844588455884658847588485884958850588515885258853588545885558856588575885858859588605886158862588635886458865588665886758868588695887058871588725887358874588755887658877588785887958880588815888258883588845888558886588875888858889588905889158892588935889458895588965889758898588995890058901589025890358904589055890658907589085890958910589115891258913589145891558916589175891858919589205892158922589235892458925589265892758928589295893058931589325893358934589355893658937589385893958940589415894258943589445894558946589475894858949589505895158952589535895458955589565895758958589595896058961589625896358964589655896658967589685896958970589715897258973589745897558976589775897858979589805898158982589835898458985589865898758988589895899058991589925899358994589955899658997589985899959000590015900259003590045900559006590075900859009590105901159012590135901459015590165901759018590195902059021590225902359024590255902659027590285902959030590315903259033590345903559036590375903859039590405904159042590435904459045590465904759048590495905059051590525905359054590555905659057590585905959060590615906259063590645906559066590675906859069590705907159072590735907459075590765907759078590795908059081590825908359084590855908659087590885908959090590915909259093590945909559096590975909859099591005910159102591035910459105591065910759108591095911059111591125911359114591155911659117591185911959120591215912259123591245912559126591275912859129591305913159132591335913459135591365913759138591395914059141591425914359144591455914659147591485914959150591515915259153591545915559156591575915859159591605916159162591635916459165591665916759168591695917059171591725917359174591755917659177591785917959180591815918259183591845918559186591875918859189591905919159192591935919459195591965919759198591995920059201592025920359204592055920659207592085920959210592115921259213592145921559216592175921859219592205922159222592235922459225592265922759228592295923059231592325923359234592355923659237592385923959240592415924259243592445924559246592475924859249592505925159252592535925459255592565925759258592595926059261592625926359264592655926659267592685926959270592715927259273592745927559276592775927859279592805928159282592835928459285592865928759288592895929059291592925929359294592955929659297592985929959300593015930259303593045930559306593075930859309593105931159312593135931459315593165931759318593195932059321593225932359324593255932659327593285932959330593315933259333593345933559336593375933859339593405934159342593435934459345593465934759348593495935059351593525935359354593555935659357593585935959360593615936259363593645936559366593675936859369593705937159372593735937459375593765937759378593795938059381593825938359384593855938659387593885938959390593915939259393593945939559396593975939859399594005940159402594035940459405594065940759408594095941059411594125941359414594155941659417594185941959420594215942259423594245942559426594275942859429594305943159432594335943459435594365943759438594395944059441594425944359444594455944659447594485944959450594515945259453594545945559456594575945859459594605946159462594635946459465594665946759468594695947059471594725947359474594755947659477594785947959480594815948259483594845948559486594875948859489594905949159492594935949459495594965949759498594995950059501595025950359504595055950659507595085950959510595115951259513595145951559516595175951859519595205952159522595235952459525595265952759528595295953059531595325953359534595355953659537595385953959540595415954259543595445954559546595475954859549595505955159552595535955459555595565955759558595595956059561595625956359564595655956659567595685956959570595715957259573595745957559576595775957859579595805958159582595835958459585595865958759588595895959059591595925959359594595955959659597595985959959600596015960259603596045960559606596075960859609596105961159612596135961459615596165961759618596195962059621596225962359624596255962659627596285962959630596315963259633596345963559636596375963859639596405964159642596435964459645596465964759648596495965059651596525965359654596555965659657596585965959660596615966259663596645966559666596675966859669596705967159672596735967459675596765967759678596795968059681596825968359684596855968659687596885968959690596915969259693596945969559696596975969859699597005970159702597035970459705597065970759708597095971059711597125971359714597155971659717597185971959720597215972259723597245972559726597275972859729597305973159732597335973459735597365973759738597395974059741597425974359744597455974659747597485974959750597515975259753597545975559756597575975859759597605976159762597635976459765597665976759768597695977059771597725977359774597755977659777597785977959780597815978259783597845978559786597875978859789597905979159792597935979459795597965979759798597995980059801598025980359804598055980659807598085980959810598115981259813598145981559816598175981859819598205982159822598235982459825598265982759828598295983059831598325983359834598355983659837598385983959840598415984259843598445984559846598475984859849598505985159852598535985459855598565985759858598595986059861598625986359864598655986659867598685986959870598715987259873598745987559876598775987859879598805988159882598835988459885598865988759888598895989059891598925989359894598955989659897598985989959900599015990259903599045990559906599075990859909599105991159912599135991459915599165991759918599195992059921599225992359924599255992659927599285992959930599315993259933599345993559936599375993859939599405994159942599435994459945599465994759948599495995059951599525995359954599555995659957599585995959960599615996259963599645996559966599675996859969599705997159972599735997459975599765997759978599795998059981599825998359984599855998659987599885998959990599915999259993599945999559996599975999859999600006000160002600036000460005600066000760008600096001060011600126001360014600156001660017600186001960020600216002260023600246002560026600276002860029600306003160032600336003460035600366003760038600396004060041600426004360044600456004660047600486004960050600516005260053600546005560056600576005860059600606006160062600636006460065600666006760068600696007060071600726007360074600756007660077600786007960080600816008260083600846008560086600876008860089600906009160092600936009460095600966009760098600996010060101601026010360104601056010660107601086010960110601116011260113601146011560116601176011860119601206012160122601236012460125601266012760128601296013060131601326013360134601356013660137601386013960140601416014260143601446014560146601476014860149601506015160152601536015460155601566015760158601596016060161601626016360164601656016660167601686016960170601716017260173601746017560176601776017860179601806018160182601836018460185601866018760188601896019060191601926019360194601956019660197601986019960200602016020260203602046020560206602076020860209602106021160212602136021460215602166021760218602196022060221602226022360224602256022660227602286022960230602316023260233602346023560236602376023860239602406024160242602436024460245602466024760248602496025060251602526025360254602556025660257602586025960260602616026260263602646026560266602676026860269602706027160272602736027460275602766027760278602796028060281602826028360284602856028660287602886028960290602916029260293602946029560296602976029860299603006030160302603036030460305603066030760308603096031060311603126031360314603156031660317603186031960320603216032260323603246032560326603276032860329603306033160332603336033460335603366033760338603396034060341603426034360344603456034660347603486034960350603516035260353603546035560356603576035860359603606036160362603636036460365603666036760368603696037060371603726037360374603756037660377603786037960380603816038260383603846038560386603876038860389603906039160392603936039460395603966039760398603996040060401604026040360404604056040660407604086040960410604116041260413604146041560416604176041860419604206042160422604236042460425604266042760428604296043060431604326043360434604356043660437604386043960440604416044260443604446044560446604476044860449604506045160452604536045460455604566045760458604596046060461604626046360464604656046660467604686046960470604716047260473604746047560476604776047860479604806048160482604836048460485604866048760488604896049060491604926049360494604956049660497604986049960500605016050260503605046050560506605076050860509605106051160512605136051460515605166051760518605196052060521605226052360524605256052660527605286052960530605316053260533605346053560536605376053860539605406054160542605436054460545605466054760548605496055060551605526055360554605556055660557605586055960560605616056260563605646056560566605676056860569605706057160572605736057460575605766057760578605796058060581605826058360584605856058660587605886058960590605916059260593605946059560596605976059860599606006060160602606036060460605606066060760608606096061060611606126061360614606156061660617606186061960620606216062260623606246062560626606276062860629606306063160632606336063460635606366063760638606396064060641606426064360644606456064660647606486064960650606516065260653606546065560656606576065860659606606066160662606636066460665606666066760668606696067060671606726067360674606756067660677606786067960680606816068260683606846068560686606876068860689606906069160692606936069460695606966069760698606996070060701607026070360704607056070660707607086070960710607116071260713607146071560716607176071860719607206072160722607236072460725607266072760728607296073060731607326073360734607356073660737607386073960740607416074260743607446074560746607476074860749607506075160752607536075460755607566075760758607596076060761607626076360764607656076660767607686076960770607716077260773607746077560776607776077860779607806078160782607836078460785607866078760788607896079060791607926079360794607956079660797607986079960800608016080260803608046080560806608076080860809608106081160812608136081460815608166081760818608196082060821608226082360824608256082660827608286082960830608316083260833608346083560836608376083860839608406084160842608436084460845608466084760848608496085060851608526085360854608556085660857608586085960860608616086260863608646086560866608676086860869608706087160872608736087460875608766087760878608796088060881608826088360884608856088660887608886088960890608916089260893608946089560896608976089860899609006090160902609036090460905609066090760908609096091060911609126091360914609156091660917609186091960920609216092260923609246092560926609276092860929609306093160932609336093460935609366093760938609396094060941609426094360944609456094660947609486094960950609516095260953609546095560956609576095860959609606096160962609636096460965609666096760968609696097060971609726097360974609756097660977609786097960980609816098260983609846098560986609876098860989609906099160992609936099460995609966099760998609996100061001610026100361004610056100661007610086100961010610116101261013610146101561016610176101861019610206102161022610236102461025610266102761028610296103061031610326103361034610356103661037610386103961040610416104261043610446104561046610476104861049610506105161052610536105461055610566105761058610596106061061610626106361064610656106661067610686106961070610716107261073610746107561076610776107861079610806108161082610836108461085610866108761088610896109061091610926109361094610956109661097610986109961100611016110261103611046110561106611076110861109611106111161112611136111461115611166111761118611196112061121611226112361124611256112661127611286112961130611316113261133611346113561136611376113861139611406114161142611436114461145611466114761148611496115061151611526115361154611556115661157611586115961160611616116261163611646116561166611676116861169611706117161172611736117461175611766117761178611796118061181611826118361184611856118661187611886118961190611916119261193611946119561196611976119861199612006120161202612036120461205612066120761208612096121061211612126121361214612156121661217612186121961220612216122261223612246122561226612276122861229612306123161232612336123461235612366123761238612396124061241612426124361244612456124661247612486124961250612516125261253612546125561256612576125861259612606126161262612636126461265612666126761268612696127061271612726127361274612756127661277612786127961280612816128261283612846128561286612876128861289612906129161292612936129461295612966129761298612996130061301613026130361304613056130661307613086130961310613116131261313613146131561316613176131861319613206132161322613236132461325613266132761328613296133061331613326133361334613356133661337613386133961340613416134261343613446134561346613476134861349613506135161352613536135461355613566135761358613596136061361613626136361364613656136661367613686136961370613716137261373613746137561376613776137861379613806138161382613836138461385613866138761388613896139061391613926139361394613956139661397613986139961400614016140261403614046140561406614076140861409614106141161412614136141461415614166141761418614196142061421614226142361424614256142661427614286142961430614316143261433614346143561436614376143861439614406144161442614436144461445614466144761448614496145061451614526145361454614556145661457614586145961460614616146261463614646146561466614676146861469614706147161472614736147461475614766147761478614796148061481614826148361484614856148661487614886148961490614916149261493614946149561496614976149861499615006150161502615036150461505615066150761508615096151061511615126151361514615156151661517615186151961520615216152261523615246152561526615276152861529615306153161532615336153461535615366153761538615396154061541615426154361544615456154661547615486154961550615516155261553615546155561556615576155861559615606156161562615636156461565615666156761568615696157061571615726157361574615756157661577615786157961580615816158261583615846158561586615876158861589615906159161592615936159461595615966159761598615996160061601616026160361604616056160661607616086160961610616116161261613616146161561616616176161861619616206162161622616236162461625616266162761628616296163061631616326163361634616356163661637616386163961640616416164261643616446164561646616476164861649616506165161652616536165461655616566165761658616596166061661616626166361664616656166661667616686166961670616716167261673616746167561676616776167861679616806168161682616836168461685616866168761688616896169061691616926169361694616956169661697616986169961700617016170261703617046170561706617076170861709617106171161712617136171461715617166171761718617196172061721617226172361724617256172661727617286172961730617316173261733617346173561736617376173861739617406174161742617436174461745617466174761748617496175061751617526175361754617556175661757617586175961760617616176261763617646176561766617676176861769617706177161772617736177461775617766177761778617796178061781617826178361784617856178661787617886178961790617916179261793617946179561796617976179861799618006180161802618036180461805618066180761808618096181061811618126181361814618156181661817618186181961820618216182261823618246182561826618276182861829618306183161832618336183461835618366183761838618396184061841618426184361844618456184661847618486184961850618516185261853618546185561856618576185861859618606186161862618636186461865618666186761868618696187061871618726187361874618756187661877618786187961880618816188261883618846188561886618876188861889618906189161892618936189461895618966189761898618996190061901619026190361904619056190661907619086190961910619116191261913619146191561916619176191861919619206192161922619236192461925619266192761928619296193061931619326193361934619356193661937619386193961940619416194261943619446194561946619476194861949619506195161952619536195461955619566195761958619596196061961619626196361964619656196661967619686196961970619716197261973619746197561976619776197861979619806198161982619836198461985619866198761988619896199061991619926199361994619956199661997619986199962000620016200262003620046200562006620076200862009620106201162012620136201462015620166201762018620196202062021620226202362024620256202662027620286202962030620316203262033620346203562036620376203862039620406204162042620436204462045620466204762048620496205062051620526205362054620556205662057620586205962060620616206262063620646206562066620676206862069620706207162072620736207462075620766207762078620796208062081620826208362084620856208662087620886208962090620916209262093620946209562096620976209862099621006210162102621036210462105621066210762108621096211062111621126211362114621156211662117621186211962120621216212262123621246212562126621276212862129621306213162132621336213462135621366213762138621396214062141621426214362144621456214662147621486214962150621516215262153621546215562156621576215862159621606216162162621636216462165621666216762168621696217062171621726217362174621756217662177621786217962180621816218262183621846218562186621876218862189621906219162192621936219462195621966219762198621996220062201622026220362204622056220662207622086220962210622116221262213622146221562216622176221862219622206222162222622236222462225622266222762228622296223062231622326223362234622356223662237622386223962240622416224262243622446224562246622476224862249622506225162252622536225462255622566225762258622596226062261622626226362264622656226662267622686226962270622716227262273622746227562276622776227862279622806228162282622836228462285622866228762288622896229062291622926229362294622956229662297622986229962300623016230262303623046230562306623076230862309623106231162312623136231462315623166231762318623196232062321623226232362324623256232662327623286232962330623316233262333623346233562336623376233862339623406234162342623436234462345623466234762348623496235062351623526235362354623556235662357623586235962360623616236262363623646236562366623676236862369623706237162372623736237462375623766237762378623796238062381623826238362384623856238662387623886238962390623916239262393623946239562396623976239862399624006240162402624036240462405624066240762408624096241062411624126241362414624156241662417624186241962420624216242262423624246242562426624276242862429624306243162432624336243462435624366243762438624396244062441624426244362444624456244662447624486244962450624516245262453624546245562456624576245862459624606246162462624636246462465624666246762468624696247062471624726247362474624756247662477624786247962480624816248262483624846248562486624876248862489624906249162492624936249462495624966249762498624996250062501625026250362504625056250662507625086250962510625116251262513625146251562516625176251862519625206252162522625236252462525625266252762528625296253062531625326253362534625356253662537625386253962540625416254262543625446254562546625476254862549625506255162552625536255462555625566255762558625596256062561625626256362564625656256662567625686256962570625716257262573625746257562576625776257862579625806258162582625836258462585625866258762588625896259062591625926259362594625956259662597625986259962600626016260262603626046260562606626076260862609626106261162612626136261462615626166261762618626196262062621626226262362624626256262662627626286262962630626316263262633626346263562636626376263862639626406264162642626436264462645626466264762648626496265062651626526265362654626556265662657626586265962660626616266262663626646266562666626676266862669626706267162672626736267462675626766267762678626796268062681626826268362684626856268662687626886268962690626916269262693626946269562696626976269862699627006270162702627036270462705627066270762708627096271062711627126271362714627156271662717627186271962720627216272262723627246272562726627276272862729627306273162732627336273462735627366273762738627396274062741627426274362744627456274662747627486274962750627516275262753627546275562756627576275862759627606276162762627636276462765627666276762768627696277062771627726277362774627756277662777627786277962780627816278262783627846278562786627876278862789627906279162792627936279462795627966279762798627996280062801628026280362804628056280662807628086280962810628116281262813628146281562816628176281862819628206282162822628236282462825628266282762828628296283062831628326283362834628356283662837628386283962840628416284262843628446284562846628476284862849628506285162852628536285462855628566285762858628596286062861628626286362864628656286662867628686286962870628716287262873628746287562876628776287862879628806288162882628836288462885628866288762888628896289062891628926289362894628956289662897628986289962900629016290262903629046290562906629076290862909629106291162912629136291462915629166291762918629196292062921629226292362924629256292662927629286292962930629316293262933629346293562936629376293862939629406294162942629436294462945629466294762948629496295062951629526295362954629556295662957629586295962960629616296262963629646296562966629676296862969629706297162972629736297462975629766297762978629796298062981629826298362984629856298662987629886298962990629916299262993629946299562996629976299862999630006300163002630036300463005630066300763008630096301063011630126301363014630156301663017630186301963020630216302263023630246302563026630276302863029630306303163032630336303463035630366303763038630396304063041630426304363044630456304663047630486304963050630516305263053630546305563056630576305863059630606306163062630636306463065630666306763068630696307063071630726307363074630756307663077630786307963080630816308263083630846308563086630876308863089630906309163092630936309463095630966309763098630996310063101631026310363104631056310663107631086310963110631116311263113631146311563116631176311863119631206312163122631236312463125631266312763128631296313063131631326313363134631356313663137631386313963140631416314263143631446314563146631476314863149631506315163152631536315463155631566315763158631596316063161631626316363164631656316663167631686316963170631716317263173631746317563176631776317863179631806318163182631836318463185631866318763188631896319063191631926319363194631956319663197631986319963200632016320263203632046320563206632076320863209632106321163212632136321463215632166321763218632196322063221632226322363224632256322663227632286322963230632316323263233632346323563236632376323863239632406324163242632436324463245632466324763248632496325063251632526325363254632556325663257632586325963260632616326263263632646326563266632676326863269632706327163272632736327463275632766327763278632796328063281632826328363284632856328663287632886328963290632916329263293632946329563296632976329863299633006330163302633036330463305633066330763308633096331063311633126331363314633156331663317633186331963320633216332263323633246332563326633276332863329633306333163332633336333463335633366333763338633396334063341633426334363344633456334663347633486334963350633516335263353633546335563356633576335863359633606336163362633636336463365633666336763368633696337063371633726337363374633756337663377633786337963380633816338263383633846338563386633876338863389633906339163392633936339463395633966339763398633996340063401634026340363404634056340663407634086340963410634116341263413634146341563416634176341863419634206342163422634236342463425634266342763428634296343063431634326343363434634356343663437634386343963440634416344263443634446344563446634476344863449634506345163452634536345463455634566345763458634596346063461634626346363464634656346663467634686346963470634716347263473634746347563476634776347863479634806348163482634836348463485634866348763488634896349063491634926349363494634956349663497634986349963500635016350263503635046350563506635076350863509635106351163512635136351463515635166351763518635196352063521635226352363524635256352663527635286352963530635316353263533635346353563536635376353863539635406354163542635436354463545635466354763548635496355063551635526355363554635556355663557635586355963560635616356263563635646356563566635676356863569635706357163572635736357463575635766357763578635796358063581635826358363584635856358663587635886358963590635916359263593635946359563596635976359863599636006360163602636036360463605636066360763608636096361063611636126361363614636156361663617636186361963620636216362263623636246362563626636276362863629636306363163632636336363463635636366363763638636396364063641636426364363644636456364663647636486364963650636516365263653636546365563656636576365863659636606366163662636636366463665636666366763668636696367063671636726367363674636756367663677636786367963680636816368263683636846368563686636876368863689636906369163692636936369463695636966369763698636996370063701637026370363704637056370663707637086370963710637116371263713637146371563716637176371863719637206372163722637236372463725637266372763728637296373063731637326373363734637356373663737637386373963740637416374263743637446374563746637476374863749637506375163752637536375463755637566375763758637596376063761637626376363764637656376663767637686376963770637716377263773637746377563776637776377863779637806378163782637836378463785637866378763788637896379063791637926379363794637956379663797637986379963800638016380263803638046380563806638076380863809638106381163812638136381463815638166381763818638196382063821638226382363824638256382663827638286382963830638316383263833638346383563836638376383863839638406384163842638436384463845638466384763848638496385063851638526385363854638556385663857638586385963860638616386263863638646386563866638676386863869638706387163872638736387463875638766387763878638796388063881638826388363884638856388663887638886388963890638916389263893638946389563896638976389863899639006390163902639036390463905639066390763908639096391063911639126391363914639156391663917639186391963920639216392263923639246392563926639276392863929639306393163932639336393463935639366393763938639396394063941639426394363944639456394663947639486394963950639516395263953639546395563956639576395863959639606396163962639636396463965639666396763968639696397063971639726397363974639756397663977639786397963980639816398263983639846398563986639876398863989639906399163992639936399463995639966399763998639996400064001640026400364004640056400664007640086400964010640116401264013640146401564016640176401864019640206402164022640236402464025640266402764028640296403064031640326403364034640356403664037640386403964040640416404264043640446404564046640476404864049640506405164052640536405464055640566405764058640596406064061640626406364064640656406664067640686406964070640716407264073640746407564076640776407864079640806408164082640836408464085640866408764088640896409064091640926409364094640956409664097640986409964100641016410264103641046410564106641076410864109641106411164112641136411464115641166411764118641196412064121641226412364124641256412664127641286412964130641316413264133641346413564136641376413864139641406414164142641436414464145641466414764148641496415064151641526415364154641556415664157641586415964160641616416264163641646416564166641676416864169641706417164172641736417464175641766417764178641796418064181641826418364184641856418664187641886418964190641916419264193641946419564196641976419864199642006420164202642036420464205642066420764208642096421064211642126421364214642156421664217642186421964220642216422264223642246422564226642276422864229642306423164232642336423464235642366423764238642396424064241642426424364244642456424664247642486424964250642516425264253642546425564256642576425864259642606426164262642636426464265642666426764268642696427064271642726427364274642756427664277642786427964280642816428264283642846428564286642876428864289642906429164292642936429464295642966429764298642996430064301643026430364304643056430664307643086430964310643116431264313643146431564316643176431864319643206432164322643236432464325643266432764328643296433064331643326433364334643356433664337643386433964340643416434264343643446434564346643476434864349643506435164352643536435464355643566435764358643596436064361643626436364364643656436664367643686436964370643716437264373643746437564376643776437864379643806438164382643836438464385643866438764388643896439064391643926439364394643956439664397643986439964400644016440264403644046440564406644076440864409644106441164412644136441464415644166441764418644196442064421644226442364424644256442664427644286442964430644316443264433644346443564436644376443864439644406444164442644436444464445644466444764448644496445064451644526445364454644556445664457644586445964460644616446264463644646446564466644676446864469644706447164472644736447464475644766447764478644796448064481644826448364484644856448664487644886448964490644916449264493644946449564496644976449864499645006450164502645036450464505645066450764508645096451064511645126451364514645156451664517645186451964520645216452264523645246452564526645276452864529645306453164532645336453464535645366453764538645396454064541645426454364544645456454664547645486454964550645516455264553645546455564556645576455864559645606456164562645636456464565645666456764568645696457064571645726457364574645756457664577645786457964580645816458264583645846458564586645876458864589645906459164592645936459464595645966459764598645996460064601646026460364604646056460664607646086460964610646116461264613646146461564616646176461864619646206462164622646236462464625646266462764628646296463064631646326463364634646356463664637646386463964640646416464264643646446464564646646476464864649646506465164652646536465464655646566465764658646596466064661646626466364664646656466664667646686466964670646716467264673646746467564676646776467864679646806468164682646836468464685646866468764688646896469064691646926469364694646956469664697646986469964700647016470264703647046470564706647076470864709647106471164712647136471464715647166471764718647196472064721647226472364724647256472664727647286472964730647316473264733647346473564736647376473864739647406474164742647436474464745647466474764748647496475064751647526475364754647556475664757647586475964760647616476264763647646476564766647676476864769647706477164772647736477464775647766477764778647796478064781647826478364784647856478664787647886478964790647916479264793647946479564796647976479864799648006480164802648036480464805648066480764808648096481064811648126481364814648156481664817648186481964820648216482264823648246482564826648276482864829648306483164832648336483464835648366483764838648396484064841648426484364844648456484664847648486484964850648516485264853648546485564856648576485864859648606486164862648636486464865648666486764868648696487064871648726487364874648756487664877648786487964880648816488264883648846488564886648876488864889648906489164892648936489464895648966489764898648996490064901649026490364904649056490664907649086490964910649116491264913649146491564916649176491864919649206492164922649236492464925649266492764928649296493064931649326493364934649356493664937649386493964940649416494264943649446494564946649476494864949649506495164952649536495464955649566495764958649596496064961649626496364964649656496664967649686496964970649716497264973649746497564976649776497864979649806498164982649836498464985649866498764988649896499064991649926499364994649956499664997649986499965000650016500265003650046500565006650076500865009650106501165012650136501465015650166501765018650196502065021650226502365024650256502665027650286502965030650316503265033650346503565036650376503865039650406504165042650436504465045650466504765048650496505065051650526505365054650556505665057650586505965060650616506265063650646506565066650676506865069650706507165072650736507465075650766507765078650796508065081650826508365084650856508665087650886508965090650916509265093650946509565096650976509865099651006510165102651036510465105651066510765108651096511065111651126511365114651156511665117651186511965120651216512265123651246512565126651276512865129651306513165132651336513465135651366513765138651396514065141651426514365144651456514665147651486514965150651516515265153651546515565156651576515865159651606516165162651636516465165651666516765168651696517065171651726517365174651756517665177651786517965180651816518265183651846518565186651876518865189651906519165192651936519465195651966519765198651996520065201652026520365204652056520665207652086520965210652116521265213652146521565216652176521865219652206522165222652236522465225652266522765228652296523065231652326523365234652356523665237652386523965240652416524265243652446524565246652476524865249652506525165252652536525465255652566525765258652596526065261652626526365264652656526665267652686526965270652716527265273652746527565276652776527865279652806528165282652836528465285652866528765288652896529065291652926529365294652956529665297652986529965300653016530265303653046530565306653076530865309653106531165312653136531465315653166531765318653196532065321653226532365324653256532665327653286532965330653316533265333653346533565336653376533865339653406534165342653436534465345653466534765348653496535065351653526535365354653556535665357653586535965360653616536265363653646536565366653676536865369653706537165372653736537465375653766537765378653796538065381653826538365384653856538665387653886538965390653916539265393653946539565396653976539865399654006540165402654036540465405654066540765408654096541065411654126541365414654156541665417654186541965420654216542265423654246542565426654276542865429654306543165432654336543465435654366543765438654396544065441654426544365444654456544665447654486544965450654516545265453654546545565456654576545865459654606546165462654636546465465654666546765468654696547065471654726547365474654756547665477654786547965480654816548265483654846548565486654876548865489654906549165492654936549465495654966549765498654996550065501655026550365504655056550665507655086550965510655116551265513655146551565516655176551865519655206552165522655236552465525655266552765528655296553065531655326553365534655356553665537655386553965540655416554265543655446554565546655476554865549655506555165552655536555465555655566555765558655596556065561655626556365564655656556665567655686556965570655716557265573655746557565576655776557865579655806558165582655836558465585655866558765588655896559065591655926559365594655956559665597655986559965600656016560265603656046560565606656076560865609656106561165612656136561465615656166561765618656196562065621656226562365624656256562665627656286562965630656316563265633656346563565636656376563865639656406564165642656436564465645656466564765648656496565065651656526565365654656556565665657656586565965660656616566265663656646566565666656676566865669656706567165672656736567465675656766567765678656796568065681656826568365684656856568665687656886568965690656916569265693656946569565696656976569865699657006570165702657036570465705657066570765708657096571065711657126571365714657156571665717657186571965720657216572265723657246572565726657276572865729657306573165732657336573465735657366573765738657396574065741657426574365744657456574665747657486574965750657516575265753657546575565756657576575865759657606576165762657636576465765657666576765768657696577065771657726577365774657756577665777657786577965780657816578265783657846578565786657876578865789657906579165792657936579465795657966579765798657996580065801658026580365804658056580665807658086580965810658116581265813658146581565816658176581865819658206582165822658236582465825658266582765828658296583065831658326583365834658356583665837658386583965840658416584265843658446584565846658476584865849658506585165852658536585465855658566585765858658596586065861658626586365864658656586665867658686586965870658716587265873658746587565876658776587865879658806588165882658836588465885658866588765888658896589065891658926589365894658956589665897658986589965900659016590265903659046590565906659076590865909659106591165912659136591465915659166591765918659196592065921659226592365924659256592665927659286592965930659316593265933659346593565936659376593865939659406594165942659436594465945659466594765948659496595065951659526595365954659556595665957659586595965960659616596265963659646596565966659676596865969659706597165972659736597465975659766597765978659796598065981659826598365984659856598665987659886598965990659916599265993659946599565996659976599865999660006600166002660036600466005660066600766008660096601066011660126601366014660156601666017660186601966020660216602266023660246602566026660276602866029660306603166032660336603466035660366603766038660396604066041660426604366044660456604666047660486604966050660516605266053660546605566056660576605866059660606606166062660636606466065660666606766068660696607066071660726607366074660756607666077660786607966080660816608266083660846608566086660876608866089660906609166092660936609466095660966609766098660996610066101661026610366104661056610666107661086610966110661116611266113661146611566116661176611866119661206612166122661236612466125661266612766128661296613066131661326613366134661356613666137661386613966140661416614266143661446614566146661476614866149661506615166152661536615466155661566615766158661596616066161661626616366164661656616666167661686616966170661716617266173661746617566176661776617866179661806618166182661836618466185661866618766188661896619066191661926619366194661956619666197661986619966200662016620266203662046620566206662076620866209662106621166212662136621466215662166621766218662196622066221662226622366224662256622666227662286622966230662316623266233662346623566236662376623866239662406624166242662436624466245662466624766248662496625066251662526625366254662556625666257662586625966260662616626266263662646626566266662676626866269662706627166272662736627466275662766627766278662796628066281662826628366284662856628666287662886628966290662916629266293662946629566296662976629866299663006630166302663036630466305663066630766308663096631066311663126631366314663156631666317663186631966320663216632266323663246632566326663276632866329663306633166332663336633466335663366633766338663396634066341663426634366344663456634666347663486634966350663516635266353663546635566356663576635866359663606636166362663636636466365663666636766368663696637066371663726637366374663756637666377663786637966380663816638266383663846638566386663876638866389663906639166392663936639466395663966639766398663996640066401664026640366404664056640666407664086640966410664116641266413664146641566416664176641866419664206642166422664236642466425664266642766428664296643066431664326643366434664356643666437664386643966440664416644266443664446644566446664476644866449664506645166452664536645466455664566645766458664596646066461664626646366464664656646666467664686646966470664716647266473664746647566476664776647866479664806648166482664836648466485664866648766488664896649066491664926649366494664956649666497664986649966500665016650266503665046650566506665076650866509665106651166512665136651466515665166651766518665196652066521665226652366524665256652666527665286652966530665316653266533665346653566536665376653866539665406654166542665436654466545665466654766548665496655066551665526655366554665556655666557665586655966560665616656266563665646656566566665676656866569665706657166572665736657466575665766657766578665796658066581665826658366584665856658666587665886658966590665916659266593665946659566596665976659866599666006660166602666036660466605666066660766608666096661066611666126661366614666156661666617666186661966620666216662266623666246662566626666276662866629666306663166632666336663466635666366663766638666396664066641666426664366644666456664666647666486664966650666516665266653666546665566656666576665866659666606666166662666636666466665666666666766668666696667066671666726667366674666756667666677666786667966680666816668266683666846668566686666876668866689666906669166692666936669466695666966669766698666996670066701667026670366704667056670666707667086670966710667116671266713667146671566716667176671866719667206672166722667236672466725667266672766728667296673066731667326673366734667356673666737667386673966740667416674266743667446674566746667476674866749667506675166752667536675466755667566675766758667596676066761667626676366764667656676666767667686676966770667716677266773667746677566776667776677866779667806678166782667836678466785667866678766788667896679066791667926679366794667956679666797667986679966800668016680266803668046680566806668076680866809668106681166812668136681466815668166681766818668196682066821668226682366824668256682666827668286682966830668316683266833668346683566836668376683866839668406684166842668436684466845668466684766848668496685066851668526685366854668556685666857668586685966860668616686266863668646686566866668676686866869668706687166872668736687466875668766687766878668796688066881668826688366884668856688666887668886688966890668916689266893668946689566896668976689866899669006690166902669036690466905669066690766908669096691066911669126691366914669156691666917669186691966920669216692266923669246692566926669276692866929669306693166932669336693466935669366693766938669396694066941669426694366944669456694666947669486694966950669516695266953669546695566956669576695866959669606696166962669636696466965669666696766968669696697066971669726697366974669756697666977669786697966980669816698266983669846698566986669876698866989669906699166992669936699466995669966699766998669996700067001670026700367004670056700667007670086700967010670116701267013670146701567016670176701867019670206702167022670236702467025670266702767028670296703067031670326703367034670356703667037670386703967040670416704267043670446704567046670476704867049670506705167052670536705467055670566705767058670596706067061670626706367064670656706667067670686706967070670716707267073670746707567076670776707867079670806708167082670836708467085670866708767088670896709067091670926709367094670956709667097670986709967100671016710267103671046710567106671076710867109671106711167112671136711467115671166711767118671196712067121671226712367124671256712667127671286712967130671316713267133671346713567136671376713867139671406714167142671436714467145671466714767148671496715067151671526715367154671556715667157671586715967160671616716267163671646716567166671676716867169671706717167172671736717467175671766717767178671796718067181671826718367184671856718667187671886718967190671916719267193671946719567196671976719867199672006720167202672036720467205672066720767208672096721067211672126721367214672156721667217672186721967220672216722267223672246722567226672276722867229672306723167232672336723467235672366723767238672396724067241672426724367244672456724667247672486724967250672516725267253672546725567256672576725867259672606726167262672636726467265672666726767268672696727067271672726727367274672756727667277672786727967280672816728267283672846728567286672876728867289672906729167292672936729467295672966729767298672996730067301673026730367304673056730667307673086730967310673116731267313673146731567316673176731867319673206732167322673236732467325673266732767328673296733067331673326733367334673356733667337673386733967340673416734267343673446734567346673476734867349673506735167352673536735467355673566735767358673596736067361673626736367364673656736667367673686736967370673716737267373673746737567376673776737867379673806738167382673836738467385673866738767388673896739067391673926739367394673956739667397673986739967400674016740267403674046740567406674076740867409674106741167412674136741467415674166741767418674196742067421674226742367424674256742667427674286742967430674316743267433674346743567436674376743867439674406744167442674436744467445674466744767448674496745067451674526745367454674556745667457674586745967460674616746267463674646746567466674676746867469674706747167472674736747467475674766747767478674796748067481674826748367484674856748667487674886748967490674916749267493674946749567496674976749867499675006750167502675036750467505675066750767508675096751067511675126751367514675156751667517675186751967520675216752267523675246752567526675276752867529675306753167532675336753467535675366753767538675396754067541675426754367544675456754667547675486754967550675516755267553675546755567556675576755867559675606756167562675636756467565675666756767568675696757067571675726757367574675756757667577675786757967580675816758267583675846758567586675876758867589675906759167592675936759467595675966759767598675996760067601676026760367604676056760667607676086760967610676116761267613676146761567616676176761867619676206762167622676236762467625676266762767628676296763067631676326763367634676356763667637676386763967640676416764267643676446764567646676476764867649676506765167652676536765467655676566765767658676596766067661676626766367664676656766667667676686766967670676716767267673676746767567676676776767867679676806768167682676836768467685676866768767688676896769067691676926769367694676956769667697676986769967700677016770267703677046770567706677076770867709677106771167712677136771467715677166771767718677196772067721677226772367724677256772667727677286772967730677316773267733677346773567736677376773867739677406774167742677436774467745677466774767748677496775067751677526775367754677556775667757677586775967760677616776267763677646776567766677676776867769677706777167772677736777467775677766777767778677796778067781677826778367784677856778667787677886778967790677916779267793677946779567796677976779867799678006780167802678036780467805678066780767808678096781067811678126781367814678156781667817678186781967820678216782267823678246782567826678276782867829678306783167832678336783467835678366783767838678396784067841678426784367844678456784667847678486784967850678516785267853678546785567856678576785867859678606786167862678636786467865678666786767868678696787067871678726787367874678756787667877678786787967880678816788267883678846788567886678876788867889678906789167892678936789467895678966789767898678996790067901679026790367904679056790667907679086790967910679116791267913679146791567916679176791867919679206792167922679236792467925679266792767928679296793067931679326793367934679356793667937679386793967940679416794267943679446794567946679476794867949679506795167952679536795467955679566795767958679596796067961679626796367964679656796667967679686796967970679716797267973679746797567976679776797867979679806798167982679836798467985679866798767988679896799067991679926799367994679956799667997679986799968000680016800268003680046800568006680076800868009680106801168012680136801468015680166801768018680196802068021680226802368024680256802668027680286802968030680316803268033680346803568036680376803868039680406804168042680436804468045680466804768048680496805068051680526805368054680556805668057680586805968060680616806268063680646806568066680676806868069680706807168072680736807468075680766807768078680796808068081680826808368084680856808668087680886808968090680916809268093680946809568096680976809868099681006810168102681036810468105681066810768108681096811068111681126811368114681156811668117681186811968120681216812268123681246812568126681276812868129681306813168132681336813468135681366813768138681396814068141681426814368144681456814668147681486814968150681516815268153681546815568156681576815868159681606816168162681636816468165681666816768168681696817068171681726817368174681756817668177681786817968180681816818268183681846818568186681876818868189681906819168192681936819468195681966819768198681996820068201682026820368204682056820668207682086820968210682116821268213682146821568216682176821868219682206822168222682236822468225682266822768228682296823068231682326823368234682356823668237682386823968240682416824268243682446824568246682476824868249682506825168252682536825468255682566825768258682596826068261682626826368264682656826668267682686826968270682716827268273682746827568276682776827868279682806828168282682836828468285682866828768288682896829068291682926829368294682956829668297682986829968300683016830268303683046830568306683076830868309683106831168312683136831468315683166831768318683196832068321683226832368324683256832668327683286832968330683316833268333683346833568336683376833868339683406834168342683436834468345683466834768348683496835068351683526835368354683556835668357683586835968360683616836268363683646836568366683676836868369683706837168372683736837468375683766837768378683796838068381683826838368384683856838668387683886838968390683916839268393683946839568396683976839868399684006840168402684036840468405684066840768408684096841068411684126841368414684156841668417684186841968420684216842268423684246842568426684276842868429684306843168432684336843468435684366843768438684396844068441684426844368444684456844668447684486844968450684516845268453684546845568456684576845868459684606846168462684636846468465684666846768468684696847068471684726847368474684756847668477684786847968480684816848268483684846848568486684876848868489684906849168492684936849468495684966849768498684996850068501685026850368504685056850668507685086850968510685116851268513685146851568516685176851868519685206852168522685236852468525685266852768528685296853068531685326853368534685356853668537685386853968540685416854268543685446854568546685476854868549685506855168552685536855468555685566855768558685596856068561685626856368564685656856668567685686856968570685716857268573685746857568576685776857868579685806858168582685836858468585685866858768588685896859068591685926859368594685956859668597685986859968600686016860268603686046860568606686076860868609686106861168612686136861468615686166861768618686196862068621686226862368624686256862668627686286862968630686316863268633686346863568636686376863868639686406864168642686436864468645686466864768648686496865068651686526865368654686556865668657686586865968660686616866268663686646866568666686676866868669686706867168672686736867468675686766867768678686796868068681686826868368684686856868668687686886868968690686916869268693686946869568696686976869868699687006870168702687036870468705687066870768708687096871068711687126871368714687156871668717687186871968720687216872268723687246872568726687276872868729687306873168732687336873468735687366873768738687396874068741687426874368744687456874668747687486874968750687516875268753687546875568756687576875868759687606876168762687636876468765687666876768768687696877068771687726877368774687756877668777687786877968780687816878268783687846878568786687876878868789687906879168792687936879468795687966879768798687996880068801688026880368804688056880668807688086880968810688116881268813688146881568816688176881868819688206882168822688236882468825688266882768828688296883068831688326883368834688356883668837688386883968840688416884268843688446884568846688476884868849688506885168852688536885468855688566885768858688596886068861688626886368864688656886668867688686886968870688716887268873688746887568876688776887868879688806888168882688836888468885688866888768888688896889068891688926889368894688956889668897688986889968900689016890268903689046890568906689076890868909689106891168912689136891468915689166891768918689196892068921689226892368924689256892668927689286892968930689316893268933689346893568936689376893868939689406894168942689436894468945689466894768948689496895068951689526895368954689556895668957689586895968960689616896268963689646896568966689676896868969689706897168972689736897468975689766897768978689796898068981689826898368984689856898668987689886898968990689916899268993689946899568996689976899868999690006900169002690036900469005690066900769008690096901069011690126901369014690156901669017690186901969020690216902269023690246902569026690276902869029690306903169032690336903469035690366903769038690396904069041690426904369044690456904669047690486904969050690516905269053690546905569056690576905869059690606906169062690636906469065690666906769068690696907069071690726907369074690756907669077690786907969080690816908269083690846908569086690876908869089690906909169092690936909469095690966909769098690996910069101691026910369104691056910669107691086910969110691116911269113691146911569116691176911869119691206912169122691236912469125691266912769128691296913069131691326913369134691356913669137691386913969140691416914269143691446914569146691476914869149691506915169152691536915469155691566915769158691596916069161691626916369164691656916669167691686916969170691716917269173691746917569176691776917869179691806918169182691836918469185691866918769188691896919069191691926919369194691956919669197691986919969200692016920269203692046920569206692076920869209692106921169212692136921469215692166921769218692196922069221692226922369224692256922669227692286922969230692316923269233692346923569236692376923869239692406924169242692436924469245692466924769248692496925069251692526925369254692556925669257692586925969260692616926269263692646926569266692676926869269692706927169272692736927469275692766927769278692796928069281692826928369284692856928669287692886928969290692916929269293692946929569296692976929869299693006930169302693036930469305693066930769308693096931069311693126931369314693156931669317693186931969320693216932269323693246932569326693276932869329693306933169332693336933469335693366933769338693396934069341693426934369344693456934669347693486934969350693516935269353693546935569356693576935869359693606936169362693636936469365693666936769368693696937069371693726937369374693756937669377693786937969380693816938269383693846938569386693876938869389693906939169392693936939469395693966939769398693996940069401694026940369404694056940669407694086940969410694116941269413694146941569416694176941869419694206942169422694236942469425694266942769428694296943069431694326943369434694356943669437694386943969440694416944269443694446944569446694476944869449694506945169452694536945469455694566945769458694596946069461694626946369464694656946669467694686946969470694716947269473694746947569476694776947869479694806948169482694836948469485694866948769488694896949069491694926949369494694956949669497694986949969500695016950269503695046950569506695076950869509695106951169512695136951469515695166951769518695196952069521695226952369524695256952669527695286952969530695316953269533695346953569536695376953869539695406954169542695436954469545695466954769548695496955069551695526955369554695556955669557695586955969560695616956269563695646956569566695676956869569695706957169572695736957469575695766957769578695796958069581695826958369584695856958669587695886958969590695916959269593695946959569596695976959869599696006960169602696036960469605696066960769608696096961069611696126961369614696156961669617696186961969620696216962269623696246962569626696276962869629696306963169632696336963469635696366963769638696396964069641696426964369644696456964669647696486964969650696516965269653696546965569656696576965869659696606966169662696636966469665696666966769668696696967069671696726967369674696756967669677696786967969680696816968269683696846968569686696876968869689696906969169692696936969469695696966969769698696996970069701697026970369704697056970669707697086970969710697116971269713697146971569716697176971869719697206972169722697236972469725697266972769728697296973069731697326973369734697356973669737697386973969740697416974269743697446974569746697476974869749697506975169752697536975469755697566975769758697596976069761697626976369764697656976669767697686976969770697716977269773697746977569776697776977869779697806978169782697836978469785697866978769788697896979069791697926979369794697956979669797697986979969800698016980269803698046980569806698076980869809698106981169812698136981469815698166981769818698196982069821698226982369824698256982669827698286982969830698316983269833698346983569836698376983869839698406984169842698436984469845698466984769848698496985069851698526985369854698556985669857698586985969860698616986269863698646986569866698676986869869698706987169872698736987469875698766987769878698796988069881698826988369884698856988669887698886988969890698916989269893698946989569896698976989869899699006990169902699036990469905699066990769908699096991069911699126991369914699156991669917699186991969920699216992269923699246992569926699276992869929699306993169932699336993469935699366993769938699396994069941699426994369944699456994669947699486994969950699516995269953699546995569956699576995869959699606996169962699636996469965699666996769968699696997069971699726997369974699756997669977699786997969980699816998269983699846998569986699876998869989699906999169992699936999469995699966999769998699997000070001700027000370004700057000670007700087000970010700117001270013700147001570016700177001870019700207002170022700237002470025700267002770028700297003070031700327003370034700357003670037700387003970040700417004270043700447004570046700477004870049700507005170052700537005470055700567005770058700597006070061700627006370064700657006670067700687006970070700717007270073700747007570076700777007870079700807008170082700837008470085700867008770088700897009070091700927009370094700957009670097700987009970100701017010270103701047010570106701077010870109701107011170112701137011470115701167011770118701197012070121701227012370124701257012670127701287012970130701317013270133701347013570136701377013870139701407014170142701437014470145701467014770148701497015070151701527015370154701557015670157701587015970160701617016270163701647016570166701677016870169701707017170172701737017470175701767017770178701797018070181701827018370184701857018670187701887018970190701917019270193701947019570196701977019870199702007020170202702037020470205702067020770208702097021070211702127021370214702157021670217702187021970220702217022270223702247022570226702277022870229702307023170232702337023470235702367023770238702397024070241702427024370244702457024670247702487024970250702517025270253702547025570256702577025870259702607026170262702637026470265702667026770268702697027070271702727027370274702757027670277702787027970280702817028270283702847028570286702877028870289702907029170292702937029470295702967029770298702997030070301703027030370304703057030670307703087030970310703117031270313703147031570316703177031870319703207032170322703237032470325703267032770328703297033070331703327033370334703357033670337703387033970340703417034270343703447034570346703477034870349703507035170352703537035470355703567035770358703597036070361703627036370364703657036670367703687036970370703717037270373703747037570376703777037870379703807038170382703837038470385703867038770388703897039070391703927039370394703957039670397703987039970400704017040270403704047040570406704077040870409704107041170412704137041470415704167041770418704197042070421704227042370424704257042670427704287042970430704317043270433704347043570436704377043870439704407044170442704437044470445704467044770448704497045070451704527045370454704557045670457704587045970460704617046270463704647046570466704677046870469704707047170472704737047470475704767047770478704797048070481704827048370484704857048670487704887048970490704917049270493704947049570496704977049870499705007050170502705037050470505705067050770508705097051070511705127051370514705157051670517705187051970520705217052270523705247052570526705277052870529705307053170532705337053470535705367053770538705397054070541705427054370544705457054670547705487054970550705517055270553705547055570556705577055870559705607056170562705637056470565705667056770568705697057070571705727057370574705757057670577705787057970580705817058270583705847058570586705877058870589705907059170592705937059470595705967059770598705997060070601706027060370604706057060670607706087060970610706117061270613706147061570616706177061870619706207062170622706237062470625706267062770628706297063070631706327063370634706357063670637706387063970640706417064270643706447064570646706477064870649706507065170652706537065470655706567065770658706597066070661706627066370664706657066670667706687066970670706717067270673706747067570676706777067870679706807068170682706837068470685706867068770688706897069070691706927069370694706957069670697706987069970700707017070270703707047070570706707077070870709707107071170712707137071470715707167071770718707197072070721707227072370724707257072670727707287072970730707317073270733707347073570736707377073870739707407074170742707437074470745707467074770748707497075070751707527075370754707557075670757707587075970760707617076270763707647076570766707677076870769707707077170772707737077470775707767077770778707797078070781707827078370784707857078670787707887078970790707917079270793707947079570796707977079870799708007080170802708037080470805708067080770808708097081070811708127081370814708157081670817708187081970820708217082270823708247082570826708277082870829708307083170832708337083470835708367083770838708397084070841708427084370844708457084670847708487084970850708517085270853708547085570856708577085870859708607086170862708637086470865708667086770868708697087070871708727087370874708757087670877708787087970880708817088270883708847088570886708877088870889708907089170892708937089470895708967089770898708997090070901709027090370904709057090670907709087090970910709117091270913709147091570916709177091870919709207092170922709237092470925709267092770928709297093070931709327093370934709357093670937709387093970940709417094270943709447094570946709477094870949709507095170952709537095470955709567095770958709597096070961709627096370964709657096670967709687096970970709717097270973709747097570976709777097870979709807098170982709837098470985709867098770988709897099070991709927099370994709957099670997709987099971000710017100271003710047100571006710077100871009710107101171012710137101471015710167101771018710197102071021710227102371024710257102671027710287102971030710317103271033710347103571036710377103871039710407104171042710437104471045710467104771048710497105071051710527105371054710557105671057710587105971060710617106271063710647106571066710677106871069710707107171072710737107471075710767107771078710797108071081710827108371084710857108671087710887108971090710917109271093710947109571096710977109871099711007110171102711037110471105711067110771108711097111071111711127111371114711157111671117711187111971120711217112271123711247112571126711277112871129711307113171132711337113471135711367113771138711397114071141711427114371144711457114671147711487114971150711517115271153711547115571156711577115871159711607116171162711637116471165711667116771168711697117071171711727117371174711757117671177711787117971180711817118271183711847118571186711877118871189711907119171192711937119471195711967119771198711997120071201712027120371204712057120671207712087120971210712117121271213712147121571216712177121871219712207122171222712237122471225712267122771228712297123071231712327123371234712357123671237712387123971240712417124271243712447124571246712477124871249712507125171252712537125471255712567125771258712597126071261712627126371264712657126671267712687126971270712717127271273712747127571276712777127871279712807128171282712837128471285712867128771288712897129071291712927129371294712957129671297712987129971300713017130271303713047130571306713077130871309713107131171312713137131471315713167131771318713197132071321713227132371324713257132671327713287132971330713317133271333713347133571336713377133871339713407134171342713437134471345713467134771348713497135071351713527135371354713557135671357713587135971360713617136271363713647136571366713677136871369713707137171372713737137471375713767137771378713797138071381713827138371384713857138671387713887138971390713917139271393713947139571396713977139871399714007140171402714037140471405714067140771408714097141071411714127141371414714157141671417714187141971420714217142271423714247142571426714277142871429714307143171432714337143471435714367143771438714397144071441714427144371444714457144671447714487144971450714517145271453714547145571456714577145871459714607146171462714637146471465714667146771468714697147071471714727147371474714757147671477714787147971480714817148271483714847148571486714877148871489714907149171492714937149471495714967149771498714997150071501715027150371504715057150671507715087150971510715117151271513715147151571516715177151871519715207152171522715237152471525715267152771528715297153071531715327153371534715357153671537715387153971540715417154271543715447154571546715477154871549715507155171552715537155471555715567155771558715597156071561715627156371564715657156671567715687156971570715717157271573715747157571576715777157871579715807158171582715837158471585715867158771588715897159071591715927159371594715957159671597715987159971600716017160271603716047160571606716077160871609716107161171612716137161471615716167161771618716197162071621716227162371624716257162671627716287162971630716317163271633716347163571636716377163871639716407164171642716437164471645716467164771648716497165071651716527165371654716557165671657716587165971660716617166271663716647166571666716677166871669716707167171672716737167471675716767167771678716797168071681716827168371684716857168671687716887168971690716917169271693716947169571696716977169871699717007170171702717037170471705717067170771708717097171071711717127171371714717157171671717717187171971720717217172271723717247172571726717277172871729717307173171732717337173471735717367173771738717397174071741717427174371744717457174671747717487174971750717517175271753717547175571756717577175871759717607176171762717637176471765717667176771768717697177071771717727177371774717757177671777717787177971780717817178271783717847178571786717877178871789717907179171792717937179471795717967179771798717997180071801718027180371804718057180671807718087180971810718117181271813718147181571816718177181871819718207182171822718237182471825718267182771828718297183071831718327183371834718357183671837718387183971840718417184271843718447184571846718477184871849718507185171852718537185471855718567185771858718597186071861718627186371864718657186671867718687186971870718717187271873718747187571876718777187871879718807188171882718837188471885718867188771888718897189071891718927189371894718957189671897718987189971900719017190271903719047190571906719077190871909719107191171912719137191471915719167191771918719197192071921719227192371924719257192671927719287192971930719317193271933719347193571936719377193871939719407194171942719437194471945719467194771948719497195071951719527195371954719557195671957719587195971960719617196271963719647196571966719677196871969719707197171972719737197471975719767197771978719797198071981719827198371984719857198671987719887198971990719917199271993719947199571996719977199871999720007200172002720037200472005720067200772008720097201072011720127201372014720157201672017720187201972020720217202272023720247202572026720277202872029720307203172032720337203472035720367203772038720397204072041720427204372044720457204672047720487204972050720517205272053720547205572056720577205872059720607206172062720637206472065720667206772068720697207072071720727207372074720757207672077720787207972080720817208272083720847208572086720877208872089720907209172092720937209472095720967209772098720997210072101721027210372104721057210672107721087210972110721117211272113721147211572116721177211872119721207212172122721237212472125721267212772128721297213072131721327213372134721357213672137721387213972140721417214272143721447214572146721477214872149721507215172152721537215472155721567215772158721597216072161721627216372164721657216672167721687216972170721717217272173721747217572176721777217872179721807218172182721837218472185721867218772188721897219072191721927219372194721957219672197721987219972200722017220272203722047220572206722077220872209722107221172212722137221472215722167221772218722197222072221722227222372224722257222672227722287222972230722317223272233722347223572236722377223872239722407224172242722437224472245722467224772248722497225072251722527225372254722557225672257722587225972260722617226272263722647226572266722677226872269722707227172272722737227472275722767227772278722797228072281722827228372284722857228672287722887228972290722917229272293722947229572296722977229872299723007230172302723037230472305723067230772308723097231072311723127231372314723157231672317723187231972320723217232272323723247232572326723277232872329723307233172332723337233472335723367233772338723397234072341723427234372344723457234672347723487234972350723517235272353723547235572356723577235872359723607236172362723637236472365723667236772368723697237072371723727237372374723757237672377723787237972380723817238272383723847238572386723877238872389723907239172392723937239472395723967239772398723997240072401724027240372404724057240672407724087240972410724117241272413724147241572416724177241872419724207242172422724237242472425724267242772428724297243072431724327243372434724357243672437724387243972440724417244272443724447244572446724477244872449724507245172452724537245472455724567245772458724597246072461724627246372464724657246672467724687246972470724717247272473724747247572476724777247872479724807248172482724837248472485724867248772488724897249072491724927249372494724957249672497724987249972500725017250272503725047250572506725077250872509725107251172512725137251472515725167251772518725197252072521725227252372524725257252672527725287252972530725317253272533725347253572536725377253872539725407254172542725437254472545725467254772548725497255072551725527255372554725557255672557725587255972560725617256272563725647256572566725677256872569725707257172572725737257472575725767257772578725797258072581725827258372584725857258672587725887258972590725917259272593725947259572596725977259872599726007260172602726037260472605726067260772608726097261072611726127261372614726157261672617726187261972620726217262272623726247262572626726277262872629726307263172632726337263472635726367263772638726397264072641726427264372644726457264672647726487264972650726517265272653726547265572656726577265872659726607266172662726637266472665726667266772668726697267072671726727267372674726757267672677726787267972680726817268272683726847268572686726877268872689726907269172692726937269472695726967269772698726997270072701727027270372704727057270672707727087270972710727117271272713727147271572716727177271872719727207272172722727237272472725727267272772728727297273072731727327273372734727357273672737727387273972740727417274272743727447274572746727477274872749727507275172752727537275472755727567275772758727597276072761727627276372764727657276672767727687276972770727717277272773727747277572776727777277872779727807278172782727837278472785727867278772788727897279072791727927279372794727957279672797727987279972800728017280272803728047280572806728077280872809728107281172812728137281472815728167281772818728197282072821728227282372824728257282672827728287282972830728317283272833728347283572836728377283872839728407284172842728437284472845728467284772848728497285072851728527285372854728557285672857728587285972860728617286272863728647286572866728677286872869728707287172872728737287472875728767287772878728797288072881728827288372884728857288672887728887288972890728917289272893728947289572896728977289872899729007290172902729037290472905729067290772908729097291072911729127291372914729157291672917729187291972920729217292272923729247292572926729277292872929729307293172932729337293472935729367293772938729397294072941729427294372944729457294672947729487294972950729517295272953729547295572956729577295872959729607296172962729637296472965729667296772968729697297072971729727297372974729757297672977729787297972980729817298272983729847298572986729877298872989729907299172992729937299472995729967299772998729997300073001730027300373004730057300673007730087300973010730117301273013730147301573016730177301873019730207302173022730237302473025730267302773028730297303073031730327303373034730357303673037730387303973040730417304273043730447304573046730477304873049730507305173052730537305473055730567305773058730597306073061730627306373064730657306673067730687306973070730717307273073730747307573076730777307873079730807308173082730837308473085730867308773088730897309073091730927309373094730957309673097730987309973100731017310273103731047310573106731077310873109731107311173112731137311473115731167311773118731197312073121731227312373124731257312673127731287312973130731317313273133731347313573136731377313873139731407314173142731437314473145731467314773148731497315073151731527315373154731557315673157731587315973160731617316273163731647316573166731677316873169731707317173172731737317473175731767317773178731797318073181731827318373184731857318673187731887318973190731917319273193731947319573196731977319873199732007320173202732037320473205732067320773208732097321073211732127321373214732157321673217732187321973220732217322273223732247322573226732277322873229732307323173232732337323473235732367323773238732397324073241732427324373244732457324673247732487324973250732517325273253732547325573256732577325873259732607326173262732637326473265732667326773268732697327073271732727327373274732757327673277732787327973280732817328273283732847328573286732877328873289732907329173292732937329473295732967329773298732997330073301733027330373304733057330673307733087330973310733117331273313733147331573316733177331873319733207332173322733237332473325733267332773328733297333073331733327333373334733357333673337733387333973340733417334273343733447334573346733477334873349733507335173352733537335473355733567335773358733597336073361733627336373364733657336673367733687336973370733717337273373733747337573376733777337873379733807338173382733837338473385733867338773388733897339073391733927339373394733957339673397733987339973400734017340273403734047340573406734077340873409734107341173412734137341473415734167341773418734197342073421734227342373424734257342673427734287342973430734317343273433734347343573436734377343873439734407344173442734437344473445734467344773448734497345073451734527345373454734557345673457734587345973460734617346273463734647346573466734677346873469734707347173472734737347473475734767347773478734797348073481734827348373484734857348673487734887348973490734917349273493734947349573496734977349873499735007350173502735037350473505735067350773508735097351073511735127351373514735157351673517735187351973520735217352273523735247352573526735277352873529735307353173532735337353473535735367353773538735397354073541735427354373544735457354673547735487354973550735517355273553735547355573556735577355873559735607356173562735637356473565735667356773568735697357073571735727357373574735757357673577735787357973580735817358273583735847358573586735877358873589735907359173592735937359473595735967359773598735997360073601736027360373604736057360673607736087360973610736117361273613736147361573616736177361873619736207362173622736237362473625736267362773628736297363073631736327363373634736357363673637736387363973640736417364273643736447364573646736477364873649736507365173652736537365473655736567365773658736597366073661736627366373664736657366673667736687366973670736717367273673736747367573676736777367873679736807368173682736837368473685736867368773688736897369073691736927369373694736957369673697736987369973700737017370273703737047370573706737077370873709737107371173712737137371473715737167371773718737197372073721737227372373724737257372673727737287372973730737317373273733737347373573736737377373873739737407374173742737437374473745737467374773748737497375073751737527375373754737557375673757737587375973760737617376273763737647376573766737677376873769737707377173772737737377473775737767377773778737797378073781737827378373784737857378673787737887378973790737917379273793737947379573796737977379873799738007380173802738037380473805738067380773808738097381073811738127381373814738157381673817738187381973820738217382273823738247382573826738277382873829738307383173832738337383473835738367383773838738397384073841738427384373844738457384673847738487384973850738517385273853738547385573856738577385873859738607386173862738637386473865738667386773868738697387073871738727387373874738757387673877738787387973880738817388273883738847388573886738877388873889738907389173892738937389473895738967389773898738997390073901739027390373904739057390673907739087390973910739117391273913739147391573916739177391873919739207392173922739237392473925739267392773928739297393073931739327393373934739357393673937739387393973940739417394273943739447394573946739477394873949739507395173952739537395473955739567395773958739597396073961739627396373964739657396673967739687396973970739717397273973739747397573976739777397873979739807398173982739837398473985739867398773988739897399073991739927399373994739957399673997739987399974000740017400274003740047400574006740077400874009740107401174012740137401474015740167401774018740197402074021740227402374024740257402674027740287402974030740317403274033740347403574036740377403874039740407404174042740437404474045740467404774048740497405074051740527405374054740557405674057740587405974060740617406274063740647406574066740677406874069740707407174072740737407474075740767407774078740797408074081740827408374084740857408674087740887408974090740917409274093740947409574096740977409874099741007410174102741037410474105741067410774108741097411074111741127411374114741157411674117741187411974120741217412274123741247412574126741277412874129741307413174132741337413474135741367413774138741397414074141741427414374144741457414674147741487414974150741517415274153741547415574156741577415874159741607416174162741637416474165741667416774168741697417074171741727417374174741757417674177741787417974180741817418274183741847418574186741877418874189741907419174192741937419474195741967419774198741997420074201742027420374204742057420674207742087420974210742117421274213742147421574216742177421874219742207422174222742237422474225742267422774228742297423074231742327423374234742357423674237742387423974240742417424274243742447424574246742477424874249742507425174252742537425474255742567425774258742597426074261742627426374264742657426674267742687426974270742717427274273742747427574276742777427874279742807428174282742837428474285742867428774288742897429074291742927429374294742957429674297742987429974300743017430274303743047430574306743077430874309743107431174312743137431474315743167431774318743197432074321743227432374324743257432674327743287432974330743317433274333743347433574336743377433874339743407434174342743437434474345743467434774348743497435074351743527435374354743557435674357743587435974360743617436274363743647436574366743677436874369743707437174372743737437474375743767437774378743797438074381743827438374384743857438674387743887438974390743917439274393743947439574396743977439874399744007440174402744037440474405744067440774408744097441074411744127441374414744157441674417744187441974420744217442274423744247442574426744277442874429744307443174432744337443474435744367443774438744397444074441744427444374444744457444674447744487444974450744517445274453744547445574456744577445874459744607446174462744637446474465744667446774468744697447074471744727447374474744757447674477744787447974480744817448274483744847448574486744877448874489744907449174492744937449474495744967449774498744997450074501745027450374504745057450674507745087450974510745117451274513745147451574516745177451874519745207452174522745237452474525745267452774528745297453074531745327453374534745357453674537745387453974540745417454274543745447454574546745477454874549745507455174552745537455474555745567455774558745597456074561745627456374564745657456674567745687456974570745717457274573745747457574576745777457874579745807458174582745837458474585745867458774588745897459074591745927459374594745957459674597745987459974600746017460274603746047460574606746077460874609746107461174612746137461474615746167461774618746197462074621746227462374624746257462674627746287462974630746317463274633746347463574636746377463874639746407464174642746437464474645746467464774648746497465074651746527465374654746557465674657746587465974660746617466274663746647466574666746677466874669746707467174672746737467474675746767467774678746797468074681746827468374684746857468674687746887468974690746917469274693746947469574696746977469874699747007470174702747037470474705747067470774708747097471074711747127471374714747157471674717747187471974720747217472274723747247472574726747277472874729747307473174732747337473474735747367473774738747397474074741747427474374744747457474674747747487474974750747517475274753747547475574756747577475874759747607476174762747637476474765747667476774768747697477074771747727477374774747757477674777747787477974780747817478274783747847478574786747877478874789747907479174792747937479474795747967479774798747997480074801748027480374804748057480674807748087480974810748117481274813748147481574816748177481874819748207482174822748237482474825748267482774828748297483074831748327483374834748357483674837748387483974840748417484274843748447484574846748477484874849748507485174852748537485474855748567485774858748597486074861748627486374864748657486674867748687486974870748717487274873748747487574876748777487874879748807488174882748837488474885748867488774888748897489074891748927489374894748957489674897748987489974900749017490274903749047490574906749077490874909749107491174912749137491474915749167491774918749197492074921749227492374924749257492674927749287492974930749317493274933749347493574936749377493874939749407494174942749437494474945749467494774948749497495074951749527495374954749557495674957749587495974960749617496274963749647496574966749677496874969749707497174972749737497474975749767497774978749797498074981749827498374984749857498674987749887498974990749917499274993749947499574996749977499874999750007500175002750037500475005750067500775008750097501075011750127501375014750157501675017750187501975020750217502275023750247502575026750277502875029750307503175032750337503475035750367503775038750397504075041750427504375044750457504675047750487504975050750517505275053750547505575056750577505875059750607506175062750637506475065750667506775068750697507075071750727507375074750757507675077750787507975080750817508275083750847508575086750877508875089750907509175092750937509475095750967509775098750997510075101751027510375104751057510675107751087510975110751117511275113751147511575116751177511875119751207512175122751237512475125751267512775128751297513075131751327513375134751357513675137751387513975140751417514275143751447514575146751477514875149751507515175152751537515475155751567515775158751597516075161751627516375164751657516675167751687516975170751717517275173751747517575176751777517875179751807518175182751837518475185751867518775188751897519075191751927519375194751957519675197751987519975200752017520275203752047520575206752077520875209752107521175212752137521475215752167521775218752197522075221752227522375224752257522675227752287522975230752317523275233752347523575236752377523875239752407524175242752437524475245752467524775248752497525075251752527525375254752557525675257752587525975260752617526275263752647526575266752677526875269752707527175272752737527475275752767527775278752797528075281752827528375284752857528675287752887528975290752917529275293752947529575296752977529875299753007530175302753037530475305753067530775308753097531075311753127531375314753157531675317753187531975320753217532275323753247532575326753277532875329753307533175332753337533475335753367533775338753397534075341753427534375344753457534675347753487534975350753517535275353753547535575356753577535875359753607536175362753637536475365753667536775368753697537075371753727537375374753757537675377753787537975380753817538275383753847538575386753877538875389753907539175392753937539475395753967539775398753997540075401754027540375404754057540675407754087540975410754117541275413754147541575416754177541875419754207542175422754237542475425754267542775428754297543075431754327543375434754357543675437754387543975440754417544275443754447544575446754477544875449754507545175452754537545475455754567545775458754597546075461754627546375464754657546675467754687546975470754717547275473754747547575476754777547875479754807548175482754837548475485754867548775488754897549075491754927549375494754957549675497754987549975500755017550275503755047550575506755077550875509755107551175512755137551475515755167551775518755197552075521755227552375524755257552675527755287552975530755317553275533755347553575536755377553875539755407554175542755437554475545755467554775548755497555075551755527555375554755557555675557755587555975560755617556275563755647556575566755677556875569755707557175572755737557475575755767557775578755797558075581755827558375584755857558675587755887558975590755917559275593755947559575596755977559875599756007560175602756037560475605756067560775608756097561075611756127561375614756157561675617756187561975620756217562275623756247562575626756277562875629756307563175632756337563475635756367563775638756397564075641756427564375644756457564675647756487564975650756517565275653756547565575656756577565875659756607566175662756637566475665756667566775668756697567075671756727567375674756757567675677756787567975680756817568275683756847568575686756877568875689756907569175692756937569475695756967569775698756997570075701757027570375704757057570675707757087570975710757117571275713757147571575716757177571875719757207572175722757237572475725757267572775728757297573075731757327573375734757357573675737757387573975740757417574275743757447574575746757477574875749757507575175752757537575475755757567575775758757597576075761757627576375764757657576675767757687576975770757717577275773757747577575776757777577875779757807578175782757837578475785757867578775788757897579075791757927579375794757957579675797757987579975800758017580275803758047580575806758077580875809758107581175812758137581475815758167581775818758197582075821758227582375824758257582675827758287582975830758317583275833758347583575836758377583875839758407584175842758437584475845758467584775848758497585075851758527585375854758557585675857758587585975860758617586275863758647586575866758677586875869758707587175872758737587475875758767587775878758797588075881758827588375884758857588675887758887588975890758917589275893758947589575896758977589875899759007590175902759037590475905759067590775908759097591075911759127591375914759157591675917759187591975920759217592275923759247592575926759277592875929759307593175932759337593475935759367593775938759397594075941759427594375944759457594675947759487594975950759517595275953759547595575956759577595875959759607596175962759637596475965759667596775968759697597075971759727597375974759757597675977759787597975980759817598275983759847598575986759877598875989759907599175992759937599475995759967599775998759997600076001760027600376004760057600676007760087600976010760117601276013760147601576016760177601876019760207602176022760237602476025760267602776028760297603076031760327603376034760357603676037760387603976040760417604276043760447604576046760477604876049760507605176052760537605476055760567605776058760597606076061760627606376064760657606676067760687606976070760717607276073760747607576076760777607876079760807608176082760837608476085760867608776088760897609076091760927609376094760957609676097760987609976100761017610276103761047610576106761077610876109761107611176112761137611476115761167611776118761197612076121761227612376124761257612676127761287612976130761317613276133761347613576136761377613876139761407614176142761437614476145761467614776148761497615076151761527615376154761557615676157761587615976160761617616276163761647616576166761677616876169761707617176172761737617476175761767617776178761797618076181761827618376184761857618676187761887618976190761917619276193761947619576196761977619876199762007620176202762037620476205762067620776208762097621076211762127621376214762157621676217762187621976220762217622276223762247622576226762277622876229762307623176232762337623476235762367623776238762397624076241762427624376244762457624676247762487624976250762517625276253762547625576256762577625876259762607626176262762637626476265762667626776268762697627076271762727627376274762757627676277762787627976280762817628276283762847628576286762877628876289762907629176292762937629476295762967629776298762997630076301763027630376304763057630676307763087630976310763117631276313763147631576316763177631876319763207632176322763237632476325763267632776328763297633076331763327633376334763357633676337763387633976340763417634276343763447634576346763477634876349763507635176352763537635476355763567635776358763597636076361763627636376364763657636676367763687636976370763717637276373763747637576376763777637876379763807638176382763837638476385763867638776388763897639076391763927639376394763957639676397763987639976400764017640276403764047640576406764077640876409764107641176412764137641476415764167641776418764197642076421764227642376424764257642676427764287642976430764317643276433764347643576436764377643876439764407644176442764437644476445764467644776448764497645076451764527645376454764557645676457764587645976460764617646276463764647646576466764677646876469764707647176472764737647476475764767647776478764797648076481764827648376484764857648676487764887648976490764917649276493764947649576496764977649876499765007650176502765037650476505765067650776508765097651076511765127651376514765157651676517765187651976520765217652276523765247652576526765277652876529765307653176532765337653476535765367653776538765397654076541765427654376544765457654676547765487654976550765517655276553765547655576556765577655876559765607656176562765637656476565765667656776568765697657076571765727657376574765757657676577765787657976580765817658276583765847658576586765877658876589765907659176592765937659476595765967659776598765997660076601766027660376604766057660676607766087660976610766117661276613766147661576616766177661876619766207662176622766237662476625766267662776628766297663076631766327663376634766357663676637766387663976640766417664276643766447664576646766477664876649766507665176652766537665476655766567665776658766597666076661766627666376664766657666676667766687666976670766717667276673766747667576676766777667876679766807668176682766837668476685766867668776688766897669076691766927669376694766957669676697766987669976700767017670276703767047670576706767077670876709767107671176712767137671476715767167671776718767197672076721767227672376724767257672676727767287672976730767317673276733767347673576736767377673876739767407674176742767437674476745767467674776748767497675076751767527675376754767557675676757767587675976760767617676276763767647676576766767677676876769767707677176772767737677476775767767677776778767797678076781767827678376784767857678676787767887678976790767917679276793767947679576796767977679876799768007680176802768037680476805768067680776808768097681076811768127681376814768157681676817768187681976820768217682276823768247682576826768277682876829768307683176832768337683476835768367683776838768397684076841768427684376844768457684676847768487684976850768517685276853768547685576856768577685876859768607686176862768637686476865768667686776868768697687076871768727687376874768757687676877768787687976880768817688276883768847688576886768877688876889768907689176892768937689476895768967689776898768997690076901769027690376904769057690676907769087690976910769117691276913769147691576916769177691876919769207692176922769237692476925769267692776928769297693076931769327693376934769357693676937769387693976940769417694276943769447694576946769477694876949769507695176952769537695476955769567695776958769597696076961769627696376964769657696676967769687696976970769717697276973769747697576976769777697876979769807698176982769837698476985769867698776988769897699076991769927699376994769957699676997769987699977000770017700277003770047700577006770077700877009770107701177012770137701477015770167701777018770197702077021770227702377024770257702677027770287702977030770317703277033770347703577036770377703877039770407704177042770437704477045770467704777048770497705077051770527705377054770557705677057770587705977060770617706277063770647706577066770677706877069770707707177072770737707477075770767707777078770797708077081770827708377084770857708677087770887708977090770917709277093770947709577096770977709877099771007710177102771037710477105771067710777108771097711077111771127711377114771157711677117771187711977120771217712277123771247712577126771277712877129771307713177132771337713477135771367713777138771397714077141771427714377144771457714677147771487714977150771517715277153771547715577156771577715877159771607716177162771637716477165771667716777168771697717077171771727717377174771757717677177771787717977180771817718277183771847718577186771877718877189771907719177192771937719477195771967719777198771997720077201772027720377204772057720677207772087720977210772117721277213772147721577216772177721877219772207722177222772237722477225772267722777228772297723077231772327723377234772357723677237772387723977240772417724277243772447724577246772477724877249772507725177252772537725477255772567725777258772597726077261772627726377264772657726677267772687726977270772717727277273772747727577276772777727877279772807728177282772837728477285772867728777288772897729077291772927729377294772957729677297772987729977300773017730277303773047730577306773077730877309773107731177312773137731477315773167731777318773197732077321773227732377324773257732677327773287732977330773317733277333773347733577336773377733877339773407734177342773437734477345773467734777348773497735077351773527735377354773557735677357773587735977360773617736277363773647736577366773677736877369773707737177372773737737477375773767737777378773797738077381773827738377384773857738677387773887738977390773917739277393773947739577396773977739877399774007740177402774037740477405774067740777408774097741077411774127741377414774157741677417774187741977420774217742277423774247742577426774277742877429774307743177432774337743477435774367743777438774397744077441774427744377444774457744677447774487744977450774517745277453774547745577456774577745877459774607746177462774637746477465774667746777468774697747077471774727747377474774757747677477774787747977480774817748277483774847748577486774877748877489774907749177492774937749477495774967749777498774997750077501775027750377504775057750677507775087750977510775117751277513775147751577516775177751877519775207752177522775237752477525775267752777528775297753077531775327753377534775357753677537775387753977540775417754277543775447754577546775477754877549775507755177552775537755477555775567755777558775597756077561775627756377564775657756677567775687756977570775717757277573775747757577576775777757877579775807758177582775837758477585775867758777588775897759077591775927759377594775957759677597775987759977600776017760277603776047760577606776077760877609776107761177612776137761477615776167761777618776197762077621776227762377624776257762677627776287762977630776317763277633776347763577636776377763877639776407764177642776437764477645776467764777648776497765077651776527765377654776557765677657776587765977660776617766277663776647766577666776677766877669776707767177672776737767477675776767767777678776797768077681776827768377684776857768677687776887768977690776917769277693776947769577696776977769877699777007770177702777037770477705777067770777708777097771077711777127771377714777157771677717777187771977720777217772277723777247772577726777277772877729777307773177732777337773477735777367773777738777397774077741777427774377744777457774677747777487774977750777517775277753777547775577756777577775877759777607776177762777637776477765777667776777768777697777077771777727777377774777757777677777777787777977780777817778277783777847778577786777877778877789777907779177792777937779477795777967779777798777997780077801778027780377804778057780677807778087780977810778117781277813778147781577816778177781877819778207782177822778237782477825778267782777828778297783077831778327783377834778357783677837778387783977840778417784277843778447784577846778477784877849778507785177852778537785477855778567785777858778597786077861778627786377864778657786677867778687786977870778717787277873778747787577876778777787877879778807788177882778837788477885778867788777888778897789077891778927789377894778957789677897778987789977900779017790277903779047790577906779077790877909779107791177912779137791477915779167791777918779197792077921779227792377924779257792677927779287792977930779317793277933779347793577936779377793877939779407794177942779437794477945779467794777948779497795077951779527795377954779557795677957779587795977960779617796277963779647796577966779677796877969779707797177972779737797477975779767797777978779797798077981779827798377984779857798677987779887798977990779917799277993779947799577996779977799877999780007800178002780037800478005780067800778008780097801078011780127801378014780157801678017780187801978020780217802278023780247802578026780277802878029780307803178032780337803478035780367803778038780397804078041780427804378044780457804678047780487804978050780517805278053780547805578056780577805878059780607806178062780637806478065780667806778068780697807078071780727807378074780757807678077780787807978080780817808278083780847808578086780877808878089780907809178092780937809478095780967809778098780997810078101781027810378104781057810678107781087810978110781117811278113781147811578116781177811878119781207812178122781237812478125781267812778128781297813078131781327813378134781357813678137781387813978140781417814278143781447814578146781477814878149781507815178152781537815478155781567815778158781597816078161781627816378164781657816678167781687816978170781717817278173781747817578176781777817878179781807818178182781837818478185781867818778188781897819078191781927819378194781957819678197781987819978200782017820278203782047820578206782077820878209782107821178212782137821478215782167821778218782197822078221782227822378224782257822678227782287822978230782317823278233782347823578236782377823878239782407824178242782437824478245782467824778248782497825078251782527825378254782557825678257782587825978260782617826278263782647826578266782677826878269782707827178272782737827478275782767827778278782797828078281782827828378284782857828678287782887828978290782917829278293782947829578296782977829878299783007830178302783037830478305783067830778308783097831078311783127831378314783157831678317783187831978320783217832278323783247832578326783277832878329783307833178332783337833478335783367833778338783397834078341783427834378344783457834678347783487834978350783517835278353783547835578356783577835878359783607836178362783637836478365783667836778368783697837078371783727837378374783757837678377783787837978380783817838278383783847838578386783877838878389783907839178392783937839478395783967839778398783997840078401784027840378404784057840678407784087840978410784117841278413784147841578416784177841878419784207842178422784237842478425784267842778428784297843078431784327843378434784357843678437784387843978440784417844278443784447844578446784477844878449784507845178452784537845478455784567845778458784597846078461784627846378464784657846678467784687846978470784717847278473784747847578476784777847878479784807848178482784837848478485784867848778488784897849078491784927849378494784957849678497784987849978500785017850278503785047850578506785077850878509785107851178512785137851478515785167851778518785197852078521785227852378524785257852678527785287852978530785317853278533785347853578536785377853878539785407854178542785437854478545785467854778548785497855078551785527855378554785557855678557785587855978560785617856278563785647856578566785677856878569785707857178572785737857478575785767857778578785797858078581785827858378584785857858678587785887858978590785917859278593785947859578596785977859878599786007860178602786037860478605786067860778608786097861078611786127861378614786157861678617786187861978620786217862278623786247862578626786277862878629786307863178632786337863478635786367863778638786397864078641786427864378644786457864678647786487864978650786517865278653786547865578656786577865878659786607866178662786637866478665786667866778668786697867078671786727867378674786757867678677786787867978680786817868278683786847868578686786877868878689786907869178692786937869478695786967869778698786997870078701787027870378704787057870678707787087870978710787117871278713787147871578716787177871878719787207872178722787237872478725787267872778728787297873078731787327873378734787357873678737787387873978740787417874278743787447874578746787477874878749787507875178752787537875478755787567875778758787597876078761787627876378764787657876678767787687876978770787717877278773787747877578776787777877878779787807878178782787837878478785787867878778788787897879078791787927879378794787957879678797787987879978800788017880278803788047880578806788077880878809788107881178812788137881478815788167881778818788197882078821788227882378824788257882678827788287882978830788317883278833788347883578836788377883878839788407884178842788437884478845788467884778848788497885078851788527885378854788557885678857788587885978860788617886278863788647886578866788677886878869788707887178872788737887478875788767887778878788797888078881788827888378884788857888678887788887888978890788917889278893788947889578896788977889878899789007890178902789037890478905789067890778908789097891078911789127891378914789157891678917789187891978920789217892278923789247892578926789277892878929789307893178932789337893478935789367893778938789397894078941789427894378944789457894678947789487894978950789517895278953789547895578956789577895878959789607896178962789637896478965789667896778968789697897078971789727897378974789757897678977789787897978980789817898278983789847898578986789877898878989789907899178992789937899478995789967899778998789997900079001790027900379004790057900679007790087900979010790117901279013790147901579016790177901879019790207902179022790237902479025790267902779028790297903079031790327903379034790357903679037790387903979040790417904279043790447904579046790477904879049790507905179052790537905479055790567905779058790597906079061790627906379064790657906679067790687906979070790717907279073790747907579076790777907879079790807908179082790837908479085790867908779088790897909079091790927909379094790957909679097790987909979100791017910279103791047910579106791077910879109791107911179112791137911479115791167911779118791197912079121791227912379124791257912679127791287912979130791317913279133791347913579136791377913879139791407914179142791437914479145791467914779148791497915079151791527915379154791557915679157791587915979160791617916279163791647916579166791677916879169791707917179172791737917479175791767917779178791797918079181791827918379184791857918679187791887918979190791917919279193791947919579196791977919879199792007920179202792037920479205792067920779208792097921079211792127921379214792157921679217792187921979220792217922279223792247922579226792277922879229792307923179232792337923479235792367923779238792397924079241792427924379244792457924679247792487924979250792517925279253792547925579256792577925879259792607926179262792637926479265792667926779268792697927079271792727927379274792757927679277792787927979280792817928279283792847928579286792877928879289792907929179292792937929479295792967929779298792997930079301793027930379304793057930679307793087930979310793117931279313793147931579316793177931879319793207932179322793237932479325793267932779328793297933079331793327933379334793357933679337793387933979340793417934279343793447934579346793477934879349793507935179352793537935479355793567935779358793597936079361793627936379364793657936679367793687936979370793717937279373793747937579376793777937879379793807938179382793837938479385793867938779388793897939079391793927939379394793957939679397793987939979400794017940279403794047940579406794077940879409794107941179412794137941479415794167941779418794197942079421794227942379424794257942679427794287942979430794317943279433794347943579436794377943879439794407944179442794437944479445794467944779448794497945079451794527945379454794557945679457794587945979460794617946279463794647946579466794677946879469794707947179472794737947479475794767947779478794797948079481794827948379484794857948679487794887948979490794917949279493794947949579496794977949879499795007950179502795037950479505795067950779508795097951079511795127951379514795157951679517795187951979520795217952279523795247952579526795277952879529795307953179532795337953479535795367953779538795397954079541795427954379544795457954679547795487954979550795517955279553795547955579556795577955879559795607956179562795637956479565795667956779568795697957079571795727957379574795757957679577795787957979580795817958279583795847958579586795877958879589795907959179592795937959479595795967959779598795997960079601796027960379604796057960679607796087960979610796117961279613796147961579616796177961879619796207962179622796237962479625796267962779628796297963079631796327963379634796357963679637796387963979640796417964279643796447964579646796477964879649796507965179652796537965479655796567965779658796597966079661796627966379664796657966679667796687966979670796717967279673796747967579676796777967879679796807968179682796837968479685796867968779688796897969079691796927969379694796957969679697796987969979700797017970279703797047970579706797077970879709797107971179712797137971479715797167971779718797197972079721797227972379724797257972679727797287972979730797317973279733797347973579736797377973879739797407974179742797437974479745797467974779748797497975079751797527975379754797557975679757797587975979760797617976279763797647976579766797677976879769797707977179772797737977479775797767977779778797797978079781797827978379784797857978679787797887978979790797917979279793797947979579796797977979879799798007980179802798037980479805798067980779808798097981079811798127981379814798157981679817798187981979820798217982279823798247982579826798277982879829798307983179832798337983479835798367983779838798397984079841798427984379844798457984679847798487984979850798517985279853798547985579856798577985879859798607986179862798637986479865798667986779868798697987079871798727987379874798757987679877798787987979880798817988279883798847988579886798877988879889798907989179892798937989479895798967989779898798997990079901799027990379904799057990679907799087990979910799117991279913799147991579916799177991879919799207992179922799237992479925799267992779928799297993079931799327993379934799357993679937799387993979940799417994279943799447994579946799477994879949799507995179952799537995479955799567995779958799597996079961799627996379964799657996679967799687996979970799717997279973799747997579976799777997879979799807998179982799837998479985799867998779988799897999079991799927999379994799957999679997799987999980000800018000280003800048000580006800078000880009800108001180012800138001480015800168001780018800198002080021800228002380024800258002680027800288002980030800318003280033800348003580036800378003880039800408004180042800438004480045800468004780048800498005080051800528005380054800558005680057800588005980060800618006280063800648006580066800678006880069800708007180072800738007480075800768007780078800798008080081800828008380084800858008680087800888008980090800918009280093800948009580096800978009880099801008010180102801038010480105801068010780108801098011080111801128011380114801158011680117801188011980120801218012280123801248012580126801278012880129801308013180132801338013480135801368013780138801398014080141801428014380144801458014680147801488014980150801518015280153801548015580156801578015880159801608016180162801638016480165801668016780168801698017080171801728017380174801758017680177801788017980180801818018280183801848018580186801878018880189801908019180192801938019480195801968019780198801998020080201802028020380204802058020680207802088020980210802118021280213802148021580216802178021880219802208022180222802238022480225802268022780228802298023080231802328023380234802358023680237802388023980240802418024280243802448024580246802478024880249802508025180252802538025480255802568025780258802598026080261802628026380264802658026680267802688026980270802718027280273802748027580276802778027880279802808028180282802838028480285802868028780288802898029080291802928029380294802958029680297802988029980300803018030280303803048030580306803078030880309803108031180312803138031480315803168031780318803198032080321803228032380324803258032680327803288032980330803318033280333803348033580336803378033880339803408034180342803438034480345803468034780348803498035080351803528035380354803558035680357803588035980360803618036280363803648036580366803678036880369803708037180372803738037480375803768037780378803798038080381803828038380384803858038680387803888038980390803918039280393803948039580396803978039880399804008040180402804038040480405804068040780408804098041080411804128041380414804158041680417804188041980420804218042280423804248042580426804278042880429804308043180432804338043480435804368043780438804398044080441804428044380444804458044680447804488044980450804518045280453804548045580456804578045880459804608046180462804638046480465804668046780468804698047080471804728047380474804758047680477804788047980480804818048280483804848048580486804878048880489804908049180492804938049480495804968049780498804998050080501805028050380504805058050680507805088050980510805118051280513805148051580516805178051880519805208052180522805238052480525805268052780528805298053080531805328053380534805358053680537805388053980540805418054280543805448054580546805478054880549805508055180552805538055480555805568055780558805598056080561805628056380564805658056680567805688056980570805718057280573805748057580576805778057880579805808058180582805838058480585805868058780588805898059080591805928059380594805958059680597805988059980600806018060280603806048060580606806078060880609806108061180612806138061480615806168061780618806198062080621806228062380624806258062680627806288062980630806318063280633806348063580636806378063880639806408064180642806438064480645806468064780648806498065080651806528065380654806558065680657806588065980660806618066280663806648066580666806678066880669806708067180672806738067480675806768067780678806798068080681806828068380684806858068680687806888068980690806918069280693806948069580696806978069880699807008070180702807038070480705807068070780708807098071080711807128071380714807158071680717807188071980720807218072280723807248072580726807278072880729807308073180732807338073480735807368073780738807398074080741807428074380744807458074680747807488074980750807518075280753807548075580756807578075880759807608076180762807638076480765807668076780768807698077080771807728077380774807758077680777807788077980780807818078280783807848078580786807878078880789807908079180792807938079480795807968079780798807998080080801808028080380804808058080680807808088080980810808118081280813808148081580816808178081880819808208082180822808238082480825808268082780828808298083080831808328083380834808358083680837808388083980840808418084280843808448084580846808478084880849808508085180852808538085480855808568085780858808598086080861808628086380864808658086680867808688086980870808718087280873808748087580876808778087880879808808088180882808838088480885808868088780888808898089080891808928089380894808958089680897808988089980900809018090280903809048090580906809078090880909809108091180912809138091480915809168091780918809198092080921809228092380924809258092680927809288092980930809318093280933809348093580936809378093880939809408094180942809438094480945809468094780948809498095080951809528095380954809558095680957809588095980960809618096280963809648096580966809678096880969809708097180972809738097480975809768097780978809798098080981809828098380984809858098680987809888098980990809918099280993809948099580996809978099880999810008100181002810038100481005810068100781008810098101081011810128101381014810158101681017810188101981020810218102281023810248102581026810278102881029810308103181032810338103481035810368103781038810398104081041810428104381044810458104681047810488104981050810518105281053810548105581056810578105881059810608106181062810638106481065810668106781068810698107081071810728107381074810758107681077810788107981080810818108281083810848108581086810878108881089810908109181092810938109481095810968109781098810998110081101811028110381104811058110681107811088110981110811118111281113811148111581116811178111881119811208112181122811238112481125811268112781128811298113081131811328113381134811358113681137811388113981140811418114281143811448114581146811478114881149811508115181152811538115481155811568115781158811598116081161811628116381164811658116681167811688116981170811718117281173811748117581176811778117881179811808118181182811838118481185811868118781188811898119081191811928119381194811958119681197811988119981200812018120281203812048120581206812078120881209812108121181212812138121481215812168121781218812198122081221812228122381224812258122681227812288122981230812318123281233812348123581236812378123881239812408124181242812438124481245812468124781248812498125081251812528125381254812558125681257812588125981260812618126281263812648126581266812678126881269812708127181272812738127481275812768127781278812798128081281812828128381284812858128681287812888128981290812918129281293812948129581296812978129881299813008130181302813038130481305813068130781308813098131081311813128131381314813158131681317813188131981320813218132281323813248132581326813278132881329813308133181332813338133481335813368133781338813398134081341813428134381344813458134681347813488134981350813518135281353813548135581356813578135881359813608136181362813638136481365813668136781368813698137081371813728137381374813758137681377813788137981380813818138281383813848138581386813878138881389813908139181392813938139481395813968139781398813998140081401814028140381404814058140681407814088140981410814118141281413814148141581416814178141881419814208142181422814238142481425814268142781428814298143081431814328143381434814358143681437814388143981440814418144281443814448144581446814478144881449814508145181452814538145481455814568145781458814598146081461814628146381464814658146681467814688146981470814718147281473814748147581476814778147881479814808148181482814838148481485814868148781488814898149081491814928149381494814958149681497814988149981500815018150281503815048150581506815078150881509815108151181512815138151481515815168151781518815198152081521815228152381524815258152681527815288152981530815318153281533815348153581536815378153881539815408154181542815438154481545815468154781548815498155081551815528155381554815558155681557815588155981560815618156281563815648156581566815678156881569815708157181572815738157481575815768157781578815798158081581815828158381584815858158681587815888158981590815918159281593815948159581596815978159881599816008160181602816038160481605816068160781608816098161081611816128161381614816158161681617816188161981620816218162281623816248162581626816278162881629816308163181632816338163481635816368163781638816398164081641816428164381644816458164681647816488164981650816518165281653816548165581656816578165881659816608166181662816638166481665816668166781668816698167081671816728167381674816758167681677816788167981680816818168281683816848168581686816878168881689816908169181692816938169481695816968169781698816998170081701817028170381704817058170681707817088170981710817118171281713817148171581716817178171881719817208172181722817238172481725817268172781728817298173081731817328173381734817358173681737817388173981740817418174281743817448174581746817478174881749817508175181752817538175481755817568175781758817598176081761817628176381764817658176681767817688176981770817718177281773817748177581776817778177881779817808178181782817838178481785817868178781788817898179081791817928179381794817958179681797817988179981800818018180281803818048180581806818078180881809818108181181812818138181481815818168181781818818198182081821818228182381824818258182681827818288182981830818318183281833818348183581836818378183881839818408184181842818438184481845818468184781848818498185081851818528185381854818558185681857818588185981860818618186281863818648186581866818678186881869818708187181872818738187481875818768187781878818798188081881818828188381884818858188681887818888188981890818918189281893818948189581896818978189881899819008190181902819038190481905819068190781908819098191081911819128191381914819158191681917819188191981920819218192281923819248192581926819278192881929819308193181932819338193481935819368193781938819398194081941819428194381944819458194681947819488194981950819518195281953819548195581956819578195881959819608196181962819638196481965819668196781968819698197081971819728197381974819758197681977819788197981980819818198281983819848198581986819878198881989819908199181992819938199481995819968199781998819998200082001820028200382004820058200682007820088200982010820118201282013820148201582016820178201882019820208202182022820238202482025820268202782028820298203082031820328203382034820358203682037820388203982040820418204282043820448204582046820478204882049820508205182052820538205482055820568205782058820598206082061820628206382064820658206682067820688206982070820718207282073820748207582076820778207882079820808208182082820838208482085820868208782088820898209082091820928209382094820958209682097820988209982100821018210282103821048210582106821078210882109821108211182112821138211482115821168211782118821198212082121821228212382124821258212682127821288212982130821318213282133821348213582136821378213882139821408214182142821438214482145821468214782148821498215082151821528215382154821558215682157821588215982160821618216282163821648216582166821678216882169821708217182172821738217482175821768217782178821798218082181821828218382184821858218682187821888218982190821918219282193821948219582196821978219882199822008220182202822038220482205822068220782208822098221082211822128221382214822158221682217822188221982220822218222282223822248222582226822278222882229822308223182232822338223482235822368223782238822398224082241822428224382244822458224682247822488224982250822518225282253822548225582256822578225882259822608226182262822638226482265822668226782268822698227082271822728227382274822758227682277822788227982280822818228282283822848228582286822878228882289822908229182292822938229482295822968229782298822998230082301823028230382304823058230682307823088230982310823118231282313823148231582316823178231882319823208232182322823238232482325823268232782328823298233082331823328233382334823358233682337823388233982340823418234282343823448234582346823478234882349823508235182352823538235482355823568235782358823598236082361823628236382364823658236682367823688236982370823718237282373823748237582376823778237882379823808238182382823838238482385823868238782388823898239082391823928239382394823958239682397823988239982400824018240282403824048240582406824078240882409824108241182412824138241482415824168241782418824198242082421824228242382424824258242682427824288242982430824318243282433824348243582436824378243882439824408244182442824438244482445824468244782448824498245082451824528245382454824558245682457824588245982460824618246282463824648246582466824678246882469824708247182472824738247482475824768247782478824798248082481824828248382484824858248682487824888248982490824918249282493824948249582496824978249882499825008250182502825038250482505825068250782508825098251082511825128251382514825158251682517825188251982520825218252282523825248252582526825278252882529825308253182532825338253482535825368253782538825398254082541825428254382544825458254682547825488254982550825518255282553825548255582556825578255882559825608256182562825638256482565825668256782568825698257082571825728257382574825758257682577825788257982580825818258282583825848258582586825878258882589825908259182592825938259482595825968259782598825998260082601 |
- /**
- * plotly.js (cartesian) v1.44.4
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- * Licensed under the MIT license
- */
- (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Plotly = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
- 'use strict';
-
- var Lib = _dereq_('../src/lib');
- var rules = {
- "X,X div": "direction:ltr;font-family:'Open Sans', verdana, arial, sans-serif;margin:0;padding:0;",
- "X input,X button": "font-family:'Open Sans', verdana, arial, sans-serif;",
- "X input:focus,X button:focus": "outline:none;",
- "X a": "text-decoration:none;",
- "X a:hover": "text-decoration:none;",
- "X .crisp": "shape-rendering:crispEdges;",
- "X .user-select-none": "-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;",
- "X svg": "overflow:hidden;",
- "X svg a": "fill:#447adb;",
- "X svg a:hover": "fill:#3c6dc5;",
- "X .main-svg": "position:absolute;top:0;left:0;pointer-events:none;",
- "X .main-svg .draglayer": "pointer-events:all;",
- "X .cursor-default": "cursor:default;",
- "X .cursor-pointer": "cursor:pointer;",
- "X .cursor-crosshair": "cursor:crosshair;",
- "X .cursor-move": "cursor:move;",
- "X .cursor-col-resize": "cursor:col-resize;",
- "X .cursor-row-resize": "cursor:row-resize;",
- "X .cursor-ns-resize": "cursor:ns-resize;",
- "X .cursor-ew-resize": "cursor:ew-resize;",
- "X .cursor-sw-resize": "cursor:sw-resize;",
- "X .cursor-s-resize": "cursor:s-resize;",
- "X .cursor-se-resize": "cursor:se-resize;",
- "X .cursor-w-resize": "cursor:w-resize;",
- "X .cursor-e-resize": "cursor:e-resize;",
- "X .cursor-nw-resize": "cursor:nw-resize;",
- "X .cursor-n-resize": "cursor:n-resize;",
- "X .cursor-ne-resize": "cursor:ne-resize;",
- "X .cursor-grab": "cursor:-webkit-grab;cursor:grab;",
- "X .modebar": "position:absolute;top:2px;right:2px;z-index:1001;",
- "X .ease-bg": "-webkit-transition:background-color 0.3s ease 0s;-moz-transition:background-color 0.3s ease 0s;-ms-transition:background-color 0.3s ease 0s;-o-transition:background-color 0.3s ease 0s;transition:background-color 0.3s ease 0s;",
- "X .modebar--hover>:not(.watermark)": "opacity:0;-webkit-transition:opacity 0.3s ease 0s;-moz-transition:opacity 0.3s ease 0s;-ms-transition:opacity 0.3s ease 0s;-o-transition:opacity 0.3s ease 0s;transition:opacity 0.3s ease 0s;",
- "X:hover .modebar--hover .modebar-group": "opacity:1;",
- "X .modebar-group": "float:left;display:inline-block;box-sizing:border-box;margin-left:8px;position:relative;vertical-align:middle;white-space:nowrap;",
- "X .modebar-btn": "position:relative;font-size:16px;padding:3px 4px;height:22px;cursor:pointer;line-height:normal;box-sizing:border-box;",
- "X .modebar-btn svg": "position:relative;top:2px;",
- "X .modebar.vertical": "display:flex;flex-direction:column;flex-wrap:wrap;align-content:flex-end;max-height:100%;",
- "X .modebar.vertical svg": "top:-1px;",
- "X .modebar.vertical .modebar-group": "display:block;float:none;margin-left:0px;margin-bottom:8px;",
- "X .modebar.vertical .modebar-group .modebar-btn": "display:block;text-align:center;",
- "X [data-title]:before,X [data-title]:after": "position:absolute;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-o-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);display:none;opacity:0;z-index:1001;pointer-events:none;top:110%;right:50%;",
- "X [data-title]:hover:before,X [data-title]:hover:after": "display:block;opacity:1;",
- "X [data-title]:before": "content:'';position:absolute;background:transparent;border:6px solid transparent;z-index:1002;margin-top:-12px;border-bottom-color:#69738a;margin-right:-6px;",
- "X [data-title]:after": "content:attr(data-title);background:#69738a;color:white;padding:8px 10px;font-size:12px;line-height:12px;white-space:nowrap;margin-right:-18px;border-radius:2px;",
- "X .vertical [data-title]:before,X .vertical [data-title]:after": "top:0%;right:200%;",
- "X .vertical [data-title]:before": "border:6px solid transparent;border-left-color:#69738a;margin-top:8px;margin-right:-30px;",
- "X .select-outline": "fill:none;stroke-width:1;shape-rendering:crispEdges;",
- "X .select-outline-1": "stroke:white;",
- "X .select-outline-2": "stroke:black;stroke-dasharray:2px 2px;",
- Y: "font-family:'Open Sans';position:fixed;top:50px;right:20px;z-index:10000;font-size:10pt;max-width:180px;",
- "Y p": "margin:0;",
- "Y .notifier-note": "min-width:180px;max-width:250px;border:1px solid #fff;z-index:3000;margin:0;background-color:#8c97af;background-color:rgba(140,151,175,0.9);color:#fff;padding:10px;overflow-wrap:break-word;word-wrap:break-word;-ms-hyphens:auto;-webkit-hyphens:auto;hyphens:auto;",
- "Y .notifier-close": "color:#fff;opacity:0.8;float:right;padding:0 5px;background:none;border:none;font-size:20px;font-weight:bold;line-height:20px;",
- "Y .notifier-close:hover": "color:#444;text-decoration:none;cursor:pointer;"
- };
-
- for(var selector in rules) {
- var fullSelector = selector.replace(/^,/,' ,')
- .replace(/X/g, '.js-plotly-plot .plotly')
- .replace(/Y/g, '.plotly-notifier');
- Lib.addStyleRule(fullSelector, rules[selector]);
- }
-
- },{"../src/lib":168}],2:[function(_dereq_,module,exports){
- 'use strict';
-
- module.exports = {
- 'undo': {
- 'width': 857.1,
- 'height': 1000,
- 'path': 'm857 350q0-87-34-166t-91-137-137-92-166-34q-96 0-183 41t-147 114q-4 6-4 13t5 11l76 77q6 5 14 5 9-1 13-7 41-53 100-82t126-29q58 0 110 23t92 61 61 91 22 111-22 111-61 91-92 61-110 23q-55 0-105-20t-90-57l77-77q17-16 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l72-72q60 57 137 88t159 31q87 0 166-34t137-92 91-137 34-166z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'home': {
- 'width': 928.6,
- 'height': 1000,
- 'path': 'm786 296v-267q0-15-11-26t-25-10h-214v214h-143v-214h-214q-15 0-25 10t-11 26v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-4-7 2-12 7l-35 41q-4 5-3 13t6 12l401 334q18 15 42 15t43-15l136-114v109q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q5-5 6-12t-4-13z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'camera-retro': {
- 'width': 1000,
- 'height': 1000,
- 'path': 'm518 386q0 8-5 13t-13 5q-37 0-63-27t-26-63q0-8 5-13t13-5 12 5 5 13q0 23 16 38t38 16q8 0 13 5t5 13z m125-73q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m-572-320h858v71h-858v-71z m643 320q0 89-62 152t-152 62-151-62-63-152 63-151 151-63 152 63 62 151z m-571 358h214v72h-214v-72z m-72-107h858v143h-462l-36-71h-360v-72z m929 143v-714q0-30-21-51t-50-21h-858q-29 0-50 21t-21 51v714q0 30 21 51t50 21h858q29 0 50-21t21-51z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'zoombox': {
- 'width': 1000,
- 'height': 1000,
- 'path': 'm1000-25l-250 251c40 63 63 138 63 218 0 224-182 406-407 406-224 0-406-182-406-406s183-406 407-406c80 0 155 22 218 62l250-250 125 125z m-812 250l0 438 437 0 0-438-437 0z m62 375l313 0 0-312-313 0 0 312z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'pan': {
- 'width': 1000,
- 'height': 1000,
- 'path': 'm1000 350l-187 188 0-125-250 0 0 250 125 0-188 187-187-187 125 0 0-250-250 0 0 125-188-188 186-187 0 125 252 0 0-250-125 0 187-188 188 188-125 0 0 250 250 0 0-126 187 188z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'zoom_plus': {
- 'width': 875,
- 'height': 1000,
- 'path': 'm1 787l0-875 875 0 0 875-875 0z m687-500l-187 0 0-187-125 0 0 187-188 0 0 125 188 0 0 187 125 0 0-187 187 0 0-125z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'zoom_minus': {
- 'width': 875,
- 'height': 1000,
- 'path': 'm0 788l0-876 875 0 0 876-875 0z m688-500l-500 0 0 125 500 0 0-125z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'autoscale': {
- 'width': 1000,
- 'height': 1000,
- 'path': 'm250 850l-187 0-63 0 0-62 0-188 63 0 0 188 187 0 0 62z m688 0l-188 0 0-62 188 0 0-188 62 0 0 188 0 62-62 0z m-875-938l0 188-63 0 0-188 0-62 63 0 187 0 0 62-187 0z m875 188l0-188-188 0 0-62 188 0 62 0 0 62 0 188-62 0z m-125 188l-1 0-93-94-156 156 156 156 92-93 2 0 0 250-250 0 0-2 93-92-156-156-156 156 94 92 0 2-250 0 0-250 0 0 93 93 157-156-157-156-93 94 0 0 0-250 250 0 0 0-94 93 156 157 156-157-93-93 0 0 250 0 0 250z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'tooltip_basic': {
- 'width': 1500,
- 'height': 1000,
- 'path': 'm375 725l0 0-375-375 375-374 0-1 1125 0 0 750-1125 0z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'tooltip_compare': {
- 'width': 1125,
- 'height': 1000,
- 'path': 'm187 786l0 2-187-188 188-187 0 0 937 0 0 373-938 0z m0-499l0 1-187-188 188-188 0 0 937 0 0 376-938-1z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'plotlylogo': {
- 'width': 1542,
- 'height': 1000,
- 'path': 'm0-10h182v-140h-182v140z m228 146h183v-286h-183v286z m225 714h182v-1000h-182v1000z m225-285h182v-715h-182v715z m225 142h183v-857h-183v857z m231-428h182v-429h-182v429z m225-291h183v-138h-183v138z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'z-axis': {
- 'width': 1000,
- 'height': 1000,
- 'path': 'm833 5l-17 108v41l-130-65 130-66c0 0 0 38 0 39 0-1 36-14 39-25 4-15-6-22-16-30-15-12-39-16-56-20-90-22-187-23-279-23-261 0-341 34-353 59 3 60 228 110 228 110-140-8-351-35-351-116 0-120 293-142 474-142 155 0 477 22 477 142 0 50-74 79-163 96z m-374 94c-58-5-99-21-99-40 0-24 65-43 144-43 79 0 143 19 143 43 0 19-42 34-98 40v216h87l-132 135-133-135h88v-216z m167 515h-136v1c16 16 31 34 46 52l84 109v54h-230v-71h124v-1c-16-17-28-32-44-51l-89-114v-51h245v72z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- '3d_rotate': {
- 'width': 1000,
- 'height': 1000,
- 'path': 'm922 660c-5 4-9 7-14 11-359 263-580-31-580-31l-102 28 58-400c0 1 1 1 2 2 118 108 351 249 351 249s-62 27-100 42c88 83 222 183 347 122 16-8 30-17 44-27-2 1-4 2-6 4z m36-329c0 0 64 229-88 296-62 27-124 14-175-11 157-78 225-208 249-266 8-19 11-31 11-31 2 5 6 15 11 32-5-13-8-20-8-20z m-775-239c70-31 117-50 198-32-121 80-199 346-199 346l-96-15-58-12c0 0 55-226 155-287z m603 133l-317-139c0 0 4-4 19-14 7-5 24-15 24-15s-177-147-389 4c235-287 536-112 536-112l31-22 100 299-4-1z m-298-153c6-4 14-9 24-15 0 0-17 10-24 15z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'camera': {
- 'width': 1000,
- 'height': 1000,
- 'path': 'm500 450c-83 0-150-67-150-150 0-83 67-150 150-150 83 0 150 67 150 150 0 83-67 150-150 150z m400 150h-120c-16 0-34 13-39 29l-31 93c-6 15-23 28-40 28h-340c-16 0-34-13-39-28l-31-94c-6-15-23-28-40-28h-120c-55 0-100-45-100-100v-450c0-55 45-100 100-100h800c55 0 100 45 100 100v450c0 55-45 100-100 100z m-400-550c-138 0-250 112-250 250 0 138 112 250 250 250 138 0 250-112 250-250 0-138-112-250-250-250z m365 380c-19 0-35 16-35 35 0 19 16 35 35 35 19 0 35-16 35-35 0-19-16-35-35-35z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'movie': {
- 'width': 1000,
- 'height': 1000,
- 'path': 'm938 413l-188-125c0 37-17 71-44 94 64 38 107 107 107 187 0 121-98 219-219 219-121 0-219-98-219-219 0-61 25-117 66-156h-115c30 33 49 76 49 125 0 103-84 187-187 187s-188-84-188-187c0-57 26-107 65-141-38-22-65-62-65-109v-250c0-70 56-126 125-126h500c69 0 125 56 125 126l188-126c34 0 62 28 62 63v375c0 35-28 63-62 63z m-750 0c-69 0-125 56-125 125s56 125 125 125 125-56 125-125-56-125-125-125z m406-1c-87 0-157 70-157 157 0 86 70 156 157 156s156-70 156-156-70-157-156-157z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'question': {
- 'width': 857.1,
- 'height': 1000,
- 'path': 'm500 82v107q0 8-5 13t-13 5h-107q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h107q8 0 13 5t5 13z m143 375q0 49-31 91t-77 65-95 23q-136 0-207-119-9-14 4-24l74-55q4-4 10-4 9 0 14 7 30 38 48 51 19 14 48 14 27 0 48-15t21-33q0-21-11-34t-38-25q-35-16-65-48t-29-70v-20q0-8 5-13t13-5h107q8 0 13 5t5 13q0 10 12 27t30 28q18 10 28 16t25 19 25 27 16 34 7 45z m214-107q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'disk': {
- 'width': 857.1,
- 'height': 1000,
- 'path': 'm214-7h429v214h-429v-214z m500 0h72v500q0 8-6 21t-11 20l-157 156q-5 6-19 12t-22 5v-232q0-22-15-38t-38-16h-322q-22 0-37 16t-16 38v232h-72v-714h72v232q0 22 16 38t37 16h465q22 0 38-16t15-38v-232z m-214 518v178q0 8-5 13t-13 5h-107q-7 0-13-5t-5-13v-178q0-8 5-13t13-5h107q7 0 13 5t5 13z m357-18v-518q0-22-15-38t-38-16h-750q-23 0-38 16t-16 38v750q0 22 16 38t38 16h517q23 0 50-12t42-26l156-157q16-15 27-42t11-49z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'lasso': {
- 'width': 1031,
- 'height': 1000,
- 'path': 'm1018 538c-36 207-290 336-568 286-277-48-473-256-436-463 10-57 36-108 76-151-13-66 11-137 68-183 34-28 75-41 114-42l-55-70 0 0c-2-1-3-2-4-3-10-14-8-34 5-45 14-11 34-8 45 4 1 1 2 3 2 5l0 0 113 140c16 11 31 24 45 40 4 3 6 7 8 11 48-3 100 0 151 9 278 48 473 255 436 462z m-624-379c-80 14-149 48-197 96 42 42 109 47 156 9 33-26 47-66 41-105z m-187-74c-19 16-33 37-39 60 50-32 109-55 174-68-42-25-95-24-135 8z m360 75c-34-7-69-9-102-8 8 62-16 128-68 170-73 59-175 54-244-5-9 20-16 40-20 61-28 159 121 317 333 354s407-60 434-217c28-159-121-318-333-355z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'selectbox': {
- 'width': 1000,
- 'height': 1000,
- 'path': 'm0 850l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m285 0l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m-857-286l0-143 143 0 0 143-143 0z m857 0l0-143 143 0 0 143-143 0z m-857-285l0-143 143 0 0 143-143 0z m857 0l0-143 143 0 0 143-143 0z m-857-286l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m285 0l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z',
- 'transform': 'matrix(1 0 0 -1 0 850)'
- },
- 'spikeline': {
- 'width': 1000,
- 'height': 1000,
- 'path': 'M512 409c0-57-46-104-103-104-57 0-104 47-104 104 0 57 47 103 104 103 57 0 103-46 103-103z m-327-39l92 0 0 92-92 0z m-185 0l92 0 0 92-92 0z m370-186l92 0 0 93-92 0z m0-184l92 0 0 92-92 0z',
- 'transform': 'matrix(1.5 0 0 -1.5 0 850)'
- },
- 'newplotlylogo': {
- 'name': 'newplotlylogo',
- 'svg': '<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 132 132\'><defs><style>.cls-1 {fill: #119dff;} .cls-2 {fill: #25fefd;} .cls-3 {fill: #fff;}</style></defs><title>plotly-logomark</title><g id=\'symbol\'><rect class=\'cls-1\' width=\'132\' height=\'132\' rx=\'6\' ry=\'6\'/><circle class=\'cls-2\' cx=\'78\' cy=\'54\' r=\'6\'/><circle class=\'cls-2\' cx=\'102\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'78\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'54\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'30\' cy=\'30\' r=\'6\'/><circle class=\'cls-2\' cx=\'30\' cy=\'54\' r=\'6\'/><path class=\'cls-3\' d=\'M30,72a6,6,0,0,0-6,6v24a6,6,0,0,0,12,0V78A6,6,0,0,0,30,72Z\'/><path class=\'cls-3\' d=\'M78,72a6,6,0,0,0-6,6v24a6,6,0,0,0,12,0V78A6,6,0,0,0,78,72Z\'/><path class=\'cls-3\' d=\'M54,48a6,6,0,0,0-6,6v48a6,6,0,0,0,12,0V54A6,6,0,0,0,54,48Z\'/><path class=\'cls-3\' d=\'M102,48a6,6,0,0,0-6,6v48a6,6,0,0,0,12,0V54A6,6,0,0,0,102,48Z\'/></g></svg>'
- }
- };
-
- },{}],3:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/traces/bar');
-
- },{"../src/traces/bar":274}],4:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/traces/box');
-
- },{"../src/traces/box":288}],5:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/traces/contour');
-
- },{"../src/traces/contour":308}],6:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/core');
-
- },{"../src/core":151}],7:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/traces/heatmap');
-
- },{"../src/traces/heatmap":324}],8:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/traces/histogram');
-
- },{"../src/traces/histogram":342}],9:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/traces/histogram2d');
-
- },{"../src/traces/histogram2d":349}],10:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/traces/histogram2dcontour');
-
- },{"../src/traces/histogram2dcontour":353}],11:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Plotly = _dereq_('./core');
-
- Plotly.register([
- _dereq_('./bar'),
- _dereq_('./box'),
- _dereq_('./heatmap'),
- _dereq_('./histogram'),
- _dereq_('./histogram2d'),
- _dereq_('./histogram2dcontour'),
- _dereq_('./pie'),
- _dereq_('./contour'),
- _dereq_('./scatterternary'),
- _dereq_('./violin')
- ]);
-
- module.exports = Plotly;
-
- },{"./bar":3,"./box":4,"./contour":5,"./core":6,"./heatmap":7,"./histogram":8,"./histogram2d":9,"./histogram2dcontour":10,"./pie":12,"./scatterternary":13,"./violin":14}],12:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/traces/pie');
-
- },{"../src/traces/pie":360}],13:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/traces/scatterternary');
-
- },{"../src/traces/scatterternary":399}],14:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = _dereq_('../src/traces/violin');
-
- },{"../src/traces/violin":407}],15:[function(_dereq_,module,exports){
- // Copyright Joyent, Inc. and other Node contributors.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a
- // copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to permit
- // persons to whom the Software is furnished to do so, subject to the
- // following conditions:
- //
- // The above copyright notice and this permission notice shall be included
- // in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
- // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
- // USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- var objectCreate = Object.create || objectCreatePolyfill
- var objectKeys = Object.keys || objectKeysPolyfill
- var bind = Function.prototype.bind || functionBindPolyfill
-
- function EventEmitter() {
- if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
- this._events = objectCreate(null);
- this._eventsCount = 0;
- }
-
- this._maxListeners = this._maxListeners || undefined;
- }
- module.exports = EventEmitter;
-
- // Backwards-compat with node 0.10.x
- EventEmitter.EventEmitter = EventEmitter;
-
- EventEmitter.prototype._events = undefined;
- EventEmitter.prototype._maxListeners = undefined;
-
- // By default EventEmitters will print a warning if more than 10 listeners are
- // added to it. This is a useful default which helps finding memory leaks.
- var defaultMaxListeners = 10;
-
- var hasDefineProperty;
- try {
- var o = {};
- if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
- hasDefineProperty = o.x === 0;
- } catch (err) { hasDefineProperty = false }
- if (hasDefineProperty) {
- Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
- enumerable: true,
- get: function() {
- return defaultMaxListeners;
- },
- set: function(arg) {
- // check whether the input is a positive number (whose value is zero or
- // greater and not a NaN).
- if (typeof arg !== 'number' || arg < 0 || arg !== arg)
- throw new TypeError('"defaultMaxListeners" must be a positive number');
- defaultMaxListeners = arg;
- }
- });
- } else {
- EventEmitter.defaultMaxListeners = defaultMaxListeners;
- }
-
- // Obviously not all Emitters should be limited to 10. This function allows
- // that to be increased. Set to zero for unlimited.
- EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
- if (typeof n !== 'number' || n < 0 || isNaN(n))
- throw new TypeError('"n" argument must be a positive number');
- this._maxListeners = n;
- return this;
- };
-
- function $getMaxListeners(that) {
- if (that._maxListeners === undefined)
- return EventEmitter.defaultMaxListeners;
- return that._maxListeners;
- }
-
- EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
- return $getMaxListeners(this);
- };
-
- // These standalone emit* functions are used to optimize calling of event
- // handlers for fast cases because emit() itself often has a variable number of
- // arguments and can be deoptimized because of that. These functions always have
- // the same number of arguments and thus do not get deoptimized, so the code
- // inside them can execute faster.
- function emitNone(handler, isFn, self) {
- if (isFn)
- handler.call(self);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self);
- }
- }
- function emitOne(handler, isFn, self, arg1) {
- if (isFn)
- handler.call(self, arg1);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self, arg1);
- }
- }
- function emitTwo(handler, isFn, self, arg1, arg2) {
- if (isFn)
- handler.call(self, arg1, arg2);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self, arg1, arg2);
- }
- }
- function emitThree(handler, isFn, self, arg1, arg2, arg3) {
- if (isFn)
- handler.call(self, arg1, arg2, arg3);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self, arg1, arg2, arg3);
- }
- }
-
- function emitMany(handler, isFn, self, args) {
- if (isFn)
- handler.apply(self, args);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].apply(self, args);
- }
- }
-
- EventEmitter.prototype.emit = function emit(type) {
- var er, handler, len, args, i, events;
- var doError = (type === 'error');
-
- events = this._events;
- if (events)
- doError = (doError && events.error == null);
- else if (!doError)
- return false;
-
- // If there is no 'error' event listener then throw.
- if (doError) {
- if (arguments.length > 1)
- er = arguments[1];
- if (er instanceof Error) {
- throw er; // Unhandled 'error' event
- } else {
- // At least give some kind of context to the user
- var err = new Error('Unhandled "error" event. (' + er + ')');
- err.context = er;
- throw err;
- }
- return false;
- }
-
- handler = events[type];
-
- if (!handler)
- return false;
-
- var isFn = typeof handler === 'function';
- len = arguments.length;
- switch (len) {
- // fast cases
- case 1:
- emitNone(handler, isFn, this);
- break;
- case 2:
- emitOne(handler, isFn, this, arguments[1]);
- break;
- case 3:
- emitTwo(handler, isFn, this, arguments[1], arguments[2]);
- break;
- case 4:
- emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
- break;
- // slower
- default:
- args = new Array(len - 1);
- for (i = 1; i < len; i++)
- args[i - 1] = arguments[i];
- emitMany(handler, isFn, this, args);
- }
-
- return true;
- };
-
- function _addListener(target, type, listener, prepend) {
- var m;
- var events;
- var existing;
-
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
-
- events = target._events;
- if (!events) {
- events = target._events = objectCreate(null);
- target._eventsCount = 0;
- } else {
- // To avoid recursion in the case that type === "newListener"! Before
- // adding it to the listeners, first emit "newListener".
- if (events.newListener) {
- target.emit('newListener', type,
- listener.listener ? listener.listener : listener);
-
- // Re-assign `events` because a newListener handler could have caused the
- // this._events to be assigned to a new object
- events = target._events;
- }
- existing = events[type];
- }
-
- if (!existing) {
- // Optimize the case of one listener. Don't need the extra array object.
- existing = events[type] = listener;
- ++target._eventsCount;
- } else {
- if (typeof existing === 'function') {
- // Adding the second element, need to change to array.
- existing = events[type] =
- prepend ? [listener, existing] : [existing, listener];
- } else {
- // If we've already got an array, just append.
- if (prepend) {
- existing.unshift(listener);
- } else {
- existing.push(listener);
- }
- }
-
- // Check for listener leak
- if (!existing.warned) {
- m = $getMaxListeners(target);
- if (m && m > 0 && existing.length > m) {
- existing.warned = true;
- var w = new Error('Possible EventEmitter memory leak detected. ' +
- existing.length + ' "' + String(type) + '" listeners ' +
- 'added. Use emitter.setMaxListeners() to ' +
- 'increase limit.');
- w.name = 'MaxListenersExceededWarning';
- w.emitter = target;
- w.type = type;
- w.count = existing.length;
- if (typeof console === 'object' && console.warn) {
- console.warn('%s: %s', w.name, w.message);
- }
- }
- }
- }
-
- return target;
- }
-
- EventEmitter.prototype.addListener = function addListener(type, listener) {
- return _addListener(this, type, listener, false);
- };
-
- EventEmitter.prototype.on = EventEmitter.prototype.addListener;
-
- EventEmitter.prototype.prependListener =
- function prependListener(type, listener) {
- return _addListener(this, type, listener, true);
- };
-
- function onceWrapper() {
- if (!this.fired) {
- this.target.removeListener(this.type, this.wrapFn);
- this.fired = true;
- switch (arguments.length) {
- case 0:
- return this.listener.call(this.target);
- case 1:
- return this.listener.call(this.target, arguments[0]);
- case 2:
- return this.listener.call(this.target, arguments[0], arguments[1]);
- case 3:
- return this.listener.call(this.target, arguments[0], arguments[1],
- arguments[2]);
- default:
- var args = new Array(arguments.length);
- for (var i = 0; i < args.length; ++i)
- args[i] = arguments[i];
- this.listener.apply(this.target, args);
- }
- }
- }
-
- function _onceWrap(target, type, listener) {
- var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
- var wrapped = bind.call(onceWrapper, state);
- wrapped.listener = listener;
- state.wrapFn = wrapped;
- return wrapped;
- }
-
- EventEmitter.prototype.once = function once(type, listener) {
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
- this.on(type, _onceWrap(this, type, listener));
- return this;
- };
-
- EventEmitter.prototype.prependOnceListener =
- function prependOnceListener(type, listener) {
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
- this.prependListener(type, _onceWrap(this, type, listener));
- return this;
- };
-
- // Emits a 'removeListener' event if and only if the listener was removed.
- EventEmitter.prototype.removeListener =
- function removeListener(type, listener) {
- var list, events, position, i, originalListener;
-
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
-
- events = this._events;
- if (!events)
- return this;
-
- list = events[type];
- if (!list)
- return this;
-
- if (list === listener || list.listener === listener) {
- if (--this._eventsCount === 0)
- this._events = objectCreate(null);
- else {
- delete events[type];
- if (events.removeListener)
- this.emit('removeListener', type, list.listener || listener);
- }
- } else if (typeof list !== 'function') {
- position = -1;
-
- for (i = list.length - 1; i >= 0; i--) {
- if (list[i] === listener || list[i].listener === listener) {
- originalListener = list[i].listener;
- position = i;
- break;
- }
- }
-
- if (position < 0)
- return this;
-
- if (position === 0)
- list.shift();
- else
- spliceOne(list, position);
-
- if (list.length === 1)
- events[type] = list[0];
-
- if (events.removeListener)
- this.emit('removeListener', type, originalListener || listener);
- }
-
- return this;
- };
-
- EventEmitter.prototype.removeAllListeners =
- function removeAllListeners(type) {
- var listeners, events, i;
-
- events = this._events;
- if (!events)
- return this;
-
- // not listening for removeListener, no need to emit
- if (!events.removeListener) {
- if (arguments.length === 0) {
- this._events = objectCreate(null);
- this._eventsCount = 0;
- } else if (events[type]) {
- if (--this._eventsCount === 0)
- this._events = objectCreate(null);
- else
- delete events[type];
- }
- return this;
- }
-
- // emit removeListener for all listeners on all events
- if (arguments.length === 0) {
- var keys = objectKeys(events);
- var key;
- for (i = 0; i < keys.length; ++i) {
- key = keys[i];
- if (key === 'removeListener') continue;
- this.removeAllListeners(key);
- }
- this.removeAllListeners('removeListener');
- this._events = objectCreate(null);
- this._eventsCount = 0;
- return this;
- }
-
- listeners = events[type];
-
- if (typeof listeners === 'function') {
- this.removeListener(type, listeners);
- } else if (listeners) {
- // LIFO order
- for (i = listeners.length - 1; i >= 0; i--) {
- this.removeListener(type, listeners[i]);
- }
- }
-
- return this;
- };
-
- function _listeners(target, type, unwrap) {
- var events = target._events;
-
- if (!events)
- return [];
-
- var evlistener = events[type];
- if (!evlistener)
- return [];
-
- if (typeof evlistener === 'function')
- return unwrap ? [evlistener.listener || evlistener] : [evlistener];
-
- return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
- }
-
- EventEmitter.prototype.listeners = function listeners(type) {
- return _listeners(this, type, true);
- };
-
- EventEmitter.prototype.rawListeners = function rawListeners(type) {
- return _listeners(this, type, false);
- };
-
- EventEmitter.listenerCount = function(emitter, type) {
- if (typeof emitter.listenerCount === 'function') {
- return emitter.listenerCount(type);
- } else {
- return listenerCount.call(emitter, type);
- }
- };
-
- EventEmitter.prototype.listenerCount = listenerCount;
- function listenerCount(type) {
- var events = this._events;
-
- if (events) {
- var evlistener = events[type];
-
- if (typeof evlistener === 'function') {
- return 1;
- } else if (evlistener) {
- return evlistener.length;
- }
- }
-
- return 0;
- }
-
- EventEmitter.prototype.eventNames = function eventNames() {
- return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
- };
-
- // About 1.5x faster than the two-arg version of Array#splice().
- function spliceOne(list, index) {
- for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
- list[i] = list[k];
- list.pop();
- }
-
- function arrayClone(arr, n) {
- var copy = new Array(n);
- for (var i = 0; i < n; ++i)
- copy[i] = arr[i];
- return copy;
- }
-
- function unwrapListeners(arr) {
- var ret = new Array(arr.length);
- for (var i = 0; i < ret.length; ++i) {
- ret[i] = arr[i].listener || arr[i];
- }
- return ret;
- }
-
- function objectCreatePolyfill(proto) {
- var F = function() {};
- F.prototype = proto;
- return new F;
- }
- function objectKeysPolyfill(obj) {
- var keys = [];
- for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
- keys.push(k);
- }
- return k;
- }
- function functionBindPolyfill(context) {
- var fn = this;
- return function () {
- return fn.apply(context, arguments);
- };
- }
-
- },{}],16:[function(_dereq_,module,exports){
- !function() {
- var d3 = {
- version: "3.5.17"
- };
- var d3_arraySlice = [].slice, d3_array = function(list) {
- return d3_arraySlice.call(list);
- };
- var d3_document = this.document;
- function d3_documentElement(node) {
- return node && (node.ownerDocument || node.document || node).documentElement;
- }
- function d3_window(node) {
- return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView);
- }
- if (d3_document) {
- try {
- d3_array(d3_document.documentElement.childNodes)[0].nodeType;
- } catch (e) {
- d3_array = function(list) {
- var i = list.length, array = new Array(i);
- while (i--) array[i] = list[i];
- return array;
- };
- }
- }
- if (!Date.now) Date.now = function() {
- return +new Date();
- };
- if (d3_document) {
- try {
- d3_document.createElement("DIV").style.setProperty("opacity", 0, "");
- } catch (error) {
- var d3_element_prototype = this.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = this.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty;
- d3_element_prototype.setAttribute = function(name, value) {
- d3_element_setAttribute.call(this, name, value + "");
- };
- d3_element_prototype.setAttributeNS = function(space, local, value) {
- d3_element_setAttributeNS.call(this, space, local, value + "");
- };
- d3_style_prototype.setProperty = function(name, value, priority) {
- d3_style_setProperty.call(this, name, value + "", priority);
- };
- }
- }
- d3.ascending = d3_ascending;
- function d3_ascending(a, b) {
- return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
- }
- d3.descending = function(a, b) {
- return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
- };
- d3.min = function(array, f) {
- var i = -1, n = array.length, a, b;
- if (arguments.length === 1) {
- while (++i < n) if ((b = array[i]) != null && b >= b) {
- a = b;
- break;
- }
- while (++i < n) if ((b = array[i]) != null && a > b) a = b;
- } else {
- while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
- a = b;
- break;
- }
- while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
- }
- return a;
- };
- d3.max = function(array, f) {
- var i = -1, n = array.length, a, b;
- if (arguments.length === 1) {
- while (++i < n) if ((b = array[i]) != null && b >= b) {
- a = b;
- break;
- }
- while (++i < n) if ((b = array[i]) != null && b > a) a = b;
- } else {
- while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
- a = b;
- break;
- }
- while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
- }
- return a;
- };
- d3.extent = function(array, f) {
- var i = -1, n = array.length, a, b, c;
- if (arguments.length === 1) {
- while (++i < n) if ((b = array[i]) != null && b >= b) {
- a = c = b;
- break;
- }
- while (++i < n) if ((b = array[i]) != null) {
- if (a > b) a = b;
- if (c < b) c = b;
- }
- } else {
- while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
- a = c = b;
- break;
- }
- while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
- if (a > b) a = b;
- if (c < b) c = b;
- }
- }
- return [ a, c ];
- };
- function d3_number(x) {
- return x === null ? NaN : +x;
- }
- function d3_numeric(x) {
- return !isNaN(x);
- }
- d3.sum = function(array, f) {
- var s = 0, n = array.length, a, i = -1;
- if (arguments.length === 1) {
- while (++i < n) if (d3_numeric(a = +array[i])) s += a;
- } else {
- while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
- }
- return s;
- };
- d3.mean = function(array, f) {
- var s = 0, n = array.length, a, i = -1, j = n;
- if (arguments.length === 1) {
- while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
- } else {
- while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
- }
- if (j) return s / j;
- };
- d3.quantile = function(values, p) {
- var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
- return e ? v + e * (values[h] - v) : v;
- };
- d3.median = function(array, f) {
- var numbers = [], n = array.length, a, i = -1;
- if (arguments.length === 1) {
- while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
- } else {
- while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
- }
- if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
- };
- d3.variance = function(array, f) {
- var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0;
- if (arguments.length === 1) {
- while (++i < n) {
- if (d3_numeric(a = d3_number(array[i]))) {
- d = a - m;
- m += d / ++j;
- s += d * (a - m);
- }
- }
- } else {
- while (++i < n) {
- if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
- d = a - m;
- m += d / ++j;
- s += d * (a - m);
- }
- }
- }
- if (j > 1) return s / (j - 1);
- };
- d3.deviation = function() {
- var v = d3.variance.apply(this, arguments);
- return v ? Math.sqrt(v) : v;
- };
- function d3_bisector(compare) {
- return {
- left: function(a, x, lo, hi) {
- if (arguments.length < 3) lo = 0;
- if (arguments.length < 4) hi = a.length;
- while (lo < hi) {
- var mid = lo + hi >>> 1;
- if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid;
- }
- return lo;
- },
- right: function(a, x, lo, hi) {
- if (arguments.length < 3) lo = 0;
- if (arguments.length < 4) hi = a.length;
- while (lo < hi) {
- var mid = lo + hi >>> 1;
- if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1;
- }
- return lo;
- }
- };
- }
- var d3_bisect = d3_bisector(d3_ascending);
- d3.bisectLeft = d3_bisect.left;
- d3.bisect = d3.bisectRight = d3_bisect.right;
- d3.bisector = function(f) {
- return d3_bisector(f.length === 1 ? function(d, x) {
- return d3_ascending(f(d), x);
- } : f);
- };
- d3.shuffle = function(array, i0, i1) {
- if ((m = arguments.length) < 3) {
- i1 = array.length;
- if (m < 2) i0 = 0;
- }
- var m = i1 - i0, t, i;
- while (m) {
- i = Math.random() * m-- | 0;
- t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
- }
- return array;
- };
- d3.permute = function(array, indexes) {
- var i = indexes.length, permutes = new Array(i);
- while (i--) permutes[i] = array[indexes[i]];
- return permutes;
- };
- d3.pairs = function(array) {
- var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
- while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ];
- return pairs;
- };
- d3.transpose = function(matrix) {
- if (!(n = matrix.length)) return [];
- for (var i = -1, m = d3.min(matrix, d3_transposeLength), transpose = new Array(m); ++i < m; ) {
- for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n; ) {
- row[j] = matrix[j][i];
- }
- }
- return transpose;
- };
- function d3_transposeLength(d) {
- return d.length;
- }
- d3.zip = function() {
- return d3.transpose(arguments);
- };
- d3.keys = function(map) {
- var keys = [];
- for (var key in map) keys.push(key);
- return keys;
- };
- d3.values = function(map) {
- var values = [];
- for (var key in map) values.push(map[key]);
- return values;
- };
- d3.entries = function(map) {
- var entries = [];
- for (var key in map) entries.push({
- key: key,
- value: map[key]
- });
- return entries;
- };
- d3.merge = function(arrays) {
- var n = arrays.length, m, i = -1, j = 0, merged, array;
- while (++i < n) j += arrays[i].length;
- merged = new Array(j);
- while (--n >= 0) {
- array = arrays[n];
- m = array.length;
- while (--m >= 0) {
- merged[--j] = array[m];
- }
- }
- return merged;
- };
- var abs = Math.abs;
- d3.range = function(start, stop, step) {
- if (arguments.length < 3) {
- step = 1;
- if (arguments.length < 2) {
- stop = start;
- start = 0;
- }
- }
- if ((stop - start) / step === Infinity) throw new Error("infinite range");
- var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
- start *= k, stop *= k, step *= k;
- if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
- return range;
- };
- function d3_range_integerScale(x) {
- var k = 1;
- while (x * k % 1) k *= 10;
- return k;
- }
- function d3_class(ctor, properties) {
- for (var key in properties) {
- Object.defineProperty(ctor.prototype, key, {
- value: properties[key],
- enumerable: false
- });
- }
- }
- d3.map = function(object, f) {
- var map = new d3_Map();
- if (object instanceof d3_Map) {
- object.forEach(function(key, value) {
- map.set(key, value);
- });
- } else if (Array.isArray(object)) {
- var i = -1, n = object.length, o;
- if (arguments.length === 1) while (++i < n) map.set(i, object[i]); else while (++i < n) map.set(f.call(object, o = object[i], i), o);
- } else {
- for (var key in object) map.set(key, object[key]);
- }
- return map;
- };
- function d3_Map() {
- this._ = Object.create(null);
- }
- var d3_map_proto = "__proto__", d3_map_zero = "\x00";
- d3_class(d3_Map, {
- has: d3_map_has,
- get: function(key) {
- return this._[d3_map_escape(key)];
- },
- set: function(key, value) {
- return this._[d3_map_escape(key)] = value;
- },
- remove: d3_map_remove,
- keys: d3_map_keys,
- values: function() {
- var values = [];
- for (var key in this._) values.push(this._[key]);
- return values;
- },
- entries: function() {
- var entries = [];
- for (var key in this._) entries.push({
- key: d3_map_unescape(key),
- value: this._[key]
- });
- return entries;
- },
- size: d3_map_size,
- empty: d3_map_empty,
- forEach: function(f) {
- for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
- }
- });
- function d3_map_escape(key) {
- return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
- }
- function d3_map_unescape(key) {
- return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
- }
- function d3_map_has(key) {
- return d3_map_escape(key) in this._;
- }
- function d3_map_remove(key) {
- return (key = d3_map_escape(key)) in this._ && delete this._[key];
- }
- function d3_map_keys() {
- var keys = [];
- for (var key in this._) keys.push(d3_map_unescape(key));
- return keys;
- }
- function d3_map_size() {
- var size = 0;
- for (var key in this._) ++size;
- return size;
- }
- function d3_map_empty() {
- for (var key in this._) return false;
- return true;
- }
- d3.nest = function() {
- var nest = {}, keys = [], sortKeys = [], sortValues, rollup;
- function map(mapType, array, depth) {
- if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
- var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values;
- while (++i < n) {
- if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
- values.push(object);
- } else {
- valuesByKey.set(keyValue, [ object ]);
- }
- }
- if (mapType) {
- object = mapType();
- setter = function(keyValue, values) {
- object.set(keyValue, map(mapType, values, depth));
- };
- } else {
- object = {};
- setter = function(keyValue, values) {
- object[keyValue] = map(mapType, values, depth);
- };
- }
- valuesByKey.forEach(setter);
- return object;
- }
- function entries(map, depth) {
- if (depth >= keys.length) return map;
- var array = [], sortKey = sortKeys[depth++];
- map.forEach(function(key, keyMap) {
- array.push({
- key: key,
- values: entries(keyMap, depth)
- });
- });
- return sortKey ? array.sort(function(a, b) {
- return sortKey(a.key, b.key);
- }) : array;
- }
- nest.map = function(array, mapType) {
- return map(mapType, array, 0);
- };
- nest.entries = function(array) {
- return entries(map(d3.map, array, 0), 0);
- };
- nest.key = function(d) {
- keys.push(d);
- return nest;
- };
- nest.sortKeys = function(order) {
- sortKeys[keys.length - 1] = order;
- return nest;
- };
- nest.sortValues = function(order) {
- sortValues = order;
- return nest;
- };
- nest.rollup = function(f) {
- rollup = f;
- return nest;
- };
- return nest;
- };
- d3.set = function(array) {
- var set = new d3_Set();
- if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
- return set;
- };
- function d3_Set() {
- this._ = Object.create(null);
- }
- d3_class(d3_Set, {
- has: d3_map_has,
- add: function(key) {
- this._[d3_map_escape(key += "")] = true;
- return key;
- },
- remove: d3_map_remove,
- values: d3_map_keys,
- size: d3_map_size,
- empty: d3_map_empty,
- forEach: function(f) {
- for (var key in this._) f.call(this, d3_map_unescape(key));
- }
- });
- d3.behavior = {};
- function d3_identity(d) {
- return d;
- }
- d3.rebind = function(target, source) {
- var i = 1, n = arguments.length, method;
- while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
- return target;
- };
- function d3_rebind(target, source, method) {
- return function() {
- var value = method.apply(source, arguments);
- return value === source ? target : value;
- };
- }
- function d3_vendorSymbol(object, name) {
- if (name in object) return name;
- name = name.charAt(0).toUpperCase() + name.slice(1);
- for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
- var prefixName = d3_vendorPrefixes[i] + name;
- if (prefixName in object) return prefixName;
- }
- }
- var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
- function d3_noop() {}
- d3.dispatch = function() {
- var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
- while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
- return dispatch;
- };
- function d3_dispatch() {}
- d3_dispatch.prototype.on = function(type, listener) {
- var i = type.indexOf("."), name = "";
- if (i >= 0) {
- name = type.slice(i + 1);
- type = type.slice(0, i);
- }
- if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
- if (arguments.length === 2) {
- if (listener == null) for (type in this) {
- if (this.hasOwnProperty(type)) this[type].on(name, null);
- }
- return this;
- }
- };
- function d3_dispatch_event(dispatch) {
- var listeners = [], listenerByName = new d3_Map();
- function event() {
- var z = listeners, i = -1, n = z.length, l;
- while (++i < n) if (l = z[i].on) l.apply(this, arguments);
- return dispatch;
- }
- event.on = function(name, listener) {
- var l = listenerByName.get(name), i;
- if (arguments.length < 2) return l && l.on;
- if (l) {
- l.on = null;
- listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
- listenerByName.remove(name);
- }
- if (listener) listeners.push(listenerByName.set(name, {
- on: listener
- }));
- return dispatch;
- };
- return event;
- }
- d3.event = null;
- function d3_eventPreventDefault() {
- d3.event.preventDefault();
- }
- function d3_eventSource() {
- var e = d3.event, s;
- while (s = e.sourceEvent) e = s;
- return e;
- }
- function d3_eventDispatch(target) {
- var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
- while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
- dispatch.of = function(thiz, argumentz) {
- return function(e1) {
- try {
- var e0 = e1.sourceEvent = d3.event;
- e1.target = target;
- d3.event = e1;
- dispatch[e1.type].apply(thiz, argumentz);
- } finally {
- d3.event = e0;
- }
- };
- };
- return dispatch;
- }
- d3.requote = function(s) {
- return s.replace(d3_requote_re, "\\$&");
- };
- var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
- var d3_subclass = {}.__proto__ ? function(object, prototype) {
- object.__proto__ = prototype;
- } : function(object, prototype) {
- for (var property in prototype) object[property] = prototype[property];
- };
- function d3_selection(groups) {
- d3_subclass(groups, d3_selectionPrototype);
- return groups;
- }
- var d3_select = function(s, n) {
- return n.querySelector(s);
- }, d3_selectAll = function(s, n) {
- return n.querySelectorAll(s);
- }, d3_selectMatches = function(n, s) {
- var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")];
- d3_selectMatches = function(n, s) {
- return d3_selectMatcher.call(n, s);
- };
- return d3_selectMatches(n, s);
- };
- if (typeof Sizzle === "function") {
- d3_select = function(s, n) {
- return Sizzle(s, n)[0] || null;
- };
- d3_selectAll = Sizzle;
- d3_selectMatches = Sizzle.matchesSelector;
- }
- d3.selection = function() {
- return d3.select(d3_document.documentElement);
- };
- var d3_selectionPrototype = d3.selection.prototype = [];
- d3_selectionPrototype.select = function(selector) {
- var subgroups = [], subgroup, subnode, group, node;
- selector = d3_selection_selector(selector);
- for (var j = -1, m = this.length; ++j < m; ) {
- subgroups.push(subgroup = []);
- subgroup.parentNode = (group = this[j]).parentNode;
- for (var i = -1, n = group.length; ++i < n; ) {
- if (node = group[i]) {
- subgroup.push(subnode = selector.call(node, node.__data__, i, j));
- if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
- } else {
- subgroup.push(null);
- }
- }
- }
- return d3_selection(subgroups);
- };
- function d3_selection_selector(selector) {
- return typeof selector === "function" ? selector : function() {
- return d3_select(selector, this);
- };
- }
- d3_selectionPrototype.selectAll = function(selector) {
- var subgroups = [], subgroup, node;
- selector = d3_selection_selectorAll(selector);
- for (var j = -1, m = this.length; ++j < m; ) {
- for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
- if (node = group[i]) {
- subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
- subgroup.parentNode = node;
- }
- }
- }
- return d3_selection(subgroups);
- };
- function d3_selection_selectorAll(selector) {
- return typeof selector === "function" ? selector : function() {
- return d3_selectAll(selector, this);
- };
- }
- var d3_nsXhtml = "http://www.w3.org/1999/xhtml";
- var d3_nsPrefix = {
- svg: "http://www.w3.org/2000/svg",
- xhtml: d3_nsXhtml,
- xlink: "http://www.w3.org/1999/xlink",
- xml: "http://www.w3.org/XML/1998/namespace",
- xmlns: "http://www.w3.org/2000/xmlns/"
- };
- d3.ns = {
- prefix: d3_nsPrefix,
- qualify: function(name) {
- var i = name.indexOf(":"), prefix = name;
- if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
- return d3_nsPrefix.hasOwnProperty(prefix) ? {
- space: d3_nsPrefix[prefix],
- local: name
- } : name;
- }
- };
- d3_selectionPrototype.attr = function(name, value) {
- if (arguments.length < 2) {
- if (typeof name === "string") {
- var node = this.node();
- name = d3.ns.qualify(name);
- return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name);
- }
- for (value in name) this.each(d3_selection_attr(value, name[value]));
- return this;
- }
- return this.each(d3_selection_attr(name, value));
- };
- function d3_selection_attr(name, value) {
- name = d3.ns.qualify(name);
- function attrNull() {
- this.removeAttribute(name);
- }
- function attrNullNS() {
- this.removeAttributeNS(name.space, name.local);
- }
- function attrConstant() {
- this.setAttribute(name, value);
- }
- function attrConstantNS() {
- this.setAttributeNS(name.space, name.local, value);
- }
- function attrFunction() {
- var x = value.apply(this, arguments);
- if (x == null) this.removeAttribute(name); else this.setAttribute(name, x);
- }
- function attrFunctionNS() {
- var x = value.apply(this, arguments);
- if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x);
- }
- return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant;
- }
- function d3_collapse(s) {
- return s.trim().replace(/\s+/g, " ");
- }
- d3_selectionPrototype.classed = function(name, value) {
- if (arguments.length < 2) {
- if (typeof name === "string") {
- var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1;
- if (value = node.classList) {
- while (++i < n) if (!value.contains(name[i])) return false;
- } else {
- value = node.getAttribute("class");
- while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
- }
- return true;
- }
- for (value in name) this.each(d3_selection_classed(value, name[value]));
- return this;
- }
- return this.each(d3_selection_classed(name, value));
- };
- function d3_selection_classedRe(name) {
- return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
- }
- function d3_selection_classes(name) {
- return (name + "").trim().split(/^|\s+/);
- }
- function d3_selection_classed(name, value) {
- name = d3_selection_classes(name).map(d3_selection_classedName);
- var n = name.length;
- function classedConstant() {
- var i = -1;
- while (++i < n) name[i](this, value);
- }
- function classedFunction() {
- var i = -1, x = value.apply(this, arguments);
- while (++i < n) name[i](this, x);
- }
- return typeof value === "function" ? classedFunction : classedConstant;
- }
- function d3_selection_classedName(name) {
- var re = d3_selection_classedRe(name);
- return function(node, value) {
- if (c = node.classList) return value ? c.add(name) : c.remove(name);
- var c = node.getAttribute("class") || "";
- if (value) {
- re.lastIndex = 0;
- if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
- } else {
- node.setAttribute("class", d3_collapse(c.replace(re, " ")));
- }
- };
- }
- d3_selectionPrototype.style = function(name, value, priority) {
- var n = arguments.length;
- if (n < 3) {
- if (typeof name !== "string") {
- if (n < 2) value = "";
- for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
- return this;
- }
- if (n < 2) {
- var node = this.node();
- return d3_window(node).getComputedStyle(node, null).getPropertyValue(name);
- }
- priority = "";
- }
- return this.each(d3_selection_style(name, value, priority));
- };
- function d3_selection_style(name, value, priority) {
- function styleNull() {
- this.style.removeProperty(name);
- }
- function styleConstant() {
- this.style.setProperty(name, value, priority);
- }
- function styleFunction() {
- var x = value.apply(this, arguments);
- if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority);
- }
- return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant;
- }
- d3_selectionPrototype.property = function(name, value) {
- if (arguments.length < 2) {
- if (typeof name === "string") return this.node()[name];
- for (value in name) this.each(d3_selection_property(value, name[value]));
- return this;
- }
- return this.each(d3_selection_property(name, value));
- };
- function d3_selection_property(name, value) {
- function propertyNull() {
- delete this[name];
- }
- function propertyConstant() {
- this[name] = value;
- }
- function propertyFunction() {
- var x = value.apply(this, arguments);
- if (x == null) delete this[name]; else this[name] = x;
- }
- return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant;
- }
- d3_selectionPrototype.text = function(value) {
- return arguments.length ? this.each(typeof value === "function" ? function() {
- var v = value.apply(this, arguments);
- this.textContent = v == null ? "" : v;
- } : value == null ? function() {
- this.textContent = "";
- } : function() {
- this.textContent = value;
- }) : this.node().textContent;
- };
- d3_selectionPrototype.html = function(value) {
- return arguments.length ? this.each(typeof value === "function" ? function() {
- var v = value.apply(this, arguments);
- this.innerHTML = v == null ? "" : v;
- } : value == null ? function() {
- this.innerHTML = "";
- } : function() {
- this.innerHTML = value;
- }) : this.node().innerHTML;
- };
- d3_selectionPrototype.append = function(name) {
- name = d3_selection_creator(name);
- return this.select(function() {
- return this.appendChild(name.apply(this, arguments));
- });
- };
- function d3_selection_creator(name) {
- function create() {
- var document = this.ownerDocument, namespace = this.namespaceURI;
- return namespace === d3_nsXhtml && document.documentElement.namespaceURI === d3_nsXhtml ? document.createElement(name) : document.createElementNS(namespace, name);
- }
- function createNS() {
- return this.ownerDocument.createElementNS(name.space, name.local);
- }
- return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create;
- }
- d3_selectionPrototype.insert = function(name, before) {
- name = d3_selection_creator(name);
- before = d3_selection_selector(before);
- return this.select(function() {
- return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
- });
- };
- d3_selectionPrototype.remove = function() {
- return this.each(d3_selectionRemove);
- };
- function d3_selectionRemove() {
- var parent = this.parentNode;
- if (parent) parent.removeChild(this);
- }
- d3_selectionPrototype.data = function(value, key) {
- var i = -1, n = this.length, group, node;
- if (!arguments.length) {
- value = new Array(n = (group = this[0]).length);
- while (++i < n) {
- if (node = group[i]) {
- value[i] = node.__data__;
- }
- }
- return value;
- }
- function bind(group, groupData) {
- var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData;
- if (key) {
- var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue;
- for (i = -1; ++i < n; ) {
- if (node = group[i]) {
- if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) {
- exitNodes[i] = node;
- } else {
- nodeByKeyValue.set(keyValue, node);
- }
- keyValues[i] = keyValue;
- }
- }
- for (i = -1; ++i < m; ) {
- if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) {
- enterNodes[i] = d3_selection_dataNode(nodeData);
- } else if (node !== true) {
- updateNodes[i] = node;
- node.__data__ = nodeData;
- }
- nodeByKeyValue.set(keyValue, true);
- }
- for (i = -1; ++i < n; ) {
- if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) {
- exitNodes[i] = group[i];
- }
- }
- } else {
- for (i = -1; ++i < n0; ) {
- node = group[i];
- nodeData = groupData[i];
- if (node) {
- node.__data__ = nodeData;
- updateNodes[i] = node;
- } else {
- enterNodes[i] = d3_selection_dataNode(nodeData);
- }
- }
- for (;i < m; ++i) {
- enterNodes[i] = d3_selection_dataNode(groupData[i]);
- }
- for (;i < n; ++i) {
- exitNodes[i] = group[i];
- }
- }
- enterNodes.update = updateNodes;
- enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode;
- enter.push(enterNodes);
- update.push(updateNodes);
- exit.push(exitNodes);
- }
- var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]);
- if (typeof value === "function") {
- while (++i < n) {
- bind(group = this[i], value.call(group, group.parentNode.__data__, i));
- }
- } else {
- while (++i < n) {
- bind(group = this[i], value);
- }
- }
- update.enter = function() {
- return enter;
- };
- update.exit = function() {
- return exit;
- };
- return update;
- };
- function d3_selection_dataNode(data) {
- return {
- __data__: data
- };
- }
- d3_selectionPrototype.datum = function(value) {
- return arguments.length ? this.property("__data__", value) : this.property("__data__");
- };
- d3_selectionPrototype.filter = function(filter) {
- var subgroups = [], subgroup, group, node;
- if (typeof filter !== "function") filter = d3_selection_filter(filter);
- for (var j = 0, m = this.length; j < m; j++) {
- subgroups.push(subgroup = []);
- subgroup.parentNode = (group = this[j]).parentNode;
- for (var i = 0, n = group.length; i < n; i++) {
- if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
- subgroup.push(node);
- }
- }
- }
- return d3_selection(subgroups);
- };
- function d3_selection_filter(selector) {
- return function() {
- return d3_selectMatches(this, selector);
- };
- }
- d3_selectionPrototype.order = function() {
- for (var j = -1, m = this.length; ++j < m; ) {
- for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) {
- if (node = group[i]) {
- if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
- next = node;
- }
- }
- }
- return this;
- };
- d3_selectionPrototype.sort = function(comparator) {
- comparator = d3_selection_sortComparator.apply(this, arguments);
- for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator);
- return this.order();
- };
- function d3_selection_sortComparator(comparator) {
- if (!arguments.length) comparator = d3_ascending;
- return function(a, b) {
- return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
- };
- }
- d3_selectionPrototype.each = function(callback) {
- return d3_selection_each(this, function(node, i, j) {
- callback.call(node, node.__data__, i, j);
- });
- };
- function d3_selection_each(groups, callback) {
- for (var j = 0, m = groups.length; j < m; j++) {
- for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
- if (node = group[i]) callback(node, i, j);
- }
- }
- return groups;
- }
- d3_selectionPrototype.call = function(callback) {
- var args = d3_array(arguments);
- callback.apply(args[0] = this, args);
- return this;
- };
- d3_selectionPrototype.empty = function() {
- return !this.node();
- };
- d3_selectionPrototype.node = function() {
- for (var j = 0, m = this.length; j < m; j++) {
- for (var group = this[j], i = 0, n = group.length; i < n; i++) {
- var node = group[i];
- if (node) return node;
- }
- }
- return null;
- };
- d3_selectionPrototype.size = function() {
- var n = 0;
- d3_selection_each(this, function() {
- ++n;
- });
- return n;
- };
- function d3_selection_enter(selection) {
- d3_subclass(selection, d3_selection_enterPrototype);
- return selection;
- }
- var d3_selection_enterPrototype = [];
- d3.selection.enter = d3_selection_enter;
- d3.selection.enter.prototype = d3_selection_enterPrototype;
- d3_selection_enterPrototype.append = d3_selectionPrototype.append;
- d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
- d3_selection_enterPrototype.node = d3_selectionPrototype.node;
- d3_selection_enterPrototype.call = d3_selectionPrototype.call;
- d3_selection_enterPrototype.size = d3_selectionPrototype.size;
- d3_selection_enterPrototype.select = function(selector) {
- var subgroups = [], subgroup, subnode, upgroup, group, node;
- for (var j = -1, m = this.length; ++j < m; ) {
- upgroup = (group = this[j]).update;
- subgroups.push(subgroup = []);
- subgroup.parentNode = group.parentNode;
- for (var i = -1, n = group.length; ++i < n; ) {
- if (node = group[i]) {
- subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
- subnode.__data__ = node.__data__;
- } else {
- subgroup.push(null);
- }
- }
- }
- return d3_selection(subgroups);
- };
- d3_selection_enterPrototype.insert = function(name, before) {
- if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
- return d3_selectionPrototype.insert.call(this, name, before);
- };
- function d3_selection_enterInsertBefore(enter) {
- var i0, j0;
- return function(d, i, j) {
- var group = enter[j].update, n = group.length, node;
- if (j != j0) j0 = j, i0 = 0;
- if (i >= i0) i0 = i + 1;
- while (!(node = group[i0]) && ++i0 < n) ;
- return node;
- };
- }
- d3.select = function(node) {
- var group;
- if (typeof node === "string") {
- group = [ d3_select(node, d3_document) ];
- group.parentNode = d3_document.documentElement;
- } else {
- group = [ node ];
- group.parentNode = d3_documentElement(node);
- }
- return d3_selection([ group ]);
- };
- d3.selectAll = function(nodes) {
- var group;
- if (typeof nodes === "string") {
- group = d3_array(d3_selectAll(nodes, d3_document));
- group.parentNode = d3_document.documentElement;
- } else {
- group = d3_array(nodes);
- group.parentNode = null;
- }
- return d3_selection([ group ]);
- };
- d3_selectionPrototype.on = function(type, listener, capture) {
- var n = arguments.length;
- if (n < 3) {
- if (typeof type !== "string") {
- if (n < 2) listener = false;
- for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
- return this;
- }
- if (n < 2) return (n = this.node()["__on" + type]) && n._;
- capture = false;
- }
- return this.each(d3_selection_on(type, listener, capture));
- };
- function d3_selection_on(type, listener, capture) {
- var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
- if (i > 0) type = type.slice(0, i);
- var filter = d3_selection_onFilters.get(type);
- if (filter) type = filter, wrap = d3_selection_onFilter;
- function onRemove() {
- var l = this[name];
- if (l) {
- this.removeEventListener(type, l, l.$);
- delete this[name];
- }
- }
- function onAdd() {
- var l = wrap(listener, d3_array(arguments));
- onRemove.call(this);
- this.addEventListener(type, this[name] = l, l.$ = capture);
- l._ = listener;
- }
- function removeAll() {
- var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match;
- for (var name in this) {
- if (match = name.match(re)) {
- var l = this[name];
- this.removeEventListener(match[1], l, l.$);
- delete this[name];
- }
- }
- }
- return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll;
- }
- var d3_selection_onFilters = d3.map({
- mouseenter: "mouseover",
- mouseleave: "mouseout"
- });
- if (d3_document) {
- d3_selection_onFilters.forEach(function(k) {
- if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
- });
- }
- function d3_selection_onListener(listener, argumentz) {
- return function(e) {
- var o = d3.event;
- d3.event = e;
- argumentz[0] = this.__data__;
- try {
- listener.apply(this, argumentz);
- } finally {
- d3.event = o;
- }
- };
- }
- function d3_selection_onFilter(listener, argumentz) {
- var l = d3_selection_onListener(listener, argumentz);
- return function(e) {
- var target = this, related = e.relatedTarget;
- if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) {
- l.call(target, e);
- }
- };
- }
- var d3_event_dragSelect, d3_event_dragId = 0;
- function d3_event_dragSuppress(node) {
- var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window(node)).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault);
- if (d3_event_dragSelect == null) {
- d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect");
- }
- if (d3_event_dragSelect) {
- var style = d3_documentElement(node).style, select = style[d3_event_dragSelect];
- style[d3_event_dragSelect] = "none";
- }
- return function(suppressClick) {
- w.on(name, null);
- if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
- if (suppressClick) {
- var off = function() {
- w.on(click, null);
- };
- w.on(click, function() {
- d3_eventPreventDefault();
- off();
- }, true);
- setTimeout(off, 0);
- }
- };
- }
- d3.mouse = function(container) {
- return d3_mousePoint(container, d3_eventSource());
- };
- var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;
- function d3_mousePoint(container, e) {
- if (e.changedTouches) e = e.changedTouches[0];
- var svg = container.ownerSVGElement || container;
- if (svg.createSVGPoint) {
- var point = svg.createSVGPoint();
- if (d3_mouse_bug44083 < 0) {
- var window = d3_window(container);
- if (window.scrollX || window.scrollY) {
- svg = d3.select("body").append("svg").style({
- position: "absolute",
- top: 0,
- left: 0,
- margin: 0,
- padding: 0,
- border: "none"
- }, "important");
- var ctm = svg[0][0].getScreenCTM();
- d3_mouse_bug44083 = !(ctm.f || ctm.e);
- svg.remove();
- }
- }
- if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX,
- point.y = e.clientY;
- point = point.matrixTransform(container.getScreenCTM().inverse());
- return [ point.x, point.y ];
- }
- var rect = container.getBoundingClientRect();
- return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
- }
- d3.touch = function(container, touches, identifier) {
- if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
- if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
- if ((touch = touches[i]).identifier === identifier) {
- return d3_mousePoint(container, touch);
- }
- }
- };
- d3.behavior.drag = function() {
- var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_window, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_identity, "touchmove", "touchend");
- function drag() {
- this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart);
- }
- function dragstart(id, position, subject, move, end) {
- return function() {
- var that = this, target = d3.event.target.correspondingElement || d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(target), position0 = position(parent, dragId);
- if (origin) {
- dragOffset = origin.apply(that, arguments);
- dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ];
- } else {
- dragOffset = [ 0, 0 ];
- }
- dispatch({
- type: "dragstart"
- });
- function moved() {
- var position1 = position(parent, dragId), dx, dy;
- if (!position1) return;
- dx = position1[0] - position0[0];
- dy = position1[1] - position0[1];
- dragged |= dx | dy;
- position0 = position1;
- dispatch({
- type: "drag",
- x: position1[0] + dragOffset[0],
- y: position1[1] + dragOffset[1],
- dx: dx,
- dy: dy
- });
- }
- function ended() {
- if (!position(parent, dragId)) return;
- dragSubject.on(move + dragName, null).on(end + dragName, null);
- dragRestore(dragged);
- dispatch({
- type: "dragend"
- });
- }
- };
- }
- drag.origin = function(x) {
- if (!arguments.length) return origin;
- origin = x;
- return drag;
- };
- return d3.rebind(drag, event, "on");
- };
- function d3_behavior_dragTouchId() {
- return d3.event.changedTouches[0].identifier;
- }
- d3.touches = function(container, touches) {
- if (arguments.length < 2) touches = d3_eventSource().touches;
- return touches ? d3_array(touches).map(function(touch) {
- var point = d3_mousePoint(container, touch);
- point.identifier = touch.identifier;
- return point;
- }) : [];
- };
- var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π;
- function d3_sgn(x) {
- return x > 0 ? 1 : x < 0 ? -1 : 0;
- }
- function d3_cross2d(a, b, c) {
- return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
- }
- function d3_acos(x) {
- return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
- }
- function d3_asin(x) {
- return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
- }
- function d3_sinh(x) {
- return ((x = Math.exp(x)) - 1 / x) / 2;
- }
- function d3_cosh(x) {
- return ((x = Math.exp(x)) + 1 / x) / 2;
- }
- function d3_tanh(x) {
- return ((x = Math.exp(2 * x)) - 1) / (x + 1);
- }
- function d3_haversin(x) {
- return (x = Math.sin(x / 2)) * x;
- }
- var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4;
- d3.interpolateZoom = function(p0, p1) {
- var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2], dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, i, S;
- if (d2 < ε2) {
- S = Math.log(w1 / w0) / ρ;
- i = function(t) {
- return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * t * S) ];
- };
- } else {
- var d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
- S = (r1 - r0) / ρ;
- i = function(t) {
- var s = t * S, coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
- return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ];
- };
- }
- i.duration = S * 1e3;
- return i;
- };
- d3.behavior.zoom = function() {
- var view = {
- x: 0,
- y: 0,
- k: 1
- }, translate0, center0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, duration = 250, zooming = 0, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1;
- if (!d3_behavior_zoomWheel) {
- d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() {
- return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1);
- }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() {
- return d3.event.wheelDelta;
- }, "mousewheel") : (d3_behavior_zoomDelta = function() {
- return -d3.event.detail;
- }, "MozMousePixelScroll");
- }
- function zoom(g) {
- g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted);
- }
- zoom.event = function(g) {
- g.each(function() {
- var dispatch = event.of(this, arguments), view1 = view;
- if (d3_transitionInheritId) {
- d3.select(this).transition().each("start.zoom", function() {
- view = this.__chart__ || {
- x: 0,
- y: 0,
- k: 1
- };
- zoomstarted(dispatch);
- }).tween("zoom:zoom", function() {
- var dx = size[0], dy = size[1], cx = center0 ? center0[0] : dx / 2, cy = center0 ? center0[1] : dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]);
- return function(t) {
- var l = i(t), k = dx / l[2];
- this.__chart__ = view = {
- x: cx - l[0] * k,
- y: cy - l[1] * k,
- k: k
- };
- zoomed(dispatch);
- };
- }).each("interrupt.zoom", function() {
- zoomended(dispatch);
- }).each("end.zoom", function() {
- zoomended(dispatch);
- });
- } else {
- this.__chart__ = view;
- zoomstarted(dispatch);
- zoomed(dispatch);
- zoomended(dispatch);
- }
- });
- };
- zoom.translate = function(_) {
- if (!arguments.length) return [ view.x, view.y ];
- view = {
- x: +_[0],
- y: +_[1],
- k: view.k
- };
- rescale();
- return zoom;
- };
- zoom.scale = function(_) {
- if (!arguments.length) return view.k;
- view = {
- x: view.x,
- y: view.y,
- k: null
- };
- scaleTo(+_);
- rescale();
- return zoom;
- };
- zoom.scaleExtent = function(_) {
- if (!arguments.length) return scaleExtent;
- scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ];
- return zoom;
- };
- zoom.center = function(_) {
- if (!arguments.length) return center;
- center = _ && [ +_[0], +_[1] ];
- return zoom;
- };
- zoom.size = function(_) {
- if (!arguments.length) return size;
- size = _ && [ +_[0], +_[1] ];
- return zoom;
- };
- zoom.duration = function(_) {
- if (!arguments.length) return duration;
- duration = +_;
- return zoom;
- };
- zoom.x = function(z) {
- if (!arguments.length) return x1;
- x1 = z;
- x0 = z.copy();
- view = {
- x: 0,
- y: 0,
- k: 1
- };
- return zoom;
- };
- zoom.y = function(z) {
- if (!arguments.length) return y1;
- y1 = z;
- y0 = z.copy();
- view = {
- x: 0,
- y: 0,
- k: 1
- };
- return zoom;
- };
- function location(p) {
- return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ];
- }
- function point(l) {
- return [ l[0] * view.k + view.x, l[1] * view.k + view.y ];
- }
- function scaleTo(s) {
- view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
- }
- function translateTo(p, l) {
- l = point(l);
- view.x += p[0] - l[0];
- view.y += p[1] - l[1];
- }
- function zoomTo(that, p, l, k) {
- that.__chart__ = {
- x: view.x,
- y: view.y,
- k: view.k
- };
- scaleTo(Math.pow(2, k));
- translateTo(center0 = p, l);
- that = d3.select(that);
- if (duration > 0) that = that.transition().duration(duration);
- that.call(zoom.event);
- }
- function rescale() {
- if (x1) x1.domain(x0.range().map(function(x) {
- return (x - view.x) / view.k;
- }).map(x0.invert));
- if (y1) y1.domain(y0.range().map(function(y) {
- return (y - view.y) / view.k;
- }).map(y0.invert));
- }
- function zoomstarted(dispatch) {
- if (!zooming++) dispatch({
- type: "zoomstart"
- });
- }
- function zoomed(dispatch) {
- rescale();
- dispatch({
- type: "zoom",
- scale: view.k,
- translate: [ view.x, view.y ]
- });
- }
- function zoomended(dispatch) {
- if (!--zooming) dispatch({
- type: "zoomend"
- }), center0 = null;
- }
- function mousedowned() {
- var that = this, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that);
- d3_selection_interrupt.call(that);
- zoomstarted(dispatch);
- function moved() {
- dragged = 1;
- translateTo(d3.mouse(that), location0);
- zoomed(dispatch);
- }
- function ended() {
- subject.on(mousemove, null).on(mouseup, null);
- dragRestore(dragged);
- zoomended(dispatch);
- }
- }
- function touchstarted() {
- var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that), dragRestore = d3_event_dragSuppress(that);
- started();
- zoomstarted(dispatch);
- subject.on(mousedown, null).on(touchstart, started);
- function relocate() {
- var touches = d3.touches(that);
- scale0 = view.k;
- touches.forEach(function(t) {
- if (t.identifier in locations0) locations0[t.identifier] = location(t);
- });
- return touches;
- }
- function started() {
- var target = d3.event.target;
- d3.select(target).on(touchmove, moved).on(touchend, ended);
- targets.push(target);
- var changed = d3.event.changedTouches;
- for (var i = 0, n = changed.length; i < n; ++i) {
- locations0[changed[i].identifier] = null;
- }
- var touches = relocate(), now = Date.now();
- if (touches.length === 1) {
- if (now - touchtime < 500) {
- var p = touches[0];
- zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
- d3_eventPreventDefault();
- }
- touchtime = now;
- } else if (touches.length > 1) {
- var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1];
- distance0 = dx * dx + dy * dy;
- }
- }
- function moved() {
- var touches = d3.touches(that), p0, l0, p1, l1;
- d3_selection_interrupt.call(that);
- for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
- p1 = touches[i];
- if (l1 = locations0[p1.identifier]) {
- if (l0) break;
- p0 = p1, l0 = l1;
- }
- }
- if (l1) {
- var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0);
- p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ];
- l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ];
- scaleTo(scale1 * scale0);
- }
- touchtime = null;
- translateTo(p0, l0);
- zoomed(dispatch);
- }
- function ended() {
- if (d3.event.touches.length) {
- var changed = d3.event.changedTouches;
- for (var i = 0, n = changed.length; i < n; ++i) {
- delete locations0[changed[i].identifier];
- }
- for (var identifier in locations0) {
- return void relocate();
- }
- }
- d3.selectAll(targets).on(zoomName, null);
- subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
- dragRestore();
- zoomended(dispatch);
- }
- }
- function mousewheeled() {
- var dispatch = event.of(this, arguments);
- if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this),
- translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch);
- mousewheelTimer = setTimeout(function() {
- mousewheelTimer = null;
- zoomended(dispatch);
- }, 50);
- d3_eventPreventDefault();
- scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
- translateTo(center0, translate0);
- zoomed(dispatch);
- }
- function dblclicked() {
- var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2;
- zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
- }
- return d3.rebind(zoom, event, "on");
- };
- var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel;
- d3.color = d3_color;
- function d3_color() {}
- d3_color.prototype.toString = function() {
- return this.rgb() + "";
- };
- d3.hsl = d3_hsl;
- function d3_hsl(h, s, l) {
- return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l) : arguments.length < 2 ? h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : new d3_hsl(h, s, l);
- }
- var d3_hslPrototype = d3_hsl.prototype = new d3_color();
- d3_hslPrototype.brighter = function(k) {
- k = Math.pow(.7, arguments.length ? k : 1);
- return new d3_hsl(this.h, this.s, this.l / k);
- };
- d3_hslPrototype.darker = function(k) {
- k = Math.pow(.7, arguments.length ? k : 1);
- return new d3_hsl(this.h, this.s, k * this.l);
- };
- d3_hslPrototype.rgb = function() {
- return d3_hsl_rgb(this.h, this.s, this.l);
- };
- function d3_hsl_rgb(h, s, l) {
- var m1, m2;
- h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
- s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
- l = l < 0 ? 0 : l > 1 ? 1 : l;
- m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
- m1 = 2 * l - m2;
- function v(h) {
- if (h > 360) h -= 360; else if (h < 0) h += 360;
- if (h < 60) return m1 + (m2 - m1) * h / 60;
- if (h < 180) return m2;
- if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
- return m1;
- }
- function vv(h) {
- return Math.round(v(h) * 255);
- }
- return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
- }
- d3.hcl = d3_hcl;
- function d3_hcl(h, c, l) {
- return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l) : arguments.length < 2 ? h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l) : h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : new d3_hcl(h, c, l);
- }
- var d3_hclPrototype = d3_hcl.prototype = new d3_color();
- d3_hclPrototype.brighter = function(k) {
- return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
- };
- d3_hclPrototype.darker = function(k) {
- return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
- };
- d3_hclPrototype.rgb = function() {
- return d3_hcl_lab(this.h, this.c, this.l).rgb();
- };
- function d3_hcl_lab(h, c, l) {
- if (isNaN(h)) h = 0;
- if (isNaN(c)) c = 0;
- return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
- }
- d3.lab = d3_lab;
- function d3_lab(l, a, b) {
- return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b) : arguments.length < 2 ? l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b) : l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l) : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b) : new d3_lab(l, a, b);
- }
- var d3_lab_K = 18;
- var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883;
- var d3_labPrototype = d3_lab.prototype = new d3_color();
- d3_labPrototype.brighter = function(k) {
- return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
- };
- d3_labPrototype.darker = function(k) {
- return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
- };
- d3_labPrototype.rgb = function() {
- return d3_lab_rgb(this.l, this.a, this.b);
- };
- function d3_lab_rgb(l, a, b) {
- var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
- x = d3_lab_xyz(x) * d3_lab_X;
- y = d3_lab_xyz(y) * d3_lab_Y;
- z = d3_lab_xyz(z) * d3_lab_Z;
- return new d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z));
- }
- function d3_lab_hcl(l, a, b) {
- return l > 0 ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : new d3_hcl(NaN, NaN, l);
- }
- function d3_lab_xyz(x) {
- return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
- }
- function d3_xyz_lab(x) {
- return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
- }
- function d3_xyz_rgb(r) {
- return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055));
- }
- d3.rgb = d3_rgb;
- function d3_rgb(r, g, b) {
- return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b) : arguments.length < 2 ? r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : new d3_rgb(r, g, b);
- }
- function d3_rgbNumber(value) {
- return new d3_rgb(value >> 16, value >> 8 & 255, value & 255);
- }
- function d3_rgbString(value) {
- return d3_rgbNumber(value) + "";
- }
- var d3_rgbPrototype = d3_rgb.prototype = new d3_color();
- d3_rgbPrototype.brighter = function(k) {
- k = Math.pow(.7, arguments.length ? k : 1);
- var r = this.r, g = this.g, b = this.b, i = 30;
- if (!r && !g && !b) return new d3_rgb(i, i, i);
- if (r && r < i) r = i;
- if (g && g < i) g = i;
- if (b && b < i) b = i;
- return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
- };
- d3_rgbPrototype.darker = function(k) {
- k = Math.pow(.7, arguments.length ? k : 1);
- return new d3_rgb(k * this.r, k * this.g, k * this.b);
- };
- d3_rgbPrototype.hsl = function() {
- return d3_rgb_hsl(this.r, this.g, this.b);
- };
- d3_rgbPrototype.toString = function() {
- return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
- };
- function d3_rgb_hex(v) {
- return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
- }
- function d3_rgb_parse(format, rgb, hsl) {
- var r = 0, g = 0, b = 0, m1, m2, color;
- m1 = /([a-z]+)\((.*)\)/.exec(format = format.toLowerCase());
- if (m1) {
- m2 = m1[2].split(",");
- switch (m1[1]) {
- case "hsl":
- {
- return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100);
- }
-
- case "rgb":
- {
- return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2]));
- }
- }
- }
- if (color = d3_rgb_names.get(format)) {
- return rgb(color.r, color.g, color.b);
- }
- if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
- if (format.length === 4) {
- r = (color & 3840) >> 4;
- r = r >> 4 | r;
- g = color & 240;
- g = g >> 4 | g;
- b = color & 15;
- b = b << 4 | b;
- } else if (format.length === 7) {
- r = (color & 16711680) >> 16;
- g = (color & 65280) >> 8;
- b = color & 255;
- }
- }
- return rgb(r, g, b);
- }
- function d3_rgb_hsl(r, g, b) {
- var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2;
- if (d) {
- s = l < .5 ? d / (max + min) : d / (2 - max - min);
- if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4;
- h *= 60;
- } else {
- h = NaN;
- s = l > 0 && l < 1 ? 0 : h;
- }
- return new d3_hsl(h, s, l);
- }
- function d3_rgb_lab(r, g, b) {
- r = d3_rgb_xyz(r);
- g = d3_rgb_xyz(g);
- b = d3_rgb_xyz(b);
- var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z);
- return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
- }
- function d3_rgb_xyz(r) {
- return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4);
- }
- function d3_rgb_parseNumber(c) {
- var f = parseFloat(c);
- return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
- }
- var d3_rgb_names = d3.map({
- aliceblue: 15792383,
- antiquewhite: 16444375,
- aqua: 65535,
- aquamarine: 8388564,
- azure: 15794175,
- beige: 16119260,
- bisque: 16770244,
- black: 0,
- blanchedalmond: 16772045,
- blue: 255,
- blueviolet: 9055202,
- brown: 10824234,
- burlywood: 14596231,
- cadetblue: 6266528,
- chartreuse: 8388352,
- chocolate: 13789470,
- coral: 16744272,
- cornflowerblue: 6591981,
- cornsilk: 16775388,
- crimson: 14423100,
- cyan: 65535,
- darkblue: 139,
- darkcyan: 35723,
- darkgoldenrod: 12092939,
- darkgray: 11119017,
- darkgreen: 25600,
- darkgrey: 11119017,
- darkkhaki: 12433259,
- darkmagenta: 9109643,
- darkolivegreen: 5597999,
- darkorange: 16747520,
- darkorchid: 10040012,
- darkred: 9109504,
- darksalmon: 15308410,
- darkseagreen: 9419919,
- darkslateblue: 4734347,
- darkslategray: 3100495,
- darkslategrey: 3100495,
- darkturquoise: 52945,
- darkviolet: 9699539,
- deeppink: 16716947,
- deepskyblue: 49151,
- dimgray: 6908265,
- dimgrey: 6908265,
- dodgerblue: 2003199,
- firebrick: 11674146,
- floralwhite: 16775920,
- forestgreen: 2263842,
- fuchsia: 16711935,
- gainsboro: 14474460,
- ghostwhite: 16316671,
- gold: 16766720,
- goldenrod: 14329120,
- gray: 8421504,
- green: 32768,
- greenyellow: 11403055,
- grey: 8421504,
- honeydew: 15794160,
- hotpink: 16738740,
- indianred: 13458524,
- indigo: 4915330,
- ivory: 16777200,
- khaki: 15787660,
- lavender: 15132410,
- lavenderblush: 16773365,
- lawngreen: 8190976,
- lemonchiffon: 16775885,
- lightblue: 11393254,
- lightcoral: 15761536,
- lightcyan: 14745599,
- lightgoldenrodyellow: 16448210,
- lightgray: 13882323,
- lightgreen: 9498256,
- lightgrey: 13882323,
- lightpink: 16758465,
- lightsalmon: 16752762,
- lightseagreen: 2142890,
- lightskyblue: 8900346,
- lightslategray: 7833753,
- lightslategrey: 7833753,
- lightsteelblue: 11584734,
- lightyellow: 16777184,
- lime: 65280,
- limegreen: 3329330,
- linen: 16445670,
- magenta: 16711935,
- maroon: 8388608,
- mediumaquamarine: 6737322,
- mediumblue: 205,
- mediumorchid: 12211667,
- mediumpurple: 9662683,
- mediumseagreen: 3978097,
- mediumslateblue: 8087790,
- mediumspringgreen: 64154,
- mediumturquoise: 4772300,
- mediumvioletred: 13047173,
- midnightblue: 1644912,
- mintcream: 16121850,
- mistyrose: 16770273,
- moccasin: 16770229,
- navajowhite: 16768685,
- navy: 128,
- oldlace: 16643558,
- olive: 8421376,
- olivedrab: 7048739,
- orange: 16753920,
- orangered: 16729344,
- orchid: 14315734,
- palegoldenrod: 15657130,
- palegreen: 10025880,
- paleturquoise: 11529966,
- palevioletred: 14381203,
- papayawhip: 16773077,
- peachpuff: 16767673,
- peru: 13468991,
- pink: 16761035,
- plum: 14524637,
- powderblue: 11591910,
- purple: 8388736,
- rebeccapurple: 6697881,
- red: 16711680,
- rosybrown: 12357519,
- royalblue: 4286945,
- saddlebrown: 9127187,
- salmon: 16416882,
- sandybrown: 16032864,
- seagreen: 3050327,
- seashell: 16774638,
- sienna: 10506797,
- silver: 12632256,
- skyblue: 8900331,
- slateblue: 6970061,
- slategray: 7372944,
- slategrey: 7372944,
- snow: 16775930,
- springgreen: 65407,
- steelblue: 4620980,
- tan: 13808780,
- teal: 32896,
- thistle: 14204888,
- tomato: 16737095,
- turquoise: 4251856,
- violet: 15631086,
- wheat: 16113331,
- white: 16777215,
- whitesmoke: 16119285,
- yellow: 16776960,
- yellowgreen: 10145074
- });
- d3_rgb_names.forEach(function(key, value) {
- d3_rgb_names.set(key, d3_rgbNumber(value));
- });
- function d3_functor(v) {
- return typeof v === "function" ? v : function() {
- return v;
- };
- }
- d3.functor = d3_functor;
- d3.xhr = d3_xhrType(d3_identity);
- function d3_xhrType(response) {
- return function(url, mimeType, callback) {
- if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType,
- mimeType = null;
- return d3_xhr(url, mimeType, response, callback);
- };
- }
- function d3_xhr(url, mimeType, response, callback) {
- var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null;
- if (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest();
- "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() {
- request.readyState > 3 && respond();
- };
- function respond() {
- var status = request.status, result;
- if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
- try {
- result = response.call(xhr, request);
- } catch (e) {
- dispatch.error.call(xhr, e);
- return;
- }
- dispatch.load.call(xhr, result);
- } else {
- dispatch.error.call(xhr, request);
- }
- }
- request.onprogress = function(event) {
- var o = d3.event;
- d3.event = event;
- try {
- dispatch.progress.call(xhr, request);
- } finally {
- d3.event = o;
- }
- };
- xhr.header = function(name, value) {
- name = (name + "").toLowerCase();
- if (arguments.length < 2) return headers[name];
- if (value == null) delete headers[name]; else headers[name] = value + "";
- return xhr;
- };
- xhr.mimeType = function(value) {
- if (!arguments.length) return mimeType;
- mimeType = value == null ? null : value + "";
- return xhr;
- };
- xhr.responseType = function(value) {
- if (!arguments.length) return responseType;
- responseType = value;
- return xhr;
- };
- xhr.response = function(value) {
- response = value;
- return xhr;
- };
- [ "get", "post" ].forEach(function(method) {
- xhr[method] = function() {
- return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments)));
- };
- });
- xhr.send = function(method, data, callback) {
- if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
- request.open(method, url, true);
- if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
- if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
- if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
- if (responseType != null) request.responseType = responseType;
- if (callback != null) xhr.on("error", callback).on("load", function(request) {
- callback(null, request);
- });
- dispatch.beforesend.call(xhr, request);
- request.send(data == null ? null : data);
- return xhr;
- };
- xhr.abort = function() {
- request.abort();
- return xhr;
- };
- d3.rebind(xhr, dispatch, "on");
- return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
- }
- function d3_xhr_fixCallback(callback) {
- return callback.length === 1 ? function(error, request) {
- callback(error == null ? request : null);
- } : callback;
- }
- function d3_xhrHasResponse(request) {
- var type = request.responseType;
- return type && type !== "text" ? request.response : request.responseText;
- }
- d3.dsv = function(delimiter, mimeType) {
- var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
- function dsv(url, row, callback) {
- if (arguments.length < 3) callback = row, row = null;
- var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback);
- xhr.row = function(_) {
- return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row;
- };
- return xhr;
- }
- function response(request) {
- return dsv.parse(request.responseText);
- }
- function typedResponse(f) {
- return function(request) {
- return dsv.parse(request.responseText, f);
- };
- }
- dsv.parse = function(text, f) {
- var o;
- return dsv.parseRows(text, function(row, i) {
- if (o) return o(row, i - 1);
- var a = new Function("d", "return {" + row.map(function(name, i) {
- return JSON.stringify(name) + ": d[" + i + "]";
- }).join(",") + "}");
- o = f ? function(row, i) {
- return f(a(row), i);
- } : a;
- });
- };
- dsv.parseRows = function(text, f) {
- var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol;
- function token() {
- if (I >= N) return EOF;
- if (eol) return eol = false, EOL;
- var j = I;
- if (text.charCodeAt(j) === 34) {
- var i = j;
- while (i++ < N) {
- if (text.charCodeAt(i) === 34) {
- if (text.charCodeAt(i + 1) !== 34) break;
- ++i;
- }
- }
- I = i + 2;
- var c = text.charCodeAt(i + 1);
- if (c === 13) {
- eol = true;
- if (text.charCodeAt(i + 2) === 10) ++I;
- } else if (c === 10) {
- eol = true;
- }
- return text.slice(j + 1, i).replace(/""/g, '"');
- }
- while (I < N) {
- var c = text.charCodeAt(I++), k = 1;
- if (c === 10) eol = true; else if (c === 13) {
- eol = true;
- if (text.charCodeAt(I) === 10) ++I, ++k;
- } else if (c !== delimiterCode) continue;
- return text.slice(j, I - k);
- }
- return text.slice(j);
- }
- while ((t = token()) !== EOF) {
- var a = [];
- while (t !== EOL && t !== EOF) {
- a.push(t);
- t = token();
- }
- if (f && (a = f(a, n++)) == null) continue;
- rows.push(a);
- }
- return rows;
- };
- dsv.format = function(rows) {
- if (Array.isArray(rows[0])) return dsv.formatRows(rows);
- var fieldSet = new d3_Set(), fields = [];
- rows.forEach(function(row) {
- for (var field in row) {
- if (!fieldSet.has(field)) {
- fields.push(fieldSet.add(field));
- }
- }
- });
- return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) {
- return fields.map(function(field) {
- return formatValue(row[field]);
- }).join(delimiter);
- })).join("\n");
- };
- dsv.formatRows = function(rows) {
- return rows.map(formatRow).join("\n");
- };
- function formatRow(row) {
- return row.map(formatValue).join(delimiter);
- }
- function formatValue(text) {
- return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text;
- }
- return dsv;
- };
- d3.csv = d3.dsv(",", "text/csv");
- d3.tsv = d3.dsv(" ", "text/tab-separated-values");
- var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) {
- setTimeout(callback, 17);
- };
- d3.timer = function() {
- d3_timer.apply(this, arguments);
- };
- function d3_timer(callback, delay, then) {
- var n = arguments.length;
- if (n < 2) delay = 0;
- if (n < 3) then = Date.now();
- var time = then + delay, timer = {
- c: callback,
- t: time,
- n: null
- };
- if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer;
- d3_timer_queueTail = timer;
- if (!d3_timer_interval) {
- d3_timer_timeout = clearTimeout(d3_timer_timeout);
- d3_timer_interval = 1;
- d3_timer_frame(d3_timer_step);
- }
- return timer;
- }
- function d3_timer_step() {
- var now = d3_timer_mark(), delay = d3_timer_sweep() - now;
- if (delay > 24) {
- if (isFinite(delay)) {
- clearTimeout(d3_timer_timeout);
- d3_timer_timeout = setTimeout(d3_timer_step, delay);
- }
- d3_timer_interval = 0;
- } else {
- d3_timer_interval = 1;
- d3_timer_frame(d3_timer_step);
- }
- }
- d3.timer.flush = function() {
- d3_timer_mark();
- d3_timer_sweep();
- };
- function d3_timer_mark() {
- var now = Date.now(), timer = d3_timer_queueHead;
- while (timer) {
- if (now >= timer.t && timer.c(now - timer.t)) timer.c = null;
- timer = timer.n;
- }
- return now;
- }
- function d3_timer_sweep() {
- var t0, t1 = d3_timer_queueHead, time = Infinity;
- while (t1) {
- if (t1.c) {
- if (t1.t < time) time = t1.t;
- t1 = (t0 = t1).n;
- } else {
- t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
- }
- }
- d3_timer_queueTail = t0;
- return time;
- }
- function d3_format_precision(x, p) {
- return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1);
- }
- d3.round = function(x, n) {
- return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x);
- };
- var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix);
- d3.formatPrefix = function(value, precision) {
- var i = 0;
- if (value = +value) {
- if (value < 0) value *= -1;
- if (precision) value = d3.round(value, d3_format_precision(value, precision));
- i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
- i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3));
- }
- return d3_formatPrefixes[8 + i / 3];
- };
- function d3_formatPrefix(d, i) {
- var k = Math.pow(10, abs(8 - i) * 3);
- return {
- scale: i > 8 ? function(d) {
- return d / k;
- } : function(d) {
- return d * k;
- },
- symbol: d
- };
- }
- function d3_locale_numberFormat(locale) {
- var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping && locale_thousands ? function(value, width) {
- var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0;
- while (i > 0 && g > 0) {
- if (length + g + 1 > width) g = Math.max(1, width - length);
- t.push(value.substring(i -= g, i + g));
- if ((length += g + 1) > width) break;
- g = locale_grouping[j = (j + 1) % locale_grouping.length];
- }
- return t.reverse().join(locale_thousands);
- } : d3_identity;
- return function(specifier) {
- var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "-", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false, exponent = true;
- if (precision) precision = +precision.substring(1);
- if (zfill || fill === "0" && align === "=") {
- zfill = fill = "0";
- align = "=";
- }
- switch (type) {
- case "n":
- comma = true;
- type = "g";
- break;
-
- case "%":
- scale = 100;
- suffix = "%";
- type = "f";
- break;
-
- case "p":
- scale = 100;
- suffix = "%";
- type = "r";
- break;
-
- case "b":
- case "o":
- case "x":
- case "X":
- if (symbol === "#") prefix = "0" + type.toLowerCase();
-
- case "c":
- exponent = false;
-
- case "d":
- integer = true;
- precision = 0;
- break;
-
- case "s":
- scale = -1;
- type = "r";
- break;
- }
- if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1];
- if (type == "r" && !precision) type = "g";
- if (precision != null) {
- if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision));
- }
- type = d3_format_types.get(type) || d3_format_typeDefault;
- var zcomma = zfill && comma;
- return function(value) {
- var fullSuffix = suffix;
- if (integer && value % 1) return "";
- var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign;
- if (scale < 0) {
- var unit = d3.formatPrefix(value, precision);
- value = unit.scale(value);
- fullSuffix = unit.symbol + suffix;
- } else {
- value *= scale;
- }
- value = type(value, precision);
- var i = value.lastIndexOf("."), before, after;
- if (i < 0) {
- var j = exponent ? value.lastIndexOf("e") : -1;
- if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j);
- } else {
- before = value.substring(0, i);
- after = locale_decimal + value.substring(i + 1);
- }
- if (!zfill && comma) before = formatGroup(before, Infinity);
- var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : "";
- if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity);
- negative += prefix;
- value = before + after;
- return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix;
- };
- };
- }
- var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i;
- var d3_format_types = d3.map({
- b: function(x) {
- return x.toString(2);
- },
- c: function(x) {
- return String.fromCharCode(x);
- },
- o: function(x) {
- return x.toString(8);
- },
- x: function(x) {
- return x.toString(16);
- },
- X: function(x) {
- return x.toString(16).toUpperCase();
- },
- g: function(x, p) {
- return x.toPrecision(p);
- },
- e: function(x, p) {
- return x.toExponential(p);
- },
- f: function(x, p) {
- return x.toFixed(p);
- },
- r: function(x, p) {
- return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p))));
- }
- });
- function d3_format_typeDefault(x) {
- return x + "";
- }
- var d3_time = d3.time = {}, d3_date = Date;
- function d3_date_utc() {
- this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]);
- }
- d3_date_utc.prototype = {
- getDate: function() {
- return this._.getUTCDate();
- },
- getDay: function() {
- return this._.getUTCDay();
- },
- getFullYear: function() {
- return this._.getUTCFullYear();
- },
- getHours: function() {
- return this._.getUTCHours();
- },
- getMilliseconds: function() {
- return this._.getUTCMilliseconds();
- },
- getMinutes: function() {
- return this._.getUTCMinutes();
- },
- getMonth: function() {
- return this._.getUTCMonth();
- },
- getSeconds: function() {
- return this._.getUTCSeconds();
- },
- getTime: function() {
- return this._.getTime();
- },
- getTimezoneOffset: function() {
- return 0;
- },
- valueOf: function() {
- return this._.valueOf();
- },
- setDate: function() {
- d3_time_prototype.setUTCDate.apply(this._, arguments);
- },
- setDay: function() {
- d3_time_prototype.setUTCDay.apply(this._, arguments);
- },
- setFullYear: function() {
- d3_time_prototype.setUTCFullYear.apply(this._, arguments);
- },
- setHours: function() {
- d3_time_prototype.setUTCHours.apply(this._, arguments);
- },
- setMilliseconds: function() {
- d3_time_prototype.setUTCMilliseconds.apply(this._, arguments);
- },
- setMinutes: function() {
- d3_time_prototype.setUTCMinutes.apply(this._, arguments);
- },
- setMonth: function() {
- d3_time_prototype.setUTCMonth.apply(this._, arguments);
- },
- setSeconds: function() {
- d3_time_prototype.setUTCSeconds.apply(this._, arguments);
- },
- setTime: function() {
- d3_time_prototype.setTime.apply(this._, arguments);
- }
- };
- var d3_time_prototype = Date.prototype;
- function d3_time_interval(local, step, number) {
- function round(date) {
- var d0 = local(date), d1 = offset(d0, 1);
- return date - d0 < d1 - date ? d0 : d1;
- }
- function ceil(date) {
- step(date = local(new d3_date(date - 1)), 1);
- return date;
- }
- function offset(date, k) {
- step(date = new d3_date(+date), k);
- return date;
- }
- function range(t0, t1, dt) {
- var time = ceil(t0), times = [];
- if (dt > 1) {
- while (time < t1) {
- if (!(number(time) % dt)) times.push(new Date(+time));
- step(time, 1);
- }
- } else {
- while (time < t1) times.push(new Date(+time)), step(time, 1);
- }
- return times;
- }
- function range_utc(t0, t1, dt) {
- try {
- d3_date = d3_date_utc;
- var utc = new d3_date_utc();
- utc._ = t0;
- return range(utc, t1, dt);
- } finally {
- d3_date = Date;
- }
- }
- local.floor = local;
- local.round = round;
- local.ceil = ceil;
- local.offset = offset;
- local.range = range;
- var utc = local.utc = d3_time_interval_utc(local);
- utc.floor = utc;
- utc.round = d3_time_interval_utc(round);
- utc.ceil = d3_time_interval_utc(ceil);
- utc.offset = d3_time_interval_utc(offset);
- utc.range = range_utc;
- return local;
- }
- function d3_time_interval_utc(method) {
- return function(date, k) {
- try {
- d3_date = d3_date_utc;
- var utc = new d3_date_utc();
- utc._ = date;
- return method(utc, k)._;
- } finally {
- d3_date = Date;
- }
- };
- }
- d3_time.year = d3_time_interval(function(date) {
- date = d3_time.day(date);
- date.setMonth(0, 1);
- return date;
- }, function(date, offset) {
- date.setFullYear(date.getFullYear() + offset);
- }, function(date) {
- return date.getFullYear();
- });
- d3_time.years = d3_time.year.range;
- d3_time.years.utc = d3_time.year.utc.range;
- d3_time.day = d3_time_interval(function(date) {
- var day = new d3_date(2e3, 0);
- day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
- return day;
- }, function(date, offset) {
- date.setDate(date.getDate() + offset);
- }, function(date) {
- return date.getDate() - 1;
- });
- d3_time.days = d3_time.day.range;
- d3_time.days.utc = d3_time.day.utc.range;
- d3_time.dayOfYear = function(date) {
- var year = d3_time.year(date);
- return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5);
- };
- [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) {
- i = 7 - i;
- var interval = d3_time[day] = d3_time_interval(function(date) {
- (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7);
- return date;
- }, function(date, offset) {
- date.setDate(date.getDate() + Math.floor(offset) * 7);
- }, function(date) {
- var day = d3_time.year(date).getDay();
- return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i);
- });
- d3_time[day + "s"] = interval.range;
- d3_time[day + "s"].utc = interval.utc.range;
- d3_time[day + "OfYear"] = function(date) {
- var day = d3_time.year(date).getDay();
- return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7);
- };
- });
- d3_time.week = d3_time.sunday;
- d3_time.weeks = d3_time.sunday.range;
- d3_time.weeks.utc = d3_time.sunday.utc.range;
- d3_time.weekOfYear = d3_time.sundayOfYear;
- function d3_locale_timeFormat(locale) {
- var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths;
- function d3_time_format(template) {
- var n = template.length;
- function format(date) {
- var string = [], i = -1, j = 0, c, p, f;
- while (++i < n) {
- if (template.charCodeAt(i) === 37) {
- string.push(template.slice(j, i));
- if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i);
- if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p);
- string.push(c);
- j = i + 1;
- }
- }
- string.push(template.slice(j, i));
- return string.join("");
- }
- format.parse = function(string) {
- var d = {
- y: 1900,
- m: 0,
- d: 1,
- H: 0,
- M: 0,
- S: 0,
- L: 0,
- Z: null
- }, i = d3_time_parse(d, template, string, 0);
- if (i != string.length) return null;
- if ("p" in d) d.H = d.H % 12 + d.p * 12;
- var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)();
- if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("W" in d || "U" in d) {
- if (!("w" in d)) d.w = "W" in d ? 1 : 0;
- date.setFullYear(d.y, 0, 1);
- date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7);
- } else date.setFullYear(d.y, d.m, d.d);
- date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L);
- return localZ ? date._ : date;
- };
- format.toString = function() {
- return template;
- };
- return format;
- }
- function d3_time_parse(date, template, string, j) {
- var c, p, t, i = 0, n = template.length, m = string.length;
- while (i < n) {
- if (j >= m) return -1;
- c = template.charCodeAt(i++);
- if (c === 37) {
- t = template.charAt(i++);
- p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t];
- if (!p || (j = p(date, string, j)) < 0) return -1;
- } else if (c != string.charCodeAt(j++)) {
- return -1;
- }
- }
- return j;
- }
- d3_time_format.utc = function(template) {
- var local = d3_time_format(template);
- function format(date) {
- try {
- d3_date = d3_date_utc;
- var utc = new d3_date();
- utc._ = date;
- return local(utc);
- } finally {
- d3_date = Date;
- }
- }
- format.parse = function(string) {
- try {
- d3_date = d3_date_utc;
- var date = local.parse(string);
- return date && date._;
- } finally {
- d3_date = Date;
- }
- };
- format.toString = local.toString;
- return format;
- };
- d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti;
- var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths);
- locale_periods.forEach(function(p, i) {
- d3_time_periodLookup.set(p.toLowerCase(), i);
- });
- var d3_time_formats = {
- a: function(d) {
- return locale_shortDays[d.getDay()];
- },
- A: function(d) {
- return locale_days[d.getDay()];
- },
- b: function(d) {
- return locale_shortMonths[d.getMonth()];
- },
- B: function(d) {
- return locale_months[d.getMonth()];
- },
- c: d3_time_format(locale_dateTime),
- d: function(d, p) {
- return d3_time_formatPad(d.getDate(), p, 2);
- },
- e: function(d, p) {
- return d3_time_formatPad(d.getDate(), p, 2);
- },
- H: function(d, p) {
- return d3_time_formatPad(d.getHours(), p, 2);
- },
- I: function(d, p) {
- return d3_time_formatPad(d.getHours() % 12 || 12, p, 2);
- },
- j: function(d, p) {
- return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3);
- },
- L: function(d, p) {
- return d3_time_formatPad(d.getMilliseconds(), p, 3);
- },
- m: function(d, p) {
- return d3_time_formatPad(d.getMonth() + 1, p, 2);
- },
- M: function(d, p) {
- return d3_time_formatPad(d.getMinutes(), p, 2);
- },
- p: function(d) {
- return locale_periods[+(d.getHours() >= 12)];
- },
- S: function(d, p) {
- return d3_time_formatPad(d.getSeconds(), p, 2);
- },
- U: function(d, p) {
- return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2);
- },
- w: function(d) {
- return d.getDay();
- },
- W: function(d, p) {
- return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2);
- },
- x: d3_time_format(locale_date),
- X: d3_time_format(locale_time),
- y: function(d, p) {
- return d3_time_formatPad(d.getFullYear() % 100, p, 2);
- },
- Y: function(d, p) {
- return d3_time_formatPad(d.getFullYear() % 1e4, p, 4);
- },
- Z: d3_time_zone,
- "%": function() {
- return "%";
- }
- };
- var d3_time_parsers = {
- a: d3_time_parseWeekdayAbbrev,
- A: d3_time_parseWeekday,
- b: d3_time_parseMonthAbbrev,
- B: d3_time_parseMonth,
- c: d3_time_parseLocaleFull,
- d: d3_time_parseDay,
- e: d3_time_parseDay,
- H: d3_time_parseHour24,
- I: d3_time_parseHour24,
- j: d3_time_parseDayOfYear,
- L: d3_time_parseMilliseconds,
- m: d3_time_parseMonthNumber,
- M: d3_time_parseMinutes,
- p: d3_time_parseAmPm,
- S: d3_time_parseSeconds,
- U: d3_time_parseWeekNumberSunday,
- w: d3_time_parseWeekdayNumber,
- W: d3_time_parseWeekNumberMonday,
- x: d3_time_parseLocaleDate,
- X: d3_time_parseLocaleTime,
- y: d3_time_parseYear,
- Y: d3_time_parseFullYear,
- Z: d3_time_parseZone,
- "%": d3_time_parseLiteralPercent
- };
- function d3_time_parseWeekdayAbbrev(date, string, i) {
- d3_time_dayAbbrevRe.lastIndex = 0;
- var n = d3_time_dayAbbrevRe.exec(string.slice(i));
- return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
- }
- function d3_time_parseWeekday(date, string, i) {
- d3_time_dayRe.lastIndex = 0;
- var n = d3_time_dayRe.exec(string.slice(i));
- return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
- }
- function d3_time_parseMonthAbbrev(date, string, i) {
- d3_time_monthAbbrevRe.lastIndex = 0;
- var n = d3_time_monthAbbrevRe.exec(string.slice(i));
- return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
- }
- function d3_time_parseMonth(date, string, i) {
- d3_time_monthRe.lastIndex = 0;
- var n = d3_time_monthRe.exec(string.slice(i));
- return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
- }
- function d3_time_parseLocaleFull(date, string, i) {
- return d3_time_parse(date, d3_time_formats.c.toString(), string, i);
- }
- function d3_time_parseLocaleDate(date, string, i) {
- return d3_time_parse(date, d3_time_formats.x.toString(), string, i);
- }
- function d3_time_parseLocaleTime(date, string, i) {
- return d3_time_parse(date, d3_time_formats.X.toString(), string, i);
- }
- function d3_time_parseAmPm(date, string, i) {
- var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase());
- return n == null ? -1 : (date.p = n, i);
- }
- return d3_time_format;
- }
- var d3_time_formatPads = {
- "-": "",
- _: " ",
- "0": "0"
- }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/;
- function d3_time_formatPad(value, fill, width) {
- var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length;
- return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
- }
- function d3_time_formatRe(names) {
- return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i");
- }
- function d3_time_formatLookup(names) {
- var map = new d3_Map(), i = -1, n = names.length;
- while (++i < n) map.set(names[i].toLowerCase(), i);
- return map;
- }
- function d3_time_parseWeekdayNumber(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i, i + 1));
- return n ? (date.w = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseWeekNumberSunday(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i));
- return n ? (date.U = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseWeekNumberMonday(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i));
- return n ? (date.W = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseFullYear(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i, i + 4));
- return n ? (date.y = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseYear(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i, i + 2));
- return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
- }
- function d3_time_parseZone(date, string, i) {
- return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string,
- i + 5) : -1;
- }
- function d3_time_expandYear(d) {
- return d + (d > 68 ? 1900 : 2e3);
- }
- function d3_time_parseMonthNumber(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i, i + 2));
- return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
- }
- function d3_time_parseDay(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i, i + 2));
- return n ? (date.d = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseDayOfYear(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i, i + 3));
- return n ? (date.j = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseHour24(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i, i + 2));
- return n ? (date.H = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseMinutes(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i, i + 2));
- return n ? (date.M = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseSeconds(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i, i + 2));
- return n ? (date.S = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseMilliseconds(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.slice(i, i + 3));
- return n ? (date.L = +n[0], i + n[0].length) : -1;
- }
- function d3_time_zone(d) {
- var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60;
- return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
- }
- function d3_time_parseLiteralPercent(date, string, i) {
- d3_time_percentRe.lastIndex = 0;
- var n = d3_time_percentRe.exec(string.slice(i, i + 1));
- return n ? i + n[0].length : -1;
- }
- function d3_time_formatMulti(formats) {
- var n = formats.length, i = -1;
- while (++i < n) formats[i][0] = this(formats[i][0]);
- return function(date) {
- var i = 0, f = formats[i];
- while (!f[1](date)) f = formats[++i];
- return f[0](date);
- };
- }
- d3.locale = function(locale) {
- return {
- numberFormat: d3_locale_numberFormat(locale),
- timeFormat: d3_locale_timeFormat(locale)
- };
- };
- var d3_locale_enUS = d3.locale({
- decimal: ".",
- thousands: ",",
- grouping: [ 3 ],
- currency: [ "$", "" ],
- dateTime: "%a %b %e %X %Y",
- date: "%m/%d/%Y",
- time: "%H:%M:%S",
- periods: [ "AM", "PM" ],
- days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
- shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
- months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ],
- shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
- });
- d3.format = d3_locale_enUS.numberFormat;
- d3.geo = {};
- function d3_adder() {}
- d3_adder.prototype = {
- s: 0,
- t: 0,
- add: function(y) {
- d3_adderSum(y, this.t, d3_adderTemp);
- d3_adderSum(d3_adderTemp.s, this.s, this);
- if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t;
- },
- reset: function() {
- this.s = this.t = 0;
- },
- valueOf: function() {
- return this.s;
- }
- };
- var d3_adderTemp = new d3_adder();
- function d3_adderSum(a, b, o) {
- var x = o.s = a + b, bv = x - a, av = x - bv;
- o.t = a - av + (b - bv);
- }
- d3.geo.stream = function(object, listener) {
- if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
- d3_geo_streamObjectType[object.type](object, listener);
- } else {
- d3_geo_streamGeometry(object, listener);
- }
- };
- function d3_geo_streamGeometry(geometry, listener) {
- if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
- d3_geo_streamGeometryType[geometry.type](geometry, listener);
- }
- }
- var d3_geo_streamObjectType = {
- Feature: function(feature, listener) {
- d3_geo_streamGeometry(feature.geometry, listener);
- },
- FeatureCollection: function(object, listener) {
- var features = object.features, i = -1, n = features.length;
- while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
- }
- };
- var d3_geo_streamGeometryType = {
- Sphere: function(object, listener) {
- listener.sphere();
- },
- Point: function(object, listener) {
- object = object.coordinates;
- listener.point(object[0], object[1], object[2]);
- },
- MultiPoint: function(object, listener) {
- var coordinates = object.coordinates, i = -1, n = coordinates.length;
- while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
- },
- LineString: function(object, listener) {
- d3_geo_streamLine(object.coordinates, listener, 0);
- },
- MultiLineString: function(object, listener) {
- var coordinates = object.coordinates, i = -1, n = coordinates.length;
- while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
- },
- Polygon: function(object, listener) {
- d3_geo_streamPolygon(object.coordinates, listener);
- },
- MultiPolygon: function(object, listener) {
- var coordinates = object.coordinates, i = -1, n = coordinates.length;
- while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
- },
- GeometryCollection: function(object, listener) {
- var geometries = object.geometries, i = -1, n = geometries.length;
- while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
- }
- };
- function d3_geo_streamLine(coordinates, listener, closed) {
- var i = -1, n = coordinates.length - closed, coordinate;
- listener.lineStart();
- while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
- listener.lineEnd();
- }
- function d3_geo_streamPolygon(coordinates, listener) {
- var i = -1, n = coordinates.length;
- listener.polygonStart();
- while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
- listener.polygonEnd();
- }
- d3.geo.area = function(object) {
- d3_geo_areaSum = 0;
- d3.geo.stream(object, d3_geo_area);
- return d3_geo_areaSum;
- };
- var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder();
- var d3_geo_area = {
- sphere: function() {
- d3_geo_areaSum += 4 * π;
- },
- point: d3_noop,
- lineStart: d3_noop,
- lineEnd: d3_noop,
- polygonStart: function() {
- d3_geo_areaRingSum.reset();
- d3_geo_area.lineStart = d3_geo_areaRingStart;
- },
- polygonEnd: function() {
- var area = 2 * d3_geo_areaRingSum;
- d3_geo_areaSum += area < 0 ? 4 * π + area : area;
- d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
- }
- };
- function d3_geo_areaRingStart() {
- var λ00, φ00, λ0, cosφ0, sinφ0;
- d3_geo_area.point = function(λ, φ) {
- d3_geo_area.point = nextPoint;
- λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4),
- sinφ0 = Math.sin(φ);
- };
- function nextPoint(λ, φ) {
- λ *= d3_radians;
- φ = φ * d3_radians / 2 + π / 4;
- var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ);
- d3_geo_areaRingSum.add(Math.atan2(v, u));
- λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
- }
- d3_geo_area.lineEnd = function() {
- nextPoint(λ00, φ00);
- };
- }
- function d3_geo_cartesian(spherical) {
- var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ);
- return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ];
- }
- function d3_geo_cartesianDot(a, b) {
- return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
- }
- function d3_geo_cartesianCross(a, b) {
- return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ];
- }
- function d3_geo_cartesianAdd(a, b) {
- a[0] += b[0];
- a[1] += b[1];
- a[2] += b[2];
- }
- function d3_geo_cartesianScale(vector, k) {
- return [ vector[0] * k, vector[1] * k, vector[2] * k ];
- }
- function d3_geo_cartesianNormalize(d) {
- var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
- d[0] /= l;
- d[1] /= l;
- d[2] /= l;
- }
- function d3_geo_spherical(cartesian) {
- return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ];
- }
- function d3_geo_sphericalEqual(a, b) {
- return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
- }
- d3.geo.bounds = function() {
- var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range;
- var bound = {
- point: point,
- lineStart: lineStart,
- lineEnd: lineEnd,
- polygonStart: function() {
- bound.point = ringPoint;
- bound.lineStart = ringStart;
- bound.lineEnd = ringEnd;
- dλSum = 0;
- d3_geo_area.polygonStart();
- },
- polygonEnd: function() {
- d3_geo_area.polygonEnd();
- bound.point = point;
- bound.lineStart = lineStart;
- bound.lineEnd = lineEnd;
- if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90;
- range[0] = λ0, range[1] = λ1;
- }
- };
- function point(λ, φ) {
- ranges.push(range = [ λ0 = λ, λ1 = λ ]);
- if (φ < φ0) φ0 = φ;
- if (φ > φ1) φ1 = φ;
- }
- function linePoint(λ, φ) {
- var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]);
- if (p0) {
- var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal);
- d3_geo_cartesianNormalize(inflection);
- inflection = d3_geo_spherical(inflection);
- var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180;
- if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
- var φi = inflection[1] * d3_degrees;
- if (φi > φ1) φ1 = φi;
- } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
- var φi = -inflection[1] * d3_degrees;
- if (φi < φ0) φ0 = φi;
- } else {
- if (φ < φ0) φ0 = φ;
- if (φ > φ1) φ1 = φ;
- }
- if (antimeridian) {
- if (λ < λ_) {
- if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
- } else {
- if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
- }
- } else {
- if (λ1 >= λ0) {
- if (λ < λ0) λ0 = λ;
- if (λ > λ1) λ1 = λ;
- } else {
- if (λ > λ_) {
- if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
- } else {
- if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
- }
- }
- }
- } else {
- point(λ, φ);
- }
- p0 = p, λ_ = λ;
- }
- function lineStart() {
- bound.point = linePoint;
- }
- function lineEnd() {
- range[0] = λ0, range[1] = λ1;
- bound.point = point;
- p0 = null;
- }
- function ringPoint(λ, φ) {
- if (p0) {
- var dλ = λ - λ_;
- dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
- } else λ__ = λ, φ__ = φ;
- d3_geo_area.point(λ, φ);
- linePoint(λ, φ);
- }
- function ringStart() {
- d3_geo_area.lineStart();
- }
- function ringEnd() {
- ringPoint(λ__, φ__);
- d3_geo_area.lineEnd();
- if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
- range[0] = λ0, range[1] = λ1;
- p0 = null;
- }
- function angle(λ0, λ1) {
- return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1;
- }
- function compareRanges(a, b) {
- return a[0] - b[0];
- }
- function withinRange(x, range) {
- return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
- }
- return function(feature) {
- φ1 = λ1 = -(λ0 = φ0 = Infinity);
- ranges = [];
- d3.geo.stream(feature, bound);
- var n = ranges.length;
- if (n) {
- ranges.sort(compareRanges);
- for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) {
- b = ranges[i];
- if (withinRange(b[0], a) || withinRange(b[1], a)) {
- if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
- if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
- } else {
- merged.push(a = b);
- }
- }
- var best = -Infinity, dλ;
- for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
- b = merged[i];
- if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
- }
- }
- ranges = range = null;
- return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ];
- };
- }();
- d3.geo.centroid = function(object) {
- d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
- d3.geo.stream(object, d3_geo_centroid);
- var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z;
- if (m < ε2) {
- x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
- if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
- m = x * x + y * y + z * z;
- if (m < ε2) return [ NaN, NaN ];
- }
- return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ];
- };
- var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2;
- var d3_geo_centroid = {
- sphere: d3_noop,
- point: d3_geo_centroidPoint,
- lineStart: d3_geo_centroidLineStart,
- lineEnd: d3_geo_centroidLineEnd,
- polygonStart: function() {
- d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
- },
- polygonEnd: function() {
- d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
- }
- };
- function d3_geo_centroidPoint(λ, φ) {
- λ *= d3_radians;
- var cosφ = Math.cos(φ *= d3_radians);
- d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
- }
- function d3_geo_centroidPointXYZ(x, y, z) {
- ++d3_geo_centroidW0;
- d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
- d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
- d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
- }
- function d3_geo_centroidLineStart() {
- var x0, y0, z0;
- d3_geo_centroid.point = function(λ, φ) {
- λ *= d3_radians;
- var cosφ = Math.cos(φ *= d3_radians);
- x0 = cosφ * Math.cos(λ);
- y0 = cosφ * Math.sin(λ);
- z0 = Math.sin(φ);
- d3_geo_centroid.point = nextPoint;
- d3_geo_centroidPointXYZ(x0, y0, z0);
- };
- function nextPoint(λ, φ) {
- λ *= d3_radians;
- var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
- d3_geo_centroidW1 += w;
- d3_geo_centroidX1 += w * (x0 + (x0 = x));
- d3_geo_centroidY1 += w * (y0 + (y0 = y));
- d3_geo_centroidZ1 += w * (z0 + (z0 = z));
- d3_geo_centroidPointXYZ(x0, y0, z0);
- }
- }
- function d3_geo_centroidLineEnd() {
- d3_geo_centroid.point = d3_geo_centroidPoint;
- }
- function d3_geo_centroidRingStart() {
- var λ00, φ00, x0, y0, z0;
- d3_geo_centroid.point = function(λ, φ) {
- λ00 = λ, φ00 = φ;
- d3_geo_centroid.point = nextPoint;
- λ *= d3_radians;
- var cosφ = Math.cos(φ *= d3_radians);
- x0 = cosφ * Math.cos(λ);
- y0 = cosφ * Math.sin(λ);
- z0 = Math.sin(φ);
- d3_geo_centroidPointXYZ(x0, y0, z0);
- };
- d3_geo_centroid.lineEnd = function() {
- nextPoint(λ00, φ00);
- d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
- d3_geo_centroid.point = d3_geo_centroidPoint;
- };
- function nextPoint(λ, φ) {
- λ *= d3_radians;
- var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u);
- d3_geo_centroidX2 += v * cx;
- d3_geo_centroidY2 += v * cy;
- d3_geo_centroidZ2 += v * cz;
- d3_geo_centroidW1 += w;
- d3_geo_centroidX1 += w * (x0 + (x0 = x));
- d3_geo_centroidY1 += w * (y0 + (y0 = y));
- d3_geo_centroidZ1 += w * (z0 + (z0 = z));
- d3_geo_centroidPointXYZ(x0, y0, z0);
- }
- }
- function d3_geo_compose(a, b) {
- function compose(x, y) {
- return x = a(x, y), b(x[0], x[1]);
- }
- if (a.invert && b.invert) compose.invert = function(x, y) {
- return x = b.invert(x, y), x && a.invert(x[0], x[1]);
- };
- return compose;
- }
- function d3_true() {
- return true;
- }
- function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
- var subject = [], clip = [];
- segments.forEach(function(segment) {
- if ((n = segment.length - 1) <= 0) return;
- var n, p0 = segment[0], p1 = segment[n];
- if (d3_geo_sphericalEqual(p0, p1)) {
- listener.lineStart();
- for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
- listener.lineEnd();
- return;
- }
- var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
- a.o = b;
- subject.push(a);
- clip.push(b);
- a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
- b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
- a.o = b;
- subject.push(a);
- clip.push(b);
- });
- clip.sort(compare);
- d3_geo_clipPolygonLinkCircular(subject);
- d3_geo_clipPolygonLinkCircular(clip);
- if (!subject.length) return;
- for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
- clip[i].e = entry = !entry;
- }
- var start = subject[0], points, point;
- while (1) {
- var current = start, isSubject = true;
- while (current.v) if ((current = current.n) === start) return;
- points = current.z;
- listener.lineStart();
- do {
- current.v = current.o.v = true;
- if (current.e) {
- if (isSubject) {
- for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
- } else {
- interpolate(current.x, current.n.x, 1, listener);
- }
- current = current.n;
- } else {
- if (isSubject) {
- points = current.p.z;
- for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
- } else {
- interpolate(current.x, current.p.x, -1, listener);
- }
- current = current.p;
- }
- current = current.o;
- points = current.z;
- isSubject = !isSubject;
- } while (!current.v);
- listener.lineEnd();
- }
- }
- function d3_geo_clipPolygonLinkCircular(array) {
- if (!(n = array.length)) return;
- var n, i = 0, a = array[0], b;
- while (++i < n) {
- a.n = b = array[i];
- b.p = a;
- a = b;
- }
- a.n = b = array[0];
- b.p = a;
- }
- function d3_geo_clipPolygonIntersection(point, points, other, entry) {
- this.x = point;
- this.z = points;
- this.o = other;
- this.e = entry;
- this.v = false;
- this.n = this.p = null;
- }
- function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
- return function(rotate, listener) {
- var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
- var clip = {
- point: point,
- lineStart: lineStart,
- lineEnd: lineEnd,
- polygonStart: function() {
- clip.point = pointRing;
- clip.lineStart = ringStart;
- clip.lineEnd = ringEnd;
- segments = [];
- polygon = [];
- },
- polygonEnd: function() {
- clip.point = point;
- clip.lineStart = lineStart;
- clip.lineEnd = lineEnd;
- segments = d3.merge(segments);
- var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
- if (segments.length) {
- if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
- d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
- } else if (clipStartInside) {
- if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
- listener.lineStart();
- interpolate(null, null, 1, listener);
- listener.lineEnd();
- }
- if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
- segments = polygon = null;
- },
- sphere: function() {
- listener.polygonStart();
- listener.lineStart();
- interpolate(null, null, 1, listener);
- listener.lineEnd();
- listener.polygonEnd();
- }
- };
- function point(λ, φ) {
- var point = rotate(λ, φ);
- if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
- }
- function pointLine(λ, φ) {
- var point = rotate(λ, φ);
- line.point(point[0], point[1]);
- }
- function lineStart() {
- clip.point = pointLine;
- line.lineStart();
- }
- function lineEnd() {
- clip.point = point;
- line.lineEnd();
- }
- var segments;
- var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring;
- function pointRing(λ, φ) {
- ring.push([ λ, φ ]);
- var point = rotate(λ, φ);
- ringListener.point(point[0], point[1]);
- }
- function ringStart() {
- ringListener.lineStart();
- ring = [];
- }
- function ringEnd() {
- pointRing(ring[0][0], ring[0][1]);
- ringListener.lineEnd();
- var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
- ring.pop();
- polygon.push(ring);
- ring = null;
- if (!n) return;
- if (clean & 1) {
- segment = ringSegments[0];
- var n = segment.length - 1, i = -1, point;
- if (n > 0) {
- if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
- listener.lineStart();
- while (++i < n) listener.point((point = segment[i])[0], point[1]);
- listener.lineEnd();
- }
- return;
- }
- if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
- segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
- }
- return clip;
- };
- }
- function d3_geo_clipSegmentLength1(segment) {
- return segment.length > 1;
- }
- function d3_geo_clipBufferListener() {
- var lines = [], line;
- return {
- lineStart: function() {
- lines.push(line = []);
- },
- point: function(λ, φ) {
- line.push([ λ, φ ]);
- },
- lineEnd: d3_noop,
- buffer: function() {
- var buffer = lines;
- lines = [];
- line = null;
- return buffer;
- },
- rejoin: function() {
- if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
- }
- };
- }
- function d3_geo_clipSort(a, b) {
- return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
- }
- var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]);
- function d3_geo_clipAntimeridianLine(listener) {
- var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
- return {
- lineStart: function() {
- listener.lineStart();
- clean = 1;
- },
- point: function(λ1, φ1) {
- var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0);
- if (abs(dλ - π) < ε) {
- listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
- listener.point(sλ0, φ0);
- listener.lineEnd();
- listener.lineStart();
- listener.point(sλ1, φ0);
- listener.point(λ1, φ0);
- clean = 0;
- } else if (sλ0 !== sλ1 && dλ >= π) {
- if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
- if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
- φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
- listener.point(sλ0, φ0);
- listener.lineEnd();
- listener.lineStart();
- listener.point(sλ1, φ0);
- clean = 0;
- }
- listener.point(λ0 = λ1, φ0 = φ1);
- sλ0 = sλ1;
- },
- lineEnd: function() {
- listener.lineEnd();
- λ0 = φ0 = NaN;
- },
- clean: function() {
- return 2 - clean;
- }
- };
- }
- function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
- var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
- return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2;
- }
- function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
- var φ;
- if (from == null) {
- φ = direction * halfπ;
- listener.point(-π, φ);
- listener.point(0, φ);
- listener.point(π, φ);
- listener.point(π, 0);
- listener.point(π, -φ);
- listener.point(0, -φ);
- listener.point(-π, -φ);
- listener.point(-π, 0);
- listener.point(-π, φ);
- } else if (abs(from[0] - to[0]) > ε) {
- var s = from[0] < to[0] ? π : -π;
- φ = direction * s / 2;
- listener.point(-s, φ);
- listener.point(0, φ);
- listener.point(s, φ);
- } else {
- listener.point(to[0], to[1]);
- }
- }
- function d3_geo_pointInPolygon(point, polygon) {
- var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0;
- d3_geo_areaRingSum.reset();
- for (var i = 0, n = polygon.length; i < n; ++i) {
- var ring = polygon[i], m = ring.length;
- if (!m) continue;
- var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1;
- while (true) {
- if (j === m) j = 0;
- point = ring[j];
- var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ;
- d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
- polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
- if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
- var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
- d3_geo_cartesianNormalize(arc);
- var intersection = d3_geo_cartesianCross(meridianNormal, arc);
- d3_geo_cartesianNormalize(intersection);
- var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
- if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
- winding += antimeridian ^ dλ >= 0 ? 1 : -1;
- }
- }
- if (!j++) break;
- λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
- }
- }
- return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < -ε) ^ winding & 1;
- }
- function d3_geo_clipCircle(radius) {
- var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
- return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]);
- function visible(λ, φ) {
- return Math.cos(λ) * Math.cos(φ) > cr;
- }
- function clipLine(listener) {
- var point0, c0, v0, v00, clean;
- return {
- lineStart: function() {
- v00 = v0 = false;
- clean = 1;
- },
- point: function(λ, φ) {
- var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
- if (!point0 && (v00 = v0 = v)) listener.lineStart();
- if (v !== v0) {
- point2 = intersect(point0, point1);
- if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
- point1[0] += ε;
- point1[1] += ε;
- v = visible(point1[0], point1[1]);
- }
- }
- if (v !== v0) {
- clean = 0;
- if (v) {
- listener.lineStart();
- point2 = intersect(point1, point0);
- listener.point(point2[0], point2[1]);
- } else {
- point2 = intersect(point0, point1);
- listener.point(point2[0], point2[1]);
- listener.lineEnd();
- }
- point0 = point2;
- } else if (notHemisphere && point0 && smallRadius ^ v) {
- var t;
- if (!(c & c0) && (t = intersect(point1, point0, true))) {
- clean = 0;
- if (smallRadius) {
- listener.lineStart();
- listener.point(t[0][0], t[0][1]);
- listener.point(t[1][0], t[1][1]);
- listener.lineEnd();
- } else {
- listener.point(t[1][0], t[1][1]);
- listener.lineEnd();
- listener.lineStart();
- listener.point(t[0][0], t[0][1]);
- }
- }
- }
- if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
- listener.point(point1[0], point1[1]);
- }
- point0 = point1, v0 = v, c0 = c;
- },
- lineEnd: function() {
- if (v0) listener.lineEnd();
- point0 = null;
- },
- clean: function() {
- return clean | (v00 && v0) << 1;
- }
- };
- }
- function intersect(a, b, two) {
- var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b);
- var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2;
- if (!determinant) return !two && a;
- var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2);
- d3_geo_cartesianAdd(A, B);
- var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
- if (t2 < 0) return;
- var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu);
- d3_geo_cartesianAdd(q, A);
- q = d3_geo_spherical(q);
- if (!two) return q;
- var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z;
- if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
- var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε;
- if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
- if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
- var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
- d3_geo_cartesianAdd(q1, A);
- return [ q, d3_geo_spherical(q1) ];
- }
- }
- function code(λ, φ) {
- var r = smallRadius ? radius : π - radius, code = 0;
- if (λ < -r) code |= 1; else if (λ > r) code |= 2;
- if (φ < -r) code |= 4; else if (φ > r) code |= 8;
- return code;
- }
- }
- function d3_geom_clipLine(x0, y0, x1, y1) {
- return function(line) {
- var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r;
- r = x0 - ax;
- if (!dx && r > 0) return;
- r /= dx;
- if (dx < 0) {
- if (r < t0) return;
- if (r < t1) t1 = r;
- } else if (dx > 0) {
- if (r > t1) return;
- if (r > t0) t0 = r;
- }
- r = x1 - ax;
- if (!dx && r < 0) return;
- r /= dx;
- if (dx < 0) {
- if (r > t1) return;
- if (r > t0) t0 = r;
- } else if (dx > 0) {
- if (r < t0) return;
- if (r < t1) t1 = r;
- }
- r = y0 - ay;
- if (!dy && r > 0) return;
- r /= dy;
- if (dy < 0) {
- if (r < t0) return;
- if (r < t1) t1 = r;
- } else if (dy > 0) {
- if (r > t1) return;
- if (r > t0) t0 = r;
- }
- r = y1 - ay;
- if (!dy && r < 0) return;
- r /= dy;
- if (dy < 0) {
- if (r > t1) return;
- if (r > t0) t0 = r;
- } else if (dy > 0) {
- if (r < t0) return;
- if (r < t1) t1 = r;
- }
- if (t0 > 0) line.a = {
- x: ax + t0 * dx,
- y: ay + t0 * dy
- };
- if (t1 < 1) line.b = {
- x: ax + t1 * dx,
- y: ay + t1 * dy
- };
- return line;
- };
- }
- var d3_geo_clipExtentMAX = 1e9;
- d3.geo.clipExtent = function() {
- var x0, y0, x1, y1, stream, clip, clipExtent = {
- stream: function(output) {
- if (stream) stream.valid = false;
- stream = clip(output);
- stream.valid = true;
- return stream;
- },
- extent: function(_) {
- if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
- clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
- if (stream) stream.valid = false, stream = null;
- return clipExtent;
- }
- };
- return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]);
- };
- function d3_geo_clipExtent(x0, y0, x1, y1) {
- return function(listener) {
- var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring;
- var clip = {
- point: point,
- lineStart: lineStart,
- lineEnd: lineEnd,
- polygonStart: function() {
- listener = bufferListener;
- segments = [];
- polygon = [];
- clean = true;
- },
- polygonEnd: function() {
- listener = listener_;
- segments = d3.merge(segments);
- var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length;
- if (inside || visible) {
- listener.polygonStart();
- if (inside) {
- listener.lineStart();
- interpolate(null, null, 1, listener);
- listener.lineEnd();
- }
- if (visible) {
- d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
- }
- listener.polygonEnd();
- }
- segments = polygon = ring = null;
- }
- };
- function insidePolygon(p) {
- var wn = 0, n = polygon.length, y = p[1];
- for (var i = 0; i < n; ++i) {
- for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
- b = v[j];
- if (a[1] <= y) {
- if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn;
- } else {
- if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
- }
- a = b;
- }
- }
- return wn !== 0;
- }
- function interpolate(from, to, direction, listener) {
- var a = 0, a1 = 0;
- if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) {
- do {
- listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
- } while ((a = (a + direction + 4) % 4) !== a1);
- } else {
- listener.point(to[0], to[1]);
- }
- }
- function pointVisible(x, y) {
- return x0 <= x && x <= x1 && y0 <= y && y <= y1;
- }
- function point(x, y) {
- if (pointVisible(x, y)) listener.point(x, y);
- }
- var x__, y__, v__, x_, y_, v_, first, clean;
- function lineStart() {
- clip.point = linePoint;
- if (polygon) polygon.push(ring = []);
- first = true;
- v_ = false;
- x_ = y_ = NaN;
- }
- function lineEnd() {
- if (segments) {
- linePoint(x__, y__);
- if (v__ && v_) bufferListener.rejoin();
- segments.push(bufferListener.buffer());
- }
- clip.point = point;
- if (v_) listener.lineEnd();
- }
- function linePoint(x, y) {
- x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
- y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
- var v = pointVisible(x, y);
- if (polygon) ring.push([ x, y ]);
- if (first) {
- x__ = x, y__ = y, v__ = v;
- first = false;
- if (v) {
- listener.lineStart();
- listener.point(x, y);
- }
- } else {
- if (v && v_) listener.point(x, y); else {
- var l = {
- a: {
- x: x_,
- y: y_
- },
- b: {
- x: x,
- y: y
- }
- };
- if (clipLine(l)) {
- if (!v_) {
- listener.lineStart();
- listener.point(l.a.x, l.a.y);
- }
- listener.point(l.b.x, l.b.y);
- if (!v) listener.lineEnd();
- clean = false;
- } else if (v) {
- listener.lineStart();
- listener.point(x, y);
- clean = false;
- }
- }
- }
- x_ = x, y_ = y, v_ = v;
- }
- return clip;
- };
- function corner(p, direction) {
- return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2;
- }
- function compare(a, b) {
- return comparePoints(a.x, b.x);
- }
- function comparePoints(a, b) {
- var ca = corner(a, 1), cb = corner(b, 1);
- return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0];
- }
- }
- function d3_geo_conic(projectAt) {
- var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1);
- p.parallels = function(_) {
- if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ];
- return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
- };
- return p;
- }
- function d3_geo_conicEqualArea(φ0, φ1) {
- var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n;
- function forward(λ, φ) {
- var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
- return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ];
- }
- forward.invert = function(x, y) {
- var ρ0_y = ρ0 - y;
- return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ];
- };
- return forward;
- }
- (d3.geo.conicEqualArea = function() {
- return d3_geo_conic(d3_geo_conicEqualArea);
- }).raw = d3_geo_conicEqualArea;
- d3.geo.albers = function() {
- return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070);
- };
- d3.geo.albersUsa = function() {
- var lower48 = d3.geo.albers();
- var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]);
- var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]);
- var point, pointStream = {
- point: function(x, y) {
- point = [ x, y ];
- }
- }, lower48Point, alaskaPoint, hawaiiPoint;
- function albersUsa(coordinates) {
- var x = coordinates[0], y = coordinates[1];
- point = null;
- (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y);
- return point;
- }
- albersUsa.invert = function(coordinates) {
- var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k;
- return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates);
- };
- albersUsa.stream = function(stream) {
- var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream);
- return {
- point: function(x, y) {
- lower48Stream.point(x, y);
- alaskaStream.point(x, y);
- hawaiiStream.point(x, y);
- },
- sphere: function() {
- lower48Stream.sphere();
- alaskaStream.sphere();
- hawaiiStream.sphere();
- },
- lineStart: function() {
- lower48Stream.lineStart();
- alaskaStream.lineStart();
- hawaiiStream.lineStart();
- },
- lineEnd: function() {
- lower48Stream.lineEnd();
- alaskaStream.lineEnd();
- hawaiiStream.lineEnd();
- },
- polygonStart: function() {
- lower48Stream.polygonStart();
- alaskaStream.polygonStart();
- hawaiiStream.polygonStart();
- },
- polygonEnd: function() {
- lower48Stream.polygonEnd();
- alaskaStream.polygonEnd();
- hawaiiStream.polygonEnd();
- }
- };
- };
- albersUsa.precision = function(_) {
- if (!arguments.length) return lower48.precision();
- lower48.precision(_);
- alaska.precision(_);
- hawaii.precision(_);
- return albersUsa;
- };
- albersUsa.scale = function(_) {
- if (!arguments.length) return lower48.scale();
- lower48.scale(_);
- alaska.scale(_ * .35);
- hawaii.scale(_);
- return albersUsa.translate(lower48.translate());
- };
- albersUsa.translate = function(_) {
- if (!arguments.length) return lower48.translate();
- var k = lower48.scale(), x = +_[0], y = +_[1];
- lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point;
- alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
- hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
- return albersUsa;
- };
- return albersUsa.scale(1070);
- };
- var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
- point: d3_noop,
- lineStart: d3_noop,
- lineEnd: d3_noop,
- polygonStart: function() {
- d3_geo_pathAreaPolygon = 0;
- d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
- },
- polygonEnd: function() {
- d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
- d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
- }
- };
- function d3_geo_pathAreaRingStart() {
- var x00, y00, x0, y0;
- d3_geo_pathArea.point = function(x, y) {
- d3_geo_pathArea.point = nextPoint;
- x00 = x0 = x, y00 = y0 = y;
- };
- function nextPoint(x, y) {
- d3_geo_pathAreaPolygon += y0 * x - x0 * y;
- x0 = x, y0 = y;
- }
- d3_geo_pathArea.lineEnd = function() {
- nextPoint(x00, y00);
- };
- }
- var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1;
- var d3_geo_pathBounds = {
- point: d3_geo_pathBoundsPoint,
- lineStart: d3_noop,
- lineEnd: d3_noop,
- polygonStart: d3_noop,
- polygonEnd: d3_noop
- };
- function d3_geo_pathBoundsPoint(x, y) {
- if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
- if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
- if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
- if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
- }
- function d3_geo_pathBuffer() {
- var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = [];
- var stream = {
- point: point,
- lineStart: function() {
- stream.point = pointLineStart;
- },
- lineEnd: lineEnd,
- polygonStart: function() {
- stream.lineEnd = lineEndPolygon;
- },
- polygonEnd: function() {
- stream.lineEnd = lineEnd;
- stream.point = point;
- },
- pointRadius: function(_) {
- pointCircle = d3_geo_pathBufferCircle(_);
- return stream;
- },
- result: function() {
- if (buffer.length) {
- var result = buffer.join("");
- buffer = [];
- return result;
- }
- }
- };
- function point(x, y) {
- buffer.push("M", x, ",", y, pointCircle);
- }
- function pointLineStart(x, y) {
- buffer.push("M", x, ",", y);
- stream.point = pointLine;
- }
- function pointLine(x, y) {
- buffer.push("L", x, ",", y);
- }
- function lineEnd() {
- stream.point = point;
- }
- function lineEndPolygon() {
- buffer.push("Z");
- }
- return stream;
- }
- function d3_geo_pathBufferCircle(radius) {
- return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
- }
- var d3_geo_pathCentroid = {
- point: d3_geo_pathCentroidPoint,
- lineStart: d3_geo_pathCentroidLineStart,
- lineEnd: d3_geo_pathCentroidLineEnd,
- polygonStart: function() {
- d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
- },
- polygonEnd: function() {
- d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
- d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
- d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
- }
- };
- function d3_geo_pathCentroidPoint(x, y) {
- d3_geo_centroidX0 += x;
- d3_geo_centroidY0 += y;
- ++d3_geo_centroidZ0;
- }
- function d3_geo_pathCentroidLineStart() {
- var x0, y0;
- d3_geo_pathCentroid.point = function(x, y) {
- d3_geo_pathCentroid.point = nextPoint;
- d3_geo_pathCentroidPoint(x0 = x, y0 = y);
- };
- function nextPoint(x, y) {
- var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
- d3_geo_centroidX1 += z * (x0 + x) / 2;
- d3_geo_centroidY1 += z * (y0 + y) / 2;
- d3_geo_centroidZ1 += z;
- d3_geo_pathCentroidPoint(x0 = x, y0 = y);
- }
- }
- function d3_geo_pathCentroidLineEnd() {
- d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
- }
- function d3_geo_pathCentroidRingStart() {
- var x00, y00, x0, y0;
- d3_geo_pathCentroid.point = function(x, y) {
- d3_geo_pathCentroid.point = nextPoint;
- d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
- };
- function nextPoint(x, y) {
- var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
- d3_geo_centroidX1 += z * (x0 + x) / 2;
- d3_geo_centroidY1 += z * (y0 + y) / 2;
- d3_geo_centroidZ1 += z;
- z = y0 * x - x0 * y;
- d3_geo_centroidX2 += z * (x0 + x);
- d3_geo_centroidY2 += z * (y0 + y);
- d3_geo_centroidZ2 += z * 3;
- d3_geo_pathCentroidPoint(x0 = x, y0 = y);
- }
- d3_geo_pathCentroid.lineEnd = function() {
- nextPoint(x00, y00);
- };
- }
- function d3_geo_pathContext(context) {
- var pointRadius = 4.5;
- var stream = {
- point: point,
- lineStart: function() {
- stream.point = pointLineStart;
- },
- lineEnd: lineEnd,
- polygonStart: function() {
- stream.lineEnd = lineEndPolygon;
- },
- polygonEnd: function() {
- stream.lineEnd = lineEnd;
- stream.point = point;
- },
- pointRadius: function(_) {
- pointRadius = _;
- return stream;
- },
- result: d3_noop
- };
- function point(x, y) {
- context.moveTo(x + pointRadius, y);
- context.arc(x, y, pointRadius, 0, τ);
- }
- function pointLineStart(x, y) {
- context.moveTo(x, y);
- stream.point = pointLine;
- }
- function pointLine(x, y) {
- context.lineTo(x, y);
- }
- function lineEnd() {
- stream.point = point;
- }
- function lineEndPolygon() {
- context.closePath();
- }
- return stream;
- }
- function d3_geo_resample(project) {
- var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16;
- function resample(stream) {
- return (maxDepth ? resampleRecursive : resampleNone)(stream);
- }
- function resampleNone(stream) {
- return d3_geo_transformPoint(stream, function(x, y) {
- x = project(x, y);
- stream.point(x[0], x[1]);
- });
- }
- function resampleRecursive(stream) {
- var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0;
- var resample = {
- point: point,
- lineStart: lineStart,
- lineEnd: lineEnd,
- polygonStart: function() {
- stream.polygonStart();
- resample.lineStart = ringStart;
- },
- polygonEnd: function() {
- stream.polygonEnd();
- resample.lineStart = lineStart;
- }
- };
- function point(x, y) {
- x = project(x, y);
- stream.point(x[0], x[1]);
- }
- function lineStart() {
- x0 = NaN;
- resample.point = linePoint;
- stream.lineStart();
- }
- function linePoint(λ, φ) {
- var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ);
- resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
- stream.point(x0, y0);
- }
- function lineEnd() {
- resample.point = point;
- stream.lineEnd();
- }
- function ringStart() {
- lineStart();
- resample.point = ringPoint;
- resample.lineEnd = ringEnd;
- }
- function ringPoint(λ, φ) {
- linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
- resample.point = linePoint;
- }
- function ringEnd() {
- resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
- resample.lineEnd = lineEnd;
- lineEnd();
- }
- return resample;
- }
- function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
- var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy;
- if (d2 > 4 * δ2 && depth--) {
- var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2;
- if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
- resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
- stream.point(x2, y2);
- resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
- }
- }
- }
- resample.precision = function(_) {
- if (!arguments.length) return Math.sqrt(δ2);
- maxDepth = (δ2 = _ * _) > 0 && 16;
- return resample;
- };
- return resample;
- }
- d3.geo.path = function() {
- var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream;
- function path(object) {
- if (object) {
- if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
- if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
- d3.geo.stream(object, cacheStream);
- }
- return contextStream.result();
- }
- path.area = function(object) {
- d3_geo_pathAreaSum = 0;
- d3.geo.stream(object, projectStream(d3_geo_pathArea));
- return d3_geo_pathAreaSum;
- };
- path.centroid = function(object) {
- d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
- d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
- return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ];
- };
- path.bounds = function(object) {
- d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
- d3.geo.stream(object, projectStream(d3_geo_pathBounds));
- return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ];
- };
- path.projection = function(_) {
- if (!arguments.length) return projection;
- projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
- return reset();
- };
- path.context = function(_) {
- if (!arguments.length) return context;
- contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_);
- if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
- return reset();
- };
- path.pointRadius = function(_) {
- if (!arguments.length) return pointRadius;
- pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
- return path;
- };
- function reset() {
- cacheStream = null;
- return path;
- }
- return path.projection(d3.geo.albersUsa()).context(null);
- };
- function d3_geo_pathProjectStream(project) {
- var resample = d3_geo_resample(function(x, y) {
- return project([ x * d3_degrees, y * d3_degrees ]);
- });
- return function(stream) {
- return d3_geo_projectionRadians(resample(stream));
- };
- }
- d3.geo.transform = function(methods) {
- return {
- stream: function(stream) {
- var transform = new d3_geo_transform(stream);
- for (var k in methods) transform[k] = methods[k];
- return transform;
- }
- };
- };
- function d3_geo_transform(stream) {
- this.stream = stream;
- }
- d3_geo_transform.prototype = {
- point: function(x, y) {
- this.stream.point(x, y);
- },
- sphere: function() {
- this.stream.sphere();
- },
- lineStart: function() {
- this.stream.lineStart();
- },
- lineEnd: function() {
- this.stream.lineEnd();
- },
- polygonStart: function() {
- this.stream.polygonStart();
- },
- polygonEnd: function() {
- this.stream.polygonEnd();
- }
- };
- function d3_geo_transformPoint(stream, point) {
- return {
- point: point,
- sphere: function() {
- stream.sphere();
- },
- lineStart: function() {
- stream.lineStart();
- },
- lineEnd: function() {
- stream.lineEnd();
- },
- polygonStart: function() {
- stream.polygonStart();
- },
- polygonEnd: function() {
- stream.polygonEnd();
- }
- };
- }
- d3.geo.projection = d3_geo_projection;
- d3.geo.projectionMutator = d3_geo_projectionMutator;
- function d3_geo_projection(project) {
- return d3_geo_projectionMutator(function() {
- return project;
- })();
- }
- function d3_geo_projectionMutator(projectAt) {
- var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) {
- x = project(x, y);
- return [ x[0] * k + δx, δy - x[1] * k ];
- }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream;
- function projection(point) {
- point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
- return [ point[0] * k + δx, δy - point[1] * k ];
- }
- function invert(point) {
- point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
- return point && [ point[0] * d3_degrees, point[1] * d3_degrees ];
- }
- projection.stream = function(output) {
- if (stream) stream.valid = false;
- stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
- stream.valid = true;
- return stream;
- };
- projection.clipAngle = function(_) {
- if (!arguments.length) return clipAngle;
- preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
- return invalidate();
- };
- projection.clipExtent = function(_) {
- if (!arguments.length) return clipExtent;
- clipExtent = _;
- postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
- return invalidate();
- };
- projection.scale = function(_) {
- if (!arguments.length) return k;
- k = +_;
- return reset();
- };
- projection.translate = function(_) {
- if (!arguments.length) return [ x, y ];
- x = +_[0];
- y = +_[1];
- return reset();
- };
- projection.center = function(_) {
- if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ];
- λ = _[0] % 360 * d3_radians;
- φ = _[1] % 360 * d3_radians;
- return reset();
- };
- projection.rotate = function(_) {
- if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ];
- δλ = _[0] % 360 * d3_radians;
- δφ = _[1] % 360 * d3_radians;
- δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
- return reset();
- };
- d3.rebind(projection, projectResample, "precision");
- function reset() {
- projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
- var center = project(λ, φ);
- δx = x - center[0] * k;
- δy = y + center[1] * k;
- return invalidate();
- }
- function invalidate() {
- if (stream) stream.valid = false, stream = null;
- return projection;
- }
- return function() {
- project = projectAt.apply(this, arguments);
- projection.invert = project.invert && invert;
- return reset();
- };
- }
- function d3_geo_projectionRadians(stream) {
- return d3_geo_transformPoint(stream, function(x, y) {
- stream.point(x * d3_radians, y * d3_radians);
- });
- }
- function d3_geo_equirectangular(λ, φ) {
- return [ λ, φ ];
- }
- (d3.geo.equirectangular = function() {
- return d3_geo_projection(d3_geo_equirectangular);
- }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
- d3.geo.rotation = function(rotate) {
- rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
- function forward(coordinates) {
- coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
- return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
- }
- forward.invert = function(coordinates) {
- coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
- return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
- };
- return forward;
- };
- function d3_geo_identityRotation(λ, φ) {
- return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
- }
- d3_geo_identityRotation.invert = d3_geo_equirectangular;
- function d3_geo_rotation(δλ, δφ, δγ) {
- return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation;
- }
- function d3_geo_forwardRotationλ(δλ) {
- return function(λ, φ) {
- return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
- };
- }
- function d3_geo_rotationλ(δλ) {
- var rotation = d3_geo_forwardRotationλ(δλ);
- rotation.invert = d3_geo_forwardRotationλ(-δλ);
- return rotation;
- }
- function d3_geo_rotationφγ(δφ, δγ) {
- var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ);
- function rotation(λ, φ) {
- var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ;
- return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ];
- }
- rotation.invert = function(λ, φ) {
- var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ;
- return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ];
- };
- return rotation;
- }
- d3.geo.circle = function() {
- var origin = [ 0, 0 ], angle, precision = 6, interpolate;
- function circle() {
- var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = [];
- interpolate(null, null, 1, {
- point: function(x, y) {
- ring.push(x = rotate(x, y));
- x[0] *= d3_degrees, x[1] *= d3_degrees;
- }
- });
- return {
- type: "Polygon",
- coordinates: [ ring ]
- };
- }
- circle.origin = function(x) {
- if (!arguments.length) return origin;
- origin = x;
- return circle;
- };
- circle.angle = function(x) {
- if (!arguments.length) return angle;
- interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
- return circle;
- };
- circle.precision = function(_) {
- if (!arguments.length) return precision;
- interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
- return circle;
- };
- return circle.angle(90);
- };
- function d3_geo_circleInterpolate(radius, precision) {
- var cr = Math.cos(radius), sr = Math.sin(radius);
- return function(from, to, direction, listener) {
- var step = direction * precision;
- if (from != null) {
- from = d3_geo_circleAngle(cr, from);
- to = d3_geo_circleAngle(cr, to);
- if (direction > 0 ? from < to : from > to) from += direction * τ;
- } else {
- from = radius + direction * τ;
- to = radius - .5 * step;
- }
- for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
- listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
- }
- };
- }
- function d3_geo_circleAngle(cr, point) {
- var a = d3_geo_cartesian(point);
- a[0] -= cr;
- d3_geo_cartesianNormalize(a);
- var angle = d3_acos(-a[1]);
- return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
- }
- d3.geo.distance = function(a, b) {
- var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t;
- return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ);
- };
- d3.geo.graticule = function() {
- var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5;
- function graticule() {
- return {
- type: "MultiLineString",
- coordinates: lines()
- };
- }
- function lines() {
- return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) {
- return abs(x % DX) > ε;
- }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) {
- return abs(y % DY) > ε;
- }).map(y));
- }
- graticule.lines = function() {
- return lines().map(function(coordinates) {
- return {
- type: "LineString",
- coordinates: coordinates
- };
- });
- };
- graticule.outline = function() {
- return {
- type: "Polygon",
- coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ]
- };
- };
- graticule.extent = function(_) {
- if (!arguments.length) return graticule.minorExtent();
- return graticule.majorExtent(_).minorExtent(_);
- };
- graticule.majorExtent = function(_) {
- if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ];
- X0 = +_[0][0], X1 = +_[1][0];
- Y0 = +_[0][1], Y1 = +_[1][1];
- if (X0 > X1) _ = X0, X0 = X1, X1 = _;
- if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
- return graticule.precision(precision);
- };
- graticule.minorExtent = function(_) {
- if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
- x0 = +_[0][0], x1 = +_[1][0];
- y0 = +_[0][1], y1 = +_[1][1];
- if (x0 > x1) _ = x0, x0 = x1, x1 = _;
- if (y0 > y1) _ = y0, y0 = y1, y1 = _;
- return graticule.precision(precision);
- };
- graticule.step = function(_) {
- if (!arguments.length) return graticule.minorStep();
- return graticule.majorStep(_).minorStep(_);
- };
- graticule.majorStep = function(_) {
- if (!arguments.length) return [ DX, DY ];
- DX = +_[0], DY = +_[1];
- return graticule;
- };
- graticule.minorStep = function(_) {
- if (!arguments.length) return [ dx, dy ];
- dx = +_[0], dy = +_[1];
- return graticule;
- };
- graticule.precision = function(_) {
- if (!arguments.length) return precision;
- precision = +_;
- x = d3_geo_graticuleX(y0, y1, 90);
- y = d3_geo_graticuleY(x0, x1, precision);
- X = d3_geo_graticuleX(Y0, Y1, 90);
- Y = d3_geo_graticuleY(X0, X1, precision);
- return graticule;
- };
- return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]);
- };
- function d3_geo_graticuleX(y0, y1, dy) {
- var y = d3.range(y0, y1 - ε, dy).concat(y1);
- return function(x) {
- return y.map(function(y) {
- return [ x, y ];
- });
- };
- }
- function d3_geo_graticuleY(x0, x1, dx) {
- var x = d3.range(x0, x1 - ε, dx).concat(x1);
- return function(y) {
- return x.map(function(x) {
- return [ x, y ];
- });
- };
- }
- function d3_source(d) {
- return d.source;
- }
- function d3_target(d) {
- return d.target;
- }
- d3.geo.greatArc = function() {
- var source = d3_source, source_, target = d3_target, target_;
- function greatArc() {
- return {
- type: "LineString",
- coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ]
- };
- }
- greatArc.distance = function() {
- return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments));
- };
- greatArc.source = function(_) {
- if (!arguments.length) return source;
- source = _, source_ = typeof _ === "function" ? null : _;
- return greatArc;
- };
- greatArc.target = function(_) {
- if (!arguments.length) return target;
- target = _, target_ = typeof _ === "function" ? null : _;
- return greatArc;
- };
- greatArc.precision = function() {
- return arguments.length ? greatArc : 0;
- };
- return greatArc;
- };
- d3.geo.interpolate = function(source, target) {
- return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians);
- };
- function d3_geo_interpolate(x0, y0, x1, y1) {
- var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d);
- var interpolate = d ? function(t) {
- var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1;
- return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ];
- } : function() {
- return [ x0 * d3_degrees, y0 * d3_degrees ];
- };
- interpolate.distance = d;
- return interpolate;
- }
- d3.geo.length = function(object) {
- d3_geo_lengthSum = 0;
- d3.geo.stream(object, d3_geo_length);
- return d3_geo_lengthSum;
- };
- var d3_geo_lengthSum;
- var d3_geo_length = {
- sphere: d3_noop,
- point: d3_noop,
- lineStart: d3_geo_lengthLineStart,
- lineEnd: d3_noop,
- polygonStart: d3_noop,
- polygonEnd: d3_noop
- };
- function d3_geo_lengthLineStart() {
- var λ0, sinφ0, cosφ0;
- d3_geo_length.point = function(λ, φ) {
- λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
- d3_geo_length.point = nextPoint;
- };
- d3_geo_length.lineEnd = function() {
- d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
- };
- function nextPoint(λ, φ) {
- var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t);
- d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ);
- λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
- }
- }
- function d3_geo_azimuthal(scale, angle) {
- function azimuthal(λ, φ) {
- var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ);
- return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ];
- }
- azimuthal.invert = function(x, y) {
- var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c);
- return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ];
- };
- return azimuthal;
- }
- var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) {
- return Math.sqrt(2 / (1 + cosλcosφ));
- }, function(ρ) {
- return 2 * Math.asin(ρ / 2);
- });
- (d3.geo.azimuthalEqualArea = function() {
- return d3_geo_projection(d3_geo_azimuthalEqualArea);
- }).raw = d3_geo_azimuthalEqualArea;
- var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) {
- var c = Math.acos(cosλcosφ);
- return c && c / Math.sin(c);
- }, d3_identity);
- (d3.geo.azimuthalEquidistant = function() {
- return d3_geo_projection(d3_geo_azimuthalEquidistant);
- }).raw = d3_geo_azimuthalEquidistant;
- function d3_geo_conicConformal(φ0, φ1) {
- var cosφ0 = Math.cos(φ0), t = function(φ) {
- return Math.tan(π / 4 + φ / 2);
- }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n;
- if (!n) return d3_geo_mercator;
- function forward(λ, φ) {
- if (F > 0) {
- if (φ < -halfπ + ε) φ = -halfπ + ε;
- } else {
- if (φ > halfπ - ε) φ = halfπ - ε;
- }
- var ρ = F / Math.pow(t(φ), n);
- return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ];
- }
- forward.invert = function(x, y) {
- var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y);
- return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ];
- };
- return forward;
- }
- (d3.geo.conicConformal = function() {
- return d3_geo_conic(d3_geo_conicConformal);
- }).raw = d3_geo_conicConformal;
- function d3_geo_conicEquidistant(φ0, φ1) {
- var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0;
- if (abs(n) < ε) return d3_geo_equirectangular;
- function forward(λ, φ) {
- var ρ = G - φ;
- return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ];
- }
- forward.invert = function(x, y) {
- var ρ0_y = G - y;
- return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ];
- };
- return forward;
- }
- (d3.geo.conicEquidistant = function() {
- return d3_geo_conic(d3_geo_conicEquidistant);
- }).raw = d3_geo_conicEquidistant;
- var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) {
- return 1 / cosλcosφ;
- }, Math.atan);
- (d3.geo.gnomonic = function() {
- return d3_geo_projection(d3_geo_gnomonic);
- }).raw = d3_geo_gnomonic;
- function d3_geo_mercator(λ, φ) {
- return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ];
- }
- d3_geo_mercator.invert = function(x, y) {
- return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ];
- };
- function d3_geo_mercatorProjection(project) {
- var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto;
- m.scale = function() {
- var v = scale.apply(m, arguments);
- return v === m ? clipAuto ? m.clipExtent(null) : m : v;
- };
- m.translate = function() {
- var v = translate.apply(m, arguments);
- return v === m ? clipAuto ? m.clipExtent(null) : m : v;
- };
- m.clipExtent = function(_) {
- var v = clipExtent.apply(m, arguments);
- if (v === m) {
- if (clipAuto = _ == null) {
- var k = π * scale(), t = translate();
- clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]);
- }
- } else if (clipAuto) {
- v = null;
- }
- return v;
- };
- return m.clipExtent(null);
- }
- (d3.geo.mercator = function() {
- return d3_geo_mercatorProjection(d3_geo_mercator);
- }).raw = d3_geo_mercator;
- var d3_geo_orthographic = d3_geo_azimuthal(function() {
- return 1;
- }, Math.asin);
- (d3.geo.orthographic = function() {
- return d3_geo_projection(d3_geo_orthographic);
- }).raw = d3_geo_orthographic;
- var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) {
- return 1 / (1 + cosλcosφ);
- }, function(ρ) {
- return 2 * Math.atan(ρ);
- });
- (d3.geo.stereographic = function() {
- return d3_geo_projection(d3_geo_stereographic);
- }).raw = d3_geo_stereographic;
- function d3_geo_transverseMercator(λ, φ) {
- return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ];
- }
- d3_geo_transverseMercator.invert = function(x, y) {
- return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ];
- };
- (d3.geo.transverseMercator = function() {
- var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate;
- projection.center = function(_) {
- return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]);
- };
- projection.rotate = function(_) {
- return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(),
- [ _[0], _[1], _[2] - 90 ]);
- };
- return rotate([ 0, 0, 90 ]);
- }).raw = d3_geo_transverseMercator;
- d3.geom = {};
- function d3_geom_pointX(d) {
- return d[0];
- }
- function d3_geom_pointY(d) {
- return d[1];
- }
- d3.geom.hull = function(vertices) {
- var x = d3_geom_pointX, y = d3_geom_pointY;
- if (arguments.length) return hull(vertices);
- function hull(data) {
- if (data.length < 3) return [];
- var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = [];
- for (i = 0; i < n; i++) {
- points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]);
- }
- points.sort(d3_geom_hullOrder);
- for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]);
- var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints);
- var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = [];
- for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
- for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
- return polygon;
- }
- hull.x = function(_) {
- return arguments.length ? (x = _, hull) : x;
- };
- hull.y = function(_) {
- return arguments.length ? (y = _, hull) : y;
- };
- return hull;
- };
- function d3_geom_hullUpper(points) {
- var n = points.length, hull = [ 0, 1 ], hs = 2;
- for (var i = 2; i < n; i++) {
- while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs;
- hull[hs++] = i;
- }
- return hull.slice(0, hs);
- }
- function d3_geom_hullOrder(a, b) {
- return a[0] - b[0] || a[1] - b[1];
- }
- d3.geom.polygon = function(coordinates) {
- d3_subclass(coordinates, d3_geom_polygonPrototype);
- return coordinates;
- };
- var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
- d3_geom_polygonPrototype.area = function() {
- var i = -1, n = this.length, a, b = this[n - 1], area = 0;
- while (++i < n) {
- a = b;
- b = this[i];
- area += a[1] * b[0] - a[0] * b[1];
- }
- return area * .5;
- };
- d3_geom_polygonPrototype.centroid = function(k) {
- var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c;
- if (!arguments.length) k = -1 / (6 * this.area());
- while (++i < n) {
- a = b;
- b = this[i];
- c = a[0] * b[1] - b[0] * a[1];
- x += (a[0] + b[0]) * c;
- y += (a[1] + b[1]) * c;
- }
- return [ x * k, y * k ];
- };
- d3_geom_polygonPrototype.clip = function(subject) {
- var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d;
- while (++i < n) {
- input = subject.slice();
- subject.length = 0;
- b = this[i];
- c = input[(m = input.length - closed) - 1];
- j = -1;
- while (++j < m) {
- d = input[j];
- if (d3_geom_polygonInside(d, a, b)) {
- if (!d3_geom_polygonInside(c, a, b)) {
- subject.push(d3_geom_polygonIntersect(c, d, a, b));
- }
- subject.push(d);
- } else if (d3_geom_polygonInside(c, a, b)) {
- subject.push(d3_geom_polygonIntersect(c, d, a, b));
- }
- c = d;
- }
- if (closed) subject.push(subject[0]);
- a = b;
- }
- return subject;
- };
- function d3_geom_polygonInside(p, a, b) {
- return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
- }
- function d3_geom_polygonIntersect(c, d, a, b) {
- var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
- return [ x1 + ua * x21, y1 + ua * y21 ];
- }
- function d3_geom_polygonClosed(coordinates) {
- var a = coordinates[0], b = coordinates[coordinates.length - 1];
- return !(a[0] - b[0] || a[1] - b[1]);
- }
- var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = [];
- function d3_geom_voronoiBeach() {
- d3_geom_voronoiRedBlackNode(this);
- this.edge = this.site = this.circle = null;
- }
- function d3_geom_voronoiCreateBeach(site) {
- var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach();
- beach.site = site;
- return beach;
- }
- function d3_geom_voronoiDetachBeach(beach) {
- d3_geom_voronoiDetachCircle(beach);
- d3_geom_voronoiBeaches.remove(beach);
- d3_geom_voronoiBeachPool.push(beach);
- d3_geom_voronoiRedBlackNode(beach);
- }
- function d3_geom_voronoiRemoveBeach(beach) {
- var circle = beach.circle, x = circle.x, y = circle.cy, vertex = {
- x: x,
- y: y
- }, previous = beach.P, next = beach.N, disappearing = [ beach ];
- d3_geom_voronoiDetachBeach(beach);
- var lArc = previous;
- while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) {
- previous = lArc.P;
- disappearing.unshift(lArc);
- d3_geom_voronoiDetachBeach(lArc);
- lArc = previous;
- }
- disappearing.unshift(lArc);
- d3_geom_voronoiDetachCircle(lArc);
- var rArc = next;
- while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) {
- next = rArc.N;
- disappearing.push(rArc);
- d3_geom_voronoiDetachBeach(rArc);
- rArc = next;
- }
- disappearing.push(rArc);
- d3_geom_voronoiDetachCircle(rArc);
- var nArcs = disappearing.length, iArc;
- for (iArc = 1; iArc < nArcs; ++iArc) {
- rArc = disappearing[iArc];
- lArc = disappearing[iArc - 1];
- d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
- }
- lArc = disappearing[0];
- rArc = disappearing[nArcs - 1];
- rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex);
- d3_geom_voronoiAttachCircle(lArc);
- d3_geom_voronoiAttachCircle(rArc);
- }
- function d3_geom_voronoiAddBeach(site) {
- var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._;
- while (node) {
- dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x;
- if (dxl > ε) node = node.L; else {
- dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix);
- if (dxr > ε) {
- if (!node.R) {
- lArc = node;
- break;
- }
- node = node.R;
- } else {
- if (dxl > -ε) {
- lArc = node.P;
- rArc = node;
- } else if (dxr > -ε) {
- lArc = node;
- rArc = node.N;
- } else {
- lArc = rArc = node;
- }
- break;
- }
- }
- }
- var newArc = d3_geom_voronoiCreateBeach(site);
- d3_geom_voronoiBeaches.insert(lArc, newArc);
- if (!lArc && !rArc) return;
- if (lArc === rArc) {
- d3_geom_voronoiDetachCircle(lArc);
- rArc = d3_geom_voronoiCreateBeach(lArc.site);
- d3_geom_voronoiBeaches.insert(newArc, rArc);
- newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
- d3_geom_voronoiAttachCircle(lArc);
- d3_geom_voronoiAttachCircle(rArc);
- return;
- }
- if (!rArc) {
- newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
- return;
- }
- d3_geom_voronoiDetachCircle(lArc);
- d3_geom_voronoiDetachCircle(rArc);
- var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = {
- x: (cy * hb - by * hc) / d + ax,
- y: (bx * hc - cx * hb) / d + ay
- };
- d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex);
- newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex);
- rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex);
- d3_geom_voronoiAttachCircle(lArc);
- d3_geom_voronoiAttachCircle(rArc);
- }
- function d3_geom_voronoiLeftBreakPoint(arc, directrix) {
- var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix;
- if (!pby2) return rfocx;
- var lArc = arc.P;
- if (!lArc) return -Infinity;
- site = lArc.site;
- var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix;
- if (!plby2) return lfocx;
- var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2;
- if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
- return (rfocx + lfocx) / 2;
- }
- function d3_geom_voronoiRightBreakPoint(arc, directrix) {
- var rArc = arc.N;
- if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix);
- var site = arc.site;
- return site.y === directrix ? site.x : Infinity;
- }
- function d3_geom_voronoiCell(site) {
- this.site = site;
- this.edges = [];
- }
- d3_geom_voronoiCell.prototype.prepare = function() {
- var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge;
- while (iHalfEdge--) {
- edge = halfEdges[iHalfEdge].edge;
- if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1);
- }
- halfEdges.sort(d3_geom_voronoiHalfEdgeOrder);
- return halfEdges.length;
- };
- function d3_geom_voronoiCloseCells(extent) {
- var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end;
- while (iCell--) {
- cell = cells[iCell];
- if (!cell || !cell.prepare()) continue;
- halfEdges = cell.edges;
- nHalfEdges = halfEdges.length;
- iHalfEdge = 0;
- while (iHalfEdge < nHalfEdges) {
- end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y;
- start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y;
- if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) {
- halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? {
- x: x0,
- y: abs(x2 - x0) < ε ? y2 : y1
- } : abs(y3 - y1) < ε && x1 - x3 > ε ? {
- x: abs(y2 - y1) < ε ? x2 : x1,
- y: y1
- } : abs(x3 - x1) < ε && y3 - y0 > ε ? {
- x: x1,
- y: abs(x2 - x1) < ε ? y2 : y0
- } : abs(y3 - y0) < ε && x3 - x0 > ε ? {
- x: abs(y2 - y0) < ε ? x2 : x0,
- y: y0
- } : null), cell.site, null));
- ++nHalfEdges;
- }
- }
- }
- }
- function d3_geom_voronoiHalfEdgeOrder(a, b) {
- return b.angle - a.angle;
- }
- function d3_geom_voronoiCircle() {
- d3_geom_voronoiRedBlackNode(this);
- this.x = this.y = this.arc = this.site = this.cy = null;
- }
- function d3_geom_voronoiAttachCircle(arc) {
- var lArc = arc.P, rArc = arc.N;
- if (!lArc || !rArc) return;
- var lSite = lArc.site, cSite = arc.site, rSite = rArc.site;
- if (lSite === rSite) return;
- var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by;
- var d = 2 * (ax * cy - ay * cx);
- if (d >= -ε2) return;
- var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by;
- var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle();
- circle.arc = arc;
- circle.site = cSite;
- circle.x = x + bx;
- circle.y = cy + Math.sqrt(x * x + y * y);
- circle.cy = cy;
- arc.circle = circle;
- var before = null, node = d3_geom_voronoiCircles._;
- while (node) {
- if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) {
- if (node.L) node = node.L; else {
- before = node.P;
- break;
- }
- } else {
- if (node.R) node = node.R; else {
- before = node;
- break;
- }
- }
- }
- d3_geom_voronoiCircles.insert(before, circle);
- if (!before) d3_geom_voronoiFirstCircle = circle;
- }
- function d3_geom_voronoiDetachCircle(arc) {
- var circle = arc.circle;
- if (circle) {
- if (!circle.P) d3_geom_voronoiFirstCircle = circle.N;
- d3_geom_voronoiCircles.remove(circle);
- d3_geom_voronoiCirclePool.push(circle);
- d3_geom_voronoiRedBlackNode(circle);
- arc.circle = null;
- }
- }
- function d3_geom_voronoiClipEdges(extent) {
- var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e;
- while (i--) {
- e = edges[i];
- if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) {
- e.a = e.b = null;
- edges.splice(i, 1);
- }
- }
- }
- function d3_geom_voronoiConnectEdge(edge, extent) {
- var vb = edge.b;
- if (vb) return true;
- var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb;
- if (ry === ly) {
- if (fx < x0 || fx >= x1) return;
- if (lx > rx) {
- if (!va) va = {
- x: fx,
- y: y0
- }; else if (va.y >= y1) return;
- vb = {
- x: fx,
- y: y1
- };
- } else {
- if (!va) va = {
- x: fx,
- y: y1
- }; else if (va.y < y0) return;
- vb = {
- x: fx,
- y: y0
- };
- }
- } else {
- fm = (lx - rx) / (ry - ly);
- fb = fy - fm * fx;
- if (fm < -1 || fm > 1) {
- if (lx > rx) {
- if (!va) va = {
- x: (y0 - fb) / fm,
- y: y0
- }; else if (va.y >= y1) return;
- vb = {
- x: (y1 - fb) / fm,
- y: y1
- };
- } else {
- if (!va) va = {
- x: (y1 - fb) / fm,
- y: y1
- }; else if (va.y < y0) return;
- vb = {
- x: (y0 - fb) / fm,
- y: y0
- };
- }
- } else {
- if (ly < ry) {
- if (!va) va = {
- x: x0,
- y: fm * x0 + fb
- }; else if (va.x >= x1) return;
- vb = {
- x: x1,
- y: fm * x1 + fb
- };
- } else {
- if (!va) va = {
- x: x1,
- y: fm * x1 + fb
- }; else if (va.x < x0) return;
- vb = {
- x: x0,
- y: fm * x0 + fb
- };
- }
- }
- }
- edge.a = va;
- edge.b = vb;
- return true;
- }
- function d3_geom_voronoiEdge(lSite, rSite) {
- this.l = lSite;
- this.r = rSite;
- this.a = this.b = null;
- }
- function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) {
- var edge = new d3_geom_voronoiEdge(lSite, rSite);
- d3_geom_voronoiEdges.push(edge);
- if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va);
- if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb);
- d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite));
- d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite));
- return edge;
- }
- function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) {
- var edge = new d3_geom_voronoiEdge(lSite, null);
- edge.a = va;
- edge.b = vb;
- d3_geom_voronoiEdges.push(edge);
- return edge;
- }
- function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) {
- if (!edge.a && !edge.b) {
- edge.a = vertex;
- edge.l = lSite;
- edge.r = rSite;
- } else if (edge.l === rSite) {
- edge.b = vertex;
- } else {
- edge.a = vertex;
- }
- }
- function d3_geom_voronoiHalfEdge(edge, lSite, rSite) {
- var va = edge.a, vb = edge.b;
- this.edge = edge;
- this.site = lSite;
- this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y);
- }
- d3_geom_voronoiHalfEdge.prototype = {
- start: function() {
- return this.edge.l === this.site ? this.edge.a : this.edge.b;
- },
- end: function() {
- return this.edge.l === this.site ? this.edge.b : this.edge.a;
- }
- };
- function d3_geom_voronoiRedBlackTree() {
- this._ = null;
- }
- function d3_geom_voronoiRedBlackNode(node) {
- node.U = node.C = node.L = node.R = node.P = node.N = null;
- }
- d3_geom_voronoiRedBlackTree.prototype = {
- insert: function(after, node) {
- var parent, grandpa, uncle;
- if (after) {
- node.P = after;
- node.N = after.N;
- if (after.N) after.N.P = node;
- after.N = node;
- if (after.R) {
- after = after.R;
- while (after.L) after = after.L;
- after.L = node;
- } else {
- after.R = node;
- }
- parent = after;
- } else if (this._) {
- after = d3_geom_voronoiRedBlackFirst(this._);
- node.P = null;
- node.N = after;
- after.P = after.L = node;
- parent = after;
- } else {
- node.P = node.N = null;
- this._ = node;
- parent = null;
- }
- node.L = node.R = null;
- node.U = parent;
- node.C = true;
- after = node;
- while (parent && parent.C) {
- grandpa = parent.U;
- if (parent === grandpa.L) {
- uncle = grandpa.R;
- if (uncle && uncle.C) {
- parent.C = uncle.C = false;
- grandpa.C = true;
- after = grandpa;
- } else {
- if (after === parent.R) {
- d3_geom_voronoiRedBlackRotateLeft(this, parent);
- after = parent;
- parent = after.U;
- }
- parent.C = false;
- grandpa.C = true;
- d3_geom_voronoiRedBlackRotateRight(this, grandpa);
- }
- } else {
- uncle = grandpa.L;
- if (uncle && uncle.C) {
- parent.C = uncle.C = false;
- grandpa.C = true;
- after = grandpa;
- } else {
- if (after === parent.L) {
- d3_geom_voronoiRedBlackRotateRight(this, parent);
- after = parent;
- parent = after.U;
- }
- parent.C = false;
- grandpa.C = true;
- d3_geom_voronoiRedBlackRotateLeft(this, grandpa);
- }
- }
- parent = after.U;
- }
- this._.C = false;
- },
- remove: function(node) {
- if (node.N) node.N.P = node.P;
- if (node.P) node.P.N = node.N;
- node.N = node.P = null;
- var parent = node.U, sibling, left = node.L, right = node.R, next, red;
- if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right);
- if (parent) {
- if (parent.L === node) parent.L = next; else parent.R = next;
- } else {
- this._ = next;
- }
- if (left && right) {
- red = next.C;
- next.C = node.C;
- next.L = left;
- left.U = next;
- if (next !== right) {
- parent = next.U;
- next.U = node.U;
- node = next.R;
- parent.L = node;
- next.R = right;
- right.U = next;
- } else {
- next.U = parent;
- parent = next;
- node = next.R;
- }
- } else {
- red = node.C;
- node = next;
- }
- if (node) node.U = parent;
- if (red) return;
- if (node && node.C) {
- node.C = false;
- return;
- }
- do {
- if (node === this._) break;
- if (node === parent.L) {
- sibling = parent.R;
- if (sibling.C) {
- sibling.C = false;
- parent.C = true;
- d3_geom_voronoiRedBlackRotateLeft(this, parent);
- sibling = parent.R;
- }
- if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
- if (!sibling.R || !sibling.R.C) {
- sibling.L.C = false;
- sibling.C = true;
- d3_geom_voronoiRedBlackRotateRight(this, sibling);
- sibling = parent.R;
- }
- sibling.C = parent.C;
- parent.C = sibling.R.C = false;
- d3_geom_voronoiRedBlackRotateLeft(this, parent);
- node = this._;
- break;
- }
- } else {
- sibling = parent.L;
- if (sibling.C) {
- sibling.C = false;
- parent.C = true;
- d3_geom_voronoiRedBlackRotateRight(this, parent);
- sibling = parent.L;
- }
- if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
- if (!sibling.L || !sibling.L.C) {
- sibling.R.C = false;
- sibling.C = true;
- d3_geom_voronoiRedBlackRotateLeft(this, sibling);
- sibling = parent.L;
- }
- sibling.C = parent.C;
- parent.C = sibling.L.C = false;
- d3_geom_voronoiRedBlackRotateRight(this, parent);
- node = this._;
- break;
- }
- }
- sibling.C = true;
- node = parent;
- parent = parent.U;
- } while (!node.C);
- if (node) node.C = false;
- }
- };
- function d3_geom_voronoiRedBlackRotateLeft(tree, node) {
- var p = node, q = node.R, parent = p.U;
- if (parent) {
- if (parent.L === p) parent.L = q; else parent.R = q;
- } else {
- tree._ = q;
- }
- q.U = parent;
- p.U = q;
- p.R = q.L;
- if (p.R) p.R.U = p;
- q.L = p;
- }
- function d3_geom_voronoiRedBlackRotateRight(tree, node) {
- var p = node, q = node.L, parent = p.U;
- if (parent) {
- if (parent.L === p) parent.L = q; else parent.R = q;
- } else {
- tree._ = q;
- }
- q.U = parent;
- p.U = q;
- p.L = q.R;
- if (p.L) p.L.U = p;
- q.R = p;
- }
- function d3_geom_voronoiRedBlackFirst(node) {
- while (node.L) node = node.L;
- return node;
- }
- function d3_geom_voronoi(sites, bbox) {
- var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle;
- d3_geom_voronoiEdges = [];
- d3_geom_voronoiCells = new Array(sites.length);
- d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree();
- d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree();
- while (true) {
- circle = d3_geom_voronoiFirstCircle;
- if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) {
- if (site.x !== x0 || site.y !== y0) {
- d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site);
- d3_geom_voronoiAddBeach(site);
- x0 = site.x, y0 = site.y;
- }
- site = sites.pop();
- } else if (circle) {
- d3_geom_voronoiRemoveBeach(circle.arc);
- } else {
- break;
- }
- }
- if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox);
- var diagram = {
- cells: d3_geom_voronoiCells,
- edges: d3_geom_voronoiEdges
- };
- d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null;
- return diagram;
- }
- function d3_geom_voronoiVertexOrder(a, b) {
- return b.y - a.y || b.x - a.x;
- }
- d3.geom.voronoi = function(points) {
- var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent;
- if (points) return voronoi(points);
- function voronoi(data) {
- var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1];
- d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) {
- var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) {
- var s = e.start();
- return [ s.x, s.y ];
- }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : [];
- polygon.point = data[i];
- });
- return polygons;
- }
- function sites(data) {
- return data.map(function(d, i) {
- return {
- x: Math.round(fx(d, i) / ε) * ε,
- y: Math.round(fy(d, i) / ε) * ε,
- i: i
- };
- });
- }
- voronoi.links = function(data) {
- return d3_geom_voronoi(sites(data)).edges.filter(function(edge) {
- return edge.l && edge.r;
- }).map(function(edge) {
- return {
- source: data[edge.l.i],
- target: data[edge.r.i]
- };
- });
- };
- voronoi.triangles = function(data) {
- var triangles = [];
- d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) {
- var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l;
- while (++j < m) {
- e0 = e1;
- s0 = s1;
- e1 = edges[j].edge;
- s1 = e1.l === site ? e1.r : e1.l;
- if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) {
- triangles.push([ data[i], data[s0.i], data[s1.i] ]);
- }
- }
- });
- return triangles;
- };
- voronoi.x = function(_) {
- return arguments.length ? (fx = d3_functor(x = _), voronoi) : x;
- };
- voronoi.y = function(_) {
- return arguments.length ? (fy = d3_functor(y = _), voronoi) : y;
- };
- voronoi.clipExtent = function(_) {
- if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent;
- clipExtent = _ == null ? d3_geom_voronoiClipExtent : _;
- return voronoi;
- };
- voronoi.size = function(_) {
- if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1];
- return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]);
- };
- return voronoi;
- };
- var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ];
- function d3_geom_voronoiTriangleArea(a, b, c) {
- return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y);
- }
- d3.geom.delaunay = function(vertices) {
- return d3.geom.voronoi().triangles(vertices);
- };
- d3.geom.quadtree = function(points, x1, y1, x2, y2) {
- var x = d3_geom_pointX, y = d3_geom_pointY, compat;
- if (compat = arguments.length) {
- x = d3_geom_quadtreeCompatX;
- y = d3_geom_quadtreeCompatY;
- if (compat === 3) {
- y2 = y1;
- x2 = x1;
- y1 = x1 = 0;
- }
- return quadtree(points);
- }
- function quadtree(data) {
- var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_;
- if (x1 != null) {
- x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2;
- } else {
- x2_ = y2_ = -(x1_ = y1_ = Infinity);
- xs = [], ys = [];
- n = data.length;
- if (compat) for (i = 0; i < n; ++i) {
- d = data[i];
- if (d.x < x1_) x1_ = d.x;
- if (d.y < y1_) y1_ = d.y;
- if (d.x > x2_) x2_ = d.x;
- if (d.y > y2_) y2_ = d.y;
- xs.push(d.x);
- ys.push(d.y);
- } else for (i = 0; i < n; ++i) {
- var x_ = +fx(d = data[i], i), y_ = +fy(d, i);
- if (x_ < x1_) x1_ = x_;
- if (y_ < y1_) y1_ = y_;
- if (x_ > x2_) x2_ = x_;
- if (y_ > y2_) y2_ = y_;
- xs.push(x_);
- ys.push(y_);
- }
- }
- var dx = x2_ - x1_, dy = y2_ - y1_;
- if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy;
- function insert(n, d, x, y, x1, y1, x2, y2) {
- if (isNaN(x) || isNaN(y)) return;
- if (n.leaf) {
- var nx = n.x, ny = n.y;
- if (nx != null) {
- if (abs(nx - x) + abs(ny - y) < .01) {
- insertChild(n, d, x, y, x1, y1, x2, y2);
- } else {
- var nPoint = n.point;
- n.x = n.y = n.point = null;
- insertChild(n, nPoint, nx, ny, x1, y1, x2, y2);
- insertChild(n, d, x, y, x1, y1, x2, y2);
- }
- } else {
- n.x = x, n.y = y, n.point = d;
- }
- } else {
- insertChild(n, d, x, y, x1, y1, x2, y2);
- }
- }
- function insertChild(n, d, x, y, x1, y1, x2, y2) {
- var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right;
- n.leaf = false;
- n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
- if (right) x1 = xm; else x2 = xm;
- if (below) y1 = ym; else y2 = ym;
- insert(n, d, x, y, x1, y1, x2, y2);
- }
- var root = d3_geom_quadtreeNode();
- root.add = function(d) {
- insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_);
- };
- root.visit = function(f) {
- d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_);
- };
- root.find = function(point) {
- return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_);
- };
- i = -1;
- if (x1 == null) {
- while (++i < n) {
- insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_);
- }
- --i;
- } else data.forEach(root.add);
- xs = ys = data = d = null;
- return root;
- }
- quadtree.x = function(_) {
- return arguments.length ? (x = _, quadtree) : x;
- };
- quadtree.y = function(_) {
- return arguments.length ? (y = _, quadtree) : y;
- };
- quadtree.extent = function(_) {
- if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ];
- if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0],
- y2 = +_[1][1];
- return quadtree;
- };
- quadtree.size = function(_) {
- if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ];
- if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
- return quadtree;
- };
- return quadtree;
- };
- function d3_geom_quadtreeCompatX(d) {
- return d.x;
- }
- function d3_geom_quadtreeCompatY(d) {
- return d.y;
- }
- function d3_geom_quadtreeNode() {
- return {
- leaf: true,
- nodes: [],
- point: null,
- x: null,
- y: null
- };
- }
- function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
- if (!f(node, x1, y1, x2, y2)) {
- var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes;
- if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
- if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
- if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
- if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
- }
- }
- function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) {
- var minDistance2 = Infinity, closestPoint;
- (function find(node, x1, y1, x2, y2) {
- if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return;
- if (point = node.point) {
- var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy;
- if (distance2 < minDistance2) {
- var distance = Math.sqrt(minDistance2 = distance2);
- x0 = x - distance, y0 = y - distance;
- x3 = x + distance, y3 = y + distance;
- closestPoint = point;
- }
- }
- var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym;
- for (var i = below << 1 | right, j = i + 4; i < j; ++i) {
- if (node = children[i & 3]) switch (i & 3) {
- case 0:
- find(node, x1, y1, xm, ym);
- break;
-
- case 1:
- find(node, xm, y1, x2, ym);
- break;
-
- case 2:
- find(node, x1, ym, xm, y2);
- break;
-
- case 3:
- find(node, xm, ym, x2, y2);
- break;
- }
- }
- })(root, x0, y0, x3, y3);
- return closestPoint;
- }
- d3.interpolateRgb = d3_interpolateRgb;
- function d3_interpolateRgb(a, b) {
- a = d3.rgb(a);
- b = d3.rgb(b);
- var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
- return function(t) {
- return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t));
- };
- }
- d3.interpolateObject = d3_interpolateObject;
- function d3_interpolateObject(a, b) {
- var i = {}, c = {}, k;
- for (k in a) {
- if (k in b) {
- i[k] = d3_interpolate(a[k], b[k]);
- } else {
- c[k] = a[k];
- }
- }
- for (k in b) {
- if (!(k in a)) {
- c[k] = b[k];
- }
- }
- return function(t) {
- for (k in i) c[k] = i[k](t);
- return c;
- };
- }
- d3.interpolateNumber = d3_interpolateNumber;
- function d3_interpolateNumber(a, b) {
- a = +a, b = +b;
- return function(t) {
- return a * (1 - t) + b * t;
- };
- }
- d3.interpolateString = d3_interpolateString;
- function d3_interpolateString(a, b) {
- var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = [];
- a = a + "", b = b + "";
- while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) {
- if ((bs = bm.index) > bi) {
- bs = b.slice(bi, bs);
- if (s[i]) s[i] += bs; else s[++i] = bs;
- }
- if ((am = am[0]) === (bm = bm[0])) {
- if (s[i]) s[i] += bm; else s[++i] = bm;
- } else {
- s[++i] = null;
- q.push({
- i: i,
- x: d3_interpolateNumber(am, bm)
- });
- }
- bi = d3_interpolate_numberB.lastIndex;
- }
- if (bi < b.length) {
- bs = b.slice(bi);
- if (s[i]) s[i] += bs; else s[++i] = bs;
- }
- return s.length < 2 ? q[0] ? (b = q[0].x, function(t) {
- return b(t) + "";
- }) : function() {
- return b;
- } : (b = q.length, function(t) {
- for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
- return s.join("");
- });
- }
- var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
- d3.interpolate = d3_interpolate;
- function d3_interpolate(a, b) {
- var i = d3.interpolators.length, f;
- while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
- return f;
- }
- d3.interpolators = [ function(a, b) {
- var t = typeof b;
- return (t === "string" ? d3_rgb_names.has(b.toLowerCase()) || /^(#|rgb\(|hsl\()/i.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b);
- } ];
- d3.interpolateArray = d3_interpolateArray;
- function d3_interpolateArray(a, b) {
- var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i;
- for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
- for (;i < na; ++i) c[i] = a[i];
- for (;i < nb; ++i) c[i] = b[i];
- return function(t) {
- for (i = 0; i < n0; ++i) c[i] = x[i](t);
- return c;
- };
- }
- var d3_ease_default = function() {
- return d3_identity;
- };
- var d3_ease = d3.map({
- linear: d3_ease_default,
- poly: d3_ease_poly,
- quad: function() {
- return d3_ease_quad;
- },
- cubic: function() {
- return d3_ease_cubic;
- },
- sin: function() {
- return d3_ease_sin;
- },
- exp: function() {
- return d3_ease_exp;
- },
- circle: function() {
- return d3_ease_circle;
- },
- elastic: d3_ease_elastic,
- back: d3_ease_back,
- bounce: function() {
- return d3_ease_bounce;
- }
- });
- var d3_ease_mode = d3.map({
- "in": d3_identity,
- out: d3_ease_reverse,
- "in-out": d3_ease_reflect,
- "out-in": function(f) {
- return d3_ease_reflect(d3_ease_reverse(f));
- }
- });
- d3.ease = function(name) {
- var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in";
- t = d3_ease.get(t) || d3_ease_default;
- m = d3_ease_mode.get(m) || d3_identity;
- return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
- };
- function d3_ease_clamp(f) {
- return function(t) {
- return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
- };
- }
- function d3_ease_reverse(f) {
- return function(t) {
- return 1 - f(1 - t);
- };
- }
- function d3_ease_reflect(f) {
- return function(t) {
- return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t));
- };
- }
- function d3_ease_quad(t) {
- return t * t;
- }
- function d3_ease_cubic(t) {
- return t * t * t;
- }
- function d3_ease_cubicInOut(t) {
- if (t <= 0) return 0;
- if (t >= 1) return 1;
- var t2 = t * t, t3 = t2 * t;
- return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
- }
- function d3_ease_poly(e) {
- return function(t) {
- return Math.pow(t, e);
- };
- }
- function d3_ease_sin(t) {
- return 1 - Math.cos(t * halfπ);
- }
- function d3_ease_exp(t) {
- return Math.pow(2, 10 * (t - 1));
- }
- function d3_ease_circle(t) {
- return 1 - Math.sqrt(1 - t * t);
- }
- function d3_ease_elastic(a, p) {
- var s;
- if (arguments.length < 2) p = .45;
- if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4;
- return function(t) {
- return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
- };
- }
- function d3_ease_back(s) {
- if (!s) s = 1.70158;
- return function(t) {
- return t * t * ((s + 1) * t - s);
- };
- }
- function d3_ease_bounce(t) {
- return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
- }
- d3.interpolateHcl = d3_interpolateHcl;
- function d3_interpolateHcl(a, b) {
- a = d3.hcl(a);
- b = d3.hcl(b);
- var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al;
- if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac;
- if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
- return function(t) {
- return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + "";
- };
- }
- d3.interpolateHsl = d3_interpolateHsl;
- function d3_interpolateHsl(a, b) {
- a = d3.hsl(a);
- b = d3.hsl(b);
- var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
- if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
- if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
- return function(t) {
- return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + "";
- };
- }
- d3.interpolateLab = d3_interpolateLab;
- function d3_interpolateLab(a, b) {
- a = d3.lab(a);
- b = d3.lab(b);
- var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab;
- return function(t) {
- return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
- };
- }
- d3.interpolateRound = d3_interpolateRound;
- function d3_interpolateRound(a, b) {
- b -= a;
- return function(t) {
- return Math.round(a + b * t);
- };
- }
- d3.transform = function(string) {
- var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
- return (d3.transform = function(string) {
- if (string != null) {
- g.setAttribute("transform", string);
- var t = g.transform.baseVal.consolidate();
- }
- return new d3_transform(t ? t.matrix : d3_transformIdentity);
- })(string);
- };
- function d3_transform(m) {
- var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
- if (r0[0] * r1[1] < r1[0] * r0[1]) {
- r0[0] *= -1;
- r0[1] *= -1;
- kx *= -1;
- kz *= -1;
- }
- this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
- this.translate = [ m.e, m.f ];
- this.scale = [ kx, ky ];
- this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
- }
- d3_transform.prototype.toString = function() {
- return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
- };
- function d3_transformDot(a, b) {
- return a[0] * b[0] + a[1] * b[1];
- }
- function d3_transformNormalize(a) {
- var k = Math.sqrt(d3_transformDot(a, a));
- if (k) {
- a[0] /= k;
- a[1] /= k;
- }
- return k;
- }
- function d3_transformCombine(a, b, k) {
- a[0] += k * b[0];
- a[1] += k * b[1];
- return a;
- }
- var d3_transformIdentity = {
- a: 1,
- b: 0,
- c: 0,
- d: 1,
- e: 0,
- f: 0
- };
- d3.interpolateTransform = d3_interpolateTransform;
- function d3_interpolateTransformPop(s) {
- return s.length ? s.pop() + "," : "";
- }
- function d3_interpolateTranslate(ta, tb, s, q) {
- if (ta[0] !== tb[0] || ta[1] !== tb[1]) {
- var i = s.push("translate(", null, ",", null, ")");
- q.push({
- i: i - 4,
- x: d3_interpolateNumber(ta[0], tb[0])
- }, {
- i: i - 2,
- x: d3_interpolateNumber(ta[1], tb[1])
- });
- } else if (tb[0] || tb[1]) {
- s.push("translate(" + tb + ")");
- }
- }
- function d3_interpolateRotate(ra, rb, s, q) {
- if (ra !== rb) {
- if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
- q.push({
- i: s.push(d3_interpolateTransformPop(s) + "rotate(", null, ")") - 2,
- x: d3_interpolateNumber(ra, rb)
- });
- } else if (rb) {
- s.push(d3_interpolateTransformPop(s) + "rotate(" + rb + ")");
- }
- }
- function d3_interpolateSkew(wa, wb, s, q) {
- if (wa !== wb) {
- q.push({
- i: s.push(d3_interpolateTransformPop(s) + "skewX(", null, ")") - 2,
- x: d3_interpolateNumber(wa, wb)
- });
- } else if (wb) {
- s.push(d3_interpolateTransformPop(s) + "skewX(" + wb + ")");
- }
- }
- function d3_interpolateScale(ka, kb, s, q) {
- if (ka[0] !== kb[0] || ka[1] !== kb[1]) {
- var i = s.push(d3_interpolateTransformPop(s) + "scale(", null, ",", null, ")");
- q.push({
- i: i - 4,
- x: d3_interpolateNumber(ka[0], kb[0])
- }, {
- i: i - 2,
- x: d3_interpolateNumber(ka[1], kb[1])
- });
- } else if (kb[0] !== 1 || kb[1] !== 1) {
- s.push(d3_interpolateTransformPop(s) + "scale(" + kb + ")");
- }
- }
- function d3_interpolateTransform(a, b) {
- var s = [], q = [];
- a = d3.transform(a), b = d3.transform(b);
- d3_interpolateTranslate(a.translate, b.translate, s, q);
- d3_interpolateRotate(a.rotate, b.rotate, s, q);
- d3_interpolateSkew(a.skew, b.skew, s, q);
- d3_interpolateScale(a.scale, b.scale, s, q);
- a = b = null;
- return function(t) {
- var i = -1, n = q.length, o;
- while (++i < n) s[(o = q[i]).i] = o.x(t);
- return s.join("");
- };
- }
- function d3_uninterpolateNumber(a, b) {
- b = (b -= a = +a) || 1 / b;
- return function(x) {
- return (x - a) / b;
- };
- }
- function d3_uninterpolateClamp(a, b) {
- b = (b -= a = +a) || 1 / b;
- return function(x) {
- return Math.max(0, Math.min(1, (x - a) / b));
- };
- }
- d3.layout = {};
- d3.layout.bundle = function() {
- return function(links) {
- var paths = [], i = -1, n = links.length;
- while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
- return paths;
- };
- };
- function d3_layout_bundlePath(link) {
- var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ];
- while (start !== lca) {
- start = start.parent;
- points.push(start);
- }
- var k = points.length;
- while (end !== lca) {
- points.splice(k, 0, end);
- end = end.parent;
- }
- return points;
- }
- function d3_layout_bundleAncestors(node) {
- var ancestors = [], parent = node.parent;
- while (parent != null) {
- ancestors.push(node);
- node = parent;
- parent = parent.parent;
- }
- ancestors.push(node);
- return ancestors;
- }
- function d3_layout_bundleLeastCommonAncestor(a, b) {
- if (a === b) return a;
- var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null;
- while (aNode === bNode) {
- sharedNode = aNode;
- aNode = aNodes.pop();
- bNode = bNodes.pop();
- }
- return sharedNode;
- }
- d3.layout.chord = function() {
- var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords;
- function relayout() {
- var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j;
- chords = [];
- groups = [];
- k = 0, i = -1;
- while (++i < n) {
- x = 0, j = -1;
- while (++j < n) {
- x += matrix[i][j];
- }
- groupSums.push(x);
- subgroupIndex.push(d3.range(n));
- k += x;
- }
- if (sortGroups) {
- groupIndex.sort(function(a, b) {
- return sortGroups(groupSums[a], groupSums[b]);
- });
- }
- if (sortSubgroups) {
- subgroupIndex.forEach(function(d, i) {
- d.sort(function(a, b) {
- return sortSubgroups(matrix[i][a], matrix[i][b]);
- });
- });
- }
- k = (τ - padding * n) / k;
- x = 0, i = -1;
- while (++i < n) {
- x0 = x, j = -1;
- while (++j < n) {
- var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k;
- subgroups[di + "-" + dj] = {
- index: di,
- subindex: dj,
- startAngle: a0,
- endAngle: a1,
- value: v
- };
- }
- groups[di] = {
- index: di,
- startAngle: x0,
- endAngle: x,
- value: groupSums[di]
- };
- x += padding;
- }
- i = -1;
- while (++i < n) {
- j = i - 1;
- while (++j < n) {
- var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
- if (source.value || target.value) {
- chords.push(source.value < target.value ? {
- source: target,
- target: source
- } : {
- source: source,
- target: target
- });
- }
- }
- }
- if (sortChords) resort();
- }
- function resort() {
- chords.sort(function(a, b) {
- return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
- });
- }
- chord.matrix = function(x) {
- if (!arguments.length) return matrix;
- n = (matrix = x) && matrix.length;
- chords = groups = null;
- return chord;
- };
- chord.padding = function(x) {
- if (!arguments.length) return padding;
- padding = x;
- chords = groups = null;
- return chord;
- };
- chord.sortGroups = function(x) {
- if (!arguments.length) return sortGroups;
- sortGroups = x;
- chords = groups = null;
- return chord;
- };
- chord.sortSubgroups = function(x) {
- if (!arguments.length) return sortSubgroups;
- sortSubgroups = x;
- chords = null;
- return chord;
- };
- chord.sortChords = function(x) {
- if (!arguments.length) return sortChords;
- sortChords = x;
- if (chords) resort();
- return chord;
- };
- chord.chords = function() {
- if (!chords) relayout();
- return chords;
- };
- chord.groups = function() {
- if (!groups) relayout();
- return groups;
- };
- return chord;
- };
- d3.layout.force = function() {
- var force = {}, event = d3.dispatch("start", "tick", "end"), timer, size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges;
- function repulse(node) {
- return function(quad, x1, _, x2) {
- if (quad.point !== node) {
- var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy;
- if (dw * dw / theta2 < dn) {
- if (dn < chargeDistance2) {
- var k = quad.charge / dn;
- node.px -= dx * k;
- node.py -= dy * k;
- }
- return true;
- }
- if (quad.point && dn && dn < chargeDistance2) {
- var k = quad.pointCharge / dn;
- node.px -= dx * k;
- node.py -= dy * k;
- }
- }
- return !quad.charge;
- };
- }
- force.tick = function() {
- if ((alpha *= .99) < .005) {
- timer = null;
- event.end({
- type: "end",
- alpha: alpha = 0
- });
- return true;
- }
- var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y;
- for (i = 0; i < m; ++i) {
- o = links[i];
- s = o.source;
- t = o.target;
- x = t.x - s.x;
- y = t.y - s.y;
- if (l = x * x + y * y) {
- l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
- x *= l;
- y *= l;
- t.x -= x * (k = s.weight + t.weight ? s.weight / (s.weight + t.weight) : .5);
- t.y -= y * k;
- s.x += x * (k = 1 - k);
- s.y += y * k;
- }
- }
- if (k = alpha * gravity) {
- x = size[0] / 2;
- y = size[1] / 2;
- i = -1;
- if (k) while (++i < n) {
- o = nodes[i];
- o.x += (x - o.x) * k;
- o.y += (y - o.y) * k;
- }
- }
- if (charge) {
- d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
- i = -1;
- while (++i < n) {
- if (!(o = nodes[i]).fixed) {
- q.visit(repulse(o));
- }
- }
- }
- i = -1;
- while (++i < n) {
- o = nodes[i];
- if (o.fixed) {
- o.x = o.px;
- o.y = o.py;
- } else {
- o.x -= (o.px - (o.px = o.x)) * friction;
- o.y -= (o.py - (o.py = o.y)) * friction;
- }
- }
- event.tick({
- type: "tick",
- alpha: alpha
- });
- };
- force.nodes = function(x) {
- if (!arguments.length) return nodes;
- nodes = x;
- return force;
- };
- force.links = function(x) {
- if (!arguments.length) return links;
- links = x;
- return force;
- };
- force.size = function(x) {
- if (!arguments.length) return size;
- size = x;
- return force;
- };
- force.linkDistance = function(x) {
- if (!arguments.length) return linkDistance;
- linkDistance = typeof x === "function" ? x : +x;
- return force;
- };
- force.distance = force.linkDistance;
- force.linkStrength = function(x) {
- if (!arguments.length) return linkStrength;
- linkStrength = typeof x === "function" ? x : +x;
- return force;
- };
- force.friction = function(x) {
- if (!arguments.length) return friction;
- friction = +x;
- return force;
- };
- force.charge = function(x) {
- if (!arguments.length) return charge;
- charge = typeof x === "function" ? x : +x;
- return force;
- };
- force.chargeDistance = function(x) {
- if (!arguments.length) return Math.sqrt(chargeDistance2);
- chargeDistance2 = x * x;
- return force;
- };
- force.gravity = function(x) {
- if (!arguments.length) return gravity;
- gravity = +x;
- return force;
- };
- force.theta = function(x) {
- if (!arguments.length) return Math.sqrt(theta2);
- theta2 = x * x;
- return force;
- };
- force.alpha = function(x) {
- if (!arguments.length) return alpha;
- x = +x;
- if (alpha) {
- if (x > 0) {
- alpha = x;
- } else {
- timer.c = null, timer.t = NaN, timer = null;
- event.end({
- type: "end",
- alpha: alpha = 0
- });
- }
- } else if (x > 0) {
- event.start({
- type: "start",
- alpha: alpha = x
- });
- timer = d3_timer(force.tick);
- }
- return force;
- };
- force.start = function() {
- var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o;
- for (i = 0; i < n; ++i) {
- (o = nodes[i]).index = i;
- o.weight = 0;
- }
- for (i = 0; i < m; ++i) {
- o = links[i];
- if (typeof o.source == "number") o.source = nodes[o.source];
- if (typeof o.target == "number") o.target = nodes[o.target];
- ++o.source.weight;
- ++o.target.weight;
- }
- for (i = 0; i < n; ++i) {
- o = nodes[i];
- if (isNaN(o.x)) o.x = position("x", w);
- if (isNaN(o.y)) o.y = position("y", h);
- if (isNaN(o.px)) o.px = o.x;
- if (isNaN(o.py)) o.py = o.y;
- }
- distances = [];
- if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance;
- strengths = [];
- if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength;
- charges = [];
- if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge;
- function position(dimension, size) {
- if (!neighbors) {
- neighbors = new Array(n);
- for (j = 0; j < n; ++j) {
- neighbors[j] = [];
- }
- for (j = 0; j < m; ++j) {
- var o = links[j];
- neighbors[o.source.index].push(o.target);
- neighbors[o.target.index].push(o.source);
- }
- }
- var candidates = neighbors[i], j = -1, l = candidates.length, x;
- while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x;
- return Math.random() * size;
- }
- return force.resume();
- };
- force.resume = function() {
- return force.alpha(.1);
- };
- force.stop = function() {
- return force.alpha(0);
- };
- force.drag = function() {
- if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend);
- if (!arguments.length) return drag;
- this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
- };
- function dragmove(d) {
- d.px = d3.event.x, d.py = d3.event.y;
- force.resume();
- }
- return d3.rebind(force, event, "on");
- };
- function d3_layout_forceDragstart(d) {
- d.fixed |= 2;
- }
- function d3_layout_forceDragend(d) {
- d.fixed &= ~6;
- }
- function d3_layout_forceMouseover(d) {
- d.fixed |= 4;
- d.px = d.x, d.py = d.y;
- }
- function d3_layout_forceMouseout(d) {
- d.fixed &= ~4;
- }
- function d3_layout_forceAccumulate(quad, alpha, charges) {
- var cx = 0, cy = 0;
- quad.charge = 0;
- if (!quad.leaf) {
- var nodes = quad.nodes, n = nodes.length, i = -1, c;
- while (++i < n) {
- c = nodes[i];
- if (c == null) continue;
- d3_layout_forceAccumulate(c, alpha, charges);
- quad.charge += c.charge;
- cx += c.charge * c.cx;
- cy += c.charge * c.cy;
- }
- }
- if (quad.point) {
- if (!quad.leaf) {
- quad.point.x += Math.random() - .5;
- quad.point.y += Math.random() - .5;
- }
- var k = alpha * charges[quad.point.index];
- quad.charge += quad.pointCharge = k;
- cx += k * quad.point.x;
- cy += k * quad.point.y;
- }
- quad.cx = cx / quad.charge;
- quad.cy = cy / quad.charge;
- }
- var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity;
- d3.layout.hierarchy = function() {
- var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
- function hierarchy(root) {
- var stack = [ root ], nodes = [], node;
- root.depth = 0;
- while ((node = stack.pop()) != null) {
- nodes.push(node);
- if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) {
- var n, childs, child;
- while (--n >= 0) {
- stack.push(child = childs[n]);
- child.parent = node;
- child.depth = node.depth + 1;
- }
- if (value) node.value = 0;
- node.children = childs;
- } else {
- if (value) node.value = +value.call(hierarchy, node, node.depth) || 0;
- delete node.children;
- }
- }
- d3_layout_hierarchyVisitAfter(root, function(node) {
- var childs, parent;
- if (sort && (childs = node.children)) childs.sort(sort);
- if (value && (parent = node.parent)) parent.value += node.value;
- });
- return nodes;
- }
- hierarchy.sort = function(x) {
- if (!arguments.length) return sort;
- sort = x;
- return hierarchy;
- };
- hierarchy.children = function(x) {
- if (!arguments.length) return children;
- children = x;
- return hierarchy;
- };
- hierarchy.value = function(x) {
- if (!arguments.length) return value;
- value = x;
- return hierarchy;
- };
- hierarchy.revalue = function(root) {
- if (value) {
- d3_layout_hierarchyVisitBefore(root, function(node) {
- if (node.children) node.value = 0;
- });
- d3_layout_hierarchyVisitAfter(root, function(node) {
- var parent;
- if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0;
- if (parent = node.parent) parent.value += node.value;
- });
- }
- return root;
- };
- return hierarchy;
- };
- function d3_layout_hierarchyRebind(object, hierarchy) {
- d3.rebind(object, hierarchy, "sort", "children", "value");
- object.nodes = object;
- object.links = d3_layout_hierarchyLinks;
- return object;
- }
- function d3_layout_hierarchyVisitBefore(node, callback) {
- var nodes = [ node ];
- while ((node = nodes.pop()) != null) {
- callback(node);
- if ((children = node.children) && (n = children.length)) {
- var n, children;
- while (--n >= 0) nodes.push(children[n]);
- }
- }
- }
- function d3_layout_hierarchyVisitAfter(node, callback) {
- var nodes = [ node ], nodes2 = [];
- while ((node = nodes.pop()) != null) {
- nodes2.push(node);
- if ((children = node.children) && (n = children.length)) {
- var i = -1, n, children;
- while (++i < n) nodes.push(children[i]);
- }
- }
- while ((node = nodes2.pop()) != null) {
- callback(node);
- }
- }
- function d3_layout_hierarchyChildren(d) {
- return d.children;
- }
- function d3_layout_hierarchyValue(d) {
- return d.value;
- }
- function d3_layout_hierarchySort(a, b) {
- return b.value - a.value;
- }
- function d3_layout_hierarchyLinks(nodes) {
- return d3.merge(nodes.map(function(parent) {
- return (parent.children || []).map(function(child) {
- return {
- source: parent,
- target: child
- };
- });
- }));
- }
- d3.layout.partition = function() {
- var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ];
- function position(node, x, dx, dy) {
- var children = node.children;
- node.x = x;
- node.y = node.depth * dy;
- node.dx = dx;
- node.dy = dy;
- if (children && (n = children.length)) {
- var i = -1, n, c, d;
- dx = node.value ? dx / node.value : 0;
- while (++i < n) {
- position(c = children[i], x, d = c.value * dx, dy);
- x += d;
- }
- }
- }
- function depth(node) {
- var children = node.children, d = 0;
- if (children && (n = children.length)) {
- var i = -1, n;
- while (++i < n) d = Math.max(d, depth(children[i]));
- }
- return 1 + d;
- }
- function partition(d, i) {
- var nodes = hierarchy.call(this, d, i);
- position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
- return nodes;
- }
- partition.size = function(x) {
- if (!arguments.length) return size;
- size = x;
- return partition;
- };
- return d3_layout_hierarchyRebind(partition, hierarchy);
- };
- d3.layout.pie = function() {
- var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0;
- function pie(data) {
- var n = data.length, values = data.map(function(d, i) {
- return +value.call(pie, d, i);
- }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), sum = d3.sum(values), k = sum ? (da - n * pa) / sum : 0, index = d3.range(n), arcs = [], v;
- if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) {
- return values[j] - values[i];
- } : function(i, j) {
- return sort(data[i], data[j]);
- });
- index.forEach(function(i) {
- arcs[i] = {
- data: data[i],
- value: v = values[i],
- startAngle: a,
- endAngle: a += v * k + pa,
- padAngle: p
- };
- });
- return arcs;
- }
- pie.value = function(_) {
- if (!arguments.length) return value;
- value = _;
- return pie;
- };
- pie.sort = function(_) {
- if (!arguments.length) return sort;
- sort = _;
- return pie;
- };
- pie.startAngle = function(_) {
- if (!arguments.length) return startAngle;
- startAngle = _;
- return pie;
- };
- pie.endAngle = function(_) {
- if (!arguments.length) return endAngle;
- endAngle = _;
- return pie;
- };
- pie.padAngle = function(_) {
- if (!arguments.length) return padAngle;
- padAngle = _;
- return pie;
- };
- return pie;
- };
- var d3_layout_pieSortByValue = {};
- d3.layout.stack = function() {
- var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY;
- function stack(data, index) {
- if (!(n = data.length)) return data;
- var series = data.map(function(d, i) {
- return values.call(stack, d, i);
- });
- var points = series.map(function(d) {
- return d.map(function(v, i) {
- return [ x.call(stack, v, i), y.call(stack, v, i) ];
- });
- });
- var orders = order.call(stack, points, index);
- series = d3.permute(series, orders);
- points = d3.permute(points, orders);
- var offsets = offset.call(stack, points, index);
- var m = series[0].length, n, i, j, o;
- for (j = 0; j < m; ++j) {
- out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
- for (i = 1; i < n; ++i) {
- out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
- }
- }
- return data;
- }
- stack.values = function(x) {
- if (!arguments.length) return values;
- values = x;
- return stack;
- };
- stack.order = function(x) {
- if (!arguments.length) return order;
- order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault;
- return stack;
- };
- stack.offset = function(x) {
- if (!arguments.length) return offset;
- offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero;
- return stack;
- };
- stack.x = function(z) {
- if (!arguments.length) return x;
- x = z;
- return stack;
- };
- stack.y = function(z) {
- if (!arguments.length) return y;
- y = z;
- return stack;
- };
- stack.out = function(z) {
- if (!arguments.length) return out;
- out = z;
- return stack;
- };
- return stack;
- };
- function d3_layout_stackX(d) {
- return d.x;
- }
- function d3_layout_stackY(d) {
- return d.y;
- }
- function d3_layout_stackOut(d, y0, y) {
- d.y0 = y0;
- d.y = y;
- }
- var d3_layout_stackOrders = d3.map({
- "inside-out": function(data) {
- var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) {
- return max[a] - max[b];
- }), top = 0, bottom = 0, tops = [], bottoms = [];
- for (i = 0; i < n; ++i) {
- j = index[i];
- if (top < bottom) {
- top += sums[j];
- tops.push(j);
- } else {
- bottom += sums[j];
- bottoms.push(j);
- }
- }
- return bottoms.reverse().concat(tops);
- },
- reverse: function(data) {
- return d3.range(data.length).reverse();
- },
- "default": d3_layout_stackOrderDefault
- });
- var d3_layout_stackOffsets = d3.map({
- silhouette: function(data) {
- var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = [];
- for (j = 0; j < m; ++j) {
- for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
- if (o > max) max = o;
- sums.push(o);
- }
- for (j = 0; j < m; ++j) {
- y0[j] = (max - sums[j]) / 2;
- }
- return y0;
- },
- wiggle: function(data) {
- var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = [];
- y0[0] = o = o0 = 0;
- for (j = 1; j < m; ++j) {
- for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
- for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
- for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
- s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
- }
- s2 += s3 * data[i][j][1];
- }
- y0[j] = o -= s1 ? s2 / s1 * dx : 0;
- if (o < o0) o0 = o;
- }
- for (j = 0; j < m; ++j) y0[j] -= o0;
- return y0;
- },
- expand: function(data) {
- var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = [];
- for (j = 0; j < m; ++j) {
- for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
- if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k;
- }
- for (j = 0; j < m; ++j) y0[j] = 0;
- return y0;
- },
- zero: d3_layout_stackOffsetZero
- });
- function d3_layout_stackOrderDefault(data) {
- return d3.range(data.length);
- }
- function d3_layout_stackOffsetZero(data) {
- var j = -1, m = data[0].length, y0 = [];
- while (++j < m) y0[j] = 0;
- return y0;
- }
- function d3_layout_stackMaxIndex(array) {
- var i = 1, j = 0, v = array[0][1], k, n = array.length;
- for (;i < n; ++i) {
- if ((k = array[i][1]) > v) {
- j = i;
- v = k;
- }
- }
- return j;
- }
- function d3_layout_stackReduceSum(d) {
- return d.reduce(d3_layout_stackSum, 0);
- }
- function d3_layout_stackSum(p, d) {
- return p + d[1];
- }
- d3.layout.histogram = function() {
- var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges;
- function histogram(data, i) {
- var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x;
- while (++i < m) {
- bin = bins[i] = [];
- bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
- bin.y = 0;
- }
- if (m > 0) {
- i = -1;
- while (++i < n) {
- x = values[i];
- if (x >= range[0] && x <= range[1]) {
- bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
- bin.y += k;
- bin.push(data[i]);
- }
- }
- }
- return bins;
- }
- histogram.value = function(x) {
- if (!arguments.length) return valuer;
- valuer = x;
- return histogram;
- };
- histogram.range = function(x) {
- if (!arguments.length) return ranger;
- ranger = d3_functor(x);
- return histogram;
- };
- histogram.bins = function(x) {
- if (!arguments.length) return binner;
- binner = typeof x === "number" ? function(range) {
- return d3_layout_histogramBinFixed(range, x);
- } : d3_functor(x);
- return histogram;
- };
- histogram.frequency = function(x) {
- if (!arguments.length) return frequency;
- frequency = !!x;
- return histogram;
- };
- return histogram;
- };
- function d3_layout_histogramBinSturges(range, values) {
- return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
- }
- function d3_layout_histogramBinFixed(range, n) {
- var x = -1, b = +range[0], m = (range[1] - b) / n, f = [];
- while (++x <= n) f[x] = m * x + b;
- return f;
- }
- function d3_layout_histogramRange(values) {
- return [ d3.min(values), d3.max(values) ];
- }
- d3.layout.pack = function() {
- var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
- function pack(d, i) {
- var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() {
- return radius;
- };
- root.x = root.y = 0;
- d3_layout_hierarchyVisitAfter(root, function(d) {
- d.r = +r(d.value);
- });
- d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
- if (padding) {
- var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
- d3_layout_hierarchyVisitAfter(root, function(d) {
- d.r += dr;
- });
- d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
- d3_layout_hierarchyVisitAfter(root, function(d) {
- d.r -= dr;
- });
- }
- d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
- return nodes;
- }
- pack.size = function(_) {
- if (!arguments.length) return size;
- size = _;
- return pack;
- };
- pack.radius = function(_) {
- if (!arguments.length) return radius;
- radius = _ == null || typeof _ === "function" ? _ : +_;
- return pack;
- };
- pack.padding = function(_) {
- if (!arguments.length) return padding;
- padding = +_;
- return pack;
- };
- return d3_layout_hierarchyRebind(pack, hierarchy);
- };
- function d3_layout_packSort(a, b) {
- return a.value - b.value;
- }
- function d3_layout_packInsert(a, b) {
- var c = a._pack_next;
- a._pack_next = b;
- b._pack_prev = a;
- b._pack_next = c;
- c._pack_prev = b;
- }
- function d3_layout_packSplice(a, b) {
- a._pack_next = b;
- b._pack_prev = a;
- }
- function d3_layout_packIntersects(a, b) {
- var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
- return .999 * dr * dr > dx * dx + dy * dy;
- }
- function d3_layout_packSiblings(node) {
- if (!(nodes = node.children) || !(n = nodes.length)) return;
- var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
- function bound(node) {
- xMin = Math.min(node.x - node.r, xMin);
- xMax = Math.max(node.x + node.r, xMax);
- yMin = Math.min(node.y - node.r, yMin);
- yMax = Math.max(node.y + node.r, yMax);
- }
- nodes.forEach(d3_layout_packLink);
- a = nodes[0];
- a.x = -a.r;
- a.y = 0;
- bound(a);
- if (n > 1) {
- b = nodes[1];
- b.x = b.r;
- b.y = 0;
- bound(b);
- if (n > 2) {
- c = nodes[2];
- d3_layout_packPlace(a, b, c);
- bound(c);
- d3_layout_packInsert(a, c);
- a._pack_prev = c;
- d3_layout_packInsert(c, b);
- b = a._pack_next;
- for (i = 3; i < n; i++) {
- d3_layout_packPlace(a, b, c = nodes[i]);
- var isect = 0, s1 = 1, s2 = 1;
- for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
- if (d3_layout_packIntersects(j, c)) {
- isect = 1;
- break;
- }
- }
- if (isect == 1) {
- for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
- if (d3_layout_packIntersects(k, c)) {
- break;
- }
- }
- }
- if (isect) {
- if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
- i--;
- } else {
- d3_layout_packInsert(a, c);
- b = c;
- bound(c);
- }
- }
- }
- }
- var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
- for (i = 0; i < n; i++) {
- c = nodes[i];
- c.x -= cx;
- c.y -= cy;
- cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
- }
- node.r = cr;
- nodes.forEach(d3_layout_packUnlink);
- }
- function d3_layout_packLink(node) {
- node._pack_next = node._pack_prev = node;
- }
- function d3_layout_packUnlink(node) {
- delete node._pack_next;
- delete node._pack_prev;
- }
- function d3_layout_packTransform(node, x, y, k) {
- var children = node.children;
- node.x = x += k * node.x;
- node.y = y += k * node.y;
- node.r *= k;
- if (children) {
- var i = -1, n = children.length;
- while (++i < n) d3_layout_packTransform(children[i], x, y, k);
- }
- }
- function d3_layout_packPlace(a, b, c) {
- var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
- if (db && (dx || dy)) {
- var da = b.r + c.r, dc = dx * dx + dy * dy;
- da *= da;
- db *= db;
- var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
- c.x = a.x + x * dx + y * dy;
- c.y = a.y + x * dy - y * dx;
- } else {
- c.x = a.x + db;
- c.y = a.y;
- }
- }
- d3.layout.tree = function() {
- var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null;
- function tree(d, i) {
- var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0);
- d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z;
- d3_layout_hierarchyVisitBefore(root1, secondWalk);
- if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else {
- var left = root0, right = root0, bottom = root0;
- d3_layout_hierarchyVisitBefore(root0, function(node) {
- if (node.x < left.x) left = node;
- if (node.x > right.x) right = node;
- if (node.depth > bottom.depth) bottom = node;
- });
- var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1);
- d3_layout_hierarchyVisitBefore(root0, function(node) {
- node.x = (node.x + tx) * kx;
- node.y = node.depth * ky;
- });
- }
- return nodes;
- }
- function wrapTree(root0) {
- var root1 = {
- A: null,
- children: [ root0 ]
- }, queue = [ root1 ], node1;
- while ((node1 = queue.pop()) != null) {
- for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) {
- queue.push((children[i] = child = {
- _: children[i],
- parent: node1,
- children: (child = children[i].children) && child.slice() || [],
- A: null,
- a: null,
- z: 0,
- m: 0,
- c: 0,
- s: 0,
- t: null,
- i: i
- }).a = child);
- }
- }
- return root1.children[0];
- }
- function firstWalk(v) {
- var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null;
- if (children.length) {
- d3_layout_treeShift(v);
- var midpoint = (children[0].z + children[children.length - 1].z) / 2;
- if (w) {
- v.z = w.z + separation(v._, w._);
- v.m = v.z - midpoint;
- } else {
- v.z = midpoint;
- }
- } else if (w) {
- v.z = w.z + separation(v._, w._);
- }
- v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
- }
- function secondWalk(v) {
- v._.x = v.z + v.parent.m;
- v.m += v.parent.m;
- }
- function apportion(v, w, ancestor) {
- if (w) {
- var vip = v, vop = v, vim = w, vom = vip.parent.children[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift;
- while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
- vom = d3_layout_treeLeft(vom);
- vop = d3_layout_treeRight(vop);
- vop.a = v;
- shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
- if (shift > 0) {
- d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift);
- sip += shift;
- sop += shift;
- }
- sim += vim.m;
- sip += vip.m;
- som += vom.m;
- sop += vop.m;
- }
- if (vim && !d3_layout_treeRight(vop)) {
- vop.t = vim;
- vop.m += sim - sop;
- }
- if (vip && !d3_layout_treeLeft(vom)) {
- vom.t = vip;
- vom.m += sip - som;
- ancestor = v;
- }
- }
- return ancestor;
- }
- function sizeNode(node) {
- node.x *= size[0];
- node.y = node.depth * size[1];
- }
- tree.separation = function(x) {
- if (!arguments.length) return separation;
- separation = x;
- return tree;
- };
- tree.size = function(x) {
- if (!arguments.length) return nodeSize ? null : size;
- nodeSize = (size = x) == null ? sizeNode : null;
- return tree;
- };
- tree.nodeSize = function(x) {
- if (!arguments.length) return nodeSize ? size : null;
- nodeSize = (size = x) == null ? null : sizeNode;
- return tree;
- };
- return d3_layout_hierarchyRebind(tree, hierarchy);
- };
- function d3_layout_treeSeparation(a, b) {
- return a.parent == b.parent ? 1 : 2;
- }
- function d3_layout_treeLeft(v) {
- var children = v.children;
- return children.length ? children[0] : v.t;
- }
- function d3_layout_treeRight(v) {
- var children = v.children, n;
- return (n = children.length) ? children[n - 1] : v.t;
- }
- function d3_layout_treeMove(wm, wp, shift) {
- var change = shift / (wp.i - wm.i);
- wp.c -= change;
- wp.s += shift;
- wm.c += change;
- wp.z += shift;
- wp.m += shift;
- }
- function d3_layout_treeShift(v) {
- var shift = 0, change = 0, children = v.children, i = children.length, w;
- while (--i >= 0) {
- w = children[i];
- w.z += shift;
- w.m += shift;
- shift += w.s + (change += w.c);
- }
- }
- function d3_layout_treeAncestor(vim, v, ancestor) {
- return vim.a.parent === v.parent ? vim.a : ancestor;
- }
- d3.layout.cluster = function() {
- var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
- function cluster(d, i) {
- var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
- d3_layout_hierarchyVisitAfter(root, function(node) {
- var children = node.children;
- if (children && children.length) {
- node.x = d3_layout_clusterX(children);
- node.y = d3_layout_clusterY(children);
- } else {
- node.x = previousNode ? x += separation(node, previousNode) : 0;
- node.y = 0;
- previousNode = node;
- }
- });
- var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2;
- d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) {
- node.x = (node.x - root.x) * size[0];
- node.y = (root.y - node.y) * size[1];
- } : function(node) {
- node.x = (node.x - x0) / (x1 - x0) * size[0];
- node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
- });
- return nodes;
- }
- cluster.separation = function(x) {
- if (!arguments.length) return separation;
- separation = x;
- return cluster;
- };
- cluster.size = function(x) {
- if (!arguments.length) return nodeSize ? null : size;
- nodeSize = (size = x) == null;
- return cluster;
- };
- cluster.nodeSize = function(x) {
- if (!arguments.length) return nodeSize ? size : null;
- nodeSize = (size = x) != null;
- return cluster;
- };
- return d3_layout_hierarchyRebind(cluster, hierarchy);
- };
- function d3_layout_clusterY(children) {
- return 1 + d3.max(children, function(child) {
- return child.y;
- });
- }
- function d3_layout_clusterX(children) {
- return children.reduce(function(x, child) {
- return x + child.x;
- }, 0) / children.length;
- }
- function d3_layout_clusterLeft(node) {
- var children = node.children;
- return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
- }
- function d3_layout_clusterRight(node) {
- var children = node.children, n;
- return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
- }
- d3.layout.treemap = function() {
- var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5));
- function scale(children, k) {
- var i = -1, n = children.length, child, area;
- while (++i < n) {
- area = (child = children[i]).value * (k < 0 ? 0 : k);
- child.area = isNaN(area) || area <= 0 ? 0 : area;
- }
- }
- function squarify(node) {
- var children = node.children;
- if (children && children.length) {
- var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n;
- scale(remaining, rect.dx * rect.dy / node.value);
- row.area = 0;
- while ((n = remaining.length) > 0) {
- row.push(child = remaining[n - 1]);
- row.area += child.area;
- if (mode !== "squarify" || (score = worst(row, u)) <= best) {
- remaining.pop();
- best = score;
- } else {
- row.area -= row.pop().area;
- position(row, u, rect, false);
- u = Math.min(rect.dx, rect.dy);
- row.length = row.area = 0;
- best = Infinity;
- }
- }
- if (row.length) {
- position(row, u, rect, true);
- row.length = row.area = 0;
- }
- children.forEach(squarify);
- }
- }
- function stickify(node) {
- var children = node.children;
- if (children && children.length) {
- var rect = pad(node), remaining = children.slice(), child, row = [];
- scale(remaining, rect.dx * rect.dy / node.value);
- row.area = 0;
- while (child = remaining.pop()) {
- row.push(child);
- row.area += child.area;
- if (child.z != null) {
- position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
- row.length = row.area = 0;
- }
- }
- children.forEach(stickify);
- }
- }
- function worst(row, u) {
- var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length;
- while (++i < n) {
- if (!(r = row[i].area)) continue;
- if (r < rmin) rmin = r;
- if (r > rmax) rmax = r;
- }
- s *= s;
- u *= u;
- return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity;
- }
- function position(row, u, rect, flush) {
- var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o;
- if (u == rect.dx) {
- if (flush || v > rect.dy) v = rect.dy;
- while (++i < n) {
- o = row[i];
- o.x = x;
- o.y = y;
- o.dy = v;
- x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0);
- }
- o.z = true;
- o.dx += rect.x + rect.dx - x;
- rect.y += v;
- rect.dy -= v;
- } else {
- if (flush || v > rect.dx) v = rect.dx;
- while (++i < n) {
- o = row[i];
- o.x = x;
- o.y = y;
- o.dx = v;
- y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0);
- }
- o.z = false;
- o.dy += rect.y + rect.dy - y;
- rect.x += v;
- rect.dx -= v;
- }
- }
- function treemap(d) {
- var nodes = stickies || hierarchy(d), root = nodes[0];
- root.x = root.y = 0;
- if (root.value) root.dx = size[0], root.dy = size[1]; else root.dx = root.dy = 0;
- if (stickies) hierarchy.revalue(root);
- scale([ root ], root.dx * root.dy / root.value);
- (stickies ? stickify : squarify)(root);
- if (sticky) stickies = nodes;
- return nodes;
- }
- treemap.size = function(x) {
- if (!arguments.length) return size;
- size = x;
- return treemap;
- };
- treemap.padding = function(x) {
- if (!arguments.length) return padding;
- function padFunction(node) {
- var p = x.call(treemap, node, node.depth);
- return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p);
- }
- function padConstant(node) {
- return d3_layout_treemapPad(node, x);
- }
- var type;
- pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ],
- padConstant) : padConstant;
- return treemap;
- };
- treemap.round = function(x) {
- if (!arguments.length) return round != Number;
- round = x ? Math.round : Number;
- return treemap;
- };
- treemap.sticky = function(x) {
- if (!arguments.length) return sticky;
- sticky = x;
- stickies = null;
- return treemap;
- };
- treemap.ratio = function(x) {
- if (!arguments.length) return ratio;
- ratio = x;
- return treemap;
- };
- treemap.mode = function(x) {
- if (!arguments.length) return mode;
- mode = x + "";
- return treemap;
- };
- return d3_layout_hierarchyRebind(treemap, hierarchy);
- };
- function d3_layout_treemapPadNull(node) {
- return {
- x: node.x,
- y: node.y,
- dx: node.dx,
- dy: node.dy
- };
- }
- function d3_layout_treemapPad(node, padding) {
- var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2];
- if (dx < 0) {
- x += dx / 2;
- dx = 0;
- }
- if (dy < 0) {
- y += dy / 2;
- dy = 0;
- }
- return {
- x: x,
- y: y,
- dx: dx,
- dy: dy
- };
- }
- d3.random = {
- normal: function(µ, σ) {
- var n = arguments.length;
- if (n < 2) σ = 1;
- if (n < 1) µ = 0;
- return function() {
- var x, y, r;
- do {
- x = Math.random() * 2 - 1;
- y = Math.random() * 2 - 1;
- r = x * x + y * y;
- } while (!r || r > 1);
- return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r);
- };
- },
- logNormal: function() {
- var random = d3.random.normal.apply(d3, arguments);
- return function() {
- return Math.exp(random());
- };
- },
- bates: function(m) {
- var random = d3.random.irwinHall(m);
- return function() {
- return random() / m;
- };
- },
- irwinHall: function(m) {
- return function() {
- for (var s = 0, j = 0; j < m; j++) s += Math.random();
- return s;
- };
- }
- };
- d3.scale = {};
- function d3_scaleExtent(domain) {
- var start = domain[0], stop = domain[domain.length - 1];
- return start < stop ? [ start, stop ] : [ stop, start ];
- }
- function d3_scaleRange(scale) {
- return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
- }
- function d3_scale_bilinear(domain, range, uninterpolate, interpolate) {
- var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]);
- return function(x) {
- return i(u(x));
- };
- }
- function d3_scale_nice(domain, nice) {
- var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx;
- if (x1 < x0) {
- dx = i0, i0 = i1, i1 = dx;
- dx = x0, x0 = x1, x1 = dx;
- }
- domain[i0] = nice.floor(x0);
- domain[i1] = nice.ceil(x1);
- return domain;
- }
- function d3_scale_niceStep(step) {
- return step ? {
- floor: function(x) {
- return Math.floor(x / step) * step;
- },
- ceil: function(x) {
- return Math.ceil(x / step) * step;
- }
- } : d3_scale_niceIdentity;
- }
- var d3_scale_niceIdentity = {
- floor: d3_identity,
- ceil: d3_identity
- };
- function d3_scale_polylinear(domain, range, uninterpolate, interpolate) {
- var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1;
- if (domain[k] < domain[0]) {
- domain = domain.slice().reverse();
- range = range.slice().reverse();
- }
- while (++j <= k) {
- u.push(uninterpolate(domain[j - 1], domain[j]));
- i.push(interpolate(range[j - 1], range[j]));
- }
- return function(x) {
- var j = d3.bisect(domain, x, 1, k) - 1;
- return i[j](u[j](x));
- };
- }
- d3.scale.linear = function() {
- return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false);
- };
- function d3_scale_linear(domain, range, interpolate, clamp) {
- var output, input;
- function rescale() {
- var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber;
- output = linear(domain, range, uninterpolate, interpolate);
- input = linear(range, domain, uninterpolate, d3_interpolate);
- return scale;
- }
- function scale(x) {
- return output(x);
- }
- scale.invert = function(y) {
- return input(y);
- };
- scale.domain = function(x) {
- if (!arguments.length) return domain;
- domain = x.map(Number);
- return rescale();
- };
- scale.range = function(x) {
- if (!arguments.length) return range;
- range = x;
- return rescale();
- };
- scale.rangeRound = function(x) {
- return scale.range(x).interpolate(d3_interpolateRound);
- };
- scale.clamp = function(x) {
- if (!arguments.length) return clamp;
- clamp = x;
- return rescale();
- };
- scale.interpolate = function(x) {
- if (!arguments.length) return interpolate;
- interpolate = x;
- return rescale();
- };
- scale.ticks = function(m) {
- return d3_scale_linearTicks(domain, m);
- };
- scale.tickFormat = function(m, format) {
- return d3_scale_linearTickFormat(domain, m, format);
- };
- scale.nice = function(m) {
- d3_scale_linearNice(domain, m);
- return rescale();
- };
- scale.copy = function() {
- return d3_scale_linear(domain, range, interpolate, clamp);
- };
- return rescale();
- }
- function d3_scale_linearRebind(scale, linear) {
- return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
- }
- function d3_scale_linearNice(domain, m) {
- d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
- d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
- return domain;
- }
- function d3_scale_linearTickRange(domain, m) {
- if (m == null) m = 10;
- var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step;
- if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2;
- extent[0] = Math.ceil(extent[0] / step) * step;
- extent[1] = Math.floor(extent[1] / step) * step + step * .5;
- extent[2] = step;
- return extent;
- }
- function d3_scale_linearTicks(domain, m) {
- return d3.range.apply(d3, d3_scale_linearTickRange(domain, m));
- }
- function d3_scale_linearTickFormat(domain, m, format) {
- var range = d3_scale_linearTickRange(domain, m);
- if (format) {
- var match = d3_format_re.exec(format);
- match.shift();
- if (match[8] === "s") {
- var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1])));
- if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2]));
- match[8] = "f";
- format = d3.format(match.join(""));
- return function(d) {
- return format(prefix.scale(d)) + prefix.symbol;
- };
- }
- if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range);
- format = match.join("");
- } else {
- format = ",." + d3_scale_linearPrecision(range[2]) + "f";
- }
- return d3.format(format);
- }
- var d3_scale_linearFormatSignificant = {
- s: 1,
- g: 1,
- p: 1,
- r: 1,
- e: 1
- };
- function d3_scale_linearPrecision(value) {
- return -Math.floor(Math.log(value) / Math.LN10 + .01);
- }
- function d3_scale_linearFormatPrecision(type, range) {
- var p = d3_scale_linearPrecision(range[2]);
- return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2;
- }
- d3.scale.log = function() {
- return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]);
- };
- function d3_scale_log(linear, base, positive, domain) {
- function log(x) {
- return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base);
- }
- function pow(x) {
- return positive ? Math.pow(base, x) : -Math.pow(base, -x);
- }
- function scale(x) {
- return linear(log(x));
- }
- scale.invert = function(x) {
- return pow(linear.invert(x));
- };
- scale.domain = function(x) {
- if (!arguments.length) return domain;
- positive = x[0] >= 0;
- linear.domain((domain = x.map(Number)).map(log));
- return scale;
- };
- scale.base = function(_) {
- if (!arguments.length) return base;
- base = +_;
- linear.domain(domain.map(log));
- return scale;
- };
- scale.nice = function() {
- var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative);
- linear.domain(niced);
- domain = niced.map(pow);
- return scale;
- };
- scale.ticks = function() {
- var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base;
- if (isFinite(j - i)) {
- if (positive) {
- for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k);
- ticks.push(pow(i));
- } else {
- ticks.push(pow(i));
- for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k);
- }
- for (i = 0; ticks[i] < u; i++) {}
- for (j = ticks.length; ticks[j - 1] > v; j--) {}
- ticks = ticks.slice(i, j);
- }
- return ticks;
- };
- scale.tickFormat = function(n, format) {
- if (!arguments.length) return d3_scale_logFormat;
- if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format);
- var k = Math.max(1, base * n / scale.ticks().length);
- return function(d) {
- var i = d / pow(Math.round(log(d)));
- if (i * base < base - .5) i *= base;
- return i <= k ? format(d) : "";
- };
- };
- scale.copy = function() {
- return d3_scale_log(linear.copy(), base, positive, domain);
- };
- return d3_scale_linearRebind(scale, linear);
- }
- var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = {
- floor: function(x) {
- return -Math.ceil(-x);
- },
- ceil: function(x) {
- return -Math.floor(-x);
- }
- };
- d3.scale.pow = function() {
- return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]);
- };
- function d3_scale_pow(linear, exponent, domain) {
- var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent);
- function scale(x) {
- return linear(powp(x));
- }
- scale.invert = function(x) {
- return powb(linear.invert(x));
- };
- scale.domain = function(x) {
- if (!arguments.length) return domain;
- linear.domain((domain = x.map(Number)).map(powp));
- return scale;
- };
- scale.ticks = function(m) {
- return d3_scale_linearTicks(domain, m);
- };
- scale.tickFormat = function(m, format) {
- return d3_scale_linearTickFormat(domain, m, format);
- };
- scale.nice = function(m) {
- return scale.domain(d3_scale_linearNice(domain, m));
- };
- scale.exponent = function(x) {
- if (!arguments.length) return exponent;
- powp = d3_scale_powPow(exponent = x);
- powb = d3_scale_powPow(1 / exponent);
- linear.domain(domain.map(powp));
- return scale;
- };
- scale.copy = function() {
- return d3_scale_pow(linear.copy(), exponent, domain);
- };
- return d3_scale_linearRebind(scale, linear);
- }
- function d3_scale_powPow(e) {
- return function(x) {
- return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e);
- };
- }
- d3.scale.sqrt = function() {
- return d3.scale.pow().exponent(.5);
- };
- d3.scale.ordinal = function() {
- return d3_scale_ordinal([], {
- t: "range",
- a: [ [] ]
- });
- };
- function d3_scale_ordinal(domain, ranger) {
- var index, range, rangeBand;
- function scale(x) {
- return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length];
- }
- function steps(start, step) {
- return d3.range(domain.length).map(function(i) {
- return start + step * i;
- });
- }
- scale.domain = function(x) {
- if (!arguments.length) return domain;
- domain = [];
- index = new d3_Map();
- var i = -1, n = x.length, xi;
- while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi));
- return scale[ranger.t].apply(scale, ranger.a);
- };
- scale.range = function(x) {
- if (!arguments.length) return range;
- range = x;
- rangeBand = 0;
- ranger = {
- t: "range",
- a: arguments
- };
- return scale;
- };
- scale.rangePoints = function(x, padding) {
- if (arguments.length < 2) padding = 0;
- var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2,
- 0) : (stop - start) / (domain.length - 1 + padding);
- range = steps(start + step * padding / 2, step);
- rangeBand = 0;
- ranger = {
- t: "rangePoints",
- a: arguments
- };
- return scale;
- };
- scale.rangeRoundPoints = function(x, padding) {
- if (arguments.length < 2) padding = 0;
- var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2),
- 0) : (stop - start) / (domain.length - 1 + padding) | 0;
- range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step);
- rangeBand = 0;
- ranger = {
- t: "rangeRoundPoints",
- a: arguments
- };
- return scale;
- };
- scale.rangeBands = function(x, padding, outerPadding) {
- if (arguments.length < 2) padding = 0;
- if (arguments.length < 3) outerPadding = padding;
- var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding);
- range = steps(start + step * outerPadding, step);
- if (reverse) range.reverse();
- rangeBand = step * (1 - padding);
- ranger = {
- t: "rangeBands",
- a: arguments
- };
- return scale;
- };
- scale.rangeRoundBands = function(x, padding, outerPadding) {
- if (arguments.length < 2) padding = 0;
- if (arguments.length < 3) outerPadding = padding;
- var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding));
- range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step);
- if (reverse) range.reverse();
- rangeBand = Math.round(step * (1 - padding));
- ranger = {
- t: "rangeRoundBands",
- a: arguments
- };
- return scale;
- };
- scale.rangeBand = function() {
- return rangeBand;
- };
- scale.rangeExtent = function() {
- return d3_scaleExtent(ranger.a[0]);
- };
- scale.copy = function() {
- return d3_scale_ordinal(domain, ranger);
- };
- return scale.domain(domain);
- }
- d3.scale.category10 = function() {
- return d3.scale.ordinal().range(d3_category10);
- };
- d3.scale.category20 = function() {
- return d3.scale.ordinal().range(d3_category20);
- };
- d3.scale.category20b = function() {
- return d3.scale.ordinal().range(d3_category20b);
- };
- d3.scale.category20c = function() {
- return d3.scale.ordinal().range(d3_category20c);
- };
- var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString);
- var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString);
- var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString);
- var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString);
- d3.scale.quantile = function() {
- return d3_scale_quantile([], []);
- };
- function d3_scale_quantile(domain, range) {
- var thresholds;
- function rescale() {
- var k = 0, q = range.length;
- thresholds = [];
- while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q);
- return scale;
- }
- function scale(x) {
- if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)];
- }
- scale.domain = function(x) {
- if (!arguments.length) return domain;
- domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending);
- return rescale();
- };
- scale.range = function(x) {
- if (!arguments.length) return range;
- range = x;
- return rescale();
- };
- scale.quantiles = function() {
- return thresholds;
- };
- scale.invertExtent = function(y) {
- y = range.indexOf(y);
- return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ];
- };
- scale.copy = function() {
- return d3_scale_quantile(domain, range);
- };
- return rescale();
- }
- d3.scale.quantize = function() {
- return d3_scale_quantize(0, 1, [ 0, 1 ]);
- };
- function d3_scale_quantize(x0, x1, range) {
- var kx, i;
- function scale(x) {
- return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))];
- }
- function rescale() {
- kx = range.length / (x1 - x0);
- i = range.length - 1;
- return scale;
- }
- scale.domain = function(x) {
- if (!arguments.length) return [ x0, x1 ];
- x0 = +x[0];
- x1 = +x[x.length - 1];
- return rescale();
- };
- scale.range = function(x) {
- if (!arguments.length) return range;
- range = x;
- return rescale();
- };
- scale.invertExtent = function(y) {
- y = range.indexOf(y);
- y = y < 0 ? NaN : y / kx + x0;
- return [ y, y + 1 / kx ];
- };
- scale.copy = function() {
- return d3_scale_quantize(x0, x1, range);
- };
- return rescale();
- }
- d3.scale.threshold = function() {
- return d3_scale_threshold([ .5 ], [ 0, 1 ]);
- };
- function d3_scale_threshold(domain, range) {
- function scale(x) {
- if (x <= x) return range[d3.bisect(domain, x)];
- }
- scale.domain = function(_) {
- if (!arguments.length) return domain;
- domain = _;
- return scale;
- };
- scale.range = function(_) {
- if (!arguments.length) return range;
- range = _;
- return scale;
- };
- scale.invertExtent = function(y) {
- y = range.indexOf(y);
- return [ domain[y - 1], domain[y] ];
- };
- scale.copy = function() {
- return d3_scale_threshold(domain, range);
- };
- return scale;
- }
- d3.scale.identity = function() {
- return d3_scale_identity([ 0, 1 ]);
- };
- function d3_scale_identity(domain) {
- function identity(x) {
- return +x;
- }
- identity.invert = identity;
- identity.domain = identity.range = function(x) {
- if (!arguments.length) return domain;
- domain = x.map(identity);
- return identity;
- };
- identity.ticks = function(m) {
- return d3_scale_linearTicks(domain, m);
- };
- identity.tickFormat = function(m, format) {
- return d3_scale_linearTickFormat(domain, m, format);
- };
- identity.copy = function() {
- return d3_scale_identity(domain);
- };
- return identity;
- }
- d3.svg = {};
- function d3_zero() {
- return 0;
- }
- d3.svg.arc = function() {
- var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, cornerRadius = d3_zero, padRadius = d3_svg_arcAuto, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle, padAngle = d3_svg_arcPadAngle;
- function arc() {
- var r0 = Math.max(0, +innerRadius.apply(this, arguments)), r1 = Math.max(0, +outerRadius.apply(this, arguments)), a0 = startAngle.apply(this, arguments) - halfπ, a1 = endAngle.apply(this, arguments) - halfπ, da = Math.abs(a1 - a0), cw = a0 > a1 ? 0 : 1;
- if (r1 < r0) rc = r1, r1 = r0, r0 = rc;
- if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z";
- var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = [];
- if (ap = (+padAngle.apply(this, arguments) || 0) / 2) {
- rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments);
- if (!cw) p1 *= -1;
- if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap));
- if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap));
- }
- if (r1) {
- x0 = r1 * Math.cos(a0 + p1);
- y0 = r1 * Math.sin(a0 + p1);
- x1 = r1 * Math.cos(a1 - p1);
- y1 = r1 * Math.sin(a1 - p1);
- var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1;
- if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) {
- var h1 = (a0 + a1) / 2;
- x0 = r1 * Math.cos(h1);
- y0 = r1 * Math.sin(h1);
- x1 = y1 = null;
- }
- } else {
- x0 = y0 = 0;
- }
- if (r0) {
- x2 = r0 * Math.cos(a1 - p0);
- y2 = r0 * Math.sin(a1 - p0);
- x3 = r0 * Math.cos(a0 + p0);
- y3 = r0 * Math.sin(a0 + p0);
- var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1;
- if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) {
- var h0 = (a0 + a1) / 2;
- x2 = r0 * Math.cos(h0);
- y2 = r0 * Math.sin(h0);
- x3 = y3 = null;
- }
- } else {
- x2 = y2 = 0;
- }
- if (da > ε && (rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) {
- cr = r0 < r1 ^ cw ? 0 : 1;
- var rc1 = rc, rc0 = rc;
- if (da < π) {
- var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]);
- rc0 = Math.min(rc, (r0 - lc) / (kc - 1));
- rc1 = Math.min(rc, (r1 - lc) / (kc + 1));
- }
- if (x1 != null) {
- var t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw);
- if (rc === rc1) {
- path.push("M", t30[0], "A", rc1, ",", rc1, " 0 0,", cr, " ", t30[1], "A", r1, ",", r1, " 0 ", 1 - cw ^ d3_svg_arcSweep(t30[1][0], t30[1][1], t12[1][0], t12[1][1]), ",", cw, " ", t12[1], "A", rc1, ",", rc1, " 0 0,", cr, " ", t12[0]);
- } else {
- path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]);
- }
- } else {
- path.push("M", x0, ",", y0);
- }
- if (x3 != null) {
- var t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw);
- if (rc === rc0) {
- path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t21[1], "A", r0, ",", r0, " 0 ", cw ^ d3_svg_arcSweep(t21[1][0], t21[1][1], t03[1][0], t03[1][1]), ",", 1 - cw, " ", t03[1], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]);
- } else {
- path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]);
- }
- } else {
- path.push("L", x2, ",", y2);
- }
- } else {
- path.push("M", x0, ",", y0);
- if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1);
- path.push("L", x2, ",", y2);
- if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3);
- }
- path.push("Z");
- return path.join("");
- }
- function circleSegment(r1, cw) {
- return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1;
- }
- arc.innerRadius = function(v) {
- if (!arguments.length) return innerRadius;
- innerRadius = d3_functor(v);
- return arc;
- };
- arc.outerRadius = function(v) {
- if (!arguments.length) return outerRadius;
- outerRadius = d3_functor(v);
- return arc;
- };
- arc.cornerRadius = function(v) {
- if (!arguments.length) return cornerRadius;
- cornerRadius = d3_functor(v);
- return arc;
- };
- arc.padRadius = function(v) {
- if (!arguments.length) return padRadius;
- padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v);
- return arc;
- };
- arc.startAngle = function(v) {
- if (!arguments.length) return startAngle;
- startAngle = d3_functor(v);
- return arc;
- };
- arc.endAngle = function(v) {
- if (!arguments.length) return endAngle;
- endAngle = d3_functor(v);
- return arc;
- };
- arc.padAngle = function(v) {
- if (!arguments.length) return padAngle;
- padAngle = d3_functor(v);
- return arc;
- };
- arc.centroid = function() {
- var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ;
- return [ Math.cos(a) * r, Math.sin(a) * r ];
- };
- return arc;
- };
- var d3_svg_arcAuto = "auto";
- function d3_svg_arcInnerRadius(d) {
- return d.innerRadius;
- }
- function d3_svg_arcOuterRadius(d) {
- return d.outerRadius;
- }
- function d3_svg_arcStartAngle(d) {
- return d.startAngle;
- }
- function d3_svg_arcEndAngle(d) {
- return d.endAngle;
- }
- function d3_svg_arcPadAngle(d) {
- return d && d.padAngle;
- }
- function d3_svg_arcSweep(x0, y0, x1, y1) {
- return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1;
- }
- function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) {
- var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(Math.max(0, r * r * d2 - D * D)), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3;
- if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
- return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ];
- }
- function d3_svg_line(projection) {
- var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
- function line(data) {
- var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
- function segment() {
- segments.push("M", interpolate(projection(points), tension));
- }
- while (++i < n) {
- if (defined.call(this, d = data[i], i)) {
- points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
- } else if (points.length) {
- segment();
- points = [];
- }
- }
- if (points.length) segment();
- return segments.length ? segments.join("") : null;
- }
- line.x = function(_) {
- if (!arguments.length) return x;
- x = _;
- return line;
- };
- line.y = function(_) {
- if (!arguments.length) return y;
- y = _;
- return line;
- };
- line.defined = function(_) {
- if (!arguments.length) return defined;
- defined = _;
- return line;
- };
- line.interpolate = function(_) {
- if (!arguments.length) return interpolateKey;
- if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
- return line;
- };
- line.tension = function(_) {
- if (!arguments.length) return tension;
- tension = _;
- return line;
- };
- return line;
- }
- d3.svg.line = function() {
- return d3_svg_line(d3_identity);
- };
- var d3_svg_lineInterpolators = d3.map({
- linear: d3_svg_lineLinear,
- "linear-closed": d3_svg_lineLinearClosed,
- step: d3_svg_lineStep,
- "step-before": d3_svg_lineStepBefore,
- "step-after": d3_svg_lineStepAfter,
- basis: d3_svg_lineBasis,
- "basis-open": d3_svg_lineBasisOpen,
- "basis-closed": d3_svg_lineBasisClosed,
- bundle: d3_svg_lineBundle,
- cardinal: d3_svg_lineCardinal,
- "cardinal-open": d3_svg_lineCardinalOpen,
- "cardinal-closed": d3_svg_lineCardinalClosed,
- monotone: d3_svg_lineMonotone
- });
- d3_svg_lineInterpolators.forEach(function(key, value) {
- value.key = key;
- value.closed = /-closed$/.test(key);
- });
- function d3_svg_lineLinear(points) {
- return points.length > 1 ? points.join("L") : points + "Z";
- }
- function d3_svg_lineLinearClosed(points) {
- return points.join("L") + "Z";
- }
- function d3_svg_lineStep(points) {
- var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
- while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
- if (n > 1) path.push("H", p[0]);
- return path.join("");
- }
- function d3_svg_lineStepBefore(points) {
- var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
- while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
- return path.join("");
- }
- function d3_svg_lineStepAfter(points) {
- var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
- while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
- return path.join("");
- }
- function d3_svg_lineCardinalOpen(points, tension) {
- return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension));
- }
- function d3_svg_lineCardinalClosed(points, tension) {
- return points.length < 3 ? d3_svg_lineLinearClosed(points) : points[0] + d3_svg_lineHermite((points.push(points[0]),
- points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
- }
- function d3_svg_lineCardinal(points, tension) {
- return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
- }
- function d3_svg_lineHermite(points, tangents) {
- if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
- return d3_svg_lineLinear(points);
- }
- var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
- if (quad) {
- path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
- p0 = points[1];
- pi = 2;
- }
- if (tangents.length > 1) {
- t = tangents[1];
- p = points[pi];
- pi++;
- path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
- for (var i = 2; i < tangents.length; i++, pi++) {
- p = points[pi];
- t = tangents[i];
- path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
- }
- }
- if (quad) {
- var lp = points[pi];
- path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
- }
- return path;
- }
- function d3_svg_lineCardinalTangents(points, tension) {
- var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
- while (++i < n) {
- p0 = p1;
- p1 = p2;
- p2 = points[i];
- tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
- }
- return tangents;
- }
- function d3_svg_lineBasis(points) {
- if (points.length < 3) return d3_svg_lineLinear(points);
- var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
- points.push(points[n - 1]);
- while (++i <= n) {
- pi = points[i];
- px.shift();
- px.push(pi[0]);
- py.shift();
- py.push(pi[1]);
- d3_svg_lineBasisBezier(path, px, py);
- }
- points.pop();
- path.push("L", pi);
- return path.join("");
- }
- function d3_svg_lineBasisOpen(points) {
- if (points.length < 4) return d3_svg_lineLinear(points);
- var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
- while (++i < 3) {
- pi = points[i];
- px.push(pi[0]);
- py.push(pi[1]);
- }
- path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
- --i;
- while (++i < n) {
- pi = points[i];
- px.shift();
- px.push(pi[0]);
- py.shift();
- py.push(pi[1]);
- d3_svg_lineBasisBezier(path, px, py);
- }
- return path.join("");
- }
- function d3_svg_lineBasisClosed(points) {
- var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
- while (++i < 4) {
- pi = points[i % n];
- px.push(pi[0]);
- py.push(pi[1]);
- }
- path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
- --i;
- while (++i < m) {
- pi = points[i % n];
- px.shift();
- px.push(pi[0]);
- py.shift();
- py.push(pi[1]);
- d3_svg_lineBasisBezier(path, px, py);
- }
- return path.join("");
- }
- function d3_svg_lineBundle(points, tension) {
- var n = points.length - 1;
- if (n) {
- var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
- while (++i <= n) {
- p = points[i];
- t = i / n;
- p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
- p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
- }
- }
- return d3_svg_lineBasis(points);
- }
- function d3_svg_lineDot4(a, b) {
- return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
- }
- var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ];
- function d3_svg_lineBasisBezier(path, x, y) {
- path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
- }
- function d3_svg_lineSlope(p0, p1) {
- return (p1[1] - p0[1]) / (p1[0] - p0[0]);
- }
- function d3_svg_lineFiniteDifferences(points) {
- var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
- while (++i < j) {
- m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
- }
- m[i] = d;
- return m;
- }
- function d3_svg_lineMonotoneTangents(points) {
- var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
- while (++i < j) {
- d = d3_svg_lineSlope(points[i], points[i + 1]);
- if (abs(d) < ε) {
- m[i] = m[i + 1] = 0;
- } else {
- a = m[i] / d;
- b = m[i + 1] / d;
- s = a * a + b * b;
- if (s > 9) {
- s = d * 3 / Math.sqrt(s);
- m[i] = s * a;
- m[i + 1] = s * b;
- }
- }
- }
- i = -1;
- while (++i <= j) {
- s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
- tangents.push([ s || 0, m[i] * s || 0 ]);
- }
- return tangents;
- }
- function d3_svg_lineMonotone(points) {
- return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
- }
- d3.svg.line.radial = function() {
- var line = d3_svg_line(d3_svg_lineRadial);
- line.radius = line.x, delete line.x;
- line.angle = line.y, delete line.y;
- return line;
- };
- function d3_svg_lineRadial(points) {
- var point, i = -1, n = points.length, r, a;
- while (++i < n) {
- point = points[i];
- r = point[0];
- a = point[1] - halfπ;
- point[0] = r * Math.cos(a);
- point[1] = r * Math.sin(a);
- }
- return points;
- }
- function d3_svg_area(projection) {
- var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7;
- function area(data) {
- var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() {
- return x;
- } : d3_functor(x1), fy1 = y0 === y1 ? function() {
- return y;
- } : d3_functor(y1), x, y;
- function segment() {
- segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z");
- }
- while (++i < n) {
- if (defined.call(this, d = data[i], i)) {
- points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]);
- points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]);
- } else if (points0.length) {
- segment();
- points0 = [];
- points1 = [];
- }
- }
- if (points0.length) segment();
- return segments.length ? segments.join("") : null;
- }
- area.x = function(_) {
- if (!arguments.length) return x1;
- x0 = x1 = _;
- return area;
- };
- area.x0 = function(_) {
- if (!arguments.length) return x0;
- x0 = _;
- return area;
- };
- area.x1 = function(_) {
- if (!arguments.length) return x1;
- x1 = _;
- return area;
- };
- area.y = function(_) {
- if (!arguments.length) return y1;
- y0 = y1 = _;
- return area;
- };
- area.y0 = function(_) {
- if (!arguments.length) return y0;
- y0 = _;
- return area;
- };
- area.y1 = function(_) {
- if (!arguments.length) return y1;
- y1 = _;
- return area;
- };
- area.defined = function(_) {
- if (!arguments.length) return defined;
- defined = _;
- return area;
- };
- area.interpolate = function(_) {
- if (!arguments.length) return interpolateKey;
- if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
- interpolateReverse = interpolate.reverse || interpolate;
- L = interpolate.closed ? "M" : "L";
- return area;
- };
- area.tension = function(_) {
- if (!arguments.length) return tension;
- tension = _;
- return area;
- };
- return area;
- }
- d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter;
- d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore;
- d3.svg.area = function() {
- return d3_svg_area(d3_identity);
- };
- d3.svg.area.radial = function() {
- var area = d3_svg_area(d3_svg_lineRadial);
- area.radius = area.x, delete area.x;
- area.innerRadius = area.x0, delete area.x0;
- area.outerRadius = area.x1, delete area.x1;
- area.angle = area.y, delete area.y;
- area.startAngle = area.y0, delete area.y0;
- area.endAngle = area.y1, delete area.y1;
- return area;
- };
- d3.svg.chord = function() {
- var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
- function chord(d, i) {
- var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i);
- return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z";
- }
- function subgroup(self, f, d, i) {
- var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) - halfπ, a1 = endAngle.call(self, subgroup, i) - halfπ;
- return {
- r: r,
- a0: a0,
- a1: a1,
- p0: [ r * Math.cos(a0), r * Math.sin(a0) ],
- p1: [ r * Math.cos(a1), r * Math.sin(a1) ]
- };
- }
- function equals(a, b) {
- return a.a0 == b.a0 && a.a1 == b.a1;
- }
- function arc(r, p, a) {
- return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p;
- }
- function curve(r0, p0, r1, p1) {
- return "Q 0,0 " + p1;
- }
- chord.radius = function(v) {
- if (!arguments.length) return radius;
- radius = d3_functor(v);
- return chord;
- };
- chord.source = function(v) {
- if (!arguments.length) return source;
- source = d3_functor(v);
- return chord;
- };
- chord.target = function(v) {
- if (!arguments.length) return target;
- target = d3_functor(v);
- return chord;
- };
- chord.startAngle = function(v) {
- if (!arguments.length) return startAngle;
- startAngle = d3_functor(v);
- return chord;
- };
- chord.endAngle = function(v) {
- if (!arguments.length) return endAngle;
- endAngle = d3_functor(v);
- return chord;
- };
- return chord;
- };
- function d3_svg_chordRadius(d) {
- return d.radius;
- }
- d3.svg.diagonal = function() {
- var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection;
- function diagonal(d, i) {
- var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
- x: p0.x,
- y: m
- }, {
- x: p3.x,
- y: m
- }, p3 ];
- p = p.map(projection);
- return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
- }
- diagonal.source = function(x) {
- if (!arguments.length) return source;
- source = d3_functor(x);
- return diagonal;
- };
- diagonal.target = function(x) {
- if (!arguments.length) return target;
- target = d3_functor(x);
- return diagonal;
- };
- diagonal.projection = function(x) {
- if (!arguments.length) return projection;
- projection = x;
- return diagonal;
- };
- return diagonal;
- };
- function d3_svg_diagonalProjection(d) {
- return [ d.x, d.y ];
- }
- d3.svg.diagonal.radial = function() {
- var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection;
- diagonal.projection = function(x) {
- return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection;
- };
- return diagonal;
- };
- function d3_svg_diagonalRadialProjection(projection) {
- return function() {
- var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ;
- return [ r * Math.cos(a), r * Math.sin(a) ];
- };
- }
- d3.svg.symbol = function() {
- var type = d3_svg_symbolType, size = d3_svg_symbolSize;
- function symbol(d, i) {
- return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i));
- }
- symbol.type = function(x) {
- if (!arguments.length) return type;
- type = d3_functor(x);
- return symbol;
- };
- symbol.size = function(x) {
- if (!arguments.length) return size;
- size = d3_functor(x);
- return symbol;
- };
- return symbol;
- };
- function d3_svg_symbolSize() {
- return 64;
- }
- function d3_svg_symbolType() {
- return "circle";
- }
- function d3_svg_symbolCircle(size) {
- var r = Math.sqrt(size / π);
- return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z";
- }
- var d3_svg_symbols = d3.map({
- circle: d3_svg_symbolCircle,
- cross: function(size) {
- var r = Math.sqrt(size / 5) / 2;
- return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z";
- },
- diamond: function(size) {
- var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30;
- return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z";
- },
- square: function(size) {
- var r = Math.sqrt(size) / 2;
- return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z";
- },
- "triangle-down": function(size) {
- var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
- return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z";
- },
- "triangle-up": function(size) {
- var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
- return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z";
- }
- });
- d3.svg.symbolTypes = d3_svg_symbols.keys();
- var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians);
- d3_selectionPrototype.transition = function(name) {
- var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || {
- time: Date.now(),
- ease: d3_ease_cubicInOut,
- delay: 0,
- duration: 250
- };
- for (var j = -1, m = this.length; ++j < m; ) {
- subgroups.push(subgroup = []);
- for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
- if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
- subgroup.push(node);
- }
- }
- return d3_transition(subgroups, ns, id);
- };
- d3_selectionPrototype.interrupt = function(name) {
- return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name)));
- };
- var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
- function d3_selection_interruptNS(ns) {
- return function() {
- var lock, activeId, active;
- if ((lock = this[ns]) && (active = lock[activeId = lock.active])) {
- active.timer.c = null;
- active.timer.t = NaN;
- if (--lock.count) delete lock[activeId]; else delete this[ns];
- lock.active += .5;
- active.event && active.event.interrupt.call(this, this.__data__, active.index);
- }
- };
- }
- function d3_transition(groups, ns, id) {
- d3_subclass(groups, d3_transitionPrototype);
- groups.namespace = ns;
- groups.id = id;
- return groups;
- }
- var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit;
- d3_transitionPrototype.call = d3_selectionPrototype.call;
- d3_transitionPrototype.empty = d3_selectionPrototype.empty;
- d3_transitionPrototype.node = d3_selectionPrototype.node;
- d3_transitionPrototype.size = d3_selectionPrototype.size;
- d3.transition = function(selection, name) {
- return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection);
- };
- d3.transition.prototype = d3_transitionPrototype;
- d3_transitionPrototype.select = function(selector) {
- var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node;
- selector = d3_selection_selector(selector);
- for (var j = -1, m = this.length; ++j < m; ) {
- subgroups.push(subgroup = []);
- for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
- if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
- if ("__data__" in node) subnode.__data__ = node.__data__;
- d3_transitionNode(subnode, i, ns, id, node[ns][id]);
- subgroup.push(subnode);
- } else {
- subgroup.push(null);
- }
- }
- }
- return d3_transition(subgroups, ns, id);
- };
- d3_transitionPrototype.selectAll = function(selector) {
- var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition;
- selector = d3_selection_selectorAll(selector);
- for (var j = -1, m = this.length; ++j < m; ) {
- for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
- if (node = group[i]) {
- transition = node[ns][id];
- subnodes = selector.call(node, node.__data__, i, j);
- subgroups.push(subgroup = []);
- for (var k = -1, o = subnodes.length; ++k < o; ) {
- if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
- subgroup.push(subnode);
- }
- }
- }
- }
- return d3_transition(subgroups, ns, id);
- };
- d3_transitionPrototype.filter = function(filter) {
- var subgroups = [], subgroup, group, node;
- if (typeof filter !== "function") filter = d3_selection_filter(filter);
- for (var j = 0, m = this.length; j < m; j++) {
- subgroups.push(subgroup = []);
- for (var group = this[j], i = 0, n = group.length; i < n; i++) {
- if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
- subgroup.push(node);
- }
- }
- }
- return d3_transition(subgroups, this.namespace, this.id);
- };
- d3_transitionPrototype.tween = function(name, tween) {
- var id = this.id, ns = this.namespace;
- if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
- return d3_selection_each(this, tween == null ? function(node) {
- node[ns][id].tween.remove(name);
- } : function(node) {
- node[ns][id].tween.set(name, tween);
- });
- };
- function d3_transition_tween(groups, name, value, tween) {
- var id = groups.id, ns = groups.namespace;
- return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) {
- node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j)));
- } : (value = tween(value), function(node) {
- node[ns][id].tween.set(name, value);
- }));
- }
- d3_transitionPrototype.attr = function(nameNS, value) {
- if (arguments.length < 2) {
- for (value in nameNS) this.attr(value, nameNS[value]);
- return this;
- }
- var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
- function attrNull() {
- this.removeAttribute(name);
- }
- function attrNullNS() {
- this.removeAttributeNS(name.space, name.local);
- }
- function attrTween(b) {
- return b == null ? attrNull : (b += "", function() {
- var a = this.getAttribute(name), i;
- return a !== b && (i = interpolate(a, b), function(t) {
- this.setAttribute(name, i(t));
- });
- });
- }
- function attrTweenNS(b) {
- return b == null ? attrNullNS : (b += "", function() {
- var a = this.getAttributeNS(name.space, name.local), i;
- return a !== b && (i = interpolate(a, b), function(t) {
- this.setAttributeNS(name.space, name.local, i(t));
- });
- });
- }
- return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
- };
- d3_transitionPrototype.attrTween = function(nameNS, tween) {
- var name = d3.ns.qualify(nameNS);
- function attrTween(d, i) {
- var f = tween.call(this, d, i, this.getAttribute(name));
- return f && function(t) {
- this.setAttribute(name, f(t));
- };
- }
- function attrTweenNS(d, i) {
- var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
- return f && function(t) {
- this.setAttributeNS(name.space, name.local, f(t));
- };
- }
- return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
- };
- d3_transitionPrototype.style = function(name, value, priority) {
- var n = arguments.length;
- if (n < 3) {
- if (typeof name !== "string") {
- if (n < 2) value = "";
- for (priority in name) this.style(priority, name[priority], value);
- return this;
- }
- priority = "";
- }
- function styleNull() {
- this.style.removeProperty(name);
- }
- function styleString(b) {
- return b == null ? styleNull : (b += "", function() {
- var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i;
- return a !== b && (i = d3_interpolate(a, b), function(t) {
- this.style.setProperty(name, i(t), priority);
- });
- });
- }
- return d3_transition_tween(this, "style." + name, value, styleString);
- };
- d3_transitionPrototype.styleTween = function(name, tween, priority) {
- if (arguments.length < 3) priority = "";
- function styleTween(d, i) {
- var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name));
- return f && function(t) {
- this.style.setProperty(name, f(t), priority);
- };
- }
- return this.tween("style." + name, styleTween);
- };
- d3_transitionPrototype.text = function(value) {
- return d3_transition_tween(this, "text", value, d3_transition_text);
- };
- function d3_transition_text(b) {
- if (b == null) b = "";
- return function() {
- this.textContent = b;
- };
- }
- d3_transitionPrototype.remove = function() {
- var ns = this.namespace;
- return this.each("end.transition", function() {
- var p;
- if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
- });
- };
- d3_transitionPrototype.ease = function(value) {
- var id = this.id, ns = this.namespace;
- if (arguments.length < 1) return this.node()[ns][id].ease;
- if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
- return d3_selection_each(this, function(node) {
- node[ns][id].ease = value;
- });
- };
- d3_transitionPrototype.delay = function(value) {
- var id = this.id, ns = this.namespace;
- if (arguments.length < 1) return this.node()[ns][id].delay;
- return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
- node[ns][id].delay = +value.call(node, node.__data__, i, j);
- } : (value = +value, function(node) {
- node[ns][id].delay = value;
- }));
- };
- d3_transitionPrototype.duration = function(value) {
- var id = this.id, ns = this.namespace;
- if (arguments.length < 1) return this.node()[ns][id].duration;
- return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
- node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j));
- } : (value = Math.max(1, value), function(node) {
- node[ns][id].duration = value;
- }));
- };
- d3_transitionPrototype.each = function(type, listener) {
- var id = this.id, ns = this.namespace;
- if (arguments.length < 2) {
- var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId;
- try {
- d3_transitionInheritId = id;
- d3_selection_each(this, function(node, i, j) {
- d3_transitionInherit = node[ns][id];
- type.call(node, node.__data__, i, j);
- });
- } finally {
- d3_transitionInherit = inherit;
- d3_transitionInheritId = inheritId;
- }
- } else {
- d3_selection_each(this, function(node) {
- var transition = node[ns][id];
- (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
- });
- }
- return this;
- };
- d3_transitionPrototype.transition = function() {
- var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition;
- for (var j = 0, m = this.length; j < m; j++) {
- subgroups.push(subgroup = []);
- for (var group = this[j], i = 0, n = group.length; i < n; i++) {
- if (node = group[i]) {
- transition = node[ns][id0];
- d3_transitionNode(node, i, ns, id1, {
- time: transition.time,
- ease: transition.ease,
- delay: transition.delay + transition.duration,
- duration: transition.duration
- });
- }
- subgroup.push(node);
- }
- }
- return d3_transition(subgroups, ns, id1);
- };
- function d3_transitionNamespace(name) {
- return name == null ? "__transition__" : "__transition_" + name + "__";
- }
- function d3_transitionNode(node, i, ns, id, inherit) {
- var lock = node[ns] || (node[ns] = {
- active: 0,
- count: 0
- }), transition = lock[id], time, timer, duration, ease, tweens;
- function schedule(elapsed) {
- var delay = transition.delay;
- timer.t = delay + time;
- if (delay <= elapsed) return start(elapsed - delay);
- timer.c = start;
- }
- function start(elapsed) {
- var activeId = lock.active, active = lock[activeId];
- if (active) {
- active.timer.c = null;
- active.timer.t = NaN;
- --lock.count;
- delete lock[activeId];
- active.event && active.event.interrupt.call(node, node.__data__, active.index);
- }
- for (var cancelId in lock) {
- if (+cancelId < id) {
- var cancel = lock[cancelId];
- cancel.timer.c = null;
- cancel.timer.t = NaN;
- --lock.count;
- delete lock[cancelId];
- }
- }
- timer.c = tick;
- d3_timer(function() {
- if (timer.c && tick(elapsed || 1)) {
- timer.c = null;
- timer.t = NaN;
- }
- return 1;
- }, 0, time);
- lock.active = id;
- transition.event && transition.event.start.call(node, node.__data__, i);
- tweens = [];
- transition.tween.forEach(function(key, value) {
- if (value = value.call(node, node.__data__, i)) {
- tweens.push(value);
- }
- });
- ease = transition.ease;
- duration = transition.duration;
- }
- function tick(elapsed) {
- var t = elapsed / duration, e = ease(t), n = tweens.length;
- while (n > 0) {
- tweens[--n].call(node, e);
- }
- if (t >= 1) {
- transition.event && transition.event.end.call(node, node.__data__, i);
- if (--lock.count) delete lock[id]; else delete node[ns];
- return 1;
- }
- }
- if (!transition) {
- time = inherit.time;
- timer = d3_timer(schedule, 0, time);
- transition = lock[id] = {
- tween: new d3_Map(),
- time: time,
- timer: timer,
- delay: inherit.delay,
- duration: inherit.duration,
- ease: inherit.ease,
- index: i
- };
- inherit = null;
- ++lock.count;
- }
- }
- d3.svg.axis = function() {
- var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_;
- function axis(g) {
- g.each(function() {
- var g = d3.select(this);
- var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy();
- var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickSpacing = Math.max(innerTickSize, 0) + tickPadding, tickTransform;
- var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"),
- d3.transition(path));
- tickEnter.append("line");
- tickEnter.append("text");
- var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"), sign = orient === "top" || orient === "left" ? -1 : 1, x1, x2, y1, y2;
- if (orient === "bottom" || orient === "top") {
- tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2";
- text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle");
- pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize);
- } else {
- tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2";
- text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start");
- pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize);
- }
- lineEnter.attr(y2, sign * innerTickSize);
- textEnter.attr(y1, sign * tickSpacing);
- lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize);
- textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing);
- if (scale1.rangeBand) {
- var x = scale1, dx = x.rangeBand() / 2;
- scale0 = scale1 = function(d) {
- return x(d) + dx;
- };
- } else if (scale0.rangeBand) {
- scale0 = scale1;
- } else {
- tickExit.call(tickTransform, scale1, scale0);
- }
- tickEnter.call(tickTransform, scale0, scale1);
- tickUpdate.call(tickTransform, scale1, scale1);
- });
- }
- axis.scale = function(x) {
- if (!arguments.length) return scale;
- scale = x;
- return axis;
- };
- axis.orient = function(x) {
- if (!arguments.length) return orient;
- orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient;
- return axis;
- };
- axis.ticks = function() {
- if (!arguments.length) return tickArguments_;
- tickArguments_ = d3_array(arguments);
- return axis;
- };
- axis.tickValues = function(x) {
- if (!arguments.length) return tickValues;
- tickValues = x;
- return axis;
- };
- axis.tickFormat = function(x) {
- if (!arguments.length) return tickFormat_;
- tickFormat_ = x;
- return axis;
- };
- axis.tickSize = function(x) {
- var n = arguments.length;
- if (!n) return innerTickSize;
- innerTickSize = +x;
- outerTickSize = +arguments[n - 1];
- return axis;
- };
- axis.innerTickSize = function(x) {
- if (!arguments.length) return innerTickSize;
- innerTickSize = +x;
- return axis;
- };
- axis.outerTickSize = function(x) {
- if (!arguments.length) return outerTickSize;
- outerTickSize = +x;
- return axis;
- };
- axis.tickPadding = function(x) {
- if (!arguments.length) return tickPadding;
- tickPadding = +x;
- return axis;
- };
- axis.tickSubdivide = function() {
- return arguments.length && axis;
- };
- return axis;
- };
- var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {
- top: 1,
- right: 1,
- bottom: 1,
- left: 1
- };
- function d3_svg_axisX(selection, x0, x1) {
- selection.attr("transform", function(d) {
- var v0 = x0(d);
- return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)";
- });
- }
- function d3_svg_axisY(selection, y0, y1) {
- selection.attr("transform", function(d) {
- var v0 = y0(d);
- return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")";
- });
- }
- d3.svg.brush = function() {
- var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0];
- function brush(g) {
- g.each(function() {
- var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart);
- var background = g.selectAll(".background").data([ 0 ]);
- background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair");
- g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move");
- var resize = g.selectAll(".resize").data(resizes, d3_identity);
- resize.exit().remove();
- resize.enter().append("g").attr("class", function(d) {
- return "resize " + d;
- }).style("cursor", function(d) {
- return d3_svg_brushCursor[d];
- }).append("rect").attr("x", function(d) {
- return /[ew]$/.test(d) ? -3 : null;
- }).attr("y", function(d) {
- return /^[ns]/.test(d) ? -3 : null;
- }).attr("width", 6).attr("height", 6).style("visibility", "hidden");
- resize.style("display", brush.empty() ? "none" : null);
- var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range;
- if (x) {
- range = d3_scaleRange(x);
- backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]);
- redrawX(gUpdate);
- }
- if (y) {
- range = d3_scaleRange(y);
- backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]);
- redrawY(gUpdate);
- }
- redraw(gUpdate);
- });
- }
- brush.event = function(g) {
- g.each(function() {
- var event_ = event.of(this, arguments), extent1 = {
- x: xExtent,
- y: yExtent,
- i: xExtentDomain,
- j: yExtentDomain
- }, extent0 = this.__chart__ || extent1;
- this.__chart__ = extent1;
- if (d3_transitionInheritId) {
- d3.select(this).transition().each("start.brush", function() {
- xExtentDomain = extent0.i;
- yExtentDomain = extent0.j;
- xExtent = extent0.x;
- yExtent = extent0.y;
- event_({
- type: "brushstart"
- });
- }).tween("brush:brush", function() {
- var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y);
- xExtentDomain = yExtentDomain = null;
- return function(t) {
- xExtent = extent1.x = xi(t);
- yExtent = extent1.y = yi(t);
- event_({
- type: "brush",
- mode: "resize"
- });
- };
- }).each("end.brush", function() {
- xExtentDomain = extent1.i;
- yExtentDomain = extent1.j;
- event_({
- type: "brush",
- mode: "resize"
- });
- event_({
- type: "brushend"
- });
- });
- } else {
- event_({
- type: "brushstart"
- });
- event_({
- type: "brush",
- mode: "resize"
- });
- event_({
- type: "brushend"
- });
- }
- });
- };
- function redraw(g) {
- g.selectAll(".resize").attr("transform", function(d) {
- return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")";
- });
- }
- function redrawX(g) {
- g.select(".extent").attr("x", xExtent[0]);
- g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]);
- }
- function redrawY(g) {
- g.select(".extent").attr("y", yExtent[0]);
- g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]);
- }
- function brushstart() {
- var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(target), center, origin = d3.mouse(target), offset;
- var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup);
- if (d3.event.changedTouches) {
- w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
- } else {
- w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
- }
- g.interrupt().selectAll("*").interrupt();
- if (dragging) {
- origin[0] = xExtent[0] - origin[0];
- origin[1] = yExtent[0] - origin[1];
- } else if (resizing) {
- var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing);
- offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ];
- origin[0] = xExtent[ex];
- origin[1] = yExtent[ey];
- } else if (d3.event.altKey) center = origin.slice();
- g.style("pointer-events", "none").selectAll(".resize").style("display", null);
- d3.select("body").style("cursor", eventTarget.style("cursor"));
- event_({
- type: "brushstart"
- });
- brushmove();
- function keydown() {
- if (d3.event.keyCode == 32) {
- if (!dragging) {
- center = null;
- origin[0] -= xExtent[1];
- origin[1] -= yExtent[1];
- dragging = 2;
- }
- d3_eventPreventDefault();
- }
- }
- function keyup() {
- if (d3.event.keyCode == 32 && dragging == 2) {
- origin[0] += xExtent[1];
- origin[1] += yExtent[1];
- dragging = 0;
- d3_eventPreventDefault();
- }
- }
- function brushmove() {
- var point = d3.mouse(target), moved = false;
- if (offset) {
- point[0] += offset[0];
- point[1] += offset[1];
- }
- if (!dragging) {
- if (d3.event.altKey) {
- if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ];
- origin[0] = xExtent[+(point[0] < center[0])];
- origin[1] = yExtent[+(point[1] < center[1])];
- } else center = null;
- }
- if (resizingX && move1(point, x, 0)) {
- redrawX(g);
- moved = true;
- }
- if (resizingY && move1(point, y, 1)) {
- redrawY(g);
- moved = true;
- }
- if (moved) {
- redraw(g);
- event_({
- type: "brush",
- mode: dragging ? "move" : "resize"
- });
- }
- }
- function move1(point, scale, i) {
- var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max;
- if (dragging) {
- r0 -= position;
- r1 -= size + position;
- }
- min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i];
- if (dragging) {
- max = (min += position) + size;
- } else {
- if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
- if (position < min) {
- max = min;
- min = position;
- } else {
- max = position;
- }
- }
- if (extent[0] != min || extent[1] != max) {
- if (i) yExtentDomain = null; else xExtentDomain = null;
- extent[0] = min;
- extent[1] = max;
- return true;
- }
- }
- function brushend() {
- brushmove();
- g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
- d3.select("body").style("cursor", null);
- w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null);
- dragRestore();
- event_({
- type: "brushend"
- });
- }
- }
- brush.x = function(z) {
- if (!arguments.length) return x;
- x = z;
- resizes = d3_svg_brushResizes[!x << 1 | !y];
- return brush;
- };
- brush.y = function(z) {
- if (!arguments.length) return y;
- y = z;
- resizes = d3_svg_brushResizes[!x << 1 | !y];
- return brush;
- };
- brush.clamp = function(z) {
- if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null;
- if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z;
- return brush;
- };
- brush.extent = function(z) {
- var x0, x1, y0, y1, t;
- if (!arguments.length) {
- if (x) {
- if (xExtentDomain) {
- x0 = xExtentDomain[0], x1 = xExtentDomain[1];
- } else {
- x0 = xExtent[0], x1 = xExtent[1];
- if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
- if (x1 < x0) t = x0, x0 = x1, x1 = t;
- }
- }
- if (y) {
- if (yExtentDomain) {
- y0 = yExtentDomain[0], y1 = yExtentDomain[1];
- } else {
- y0 = yExtent[0], y1 = yExtent[1];
- if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
- if (y1 < y0) t = y0, y0 = y1, y1 = t;
- }
- }
- return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ];
- }
- if (x) {
- x0 = z[0], x1 = z[1];
- if (y) x0 = x0[0], x1 = x1[0];
- xExtentDomain = [ x0, x1 ];
- if (x.invert) x0 = x(x0), x1 = x(x1);
- if (x1 < x0) t = x0, x0 = x1, x1 = t;
- if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ];
- }
- if (y) {
- y0 = z[0], y1 = z[1];
- if (x) y0 = y0[1], y1 = y1[1];
- yExtentDomain = [ y0, y1 ];
- if (y.invert) y0 = y(y0), y1 = y(y1);
- if (y1 < y0) t = y0, y0 = y1, y1 = t;
- if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ];
- }
- return brush;
- };
- brush.clear = function() {
- if (!brush.empty()) {
- xExtent = [ 0, 0 ], yExtent = [ 0, 0 ];
- xExtentDomain = yExtentDomain = null;
- }
- return brush;
- };
- brush.empty = function() {
- return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1];
- };
- return d3.rebind(brush, event, "on");
- };
- var d3_svg_brushCursor = {
- n: "ns-resize",
- e: "ew-resize",
- s: "ns-resize",
- w: "ew-resize",
- nw: "nwse-resize",
- ne: "nesw-resize",
- se: "nwse-resize",
- sw: "nesw-resize"
- };
- var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ];
- var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat;
- var d3_time_formatUtc = d3_time_format.utc;
- var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ");
- d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso;
- function d3_time_formatIsoNative(date) {
- return date.toISOString();
- }
- d3_time_formatIsoNative.parse = function(string) {
- var date = new Date(string);
- return isNaN(date) ? null : date;
- };
- d3_time_formatIsoNative.toString = d3_time_formatIso.toString;
- d3_time.second = d3_time_interval(function(date) {
- return new d3_date(Math.floor(date / 1e3) * 1e3);
- }, function(date, offset) {
- date.setTime(date.getTime() + Math.floor(offset) * 1e3);
- }, function(date) {
- return date.getSeconds();
- });
- d3_time.seconds = d3_time.second.range;
- d3_time.seconds.utc = d3_time.second.utc.range;
- d3_time.minute = d3_time_interval(function(date) {
- return new d3_date(Math.floor(date / 6e4) * 6e4);
- }, function(date, offset) {
- date.setTime(date.getTime() + Math.floor(offset) * 6e4);
- }, function(date) {
- return date.getMinutes();
- });
- d3_time.minutes = d3_time.minute.range;
- d3_time.minutes.utc = d3_time.minute.utc.range;
- d3_time.hour = d3_time_interval(function(date) {
- var timezone = date.getTimezoneOffset() / 60;
- return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5);
- }, function(date, offset) {
- date.setTime(date.getTime() + Math.floor(offset) * 36e5);
- }, function(date) {
- return date.getHours();
- });
- d3_time.hours = d3_time.hour.range;
- d3_time.hours.utc = d3_time.hour.utc.range;
- d3_time.month = d3_time_interval(function(date) {
- date = d3_time.day(date);
- date.setDate(1);
- return date;
- }, function(date, offset) {
- date.setMonth(date.getMonth() + offset);
- }, function(date) {
- return date.getMonth();
- });
- d3_time.months = d3_time.month.range;
- d3_time.months.utc = d3_time.month.utc.range;
- function d3_time_scale(linear, methods, format) {
- function scale(x) {
- return linear(x);
- }
- scale.invert = function(x) {
- return d3_time_scaleDate(linear.invert(x));
- };
- scale.domain = function(x) {
- if (!arguments.length) return linear.domain().map(d3_time_scaleDate);
- linear.domain(x);
- return scale;
- };
- function tickMethod(extent, count) {
- var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target);
- return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) {
- return d / 31536e6;
- }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i];
- }
- scale.nice = function(interval, skip) {
- var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval);
- if (method) interval = method[0], skip = method[1];
- function skipped(date) {
- return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
- }
- return scale.domain(d3_scale_nice(domain, skip > 1 ? {
- floor: function(date) {
- while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
- return date;
- },
- ceil: function(date) {
- while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
- return date;
- }
- } : interval));
- };
- scale.ticks = function(interval, skip) {
- var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ {
- range: interval
- }, skip ];
- if (method) interval = method[0], skip = method[1];
- return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip);
- };
- scale.tickFormat = function() {
- return format;
- };
- scale.copy = function() {
- return d3_time_scale(linear.copy(), methods, format);
- };
- return d3_scale_linearRebind(scale, linear);
- }
- function d3_time_scaleDate(t) {
- return new Date(t);
- }
- var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ];
- var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ];
- var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) {
- return d.getMilliseconds();
- } ], [ ":%S", function(d) {
- return d.getSeconds();
- } ], [ "%I:%M", function(d) {
- return d.getMinutes();
- } ], [ "%I %p", function(d) {
- return d.getHours();
- } ], [ "%a %d", function(d) {
- return d.getDay() && d.getDate() != 1;
- } ], [ "%b %d", function(d) {
- return d.getDate() != 1;
- } ], [ "%B", function(d) {
- return d.getMonth();
- } ], [ "%Y", d3_true ] ]);
- var d3_time_scaleMilliseconds = {
- range: function(start, stop, step) {
- return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate);
- },
- floor: d3_identity,
- ceil: d3_identity
- };
- d3_time_scaleLocalMethods.year = d3_time.year;
- d3_time.scale = function() {
- return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat);
- };
- var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) {
- return [ m[0].utc, m[1] ];
- });
- var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) {
- return d.getUTCMilliseconds();
- } ], [ ":%S", function(d) {
- return d.getUTCSeconds();
- } ], [ "%I:%M", function(d) {
- return d.getUTCMinutes();
- } ], [ "%I %p", function(d) {
- return d.getUTCHours();
- } ], [ "%a %d", function(d) {
- return d.getUTCDay() && d.getUTCDate() != 1;
- } ], [ "%b %d", function(d) {
- return d.getUTCDate() != 1;
- } ], [ "%B", function(d) {
- return d.getUTCMonth();
- } ], [ "%Y", d3_true ] ]);
- d3_time_scaleUtcMethods.year = d3_time.year.utc;
- d3_time.scale.utc = function() {
- return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat);
- };
- d3.text = d3_xhrType(function(request) {
- return request.responseText;
- });
- d3.json = function(url, callback) {
- return d3_xhr(url, "application/json", d3_json, callback);
- };
- function d3_json(request) {
- return JSON.parse(request.responseText);
- }
- d3.html = function(url, callback) {
- return d3_xhr(url, "text/html", d3_html, callback);
- };
- function d3_html(request) {
- var range = d3_document.createRange();
- range.selectNode(d3_document.body);
- return range.createContextualFragment(request.responseText);
- }
- d3.xml = d3_xhrType(function(request) {
- return request.responseXML;
- });
- if (typeof define === "function" && define.amd) this.d3 = d3, define(d3); else if (typeof module === "object" && module.exports) module.exports = d3; else this.d3 = d3;
- }();
- },{}],17:[function(_dereq_,module,exports){
- (function (process,global){
- /*!
- * @overview es6-promise - a tiny implementation of Promises/A+.
- * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
- * @license Licensed under MIT license
- * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
- * @version 3.3.1
- */
-
- (function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- (global.ES6Promise = factory());
- }(this, (function () { 'use strict';
-
- function objectOrFunction(x) {
- return typeof x === 'function' || typeof x === 'object' && x !== null;
- }
-
- function isFunction(x) {
- return typeof x === 'function';
- }
-
- var _isArray = undefined;
- if (!Array.isArray) {
- _isArray = function (x) {
- return Object.prototype.toString.call(x) === '[object Array]';
- };
- } else {
- _isArray = Array.isArray;
- }
-
- var isArray = _isArray;
-
- var len = 0;
- var vertxNext = undefined;
- var customSchedulerFn = undefined;
-
- var asap = function asap(callback, arg) {
- queue[len] = callback;
- queue[len + 1] = arg;
- len += 2;
- if (len === 2) {
- // If len is 2, that means that we need to schedule an async flush.
- // If additional callbacks are queued before the queue is flushed, they
- // will be processed by this flush that we are scheduling.
- if (customSchedulerFn) {
- customSchedulerFn(flush);
- } else {
- scheduleFlush();
- }
- }
- };
-
- function setScheduler(scheduleFn) {
- customSchedulerFn = scheduleFn;
- }
-
- function setAsap(asapFn) {
- asap = asapFn;
- }
-
- var browserWindow = typeof window !== 'undefined' ? window : undefined;
- var browserGlobal = browserWindow || {};
- var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
- var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && ({}).toString.call(process) === '[object process]';
-
- // test for web worker but not in IE10
- var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
-
- // node
- function useNextTick() {
- // node version 0.10.x displays a deprecation warning when nextTick is used recursively
- // see https://github.com/cujojs/when/issues/410 for details
- return function () {
- return process.nextTick(flush);
- };
- }
-
- // vertx
- function useVertxTimer() {
- return function () {
- vertxNext(flush);
- };
- }
-
- function useMutationObserver() {
- var iterations = 0;
- var observer = new BrowserMutationObserver(flush);
- var node = document.createTextNode('');
- observer.observe(node, { characterData: true });
-
- return function () {
- node.data = iterations = ++iterations % 2;
- };
- }
-
- // web worker
- function useMessageChannel() {
- var channel = new MessageChannel();
- channel.port1.onmessage = flush;
- return function () {
- return channel.port2.postMessage(0);
- };
- }
-
- function useSetTimeout() {
- // Store setTimeout reference so es6-promise will be unaffected by
- // other code modifying setTimeout (like sinon.useFakeTimers())
- var globalSetTimeout = setTimeout;
- return function () {
- return globalSetTimeout(flush, 1);
- };
- }
-
- var queue = new Array(1000);
- function flush() {
- for (var i = 0; i < len; i += 2) {
- var callback = queue[i];
- var arg = queue[i + 1];
-
- callback(arg);
-
- queue[i] = undefined;
- queue[i + 1] = undefined;
- }
-
- len = 0;
- }
-
- function attemptVertx() {
- try {
- var r = _dereq_;
- var vertx = r('vertx');
- vertxNext = vertx.runOnLoop || vertx.runOnContext;
- return useVertxTimer();
- } catch (e) {
- return useSetTimeout();
- }
- }
-
- var scheduleFlush = undefined;
- // Decide what async method to use to triggering processing of queued callbacks:
- if (isNode) {
- scheduleFlush = useNextTick();
- } else if (BrowserMutationObserver) {
- scheduleFlush = useMutationObserver();
- } else if (isWorker) {
- scheduleFlush = useMessageChannel();
- } else if (browserWindow === undefined && typeof _dereq_ === 'function') {
- scheduleFlush = attemptVertx();
- } else {
- scheduleFlush = useSetTimeout();
- }
-
- function then(onFulfillment, onRejection) {
- var _arguments = arguments;
-
- var parent = this;
-
- var child = new this.constructor(noop);
-
- if (child[PROMISE_ID] === undefined) {
- makePromise(child);
- }
-
- var _state = parent._state;
-
- if (_state) {
- (function () {
- var callback = _arguments[_state - 1];
- asap(function () {
- return invokeCallback(_state, child, callback, parent._result);
- });
- })();
- } else {
- subscribe(parent, child, onFulfillment, onRejection);
- }
-
- return child;
- }
-
- /**
- `Promise.resolve` returns a promise that will become resolved with the
- passed `value`. It is shorthand for the following:
-
- ```javascript
- let promise = new Promise(function(resolve, reject){
- resolve(1);
- });
-
- promise.then(function(value){
- // value === 1
- });
- ```
-
- Instead of writing the above, your code now simply becomes the following:
-
- ```javascript
- let promise = Promise.resolve(1);
-
- promise.then(function(value){
- // value === 1
- });
- ```
-
- @method resolve
- @static
- @param {Any} value value that the returned promise will be resolved with
- Useful for tooling.
- @return {Promise} a promise that will become fulfilled with the given
- `value`
- */
- function resolve(object) {
- /*jshint validthis:true */
- var Constructor = this;
-
- if (object && typeof object === 'object' && object.constructor === Constructor) {
- return object;
- }
-
- var promise = new Constructor(noop);
- _resolve(promise, object);
- return promise;
- }
-
- var PROMISE_ID = Math.random().toString(36).substring(16);
-
- function noop() {}
-
- var PENDING = void 0;
- var FULFILLED = 1;
- var REJECTED = 2;
-
- var GET_THEN_ERROR = new ErrorObject();
-
- function selfFulfillment() {
- return new TypeError("You cannot resolve a promise with itself");
- }
-
- function cannotReturnOwn() {
- return new TypeError('A promises callback cannot return that same promise.');
- }
-
- function getThen(promise) {
- try {
- return promise.then;
- } catch (error) {
- GET_THEN_ERROR.error = error;
- return GET_THEN_ERROR;
- }
- }
-
- function tryThen(then, value, fulfillmentHandler, rejectionHandler) {
- try {
- then.call(value, fulfillmentHandler, rejectionHandler);
- } catch (e) {
- return e;
- }
- }
-
- function handleForeignThenable(promise, thenable, then) {
- asap(function (promise) {
- var sealed = false;
- var error = tryThen(then, thenable, function (value) {
- if (sealed) {
- return;
- }
- sealed = true;
- if (thenable !== value) {
- _resolve(promise, value);
- } else {
- fulfill(promise, value);
- }
- }, function (reason) {
- if (sealed) {
- return;
- }
- sealed = true;
-
- _reject(promise, reason);
- }, 'Settle: ' + (promise._label || ' unknown promise'));
-
- if (!sealed && error) {
- sealed = true;
- _reject(promise, error);
- }
- }, promise);
- }
-
- function handleOwnThenable(promise, thenable) {
- if (thenable._state === FULFILLED) {
- fulfill(promise, thenable._result);
- } else if (thenable._state === REJECTED) {
- _reject(promise, thenable._result);
- } else {
- subscribe(thenable, undefined, function (value) {
- return _resolve(promise, value);
- }, function (reason) {
- return _reject(promise, reason);
- });
- }
- }
-
- function handleMaybeThenable(promise, maybeThenable, then$$) {
- if (maybeThenable.constructor === promise.constructor && then$$ === then && maybeThenable.constructor.resolve === resolve) {
- handleOwnThenable(promise, maybeThenable);
- } else {
- if (then$$ === GET_THEN_ERROR) {
- _reject(promise, GET_THEN_ERROR.error);
- } else if (then$$ === undefined) {
- fulfill(promise, maybeThenable);
- } else if (isFunction(then$$)) {
- handleForeignThenable(promise, maybeThenable, then$$);
- } else {
- fulfill(promise, maybeThenable);
- }
- }
- }
-
- function _resolve(promise, value) {
- if (promise === value) {
- _reject(promise, selfFulfillment());
- } else if (objectOrFunction(value)) {
- handleMaybeThenable(promise, value, getThen(value));
- } else {
- fulfill(promise, value);
- }
- }
-
- function publishRejection(promise) {
- if (promise._onerror) {
- promise._onerror(promise._result);
- }
-
- publish(promise);
- }
-
- function fulfill(promise, value) {
- if (promise._state !== PENDING) {
- return;
- }
-
- promise._result = value;
- promise._state = FULFILLED;
-
- if (promise._subscribers.length !== 0) {
- asap(publish, promise);
- }
- }
-
- function _reject(promise, reason) {
- if (promise._state !== PENDING) {
- return;
- }
- promise._state = REJECTED;
- promise._result = reason;
-
- asap(publishRejection, promise);
- }
-
- function subscribe(parent, child, onFulfillment, onRejection) {
- var _subscribers = parent._subscribers;
- var length = _subscribers.length;
-
- parent._onerror = null;
-
- _subscribers[length] = child;
- _subscribers[length + FULFILLED] = onFulfillment;
- _subscribers[length + REJECTED] = onRejection;
-
- if (length === 0 && parent._state) {
- asap(publish, parent);
- }
- }
-
- function publish(promise) {
- var subscribers = promise._subscribers;
- var settled = promise._state;
-
- if (subscribers.length === 0) {
- return;
- }
-
- var child = undefined,
- callback = undefined,
- detail = promise._result;
-
- for (var i = 0; i < subscribers.length; i += 3) {
- child = subscribers[i];
- callback = subscribers[i + settled];
-
- if (child) {
- invokeCallback(settled, child, callback, detail);
- } else {
- callback(detail);
- }
- }
-
- promise._subscribers.length = 0;
- }
-
- function ErrorObject() {
- this.error = null;
- }
-
- var TRY_CATCH_ERROR = new ErrorObject();
-
- function tryCatch(callback, detail) {
- try {
- return callback(detail);
- } catch (e) {
- TRY_CATCH_ERROR.error = e;
- return TRY_CATCH_ERROR;
- }
- }
-
- function invokeCallback(settled, promise, callback, detail) {
- var hasCallback = isFunction(callback),
- value = undefined,
- error = undefined,
- succeeded = undefined,
- failed = undefined;
-
- if (hasCallback) {
- value = tryCatch(callback, detail);
-
- if (value === TRY_CATCH_ERROR) {
- failed = true;
- error = value.error;
- value = null;
- } else {
- succeeded = true;
- }
-
- if (promise === value) {
- _reject(promise, cannotReturnOwn());
- return;
- }
- } else {
- value = detail;
- succeeded = true;
- }
-
- if (promise._state !== PENDING) {
- // noop
- } else if (hasCallback && succeeded) {
- _resolve(promise, value);
- } else if (failed) {
- _reject(promise, error);
- } else if (settled === FULFILLED) {
- fulfill(promise, value);
- } else if (settled === REJECTED) {
- _reject(promise, value);
- }
- }
-
- function initializePromise(promise, resolver) {
- try {
- resolver(function resolvePromise(value) {
- _resolve(promise, value);
- }, function rejectPromise(reason) {
- _reject(promise, reason);
- });
- } catch (e) {
- _reject(promise, e);
- }
- }
-
- var id = 0;
- function nextId() {
- return id++;
- }
-
- function makePromise(promise) {
- promise[PROMISE_ID] = id++;
- promise._state = undefined;
- promise._result = undefined;
- promise._subscribers = [];
- }
-
- function Enumerator(Constructor, input) {
- this._instanceConstructor = Constructor;
- this.promise = new Constructor(noop);
-
- if (!this.promise[PROMISE_ID]) {
- makePromise(this.promise);
- }
-
- if (isArray(input)) {
- this._input = input;
- this.length = input.length;
- this._remaining = input.length;
-
- this._result = new Array(this.length);
-
- if (this.length === 0) {
- fulfill(this.promise, this._result);
- } else {
- this.length = this.length || 0;
- this._enumerate();
- if (this._remaining === 0) {
- fulfill(this.promise, this._result);
- }
- }
- } else {
- _reject(this.promise, validationError());
- }
- }
-
- function validationError() {
- return new Error('Array Methods must be provided an Array');
- };
-
- Enumerator.prototype._enumerate = function () {
- var length = this.length;
- var _input = this._input;
-
- for (var i = 0; this._state === PENDING && i < length; i++) {
- this._eachEntry(_input[i], i);
- }
- };
-
- Enumerator.prototype._eachEntry = function (entry, i) {
- var c = this._instanceConstructor;
- var resolve$$ = c.resolve;
-
- if (resolve$$ === resolve) {
- var _then = getThen(entry);
-
- if (_then === then && entry._state !== PENDING) {
- this._settledAt(entry._state, i, entry._result);
- } else if (typeof _then !== 'function') {
- this._remaining--;
- this._result[i] = entry;
- } else if (c === Promise) {
- var promise = new c(noop);
- handleMaybeThenable(promise, entry, _then);
- this._willSettleAt(promise, i);
- } else {
- this._willSettleAt(new c(function (resolve$$) {
- return resolve$$(entry);
- }), i);
- }
- } else {
- this._willSettleAt(resolve$$(entry), i);
- }
- };
-
- Enumerator.prototype._settledAt = function (state, i, value) {
- var promise = this.promise;
-
- if (promise._state === PENDING) {
- this._remaining--;
-
- if (state === REJECTED) {
- _reject(promise, value);
- } else {
- this._result[i] = value;
- }
- }
-
- if (this._remaining === 0) {
- fulfill(promise, this._result);
- }
- };
-
- Enumerator.prototype._willSettleAt = function (promise, i) {
- var enumerator = this;
-
- subscribe(promise, undefined, function (value) {
- return enumerator._settledAt(FULFILLED, i, value);
- }, function (reason) {
- return enumerator._settledAt(REJECTED, i, reason);
- });
- };
-
- /**
- `Promise.all` accepts an array of promises, and returns a new promise which
- is fulfilled with an array of fulfillment values for the passed promises, or
- rejected with the reason of the first passed promise to be rejected. It casts all
- elements of the passed iterable to promises as it runs this algorithm.
-
- Example:
-
- ```javascript
- let promise1 = resolve(1);
- let promise2 = resolve(2);
- let promise3 = resolve(3);
- let promises = [ promise1, promise2, promise3 ];
-
- Promise.all(promises).then(function(array){
- // The array here would be [ 1, 2, 3 ];
- });
- ```
-
- If any of the `promises` given to `all` are rejected, the first promise
- that is rejected will be given as an argument to the returned promises's
- rejection handler. For example:
-
- Example:
-
- ```javascript
- let promise1 = resolve(1);
- let promise2 = reject(new Error("2"));
- let promise3 = reject(new Error("3"));
- let promises = [ promise1, promise2, promise3 ];
-
- Promise.all(promises).then(function(array){
- // Code here never runs because there are rejected promises!
- }, function(error) {
- // error.message === "2"
- });
- ```
-
- @method all
- @static
- @param {Array} entries array of promises
- @param {String} label optional string for labeling the promise.
- Useful for tooling.
- @return {Promise} promise that is fulfilled when all `promises` have been
- fulfilled, or rejected if any of them become rejected.
- @static
- */
- function all(entries) {
- return new Enumerator(this, entries).promise;
- }
-
- /**
- `Promise.race` returns a new promise which is settled in the same way as the
- first passed promise to settle.
-
- Example:
-
- ```javascript
- let promise1 = new Promise(function(resolve, reject){
- setTimeout(function(){
- resolve('promise 1');
- }, 200);
- });
-
- let promise2 = new Promise(function(resolve, reject){
- setTimeout(function(){
- resolve('promise 2');
- }, 100);
- });
-
- Promise.race([promise1, promise2]).then(function(result){
- // result === 'promise 2' because it was resolved before promise1
- // was resolved.
- });
- ```
-
- `Promise.race` is deterministic in that only the state of the first
- settled promise matters. For example, even if other promises given to the
- `promises` array argument are resolved, but the first settled promise has
- become rejected before the other promises became fulfilled, the returned
- promise will become rejected:
-
- ```javascript
- let promise1 = new Promise(function(resolve, reject){
- setTimeout(function(){
- resolve('promise 1');
- }, 200);
- });
-
- let promise2 = new Promise(function(resolve, reject){
- setTimeout(function(){
- reject(new Error('promise 2'));
- }, 100);
- });
-
- Promise.race([promise1, promise2]).then(function(result){
- // Code here never runs
- }, function(reason){
- // reason.message === 'promise 2' because promise 2 became rejected before
- // promise 1 became fulfilled
- });
- ```
-
- An example real-world use case is implementing timeouts:
-
- ```javascript
- Promise.race([ajax('foo.json'), timeout(5000)])
- ```
-
- @method race
- @static
- @param {Array} promises array of promises to observe
- Useful for tooling.
- @return {Promise} a promise which settles in the same way as the first passed
- promise to settle.
- */
- function race(entries) {
- /*jshint validthis:true */
- var Constructor = this;
-
- if (!isArray(entries)) {
- return new Constructor(function (_, reject) {
- return reject(new TypeError('You must pass an array to race.'));
- });
- } else {
- return new Constructor(function (resolve, reject) {
- var length = entries.length;
- for (var i = 0; i < length; i++) {
- Constructor.resolve(entries[i]).then(resolve, reject);
- }
- });
- }
- }
-
- /**
- `Promise.reject` returns a promise rejected with the passed `reason`.
- It is shorthand for the following:
-
- ```javascript
- let promise = new Promise(function(resolve, reject){
- reject(new Error('WHOOPS'));
- });
-
- promise.then(function(value){
- // Code here doesn't run because the promise is rejected!
- }, function(reason){
- // reason.message === 'WHOOPS'
- });
- ```
-
- Instead of writing the above, your code now simply becomes the following:
-
- ```javascript
- let promise = Promise.reject(new Error('WHOOPS'));
-
- promise.then(function(value){
- // Code here doesn't run because the promise is rejected!
- }, function(reason){
- // reason.message === 'WHOOPS'
- });
- ```
-
- @method reject
- @static
- @param {Any} reason value that the returned promise will be rejected with.
- Useful for tooling.
- @return {Promise} a promise rejected with the given `reason`.
- */
- function reject(reason) {
- /*jshint validthis:true */
- var Constructor = this;
- var promise = new Constructor(noop);
- _reject(promise, reason);
- return promise;
- }
-
- function needsResolver() {
- throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
- }
-
- function needsNew() {
- throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
- }
-
- /**
- Promise objects represent the eventual result of an asynchronous operation. The
- primary way of interacting with a promise is through its `then` method, which
- registers callbacks to receive either a promise's eventual value or the reason
- why the promise cannot be fulfilled.
-
- Terminology
- -----------
-
- - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
- - `thenable` is an object or function that defines a `then` method.
- - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
- - `exception` is a value that is thrown using the throw statement.
- - `reason` is a value that indicates why a promise was rejected.
- - `settled` the final resting state of a promise, fulfilled or rejected.
-
- A promise can be in one of three states: pending, fulfilled, or rejected.
-
- Promises that are fulfilled have a fulfillment value and are in the fulfilled
- state. Promises that are rejected have a rejection reason and are in the
- rejected state. A fulfillment value is never a thenable.
-
- Promises can also be said to *resolve* a value. If this value is also a
- promise, then the original promise's settled state will match the value's
- settled state. So a promise that *resolves* a promise that rejects will
- itself reject, and a promise that *resolves* a promise that fulfills will
- itself fulfill.
-
-
- Basic Usage:
- ------------
-
- ```js
- let promise = new Promise(function(resolve, reject) {
- // on success
- resolve(value);
-
- // on failure
- reject(reason);
- });
-
- promise.then(function(value) {
- // on fulfillment
- }, function(reason) {
- // on rejection
- });
- ```
-
- Advanced Usage:
- ---------------
-
- Promises shine when abstracting away asynchronous interactions such as
- `XMLHttpRequest`s.
-
- ```js
- function getJSON(url) {
- return new Promise(function(resolve, reject){
- let xhr = new XMLHttpRequest();
-
- xhr.open('GET', url);
- xhr.onreadystatechange = handler;
- xhr.responseType = 'json';
- xhr.setRequestHeader('Accept', 'application/json');
- xhr.send();
-
- function handler() {
- if (this.readyState === this.DONE) {
- if (this.status === 200) {
- resolve(this.response);
- } else {
- reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
- }
- }
- };
- });
- }
-
- getJSON('/posts.json').then(function(json) {
- // on fulfillment
- }, function(reason) {
- // on rejection
- });
- ```
-
- Unlike callbacks, promises are great composable primitives.
-
- ```js
- Promise.all([
- getJSON('/posts'),
- getJSON('/comments')
- ]).then(function(values){
- values[0] // => postsJSON
- values[1] // => commentsJSON
-
- return values;
- });
- ```
-
- @class Promise
- @param {function} resolver
- Useful for tooling.
- @constructor
- */
- function Promise(resolver) {
- this[PROMISE_ID] = nextId();
- this._result = this._state = undefined;
- this._subscribers = [];
-
- if (noop !== resolver) {
- typeof resolver !== 'function' && needsResolver();
- this instanceof Promise ? initializePromise(this, resolver) : needsNew();
- }
- }
-
- Promise.all = all;
- Promise.race = race;
- Promise.resolve = resolve;
- Promise.reject = reject;
- Promise._setScheduler = setScheduler;
- Promise._setAsap = setAsap;
- Promise._asap = asap;
-
- Promise.prototype = {
- constructor: Promise,
-
- /**
- The primary way of interacting with a promise is through its `then` method,
- which registers callbacks to receive either a promise's eventual value or the
- reason why the promise cannot be fulfilled.
-
- ```js
- findUser().then(function(user){
- // user is available
- }, function(reason){
- // user is unavailable, and you are given the reason why
- });
- ```
-
- Chaining
- --------
-
- The return value of `then` is itself a promise. This second, 'downstream'
- promise is resolved with the return value of the first promise's fulfillment
- or rejection handler, or rejected if the handler throws an exception.
-
- ```js
- findUser().then(function (user) {
- return user.name;
- }, function (reason) {
- return 'default name';
- }).then(function (userName) {
- // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
- // will be `'default name'`
- });
-
- findUser().then(function (user) {
- throw new Error('Found user, but still unhappy');
- }, function (reason) {
- throw new Error('`findUser` rejected and we're unhappy');
- }).then(function (value) {
- // never reached
- }, function (reason) {
- // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
- // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
- });
- ```
- If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
-
- ```js
- findUser().then(function (user) {
- throw new PedagogicalException('Upstream error');
- }).then(function (value) {
- // never reached
- }).then(function (value) {
- // never reached
- }, function (reason) {
- // The `PedgagocialException` is propagated all the way down to here
- });
- ```
-
- Assimilation
- ------------
-
- Sometimes the value you want to propagate to a downstream promise can only be
- retrieved asynchronously. This can be achieved by returning a promise in the
- fulfillment or rejection handler. The downstream promise will then be pending
- until the returned promise is settled. This is called *assimilation*.
-
- ```js
- findUser().then(function (user) {
- return findCommentsByAuthor(user);
- }).then(function (comments) {
- // The user's comments are now available
- });
- ```
-
- If the assimliated promise rejects, then the downstream promise will also reject.
-
- ```js
- findUser().then(function (user) {
- return findCommentsByAuthor(user);
- }).then(function (comments) {
- // If `findCommentsByAuthor` fulfills, we'll have the value here
- }, function (reason) {
- // If `findCommentsByAuthor` rejects, we'll have the reason here
- });
- ```
-
- Simple Example
- --------------
-
- Synchronous Example
-
- ```javascript
- let result;
-
- try {
- result = findResult();
- // success
- } catch(reason) {
- // failure
- }
- ```
-
- Errback Example
-
- ```js
- findResult(function(result, err){
- if (err) {
- // failure
- } else {
- // success
- }
- });
- ```
-
- Promise Example;
-
- ```javascript
- findResult().then(function(result){
- // success
- }, function(reason){
- // failure
- });
- ```
-
- Advanced Example
- --------------
-
- Synchronous Example
-
- ```javascript
- let author, books;
-
- try {
- author = findAuthor();
- books = findBooksByAuthor(author);
- // success
- } catch(reason) {
- // failure
- }
- ```
-
- Errback Example
-
- ```js
-
- function foundBooks(books) {
-
- }
-
- function failure(reason) {
-
- }
-
- findAuthor(function(author, err){
- if (err) {
- failure(err);
- // failure
- } else {
- try {
- findBoooksByAuthor(author, function(books, err) {
- if (err) {
- failure(err);
- } else {
- try {
- foundBooks(books);
- } catch(reason) {
- failure(reason);
- }
- }
- });
- } catch(error) {
- failure(err);
- }
- // success
- }
- });
- ```
-
- Promise Example;
-
- ```javascript
- findAuthor().
- then(findBooksByAuthor).
- then(function(books){
- // found books
- }).catch(function(reason){
- // something went wrong
- });
- ```
-
- @method then
- @param {Function} onFulfilled
- @param {Function} onRejected
- Useful for tooling.
- @return {Promise}
- */
- then: then,
-
- /**
- `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
- as the catch block of a try/catch statement.
-
- ```js
- function findAuthor(){
- throw new Error('couldn't find that author');
- }
-
- // synchronous
- try {
- findAuthor();
- } catch(reason) {
- // something went wrong
- }
-
- // async with promises
- findAuthor().catch(function(reason){
- // something went wrong
- });
- ```
-
- @method catch
- @param {Function} onRejection
- Useful for tooling.
- @return {Promise}
- */
- 'catch': function _catch(onRejection) {
- return this.then(null, onRejection);
- }
- };
-
- function polyfill() {
- var local = undefined;
-
- if (typeof global !== 'undefined') {
- local = global;
- } else if (typeof self !== 'undefined') {
- local = self;
- } else {
- try {
- local = Function('return this')();
- } catch (e) {
- throw new Error('polyfill failed because global object is unavailable in this environment');
- }
- }
-
- var P = local.Promise;
-
- if (P) {
- var promiseToString = null;
- try {
- promiseToString = Object.prototype.toString.call(P.resolve());
- } catch (e) {
- // silently ignored
- }
-
- if (promiseToString === '[object Promise]' && !P.cast) {
- return;
- }
- }
-
- local.Promise = Promise;
- }
-
- polyfill();
- // Strange compat..
- Promise.polyfill = polyfill;
- Promise.Promise = Promise;
-
- return Promise;
-
- })));
-
- }).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
- },{"_process":33}],18:[function(_dereq_,module,exports){
- /**
- * inspired by is-number <https://github.com/jonschlinkert/is-number>
- * but significantly simplified and sped up by ignoring number and string constructors
- * ie these return false:
- * new Number(1)
- * new String('1')
- */
-
- 'use strict';
-
- var allBlankCharCodes = _dereq_('is-string-blank');
-
- module.exports = function(n) {
- var type = typeof n;
- if(type === 'string') {
- var original = n;
- n = +n;
- // whitespace strings cast to zero - filter them out
- if(n===0 && allBlankCharCodes(original)) return false;
- }
- else if(type !== 'number') return false;
-
- return n - n < 1;
- };
-
- },{"is-string-blank":23}],19:[function(_dereq_,module,exports){
- module.exports = fromQuat;
-
- /**
- * Creates a matrix from a quaternion rotation.
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {quat4} q Rotation quaternion
- * @returns {mat4} out
- */
- function fromQuat(out, q) {
- var x = q[0], y = q[1], z = q[2], w = q[3],
- x2 = x + x,
- y2 = y + y,
- z2 = z + z,
-
- xx = x * x2,
- yx = y * x2,
- yy = y * y2,
- zx = z * x2,
- zy = z * y2,
- zz = z * z2,
- wx = w * x2,
- wy = w * y2,
- wz = w * z2;
-
- out[0] = 1 - yy - zz;
- out[1] = yx + wz;
- out[2] = zx - wy;
- out[3] = 0;
-
- out[4] = yx - wz;
- out[5] = 1 - xx - zz;
- out[6] = zy + wx;
- out[7] = 0;
-
- out[8] = zx + wy;
- out[9] = zy - wx;
- out[10] = 1 - xx - yy;
- out[11] = 0;
-
- out[12] = 0;
- out[13] = 0;
- out[14] = 0;
- out[15] = 1;
-
- return out;
- };
- },{}],20:[function(_dereq_,module,exports){
- (function (global){
- 'use strict'
-
- var isBrowser = _dereq_('is-browser')
- var hasHover
-
- if (typeof global.matchMedia === 'function') {
- hasHover = !global.matchMedia('(hover: none)').matches
- }
- else {
- hasHover = isBrowser
- }
-
- module.exports = hasHover
-
- }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
- },{"is-browser":22}],21:[function(_dereq_,module,exports){
- 'use strict'
-
- var isBrowser = _dereq_('is-browser')
-
- function detect() {
- var supported = false
-
- try {
- var opts = Object.defineProperty({}, 'passive', {
- get: function() {
- supported = true
- }
- })
-
- window.addEventListener('test', null, opts)
- window.removeEventListener('test', null, opts)
- } catch(e) {
- supported = false
- }
-
- return supported
- }
-
- module.exports = isBrowser && detect()
-
- },{"is-browser":22}],22:[function(_dereq_,module,exports){
- module.exports = true;
- },{}],23:[function(_dereq_,module,exports){
- 'use strict';
-
- /**
- * Is this string all whitespace?
- * This solution kind of makes my brain hurt, but it's significantly faster
- * than !str.trim() or any other solution I could find.
- *
- * whitespace codes from: http://en.wikipedia.org/wiki/Whitespace_character
- * and verified with:
- *
- * for(var i = 0; i < 65536; i++) {
- * var s = String.fromCharCode(i);
- * if(+s===0 && !s.trim()) console.log(i, s);
- * }
- *
- * which counts a couple of these as *not* whitespace, but finds nothing else
- * that *is* whitespace. Note that charCodeAt stops at 16 bits, but it appears
- * that there are no whitespace characters above this, and code points above
- * this do not map onto white space characters.
- */
-
- module.exports = function(str){
- var l = str.length,
- a;
- for(var i = 0; i < l; i++) {
- a = str.charCodeAt(i);
- if((a < 9 || a > 13) && (a !== 32) && (a !== 133) && (a !== 160) &&
- (a !== 5760) && (a !== 6158) && (a < 8192 || a > 8205) &&
- (a !== 8232) && (a !== 8233) && (a !== 8239) && (a !== 8287) &&
- (a !== 8288) && (a !== 12288) && (a !== 65279)) {
- return false;
- }
- }
- return true;
- }
-
- },{}],24:[function(_dereq_,module,exports){
- var rootPosition = { left: 0, top: 0 }
-
- module.exports = mouseEventOffset
- function mouseEventOffset (ev, target, out) {
- target = target || ev.currentTarget || ev.srcElement
- if (!Array.isArray(out)) {
- out = [ 0, 0 ]
- }
- var cx = ev.clientX || 0
- var cy = ev.clientY || 0
- var rect = getBoundingClientOffset(target)
- out[0] = cx - rect.left
- out[1] = cy - rect.top
- return out
- }
-
- function getBoundingClientOffset (element) {
- if (element === window ||
- element === document ||
- element === document.body) {
- return rootPosition
- } else {
- return element.getBoundingClientRect()
- }
- }
-
- },{}],25:[function(_dereq_,module,exports){
- /*
- * @copyright 2016 Sean Connelly (@voidqk), http://syntheti.cc
- * @license MIT
- * @preserve Project Home: https://github.com/voidqk/polybooljs
- */
-
- var BuildLog = _dereq_('./lib/build-log');
- var Epsilon = _dereq_('./lib/epsilon');
- var Intersecter = _dereq_('./lib/intersecter');
- var SegmentChainer = _dereq_('./lib/segment-chainer');
- var SegmentSelector = _dereq_('./lib/segment-selector');
- var GeoJSON = _dereq_('./lib/geojson');
-
- var buildLog = false;
- var epsilon = Epsilon();
-
- var PolyBool;
- PolyBool = {
- // getter/setter for buildLog
- buildLog: function(bl){
- if (bl === true)
- buildLog = BuildLog();
- else if (bl === false)
- buildLog = false;
- return buildLog === false ? false : buildLog.list;
- },
- // getter/setter for epsilon
- epsilon: function(v){
- return epsilon.epsilon(v);
- },
-
- // core API
- segments: function(poly){
- var i = Intersecter(true, epsilon, buildLog);
- poly.regions.forEach(i.addRegion);
- return {
- segments: i.calculate(poly.inverted),
- inverted: poly.inverted
- };
- },
- combine: function(segments1, segments2){
- var i3 = Intersecter(false, epsilon, buildLog);
- return {
- combined: i3.calculate(
- segments1.segments, segments1.inverted,
- segments2.segments, segments2.inverted
- ),
- inverted1: segments1.inverted,
- inverted2: segments2.inverted
- };
- },
- selectUnion: function(combined){
- return {
- segments: SegmentSelector.union(combined.combined, buildLog),
- inverted: combined.inverted1 || combined.inverted2
- }
- },
- selectIntersect: function(combined){
- return {
- segments: SegmentSelector.intersect(combined.combined, buildLog),
- inverted: combined.inverted1 && combined.inverted2
- }
- },
- selectDifference: function(combined){
- return {
- segments: SegmentSelector.difference(combined.combined, buildLog),
- inverted: combined.inverted1 && !combined.inverted2
- }
- },
- selectDifferenceRev: function(combined){
- return {
- segments: SegmentSelector.differenceRev(combined.combined, buildLog),
- inverted: !combined.inverted1 && combined.inverted2
- }
- },
- selectXor: function(combined){
- return {
- segments: SegmentSelector.xor(combined.combined, buildLog),
- inverted: combined.inverted1 !== combined.inverted2
- }
- },
- polygon: function(segments){
- return {
- regions: SegmentChainer(segments.segments, epsilon, buildLog),
- inverted: segments.inverted
- };
- },
-
- // GeoJSON converters
- polygonFromGeoJSON: function(geojson){
- return GeoJSON.toPolygon(PolyBool, geojson);
- },
- polygonToGeoJSON: function(poly){
- return GeoJSON.fromPolygon(PolyBool, epsilon, poly);
- },
-
- // helper functions for common operations
- union: function(poly1, poly2){
- return operate(poly1, poly2, PolyBool.selectUnion);
- },
- intersect: function(poly1, poly2){
- return operate(poly1, poly2, PolyBool.selectIntersect);
- },
- difference: function(poly1, poly2){
- return operate(poly1, poly2, PolyBool.selectDifference);
- },
- differenceRev: function(poly1, poly2){
- return operate(poly1, poly2, PolyBool.selectDifferenceRev);
- },
- xor: function(poly1, poly2){
- return operate(poly1, poly2, PolyBool.selectXor);
- }
- };
-
- function operate(poly1, poly2, selector){
- var seg1 = PolyBool.segments(poly1);
- var seg2 = PolyBool.segments(poly2);
- var comb = PolyBool.combine(seg1, seg2);
- var seg3 = selector(comb);
- return PolyBool.polygon(seg3);
- }
-
- if (typeof window === 'object')
- window.PolyBool = PolyBool;
-
- module.exports = PolyBool;
-
- },{"./lib/build-log":26,"./lib/epsilon":27,"./lib/geojson":28,"./lib/intersecter":29,"./lib/segment-chainer":31,"./lib/segment-selector":32}],26:[function(_dereq_,module,exports){
- // (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
- // MIT License
- // Project Home: https://github.com/voidqk/polybooljs
-
- //
- // used strictly for logging the processing of the algorithm... only useful if you intend on
- // looking under the covers (for pretty UI's or debugging)
- //
-
- function BuildLog(){
- var my;
- var nextSegmentId = 0;
- var curVert = false;
-
- function push(type, data){
- my.list.push({
- type: type,
- data: data ? JSON.parse(JSON.stringify(data)) : void 0
- });
- return my;
- }
-
- my = {
- list: [],
- segmentId: function(){
- return nextSegmentId++;
- },
- checkIntersection: function(seg1, seg2){
- return push('check', { seg1: seg1, seg2: seg2 });
- },
- segmentChop: function(seg, end){
- push('div_seg', { seg: seg, pt: end });
- return push('chop', { seg: seg, pt: end });
- },
- statusRemove: function(seg){
- return push('pop_seg', { seg: seg });
- },
- segmentUpdate: function(seg){
- return push('seg_update', { seg: seg });
- },
- segmentNew: function(seg, primary){
- return push('new_seg', { seg: seg, primary: primary });
- },
- segmentRemove: function(seg){
- return push('rem_seg', { seg: seg });
- },
- tempStatus: function(seg, above, below){
- return push('temp_status', { seg: seg, above: above, below: below });
- },
- rewind: function(seg){
- return push('rewind', { seg: seg });
- },
- status: function(seg, above, below){
- return push('status', { seg: seg, above: above, below: below });
- },
- vert: function(x){
- if (x === curVert)
- return my;
- curVert = x;
- return push('vert', { x: x });
- },
- log: function(data){
- if (typeof data !== 'string')
- data = JSON.stringify(data, false, ' ');
- return push('log', { txt: data });
- },
- reset: function(){
- return push('reset');
- },
- selected: function(segs){
- return push('selected', { segs: segs });
- },
- chainStart: function(seg){
- return push('chain_start', { seg: seg });
- },
- chainRemoveHead: function(index, pt){
- return push('chain_rem_head', { index: index, pt: pt });
- },
- chainRemoveTail: function(index, pt){
- return push('chain_rem_tail', { index: index, pt: pt });
- },
- chainNew: function(pt1, pt2){
- return push('chain_new', { pt1: pt1, pt2: pt2 });
- },
- chainMatch: function(index){
- return push('chain_match', { index: index });
- },
- chainClose: function(index){
- return push('chain_close', { index: index });
- },
- chainAddHead: function(index, pt){
- return push('chain_add_head', { index: index, pt: pt });
- },
- chainAddTail: function(index, pt){
- return push('chain_add_tail', { index: index, pt: pt, });
- },
- chainConnect: function(index1, index2){
- return push('chain_con', { index1: index1, index2: index2 });
- },
- chainReverse: function(index){
- return push('chain_rev', { index: index });
- },
- chainJoin: function(index1, index2){
- return push('chain_join', { index1: index1, index2: index2 });
- },
- done: function(){
- return push('done');
- }
- };
- return my;
- }
-
- module.exports = BuildLog;
-
- },{}],27:[function(_dereq_,module,exports){
- // (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
- // MIT License
- // Project Home: https://github.com/voidqk/polybooljs
-
- //
- // provides the raw computation functions that takes epsilon into account
- //
- // zero is defined to be between (-epsilon, epsilon) exclusive
- //
-
- function Epsilon(eps){
- if (typeof eps !== 'number')
- eps = 0.0000000001; // sane default? sure why not
- var my = {
- epsilon: function(v){
- if (typeof v === 'number')
- eps = v;
- return eps;
- },
- pointAboveOrOnLine: function(pt, left, right){
- var Ax = left[0];
- var Ay = left[1];
- var Bx = right[0];
- var By = right[1];
- var Cx = pt[0];
- var Cy = pt[1];
- return (Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax) >= -eps;
- },
- pointBetween: function(p, left, right){
- // p must be collinear with left->right
- // returns false if p == left, p == right, or left == right
- var d_py_ly = p[1] - left[1];
- var d_rx_lx = right[0] - left[0];
- var d_px_lx = p[0] - left[0];
- var d_ry_ly = right[1] - left[1];
-
- var dot = d_px_lx * d_rx_lx + d_py_ly * d_ry_ly;
- // if `dot` is 0, then `p` == `left` or `left` == `right` (reject)
- // if `dot` is less than 0, then `p` is to the left of `left` (reject)
- if (dot < eps)
- return false;
-
- var sqlen = d_rx_lx * d_rx_lx + d_ry_ly * d_ry_ly;
- // if `dot` > `sqlen`, then `p` is to the right of `right` (reject)
- // therefore, if `dot - sqlen` is greater than 0, then `p` is to the right of `right` (reject)
- if (dot - sqlen > -eps)
- return false;
-
- return true;
- },
- pointsSameX: function(p1, p2){
- return Math.abs(p1[0] - p2[0]) < eps;
- },
- pointsSameY: function(p1, p2){
- return Math.abs(p1[1] - p2[1]) < eps;
- },
- pointsSame: function(p1, p2){
- return my.pointsSameX(p1, p2) && my.pointsSameY(p1, p2);
- },
- pointsCompare: function(p1, p2){
- // returns -1 if p1 is smaller, 1 if p2 is smaller, 0 if equal
- if (my.pointsSameX(p1, p2))
- return my.pointsSameY(p1, p2) ? 0 : (p1[1] < p2[1] ? -1 : 1);
- return p1[0] < p2[0] ? -1 : 1;
- },
- pointsCollinear: function(pt1, pt2, pt3){
- // does pt1->pt2->pt3 make a straight line?
- // essentially this is just checking to see if the slope(pt1->pt2) === slope(pt2->pt3)
- // if slopes are equal, then they must be collinear, because they share pt2
- var dx1 = pt1[0] - pt2[0];
- var dy1 = pt1[1] - pt2[1];
- var dx2 = pt2[0] - pt3[0];
- var dy2 = pt2[1] - pt3[1];
- return Math.abs(dx1 * dy2 - dx2 * dy1) < eps;
- },
- linesIntersect: function(a0, a1, b0, b1){
- // returns false if the lines are coincident (e.g., parallel or on top of each other)
- //
- // returns an object if the lines intersect:
- // {
- // pt: [x, y], where the intersection point is at
- // alongA: where intersection point is along A,
- // alongB: where intersection point is along B
- // }
- //
- // alongA and alongB will each be one of: -2, -1, 0, 1, 2
- //
- // with the following meaning:
- //
- // -2 intersection point is before segment's first point
- // -1 intersection point is directly on segment's first point
- // 0 intersection point is between segment's first and second points (exclusive)
- // 1 intersection point is directly on segment's second point
- // 2 intersection point is after segment's second point
- var adx = a1[0] - a0[0];
- var ady = a1[1] - a0[1];
- var bdx = b1[0] - b0[0];
- var bdy = b1[1] - b0[1];
-
- var axb = adx * bdy - ady * bdx;
- if (Math.abs(axb) < eps)
- return false; // lines are coincident
-
- var dx = a0[0] - b0[0];
- var dy = a0[1] - b0[1];
-
- var A = (bdx * dy - bdy * dx) / axb;
- var B = (adx * dy - ady * dx) / axb;
-
- var ret = {
- alongA: 0,
- alongB: 0,
- pt: [
- a0[0] + A * adx,
- a0[1] + A * ady
- ]
- };
-
- // categorize where intersection point is along A and B
-
- if (A <= -eps)
- ret.alongA = -2;
- else if (A < eps)
- ret.alongA = -1;
- else if (A - 1 <= -eps)
- ret.alongA = 0;
- else if (A - 1 < eps)
- ret.alongA = 1;
- else
- ret.alongA = 2;
-
- if (B <= -eps)
- ret.alongB = -2;
- else if (B < eps)
- ret.alongB = -1;
- else if (B - 1 <= -eps)
- ret.alongB = 0;
- else if (B - 1 < eps)
- ret.alongB = 1;
- else
- ret.alongB = 2;
-
- return ret;
- },
- pointInsideRegion: function(pt, region){
- var x = pt[0];
- var y = pt[1];
- var last_x = region[region.length - 1][0];
- var last_y = region[region.length - 1][1];
- var inside = false;
- for (var i = 0; i < region.length; i++){
- var curr_x = region[i][0];
- var curr_y = region[i][1];
-
- // if y is between curr_y and last_y, and
- // x is to the right of the boundary created by the line
- if ((curr_y - y > eps) != (last_y - y > eps) &&
- (last_x - curr_x) * (y - curr_y) / (last_y - curr_y) + curr_x - x > eps)
- inside = !inside
-
- last_x = curr_x;
- last_y = curr_y;
- }
- return inside;
- }
- };
- return my;
- }
-
- module.exports = Epsilon;
-
- },{}],28:[function(_dereq_,module,exports){
- // (c) Copyright 2017, Sean Connelly (@voidqk), http://syntheti.cc
- // MIT License
- // Project Home: https://github.com/voidqk/polybooljs
-
- //
- // convert between PolyBool polygon format and GeoJSON formats (Polygon and MultiPolygon)
- //
-
- var GeoJSON = {
- // convert a GeoJSON object to a PolyBool polygon
- toPolygon: function(PolyBool, geojson){
-
- // converts list of LineString's to segments
- function GeoPoly(coords){
- // check for empty coords
- if (coords.length <= 0)
- return PolyBool.segments({ inverted: false, regions: [] });
-
- // convert LineString to segments
- function LineString(ls){
- // remove tail which should be the same as head
- var reg = ls.slice(0, ls.length - 1);
- return PolyBool.segments({ inverted: false, regions: [reg] });
- }
-
- // the first LineString is considered the outside
- var out = LineString(coords[0]);
-
- // the rest of the LineStrings are considered interior holes, so subtract them from the
- // current result
- for (var i = 1; i < coords.length; i++)
- out = PolyBool.selectDifference(PolyBool.combine(out, LineString(coords[i])));
-
- return out;
- }
-
- if (geojson.type === 'Polygon'){
- // single polygon, so just convert it and we're done
- return PolyBool.polygon(GeoPoly(geojson.coordinates));
- }
- else if (geojson.type === 'MultiPolygon'){
- // multiple polygons, so union all the polygons together
- var out = PolyBool.segments({ inverted: false, regions: [] });
- for (var i = 0; i < geojson.coordinates.length; i++)
- out = PolyBool.selectUnion(PolyBool.combine(out, GeoPoly(geojson.coordinates[i])));
- return PolyBool.polygon(out);
- }
- throw new Error('PolyBool: Cannot convert GeoJSON object to PolyBool polygon');
- },
-
- // convert a PolyBool polygon to a GeoJSON object
- fromPolygon: function(PolyBool, eps, poly){
- // make sure out polygon is clean
- poly = PolyBool.polygon(PolyBool.segments(poly));
-
- // test if r1 is inside r2
- function regionInsideRegion(r1, r2){
- // we're guaranteed no lines intersect (because the polygon is clean), but a vertex
- // could be on the edge -- so we just average pt[0] and pt[1] to produce a point on the
- // edge of the first line, which cannot be on an edge
- return eps.pointInsideRegion([
- (r1[0][0] + r1[1][0]) * 0.5,
- (r1[0][1] + r1[1][1]) * 0.5
- ], r2);
- }
-
- // calculate inside heirarchy
- //
- // _____________________ _______ roots -> A -> F
- // | A | | F | | |
- // | _______ _______ | | ___ | +-- B +-- G
- // | | B | | C | | | | | | | |
- // | | ___ | | ___ | | | | | | | +-- D
- // | | | D | | | | E | | | | | G | | |
- // | | |___| | | |___| | | | | | | +-- C
- // | |_______| |_______| | | |___| | |
- // |_____________________| |_______| +-- E
-
- function newNode(region){
- return {
- region: region,
- children: []
- };
- }
-
- var roots = newNode(null);
-
- function addChild(root, region){
- // first check if we're inside any children
- for (var i = 0; i < root.children.length; i++){
- var child = root.children[i];
- if (regionInsideRegion(region, child.region)){
- // we are, so insert inside them instead
- addChild(child, region);
- return;
- }
- }
-
- // not inside any children, so check to see if any children are inside us
- var node = newNode(region);
- for (var i = 0; i < root.children.length; i++){
- var child = root.children[i];
- if (regionInsideRegion(child.region, region)){
- // oops... move the child beneath us, and remove them from root
- node.children.push(child);
- root.children.splice(i, 1);
- i--;
- }
- }
-
- // now we can add ourselves
- root.children.push(node);
- }
-
- // add all regions to the root
- for (var i = 0; i < poly.regions.length; i++){
- var region = poly.regions[i];
- if (region.length < 3) // regions must have at least 3 points (sanity check)
- continue;
- addChild(roots, region);
- }
-
- // with our heirarchy, we can distinguish between exterior borders, and interior holes
- // the root nodes are exterior, children are interior, children's children are exterior,
- // children's children's children are interior, etc
-
- // while we're at it, exteriors are counter-clockwise, and interiors are clockwise
-
- function forceWinding(region, clockwise){
- // first, see if we're clockwise or counter-clockwise
- // https://en.wikipedia.org/wiki/Shoelace_formula
- var winding = 0;
- var last_x = region[region.length - 1][0];
- var last_y = region[region.length - 1][1];
- var copy = [];
- for (var i = 0; i < region.length; i++){
- var curr_x = region[i][0];
- var curr_y = region[i][1];
- copy.push([curr_x, curr_y]); // create a copy while we're at it
- winding += curr_y * last_x - curr_x * last_y;
- last_x = curr_x;
- last_y = curr_y;
- }
- // this assumes Cartesian coordinates (Y is positive going up)
- var isclockwise = winding < 0;
- if (isclockwise !== clockwise)
- copy.reverse();
- // while we're here, the last point must be the first point...
- copy.push([copy[0][0], copy[0][1]]);
- return copy;
- }
-
- var geopolys = [];
-
- function addExterior(node){
- var poly = [forceWinding(node.region, false)];
- geopolys.push(poly);
- // children of exteriors are interior
- for (var i = 0; i < node.children.length; i++)
- poly.push(getInterior(node.children[i]));
- }
-
- function getInterior(node){
- // children of interiors are exterior
- for (var i = 0; i < node.children.length; i++)
- addExterior(node.children[i]);
- // return the clockwise interior
- return forceWinding(node.region, true);
- }
-
- // root nodes are exterior
- for (var i = 0; i < roots.children.length; i++)
- addExterior(roots.children[i]);
-
- // lastly, construct the approrpriate GeoJSON object
-
- if (geopolys.length <= 0) // empty GeoJSON Polygon
- return { type: 'Polygon', coordinates: [] };
- if (geopolys.length == 1) // use a GeoJSON Polygon
- return { type: 'Polygon', coordinates: geopolys[0] };
- return { // otherwise, use a GeoJSON MultiPolygon
- type: 'MultiPolygon',
- coordinates: geopolys
- };
- }
- };
-
- module.exports = GeoJSON;
-
- },{}],29:[function(_dereq_,module,exports){
- // (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
- // MIT License
- // Project Home: https://github.com/voidqk/polybooljs
-
- //
- // this is the core work-horse
- //
-
- var LinkedList = _dereq_('./linked-list');
-
- function Intersecter(selfIntersection, eps, buildLog){
- // selfIntersection is true/false depending on the phase of the overall algorithm
-
- //
- // segment creation
- //
-
- function segmentNew(start, end){
- return {
- id: buildLog ? buildLog.segmentId() : -1,
- start: start,
- end: end,
- myFill: {
- above: null, // is there fill above us?
- below: null // is there fill below us?
- },
- otherFill: null
- };
- }
-
- function segmentCopy(start, end, seg){
- return {
- id: buildLog ? buildLog.segmentId() : -1,
- start: start,
- end: end,
- myFill: {
- above: seg.myFill.above,
- below: seg.myFill.below
- },
- otherFill: null
- };
- }
-
- //
- // event logic
- //
-
- var event_root = LinkedList.create();
-
- function eventCompare(p1_isStart, p1_1, p1_2, p2_isStart, p2_1, p2_2){
- // compare the selected points first
- var comp = eps.pointsCompare(p1_1, p2_1);
- if (comp !== 0)
- return comp;
- // the selected points are the same
-
- if (eps.pointsSame(p1_2, p2_2)) // if the non-selected points are the same too...
- return 0; // then the segments are equal
-
- if (p1_isStart !== p2_isStart) // if one is a start and the other isn't...
- return p1_isStart ? 1 : -1; // favor the one that isn't the start
-
- // otherwise, we'll have to calculate which one is below the other manually
- return eps.pointAboveOrOnLine(p1_2,
- p2_isStart ? p2_1 : p2_2, // order matters
- p2_isStart ? p2_2 : p2_1
- ) ? 1 : -1;
- }
-
- function eventAdd(ev, other_pt){
- event_root.insertBefore(ev, function(here){
- // should ev be inserted before here?
- var comp = eventCompare(
- ev .isStart, ev .pt, other_pt,
- here.isStart, here.pt, here.other.pt
- );
- return comp < 0;
- });
- }
-
- function eventAddSegmentStart(seg, primary){
- var ev_start = LinkedList.node({
- isStart: true,
- pt: seg.start,
- seg: seg,
- primary: primary,
- other: null,
- status: null
- });
- eventAdd(ev_start, seg.end);
- return ev_start;
- }
-
- function eventAddSegmentEnd(ev_start, seg, primary){
- var ev_end = LinkedList.node({
- isStart: false,
- pt: seg.end,
- seg: seg,
- primary: primary,
- other: ev_start,
- status: null
- });
- ev_start.other = ev_end;
- eventAdd(ev_end, ev_start.pt);
- }
-
- function eventAddSegment(seg, primary){
- var ev_start = eventAddSegmentStart(seg, primary);
- eventAddSegmentEnd(ev_start, seg, primary);
- return ev_start;
- }
-
- function eventUpdateEnd(ev, end){
- // slides an end backwards
- // (start)------------(end) to:
- // (start)---(end)
-
- if (buildLog)
- buildLog.segmentChop(ev.seg, end);
-
- ev.other.remove();
- ev.seg.end = end;
- ev.other.pt = end;
- eventAdd(ev.other, ev.pt);
- }
-
- function eventDivide(ev, pt){
- var ns = segmentCopy(pt, ev.seg.end, ev.seg);
- eventUpdateEnd(ev, pt);
- return eventAddSegment(ns, ev.primary);
- }
-
- function calculate(primaryPolyInverted, secondaryPolyInverted){
- // if selfIntersection is true then there is no secondary polygon, so that isn't used
-
- //
- // status logic
- //
-
- var status_root = LinkedList.create();
-
- function statusCompare(ev1, ev2){
- var a1 = ev1.seg.start;
- var a2 = ev1.seg.end;
- var b1 = ev2.seg.start;
- var b2 = ev2.seg.end;
-
- if (eps.pointsCollinear(a1, b1, b2)){
- if (eps.pointsCollinear(a2, b1, b2))
- return 1;//eventCompare(true, a1, a2, true, b1, b2);
- return eps.pointAboveOrOnLine(a2, b1, b2) ? 1 : -1;
- }
- return eps.pointAboveOrOnLine(a1, b1, b2) ? 1 : -1;
- }
-
- function statusFindSurrounding(ev){
- return status_root.findTransition(function(here){
- var comp = statusCompare(ev, here.ev);
- return comp > 0;
- });
- }
-
- function checkIntersection(ev1, ev2){
- // returns the segment equal to ev1, or false if nothing equal
-
- var seg1 = ev1.seg;
- var seg2 = ev2.seg;
- var a1 = seg1.start;
- var a2 = seg1.end;
- var b1 = seg2.start;
- var b2 = seg2.end;
-
- if (buildLog)
- buildLog.checkIntersection(seg1, seg2);
-
- var i = eps.linesIntersect(a1, a2, b1, b2);
-
- if (i === false){
- // segments are parallel or coincident
-
- // if points aren't collinear, then the segments are parallel, so no intersections
- if (!eps.pointsCollinear(a1, a2, b1))
- return false;
- // otherwise, segments are on top of each other somehow (aka coincident)
-
- if (eps.pointsSame(a1, b2) || eps.pointsSame(a2, b1))
- return false; // segments touch at endpoints... no intersection
-
- var a1_equ_b1 = eps.pointsSame(a1, b1);
- var a2_equ_b2 = eps.pointsSame(a2, b2);
-
- if (a1_equ_b1 && a2_equ_b2)
- return ev2; // segments are exactly equal
-
- var a1_between = !a1_equ_b1 && eps.pointBetween(a1, b1, b2);
- var a2_between = !a2_equ_b2 && eps.pointBetween(a2, b1, b2);
-
- // handy for debugging:
- // buildLog.log({
- // a1_equ_b1: a1_equ_b1,
- // a2_equ_b2: a2_equ_b2,
- // a1_between: a1_between,
- // a2_between: a2_between
- // });
-
- if (a1_equ_b1){
- if (a2_between){
- // (a1)---(a2)
- // (b1)----------(b2)
- eventDivide(ev2, a2);
- }
- else{
- // (a1)----------(a2)
- // (b1)---(b2)
- eventDivide(ev1, b2);
- }
- return ev2;
- }
- else if (a1_between){
- if (!a2_equ_b2){
- // make a2 equal to b2
- if (a2_between){
- // (a1)---(a2)
- // (b1)-----------------(b2)
- eventDivide(ev2, a2);
- }
- else{
- // (a1)----------(a2)
- // (b1)----------(b2)
- eventDivide(ev1, b2);
- }
- }
-
- // (a1)---(a2)
- // (b1)----------(b2)
- eventDivide(ev2, a1);
- }
- }
- else{
- // otherwise, lines intersect at i.pt, which may or may not be between the endpoints
-
- // is A divided between its endpoints? (exclusive)
- if (i.alongA === 0){
- if (i.alongB === -1) // yes, at exactly b1
- eventDivide(ev1, b1);
- else if (i.alongB === 0) // yes, somewhere between B's endpoints
- eventDivide(ev1, i.pt);
- else if (i.alongB === 1) // yes, at exactly b2
- eventDivide(ev1, b2);
- }
-
- // is B divided between its endpoints? (exclusive)
- if (i.alongB === 0){
- if (i.alongA === -1) // yes, at exactly a1
- eventDivide(ev2, a1);
- else if (i.alongA === 0) // yes, somewhere between A's endpoints (exclusive)
- eventDivide(ev2, i.pt);
- else if (i.alongA === 1) // yes, at exactly a2
- eventDivide(ev2, a2);
- }
- }
- return false;
- }
-
- //
- // main event loop
- //
- var segments = [];
- while (!event_root.isEmpty()){
- var ev = event_root.getHead();
-
- if (buildLog)
- buildLog.vert(ev.pt[0]);
-
- if (ev.isStart){
-
- if (buildLog)
- buildLog.segmentNew(ev.seg, ev.primary);
-
- var surrounding = statusFindSurrounding(ev);
- var above = surrounding.before ? surrounding.before.ev : null;
- var below = surrounding.after ? surrounding.after.ev : null;
-
- if (buildLog){
- buildLog.tempStatus(
- ev.seg,
- above ? above.seg : false,
- below ? below.seg : false
- );
- }
-
- function checkBothIntersections(){
- if (above){
- var eve = checkIntersection(ev, above);
- if (eve)
- return eve;
- }
- if (below)
- return checkIntersection(ev, below);
- return false;
- }
-
- var eve = checkBothIntersections();
- if (eve){
- // ev and eve are equal
- // we'll keep eve and throw away ev
-
- // merge ev.seg's fill information into eve.seg
-
- if (selfIntersection){
- var toggle; // are we a toggling edge?
- if (ev.seg.myFill.below === null)
- toggle = true;
- else
- toggle = ev.seg.myFill.above !== ev.seg.myFill.below;
-
- // merge two segments that belong to the same polygon
- // think of this as sandwiching two segments together, where `eve.seg` is
- // the bottom -- this will cause the above fill flag to toggle
- if (toggle)
- eve.seg.myFill.above = !eve.seg.myFill.above;
- }
- else{
- // merge two segments that belong to different polygons
- // each segment has distinct knowledge, so no special logic is needed
- // note that this can only happen once per segment in this phase, because we
- // are guaranteed that all self-intersections are gone
- eve.seg.otherFill = ev.seg.myFill;
- }
-
- if (buildLog)
- buildLog.segmentUpdate(eve.seg);
-
- ev.other.remove();
- ev.remove();
- }
-
- if (event_root.getHead() !== ev){
- // something was inserted before us in the event queue, so loop back around and
- // process it before continuing
- if (buildLog)
- buildLog.rewind(ev.seg);
- continue;
- }
-
- //
- // calculate fill flags
- //
- if (selfIntersection){
- var toggle; // are we a toggling edge?
- if (ev.seg.myFill.below === null) // if we are a new segment...
- toggle = true; // then we toggle
- else // we are a segment that has previous knowledge from a division
- toggle = ev.seg.myFill.above !== ev.seg.myFill.below; // calculate toggle
-
- // next, calculate whether we are filled below us
- if (!below){ // if nothing is below us...
- // we are filled below us if the polygon is inverted
- ev.seg.myFill.below = primaryPolyInverted;
- }
- else{
- // otherwise, we know the answer -- it's the same if whatever is below
- // us is filled above it
- ev.seg.myFill.below = below.seg.myFill.above;
- }
-
- // since now we know if we're filled below us, we can calculate whether
- // we're filled above us by applying toggle to whatever is below us
- if (toggle)
- ev.seg.myFill.above = !ev.seg.myFill.below;
- else
- ev.seg.myFill.above = ev.seg.myFill.below;
- }
- else{
- // now we fill in any missing transition information, since we are all-knowing
- // at this point
-
- if (ev.seg.otherFill === null){
- // if we don't have other information, then we need to figure out if we're
- // inside the other polygon
- var inside;
- if (!below){
- // if nothing is below us, then we're inside if the other polygon is
- // inverted
- inside =
- ev.primary ? secondaryPolyInverted : primaryPolyInverted;
- }
- else{ // otherwise, something is below us
- // so copy the below segment's other polygon's above
- if (ev.primary === below.primary)
- inside = below.seg.otherFill.above;
- else
- inside = below.seg.myFill.above;
- }
- ev.seg.otherFill = {
- above: inside,
- below: inside
- };
- }
- }
-
- if (buildLog){
- buildLog.status(
- ev.seg,
- above ? above.seg : false,
- below ? below.seg : false
- );
- }
-
- // insert the status and remember it for later removal
- ev.other.status = surrounding.insert(LinkedList.node({ ev: ev }));
- }
- else{
- var st = ev.status;
-
- if (st === null){
- throw new Error('PolyBool: Zero-length segment detected; your epsilon is ' +
- 'probably too small or too large');
- }
-
- // removing the status will create two new adjacent edges, so we'll need to check
- // for those
- if (status_root.exists(st.prev) && status_root.exists(st.next))
- checkIntersection(st.prev.ev, st.next.ev);
-
- if (buildLog)
- buildLog.statusRemove(st.ev.seg);
-
- // remove the status
- st.remove();
-
- // if we've reached this point, we've calculated everything there is to know, so
- // save the segment for reporting
- if (!ev.primary){
- // make sure `seg.myFill` actually points to the primary polygon though
- var s = ev.seg.myFill;
- ev.seg.myFill = ev.seg.otherFill;
- ev.seg.otherFill = s;
- }
- segments.push(ev.seg);
- }
-
- // remove the event and continue
- event_root.getHead().remove();
- }
-
- if (buildLog)
- buildLog.done();
-
- return segments;
- }
-
- // return the appropriate API depending on what we're doing
- if (!selfIntersection){
- // performing combination of polygons, so only deal with already-processed segments
- return {
- calculate: function(segments1, inverted1, segments2, inverted2){
- // segmentsX come from the self-intersection API, or this API
- // invertedX is whether we treat that list of segments as an inverted polygon or not
- // returns segments that can be used for further operations
- segments1.forEach(function(seg){
- eventAddSegment(segmentCopy(seg.start, seg.end, seg), true);
- });
- segments2.forEach(function(seg){
- eventAddSegment(segmentCopy(seg.start, seg.end, seg), false);
- });
- return calculate(inverted1, inverted2);
- }
- };
- }
-
- // otherwise, performing self-intersection, so deal with regions
- return {
- addRegion: function(region){
- // regions are a list of points:
- // [ [0, 0], [100, 0], [50, 100] ]
- // you can add multiple regions before running calculate
- var pt1;
- var pt2 = region[region.length - 1];
- for (var i = 0; i < region.length; i++){
- pt1 = pt2;
- pt2 = region[i];
-
- var forward = eps.pointsCompare(pt1, pt2);
- if (forward === 0) // points are equal, so we have a zero-length segment
- continue; // just skip it
-
- eventAddSegment(
- segmentNew(
- forward < 0 ? pt1 : pt2,
- forward < 0 ? pt2 : pt1
- ),
- true
- );
- }
- },
- calculate: function(inverted){
- // is the polygon inverted?
- // returns segments
- return calculate(inverted, false);
- }
- };
- }
-
- module.exports = Intersecter;
-
- },{"./linked-list":30}],30:[function(_dereq_,module,exports){
- // (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
- // MIT License
- // Project Home: https://github.com/voidqk/polybooljs
-
- //
- // simple linked list implementation that allows you to traverse down nodes and save positions
- //
-
- var LinkedList = {
- create: function(){
- var my = {
- root: { root: true, next: null },
- exists: function(node){
- if (node === null || node === my.root)
- return false;
- return true;
- },
- isEmpty: function(){
- return my.root.next === null;
- },
- getHead: function(){
- return my.root.next;
- },
- insertBefore: function(node, check){
- var last = my.root;
- var here = my.root.next;
- while (here !== null){
- if (check(here)){
- node.prev = here.prev;
- node.next = here;
- here.prev.next = node;
- here.prev = node;
- return;
- }
- last = here;
- here = here.next;
- }
- last.next = node;
- node.prev = last;
- node.next = null;
- },
- findTransition: function(check){
- var prev = my.root;
- var here = my.root.next;
- while (here !== null){
- if (check(here))
- break;
- prev = here;
- here = here.next;
- }
- return {
- before: prev === my.root ? null : prev,
- after: here,
- insert: function(node){
- node.prev = prev;
- node.next = here;
- prev.next = node;
- if (here !== null)
- here.prev = node;
- return node;
- }
- };
- }
- };
- return my;
- },
- node: function(data){
- data.prev = null;
- data.next = null;
- data.remove = function(){
- data.prev.next = data.next;
- if (data.next)
- data.next.prev = data.prev;
- data.prev = null;
- data.next = null;
- };
- return data;
- }
- };
-
- module.exports = LinkedList;
-
- },{}],31:[function(_dereq_,module,exports){
- // (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
- // MIT License
- // Project Home: https://github.com/voidqk/polybooljs
-
- //
- // converts a list of segments into a list of regions, while also removing unnecessary verticies
- //
-
- function SegmentChainer(segments, eps, buildLog){
- var chains = [];
- var regions = [];
-
- segments.forEach(function(seg){
- var pt1 = seg.start;
- var pt2 = seg.end;
- if (eps.pointsSame(pt1, pt2)){
- console.warn('PolyBool: Warning: Zero-length segment detected; your epsilon is ' +
- 'probably too small or too large');
- return;
- }
-
- if (buildLog)
- buildLog.chainStart(seg);
-
- // search for two chains that this segment matches
- var first_match = {
- index: 0,
- matches_head: false,
- matches_pt1: false
- };
- var second_match = {
- index: 0,
- matches_head: false,
- matches_pt1: false
- };
- var next_match = first_match;
- function setMatch(index, matches_head, matches_pt1){
- // return true if we've matched twice
- next_match.index = index;
- next_match.matches_head = matches_head;
- next_match.matches_pt1 = matches_pt1;
- if (next_match === first_match){
- next_match = second_match;
- return false;
- }
- next_match = null;
- return true; // we've matched twice, we're done here
- }
- for (var i = 0; i < chains.length; i++){
- var chain = chains[i];
- var head = chain[0];
- var head2 = chain[1];
- var tail = chain[chain.length - 1];
- var tail2 = chain[chain.length - 2];
- if (eps.pointsSame(head, pt1)){
- if (setMatch(i, true, true))
- break;
- }
- else if (eps.pointsSame(head, pt2)){
- if (setMatch(i, true, false))
- break;
- }
- else if (eps.pointsSame(tail, pt1)){
- if (setMatch(i, false, true))
- break;
- }
- else if (eps.pointsSame(tail, pt2)){
- if (setMatch(i, false, false))
- break;
- }
- }
-
- if (next_match === first_match){
- // we didn't match anything, so create a new chain
- chains.push([ pt1, pt2 ]);
- if (buildLog)
- buildLog.chainNew(pt1, pt2);
- return;
- }
-
- if (next_match === second_match){
- // we matched a single chain
-
- if (buildLog)
- buildLog.chainMatch(first_match.index);
-
- // add the other point to the apporpriate end, and check to see if we've closed the
- // chain into a loop
-
- var index = first_match.index;
- var pt = first_match.matches_pt1 ? pt2 : pt1; // if we matched pt1, then we add pt2, etc
- var addToHead = first_match.matches_head; // if we matched at head, then add to the head
-
- var chain = chains[index];
- var grow = addToHead ? chain[0] : chain[chain.length - 1];
- var grow2 = addToHead ? chain[1] : chain[chain.length - 2];
- var oppo = addToHead ? chain[chain.length - 1] : chain[0];
- var oppo2 = addToHead ? chain[chain.length - 2] : chain[1];
-
- if (eps.pointsCollinear(grow2, grow, pt)){
- // grow isn't needed because it's directly between grow2 and pt:
- // grow2 ---grow---> pt
- if (addToHead){
- if (buildLog)
- buildLog.chainRemoveHead(first_match.index, pt);
- chain.shift();
- }
- else{
- if (buildLog)
- buildLog.chainRemoveTail(first_match.index, pt);
- chain.pop();
- }
- grow = grow2; // old grow is gone... new grow is what grow2 was
- }
-
- if (eps.pointsSame(oppo, pt)){
- // we're closing the loop, so remove chain from chains
- chains.splice(index, 1);
-
- if (eps.pointsCollinear(oppo2, oppo, grow)){
- // oppo isn't needed because it's directly between oppo2 and grow:
- // oppo2 ---oppo--->grow
- if (addToHead){
- if (buildLog)
- buildLog.chainRemoveTail(first_match.index, grow);
- chain.pop();
- }
- else{
- if (buildLog)
- buildLog.chainRemoveHead(first_match.index, grow);
- chain.shift();
- }
- }
-
- if (buildLog)
- buildLog.chainClose(first_match.index);
-
- // we have a closed chain!
- regions.push(chain);
- return;
- }
-
- // not closing a loop, so just add it to the apporpriate side
- if (addToHead){
- if (buildLog)
- buildLog.chainAddHead(first_match.index, pt);
- chain.unshift(pt);
- }
- else{
- if (buildLog)
- buildLog.chainAddTail(first_match.index, pt);
- chain.push(pt);
- }
- return;
- }
-
- // otherwise, we matched two chains, so we need to combine those chains together
-
- function reverseChain(index){
- if (buildLog)
- buildLog.chainReverse(index);
- chains[index].reverse(); // gee, that's easy
- }
-
- function appendChain(index1, index2){
- // index1 gets index2 appended to it, and index2 is removed
- var chain1 = chains[index1];
- var chain2 = chains[index2];
- var tail = chain1[chain1.length - 1];
- var tail2 = chain1[chain1.length - 2];
- var head = chain2[0];
- var head2 = chain2[1];
-
- if (eps.pointsCollinear(tail2, tail, head)){
- // tail isn't needed because it's directly between tail2 and head
- // tail2 ---tail---> head
- if (buildLog)
- buildLog.chainRemoveTail(index1, tail);
- chain1.pop();
- tail = tail2; // old tail is gone... new tail is what tail2 was
- }
-
- if (eps.pointsCollinear(tail, head, head2)){
- // head isn't needed because it's directly between tail and head2
- // tail ---head---> head2
- if (buildLog)
- buildLog.chainRemoveHead(index2, head);
- chain2.shift();
- }
-
- if (buildLog)
- buildLog.chainJoin(index1, index2);
- chains[index1] = chain1.concat(chain2);
- chains.splice(index2, 1);
- }
-
- var F = first_match.index;
- var S = second_match.index;
-
- if (buildLog)
- buildLog.chainConnect(F, S);
-
- var reverseF = chains[F].length < chains[S].length; // reverse the shorter chain, if needed
- if (first_match.matches_head){
- if (second_match.matches_head){
- if (reverseF){
- // <<<< F <<<< --- >>>> S >>>>
- reverseChain(F);
- // >>>> F >>>> --- >>>> S >>>>
- appendChain(F, S);
- }
- else{
- // <<<< F <<<< --- >>>> S >>>>
- reverseChain(S);
- // <<<< F <<<< --- <<<< S <<<< logically same as:
- // >>>> S >>>> --- >>>> F >>>>
- appendChain(S, F);
- }
- }
- else{
- // <<<< F <<<< --- <<<< S <<<< logically same as:
- // >>>> S >>>> --- >>>> F >>>>
- appendChain(S, F);
- }
- }
- else{
- if (second_match.matches_head){
- // >>>> F >>>> --- >>>> S >>>>
- appendChain(F, S);
- }
- else{
- if (reverseF){
- // >>>> F >>>> --- <<<< S <<<<
- reverseChain(F);
- // <<<< F <<<< --- <<<< S <<<< logically same as:
- // >>>> S >>>> --- >>>> F >>>>
- appendChain(S, F);
- }
- else{
- // >>>> F >>>> --- <<<< S <<<<
- reverseChain(S);
- // >>>> F >>>> --- >>>> S >>>>
- appendChain(F, S);
- }
- }
- }
- });
-
- return regions;
- }
-
- module.exports = SegmentChainer;
-
- },{}],32:[function(_dereq_,module,exports){
- // (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
- // MIT License
- // Project Home: https://github.com/voidqk/polybooljs
-
- //
- // filter a list of segments based on boolean operations
- //
-
- function select(segments, selection, buildLog){
- var result = [];
- segments.forEach(function(seg){
- var index =
- (seg.myFill.above ? 8 : 0) +
- (seg.myFill.below ? 4 : 0) +
- ((seg.otherFill && seg.otherFill.above) ? 2 : 0) +
- ((seg.otherFill && seg.otherFill.below) ? 1 : 0);
- if (selection[index] !== 0){
- // copy the segment to the results, while also calculating the fill status
- result.push({
- id: buildLog ? buildLog.segmentId() : -1,
- start: seg.start,
- end: seg.end,
- myFill: {
- above: selection[index] === 1, // 1 if filled above
- below: selection[index] === 2 // 2 if filled below
- },
- otherFill: null
- });
- }
- });
-
- if (buildLog)
- buildLog.selected(result);
-
- return result;
- }
-
- var SegmentSelector = {
- union: function(segments, buildLog){ // primary | secondary
- // above1 below1 above2 below2 Keep? Value
- // 0 0 0 0 => no 0
- // 0 0 0 1 => yes filled below 2
- // 0 0 1 0 => yes filled above 1
- // 0 0 1 1 => no 0
- // 0 1 0 0 => yes filled below 2
- // 0 1 0 1 => yes filled below 2
- // 0 1 1 0 => no 0
- // 0 1 1 1 => no 0
- // 1 0 0 0 => yes filled above 1
- // 1 0 0 1 => no 0
- // 1 0 1 0 => yes filled above 1
- // 1 0 1 1 => no 0
- // 1 1 0 0 => no 0
- // 1 1 0 1 => no 0
- // 1 1 1 0 => no 0
- // 1 1 1 1 => no 0
- return select(segments, [
- 0, 2, 1, 0,
- 2, 2, 0, 0,
- 1, 0, 1, 0,
- 0, 0, 0, 0
- ], buildLog);
- },
- intersect: function(segments, buildLog){ // primary & secondary
- // above1 below1 above2 below2 Keep? Value
- // 0 0 0 0 => no 0
- // 0 0 0 1 => no 0
- // 0 0 1 0 => no 0
- // 0 0 1 1 => no 0
- // 0 1 0 0 => no 0
- // 0 1 0 1 => yes filled below 2
- // 0 1 1 0 => no 0
- // 0 1 1 1 => yes filled below 2
- // 1 0 0 0 => no 0
- // 1 0 0 1 => no 0
- // 1 0 1 0 => yes filled above 1
- // 1 0 1 1 => yes filled above 1
- // 1 1 0 0 => no 0
- // 1 1 0 1 => yes filled below 2
- // 1 1 1 0 => yes filled above 1
- // 1 1 1 1 => no 0
- return select(segments, [
- 0, 0, 0, 0,
- 0, 2, 0, 2,
- 0, 0, 1, 1,
- 0, 2, 1, 0
- ], buildLog);
- },
- difference: function(segments, buildLog){ // primary - secondary
- // above1 below1 above2 below2 Keep? Value
- // 0 0 0 0 => no 0
- // 0 0 0 1 => no 0
- // 0 0 1 0 => no 0
- // 0 0 1 1 => no 0
- // 0 1 0 0 => yes filled below 2
- // 0 1 0 1 => no 0
- // 0 1 1 0 => yes filled below 2
- // 0 1 1 1 => no 0
- // 1 0 0 0 => yes filled above 1
- // 1 0 0 1 => yes filled above 1
- // 1 0 1 0 => no 0
- // 1 0 1 1 => no 0
- // 1 1 0 0 => no 0
- // 1 1 0 1 => yes filled above 1
- // 1 1 1 0 => yes filled below 2
- // 1 1 1 1 => no 0
- return select(segments, [
- 0, 0, 0, 0,
- 2, 0, 2, 0,
- 1, 1, 0, 0,
- 0, 1, 2, 0
- ], buildLog);
- },
- differenceRev: function(segments, buildLog){ // secondary - primary
- // above1 below1 above2 below2 Keep? Value
- // 0 0 0 0 => no 0
- // 0 0 0 1 => yes filled below 2
- // 0 0 1 0 => yes filled above 1
- // 0 0 1 1 => no 0
- // 0 1 0 0 => no 0
- // 0 1 0 1 => no 0
- // 0 1 1 0 => yes filled above 1
- // 0 1 1 1 => yes filled above 1
- // 1 0 0 0 => no 0
- // 1 0 0 1 => yes filled below 2
- // 1 0 1 0 => no 0
- // 1 0 1 1 => yes filled below 2
- // 1 1 0 0 => no 0
- // 1 1 0 1 => no 0
- // 1 1 1 0 => no 0
- // 1 1 1 1 => no 0
- return select(segments, [
- 0, 2, 1, 0,
- 0, 0, 1, 1,
- 0, 2, 0, 2,
- 0, 0, 0, 0
- ], buildLog);
- },
- xor: function(segments, buildLog){ // primary ^ secondary
- // above1 below1 above2 below2 Keep? Value
- // 0 0 0 0 => no 0
- // 0 0 0 1 => yes filled below 2
- // 0 0 1 0 => yes filled above 1
- // 0 0 1 1 => no 0
- // 0 1 0 0 => yes filled below 2
- // 0 1 0 1 => no 0
- // 0 1 1 0 => no 0
- // 0 1 1 1 => yes filled above 1
- // 1 0 0 0 => yes filled above 1
- // 1 0 0 1 => no 0
- // 1 0 1 0 => no 0
- // 1 0 1 1 => yes filled below 2
- // 1 1 0 0 => no 0
- // 1 1 0 1 => yes filled above 1
- // 1 1 1 0 => yes filled below 2
- // 1 1 1 1 => no 0
- return select(segments, [
- 0, 2, 1, 0,
- 2, 0, 0, 1,
- 1, 0, 0, 2,
- 0, 1, 2, 0
- ], buildLog);
- }
- };
-
- module.exports = SegmentSelector;
-
- },{}],33:[function(_dereq_,module,exports){
- // shim for using process in browser
- var process = module.exports = {};
-
- // cached from whatever global is present so that test runners that stub it
- // don't break things. But we need to wrap it in a try catch in case it is
- // wrapped in strict mode code which doesn't define any globals. It's inside a
- // function because try/catches deoptimize in certain engines.
-
- var cachedSetTimeout;
- var cachedClearTimeout;
-
- function defaultSetTimout() {
- throw new Error('setTimeout has not been defined');
- }
- function defaultClearTimeout () {
- throw new Error('clearTimeout has not been defined');
- }
- (function () {
- try {
- if (typeof setTimeout === 'function') {
- cachedSetTimeout = setTimeout;
- } else {
- cachedSetTimeout = defaultSetTimout;
- }
- } catch (e) {
- cachedSetTimeout = defaultSetTimout;
- }
- try {
- if (typeof clearTimeout === 'function') {
- cachedClearTimeout = clearTimeout;
- } else {
- cachedClearTimeout = defaultClearTimeout;
- }
- } catch (e) {
- cachedClearTimeout = defaultClearTimeout;
- }
- } ())
- function runTimeout(fun) {
- if (cachedSetTimeout === setTimeout) {
- //normal enviroments in sane situations
- return setTimeout(fun, 0);
- }
- // if setTimeout wasn't available but was latter defined
- if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
- cachedSetTimeout = setTimeout;
- return setTimeout(fun, 0);
- }
- try {
- // when when somebody has screwed with setTimeout but no I.E. maddness
- return cachedSetTimeout(fun, 0);
- } catch(e){
- try {
- // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
- return cachedSetTimeout.call(null, fun, 0);
- } catch(e){
- // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
- return cachedSetTimeout.call(this, fun, 0);
- }
- }
-
-
- }
- function runClearTimeout(marker) {
- if (cachedClearTimeout === clearTimeout) {
- //normal enviroments in sane situations
- return clearTimeout(marker);
- }
- // if clearTimeout wasn't available but was latter defined
- if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
- cachedClearTimeout = clearTimeout;
- return clearTimeout(marker);
- }
- try {
- // when when somebody has screwed with setTimeout but no I.E. maddness
- return cachedClearTimeout(marker);
- } catch (e){
- try {
- // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
- return cachedClearTimeout.call(null, marker);
- } catch (e){
- // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
- // Some versions of I.E. have different rules for clearTimeout vs setTimeout
- return cachedClearTimeout.call(this, marker);
- }
- }
-
-
-
- }
- var queue = [];
- var draining = false;
- var currentQueue;
- var queueIndex = -1;
-
- function cleanUpNextTick() {
- if (!draining || !currentQueue) {
- return;
- }
- draining = false;
- if (currentQueue.length) {
- queue = currentQueue.concat(queue);
- } else {
- queueIndex = -1;
- }
- if (queue.length) {
- drainQueue();
- }
- }
-
- function drainQueue() {
- if (draining) {
- return;
- }
- var timeout = runTimeout(cleanUpNextTick);
- draining = true;
-
- var len = queue.length;
- while(len) {
- currentQueue = queue;
- queue = [];
- while (++queueIndex < len) {
- if (currentQueue) {
- currentQueue[queueIndex].run();
- }
- }
- queueIndex = -1;
- len = queue.length;
- }
- currentQueue = null;
- draining = false;
- runClearTimeout(timeout);
- }
-
- process.nextTick = function (fun) {
- var args = new Array(arguments.length - 1);
- if (arguments.length > 1) {
- for (var i = 1; i < arguments.length; i++) {
- args[i - 1] = arguments[i];
- }
- }
- queue.push(new Item(fun, args));
- if (queue.length === 1 && !draining) {
- runTimeout(drainQueue);
- }
- };
-
- // v8 likes predictible objects
- function Item(fun, array) {
- this.fun = fun;
- this.array = array;
- }
- Item.prototype.run = function () {
- this.fun.apply(null, this.array);
- };
- process.title = 'browser';
- process.browser = true;
- process.env = {};
- process.argv = [];
- process.version = ''; // empty string to avoid regexp issues
- process.versions = {};
-
- function noop() {}
-
- process.on = noop;
- process.addListener = noop;
- process.once = noop;
- process.off = noop;
- process.removeListener = noop;
- process.removeAllListeners = noop;
- process.emit = noop;
- process.prependListener = noop;
- process.prependOnceListener = noop;
-
- process.listeners = function (name) { return [] }
-
- process.binding = function (name) {
- throw new Error('process.binding is not supported');
- };
-
- process.cwd = function () { return '/' };
- process.chdir = function (dir) {
- throw new Error('process.chdir is not supported');
- };
- process.umask = function() { return 0; };
-
- },{}],34:[function(_dereq_,module,exports){
- // TinyColor v1.4.1
- // https://github.com/bgrins/TinyColor
- // Brian Grinstead, MIT License
-
- (function(Math) {
-
- var trimLeft = /^\s+/,
- trimRight = /\s+$/,
- tinyCounter = 0,
- mathRound = Math.round,
- mathMin = Math.min,
- mathMax = Math.max,
- mathRandom = Math.random;
-
- function tinycolor (color, opts) {
-
- color = (color) ? color : '';
- opts = opts || { };
-
- // If input is already a tinycolor, return itself
- if (color instanceof tinycolor) {
- return color;
- }
- // If we are called as a function, call using new instead
- if (!(this instanceof tinycolor)) {
- return new tinycolor(color, opts);
- }
-
- var rgb = inputToRGB(color);
- this._originalInput = color,
- this._r = rgb.r,
- this._g = rgb.g,
- this._b = rgb.b,
- this._a = rgb.a,
- this._roundA = mathRound(100*this._a) / 100,
- this._format = opts.format || rgb.format;
- this._gradientType = opts.gradientType;
-
- // Don't let the range of [0,255] come back in [0,1].
- // Potentially lose a little bit of precision here, but will fix issues where
- // .5 gets interpreted as half of the total, instead of half of 1
- // If it was supposed to be 128, this was already taken care of by `inputToRgb`
- if (this._r < 1) { this._r = mathRound(this._r); }
- if (this._g < 1) { this._g = mathRound(this._g); }
- if (this._b < 1) { this._b = mathRound(this._b); }
-
- this._ok = rgb.ok;
- this._tc_id = tinyCounter++;
- }
-
- tinycolor.prototype = {
- isDark: function() {
- return this.getBrightness() < 128;
- },
- isLight: function() {
- return !this.isDark();
- },
- isValid: function() {
- return this._ok;
- },
- getOriginalInput: function() {
- return this._originalInput;
- },
- getFormat: function() {
- return this._format;
- },
- getAlpha: function() {
- return this._a;
- },
- getBrightness: function() {
- //http://www.w3.org/TR/AERT#color-contrast
- var rgb = this.toRgb();
- return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
- },
- getLuminance: function() {
- //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
- var rgb = this.toRgb();
- var RsRGB, GsRGB, BsRGB, R, G, B;
- RsRGB = rgb.r/255;
- GsRGB = rgb.g/255;
- BsRGB = rgb.b/255;
-
- if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);}
- if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);}
- if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);}
- return (0.2126 * R) + (0.7152 * G) + (0.0722 * B);
- },
- setAlpha: function(value) {
- this._a = boundAlpha(value);
- this._roundA = mathRound(100*this._a) / 100;
- return this;
- },
- toHsv: function() {
- var hsv = rgbToHsv(this._r, this._g, this._b);
- return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
- },
- toHsvString: function() {
- var hsv = rgbToHsv(this._r, this._g, this._b);
- var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
- return (this._a == 1) ?
- "hsv(" + h + ", " + s + "%, " + v + "%)" :
- "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
- },
- toHsl: function() {
- var hsl = rgbToHsl(this._r, this._g, this._b);
- return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
- },
- toHslString: function() {
- var hsl = rgbToHsl(this._r, this._g, this._b);
- var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
- return (this._a == 1) ?
- "hsl(" + h + ", " + s + "%, " + l + "%)" :
- "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
- },
- toHex: function(allow3Char) {
- return rgbToHex(this._r, this._g, this._b, allow3Char);
- },
- toHexString: function(allow3Char) {
- return '#' + this.toHex(allow3Char);
- },
- toHex8: function(allow4Char) {
- return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char);
- },
- toHex8String: function(allow4Char) {
- return '#' + this.toHex8(allow4Char);
- },
- toRgb: function() {
- return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
- },
- toRgbString: function() {
- return (this._a == 1) ?
- "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
- "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
- },
- toPercentageRgb: function() {
- return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
- },
- toPercentageRgbString: function() {
- return (this._a == 1) ?
- "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
- "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
- },
- toName: function() {
- if (this._a === 0) {
- return "transparent";
- }
-
- if (this._a < 1) {
- return false;
- }
-
- return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
- },
- toFilter: function(secondColor) {
- var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a);
- var secondHex8String = hex8String;
- var gradientType = this._gradientType ? "GradientType = 1, " : "";
-
- if (secondColor) {
- var s = tinycolor(secondColor);
- secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a);
- }
-
- return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
- },
- toString: function(format) {
- var formatSet = !!format;
- format = format || this._format;
-
- var formattedString = false;
- var hasAlpha = this._a < 1 && this._a >= 0;
- var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name");
-
- if (needsAlphaFormat) {
- // Special case for "transparent", all other non-alpha formats
- // will return rgba when there is transparency.
- if (format === "name" && this._a === 0) {
- return this.toName();
- }
- return this.toRgbString();
- }
- if (format === "rgb") {
- formattedString = this.toRgbString();
- }
- if (format === "prgb") {
- formattedString = this.toPercentageRgbString();
- }
- if (format === "hex" || format === "hex6") {
- formattedString = this.toHexString();
- }
- if (format === "hex3") {
- formattedString = this.toHexString(true);
- }
- if (format === "hex4") {
- formattedString = this.toHex8String(true);
- }
- if (format === "hex8") {
- formattedString = this.toHex8String();
- }
- if (format === "name") {
- formattedString = this.toName();
- }
- if (format === "hsl") {
- formattedString = this.toHslString();
- }
- if (format === "hsv") {
- formattedString = this.toHsvString();
- }
-
- return formattedString || this.toHexString();
- },
- clone: function() {
- return tinycolor(this.toString());
- },
-
- _applyModification: function(fn, args) {
- var color = fn.apply(null, [this].concat([].slice.call(args)));
- this._r = color._r;
- this._g = color._g;
- this._b = color._b;
- this.setAlpha(color._a);
- return this;
- },
- lighten: function() {
- return this._applyModification(lighten, arguments);
- },
- brighten: function() {
- return this._applyModification(brighten, arguments);
- },
- darken: function() {
- return this._applyModification(darken, arguments);
- },
- desaturate: function() {
- return this._applyModification(desaturate, arguments);
- },
- saturate: function() {
- return this._applyModification(saturate, arguments);
- },
- greyscale: function() {
- return this._applyModification(greyscale, arguments);
- },
- spin: function() {
- return this._applyModification(spin, arguments);
- },
-
- _applyCombination: function(fn, args) {
- return fn.apply(null, [this].concat([].slice.call(args)));
- },
- analogous: function() {
- return this._applyCombination(analogous, arguments);
- },
- complement: function() {
- return this._applyCombination(complement, arguments);
- },
- monochromatic: function() {
- return this._applyCombination(monochromatic, arguments);
- },
- splitcomplement: function() {
- return this._applyCombination(splitcomplement, arguments);
- },
- triad: function() {
- return this._applyCombination(triad, arguments);
- },
- tetrad: function() {
- return this._applyCombination(tetrad, arguments);
- }
- };
-
- // If input is an object, force 1 into "1.0" to handle ratios properly
- // String input requires "1.0" as input, so 1 will be treated as 1
- tinycolor.fromRatio = function(color, opts) {
- if (typeof color == "object") {
- var newColor = {};
- for (var i in color) {
- if (color.hasOwnProperty(i)) {
- if (i === "a") {
- newColor[i] = color[i];
- }
- else {
- newColor[i] = convertToPercentage(color[i]);
- }
- }
- }
- color = newColor;
- }
-
- return tinycolor(color, opts);
- };
-
- // Given a string or object, convert that input to RGB
- // Possible string inputs:
- //
- // "red"
- // "#f00" or "f00"
- // "#ff0000" or "ff0000"
- // "#ff000000" or "ff000000"
- // "rgb 255 0 0" or "rgb (255, 0, 0)"
- // "rgb 1.0 0 0" or "rgb (1, 0, 0)"
- // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
- // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
- // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
- // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
- // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
- //
- function inputToRGB(color) {
-
- var rgb = { r: 0, g: 0, b: 0 };
- var a = 1;
- var s = null;
- var v = null;
- var l = null;
- var ok = false;
- var format = false;
-
- if (typeof color == "string") {
- color = stringInputToObject(color);
- }
-
- if (typeof color == "object") {
- if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
- rgb = rgbToRgb(color.r, color.g, color.b);
- ok = true;
- format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
- }
- else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
- s = convertToPercentage(color.s);
- v = convertToPercentage(color.v);
- rgb = hsvToRgb(color.h, s, v);
- ok = true;
- format = "hsv";
- }
- else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
- s = convertToPercentage(color.s);
- l = convertToPercentage(color.l);
- rgb = hslToRgb(color.h, s, l);
- ok = true;
- format = "hsl";
- }
-
- if (color.hasOwnProperty("a")) {
- a = color.a;
- }
- }
-
- a = boundAlpha(a);
-
- return {
- ok: ok,
- format: color.format || format,
- r: mathMin(255, mathMax(rgb.r, 0)),
- g: mathMin(255, mathMax(rgb.g, 0)),
- b: mathMin(255, mathMax(rgb.b, 0)),
- a: a
- };
- }
-
-
- // Conversion Functions
- // --------------------
-
- // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
- // <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
-
- // `rgbToRgb`
- // Handle bounds / percentage checking to conform to CSS color spec
- // <http://www.w3.org/TR/css3-color/>
- // *Assumes:* r, g, b in [0, 255] or [0, 1]
- // *Returns:* { r, g, b } in [0, 255]
- function rgbToRgb(r, g, b){
- return {
- r: bound01(r, 255) * 255,
- g: bound01(g, 255) * 255,
- b: bound01(b, 255) * 255
- };
- }
-
- // `rgbToHsl`
- // Converts an RGB color value to HSL.
- // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
- // *Returns:* { h, s, l } in [0,1]
- function rgbToHsl(r, g, b) {
-
- r = bound01(r, 255);
- g = bound01(g, 255);
- b = bound01(b, 255);
-
- var max = mathMax(r, g, b), min = mathMin(r, g, b);
- var h, s, l = (max + min) / 2;
-
- if(max == min) {
- h = s = 0; // achromatic
- }
- else {
- var d = max - min;
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
- switch(max) {
- case r: h = (g - b) / d + (g < b ? 6 : 0); break;
- case g: h = (b - r) / d + 2; break;
- case b: h = (r - g) / d + 4; break;
- }
-
- h /= 6;
- }
-
- return { h: h, s: s, l: l };
- }
-
- // `hslToRgb`
- // Converts an HSL color value to RGB.
- // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
- // *Returns:* { r, g, b } in the set [0, 255]
- function hslToRgb(h, s, l) {
- var r, g, b;
-
- h = bound01(h, 360);
- s = bound01(s, 100);
- l = bound01(l, 100);
-
- function hue2rgb(p, q, t) {
- if(t < 0) t += 1;
- if(t > 1) t -= 1;
- if(t < 1/6) return p + (q - p) * 6 * t;
- if(t < 1/2) return q;
- if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
- return p;
- }
-
- if(s === 0) {
- r = g = b = l; // achromatic
- }
- else {
- var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- var p = 2 * l - q;
- r = hue2rgb(p, q, h + 1/3);
- g = hue2rgb(p, q, h);
- b = hue2rgb(p, q, h - 1/3);
- }
-
- return { r: r * 255, g: g * 255, b: b * 255 };
- }
-
- // `rgbToHsv`
- // Converts an RGB color value to HSV
- // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
- // *Returns:* { h, s, v } in [0,1]
- function rgbToHsv(r, g, b) {
-
- r = bound01(r, 255);
- g = bound01(g, 255);
- b = bound01(b, 255);
-
- var max = mathMax(r, g, b), min = mathMin(r, g, b);
- var h, s, v = max;
-
- var d = max - min;
- s = max === 0 ? 0 : d / max;
-
- if(max == min) {
- h = 0; // achromatic
- }
- else {
- switch(max) {
- case r: h = (g - b) / d + (g < b ? 6 : 0); break;
- case g: h = (b - r) / d + 2; break;
- case b: h = (r - g) / d + 4; break;
- }
- h /= 6;
- }
- return { h: h, s: s, v: v };
- }
-
- // `hsvToRgb`
- // Converts an HSV color value to RGB.
- // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
- // *Returns:* { r, g, b } in the set [0, 255]
- function hsvToRgb(h, s, v) {
-
- h = bound01(h, 360) * 6;
- s = bound01(s, 100);
- v = bound01(v, 100);
-
- var i = Math.floor(h),
- f = h - i,
- p = v * (1 - s),
- q = v * (1 - f * s),
- t = v * (1 - (1 - f) * s),
- mod = i % 6,
- r = [v, q, p, p, t, v][mod],
- g = [t, v, v, q, p, p][mod],
- b = [p, p, t, v, v, q][mod];
-
- return { r: r * 255, g: g * 255, b: b * 255 };
- }
-
- // `rgbToHex`
- // Converts an RGB color to hex
- // Assumes r, g, and b are contained in the set [0, 255]
- // Returns a 3 or 6 character hex
- function rgbToHex(r, g, b, allow3Char) {
-
- var hex = [
- pad2(mathRound(r).toString(16)),
- pad2(mathRound(g).toString(16)),
- pad2(mathRound(b).toString(16))
- ];
-
- // Return a 3 character hex if possible
- if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
- return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
- }
-
- return hex.join("");
- }
-
- // `rgbaToHex`
- // Converts an RGBA color plus alpha transparency to hex
- // Assumes r, g, b are contained in the set [0, 255] and
- // a in [0, 1]. Returns a 4 or 8 character rgba hex
- function rgbaToHex(r, g, b, a, allow4Char) {
-
- var hex = [
- pad2(mathRound(r).toString(16)),
- pad2(mathRound(g).toString(16)),
- pad2(mathRound(b).toString(16)),
- pad2(convertDecimalToHex(a))
- ];
-
- // Return a 4 character hex if possible
- if (allow4Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1) && hex[3].charAt(0) == hex[3].charAt(1)) {
- return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
- }
-
- return hex.join("");
- }
-
- // `rgbaToArgbHex`
- // Converts an RGBA color to an ARGB Hex8 string
- // Rarely used, but required for "toFilter()"
- function rgbaToArgbHex(r, g, b, a) {
-
- var hex = [
- pad2(convertDecimalToHex(a)),
- pad2(mathRound(r).toString(16)),
- pad2(mathRound(g).toString(16)),
- pad2(mathRound(b).toString(16))
- ];
-
- return hex.join("");
- }
-
- // `equals`
- // Can be called with any tinycolor input
- tinycolor.equals = function (color1, color2) {
- if (!color1 || !color2) { return false; }
- return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
- };
-
- tinycolor.random = function() {
- return tinycolor.fromRatio({
- r: mathRandom(),
- g: mathRandom(),
- b: mathRandom()
- });
- };
-
-
- // Modification Functions
- // ----------------------
- // Thanks to less.js for some of the basics here
- // <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
-
- function desaturate(color, amount) {
- amount = (amount === 0) ? 0 : (amount || 10);
- var hsl = tinycolor(color).toHsl();
- hsl.s -= amount / 100;
- hsl.s = clamp01(hsl.s);
- return tinycolor(hsl);
- }
-
- function saturate(color, amount) {
- amount = (amount === 0) ? 0 : (amount || 10);
- var hsl = tinycolor(color).toHsl();
- hsl.s += amount / 100;
- hsl.s = clamp01(hsl.s);
- return tinycolor(hsl);
- }
-
- function greyscale(color) {
- return tinycolor(color).desaturate(100);
- }
-
- function lighten (color, amount) {
- amount = (amount === 0) ? 0 : (amount || 10);
- var hsl = tinycolor(color).toHsl();
- hsl.l += amount / 100;
- hsl.l = clamp01(hsl.l);
- return tinycolor(hsl);
- }
-
- function brighten(color, amount) {
- amount = (amount === 0) ? 0 : (amount || 10);
- var rgb = tinycolor(color).toRgb();
- rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
- rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
- rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
- return tinycolor(rgb);
- }
-
- function darken (color, amount) {
- amount = (amount === 0) ? 0 : (amount || 10);
- var hsl = tinycolor(color).toHsl();
- hsl.l -= amount / 100;
- hsl.l = clamp01(hsl.l);
- return tinycolor(hsl);
- }
-
- // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
- // Values outside of this range will be wrapped into this range.
- function spin(color, amount) {
- var hsl = tinycolor(color).toHsl();
- var hue = (hsl.h + amount) % 360;
- hsl.h = hue < 0 ? 360 + hue : hue;
- return tinycolor(hsl);
- }
-
- // Combination Functions
- // ---------------------
- // Thanks to jQuery xColor for some of the ideas behind these
- // <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
-
- function complement(color) {
- var hsl = tinycolor(color).toHsl();
- hsl.h = (hsl.h + 180) % 360;
- return tinycolor(hsl);
- }
-
- function triad(color) {
- var hsl = tinycolor(color).toHsl();
- var h = hsl.h;
- return [
- tinycolor(color),
- tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
- tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
- ];
- }
-
- function tetrad(color) {
- var hsl = tinycolor(color).toHsl();
- var h = hsl.h;
- return [
- tinycolor(color),
- tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
- tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
- tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
- ];
- }
-
- function splitcomplement(color) {
- var hsl = tinycolor(color).toHsl();
- var h = hsl.h;
- return [
- tinycolor(color),
- tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
- tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
- ];
- }
-
- function analogous(color, results, slices) {
- results = results || 6;
- slices = slices || 30;
-
- var hsl = tinycolor(color).toHsl();
- var part = 360 / slices;
- var ret = [tinycolor(color)];
-
- for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
- hsl.h = (hsl.h + part) % 360;
- ret.push(tinycolor(hsl));
- }
- return ret;
- }
-
- function monochromatic(color, results) {
- results = results || 6;
- var hsv = tinycolor(color).toHsv();
- var h = hsv.h, s = hsv.s, v = hsv.v;
- var ret = [];
- var modification = 1 / results;
-
- while (results--) {
- ret.push(tinycolor({ h: h, s: s, v: v}));
- v = (v + modification) % 1;
- }
-
- return ret;
- }
-
- // Utility Functions
- // ---------------------
-
- tinycolor.mix = function(color1, color2, amount) {
- amount = (amount === 0) ? 0 : (amount || 50);
-
- var rgb1 = tinycolor(color1).toRgb();
- var rgb2 = tinycolor(color2).toRgb();
-
- var p = amount / 100;
-
- var rgba = {
- r: ((rgb2.r - rgb1.r) * p) + rgb1.r,
- g: ((rgb2.g - rgb1.g) * p) + rgb1.g,
- b: ((rgb2.b - rgb1.b) * p) + rgb1.b,
- a: ((rgb2.a - rgb1.a) * p) + rgb1.a
- };
-
- return tinycolor(rgba);
- };
-
-
- // Readability Functions
- // ---------------------
- // <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
-
- // `contrast`
- // Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)
- tinycolor.readability = function(color1, color2) {
- var c1 = tinycolor(color1);
- var c2 = tinycolor(color2);
- return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);
- };
-
- // `isReadable`
- // Ensure that foreground and background color combinations meet WCAG2 guidelines.
- // The third argument is an optional Object.
- // the 'level' property states 'AA' or 'AAA' - if missing or invalid, it defaults to 'AA';
- // the 'size' property states 'large' or 'small' - if missing or invalid, it defaults to 'small'.
- // If the entire object is absent, isReadable defaults to {level:"AA",size:"small"}.
-
- // *Example*
- // tinycolor.isReadable("#000", "#111") => false
- // tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false
- tinycolor.isReadable = function(color1, color2, wcag2) {
- var readability = tinycolor.readability(color1, color2);
- var wcag2Parms, out;
-
- out = false;
-
- wcag2Parms = validateWCAG2Parms(wcag2);
- switch (wcag2Parms.level + wcag2Parms.size) {
- case "AAsmall":
- case "AAAlarge":
- out = readability >= 4.5;
- break;
- case "AAlarge":
- out = readability >= 3;
- break;
- case "AAAsmall":
- out = readability >= 7;
- break;
- }
- return out;
-
- };
-
- // `mostReadable`
- // Given a base color and a list of possible foreground or background
- // colors for that base, returns the most readable color.
- // Optionally returns Black or White if the most readable color is unreadable.
- // *Example*
- // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255"
- // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff"
- // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3"
- // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff"
- tinycolor.mostReadable = function(baseColor, colorList, args) {
- var bestColor = null;
- var bestScore = 0;
- var readability;
- var includeFallbackColors, level, size ;
- args = args || {};
- includeFallbackColors = args.includeFallbackColors ;
- level = args.level;
- size = args.size;
-
- for (var i= 0; i < colorList.length ; i++) {
- readability = tinycolor.readability(baseColor, colorList[i]);
- if (readability > bestScore) {
- bestScore = readability;
- bestColor = tinycolor(colorList[i]);
- }
- }
-
- if (tinycolor.isReadable(baseColor, bestColor, {"level":level,"size":size}) || !includeFallbackColors) {
- return bestColor;
- }
- else {
- args.includeFallbackColors=false;
- return tinycolor.mostReadable(baseColor,["#fff", "#000"],args);
- }
- };
-
-
- // Big List of Colors
- // ------------------
- // <http://www.w3.org/TR/css3-color/#svg-color>
- var names = tinycolor.names = {
- aliceblue: "f0f8ff",
- antiquewhite: "faebd7",
- aqua: "0ff",
- aquamarine: "7fffd4",
- azure: "f0ffff",
- beige: "f5f5dc",
- bisque: "ffe4c4",
- black: "000",
- blanchedalmond: "ffebcd",
- blue: "00f",
- blueviolet: "8a2be2",
- brown: "a52a2a",
- burlywood: "deb887",
- burntsienna: "ea7e5d",
- cadetblue: "5f9ea0",
- chartreuse: "7fff00",
- chocolate: "d2691e",
- coral: "ff7f50",
- cornflowerblue: "6495ed",
- cornsilk: "fff8dc",
- crimson: "dc143c",
- cyan: "0ff",
- darkblue: "00008b",
- darkcyan: "008b8b",
- darkgoldenrod: "b8860b",
- darkgray: "a9a9a9",
- darkgreen: "006400",
- darkgrey: "a9a9a9",
- darkkhaki: "bdb76b",
- darkmagenta: "8b008b",
- darkolivegreen: "556b2f",
- darkorange: "ff8c00",
- darkorchid: "9932cc",
- darkred: "8b0000",
- darksalmon: "e9967a",
- darkseagreen: "8fbc8f",
- darkslateblue: "483d8b",
- darkslategray: "2f4f4f",
- darkslategrey: "2f4f4f",
- darkturquoise: "00ced1",
- darkviolet: "9400d3",
- deeppink: "ff1493",
- deepskyblue: "00bfff",
- dimgray: "696969",
- dimgrey: "696969",
- dodgerblue: "1e90ff",
- firebrick: "b22222",
- floralwhite: "fffaf0",
- forestgreen: "228b22",
- fuchsia: "f0f",
- gainsboro: "dcdcdc",
- ghostwhite: "f8f8ff",
- gold: "ffd700",
- goldenrod: "daa520",
- gray: "808080",
- green: "008000",
- greenyellow: "adff2f",
- grey: "808080",
- honeydew: "f0fff0",
- hotpink: "ff69b4",
- indianred: "cd5c5c",
- indigo: "4b0082",
- ivory: "fffff0",
- khaki: "f0e68c",
- lavender: "e6e6fa",
- lavenderblush: "fff0f5",
- lawngreen: "7cfc00",
- lemonchiffon: "fffacd",
- lightblue: "add8e6",
- lightcoral: "f08080",
- lightcyan: "e0ffff",
- lightgoldenrodyellow: "fafad2",
- lightgray: "d3d3d3",
- lightgreen: "90ee90",
- lightgrey: "d3d3d3",
- lightpink: "ffb6c1",
- lightsalmon: "ffa07a",
- lightseagreen: "20b2aa",
- lightskyblue: "87cefa",
- lightslategray: "789",
- lightslategrey: "789",
- lightsteelblue: "b0c4de",
- lightyellow: "ffffe0",
- lime: "0f0",
- limegreen: "32cd32",
- linen: "faf0e6",
- magenta: "f0f",
- maroon: "800000",
- mediumaquamarine: "66cdaa",
- mediumblue: "0000cd",
- mediumorchid: "ba55d3",
- mediumpurple: "9370db",
- mediumseagreen: "3cb371",
- mediumslateblue: "7b68ee",
- mediumspringgreen: "00fa9a",
- mediumturquoise: "48d1cc",
- mediumvioletred: "c71585",
- midnightblue: "191970",
- mintcream: "f5fffa",
- mistyrose: "ffe4e1",
- moccasin: "ffe4b5",
- navajowhite: "ffdead",
- navy: "000080",
- oldlace: "fdf5e6",
- olive: "808000",
- olivedrab: "6b8e23",
- orange: "ffa500",
- orangered: "ff4500",
- orchid: "da70d6",
- palegoldenrod: "eee8aa",
- palegreen: "98fb98",
- paleturquoise: "afeeee",
- palevioletred: "db7093",
- papayawhip: "ffefd5",
- peachpuff: "ffdab9",
- peru: "cd853f",
- pink: "ffc0cb",
- plum: "dda0dd",
- powderblue: "b0e0e6",
- purple: "800080",
- rebeccapurple: "663399",
- red: "f00",
- rosybrown: "bc8f8f",
- royalblue: "4169e1",
- saddlebrown: "8b4513",
- salmon: "fa8072",
- sandybrown: "f4a460",
- seagreen: "2e8b57",
- seashell: "fff5ee",
- sienna: "a0522d",
- silver: "c0c0c0",
- skyblue: "87ceeb",
- slateblue: "6a5acd",
- slategray: "708090",
- slategrey: "708090",
- snow: "fffafa",
- springgreen: "00ff7f",
- steelblue: "4682b4",
- tan: "d2b48c",
- teal: "008080",
- thistle: "d8bfd8",
- tomato: "ff6347",
- turquoise: "40e0d0",
- violet: "ee82ee",
- wheat: "f5deb3",
- white: "fff",
- whitesmoke: "f5f5f5",
- yellow: "ff0",
- yellowgreen: "9acd32"
- };
-
- // Make it easy to access colors via `hexNames[hex]`
- var hexNames = tinycolor.hexNames = flip(names);
-
-
- // Utilities
- // ---------
-
- // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
- function flip(o) {
- var flipped = { };
- for (var i in o) {
- if (o.hasOwnProperty(i)) {
- flipped[o[i]] = i;
- }
- }
- return flipped;
- }
-
- // Return a valid alpha value [0,1] with all invalid values being set to 1
- function boundAlpha(a) {
- a = parseFloat(a);
-
- if (isNaN(a) || a < 0 || a > 1) {
- a = 1;
- }
-
- return a;
- }
-
- // Take input from [0, n] and return it as [0, 1]
- function bound01(n, max) {
- if (isOnePointZero(n)) { n = "100%"; }
-
- var processPercent = isPercentage(n);
- n = mathMin(max, mathMax(0, parseFloat(n)));
-
- // Automatically convert percentage into number
- if (processPercent) {
- n = parseInt(n * max, 10) / 100;
- }
-
- // Handle floating point rounding errors
- if ((Math.abs(n - max) < 0.000001)) {
- return 1;
- }
-
- // Convert into [0, 1] range if it isn't already
- return (n % max) / parseFloat(max);
- }
-
- // Force a number between 0 and 1
- function clamp01(val) {
- return mathMin(1, mathMax(0, val));
- }
-
- // Parse a base-16 hex value into a base-10 integer
- function parseIntFromHex(val) {
- return parseInt(val, 16);
- }
-
- // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
- // <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
- function isOnePointZero(n) {
- return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
- }
-
- // Check to see if string passed in is a percentage
- function isPercentage(n) {
- return typeof n === "string" && n.indexOf('%') != -1;
- }
-
- // Force a hex value to have 2 characters
- function pad2(c) {
- return c.length == 1 ? '0' + c : '' + c;
- }
-
- // Replace a decimal with it's percentage value
- function convertToPercentage(n) {
- if (n <= 1) {
- n = (n * 100) + "%";
- }
-
- return n;
- }
-
- // Converts a decimal to a hex value
- function convertDecimalToHex(d) {
- return Math.round(parseFloat(d) * 255).toString(16);
- }
- // Converts a hex value to a decimal
- function convertHexToDecimal(h) {
- return (parseIntFromHex(h) / 255);
- }
-
- var matchers = (function() {
-
- // <http://www.w3.org/TR/css3-values/#integers>
- var CSS_INTEGER = "[-\\+]?\\d+%?";
-
- // <http://www.w3.org/TR/css3-values/#number-value>
- var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
-
- // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
- var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
-
- // Actual matching.
- // Parentheses and commas are optional, but not required.
- // Whitespace can take the place of commas or opening paren
- var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
- var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
-
- return {
- CSS_UNIT: new RegExp(CSS_UNIT),
- rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
- rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
- hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
- hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
- hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
- hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
- hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
- hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
- hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
- hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
- };
- })();
-
- // `isValidCSSUnit`
- // Take in a single string / number and check to see if it looks like a CSS unit
- // (see `matchers` above for definition).
- function isValidCSSUnit(color) {
- return !!matchers.CSS_UNIT.exec(color);
- }
-
- // `stringInputToObject`
- // Permissive string parsing. Take in a number of formats, and output an object
- // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
- function stringInputToObject(color) {
-
- color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
- var named = false;
- if (names[color]) {
- color = names[color];
- named = true;
- }
- else if (color == 'transparent') {
- return { r: 0, g: 0, b: 0, a: 0, format: "name" };
- }
-
- // Try to match string input using regular expressions.
- // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
- // Just return an object and let the conversion functions handle that.
- // This way the result will be the same whether the tinycolor is initialized with string or object.
- var match;
- if ((match = matchers.rgb.exec(color))) {
- return { r: match[1], g: match[2], b: match[3] };
- }
- if ((match = matchers.rgba.exec(color))) {
- return { r: match[1], g: match[2], b: match[3], a: match[4] };
- }
- if ((match = matchers.hsl.exec(color))) {
- return { h: match[1], s: match[2], l: match[3] };
- }
- if ((match = matchers.hsla.exec(color))) {
- return { h: match[1], s: match[2], l: match[3], a: match[4] };
- }
- if ((match = matchers.hsv.exec(color))) {
- return { h: match[1], s: match[2], v: match[3] };
- }
- if ((match = matchers.hsva.exec(color))) {
- return { h: match[1], s: match[2], v: match[3], a: match[4] };
- }
- if ((match = matchers.hex8.exec(color))) {
- return {
- r: parseIntFromHex(match[1]),
- g: parseIntFromHex(match[2]),
- b: parseIntFromHex(match[3]),
- a: convertHexToDecimal(match[4]),
- format: named ? "name" : "hex8"
- };
- }
- if ((match = matchers.hex6.exec(color))) {
- return {
- r: parseIntFromHex(match[1]),
- g: parseIntFromHex(match[2]),
- b: parseIntFromHex(match[3]),
- format: named ? "name" : "hex"
- };
- }
- if ((match = matchers.hex4.exec(color))) {
- return {
- r: parseIntFromHex(match[1] + '' + match[1]),
- g: parseIntFromHex(match[2] + '' + match[2]),
- b: parseIntFromHex(match[3] + '' + match[3]),
- a: convertHexToDecimal(match[4] + '' + match[4]),
- format: named ? "name" : "hex8"
- };
- }
- if ((match = matchers.hex3.exec(color))) {
- return {
- r: parseIntFromHex(match[1] + '' + match[1]),
- g: parseIntFromHex(match[2] + '' + match[2]),
- b: parseIntFromHex(match[3] + '' + match[3]),
- format: named ? "name" : "hex"
- };
- }
-
- return false;
- }
-
- function validateWCAG2Parms(parms) {
- // return valid WCAG2 parms for isReadable.
- // If input parms are invalid, return {"level":"AA", "size":"small"}
- var level, size;
- parms = parms || {"level":"AA", "size":"small"};
- level = (parms.level || "AA").toUpperCase();
- size = (parms.size || "small").toLowerCase();
- if (level !== "AA" && level !== "AAA") {
- level = "AA";
- }
- if (size !== "small" && size !== "large") {
- size = "small";
- }
- return {"level":level, "size":size};
- }
-
- // Node: Export function
- if (typeof module !== "undefined" && module.exports) {
- module.exports = tinycolor;
- }
- // AMD/requirejs: Define the module
- else if (typeof define === 'function' && define.amd) {
- define(function () {return tinycolor;});
- }
- // Browser: Expose to window
- else {
- window.tinycolor = tinycolor;
- }
-
- })(Math);
-
- },{}],35:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /**
- * All paths are tuned for maximum scalability of the arrowhead,
- * ie throughout arrowwidth=0.3..3 the head is joined smoothly
- * to the line, with the line coming from the left and ending at (0, 0).
- *
- * `backoff` is the distance to move the arrowhead and the end of the line,
- * in order that the arrowhead points to the desired place, either at
- * the tip of the arrow or (in the case of circle or square)
- * the center of the symbol.
- *
- * `noRotate`, if truthy, says that this arrowhead should not rotate with the
- * arrow. That's the case for squares, which should always be straight, and
- * circles, for which it's irrelevant.
- */
-
- module.exports = [
- // no arrow
- {
- path: '',
- backoff: 0
- },
- // wide with flat back
- {
- path: 'M-2.4,-3V3L0.6,0Z',
- backoff: 0.6
- },
- // narrower with flat back
- {
- path: 'M-3.7,-2.5V2.5L1.3,0Z',
- backoff: 1.3
- },
- // barbed
- {
- path: 'M-4.45,-3L-1.65,-0.2V0.2L-4.45,3L1.55,0Z',
- backoff: 1.55
- },
- // wide line-drawn
- {
- path: 'M-2.2,-2.2L-0.2,-0.2V0.2L-2.2,2.2L-1.4,3L1.6,0L-1.4,-3Z',
- backoff: 1.6
- },
- // narrower line-drawn
- {
- path: 'M-4.4,-2.1L-0.6,-0.2V0.2L-4.4,2.1L-4,3L2,0L-4,-3Z',
- backoff: 2
- },
- // circle
- {
- path: 'M2,0A2,2 0 1,1 0,-2A2,2 0 0,1 2,0Z',
- backoff: 0,
- noRotate: true
- },
- // square
- {
- path: 'M2,2V-2H-2V2Z',
- backoff: 0,
- noRotate: true
- }
- ];
-
- },{}],36:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var ARROWPATHS = _dereq_('./arrow_paths');
- var fontAttrs = _dereq_('../../plots/font_attributes');
- var cartesianConstants = _dereq_('../../plots/cartesian/constants');
- var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
-
-
- module.exports = templatedArray('annotation', {
- visible: {
- valType: 'boolean',
-
- dflt: true,
- editType: 'calc+arraydraw',
-
- },
-
- text: {
- valType: 'string',
-
- editType: 'calc+arraydraw',
-
- },
- textangle: {
- valType: 'angle',
- dflt: 0,
-
- editType: 'calc+arraydraw',
-
- },
- font: fontAttrs({
- editType: 'calc+arraydraw',
- colorEditType: 'arraydraw',
-
- }),
- width: {
- valType: 'number',
- min: 1,
- dflt: null,
-
- editType: 'calc+arraydraw',
-
- },
- height: {
- valType: 'number',
- min: 1,
- dflt: null,
-
- editType: 'calc+arraydraw',
-
- },
- opacity: {
- valType: 'number',
- min: 0,
- max: 1,
- dflt: 1,
-
- editType: 'arraydraw',
-
- },
- align: {
- valType: 'enumerated',
- values: ['left', 'center', 'right'],
- dflt: 'center',
-
- editType: 'arraydraw',
-
- },
- valign: {
- valType: 'enumerated',
- values: ['top', 'middle', 'bottom'],
- dflt: 'middle',
-
- editType: 'arraydraw',
-
- },
- bgcolor: {
- valType: 'color',
- dflt: 'rgba(0,0,0,0)',
-
- editType: 'arraydraw',
-
- },
- bordercolor: {
- valType: 'color',
- dflt: 'rgba(0,0,0,0)',
-
- editType: 'arraydraw',
-
- },
- borderpad: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
- editType: 'calc+arraydraw',
-
- },
- borderwidth: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
- editType: 'calc+arraydraw',
-
- },
- // arrow
- showarrow: {
- valType: 'boolean',
- dflt: true,
-
- editType: 'calc+arraydraw',
-
- },
- arrowcolor: {
- valType: 'color',
-
- editType: 'arraydraw',
-
- },
- arrowhead: {
- valType: 'integer',
- min: 0,
- max: ARROWPATHS.length,
- dflt: 1,
-
- editType: 'arraydraw',
-
- },
- startarrowhead: {
- valType: 'integer',
- min: 0,
- max: ARROWPATHS.length,
- dflt: 1,
-
- editType: 'arraydraw',
-
- },
- arrowside: {
- valType: 'flaglist',
- flags: ['end', 'start'],
- extras: ['none'],
- dflt: 'end',
-
- editType: 'arraydraw',
-
- },
- arrowsize: {
- valType: 'number',
- min: 0.3,
- dflt: 1,
-
- editType: 'calc+arraydraw',
-
- },
- startarrowsize: {
- valType: 'number',
- min: 0.3,
- dflt: 1,
-
- editType: 'calc+arraydraw',
-
- },
- arrowwidth: {
- valType: 'number',
- min: 0.1,
-
- editType: 'calc+arraydraw',
-
- },
- standoff: {
- valType: 'number',
- min: 0,
- dflt: 0,
-
- editType: 'calc+arraydraw',
-
- },
- startstandoff: {
- valType: 'number',
- min: 0,
- dflt: 0,
-
- editType: 'calc+arraydraw',
-
- },
- ax: {
- valType: 'any',
-
- editType: 'calc+arraydraw',
-
- },
- ay: {
- valType: 'any',
-
- editType: 'calc+arraydraw',
-
- },
- axref: {
- valType: 'enumerated',
- dflt: 'pixel',
- values: [
- 'pixel',
- cartesianConstants.idRegex.x.toString()
- ],
-
- editType: 'calc',
-
- },
- ayref: {
- valType: 'enumerated',
- dflt: 'pixel',
- values: [
- 'pixel',
- cartesianConstants.idRegex.y.toString()
- ],
-
- editType: 'calc',
-
- },
- // positioning
- xref: {
- valType: 'enumerated',
- values: [
- 'paper',
- cartesianConstants.idRegex.x.toString()
- ],
-
- editType: 'calc',
-
- },
- x: {
- valType: 'any',
-
- editType: 'calc+arraydraw',
-
- },
- xanchor: {
- valType: 'enumerated',
- values: ['auto', 'left', 'center', 'right'],
- dflt: 'auto',
-
- editType: 'calc+arraydraw',
-
- },
- xshift: {
- valType: 'number',
- dflt: 0,
-
- editType: 'calc+arraydraw',
-
- },
- yref: {
- valType: 'enumerated',
- values: [
- 'paper',
- cartesianConstants.idRegex.y.toString()
- ],
-
- editType: 'calc',
-
- },
- y: {
- valType: 'any',
-
- editType: 'calc+arraydraw',
-
- },
- yanchor: {
- valType: 'enumerated',
- values: ['auto', 'top', 'middle', 'bottom'],
- dflt: 'auto',
-
- editType: 'calc+arraydraw',
-
- },
- yshift: {
- valType: 'number',
- dflt: 0,
-
- editType: 'calc+arraydraw',
-
- },
- clicktoshow: {
- valType: 'enumerated',
- values: [false, 'onoff', 'onout'],
- dflt: false,
-
- editType: 'arraydraw',
-
- },
- xclick: {
- valType: 'any',
-
- editType: 'arraydraw',
-
- },
- yclick: {
- valType: 'any',
-
- editType: 'arraydraw',
-
- },
- hovertext: {
- valType: 'string',
-
- editType: 'arraydraw',
-
- },
- hoverlabel: {
- bgcolor: {
- valType: 'color',
-
- editType: 'arraydraw',
-
- },
- bordercolor: {
- valType: 'color',
-
- editType: 'arraydraw',
-
- },
- font: fontAttrs({
- editType: 'arraydraw',
-
- }),
- editType: 'arraydraw'
- },
- captureevents: {
- valType: 'boolean',
-
- editType: 'arraydraw',
-
- },
- editType: 'calc',
-
- _deprecated: {
- ref: {
- valType: 'string',
-
- editType: 'calc',
-
- }
- }
- });
-
- },{"../../plot_api/plot_template":202,"../../plots/cartesian/constants":218,"../../plots/font_attributes":239,"./arrow_paths":35}],37:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
-
- var draw = _dereq_('./draw').draw;
-
-
- module.exports = function calcAutorange(gd) {
- var fullLayout = gd._fullLayout;
- var annotationList = Lib.filterVisible(fullLayout.annotations);
-
- if(annotationList.length && gd._fullData.length) {
- return Lib.syncOrAsync([draw, annAutorange], gd);
- }
- };
-
- function annAutorange(gd) {
- var fullLayout = gd._fullLayout;
-
- // find the bounding boxes for each of these annotations'
- // relative to their anchor points
- // use the arrow and the text bg rectangle,
- // as the whole anno may include hidden text in its bbox
- Lib.filterVisible(fullLayout.annotations).forEach(function(ann) {
- var xa = Axes.getFromId(gd, ann.xref);
- var ya = Axes.getFromId(gd, ann.yref);
-
- ann._extremes = {};
- if(xa) calcAxisExpansion(ann, xa);
- if(ya) calcAxisExpansion(ann, ya);
- });
- }
-
- function calcAxisExpansion(ann, ax) {
- var axId = ax._id;
- var letter = axId.charAt(0);
- var pos = ann[letter];
- var apos = ann['a' + letter];
- var ref = ann[letter + 'ref'];
- var aref = ann['a' + letter + 'ref'];
- var padplus = ann['_' + letter + 'padplus'];
- var padminus = ann['_' + letter + 'padminus'];
- var shift = {x: 1, y: -1}[letter] * ann[letter + 'shift'];
- var headSize = 3 * ann.arrowsize * ann.arrowwidth || 0;
- var headPlus = headSize + shift;
- var headMinus = headSize - shift;
- var startHeadSize = 3 * ann.startarrowsize * ann.arrowwidth || 0;
- var startHeadPlus = startHeadSize + shift;
- var startHeadMinus = startHeadSize - shift;
- var extremes;
-
- if(aref === ref) {
- // expand for the arrowhead (padded by arrowhead)
- var extremeArrowHead = Axes.findExtremes(ax, [ax.r2c(pos)], {
- ppadplus: headPlus,
- ppadminus: headMinus
- });
- // again for the textbox (padded by textbox)
- var extremeText = Axes.findExtremes(ax, [ax.r2c(apos)], {
- ppadplus: Math.max(padplus, startHeadPlus),
- ppadminus: Math.max(padminus, startHeadMinus)
- });
- extremes = {
- min: [extremeArrowHead.min[0], extremeText.min[0]],
- max: [extremeArrowHead.max[0], extremeText.max[0]]
- };
- } else {
- startHeadPlus = apos ? startHeadPlus + apos : startHeadPlus;
- startHeadMinus = apos ? startHeadMinus - apos : startHeadMinus;
- extremes = Axes.findExtremes(ax, [ax.r2c(pos)], {
- ppadplus: Math.max(padplus, headPlus, startHeadPlus),
- ppadminus: Math.max(padminus, headMinus, startHeadMinus)
- });
- }
-
- ann._extremes[axId] = extremes;
- }
-
- },{"../../lib":168,"../../plots/cartesian/axes":212,"./draw":42}],38:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Registry = _dereq_('../../registry');
- var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
-
- module.exports = {
- hasClickToShow: hasClickToShow,
- onClick: onClick
- };
-
- /*
- * hasClickToShow: does the given hoverData have ANY annotations which will
- * turn ON if we click here? (used by hover events to set cursor)
- *
- * gd: graphDiv
- * hoverData: a hoverData array, as included with the *plotly_hover* or
- * *plotly_click* events in the `points` attribute
- *
- * returns: boolean
- */
- function hasClickToShow(gd, hoverData) {
- var sets = getToggleSets(gd, hoverData);
- return sets.on.length > 0 || sets.explicitOff.length > 0;
- }
-
- /*
- * onClick: perform the toggling (via Plotly.update) implied by clicking
- * at this hoverData
- *
- * gd: graphDiv
- * hoverData: a hoverData array, as included with the *plotly_hover* or
- * *plotly_click* events in the `points` attribute
- *
- * returns: Promise that the update is complete
- */
- function onClick(gd, hoverData) {
- var toggleSets = getToggleSets(gd, hoverData);
- var onSet = toggleSets.on;
- var offSet = toggleSets.off.concat(toggleSets.explicitOff);
- var update = {};
- var annotationsOut = gd._fullLayout.annotations;
- var i, editHelpers;
-
- if(!(onSet.length || offSet.length)) return;
-
- for(i = 0; i < onSet.length; i++) {
- editHelpers = arrayEditor(gd.layout, 'annotations', annotationsOut[onSet[i]]);
- editHelpers.modifyItem('visible', true);
- Lib.extendFlat(update, editHelpers.getUpdateObj());
- }
-
- for(i = 0; i < offSet.length; i++) {
- editHelpers = arrayEditor(gd.layout, 'annotations', annotationsOut[offSet[i]]);
- editHelpers.modifyItem('visible', false);
- Lib.extendFlat(update, editHelpers.getUpdateObj());
- }
-
- return Registry.call('update', gd, {}, update);
- }
-
- /*
- * getToggleSets: find the annotations which will turn on or off at this
- * hoverData
- *
- * gd: graphDiv
- * hoverData: a hoverData array, as included with the *plotly_hover* or
- * *plotly_click* events in the `points` attribute
- *
- * returns: {
- * on: Array (indices of annotations to turn on),
- * off: Array (indices to turn off because you're not hovering on them),
- * explicitOff: Array (indices to turn off because you *are* hovering on them)
- * }
- */
- function getToggleSets(gd, hoverData) {
- var annotations = gd._fullLayout.annotations;
- var onSet = [];
- var offSet = [];
- var explicitOffSet = [];
- var hoverLen = (hoverData || []).length;
-
- var i, j, anni, showMode, pointj, xa, ya, toggleType;
-
- for(i = 0; i < annotations.length; i++) {
- anni = annotations[i];
- showMode = anni.clicktoshow;
-
- if(showMode) {
- for(j = 0; j < hoverLen; j++) {
- pointj = hoverData[j];
- xa = pointj.xaxis;
- ya = pointj.yaxis;
-
- if(xa._id === anni.xref &&
- ya._id === anni.yref &&
- xa.d2r(pointj.x) === clickData2r(anni._xclick, xa) &&
- ya.d2r(pointj.y) === clickData2r(anni._yclick, ya)
- ) {
- // match! toggle this annotation
- // regardless of its clicktoshow mode
- // but if it's onout mode, off is implicit
- if(anni.visible) {
- if(showMode === 'onout') toggleType = offSet;
- else toggleType = explicitOffSet;
- }
- else {
- toggleType = onSet;
- }
- toggleType.push(i);
- break;
- }
- }
-
- if(j === hoverLen) {
- // no match - only turn this annotation OFF, and only if
- // showmode is 'onout'
- if(anni.visible && showMode === 'onout') offSet.push(i);
- }
- }
- }
-
- return {on: onSet, off: offSet, explicitOff: explicitOffSet};
- }
-
- // to handle log axes until v2
- function clickData2r(d, ax) {
- return ax.type === 'log' ? ax.l2r(d) : ax.d2r(d);
- }
-
- },{"../../lib":168,"../../plot_api/plot_template":202,"../../registry":257}],39:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Color = _dereq_('../color');
-
- // defaults common to 'annotations' and 'annotations3d'
- module.exports = function handleAnnotationCommonDefaults(annIn, annOut, fullLayout, coerce) {
- coerce('opacity');
- var bgColor = coerce('bgcolor');
-
- var borderColor = coerce('bordercolor');
- var borderOpacity = Color.opacity(borderColor);
-
- coerce('borderpad');
-
- var borderWidth = coerce('borderwidth');
- var showArrow = coerce('showarrow');
-
- coerce('text', showArrow ? ' ' : fullLayout._dfltTitle.annotation);
- coerce('textangle');
- Lib.coerceFont(coerce, 'font', fullLayout.font);
-
- coerce('width');
- coerce('align');
-
- var h = coerce('height');
- if(h) coerce('valign');
-
- if(showArrow) {
- var arrowside = coerce('arrowside');
- var arrowhead;
- var arrowsize;
-
- if(arrowside.indexOf('end') !== -1) {
- arrowhead = coerce('arrowhead');
- arrowsize = coerce('arrowsize');
- }
-
- if(arrowside.indexOf('start') !== -1) {
- coerce('startarrowhead', arrowhead);
- coerce('startarrowsize', arrowsize);
- }
- coerce('arrowcolor', borderOpacity ? annOut.bordercolor : Color.defaultLine);
- coerce('arrowwidth', ((borderOpacity && borderWidth) || 1) * 2);
- coerce('standoff');
- coerce('startstandoff');
-
- }
-
- var hoverText = coerce('hovertext');
- var globalHoverLabel = fullLayout.hoverlabel || {};
-
- if(hoverText) {
- var hoverBG = coerce('hoverlabel.bgcolor', globalHoverLabel.bgcolor ||
- (Color.opacity(bgColor) ? Color.rgb(bgColor) : Color.defaultLine)
- );
-
- var hoverBorder = coerce('hoverlabel.bordercolor', globalHoverLabel.bordercolor ||
- Color.contrast(hoverBG)
- );
-
- Lib.coerceFont(coerce, 'hoverlabel.font', {
- family: globalHoverLabel.font.family,
- size: globalHoverLabel.font.size,
- color: globalHoverLabel.font.color || hoverBorder
- });
- }
-
- coerce('captureevents', !!hoverText);
- };
-
- },{"../../lib":168,"../color":51}],40:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var toLogRange = _dereq_('../../lib/to_log_range');
-
- /*
- * convertCoords: when converting an axis between log and linear
- * you need to alter any annotations on that axis to keep them
- * pointing at the same data point.
- * In v2.0 this will become obsolete
- *
- * gd: the plot div
- * ax: the axis being changed
- * newType: the type it's getting
- * doExtra: function(attr, val) from inside relayout that sets the attribute.
- * Use this to make the changes as it's aware if any other changes in the
- * same relayout call should override this conversion.
- */
- module.exports = function convertCoords(gd, ax, newType, doExtra) {
- ax = ax || {};
-
- var toLog = (newType === 'log') && (ax.type === 'linear');
- var fromLog = (newType === 'linear') && (ax.type === 'log');
-
- if(!(toLog || fromLog)) return;
-
- var annotations = gd._fullLayout.annotations;
- var axLetter = ax._id.charAt(0);
- var ann;
- var attrPrefix;
-
- function convert(attr) {
- var currentVal = ann[attr];
- var newVal = null;
-
- if(toLog) newVal = toLogRange(currentVal, ax.range);
- else newVal = Math.pow(10, currentVal);
-
- // if conversion failed, delete the value so it gets a default value
- if(!isNumeric(newVal)) newVal = null;
-
- doExtra(attrPrefix + attr, newVal);
- }
-
- for(var i = 0; i < annotations.length; i++) {
- ann = annotations[i];
- attrPrefix = 'annotations[' + i + '].';
-
- if(ann[axLetter + 'ref'] === ax._id) convert(axLetter);
- if(ann['a' + axLetter + 'ref'] === ax._id) convert('a' + axLetter);
- }
- };
-
- },{"../../lib/to_log_range":191,"fast-isnumeric":18}],41:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
-
- var handleAnnotationCommonDefaults = _dereq_('./common_defaults');
- var attributes = _dereq_('./attributes');
-
-
- module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
- handleArrayContainerDefaults(layoutIn, layoutOut, {
- name: 'annotations',
- handleItemDefaults: handleAnnotationDefaults
- });
- };
-
- function handleAnnotationDefaults(annIn, annOut, fullLayout) {
- function coerce(attr, dflt) {
- return Lib.coerce(annIn, annOut, attributes, attr, dflt);
- }
-
- var visible = coerce('visible');
- var clickToShow = coerce('clicktoshow');
-
- if(!(visible || clickToShow)) return;
-
- handleAnnotationCommonDefaults(annIn, annOut, fullLayout, coerce);
-
- var showArrow = annOut.showarrow;
-
- // positioning
- var axLetters = ['x', 'y'];
- var arrowPosDflt = [-10, -30];
- var gdMock = {_fullLayout: fullLayout};
-
- for(var i = 0; i < 2; i++) {
- var axLetter = axLetters[i];
-
- // xref, yref
- var axRef = Axes.coerceRef(annIn, annOut, gdMock, axLetter, '', 'paper');
-
- if(axRef !== 'paper') {
- var ax = Axes.getFromId(gdMock, axRef);
- ax._annIndices.push(annOut._index);
- }
-
- // x, y
- Axes.coercePosition(annOut, gdMock, coerce, axRef, axLetter, 0.5);
-
- if(showArrow) {
- var arrowPosAttr = 'a' + axLetter;
- // axref, ayref
- var aaxRef = Axes.coerceRef(annIn, annOut, gdMock, arrowPosAttr, 'pixel');
-
- // for now the arrow can only be on the same axis or specified as pixels
- // TODO: sometime it might be interesting to allow it to be on *any* axis
- // but that would require updates to drawing & autorange code and maybe more
- if(aaxRef !== 'pixel' && aaxRef !== axRef) {
- aaxRef = annOut[arrowPosAttr] = 'pixel';
- }
-
- // ax, ay
- var aDflt = (aaxRef === 'pixel') ? arrowPosDflt[i] : 0.4;
- Axes.coercePosition(annOut, gdMock, coerce, aaxRef, arrowPosAttr, aDflt);
- }
-
- // xanchor, yanchor
- coerce(axLetter + 'anchor');
-
- // xshift, yshift
- coerce(axLetter + 'shift');
- }
-
- // if you have one coordinate you should have both
- Lib.noneOrAll(annIn, annOut, ['x', 'y']);
-
- // if you have one part of arrow length you should have both
- if(showArrow) {
- Lib.noneOrAll(annIn, annOut, ['ax', 'ay']);
- }
-
- if(clickToShow) {
- var xClick = coerce('xclick');
- var yClick = coerce('yclick');
-
- // put the actual click data to bind to into private attributes
- // so we don't have to do this little bit of logic on every hover event
- annOut._xclick = (xClick === undefined) ?
- annOut.x :
- Axes.cleanPosition(xClick, gdMock, annOut.xref);
- annOut._yclick = (yClick === undefined) ?
- annOut.y :
- Axes.cleanPosition(yClick, gdMock, annOut.yref);
- }
- }
-
- },{"../../lib":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"./attributes":36,"./common_defaults":39}],42:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Registry = _dereq_('../../registry');
- var Plots = _dereq_('../../plots/plots');
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var Color = _dereq_('../color');
- var Drawing = _dereq_('../drawing');
- var Fx = _dereq_('../fx');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var setCursor = _dereq_('../../lib/setcursor');
- var dragElement = _dereq_('../dragelement');
- var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
-
- var drawArrowHead = _dereq_('./draw_arrow_head');
-
- // Annotations are stored in gd.layout.annotations, an array of objects
- // index can point to one item in this array,
- // or non-numeric to simply add a new one
- // or -1 to modify all existing
- // opt can be the full options object, or one key (to be set to value)
- // or undefined to simply redraw
- // if opt is blank, val can be 'add' or a full options object to add a new
- // annotation at that point in the array, or 'remove' to delete this one
-
- module.exports = {
- draw: draw,
- drawOne: drawOne,
- drawRaw: drawRaw
- };
-
- /*
- * draw: draw all annotations without any new modifications
- */
- function draw(gd) {
- var fullLayout = gd._fullLayout;
-
- fullLayout._infolayer.selectAll('.annotation').remove();
-
- for(var i = 0; i < fullLayout.annotations.length; i++) {
- if(fullLayout.annotations[i].visible) {
- drawOne(gd, i);
- }
- }
-
- return Plots.previousPromises(gd);
- }
-
- /*
- * drawOne: draw a single cartesian or paper-ref annotation, potentially with modifications
- *
- * index (int): the annotation to draw
- */
- function drawOne(gd, index) {
- var fullLayout = gd._fullLayout;
- var options = fullLayout.annotations[index] || {};
- var xa = Axes.getFromId(gd, options.xref);
- var ya = Axes.getFromId(gd, options.yref);
-
- if(xa) xa.setScale();
- if(ya) ya.setScale();
-
- drawRaw(gd, options, index, false, xa, ya);
- }
-
- /**
- * drawRaw: draw a single annotation, potentially with modifications
- *
- * @param {DOM element} gd
- * @param {object} options : this annotation's fullLayout options
- * @param {integer} index : index in 'annotations' container of the annotation to draw
- * @param {string} subplotId : id of the annotation's subplot
- * - use false for 2d (i.e. cartesian or paper-ref) annotations
- * @param {object | undefined} xa : full x-axis object to compute subplot pos-to-px
- * @param {object | undefined} ya : ... y-axis
- */
- function drawRaw(gd, options, index, subplotId, xa, ya) {
- var fullLayout = gd._fullLayout;
- var gs = gd._fullLayout._size;
- var edits = gd._context.edits;
-
- var className, containerStr;
-
- if(subplotId) {
- className = 'annotation-' + subplotId;
- containerStr = subplotId + '.annotations';
- } else {
- className = 'annotation';
- containerStr = 'annotations';
- }
-
- var editHelpers = arrayEditor(gd.layout, containerStr, options);
- var modifyBase = editHelpers.modifyBase;
- var modifyItem = editHelpers.modifyItem;
- var getUpdateObj = editHelpers.getUpdateObj;
-
- // remove the existing annotation if there is one
- fullLayout._infolayer
- .selectAll('.' + className + '[data-index="' + index + '"]')
- .remove();
-
- var annClipID = 'clip' + fullLayout._uid + '_ann' + index;
-
- // this annotation is gone - quit now after deleting it
- // TODO: use d3 idioms instead of deleting and redrawing every time
- if(!options._input || options.visible === false) {
- d3.selectAll('#' + annClipID).remove();
- return;
- }
-
- // calculated pixel positions
- // x & y each will get text, head, and tail as appropriate
- var annPosPx = {x: {}, y: {}};
- var textangle = +options.textangle || 0;
-
- // create the components
- // made a single group to contain all, so opacity can work right
- // with border/arrow together this could handle a whole bunch of
- // cleanup at this point, but works for now
- var annGroup = fullLayout._infolayer.append('g')
- .classed(className, true)
- .attr('data-index', String(index))
- .style('opacity', options.opacity);
-
- // another group for text+background so that they can rotate together
- var annTextGroup = annGroup.append('g')
- .classed('annotation-text-g', true);
-
- var editTextPosition = edits[options.showarrow ? 'annotationTail' : 'annotationPosition'];
- var textEvents = options.captureevents || edits.annotationText || editTextPosition;
-
- var annTextGroupInner = annTextGroup.append('g')
- .style('pointer-events', textEvents ? 'all' : null)
- .call(setCursor, 'pointer')
- .on('click', function() {
- gd._dragging = false;
-
- var eventData = {
- index: index,
- annotation: options._input,
- fullAnnotation: options,
- event: d3.event
- };
-
- if(subplotId) {
- eventData.subplotId = subplotId;
- }
-
- gd.emit('plotly_clickannotation', eventData);
- });
-
- if(options.hovertext) {
- annTextGroupInner
- .on('mouseover', function() {
- var hoverOptions = options.hoverlabel;
- var hoverFont = hoverOptions.font;
- var bBox = this.getBoundingClientRect();
- var bBoxRef = gd.getBoundingClientRect();
-
- Fx.loneHover({
- x0: bBox.left - bBoxRef.left,
- x1: bBox.right - bBoxRef.left,
- y: (bBox.top + bBox.bottom) / 2 - bBoxRef.top,
- text: options.hovertext,
- color: hoverOptions.bgcolor,
- borderColor: hoverOptions.bordercolor,
- fontFamily: hoverFont.family,
- fontSize: hoverFont.size,
- fontColor: hoverFont.color
- }, {
- container: fullLayout._hoverlayer.node(),
- outerContainer: fullLayout._paper.node(),
- gd: gd
- });
- })
- .on('mouseout', function() {
- Fx.loneUnhover(fullLayout._hoverlayer.node());
- });
- }
-
- var borderwidth = options.borderwidth;
- var borderpad = options.borderpad;
- var borderfull = borderwidth + borderpad;
-
- var annTextBG = annTextGroupInner.append('rect')
- .attr('class', 'bg')
- .style('stroke-width', borderwidth + 'px')
- .call(Color.stroke, options.bordercolor)
- .call(Color.fill, options.bgcolor);
-
- var isSizeConstrained = options.width || options.height;
-
- var annTextClip = fullLayout._topclips
- .selectAll('#' + annClipID)
- .data(isSizeConstrained ? [0] : []);
-
- annTextClip.enter().append('clipPath')
- .classed('annclip', true)
- .attr('id', annClipID)
- .append('rect');
- annTextClip.exit().remove();
-
- var font = options.font;
-
- var text = fullLayout.meta ?
- Lib.templateString(options.text, {meta: fullLayout.meta}) :
- options.text;
-
- var annText = annTextGroupInner.append('text')
- .classed('annotation-text', true)
- .text(text);
-
- function textLayout(s) {
- s.call(Drawing.font, font)
- .attr({
- 'text-anchor': {
- left: 'start',
- right: 'end'
- }[options.align] || 'middle'
- });
-
- svgTextUtils.convertToTspans(s, gd, drawGraphicalElements);
- return s;
- }
-
- function drawGraphicalElements() {
- // if the text has *only* a link, make the whole box into a link
- var anchor3 = annText.selectAll('a');
- if(anchor3.size() === 1 && anchor3.text() === annText.text()) {
- var wholeLink = annTextGroupInner.insert('a', ':first-child').attr({
- 'xlink:xlink:href': anchor3.attr('xlink:href'),
- 'xlink:xlink:show': anchor3.attr('xlink:show')
- })
- .style({cursor: 'pointer'});
-
- wholeLink.node().appendChild(annTextBG.node());
- }
-
- var mathjaxGroup = annTextGroupInner.select('.annotation-text-math-group');
- var hasMathjax = !mathjaxGroup.empty();
- var anntextBB = Drawing.bBox(
- (hasMathjax ? mathjaxGroup : annText).node());
- var textWidth = anntextBB.width;
- var textHeight = anntextBB.height;
- var annWidth = options.width || textWidth;
- var annHeight = options.height || textHeight;
- var outerWidth = Math.round(annWidth + 2 * borderfull);
- var outerHeight = Math.round(annHeight + 2 * borderfull);
-
- function shiftFraction(v, anchor) {
- if(anchor === 'auto') {
- if(v < 1 / 3) anchor = 'left';
- else if(v > 2 / 3) anchor = 'right';
- else anchor = 'center';
- }
- return {
- center: 0,
- middle: 0,
- left: 0.5,
- bottom: -0.5,
- right: -0.5,
- top: 0.5
- }[anchor];
- }
-
- var annotationIsOffscreen = false;
- var letters = ['x', 'y'];
-
- for(var i = 0; i < letters.length; i++) {
- var axLetter = letters[i];
- var axRef = options[axLetter + 'ref'] || axLetter;
- var tailRef = options['a' + axLetter + 'ref'];
- var ax = {x: xa, y: ya}[axLetter];
- var dimAngle = (textangle + (axLetter === 'x' ? 0 : -90)) * Math.PI / 180;
- // note that these two can be either positive or negative
- var annSizeFromWidth = outerWidth * Math.cos(dimAngle);
- var annSizeFromHeight = outerHeight * Math.sin(dimAngle);
- // but this one is the positive total size
- var annSize = Math.abs(annSizeFromWidth) + Math.abs(annSizeFromHeight);
- var anchor = options[axLetter + 'anchor'];
- var overallShift = options[axLetter + 'shift'] * (axLetter === 'x' ? 1 : -1);
- var posPx = annPosPx[axLetter];
- var basePx;
- var textPadShift;
- var alignPosition;
- var autoAlignFraction;
- var textShift;
-
- /*
- * calculate the *primary* pixel position
- * which is the arrowhead if there is one,
- * otherwise the text anchor point
- */
- if(ax) {
- // check if annotation is off screen, to bypass DOM manipulations
- var posFraction = ax.r2fraction(options[axLetter]);
- if(posFraction < 0 || posFraction > 1) {
- if(tailRef === axRef) {
- posFraction = ax.r2fraction(options['a' + axLetter]);
- if(posFraction < 0 || posFraction > 1) {
- annotationIsOffscreen = true;
- }
- } else {
- annotationIsOffscreen = true;
- }
- }
- basePx = ax._offset + ax.r2p(options[axLetter]);
- autoAlignFraction = 0.5;
- }
- else {
- if(axLetter === 'x') {
- alignPosition = options[axLetter];
- basePx = gs.l + gs.w * alignPosition;
- }
- else {
- alignPosition = 1 - options[axLetter];
- basePx = gs.t + gs.h * alignPosition;
- }
- autoAlignFraction = options.showarrow ? 0.5 : alignPosition;
- }
-
- // now translate this into pixel positions of head, tail, and text
- // as well as paddings for autorange
- if(options.showarrow) {
- posPx.head = basePx;
-
- var arrowLength = options['a' + axLetter];
-
- // with an arrow, the text rotates around the anchor point
- textShift = annSizeFromWidth * shiftFraction(0.5, options.xanchor) -
- annSizeFromHeight * shiftFraction(0.5, options.yanchor);
-
- if(tailRef === axRef) {
- posPx.tail = ax._offset + ax.r2p(arrowLength);
- // tail is data-referenced: autorange pads the text in px from the tail
- textPadShift = textShift;
- }
- else {
- posPx.tail = basePx + arrowLength;
- // tail is specified in px from head, so autorange also pads vs head
- textPadShift = textShift + arrowLength;
- }
-
- posPx.text = posPx.tail + textShift;
-
- // constrain pixel/paper referenced so the draggers are at least
- // partially visible
- var maxPx = fullLayout[(axLetter === 'x') ? 'width' : 'height'];
- if(axRef === 'paper') {
- posPx.head = Lib.constrain(posPx.head, 1, maxPx - 1);
- }
- if(tailRef === 'pixel') {
- var shiftPlus = -Math.max(posPx.tail - 3, posPx.text);
- var shiftMinus = Math.min(posPx.tail + 3, posPx.text) - maxPx;
- if(shiftPlus > 0) {
- posPx.tail += shiftPlus;
- posPx.text += shiftPlus;
- }
- else if(shiftMinus > 0) {
- posPx.tail -= shiftMinus;
- posPx.text -= shiftMinus;
- }
- }
-
- posPx.tail += overallShift;
- posPx.head += overallShift;
- }
- else {
- // with no arrow, the text rotates and *then* we put the anchor
- // relative to the new bounding box
- textShift = annSize * shiftFraction(autoAlignFraction, anchor);
- textPadShift = textShift;
- posPx.text = basePx + textShift;
- }
-
- posPx.text += overallShift;
- textShift += overallShift;
- textPadShift += overallShift;
-
- // padplus/minus are used by autorange
- options['_' + axLetter + 'padplus'] = (annSize / 2) + textPadShift;
- options['_' + axLetter + 'padminus'] = (annSize / 2) - textPadShift;
-
- // size/shift are used during dragging
- options['_' + axLetter + 'size'] = annSize;
- options['_' + axLetter + 'shift'] = textShift;
- }
-
- // We have everything we need for calcAutorange at this point,
- // we can safely exit - unless we're currently dragging the plot
- if(!gd._dragging && annotationIsOffscreen) {
- annTextGroupInner.remove();
- return;
- }
-
- var xShift = 0;
- var yShift = 0;
-
- if(options.align !== 'left') {
- xShift = (annWidth - textWidth) * (options.align === 'center' ? 0.5 : 1);
- }
- if(options.valign !== 'top') {
- yShift = (annHeight - textHeight) * (options.valign === 'middle' ? 0.5 : 1);
- }
-
- if(hasMathjax) {
- mathjaxGroup.select('svg').attr({
- x: borderfull + xShift - 1,
- y: borderfull + yShift
- })
- .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
- }
- else {
- var texty = borderfull + yShift - anntextBB.top;
- var textx = borderfull + xShift - anntextBB.left;
-
- annText.call(svgTextUtils.positionText, textx, texty)
- .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
- }
-
- annTextClip.select('rect').call(Drawing.setRect, borderfull, borderfull,
- annWidth, annHeight);
-
- annTextBG.call(Drawing.setRect, borderwidth / 2, borderwidth / 2,
- outerWidth - borderwidth, outerHeight - borderwidth);
-
- annTextGroupInner.call(Drawing.setTranslate,
- Math.round(annPosPx.x.text - outerWidth / 2),
- Math.round(annPosPx.y.text - outerHeight / 2));
-
- /*
- * rotate text and background
- * we already calculated the text center position *as rotated*
- * because we needed that for autoranging anyway, so now whether
- * we have an arrow or not, we rotate about the text center.
- */
- annTextGroup.attr({transform: 'rotate(' + textangle + ',' +
- annPosPx.x.text + ',' + annPosPx.y.text + ')'});
-
- /*
- * add the arrow
- * uses options[arrowwidth,arrowcolor,arrowhead] for styling
- * dx and dy are normally zero, but when you are dragging the textbox
- * while the head stays put, dx and dy are the pixel offsets
- */
- var drawArrow = function(dx, dy) {
- annGroup
- .selectAll('.annotation-arrow-g')
- .remove();
-
- var headX = annPosPx.x.head;
- var headY = annPosPx.y.head;
- var tailX = annPosPx.x.tail + dx;
- var tailY = annPosPx.y.tail + dy;
- var textX = annPosPx.x.text + dx;
- var textY = annPosPx.y.text + dy;
-
- // find the edge of the text box, where we'll start the arrow:
- // create transform matrix to rotate the text box corners
- var transform = Lib.rotationXYMatrix(textangle, textX, textY);
- var applyTransform = Lib.apply2DTransform(transform);
- var applyTransform2 = Lib.apply2DTransform2(transform);
-
- // calculate and transform bounding box
- var width = +annTextBG.attr('width');
- var height = +annTextBG.attr('height');
- var xLeft = textX - 0.5 * width;
- var xRight = xLeft + width;
- var yTop = textY - 0.5 * height;
- var yBottom = yTop + height;
- var edges = [
- [xLeft, yTop, xLeft, yBottom],
- [xLeft, yBottom, xRight, yBottom],
- [xRight, yBottom, xRight, yTop],
- [xRight, yTop, xLeft, yTop]
- ].map(applyTransform2);
-
- // Remove the line if it ends inside the box. Use ray
- // casting for rotated boxes: see which edges intersect a
- // line from the arrowhead to far away and reduce with xor
- // to get the parity of the number of intersections.
- if(edges.reduce(function(a, x) {
- return a ^
- !!Lib.segmentsIntersect(headX, headY, headX + 1e6, headY + 1e6,
- x[0], x[1], x[2], x[3]);
- }, false)) {
- // no line or arrow - so quit drawArrow now
- return;
- }
-
- edges.forEach(function(x) {
- var p = Lib.segmentsIntersect(tailX, tailY, headX, headY,
- x[0], x[1], x[2], x[3]);
- if(p) {
- tailX = p.x;
- tailY = p.y;
- }
- });
-
- var strokewidth = options.arrowwidth;
- var arrowColor = options.arrowcolor;
- var arrowSide = options.arrowside;
-
- var arrowGroup = annGroup.append('g')
- .style({opacity: Color.opacity(arrowColor)})
- .classed('annotation-arrow-g', true);
-
- var arrow = arrowGroup.append('path')
- .attr('d', 'M' + tailX + ',' + tailY + 'L' + headX + ',' + headY)
- .style('stroke-width', strokewidth + 'px')
- .call(Color.stroke, Color.rgb(arrowColor));
-
- drawArrowHead(arrow, arrowSide, options);
-
- // the arrow dragger is a small square right at the head, then a line to the tail,
- // all expanded by a stroke width of 6px plus the arrow line width
- if(edits.annotationPosition && arrow.node().parentNode && !subplotId) {
- var arrowDragHeadX = headX;
- var arrowDragHeadY = headY;
- if(options.standoff) {
- var arrowLength = Math.sqrt(Math.pow(headX - tailX, 2) + Math.pow(headY - tailY, 2));
- arrowDragHeadX += options.standoff * (tailX - headX) / arrowLength;
- arrowDragHeadY += options.standoff * (tailY - headY) / arrowLength;
- }
- var arrowDrag = arrowGroup.append('path')
- .classed('annotation-arrow', true)
- .classed('anndrag', true)
- .classed('cursor-move', true)
- .attr({
- d: 'M3,3H-3V-3H3ZM0,0L' + (tailX - arrowDragHeadX) + ',' + (tailY - arrowDragHeadY),
- transform: 'translate(' + arrowDragHeadX + ',' + arrowDragHeadY + ')'
- })
- .style('stroke-width', (strokewidth + 6) + 'px')
- .call(Color.stroke, 'rgba(0,0,0,0)')
- .call(Color.fill, 'rgba(0,0,0,0)');
-
- var annx0, anny0;
-
- // dragger for the arrow & head: translates the whole thing
- // (head/tail/text) all together
- dragElement.init({
- element: arrowDrag.node(),
- gd: gd,
- prepFn: function() {
- var pos = Drawing.getTranslate(annTextGroupInner);
-
- annx0 = pos.x;
- anny0 = pos.y;
- if(xa && xa.autorange) {
- modifyBase(xa._name + '.autorange', true);
- }
- if(ya && ya.autorange) {
- modifyBase(ya._name + '.autorange', true);
- }
- },
- moveFn: function(dx, dy) {
- var annxy0 = applyTransform(annx0, anny0);
- var xcenter = annxy0[0] + dx;
- var ycenter = annxy0[1] + dy;
- annTextGroupInner.call(Drawing.setTranslate, xcenter, ycenter);
-
- modifyItem('x', xa ?
- xa.p2r(xa.r2p(options.x) + dx) :
- (options.x + (dx / gs.w)));
- modifyItem('y', ya ?
- ya.p2r(ya.r2p(options.y) + dy) :
- (options.y - (dy / gs.h)));
-
- if(options.axref === options.xref) {
- modifyItem('ax', xa.p2r(xa.r2p(options.ax) + dx));
- }
-
- if(options.ayref === options.yref) {
- modifyItem('ay', ya.p2r(ya.r2p(options.ay) + dy));
- }
-
- arrowGroup.attr('transform', 'translate(' + dx + ',' + dy + ')');
- annTextGroup.attr({
- transform: 'rotate(' + textangle + ',' +
- xcenter + ',' + ycenter + ')'
- });
- },
- doneFn: function() {
- Registry.call('_guiRelayout', gd, getUpdateObj());
- var notesBox = document.querySelector('.js-notes-box-panel');
- if(notesBox) notesBox.redraw(notesBox.selectedObj);
- }
- });
- }
- };
-
- if(options.showarrow) drawArrow(0, 0);
-
- // user dragging the annotation (text, not arrow)
- if(editTextPosition) {
- var baseTextTransform;
-
- // dragger for the textbox: if there's an arrow, just drag the
- // textbox and tail, leave the head untouched
- dragElement.init({
- element: annTextGroupInner.node(),
- gd: gd,
- prepFn: function() {
- baseTextTransform = annTextGroup.attr('transform');
- },
- moveFn: function(dx, dy) {
- var csr = 'pointer';
- if(options.showarrow) {
- if(options.axref === options.xref) {
- modifyItem('ax', xa.p2r(xa.r2p(options.ax) + dx));
- } else {
- modifyItem('ax', options.ax + dx);
- }
-
- if(options.ayref === options.yref) {
- modifyItem('ay', ya.p2r(ya.r2p(options.ay) + dy));
- } else {
- modifyItem('ay', options.ay + dy);
- }
-
- drawArrow(dx, dy);
- }
- else if(!subplotId) {
- var xUpdate, yUpdate;
- if(xa) {
- xUpdate = xa.p2r(xa.r2p(options.x) + dx);
-
- } else {
- var widthFraction = options._xsize / gs.w;
- var xLeft = options.x + (options._xshift - options.xshift) / gs.w - widthFraction / 2;
-
- xUpdate = dragElement.align(xLeft + dx / gs.w,
- widthFraction, 0, 1, options.xanchor);
- }
-
- if(ya) {
- yUpdate = ya.p2r(ya.r2p(options.y) + dy);
- } else {
- var heightFraction = options._ysize / gs.h;
- var yBottom = options.y - (options._yshift + options.yshift) / gs.h - heightFraction / 2;
-
- yUpdate = dragElement.align(yBottom - dy / gs.h,
- heightFraction, 0, 1, options.yanchor);
- }
- modifyItem('x', xUpdate);
- modifyItem('y', yUpdate);
- if(!xa || !ya) {
- csr = dragElement.getCursor(
- xa ? 0.5 : xUpdate,
- ya ? 0.5 : yUpdate,
- options.xanchor, options.yanchor
- );
- }
- }
- else return;
-
- annTextGroup.attr({
- transform: 'translate(' + dx + ',' + dy + ')' + baseTextTransform
- });
-
- setCursor(annTextGroupInner, csr);
- },
- doneFn: function() {
- setCursor(annTextGroupInner);
- Registry.call('_guiRelayout', gd, getUpdateObj());
- var notesBox = document.querySelector('.js-notes-box-panel');
- if(notesBox) notesBox.redraw(notesBox.selectedObj);
- }
- });
- }
- }
-
- if(edits.annotationText) {
- annText.call(svgTextUtils.makeEditable, {delegate: annTextGroupInner, gd: gd})
- .call(textLayout)
- .on('edit', function(_text) {
- options.text = _text;
-
- this.call(textLayout);
-
- modifyItem('text', _text);
-
- if(xa && xa.autorange) {
- modifyBase(xa._name + '.autorange', true);
- }
- if(ya && ya.autorange) {
- modifyBase(ya._name + '.autorange', true);
- }
-
- Registry.call('_guiRelayout', gd, getUpdateObj());
- });
- }
- else annText.call(textLayout);
- }
-
- },{"../../lib":168,"../../lib/setcursor":187,"../../lib/svg_text_utils":189,"../../plot_api/plot_template":202,"../../plots/cartesian/axes":212,"../../plots/plots":245,"../../registry":257,"../color":51,"../dragelement":69,"../drawing":72,"../fx":90,"./draw_arrow_head":43,"d3":16}],43:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Color = _dereq_('../color');
-
- var ARROWPATHS = _dereq_('./arrow_paths');
-
- /**
- * Add arrowhead(s) to a path or line element
- *
- * @param {d3.selection} el3: a d3-selected line or path element
- *
- * @param {string} ends: 'none', 'start', 'end', or 'start+end' for which ends get arrowheads
- *
- * @param {object} options: style information. Must have all the following:
- * @param {number} options.arrowhead: end head style - see ./arrow_paths
- * @param {number} options.startarrowhead: start head style - see ./arrow_paths
- * @param {number} options.arrowsize: relative size of the end head vs line width
- * @param {number} options.startarrowsize: relative size of the start head vs line width
- * @param {number} options.standoff: distance in px to move the end arrow point from its target
- * @param {number} options.startstandoff: distance in px to move the start arrow point from its target
- * @param {number} options.arrowwidth: width of the arrow line
- * @param {string} options.arrowcolor: color of the arrow line, for the head to match
- * Note that the opacity of this color is ignored, as it's assumed the container
- * of both the line and head has opacity applied to it so there isn't greater opacity
- * where they overlap.
- */
- module.exports = function drawArrowHead(el3, ends, options) {
- var el = el3.node();
- var headStyle = ARROWPATHS[options.arrowhead || 0];
- var startHeadStyle = ARROWPATHS[options.startarrowhead || 0];
- var scale = (options.arrowwidth || 1) * (options.arrowsize || 1);
- var startScale = (options.arrowwidth || 1) * (options.startarrowsize || 1);
- var doStart = ends.indexOf('start') >= 0;
- var doEnd = ends.indexOf('end') >= 0;
- var backOff = headStyle.backoff * scale + options.standoff;
- var startBackOff = startHeadStyle.backoff * startScale + options.startstandoff;
-
- var start, end, startRot, endRot;
-
- if(el.nodeName === 'line') {
- start = {x: +el3.attr('x1'), y: +el3.attr('y1')};
- end = {x: +el3.attr('x2'), y: +el3.attr('y2')};
-
- var dx = start.x - end.x;
- var dy = start.y - end.y;
-
- startRot = Math.atan2(dy, dx);
- endRot = startRot + Math.PI;
- if(backOff && startBackOff) {
- if(backOff + startBackOff > Math.sqrt(dx * dx + dy * dy)) {
- hideLine();
- return;
- }
- }
-
- if(backOff) {
- if(backOff * backOff > dx * dx + dy * dy) {
- hideLine();
- return;
- }
- var backOffX = backOff * Math.cos(startRot);
- var backOffY = backOff * Math.sin(startRot);
-
- end.x += backOffX;
- end.y += backOffY;
- el3.attr({x2: end.x, y2: end.y});
-
- }
-
- if(startBackOff) {
- if(startBackOff * startBackOff > dx * dx + dy * dy) {
- hideLine();
- return;
- }
- var startBackOffX = startBackOff * Math.cos(startRot);
- var startbackOffY = startBackOff * Math.sin(startRot);
-
- start.x -= startBackOffX;
- start.y -= startbackOffY;
- el3.attr({x1: start.x, y1: start.y});
-
- }
- }
- else if(el.nodeName === 'path') {
- var pathlen = el.getTotalLength();
- // using dash to hide the backOff region of the path.
- // if we ever allow dash for the arrow we'll have to
- // do better than this hack... maybe just manually
- // combine the two
- var dashArray = '';
-
- if(pathlen < backOff + startBackOff) {
- hideLine();
- return;
- }
-
-
- var start0 = el.getPointAtLength(0);
- var dstart = el.getPointAtLength(0.1);
-
- startRot = Math.atan2(start0.y - dstart.y, start0.x - dstart.x);
- start = el.getPointAtLength(Math.min(startBackOff, pathlen));
-
- dashArray = '0px,' + startBackOff + 'px,';
-
- var end0 = el.getPointAtLength(pathlen);
- var dend = el.getPointAtLength(pathlen - 0.1);
-
- endRot = Math.atan2(end0.y - dend.y, end0.x - dend.x);
- end = el.getPointAtLength(Math.max(0, pathlen - backOff));
-
- var shortening = dashArray ? startBackOff + backOff : backOff;
- dashArray += (pathlen - shortening) + 'px,' + pathlen + 'px';
-
- el3.style('stroke-dasharray', dashArray);
- }
-
- function hideLine() { el3.style('stroke-dasharray', '0px,100px'); }
-
- function drawhead(arrowHeadStyle, p, rot, arrowScale) {
- if(!arrowHeadStyle.path) return;
- if(arrowHeadStyle.noRotate) rot = 0;
-
- d3.select(el.parentNode).append('path')
- .attr({
- 'class': el3.attr('class'),
- d: arrowHeadStyle.path,
- transform:
- 'translate(' + p.x + ',' + p.y + ')' +
- (rot ? 'rotate(' + (rot * 180 / Math.PI) + ')' : '') +
- 'scale(' + arrowScale + ')'
- })
- .style({
- fill: Color.rgb(options.arrowcolor),
- 'stroke-width': 0
- });
- }
-
- if(doStart) drawhead(startHeadStyle, start, startRot, startScale);
- if(doEnd) drawhead(headStyle, end, endRot, scale);
- };
-
- },{"../color":51,"./arrow_paths":35,"d3":16}],44:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var drawModule = _dereq_('./draw');
- var clickModule = _dereq_('./click');
-
- module.exports = {
- moduleType: 'component',
- name: 'annotations',
-
- layoutAttributes: _dereq_('./attributes'),
- supplyLayoutDefaults: _dereq_('./defaults'),
- includeBasePlot: _dereq_('../../plots/cartesian/include_components')('annotations'),
-
- calcAutorange: _dereq_('./calc_autorange'),
- draw: drawModule.draw,
- drawOne: drawModule.drawOne,
- drawRaw: drawModule.drawRaw,
-
- hasClickToShow: clickModule.hasClickToShow,
- onClick: clickModule.onClick,
-
- convertCoords: _dereq_('./convert_coords')
- };
-
- },{"../../plots/cartesian/include_components":223,"./attributes":36,"./calc_autorange":37,"./click":38,"./convert_coords":40,"./defaults":41,"./draw":42}],45:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var annAtts = _dereq_('../annotations/attributes');
- var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
- var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
-
- module.exports = overrideAll(templatedArray('annotation', {
- visible: annAtts.visible,
- x: {
- valType: 'any',
-
-
- },
- y: {
- valType: 'any',
-
-
- },
- z: {
- valType: 'any',
-
-
- },
- ax: {
- valType: 'number',
-
-
- },
- ay: {
- valType: 'number',
-
-
- },
-
- xanchor: annAtts.xanchor,
- xshift: annAtts.xshift,
- yanchor: annAtts.yanchor,
- yshift: annAtts.yshift,
-
- text: annAtts.text,
- textangle: annAtts.textangle,
- font: annAtts.font,
- width: annAtts.width,
- height: annAtts.height,
- opacity: annAtts.opacity,
- align: annAtts.align,
- valign: annAtts.valign,
- bgcolor: annAtts.bgcolor,
- bordercolor: annAtts.bordercolor,
- borderpad: annAtts.borderpad,
- borderwidth: annAtts.borderwidth,
- showarrow: annAtts.showarrow,
- arrowcolor: annAtts.arrowcolor,
- arrowhead: annAtts.arrowhead,
- startarrowhead: annAtts.startarrowhead,
- arrowside: annAtts.arrowside,
- arrowsize: annAtts.arrowsize,
- startarrowsize: annAtts.startarrowsize,
- arrowwidth: annAtts.arrowwidth,
- standoff: annAtts.standoff,
- startstandoff: annAtts.startstandoff,
- hovertext: annAtts.hovertext,
- hoverlabel: annAtts.hoverlabel,
- captureevents: annAtts.captureevents,
-
- // maybes later?
- // clicktoshow: annAtts.clicktoshow,
- // xclick: annAtts.xclick,
- // yclick: annAtts.yclick,
-
- // not needed!
- // axref: 'pixel'
- // ayref: 'pixel'
- // xref: 'x'
- // yref: 'y
- // zref: 'z'
- }), 'calc', 'from-root');
-
- },{"../../plot_api/edit_types":195,"../../plot_api/plot_template":202,"../annotations/attributes":36}],46:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
-
- module.exports = function convert(scene) {
- var fullSceneLayout = scene.fullSceneLayout;
- var anns = fullSceneLayout.annotations;
-
- for(var i = 0; i < anns.length; i++) {
- mockAnnAxes(anns[i], scene);
- }
-
- scene.fullLayout._infolayer
- .selectAll('.annotation-' + scene.id)
- .remove();
- };
-
- function mockAnnAxes(ann, scene) {
- var fullSceneLayout = scene.fullSceneLayout;
- var domain = fullSceneLayout.domain;
- var size = scene.fullLayout._size;
-
- var base = {
- // this gets fill in on render
- pdata: null,
-
- // to get setConvert to not execute cleanly
- type: 'linear',
-
- // don't try to update them on `editable: true`
- autorange: false,
-
- // set infinite range so that annotation draw routine
- // does not try to remove 'outside-range' annotations,
- // this case is handled in the render loop
- range: [-Infinity, Infinity]
- };
-
- ann._xa = {};
- Lib.extendFlat(ann._xa, base);
- Axes.setConvert(ann._xa);
- ann._xa._offset = size.l + domain.x[0] * size.w;
- ann._xa.l2p = function() {
- return 0.5 * (1 + ann._pdata[0] / ann._pdata[3]) * size.w * (domain.x[1] - domain.x[0]);
- };
-
- ann._ya = {};
- Lib.extendFlat(ann._ya, base);
- Axes.setConvert(ann._ya);
- ann._ya._offset = size.t + (1 - domain.y[1]) * size.h;
- ann._ya.l2p = function() {
- return 0.5 * (1 - ann._pdata[1] / ann._pdata[3]) * size.h * (domain.y[1] - domain.y[0]);
- };
- }
-
- },{"../../lib":168,"../../plots/cartesian/axes":212}],47:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
- var handleAnnotationCommonDefaults = _dereq_('../annotations/common_defaults');
- var attributes = _dereq_('./attributes');
-
- module.exports = function handleDefaults(sceneLayoutIn, sceneLayoutOut, opts) {
- handleArrayContainerDefaults(sceneLayoutIn, sceneLayoutOut, {
- name: 'annotations',
- handleItemDefaults: handleAnnotationDefaults,
- fullLayout: opts.fullLayout
- });
- };
-
- function handleAnnotationDefaults(annIn, annOut, sceneLayout, opts) {
- function coerce(attr, dflt) {
- return Lib.coerce(annIn, annOut, attributes, attr, dflt);
- }
-
- function coercePosition(axLetter) {
- var axName = axLetter + 'axis';
-
- // mock in such way that getFromId grabs correct 3D axis
- var gdMock = { _fullLayout: {} };
- gdMock._fullLayout[axName] = sceneLayout[axName];
-
- return Axes.coercePosition(annOut, gdMock, coerce, axLetter, axLetter, 0.5);
- }
-
-
- var visible = coerce('visible');
- if(!visible) return;
-
- handleAnnotationCommonDefaults(annIn, annOut, opts.fullLayout, coerce);
-
- coercePosition('x');
- coercePosition('y');
- coercePosition('z');
-
- // if you have one coordinate you should all three
- Lib.noneOrAll(annIn, annOut, ['x', 'y', 'z']);
-
- // hard-set here for completeness
- annOut.xref = 'x';
- annOut.yref = 'y';
- annOut.zref = 'z';
-
- coerce('xanchor');
- coerce('yanchor');
- coerce('xshift');
- coerce('yshift');
-
- if(annOut.showarrow) {
- annOut.axref = 'pixel';
- annOut.ayref = 'pixel';
-
- // TODO maybe default values should be bigger than the 2D case?
- coerce('ax', -10);
- coerce('ay', -30);
-
- // if you have one part of arrow length you should have both
- Lib.noneOrAll(annIn, annOut, ['ax', 'ay']);
- }
- }
-
- },{"../../lib":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"../annotations/common_defaults":39,"./attributes":45}],48:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var drawRaw = _dereq_('../annotations/draw').drawRaw;
- var project = _dereq_('../../plots/gl3d/project');
- var axLetters = ['x', 'y', 'z'];
-
- module.exports = function draw(scene) {
- var fullSceneLayout = scene.fullSceneLayout;
- var dataScale = scene.dataScale;
- var anns = fullSceneLayout.annotations;
-
- for(var i = 0; i < anns.length; i++) {
- var ann = anns[i];
- var annotationIsOffscreen = false;
-
- for(var j = 0; j < 3; j++) {
- var axLetter = axLetters[j];
- var pos = ann[axLetter];
- var ax = fullSceneLayout[axLetter + 'axis'];
- var posFraction = ax.r2fraction(pos);
-
- if(posFraction < 0 || posFraction > 1) {
- annotationIsOffscreen = true;
- break;
- }
- }
-
- if(annotationIsOffscreen) {
- scene.fullLayout._infolayer
- .select('.annotation-' + scene.id + '[data-index="' + i + '"]')
- .remove();
- } else {
- ann._pdata = project(scene.glplot.cameraParams, [
- fullSceneLayout.xaxis.r2l(ann.x) * dataScale[0],
- fullSceneLayout.yaxis.r2l(ann.y) * dataScale[1],
- fullSceneLayout.zaxis.r2l(ann.z) * dataScale[2]
- ]);
-
- drawRaw(scene.graphDiv, ann, i, scene.id, ann._xa, ann._ya);
- }
- }
- };
-
- },{"../../plots/gl3d/project":242,"../annotations/draw":42}],49:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
-
- module.exports = {
- moduleType: 'component',
- name: 'annotations3d',
-
- schema: {
- subplots: {
- scene: {annotations: _dereq_('./attributes')}
- }
- },
-
- layoutAttributes: _dereq_('./attributes'),
- handleDefaults: _dereq_('./defaults'),
- includeBasePlot: includeGL3D,
-
- convert: _dereq_('./convert'),
- draw: _dereq_('./draw')
- };
-
- function includeGL3D(layoutIn, layoutOut) {
- var GL3D = Registry.subplotsRegistry.gl3d;
- if(!GL3D) return;
-
- var attrRegex = GL3D.attrRegex;
-
- var keys = Object.keys(layoutIn);
- for(var i = 0; i < keys.length; i++) {
- var k = keys[i];
- if(attrRegex.test(k) && (layoutIn[k].annotations || []).length) {
- Lib.pushUnique(layoutOut._basePlotModules, GL3D);
- Lib.pushUnique(layoutOut._subplots.gl3d, k);
- }
- }
- }
-
- },{"../../lib":168,"../../registry":257,"./attributes":45,"./convert":46,"./defaults":47,"./draw":48}],50:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
-
- // IMPORTANT - default colors should be in hex for compatibility
- exports.defaults = [
- '#1f77b4', // muted blue
- '#ff7f0e', // safety orange
- '#2ca02c', // cooked asparagus green
- '#d62728', // brick red
- '#9467bd', // muted purple
- '#8c564b', // chestnut brown
- '#e377c2', // raspberry yogurt pink
- '#7f7f7f', // middle gray
- '#bcbd22', // curry yellow-green
- '#17becf' // blue-teal
- ];
-
- exports.defaultLine = '#444';
-
- exports.lightLine = '#eee';
-
- exports.background = '#fff';
-
- exports.borderLine = '#BEC8D9';
-
- // with axis.color and Color.interp we aren't using lightLine
- // itself anymore, instead interpolating between axis.color
- // and the background color using tinycolor.mix. lightFraction
- // gives back exactly lightLine if the other colors are defaults.
- exports.lightFraction = 100 * (0xe - 0x4) / (0xf - 0x4);
-
- },{}],51:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var tinycolor = _dereq_('tinycolor2');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var color = module.exports = {};
-
- var colorAttrs = _dereq_('./attributes');
- color.defaults = colorAttrs.defaults;
- var defaultLine = color.defaultLine = colorAttrs.defaultLine;
- color.lightLine = colorAttrs.lightLine;
- var background = color.background = colorAttrs.background;
-
- /*
- * tinyRGB: turn a tinycolor into an rgb string, but
- * unlike the built-in tinycolor.toRgbString this never includes alpha
- */
- color.tinyRGB = function(tc) {
- var c = tc.toRgb();
- return 'rgb(' + Math.round(c.r) + ', ' +
- Math.round(c.g) + ', ' + Math.round(c.b) + ')';
- };
-
- color.rgb = function(cstr) { return color.tinyRGB(tinycolor(cstr)); };
-
- color.opacity = function(cstr) { return cstr ? tinycolor(cstr).getAlpha() : 0; };
-
- color.addOpacity = function(cstr, op) {
- var c = tinycolor(cstr).toRgb();
- return 'rgba(' + Math.round(c.r) + ', ' +
- Math.round(c.g) + ', ' + Math.round(c.b) + ', ' + op + ')';
- };
-
- // combine two colors into one apparent color
- // if back has transparency or is missing,
- // color.background is assumed behind it
- color.combine = function(front, back) {
- var fc = tinycolor(front).toRgb();
- if(fc.a === 1) return tinycolor(front).toRgbString();
-
- var bc = tinycolor(back || background).toRgb();
- var bcflat = bc.a === 1 ? bc : {
- r: 255 * (1 - bc.a) + bc.r * bc.a,
- g: 255 * (1 - bc.a) + bc.g * bc.a,
- b: 255 * (1 - bc.a) + bc.b * bc.a
- };
- var fcflat = {
- r: bcflat.r * (1 - fc.a) + fc.r * fc.a,
- g: bcflat.g * (1 - fc.a) + fc.g * fc.a,
- b: bcflat.b * (1 - fc.a) + fc.b * fc.a
- };
- return tinycolor(fcflat).toRgbString();
- };
-
- /*
- * Create a color that contrasts with cstr.
- *
- * If cstr is a dark color, we lighten it; if it's light, we darken.
- *
- * If lightAmount / darkAmount are used, we adjust by these percentages,
- * otherwise we go all the way to white or black.
- */
- color.contrast = function(cstr, lightAmount, darkAmount) {
- var tc = tinycolor(cstr);
-
- if(tc.getAlpha() !== 1) tc = tinycolor(color.combine(cstr, background));
-
- var newColor = tc.isDark() ?
- (lightAmount ? tc.lighten(lightAmount) : background) :
- (darkAmount ? tc.darken(darkAmount) : defaultLine);
-
- return newColor.toString();
- };
-
- color.stroke = function(s, c) {
- var tc = tinycolor(c);
- s.style({'stroke': color.tinyRGB(tc), 'stroke-opacity': tc.getAlpha()});
- };
-
- color.fill = function(s, c) {
- var tc = tinycolor(c);
- s.style({
- 'fill': color.tinyRGB(tc),
- 'fill-opacity': tc.getAlpha()
- });
- };
-
- // search container for colors with the deprecated rgb(fractions) format
- // and convert them to rgb(0-255 values)
- color.clean = function(container) {
- if(!container || typeof container !== 'object') return;
-
- var keys = Object.keys(container);
- var i, j, key, val;
-
- for(i = 0; i < keys.length; i++) {
- key = keys[i];
- val = container[key];
-
- // only sanitize keys that end in "color" or "colorscale"
- if(key.substr(key.length - 5) === 'color') {
- if(Array.isArray(val)) {
- for(j = 0; j < val.length; j++) val[j] = cleanOne(val[j]);
- }
- else container[key] = cleanOne(val);
- }
- else if(key.substr(key.length - 10) === 'colorscale' && Array.isArray(val)) {
- // colorscales have the format [[0, color1], [frac, color2], ... [1, colorN]]
- for(j = 0; j < val.length; j++) {
- if(Array.isArray(val[j])) val[j][1] = cleanOne(val[j][1]);
- }
- }
- // recurse into arrays of objects, and plain objects
- else if(Array.isArray(val)) {
- var el0 = val[0];
- if(!Array.isArray(el0) && el0 && typeof el0 === 'object') {
- for(j = 0; j < val.length; j++) color.clean(val[j]);
- }
- }
- else if(val && typeof val === 'object') color.clean(val);
- }
- };
-
- function cleanOne(val) {
- if(isNumeric(val) || typeof val !== 'string') return val;
-
- var valTrim = val.trim();
- if(valTrim.substr(0, 3) !== 'rgb') return val;
-
- var match = valTrim.match(/^rgba?\s*\(([^()]*)\)$/);
- if(!match) return val;
-
- var parts = match[1].trim().split(/\s*[\s,]\s*/);
- var rgba = valTrim.charAt(3) === 'a' && parts.length === 4;
- if(!rgba && parts.length !== 3) return val;
-
- for(var i = 0; i < parts.length; i++) {
- if(!parts[i].length) return val;
- parts[i] = Number(parts[i]);
-
- // all parts must be non-negative numbers
- if(!(parts[i] >= 0)) return val;
- // alpha>1 gets clipped to 1
- if(i === 3) {
- if(parts[i] > 1) parts[i] = 1;
- }
- // r, g, b must be < 1 (ie 1 itself is not allowed)
- else if(parts[i] >= 1) return val;
- }
-
- var rgbStr = Math.round(parts[0] * 255) + ', ' +
- Math.round(parts[1] * 255) + ', ' +
- Math.round(parts[2] * 255);
-
- if(rgba) return 'rgba(' + rgbStr + ', ' + parts[3] + ')';
- return 'rgb(' + rgbStr + ')';
- }
-
- },{"./attributes":50,"fast-isnumeric":18,"tinycolor2":34}],52:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var axesAttrs = _dereq_('../../plots/cartesian/layout_attributes');
- var fontAttrs = _dereq_('../../plots/font_attributes');
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
- var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
-
-
- module.exports = overrideAll({
- // TODO: only right is supported currently
- // orient: {
- // valType: 'enumerated',
- //
- // values: ['left', 'right', 'top', 'bottom'],
- // dflt: 'right',
- //
- // },
- thicknessmode: {
- valType: 'enumerated',
- values: ['fraction', 'pixels'],
-
- dflt: 'pixels',
-
- },
- thickness: {
- valType: 'number',
-
- min: 0,
- dflt: 30,
-
- },
- lenmode: {
- valType: 'enumerated',
- values: ['fraction', 'pixels'],
-
- dflt: 'fraction',
-
- },
- len: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
-
- },
- x: {
- valType: 'number',
- dflt: 1.02,
- min: -2,
- max: 3,
-
-
- },
- xanchor: {
- valType: 'enumerated',
- values: ['left', 'center', 'right'],
- dflt: 'left',
-
-
- },
- xpad: {
- valType: 'number',
-
- min: 0,
- dflt: 10,
-
- },
- y: {
- valType: 'number',
-
- dflt: 0.5,
- min: -2,
- max: 3,
-
- },
- yanchor: {
- valType: 'enumerated',
- values: ['top', 'middle', 'bottom'],
-
- dflt: 'middle',
-
- },
- ypad: {
- valType: 'number',
-
- min: 0,
- dflt: 10,
-
- },
- // a possible line around the bar itself
- outlinecolor: axesAttrs.linecolor,
- outlinewidth: axesAttrs.linewidth,
- // Should outlinewidth have {dflt: 0} ?
- // another possible line outside the padding and tick labels
- bordercolor: axesAttrs.linecolor,
- borderwidth: {
- valType: 'number',
-
- min: 0,
- dflt: 0,
-
- },
- bgcolor: {
- valType: 'color',
-
- dflt: 'rgba(0,0,0,0)',
-
- },
- // tick and title properties named and function exactly as in axes
- tickmode: axesAttrs.tickmode,
- nticks: axesAttrs.nticks,
- tick0: axesAttrs.tick0,
- dtick: axesAttrs.dtick,
- tickvals: axesAttrs.tickvals,
- ticktext: axesAttrs.ticktext,
- ticks: extendFlat({}, axesAttrs.ticks, {dflt: ''}),
- ticklen: axesAttrs.ticklen,
- tickwidth: axesAttrs.tickwidth,
- tickcolor: axesAttrs.tickcolor,
- showticklabels: axesAttrs.showticklabels,
- tickfont: fontAttrs({
-
- }),
- tickangle: axesAttrs.tickangle,
- tickformat: axesAttrs.tickformat,
- tickformatstops: axesAttrs.tickformatstops,
- tickprefix: axesAttrs.tickprefix,
- showtickprefix: axesAttrs.showtickprefix,
- ticksuffix: axesAttrs.ticksuffix,
- showticksuffix: axesAttrs.showticksuffix,
- separatethousands: axesAttrs.separatethousands,
- exponentformat: axesAttrs.exponentformat,
- showexponent: axesAttrs.showexponent,
- title: {
- text: {
- valType: 'string',
-
-
- },
- font: fontAttrs({
-
- }),
- side: {
- valType: 'enumerated',
- values: ['right', 'top', 'bottom'],
-
- dflt: 'top',
-
- }
- },
-
- _deprecated: {
- title: {
- valType: 'string',
-
-
- },
- titlefont: fontAttrs({
-
- }),
- titleside: {
- valType: 'enumerated',
- values: ['right', 'top', 'bottom'],
-
- dflt: 'top',
-
- }
- }
- }, 'colorbars', 'from-root');
-
- },{"../../lib/extend":162,"../../plot_api/edit_types":195,"../../plots/cartesian/layout_attributes":225,"../../plots/font_attributes":239}],53:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var drawColorbar = _dereq_('./draw');
- var flipScale = _dereq_('../colorscale/helpers').flipScale;
-
- /**
- * connectColorbar: create a colorbar from a trace, using its module to
- * describe the connection.
- *
- * @param {DOM element} gd
- *
- * @param {Array} cd
- * calcdata entry for this trace. cd[0].trace is the trace itself, and the
- * colorbar object will be stashed in cd[0].t.cb
- *
- * @param {object|function} moduleOpts
- * may be a function(gd, cd) to override the standard handling below. If
- * an object, should have these keys:
- * @param {Optional(string)} moduleOpts.container
- * name of the container inside the trace where the colorbar and colorscale
- * attributes live (ie 'marker', 'line') - omit if they're at the trace root.
- * @param {string} moduleOpts.min
- * name of the attribute holding the value of the minimum color
- * @param {string} moduleOpts.max
- * name of the attribute holding the value of the maximum color
- * @param {Optional(string)} moduleOpts.vals
- * name of the attribute holding the (numeric) color data
- * used only if min/max fail. May be omitted if these are always
- * pre-calculated.
- */
- module.exports = function connectColorbar(gd, cd, moduleOpts) {
- if(typeof moduleOpts === 'function') return moduleOpts(gd, cd);
-
- var trace = cd[0].trace;
- var cbId = 'cb' + trace.uid;
- moduleOpts = Array.isArray(moduleOpts) ? moduleOpts : [moduleOpts];
-
- for(var i = 0; i < moduleOpts.length; i++) {
- var containerName = moduleOpts[i].container;
-
- var container = containerName ? trace[containerName] : trace;
-
- gd._fullLayout._infolayer.selectAll('.' + cbId).remove();
- if(!container || !container.showscale) continue;
-
- var cb = cd[0].t.cb = drawColorbar(gd, cbId);
-
- var scl = container.reversescale ?
- flipScale(container.colorscale) :
- container.colorscale;
-
- cb.fillgradient(scl)
- .zrange([container[moduleOpts[i].min], container[moduleOpts[i].max]])
- .options(container.colorbar)();
-
- return;
- }
- };
-
- },{"../colorscale/helpers":62,"./draw":56}],54:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- cn: {
- colorbar: 'colorbar',
- cbbg: 'cbbg',
- cbfill: 'cbfill',
- cbfills: 'cbfills',
- cbline: 'cbline',
- cblines: 'cblines',
- cbaxis: 'cbaxis',
- cbtitleunshift: 'cbtitleunshift',
- cbtitle: 'cbtitle',
- cboutline: 'cboutline',
- crisp: 'crisp',
- jsPlaceholder: 'js-placeholder'
- }
- };
-
- },{}],55:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Template = _dereq_('../../plot_api/plot_template');
-
- var handleTickValueDefaults = _dereq_('../../plots/cartesian/tick_value_defaults');
- var handleTickMarkDefaults = _dereq_('../../plots/cartesian/tick_mark_defaults');
- var handleTickLabelDefaults = _dereq_('../../plots/cartesian/tick_label_defaults');
-
- var attributes = _dereq_('./attributes');
-
- module.exports = function colorbarDefaults(containerIn, containerOut, layout) {
- var colorbarOut = Template.newContainer(containerOut, 'colorbar');
- var colorbarIn = containerIn.colorbar || {};
-
- function coerce(attr, dflt) {
- return Lib.coerce(colorbarIn, colorbarOut, attributes, attr, dflt);
- }
-
- var thicknessmode = coerce('thicknessmode');
- coerce('thickness', (thicknessmode === 'fraction') ?
- 30 / (layout.width - layout.margin.l - layout.margin.r) :
- 30
- );
-
- var lenmode = coerce('lenmode');
- coerce('len', (lenmode === 'fraction') ?
- 1 :
- layout.height - layout.margin.t - layout.margin.b
- );
-
- coerce('x');
- coerce('xanchor');
- coerce('xpad');
- coerce('y');
- coerce('yanchor');
- coerce('ypad');
- Lib.noneOrAll(colorbarIn, colorbarOut, ['x', 'y']);
-
- coerce('outlinecolor');
- coerce('outlinewidth');
- coerce('bordercolor');
- coerce('borderwidth');
- coerce('bgcolor');
-
- handleTickValueDefaults(colorbarIn, colorbarOut, coerce, 'linear');
-
- var opts = {outerTicks: false, font: layout.font};
- handleTickLabelDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts);
- handleTickMarkDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts);
-
- coerce('title.text', layout._dfltTitle.colorbar);
- Lib.coerceFont(coerce, 'title.font', layout.font);
- coerce('title.side');
- };
-
- },{"../../lib":168,"../../plot_api/plot_template":202,"../../plots/cartesian/tick_label_defaults":232,"../../plots/cartesian/tick_mark_defaults":233,"../../plots/cartesian/tick_value_defaults":234,"./attributes":52}],56:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var tinycolor = _dereq_('tinycolor2');
-
- var Plots = _dereq_('../../plots/plots');
- var Registry = _dereq_('../../registry');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var dragElement = _dereq_('../dragelement');
- var Lib = _dereq_('../../lib');
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
- var setCursor = _dereq_('../../lib/setcursor');
- var Drawing = _dereq_('../drawing');
- var Color = _dereq_('../color');
- var Titles = _dereq_('../titles');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var alignmentConstants = _dereq_('../../constants/alignment');
- var LINE_SPACING = alignmentConstants.LINE_SPACING;
- var FROM_TL = alignmentConstants.FROM_TL;
- var FROM_BR = alignmentConstants.FROM_BR;
-
- var handleAxisDefaults = _dereq_('../../plots/cartesian/axis_defaults');
- var handleAxisPositionDefaults = _dereq_('../../plots/cartesian/position_defaults');
- var axisLayoutAttrs = _dereq_('../../plots/cartesian/layout_attributes');
-
- var attributes = _dereq_('./attributes');
- var cn = _dereq_('./constants').cn;
-
- module.exports = function draw(gd, id) {
- // opts: options object, containing everything from attributes
- // plus a few others that are the equivalent of the colorbar "data"
- var opts = {};
- for(var k in attributes) {
- opts[k] = null;
- }
- // fillcolor can be a d3 scale, domain is z values, range is colors
- // or leave it out for no fill,
- // or set to a string constant for single-color fill
- opts.fillcolor = null;
- // line.color has the same options as fillcolor
- opts.line = {color: null, width: null, dash: null};
- // levels of lines to draw.
- // note that this DOES NOT determine the extent of the bar
- // that's given by the domain of fillcolor
- // (or line.color if no fillcolor domain)
- opts.levels = {start: null, end: null, size: null};
- // separate fill levels (for example, heatmap coloring of a
- // contour map) if this is omitted, fillcolors will be
- // evaluated halfway between levels
- opts.filllevels = null;
- // for continuous colorscales: fill with a gradient instead of explicit levels
- // value should be the colorscale [[0, c0], [v1, c1], ..., [1, cEnd]]
- opts.fillgradient = null;
- // when using a gradient, we need the data range specified separately
- opts.zrange = null;
-
- function component() {
- var fullLayout = gd._fullLayout;
- var gs = fullLayout._size;
- if((typeof opts.fillcolor !== 'function') &&
- (typeof opts.line.color !== 'function') &&
- !opts.fillgradient) {
- fullLayout._infolayer.selectAll('g.' + id).remove();
- return;
- }
- var zrange = opts.zrange || (d3.extent(((typeof opts.fillcolor === 'function') ?
- opts.fillcolor : opts.line.color).domain()));
- var linelevels = [];
- var filllevels = [];
- var linecolormap = typeof opts.line.color === 'function' ?
- opts.line.color : function() { return opts.line.color; };
- var fillcolormap = typeof opts.fillcolor === 'function' ?
- opts.fillcolor : function() { return opts.fillcolor; };
- var l;
- var i;
-
- var l0 = opts.levels.end + opts.levels.size / 100;
- var ls = opts.levels.size;
- var zr0 = (1.001 * zrange[0] - 0.001 * zrange[1]);
- var zr1 = (1.001 * zrange[1] - 0.001 * zrange[0]);
- for(i = 0; i < 1e5; i++) {
- l = opts.levels.start + i * ls;
- if(ls > 0 ? (l >= l0) : (l <= l0)) break;
- if(l > zr0 && l < zr1) linelevels.push(l);
- }
-
- if(opts.fillgradient) {
- filllevels = [0];
- }
- else if(typeof opts.fillcolor === 'function') {
- if(opts.filllevels) {
- l0 = opts.filllevels.end + opts.filllevels.size / 100;
- ls = opts.filllevels.size;
- for(i = 0; i < 1e5; i++) {
- l = opts.filllevels.start + i * ls;
- if(ls > 0 ? (l >= l0) : (l <= l0)) break;
- if(l > zrange[0] && l < zrange[1]) filllevels.push(l);
- }
- }
- else {
- filllevels = linelevels.map(function(v) {
- return v - opts.levels.size / 2;
- });
- filllevels.push(filllevels[filllevels.length - 1] +
- opts.levels.size);
- }
- }
- else if(opts.fillcolor && typeof opts.fillcolor === 'string') {
- // doesn't matter what this value is, with a single value
- // we'll make a single fill rect covering the whole bar
- filllevels = [0];
- }
-
- if(opts.levels.size < 0) {
- linelevels.reverse();
- filllevels.reverse();
- }
-
- // now make a Plotly Axes object to scale with and draw ticks
- // TODO: does not support orientation other than right
-
- // we calculate pixel sizes based on the specified graph size,
- // not the actual (in case something pushed the margins around)
- // which is a little odd but avoids an odd iterative effect
- // when the colorbar itself is pushing the margins.
- // but then the fractional size is calculated based on the
- // actual graph size, so that the axes will size correctly.
- var plotHeight = gs.h;
- var plotWidth = gs.w;
- var thickPx = Math.round(opts.thickness * (opts.thicknessmode === 'fraction' ? plotWidth : 1));
- var thickFrac = thickPx / gs.w;
- var lenPx = Math.round(opts.len * (opts.lenmode === 'fraction' ? plotHeight : 1));
- var lenFrac = lenPx / gs.h;
- var xpadFrac = opts.xpad / gs.w;
- var yExtraPx = (opts.borderwidth + opts.outlinewidth) / 2;
- var ypadFrac = opts.ypad / gs.h;
-
- // x positioning: do it initially just for left anchor,
- // then fix at the end (since we don't know the width yet)
- var xLeft = Math.round(opts.x * gs.w + opts.xpad);
- // for dragging... this is getting a little muddled...
- var xLeftFrac = opts.x - thickFrac * ({middle: 0.5, right: 1}[opts.xanchor]||0);
-
- // y positioning we can do correctly from the start
- var yBottomFrac = opts.y + lenFrac * (({top: -0.5, bottom: 0.5}[opts.yanchor] || 0) - 0.5);
- var yBottomPx = Math.round(gs.h * (1 - yBottomFrac));
- var yTopPx = yBottomPx - lenPx;
-
- var titleEl;
-
- var cbAxisIn = {
- type: 'linear',
- range: zrange,
- tickmode: opts.tickmode,
- nticks: opts.nticks,
- tick0: opts.tick0,
- dtick: opts.dtick,
- tickvals: opts.tickvals,
- ticktext: opts.ticktext,
- ticks: opts.ticks,
- ticklen: opts.ticklen,
- tickwidth: opts.tickwidth,
- tickcolor: opts.tickcolor,
- showticklabels: opts.showticklabels,
- tickfont: opts.tickfont,
- tickangle: opts.tickangle,
- tickformat: opts.tickformat,
- exponentformat: opts.exponentformat,
- separatethousands: opts.separatethousands,
- showexponent: opts.showexponent,
- showtickprefix: opts.showtickprefix,
- tickprefix: opts.tickprefix,
- showticksuffix: opts.showticksuffix,
- ticksuffix: opts.ticksuffix,
- title: opts.title,
- showline: true,
- anchor: 'free',
- side: 'right',
- position: 1
- };
- var cbAxisOut = {
- type: 'linear',
- _id: 'y' + id
- };
- var axisOptions = {
- letter: 'y',
- font: fullLayout.font,
- noHover: true,
- noTickson: true,
- calendar: fullLayout.calendar // not really necessary (yet?)
- };
-
- // Coerce w.r.t. Axes layoutAttributes:
- // re-use axes.js logic without updating _fullData
- function coerce(attr, dflt) {
- return Lib.coerce(cbAxisIn, cbAxisOut, axisLayoutAttrs, attr, dflt);
- }
-
- // Prepare the Plotly axis object
- handleAxisDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions, fullLayout);
- handleAxisPositionDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions);
-
- // position can't go in through supplyDefaults
- // because that restricts it to [0,1]
- cbAxisOut.position = opts.x + xpadFrac + thickFrac;
-
- // save for other callers to access this axis
- component.axis = cbAxisOut;
-
- if(['top', 'bottom'].indexOf(opts.title.side) !== -1) {
- cbAxisOut.title.side = opts.title.side;
- cbAxisOut.titlex = opts.x + xpadFrac;
- cbAxisOut.titley = yBottomFrac +
- (opts.title.side === 'top' ? lenFrac - ypadFrac : ypadFrac);
- }
-
- if(opts.line.color && opts.tickmode === 'auto') {
- cbAxisOut.tickmode = 'linear';
- cbAxisOut.tick0 = opts.levels.start;
- var dtick = opts.levels.size;
- // expand if too many contours, so we don't get too many ticks
- var autoNtick = Lib.constrain((yBottomPx - yTopPx) / 50, 4, 15) + 1;
- var dtFactor = (zrange[1] - zrange[0]) / ((opts.nticks || autoNtick) * dtick);
- if(dtFactor > 1) {
- var dtexp = Math.pow(10, Math.floor(
- Math.log(dtFactor) / Math.LN10));
- dtick *= dtexp * Lib.roundUp(dtFactor / dtexp, [2, 5, 10]);
- // if the contours are at round multiples, reset tick0
- // so they're still at round multiples. Otherwise,
- // keep the first label on the first contour level
- if((Math.abs(opts.levels.start) /
- opts.levels.size + 1e-6) % 1 < 2e-6) {
- cbAxisOut.tick0 = 0;
- }
- }
- cbAxisOut.dtick = dtick;
- }
-
- // set domain after init, because we may want to
- // allow it outside [0,1]
- cbAxisOut.domain = [
- yBottomFrac + ypadFrac,
- yBottomFrac + lenFrac - ypadFrac
- ];
- cbAxisOut.setScale();
-
- // now draw the elements
- var container = Lib.ensureSingle(fullLayout._infolayer, 'g', id, function(s) {
- s.classed(cn.colorbar, true)
- .each(function() {
- var s = d3.select(this);
- s.append('rect').classed(cn.cbbg, true);
- s.append('g').classed(cn.cbfills, true);
- s.append('g').classed(cn.cblines, true);
- s.append('g').classed(cn.cbaxis, true).classed(cn.crisp, true);
- s.append('g').classed(cn.cbtitleunshift, true)
- .append('g').classed(cn.cbtitle, true);
- s.append('rect').classed(cn.cboutline, true);
- s.select('.cbtitle').datum(0);
- });
- });
-
- container.attr('transform', 'translate(' + Math.round(gs.l) +
- ',' + Math.round(gs.t) + ')');
- // TODO: this opposite transform is a hack until we make it
- // more rational which items get this offset
- var titleCont = container.select('.cbtitleunshift')
- .attr('transform', 'translate(-' +
- Math.round(gs.l) + ',-' +
- Math.round(gs.t) + ')');
-
- var axisLayer = container.select('.cbaxis');
-
- var titleHeight = 0;
- if(['top', 'bottom'].indexOf(opts.title.side) !== -1) {
- // draw the title so we know how much room it needs
- // when we squish the axis. This one only applies to
- // top or bottom titles, not right side.
- var x = gs.l + (opts.x + xpadFrac) * gs.w;
- var fontSize = cbAxisOut.title.font.size;
- var y;
-
- if(opts.title.side === 'top') {
- y = (1 - (yBottomFrac + lenFrac - ypadFrac)) * gs.h +
- gs.t + 3 + fontSize * 0.75;
- }
- else {
- y = (1 - (yBottomFrac + ypadFrac)) * gs.h +
- gs.t - 3 - fontSize * 0.25;
- }
- drawTitle(cbAxisOut._id + 'title', {
- attributes: {x: x, y: y, 'text-anchor': 'start'}
- });
- }
-
- function drawAxis() {
- if(['top', 'bottom'].indexOf(opts.title.side) !== -1) {
- // squish the axis top to make room for the title
- var titleGroup = container.select('.cbtitle');
- var titleText = titleGroup.select('text');
- var titleTrans = [-opts.outlinewidth / 2, opts.outlinewidth / 2];
- var mathJaxNode = titleGroup
- .select('.h' + cbAxisOut._id + 'title-math-group')
- .node();
- var lineSize = 15.6;
- if(titleText.node()) {
- lineSize =
- parseInt(titleText.node().style.fontSize, 10) * LINE_SPACING;
- }
- if(mathJaxNode) {
- titleHeight = Drawing.bBox(mathJaxNode).height;
- if(titleHeight > lineSize) {
- // not entirely sure how mathjax is doing
- // vertical alignment, but this seems to work.
- titleTrans[1] -= (titleHeight - lineSize) / 2;
- }
- }
- else if(titleText.node() &&
- !titleText.classed(cn.jsPlaceholder)) {
- titleHeight = Drawing.bBox(titleText.node()).height;
- }
- if(titleHeight) {
- // buffer btwn colorbar and title
- // TODO: configurable
- titleHeight += 5;
-
- if(opts.title.side === 'top') {
- cbAxisOut.domain[1] -= titleHeight / gs.h;
- titleTrans[1] *= -1;
- }
- else {
- cbAxisOut.domain[0] += titleHeight / gs.h;
- var nlines = svgTextUtils.lineCount(titleText);
- titleTrans[1] += (1 - nlines) * lineSize;
- }
-
- titleGroup.attr('transform',
- 'translate(' + titleTrans + ')');
-
- cbAxisOut.setScale();
- }
- }
-
- container.selectAll('.cbfills,.cblines')
- .attr('transform', 'translate(0,' +
- Math.round(gs.h * (1 - cbAxisOut.domain[1])) + ')');
-
- axisLayer.attr('transform', 'translate(0,' + Math.round(-gs.t) + ')');
-
- var fills = container.select('.cbfills')
- .selectAll('rect.cbfill')
- .data(filllevels);
- fills.enter().append('rect')
- .classed(cn.cbfill, true)
- .style('stroke', 'none');
- fills.exit().remove();
-
- var zBounds = zrange
- .map(cbAxisOut.c2p)
- .map(Math.round)
- .sort(function(a, b) { return a - b; });
-
- fills.each(function(d, i) {
- var z = [
- (i === 0) ? zrange[0] :
- (filllevels[i] + filllevels[i - 1]) / 2,
- (i === filllevels.length - 1) ? zrange[1] :
- (filllevels[i] + filllevels[i + 1]) / 2
- ]
- .map(cbAxisOut.c2p)
- .map(Math.round);
-
- // offset the side adjoining the next rectangle so they
- // overlap, to prevent antialiasing gaps
- z[1] = Lib.constrain(z[1] + (z[1] > z[0]) ? 1 : -1, zBounds[0], zBounds[1]);
-
- // Colorbar cannot currently support opacities so we
- // use an opaque fill even when alpha channels present
- var fillEl = d3.select(this).attr({
- x: xLeft,
- width: Math.max(thickPx, 2),
- y: d3.min(z),
- height: Math.max(d3.max(z) - d3.min(z), 2),
- });
-
- if(opts.fillgradient) {
- Drawing.gradient(fillEl, gd, id, 'vertical',
- opts.fillgradient, 'fill');
- }
- else {
- // Tinycolor can't handle exponents and
- // at this scale, removing it makes no difference.
- var colorString = fillcolormap(d).replace('e-', '');
- fillEl.attr('fill', tinycolor(colorString).toHexString());
- }
- });
-
- var lines = container.select('.cblines')
- .selectAll('path.cbline')
- .data(opts.line.color && opts.line.width ?
- linelevels : []);
- lines.enter().append('path')
- .classed(cn.cbline, true);
- lines.exit().remove();
- lines.each(function(d) {
- d3.select(this)
- .attr('d', 'M' + xLeft + ',' +
- (Math.round(cbAxisOut.c2p(d)) + (opts.line.width / 2) % 1) +
- 'h' + thickPx)
- .call(Drawing.lineGroupStyle,
- opts.line.width, linecolormap(d), opts.line.dash);
- });
-
- // force full redraw of labels and ticks
- axisLayer.selectAll('g.' + cbAxisOut._id + 'tick,path').remove();
-
- // separate out axis and title drawing,
- // so we don't need such complicated logic in Titles.draw
- // if title is on the top or bottom, we've already drawn it
- // this title call only handles side=right
- return Lib.syncOrAsync([
- function() {
- var shift = xLeft + thickPx +
- (opts.outlinewidth || 0) / 2 - (opts.ticks === 'outside' ? 1 : 0);
-
- var vals = Axes.calcTicks(cbAxisOut);
- var transFn = Axes.makeTransFn(cbAxisOut);
- var labelFns = Axes.makeLabelFns(cbAxisOut, shift);
- var tickSign = Axes.getTickSigns(cbAxisOut)[2];
-
- Axes.drawTicks(gd, cbAxisOut, {
- vals: cbAxisOut.ticks === 'inside' ? Axes.clipEnds(cbAxisOut, vals) : vals,
- layer: axisLayer,
- path: Axes.makeTickPath(cbAxisOut, shift, tickSign),
- transFn: transFn
- });
-
- return Axes.drawLabels(gd, cbAxisOut, {
- vals: vals,
- layer: axisLayer,
- transFn: transFn,
- labelXFn: labelFns.labelXFn,
- labelYFn: labelFns.labelYFn,
- labelAnchorFn: labelFns.labelAnchorFn
- });
- },
- function() {
- if(['top', 'bottom'].indexOf(opts.title.side) === -1) {
- var fontSize = cbAxisOut.title.font.size;
- var y = cbAxisOut._offset + cbAxisOut._length / 2;
- var x = gs.l + (cbAxisOut.position || 0) * gs.w + ((cbAxisOut.side === 'right') ?
- 10 + fontSize * ((cbAxisOut.showticklabels ? 1 : 0.5)) :
- -10 - fontSize * ((cbAxisOut.showticklabels ? 0.5 : 0)));
-
- // the 'h' + is a hack to get around the fact that
- // convertToTspans rotates any 'y...' class by 90 degrees.
- // TODO: find a better way to control this.
- drawTitle('h' + cbAxisOut._id + 'title', {
- avoid: {
- selection: d3.select(gd).selectAll('g.' + cbAxisOut._id + 'tick'),
- side: opts.title.side,
- offsetLeft: gs.l,
- offsetTop: 0,
- maxShift: fullLayout.width
- },
- attributes: {x: x, y: y, 'text-anchor': 'middle'},
- transform: {rotate: '-90', offset: 0}
- });
- }
- }]);
- }
-
- function drawTitle(titleClass, titleOpts) {
- var dfltTitleOpts = {
- propContainer: cbAxisOut,
- propName: getPropName('title'),
- traceIndex: getTrace().index,
- placeholder: fullLayout._dfltTitle.colorbar,
- containerGroup: container.select('.cbtitle')
- };
-
- // this class-to-rotate thing with convertToTspans is
- // getting hackier and hackier... delete groups with the
- // wrong class (in case earlier the colorbar was drawn on
- // a different side, I think?)
- var otherClass = titleClass.charAt(0) === 'h' ?
- titleClass.substr(1) : ('h' + titleClass);
- container.selectAll('.' + otherClass + ',.' + otherClass + '-math-group')
- .remove();
-
- Titles.draw(gd, titleClass,
- extendFlat(dfltTitleOpts, titleOpts || {}));
- }
-
- function positionCB() {
- // wait for the axis & title to finish rendering before
- // continuing positioning
- // TODO: why are we redrawing multiple times now with this?
- // I guess autoMargin doesn't like being post-promise?
- var innerWidth = thickPx + opts.outlinewidth / 2 +
- Drawing.bBox(axisLayer.node()).width;
- titleEl = titleCont.select('text');
- if(titleEl.node() && !titleEl.classed(cn.jsPlaceholder)) {
- var mathJaxNode = titleCont
- .select('.h' + cbAxisOut._id + 'title-math-group')
- .node();
- var titleWidth;
- if(mathJaxNode &&
- ['top', 'bottom'].indexOf(opts.title.side) !== -1) {
- titleWidth = Drawing.bBox(mathJaxNode).width;
- }
- else {
- // note: the formula below works for all title sides,
- // (except for top/bottom mathjax, above)
- // but the weird gs.l is because the titleunshift
- // transform gets removed by Drawing.bBox
- titleWidth =
- Drawing.bBox(titleCont.node()).right -
- xLeft - gs.l;
- }
- innerWidth = Math.max(innerWidth, titleWidth);
- }
-
- var outerwidth = 2 * opts.xpad + innerWidth +
- opts.borderwidth + opts.outlinewidth / 2;
- var outerheight = yBottomPx - yTopPx;
-
- container.select('.cbbg').attr({
- x: xLeft - opts.xpad -
- (opts.borderwidth + opts.outlinewidth) / 2,
- y: yTopPx - yExtraPx,
- width: Math.max(outerwidth, 2),
- height: Math.max(outerheight + 2 * yExtraPx, 2)
- })
- .call(Color.fill, opts.bgcolor)
- .call(Color.stroke, opts.bordercolor)
- .style({'stroke-width': opts.borderwidth});
-
- container.selectAll('.cboutline').attr({
- x: xLeft,
- y: yTopPx + opts.ypad +
- (opts.title.side === 'top' ? titleHeight : 0),
- width: Math.max(thickPx, 2),
- height: Math.max(outerheight - 2 * opts.ypad - titleHeight, 2)
- })
- .call(Color.stroke, opts.outlinecolor)
- .style({
- fill: 'None',
- 'stroke-width': opts.outlinewidth
- });
-
- // fix positioning for xanchor!='left'
- var xoffset = ({center: 0.5, right: 1}[opts.xanchor] || 0) *
- outerwidth;
- container.attr('transform',
- 'translate(' + (gs.l - xoffset) + ',' + gs.t + ')');
-
- // auto margin adjustment
- var marginOpts = {};
- var tFrac = FROM_TL[opts.yanchor];
- var bFrac = FROM_BR[opts.yanchor];
- if(opts.lenmode === 'pixels') {
- marginOpts.y = opts.y;
- marginOpts.t = outerheight * tFrac;
- marginOpts.b = outerheight * bFrac;
- }
- else {
- marginOpts.t = marginOpts.b = 0;
- marginOpts.yt = opts.y + opts.len * tFrac;
- marginOpts.yb = opts.y - opts.len * bFrac;
- }
-
- var lFrac = FROM_TL[opts.xanchor];
- var rFrac = FROM_BR[opts.xanchor];
- if(opts.thicknessmode === 'pixels') {
- marginOpts.x = opts.x;
- marginOpts.l = outerwidth * lFrac;
- marginOpts.r = outerwidth * rFrac;
- }
- else {
- var extraThickness = outerwidth - thickPx;
- marginOpts.l = extraThickness * lFrac;
- marginOpts.r = extraThickness * rFrac;
- marginOpts.xl = opts.x - opts.thickness * lFrac;
- marginOpts.xr = opts.x + opts.thickness * rFrac;
- }
- Plots.autoMargin(gd, id, marginOpts);
- }
-
- var cbDone = Lib.syncOrAsync([
- Plots.previousPromises,
- drawAxis,
- Plots.previousPromises,
- positionCB
- ], gd);
-
- if(cbDone && cbDone.then) (gd._promises || []).push(cbDone);
-
- // dragging...
- if(gd._context.edits.colorbarPosition) {
- var t0,
- xf,
- yf;
-
- dragElement.init({
- element: container.node(),
- gd: gd,
- prepFn: function() {
- t0 = container.attr('transform');
- setCursor(container);
- },
- moveFn: function(dx, dy) {
- container.attr('transform',
- t0 + ' ' + 'translate(' + dx + ',' + dy + ')');
-
- xf = dragElement.align(xLeftFrac + (dx / gs.w), thickFrac,
- 0, 1, opts.xanchor);
- yf = dragElement.align(yBottomFrac - (dy / gs.h), lenFrac,
- 0, 1, opts.yanchor);
-
- var csr = dragElement.getCursor(xf, yf,
- opts.xanchor, opts.yanchor);
- setCursor(container, csr);
- },
- doneFn: function() {
- setCursor(container);
-
- if(xf !== undefined && yf !== undefined) {
- var update = {};
- update[getPropName('x')] = xf;
- update[getPropName('y')] = yf;
- Registry.call('_guiRestyle', gd, update, getTrace().index);
- }
- }
- });
- }
- return cbDone;
- }
-
- function getTrace() {
- var idNum = id.substr(2);
- for(var i = 0; i < gd._fullData.length; i++) {
- var trace = gd._fullData[i];
- if(trace.uid === idNum) return trace;
- }
- }
-
- function getPropName(suffix) {
- var trace = getTrace();
- var propName = 'colorbar.';
- var containerName = trace._module.colorbar.container;
- if(containerName) propName = containerName + '.' + propName;
- return propName + suffix;
- }
-
- // setter/getters for every item defined in opts
- Object.keys(opts).forEach(function(name) {
- component[name] = function(v) {
- // getter
- if(!arguments.length) return opts[name];
-
- // setter - for multi-part properties,
- // set only the parts that are provided
- opts[name] = Lib.isPlainObject(opts[name]) ?
- Lib.extendFlat(opts[name], v) :
- v;
-
- return component;
- };
- });
-
- // or use .options to set multiple options at once via a dictionary
- component.options = function(o) {
- for(var name in o) {
- // in case something random comes through
- // that's not an option, ignore it
- if(typeof component[name] === 'function') {
- component[name](o[name]);
- }
- }
- return component;
- };
-
- component._opts = opts;
-
- return component;
- };
-
- },{"../../constants/alignment":146,"../../lib":168,"../../lib/extend":162,"../../lib/setcursor":187,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../plots/cartesian/axis_defaults":214,"../../plots/cartesian/layout_attributes":225,"../../plots/cartesian/position_defaults":228,"../../plots/plots":245,"../../registry":257,"../color":51,"../dragelement":69,"../drawing":72,"../titles":139,"./attributes":52,"./constants":54,"d3":16,"tinycolor2":34}],57:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
-
- module.exports = function hasColorbar(container) {
- return Lib.isPlainObject(container.colorbar);
- };
-
- },{"../../lib":168}],58:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var palettes = _dereq_('./scales.js').scales;
- var paletteStr = Object.keys(palettes);
-
- function code(s) {
- return '`' + s + '`';
- }
-
- /**
- * Make colorscale attribute declarations for
- *
- * - colorscale,
- * - (c|z)auto, (c|z)min, (c|z)max,
- * - autocolorscale, reversescale,
- * - showscale (optionally)
- * - color (optionally)
- *
- * @param {string} context (dflt: '', i.e. from trace root):
- * the container this is in ('', *marker*, *marker.line* etc)
- *
- * @param {object} opts:
- * - cLetter {string} (dflt: 'c'):
- * leading letter for 'min', 'max and 'auto' attribute (either 'z' or 'c')
- *
- * - colorAttr {string} (dflt: 'z' if `cLetter: 'z'`, 'color' if `cLetter: 'c'`):
- * (for descriptions) sets the name of the color attribute that maps to the colorscale.
- *
- * N.B. if `colorAttr: 'color'`, we include the `color` declaration here.
- *
- * - onlyIfNumerical {string} (dflt: false' if `cLetter: 'z'`, true if `cLetter: 'c'`):
- * (for descriptions) set to true if colorscale attribute only
- *
- * - colorscaleDflt {string}:
- * overrides the colorscale dflt
- *
- * - autoColorDflt {boolean} (dflt true):
- * normally autocolorscale.dflt is `true`, but pass `false` to override
- *
- * - noScale {boolean} (dflt: true if `context: 'marker.line'`, false otherwise):
- * set to `false` to not include showscale attribute (e.g. for 'marker.line')
- *
- * - showScaleDflt {boolean} (dflt: true if `cLetter: 'z'`, false otherwise)
- *
- * - editTypeOverride {boolean} (dflt: ''):
- * most of these attributes already require a recalc, but the ones that do not
- * have editType *style* or *plot* unless you override (presumably with *calc*)
- *
- * - anim {boolean) (dflt: undefined): is 'color' animatable?
- *
- * @return {object}
- */
- module.exports = function colorScaleAttrs(context, opts) {
- context = context || '';
- opts = opts || {};
-
- var cLetter = opts.cLetter || 'c';
- var onlyIfNumerical = ('onlyIfNumerical' in opts) ? opts.onlyIfNumerical : Boolean(context);
- var noScale = ('noScale' in opts) ? opts.noScale : context === 'marker.line';
- var showScaleDflt = ('showScaleDflt' in opts) ? opts.showScaleDflt : cLetter === 'z';
- var colorscaleDflt = typeof opts.colorscaleDflt === 'string' ? palettes[opts.colorscaleDflt] : null;
- var editTypeOverride = opts.editTypeOverride || '';
- var contextHead = context ? (context + '.') : '';
-
- var colorAttr, colorAttrFull;
-
- if('colorAttr' in opts) {
- colorAttr = opts.colorAttr;
- colorAttrFull = opts.colorAttr;
- } else {
- colorAttr = {z: 'z', c: 'color'}[cLetter];
- colorAttrFull = 'in ' + code(contextHead + colorAttr);
- }
-
- var effectDesc = onlyIfNumerical ?
- ' Has an effect only if ' + colorAttrFull + 'is set to a numerical array.' :
- '';
-
- var auto = cLetter + 'auto';
- var min = cLetter + 'min';
- var max = cLetter + 'max';
- var minFull = code(contextHead + min);
- var maxFull = code(contextHead + max);
- var minmaxFull = minFull + ' and ' + maxFull;
- var autoImpliedEdits = {};
- autoImpliedEdits[min] = autoImpliedEdits[max] = undefined;
- var minmaxImpliedEdits = {};
- minmaxImpliedEdits[auto] = false;
-
- var attrs = {};
-
- if(colorAttr === 'color') {
- attrs.color = {
- valType: 'color',
- arrayOk: true,
-
- editType: editTypeOverride || 'style',
-
- };
-
- if(opts.anim) {
- attrs.color.anim = true;
- }
- }
-
- attrs[auto] = {
- valType: 'boolean',
-
- dflt: true,
- editType: 'calc',
- impliedEdits: autoImpliedEdits,
-
- };
-
- attrs[min] = {
- valType: 'number',
-
- dflt: null,
- editType: editTypeOverride || 'plot',
- impliedEdits: minmaxImpliedEdits,
-
- };
-
- attrs[max] = {
- valType: 'number',
-
- dflt: null,
- editType: editTypeOverride || 'plot',
- impliedEdits: minmaxImpliedEdits,
-
- };
-
- attrs.colorscale = {
- valType: 'colorscale',
-
- editType: 'calc',
- dflt: colorscaleDflt,
- impliedEdits: {autocolorscale: false},
-
- };
-
- attrs.autocolorscale = {
- valType: 'boolean',
-
- // gets overrode in 'heatmap' & 'surface' for backwards comp.
- dflt: opts.autoColorDflt === false ? false : true,
- editType: 'calc',
- impliedEdits: {colorscale: undefined},
-
- };
-
- attrs.reversescale = {
- valType: 'boolean',
-
- dflt: false,
- editType: 'plot',
-
- };
-
- if(!noScale) {
- attrs.showscale = {
- valType: 'boolean',
-
- dflt: showScaleDflt,
- editType: 'calc',
-
- };
- }
-
- return attrs;
- };
-
- },{"./scales.js":66}],59:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- module.exports = function calc(gd, trace, opts) {
- var fullLayout = gd._fullLayout;
- var vals = opts.vals;
- var containerStr = opts.containerStr;
- var cLetter = opts.cLetter;
-
- var container = containerStr ?
- Lib.nestedProperty(trace, containerStr).get() :
- trace;
-
- var autoAttr = cLetter + 'auto';
- var minAttr = cLetter + 'min';
- var maxAttr = cLetter + 'max';
- var auto = container[autoAttr];
- var min = container[minAttr];
- var max = container[maxAttr];
- var scl = container.colorscale;
-
- if(auto !== false || min === undefined) {
- min = Lib.aggNums(Math.min, null, vals);
- }
-
- if(auto !== false || max === undefined) {
- max = Lib.aggNums(Math.max, null, vals);
- }
-
- if(min === max) {
- min -= 0.5;
- max += 0.5;
- }
-
- container['_' + minAttr] = container[minAttr] = min;
- container['_' + maxAttr] = container[maxAttr] = max;
-
- if(container.autocolorscale) {
- if(min * max < 0) scl = fullLayout.colorscale.diverging;
- else if(min >= 0) scl = fullLayout.colorscale.sequential;
- else scl = fullLayout.colorscale.sequentialminus;
-
- container._colorscale = container.colorscale = scl;
- }
- };
-
- },{"../../lib":168}],60:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var hasColorscale = _dereq_('./helpers').hasColorscale;
-
- module.exports = function crossTraceDefaults(fullData) {
- function replace(cont, k) {
- var val = cont['_' + k];
- if(val !== undefined) {
- cont[k] = val;
- }
- }
-
- function relinkColorAtts(trace, cAttrs) {
- var cont = cAttrs.container ?
- Lib.nestedProperty(trace, cAttrs.container).get() :
- trace;
-
- if(cont) {
- var isAuto = cont.zauto || cont.cauto;
- var minAttr = cAttrs.min;
- var maxAttr = cAttrs.max;
-
- if(isAuto || cont[minAttr] === undefined) {
- replace(cont, minAttr);
- }
- if(isAuto || cont[maxAttr] === undefined) {
- replace(cont, maxAttr);
- }
- if(cont.autocolorscale) {
- replace(cont, 'colorscale');
- }
- }
- }
-
- for(var i = 0; i < fullData.length; i++) {
- var trace = fullData[i];
- var _module = trace._module;
-
- if(_module.colorbar) {
- relinkColorAtts(trace, _module.colorbar);
- }
-
- // TODO could generalize _module.colorscale and use it here?
-
- if(hasColorscale(trace, 'marker.line')) {
- relinkColorAtts(trace, {
- container: 'marker.line',
- min: 'cmin',
- max: 'cmax'
- });
- }
-
- if(hasColorscale(trace, 'line')) {
- relinkColorAtts(trace, {
- container: 'line',
- min: 'cmin',
- max: 'cmax'
- });
- }
- }
- };
-
- },{"../../lib":168,"./helpers":62}],61:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Lib = _dereq_('../../lib');
- var hasColorbar = _dereq_('../colorbar/has_colorbar');
- var colorbarDefaults = _dereq_('../colorbar/defaults');
-
- var isValidScale = _dereq_('./scales').isValid;
-
- function npMaybe(cont, prefix) {
- var containerStr = prefix.slice(0, prefix.length - 1);
- return prefix ?
- Lib.nestedProperty(cont, containerStr).get() || {} :
- cont;
- }
-
- module.exports = function colorScaleDefaults(traceIn, traceOut, layout, coerce, opts) {
- var prefix = opts.prefix;
- var cLetter = opts.cLetter;
- var containerIn = npMaybe(traceIn, prefix);
- var containerOut = npMaybe(traceOut, prefix);
- var template = npMaybe(traceOut._template || {}, prefix) || {};
-
- var minIn = containerIn[cLetter + 'min'];
- var maxIn = containerIn[cLetter + 'max'];
- var validMinMax = isNumeric(minIn) && isNumeric(maxIn) && (minIn < maxIn);
- coerce(prefix + cLetter + 'auto', !validMinMax);
- coerce(prefix + cLetter + 'min');
- coerce(prefix + cLetter + 'max');
-
- // handles both the trace case (autocolorscale is false by default) and
- // the marker and marker.line case (autocolorscale is true by default)
- var sclIn = containerIn.colorscale;
- var sclTemplate = template.colorscale;
- var autoColorscaleDflt;
- if(sclIn !== undefined) autoColorscaleDflt = !isValidScale(sclIn);
- if(sclTemplate !== undefined) autoColorscaleDflt = !isValidScale(sclTemplate);
- coerce(prefix + 'autocolorscale', autoColorscaleDflt);
-
- coerce(prefix + 'colorscale');
- coerce(prefix + 'reversescale');
-
- if(!opts.noScale && prefix !== 'marker.line.') {
- // handles both the trace case where the dflt is listed in attributes and
- // the marker case where the dflt is determined by hasColorbar
- var showScaleDflt;
- if(prefix) showScaleDflt = hasColorbar(containerIn);
-
- var showScale = coerce(prefix + 'showscale', showScaleDflt);
- if(showScale) colorbarDefaults(containerIn, containerOut, layout);
- }
- };
-
- },{"../../lib":168,"../colorbar/defaults":55,"../colorbar/has_colorbar":57,"./scales":66,"fast-isnumeric":18}],62:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var tinycolor = _dereq_('tinycolor2');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Lib = _dereq_('../../lib');
- var Color = _dereq_('../color');
-
- var isValidScale = _dereq_('./scales').isValid;
-
- function hasColorscale(trace, containerStr) {
- var container = containerStr ?
- Lib.nestedProperty(trace, containerStr).get() || {} :
- trace;
- var color = container.color;
-
- var isArrayWithOneNumber = false;
- if(Lib.isArrayOrTypedArray(color)) {
- for(var i = 0; i < color.length; i++) {
- if(isNumeric(color[i])) {
- isArrayWithOneNumber = true;
- break;
- }
- }
- }
-
- return (
- Lib.isPlainObject(container) && (
- isArrayWithOneNumber ||
- container.showscale === true ||
- (isNumeric(container.cmin) && isNumeric(container.cmax)) ||
- isValidScale(container.colorscale) ||
- Lib.isPlainObject(container.colorbar)
- )
- );
- }
-
- /**
- * Extract colorscale into numeric domain and color range.
- *
- * @param {object} cont colorscale container (e.g. trace, marker)
- * - colorscale {array of arrays}
- * - cmin/zmin {number}
- * - cmax/zmax {number}
- * - reversescale {boolean}
- * @param {object} opts
- * - cLetter {string} 'c' (for cmin/cmax) or 'z' (for zmin/zmax)
- *
- * @return {object}
- * - domain {array}
- * - range {array}
- */
- function extractScale(cont, opts) {
- var cLetter = opts.cLetter;
-
- var scl = cont.reversescale ?
- flipScale(cont.colorscale) :
- cont.colorscale;
-
- // minimum color value (used to clamp scale)
- var cmin = cont[cLetter + 'min'];
- // maximum color value (used to clamp scale)
- var cmax = cont[cLetter + 'max'];
-
- var N = scl.length;
- var domain = new Array(N);
- var range = new Array(N);
-
- for(var i = 0; i < N; i++) {
- var si = scl[i];
- domain[i] = cmin + si[0] * (cmax - cmin);
- range[i] = si[1];
- }
-
- return {
- domain: domain,
- range: range
- };
- }
-
- function flipScale(scl) {
- var N = scl.length;
- var sclNew = new Array(N);
-
- for(var i = N - 1, j = 0; i >= 0; i--, j++) {
- var si = scl[i];
- sclNew[j] = [1 - si[0], si[1]];
- }
- return sclNew;
- }
-
- /**
- * General colorscale function generator.
- *
- * @param {object} specs output of Colorscale.extractScale or precomputed domain, range.
- * - domain {array}
- * - range {array}
- *
- * @param {object} opts
- * - noNumericCheck {boolean} if true, scale func bypasses numeric checks
- * - returnArray {boolean} if true, scale func return 4-item array instead of color strings
- *
- * @return {function}
- */
- function makeColorScaleFunc(specs, opts) {
- opts = opts || {};
-
- var domain = specs.domain;
- var range = specs.range;
- var N = range.length;
- var _range = new Array(N);
-
- for(var i = 0; i < N; i++) {
- var rgba = tinycolor(range[i]).toRgb();
- _range[i] = [rgba.r, rgba.g, rgba.b, rgba.a];
- }
-
- var _sclFunc = d3.scale.linear()
- .domain(domain)
- .range(_range)
- .clamp(true);
-
- var noNumericCheck = opts.noNumericCheck;
- var returnArray = opts.returnArray;
- var sclFunc;
-
- if(noNumericCheck && returnArray) {
- sclFunc = _sclFunc;
- }
- else if(noNumericCheck) {
- sclFunc = function(v) {
- return colorArray2rbga(_sclFunc(v));
- };
- }
- else if(returnArray) {
- sclFunc = function(v) {
- if(isNumeric(v)) return _sclFunc(v);
- else if(tinycolor(v).isValid()) return v;
- else return Color.defaultLine;
- };
- }
- else {
- sclFunc = function(v) {
- if(isNumeric(v)) return colorArray2rbga(_sclFunc(v));
- else if(tinycolor(v).isValid()) return v;
- else return Color.defaultLine;
- };
- }
-
- // colorbar draw looks into the d3 scale closure for domain and range
-
- sclFunc.domain = _sclFunc.domain;
-
- sclFunc.range = function() { return range; };
-
- return sclFunc;
- }
-
- function colorArray2rbga(colorArray) {
- var colorObj = {
- r: colorArray[0],
- g: colorArray[1],
- b: colorArray[2],
- a: colorArray[3]
- };
-
- return tinycolor(colorObj).toRgbString();
- }
-
- module.exports = {
- hasColorscale: hasColorscale,
- extractScale: extractScale,
- flipScale: flipScale,
- makeColorScaleFunc: makeColorScaleFunc
- };
-
- },{"../../lib":168,"../color":51,"./scales":66,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],63:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var scales = _dereq_('./scales');
- var helpers = _dereq_('./helpers');
-
- module.exports = {
- moduleType: 'component',
- name: 'colorscale',
-
- attributes: _dereq_('./attributes'),
- layoutAttributes: _dereq_('./layout_attributes'),
-
- supplyLayoutDefaults: _dereq_('./layout_defaults'),
- handleDefaults: _dereq_('./defaults'),
- crossTraceDefaults: _dereq_('./cross_trace_defaults'),
-
- calc: _dereq_('./calc'),
-
- // ./scales.js is required in lib/coerce.js ;
- // it needs to be a seperate module to avoid circular a dependency
- scales: scales.scales,
- defaultScale: scales.defaultScale,
- getScale: scales.get,
- isValidScale: scales.isValid,
-
- hasColorscale: helpers.hasColorscale,
- flipScale: helpers.flipScale,
- extractScale: helpers.extractScale,
- makeColorScaleFunc: helpers.makeColorScaleFunc
- };
-
- },{"./attributes":58,"./calc":59,"./cross_trace_defaults":60,"./defaults":61,"./helpers":62,"./layout_attributes":64,"./layout_defaults":65,"./scales":66}],64:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var scales = _dereq_('./scales').scales;
-
- var msg = 'Note that `autocolorscale` must be true for this attribute to work.';
-
- module.exports = {
- editType: 'calc',
- sequential: {
- valType: 'colorscale',
- dflt: scales.Reds,
-
- editType: 'calc',
-
- },
- sequentialminus: {
- valType: 'colorscale',
- dflt: scales.Blues,
-
- editType: 'calc',
-
- },
- diverging: {
- valType: 'colorscale',
- dflt: scales.RdBu,
-
- editType: 'calc',
-
- }
- };
-
- },{"./scales":66}],65:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var colorscaleAttrs = _dereq_('./layout_attributes');
- var Template = _dereq_('../../plot_api/plot_template');
-
- module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
- var colorscaleIn = layoutIn.colorscale;
- var colorscaleOut = Template.newContainer(layoutOut, 'colorscale');
- function coerce(attr, dflt) {
- return Lib.coerce(colorscaleIn, colorscaleOut, colorscaleAttrs, attr, dflt);
- }
-
- coerce('sequential');
- coerce('sequentialminus');
- coerce('diverging');
- };
-
- },{"../../lib":168,"../../plot_api/plot_template":202,"./layout_attributes":64}],66:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var tinycolor = _dereq_('tinycolor2');
-
- var scales = {
- 'Greys': [
- [0, 'rgb(0,0,0)'], [1, 'rgb(255,255,255)']
- ],
-
- 'YlGnBu': [
- [0, 'rgb(8,29,88)'], [0.125, 'rgb(37,52,148)'],
- [0.25, 'rgb(34,94,168)'], [0.375, 'rgb(29,145,192)'],
- [0.5, 'rgb(65,182,196)'], [0.625, 'rgb(127,205,187)'],
- [0.75, 'rgb(199,233,180)'], [0.875, 'rgb(237,248,217)'],
- [1, 'rgb(255,255,217)']
- ],
-
- 'Greens': [
- [0, 'rgb(0,68,27)'], [0.125, 'rgb(0,109,44)'],
- [0.25, 'rgb(35,139,69)'], [0.375, 'rgb(65,171,93)'],
- [0.5, 'rgb(116,196,118)'], [0.625, 'rgb(161,217,155)'],
- [0.75, 'rgb(199,233,192)'], [0.875, 'rgb(229,245,224)'],
- [1, 'rgb(247,252,245)']
- ],
-
- 'YlOrRd': [
- [0, 'rgb(128,0,38)'], [0.125, 'rgb(189,0,38)'],
- [0.25, 'rgb(227,26,28)'], [0.375, 'rgb(252,78,42)'],
- [0.5, 'rgb(253,141,60)'], [0.625, 'rgb(254,178,76)'],
- [0.75, 'rgb(254,217,118)'], [0.875, 'rgb(255,237,160)'],
- [1, 'rgb(255,255,204)']
- ],
-
- 'Bluered': [
- [0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']
- ],
-
- // modified RdBu based on
- // www.sandia.gov/~kmorel/documents/ColorMaps/ColorMapsExpanded.pdf
- 'RdBu': [
- [0, 'rgb(5,10,172)'], [0.35, 'rgb(106,137,247)'],
- [0.5, 'rgb(190,190,190)'], [0.6, 'rgb(220,170,132)'],
- [0.7, 'rgb(230,145,90)'], [1, 'rgb(178,10,28)']
- ],
-
- // Scale for non-negative numeric values
- 'Reds': [
- [0, 'rgb(220,220,220)'], [0.2, 'rgb(245,195,157)'],
- [0.4, 'rgb(245,160,105)'], [1, 'rgb(178,10,28)']
- ],
-
- // Scale for non-positive numeric values
- 'Blues': [
- [0, 'rgb(5,10,172)'], [0.35, 'rgb(40,60,190)'],
- [0.5, 'rgb(70,100,245)'], [0.6, 'rgb(90,120,245)'],
- [0.7, 'rgb(106,137,247)'], [1, 'rgb(220,220,220)']
- ],
-
- 'Picnic': [
- [0, 'rgb(0,0,255)'], [0.1, 'rgb(51,153,255)'],
- [0.2, 'rgb(102,204,255)'], [0.3, 'rgb(153,204,255)'],
- [0.4, 'rgb(204,204,255)'], [0.5, 'rgb(255,255,255)'],
- [0.6, 'rgb(255,204,255)'], [0.7, 'rgb(255,153,255)'],
- [0.8, 'rgb(255,102,204)'], [0.9, 'rgb(255,102,102)'],
- [1, 'rgb(255,0,0)']
- ],
-
- 'Rainbow': [
- [0, 'rgb(150,0,90)'], [0.125, 'rgb(0,0,200)'],
- [0.25, 'rgb(0,25,255)'], [0.375, 'rgb(0,152,255)'],
- [0.5, 'rgb(44,255,150)'], [0.625, 'rgb(151,255,0)'],
- [0.75, 'rgb(255,234,0)'], [0.875, 'rgb(255,111,0)'],
- [1, 'rgb(255,0,0)']
- ],
-
- 'Portland': [
- [0, 'rgb(12,51,131)'], [0.25, 'rgb(10,136,186)'],
- [0.5, 'rgb(242,211,56)'], [0.75, 'rgb(242,143,56)'],
- [1, 'rgb(217,30,30)']
- ],
-
- 'Jet': [
- [0, 'rgb(0,0,131)'], [0.125, 'rgb(0,60,170)'],
- [0.375, 'rgb(5,255,255)'], [0.625, 'rgb(255,255,0)'],
- [0.875, 'rgb(250,0,0)'], [1, 'rgb(128,0,0)']
- ],
-
- 'Hot': [
- [0, 'rgb(0,0,0)'], [0.3, 'rgb(230,0,0)'],
- [0.6, 'rgb(255,210,0)'], [1, 'rgb(255,255,255)']
- ],
-
- 'Blackbody': [
- [0, 'rgb(0,0,0)'], [0.2, 'rgb(230,0,0)'],
- [0.4, 'rgb(230,210,0)'], [0.7, 'rgb(255,255,255)'],
- [1, 'rgb(160,200,255)']
- ],
-
- 'Earth': [
- [0, 'rgb(0,0,130)'], [0.1, 'rgb(0,180,180)'],
- [0.2, 'rgb(40,210,40)'], [0.4, 'rgb(230,230,50)'],
- [0.6, 'rgb(120,70,20)'], [1, 'rgb(255,255,255)']
- ],
-
- 'Electric': [
- [0, 'rgb(0,0,0)'], [0.15, 'rgb(30,0,100)'],
- [0.4, 'rgb(120,0,100)'], [0.6, 'rgb(160,90,0)'],
- [0.8, 'rgb(230,200,0)'], [1, 'rgb(255,250,220)']
- ],
-
- 'Viridis': [
- [0, '#440154'], [0.06274509803921569, '#48186a'],
- [0.12549019607843137, '#472d7b'], [0.18823529411764706, '#424086'],
- [0.25098039215686274, '#3b528b'], [0.3137254901960784, '#33638d'],
- [0.3764705882352941, '#2c728e'], [0.4392156862745098, '#26828e'],
- [0.5019607843137255, '#21918c'], [0.5647058823529412, '#1fa088'],
- [0.6274509803921569, '#28ae80'], [0.6901960784313725, '#3fbc73'],
- [0.7529411764705882, '#5ec962'], [0.8156862745098039, '#84d44b'],
- [0.8784313725490196, '#addc30'], [0.9411764705882353, '#d8e219'],
- [1, '#fde725']
- ],
-
- 'Cividis': [
- [0.000000, 'rgb(0,32,76)'], [0.058824, 'rgb(0,42,102)'],
- [0.117647, 'rgb(0,52,110)'], [0.176471, 'rgb(39,63,108)'],
- [0.235294, 'rgb(60,74,107)'], [0.294118, 'rgb(76,85,107)'],
- [0.352941, 'rgb(91,95,109)'], [0.411765, 'rgb(104,106,112)'],
- [0.470588, 'rgb(117,117,117)'], [0.529412, 'rgb(131,129,120)'],
- [0.588235, 'rgb(146,140,120)'], [0.647059, 'rgb(161,152,118)'],
- [0.705882, 'rgb(176,165,114)'], [0.764706, 'rgb(192,177,109)'],
- [0.823529, 'rgb(209,191,102)'], [0.882353, 'rgb(225,204,92)'],
- [0.941176, 'rgb(243,219,79)'], [1.000000, 'rgb(255,233,69)']
- ]
- };
-
- var defaultScale = scales.RdBu;
-
- function getScale(scl, dflt) {
- if(!dflt) dflt = defaultScale;
- if(!scl) return dflt;
-
- function parseScale() {
- try {
- scl = scales[scl] || JSON.parse(scl);
- } catch(e) {
- scl = dflt;
- }
- }
-
- if(typeof scl === 'string') {
- parseScale();
- // occasionally scl is double-JSON encoded...
- if(typeof scl === 'string') parseScale();
- }
-
- if(!isValidScaleArray(scl)) return dflt;
- return scl;
- }
-
-
- function isValidScaleArray(scl) {
- var highestVal = 0;
-
- if(!Array.isArray(scl) || scl.length < 2) return false;
-
- if(!scl[0] || !scl[scl.length - 1]) return false;
-
- if(+scl[0][0] !== 0 || +scl[scl.length - 1][0] !== 1) return false;
-
- for(var i = 0; i < scl.length; i++) {
- var si = scl[i];
-
- if(si.length !== 2 || +si[0] < highestVal || !tinycolor(si[1]).isValid()) {
- return false;
- }
-
- highestVal = +si[0];
- }
-
- return true;
- }
-
- function isValidScale(scl) {
- if(scales[scl] !== undefined) return true;
- else return isValidScaleArray(scl);
- }
-
- module.exports = {
- scales: scales,
- defaultScale: defaultScale,
-
- get: getScale,
- isValid: isValidScale
- };
-
- },{"tinycolor2":34}],67:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- // for automatic alignment on dragging, <1/3 means left align,
- // >2/3 means right, and between is center. Pick the right fraction
- // based on where you are, and return the fraction corresponding to
- // that position on the object
- module.exports = function align(v, dv, v0, v1, anchor) {
- var vmin = (v - v0) / (v1 - v0);
- var vmax = vmin + dv / (v1 - v0);
- var vc = (vmin + vmax) / 2;
-
- // explicitly specified anchor
- if(anchor === 'left' || anchor === 'bottom') return vmin;
- if(anchor === 'center' || anchor === 'middle') return vc;
- if(anchor === 'right' || anchor === 'top') return vmax;
-
- // automatic based on position
- if(vmin < (2 / 3) - vc) return vmin;
- if(vmax > (4 / 3) - vc) return vmax;
- return vc;
- };
-
- },{}],68:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
-
- // set cursors pointing toward the closest corner/side,
- // to indicate alignment
- // x and y are 0-1, fractions of the plot area
- var cursorset = [
- ['sw-resize', 's-resize', 'se-resize'],
- ['w-resize', 'move', 'e-resize'],
- ['nw-resize', 'n-resize', 'ne-resize']
- ];
-
- module.exports = function getCursor(x, y, xanchor, yanchor) {
- if(xanchor === 'left') x = 0;
- else if(xanchor === 'center') x = 1;
- else if(xanchor === 'right') x = 2;
- else x = Lib.constrain(Math.floor(x * 3), 0, 2);
-
- if(yanchor === 'bottom') y = 0;
- else if(yanchor === 'middle') y = 1;
- else if(yanchor === 'top') y = 2;
- else y = Lib.constrain(Math.floor(y * 3), 0, 2);
-
- return cursorset[y][x];
- };
-
- },{"../../lib":168}],69:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var mouseOffset = _dereq_('mouse-event-offset');
- var hasHover = _dereq_('has-hover');
- var supportsPassive = _dereq_('has-passive-events');
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
-
- var constants = _dereq_('../../plots/cartesian/constants');
- var interactConstants = _dereq_('../../constants/interactions');
-
- var dragElement = module.exports = {};
-
- dragElement.align = _dereq_('./align');
- dragElement.getCursor = _dereq_('./cursor');
-
- var unhover = _dereq_('./unhover');
- dragElement.unhover = unhover.wrapped;
- dragElement.unhoverRaw = unhover.raw;
-
-
- /**
- * Abstracts click & drag interactions
- *
- * During the interaction, a "coverSlip" element - a transparent
- * div covering the whole page - is created, which has two key effects:
- * - Lets you drag beyond the boundaries of the plot itself without
- * dropping (but if you drag all the way out of the browser window the
- * interaction will end)
- * - Freezes the cursor: whatever mouse cursor the drag element had when the
- * interaction started gets copied to the coverSlip for use until mouseup
- *
- * If the user executes a drag bigger than MINDRAG, callbacks will fire as:
- * prepFn, moveFn (1 or more times), doneFn
- * If the user does not drag enough, prepFn and clickFn will fire.
- *
- * Note: If you cancel contextmenu, clickFn will fire even with a right click
- * (unlike native events) so you'll get a `plotly_click` event. Cancel context eg:
- * gd.addEventListener('contextmenu', function(e) { e.preventDefault(); });
- * TODO: we should probably turn this into a `config` parameter, so we can fix it
- * such that if you *don't* cancel contextmenu, we can prevent partial drags, which
- * put you in a weird state.
- *
- * If the user clicks multiple times quickly, clickFn will fire each time
- * but numClicks will increase to help you recognize doubleclicks.
- *
- * @param {object} options with keys:
- * element (required) the DOM element to drag
- * prepFn (optional) function(event, startX, startY)
- * executed on mousedown
- * startX and startY are the clientX and clientY pixel position
- * of the mousedown event
- * moveFn (optional) function(dx, dy)
- * executed on move, ONLY after we've exceeded MINDRAG
- * (we keep executing moveFn if you move back to where you started)
- * dx and dy are the net pixel offset of the drag,
- * dragged is true/false, has the mouse moved enough to
- * constitute a drag
- * doneFn (optional) function(e)
- * executed on mouseup, ONLY if we exceeded MINDRAG (so you can be
- * sure that moveFn has been called at least once)
- * numClicks is how many clicks we've registered within
- * a doubleclick time
- * e is the original mouseup event
- * clickFn (optional) function(numClicks, e)
- * executed on mouseup if we have NOT exceeded MINDRAG (ie moveFn
- * has not been called at all)
- * numClicks is how many clicks we've registered within
- * a doubleclick time
- * e is the original mousedown event
- * clampFn (optional, function(dx, dy) return [dx2, dy2])
- * Provide custom clamping function for small displacements.
- * By default, clamping is done using `minDrag` to x and y displacements
- * independently.
- */
- dragElement.init = function init(options) {
- var gd = options.gd;
- var numClicks = 1;
- var DBLCLICKDELAY = interactConstants.DBLCLICKDELAY;
- var element = options.element;
-
- var startX,
- startY,
- newMouseDownTime,
- cursor,
- dragCover,
- initialEvent,
- initialTarget,
- rightClick;
-
- if(!gd._mouseDownTime) gd._mouseDownTime = 0;
-
- element.style.pointerEvents = 'all';
-
- element.onmousedown = onStart;
-
- if(!supportsPassive) {
- element.ontouchstart = onStart;
- }
- else {
- if(element._ontouchstart) {
- element.removeEventListener('touchstart', element._ontouchstart);
- }
- element._ontouchstart = onStart;
- element.addEventListener('touchstart', onStart, {passive: false});
- }
-
- function _clampFn(dx, dy, minDrag) {
- if(Math.abs(dx) < minDrag) dx = 0;
- if(Math.abs(dy) < minDrag) dy = 0;
- return [dx, dy];
- }
-
- var clampFn = options.clampFn || _clampFn;
-
- function onStart(e) {
- // make dragging and dragged into properties of gd
- // so that others can look at and modify them
- gd._dragged = false;
- gd._dragging = true;
- var offset = pointerOffset(e);
- startX = offset[0];
- startY = offset[1];
- initialTarget = e.target;
- initialEvent = e;
- rightClick = e.buttons === 2 || e.ctrlKey;
-
- // fix Fx.hover for touch events
- if(typeof e.clientX === 'undefined' && typeof e.clientY === 'undefined') {
- e.clientX = startX;
- e.clientY = startY;
- }
-
- newMouseDownTime = (new Date()).getTime();
- if(newMouseDownTime - gd._mouseDownTime < DBLCLICKDELAY) {
- // in a click train
- numClicks += 1;
- }
- else {
- // new click train
- numClicks = 1;
- gd._mouseDownTime = newMouseDownTime;
- }
-
- if(options.prepFn) options.prepFn(e, startX, startY);
-
- if(hasHover && !rightClick) {
- dragCover = coverSlip();
- dragCover.style.cursor = window.getComputedStyle(element).cursor;
- }
- else if(!hasHover) {
- // document acts as a dragcover for mobile, bc we can't create dragcover dynamically
- dragCover = document;
- cursor = window.getComputedStyle(document.documentElement).cursor;
- document.documentElement.style.cursor = window.getComputedStyle(element).cursor;
- }
-
- document.addEventListener('mouseup', onDone);
- document.addEventListener('touchend', onDone);
-
- if(options.dragmode !== false) {
- e.preventDefault();
- document.addEventListener('mousemove', onMove);
- document.addEventListener('touchmove', onMove);
- }
-
- return;
- }
-
- function onMove(e) {
- e.preventDefault();
-
- var offset = pointerOffset(e);
- var minDrag = options.minDrag || constants.MINDRAG;
- var dxdy = clampFn(offset[0] - startX, offset[1] - startY, minDrag);
- var dx = dxdy[0];
- var dy = dxdy[1];
-
- if(dx || dy) {
- gd._dragged = true;
- dragElement.unhover(gd);
- }
-
- if(gd._dragged && options.moveFn && !rightClick) options.moveFn(dx, dy);
-
- return;
- }
-
- function onDone(e) {
- if(options.dragmode !== false) {
- e.preventDefault();
- document.removeEventListener('mousemove', onMove);
- document.removeEventListener('touchmove', onMove);
- }
-
- document.removeEventListener('mouseup', onDone);
- document.removeEventListener('touchend', onDone);
-
- if(hasHover) {
- Lib.removeElement(dragCover);
- }
- else if(cursor) {
- dragCover.documentElement.style.cursor = cursor;
- cursor = null;
- }
-
- if(!gd._dragging) {
- gd._dragged = false;
- return;
- }
- gd._dragging = false;
-
- // don't count as a dblClick unless the mouseUp is also within
- // the dblclick delay
- if((new Date()).getTime() - gd._mouseDownTime > DBLCLICKDELAY) {
- numClicks = Math.max(numClicks - 1, 1);
- }
-
- if(gd._dragged) {
- if(options.doneFn) options.doneFn();
- }
- else {
- if(options.clickFn) options.clickFn(numClicks, initialEvent);
-
- // If we haven't dragged, this should be a click. But because of the
- // coverSlip changing the element, the natural system might not generate one,
- // so we need to make our own. But right clicks don't normally generate
- // click events, only contextmenu events, which happen on mousedown.
- if(!rightClick) {
- var e2;
-
- try {
- e2 = new MouseEvent('click', e);
- }
- catch(err) {
- var offset = pointerOffset(e);
- e2 = document.createEvent('MouseEvents');
- e2.initMouseEvent('click',
- e.bubbles, e.cancelable,
- e.view, e.detail,
- e.screenX, e.screenY,
- offset[0], offset[1],
- e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
- e.button, e.relatedTarget);
- }
-
- initialTarget.dispatchEvent(e2);
- }
- }
-
- finishDrag(gd);
-
- gd._dragged = false;
-
- return;
- }
- };
-
- function coverSlip() {
- var cover = document.createElement('div');
-
- cover.className = 'dragcover';
- var cStyle = cover.style;
- cStyle.position = 'fixed';
- cStyle.left = 0;
- cStyle.right = 0;
- cStyle.top = 0;
- cStyle.bottom = 0;
- cStyle.zIndex = 999999999;
- cStyle.background = 'none';
-
- document.body.appendChild(cover);
-
- return cover;
- }
-
- dragElement.coverSlip = coverSlip;
-
- function finishDrag(gd) {
- gd._dragging = false;
- if(gd._replotPending) Registry.call('plot', gd);
- }
-
- function pointerOffset(e) {
- return mouseOffset(
- e.changedTouches ? e.changedTouches[0] : e,
- document.body
- );
- }
-
- },{"../../constants/interactions":148,"../../lib":168,"../../plots/cartesian/constants":218,"../../registry":257,"./align":67,"./cursor":68,"./unhover":70,"has-hover":20,"has-passive-events":21,"mouse-event-offset":24}],70:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- var Events = _dereq_('../../lib/events');
- var throttle = _dereq_('../../lib/throttle');
- var getGraphDiv = _dereq_('../../lib/get_graph_div');
-
- var hoverConstants = _dereq_('../fx/constants');
-
- var unhover = module.exports = {};
-
-
- unhover.wrapped = function(gd, evt, subplot) {
- gd = getGraphDiv(gd);
-
- // Important, clear any queued hovers
- if(gd._fullLayout) {
- throttle.clear(gd._fullLayout._uid + hoverConstants.HOVERID);
- }
-
- unhover.raw(gd, evt, subplot);
- };
-
-
- // remove hover effects on mouse out, and emit unhover event
- unhover.raw = function unhoverRaw(gd, evt) {
- var fullLayout = gd._fullLayout;
- var oldhoverdata = gd._hoverdata;
-
- if(!evt) evt = {};
- if(evt.target &&
- Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) {
- return;
- }
-
- fullLayout._hoverlayer.selectAll('g').remove();
- fullLayout._hoverlayer.selectAll('line').remove();
- fullLayout._hoverlayer.selectAll('circle').remove();
- gd._hoverdata = undefined;
-
- if(evt.target && oldhoverdata) {
- gd.emit('plotly_unhover', {
- event: evt,
- points: oldhoverdata
- });
- }
- };
-
- },{"../../lib/events":161,"../../lib/get_graph_div":166,"../../lib/throttle":190,"../fx/constants":84}],71:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- exports.dash = {
- valType: 'string',
- // string type usually doesn't take values... this one should really be
- // a special type or at least a special coercion function, from the GUI
- // you only get these values but elsewhere the user can supply a list of
- // dash lengths in px, and it will be honored
- values: ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'],
- dflt: 'solid',
-
- editType: 'style',
-
- };
-
- },{}],72:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
- var tinycolor = _dereq_('tinycolor2');
-
- var Registry = _dereq_('../../registry');
- var Color = _dereq_('../color');
- var Colorscale = _dereq_('../colorscale');
- var Lib = _dereq_('../../lib');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
-
- var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
- var alignment = _dereq_('../../constants/alignment');
- var LINE_SPACING = alignment.LINE_SPACING;
- var DESELECTDIM = _dereq_('../../constants/interactions').DESELECTDIM;
-
- var subTypes = _dereq_('../../traces/scatter/subtypes');
- var makeBubbleSizeFn = _dereq_('../../traces/scatter/make_bubble_size_func');
-
- var drawing = module.exports = {};
-
- // -----------------------------------------------------
- // styling functions for plot elements
- // -----------------------------------------------------
-
- drawing.font = function(s, family, size, color) {
- // also allow the form font(s, {family, size, color})
- if(Lib.isPlainObject(family)) {
- color = family.color;
- size = family.size;
- family = family.family;
- }
- if(family) s.style('font-family', family);
- if(size + 1) s.style('font-size', size + 'px');
- if(color) s.call(Color.fill, color);
- };
-
- /*
- * Positioning helpers
- * Note: do not use `setPosition` with <text> nodes modified by
- * `svgTextUtils.convertToTspans`. Use `svgTextUtils.positionText`
- * instead, so that <tspan.line> elements get updated to match.
- */
- drawing.setPosition = function(s, x, y) { s.attr('x', x).attr('y', y); };
- drawing.setSize = function(s, w, h) { s.attr('width', w).attr('height', h); };
- drawing.setRect = function(s, x, y, w, h) {
- s.call(drawing.setPosition, x, y).call(drawing.setSize, w, h);
- };
-
- /** Translate node
- *
- * @param {object} d : calcdata point item
- * @param {sel} sel : d3 selction of node to translate
- * @param {object} xa : corresponding full xaxis object
- * @param {object} ya : corresponding full yaxis object
- *
- * @return {boolean} :
- * true if selection got translated
- * false if selection could not get translated
- */
- drawing.translatePoint = function(d, sel, xa, ya) {
- var x = xa.c2p(d.x);
- var y = ya.c2p(d.y);
-
- if(isNumeric(x) && isNumeric(y) && sel.node()) {
- // for multiline text this works better
- if(sel.node().nodeName === 'text') {
- sel.attr('x', x).attr('y', y);
- } else {
- sel.attr('transform', 'translate(' + x + ',' + y + ')');
- }
- } else {
- return false;
- }
-
- return true;
- };
-
- drawing.translatePoints = function(s, xa, ya) {
- s.each(function(d) {
- var sel = d3.select(this);
- drawing.translatePoint(d, sel, xa, ya);
- });
- };
-
- drawing.hideOutsideRangePoint = function(d, sel, xa, ya, xcalendar, ycalendar) {
- sel.attr(
- 'display',
- (xa.isPtWithinRange(d, xcalendar) && ya.isPtWithinRange(d, ycalendar)) ? null : 'none'
- );
- };
-
- drawing.hideOutsideRangePoints = function(traceGroups, subplot) {
- if(!subplot._hasClipOnAxisFalse) return;
-
- var xa = subplot.xaxis;
- var ya = subplot.yaxis;
-
- traceGroups.each(function(d) {
- var trace = d[0].trace;
- var xcalendar = trace.xcalendar;
- var ycalendar = trace.ycalendar;
- var selector = trace.type === 'bar' ? '.bartext' : '.point,.textpoint';
-
- traceGroups.selectAll(selector).each(function(d) {
- drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya, xcalendar, ycalendar);
- });
- });
- };
-
- drawing.crispRound = function(gd, lineWidth, dflt) {
- // for lines that disable antialiasing we want to
- // make sure the width is an integer, and at least 1 if it's nonzero
-
- if(!lineWidth || !isNumeric(lineWidth)) return dflt || 0;
-
- // but not for static plots - these don't get antialiased anyway.
- if(gd._context.staticPlot) return lineWidth;
-
- if(lineWidth < 1) return 1;
- return Math.round(lineWidth);
- };
-
- drawing.singleLineStyle = function(d, s, lw, lc, ld) {
- s.style('fill', 'none');
- var line = (((d || [])[0] || {}).trace || {}).line || {};
- var lw1 = lw || line.width||0;
- var dash = ld || line.dash || '';
-
- Color.stroke(s, lc || line.color);
- drawing.dashLine(s, dash, lw1);
- };
-
- drawing.lineGroupStyle = function(s, lw, lc, ld) {
- s.style('fill', 'none')
- .each(function(d) {
- var line = (((d || [])[0] || {}).trace || {}).line || {};
- var lw1 = lw || line.width||0;
- var dash = ld || line.dash || '';
-
- d3.select(this)
- .call(Color.stroke, lc || line.color)
- .call(drawing.dashLine, dash, lw1);
- });
- };
-
- drawing.dashLine = function(s, dash, lineWidth) {
- lineWidth = +lineWidth || 0;
-
- dash = drawing.dashStyle(dash, lineWidth);
-
- s.style({
- 'stroke-dasharray': dash,
- 'stroke-width': lineWidth + 'px'
- });
- };
-
- drawing.dashStyle = function(dash, lineWidth) {
- lineWidth = +lineWidth || 1;
- var dlw = Math.max(lineWidth, 3);
-
- if(dash === 'solid') dash = '';
- else if(dash === 'dot') dash = dlw + 'px,' + dlw + 'px';
- else if(dash === 'dash') dash = (3 * dlw) + 'px,' + (3 * dlw) + 'px';
- else if(dash === 'longdash') dash = (5 * dlw) + 'px,' + (5 * dlw) + 'px';
- else if(dash === 'dashdot') {
- dash = (3 * dlw) + 'px,' + dlw + 'px,' + dlw + 'px,' + dlw + 'px';
- }
- else if(dash === 'longdashdot') {
- dash = (5 * dlw) + 'px,' + (2 * dlw) + 'px,' + dlw + 'px,' + (2 * dlw) + 'px';
- }
- // otherwise user wrote the dasharray themselves - leave it be
-
- return dash;
- };
-
- // Same as fillGroupStyle, except in this case the selection may be a transition
- drawing.singleFillStyle = function(sel) {
- var node = d3.select(sel.node());
- var data = node.data();
- var fillcolor = (((data[0] || [])[0] || {}).trace || {}).fillcolor;
- if(fillcolor) {
- sel.call(Color.fill, fillcolor);
- }
- };
-
- drawing.fillGroupStyle = function(s) {
- s.style('stroke-width', 0)
- .each(function(d) {
- var shape = d3.select(this);
- // N.B. 'd' won't be a calcdata item when
- // fill !== 'none' on a segment-less and marker-less trace
- if(d[0].trace) {
- shape.call(Color.fill, d[0].trace.fillcolor);
- }
- });
- };
-
- var SYMBOLDEFS = _dereq_('./symbol_defs');
-
- drawing.symbolNames = [];
- drawing.symbolFuncs = [];
- drawing.symbolNeedLines = {};
- drawing.symbolNoDot = {};
- drawing.symbolNoFill = {};
- drawing.symbolList = [];
-
- Object.keys(SYMBOLDEFS).forEach(function(k) {
- var symDef = SYMBOLDEFS[k];
- drawing.symbolList = drawing.symbolList.concat(
- [symDef.n, k, symDef.n + 100, k + '-open']);
- drawing.symbolNames[symDef.n] = k;
- drawing.symbolFuncs[symDef.n] = symDef.f;
- if(symDef.needLine) {
- drawing.symbolNeedLines[symDef.n] = true;
- }
- if(symDef.noDot) {
- drawing.symbolNoDot[symDef.n] = true;
- }
- else {
- drawing.symbolList = drawing.symbolList.concat(
- [symDef.n + 200, k + '-dot', symDef.n + 300, k + '-open-dot']);
- }
- if(symDef.noFill) {
- drawing.symbolNoFill[symDef.n] = true;
- }
- });
-
- var MAXSYMBOL = drawing.symbolNames.length;
- // add a dot in the middle of the symbol
- var DOTPATH = 'M0,0.5L0.5,0L0,-0.5L-0.5,0Z';
-
- drawing.symbolNumber = function(v) {
- if(typeof v === 'string') {
- var vbase = 0;
- if(v.indexOf('-open') > 0) {
- vbase = 100;
- v = v.replace('-open', '');
- }
- if(v.indexOf('-dot') > 0) {
- vbase += 200;
- v = v.replace('-dot', '');
- }
- v = drawing.symbolNames.indexOf(v);
- if(v >= 0) { v += vbase; }
- }
- if((v % 100 >= MAXSYMBOL) || v >= 400) { return 0; }
- return Math.floor(Math.max(v, 0));
- };
-
- function makePointPath(symbolNumber, r) {
- var base = symbolNumber % 100;
- return drawing.symbolFuncs[base](r) + (symbolNumber >= 200 ? DOTPATH : '');
- }
-
- var HORZGRADIENT = {x1: 1, x2: 0, y1: 0, y2: 0};
- var VERTGRADIENT = {x1: 0, x2: 0, y1: 1, y2: 0};
- var stopFormatter = d3.format('~.1f');
- var gradientInfo = {
- radial: {node: 'radialGradient'},
- radialreversed: {node: 'radialGradient', reversed: true},
- horizontal: {node: 'linearGradient', attrs: HORZGRADIENT},
- horizontalreversed: {node: 'linearGradient', attrs: HORZGRADIENT, reversed: true},
- vertical: {node: 'linearGradient', attrs: VERTGRADIENT},
- verticalreversed: {node: 'linearGradient', attrs: VERTGRADIENT, reversed: true}
- };
-
- /**
- * gradient: create and apply a gradient fill
- *
- * @param {object} sel: d3 selection to apply this gradient to
- * You can use `selection.call(Drawing.gradient, ...)`
- * @param {DOM element} gd: the graph div `sel` is part of
- * @param {string} gradientID: a unique (within this plot) identifier
- * for this gradient, so that we don't create unnecessary definitions
- * @param {string} type: 'radial', 'horizontal', or 'vertical', optionally with
- * 'reversed' at the end. Normally radial goes center to edge,
- * horizontal goes right to left, and vertical goes bottom to top
- * @param {array} colorscale: as in attribute values, [[fraction, color], ...]
- * @param {string} prop: the property to apply to, 'fill' or 'stroke'
- */
- drawing.gradient = function(sel, gd, gradientID, type, colorscale, prop) {
- var len = colorscale.length;
- var info = gradientInfo[type];
- var colorStops = new Array(len);
- for(var i = 0; i < len; i++) {
- if(info.reversed) {
- colorStops[len - 1 - i] = [stopFormatter((1 - colorscale[i][0]) * 100), colorscale[i][1]];
- }
- else {
- colorStops[i] = [stopFormatter(colorscale[i][0] * 100), colorscale[i][1]];
- }
- }
-
- var fullID = 'g' + gd._fullLayout._uid + '-' + gradientID;
-
- var gradient = gd._fullLayout._defs.select('.gradients')
- .selectAll('#' + fullID)
- .data([type + colorStops.join(';')], Lib.identity);
-
- gradient.exit().remove();
-
- gradient.enter()
- .append(info.node)
- .each(function() {
- var el = d3.select(this);
- if(info.attrs) el.attr(info.attrs);
-
- el.attr('id', fullID);
-
- var stops = el.selectAll('stop')
- .data(colorStops);
- stops.exit().remove();
- stops.enter().append('stop');
-
- stops.each(function(d) {
- var tc = tinycolor(d[1]);
- d3.select(this).attr({
- offset: d[0] + '%',
- 'stop-color': Color.tinyRGB(tc),
- 'stop-opacity': tc.getAlpha()
- });
- });
- });
-
- sel.style(prop, 'url(#' + fullID + ')')
- .style(prop + '-opacity', null);
- };
-
- /*
- * Make the gradients container and clear out any previous gradients.
- * We never collect all the gradients we need in one place,
- * so we can't ever remove gradients that have stopped being useful,
- * except all at once before a full redraw.
- * The upside of this is arbitrary points can share gradient defs
- */
- drawing.initGradients = function(gd) {
- var gradientsGroup = Lib.ensureSingle(gd._fullLayout._defs, 'g', 'gradients');
- gradientsGroup.selectAll('linearGradient,radialGradient').remove();
- };
-
-
- drawing.pointStyle = function(s, trace, gd) {
- if(!s.size()) return;
-
- var fns = drawing.makePointStyleFns(trace);
-
- s.each(function(d) {
- drawing.singlePointStyle(d, d3.select(this), trace, fns, gd);
- });
- };
-
- drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
- var marker = trace.marker;
- var markerLine = marker.line;
-
- sel.style('opacity',
- fns.selectedOpacityFn ? fns.selectedOpacityFn(d) :
- (d.mo === undefined ? marker.opacity : d.mo)
- );
-
- if(fns.ms2mrc) {
- var r;
-
- // handle multi-trace graph edit case
- if(d.ms === 'various' || marker.size === 'various') {
- r = 3;
- } else {
- r = fns.ms2mrc(d.ms);
- }
-
- // store the calculated size so hover can use it
- d.mrc = r;
-
- if(fns.selectedSizeFn) {
- r = d.mrc = fns.selectedSizeFn(d);
- }
-
- // turn the symbol into a sanitized number
- var x = drawing.symbolNumber(d.mx || marker.symbol) || 0;
-
- // save if this marker is open
- // because that impacts how to handle colors
- d.om = x % 200 >= 100;
-
- sel.attr('d', makePointPath(x, r));
- }
-
- var perPointGradient = false;
- var fillColor, lineColor, lineWidth;
-
- // 'so' is suspected outliers, for box plots
- if(d.so) {
- lineWidth = markerLine.outlierwidth;
- lineColor = markerLine.outliercolor;
- fillColor = marker.outliercolor;
- } else {
- var markerLineWidth = (markerLine || {}).width;
-
- lineWidth = (
- d.mlw + 1 ||
- markerLineWidth + 1 ||
- // TODO: we need the latter for legends... can we get rid of it?
- (d.trace ? (d.trace.marker.line || {}).width : 0) + 1
- ) - 1 || 0;
-
- if('mlc' in d) lineColor = d.mlcc = fns.lineScale(d.mlc);
- // weird case: array wasn't long enough to apply to every point
- else if(Lib.isArrayOrTypedArray(markerLine.color)) lineColor = Color.defaultLine;
- else lineColor = markerLine.color;
-
- if(Lib.isArrayOrTypedArray(marker.color)) {
- fillColor = Color.defaultLine;
- perPointGradient = true;
- }
-
- if('mc' in d) {
- fillColor = d.mcc = fns.markerScale(d.mc);
- } else {
- fillColor = marker.color || 'rgba(0,0,0,0)';
- }
-
- if(fns.selectedColorFn) {
- fillColor = fns.selectedColorFn(d);
- }
- }
-
- if(d.om) {
- // open markers can't have zero linewidth, default to 1px,
- // and use fill color as stroke color
- sel.call(Color.stroke, fillColor)
- .style({
- 'stroke-width': (lineWidth || 1) + 'px',
- fill: 'none'
- });
- } else {
- sel.style('stroke-width', lineWidth + 'px');
-
- var markerGradient = marker.gradient;
-
- var gradientType = d.mgt;
- if(gradientType) perPointGradient = true;
- else gradientType = markerGradient && markerGradient.type;
-
- // for legend - arrays will propagate through here, but we don't need
- // to treat it as per-point.
- if(Array.isArray(gradientType)) {
- gradientType = gradientType[0];
- if(!gradientInfo[gradientType]) gradientType = 0;
- }
-
- if(gradientType && gradientType !== 'none') {
- var gradientColor = d.mgc;
- if(gradientColor) perPointGradient = true;
- else gradientColor = markerGradient.color;
-
- var gradientID = trace.uid;
- if(perPointGradient) gradientID += '-' + d.i;
-
- drawing.gradient(sel, gd, gradientID, gradientType,
- [[0, gradientColor], [1, fillColor]], 'fill');
- } else {
- Color.fill(sel, fillColor);
- }
-
- if(lineWidth) {
- Color.stroke(sel, lineColor);
- }
- }
- };
-
- drawing.makePointStyleFns = function(trace) {
- var out = {};
- var marker = trace.marker;
-
- // allow array marker and marker line colors to be
- // scaled by given max and min to colorscales
- out.markerScale = drawing.tryColorscale(marker, '');
- out.lineScale = drawing.tryColorscale(marker, 'line');
-
- if(Registry.traceIs(trace, 'symbols')) {
- out.ms2mrc = subTypes.isBubble(trace) ?
- makeBubbleSizeFn(trace) :
- function() { return (marker.size || 6) / 2; };
- }
-
- if(trace.selectedpoints) {
- Lib.extendFlat(out, drawing.makeSelectedPointStyleFns(trace));
- }
-
- return out;
- };
-
- drawing.makeSelectedPointStyleFns = function(trace) {
- var out = {};
-
- var selectedAttrs = trace.selected || {};
- var unselectedAttrs = trace.unselected || {};
-
- var marker = trace.marker || {};
- var selectedMarker = selectedAttrs.marker || {};
- var unselectedMarker = unselectedAttrs.marker || {};
-
- var mo = marker.opacity;
- var smo = selectedMarker.opacity;
- var usmo = unselectedMarker.opacity;
- var smoIsDefined = smo !== undefined;
- var usmoIsDefined = usmo !== undefined;
-
- if(Lib.isArrayOrTypedArray(mo) || smoIsDefined || usmoIsDefined) {
- out.selectedOpacityFn = function(d) {
- var base = d.mo === undefined ? marker.opacity : d.mo;
-
- if(d.selected) {
- return smoIsDefined ? smo : base;
- } else {
- return usmoIsDefined ? usmo : DESELECTDIM * base;
- }
- };
- }
-
- var mc = marker.color;
- var smc = selectedMarker.color;
- var usmc = unselectedMarker.color;
-
- if(smc || usmc) {
- out.selectedColorFn = function(d) {
- var base = d.mcc || mc;
-
- if(d.selected) {
- return smc || base;
- } else {
- return usmc || base;
- }
- };
- }
-
- var ms = marker.size;
- var sms = selectedMarker.size;
- var usms = unselectedMarker.size;
- var smsIsDefined = sms !== undefined;
- var usmsIsDefined = usms !== undefined;
-
- if(Registry.traceIs(trace, 'symbols') && (smsIsDefined || usmsIsDefined)) {
- out.selectedSizeFn = function(d) {
- var base = d.mrc || ms / 2;
-
- if(d.selected) {
- return smsIsDefined ? sms / 2 : base;
- } else {
- return usmsIsDefined ? usms / 2 : base;
- }
- };
- }
-
- return out;
- };
-
- drawing.makeSelectedTextStyleFns = function(trace) {
- var out = {};
-
- var selectedAttrs = trace.selected || {};
- var unselectedAttrs = trace.unselected || {};
-
- var textFont = trace.textfont || {};
- var selectedTextFont = selectedAttrs.textfont || {};
- var unselectedTextFont = unselectedAttrs.textfont || {};
-
- var tc = textFont.color;
- var stc = selectedTextFont.color;
- var utc = unselectedTextFont.color;
-
- out.selectedTextColorFn = function(d) {
- var base = d.tc || tc;
-
- if(d.selected) {
- return stc || base;
- } else {
- if(utc) return utc;
- else return stc ? base : Color.addOpacity(base, DESELECTDIM);
- }
- };
-
- return out;
- };
-
- drawing.selectedPointStyle = function(s, trace) {
- if(!s.size() || !trace.selectedpoints) return;
-
- var fns = drawing.makeSelectedPointStyleFns(trace);
- var marker = trace.marker || {};
- var seq = [];
-
- if(fns.selectedOpacityFn) {
- seq.push(function(pt, d) {
- pt.style('opacity', fns.selectedOpacityFn(d));
- });
- }
-
- if(fns.selectedColorFn) {
- seq.push(function(pt, d) {
- Color.fill(pt, fns.selectedColorFn(d));
- });
- }
-
- if(fns.selectedSizeFn) {
- seq.push(function(pt, d) {
- var mx = d.mx || marker.symbol || 0;
- var mrc2 = fns.selectedSizeFn(d);
-
- pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2));
-
- // save for Drawing.selectedTextStyle
- d.mrc2 = mrc2;
- });
- }
-
- if(seq.length) {
- s.each(function(d) {
- var pt = d3.select(this);
- for(var i = 0; i < seq.length; i++) {
- seq[i](pt, d);
- }
- });
- }
- };
-
- drawing.tryColorscale = function(marker, prefix) {
- var cont = prefix ? Lib.nestedProperty(marker, prefix).get() : marker;
-
- if(cont) {
- var scl = cont.colorscale;
- var colorArray = cont.color;
-
- if(scl && Lib.isArrayOrTypedArray(colorArray)) {
- return Colorscale.makeColorScaleFunc(
- Colorscale.extractScale(cont, {cLetter: 'c'})
- );
- }
- }
- return Lib.identity;
- };
-
- var TEXTOFFSETSIGN = {
- start: 1, end: -1, middle: 0, bottom: 1, top: -1
- };
-
- function textPointPosition(s, textPosition, fontSize, markerRadius) {
- var group = d3.select(s.node().parentNode);
-
- var v = textPosition.indexOf('top') !== -1 ?
- 'top' :
- textPosition.indexOf('bottom') !== -1 ? 'bottom' : 'middle';
- var h = textPosition.indexOf('left') !== -1 ?
- 'end' :
- textPosition.indexOf('right') !== -1 ? 'start' : 'middle';
-
- // if markers are shown, offset a little more than
- // the nominal marker size
- // ie 2/1.6 * nominal, bcs some markers are a bit bigger
- var r = markerRadius ? markerRadius / 0.8 + 1 : 0;
-
- var numLines = (svgTextUtils.lineCount(s) - 1) * LINE_SPACING + 1;
- var dx = TEXTOFFSETSIGN[h] * r;
- var dy = fontSize * 0.75 + TEXTOFFSETSIGN[v] * r +
- (TEXTOFFSETSIGN[v] - 1) * numLines * fontSize / 2;
-
- // fix the overall text group position
- s.attr('text-anchor', h);
- group.attr('transform', 'translate(' + dx + ',' + dy + ')');
- }
-
- function extracTextFontSize(d, trace) {
- var fontSize = d.ts || trace.textfont.size;
- return (isNumeric(fontSize) && fontSize > 0) ? fontSize : 0;
- }
-
- // draw text at points
- drawing.textPointStyle = function(s, trace, gd) {
- if(!s.size()) return;
-
- var selectedTextColorFn;
-
- if(trace.selectedpoints) {
- var fns = drawing.makeSelectedTextStyleFns(trace);
- selectedTextColorFn = fns.selectedTextColorFn;
- }
-
- s.each(function(d) {
- var p = d3.select(this);
- var text = Lib.extractOption(d, trace, 'tx', 'text');
-
- if(!text && text !== 0) {
- p.remove();
- return;
- }
-
- var pos = d.tp || trace.textposition;
- var fontSize = extracTextFontSize(d, trace);
- var fontColor = selectedTextColorFn ?
- selectedTextColorFn(d) :
- (d.tc || trace.textfont.color);
-
- p.call(drawing.font,
- d.tf || trace.textfont.family,
- fontSize,
- fontColor)
- .text(text)
- .call(svgTextUtils.convertToTspans, gd)
- .call(textPointPosition, pos, fontSize, d.mrc);
- });
- };
-
- drawing.selectedTextStyle = function(s, trace) {
- if(!s.size() || !trace.selectedpoints) return;
-
- var fns = drawing.makeSelectedTextStyleFns(trace);
-
- s.each(function(d) {
- var tx = d3.select(this);
- var tc = fns.selectedTextColorFn(d);
- var tp = d.tp || trace.textposition;
- var fontSize = extracTextFontSize(d, trace);
-
- Color.fill(tx, tc);
- textPointPosition(tx, tp, fontSize, d.mrc2 || d.mrc);
- });
- };
-
- // generalized Catmull-Rom splines, per
- // http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
- var CatmullRomExp = 0.5;
- drawing.smoothopen = function(pts, smoothness) {
- if(pts.length < 3) { return 'M' + pts.join('L');}
- var path = 'M' + pts[0];
- var tangents = [];
- var i;
- for(i = 1; i < pts.length - 1; i++) {
- tangents.push(makeTangent(pts[i - 1], pts[i], pts[i + 1], smoothness));
- }
- path += 'Q' + tangents[0][0] + ' ' + pts[1];
- for(i = 2; i < pts.length - 1; i++) {
- path += 'C' + tangents[i - 2][1] + ' ' + tangents[i - 1][0] + ' ' + pts[i];
- }
- path += 'Q' + tangents[pts.length - 3][1] + ' ' + pts[pts.length - 1];
- return path;
- };
-
- drawing.smoothclosed = function(pts, smoothness) {
- if(pts.length < 3) { return 'M' + pts.join('L') + 'Z'; }
- var path = 'M' + pts[0];
- var pLast = pts.length - 1;
- var tangents = [makeTangent(pts[pLast], pts[0], pts[1], smoothness)];
- var i;
- for(i = 1; i < pLast; i++) {
- tangents.push(makeTangent(pts[i - 1], pts[i], pts[i + 1], smoothness));
- }
- tangents.push(
- makeTangent(pts[pLast - 1], pts[pLast], pts[0], smoothness)
- );
-
- for(i = 1; i <= pLast; i++) {
- path += 'C' + tangents[i - 1][1] + ' ' + tangents[i][0] + ' ' + pts[i];
- }
- path += 'C' + tangents[pLast][1] + ' ' + tangents[0][0] + ' ' + pts[0] + 'Z';
- return path;
- };
-
- function makeTangent(prevpt, thispt, nextpt, smoothness) {
- var d1x = prevpt[0] - thispt[0];
- var d1y = prevpt[1] - thispt[1];
- var d2x = nextpt[0] - thispt[0];
- var d2y = nextpt[1] - thispt[1];
- var d1a = Math.pow(d1x * d1x + d1y * d1y, CatmullRomExp / 2);
- var d2a = Math.pow(d2x * d2x + d2y * d2y, CatmullRomExp / 2);
- var numx = (d2a * d2a * d1x - d1a * d1a * d2x) * smoothness;
- var numy = (d2a * d2a * d1y - d1a * d1a * d2y) * smoothness;
- var denom1 = 3 * d2a * (d1a + d2a);
- var denom2 = 3 * d1a * (d1a + d2a);
- return [
- [
- d3.round(thispt[0] + (denom1 && numx / denom1), 2),
- d3.round(thispt[1] + (denom1 && numy / denom1), 2)
- ], [
- d3.round(thispt[0] - (denom2 && numx / denom2), 2),
- d3.round(thispt[1] - (denom2 && numy / denom2), 2)
- ]
- ];
- }
-
- // step paths - returns a generator function for paths
- // with the given step shape
- var STEPPATH = {
- hv: function(p0, p1) {
- return 'H' + d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2);
- },
- vh: function(p0, p1) {
- return 'V' + d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2);
- },
- hvh: function(p0, p1) {
- return 'H' + d3.round((p0[0] + p1[0]) / 2, 2) + 'V' +
- d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2);
- },
- vhv: function(p0, p1) {
- return 'V' + d3.round((p0[1] + p1[1]) / 2, 2) + 'H' +
- d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2);
- }
- };
- var STEPLINEAR = function(p0, p1) {
- return 'L' + d3.round(p1[0], 2) + ',' + d3.round(p1[1], 2);
- };
- drawing.steps = function(shape) {
- var onestep = STEPPATH[shape] || STEPLINEAR;
- return function(pts) {
- var path = 'M' + d3.round(pts[0][0], 2) + ',' + d3.round(pts[0][1], 2);
- for(var i = 1; i < pts.length; i++) {
- path += onestep(pts[i - 1], pts[i]);
- }
- return path;
- };
- };
-
- // off-screen svg render testing element, shared by the whole page
- // uses the id 'js-plotly-tester' and stores it in drawing.tester
- drawing.makeTester = function() {
- var tester = Lib.ensureSingleById(d3.select('body'), 'svg', 'js-plotly-tester', function(s) {
- s.attr(xmlnsNamespaces.svgAttrs)
- .style({
- position: 'absolute',
- left: '-10000px',
- top: '-10000px',
- width: '9000px',
- height: '9000px',
- 'z-index': '1'
- });
- });
-
- // browsers differ on how they describe the bounding rect of
- // the svg if its contents spill over... so make a 1x1px
- // reference point we can measure off of.
- var testref = Lib.ensureSingle(tester, 'path', 'js-reference-point', function(s) {
- s.attr('d', 'M0,0H1V1H0Z')
- .style({
- 'stroke-width': 0,
- fill: 'black'
- });
- });
-
- drawing.tester = tester;
- drawing.testref = testref;
- };
-
- /*
- * use our offscreen tester to get a clientRect for an element,
- * in a reference frame where it isn't translated (or transformed) and
- * its anchor point is at (0,0)
- * always returns a copy of the bbox, so the caller can modify it safely
- *
- * @param {SVGElement} node: the element to measure. If possible this should be
- * a <text> or MathJax <g> element that's already passed through
- * `convertToTspans` because in that case we can cache the results, but it's
- * possible to pass in any svg element.
- *
- * @param {boolean} inTester: is this element already in `drawing.tester`?
- * If you are measuring a dummy element, rather than one you really intend
- * to use on the plot, making it in `drawing.tester` in the first place
- * allows us to test faster because it cuts out cloning and appending it.
- *
- * @param {string} hash: for internal use only, if we already know the cache key
- * for this element beforehand.
- *
- * @return {object}: a plain object containing the width, height, left, right,
- * top, and bottom of `node`
- */
- drawing.savedBBoxes = {};
- var savedBBoxesCount = 0;
- var maxSavedBBoxes = 10000;
-
- drawing.bBox = function(node, inTester, hash) {
- /*
- * Cache elements we've already measured so we don't have to
- * remeasure the same thing many times
- * We have a few bBox callers though who pass a node larger than
- * a <text> or a MathJax <g>, such as an axis group containing many labels.
- * These will not generate a hash (unless we figure out an appropriate
- * hash key for them) and thus we will not hash them.
- */
- if(!hash) hash = nodeHash(node);
- var out;
- if(hash) {
- out = drawing.savedBBoxes[hash];
- if(out) return Lib.extendFlat({}, out);
- }
- else if(node.childNodes.length === 1) {
- /*
- * If we have only one child element, which is itself hashable, make
- * a new hash from this element plus its x,y,transform
- * These bounding boxes *include* x,y,transform - mostly for use by
- * callers trying to avoid overlaps (ie titles)
- */
- var innerNode = node.childNodes[0];
-
- hash = nodeHash(innerNode);
- if(hash) {
- var x = +innerNode.getAttribute('x') || 0;
- var y = +innerNode.getAttribute('y') || 0;
- var transform = innerNode.getAttribute('transform');
-
- if(!transform) {
- // in this case, just varying x and y, don't bother caching
- // the final bBox because the alteration is quick.
- var innerBB = drawing.bBox(innerNode, false, hash);
- if(x) {
- innerBB.left += x;
- innerBB.right += x;
- }
- if(y) {
- innerBB.top += y;
- innerBB.bottom += y;
- }
- return innerBB;
- }
- /*
- * else we have a transform - rather than make a complicated
- * (and error-prone and probably slow) transform parser/calculator,
- * just continue on calculating the boundingClientRect of the group
- * and use the new composite hash to cache it.
- * That said, `innerNode.transform.baseVal` is an array of
- * `SVGTransform` objects, that *do* seem to have a nice matrix
- * multiplication interface that we could use to avoid making
- * another getBoundingClientRect call...
- */
- hash += '~' + x + '~' + y + '~' + transform;
-
- out = drawing.savedBBoxes[hash];
- if(out) return Lib.extendFlat({}, out);
- }
- }
- var testNode, tester;
- if(inTester) {
- testNode = node;
- }
- else {
- tester = drawing.tester.node();
-
- // copy the node to test into the tester
- testNode = node.cloneNode(true);
- tester.appendChild(testNode);
- }
-
- // standardize its position (and newline tspans if any)
- d3.select(testNode)
- .attr('transform', null)
- .call(svgTextUtils.positionText, 0, 0);
-
- var testRect = testNode.getBoundingClientRect();
- var refRect = drawing.testref
- .node()
- .getBoundingClientRect();
-
- if(!inTester) tester.removeChild(testNode);
-
- var bb = {
- height: testRect.height,
- width: testRect.width,
- left: testRect.left - refRect.left,
- top: testRect.top - refRect.top,
- right: testRect.right - refRect.left,
- bottom: testRect.bottom - refRect.top
- };
-
- // make sure we don't have too many saved boxes,
- // or a long session could overload on memory
- // by saving boxes for long-gone elements
- if(savedBBoxesCount >= maxSavedBBoxes) {
- drawing.savedBBoxes = {};
- savedBBoxesCount = 0;
- }
-
- // cache this bbox
- if(hash) drawing.savedBBoxes[hash] = bb;
- savedBBoxesCount++;
-
- return Lib.extendFlat({}, bb);
- };
-
- // capture everything about a node (at least in our usage) that
- // impacts its bounding box, given that bBox clears x, y, and transform
- function nodeHash(node) {
- var inputText = node.getAttribute('data-unformatted');
- if(inputText === null) return;
- return inputText +
- node.getAttribute('data-math') +
- node.getAttribute('text-anchor') +
- node.getAttribute('style');
- }
-
- /**
- * Set clipPath URL in a way that work for all situations.
- *
- * In details, graphs on pages with <base> HTML tags need to prepend
- * the clip path ids with the page's base url EXCEPT during toImage exports.
- *
- * @param {d3 selection} s : node to add clip-path attribute
- * @param {string} localId : local clip-path (w/o base url) id
- * @param {DOM element || object} gd
- * - context._baseUrl {string}
- * - context._exportedPlot {boolean}
- */
- drawing.setClipUrl = function(s, localId, gd) {
- if(!localId) {
- s.attr('clip-path', null);
- return;
- }
-
- var context = gd._context;
- var baseUrl = context._exportedPlot ? '' : (context._baseUrl || '');
-
- s.attr('clip-path', 'url(' + baseUrl + '#' + localId + ')');
- };
-
- drawing.getTranslate = function(element) {
- // Note the separator [^\d] between x and y in this regex
- // We generally use ',' but IE will convert it to ' '
- var re = /.*\btranslate\((-?\d*\.?\d*)[^-\d]*(-?\d*\.?\d*)[^\d].*/;
- var getter = element.attr ? 'attr' : 'getAttribute';
- var transform = element[getter]('transform') || '';
-
- var translate = transform.replace(re, function(match, p1, p2) {
- return [p1, p2].join(' ');
- })
- .split(' ');
-
- return {
- x: +translate[0] || 0,
- y: +translate[1] || 0
- };
- };
-
- drawing.setTranslate = function(element, x, y) {
- var re = /(\btranslate\(.*?\);?)/;
- var getter = element.attr ? 'attr' : 'getAttribute';
- var setter = element.attr ? 'attr' : 'setAttribute';
- var transform = element[getter]('transform') || '';
-
- x = x || 0;
- y = y || 0;
-
- transform = transform.replace(re, '').trim();
- transform += ' translate(' + x + ', ' + y + ')';
- transform = transform.trim();
-
- element[setter]('transform', transform);
-
- return transform;
- };
-
- drawing.getScale = function(element) {
- var re = /.*\bscale\((\d*\.?\d*)[^\d]*(\d*\.?\d*)[^\d].*/;
- var getter = element.attr ? 'attr' : 'getAttribute';
- var transform = element[getter]('transform') || '';
-
- var translate = transform.replace(re, function(match, p1, p2) {
- return [p1, p2].join(' ');
- })
- .split(' ');
-
- return {
- x: +translate[0] || 1,
- y: +translate[1] || 1
- };
- };
-
- drawing.setScale = function(element, x, y) {
- var re = /(\bscale\(.*?\);?)/;
- var getter = element.attr ? 'attr' : 'getAttribute';
- var setter = element.attr ? 'attr' : 'setAttribute';
- var transform = element[getter]('transform') || '';
-
- x = x || 1;
- y = y || 1;
-
- transform = transform.replace(re, '').trim();
- transform += ' scale(' + x + ', ' + y + ')';
- transform = transform.trim();
-
- element[setter]('transform', transform);
-
- return transform;
- };
-
- var SCALE_RE = /\s*sc.*/;
-
- drawing.setPointGroupScale = function(selection, xScale, yScale) {
- xScale = xScale || 1;
- yScale = yScale || 1;
-
- if(!selection) return;
-
- // The same scale transform for every point:
- var scale = (xScale === 1 && yScale === 1) ?
- '' :
- ' scale(' + xScale + ',' + yScale + ')';
-
- selection.each(function() {
- var t = (this.getAttribute('transform') || '').replace(SCALE_RE, '');
- t += scale;
- t = t.trim();
- this.setAttribute('transform', t);
- });
- };
-
- var TEXT_POINT_LAST_TRANSLATION_RE = /translate\([^)]*\)\s*$/;
-
- drawing.setTextPointsScale = function(selection, xScale, yScale) {
- if(!selection) return;
-
- selection.each(function() {
- var transforms;
- var el = d3.select(this);
- var text = el.select('text');
-
- if(!text.node()) return;
-
- var x = parseFloat(text.attr('x') || 0);
- var y = parseFloat(text.attr('y') || 0);
-
- var existingTransform = (el.attr('transform') || '').match(TEXT_POINT_LAST_TRANSLATION_RE);
-
- if(xScale === 1 && yScale === 1) {
- transforms = [];
- } else {
- transforms = [
- 'translate(' + x + ',' + y + ')',
- 'scale(' + xScale + ',' + yScale + ')',
- 'translate(' + (-x) + ',' + (-y) + ')',
- ];
- }
-
- if(existingTransform) {
- transforms.push(existingTransform);
- }
-
- el.attr('transform', transforms.join(' '));
- });
- };
-
- },{"../../constants/alignment":146,"../../constants/interactions":148,"../../constants/xmlns_namespaces":150,"../../lib":168,"../../lib/svg_text_utils":189,"../../registry":257,"../../traces/scatter/make_bubble_size_func":384,"../../traces/scatter/subtypes":391,"../color":51,"../colorscale":63,"./symbol_defs":73,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],73:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- /** Marker symbol definitions
- * users can specify markers either by number or name
- * add 100 (or '-open') and you get an open marker
- * open markers have no fill and use line color as the stroke color
- * add 200 (or '-dot') and you get a dot in the middle
- * add both and you get both
- */
-
- module.exports = {
- circle: {
- n: 0,
- f: function(r) {
- var rs = d3.round(r, 2);
- return 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
- 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
- }
- },
- square: {
- n: 1,
- f: function(r) {
- var rs = d3.round(r, 2);
- return 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
- }
- },
- diamond: {
- n: 2,
- f: function(r) {
- var rd = d3.round(r * 1.3, 2);
- return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z';
- }
- },
- cross: {
- n: 3,
- f: function(r) {
- var rc = d3.round(r * 0.4, 2);
- var rc2 = d3.round(r * 1.2, 2);
- return 'M' + rc2 + ',' + rc + 'H' + rc + 'V' + rc2 + 'H-' + rc +
- 'V' + rc + 'H-' + rc2 + 'V-' + rc + 'H-' + rc + 'V-' + rc2 +
- 'H' + rc + 'V-' + rc + 'H' + rc2 + 'Z';
- }
- },
- x: {
- n: 4,
- f: function(r) {
- var rx = d3.round(r * 0.8 / Math.sqrt(2), 2);
- var ne = 'l' + rx + ',' + rx;
- var se = 'l' + rx + ',-' + rx;
- var sw = 'l-' + rx + ',-' + rx;
- var nw = 'l-' + rx + ',' + rx;
- return 'M0,' + rx + ne + se + sw + se + sw + nw + sw + nw + ne + nw + ne + 'Z';
- }
- },
- 'triangle-up': {
- n: 5,
- f: function(r) {
- var rt = d3.round(r * 2 / Math.sqrt(3), 2);
- var r2 = d3.round(r / 2, 2);
- var rs = d3.round(r, 2);
- return 'M-' + rt + ',' + r2 + 'H' + rt + 'L0,-' + rs + 'Z';
- }
- },
- 'triangle-down': {
- n: 6,
- f: function(r) {
- var rt = d3.round(r * 2 / Math.sqrt(3), 2);
- var r2 = d3.round(r / 2, 2);
- var rs = d3.round(r, 2);
- return 'M-' + rt + ',-' + r2 + 'H' + rt + 'L0,' + rs + 'Z';
- }
- },
- 'triangle-left': {
- n: 7,
- f: function(r) {
- var rt = d3.round(r * 2 / Math.sqrt(3), 2);
- var r2 = d3.round(r / 2, 2);
- var rs = d3.round(r, 2);
- return 'M' + r2 + ',-' + rt + 'V' + rt + 'L-' + rs + ',0Z';
- }
- },
- 'triangle-right': {
- n: 8,
- f: function(r) {
- var rt = d3.round(r * 2 / Math.sqrt(3), 2);
- var r2 = d3.round(r / 2, 2);
- var rs = d3.round(r, 2);
- return 'M-' + r2 + ',-' + rt + 'V' + rt + 'L' + rs + ',0Z';
- }
- },
- 'triangle-ne': {
- n: 9,
- f: function(r) {
- var r1 = d3.round(r * 0.6, 2);
- var r2 = d3.round(r * 1.2, 2);
- return 'M-' + r2 + ',-' + r1 + 'H' + r1 + 'V' + r2 + 'Z';
- }
- },
- 'triangle-se': {
- n: 10,
- f: function(r) {
- var r1 = d3.round(r * 0.6, 2);
- var r2 = d3.round(r * 1.2, 2);
- return 'M' + r1 + ',-' + r2 + 'V' + r1 + 'H-' + r2 + 'Z';
- }
- },
- 'triangle-sw': {
- n: 11,
- f: function(r) {
- var r1 = d3.round(r * 0.6, 2);
- var r2 = d3.round(r * 1.2, 2);
- return 'M' + r2 + ',' + r1 + 'H-' + r1 + 'V-' + r2 + 'Z';
- }
- },
- 'triangle-nw': {
- n: 12,
- f: function(r) {
- var r1 = d3.round(r * 0.6, 2);
- var r2 = d3.round(r * 1.2, 2);
- return 'M-' + r1 + ',' + r2 + 'V-' + r1 + 'H' + r2 + 'Z';
- }
- },
- pentagon: {
- n: 13,
- f: function(r) {
- var x1 = d3.round(r * 0.951, 2);
- var x2 = d3.round(r * 0.588, 2);
- var y0 = d3.round(-r, 2);
- var y1 = d3.round(r * -0.309, 2);
- var y2 = d3.round(r * 0.809, 2);
- return 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'H-' + x2 +
- 'L-' + x1 + ',' + y1 + 'L0,' + y0 + 'Z';
- }
- },
- hexagon: {
- n: 14,
- f: function(r) {
- var y0 = d3.round(r, 2);
- var y1 = d3.round(r / 2, 2);
- var x = d3.round(r * Math.sqrt(3) / 2, 2);
- return 'M' + x + ',-' + y1 + 'V' + y1 + 'L0,' + y0 +
- 'L-' + x + ',' + y1 + 'V-' + y1 + 'L0,-' + y0 + 'Z';
- }
- },
- hexagon2: {
- n: 15,
- f: function(r) {
- var x0 = d3.round(r, 2);
- var x1 = d3.round(r / 2, 2);
- var y = d3.round(r * Math.sqrt(3) / 2, 2);
- return 'M-' + x1 + ',' + y + 'H' + x1 + 'L' + x0 +
- ',0L' + x1 + ',-' + y + 'H-' + x1 + 'L-' + x0 + ',0Z';
- }
- },
- octagon: {
- n: 16,
- f: function(r) {
- var a = d3.round(r * 0.924, 2);
- var b = d3.round(r * 0.383, 2);
- return 'M-' + b + ',-' + a + 'H' + b + 'L' + a + ',-' + b + 'V' + b +
- 'L' + b + ',' + a + 'H-' + b + 'L-' + a + ',' + b + 'V-' + b + 'Z';
- }
- },
- star: {
- n: 17,
- f: function(r) {
- var rs = r * 1.4;
- var x1 = d3.round(rs * 0.225, 2);
- var x2 = d3.round(rs * 0.951, 2);
- var x3 = d3.round(rs * 0.363, 2);
- var x4 = d3.round(rs * 0.588, 2);
- var y0 = d3.round(-rs, 2);
- var y1 = d3.round(rs * -0.309, 2);
- var y3 = d3.round(rs * 0.118, 2);
- var y4 = d3.round(rs * 0.809, 2);
- var y5 = d3.round(rs * 0.382, 2);
- return 'M' + x1 + ',' + y1 + 'H' + x2 + 'L' + x3 + ',' + y3 +
- 'L' + x4 + ',' + y4 + 'L0,' + y5 + 'L-' + x4 + ',' + y4 +
- 'L-' + x3 + ',' + y3 + 'L-' + x2 + ',' + y1 + 'H-' + x1 +
- 'L0,' + y0 + 'Z';
- }
- },
- hexagram: {
- n: 18,
- f: function(r) {
- var y = d3.round(r * 0.66, 2);
- var x1 = d3.round(r * 0.38, 2);
- var x2 = d3.round(r * 0.76, 2);
- return 'M-' + x2 + ',0l-' + x1 + ',-' + y + 'h' + x2 +
- 'l' + x1 + ',-' + y + 'l' + x1 + ',' + y + 'h' + x2 +
- 'l-' + x1 + ',' + y + 'l' + x1 + ',' + y + 'h-' + x2 +
- 'l-' + x1 + ',' + y + 'l-' + x1 + ',-' + y + 'h-' + x2 + 'Z';
- }
- },
- 'star-triangle-up': {
- n: 19,
- f: function(r) {
- var x = d3.round(r * Math.sqrt(3) * 0.8, 2);
- var y1 = d3.round(r * 0.8, 2);
- var y2 = d3.round(r * 1.6, 2);
- var rc = d3.round(r * 4, 2);
- var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
- return 'M-' + x + ',' + y1 + aPart + x + ',' + y1 +
- aPart + '0,-' + y2 + aPart + '-' + x + ',' + y1 + 'Z';
- }
- },
- 'star-triangle-down': {
- n: 20,
- f: function(r) {
- var x = d3.round(r * Math.sqrt(3) * 0.8, 2);
- var y1 = d3.round(r * 0.8, 2);
- var y2 = d3.round(r * 1.6, 2);
- var rc = d3.round(r * 4, 2);
- var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
- return 'M' + x + ',-' + y1 + aPart + '-' + x + ',-' + y1 +
- aPart + '0,' + y2 + aPart + x + ',-' + y1 + 'Z';
- }
- },
- 'star-square': {
- n: 21,
- f: function(r) {
- var rp = d3.round(r * 1.1, 2);
- var rc = d3.round(r * 2, 2);
- var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
- return 'M-' + rp + ',-' + rp + aPart + '-' + rp + ',' + rp +
- aPart + rp + ',' + rp + aPart + rp + ',-' + rp +
- aPart + '-' + rp + ',-' + rp + 'Z';
- }
- },
- 'star-diamond': {
- n: 22,
- f: function(r) {
- var rp = d3.round(r * 1.4, 2);
- var rc = d3.round(r * 1.9, 2);
- var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
- return 'M-' + rp + ',0' + aPart + '0,' + rp +
- aPart + rp + ',0' + aPart + '0,-' + rp +
- aPart + '-' + rp + ',0' + 'Z';
- }
- },
- 'diamond-tall': {
- n: 23,
- f: function(r) {
- var x = d3.round(r * 0.7, 2);
- var y = d3.round(r * 1.4, 2);
- return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z';
- }
- },
- 'diamond-wide': {
- n: 24,
- f: function(r) {
- var x = d3.round(r * 1.4, 2);
- var y = d3.round(r * 0.7, 2);
- return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z';
- }
- },
- hourglass: {
- n: 25,
- f: function(r) {
- var rs = d3.round(r, 2);
- return 'M' + rs + ',' + rs + 'H-' + rs + 'L' + rs + ',-' + rs + 'H-' + rs + 'Z';
- },
- noDot: true
- },
- bowtie: {
- n: 26,
- f: function(r) {
- var rs = d3.round(r, 2);
- return 'M' + rs + ',' + rs + 'V-' + rs + 'L-' + rs + ',' + rs + 'V-' + rs + 'Z';
- },
- noDot: true
- },
- 'circle-cross': {
- n: 27,
- f: function(r) {
- var rs = d3.round(r, 2);
- return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
- 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
- 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
- },
- needLine: true,
- noDot: true
- },
- 'circle-x': {
- n: 28,
- f: function(r) {
- var rs = d3.round(r, 2);
- var rc = d3.round(r / Math.sqrt(2), 2);
- return 'M' + rc + ',' + rc + 'L-' + rc + ',-' + rc +
- 'M' + rc + ',-' + rc + 'L-' + rc + ',' + rc +
- 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
- 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
- },
- needLine: true,
- noDot: true
- },
- 'square-cross': {
- n: 29,
- f: function(r) {
- var rs = d3.round(r, 2);
- return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
- 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
- },
- needLine: true,
- noDot: true
- },
- 'square-x': {
- n: 30,
- f: function(r) {
- var rs = d3.round(r, 2);
- return 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
- 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs +
- 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
- },
- needLine: true,
- noDot: true
- },
- 'diamond-cross': {
- n: 31,
- f: function(r) {
- var rd = d3.round(r * 1.3, 2);
- return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
- 'M0,-' + rd + 'V' + rd + 'M-' + rd + ',0H' + rd;
- },
- needLine: true,
- noDot: true
- },
- 'diamond-x': {
- n: 32,
- f: function(r) {
- var rd = d3.round(r * 1.3, 2);
- var r2 = d3.round(r * 0.65, 2);
- return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
- 'M-' + r2 + ',-' + r2 + 'L' + r2 + ',' + r2 +
- 'M-' + r2 + ',' + r2 + 'L' + r2 + ',-' + r2;
- },
- needLine: true,
- noDot: true
- },
- 'cross-thin': {
- n: 33,
- f: function(r) {
- var rc = d3.round(r * 1.4, 2);
- return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc;
- },
- needLine: true,
- noDot: true,
- noFill: true
- },
- 'x-thin': {
- n: 34,
- f: function(r) {
- var rx = d3.round(r, 2);
- return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx +
- 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
- },
- needLine: true,
- noDot: true,
- noFill: true
- },
- asterisk: {
- n: 35,
- f: function(r) {
- var rc = d3.round(r * 1.2, 2);
- var rs = d3.round(r * 0.85, 2);
- return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc +
- 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
- 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs;
- },
- needLine: true,
- noDot: true,
- noFill: true
- },
- hash: {
- n: 36,
- f: function(r) {
- var r1 = d3.round(r / 2, 2);
- var r2 = d3.round(r, 2);
- return 'M' + r1 + ',' + r2 + 'V-' + r2 +
- 'm-' + r2 + ',0V' + r2 +
- 'M' + r2 + ',' + r1 + 'H-' + r2 +
- 'm0,-' + r2 + 'H' + r2;
- },
- needLine: true,
- noFill: true
- },
- 'y-up': {
- n: 37,
- f: function(r) {
- var x = d3.round(r * 1.2, 2);
- var y0 = d3.round(r * 1.6, 2);
- var y1 = d3.round(r * 0.8, 2);
- return 'M-' + x + ',' + y1 + 'L0,0M' + x + ',' + y1 + 'L0,0M0,-' + y0 + 'L0,0';
- },
- needLine: true,
- noDot: true,
- noFill: true
- },
- 'y-down': {
- n: 38,
- f: function(r) {
- var x = d3.round(r * 1.2, 2);
- var y0 = d3.round(r * 1.6, 2);
- var y1 = d3.round(r * 0.8, 2);
- return 'M-' + x + ',-' + y1 + 'L0,0M' + x + ',-' + y1 + 'L0,0M0,' + y0 + 'L0,0';
- },
- needLine: true,
- noDot: true,
- noFill: true
- },
- 'y-left': {
- n: 39,
- f: function(r) {
- var y = d3.round(r * 1.2, 2);
- var x0 = d3.round(r * 1.6, 2);
- var x1 = d3.round(r * 0.8, 2);
- return 'M' + x1 + ',' + y + 'L0,0M' + x1 + ',-' + y + 'L0,0M-' + x0 + ',0L0,0';
- },
- needLine: true,
- noDot: true,
- noFill: true
- },
- 'y-right': {
- n: 40,
- f: function(r) {
- var y = d3.round(r * 1.2, 2);
- var x0 = d3.round(r * 1.6, 2);
- var x1 = d3.round(r * 0.8, 2);
- return 'M-' + x1 + ',' + y + 'L0,0M-' + x1 + ',-' + y + 'L0,0M' + x0 + ',0L0,0';
- },
- needLine: true,
- noDot: true,
- noFill: true
- },
- 'line-ew': {
- n: 41,
- f: function(r) {
- var rc = d3.round(r * 1.4, 2);
- return 'M' + rc + ',0H-' + rc;
- },
- needLine: true,
- noDot: true,
- noFill: true
- },
- 'line-ns': {
- n: 42,
- f: function(r) {
- var rc = d3.round(r * 1.4, 2);
- return 'M0,' + rc + 'V-' + rc;
- },
- needLine: true,
- noDot: true,
- noFill: true
- },
- 'line-ne': {
- n: 43,
- f: function(r) {
- var rx = d3.round(r, 2);
- return 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
- },
- needLine: true,
- noDot: true,
- noFill: true
- },
- 'line-nw': {
- n: 44,
- f: function(r) {
- var rx = d3.round(r, 2);
- return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx;
- },
- needLine: true,
- noDot: true,
- noFill: true
- }
- };
-
- },{"d3":16}],74:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
-
- module.exports = {
- visible: {
- valType: 'boolean',
-
- editType: 'calc',
-
- },
- type: {
- valType: 'enumerated',
- values: ['percent', 'constant', 'sqrt', 'data'],
-
- editType: 'calc',
-
- },
- symmetric: {
- valType: 'boolean',
-
- editType: 'calc',
-
- },
- array: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- arrayminus: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- value: {
- valType: 'number',
- min: 0,
- dflt: 10,
-
- editType: 'calc',
-
- },
- valueminus: {
- valType: 'number',
- min: 0,
- dflt: 10,
-
- editType: 'calc',
-
- },
- traceref: {
- valType: 'integer',
- min: 0,
- dflt: 0,
-
- editType: 'style'
- },
- tracerefminus: {
- valType: 'integer',
- min: 0,
- dflt: 0,
-
- editType: 'style'
- },
- copy_ystyle: {
- valType: 'boolean',
-
- editType: 'plot'
- },
- copy_zstyle: {
- valType: 'boolean',
-
- editType: 'style'
- },
- color: {
- valType: 'color',
-
- editType: 'style',
-
- },
- thickness: {
- valType: 'number',
- min: 0,
- dflt: 2,
-
- editType: 'style',
-
- },
- width: {
- valType: 'number',
- min: 0,
-
- editType: 'plot',
-
- },
- editType: 'calc',
-
- _deprecated: {
- opacity: {
- valType: 'number',
-
- editType: 'style',
-
- }
- }
- };
-
- },{}],75:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Registry = _dereq_('../../registry');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var Lib = _dereq_('../../lib');
-
- var makeComputeError = _dereq_('./compute_error');
-
- module.exports = function calc(gd) {
- var calcdata = gd.calcdata;
-
- for(var i = 0; i < calcdata.length; i++) {
- var calcTrace = calcdata[i];
- var trace = calcTrace[0].trace;
-
- if(trace.visible === true && Registry.traceIs(trace, 'errorBarsOK')) {
- var xa = Axes.getFromId(gd, trace.xaxis);
- var ya = Axes.getFromId(gd, trace.yaxis);
- calcOneAxis(calcTrace, trace, xa, 'x');
- calcOneAxis(calcTrace, trace, ya, 'y');
- }
- }
- };
-
- function calcOneAxis(calcTrace, trace, axis, coord) {
- var opts = trace['error_' + coord] || {};
- var isVisible = (opts.visible && ['linear', 'log'].indexOf(axis.type) !== -1);
- var vals = [];
-
- if(!isVisible) return;
-
- var computeError = makeComputeError(opts);
-
- for(var i = 0; i < calcTrace.length; i++) {
- var calcPt = calcTrace[i];
-
- var iIn = calcPt.i;
-
- // for types that don't include `i` in each calcdata point
- if(iIn === undefined) iIn = i;
-
- // for stacked area inserted points
- // TODO: errorbars have been tested cursorily with stacked area,
- // but not thoroughly. It's not even really clear what you want to do:
- // Should it just be calculated based on that trace's size data?
- // Should you add errors from below in quadrature?
- // And what about normalization, where in principle the errors shrink
- // again when you get up to the top end?
- // One option would be to forbid errorbars with stacking until we
- // decide how to handle these questions.
- else if(iIn === null) continue;
-
- var calcCoord = calcPt[coord];
-
- if(!isNumeric(axis.c2l(calcCoord))) continue;
-
- var errors = computeError(calcCoord, iIn);
- if(isNumeric(errors[0]) && isNumeric(errors[1])) {
- var shoe = calcPt[coord + 's'] = calcCoord - errors[0];
- var hat = calcPt[coord + 'h'] = calcCoord + errors[1];
- vals.push(shoe, hat);
- }
- }
-
- var axId = axis._id;
- var baseExtremes = trace._extremes[axId];
- var extremes = Axes.findExtremes(
- axis,
- vals,
- Lib.extendFlat({tozero: baseExtremes.opts.tozero}, {padded: true})
- );
- baseExtremes.min = baseExtremes.min.concat(extremes.min);
- baseExtremes.max = baseExtremes.max.concat(extremes.max);
- }
-
- },{"../../lib":168,"../../plots/cartesian/axes":212,"../../registry":257,"./compute_error":76,"fast-isnumeric":18}],76:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- /**
- * Error bar computing function generator
- *
- * N.B. The generated function does not clean the dataPt entries. Non-numeric
- * entries result in undefined error magnitudes.
- *
- * @param {object} opts error bar attributes
- *
- * @return {function} :
- * @param {numeric} dataPt data point from where to compute the error magnitude
- * @param {number} index index of dataPt in its corresponding data array
- * @return {array}
- * - error[0] : error magnitude in the negative direction
- * - error[1] : " " " " positive "
- */
- module.exports = function makeComputeError(opts) {
- var type = opts.type;
- var symmetric = opts.symmetric;
-
- if(type === 'data') {
- var array = opts.array || [];
-
- if(symmetric) {
- return function computeError(dataPt, index) {
- var val = +(array[index]);
- return [val, val];
- };
- }
- else {
- var arrayminus = opts.arrayminus || [];
- return function computeError(dataPt, index) {
- var val = +array[index];
- var valMinus = +arrayminus[index];
- // in case one is present and the other is missing, fill in 0
- // so we still see the present one. Mostly useful during manual
- // data entry.
- if(!isNaN(val) || !isNaN(valMinus)) {
- return [valMinus || 0, val || 0];
- }
- return [NaN, NaN];
- };
- }
- }
- else {
- var computeErrorValue = makeComputeErrorValue(type, opts.value);
- var computeErrorValueMinus = makeComputeErrorValue(type, opts.valueminus);
-
- if(symmetric || opts.valueminus === undefined) {
- return function computeError(dataPt) {
- var val = computeErrorValue(dataPt);
- return [val, val];
- };
- }
- else {
- return function computeError(dataPt) {
- return [
- computeErrorValueMinus(dataPt),
- computeErrorValue(dataPt)
- ];
- };
- }
- }
- };
-
- /**
- * Compute error bar magnitude (for all types except data)
- *
- * @param {string} type error bar type
- * @param {numeric} value error bar value
- *
- * @return {function} :
- * @param {numeric} dataPt
- */
- function makeComputeErrorValue(type, value) {
- if(type === 'percent') {
- return function(dataPt) {
- return Math.abs(dataPt * value / 100);
- };
- }
- if(type === 'constant') {
- return function() {
- return Math.abs(value);
- };
- }
- if(type === 'sqrt') {
- return function(dataPt) {
- return Math.sqrt(Math.abs(dataPt));
- };
- }
- }
-
- },{}],77:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var Template = _dereq_('../../plot_api/plot_template');
-
- var attributes = _dereq_('./attributes');
-
-
- module.exports = function(traceIn, traceOut, defaultColor, opts) {
- var objName = 'error_' + opts.axis;
- var containerOut = Template.newContainer(traceOut, objName);
- var containerIn = traceIn[objName] || {};
-
- function coerce(attr, dflt) {
- return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
- }
-
- var hasErrorBars = (
- containerIn.array !== undefined ||
- containerIn.value !== undefined ||
- containerIn.type === 'sqrt'
- );
-
- var visible = coerce('visible', hasErrorBars);
-
- if(visible === false) return;
-
- var type = coerce('type', 'array' in containerIn ? 'data' : 'percent');
- var symmetric = true;
-
- if(type !== 'sqrt') {
- symmetric = coerce('symmetric',
- !((type === 'data' ? 'arrayminus' : 'valueminus') in containerIn));
- }
-
- if(type === 'data') {
- coerce('array');
- coerce('traceref');
- if(!symmetric) {
- coerce('arrayminus');
- coerce('tracerefminus');
- }
- }
- else if(type === 'percent' || type === 'constant') {
- coerce('value');
- if(!symmetric) coerce('valueminus');
- }
-
- var copyAttr = 'copy_' + opts.inherit + 'style';
- if(opts.inherit) {
- var inheritObj = traceOut['error_' + opts.inherit];
- if((inheritObj || {}).visible) {
- coerce(copyAttr, !(containerIn.color ||
- isNumeric(containerIn.thickness) ||
- isNumeric(containerIn.width)));
- }
- }
- if(!opts.inherit || !containerOut[copyAttr]) {
- coerce('color', defaultColor);
- coerce('thickness');
- coerce('width', Registry.traceIs(traceOut, 'gl3d') ? 0 : 4);
- }
- };
-
- },{"../../lib":168,"../../plot_api/plot_template":202,"../../registry":257,"./attributes":74,"fast-isnumeric":18}],78:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
-
- var attributes = _dereq_('./attributes');
-
- var xyAttrs = {
- error_x: Lib.extendFlat({}, attributes),
- error_y: Lib.extendFlat({}, attributes)
- };
- delete xyAttrs.error_x.copy_zstyle;
- delete xyAttrs.error_y.copy_zstyle;
- delete xyAttrs.error_y.copy_ystyle;
-
- var xyzAttrs = {
- error_x: Lib.extendFlat({}, attributes),
- error_y: Lib.extendFlat({}, attributes),
- error_z: Lib.extendFlat({}, attributes)
- };
- delete xyzAttrs.error_x.copy_ystyle;
- delete xyzAttrs.error_y.copy_ystyle;
- delete xyzAttrs.error_z.copy_ystyle;
- delete xyzAttrs.error_z.copy_zstyle;
-
- module.exports = {
- moduleType: 'component',
- name: 'errorbars',
-
- schema: {
- traces: {
- scatter: xyAttrs,
- bar: xyAttrs,
- histogram: xyAttrs,
- scatter3d: overrideAll(xyzAttrs, 'calc', 'nested'),
- scattergl: overrideAll(xyAttrs, 'calc', 'nested')
- }
- },
-
- supplyDefaults: _dereq_('./defaults'),
-
- calc: _dereq_('./calc'),
- makeComputeError: _dereq_('./compute_error'),
-
- plot: _dereq_('./plot'),
- style: _dereq_('./style'),
- hoverInfo: hoverInfo
- };
-
- function hoverInfo(calcPoint, trace, hoverPoint) {
- if((trace.error_y || {}).visible) {
- hoverPoint.yerr = calcPoint.yh - calcPoint.y;
- if(!trace.error_y.symmetric) hoverPoint.yerrneg = calcPoint.y - calcPoint.ys;
- }
- if((trace.error_x || {}).visible) {
- hoverPoint.xerr = calcPoint.xh - calcPoint.x;
- if(!trace.error_x.symmetric) hoverPoint.xerrneg = calcPoint.x - calcPoint.xs;
- }
- }
-
- },{"../../lib":168,"../../plot_api/edit_types":195,"./attributes":74,"./calc":75,"./compute_error":76,"./defaults":77,"./plot":79,"./style":80}],79:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Drawing = _dereq_('../drawing');
- var subTypes = _dereq_('../../traces/scatter/subtypes');
-
- module.exports = function plot(gd, traces, plotinfo, transitionOpts) {
- var isNew;
-
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- var hasAnimation = transitionOpts && transitionOpts.duration > 0;
-
- traces.each(function(d) {
- var trace = d[0].trace;
- // || {} is in case the trace (specifically scatterternary)
- // doesn't support error bars at all, but does go through
- // the scatter.plot mechanics, which calls ErrorBars.plot
- // internally
- var xObj = trace.error_x || {};
- var yObj = trace.error_y || {};
-
- var keyFunc;
-
- if(trace.ids) {
- keyFunc = function(d) {return d.id;};
- }
-
- var sparse = (
- subTypes.hasMarkers(trace) &&
- trace.marker.maxdisplayed > 0
- );
-
- if(!yObj.visible && !xObj.visible) d = [];
-
- var errorbars = d3.select(this).selectAll('g.errorbar')
- .data(d, keyFunc);
-
- errorbars.exit().remove();
-
- if(!d.length) return;
-
- if(!xObj.visible) errorbars.selectAll('path.xerror').remove();
- if(!yObj.visible) errorbars.selectAll('path.yerror').remove();
-
- errorbars.style('opacity', 1);
-
- var enter = errorbars.enter().append('g')
- .classed('errorbar', true);
-
- if(hasAnimation) {
- enter.style('opacity', 0).transition()
- .duration(transitionOpts.duration)
- .style('opacity', 1);
- }
-
- Drawing.setClipUrl(errorbars, plotinfo.layerClipId, gd);
-
- errorbars.each(function(d) {
- var errorbar = d3.select(this);
- var coords = errorCoords(d, xa, ya);
-
- if(sparse && !d.vis) return;
-
- var path;
-
- var yerror = errorbar.select('path.yerror');
- if(yObj.visible && isNumeric(coords.x) &&
- isNumeric(coords.yh) &&
- isNumeric(coords.ys)) {
- var yw = yObj.width;
-
- path = 'M' + (coords.x - yw) + ',' +
- coords.yh + 'h' + (2 * yw) + // hat
- 'm-' + yw + ',0V' + coords.ys; // bar
-
-
- if(!coords.noYS) path += 'm-' + yw + ',0h' + (2 * yw); // shoe
-
- isNew = !yerror.size();
-
- if(isNew) {
- yerror = errorbar.append('path')
- .style('vector-effect', 'non-scaling-stroke')
- .classed('yerror', true);
- } else if(hasAnimation) {
- yerror = yerror
- .transition()
- .duration(transitionOpts.duration)
- .ease(transitionOpts.easing);
- }
-
- yerror.attr('d', path);
- }
- else yerror.remove();
-
- var xerror = errorbar.select('path.xerror');
- if(xObj.visible && isNumeric(coords.y) &&
- isNumeric(coords.xh) &&
- isNumeric(coords.xs)) {
- var xw = (xObj.copy_ystyle ? yObj : xObj).width;
-
- path = 'M' + coords.xh + ',' +
- (coords.y - xw) + 'v' + (2 * xw) + // hat
- 'm0,-' + xw + 'H' + coords.xs; // bar
-
- if(!coords.noXS) path += 'm0,-' + xw + 'v' + (2 * xw); // shoe
-
- isNew = !xerror.size();
-
- if(isNew) {
- xerror = errorbar.append('path')
- .style('vector-effect', 'non-scaling-stroke')
- .classed('xerror', true);
- } else if(hasAnimation) {
- xerror = xerror
- .transition()
- .duration(transitionOpts.duration)
- .ease(transitionOpts.easing);
- }
-
- xerror.attr('d', path);
- }
- else xerror.remove();
- });
- });
- };
-
- // compute the coordinates of the error-bar objects
- function errorCoords(d, xa, ya) {
- var out = {
- x: xa.c2p(d.x),
- y: ya.c2p(d.y)
- };
-
- // calculate the error bar size and hat and shoe locations
- if(d.yh !== undefined) {
- out.yh = ya.c2p(d.yh);
- out.ys = ya.c2p(d.ys);
-
- // if the shoes go off-scale (ie log scale, error bars past zero)
- // clip the bar and hide the shoes
- if(!isNumeric(out.ys)) {
- out.noYS = true;
- out.ys = ya.c2p(d.ys, true);
- }
- }
-
- if(d.xh !== undefined) {
- out.xh = xa.c2p(d.xh);
- out.xs = xa.c2p(d.xs);
-
- if(!isNumeric(out.xs)) {
- out.noXS = true;
- out.xs = xa.c2p(d.xs, true);
- }
- }
-
- return out;
- }
-
- },{"../../traces/scatter/subtypes":391,"../drawing":72,"d3":16,"fast-isnumeric":18}],80:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Color = _dereq_('../color');
-
-
- module.exports = function style(traces) {
- traces.each(function(d) {
- var trace = d[0].trace;
- var yObj = trace.error_y || {};
- var xObj = trace.error_x || {};
-
- var s = d3.select(this);
-
- s.selectAll('path.yerror')
- .style('stroke-width', yObj.thickness + 'px')
- .call(Color.stroke, yObj.color);
-
- if(xObj.copy_ystyle) xObj = yObj;
-
- s.selectAll('path.xerror')
- .style('stroke-width', xObj.thickness + 'px')
- .call(Color.stroke, xObj.color);
- });
- };
-
- },{"../color":51,"d3":16}],81:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var fontAttrs = _dereq_('../../plots/font_attributes');
-
- module.exports = {
- hoverlabel: {
- bgcolor: {
- valType: 'color',
-
- arrayOk: true,
- editType: 'none',
-
- },
- bordercolor: {
- valType: 'color',
-
- arrayOk: true,
- editType: 'none',
-
- },
- font: fontAttrs({
- arrayOk: true,
- editType: 'none',
-
- }),
- namelength: {
- valType: 'integer',
- min: -1,
- arrayOk: true,
-
- editType: 'none',
-
- },
- editType: 'calc'
- }
- };
-
- },{"../../plots/font_attributes":239}],82:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Registry = _dereq_('../../registry');
-
- module.exports = function calc(gd) {
- var calcdata = gd.calcdata;
- var fullLayout = gd._fullLayout;
-
- function makeCoerceHoverInfo(trace) {
- return function(val) {
- return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout);
- };
- }
-
- for(var i = 0; i < calcdata.length; i++) {
- var cd = calcdata[i];
- var trace = cd[0].trace;
-
- // don't include hover calc fields for pie traces
- // as calcdata items might be sorted by value and
- // won't match the data array order.
- if(Registry.traceIs(trace, 'pie')) continue;
-
- var fillFn = Registry.traceIs(trace, '2dMap') ? paste : Lib.fillArray;
-
- fillFn(trace.hoverinfo, cd, 'hi', makeCoerceHoverInfo(trace));
-
- if(trace.hovertemplate) fillFn(trace.hovertemplate, cd, 'ht');
-
- if(!trace.hoverlabel) continue;
-
- fillFn(trace.hoverlabel.bgcolor, cd, 'hbg');
- fillFn(trace.hoverlabel.bordercolor, cd, 'hbc');
- fillFn(trace.hoverlabel.font.size, cd, 'hts');
- fillFn(trace.hoverlabel.font.color, cd, 'htc');
- fillFn(trace.hoverlabel.font.family, cd, 'htf');
- fillFn(trace.hoverlabel.namelength, cd, 'hnl');
- }
- };
-
- function paste(traceAttr, cd, cdAttr, fn) {
- fn = fn || Lib.identity;
-
- if(Array.isArray(traceAttr)) {
- cd[0][cdAttr] = fn(traceAttr);
- }
- }
-
- },{"../../lib":168,"../../registry":257}],83:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var hover = _dereq_('./hover').hover;
-
- module.exports = function click(gd, evt, subplot) {
- var annotationsDone = Registry.getComponentMethod('annotations', 'onClick')(gd, gd._hoverdata);
-
- // fallback to fail-safe in case the plot type's hover method doesn't pass the subplot.
- // Ternary, for example, didn't, but it was caught because tested.
- if(subplot !== undefined) {
- // The true flag at the end causes it to re-run the hover computation to figure out *which*
- // point is being clicked. Without this, clicking is somewhat unreliable.
- hover(gd, evt, subplot, true);
- }
-
- function emitClick() { gd.emit('plotly_click', {points: gd._hoverdata, event: evt}); }
-
- if(gd._hoverdata && evt && evt.target) {
- if(annotationsDone && annotationsDone.then) {
- annotationsDone.then(emitClick);
- }
- else emitClick();
-
- // why do we get a double event without this???
- if(evt.stopImmediatePropagation) evt.stopImmediatePropagation();
- }
- };
-
- },{"../../registry":257,"./hover":87}],84:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- // hover labels for multiple horizontal bars get tilted by this angle
- YANGLE: 60,
-
- // size and display constants for hover text
-
- // pixel size of hover arrows
- HOVERARROWSIZE: 6,
- // pixels padding around text
- HOVERTEXTPAD: 3,
- // hover font
- HOVERFONTSIZE: 13,
- HOVERFONT: 'Arial, sans-serif',
-
- // minimum time (msec) between hover calls
- HOVERMINTIME: 50,
-
- // ID suffix (with fullLayout._uid) for hover events in the throttle cache
- HOVERID: '-hover'
- };
-
- },{}],85:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var attributes = _dereq_('./attributes');
- var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- handleHoverLabelDefaults(traceIn, traceOut, coerce, layout.hoverlabel);
- };
-
- },{"../../lib":168,"./attributes":81,"./hoverlabel_defaults":88}],86:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- // look for either subplot or xaxis and yaxis attributes
- // does not handle splom case
- exports.getSubplot = function getSubplot(trace) {
- return trace.subplot || (trace.xaxis + trace.yaxis) || trace.geo;
- };
-
- // is trace in given list of subplots?
- // does handle splom case
- exports.isTraceInSubplots = function isTraceInSubplot(trace, subplots) {
- if(trace.type === 'splom') {
- var xaxes = trace.xaxes || [];
- var yaxes = trace.yaxes || [];
- for(var i = 0; i < xaxes.length; i++) {
- for(var j = 0; j < yaxes.length; j++) {
- if(subplots.indexOf(xaxes[i] + yaxes[j]) !== -1) {
- return true;
- }
- }
- }
- return false;
- }
-
- return subplots.indexOf(exports.getSubplot(trace)) !== -1;
- };
-
- // convenience functions for mapping all relevant axes
- exports.flat = function flat(subplots, v) {
- var out = new Array(subplots.length);
- for(var i = 0; i < subplots.length; i++) {
- out[i] = v;
- }
- return out;
- };
-
- exports.p2c = function p2c(axArray, v) {
- var out = new Array(axArray.length);
- for(var i = 0; i < axArray.length; i++) {
- out[i] = axArray[i].p2c(v);
- }
- return out;
- };
-
- exports.getDistanceFunction = function getDistanceFunction(mode, dx, dy, dxy) {
- if(mode === 'closest') return dxy || exports.quadrature(dx, dy);
- return mode === 'x' ? dx : dy;
- };
-
- exports.getClosest = function getClosest(cd, distfn, pointData) {
- // do we already have a point number? (array mode only)
- if(pointData.index !== false) {
- if(pointData.index >= 0 && pointData.index < cd.length) {
- pointData.distance = 0;
- }
- else pointData.index = false;
- }
- else {
- // apply the distance function to each data point
- // this is the longest loop... if this bogs down, we may need
- // to create pre-sorted data (by x or y), not sure how to
- // do this for 'closest'
- for(var i = 0; i < cd.length; i++) {
- var newDistance = distfn(cd[i]);
- if(newDistance <= pointData.distance) {
- pointData.index = i;
- pointData.distance = newDistance;
- }
- }
- }
- return pointData;
- };
-
- /*
- * pseudo-distance function for hover effects on areas: inside the region
- * distance is finite (`passVal`), outside it's Infinity.
- *
- * @param {number} v0: signed difference between the current position and the left edge
- * @param {number} v1: signed difference between the current position and the right edge
- * @param {number} passVal: the value to return on success
- */
- exports.inbox = function inbox(v0, v1, passVal) {
- return (v0 * v1 < 0 || v0 === 0) ? passVal : Infinity;
- };
-
- exports.quadrature = function quadrature(dx, dy) {
- return function(di) {
- var x = dx(di);
- var y = dy(di);
- return Math.sqrt(x * x + y * y);
- };
- };
-
- /** Fill event data point object for hover and selection.
- * Invokes _module.eventData if present.
- *
- * N.B. note that point 'index' corresponds to input data array index
- * whereas 'number' is its post-transform version.
- *
- * If the hovered/selected pt corresponds to an multiple input points
- * (e.g. for histogram and transformed traces), 'pointNumbers` and 'pointIndices'
- * are include in the event data.
- *
- * @param {object} pt
- * @param {object} trace
- * @param {object} cd
- * @return {object}
- */
- exports.makeEventData = function makeEventData(pt, trace, cd) {
- // hover uses 'index', select uses 'pointNumber'
- var pointNumber = 'index' in pt ? pt.index : pt.pointNumber;
-
- var out = {
- data: trace._input,
- fullData: trace,
- curveNumber: trace.index,
- pointNumber: pointNumber
- };
-
- if(trace._indexToPoints) {
- var pointIndices = trace._indexToPoints[pointNumber];
-
- if(pointIndices.length === 1) {
- out.pointIndex = pointIndices[0];
- } else {
- out.pointIndices = pointIndices;
- }
- } else {
- out.pointIndex = pointNumber;
- }
-
- if(trace._module.eventData) {
- out = trace._module.eventData(out, pt, trace, cd, pointNumber);
- } else {
- if('xVal' in pt) out.x = pt.xVal;
- else if('x' in pt) out.x = pt.x;
-
- if('yVal' in pt) out.y = pt.yVal;
- else if('y' in pt) out.y = pt.y;
-
- if(pt.xa) out.xaxis = pt.xa;
- if(pt.ya) out.yaxis = pt.ya;
- if(pt.zLabelVal !== undefined) out.z = pt.zLabelVal;
- }
-
- exports.appendArrayPointValue(out, trace, pointNumber);
-
- return out;
- };
-
- /** Appends values inside array attributes corresponding to given point number
- *
- * @param {object} pointData : point data object (gets mutated here)
- * @param {object} trace : full trace object
- * @param {number|Array(number)} pointNumber : point number. May be a length-2 array
- * [row, col] to dig into 2D arrays
- */
- exports.appendArrayPointValue = function(pointData, trace, pointNumber) {
- var arrayAttrs = trace._arrayAttrs;
-
- if(!arrayAttrs) {
- return;
- }
-
- for(var i = 0; i < arrayAttrs.length; i++) {
- var astr = arrayAttrs[i];
- var key = getPointKey(astr);
-
- if(pointData[key] === undefined) {
- var val = Lib.nestedProperty(trace, astr).get();
- var pointVal = getPointData(val, pointNumber);
-
- if(pointVal !== undefined) pointData[key] = pointVal;
- }
- }
- };
-
- /**
- * Appends values inside array attributes corresponding to given point number array
- * For use when pointData references a plot entity that arose (or potentially arose)
- * from multiple points in the input data
- *
- * @param {object} pointData : point data object (gets mutated here)
- * @param {object} trace : full trace object
- * @param {Array(number)|Array(Array(number))} pointNumbers : Array of point numbers.
- * Each entry in the array may itself be a length-2 array [row, col] to dig into 2D arrays
- */
- exports.appendArrayMultiPointValues = function(pointData, trace, pointNumbers) {
- var arrayAttrs = trace._arrayAttrs;
-
- if(!arrayAttrs) {
- return;
- }
-
- for(var i = 0; i < arrayAttrs.length; i++) {
- var astr = arrayAttrs[i];
- var key = getPointKey(astr);
-
- if(pointData[key] === undefined) {
- var val = Lib.nestedProperty(trace, astr).get();
- var keyVal = new Array(pointNumbers.length);
-
- for(var j = 0; j < pointNumbers.length; j++) {
- keyVal[j] = getPointData(val, pointNumbers[j]);
- }
- pointData[key] = keyVal;
- }
- }
- };
-
- var pointKeyMap = {
- ids: 'id',
- locations: 'location',
- labels: 'label',
- values: 'value',
- 'marker.colors': 'color'
- };
-
- function getPointKey(astr) {
- return pointKeyMap[astr] || astr;
- }
-
- function getPointData(val, pointNumber) {
- if(Array.isArray(pointNumber)) {
- if(Array.isArray(val) && Array.isArray(val[pointNumber[0]])) {
- return val[pointNumber[0]][pointNumber[1]];
- }
- } else {
- return val[pointNumber];
- }
- }
-
- },{"../../lib":168}],87:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
- var tinycolor = _dereq_('tinycolor2');
-
- var Lib = _dereq_('../../lib');
- var Events = _dereq_('../../lib/events');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var overrideCursor = _dereq_('../../lib/override_cursor');
- var Drawing = _dereq_('../drawing');
- var Color = _dereq_('../color');
- var dragElement = _dereq_('../dragelement');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var Registry = _dereq_('../../registry');
-
- var helpers = _dereq_('./helpers');
- var constants = _dereq_('./constants');
-
- // hover labels for multiple horizontal bars get tilted by some angle,
- // then need to be offset differently if they overlap
- var YANGLE = constants.YANGLE;
- var YA_RADIANS = Math.PI * YANGLE / 180;
-
- // expansion of projected height
- var YFACTOR = 1 / Math.sin(YA_RADIANS);
-
- // to make the appropriate post-rotation x offset,
- // you need both x and y offsets
- var YSHIFTX = Math.cos(YA_RADIANS);
- var YSHIFTY = Math.sin(YA_RADIANS);
-
- // size and display constants for hover text
- var HOVERARROWSIZE = constants.HOVERARROWSIZE;
- var HOVERTEXTPAD = constants.HOVERTEXTPAD;
-
- // fx.hover: highlight data on hover
- // evt can be a mousemove event, or an object with data about what points
- // to hover on
- // {xpx,ypx[,hovermode]} - pixel locations from top left
- // (with optional overriding hovermode)
- // {xval,yval[,hovermode]} - data values
- // [{curveNumber,(pointNumber|xval and/or yval)}] -
- // array of specific points to highlight
- // pointNumber is a single integer if gd.data[curveNumber] is 1D,
- // or a two-element array if it's 2D
- // xval and yval are data values,
- // 1D data may specify either or both,
- // 2D data must specify both
- // subplot is an id string (default "xy")
- // makes use of gl.hovermode, which can be:
- // x (find the points with the closest x values, ie a column),
- // closest (find the single closest point)
- // internally there are two more that occasionally get used:
- // y (pick out a row - only used for multiple horizontal bar charts)
- // array (used when the user specifies an explicit
- // array of points to hover on)
- //
- // We wrap the hovers in a timer, to limit their frequency.
- // The actual rendering is done by private function _hover.
- exports.hover = function hover(gd, evt, subplot, noHoverEvent) {
- gd = Lib.getGraphDiv(gd);
-
- Lib.throttle(
- gd._fullLayout._uid + constants.HOVERID,
- constants.HOVERMINTIME,
- function() { _hover(gd, evt, subplot, noHoverEvent); }
- );
- };
-
- /*
- * Draw a single hover item in a pre-existing svg container somewhere
- * hoverItem should have keys:
- * - x and y (or x0, x1, y0, and y1):
- * the pixel position to mark, relative to opts.container
- * - xLabel, yLabel, zLabel, text, and name:
- * info to go in the label
- * - color:
- * the background color for the label.
- * - idealAlign (optional):
- * 'left' or 'right' for which side of the x/y box to try to put this on first
- * - borderColor (optional):
- * color for the border, defaults to strongest contrast with color
- * - fontFamily (optional):
- * string, the font for this label, defaults to constants.HOVERFONT
- * - fontSize (optional):
- * the label font size, defaults to constants.HOVERFONTSIZE
- * - fontColor (optional):
- * defaults to borderColor
- * opts should have keys:
- * - bgColor:
- * the background color this is against, used if the trace is
- * non-opaque, and for the name, which goes outside the box
- * - container:
- * a <svg> or <g> element to add the hover label to
- * - outerContainer:
- * normally a parent of `container`, sets the bounding box to use to
- * constrain the hover label and determine whether to show it on the left or right
- */
- exports.loneHover = function loneHover(hoverItem, opts) {
- var pointData = {
- color: hoverItem.color || Color.defaultLine,
- x0: hoverItem.x0 || hoverItem.x || 0,
- x1: hoverItem.x1 || hoverItem.x || 0,
- y0: hoverItem.y0 || hoverItem.y || 0,
- y1: hoverItem.y1 || hoverItem.y || 0,
- xLabel: hoverItem.xLabel,
- yLabel: hoverItem.yLabel,
- zLabel: hoverItem.zLabel,
- text: hoverItem.text,
- name: hoverItem.name,
- idealAlign: hoverItem.idealAlign,
-
- // optional extra bits of styling
- borderColor: hoverItem.borderColor,
- fontFamily: hoverItem.fontFamily,
- fontSize: hoverItem.fontSize,
- fontColor: hoverItem.fontColor,
-
- // filler to make createHoverText happy
- trace: hoverItem.trace || {
- index: 0,
- hoverinfo: ''
- },
- xa: {_offset: 0},
- ya: {_offset: 0},
- index: 0,
-
- hovertemplate: hoverItem.hovertemplate || false,
- eventData: hoverItem.eventData || false,
- hovertemplateLabels: hoverItem.hovertemplateLabels || false,
- };
-
- var container3 = d3.select(opts.container);
- var outerContainer3 = opts.outerContainer ?
- d3.select(opts.outerContainer) : container3;
-
- var fullOpts = {
- hovermode: 'closest',
- rotateLabels: false,
- bgColor: opts.bgColor || Color.background,
- container: container3,
- outerContainer: outerContainer3
- };
- var hoverLabel = createHoverText([pointData], fullOpts, opts.gd);
- alignHoverText(hoverLabel, fullOpts.rotateLabels);
-
- return hoverLabel.node();
- };
-
- exports.multiHovers = function multiHovers(hoverItems, opts) {
-
- if(!Array.isArray(hoverItems)) {
- hoverItems = [hoverItems];
- }
-
- var pointsData = hoverItems.map(function(hoverItem) {
- return {
- color: hoverItem.color || Color.defaultLine,
- x0: hoverItem.x0 || hoverItem.x || 0,
- x1: hoverItem.x1 || hoverItem.x || 0,
- y0: hoverItem.y0 || hoverItem.y || 0,
- y1: hoverItem.y1 || hoverItem.y || 0,
- xLabel: hoverItem.xLabel,
- yLabel: hoverItem.yLabel,
- zLabel: hoverItem.zLabel,
- text: hoverItem.text,
- name: hoverItem.name,
- idealAlign: hoverItem.idealAlign,
-
- // optional extra bits of styling
- borderColor: hoverItem.borderColor,
- fontFamily: hoverItem.fontFamily,
- fontSize: hoverItem.fontSize,
- fontColor: hoverItem.fontColor,
-
- // filler to make createHoverText happy
- trace: hoverItem.trace || {
- index: 0,
- hoverinfo: ''
- },
- xa: {_offset: 0},
- ya: {_offset: 0},
- index: 0,
-
- hovertemplate: hoverItem.hovertemplate || false,
- eventData: hoverItem.eventData || false,
- hovertemplateLabels: hoverItem.hovertemplateLabels || false,
- };
- });
-
-
- var container3 = d3.select(opts.container);
- var outerContainer3 = opts.outerContainer ? d3.select(opts.outerContainer) : container3;
-
- var fullOpts = {
- hovermode: 'closest',
- rotateLabels: false,
- bgColor: opts.bgColor || Color.background,
- container: container3,
- outerContainer: outerContainer3
- };
-
- var hoverLabel = createHoverText(pointsData, fullOpts, opts.gd);
-
- // Fix vertical overlap
- var tooltipSpacing = 5;
- var lastBottomY = 0;
- hoverLabel
- .sort(function(a, b) {return a.y0 - b.y0;})
- .each(function(d) {
- var topY = d.y0 - d.by / 2;
-
- if((topY - tooltipSpacing) < lastBottomY) {
- d.offset = (lastBottomY - topY) + tooltipSpacing;
- } else {
- d.offset = 0;
- }
-
- lastBottomY = topY + d.by + d.offset;
- });
-
-
- alignHoverText(hoverLabel, fullOpts.rotateLabels);
-
- return hoverLabel.node();
- };
-
- // The actual implementation is here:
- function _hover(gd, evt, subplot, noHoverEvent) {
- if(!subplot) subplot = 'xy';
-
- // if the user passed in an array of subplots,
- // use those instead of finding overlayed plots
- var subplots = Array.isArray(subplot) ? subplot : [subplot];
-
- var fullLayout = gd._fullLayout;
- var plots = fullLayout._plots || [];
- var plotinfo = plots[subplot];
- var hasCartesian = fullLayout._has('cartesian');
-
- // list of all overlaid subplots to look at
- if(plotinfo) {
- var overlayedSubplots = plotinfo.overlays.map(function(pi) {
- return pi.id;
- });
-
- subplots = subplots.concat(overlayedSubplots);
- }
-
- var len = subplots.length;
- var xaArray = new Array(len);
- var yaArray = new Array(len);
- var supportsCompare = false;
-
- for(var i = 0; i < len; i++) {
- var spId = subplots[i];
-
- // 'cartesian' case
- var plotObj = plots[spId];
- if(plotObj) {
- supportsCompare = true;
-
- // TODO make sure that fullLayout_plots axis refs
- // get updated properly so that we don't have
- // to use Axes.getFromId in general.
-
- xaArray[i] = Axes.getFromId(gd, plotObj.xaxis._id);
- yaArray[i] = Axes.getFromId(gd, plotObj.yaxis._id);
- continue;
- }
-
- // other subplot types
- var _subplot = fullLayout[spId]._subplot;
- xaArray[i] = _subplot.xaxis;
- yaArray[i] = _subplot.yaxis;
- }
-
- var hovermode = evt.hovermode || fullLayout.hovermode;
-
- if(hovermode && !supportsCompare) hovermode = 'closest';
-
- if(['x', 'y', 'closest'].indexOf(hovermode) === -1 || !gd.calcdata ||
- gd.querySelector('.zoombox') || gd._dragging) {
- return dragElement.unhoverRaw(gd, evt);
- }
-
- var hoverdistance = fullLayout.hoverdistance === -1 ? Infinity : fullLayout.hoverdistance;
- var spikedistance = fullLayout.spikedistance === -1 ? Infinity : fullLayout.spikedistance;
-
- // hoverData: the set of candidate points we've found to highlight
- var hoverData = [];
-
- // searchData: the data to search in. Mostly this is just a copy of
- // gd.calcdata, filtered to the subplot and overlays we're on
- // but if a point array is supplied it will be a mapping
- // of indicated curves
- var searchData = [];
-
- // [x|y]valArray: the axis values of the hover event
- // mapped onto each of the currently selected overlaid subplots
- var xvalArray, yvalArray;
-
- var itemnum, curvenum, cd, trace, subplotId, subploti, mode,
- xval, yval, pointData, closedataPreviousLength;
-
- // spikePoints: the set of candidate points we've found to draw spikes to
- var spikePoints = {
- hLinePoint: null,
- vLinePoint: null
- };
-
- // does subplot have one (or more) horizontal traces?
- // This is used to determine whether we rotate the labels or not
- var hasOneHorizontalTrace = false;
-
- // Figure out what we're hovering on:
- // mouse location or user-supplied data
-
- if(Array.isArray(evt)) {
- // user specified an array of points to highlight
- hovermode = 'array';
- for(itemnum = 0; itemnum < evt.length; itemnum++) {
- cd = gd.calcdata[evt[itemnum].curveNumber || 0];
- if(cd) {
- trace = cd[0].trace;
- if(cd[0].trace.hoverinfo !== 'skip') {
- searchData.push(cd);
- if(trace.orientation === 'h') {
- hasOneHorizontalTrace = true;
- }
- }
- }
- }
- }
- else {
- for(curvenum = 0; curvenum < gd.calcdata.length; curvenum++) {
- cd = gd.calcdata[curvenum];
- trace = cd[0].trace;
- if(trace.hoverinfo !== 'skip' && helpers.isTraceInSubplots(trace, subplots)) {
- searchData.push(cd);
- if(trace.orientation === 'h') {
- hasOneHorizontalTrace = true;
- }
- }
- }
-
- // [x|y]px: the pixels (from top left) of the mouse location
- // on the currently selected plot area
- // add pointerX|Y property for drawing the spikes in spikesnap 'cursor' situation
- var hasUserCalledHover = !evt.target;
- var xpx, ypx;
-
- if(hasUserCalledHover) {
- if('xpx' in evt) xpx = evt.xpx;
- else xpx = xaArray[0]._length / 2;
-
- if('ypx' in evt) ypx = evt.ypx;
- else ypx = yaArray[0]._length / 2;
- }
- else {
- // fire the beforehover event and quit if it returns false
- // note that we're only calling this on real mouse events, so
- // manual calls to fx.hover will always run.
- if(Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) {
- return;
- }
-
- var dbb = evt.target.getBoundingClientRect();
-
- xpx = evt.clientX - dbb.left;
- ypx = evt.clientY - dbb.top;
-
- // in case hover was called from mouseout into hovertext,
- // it's possible you're not actually over the plot anymore
- if(xpx < 0 || xpx > xaArray[0]._length || ypx < 0 || ypx > yaArray[0]._length) {
- return dragElement.unhoverRaw(gd, evt);
- }
- }
-
- evt.pointerX = xpx + xaArray[0]._offset;
- evt.pointerY = ypx + yaArray[0]._offset;
-
- if('xval' in evt) xvalArray = helpers.flat(subplots, evt.xval);
- else xvalArray = helpers.p2c(xaArray, xpx);
-
- if('yval' in evt) yvalArray = helpers.flat(subplots, evt.yval);
- else yvalArray = helpers.p2c(yaArray, ypx);
-
- if(!isNumeric(xvalArray[0]) || !isNumeric(yvalArray[0])) {
- Lib.warn('Fx.hover failed', evt, gd);
- return dragElement.unhoverRaw(gd, evt);
- }
- }
-
- // the pixel distance to beat as a matching point
- // in 'x' or 'y' mode this resets for each trace
- var distance = Infinity;
-
- // find the closest point in each trace
- // this is minimum dx and/or dy, depending on mode
- // and the pixel position for the label (labelXpx, labelYpx)
- for(curvenum = 0; curvenum < searchData.length; curvenum++) {
- cd = searchData[curvenum];
-
- // filter out invisible or broken data
- if(!cd || !cd[0] || !cd[0].trace || cd[0].trace.visible !== true) continue;
-
- trace = cd[0].trace;
-
- // Explicitly bail out for these two. I don't know how to otherwise prevent
- // the rest of this function from running and failing
- if(['carpet', 'contourcarpet'].indexOf(trace._module.name) !== -1) continue;
-
- if(trace.type === 'splom') {
- // splom traces do not generate overlay subplots,
- // it is safe to assume here splom traces correspond to the 0th subplot
- subploti = 0;
- subplotId = subplots[subploti];
- } else {
- subplotId = helpers.getSubplot(trace);
- subploti = subplots.indexOf(subplotId);
- }
-
- // within one trace mode can sometimes be overridden
- mode = hovermode;
-
- // container for new point, also used to pass info into module.hoverPoints
- pointData = {
- // trace properties
- cd: cd,
- trace: trace,
- xa: xaArray[subploti],
- ya: yaArray[subploti],
-
- // max distances for hover and spikes - for points that want to show but do not
- // want to override other points, set distance/spikeDistance equal to max*Distance
- // and it will not get filtered out but it will be guaranteed to have a greater
- // distance than any point that calculated a real distance.
- maxHoverDistance: hoverdistance,
- maxSpikeDistance: spikedistance,
-
- // point properties - override all of these
- index: false, // point index in trace - only used by plotly.js hoverdata consumers
- distance: Math.min(distance, hoverdistance), // pixel distance or pseudo-distance
-
- // distance/pseudo-distance for spikes. This distance should always be calculated
- // as if in "closest" mode, and should only be set if this point should
- // generate a spike.
- spikeDistance: Infinity,
-
- // in some cases the spikes have different positioning from the hover label
- // they don't need x0/x1, just one position
- xSpike: undefined,
- ySpike: undefined,
-
- // where and how to display the hover label
- color: Color.defaultLine, // trace color
- name: trace.name,
- x0: undefined,
- x1: undefined,
- y0: undefined,
- y1: undefined,
- xLabelVal: undefined,
- yLabelVal: undefined,
- zLabelVal: undefined,
- text: undefined
- };
-
- // add ref to subplot object (non-cartesian case)
- if(fullLayout[subplotId]) {
- pointData.subplot = fullLayout[subplotId]._subplot;
- }
- // add ref to splom scene
- if(fullLayout._splomScenes && fullLayout._splomScenes[trace.uid]) {
- pointData.scene = fullLayout._splomScenes[trace.uid];
- }
-
- closedataPreviousLength = hoverData.length;
-
- // for a highlighting array, figure out what
- // we're searching for with this element
- if(mode === 'array') {
- var selection = evt[curvenum];
- if('pointNumber' in selection) {
- pointData.index = selection.pointNumber;
- mode = 'closest';
- }
- else {
- mode = '';
- if('xval' in selection) {
- xval = selection.xval;
- mode = 'x';
- }
- if('yval' in selection) {
- yval = selection.yval;
- mode = mode ? 'closest' : 'y';
- }
- }
- }
- else {
- xval = xvalArray[subploti];
- yval = yvalArray[subploti];
- }
-
- // Now if there is range to look in, find the points to hover.
- if(hoverdistance !== 0) {
- if(trace._module && trace._module.hoverPoints) {
- var newPoints = trace._module.hoverPoints(pointData, xval, yval, mode, fullLayout._hoverlayer);
- if(newPoints) {
- var newPoint;
- for(var newPointNum = 0; newPointNum < newPoints.length; newPointNum++) {
- newPoint = newPoints[newPointNum];
- if(isNumeric(newPoint.x0) && isNumeric(newPoint.y0)) {
- hoverData.push(cleanPoint(newPoint, hovermode));
- }
- }
- }
- }
- else {
- Lib.log('Unrecognized trace type in hover:', trace);
- }
- }
-
- // in closest mode, remove any existing (farther) points
- // and don't look any farther than this latest point (or points, some
- // traces like box & violin make multiple hover labels at once)
- if(hovermode === 'closest' && hoverData.length > closedataPreviousLength) {
- hoverData.splice(0, closedataPreviousLength);
- distance = hoverData[0].distance;
- }
-
- // Now if there is range to look in, find the points to draw the spikelines
- // Do it only if there is no hoverData
- if(hasCartesian && (spikedistance !== 0)) {
- if(hoverData.length === 0) {
- pointData.distance = spikedistance;
- pointData.index = false;
- var closestPoints = trace._module.hoverPoints(pointData, xval, yval, 'closest', fullLayout._hoverlayer);
- if(closestPoints) {
- closestPoints = closestPoints.filter(function(point) {
- // some hover points, like scatter fills, do not allow spikes,
- // so will generate a hover point but without a valid spikeDistance
- return point.spikeDistance <= spikedistance;
- });
- }
- if(closestPoints && closestPoints.length) {
- var tmpPoint;
- var closestVPoints = closestPoints.filter(function(point) {
- return point.xa.showspikes;
- });
- if(closestVPoints.length) {
- var closestVPt = closestVPoints[0];
- if(isNumeric(closestVPt.x0) && isNumeric(closestVPt.y0)) {
- tmpPoint = fillSpikePoint(closestVPt);
- if(!spikePoints.vLinePoint || (spikePoints.vLinePoint.spikeDistance > tmpPoint.spikeDistance)) {
- spikePoints.vLinePoint = tmpPoint;
- }
- }
- }
-
- var closestHPoints = closestPoints.filter(function(point) {
- return point.ya.showspikes;
- });
- if(closestHPoints.length) {
- var closestHPt = closestHPoints[0];
- if(isNumeric(closestHPt.x0) && isNumeric(closestHPt.y0)) {
- tmpPoint = fillSpikePoint(closestHPt);
- if(!spikePoints.hLinePoint || (spikePoints.hLinePoint.spikeDistance > tmpPoint.spikeDistance)) {
- spikePoints.hLinePoint = tmpPoint;
- }
- }
- }
- }
- }
- }
- }
-
- function selectClosestPoint(pointsData, spikedistance) {
- var resultPoint = null;
- var minDistance = Infinity;
- var thisSpikeDistance;
- for(var i = 0; i < pointsData.length; i++) {
- thisSpikeDistance = pointsData[i].spikeDistance;
- if(thisSpikeDistance < minDistance && thisSpikeDistance <= spikedistance) {
- resultPoint = pointsData[i];
- minDistance = thisSpikeDistance;
- }
- }
- return resultPoint;
- }
-
- function fillSpikePoint(point) {
- if(!point) return null;
- return {
- xa: point.xa,
- ya: point.ya,
- x: point.xSpike !== undefined ? point.xSpike : (point.x0 + point.x1) / 2,
- y: point.ySpike !== undefined ? point.ySpike : (point.y0 + point.y1) / 2,
- distance: point.distance,
- spikeDistance: point.spikeDistance,
- curveNumber: point.trace.index,
- color: point.color,
- pointNumber: point.index
- };
- }
-
- var spikelineOpts = {
- fullLayout: fullLayout,
- container: fullLayout._hoverlayer,
- outerContainer: fullLayout._paperdiv,
- event: evt
- };
- var oldspikepoints = gd._spikepoints;
- var newspikepoints = {
- vLinePoint: spikePoints.vLinePoint,
- hLinePoint: spikePoints.hLinePoint
- };
- gd._spikepoints = newspikepoints;
-
- // Now if it is not restricted by spikedistance option, set the points to draw the spikelines
- if(hasCartesian && (spikedistance !== 0)) {
- if(hoverData.length !== 0) {
- var tmpHPointData = hoverData.filter(function(point) {
- return point.ya.showspikes;
- });
- var tmpHPoint = selectClosestPoint(tmpHPointData, spikedistance);
- spikePoints.hLinePoint = fillSpikePoint(tmpHPoint);
-
- var tmpVPointData = hoverData.filter(function(point) {
- return point.xa.showspikes;
- });
- var tmpVPoint = selectClosestPoint(tmpVPointData, spikedistance);
- spikePoints.vLinePoint = fillSpikePoint(tmpVPoint);
- }
- }
-
- // if hoverData is empty check for the spikes to draw and quit if there are none
- if(hoverData.length === 0) {
- var result = dragElement.unhoverRaw(gd, evt);
- if(hasCartesian && ((spikePoints.hLinePoint !== null) || (spikePoints.vLinePoint !== null))) {
- if(spikesChanged(oldspikepoints)) {
- createSpikelines(spikePoints, spikelineOpts);
- }
- }
- return result;
- }
-
- if(hasCartesian) {
- if(spikesChanged(oldspikepoints)) {
- createSpikelines(spikePoints, spikelineOpts);
- }
- }
-
- hoverData.sort(function(d1, d2) { return d1.distance - d2.distance; });
-
- // lastly, emit custom hover/unhover events
- var oldhoverdata = gd._hoverdata;
- var newhoverdata = [];
-
- // pull out just the data that's useful to
- // other people and send it to the event
- for(itemnum = 0; itemnum < hoverData.length; itemnum++) {
- var pt = hoverData[itemnum];
- var eventData = helpers.makeEventData(pt, pt.trace, pt.cd);
-
- var ht = false;
- if(pt.cd[pt.index] && pt.cd[pt.index].ht) ht = pt.cd[pt.index].ht;
- hoverData[itemnum].hovertemplate = ht || pt.trace.hovertemplate || false;
- hoverData[itemnum].eventData = [eventData];
-
- newhoverdata.push(eventData);
- }
-
- gd._hoverdata = newhoverdata;
-
- var rotateLabels = (
- (hovermode === 'y' && (searchData.length > 1 || hoverData.length > 1)) ||
- (hovermode === 'closest' && hasOneHorizontalTrace && hoverData.length > 1)
- );
-
- var bgColor = Color.combine(
- fullLayout.plot_bgcolor || Color.background,
- fullLayout.paper_bgcolor
- );
-
- var labelOpts = {
- hovermode: hovermode,
- rotateLabels: rotateLabels,
- bgColor: bgColor,
- container: fullLayout._hoverlayer,
- outerContainer: fullLayout._paperdiv,
- commonLabelOpts: fullLayout.hoverlabel,
- hoverdistance: fullLayout.hoverdistance
- };
-
- var hoverLabels = createHoverText(hoverData, labelOpts, gd);
-
- hoverAvoidOverlaps(hoverData, rotateLabels ? 'xa' : 'ya', fullLayout);
-
- alignHoverText(hoverLabels, rotateLabels);
-
- // TODO: tagName hack is needed to appease geo.js's hack of using evt.target=true
- // we should improve the "fx" API so other plots can use it without these hack.
- if(evt.target && evt.target.tagName) {
- var hasClickToShow = Registry.getComponentMethod('annotations', 'hasClickToShow')(gd, newhoverdata);
- overrideCursor(d3.select(evt.target), hasClickToShow ? 'pointer' : '');
- }
-
- // don't emit events if called manually
- if(!evt.target || noHoverEvent || !hoverChanged(gd, evt, oldhoverdata)) return;
-
- if(oldhoverdata) {
- gd.emit('plotly_unhover', {
- event: evt,
- points: oldhoverdata
- });
- }
-
- gd.emit('plotly_hover', {
- event: evt,
- points: gd._hoverdata,
- xaxes: xaArray,
- yaxes: yaArray,
- xvals: xvalArray,
- yvals: yvalArray
- });
- }
-
- var EXTRA_STRING_REGEX = /<extra>([\s\S]*)<\/extra>/;
-
- function createHoverText(hoverData, opts, gd) {
- var hovermode = opts.hovermode;
- var rotateLabels = opts.rotateLabels;
- var bgColor = opts.bgColor;
- var container = opts.container;
- var outerContainer = opts.outerContainer;
- var commonLabelOpts = opts.commonLabelOpts || {};
-
- // opts.fontFamily/Size are used for the common label
- // and as defaults for each hover label, though the individual labels
- // can override this.
- var fontFamily = opts.fontFamily || constants.HOVERFONT;
- var fontSize = opts.fontSize || constants.HOVERFONTSIZE;
-
- var c0 = hoverData[0];
- var xa = c0.xa;
- var ya = c0.ya;
- var commonAttr = hovermode === 'y' ? 'yLabel' : 'xLabel';
- var t0 = c0[commonAttr];
- var t00 = (String(t0) || '').split(' ')[0];
- var outerContainerBB = outerContainer.node().getBoundingClientRect();
- var outerTop = outerContainerBB.top;
- var outerWidth = outerContainerBB.width;
- var outerHeight = outerContainerBB.height;
-
- // show the common label, if any, on the axis
- // never show a common label in array mode,
- // even if sometimes there could be one
- var showCommonLabel = (
- (t0 !== undefined) &&
- (c0.distance <= opts.hoverdistance) &&
- (hovermode === 'x' || hovermode === 'y')
- );
-
- // all hover traces hoverinfo must contain the hovermode
- // to have common labels
- if(showCommonLabel) {
- var allHaveZ = true;
- var i, traceHoverinfo;
- for(i = 0; i < hoverData.length; i++) {
- if(allHaveZ && hoverData[i].zLabel === undefined) allHaveZ = false;
-
- traceHoverinfo = hoverData[i].hoverinfo || hoverData[i].trace.hoverinfo;
- if(traceHoverinfo) {
- var parts = Array.isArray(traceHoverinfo) ? traceHoverinfo : traceHoverinfo.split('+');
- if(parts.indexOf('all') === -1 &&
- parts.indexOf(hovermode) === -1) {
- showCommonLabel = false;
- break;
- }
- }
- }
-
- // xyz labels put all info in their main label, so have no need of a common label
- if(allHaveZ) showCommonLabel = false;
- }
-
- var commonLabel = container.selectAll('g.axistext')
- .data(showCommonLabel ? [0] : []);
- commonLabel.enter().append('g')
- .classed('axistext', true);
- commonLabel.exit().remove();
-
- commonLabel.each(function() {
- var label = d3.select(this);
- var lpath = Lib.ensureSingle(label, 'path', '', function(s) {
- s.style({'stroke-width': '1px'});
- });
- var ltext = Lib.ensureSingle(label, 'text', '', function(s) {
- // prohibit tex interpretation until we can handle
- // tex and regular text together
- s.attr('data-notex', 1);
- });
-
- var commonBgColor = commonLabelOpts.bgcolor || Color.defaultLine;
- var commonStroke = commonLabelOpts.bordercolor || Color.contrast(commonBgColor);
- var contrastColor = Color.contrast(commonBgColor);
-
- lpath.style({
- fill: commonBgColor,
- stroke: commonStroke
- });
-
- ltext.text(t0)
- .call(Drawing.font,
- commonLabelOpts.font.family || fontFamily,
- commonLabelOpts.font.size || fontSize,
- commonLabelOpts.font.color || contrastColor
- )
- .call(svgTextUtils.positionText, 0, 0)
- .call(svgTextUtils.convertToTspans, gd);
-
- label.attr('transform', '');
-
- var tbb = ltext.node().getBoundingClientRect();
- if(hovermode === 'x') {
- ltext.attr('text-anchor', 'middle')
- .call(svgTextUtils.positionText, 0, (xa.side === 'top' ?
- (outerTop - tbb.bottom - HOVERARROWSIZE - HOVERTEXTPAD) :
- (outerTop - tbb.top + HOVERARROWSIZE + HOVERTEXTPAD)));
-
- var topsign = xa.side === 'top' ? '-' : '';
- lpath.attr('d', 'M0,0' +
- 'L' + HOVERARROWSIZE + ',' + topsign + HOVERARROWSIZE +
- 'H' + (HOVERTEXTPAD + tbb.width / 2) +
- 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) +
- 'H-' + (HOVERTEXTPAD + tbb.width / 2) +
- 'V' + topsign + HOVERARROWSIZE + 'H-' + HOVERARROWSIZE + 'Z');
-
- label.attr('transform', 'translate(' +
- (xa._offset + (c0.x0 + c0.x1) / 2) + ',' +
- (ya._offset + (xa.side === 'top' ? 0 : ya._length)) + ')');
- }
- else {
- ltext.attr('text-anchor', ya.side === 'right' ? 'start' : 'end')
- .call(svgTextUtils.positionText,
- (ya.side === 'right' ? 1 : -1) * (HOVERTEXTPAD + HOVERARROWSIZE),
- outerTop - tbb.top - tbb.height / 2);
-
- var leftsign = ya.side === 'right' ? '' : '-';
- lpath.attr('d', 'M0,0' +
- 'L' + leftsign + HOVERARROWSIZE + ',' + HOVERARROWSIZE +
- 'V' + (HOVERTEXTPAD + tbb.height / 2) +
- 'h' + leftsign + (HOVERTEXTPAD * 2 + tbb.width) +
- 'V-' + (HOVERTEXTPAD + tbb.height / 2) +
- 'H' + leftsign + HOVERARROWSIZE + 'V-' + HOVERARROWSIZE + 'Z');
-
- label.attr('transform', 'translate(' +
- (xa._offset + (ya.side === 'right' ? xa._length : 0)) + ',' +
- (ya._offset + (c0.y0 + c0.y1) / 2) + ')');
- }
- // remove the "close but not quite" points
- // because of error bars, only take up to a space
- hoverData = hoverData.filter(function(d) {
- return (d.zLabelVal !== undefined) ||
- (d[commonAttr] || '').split(' ')[0] === t00;
- });
- });
-
- // show all the individual labels
-
- // first create the objects
- var hoverLabels = container.selectAll('g.hovertext')
- .data(hoverData, function(d) {
- return [d.trace.index, d.index, d.x0, d.y0, d.name, d.attr, d.xa, d.ya || ''].join(',');
- });
- hoverLabels.enter().append('g')
- .classed('hovertext', true)
- .each(function() {
- var g = d3.select(this);
- // trace name label (rect and text.name)
- g.append('rect')
- .call(Color.fill, Color.addOpacity(bgColor, 0.8));
- g.append('text').classed('name', true);
- // trace data label (path and text.nums)
- g.append('path')
- .style('stroke-width', '1px');
- g.append('text').classed('nums', true)
- .call(Drawing.font, fontFamily, fontSize);
- });
- hoverLabels.exit().remove();
-
- // then put the text in, position the pointer to the data,
- // and figure out sizes
- hoverLabels.each(function(d) {
- var g = d3.select(this).attr('transform', '');
- var name = '';
- var text = '';
-
- // combine possible non-opaque trace color with bgColor
- var color0 = d.bgcolor || d.color;
- // color for 'nums' part of the label
- var numsColor = Color.combine(
- Color.opacity(color0) ? color0 : Color.defaultLine,
- bgColor
- );
- // color for 'name' part of the label
- var nameColor = Color.combine(
- Color.opacity(d.color) ? d.color : Color.defaultLine,
- bgColor
- );
- // find a contrasting color for border and text
- var contrastColor = d.borderColor || Color.contrast(numsColor);
-
- // to get custom 'name' labels pass cleanPoint
- if(d.nameOverride !== undefined) d.name = d.nameOverride;
-
- if(d.name) {
- name = svgTextUtils.plainText(d.name || '', {
- len: d.nameLength,
- allowedTags: ['br', 'sub', 'sup', 'b', 'i', 'em']
- });
- }
-
- if(d.zLabel !== undefined) {
- if(d.xLabel !== undefined) text += 'x: ' + d.xLabel + '<br>';
- if(d.yLabel !== undefined) text += 'y: ' + d.yLabel + '<br>';
- text += (text ? 'z: ' : '') + d.zLabel;
- }
- else if(showCommonLabel && d[hovermode + 'Label'] === t0) {
- text = d[(hovermode === 'x' ? 'y' : 'x') + 'Label'] || '';
- }
- else if(d.xLabel === undefined) {
- if(d.yLabel !== undefined) text = d.yLabel;
- }
- else if(d.yLabel === undefined) text = d.xLabel;
- else text = '(' + d.xLabel + ', ' + d.yLabel + ')';
-
- if((d.text || d.text === 0) && !Array.isArray(d.text)) {
- text += (text ? '<br>' : '') + d.text;
- }
-
- // used by other modules (initially just ternary) that
- // manage their own hoverinfo independent of cleanPoint
- // the rest of this will still apply, so such modules
- // can still put things in (x|y|z)Label, text, and name
- // and hoverinfo will still determine their visibility
- if(d.extraText !== undefined) text += (text ? '<br>' : '') + d.extraText;
-
- // if 'text' is empty at this point,
- // and hovertemplate is not defined,
- // put 'name' in main label and don't show secondary label
- if(text === '' && !d.hovertemplate) {
- // if 'name' is also empty, remove entire label
- if(name === '') g.remove();
- text = name;
- }
-
- // hovertemplate
- var hovertemplate = d.hovertemplate || false;
- var hovertemplateLabels = d.hovertemplateLabels || d;
- var eventData = d.eventData[0] || {};
- if(hovertemplate) {
- text = Lib.hovertemplateString(hovertemplate, hovertemplateLabels, eventData);
-
- text = text.replace(EXTRA_STRING_REGEX, function(match, extra) {
- name = extra; // Assign name for secondary text label
- return ''; // Remove from main text label
- });
- }
-
- // main label
- var tx = g.select('text.nums')
- .call(Drawing.font,
- d.fontFamily || fontFamily,
- d.fontSize || fontSize,
- d.fontColor || contrastColor)
- .text(text)
- .attr('data-notex', 1)
- .call(svgTextUtils.positionText, 0, 0)
- .call(svgTextUtils.convertToTspans, gd);
-
- var tx2 = g.select('text.name');
- var tx2width = 0;
- var tx2height = 0;
-
- // secondary label for non-empty 'name'
- if(name && name !== text) {
- tx2.call(Drawing.font,
- d.fontFamily || fontFamily,
- d.fontSize || fontSize,
- nameColor)
- .text(name)
- .attr('data-notex', 1)
- .call(svgTextUtils.positionText, 0, 0)
- .call(svgTextUtils.convertToTspans, gd);
-
- var t2bb = tx2.node().getBoundingClientRect();
- tx2width = t2bb.width + 2 * HOVERTEXTPAD;
- tx2height = t2bb.height + 2 * HOVERTEXTPAD;
- } else {
- tx2.remove();
- g.select('rect').remove();
- }
-
- g.select('path').style({
- fill: numsColor,
- stroke: contrastColor
- });
-
- var tbb = tx.node().getBoundingClientRect();
- var htx = d.xa._offset + (d.x0 + d.x1) / 2;
- var hty = d.ya._offset + (d.y0 + d.y1) / 2;
- var dx = Math.abs(d.x1 - d.x0);
- var dy = Math.abs(d.y1 - d.y0);
- var txTotalWidth = tbb.width + HOVERARROWSIZE + HOVERTEXTPAD + tx2width;
- var anchorStartOK, anchorEndOK;
-
- d.ty0 = outerTop - tbb.top;
- d.bx = tbb.width + 2 * HOVERTEXTPAD;
- d.by = Math.max(tbb.height + 2 * HOVERTEXTPAD, tx2height);
- d.anchor = 'start';
- d.txwidth = tbb.width;
- d.tx2width = tx2width;
- d.offset = 0;
-
- if(rotateLabels) {
- d.pos = htx;
- anchorStartOK = hty + dy / 2 + txTotalWidth <= outerHeight;
- anchorEndOK = hty - dy / 2 - txTotalWidth >= 0;
- if((d.idealAlign === 'top' || !anchorStartOK) && anchorEndOK) {
- hty -= dy / 2;
- d.anchor = 'end';
- } else if(anchorStartOK) {
- hty += dy / 2;
- d.anchor = 'start';
- } else d.anchor = 'middle';
- }
- else {
- d.pos = hty;
- anchorStartOK = htx + dx / 2 + txTotalWidth <= outerWidth;
- anchorEndOK = htx - dx / 2 - txTotalWidth >= 0;
- if((d.idealAlign === 'left' || !anchorStartOK) && anchorEndOK) {
- htx -= dx / 2;
- d.anchor = 'end';
- } else if(anchorStartOK) {
- htx += dx / 2;
- d.anchor = 'start';
- } else d.anchor = 'middle';
- }
-
- tx.attr('text-anchor', d.anchor);
- if(tx2width) tx2.attr('text-anchor', d.anchor);
- g.attr('transform', 'translate(' + htx + ',' + hty + ')' +
- (rotateLabels ? 'rotate(' + YANGLE + ')' : ''));
- });
-
- return hoverLabels;
- }
-
- // Make groups of touching points, and within each group
- // move each point so that no labels overlap, but the average
- // label position is the same as it was before moving. Indicentally,
- // this is equivalent to saying all the labels are on equal linear
- // springs about their initial position. Initially, each point is
- // its own group, but as we find overlaps we will clump the points.
- //
- // Also, there are hard constraints at the edges of the graphs,
- // that push all groups to the middle so they are visible. I don't
- // know what happens if the group spans all the way from one edge to
- // the other, though it hardly matters - there's just too much
- // information then.
- function hoverAvoidOverlaps(hoverData, ax, fullLayout) {
- var nummoves = 0;
- var axSign = 1;
-
- // make groups of touching points
- var pointgroups = hoverData.map(function(d, i) {
- var axis = d[ax];
- var axIsX = axis._id.charAt(0) === 'x';
- var rng = axis.range;
- if(!i && rng && ((rng[0] > rng[1]) !== axIsX)) axSign = -1;
- return [{
- i: i,
- traceIndex: d.trace.index,
- dp: 0,
- pos: d.pos,
- posref: d.posref,
- size: d.by * (axIsX ? YFACTOR : 1) / 2,
- pmin: 0,
- pmax: (axIsX ? fullLayout.width : fullLayout.height)
- }];
- })
- .sort(function(a, b) {
- return (a[0].posref - b[0].posref) ||
- // for equal positions, sort trace indices increasing or decreasing
- // depending on whether the axis is reversed or not... so stacked
- // traces will generally keep their order even if one trace adds
- // nothing to the stack.
- (axSign * (b[0].traceIndex - a[0].traceIndex));
- });
-
- var donepositioning, topOverlap, bottomOverlap, i, j, pti, sumdp;
-
- function constrainGroup(grp) {
- var minPt = grp[0];
- var maxPt = grp[grp.length - 1];
-
- // overlap with the top - positive vals are overlaps
- topOverlap = minPt.pmin - minPt.pos - minPt.dp + minPt.size;
-
- // overlap with the bottom - positive vals are overlaps
- bottomOverlap = maxPt.pos + maxPt.dp + maxPt.size - minPt.pmax;
-
- // check for min overlap first, so that we always
- // see the largest labels
- // allow for .01px overlap, so we don't get an
- // infinite loop from rounding errors
- if(topOverlap > 0.01) {
- for(j = grp.length - 1; j >= 0; j--) grp[j].dp += topOverlap;
- donepositioning = false;
- }
- if(bottomOverlap < 0.01) return;
- if(topOverlap < -0.01) {
- // make sure we're not pushing back and forth
- for(j = grp.length - 1; j >= 0; j--) grp[j].dp -= bottomOverlap;
- donepositioning = false;
- }
- if(!donepositioning) return;
-
- // no room to fix positioning, delete off-screen points
-
- // first see how many points we need to delete
- var deleteCount = 0;
- for(i = 0; i < grp.length; i++) {
- pti = grp[i];
- if(pti.pos + pti.dp + pti.size > minPt.pmax) deleteCount++;
- }
-
- // start by deleting points whose data is off screen
- for(i = grp.length - 1; i >= 0; i--) {
- if(deleteCount <= 0) break;
- pti = grp[i];
-
- // pos has already been constrained to [pmin,pmax]
- // so look for points close to that to delete
- if(pti.pos > minPt.pmax - 1) {
- pti.del = true;
- deleteCount--;
- }
- }
- for(i = 0; i < grp.length; i++) {
- if(deleteCount <= 0) break;
- pti = grp[i];
-
- // pos has already been constrained to [pmin,pmax]
- // so look for points close to that to delete
- if(pti.pos < minPt.pmin + 1) {
- pti.del = true;
- deleteCount--;
-
- // shift the whole group minus into this new space
- bottomOverlap = pti.size * 2;
- for(j = grp.length - 1; j >= 0; j--) grp[j].dp -= bottomOverlap;
- }
- }
- // then delete points that go off the bottom
- for(i = grp.length - 1; i >= 0; i--) {
- if(deleteCount <= 0) break;
- pti = grp[i];
- if(pti.pos + pti.dp + pti.size > minPt.pmax) {
- pti.del = true;
- deleteCount--;
- }
- }
- }
-
- // loop through groups, combining them if they overlap,
- // until nothing moves
- while(!donepositioning && nummoves <= hoverData.length) {
- // to avoid infinite loops, don't move more times
- // than there are traces
- nummoves++;
-
- // assume nothing will move in this iteration,
- // reverse this if it does
- donepositioning = true;
- i = 0;
- while(i < pointgroups.length - 1) {
- // the higher (g0) and lower (g1) point group
- var g0 = pointgroups[i];
- var g1 = pointgroups[i + 1];
-
- // the lowest point in the higher group (p0)
- // the highest point in the lower group (p1)
- var p0 = g0[g0.length - 1];
- var p1 = g1[0];
- topOverlap = p0.pos + p0.dp + p0.size - p1.pos - p1.dp + p1.size;
-
- // Only group points that lie on the same axes
- if(topOverlap > 0.01 && (p0.pmin === p1.pmin) && (p0.pmax === p1.pmax)) {
- // push the new point(s) added to this group out of the way
- for(j = g1.length - 1; j >= 0; j--) g1[j].dp += topOverlap;
-
- // add them to the group
- g0.push.apply(g0, g1);
- pointgroups.splice(i + 1, 1);
-
- // adjust for minimum average movement
- sumdp = 0;
- for(j = g0.length - 1; j >= 0; j--) sumdp += g0[j].dp;
- bottomOverlap = sumdp / g0.length;
- for(j = g0.length - 1; j >= 0; j--) g0[j].dp -= bottomOverlap;
- donepositioning = false;
- }
- else i++;
- }
-
- // check if we're going off the plot on either side and fix
- pointgroups.forEach(constrainGroup);
- }
-
- // now put these offsets into hoverData
- for(i = pointgroups.length - 1; i >= 0; i--) {
- var grp = pointgroups[i];
- for(j = grp.length - 1; j >= 0; j--) {
- var pt = grp[j];
- var hoverPt = hoverData[pt.i];
- hoverPt.offset = pt.dp;
- hoverPt.del = pt.del;
- }
- }
- }
-
- function alignHoverText(hoverLabels, rotateLabels) {
- // finally set the text positioning relative to the data and draw the
- // box around it
- hoverLabels.each(function(d) {
- var g = d3.select(this);
- if(d.del) {
- g.remove();
- return;
- }
-
- var horzSign = d.anchor === 'end' ? -1 : 1;
- var tx = g.select('text.nums');
- var alignShift = {start: 1, end: -1, middle: 0}[d.anchor];
- var txx = alignShift * (HOVERARROWSIZE + HOVERTEXTPAD);
- var tx2x = txx + alignShift * (d.txwidth + HOVERTEXTPAD);
- var offsetX = 0;
- var offsetY = d.offset;
-
- if(d.anchor === 'middle') {
- txx -= d.tx2width / 2;
- tx2x += d.txwidth / 2 + HOVERTEXTPAD;
- }
- if(rotateLabels) {
- offsetY *= -YSHIFTY;
- offsetX = d.offset * YSHIFTX;
- }
-
- g.select('path').attr('d', d.anchor === 'middle' ?
- // middle aligned: rect centered on data
- ('M-' + (d.bx / 2 + d.tx2width / 2) + ',' + (offsetY - d.by / 2) +
- 'h' + d.bx + 'v' + d.by + 'h-' + d.bx + 'Z') :
- // left or right aligned: side rect with arrow to data
- ('M0,0L' + (horzSign * HOVERARROWSIZE + offsetX) + ',' + (HOVERARROWSIZE + offsetY) +
- 'v' + (d.by / 2 - HOVERARROWSIZE) +
- 'h' + (horzSign * d.bx) +
- 'v-' + d.by +
- 'H' + (horzSign * HOVERARROWSIZE + offsetX) +
- 'V' + (offsetY - HOVERARROWSIZE) +
- 'Z'));
-
- tx.call(svgTextUtils.positionText,
- txx + offsetX, offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD);
-
- if(d.tx2width) {
- g.select('text.name')
- .call(svgTextUtils.positionText,
- tx2x + alignShift * HOVERTEXTPAD + offsetX,
- offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD);
- g.select('rect')
- .call(Drawing.setRect,
- tx2x + (alignShift - 1) * d.tx2width / 2 + offsetX,
- offsetY - d.by / 2 - 1,
- d.tx2width, d.by + 2);
- }
- });
- }
-
- function cleanPoint(d, hovermode) {
- var index = d.index;
- var trace = d.trace || {};
- var cd0 = d.cd[0];
- var cd = d.cd[index] || {};
-
- var getVal = Array.isArray(index) ?
- function(calcKey, traceKey) {
- return Lib.castOption(cd0, index, calcKey) ||
- Lib.extractOption({}, trace, '', traceKey);
- } :
- function(calcKey, traceKey) {
- return Lib.extractOption(cd, trace, calcKey, traceKey);
- };
-
- function fill(key, calcKey, traceKey) {
- var val = getVal(calcKey, traceKey);
- if(val) d[key] = val;
- }
-
- fill('hoverinfo', 'hi', 'hoverinfo');
- fill('bgcolor', 'hbg', 'hoverlabel.bgcolor');
- fill('borderColor', 'hbc', 'hoverlabel.bordercolor');
- fill('fontFamily', 'htf', 'hoverlabel.font.family');
- fill('fontSize', 'hts', 'hoverlabel.font.size');
- fill('fontColor', 'htc', 'hoverlabel.font.color');
- fill('nameLength', 'hnl', 'hoverlabel.namelength');
-
- d.posref = (hovermode === 'y' || (hovermode === 'closest' && trace.orientation === 'h')) ?
- (d.xa._offset + (d.x0 + d.x1) / 2) :
- (d.ya._offset + (d.y0 + d.y1) / 2);
-
- // then constrain all the positions to be on the plot
- d.x0 = Lib.constrain(d.x0, 0, d.xa._length);
- d.x1 = Lib.constrain(d.x1, 0, d.xa._length);
- d.y0 = Lib.constrain(d.y0, 0, d.ya._length);
- d.y1 = Lib.constrain(d.y1, 0, d.ya._length);
-
- // and convert the x and y label values into formatted text
- if(d.xLabelVal !== undefined) {
- d.xLabel = ('xLabel' in d) ? d.xLabel : Axes.hoverLabelText(d.xa, d.xLabelVal);
- d.xVal = d.xa.c2d(d.xLabelVal);
- }
- if(d.yLabelVal !== undefined) {
- d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal);
- d.yVal = d.ya.c2d(d.yLabelVal);
- }
-
- // Traces like heatmaps generate the zLabel in their hoverPoints function
- if(d.zLabelVal !== undefined && d.zLabel === undefined) {
- d.zLabel = String(d.zLabelVal);
- }
-
- // for box means and error bars, add the range to the label
- if(!isNaN(d.xerr) && !(d.xa.type === 'log' && d.xerr <= 0)) {
- var xeText = Axes.tickText(d.xa, d.xa.c2l(d.xerr), 'hover').text;
- if(d.xerrneg !== undefined) {
- d.xLabel += ' +' + xeText + ' / -' +
- Axes.tickText(d.xa, d.xa.c2l(d.xerrneg), 'hover').text;
- }
- else d.xLabel += ' ± ' + xeText;
-
- // small distance penalty for error bars, so that if there are
- // traces with errors and some without, the error bar label will
- // hoist up to the point
- if(hovermode === 'x') d.distance += 1;
- }
- if(!isNaN(d.yerr) && !(d.ya.type === 'log' && d.yerr <= 0)) {
- var yeText = Axes.tickText(d.ya, d.ya.c2l(d.yerr), 'hover').text;
- if(d.yerrneg !== undefined) {
- d.yLabel += ' +' + yeText + ' / -' +
- Axes.tickText(d.ya, d.ya.c2l(d.yerrneg), 'hover').text;
- }
- else d.yLabel += ' ± ' + yeText;
-
- if(hovermode === 'y') d.distance += 1;
- }
-
- var infomode = d.hoverinfo || d.trace.hoverinfo;
-
- if(infomode && infomode !== 'all') {
- infomode = Array.isArray(infomode) ? infomode : infomode.split('+');
- if(infomode.indexOf('x') === -1) d.xLabel = undefined;
- if(infomode.indexOf('y') === -1) d.yLabel = undefined;
- if(infomode.indexOf('z') === -1) d.zLabel = undefined;
- if(infomode.indexOf('text') === -1) d.text = undefined;
- if(infomode.indexOf('name') === -1) d.name = undefined;
- }
-
- return d;
- }
-
- function createSpikelines(closestPoints, opts) {
- var container = opts.container;
- var fullLayout = opts.fullLayout;
- var evt = opts.event;
- var showY = !!closestPoints.hLinePoint;
- var showX = !!closestPoints.vLinePoint;
-
- var xa, ya;
-
- // Remove old spikeline items
- container.selectAll('.spikeline').remove();
-
- if(!(showX || showY)) return;
-
- var contrastColor = Color.combine(fullLayout.plot_bgcolor, fullLayout.paper_bgcolor);
-
- // Horizontal line (to y-axis)
- if(showY) {
- var hLinePoint = closestPoints.hLinePoint;
- var hLinePointX, hLinePointY;
-
- xa = hLinePoint && hLinePoint.xa;
- ya = hLinePoint && hLinePoint.ya;
- var ySnap = ya.spikesnap;
-
- if(ySnap === 'cursor') {
- hLinePointX = evt.pointerX;
- hLinePointY = evt.pointerY;
- } else {
- hLinePointX = xa._offset + hLinePoint.x;
- hLinePointY = ya._offset + hLinePoint.y;
- }
- var dfltHLineColor = tinycolor.readability(hLinePoint.color, contrastColor) < 1.5 ?
- Color.contrast(contrastColor) : hLinePoint.color;
- var yMode = ya.spikemode;
- var yThickness = ya.spikethickness;
- var yColor = ya.spikecolor || dfltHLineColor;
- var yBB = ya._boundingBox;
- var xEdge = ((yBB.left + yBB.right) / 2) < hLinePointX ? yBB.right : yBB.left;
- var xBase, xEndSpike;
-
- if(yMode.indexOf('toaxis') !== -1 || yMode.indexOf('across') !== -1) {
- if(yMode.indexOf('toaxis') !== -1) {
- xBase = xEdge;
- xEndSpike = hLinePointX;
- }
- if(yMode.indexOf('across') !== -1) {
- xBase = ya._counterSpan[0];
- xEndSpike = ya._counterSpan[1];
- }
-
- // Foreground horizontal line (to y-axis)
- container.insert('line', ':first-child')
- .attr({
- x1: xBase,
- x2: xEndSpike,
- y1: hLinePointY,
- y2: hLinePointY,
- 'stroke-width': yThickness,
- stroke: yColor,
- 'stroke-dasharray': Drawing.dashStyle(ya.spikedash, yThickness)
- })
- .classed('spikeline', true)
- .classed('crisp', true);
-
- // Background horizontal Line (to y-axis)
- container.insert('line', ':first-child')
- .attr({
- x1: xBase,
- x2: xEndSpike,
- y1: hLinePointY,
- y2: hLinePointY,
- 'stroke-width': yThickness + 2,
- stroke: contrastColor
- })
- .classed('spikeline', true)
- .classed('crisp', true);
- }
- // Y axis marker
- if(yMode.indexOf('marker') !== -1) {
- container.insert('circle', ':first-child')
- .attr({
- cx: xEdge + (ya.side !== 'right' ? yThickness : -yThickness),
- cy: hLinePointY,
- r: yThickness,
- fill: yColor
- })
- .classed('spikeline', true);
- }
- }
-
- if(showX) {
- var vLinePoint = closestPoints.vLinePoint;
- var vLinePointX, vLinePointY;
-
- xa = vLinePoint && vLinePoint.xa;
- ya = vLinePoint && vLinePoint.ya;
- var xSnap = xa.spikesnap;
-
- if(xSnap === 'cursor') {
- vLinePointX = evt.pointerX;
- vLinePointY = evt.pointerY;
- } else {
- vLinePointX = xa._offset + vLinePoint.x;
- vLinePointY = ya._offset + vLinePoint.y;
- }
- var dfltVLineColor = tinycolor.readability(vLinePoint.color, contrastColor) < 1.5 ?
- Color.contrast(contrastColor) : vLinePoint.color;
- var xMode = xa.spikemode;
- var xThickness = xa.spikethickness;
- var xColor = xa.spikecolor || dfltVLineColor;
- var xBB = xa._boundingBox;
- var yEdge = ((xBB.top + xBB.bottom) / 2) < vLinePointY ? xBB.bottom : xBB.top;
- var yBase, yEndSpike;
-
- if(xMode.indexOf('toaxis') !== -1 || xMode.indexOf('across') !== -1) {
- if(xMode.indexOf('toaxis') !== -1) {
- yBase = yEdge;
- yEndSpike = vLinePointY;
- }
- if(xMode.indexOf('across') !== -1) {
- yBase = xa._counterSpan[0];
- yEndSpike = xa._counterSpan[1];
- }
-
- // Foreground vertical line (to x-axis)
- container.insert('line', ':first-child')
- .attr({
- x1: vLinePointX,
- x2: vLinePointX,
- y1: yBase,
- y2: yEndSpike,
- 'stroke-width': xThickness,
- stroke: xColor,
- 'stroke-dasharray': Drawing.dashStyle(xa.spikedash, xThickness)
- })
- .classed('spikeline', true)
- .classed('crisp', true);
-
- // Background vertical line (to x-axis)
- container.insert('line', ':first-child')
- .attr({
- x1: vLinePointX,
- x2: vLinePointX,
- y1: yBase,
- y2: yEndSpike,
- 'stroke-width': xThickness + 2,
- stroke: contrastColor
- })
- .classed('spikeline', true)
- .classed('crisp', true);
- }
-
- // X axis marker
- if(xMode.indexOf('marker') !== -1) {
- container.insert('circle', ':first-child')
- .attr({
- cx: vLinePointX,
- cy: yEdge - (xa.side !== 'top' ? xThickness : -xThickness),
- r: xThickness,
- fill: xColor
- })
- .classed('spikeline', true);
- }
- }
- }
-
- function hoverChanged(gd, evt, oldhoverdata) {
- // don't emit any events if nothing changed
- if(!oldhoverdata || oldhoverdata.length !== gd._hoverdata.length) return true;
-
- for(var i = oldhoverdata.length - 1; i >= 0; i--) {
- var oldPt = oldhoverdata[i];
- var newPt = gd._hoverdata[i];
-
- if(oldPt.curveNumber !== newPt.curveNumber ||
- String(oldPt.pointNumber) !== String(newPt.pointNumber) ||
- String(oldPt.pointNumbers) !== String(newPt.pointNumbers)
- ) {
- return true;
- }
- }
- return false;
- }
-
- function spikesChanged(gd, oldspikepoints) {
- // don't relayout the plot because of new spikelines if spikelines points didn't change
- if(!oldspikepoints) return true;
- if(oldspikepoints.vLinePoint !== gd._spikepoints.vLinePoint ||
- oldspikepoints.hLinePoint !== gd._spikepoints.hLinePoint
- ) return true;
- return false;
- }
-
- },{"../../lib":168,"../../lib/events":161,"../../lib/override_cursor":179,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../registry":257,"../color":51,"../dragelement":69,"../drawing":72,"./constants":84,"./helpers":86,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],88:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts) {
- opts = opts || {};
-
- coerce('hoverlabel.bgcolor', opts.bgcolor);
- coerce('hoverlabel.bordercolor', opts.bordercolor);
- coerce('hoverlabel.namelength', opts.namelength);
- Lib.coerceFont(coerce, 'hoverlabel.font', opts.font);
- };
-
- },{"../../lib":168}],89:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = function(opts, extra) {
- opts = opts || {};
- extra = extra || {};
-
- var descPart = extra.description ? ' ' + extra.description : '';
- var keys = extra.keys || [];
- if(keys.length > 0) {
- var quotedKeys = [];
- for(var i = 0; i < keys.length; i++) {
- quotedKeys[i] = '`' + keys[i] + '`';
- }
- descPart = descPart + 'Finally, the template string has access to ';
- if(keys.length === 1) {
- descPart = 'variable ' + quotedKeys[0];
- } else {
- descPart = 'variables ' + quotedKeys.slice(0, -1).join(', ') + ' and ' + quotedKeys.slice(-1) + '.';
- }
- }
-
- var hovertemplate = {
- valType: 'string',
-
- dflt: '',
- arrayOk: true,
- editType: 'none',
-
- };
-
- return hovertemplate;
- };
-
- },{}],90:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var Lib = _dereq_('../../lib');
- var dragElement = _dereq_('../dragelement');
- var helpers = _dereq_('./helpers');
- var layoutAttributes = _dereq_('./layout_attributes');
- var hoverModule = _dereq_('./hover');
-
- module.exports = {
- moduleType: 'component',
- name: 'fx',
-
- constants: _dereq_('./constants'),
- schema: {
- layout: layoutAttributes
- },
-
- attributes: _dereq_('./attributes'),
- layoutAttributes: layoutAttributes,
-
- supplyLayoutGlobalDefaults: _dereq_('./layout_global_defaults'),
- supplyDefaults: _dereq_('./defaults'),
- supplyLayoutDefaults: _dereq_('./layout_defaults'),
-
- calc: _dereq_('./calc'),
-
- getDistanceFunction: helpers.getDistanceFunction,
- getClosest: helpers.getClosest,
- inbox: helpers.inbox,
- quadrature: helpers.quadrature,
- appendArrayPointValue: helpers.appendArrayPointValue,
-
- castHoverOption: castHoverOption,
- castHoverinfo: castHoverinfo,
-
- hover: hoverModule.hover,
- unhover: dragElement.unhover,
-
- loneHover: hoverModule.loneHover,
- multiHovers: hoverModule.multiHovers,
- loneUnhover: loneUnhover,
-
- click: _dereq_('./click')
- };
-
- function loneUnhover(containerOrSelection) {
- // duck type whether the arg is a d3 selection because ie9 doesn't
- // handle instanceof like modern browsers do.
- var selection = Lib.isD3Selection(containerOrSelection) ?
- containerOrSelection :
- d3.select(containerOrSelection);
-
- selection.selectAll('g.hovertext').remove();
- selection.selectAll('.spikeline').remove();
- }
-
- // helpers for traces that use Fx.loneHover
-
- function castHoverOption(trace, ptNumber, attr) {
- return Lib.castOption(trace, ptNumber, 'hoverlabel.' + attr);
- }
-
- function castHoverinfo(trace, fullLayout, ptNumber) {
- function _coerce(val) {
- return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout);
- }
-
- return Lib.castOption(trace, ptNumber, 'hoverinfo', _coerce);
- }
-
- },{"../../lib":168,"../dragelement":69,"./attributes":81,"./calc":82,"./click":83,"./constants":84,"./defaults":85,"./helpers":86,"./hover":87,"./layout_attributes":91,"./layout_defaults":92,"./layout_global_defaults":93,"d3":16}],91:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var constants = _dereq_('./constants');
-
- var fontAttrs = _dereq_('../../plots/font_attributes')({
- editType: 'none',
-
- });
- fontAttrs.family.dflt = constants.HOVERFONT;
- fontAttrs.size.dflt = constants.HOVERFONTSIZE;
-
- module.exports = {
- clickmode: {
- valType: 'flaglist',
-
- flags: ['event', 'select'],
- dflt: 'event',
- editType: 'plot',
- extras: ['none'],
-
- },
- dragmode: {
- valType: 'enumerated',
-
- values: ['zoom', 'pan', 'select', 'lasso', 'orbit', 'turntable', false],
- dflt: 'zoom',
- editType: 'modebar',
-
- },
- hovermode: {
- valType: 'enumerated',
-
- values: ['x', 'y', 'closest', false],
- editType: 'modebar',
-
- },
- hoverdistance: {
- valType: 'integer',
- min: -1,
- dflt: 20,
-
- editType: 'none',
-
- },
- spikedistance: {
- valType: 'integer',
- min: -1,
- dflt: 20,
-
- editType: 'none',
-
- },
- hoverlabel: {
- bgcolor: {
- valType: 'color',
-
- editType: 'none',
-
- },
- bordercolor: {
- valType: 'color',
-
- editType: 'none',
-
- },
- font: fontAttrs,
- namelength: {
- valType: 'integer',
- min: -1,
- dflt: 15,
-
- editType: 'none',
-
- },
- editType: 'none'
- },
- selectdirection: {
- valType: 'enumerated',
-
- values: ['h', 'v', 'd', 'any'],
- dflt: 'any',
-
- editType: 'none'
- }
- };
-
- },{"../../plots/font_attributes":239,"./constants":84}],92:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var layoutAttributes = _dereq_('./layout_attributes');
-
- module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
- function coerce(attr, dflt) {
- return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
- }
-
- var clickmode = coerce('clickmode');
-
- var dragMode = coerce('dragmode');
- if(dragMode === 'select') coerce('selectdirection');
-
- var hovermodeDflt;
- if(layoutOut._has('cartesian')) {
- if(clickmode.indexOf('select') > -1) {
- hovermodeDflt = 'closest';
- } else {
- // flag for 'horizontal' plots:
- // determines the state of the mode bar 'compare' hovermode button
- layoutOut._isHoriz = isHoriz(fullData);
- hovermodeDflt = layoutOut._isHoriz ? 'y' : 'x';
- }
- }
- else hovermodeDflt = 'closest';
-
- var hoverMode = coerce('hovermode', hovermodeDflt);
- if(hoverMode) {
- coerce('hoverdistance');
- coerce('spikedistance');
- }
-
- // if only mapbox or geo subplots is present on graph,
- // reset 'zoom' dragmode to 'pan' until 'zoom' is implemented,
- // so that the correct modebar button is active
- var hasMapbox = layoutOut._has('mapbox');
- var hasGeo = layoutOut._has('geo');
- var len = layoutOut._basePlotModules.length;
-
- if(layoutOut.dragmode === 'zoom' && (
- ((hasMapbox || hasGeo) && len === 1) ||
- (hasMapbox && hasGeo && len === 2)
- )) {
- layoutOut.dragmode = 'pan';
- }
- };
-
- function isHoriz(fullData) {
- var out = true;
-
- for(var i = 0; i < fullData.length; i++) {
- var trace = fullData[i];
-
- if(trace.orientation !== 'h') {
- out = false;
- break;
- }
- }
-
- return out;
- }
-
- },{"../../lib":168,"./layout_attributes":91}],93:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
- var layoutAttributes = _dereq_('./layout_attributes');
-
- module.exports = function supplyLayoutGlobalDefaults(layoutIn, layoutOut) {
- function coerce(attr, dflt) {
- return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
- }
-
- handleHoverLabelDefaults(layoutIn, layoutOut, coerce);
- };
-
- },{"../../lib":168,"./hoverlabel_defaults":88,"./layout_attributes":91}],94:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var counterRegex = _dereq_('../../lib/regex').counter;
- var domainAttrs = _dereq_('../../plots/domain').attributes;
- var cartesianIdRegex = _dereq_('../../plots/cartesian/constants').idRegex;
- var Template = _dereq_('../../plot_api/plot_template');
-
- var gridAttrs = {
- rows: {
- valType: 'integer',
- min: 1,
-
- editType: 'plot',
-
- },
- roworder: {
- valType: 'enumerated',
- values: ['top to bottom', 'bottom to top'],
- dflt: 'top to bottom',
-
- editType: 'plot',
-
- },
- columns: {
- valType: 'integer',
- min: 1,
-
- editType: 'plot',
-
- },
- subplots: {
- valType: 'info_array',
- freeLength: true,
- dimensions: 2,
- items: {valType: 'enumerated', values: [counterRegex('xy').toString(), ''], editType: 'plot'},
-
- editType: 'plot',
-
- },
- xaxes: {
- valType: 'info_array',
- freeLength: true,
- items: {valType: 'enumerated', values: [cartesianIdRegex.x.toString(), ''], editType: 'plot'},
-
- editType: 'plot',
-
- },
- yaxes: {
- valType: 'info_array',
- freeLength: true,
- items: {valType: 'enumerated', values: [cartesianIdRegex.y.toString(), ''], editType: 'plot'},
-
- editType: 'plot',
-
- },
- pattern: {
- valType: 'enumerated',
- values: ['independent', 'coupled'],
- dflt: 'coupled',
-
- editType: 'plot',
-
- },
- xgap: {
- valType: 'number',
- min: 0,
- max: 1,
-
- editType: 'plot',
-
- },
- ygap: {
- valType: 'number',
- min: 0,
- max: 1,
-
- editType: 'plot',
-
- },
- domain: domainAttrs({name: 'grid', editType: 'plot', noGridCell: true}, {
-
- }),
- xside: {
- valType: 'enumerated',
- values: ['bottom', 'bottom plot', 'top plot', 'top'],
- dflt: 'bottom plot',
-
- editType: 'plot',
-
- },
- yside: {
- valType: 'enumerated',
- values: ['left', 'left plot', 'right plot', 'right'],
- dflt: 'left plot',
-
- editType: 'plot',
-
- },
- editType: 'plot'
- };
-
- function getAxes(layout, grid, axLetter) {
- var gridVal = grid[axLetter + 'axes'];
- var splomVal = Object.keys((layout._splomAxes || {})[axLetter] || {});
-
- if(Array.isArray(gridVal)) return gridVal;
- if(splomVal.length) return splomVal;
- }
-
- // the shape of the grid - this needs to be done BEFORE supplyDataDefaults
- // so that non-subplot traces can place themselves in the grid
- function sizeDefaults(layoutIn, layoutOut) {
- var gridIn = layoutIn.grid || {};
- var xAxes = getAxes(layoutOut, gridIn, 'x');
- var yAxes = getAxes(layoutOut, gridIn, 'y');
-
- if(!layoutIn.grid && !xAxes && !yAxes) return;
-
- var hasSubplotGrid = Array.isArray(gridIn.subplots) && Array.isArray(gridIn.subplots[0]);
- var hasXaxes = Array.isArray(xAxes);
- var hasYaxes = Array.isArray(yAxes);
- var isSplomGenerated = (
- hasXaxes && xAxes !== gridIn.xaxes &&
- hasYaxes && yAxes !== gridIn.yaxes
- );
-
- var dfltRows, dfltColumns;
-
- if(hasSubplotGrid) {
- dfltRows = gridIn.subplots.length;
- dfltColumns = gridIn.subplots[0].length;
- }
- else {
- if(hasYaxes) dfltRows = yAxes.length;
- if(hasXaxes) dfltColumns = xAxes.length;
- }
-
- var gridOut = Template.newContainer(layoutOut, 'grid');
-
- function coerce(attr, dflt) {
- return Lib.coerce(gridIn, gridOut, gridAttrs, attr, dflt);
- }
-
- var rows = coerce('rows', dfltRows);
- var columns = coerce('columns', dfltColumns);
-
- if(!(rows * columns > 1)) {
- delete layoutOut.grid;
- return;
- }
-
- if(!hasSubplotGrid && !hasXaxes && !hasYaxes) {
- var useDefaultSubplots = coerce('pattern') === 'independent';
- if(useDefaultSubplots) hasSubplotGrid = true;
- }
- gridOut._hasSubplotGrid = hasSubplotGrid;
-
- var rowOrder = coerce('roworder');
- var reversed = rowOrder === 'top to bottom';
-
- var dfltGapX = hasSubplotGrid ? 0.2 : 0.1;
- var dfltGapY = hasSubplotGrid ? 0.3 : 0.1;
-
- var dfltSideX, dfltSideY;
- if(isSplomGenerated && layoutOut._splomGridDflt) {
- dfltSideX = layoutOut._splomGridDflt.xside;
- dfltSideY = layoutOut._splomGridDflt.yside;
- }
-
- gridOut._domains = {
- x: fillGridPositions('x', coerce, dfltGapX, dfltSideX, columns),
- y: fillGridPositions('y', coerce, dfltGapY, dfltSideY, rows, reversed)
- };
- }
-
- // coerce x or y sizing attributes and return an array of domains for this direction
- function fillGridPositions(axLetter, coerce, dfltGap, dfltSide, len, reversed) {
- var dirGap = coerce(axLetter + 'gap', dfltGap);
- var domain = coerce('domain.' + axLetter);
- coerce(axLetter + 'side', dfltSide);
-
- var out = new Array(len);
- var start = domain[0];
- var step = (domain[1] - start) / (len - dirGap);
- var cellDomain = step * (1 - dirGap);
- for(var i = 0; i < len; i++) {
- var cellStart = start + step * i;
- out[reversed ? (len - 1 - i) : i] = [cellStart, cellStart + cellDomain];
- }
- return out;
- }
-
- // the (cartesian) contents of the grid - this needs to happen AFTER supplyDataDefaults
- // so that we know what cartesian subplots are available
- function contentDefaults(layoutIn, layoutOut) {
- var gridOut = layoutOut.grid;
- // make sure we got to the end of handleGridSizing
- if(!gridOut || !gridOut._domains) return;
-
- var gridIn = layoutIn.grid || {};
- var subplots = layoutOut._subplots;
- var hasSubplotGrid = gridOut._hasSubplotGrid;
- var rows = gridOut.rows;
- var columns = gridOut.columns;
- var useDefaultSubplots = gridOut.pattern === 'independent';
-
- var i, j, xId, yId, subplotId, subplotsOut, yPos;
-
- var axisMap = gridOut._axisMap = {};
-
- if(hasSubplotGrid) {
- var subplotsIn = gridIn.subplots || [];
- subplotsOut = gridOut.subplots = new Array(rows);
- var index = 1;
-
- for(i = 0; i < rows; i++) {
- var rowOut = subplotsOut[i] = new Array(columns);
- var rowIn = subplotsIn[i] || [];
- for(j = 0; j < columns; j++) {
- if(useDefaultSubplots) {
- subplotId = (index === 1) ? 'xy' : ('x' + index + 'y' + index);
- index++;
- }
- else subplotId = rowIn[j];
-
- rowOut[j] = '';
-
- if(subplots.cartesian.indexOf(subplotId) !== -1) {
- yPos = subplotId.indexOf('y');
- xId = subplotId.slice(0, yPos);
- yId = subplotId.slice(yPos);
- if((axisMap[xId] !== undefined && axisMap[xId] !== j) ||
- (axisMap[yId] !== undefined && axisMap[yId] !== i)
- ) {
- continue;
- }
-
- rowOut[j] = subplotId;
- axisMap[xId] = j;
- axisMap[yId] = i;
- }
- }
- }
- }
- else {
- var xAxes = getAxes(layoutOut, gridIn, 'x');
- var yAxes = getAxes(layoutOut, gridIn, 'y');
- gridOut.xaxes = fillGridAxes(xAxes, subplots.xaxis, columns, axisMap, 'x');
- gridOut.yaxes = fillGridAxes(yAxes, subplots.yaxis, rows, axisMap, 'y');
- }
-
- var anchors = gridOut._anchors = {};
- var reversed = gridOut.roworder === 'top to bottom';
-
- for(var axisId in axisMap) {
- var axLetter = axisId.charAt(0);
- var side = gridOut[axLetter + 'side'];
-
- var i0, inc, iFinal;
-
- if(side.length < 8) {
- // grid edge - ie not "* plot" - make these as free axes
- // since we're not guaranteed to have a subplot there at all
- anchors[axisId] = 'free';
- }
- else if(axLetter === 'x') {
- if((side.charAt(0) === 't') === reversed) {
- i0 = 0;
- inc = 1;
- iFinal = rows;
- }
- else {
- i0 = rows - 1;
- inc = -1;
- iFinal = -1;
- }
- if(hasSubplotGrid) {
- var column = axisMap[axisId];
- for(i = i0; i !== iFinal; i += inc) {
- subplotId = subplotsOut[i][column];
- if(!subplotId) continue;
- yPos = subplotId.indexOf('y');
- if(subplotId.slice(0, yPos) === axisId) {
- anchors[axisId] = subplotId.slice(yPos);
- break;
- }
- }
- }
- else {
- for(i = i0; i !== iFinal; i += inc) {
- yId = gridOut.yaxes[i];
- if(subplots.cartesian.indexOf(axisId + yId) !== -1) {
- anchors[axisId] = yId;
- break;
- }
- }
- }
- }
- else {
- if((side.charAt(0) === 'l')) {
- i0 = 0;
- inc = 1;
- iFinal = columns;
- }
- else {
- i0 = columns - 1;
- inc = -1;
- iFinal = -1;
- }
- if(hasSubplotGrid) {
- var row = axisMap[axisId];
- for(i = i0; i !== iFinal; i += inc) {
- subplotId = subplotsOut[row][i];
- if(!subplotId) continue;
- yPos = subplotId.indexOf('y');
- if(subplotId.slice(yPos) === axisId) {
- anchors[axisId] = subplotId.slice(0, yPos);
- break;
- }
- }
- }
- else {
- for(i = i0; i !== iFinal; i += inc) {
- xId = gridOut.xaxes[i];
- if(subplots.cartesian.indexOf(xId + axisId) !== -1) {
- anchors[axisId] = xId;
- break;
- }
- }
- }
- }
- }
- }
-
- function fillGridAxes(axesIn, axesAllowed, len, axisMap, axLetter) {
- var out = new Array(len);
- var i;
-
- function fillOneAxis(i, axisId) {
- if(axesAllowed.indexOf(axisId) !== -1 && axisMap[axisId] === undefined) {
- out[i] = axisId;
- axisMap[axisId] = i;
- }
- else out[i] = '';
- }
-
- if(Array.isArray(axesIn)) {
- for(i = 0; i < len; i++) {
- fillOneAxis(i, axesIn[i]);
- }
- }
- else {
- // default axis list is the first `len` axis ids
- fillOneAxis(0, axLetter);
- for(i = 1; i < len; i++) {
- fillOneAxis(i, axLetter + (i + 1));
- }
- }
-
- return out;
- }
-
- module.exports = {
- moduleType: 'component',
- name: 'grid',
-
- schema: {
- layout: {grid: gridAttrs}
- },
-
- layoutAttributes: gridAttrs,
- sizeDefaults: sizeDefaults,
- contentDefaults: contentDefaults
- };
-
- },{"../../lib":168,"../../lib/regex":183,"../../plot_api/plot_template":202,"../../plots/cartesian/constants":218,"../../plots/domain":238}],95:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var cartesianConstants = _dereq_('../../plots/cartesian/constants');
- var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
-
-
- module.exports = templatedArray('image', {
- visible: {
- valType: 'boolean',
-
- dflt: true,
- editType: 'arraydraw',
-
- },
-
- source: {
- valType: 'string',
-
- editType: 'arraydraw',
-
- },
-
- layer: {
- valType: 'enumerated',
- values: ['below', 'above'],
- dflt: 'above',
-
- editType: 'arraydraw',
-
- },
-
- sizex: {
- valType: 'number',
-
- dflt: 0,
- editType: 'arraydraw',
-
- },
-
- sizey: {
- valType: 'number',
-
- dflt: 0,
- editType: 'arraydraw',
-
- },
-
- sizing: {
- valType: 'enumerated',
- values: ['fill', 'contain', 'stretch'],
- dflt: 'contain',
-
- editType: 'arraydraw',
-
- },
-
- opacity: {
- valType: 'number',
-
- min: 0,
- max: 1,
- dflt: 1,
- editType: 'arraydraw',
-
- },
-
- x: {
- valType: 'any',
-
- dflt: 0,
- editType: 'arraydraw',
-
- },
-
- y: {
- valType: 'any',
-
- dflt: 0,
- editType: 'arraydraw',
-
- },
-
- xanchor: {
- valType: 'enumerated',
- values: ['left', 'center', 'right'],
- dflt: 'left',
-
- editType: 'arraydraw',
-
- },
-
- yanchor: {
- valType: 'enumerated',
- values: ['top', 'middle', 'bottom'],
- dflt: 'top',
-
- editType: 'arraydraw',
-
- },
-
- xref: {
- valType: 'enumerated',
- values: [
- 'paper',
- cartesianConstants.idRegex.x.toString()
- ],
- dflt: 'paper',
-
- editType: 'arraydraw',
-
- },
-
- yref: {
- valType: 'enumerated',
- values: [
- 'paper',
- cartesianConstants.idRegex.y.toString()
- ],
- dflt: 'paper',
-
- editType: 'arraydraw',
-
- },
- editType: 'arraydraw'
- });
-
- },{"../../plot_api/plot_template":202,"../../plots/cartesian/constants":218}],96:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var toLogRange = _dereq_('../../lib/to_log_range');
-
- /*
- * convertCoords: when converting an axis between log and linear
- * you need to alter any images on that axis to keep them
- * pointing at the same data point.
- * In v2.0 this will become obsolete (or perhaps size will still need conversion?)
- * we convert size by declaring that the maximum extent *in data units* should be
- * the same, assuming the image is anchored by its center (could remove that restriction
- * if we think it's important) even though the actual left and right values will not be
- * quite the same since the scale becomes nonlinear (and central anchor means the pixel
- * center of the image, not the data units center)
- *
- * gd: the plot div
- * ax: the axis being changed
- * newType: the type it's getting
- * doExtra: function(attr, val) from inside relayout that sets the attribute.
- * Use this to make the changes as it's aware if any other changes in the
- * same relayout call should override this conversion.
- */
- module.exports = function convertCoords(gd, ax, newType, doExtra) {
- ax = ax || {};
-
- var toLog = (newType === 'log') && (ax.type === 'linear');
- var fromLog = (newType === 'linear') && (ax.type === 'log');
-
- if(!(toLog || fromLog)) return;
-
- var images = gd._fullLayout.images;
- var axLetter = ax._id.charAt(0);
- var image;
- var attrPrefix;
-
- for(var i = 0; i < images.length; i++) {
- image = images[i];
- attrPrefix = 'images[' + i + '].';
-
- if(image[axLetter + 'ref'] === ax._id) {
- var currentPos = image[axLetter];
- var currentSize = image['size' + axLetter];
- var newPos = null;
- var newSize = null;
-
- if(toLog) {
- newPos = toLogRange(currentPos, ax.range);
-
- // this is the inverse of the conversion we do in fromLog below
- // so that the conversion is reversible (notice the fromLog conversion
- // is like sinh, and this one looks like arcsinh)
- var dx = currentSize / Math.pow(10, newPos) / 2;
- newSize = 2 * Math.log(dx + Math.sqrt(1 + dx * dx)) / Math.LN10;
- }
- else {
- newPos = Math.pow(10, currentPos);
- newSize = newPos * (Math.pow(10, currentSize / 2) - Math.pow(10, -currentSize / 2));
- }
-
- // if conversion failed, delete the value so it can get a default later on
- if(!isNumeric(newPos)) {
- newPos = null;
- newSize = null;
- }
- else if(!isNumeric(newSize)) newSize = null;
-
- doExtra(attrPrefix + axLetter, newPos);
- doExtra(attrPrefix + 'size' + axLetter, newSize);
- }
- }
- };
-
- },{"../../lib/to_log_range":191,"fast-isnumeric":18}],97:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
-
- var attributes = _dereq_('./attributes');
- var name = 'images';
-
- module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
- var opts = {
- name: name,
- handleItemDefaults: imageDefaults
- };
-
- handleArrayContainerDefaults(layoutIn, layoutOut, opts);
- };
-
-
- function imageDefaults(imageIn, imageOut, fullLayout) {
-
- function coerce(attr, dflt) {
- return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
- }
-
- var source = coerce('source');
- var visible = coerce('visible', !!source);
-
- if(!visible) return imageOut;
-
- coerce('layer');
- coerce('xanchor');
- coerce('yanchor');
- coerce('sizex');
- coerce('sizey');
- coerce('sizing');
- coerce('opacity');
-
- var gdMock = { _fullLayout: fullLayout };
- var axLetters = ['x', 'y'];
-
- for(var i = 0; i < 2; i++) {
- // 'paper' is the fallback axref
- var axLetter = axLetters[i];
- var axRef = Axes.coerceRef(imageIn, imageOut, gdMock, axLetter, 'paper');
-
- if(axRef !== 'paper') {
- var ax = Axes.getFromId(gdMock, axRef);
- ax._imgIndices.push(imageOut._index);
- }
-
- Axes.coercePosition(imageOut, gdMock, coerce, axRef, axLetter, 0);
- }
-
- return imageOut;
- }
-
- },{"../../lib":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"./attributes":95}],98:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var Drawing = _dereq_('../drawing');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
-
- module.exports = function draw(gd) {
- var fullLayout = gd._fullLayout;
- var imageDataAbove = [];
- var imageDataSubplot = {};
- var imageDataBelow = [];
- var subplot;
- var i;
-
- // Sort into top, subplot, and bottom layers
- for(i = 0; i < fullLayout.images.length; i++) {
- var img = fullLayout.images[i];
-
- if(img.visible) {
- if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
- subplot = img.xref + img.yref;
-
- var plotinfo = fullLayout._plots[subplot];
-
- if(!plotinfo) {
- // Fall back to _imageLowerLayer in case the requested subplot doesn't exist.
- // This can happen if you reference the image to an x / y axis combination
- // that doesn't have any data on it (and layer is below)
- imageDataBelow.push(img);
- continue;
- }
-
- if(plotinfo.mainplot) {
- subplot = plotinfo.mainplot.id;
- }
-
- if(!imageDataSubplot[subplot]) {
- imageDataSubplot[subplot] = [];
- }
- imageDataSubplot[subplot].push(img);
- } else if(img.layer === 'above') {
- imageDataAbove.push(img);
- } else {
- imageDataBelow.push(img);
- }
- }
- }
-
-
- var anchors = {
- x: {
- left: { sizing: 'xMin', offset: 0 },
- center: { sizing: 'xMid', offset: -1 / 2 },
- right: { sizing: 'xMax', offset: -1 }
- },
- y: {
- top: { sizing: 'YMin', offset: 0 },
- middle: { sizing: 'YMid', offset: -1 / 2 },
- bottom: { sizing: 'YMax', offset: -1 }
- }
- };
-
-
- // Images must be converted to dataURL's for exporting.
- function setImage(d) {
- var thisImage = d3.select(this);
-
- if(this.img && this.img.src === d.source) {
- return;
- }
-
- thisImage.attr('xmlns', xmlnsNamespaces.svg);
-
- var imagePromise = new Promise(function(resolve) {
-
- var img = new Image();
- this.img = img;
-
- // If not set, a `tainted canvas` error is thrown
- img.setAttribute('crossOrigin', 'anonymous');
- img.onerror = errorHandler;
- img.onload = function() {
- var canvas = document.createElement('canvas');
- canvas.width = this.width;
- canvas.height = this.height;
-
- var ctx = canvas.getContext('2d');
- ctx.drawImage(this, 0, 0);
-
- var dataURL = canvas.toDataURL('image/png');
-
- thisImage.attr('xlink:href', dataURL);
-
- // resolve promise in onload handler instead of on 'load' to support IE11
- // see https://github.com/plotly/plotly.js/issues/1685
- // for more details
- resolve();
- };
-
-
- thisImage.on('error', errorHandler);
-
- img.src = d.source;
-
- function errorHandler() {
- thisImage.remove();
- resolve();
- }
- }.bind(this));
-
- gd._promises.push(imagePromise);
- }
-
- function applyAttributes(d) {
- var thisImage = d3.select(this);
-
- // Axes if specified
- var xa = Axes.getFromId(gd, d.xref);
- var ya = Axes.getFromId(gd, d.yref);
-
- var size = fullLayout._size;
- var width = xa ? Math.abs(xa.l2p(d.sizex) - xa.l2p(0)) : d.sizex * size.w;
- var height = ya ? Math.abs(ya.l2p(d.sizey) - ya.l2p(0)) : d.sizey * size.h;
-
- // Offsets for anchor positioning
- var xOffset = width * anchors.x[d.xanchor].offset;
- var yOffset = height * anchors.y[d.yanchor].offset;
-
- var sizing = anchors.x[d.xanchor].sizing + anchors.y[d.yanchor].sizing;
-
- // Final positions
- var xPos = (xa ? xa.r2p(d.x) + xa._offset : d.x * size.w + size.l) + xOffset;
- var yPos = (ya ? ya.r2p(d.y) + ya._offset : size.h - d.y * size.h + size.t) + yOffset;
-
- // Construct the proper aspectRatio attribute
- switch(d.sizing) {
- case 'fill':
- sizing += ' slice';
- break;
-
- case 'stretch':
- sizing = 'none';
- break;
- }
-
- thisImage.attr({
- x: xPos,
- y: yPos,
- width: width,
- height: height,
- preserveAspectRatio: sizing,
- opacity: d.opacity
- });
-
-
- // Set proper clipping on images
- var xId = xa ? xa._id : '';
- var yId = ya ? ya._id : '';
- var clipAxes = xId + yId;
-
- Drawing.setClipUrl(
- thisImage,
- clipAxes ? ('clip' + fullLayout._uid + clipAxes) : null,
- gd
- );
- }
-
- var imagesBelow = fullLayout._imageLowerLayer.selectAll('image')
- .data(imageDataBelow);
- var imagesAbove = fullLayout._imageUpperLayer.selectAll('image')
- .data(imageDataAbove);
-
- imagesBelow.enter().append('image');
- imagesAbove.enter().append('image');
-
- imagesBelow.exit().remove();
- imagesAbove.exit().remove();
-
- imagesBelow.each(function(d) {
- setImage.bind(this)(d);
- applyAttributes.bind(this)(d);
- });
- imagesAbove.each(function(d) {
- setImage.bind(this)(d);
- applyAttributes.bind(this)(d);
- });
-
- var allSubplots = Object.keys(fullLayout._plots);
- for(i = 0; i < allSubplots.length; i++) {
- subplot = allSubplots[i];
- var subplotObj = fullLayout._plots[subplot];
-
- // filter out overlaid plots (which havd their images on the main plot)
- // and gl2d plots (which don't support below images, at least not yet)
- if(!subplotObj.imagelayer) continue;
-
- var imagesOnSubplot = subplotObj.imagelayer.selectAll('image')
- // even if there are no images on this subplot, we need to run
- // enter and exit in case there were previously
- .data(imageDataSubplot[subplot] || []);
-
- imagesOnSubplot.enter().append('image');
- imagesOnSubplot.exit().remove();
-
- imagesOnSubplot.each(function(d) {
- setImage.bind(this)(d);
- applyAttributes.bind(this)(d);
- });
- }
- };
-
- },{"../../constants/xmlns_namespaces":150,"../../plots/cartesian/axes":212,"../drawing":72,"d3":16}],99:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- moduleType: 'component',
- name: 'images',
-
- layoutAttributes: _dereq_('./attributes'),
- supplyLayoutDefaults: _dereq_('./defaults'),
- includeBasePlot: _dereq_('../../plots/cartesian/include_components')('images'),
-
- draw: _dereq_('./draw'),
-
- convertCoords: _dereq_('./convert_coords')
- };
-
- },{"../../plots/cartesian/include_components":223,"./attributes":95,"./convert_coords":96,"./defaults":97,"./draw":98}],100:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var fontAttrs = _dereq_('../../plots/font_attributes');
- var colorAttrs = _dereq_('../color/attributes');
-
-
- module.exports = {
- bgcolor: {
- valType: 'color',
-
- editType: 'legend',
-
- },
- bordercolor: {
- valType: 'color',
- dflt: colorAttrs.defaultLine,
-
- editType: 'legend',
-
- },
- borderwidth: {
- valType: 'number',
- min: 0,
- dflt: 0,
-
- editType: 'legend',
-
- },
- font: fontAttrs({
- editType: 'legend',
-
- }),
- orientation: {
- valType: 'enumerated',
- values: ['v', 'h'],
- dflt: 'v',
-
- editType: 'legend',
-
- },
- traceorder: {
- valType: 'flaglist',
- flags: ['reversed', 'grouped'],
- extras: ['normal'],
-
- editType: 'legend',
-
- },
- tracegroupgap: {
- valType: 'number',
- min: 0,
- dflt: 10,
-
- editType: 'legend',
-
- },
- x: {
- valType: 'number',
- min: -2,
- max: 3,
- dflt: 1.02,
-
- editType: 'legend',
-
- },
- xanchor: {
- valType: 'enumerated',
- values: ['auto', 'left', 'center', 'right'],
- dflt: 'left',
-
- editType: 'legend',
-
- },
- y: {
- valType: 'number',
- min: -2,
- max: 3,
- dflt: 1,
-
- editType: 'legend',
-
- },
- yanchor: {
- valType: 'enumerated',
- values: ['auto', 'top', 'middle', 'bottom'],
- dflt: 'auto',
-
- editType: 'legend',
-
- },
- uirevision: {
- valType: 'any',
-
- editType: 'none',
-
- },
- valign: {
- valType: 'enumerated',
- values: ['top', 'middle', 'bottom'],
- dflt: 'middle',
-
- editType: 'legend',
-
- },
- editType: 'legend'
- };
-
- },{"../../plots/font_attributes":239,"../color/attributes":50}],101:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- scrollBarWidth: 6,
- scrollBarMinHeight: 20,
- scrollBarColor: '#808BA4',
- scrollBarMargin: 4,
- textOffsetX: 40
- };
-
- },{}],102:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var Template = _dereq_('../../plot_api/plot_template');
-
- var attributes = _dereq_('./attributes');
- var basePlotLayoutAttributes = _dereq_('../../plots/layout_attributes');
- var helpers = _dereq_('./helpers');
-
-
- module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
- var containerIn = layoutIn.legend || {};
-
- var legendTraceCount = 0;
- var legendReallyHasATrace = false;
- var defaultOrder = 'normal';
-
- var defaultX, defaultY, defaultXAnchor, defaultYAnchor;
-
- for(var i = 0; i < fullData.length; i++) {
- var trace = fullData[i];
-
- if(!trace.visible) continue;
-
- // Note that we explicitly count any trace that is either shown or
- // *would* be shown by default, toward the two traces you need to
- // ensure the legend is shown by default, because this can still help
- // disambiguate.
- if(trace.showlegend || trace._dfltShowLegend) {
- legendTraceCount++;
- if(trace.showlegend) {
- legendReallyHasATrace = true;
- // Always show the legend by default if there's a pie,
- // or if there's only one trace but it's explicitly shown
- if(Registry.traceIs(trace, 'pie') ||
- trace._input.showlegend === true
- ) {
- legendTraceCount++;
- }
- }
- }
-
- if((Registry.traceIs(trace, 'bar') && layoutOut.barmode === 'stack') ||
- ['tonextx', 'tonexty'].indexOf(trace.fill) !== -1) {
- defaultOrder = helpers.isGrouped({traceorder: defaultOrder}) ?
- 'grouped+reversed' : 'reversed';
- }
-
- if(trace.legendgroup !== undefined && trace.legendgroup !== '') {
- defaultOrder = helpers.isReversed({traceorder: defaultOrder}) ?
- 'reversed+grouped' : 'grouped';
- }
- }
-
- var showLegend = Lib.coerce(layoutIn, layoutOut,
- basePlotLayoutAttributes, 'showlegend',
- legendReallyHasATrace && legendTraceCount > 1);
-
- if(showLegend === false && !containerIn.uirevision) return;
-
- var containerOut = Template.newContainer(layoutOut, 'legend');
-
- function coerce(attr, dflt) {
- return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
- }
-
- coerce('uirevision', layoutOut.uirevision);
-
- if(showLegend === false) return;
-
- coerce('bgcolor', layoutOut.paper_bgcolor);
- coerce('bordercolor');
- coerce('borderwidth');
- Lib.coerceFont(coerce, 'font', layoutOut.font);
-
- coerce('orientation');
- if(containerOut.orientation === 'h') {
- var xaxis = layoutIn.xaxis;
- if(Registry.getComponentMethod('rangeslider', 'isVisible')(xaxis)) {
- defaultX = 0;
- defaultXAnchor = 'left';
- defaultY = 1.1;
- defaultYAnchor = 'bottom';
- }
- else {
- defaultX = 0;
- defaultXAnchor = 'left';
- defaultY = -0.1;
- defaultYAnchor = 'top';
- }
- }
-
- coerce('traceorder', defaultOrder);
- if(helpers.isGrouped(layoutOut.legend)) coerce('tracegroupgap');
-
- coerce('x', defaultX);
- coerce('xanchor', defaultXAnchor);
- coerce('y', defaultY);
- coerce('yanchor', defaultYAnchor);
- coerce('valign');
- Lib.noneOrAll(containerIn, containerOut, ['x', 'y']);
- };
-
- },{"../../lib":168,"../../plot_api/plot_template":202,"../../plots/layout_attributes":243,"../../registry":257,"./attributes":100,"./helpers":106}],103:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Lib = _dereq_('../../lib');
- var Plots = _dereq_('../../plots/plots');
- var Registry = _dereq_('../../registry');
- var Events = _dereq_('../../lib/events');
- var dragElement = _dereq_('../dragelement');
- var Drawing = _dereq_('../drawing');
- var Color = _dereq_('../color');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var handleClick = _dereq_('./handle_click');
-
- var constants = _dereq_('./constants');
- var interactConstants = _dereq_('../../constants/interactions');
- var alignmentConstants = _dereq_('../../constants/alignment');
- var LINE_SPACING = alignmentConstants.LINE_SPACING;
- var FROM_TL = alignmentConstants.FROM_TL;
- var FROM_BR = alignmentConstants.FROM_BR;
-
- var getLegendData = _dereq_('./get_legend_data');
- var style = _dereq_('./style');
- var helpers = _dereq_('./helpers');
-
- var DBLCLICKDELAY = interactConstants.DBLCLICKDELAY;
-
- module.exports = function draw(gd) {
- var fullLayout = gd._fullLayout;
- var clipId = 'legend' + fullLayout._uid;
-
- if(!fullLayout._infolayer || !gd.calcdata) return;
-
- if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0;
-
- var opts = fullLayout.legend;
- var legendData = fullLayout.showlegend && getLegendData(gd.calcdata, opts);
- var hiddenSlices = fullLayout.hiddenlabels || [];
-
- if(!fullLayout.showlegend || !legendData.length) {
- fullLayout._infolayer.selectAll('.legend').remove();
- fullLayout._topdefs.select('#' + clipId).remove();
-
- Plots.autoMargin(gd, 'legend');
- return;
- }
-
- var maxLength = 0;
- for(var i = 0; i < legendData.length; i++) {
- for(var j = 0; j < legendData[i].length; j++) {
- var item = legendData[i][j][0];
- var trace = item.trace;
- var isPie = Registry.traceIs(trace, 'pie');
- var name = isPie ? item.label : trace.name;
- maxLength = Math.max(maxLength, name && name.length || 0);
- }
- }
-
- var firstRender = false;
- var legend = Lib.ensureSingle(fullLayout._infolayer, 'g', 'legend', function(s) {
- s.attr('pointer-events', 'all');
- firstRender = true;
- });
-
- var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', clipId, function(s) {
- s.append('rect');
- });
-
- var bg = Lib.ensureSingle(legend, 'rect', 'bg', function(s) {
- s.attr('shape-rendering', 'crispEdges');
- });
-
- bg.call(Color.stroke, opts.bordercolor)
- .call(Color.fill, opts.bgcolor)
- .style('stroke-width', opts.borderwidth + 'px');
-
- var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox');
-
- var scrollBar = Lib.ensureSingle(legend, 'rect', 'scrollbar', function(s) {
- s.attr({
- rx: 20,
- ry: 3,
- width: 0,
- height: 0
- })
- .call(Color.fill, '#808BA4');
- });
-
- var groups = scrollBox.selectAll('g.groups')
- .data(legendData);
-
- groups.enter().append('g')
- .attr('class', 'groups');
-
- groups.exit().remove();
-
- var traces = groups.selectAll('g.traces')
- .data(Lib.identity);
-
- traces.enter().append('g').attr('class', 'traces');
- traces.exit().remove();
-
- traces.style('opacity', function(d) {
- var trace = d[0].trace;
- if(Registry.traceIs(trace, 'pie')) {
- return hiddenSlices.indexOf(d[0].label) !== -1 ? 0.5 : 1;
- } else {
- return trace.visible === 'legendonly' ? 0.5 : 1;
- }
- })
- .each(function() {
- d3.select(this)
- .call(drawTexts, gd, maxLength)
- .call(setupTraceToggle, gd);
- })
- .call(style, gd);
-
- Lib.syncOrAsync([Plots.previousPromises,
- function() {
- if(firstRender) {
- computeLegendDimensions(gd, groups, traces);
- expandMargin(gd);
- }
-
- // Position and size the legend
- var lxMin = 0;
- var lxMax = fullLayout.width;
- var lyMin = 0;
- var lyMax = fullLayout.height;
-
- computeLegendDimensions(gd, groups, traces);
-
- if(opts._height > lyMax) {
- // If the legend doesn't fit in the plot area,
- // do not expand the vertical margins.
- expandHorizontalMargin(gd);
- } else {
- expandMargin(gd);
- }
-
- // Scroll section must be executed after repositionLegend.
- // It requires the legend width, height, x and y to position the scrollbox
- // and these values are mutated in repositionLegend.
- var gs = fullLayout._size;
- var lx = gs.l + gs.w * opts.x;
- var ly = gs.t + gs.h * (1 - opts.y);
-
- if(Lib.isRightAnchor(opts)) {
- lx -= opts._width;
- }
- else if(Lib.isCenterAnchor(opts)) {
- lx -= opts._width / 2;
- }
-
- if(Lib.isBottomAnchor(opts)) {
- ly -= opts._height;
- }
- else if(Lib.isMiddleAnchor(opts)) {
- ly -= opts._height / 2;
- }
-
- // Make sure the legend left and right sides are visible
- var legendWidth = opts._width;
- var legendWidthMax = gs.w;
-
- if(legendWidth > legendWidthMax) {
- lx = gs.l;
- legendWidth = legendWidthMax;
- }
- else {
- if(lx + legendWidth > lxMax) lx = lxMax - legendWidth;
- if(lx < lxMin) lx = lxMin;
- legendWidth = Math.min(lxMax - lx, opts._width);
- }
-
- // Make sure the legend top and bottom are visible
- // (legends with a scroll bar are not allowed to stretch beyond the extended
- // margins)
- var legendHeight = opts._height;
- var legendHeightMax = gs.h;
-
- if(legendHeight > legendHeightMax) {
- ly = gs.t;
- legendHeight = legendHeightMax;
- }
- else {
- if(ly + legendHeight > lyMax) ly = lyMax - legendHeight;
- if(ly < lyMin) ly = lyMin;
- legendHeight = Math.min(lyMax - ly, opts._height);
- }
-
- // Set size and position of all the elements that make up a legend:
- // legend, background and border, scroll box and scroll bar
- Drawing.setTranslate(legend, lx, ly);
-
- // to be safe, remove previous listeners
- scrollBar.on('.drag', null);
- legend.on('wheel', null);
-
- if(opts._height <= legendHeight || gd._context.staticPlot) {
- // if scrollbar should not be shown.
- bg.attr({
- width: legendWidth - opts.borderwidth,
- height: legendHeight - opts.borderwidth,
- x: opts.borderwidth / 2,
- y: opts.borderwidth / 2
- });
-
- Drawing.setTranslate(scrollBox, 0, 0);
-
- clipPath.select('rect').attr({
- width: legendWidth - 2 * opts.borderwidth,
- height: legendHeight - 2 * opts.borderwidth,
- x: opts.borderwidth,
- y: opts.borderwidth
- });
-
- Drawing.setClipUrl(scrollBox, clipId, gd);
-
- Drawing.setRect(scrollBar, 0, 0, 0, 0);
- delete opts._scrollY;
- }
- else {
- var scrollBarHeight = Math.max(constants.scrollBarMinHeight,
- legendHeight * legendHeight / opts._height);
- var scrollBarYMax = legendHeight -
- scrollBarHeight -
- 2 * constants.scrollBarMargin;
- var scrollBoxYMax = opts._height - legendHeight;
- var scrollRatio = scrollBarYMax / scrollBoxYMax;
-
- var scrollBoxY = Math.min(opts._scrollY || 0, scrollBoxYMax);
-
- // increase the background and clip-path width
- // by the scrollbar width and margin
- bg.attr({
- width: legendWidth -
- 2 * opts.borderwidth +
- constants.scrollBarWidth +
- constants.scrollBarMargin,
- height: legendHeight - opts.borderwidth,
- x: opts.borderwidth / 2,
- y: opts.borderwidth / 2
- });
-
- clipPath.select('rect').attr({
- width: legendWidth -
- 2 * opts.borderwidth +
- constants.scrollBarWidth +
- constants.scrollBarMargin,
- height: legendHeight - 2 * opts.borderwidth,
- x: opts.borderwidth,
- y: opts.borderwidth + scrollBoxY
- });
-
- Drawing.setClipUrl(scrollBox, clipId, gd);
-
- scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
-
- legend.on('wheel', function() {
- scrollBoxY = Lib.constrain(
- opts._scrollY +
- d3.event.deltaY / scrollBarYMax * scrollBoxYMax,
- 0, scrollBoxYMax);
- scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
- if(scrollBoxY !== 0 && scrollBoxY !== scrollBoxYMax) {
- d3.event.preventDefault();
- }
- });
-
- var eventY0, scrollBoxY0;
-
- var drag = d3.behavior.drag()
- .on('dragstart', function() {
- eventY0 = d3.event.sourceEvent.clientY;
- scrollBoxY0 = scrollBoxY;
- })
- .on('drag', function() {
- var e = d3.event.sourceEvent;
- if(e.buttons === 2 || e.ctrlKey) return;
-
- scrollBoxY = Lib.constrain(
- (e.clientY - eventY0) / scrollRatio + scrollBoxY0,
- 0, scrollBoxYMax);
- scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
- });
-
- scrollBar.call(drag);
- }
-
-
- function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) {
- opts._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY;
- Drawing.setTranslate(scrollBox, 0, -scrollBoxY);
-
- Drawing.setRect(
- scrollBar,
- legendWidth,
- constants.scrollBarMargin + scrollBoxY * scrollRatio,
- constants.scrollBarWidth,
- scrollBarHeight
- );
- clipPath.select('rect').attr({
- y: opts.borderwidth + scrollBoxY
- });
- }
-
- if(gd._context.edits.legendPosition) {
- var xf, yf, x0, y0;
-
- legend.classed('cursor-move', true);
-
- dragElement.init({
- element: legend.node(),
- gd: gd,
- prepFn: function() {
- var transform = Drawing.getTranslate(legend);
-
- x0 = transform.x;
- y0 = transform.y;
- },
- moveFn: function(dx, dy) {
- var newX = x0 + dx;
- var newY = y0 + dy;
-
- Drawing.setTranslate(legend, newX, newY);
-
- xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, opts.xanchor);
- yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, opts.yanchor);
- },
- doneFn: function() {
- if(xf !== undefined && yf !== undefined) {
- Registry.call('_guiRelayout', gd, {'legend.x': xf, 'legend.y': yf});
- }
- },
- clickFn: function(numClicks, e) {
- var clickedTrace = fullLayout._infolayer.selectAll('g.traces').filter(function() {
- var bbox = this.getBoundingClientRect();
- return (
- e.clientX >= bbox.left && e.clientX <= bbox.right &&
- e.clientY >= bbox.top && e.clientY <= bbox.bottom
- );
- });
- if(clickedTrace.size() > 0) {
- clickOrDoubleClick(gd, legend, clickedTrace, numClicks, e);
- }
- }
- });
- }
- }], gd);
- };
-
- function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
- var trace = legendItem.data()[0][0].trace;
-
- var evtData = {
- event: evt,
- node: legendItem.node(),
- curveNumber: trace.index,
- expandedIndex: trace._expandedIndex,
- data: gd.data,
- layout: gd.layout,
- frames: gd._transitionData._frames,
- config: gd._context,
- fullData: gd._fullData,
- fullLayout: gd._fullLayout
- };
-
- if(trace._group) {
- evtData.group = trace._group;
- }
- if(trace.type === 'pie') {
- evtData.label = legendItem.datum()[0].label;
- }
-
- var clickVal = Events.triggerHandler(gd, 'plotly_legendclick', evtData);
- if(clickVal === false) return;
-
- if(numClicks === 1) {
- legend._clickTimeout = setTimeout(function() {
- handleClick(legendItem, gd, numClicks);
- }, DBLCLICKDELAY);
- }
- else if(numClicks === 2) {
- if(legend._clickTimeout) clearTimeout(legend._clickTimeout);
- gd._legendMouseDownTime = 0;
-
- var dblClickVal = Events.triggerHandler(gd, 'plotly_legenddoubleclick', evtData);
- if(dblClickVal !== false) handleClick(legendItem, gd, numClicks);
- }
- }
-
- function drawTexts(g, gd, maxLength) {
- var legendItem = g.data()[0][0];
- var fullLayout = gd._fullLayout;
- var trace = legendItem.trace;
- var isPie = Registry.traceIs(trace, 'pie');
- var traceIndex = trace.index;
- var name = isPie ? legendItem.label : trace.name;
- var isEditable = gd._context.edits.legendText && !isPie;
-
- var textEl = Lib.ensureSingle(g, 'text', 'legendtext');
-
- textEl.attr('text-anchor', 'start')
- .classed('user-select-none', true)
- .call(Drawing.font, fullLayout.legend.font)
- .text(isEditable ? ensureLength(name, maxLength) : name);
-
- svgTextUtils.positionText(textEl, constants.textOffsetX, 0);
-
- function textLayout(s) {
- svgTextUtils.convertToTspans(s, gd, function() {
- computeTextDimensions(g, gd);
- });
- }
-
- if(isEditable) {
- textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name})
- .call(textLayout)
- .on('edit', function(newName) {
- this.text(ensureLength(newName, maxLength))
- .call(textLayout);
-
- var fullInput = legendItem.trace._fullInput || {};
- var update = {};
-
- if(Registry.hasTransform(fullInput, 'groupby')) {
- var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby');
- var index = groupbyIndices[groupbyIndices.length - 1];
-
- var kcont = Lib.keyedContainer(fullInput, 'transforms[' + index + '].styles', 'target', 'value.name');
-
- kcont.set(legendItem.trace._group, newName);
-
- update = kcont.constructUpdate();
- } else {
- update.name = newName;
- }
-
- return Registry.call('_guiRestyle', gd, update, traceIndex);
- });
- } else {
- textLayout(textEl);
- }
- }
-
- /*
- * Make sure we have a reasonably clickable region.
- * If this string is missing or very short, pad it with spaces out to at least
- * 4 characters, up to the max length of other labels, on the assumption that
- * most characters are wider than spaces so a string of spaces will usually be
- * no wider than the real labels.
- */
- function ensureLength(str, maxLength) {
- var targetLength = Math.max(4, maxLength);
- if(str && str.trim().length >= targetLength / 2) return str;
- str = str || '';
- for(var i = targetLength - str.length; i > 0; i--) str += ' ';
- return str;
- }
-
- function setupTraceToggle(g, gd) {
- var newMouseDownTime;
- var numClicks = 1;
-
- var traceToggle = Lib.ensureSingle(g, 'rect', 'legendtoggle', function(s) {
- s.style('cursor', 'pointer')
- .attr('pointer-events', 'all')
- .call(Color.fill, 'rgba(0,0,0,0)');
- });
-
- traceToggle.on('mousedown', function() {
- newMouseDownTime = (new Date()).getTime();
- if(newMouseDownTime - gd._legendMouseDownTime < DBLCLICKDELAY) {
- // in a click train
- numClicks += 1;
- }
- else {
- // new click train
- numClicks = 1;
- gd._legendMouseDownTime = newMouseDownTime;
- }
- });
- traceToggle.on('mouseup', function() {
- if(gd._dragged || gd._editing) return;
- var legend = gd._fullLayout.legend;
-
- if((new Date()).getTime() - gd._legendMouseDownTime > DBLCLICKDELAY) {
- numClicks = Math.max(numClicks - 1, 1);
- }
-
- clickOrDoubleClick(gd, legend, g, numClicks, d3.event);
- });
- }
-
- function computeTextDimensions(g, gd) {
- var legendItem = g.data()[0][0];
-
- if(!legendItem.trace.showlegend) {
- g.remove();
- return;
- }
-
- var mathjaxGroup = g.select('g[class*=math-group]');
- var mathjaxNode = mathjaxGroup.node();
- var opts = gd._fullLayout.legend;
- var lineHeight = opts.font.size * LINE_SPACING;
- var height, width;
-
- if(mathjaxNode) {
- var mathjaxBB = Drawing.bBox(mathjaxNode);
-
- height = mathjaxBB.height;
- width = mathjaxBB.width;
-
- Drawing.setTranslate(mathjaxGroup, 0, (height / 4));
- } else {
- var text = g.select('.legendtext');
- var textLines = svgTextUtils.lineCount(text);
- var textNode = text.node();
-
- height = lineHeight * textLines;
- width = textNode ? Drawing.bBox(textNode).width : 0;
-
- // approximation to height offset to center the font
- // to avoid getBoundingClientRect
- var textY = lineHeight * (0.3 + (1 - textLines) / 2);
- svgTextUtils.positionText(text, constants.textOffsetX, textY);
- }
-
- legendItem.lineHeight = lineHeight;
- legendItem.height = Math.max(height, 16) + 3;
- legendItem.width = width;
- }
-
- function computeLegendDimensions(gd, groups, traces) {
- var fullLayout = gd._fullLayout;
- var opts = fullLayout.legend;
- var borderwidth = opts.borderwidth;
- var isGrouped = helpers.isGrouped(opts);
-
- var extraWidth = 0;
-
- opts._width = 0;
- opts._height = 0;
-
- if(helpers.isVertical(opts)) {
- if(isGrouped) {
- groups.each(function(d, i) {
- Drawing.setTranslate(this, 0, i * opts.tracegroupgap);
- });
- }
-
- traces.each(function(d) {
- var legendItem = d[0];
- var textHeight = legendItem.height;
- var textWidth = legendItem.width;
-
- Drawing.setTranslate(this,
- borderwidth,
- (5 + borderwidth + opts._height + textHeight / 2));
-
- opts._height += textHeight;
- opts._width = Math.max(opts._width, textWidth);
- });
-
- opts._width += 45 + borderwidth * 2;
- opts._height += 10 + borderwidth * 2;
-
- if(isGrouped) {
- opts._height += (opts._lgroupsLength - 1) * opts.tracegroupgap;
- }
-
- extraWidth = 40;
- }
- else if(isGrouped) {
- var groupXOffsets = [opts._width];
- var groupData = groups.data();
-
- for(var i = 0, n = groupData.length; i < n; i++) {
- var textWidths = groupData[i].map(function(legendItemArray) {
- return legendItemArray[0].width;
- });
-
- var groupWidth = 40 + Math.max.apply(null, textWidths);
-
- opts._width += opts.tracegroupgap + groupWidth;
-
- groupXOffsets.push(opts._width);
- }
-
- groups.each(function(d, i) {
- Drawing.setTranslate(this, groupXOffsets[i], 0);
- });
-
- groups.each(function() {
- var group = d3.select(this);
- var groupTraces = group.selectAll('g.traces');
- var groupHeight = 0;
-
- groupTraces.each(function(d) {
- var legendItem = d[0];
- var textHeight = legendItem.height;
-
- Drawing.setTranslate(this,
- 0,
- (5 + borderwidth + groupHeight + textHeight / 2));
-
- groupHeight += textHeight;
- });
-
- opts._height = Math.max(opts._height, groupHeight);
- });
-
- opts._height += 10 + borderwidth * 2;
- opts._width += borderwidth * 2;
- }
- else {
- var rowHeight = 0;
- var maxTraceHeight = 0;
- var maxTraceWidth = 0;
- var offsetX = 0;
- var fullTracesWidth = 0;
- var traceGap = opts.tracegroupgap || 5;
-
- // calculate largest width for traces and use for width of all legend items
- traces.each(function(d) {
- maxTraceWidth = Math.max(40 + d[0].width, maxTraceWidth);
- fullTracesWidth += 40 + d[0].width + traceGap;
- });
-
- // check if legend fits in one row
- var oneRowLegend = fullLayout._size.w > borderwidth + fullTracesWidth - traceGap;
-
- traces.each(function(d) {
- var legendItem = d[0];
- var traceWidth = oneRowLegend ? 40 + d[0].width : maxTraceWidth;
-
- if((borderwidth + offsetX + traceGap + traceWidth) > fullLayout._size.w) {
- offsetX = 0;
- rowHeight += maxTraceHeight;
- opts._height += maxTraceHeight;
- // reset for next row
- maxTraceHeight = 0;
- }
-
- Drawing.setTranslate(this,
- (borderwidth + offsetX),
- (5 + borderwidth + legendItem.height / 2) + rowHeight);
-
- opts._width += traceGap + traceWidth;
-
- // keep track of tallest trace in group
- offsetX += traceGap + traceWidth;
- maxTraceHeight = Math.max(legendItem.height, maxTraceHeight);
- });
-
- if(oneRowLegend) {
- opts._height = maxTraceHeight;
- } else {
- opts._height += maxTraceHeight;
- }
-
- opts._width += borderwidth * 2;
- opts._height += 10 + borderwidth * 2;
- }
-
- // make sure we're only getting full pixels
- opts._width = Math.ceil(opts._width);
- opts._height = Math.ceil(opts._height);
-
- var isEditable = (
- gd._context.edits.legendText ||
- gd._context.edits.legendPosition
- );
-
- traces.each(function(d) {
- var legendItem = d[0];
- var bg = d3.select(this).select('.legendtoggle');
-
- Drawing.setRect(bg,
- 0,
- -legendItem.height / 2,
- (isEditable ? 0 : opts._width) + extraWidth,
- legendItem.height
- );
- });
- }
-
- function expandMargin(gd) {
- var fullLayout = gd._fullLayout;
- var opts = fullLayout.legend;
-
- var xanchor = 'left';
- if(Lib.isRightAnchor(opts)) {
- xanchor = 'right';
- }
- else if(Lib.isCenterAnchor(opts)) {
- xanchor = 'center';
- }
-
- var yanchor = 'top';
- if(Lib.isBottomAnchor(opts)) {
- yanchor = 'bottom';
- }
- else if(Lib.isMiddleAnchor(opts)) {
- yanchor = 'middle';
- }
-
- // lastly check if the margin auto-expand has changed
- Plots.autoMargin(gd, 'legend', {
- x: opts.x,
- y: opts.y,
- l: opts._width * (FROM_TL[xanchor]),
- r: opts._width * (FROM_BR[xanchor]),
- b: opts._height * (FROM_BR[yanchor]),
- t: opts._height * (FROM_TL[yanchor])
- });
- }
-
- function expandHorizontalMargin(gd) {
- var fullLayout = gd._fullLayout;
- var opts = fullLayout.legend;
-
- var xanchor = 'left';
- if(Lib.isRightAnchor(opts)) {
- xanchor = 'right';
- }
- else if(Lib.isCenterAnchor(opts)) {
- xanchor = 'center';
- }
-
- // lastly check if the margin auto-expand has changed
- Plots.autoMargin(gd, 'legend', {
- x: opts.x,
- y: 0.5,
- l: opts._width * (FROM_TL[xanchor]),
- r: opts._width * (FROM_BR[xanchor]),
- b: 0,
- t: 0
- });
- }
-
- },{"../../constants/alignment":146,"../../constants/interactions":148,"../../lib":168,"../../lib/events":161,"../../lib/svg_text_utils":189,"../../plots/plots":245,"../../registry":257,"../color":51,"../dragelement":69,"../drawing":72,"./constants":101,"./get_legend_data":104,"./handle_click":105,"./helpers":106,"./style":108,"d3":16}],104:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var helpers = _dereq_('./helpers');
-
-
- module.exports = function getLegendData(calcdata, opts) {
- var lgroupToTraces = {};
- var lgroups = [];
- var hasOneNonBlankGroup = false;
- var slicesShown = {};
- var lgroupi = 0;
- var i, j;
-
- function addOneItem(legendGroup, legendItem) {
- // each '' legend group is treated as a separate group
- if(legendGroup === '' || !helpers.isGrouped(opts)) {
- var uniqueGroup = '~~i' + lgroupi; // TODO: check this against fullData legendgroups?
-
- lgroups.push(uniqueGroup);
- lgroupToTraces[uniqueGroup] = [[legendItem]];
- lgroupi++;
- }
- else if(lgroups.indexOf(legendGroup) === -1) {
- lgroups.push(legendGroup);
- hasOneNonBlankGroup = true;
- lgroupToTraces[legendGroup] = [[legendItem]];
- }
- else lgroupToTraces[legendGroup].push([legendItem]);
- }
-
- // build an { legendgroup: [cd0, cd0], ... } object
- for(i = 0; i < calcdata.length; i++) {
- var cd = calcdata[i];
- var cd0 = cd[0];
- var trace = cd0.trace;
- var lgroup = trace.legendgroup;
-
- if(!trace.visible || !trace.showlegend) continue;
-
- if(Registry.traceIs(trace, 'pie')) {
- if(!slicesShown[lgroup]) slicesShown[lgroup] = {};
-
- for(j = 0; j < cd.length; j++) {
- var labelj = cd[j].label;
-
- if(!slicesShown[lgroup][labelj]) {
- addOneItem(lgroup, {
- label: labelj,
- color: cd[j].color,
- i: cd[j].i,
- trace: trace,
- pts: cd[j].pts
- });
-
- slicesShown[lgroup][labelj] = true;
- }
- }
- }
-
- else addOneItem(lgroup, cd0);
- }
-
- // won't draw a legend in this case
- if(!lgroups.length) return [];
-
- // rearrange lgroupToTraces into a d3-friendly array of arrays
- var lgroupsLength = lgroups.length;
- var ltraces;
- var legendData;
-
- if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
- legendData = new Array(lgroupsLength);
-
- for(i = 0; i < lgroupsLength; i++) {
- ltraces = lgroupToTraces[lgroups[i]];
- legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
- }
- }
- else {
- // collapse all groups into one if all groups are blank
- legendData = [new Array(lgroupsLength)];
-
- for(i = 0; i < lgroupsLength; i++) {
- ltraces = lgroupToTraces[lgroups[i]][0];
- legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
- }
- lgroupsLength = 1;
- }
-
- // needed in repositionLegend
- opts._lgroupsLength = lgroupsLength;
- return legendData;
- };
-
- },{"../../registry":257,"./helpers":106}],105:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Registry = _dereq_('../../registry');
-
- var SHOWISOLATETIP = true;
-
- module.exports = function handleClick(g, gd, numClicks) {
- if(gd._dragged || gd._editing) return;
-
- var hiddenSlices = gd._fullLayout.hiddenlabels ?
- gd._fullLayout.hiddenlabels.slice() :
- [];
-
- var legendItem = g.data()[0][0];
- var fullData = gd._fullData;
- var fullTrace = legendItem.trace;
- var legendgroup = fullTrace.legendgroup;
-
- var i, j, kcont, key, keys, val;
- var attrUpdate = {};
- var attrIndices = [];
- var carrs = [];
- var carrIdx = [];
-
- function insertUpdate(traceIndex, key, value) {
- var attrIndex = attrIndices.indexOf(traceIndex);
- var valueArray = attrUpdate[key];
- if(!valueArray) {
- valueArray = attrUpdate[key] = [];
- }
-
- if(attrIndices.indexOf(traceIndex) === -1) {
- attrIndices.push(traceIndex);
- attrIndex = attrIndices.length - 1;
- }
-
- valueArray[attrIndex] = value;
-
- return attrIndex;
- }
-
- function setVisibility(fullTrace, visibility) {
- var fullInput = fullTrace._fullInput;
- if(Registry.hasTransform(fullInput, 'groupby')) {
- var kcont = carrs[fullInput.index];
- if(!kcont) {
- var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby');
- var lastGroupbyIndex = groupbyIndices[groupbyIndices.length - 1];
- kcont = Lib.keyedContainer(fullInput, 'transforms[' + lastGroupbyIndex + '].styles', 'target', 'value.visible');
- carrs[fullInput.index] = kcont;
- }
-
- var curState = kcont.get(fullTrace._group);
-
- // If not specified, assume visible. This happens if there are other style
- // properties set for a group but not the visibility. There are many similar
- // ways to do this (e.g. why not just `curState = fullTrace.visible`??? The
- // answer is: because it breaks other things like groupby trace names in
- // subtle ways.)
- if(curState === undefined) {
- curState = true;
- }
-
- if(curState !== false) {
- // true -> legendonly. All others toggle to true:
- kcont.set(fullTrace._group, visibility);
- }
- carrIdx[fullInput.index] = insertUpdate(fullInput.index, 'visible', fullInput.visible === false ? false : true);
- } else {
- // false -> false (not possible since will not be visible in legend)
- // true -> legendonly
- // legendonly -> true
- var nextVisibility = fullInput.visible === false ? false : visibility;
-
- insertUpdate(fullInput.index, 'visible', nextVisibility);
- }
- }
-
- if(numClicks === 1 && SHOWISOLATETIP && gd.data && gd._context.showTips) {
- Lib.notifier(Lib._(gd, 'Double-click on legend to isolate one trace'), 'long');
- SHOWISOLATETIP = false;
- } else {
- SHOWISOLATETIP = false;
- }
-
- if(Registry.traceIs(fullTrace, 'pie')) {
- var thisLabel = legendItem.label;
- var thisLabelIndex = hiddenSlices.indexOf(thisLabel);
-
- if(numClicks === 1) {
- if(thisLabelIndex === -1) hiddenSlices.push(thisLabel);
- else hiddenSlices.splice(thisLabelIndex, 1);
- } else if(numClicks === 2) {
- hiddenSlices = [];
- gd.calcdata[0].forEach(function(d) {
- if(thisLabel !== d.label) {
- hiddenSlices.push(d.label);
- }
- });
- if(gd._fullLayout.hiddenlabels && gd._fullLayout.hiddenlabels.length === hiddenSlices.length && thisLabelIndex === -1) {
- hiddenSlices = [];
- }
- }
-
- Registry.call('_guiRelayout', gd, 'hiddenlabels', hiddenSlices);
- } else {
- var hasLegendgroup = legendgroup && legendgroup.length;
- var traceIndicesInGroup = [];
- var tracei;
- if(hasLegendgroup) {
- for(i = 0; i < fullData.length; i++) {
- tracei = fullData[i];
- if(!tracei.visible) continue;
- if(tracei.legendgroup === legendgroup) {
- traceIndicesInGroup.push(i);
- }
- }
- }
-
- if(numClicks === 1) {
- var nextVisibility;
-
- switch(fullTrace.visible) {
- case true:
- nextVisibility = 'legendonly';
- break;
- case false:
- nextVisibility = false;
- break;
- case 'legendonly':
- nextVisibility = true;
- break;
- }
-
- if(hasLegendgroup) {
- for(i = 0; i < fullData.length; i++) {
- if(fullData[i].visible !== false && fullData[i].legendgroup === legendgroup) {
- setVisibility(fullData[i], nextVisibility);
- }
- }
- } else {
- setVisibility(fullTrace, nextVisibility);
- }
- } else if(numClicks === 2) {
- // Compute the clicked index. expandedIndex does what we want for expanded traces
- // but also culls hidden traces. That means we have some work to do.
- var isClicked, isInGroup, otherState;
- var isIsolated = true;
- for(i = 0; i < fullData.length; i++) {
- isClicked = fullData[i] === fullTrace;
- if(isClicked) continue;
-
- isInGroup = (hasLegendgroup && fullData[i].legendgroup === legendgroup);
-
- if(!isInGroup && fullData[i].visible === true && !Registry.traceIs(fullData[i], 'notLegendIsolatable')) {
- isIsolated = false;
- break;
- }
- }
-
- for(i = 0; i < fullData.length; i++) {
- // False is sticky; we don't change it.
- if(fullData[i].visible === false) continue;
-
- if(Registry.traceIs(fullData[i], 'notLegendIsolatable')) {
- continue;
- }
-
- switch(fullTrace.visible) {
- case 'legendonly':
- setVisibility(fullData[i], true);
- break;
- case true:
- otherState = isIsolated ? true : 'legendonly';
- isClicked = fullData[i] === fullTrace;
- isInGroup = isClicked || (hasLegendgroup && fullData[i].legendgroup === legendgroup);
- setVisibility(fullData[i], isInGroup ? true : otherState);
- break;
- }
- }
- }
-
- for(i = 0; i < carrs.length; i++) {
- kcont = carrs[i];
- if(!kcont) continue;
- var update = kcont.constructUpdate();
-
- var updateKeys = Object.keys(update);
- for(j = 0; j < updateKeys.length; j++) {
- key = updateKeys[j];
- val = attrUpdate[key] = attrUpdate[key] || [];
- val[carrIdx[i]] = update[key];
- }
- }
-
- // The length of the value arrays should be equal and any unspecified
- // values should be explicitly undefined for them to get properly culled
- // as updates and not accidentally reset to the default value. This fills
- // out sparse arrays with the required number of undefined values:
- keys = Object.keys(attrUpdate);
- for(i = 0; i < keys.length; i++) {
- key = keys[i];
- for(j = 0; j < attrIndices.length; j++) {
- // Use hasOwnPropety to protect against falsey values:
- if(!attrUpdate[key].hasOwnProperty(j)) {
- attrUpdate[key][j] = undefined;
- }
- }
- }
-
- Registry.call('_guiRestyle', gd, attrUpdate, attrIndices);
- }
- };
-
- },{"../../lib":168,"../../registry":257}],106:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- exports.isGrouped = function isGrouped(legendLayout) {
- return (legendLayout.traceorder || '').indexOf('grouped') !== -1;
- };
-
- exports.isVertical = function isVertical(legendLayout) {
- return legendLayout.orientation !== 'h';
- };
-
- exports.isReversed = function isReversed(legendLayout) {
- return (legendLayout.traceorder || '').indexOf('reversed') !== -1;
- };
-
- },{}],107:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- module.exports = {
- moduleType: 'component',
- name: 'legend',
-
- layoutAttributes: _dereq_('./attributes'),
- supplyLayoutDefaults: _dereq_('./defaults'),
-
- draw: _dereq_('./draw'),
- style: _dereq_('./style')
- };
-
- },{"./attributes":100,"./defaults":102,"./draw":103,"./style":108}],108:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var Drawing = _dereq_('../drawing');
- var Color = _dereq_('../color');
-
- var subTypes = _dereq_('../../traces/scatter/subtypes');
- var stylePie = _dereq_('../../traces/pie/style_one');
-
- module.exports = function style(s, gd) {
- s.each(function(d) {
- var traceGroup = d3.select(this);
-
- var layers = Lib.ensureSingle(traceGroup, 'g', 'layers');
- layers.style('opacity', d[0].trace.opacity);
-
- // Marker vertical alignment
- var valign = gd._fullLayout.legend.valign;
- var lineHeight = d[0].lineHeight;
- var height = d[0].height;
-
- if(valign === 'middle' || !lineHeight || !height) {
- layers.attr('transform', null);
- } else {
- var factor = {top: 1, bottom: -1}[valign];
- var markerOffsetY = factor * (0.5 * (lineHeight - height + 3));
- layers.attr('transform', 'translate(0,' + markerOffsetY + ')');
- }
-
- var fill = layers
- .selectAll('g.legendfill')
- .data([d]);
- fill.enter().append('g')
- .classed('legendfill', true);
-
- var line = layers
- .selectAll('g.legendlines')
- .data([d]);
- line.enter().append('g')
- .classed('legendlines', true);
-
- var symbol = layers
- .selectAll('g.legendsymbols')
- .data([d]);
- symbol.enter().append('g')
- .classed('legendsymbols', true);
-
- symbol.selectAll('g.legendpoints')
- .data([d])
- .enter().append('g')
- .classed('legendpoints', true);
- })
- .each(styleBars)
- .each(styleBoxes)
- .each(stylePies)
- .each(styleLines)
- .each(stylePoints)
- .each(styleCandles)
- .each(styleOHLC);
-
- function styleLines(d) {
- var trace = d[0].trace;
- var showFill = trace.visible && trace.fill && trace.fill !== 'none';
- var showLine = subTypes.hasLines(trace);
- var contours = trace.contours;
- var showGradientLine = false;
- var showGradientFill = false;
-
- if(contours) {
- var coloring = contours.coloring;
-
- if(coloring === 'lines') {
- showGradientLine = true;
- }
- else {
- showLine = coloring === 'none' || coloring === 'heatmap' ||
- contours.showlines;
- }
-
- if(contours.type === 'constraint') {
- showFill = contours._operation !== '=';
- }
- else if(coloring === 'fill' || coloring === 'heatmap') {
- showGradientFill = true;
- }
- }
-
- // with fill and no markers or text, move the line and fill up a bit
- // so it's more centered
- var markersOrText = subTypes.hasMarkers(trace) || subTypes.hasText(trace);
- var anyFill = showFill || showGradientFill;
- var anyLine = showLine || showGradientLine;
- var pathStart = (markersOrText || !anyFill) ? 'M5,0' :
- // with a line leave it slightly below center, to leave room for the
- // line thickness and because the line is usually more prominent
- anyLine ? 'M5,-2' : 'M5,-3';
-
- var this3 = d3.select(this);
-
- var fill = this3.select('.legendfill').selectAll('path')
- .data(showFill || showGradientFill ? [d] : []);
- fill.enter().append('path').classed('js-fill', true);
- fill.exit().remove();
- fill.attr('d', pathStart + 'h30v6h-30z')
- .call(showFill ? Drawing.fillGroupStyle : fillGradient);
-
- var line = this3.select('.legendlines').selectAll('path')
- .data(showLine || showGradientLine ? [d] : []);
- line.enter().append('path').classed('js-line', true);
- line.exit().remove();
-
- // this is ugly... but you can't apply a gradient to a perfectly
- // horizontal or vertical line. Presumably because then
- // the system doesn't know how to scale vertical variation, even
- // though there *is* no vertical variation in this case.
- // so add an invisibly small angle to the line
- // This issue (and workaround) exist across (Mac) Chrome, FF, and Safari
- line.attr('d', pathStart + (showGradientLine ? 'l30,0.0001' : 'h30'))
- .call(showLine ? Drawing.lineGroupStyle : lineGradient);
-
- function fillGradient(s) {
- if(s.size()) {
- var gradientID = 'legendfill-' + trace.uid;
- Drawing.gradient(s, gd, gradientID, 'horizontalreversed',
- trace.colorscale, 'fill');
- }
- }
-
- function lineGradient(s) {
- if(s.size()) {
- var gradientID = 'legendline-' + trace.uid;
- Drawing.lineGroupStyle(s);
- Drawing.gradient(s, gd, gradientID, 'horizontalreversed',
- trace.colorscale, 'stroke');
- }
- }
-
- }
-
- function stylePoints(d) {
- var d0 = d[0];
- var trace = d0.trace;
- var showMarkers = subTypes.hasMarkers(trace);
- var showText = subTypes.hasText(trace);
- var showLines = subTypes.hasLines(trace);
- var dMod, tMod;
-
- // 'scatter3d' don't use gd.calcdata,
- // use d0.trace to infer arrayOk attributes
-
- function boundVal(attrIn, arrayToValFn, bounds) {
- var valIn = Lib.nestedProperty(trace, attrIn).get();
- var valToBound = (Lib.isArrayOrTypedArray(valIn) && arrayToValFn) ?
- arrayToValFn(valIn) :
- valIn;
-
- if(bounds) {
- if(valToBound < bounds[0]) return bounds[0];
- else if(valToBound > bounds[1]) return bounds[1];
- }
- return valToBound;
- }
-
- function pickFirst(array) { return array[0]; }
-
- // constrain text, markers, etc so they'll fit on the legend
- if(showMarkers || showText || showLines) {
- var dEdit = {};
- var tEdit = {};
-
- if(showMarkers) {
- dEdit.mc = boundVal('marker.color', pickFirst);
- dEdit.mx = boundVal('marker.symbol', pickFirst);
- dEdit.mo = boundVal('marker.opacity', Lib.mean, [0.2, 1]);
- dEdit.mlc = boundVal('marker.line.color', pickFirst);
- dEdit.mlw = boundVal('marker.line.width', Lib.mean, [0, 5]);
- tEdit.marker = {
- sizeref: 1,
- sizemin: 1,
- sizemode: 'diameter'
- };
-
- var ms = boundVal('marker.size', Lib.mean, [2, 16]);
- dEdit.ms = ms;
- tEdit.marker.size = ms;
- }
-
- if(showLines) {
- tEdit.line = {
- width: boundVal('line.width', pickFirst, [0, 10])
- };
- }
-
- if(showText) {
- dEdit.tx = 'Aa';
- dEdit.tp = boundVal('textposition', pickFirst);
- dEdit.ts = 10;
- dEdit.tc = boundVal('textfont.color', pickFirst);
- dEdit.tf = boundVal('textfont.family', pickFirst);
- }
-
- dMod = [Lib.minExtend(d0, dEdit)];
- tMod = Lib.minExtend(trace, tEdit);
-
- // always show legend items in base state
- tMod.selectedpoints = null;
- }
-
- var ptgroup = d3.select(this).select('g.legendpoints');
-
- var pts = ptgroup.selectAll('path.scatterpts')
- .data(showMarkers ? dMod : []);
- // make sure marker is on the bottom, in case it enters after text
- pts.enter().insert('path', ':first-child')
- .classed('scatterpts', true)
- .attr('transform', 'translate(20,0)');
- pts.exit().remove();
- pts.call(Drawing.pointStyle, tMod, gd);
-
- // 'mrc' is set in pointStyle and used in textPointStyle:
- // constrain it here
- if(showMarkers) dMod[0].mrc = 3;
-
- var txt = ptgroup.selectAll('g.pointtext')
- .data(showText ? dMod : []);
- txt.enter()
- .append('g').classed('pointtext', true)
- .append('text').attr('transform', 'translate(20,0)');
- txt.exit().remove();
- txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd);
- }
-
- function styleBars(d) {
- var trace = d[0].trace;
- var marker = trace.marker || {};
- var markerLine = marker.line || {};
-
- var barpath = d3.select(this).select('g.legendpoints')
- .selectAll('path.legendbar')
- .data(Registry.traceIs(trace, 'bar') ? [d] : []);
- barpath.enter().append('path').classed('legendbar', true)
- .attr('d', 'M6,6H-6V-6H6Z')
- .attr('transform', 'translate(20,0)');
- barpath.exit().remove();
-
- barpath.each(function(d) {
- var p = d3.select(this);
- var d0 = d[0];
- var w = (d0.mlw + 1 || markerLine.width + 1) - 1;
-
- p.style('stroke-width', w + 'px')
- .call(Color.fill, d0.mc || marker.color);
-
- if(w) {
- p.call(Color.stroke, d0.mlc || markerLine.color);
- }
- });
- }
-
- function styleBoxes(d) {
- var trace = d[0].trace;
-
- var pts = d3.select(this).select('g.legendpoints')
- .selectAll('path.legendbox')
- .data(Registry.traceIs(trace, 'box-violin') && trace.visible ? [d] : []);
- pts.enter().append('path').classed('legendbox', true)
- // if we want the median bar, prepend M6,0H-6
- .attr('d', 'M6,6H-6V-6H6Z')
- .attr('transform', 'translate(20,0)');
- pts.exit().remove();
-
- pts.each(function() {
- var w = trace.line.width;
- var p = d3.select(this);
-
- p.style('stroke-width', w + 'px')
- .call(Color.fill, trace.fillcolor);
-
- if(w) {
- Color.stroke(p, trace.line.color);
- }
- });
- }
-
- function styleCandles(d) {
- var trace = d[0].trace;
-
- var pts = d3.select(this).select('g.legendpoints')
- .selectAll('path.legendcandle')
- .data(trace.type === 'candlestick' && trace.visible ? [d, d] : []);
- pts.enter().append('path').classed('legendcandle', true)
- .attr('d', function(_, i) {
- if(i) return 'M-15,0H-8M-8,6V-6H8Z'; // increasing
- return 'M15,0H8M8,-6V6H-8Z'; // decreasing
- })
- .attr('transform', 'translate(20,0)')
- .style('stroke-miterlimit', 1);
- pts.exit().remove();
-
- pts.each(function(_, i) {
- var container = trace[i ? 'increasing' : 'decreasing'];
- var w = container.line.width;
- var p = d3.select(this);
-
- p.style('stroke-width', w + 'px')
- .call(Color.fill, container.fillcolor);
-
- if(w) {
- Color.stroke(p, container.line.color);
- }
- });
- }
-
- function styleOHLC(d) {
- var trace = d[0].trace;
-
- var pts = d3.select(this).select('g.legendpoints')
- .selectAll('path.legendohlc')
- .data(trace.type === 'ohlc' && trace.visible ? [d, d] : []);
- pts.enter().append('path').classed('legendohlc', true)
- .attr('d', function(_, i) {
- if(i) return 'M-15,0H0M-8,-6V0'; // increasing
- return 'M15,0H0M8,6V0'; // decreasing
- })
- .attr('transform', 'translate(20,0)')
- .style('stroke-miterlimit', 1);
- pts.exit().remove();
-
- pts.each(function(_, i) {
- var container = trace[i ? 'increasing' : 'decreasing'];
- var w = container.line.width;
- var p = d3.select(this);
-
- p.style('fill', 'none')
- .call(Drawing.dashLine, container.line.dash, w);
-
- if(w) {
- Color.stroke(p, container.line.color);
- }
- });
- }
-
- function stylePies(d) {
- var trace = d[0].trace;
-
- var pts = d3.select(this).select('g.legendpoints')
- .selectAll('path.legendpie')
- .data(Registry.traceIs(trace, 'pie') && trace.visible ? [d] : []);
- pts.enter().append('path').classed('legendpie', true)
- .attr('d', 'M6,6H-6V-6H6Z')
- .attr('transform', 'translate(20,0)');
- pts.exit().remove();
-
- if(pts.size()) pts.call(stylePie, d[0], trace);
- }
- };
-
- },{"../../lib":168,"../../registry":257,"../../traces/pie/style_one":365,"../../traces/scatter/subtypes":391,"../color":51,"../drawing":72,"d3":16}],109:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Plots = _dereq_('../../plots/plots');
- var axisIds = _dereq_('../../plots/cartesian/axis_ids');
- var Lib = _dereq_('../../lib');
- var Icons = _dereq_('../../../build/ploticon');
-
- var _ = Lib._;
-
- var modeBarButtons = module.exports = {};
-
- /**
- * ModeBar buttons configuration
- *
- * @param {string} name
- * name / id of the buttons (for tracking)
- * @param {string} title
- * text that appears while hovering over the button,
- * enter null, false or '' for no hover text
- * @param {string} icon
- * svg icon object associated with the button
- * can be linked to Plotly.Icons to use the default plotly icons
- * @param {string} [gravity]
- * icon positioning
- * @param {function} click
- * click handler associated with the button, a function of
- * 'gd' (the main graph object) and
- * 'ev' (the event object)
- * @param {string} [attr]
- * attribute associated with button,
- * use this with 'val' to keep track of the state
- * @param {*} [val]
- * initial 'attr' value, can be a function of gd
- * @param {boolean} [toggle]
- * is the button a toggle button?
- */
-
- modeBarButtons.toImage = {
- name: 'toImage',
- title: function(gd) {
- var opts = gd._context.toImageButtonOptions || {};
- var format = opts.format || 'png';
- return format === 'png' ?
- _(gd, 'Download plot as a png') : // legacy text
- _(gd, 'Download plot'); // generic non-PNG text
- },
- icon: Icons.camera,
- click: function(gd) {
- var toImageButtonOptions = gd._context.toImageButtonOptions;
- var opts = {format: toImageButtonOptions.format || 'png'};
-
- Lib.notifier(_(gd, 'Taking snapshot - this may take a few seconds'), 'long');
-
- if(opts.format !== 'svg' && Lib.isIE()) {
- Lib.notifier(_(gd, 'IE only supports svg. Changing format to svg.'), 'long');
- opts.format = 'svg';
- }
-
- ['filename', 'width', 'height', 'scale'].forEach(function(key) {
- if(toImageButtonOptions[key]) {
- opts[key] = toImageButtonOptions[key];
- }
- });
-
- Registry.call('downloadImage', gd, opts)
- .then(function(filename) {
- Lib.notifier(_(gd, 'Snapshot succeeded') + ' - ' + filename, 'long');
- })
- .catch(function() {
- Lib.notifier(_(gd, 'Sorry, there was a problem downloading your snapshot!'), 'long');
- });
- }
- };
-
- modeBarButtons.sendDataToCloud = {
- name: 'sendDataToCloud',
- title: function(gd) { return _(gd, 'Edit in Chart Studio'); },
- icon: Icons.disk,
- click: function(gd) {
- Plots.sendDataToCloud(gd);
- }
- };
-
- modeBarButtons.zoom2d = {
- name: 'zoom2d',
- title: function(gd) { return _(gd, 'Zoom'); },
- attr: 'dragmode',
- val: 'zoom',
- icon: Icons.zoombox,
- click: handleCartesian
- };
-
- modeBarButtons.pan2d = {
- name: 'pan2d',
- title: function(gd) { return _(gd, 'Pan'); },
- attr: 'dragmode',
- val: 'pan',
- icon: Icons.pan,
- click: handleCartesian
- };
-
- modeBarButtons.select2d = {
- name: 'select2d',
- title: function(gd) { return _(gd, 'Box Select'); },
- attr: 'dragmode',
- val: 'select',
- icon: Icons.selectbox,
- click: handleCartesian
- };
-
- modeBarButtons.lasso2d = {
- name: 'lasso2d',
- title: function(gd) { return _(gd, 'Lasso Select'); },
- attr: 'dragmode',
- val: 'lasso',
- icon: Icons.lasso,
- click: handleCartesian
- };
-
- modeBarButtons.zoomIn2d = {
- name: 'zoomIn2d',
- title: function(gd) { return _(gd, 'Zoom in'); },
- attr: 'zoom',
- val: 'in',
- icon: Icons.zoom_plus,
- click: handleCartesian
- };
-
- modeBarButtons.zoomOut2d = {
- name: 'zoomOut2d',
- title: function(gd) { return _(gd, 'Zoom out'); },
- attr: 'zoom',
- val: 'out',
- icon: Icons.zoom_minus,
- click: handleCartesian
- };
-
- modeBarButtons.autoScale2d = {
- name: 'autoScale2d',
- title: function(gd) { return _(gd, 'Autoscale'); },
- attr: 'zoom',
- val: 'auto',
- icon: Icons.autoscale,
- click: handleCartesian
- };
-
- modeBarButtons.resetScale2d = {
- name: 'resetScale2d',
- title: function(gd) { return _(gd, 'Reset axes'); },
- attr: 'zoom',
- val: 'reset',
- icon: Icons.home,
- click: handleCartesian
- };
-
- modeBarButtons.hoverClosestCartesian = {
- name: 'hoverClosestCartesian',
- title: function(gd) { return _(gd, 'Show closest data on hover'); },
- attr: 'hovermode',
- val: 'closest',
- icon: Icons.tooltip_basic,
- gravity: 'ne',
- click: handleCartesian
- };
-
- modeBarButtons.hoverCompareCartesian = {
- name: 'hoverCompareCartesian',
- title: function(gd) { return _(gd, 'Compare data on hover'); },
- attr: 'hovermode',
- val: function(gd) {
- return gd._fullLayout._isHoriz ? 'y' : 'x';
- },
- icon: Icons.tooltip_compare,
- gravity: 'ne',
- click: handleCartesian
- };
-
- function handleCartesian(gd, ev) {
- var button = ev.currentTarget;
- var astr = button.getAttribute('data-attr');
- var val = button.getAttribute('data-val') || true;
- var fullLayout = gd._fullLayout;
- var aobj = {};
- var axList = axisIds.list(gd, null, true);
- var allSpikesEnabled = 'on';
-
- var ax, i;
-
- if(astr === 'zoom') {
- var mag = (val === 'in') ? 0.5 : 2;
- var r0 = (1 + mag) / 2;
- var r1 = (1 - mag) / 2;
- var axName;
-
- for(i = 0; i < axList.length; i++) {
- ax = axList[i];
-
- if(!ax.fixedrange) {
- axName = ax._name;
- if(val === 'auto') aobj[axName + '.autorange'] = true;
- else if(val === 'reset') {
- if(ax._rangeInitial === undefined) {
- aobj[axName + '.autorange'] = true;
- }
- else {
- var rangeInitial = ax._rangeInitial.slice();
- aobj[axName + '.range[0]'] = rangeInitial[0];
- aobj[axName + '.range[1]'] = rangeInitial[1];
- }
- if(ax._showSpikeInitial !== undefined) {
- aobj[axName + '.showspikes'] = ax._showSpikeInitial;
- if(allSpikesEnabled === 'on' && !ax._showSpikeInitial) {
- allSpikesEnabled = 'off';
- }
- }
- }
- else {
- var rangeNow = [
- ax.r2l(ax.range[0]),
- ax.r2l(ax.range[1]),
- ];
-
- var rangeNew = [
- r0 * rangeNow[0] + r1 * rangeNow[1],
- r0 * rangeNow[1] + r1 * rangeNow[0]
- ];
-
- aobj[axName + '.range[0]'] = ax.l2r(rangeNew[0]);
- aobj[axName + '.range[1]'] = ax.l2r(rangeNew[1]);
- }
- }
- }
- fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
- }
- else {
- // if ALL traces have orientation 'h', 'hovermode': 'x' otherwise: 'y'
- if(astr === 'hovermode' && (val === 'x' || val === 'y')) {
- val = fullLayout._isHoriz ? 'y' : 'x';
- button.setAttribute('data-val', val);
- } else if(astr === 'hovermode' && val === 'closest') {
- for(i = 0; i < axList.length; i++) {
- ax = axList[i];
- if(allSpikesEnabled === 'on' && !ax.showspikes) {
- allSpikesEnabled = 'off';
- }
- }
- fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
- }
-
- aobj[astr] = val;
- }
-
- Registry.call('_guiRelayout', gd, aobj);
- }
-
- modeBarButtons.zoom3d = {
- name: 'zoom3d',
- title: function(gd) { return _(gd, 'Zoom'); },
- attr: 'scene.dragmode',
- val: 'zoom',
- icon: Icons.zoombox,
- click: handleDrag3d
- };
-
- modeBarButtons.pan3d = {
- name: 'pan3d',
- title: function(gd) { return _(gd, 'Pan'); },
- attr: 'scene.dragmode',
- val: 'pan',
- icon: Icons.pan,
- click: handleDrag3d
- };
-
- modeBarButtons.orbitRotation = {
- name: 'orbitRotation',
- title: function(gd) { return _(gd, 'Orbital rotation'); },
- attr: 'scene.dragmode',
- val: 'orbit',
- icon: Icons['3d_rotate'],
- click: handleDrag3d
- };
-
- modeBarButtons.tableRotation = {
- name: 'tableRotation',
- title: function(gd) { return _(gd, 'Turntable rotation'); },
- attr: 'scene.dragmode',
- val: 'turntable',
- icon: Icons['z-axis'],
- click: handleDrag3d
- };
-
- function handleDrag3d(gd, ev) {
- var button = ev.currentTarget;
- var attr = button.getAttribute('data-attr');
- var val = button.getAttribute('data-val') || true;
- var sceneIds = gd._fullLayout._subplots.gl3d;
- var layoutUpdate = {};
-
- var parts = attr.split('.');
-
- for(var i = 0; i < sceneIds.length; i++) {
- layoutUpdate[sceneIds[i] + '.' + parts[1]] = val;
- }
-
- // for multi-type subplots
- var val2d = (val === 'pan') ? val : 'zoom';
- layoutUpdate.dragmode = val2d;
-
- Registry.call('_guiRelayout', gd, layoutUpdate);
- }
-
- modeBarButtons.resetCameraDefault3d = {
- name: 'resetCameraDefault3d',
- title: function(gd) { return _(gd, 'Reset camera to default'); },
- attr: 'resetDefault',
- icon: Icons.home,
- click: handleCamera3d
- };
-
- modeBarButtons.resetCameraLastSave3d = {
- name: 'resetCameraLastSave3d',
- title: function(gd) { return _(gd, 'Reset camera to last save'); },
- attr: 'resetLastSave',
- icon: Icons.movie,
- click: handleCamera3d
- };
-
- function handleCamera3d(gd, ev) {
- var button = ev.currentTarget;
- var attr = button.getAttribute('data-attr');
- var fullLayout = gd._fullLayout;
- var sceneIds = fullLayout._subplots.gl3d;
- var aobj = {};
-
- for(var i = 0; i < sceneIds.length; i++) {
- var sceneId = sceneIds[i];
- var key = sceneId + '.camera';
- var scene = fullLayout[sceneId]._scene;
-
- if(attr === 'resetDefault') {
- aobj[key] = null;
- }
- else if(attr === 'resetLastSave') {
- aobj[key] = Lib.extendDeep({}, scene.cameraInitial);
- }
- }
-
- Registry.call('_guiRelayout', gd, aobj);
- }
-
- modeBarButtons.hoverClosest3d = {
- name: 'hoverClosest3d',
- title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
- attr: 'hovermode',
- val: null,
- toggle: true,
- icon: Icons.tooltip_basic,
- gravity: 'ne',
- click: handleHover3d
- };
-
- function getNextHover3d(gd, ev) {
- var button = ev.currentTarget;
- var val = button._previousVal;
- var fullLayout = gd._fullLayout;
- var sceneIds = fullLayout._subplots.gl3d;
-
- var axes = ['xaxis', 'yaxis', 'zaxis'];
-
- // initialize 'current spike' object to be stored in the DOM
- var currentSpikes = {};
- var layoutUpdate = {};
-
- if(val) {
- layoutUpdate = val;
- button._previousVal = null;
- }
- else {
- for(var i = 0; i < sceneIds.length; i++) {
- var sceneId = sceneIds[i];
- var sceneLayout = fullLayout[sceneId];
-
- var hovermodeAStr = sceneId + '.hovermode';
- currentSpikes[hovermodeAStr] = sceneLayout.hovermode;
- layoutUpdate[hovermodeAStr] = false;
-
- // copy all the current spike attrs
- for(var j = 0; j < 3; j++) {
- var axis = axes[j];
- var spikeAStr = sceneId + '.' + axis + '.showspikes';
- layoutUpdate[spikeAStr] = false;
- currentSpikes[spikeAStr] = sceneLayout[axis].showspikes;
- }
- }
-
- button._previousVal = currentSpikes;
- }
- return layoutUpdate;
- }
-
- function handleHover3d(gd, ev) {
- var layoutUpdate = getNextHover3d(gd, ev);
- Registry.call('_guiRelayout', gd, layoutUpdate);
- }
-
- modeBarButtons.zoomInGeo = {
- name: 'zoomInGeo',
- title: function(gd) { return _(gd, 'Zoom in'); },
- attr: 'zoom',
- val: 'in',
- icon: Icons.zoom_plus,
- click: handleGeo
- };
-
- modeBarButtons.zoomOutGeo = {
- name: 'zoomOutGeo',
- title: function(gd) { return _(gd, 'Zoom out'); },
- attr: 'zoom',
- val: 'out',
- icon: Icons.zoom_minus,
- click: handleGeo
- };
-
- modeBarButtons.resetGeo = {
- name: 'resetGeo',
- title: function(gd) { return _(gd, 'Reset'); },
- attr: 'reset',
- val: null,
- icon: Icons.autoscale,
- click: handleGeo
- };
-
- modeBarButtons.hoverClosestGeo = {
- name: 'hoverClosestGeo',
- title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
- attr: 'hovermode',
- val: null,
- toggle: true,
- icon: Icons.tooltip_basic,
- gravity: 'ne',
- click: toggleHover
- };
-
- function handleGeo(gd, ev) {
- var button = ev.currentTarget;
- var attr = button.getAttribute('data-attr');
- var val = button.getAttribute('data-val') || true;
- var fullLayout = gd._fullLayout;
- var geoIds = fullLayout._subplots.geo;
-
- for(var i = 0; i < geoIds.length; i++) {
- var id = geoIds[i];
- var geoLayout = fullLayout[id];
-
- if(attr === 'zoom') {
- var scale = geoLayout.projection.scale;
- var newScale = (val === 'in') ? 2 * scale : 0.5 * scale;
-
- Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale);
- } else if(attr === 'reset') {
- resetView(gd, 'geo');
- }
- }
- }
-
- modeBarButtons.hoverClosestGl2d = {
- name: 'hoverClosestGl2d',
- title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
- attr: 'hovermode',
- val: null,
- toggle: true,
- icon: Icons.tooltip_basic,
- gravity: 'ne',
- click: toggleHover
- };
-
- modeBarButtons.hoverClosestPie = {
- name: 'hoverClosestPie',
- title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
- attr: 'hovermode',
- val: 'closest',
- icon: Icons.tooltip_basic,
- gravity: 'ne',
- click: toggleHover
- };
-
- function getNextHover(gd) {
- var fullLayout = gd._fullLayout;
-
- if(fullLayout.hovermode) return false;
-
- if(fullLayout._has('cartesian')) {
- return fullLayout._isHoriz ? 'y' : 'x';
- }
- return 'closest';
- }
-
- function toggleHover(gd) {
- var newHover = getNextHover(gd);
- Registry.call('_guiRelayout', gd, 'hovermode', newHover);
- }
-
- // buttons when more then one plot types are present
-
- modeBarButtons.toggleHover = {
- name: 'toggleHover',
- title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
- attr: 'hovermode',
- val: null,
- toggle: true,
- icon: Icons.tooltip_basic,
- gravity: 'ne',
- click: function(gd, ev) {
- var layoutUpdate = getNextHover3d(gd, ev);
- layoutUpdate.hovermode = getNextHover(gd);
-
- Registry.call('_guiRelayout', gd, layoutUpdate);
- }
- };
-
- modeBarButtons.resetViews = {
- name: 'resetViews',
- title: function(gd) { return _(gd, 'Reset views'); },
- icon: Icons.home,
- click: function(gd, ev) {
- var button = ev.currentTarget;
-
- button.setAttribute('data-attr', 'zoom');
- button.setAttribute('data-val', 'reset');
- handleCartesian(gd, ev);
-
- button.setAttribute('data-attr', 'resetLastSave');
- handleCamera3d(gd, ev);
-
- resetView(gd, 'geo');
- resetView(gd, 'mapbox');
- }
- };
-
- modeBarButtons.toggleSpikelines = {
- name: 'toggleSpikelines',
- title: function(gd) { return _(gd, 'Toggle Spike Lines'); },
- icon: Icons.spikeline,
- attr: '_cartesianSpikesEnabled',
- val: 'on',
- click: function(gd) {
- var fullLayout = gd._fullLayout;
-
- fullLayout._cartesianSpikesEnabled = fullLayout._cartesianSpikesEnabled === 'on' ? 'off' : 'on';
-
- var aobj = setSpikelineVisibility(gd);
-
- Registry.call('_guiRelayout', gd, aobj);
- }
- };
-
- function setSpikelineVisibility(gd) {
- var fullLayout = gd._fullLayout;
- var axList = axisIds.list(gd, null, true);
- var aobj = {};
-
- var ax, axName;
-
- for(var i = 0; i < axList.length; i++) {
- ax = axList[i];
- axName = ax._name;
- aobj[axName + '.showspikes'] = fullLayout._cartesianSpikesEnabled === 'on' ? true : ax._showSpikeInitial;
- }
-
- return aobj;
- }
-
- modeBarButtons.resetViewMapbox = {
- name: 'resetViewMapbox',
- title: function(gd) { return _(gd, 'Reset view'); },
- attr: 'reset',
- icon: Icons.home,
- click: function(gd) {
- resetView(gd, 'mapbox');
- }
- };
-
- function resetView(gd, subplotType) {
- var fullLayout = gd._fullLayout;
- var subplotIds = fullLayout._subplots[subplotType];
- var aObj = {};
-
- for(var i = 0; i < subplotIds.length; i++) {
- var id = subplotIds[i];
- var subplotObj = fullLayout[id]._subplot;
- var viewInitial = subplotObj.viewInitial;
- var viewKeys = Object.keys(viewInitial);
-
- for(var j = 0; j < viewKeys.length; j++) {
- var key = viewKeys[j];
- aObj[id + '.' + key] = viewInitial[key];
- }
- }
-
- Registry.call('_guiRelayout', gd, aObj);
- }
-
- },{"../../../build/ploticon":2,"../../lib":168,"../../plots/cartesian/axis_ids":215,"../../plots/plots":245,"../../registry":257}],110:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- exports.manage = _dereq_('./manage');
-
- },{"./manage":111}],111:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var axisIds = _dereq_('../../plots/cartesian/axis_ids');
- var scatterSubTypes = _dereq_('../../traces/scatter/subtypes');
- var Registry = _dereq_('../../registry');
-
- var createModeBar = _dereq_('./modebar');
- var modeBarButtons = _dereq_('./buttons');
-
- /**
- * ModeBar wrapper around 'create' and 'update',
- * chooses buttons to pass to ModeBar constructor based on
- * plot type and plot config.
- *
- * @param {object} gd main plot object
- *
- */
- module.exports = function manageModeBar(gd) {
- var fullLayout = gd._fullLayout;
- var context = gd._context;
- var modeBar = fullLayout._modeBar;
-
- if(!context.displayModeBar && !context.watermark) {
- if(modeBar) {
- modeBar.destroy();
- delete fullLayout._modeBar;
- }
- return;
- }
-
- if(!Array.isArray(context.modeBarButtonsToRemove)) {
- throw new Error([
- '*modeBarButtonsToRemove* configuration options',
- 'must be an array.'
- ].join(' '));
- }
-
- if(!Array.isArray(context.modeBarButtonsToAdd)) {
- throw new Error([
- '*modeBarButtonsToAdd* configuration options',
- 'must be an array.'
- ].join(' '));
- }
-
- var customButtons = context.modeBarButtons;
- var buttonGroups;
-
- if(Array.isArray(customButtons) && customButtons.length) {
- buttonGroups = fillCustomButton(customButtons);
- }
- else if(!context.displayModeBar && context.watermark) {
- buttonGroups = [];
- }
- else {
- buttonGroups = getButtonGroups(
- gd,
- context.modeBarButtonsToRemove,
- context.modeBarButtonsToAdd,
- context.showSendToCloud
- );
- }
-
- if(modeBar) modeBar.update(gd, buttonGroups);
- else fullLayout._modeBar = createModeBar(gd, buttonGroups);
- };
-
- // logic behind which buttons are displayed by default
- function getButtonGroups(gd, buttonsToRemove, buttonsToAdd, showSendToCloud) {
- var fullLayout = gd._fullLayout;
- var fullData = gd._fullData;
-
- var hasCartesian = fullLayout._has('cartesian');
- var hasGL3D = fullLayout._has('gl3d');
- var hasGeo = fullLayout._has('geo');
- var hasPie = fullLayout._has('pie');
- var hasGL2D = fullLayout._has('gl2d');
- var hasTernary = fullLayout._has('ternary');
- var hasMapbox = fullLayout._has('mapbox');
- var hasPolar = fullLayout._has('polar');
- var allAxesFixed = areAllAxesFixed(fullLayout);
-
- var groups = [];
-
- function addGroup(newGroup) {
- if(!newGroup.length) return;
-
- var out = [];
-
- for(var i = 0; i < newGroup.length; i++) {
- var button = newGroup[i];
- if(buttonsToRemove.indexOf(button) !== -1) continue;
- out.push(modeBarButtons[button]);
- }
-
- groups.push(out);
- }
-
- // buttons common to all plot types
- var commonGroup = ['toImage'];
- if(showSendToCloud) commonGroup.push('sendDataToCloud');
- addGroup(commonGroup);
-
- var zoomGroup = [];
- var hoverGroup = [];
- var resetGroup = [];
- var dragModeGroup = [];
-
- if((hasCartesian || hasGL2D || hasPie || hasTernary) + hasGeo + hasGL3D + hasMapbox + hasPolar > 1) {
- // graphs with more than one plot types get 'union buttons'
- // which reset the view or toggle hover labels across all subplots.
- hoverGroup = ['toggleHover'];
- resetGroup = ['resetViews'];
- }
- else if(hasGeo) {
- zoomGroup = ['zoomInGeo', 'zoomOutGeo'];
- hoverGroup = ['hoverClosestGeo'];
- resetGroup = ['resetGeo'];
- }
- else if(hasGL3D) {
- hoverGroup = ['hoverClosest3d'];
- resetGroup = ['resetCameraDefault3d', 'resetCameraLastSave3d'];
- }
- else if(hasMapbox) {
- hoverGroup = ['toggleHover'];
- resetGroup = ['resetViewMapbox'];
- }
- else if(hasGL2D) {
- hoverGroup = ['hoverClosestGl2d'];
- }
- else if(hasPie) {
- hoverGroup = ['hoverClosestPie'];
- }
- else { // hasPolar, hasTernary
- // always show at least one hover icon.
- hoverGroup = ['toggleHover'];
- }
- // if we have cartesian, allow switching between closest and compare
- // regardless of what other types are on the plot, since they'll all
- // just treat any truthy hovermode as 'closest'
- if(hasCartesian) {
- hoverGroup = ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'];
- }
-
- if((hasCartesian || hasGL2D) && !allAxesFixed) {
- zoomGroup = ['zoomIn2d', 'zoomOut2d', 'autoScale2d'];
- if(resetGroup[0] !== 'resetViews') resetGroup = ['resetScale2d'];
- }
-
- if(hasGL3D) {
- dragModeGroup = ['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation'];
- }
- else if(((hasCartesian || hasGL2D) && !allAxesFixed) || hasTernary) {
- dragModeGroup = ['zoom2d', 'pan2d'];
- }
- else if(hasMapbox || hasGeo) {
- dragModeGroup = ['pan2d'];
- }
- else if(hasPolar) {
- dragModeGroup = ['zoom2d'];
- }
- if(isSelectable(fullData)) {
- dragModeGroup.push('select2d', 'lasso2d');
- }
-
- addGroup(dragModeGroup);
- addGroup(zoomGroup.concat(resetGroup));
- addGroup(hoverGroup);
-
- return appendButtonsToGroups(groups, buttonsToAdd);
- }
-
- function areAllAxesFixed(fullLayout) {
- var axList = axisIds.list({_fullLayout: fullLayout}, null, true);
-
- for(var i = 0; i < axList.length; i++) {
- if(!axList[i].fixedrange) {
- return false;
- }
- }
-
- return true;
- }
-
- // look for traces that support selection
- // to be updated as we add more selectPoints handlers
- function isSelectable(fullData) {
- var selectable = false;
-
- for(var i = 0; i < fullData.length; i++) {
- if(selectable) break;
-
- var trace = fullData[i];
-
- if(!trace._module || !trace._module.selectPoints) continue;
-
- if(Registry.traceIs(trace, 'scatter-like')) {
- if(scatterSubTypes.hasMarkers(trace) || scatterSubTypes.hasText(trace)) {
- selectable = true;
- }
- } else if(Registry.traceIs(trace, 'box-violin')) {
- if(trace.boxpoints === 'all' || trace.points === 'all') {
- selectable = true;
- }
- }
- // assume that in general if the trace module has selectPoints,
- // then it's selectable. Scatter is an exception to this because it must
- // have markers or text, not just be a scatter type.
- else selectable = true;
- }
-
- return selectable;
- }
-
- function appendButtonsToGroups(groups, buttons) {
- if(buttons.length) {
- if(Array.isArray(buttons[0])) {
- for(var i = 0; i < buttons.length; i++) {
- groups.push(buttons[i]);
- }
- }
- else groups.push(buttons);
- }
-
- return groups;
- }
-
- // fill in custom buttons referring to default mode bar buttons
- function fillCustomButton(customButtons) {
- for(var i = 0; i < customButtons.length; i++) {
- var buttonGroup = customButtons[i];
-
- for(var j = 0; j < buttonGroup.length; j++) {
- var button = buttonGroup[j];
-
- if(typeof button === 'string') {
- if(modeBarButtons[button] !== undefined) {
- customButtons[i][j] = modeBarButtons[button];
- }
- else {
- throw new Error([
- '*modeBarButtons* configuration options',
- 'invalid button name'
- ].join(' '));
- }
- }
- }
- }
-
- return customButtons;
- }
-
- },{"../../plots/cartesian/axis_ids":215,"../../registry":257,"../../traces/scatter/subtypes":391,"./buttons":109,"./modebar":112}],112:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Lib = _dereq_('../../lib');
- var Icons = _dereq_('../../../build/ploticon');
- var Parser = new DOMParser();
-
- /**
- * UI controller for interactive plots
- * @Class
- * @Param {object} opts
- * @Param {object} opts.buttons nested arrays of grouped buttons config objects
- * @Param {object} opts.container container div to append modeBar
- * @Param {object} opts.graphInfo primary plot object containing data and layout
- */
- function ModeBar(opts) {
- this.container = opts.container;
- this.element = document.createElement('div');
-
- this.update(opts.graphInfo, opts.buttons);
-
- this.container.appendChild(this.element);
- }
-
- var proto = ModeBar.prototype;
-
- /**
- * Update modeBar (buttons and logo)
- *
- * @param {object} graphInfo primary plot object containing data and layout
- * @param {array of arrays} buttons nested arrays of grouped buttons to initialize
- *
- */
- proto.update = function(graphInfo, buttons) {
- this.graphInfo = graphInfo;
-
- var context = this.graphInfo._context;
- var fullLayout = this.graphInfo._fullLayout;
- var modeBarId = 'modebar-' + fullLayout._uid;
-
- this.element.setAttribute('id', modeBarId);
- this._uid = modeBarId;
-
- this.element.className = 'modebar';
- if(context.displayModeBar === 'hover') this.element.className += ' modebar--hover ease-bg';
-
- if(fullLayout.modebar.orientation === 'v') {
- this.element.className += ' vertical';
- buttons = buttons.reverse();
- }
-
- var style = fullLayout.modebar;
- var bgSelector = context.displayModeBar === 'hover' ? '.js-plotly-plot .plotly:hover ' : '';
-
- Lib.deleteRelatedStyleRule(modeBarId);
- Lib.addRelatedStyleRule(modeBarId, bgSelector + '#' + modeBarId, 'background-color: ' + style.bgcolor);
- Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn .icon path', 'fill: ' + style.color);
- Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn:hover .icon path', 'fill: ' + style.activecolor);
- Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn.active .icon path', 'fill: ' + style.activecolor);
-
- // if buttons or logo have changed, redraw modebar interior
- var needsNewButtons = !this.hasButtons(buttons);
- var needsNewLogo = (this.hasLogo !== context.displaylogo);
- var needsNewLocale = (this.locale !== context.locale);
-
- this.locale = context.locale;
-
- if(needsNewButtons || needsNewLogo || needsNewLocale) {
- this.removeAllButtons();
-
- this.updateButtons(buttons);
-
- if(context.watermark || context.displaylogo) {
- var logoGroup = this.getLogo();
- if(context.watermark) {
- logoGroup.className = logoGroup.className + ' watermark';
- }
-
- if(fullLayout.modebar.orientation === 'v') {
- this.element.insertBefore(logoGroup, this.element.childNodes[0]);
- } else {
- this.element.appendChild(logoGroup);
- }
-
- this.hasLogo = true;
- }
- }
-
- this.updateActiveButton();
- };
-
- proto.updateButtons = function(buttons) {
- var _this = this;
-
- this.buttons = buttons;
- this.buttonElements = [];
- this.buttonsNames = [];
-
- this.buttons.forEach(function(buttonGroup) {
- var group = _this.createGroup();
-
- buttonGroup.forEach(function(buttonConfig) {
- var buttonName = buttonConfig.name;
- if(!buttonName) {
- throw new Error('must provide button \'name\' in button config');
- }
- if(_this.buttonsNames.indexOf(buttonName) !== -1) {
- throw new Error('button name \'' + buttonName + '\' is taken');
- }
- _this.buttonsNames.push(buttonName);
-
- var button = _this.createButton(buttonConfig);
- _this.buttonElements.push(button);
- group.appendChild(button);
- });
-
- _this.element.appendChild(group);
- });
- };
-
- /**
- * Empty div for containing a group of buttons
- * @Return {HTMLelement}
- */
- proto.createGroup = function() {
- var group = document.createElement('div');
- group.className = 'modebar-group';
- return group;
- };
-
- /**
- * Create a new button div and set constant and configurable attributes
- * @Param {object} config (see ./buttons.js for more info)
- * @Return {HTMLelement}
- */
- proto.createButton = function(config) {
- var _this = this;
- var button = document.createElement('a');
-
- button.setAttribute('rel', 'tooltip');
- button.className = 'modebar-btn';
-
- var title = config.title;
- if(title === undefined) title = config.name;
- // for localization: allow title to be a callable that takes gd as arg
- else if(typeof title === 'function') title = title(this.graphInfo);
-
- if(title || title === 0) button.setAttribute('data-title', title);
-
- if(config.attr !== undefined) button.setAttribute('data-attr', config.attr);
-
- var val = config.val;
- if(val !== undefined) {
- if(typeof val === 'function') val = val(this.graphInfo);
- button.setAttribute('data-val', val);
- }
-
- var click = config.click;
- if(typeof click !== 'function') {
- throw new Error('must provide button \'click\' function in button config');
- }
- else {
- button.addEventListener('click', function(ev) {
- config.click(_this.graphInfo, ev);
-
- // only needed for 'hoverClosestGeo' which does not call relayout
- _this.updateActiveButton(ev.currentTarget);
- });
- }
-
- button.setAttribute('data-toggle', config.toggle || false);
- if(config.toggle) d3.select(button).classed('active', true);
-
- var icon = config.icon;
- if(typeof icon === 'function') {
- button.appendChild(icon());
- }
- else {
- button.appendChild(this.createIcon(icon || Icons.question));
- }
- button.setAttribute('data-gravity', config.gravity || 'n');
-
- return button;
- };
-
- /**
- * Add an icon to a button
- * @Param {object} thisIcon
- * @Param {number} thisIcon.width
- * @Param {string} thisIcon.path
- * @Param {string} thisIcon.color
- * @Return {HTMLelement}
- */
- proto.createIcon = function(thisIcon) {
- var iconHeight = isNumeric(thisIcon.height) ?
- Number(thisIcon.height) :
- thisIcon.ascent - thisIcon.descent;
- var svgNS = 'http://www.w3.org/2000/svg';
- var icon;
-
- if(thisIcon.path) {
- icon = document.createElementNS(svgNS, 'svg');
- icon.setAttribute('viewBox', [0, 0, thisIcon.width, iconHeight].join(' '));
- icon.setAttribute('class', 'icon');
-
- var path = document.createElementNS(svgNS, 'path');
- path.setAttribute('d', thisIcon.path);
-
- if(thisIcon.transform) {
- path.setAttribute('transform', thisIcon.transform);
- }
- else if(thisIcon.ascent !== undefined) {
- // Legacy icon transform calculation
- path.setAttribute('transform', 'matrix(1 0 0 -1 0 ' + thisIcon.ascent + ')');
- }
-
- icon.appendChild(path);
- }
-
- if(thisIcon.svg) {
- var svgDoc = Parser.parseFromString(thisIcon.svg, 'application/xml');
- icon = svgDoc.childNodes[0];
- }
-
- icon.setAttribute('height', '1em');
- icon.setAttribute('width', '1em');
-
- return icon;
- };
-
- /**
- * Updates active button with attribute specified in layout
- * @Param {object} graphInfo plot object containing data and layout
- * @Return {HTMLelement}
- */
- proto.updateActiveButton = function(buttonClicked) {
- var fullLayout = this.graphInfo._fullLayout;
- var dataAttrClicked = (buttonClicked !== undefined) ?
- buttonClicked.getAttribute('data-attr') :
- null;
-
- this.buttonElements.forEach(function(button) {
- var thisval = button.getAttribute('data-val') || true;
- var dataAttr = button.getAttribute('data-attr');
- var isToggleButton = (button.getAttribute('data-toggle') === 'true');
- var button3 = d3.select(button);
-
- // Use 'data-toggle' and 'buttonClicked' to toggle buttons
- // that have no one-to-one equivalent in fullLayout
- if(isToggleButton) {
- if(dataAttr === dataAttrClicked) {
- button3.classed('active', !button3.classed('active'));
- }
- }
- else {
- var val = (dataAttr === null) ?
- dataAttr :
- Lib.nestedProperty(fullLayout, dataAttr).get();
-
- button3.classed('active', val === thisval);
- }
-
- });
- };
-
- /**
- * Check if modeBar is configured as button configuration argument
- *
- * @Param {object} buttons 2d array of grouped button config objects
- * @Return {boolean}
- */
- proto.hasButtons = function(buttons) {
- var currentButtons = this.buttons;
-
- if(!currentButtons) return false;
-
- if(buttons.length !== currentButtons.length) return false;
-
- for(var i = 0; i < buttons.length; ++i) {
- if(buttons[i].length !== currentButtons[i].length) return false;
- for(var j = 0; j < buttons[i].length; j++) {
- if(buttons[i][j].name !== currentButtons[i][j].name) return false;
- }
- }
-
- return true;
- };
-
- /**
- * @return {HTMLDivElement} The logo image wrapped in a group
- */
- proto.getLogo = function() {
- var group = this.createGroup();
- var a = document.createElement('a');
-
- a.href = 'https://plot.ly/';
- a.target = '_blank';
- a.setAttribute('data-title', Lib._(this.graphInfo, 'Produced with Plotly'));
- a.className = 'modebar-btn plotlyjsicon modebar-btn--logo';
-
- a.appendChild(this.createIcon(Icons.newplotlylogo));
-
- group.appendChild(a);
- return group;
- };
-
- proto.removeAllButtons = function() {
- while(this.element.firstChild) {
- this.element.removeChild(this.element.firstChild);
- }
-
- this.hasLogo = false;
- };
-
- proto.destroy = function() {
- Lib.removeElement(this.container.querySelector('.modebar'));
- Lib.deleteRelatedStyleRule(this._uid);
- };
-
- function createModeBar(gd, buttons) {
- var fullLayout = gd._fullLayout;
-
- var modeBar = new ModeBar({
- graphInfo: gd,
- container: fullLayout._paperdiv.node(),
- buttons: buttons
- });
-
- if(fullLayout._privateplot) {
- d3.select(modeBar.element).append('span')
- .classed('badge-private float--left', true)
- .text('PRIVATE');
- }
-
- return modeBar;
- }
-
- module.exports = createModeBar;
-
- },{"../../../build/ploticon":2,"../../lib":168,"d3":16,"fast-isnumeric":18}],113:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var fontAttrs = _dereq_('../../plots/font_attributes');
- var colorAttrs = _dereq_('../color/attributes');
- var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
-
- var buttonAttrs = templatedArray('button', {
- visible: {
- valType: 'boolean',
-
- dflt: true,
- editType: 'plot',
-
- },
- step: {
- valType: 'enumerated',
-
- values: ['month', 'year', 'day', 'hour', 'minute', 'second', 'all'],
- dflt: 'month',
- editType: 'plot',
-
- },
- stepmode: {
- valType: 'enumerated',
-
- values: ['backward', 'todate'],
- dflt: 'backward',
- editType: 'plot',
-
- },
- count: {
- valType: 'number',
-
- min: 0,
- dflt: 1,
- editType: 'plot',
-
- },
- label: {
- valType: 'string',
-
- editType: 'plot',
-
- },
- editType: 'plot',
-
- });
-
- module.exports = {
- visible: {
- valType: 'boolean',
-
- editType: 'plot',
-
- },
-
- buttons: buttonAttrs,
-
- x: {
- valType: 'number',
- min: -2,
- max: 3,
-
- editType: 'plot',
-
- },
- xanchor: {
- valType: 'enumerated',
- values: ['auto', 'left', 'center', 'right'],
- dflt: 'left',
-
- editType: 'plot',
-
- },
- y: {
- valType: 'number',
- min: -2,
- max: 3,
-
- editType: 'plot',
-
- },
- yanchor: {
- valType: 'enumerated',
- values: ['auto', 'top', 'middle', 'bottom'],
- dflt: 'bottom',
-
- editType: 'plot',
-
- },
-
- font: fontAttrs({
- editType: 'plot',
-
- }),
-
- bgcolor: {
- valType: 'color',
- dflt: colorAttrs.lightLine,
-
- editType: 'plot',
-
- },
- activecolor: {
- valType: 'color',
-
- editType: 'plot',
-
- },
- bordercolor: {
- valType: 'color',
- dflt: colorAttrs.defaultLine,
-
- editType: 'plot',
-
- },
- borderwidth: {
- valType: 'number',
- min: 0,
- dflt: 0,
-
- editType: 'plot',
-
- },
- editType: 'plot'
- };
-
- },{"../../plot_api/plot_template":202,"../../plots/font_attributes":239,"../color/attributes":50}],114:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
-
- module.exports = {
-
- // 'y' position pad above counter axis domain
- yPad: 0.02,
-
- // minimum button width (regardless of text size)
- minButtonWidth: 30,
-
- // buttons rect radii
- rx: 3,
- ry: 3,
-
- // light fraction used to compute the 'activecolor' default
- lightAmount: 25,
- darkAmount: 10
- };
-
- },{}],115:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Color = _dereq_('../color');
- var Template = _dereq_('../../plot_api/plot_template');
- var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
-
- var attributes = _dereq_('./attributes');
- var constants = _dereq_('./constants');
-
-
- module.exports = function handleDefaults(containerIn, containerOut, layout, counterAxes, calendar) {
- var selectorIn = containerIn.rangeselector || {};
- var selectorOut = Template.newContainer(containerOut, 'rangeselector');
-
- function coerce(attr, dflt) {
- return Lib.coerce(selectorIn, selectorOut, attributes, attr, dflt);
- }
-
- var buttons = handleArrayContainerDefaults(selectorIn, selectorOut, {
- name: 'buttons',
- handleItemDefaults: buttonDefaults,
- calendar: calendar
- });
-
- var visible = coerce('visible', buttons.length > 0);
- if(visible) {
- var posDflt = getPosDflt(containerOut, layout, counterAxes);
- coerce('x', posDflt[0]);
- coerce('y', posDflt[1]);
- Lib.noneOrAll(containerIn, containerOut, ['x', 'y']);
-
- coerce('xanchor');
- coerce('yanchor');
-
- Lib.coerceFont(coerce, 'font', layout.font);
-
- var bgColor = coerce('bgcolor');
- coerce('activecolor', Color.contrast(bgColor, constants.lightAmount, constants.darkAmount));
- coerce('bordercolor');
- coerce('borderwidth');
- }
- };
-
- function buttonDefaults(buttonIn, buttonOut, selectorOut, opts) {
- var calendar = opts.calendar;
-
- function coerce(attr, dflt) {
- return Lib.coerce(buttonIn, buttonOut, attributes.buttons, attr, dflt);
- }
-
- var visible = coerce('visible');
-
- if(visible) {
- var step = coerce('step');
- if(step !== 'all') {
- if(calendar && calendar !== 'gregorian' && (step === 'month' || step === 'year')) {
- buttonOut.stepmode = 'backward';
- }
- else {
- coerce('stepmode');
- }
-
- coerce('count');
- }
-
- coerce('label');
- }
- }
-
- function getPosDflt(containerOut, layout, counterAxes) {
- var anchoredList = counterAxes.filter(function(ax) {
- return layout[ax].anchor === containerOut._id;
- });
-
- var posY = 0;
- for(var i = 0; i < anchoredList.length; i++) {
- var domain = layout[anchoredList[i]].domain;
- if(domain) posY = Math.max(domain[1], posY);
- }
-
- return [containerOut.domain[0], posY + constants.yPad];
- }
-
- },{"../../lib":168,"../../plot_api/plot_template":202,"../../plots/array_container_defaults":208,"../color":51,"./attributes":113,"./constants":114}],116:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Registry = _dereq_('../../registry');
- var Plots = _dereq_('../../plots/plots');
- var Color = _dereq_('../color');
- var Drawing = _dereq_('../drawing');
- var Lib = _dereq_('../../lib');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var axisIds = _dereq_('../../plots/cartesian/axis_ids');
-
- var alignmentConstants = _dereq_('../../constants/alignment');
- var LINE_SPACING = alignmentConstants.LINE_SPACING;
- var FROM_TL = alignmentConstants.FROM_TL;
- var FROM_BR = alignmentConstants.FROM_BR;
-
- var constants = _dereq_('./constants');
- var getUpdateObject = _dereq_('./get_update_object');
-
-
- module.exports = function draw(gd) {
- var fullLayout = gd._fullLayout;
-
- var selectors = fullLayout._infolayer.selectAll('.rangeselector')
- .data(makeSelectorData(gd), selectorKeyFunc);
-
- selectors.enter().append('g')
- .classed('rangeselector', true);
-
- selectors.exit().remove();
-
- selectors.style({
- cursor: 'pointer',
- 'pointer-events': 'all'
- });
-
- selectors.each(function(d) {
- var selector = d3.select(this);
- var axisLayout = d;
- var selectorLayout = axisLayout.rangeselector;
-
- var buttons = selector.selectAll('g.button')
- .data(Lib.filterVisible(selectorLayout.buttons));
-
- buttons.enter().append('g')
- .classed('button', true);
-
- buttons.exit().remove();
-
- buttons.each(function(d) {
- var button = d3.select(this);
- var update = getUpdateObject(axisLayout, d);
-
- d._isActive = isActive(axisLayout, d, update);
-
- button.call(drawButtonRect, selectorLayout, d);
- button.call(drawButtonText, selectorLayout, d, gd);
-
- button.on('click', function() {
- if(gd._dragged) return;
-
- Registry.call('_guiRelayout', gd, update);
- });
-
- button.on('mouseover', function() {
- d._isHovered = true;
- button.call(drawButtonRect, selectorLayout, d);
- });
-
- button.on('mouseout', function() {
- d._isHovered = false;
- button.call(drawButtonRect, selectorLayout, d);
- });
- });
-
- reposition(gd, buttons, selectorLayout, axisLayout._name, selector);
- });
-
- };
-
- function makeSelectorData(gd) {
- var axes = axisIds.list(gd, 'x', true);
- var data = [];
-
- for(var i = 0; i < axes.length; i++) {
- var axis = axes[i];
-
- if(axis.rangeselector && axis.rangeselector.visible) {
- data.push(axis);
- }
- }
-
- return data;
- }
-
- function selectorKeyFunc(d) {
- return d._id;
- }
-
- function isActive(axisLayout, opts, update) {
- if(opts.step === 'all') {
- return axisLayout.autorange === true;
- }
- else {
- var keys = Object.keys(update);
-
- return (
- axisLayout.range[0] === update[keys[0]] &&
- axisLayout.range[1] === update[keys[1]]
- );
- }
- }
-
- function drawButtonRect(button, selectorLayout, d) {
- var rect = Lib.ensureSingle(button, 'rect', 'selector-rect', function(s) {
- s.attr('shape-rendering', 'crispEdges');
- });
-
- rect.attr({
- 'rx': constants.rx,
- 'ry': constants.ry
- });
-
- rect.call(Color.stroke, selectorLayout.bordercolor)
- .call(Color.fill, getFillColor(selectorLayout, d))
- .style('stroke-width', selectorLayout.borderwidth + 'px');
- }
-
- function getFillColor(selectorLayout, d) {
- return (d._isActive || d._isHovered) ?
- selectorLayout.activecolor :
- selectorLayout.bgcolor;
- }
-
- function drawButtonText(button, selectorLayout, d, gd) {
- function textLayout(s) {
- svgTextUtils.convertToTspans(s, gd);
- }
-
- var text = Lib.ensureSingle(button, 'text', 'selector-text', function(s) {
- s.classed('user-select-none', true)
- .attr('text-anchor', 'middle');
- });
-
- text.call(Drawing.font, selectorLayout.font)
- .text(getLabel(d))
- .call(textLayout);
- }
-
- function getLabel(opts) {
- if(opts.label) return opts.label;
-
- if(opts.step === 'all') return 'all';
-
- return opts.count + opts.step.charAt(0);
- }
-
- function reposition(gd, buttons, opts, axName, selector) {
- var width = 0;
- var height = 0;
-
- var borderWidth = opts.borderwidth;
-
- buttons.each(function() {
- var button = d3.select(this);
- var text = button.select('.selector-text');
-
- var tHeight = opts.font.size * LINE_SPACING;
- var hEff = Math.max(tHeight * svgTextUtils.lineCount(text), 16) + 3;
-
- height = Math.max(height, hEff);
- });
-
- buttons.each(function() {
- var button = d3.select(this);
- var rect = button.select('.selector-rect');
- var text = button.select('.selector-text');
-
- var tWidth = text.node() && Drawing.bBox(text.node()).width;
- var tHeight = opts.font.size * LINE_SPACING;
- var tLines = svgTextUtils.lineCount(text);
-
- var wEff = Math.max(tWidth + 10, constants.minButtonWidth);
-
- // TODO add MathJax support
-
- // TODO add buttongap attribute
-
- button.attr('transform', 'translate(' +
- (borderWidth + width) + ',' + borderWidth +
- ')');
-
- rect.attr({
- x: 0,
- y: 0,
- width: wEff,
- height: height
- });
-
- svgTextUtils.positionText(text, wEff / 2,
- height / 2 - ((tLines - 1) * tHeight / 2) + 3);
-
- width += wEff + 5;
- });
-
- var graphSize = gd._fullLayout._size;
- var lx = graphSize.l + graphSize.w * opts.x;
- var ly = graphSize.t + graphSize.h * (1 - opts.y);
-
- var xanchor = 'left';
- if(Lib.isRightAnchor(opts)) {
- lx -= width;
- xanchor = 'right';
- }
- if(Lib.isCenterAnchor(opts)) {
- lx -= width / 2;
- xanchor = 'center';
- }
-
- var yanchor = 'top';
- if(Lib.isBottomAnchor(opts)) {
- ly -= height;
- yanchor = 'bottom';
- }
- if(Lib.isMiddleAnchor(opts)) {
- ly -= height / 2;
- yanchor = 'middle';
- }
-
- width = Math.ceil(width);
- height = Math.ceil(height);
- lx = Math.round(lx);
- ly = Math.round(ly);
-
- Plots.autoMargin(gd, axName + '-range-selector', {
- x: opts.x,
- y: opts.y,
- l: width * FROM_TL[xanchor],
- r: width * FROM_BR[xanchor],
- b: height * FROM_BR[yanchor],
- t: height * FROM_TL[yanchor]
- });
-
- selector.attr('transform', 'translate(' + lx + ',' + ly + ')');
- }
-
- },{"../../constants/alignment":146,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/cartesian/axis_ids":215,"../../plots/plots":245,"../../registry":257,"../color":51,"../drawing":72,"./constants":114,"./get_update_object":117,"d3":16}],117:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- module.exports = function getUpdateObject(axisLayout, buttonLayout) {
- var axName = axisLayout._name;
- var update = {};
-
- if(buttonLayout.step === 'all') {
- update[axName + '.autorange'] = true;
- }
- else {
- var xrange = getXRange(axisLayout, buttonLayout);
-
- update[axName + '.range[0]'] = xrange[0];
- update[axName + '.range[1]'] = xrange[1];
- }
-
- return update;
- };
-
- function getXRange(axisLayout, buttonLayout) {
- var currentRange = axisLayout.range;
- var base = new Date(axisLayout.r2l(currentRange[1]));
- var step = buttonLayout.step;
- var count = buttonLayout.count;
- var range0;
-
- switch(buttonLayout.stepmode) {
- case 'backward':
- range0 = axisLayout.l2r(+d3.time[step].utc.offset(base, -count));
- break;
-
- case 'todate':
- var base2 = d3.time[step].utc.offset(base, -count);
-
- range0 = axisLayout.l2r(+d3.time[step].utc.ceil(base2));
- break;
- }
-
- var range1 = currentRange[1];
-
- return [range0, range1];
- }
-
- },{"d3":16}],118:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- moduleType: 'component',
- name: 'rangeselector',
-
- schema: {
- subplots: {
- xaxis: {rangeselector: _dereq_('./attributes')}
- }
- },
-
- layoutAttributes: _dereq_('./attributes'),
- handleDefaults: _dereq_('./defaults'),
-
- draw: _dereq_('./draw')
- };
-
- },{"./attributes":113,"./defaults":115,"./draw":116}],119:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var colorAttributes = _dereq_('../color/attributes');
-
- module.exports = {
- bgcolor: {
- valType: 'color',
- dflt: colorAttributes.background,
-
- editType: 'plot',
-
- },
- bordercolor: {
- valType: 'color',
- dflt: colorAttributes.defaultLine,
-
- editType: 'plot',
-
- },
- borderwidth: {
- valType: 'integer',
- dflt: 0,
- min: 0,
-
- editType: 'plot',
-
- },
- autorange: {
- valType: 'boolean',
- dflt: true,
-
- editType: 'calc',
- impliedEdits: {'range[0]': undefined, 'range[1]': undefined},
-
- },
- range: {
- valType: 'info_array',
-
- items: [
- {valType: 'any', editType: 'calc', impliedEdits: {'^autorange': false}},
- {valType: 'any', editType: 'calc', impliedEdits: {'^autorange': false}}
- ],
- editType: 'calc',
- impliedEdits: {'autorange': false},
-
- },
- thickness: {
- valType: 'number',
- dflt: 0.15,
- min: 0,
- max: 1,
-
- editType: 'plot',
-
- },
- visible: {
- valType: 'boolean',
- dflt: true,
-
- editType: 'calc',
-
- },
- editType: 'calc'
- };
-
- },{"../color/attributes":50}],120:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var listAxes = _dereq_('../../plots/cartesian/axis_ids').list;
- var getAutoRange = _dereq_('../../plots/cartesian/autorange').getAutoRange;
- var constants = _dereq_('./constants');
-
- module.exports = function calcAutorange(gd) {
- var axes = listAxes(gd, 'x', true);
-
- // Compute new slider range using axis autorange if necessary.
- //
- // Copy back range to input range slider container to skip
- // this step in subsequent draw calls.
-
- for(var i = 0; i < axes.length; i++) {
- var ax = axes[i];
- var opts = ax[constants.name];
-
- if(opts && opts.visible && opts.autorange) {
- opts._input.autorange = true;
- opts._input.range = opts.range = getAutoRange(gd, ax);
- }
- }
- };
-
- },{"../../plots/cartesian/autorange":211,"../../plots/cartesian/axis_ids":215,"./constants":121}],121:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
-
- // attribute container name
- name: 'rangeslider',
-
- // class names
-
- containerClassName: 'rangeslider-container',
- bgClassName: 'rangeslider-bg',
- rangePlotClassName: 'rangeslider-rangeplot',
-
- maskMinClassName: 'rangeslider-mask-min',
- maskMaxClassName: 'rangeslider-mask-max',
- slideBoxClassName: 'rangeslider-slidebox',
-
- grabberMinClassName: 'rangeslider-grabber-min',
- grabAreaMinClassName: 'rangeslider-grabarea-min',
- handleMinClassName: 'rangeslider-handle-min',
-
- grabberMaxClassName: 'rangeslider-grabber-max',
- grabAreaMaxClassName: 'rangeslider-grabarea-max',
- handleMaxClassName: 'rangeslider-handle-max',
-
- maskMinOppAxisClassName: 'rangeslider-mask-min-opp-axis',
- maskMaxOppAxisClassName: 'rangeslider-mask-max-opp-axis',
-
- // style constants
-
- maskColor: 'rgba(0,0,0,0.4)',
- maskOppAxisColor: 'rgba(0,0,0,0.2)',
-
- slideBoxFill: 'transparent',
- slideBoxCursor: 'ew-resize',
-
- grabAreaFill: 'transparent',
- grabAreaCursor: 'col-resize',
- grabAreaWidth: 10,
-
- handleWidth: 4,
- handleRadius: 1,
- handleStrokeWidth: 1,
-
- extraPad: 15
- };
-
- },{}],122:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Template = _dereq_('../../plot_api/plot_template');
- var axisIds = _dereq_('../../plots/cartesian/axis_ids');
-
- var attributes = _dereq_('./attributes');
- var oppAxisAttrs = _dereq_('./oppaxis_attributes');
-
- module.exports = function handleDefaults(layoutIn, layoutOut, axName) {
- var axIn = layoutIn[axName];
- var axOut = layoutOut[axName];
-
- if(!(axIn.rangeslider || layoutOut._requestRangeslider[axOut._id])) return;
-
- // not super proud of this (maybe store _ in axis object instead
- if(!Lib.isPlainObject(axIn.rangeslider)) {
- axIn.rangeslider = {};
- }
-
- var containerIn = axIn.rangeslider;
- var containerOut = Template.newContainer(axOut, 'rangeslider');
-
- function coerce(attr, dflt) {
- return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
- }
-
- var rangeContainerIn, rangeContainerOut;
- function coerceRange(attr, dflt) {
- return Lib.coerce(rangeContainerIn, rangeContainerOut, oppAxisAttrs, attr, dflt);
- }
-
- var visible = coerce('visible');
- if(!visible) return;
-
- coerce('bgcolor', layoutOut.plot_bgcolor);
- coerce('bordercolor');
- coerce('borderwidth');
- coerce('thickness');
-
- coerce('autorange', !axOut.isValidRange(containerIn.range));
- coerce('range');
-
- var subplots = layoutOut._subplots;
- if(subplots) {
- var yIds = subplots.cartesian
- .filter(function(subplotId) {
- return subplotId.substr(0, subplotId.indexOf('y')) === axisIds.name2id(axName);
- })
- .map(function(subplotId) {
- return subplotId.substr(subplotId.indexOf('y'), subplotId.length);
- });
- var yNames = Lib.simpleMap(yIds, axisIds.id2name);
- for(var i = 0; i < yNames.length; i++) {
- var yName = yNames[i];
-
- rangeContainerIn = containerIn[yName] || {};
- rangeContainerOut = Template.newContainer(containerOut, yName, 'yaxis');
-
- var yAxOut = layoutOut[yName];
-
- var rangemodeDflt;
- if(rangeContainerIn.range && yAxOut.isValidRange(rangeContainerIn.range)) {
- rangemodeDflt = 'fixed';
- }
-
- var rangeMode = coerceRange('rangemode', rangemodeDflt);
- if(rangeMode !== 'match') {
- coerceRange('range', yAxOut.range.slice());
- }
- }
- }
-
- // to map back range slider (auto) range
- containerOut._input = containerIn;
- };
-
- },{"../../lib":168,"../../plot_api/plot_template":202,"../../plots/cartesian/axis_ids":215,"./attributes":119,"./oppaxis_attributes":126}],123:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Registry = _dereq_('../../registry');
- var Plots = _dereq_('../../plots/plots');
-
- var Lib = _dereq_('../../lib');
- var Drawing = _dereq_('../drawing');
- var Color = _dereq_('../color');
- var Titles = _dereq_('../titles');
-
- var Cartesian = _dereq_('../../plots/cartesian');
- var axisIDs = _dereq_('../../plots/cartesian/axis_ids');
-
- var dragElement = _dereq_('../dragelement');
- var setCursor = _dereq_('../../lib/setcursor');
-
- var constants = _dereq_('./constants');
-
- module.exports = function(gd) {
- var fullLayout = gd._fullLayout;
- var rangeSliderData = fullLayout._rangeSliderData;
- for(var i = 0; i < rangeSliderData.length; i++) {
- var opts = rangeSliderData[i][constants.name];
- // fullLayout._uid may not exist when we call makeData
- opts._clipId = opts._id + '-' + fullLayout._uid;
- }
-
- /*
- * <g container />
- * <rect bg />
- * < .... range plot />
- * <rect mask-min />
- * <rect mask-max />
- * <rect slidebox />
- * <g grabber-min />
- * <rect handle-min />
- * <rect grabare-min />
- * <g grabber-max />
- * <rect handle-max />
- * <rect grabare-max />
- *
- * ...
- */
-
- function keyFunction(axisOpts) {
- return axisOpts._name;
- }
-
- var rangeSliders = fullLayout._infolayer
- .selectAll('g.' + constants.containerClassName)
- .data(rangeSliderData, keyFunction);
-
- // remove exiting sliders and their corresponding clip paths
- rangeSliders.exit().each(function(axisOpts) {
- var opts = axisOpts[constants.name];
- fullLayout._topdefs.select('#' + opts._clipId).remove();
- }).remove();
-
- // return early if no range slider is visible
- if(rangeSliderData.length === 0) return;
-
- rangeSliders.enter().append('g')
- .classed(constants.containerClassName, true)
- .attr('pointer-events', 'all');
-
- // for all present range sliders
- rangeSliders.each(function(axisOpts) {
- var rangeSlider = d3.select(this);
- var opts = axisOpts[constants.name];
- var oppAxisOpts = fullLayout[axisIDs.id2name(axisOpts.anchor)];
- var oppAxisRangeOpts = opts[axisIDs.id2name(axisOpts.anchor)];
-
- // update range
- // Expand slider range to the axis range
- if(opts.range) {
- var rng = Lib.simpleMap(opts.range, axisOpts.r2l);
- var axRng = Lib.simpleMap(axisOpts.range, axisOpts.r2l);
- var newRng;
-
- if(axRng[0] < axRng[1]) {
- newRng = [
- Math.min(rng[0], axRng[0]),
- Math.max(rng[1], axRng[1])
- ];
- } else {
- newRng = [
- Math.max(rng[0], axRng[0]),
- Math.min(rng[1], axRng[1])
- ];
- }
-
- opts.range = opts._input.range = Lib.simpleMap(newRng, axisOpts.l2r);
- }
-
- axisOpts.cleanRange('rangeslider.range');
-
- // update range slider dimensions
-
- var margin = fullLayout.margin;
- var graphSize = fullLayout._size;
- var domain = axisOpts.domain;
- var tickHeight = opts._tickHeight;
-
- var oppBottom = opts._oppBottom;
-
- opts._width = graphSize.w * (domain[1] - domain[0]);
-
- var x = Math.round(margin.l + (graphSize.w * domain[0]));
-
- var y = Math.round(
- graphSize.t + graphSize.h * (1 - oppBottom) +
- tickHeight +
- opts._offsetShift + constants.extraPad
- );
-
- rangeSlider.attr('transform', 'translate(' + x + ',' + y + ')');
-
- // update data <--> pixel coordinate conversion methods
-
- var range0 = axisOpts.r2l(opts.range[0]);
- var range1 = axisOpts.r2l(opts.range[1]);
- var dist = range1 - range0;
-
- opts.p2d = function(v) {
- return (v / opts._width) * dist + range0;
- };
-
- opts.d2p = function(v) {
- return (v - range0) / dist * opts._width;
- };
-
- opts._rl = [range0, range1];
-
- if(oppAxisRangeOpts.rangemode !== 'match') {
- var range0OppAxis = oppAxisOpts.r2l(oppAxisRangeOpts.range[0]);
- var range1OppAxis = oppAxisOpts.r2l(oppAxisRangeOpts.range[1]);
- var distOppAxis = range1OppAxis - range0OppAxis;
-
- opts.d2pOppAxis = function(v) {
- return (v - range0OppAxis) / distOppAxis * opts._height;
- };
- }
-
- // update inner nodes
-
- rangeSlider
- .call(drawBg, gd, axisOpts, opts)
- .call(addClipPath, gd, axisOpts, opts)
- .call(drawRangePlot, gd, axisOpts, opts)
- .call(drawMasks, gd, axisOpts, opts, oppAxisRangeOpts)
- .call(drawSlideBox, gd, axisOpts, opts)
- .call(drawGrabbers, gd, axisOpts, opts);
-
- // setup drag element
- setupDragElement(rangeSlider, gd, axisOpts, opts);
-
- // update current range
- setPixelRange(rangeSlider, gd, axisOpts, opts, oppAxisOpts, oppAxisRangeOpts);
-
- // title goes next to range slider instead of tick labels, so
- // just take it over and draw it from here
- if(axisOpts.side === 'bottom') {
- Titles.draw(gd, axisOpts._id + 'title', {
- propContainer: axisOpts,
- propName: axisOpts._name + '.title',
- placeholder: fullLayout._dfltTitle.x,
- attributes: {
- x: axisOpts._offset + axisOpts._length / 2,
- y: y + opts._height + opts._offsetShift + 10 + 1.5 * axisOpts.title.font.size,
- 'text-anchor': 'middle'
- }
- });
- }
- });
- };
-
- function setupDragElement(rangeSlider, gd, axisOpts, opts) {
- var slideBox = rangeSlider.select('rect.' + constants.slideBoxClassName).node();
- var grabAreaMin = rangeSlider.select('rect.' + constants.grabAreaMinClassName).node();
- var grabAreaMax = rangeSlider.select('rect.' + constants.grabAreaMaxClassName).node();
-
- rangeSlider.on('mousedown', function() {
- var event = d3.event;
- var target = event.target;
- var startX = event.clientX;
- var offsetX = startX - rangeSlider.node().getBoundingClientRect().left;
- var minVal = opts.d2p(axisOpts._rl[0]);
- var maxVal = opts.d2p(axisOpts._rl[1]);
-
- var dragCover = dragElement.coverSlip();
-
- dragCover.addEventListener('mousemove', mouseMove);
- dragCover.addEventListener('mouseup', mouseUp);
-
- function mouseMove(e) {
- var delta = +e.clientX - startX;
- var pixelMin, pixelMax, cursor;
-
- switch(target) {
- case slideBox:
- cursor = 'ew-resize';
- pixelMin = minVal + delta;
- pixelMax = maxVal + delta;
- break;
-
- case grabAreaMin:
- cursor = 'col-resize';
- pixelMin = minVal + delta;
- pixelMax = maxVal;
- break;
-
- case grabAreaMax:
- cursor = 'col-resize';
- pixelMin = minVal;
- pixelMax = maxVal + delta;
- break;
-
- default:
- cursor = 'ew-resize';
- pixelMin = offsetX;
- pixelMax = offsetX + delta;
- break;
- }
-
- if(pixelMax < pixelMin) {
- var tmp = pixelMax;
- pixelMax = pixelMin;
- pixelMin = tmp;
- }
-
- opts._pixelMin = pixelMin;
- opts._pixelMax = pixelMax;
-
- setCursor(d3.select(dragCover), cursor);
- setDataRange(rangeSlider, gd, axisOpts, opts);
- }
-
- function mouseUp() {
- dragCover.removeEventListener('mousemove', mouseMove);
- dragCover.removeEventListener('mouseup', mouseUp);
- Lib.removeElement(dragCover);
- }
- });
- }
-
- function setDataRange(rangeSlider, gd, axisOpts, opts) {
-
- function clamp(v) {
- return axisOpts.l2r(Lib.constrain(v, opts._rl[0], opts._rl[1]));
- }
-
- var dataMin = clamp(opts.p2d(opts._pixelMin));
- var dataMax = clamp(opts.p2d(opts._pixelMax));
-
- window.requestAnimationFrame(function() {
- Registry.call('_guiRelayout', gd, axisOpts._name + '.range', [dataMin, dataMax]);
- });
- }
-
- function setPixelRange(rangeSlider, gd, axisOpts, opts, oppAxisOpts, oppAxisRangeOpts) {
- var hw2 = constants.handleWidth / 2;
-
- function clamp(v) {
- return Lib.constrain(v, 0, opts._width);
- }
-
- function clampOppAxis(v) {
- return Lib.constrain(v, 0, opts._height);
- }
-
- function clampHandle(v) {
- return Lib.constrain(v, -hw2, opts._width + hw2);
- }
-
- var pixelMin = clamp(opts.d2p(axisOpts._rl[0]));
- var pixelMax = clamp(opts.d2p(axisOpts._rl[1]));
-
- rangeSlider.select('rect.' + constants.slideBoxClassName)
- .attr('x', pixelMin)
- .attr('width', pixelMax - pixelMin);
-
- rangeSlider.select('rect.' + constants.maskMinClassName)
- .attr('width', pixelMin);
-
- rangeSlider.select('rect.' + constants.maskMaxClassName)
- .attr('x', pixelMax)
- .attr('width', opts._width - pixelMax);
-
- if(oppAxisRangeOpts.rangemode !== 'match') {
- var pixelMinOppAxis = opts._height - clampOppAxis(opts.d2pOppAxis(oppAxisOpts._rl[1]));
- var pixelMaxOppAxis = opts._height - clampOppAxis(opts.d2pOppAxis(oppAxisOpts._rl[0]));
-
- rangeSlider.select('rect.' + constants.maskMinOppAxisClassName)
- .attr('x', pixelMin)
- .attr('height', pixelMinOppAxis)
- .attr('width', pixelMax - pixelMin);
-
- rangeSlider.select('rect.' + constants.maskMaxOppAxisClassName)
- .attr('x', pixelMin)
- .attr('y', pixelMaxOppAxis)
- .attr('height', opts._height - pixelMaxOppAxis)
- .attr('width', pixelMax - pixelMin);
-
- rangeSlider.select('rect.' + constants.slideBoxClassName)
- .attr('y', pixelMinOppAxis)
- .attr('height', pixelMaxOppAxis - pixelMinOppAxis);
- }
-
- // add offset for crispier corners
- // https://github.com/plotly/plotly.js/pull/1409
- var offset = 0.5;
-
- var xMin = Math.round(clampHandle(pixelMin - hw2)) - offset;
- var xMax = Math.round(clampHandle(pixelMax - hw2)) + offset;
-
- rangeSlider.select('g.' + constants.grabberMinClassName)
- .attr('transform', 'translate(' + xMin + ',' + offset + ')');
-
- rangeSlider.select('g.' + constants.grabberMaxClassName)
- .attr('transform', 'translate(' + xMax + ',' + offset + ')');
- }
-
- function drawBg(rangeSlider, gd, axisOpts, opts) {
- var bg = Lib.ensureSingle(rangeSlider, 'rect', constants.bgClassName, function(s) {
- s.attr({
- x: 0,
- y: 0,
- 'shape-rendering': 'crispEdges'
- });
- });
-
- var borderCorrect = (opts.borderwidth % 2) === 0 ?
- opts.borderwidth :
- opts.borderwidth - 1;
-
- var offsetShift = -opts._offsetShift;
- var lw = Drawing.crispRound(gd, opts.borderwidth);
-
- bg.attr({
- width: opts._width + borderCorrect,
- height: opts._height + borderCorrect,
- transform: 'translate(' + offsetShift + ',' + offsetShift + ')',
- fill: opts.bgcolor,
- stroke: opts.bordercolor,
- 'stroke-width': lw
- });
- }
-
- function addClipPath(rangeSlider, gd, axisOpts, opts) {
- var fullLayout = gd._fullLayout;
-
- var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', opts._clipId, function(s) {
- s.append('rect').attr({ x: 0, y: 0 });
- });
-
- clipPath.select('rect').attr({
- width: opts._width,
- height: opts._height
- });
- }
-
- function drawRangePlot(rangeSlider, gd, axisOpts, opts) {
- var calcData = gd.calcdata;
-
- var rangePlots = rangeSlider.selectAll('g.' + constants.rangePlotClassName)
- .data(axisOpts._subplotsWith, Lib.identity);
-
- rangePlots.enter().append('g')
- .attr('class', function(id) { return constants.rangePlotClassName + ' ' + id; })
- .call(Drawing.setClipUrl, opts._clipId, gd);
-
- rangePlots.order();
-
- rangePlots.exit().remove();
-
- var mainplotinfo;
-
- rangePlots.each(function(id, i) {
- var plotgroup = d3.select(this);
- var isMainPlot = (i === 0);
-
- var oppAxisOpts = axisIDs.getFromId(gd, id, 'y');
- var oppAxisName = oppAxisOpts._name;
- var oppAxisRangeOpts = opts[oppAxisName];
-
- var mockFigure = {
- data: [],
- layout: {
- xaxis: {
- type: axisOpts.type,
- domain: [0, 1],
- range: opts.range.slice(),
- calendar: axisOpts.calendar
- },
- width: opts._width,
- height: opts._height,
- margin: { t: 0, b: 0, l: 0, r: 0 }
- },
- _context: gd._context
- };
-
- mockFigure.layout[oppAxisName] = {
- type: oppAxisOpts.type,
- domain: [0, 1],
- range: oppAxisRangeOpts.rangemode !== 'match' ? oppAxisRangeOpts.range.slice() : oppAxisOpts.range.slice(),
- calendar: oppAxisOpts.calendar
- };
-
- Plots.supplyDefaults(mockFigure);
-
- var xa = mockFigure._fullLayout.xaxis;
- var ya = mockFigure._fullLayout[oppAxisName];
-
- xa.clearCalc();
- xa.setScale();
- ya.clearCalc();
- ya.setScale();
-
- var plotinfo = {
- id: id,
- plotgroup: plotgroup,
- xaxis: xa,
- yaxis: ya,
- isRangePlot: true
- };
-
- if(isMainPlot) mainplotinfo = plotinfo;
- else {
- plotinfo.mainplot = 'xy';
- plotinfo.mainplotinfo = mainplotinfo;
- }
-
- Cartesian.rangePlot(gd, plotinfo, filterRangePlotCalcData(calcData, id));
- });
- }
-
- function filterRangePlotCalcData(calcData, subplotId) {
- var out = [];
-
- for(var i = 0; i < calcData.length; i++) {
- var calcTrace = calcData[i];
- var trace = calcTrace[0].trace;
-
- if(trace.xaxis + trace.yaxis === subplotId) {
- out.push(calcTrace);
- }
- }
-
- return out;
- }
-
- function drawMasks(rangeSlider, gd, axisOpts, opts, oppAxisRangeOpts) {
- var maskMin = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMinClassName, function(s) {
- s.attr({
- x: 0,
- y: 0,
- 'shape-rendering': 'crispEdges'
- });
- });
-
- maskMin
- .attr('height', opts._height)
- .call(Color.fill, constants.maskColor);
-
- var maskMax = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMaxClassName, function(s) {
- s.attr({
- y: 0,
- 'shape-rendering': 'crispEdges'
- });
- });
-
- maskMax
- .attr('height', opts._height)
- .call(Color.fill, constants.maskColor);
-
- // masks used for oppAxis zoom
- if(oppAxisRangeOpts.rangemode !== 'match') {
- var maskMinOppAxis = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMinOppAxisClassName, function(s) {
- s.attr({
- y: 0,
- 'shape-rendering': 'crispEdges'
- });
- });
-
- maskMinOppAxis
- .attr('width', opts._width)
- .call(Color.fill, constants.maskOppAxisColor);
-
- var maskMaxOppAxis = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMaxOppAxisClassName, function(s) {
- s.attr({
- y: 0,
- 'shape-rendering': 'crispEdges'
- });
- });
-
- maskMaxOppAxis
- .attr('width', opts._width)
- .style('border-top', constants.maskOppBorder)
- .call(Color.fill, constants.maskOppAxisColor);
- }
- }
-
- function drawSlideBox(rangeSlider, gd, axisOpts, opts) {
- if(gd._context.staticPlot) return;
-
- var slideBox = Lib.ensureSingle(rangeSlider, 'rect', constants.slideBoxClassName, function(s) {
- s.attr({
- y: 0,
- cursor: constants.slideBoxCursor,
- 'shape-rendering': 'crispEdges'
- });
- });
-
- slideBox.attr({
- height: opts._height,
- fill: constants.slideBoxFill
- });
- }
-
- function drawGrabbers(rangeSlider, gd, axisOpts, opts) {
- // <g grabber />
- var grabberMin = Lib.ensureSingle(rangeSlider, 'g', constants.grabberMinClassName);
- var grabberMax = Lib.ensureSingle(rangeSlider, 'g', constants.grabberMaxClassName);
-
- // <g handle />
- var handleFixAttrs = {
- x: 0,
- width: constants.handleWidth,
- rx: constants.handleRadius,
- fill: Color.background,
- stroke: Color.defaultLine,
- 'stroke-width': constants.handleStrokeWidth,
- 'shape-rendering': 'crispEdges'
- };
- var handleDynamicAttrs = {
- y: Math.round(opts._height / 4),
- height: Math.round(opts._height / 2),
- };
- var handleMin = Lib.ensureSingle(grabberMin, 'rect', constants.handleMinClassName, function(s) {
- s.attr(handleFixAttrs);
- });
- handleMin.attr(handleDynamicAttrs);
-
- var handleMax = Lib.ensureSingle(grabberMax, 'rect', constants.handleMaxClassName, function(s) {
- s.attr(handleFixAttrs);
- });
- handleMax.attr(handleDynamicAttrs);
-
- // <g grabarea />
- if(gd._context.staticPlot) return;
-
- var grabAreaFixAttrs = {
- width: constants.grabAreaWidth,
- x: 0,
- y: 0,
- fill: constants.grabAreaFill,
- cursor: constants.grabAreaCursor
- };
-
- var grabAreaMin = Lib.ensureSingle(grabberMin, 'rect', constants.grabAreaMinClassName, function(s) {
- s.attr(grabAreaFixAttrs);
- });
- grabAreaMin.attr('height', opts._height);
-
- var grabAreaMax = Lib.ensureSingle(grabberMax, 'rect', constants.grabAreaMaxClassName, function(s) {
- s.attr(grabAreaFixAttrs);
- });
- grabAreaMax.attr('height', opts._height);
- }
-
- },{"../../lib":168,"../../lib/setcursor":187,"../../plots/cartesian":224,"../../plots/cartesian/axis_ids":215,"../../plots/plots":245,"../../registry":257,"../color":51,"../dragelement":69,"../drawing":72,"../titles":139,"./constants":121,"d3":16}],124:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var axisIDs = _dereq_('../../plots/cartesian/axis_ids');
- var constants = _dereq_('./constants');
- var name = constants.name;
-
- function isVisible(ax) {
- var rangeSlider = ax && ax[name];
- return rangeSlider && rangeSlider.visible;
- }
- exports.isVisible = isVisible;
-
- exports.makeData = function(fullLayout) {
- var axes = axisIDs.list({ _fullLayout: fullLayout }, 'x', true);
- var margin = fullLayout.margin;
- var rangeSliderData = [];
-
- if(!fullLayout._has('gl2d')) {
- for(var i = 0; i < axes.length; i++) {
- var ax = axes[i];
-
- if(isVisible(ax)) {
- rangeSliderData.push(ax);
-
- var opts = ax[name];
- opts._id = name + ax._id;
- opts._height = (fullLayout.height - margin.b - margin.t) * opts.thickness;
- opts._offsetShift = Math.floor(opts.borderwidth / 2);
- }
- }
- }
-
- fullLayout._rangeSliderData = rangeSliderData;
- };
-
- exports.autoMarginOpts = function(gd, ax) {
- var opts = ax[name];
-
- var oppBottom = Infinity;
- var counterAxes = ax._counterAxes;
- for(var j = 0; j < counterAxes.length; j++) {
- var counterId = counterAxes[j];
- var oppAxis = axisIDs.getFromId(gd, counterId);
- oppBottom = Math.min(oppBottom, oppAxis.domain[0]);
- }
- opts._oppBottom = oppBottom;
-
- var tickHeight = (ax.side === 'bottom' && ax._boundingBox.height) || 0;
- opts._tickHeight = tickHeight;
-
- return {
- x: 0,
- y: oppBottom,
- l: 0,
- r: 0,
- t: 0,
- b: opts._height + gd._fullLayout.margin.b + tickHeight,
- pad: constants.extraPad + opts._offsetShift * 2
- };
- };
-
- },{"../../plots/cartesian/axis_ids":215,"./constants":121}],125:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var attrs = _dereq_('./attributes');
- var oppAxisAttrs = _dereq_('./oppaxis_attributes');
- var helpers = _dereq_('./helpers');
-
- module.exports = {
- moduleType: 'component',
- name: 'rangeslider',
-
- schema: {
- subplots: {
- xaxis: {
- rangeslider: Lib.extendFlat({}, attrs, {
- yaxis: oppAxisAttrs
- })
- }
- }
- },
-
- layoutAttributes: _dereq_('./attributes'),
- handleDefaults: _dereq_('./defaults'),
- calcAutorange: _dereq_('./calc_autorange'),
- draw: _dereq_('./draw'),
- isVisible: helpers.isVisible,
- makeData: helpers.makeData,
- autoMarginOpts: helpers.autoMarginOpts
- };
-
- },{"../../lib":168,"./attributes":119,"./calc_autorange":120,"./defaults":122,"./draw":123,"./helpers":124,"./oppaxis_attributes":126}],126:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- // not really a 'subplot' attribute container,
- // but this is the flag we use to denote attributes that
- // support yaxis, yaxis2, yaxis3, ... counters
- _isSubplotObj: true,
-
- rangemode: {
- valType: 'enumerated',
- values: ['auto', 'fixed', 'match'],
- dflt: 'match',
-
- editType: 'calc',
-
- },
- range: {
- valType: 'info_array',
-
- items: [
- {valType: 'any', editType: 'plot'},
- {valType: 'any', editType: 'plot'}
- ],
- editType: 'plot',
-
- },
- editType: 'calc'
- };
-
- },{}],127:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var annAttrs = _dereq_('../annotations/attributes');
- var scatterLineAttrs = _dereq_('../../traces/scatter/attributes').line;
- var dash = _dereq_('../drawing/attributes').dash;
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
- var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
-
- module.exports = templatedArray('shape', {
- visible: {
- valType: 'boolean',
-
- dflt: true,
- editType: 'calc+arraydraw',
-
- },
-
- type: {
- valType: 'enumerated',
- values: ['circle', 'rect', 'path', 'line'],
-
- editType: 'calc+arraydraw',
-
- },
-
- layer: {
- valType: 'enumerated',
- values: ['below', 'above'],
- dflt: 'above',
-
- editType: 'arraydraw',
-
- },
-
- xref: extendFlat({}, annAttrs.xref, {
-
- }),
- xsizemode: {
- valType: 'enumerated',
- values: ['scaled', 'pixel'],
- dflt: 'scaled',
-
- editType: 'calc+arraydraw',
-
- },
- xanchor: {
- valType: 'any',
-
- editType: 'calc+arraydraw',
-
- },
- x0: {
- valType: 'any',
-
- editType: 'calc+arraydraw',
-
- },
- x1: {
- valType: 'any',
-
- editType: 'calc+arraydraw',
-
- },
-
- yref: extendFlat({}, annAttrs.yref, {
-
- }),
- ysizemode: {
- valType: 'enumerated',
- values: ['scaled', 'pixel'],
- dflt: 'scaled',
-
- editType: 'calc+arraydraw',
-
- },
- yanchor: {
- valType: 'any',
-
- editType: 'calc+arraydraw',
-
- },
- y0: {
- valType: 'any',
-
- editType: 'calc+arraydraw',
-
- },
- y1: {
- valType: 'any',
-
- editType: 'calc+arraydraw',
-
- },
-
- path: {
- valType: 'string',
-
- editType: 'calc+arraydraw',
-
- },
-
- opacity: {
- valType: 'number',
- min: 0,
- max: 1,
- dflt: 1,
-
- editType: 'arraydraw',
-
- },
- line: {
- color: extendFlat({}, scatterLineAttrs.color, {editType: 'arraydraw'}),
- width: extendFlat({}, scatterLineAttrs.width, {editType: 'calc+arraydraw'}),
- dash: extendFlat({}, dash, {editType: 'arraydraw'}),
-
- editType: 'calc+arraydraw'
- },
- fillcolor: {
- valType: 'color',
- dflt: 'rgba(0,0,0,0)',
-
- editType: 'arraydraw',
-
- },
- editType: 'arraydraw'
- });
-
- },{"../../lib/extend":162,"../../plot_api/plot_template":202,"../../traces/scatter/attributes":367,"../annotations/attributes":36,"../drawing/attributes":71}],128:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
-
- var constants = _dereq_('./constants');
- var helpers = _dereq_('./helpers');
-
-
- module.exports = function calcAutorange(gd) {
- var fullLayout = gd._fullLayout;
- var shapeList = Lib.filterVisible(fullLayout.shapes);
-
- if(!shapeList.length || !gd._fullData.length) return;
-
- for(var i = 0; i < shapeList.length; i++) {
- var shape = shapeList[i];
- shape._extremes = {};
-
- var ax, bounds;
-
- if(shape.xref !== 'paper') {
- var vx0 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x0;
- var vx1 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x1;
- ax = Axes.getFromId(gd, shape.xref);
-
- bounds = shapeBounds(ax, vx0, vx1, shape.path, constants.paramIsX);
- if(bounds) {
- shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcXPaddingOptions(shape));
- }
- }
-
- if(shape.yref !== 'paper') {
- var vy0 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y0;
- var vy1 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y1;
- ax = Axes.getFromId(gd, shape.yref);
-
- bounds = shapeBounds(ax, vy0, vy1, shape.path, constants.paramIsY);
- if(bounds) {
- shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcYPaddingOptions(shape));
- }
- }
- }
- };
-
- function calcXPaddingOptions(shape) {
- return calcPaddingOptions(shape.line.width, shape.xsizemode, shape.x0, shape.x1, shape.path, false);
- }
-
- function calcYPaddingOptions(shape) {
- return calcPaddingOptions(shape.line.width, shape.ysizemode, shape.y0, shape.y1, shape.path, true);
- }
-
- function calcPaddingOptions(lineWidth, sizeMode, v0, v1, path, isYAxis) {
- var ppad = lineWidth / 2;
- var axisDirectionReverted = isYAxis;
-
- if(sizeMode === 'pixel') {
- var coords = path ?
- helpers.extractPathCoords(path, isYAxis ? constants.paramIsY : constants.paramIsX) :
- [v0, v1];
- var maxValue = Lib.aggNums(Math.max, null, coords);
- var minValue = Lib.aggNums(Math.min, null, coords);
- var beforePad = minValue < 0 ? Math.abs(minValue) + ppad : ppad;
- var afterPad = maxValue > 0 ? maxValue + ppad : ppad;
-
- return {
- ppad: ppad,
- ppadplus: axisDirectionReverted ? beforePad : afterPad,
- ppadminus: axisDirectionReverted ? afterPad : beforePad
- };
- } else {
- return {ppad: ppad};
- }
- }
-
- function shapeBounds(ax, v0, v1, path, paramsToUse) {
- var convertVal = (ax.type === 'category' || ax.type === 'multicategory') ? ax.r2c : ax.d2c;
-
- if(v0 !== undefined) return [convertVal(v0), convertVal(v1)];
- if(!path) return;
-
- var min = Infinity;
- var max = -Infinity;
- var segments = path.match(constants.segmentRE);
- var i;
- var segment;
- var drawnParam;
- var params;
- var val;
-
- if(ax.type === 'date') convertVal = helpers.decodeDate(convertVal);
-
- for(i = 0; i < segments.length; i++) {
- segment = segments[i];
- drawnParam = paramsToUse[segment.charAt(0)].drawn;
- if(drawnParam === undefined) continue;
-
- params = segments[i].substr(1).match(constants.paramRE);
- if(!params || params.length < drawnParam) continue;
-
- val = convertVal(params[drawnParam]);
- if(val < min) min = val;
- if(val > max) max = val;
- }
- if(max >= min) return [min, max];
- }
-
- },{"../../lib":168,"../../plots/cartesian/axes":212,"./constants":129,"./helpers":132}],129:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- module.exports = {
- segmentRE: /[MLHVQCTSZ][^MLHVQCTSZ]*/g,
- paramRE: /[^\s,]+/g,
-
- // which numbers in each path segment are x (or y) values
- // drawn is which param is a drawn point, as opposed to a
- // control point (which doesn't count toward autorange.
- // TODO: this means curved paths could extend beyond the
- // autorange bounds. This is a bit tricky to get right
- // unless we revert to bounding boxes, but perhaps there's
- // a calculation we could do...)
- paramIsX: {
- M: {0: true, drawn: 0},
- L: {0: true, drawn: 0},
- H: {0: true, drawn: 0},
- V: {},
- Q: {0: true, 2: true, drawn: 2},
- C: {0: true, 2: true, 4: true, drawn: 4},
- T: {0: true, drawn: 0},
- S: {0: true, 2: true, drawn: 2},
- // A: {0: true, 5: true},
- Z: {}
- },
-
- paramIsY: {
- M: {1: true, drawn: 1},
- L: {1: true, drawn: 1},
- H: {},
- V: {0: true, drawn: 0},
- Q: {1: true, 3: true, drawn: 3},
- C: {1: true, 3: true, 5: true, drawn: 5},
- T: {1: true, drawn: 1},
- S: {1: true, 3: true, drawn: 5},
- // A: {1: true, 6: true},
- Z: {}
- },
-
- numParams: {
- M: 2,
- L: 2,
- H: 1,
- V: 1,
- Q: 4,
- C: 6,
- T: 2,
- S: 4,
- // A: 7,
- Z: 0
- }
- };
-
- },{}],130:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
-
- var attributes = _dereq_('./attributes');
- var helpers = _dereq_('./helpers');
-
-
- module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
- handleArrayContainerDefaults(layoutIn, layoutOut, {
- name: 'shapes',
- handleItemDefaults: handleShapeDefaults
- });
- };
-
- function handleShapeDefaults(shapeIn, shapeOut, fullLayout) {
- function coerce(attr, dflt) {
- return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
- }
-
- var visible = coerce('visible');
-
- if(!visible) return;
-
- coerce('layer');
- coerce('opacity');
- coerce('fillcolor');
- coerce('line.color');
- coerce('line.width');
- coerce('line.dash');
-
- var dfltType = shapeIn.path ? 'path' : 'rect';
- var shapeType = coerce('type', dfltType);
- var xSizeMode = coerce('xsizemode');
- var ySizeMode = coerce('ysizemode');
-
- // positioning
- var axLetters = ['x', 'y'];
- for(var i = 0; i < 2; i++) {
- var axLetter = axLetters[i];
- var attrAnchor = axLetter + 'anchor';
- var sizeMode = axLetter === 'x' ? xSizeMode : ySizeMode;
- var gdMock = {_fullLayout: fullLayout};
- var ax;
- var pos2r;
- var r2pos;
-
- // xref, yref
- var axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, '', 'paper');
-
- if(axRef !== 'paper') {
- ax = Axes.getFromId(gdMock, axRef);
- ax._shapeIndices.push(shapeOut._index);
- r2pos = helpers.rangeToShapePosition(ax);
- pos2r = helpers.shapePositionToRange(ax);
- }
- else {
- pos2r = r2pos = Lib.identity;
- }
-
- // Coerce x0, x1, y0, y1
- if(shapeType !== 'path') {
- var dflt0 = 0.25;
- var dflt1 = 0.75;
-
- // hack until V2.0 when log has regular range behavior - make it look like other
- // ranges to send to coerce, then put it back after
- // this is all to give reasonable default position behavior on log axes, which is
- // a pretty unimportant edge case so we could just ignore this.
- var attr0 = axLetter + '0';
- var attr1 = axLetter + '1';
- var in0 = shapeIn[attr0];
- var in1 = shapeIn[attr1];
- shapeIn[attr0] = pos2r(shapeIn[attr0], true);
- shapeIn[attr1] = pos2r(shapeIn[attr1], true);
-
- if(sizeMode === 'pixel') {
- coerce(attr0, 0);
- coerce(attr1, 10);
- } else {
- Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr0, dflt0);
- Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr1, dflt1);
- }
-
- // hack part 2
- shapeOut[attr0] = r2pos(shapeOut[attr0]);
- shapeOut[attr1] = r2pos(shapeOut[attr1]);
- shapeIn[attr0] = in0;
- shapeIn[attr1] = in1;
- }
-
- // Coerce xanchor and yanchor
- if(sizeMode === 'pixel') {
- // Hack for log axis described above
- var inAnchor = shapeIn[attrAnchor];
- shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true);
-
- Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attrAnchor, 0.25);
-
- // Hack part 2
- shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
- shapeIn[attrAnchor] = inAnchor;
- }
- }
-
- if(shapeType === 'path') {
- coerce('path');
- }
- else {
- Lib.noneOrAll(shapeIn, shapeOut, ['x0', 'x1', 'y0', 'y1']);
- }
- }
-
- },{"../../lib":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"./attributes":127,"./helpers":132}],131:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var Color = _dereq_('../color');
- var Drawing = _dereq_('../drawing');
- var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
-
- var dragElement = _dereq_('../dragelement');
- var setCursor = _dereq_('../../lib/setcursor');
-
- var constants = _dereq_('./constants');
- var helpers = _dereq_('./helpers');
-
-
- // Shapes are stored in gd.layout.shapes, an array of objects
- // index can point to one item in this array,
- // or non-numeric to simply add a new one
- // or -1 to modify all existing
- // opt can be the full options object, or one key (to be set to value)
- // or undefined to simply redraw
- // if opt is blank, val can be 'add' or a full options object to add a new
- // annotation at that point in the array, or 'remove' to delete this one
-
- module.exports = {
- draw: draw,
- drawOne: drawOne
- };
-
- function draw(gd) {
- var fullLayout = gd._fullLayout;
-
- // Remove previous shapes before drawing new in shapes in fullLayout.shapes
- fullLayout._shapeUpperLayer.selectAll('path').remove();
- fullLayout._shapeLowerLayer.selectAll('path').remove();
-
- for(var k in fullLayout._plots) {
- var shapelayer = fullLayout._plots[k].shapelayer;
- if(shapelayer) shapelayer.selectAll('path').remove();
- }
-
- for(var i = 0; i < fullLayout.shapes.length; i++) {
- if(fullLayout.shapes[i].visible) {
- drawOne(gd, i);
- }
- }
-
- // may need to resurrect this if we put text (LaTeX) in shapes
- // return Plots.previousPromises(gd);
- }
-
- function drawOne(gd, index) {
- // remove the existing shape if there is one.
- // because indices can change, we need to look in all shape layers
- gd._fullLayout._paperdiv
- .selectAll('.shapelayer [data-index="' + index + '"]')
- .remove();
-
- var options = gd._fullLayout.shapes[index] || {};
-
- // this shape is gone - quit now after deleting it
- // TODO: use d3 idioms instead of deleting and redrawing every time
- if(!options._input || options.visible === false) return;
-
- if(options.layer !== 'below') {
- drawShape(gd._fullLayout._shapeUpperLayer);
- }
- else if(options.xref === 'paper' || options.yref === 'paper') {
- drawShape(gd._fullLayout._shapeLowerLayer);
- }
- else {
- var plotinfo = gd._fullLayout._plots[options.xref + options.yref];
- if(plotinfo) {
- var mainPlot = plotinfo.mainplotinfo || plotinfo;
- drawShape(mainPlot.shapelayer);
- }
- else {
- // Fall back to _shapeLowerLayer in case the requested subplot doesn't exist.
- // This can happen if you reference the shape to an x / y axis combination
- // that doesn't have any data on it (and layer is below)
- drawShape(gd._fullLayout._shapeLowerLayer);
- }
- }
-
- function drawShape(shapeLayer) {
- var attrs = {
- 'data-index': index,
- 'fill-rule': 'evenodd',
- d: getPathString(gd, options)
- };
- var lineColor = options.line.width ? options.line.color : 'rgba(0,0,0,0)';
-
- var path = shapeLayer.append('path')
- .attr(attrs)
- .style('opacity', options.opacity)
- .call(Color.stroke, lineColor)
- .call(Color.fill, options.fillcolor)
- .call(Drawing.dashLine, options.line.dash, options.line.width);
-
- setClipPath(path, gd, options);
-
- if(gd._context.edits.shapePosition) setupDragElement(gd, path, options, index, shapeLayer);
- }
- }
-
- function setClipPath(shapePath, gd, shapeOptions) {
- // note that for layer="below" the clipAxes can be different from the
- // subplot we're drawing this in. This could cause problems if the shape
- // spans two subplots. See https://github.com/plotly/plotly.js/issues/1452
- var clipAxes = (shapeOptions.xref + shapeOptions.yref).replace(/paper/g, '');
-
- Drawing.setClipUrl(
- shapePath,
- clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
- gd
- );
- }
-
- function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer) {
- var MINWIDTH = 10;
- var MINHEIGHT = 10;
-
- var xPixelSized = shapeOptions.xsizemode === 'pixel';
- var yPixelSized = shapeOptions.ysizemode === 'pixel';
- var isLine = shapeOptions.type === 'line';
- var isPath = shapeOptions.type === 'path';
-
- var editHelpers = arrayEditor(gd.layout, 'shapes', shapeOptions);
- var modifyItem = editHelpers.modifyItem;
-
- var x0, y0, x1, y1, xAnchor, yAnchor;
- var n0, s0, w0, e0, optN, optS, optW, optE;
- var pathIn;
-
- // setup conversion functions
- var xa = Axes.getFromId(gd, shapeOptions.xref);
- var ya = Axes.getFromId(gd, shapeOptions.yref);
- var x2p = helpers.getDataToPixel(gd, xa);
- var y2p = helpers.getDataToPixel(gd, ya, true);
- var p2x = helpers.getPixelToData(gd, xa);
- var p2y = helpers.getPixelToData(gd, ya, true);
-
- var sensoryElement = obtainSensoryElement();
- var dragOptions = {
- element: sensoryElement.node(),
- gd: gd,
- prepFn: startDrag,
- doneFn: endDrag,
- clickFn: abortDrag
- };
- var dragMode;
-
- dragElement.init(dragOptions);
-
- sensoryElement.node().onmousemove = updateDragMode;
-
- function obtainSensoryElement() {
- return isLine ? createLineDragHandles() : shapePath;
- }
-
- function createLineDragHandles() {
- var minSensoryWidth = 10;
- var sensoryWidth = Math.max(shapeOptions.line.width, minSensoryWidth);
-
- // Helper shapes group
- // Note that by setting the `data-index` attr, it is ensured that
- // the helper group is purged in this modules `draw` function
- var g = shapeLayer.append('g')
- .attr('data-index', index);
-
- // Helper path for moving
- g.append('path')
- .attr('d', shapePath.attr('d'))
- .style({
- 'cursor': 'move',
- 'stroke-width': sensoryWidth,
- 'stroke-opacity': '0' // ensure not visible
- });
-
- // Helper circles for resizing
- var circleStyle = {
- 'fill-opacity': '0' // ensure not visible
- };
- var circleRadius = sensoryWidth / 2 > minSensoryWidth ? sensoryWidth / 2 : minSensoryWidth;
-
- g.append('circle')
- .attr({
- 'data-line-point': 'start-point',
- 'cx': xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x0 : x2p(shapeOptions.x0),
- 'cy': yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y0 : y2p(shapeOptions.y0),
- 'r': circleRadius
- })
- .style(circleStyle)
- .classed('cursor-grab', true);
-
- g.append('circle')
- .attr({
- 'data-line-point': 'end-point',
- 'cx': xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x1 : x2p(shapeOptions.x1),
- 'cy': yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y1 : y2p(shapeOptions.y1),
- 'r': circleRadius
- })
- .style(circleStyle)
- .classed('cursor-grab', true);
-
- return g;
- }
-
- function updateDragMode(evt) {
- if(isLine) {
- if(evt.target.tagName === 'path') {
- dragMode = 'move';
- } else {
- dragMode = evt.target.attributes['data-line-point'].value === 'start-point' ?
- 'resize-over-start-point' : 'resize-over-end-point';
- }
- } else {
- // element might not be on screen at time of setup,
- // so obtain bounding box here
- var dragBBox = dragOptions.element.getBoundingClientRect();
-
- // choose 'move' or 'resize'
- // based on initial position of cursor within the drag element
- var w = dragBBox.right - dragBBox.left;
- var h = dragBBox.bottom - dragBBox.top;
- var x = evt.clientX - dragBBox.left;
- var y = evt.clientY - dragBBox.top;
- var cursor = (!isPath && w > MINWIDTH && h > MINHEIGHT && !evt.shiftKey) ?
- dragElement.getCursor(x / w, 1 - y / h) :
- 'move';
-
- setCursor(shapePath, cursor);
-
- // possible values 'move', 'sw', 'w', 'se', 'e', 'ne', 'n', 'nw' and 'w'
- dragMode = cursor.split('-')[0];
- }
- }
-
- function startDrag(evt) {
- // setup update strings and initial values
- if(xPixelSized) {
- xAnchor = x2p(shapeOptions.xanchor);
- }
- if(yPixelSized) {
- yAnchor = y2p(shapeOptions.yanchor);
- }
-
- if(shapeOptions.type === 'path') {
- pathIn = shapeOptions.path;
- }
- else {
- x0 = xPixelSized ? shapeOptions.x0 : x2p(shapeOptions.x0);
- y0 = yPixelSized ? shapeOptions.y0 : y2p(shapeOptions.y0);
- x1 = xPixelSized ? shapeOptions.x1 : x2p(shapeOptions.x1);
- y1 = yPixelSized ? shapeOptions.y1 : y2p(shapeOptions.y1);
- }
-
- if(x0 < x1) {
- w0 = x0;
- optW = 'x0';
- e0 = x1;
- optE = 'x1';
- }
- else {
- w0 = x1;
- optW = 'x1';
- e0 = x0;
- optE = 'x0';
- }
-
- // For fixed size shapes take opposing direction of y-axis into account.
- // Hint: For data sized shapes this is done by the y2p function.
- if((!yPixelSized && y0 < y1) || (yPixelSized && y0 > y1)) {
- n0 = y0;
- optN = 'y0';
- s0 = y1;
- optS = 'y1';
- }
- else {
- n0 = y1;
- optN = 'y1';
- s0 = y0;
- optS = 'y0';
- }
-
- // setup dragMode and the corresponding handler
- updateDragMode(evt);
- renderVisualCues(shapeLayer, shapeOptions);
- deactivateClipPathTemporarily(shapePath, shapeOptions, gd);
- dragOptions.moveFn = (dragMode === 'move') ? moveShape : resizeShape;
- }
-
- function endDrag() {
- setCursor(shapePath);
- removeVisualCues(shapeLayer);
-
- // Don't rely on clipPath being activated during re-layout
- setClipPath(shapePath, gd, shapeOptions);
- Registry.call('_guiRelayout', gd, editHelpers.getUpdateObj());
- }
-
- function abortDrag() {
- removeVisualCues(shapeLayer);
- }
-
- function moveShape(dx, dy) {
- if(shapeOptions.type === 'path') {
- var noOp = function(coord) { return coord; };
- var moveX = noOp;
- var moveY = noOp;
-
- if(xPixelSized) {
- modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
- } else {
- moveX = function moveX(x) { return p2x(x2p(x) + dx); };
- if(xa && xa.type === 'date') moveX = helpers.encodeDate(moveX);
- }
-
- if(yPixelSized) {
- modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
- } else {
- moveY = function moveY(y) { return p2y(y2p(y) + dy); };
- if(ya && ya.type === 'date') moveY = helpers.encodeDate(moveY);
- }
-
- modifyItem('path', shapeOptions.path = movePath(pathIn, moveX, moveY));
- }
- else {
- if(xPixelSized) {
- modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
- } else {
- modifyItem('x0', shapeOptions.x0 = p2x(x0 + dx));
- modifyItem('x1', shapeOptions.x1 = p2x(x1 + dx));
- }
-
- if(yPixelSized) {
- modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
- } else {
- modifyItem('y0', shapeOptions.y0 = p2y(y0 + dy));
- modifyItem('y1', shapeOptions.y1 = p2y(y1 + dy));
- }
- }
-
- shapePath.attr('d', getPathString(gd, shapeOptions));
- renderVisualCues(shapeLayer, shapeOptions);
- }
-
- function resizeShape(dx, dy) {
- if(isPath) {
- // TODO: implement path resize, don't forget to update dragMode code
- var noOp = function(coord) { return coord; };
- var moveX = noOp;
- var moveY = noOp;
-
- if(xPixelSized) {
- modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
- } else {
- moveX = function moveX(x) { return p2x(x2p(x) + dx); };
- if(xa && xa.type === 'date') moveX = helpers.encodeDate(moveX);
- }
-
- if(yPixelSized) {
- modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
- } else {
- moveY = function moveY(y) { return p2y(y2p(y) + dy); };
- if(ya && ya.type === 'date') moveY = helpers.encodeDate(moveY);
- }
-
- modifyItem('path', shapeOptions.path = movePath(pathIn, moveX, moveY));
- }
- else if(isLine) {
- if(dragMode === 'resize-over-start-point') {
- var newX0 = x0 + dx;
- var newY0 = yPixelSized ? y0 - dy : y0 + dy;
- modifyItem('x0', shapeOptions.x0 = xPixelSized ? newX0 : p2x(newX0));
- modifyItem('y0', shapeOptions.y0 = yPixelSized ? newY0 : p2y(newY0));
- } else if(dragMode === 'resize-over-end-point') {
- var newX1 = x1 + dx;
- var newY1 = yPixelSized ? y1 - dy : y1 + dy;
- modifyItem('x1', shapeOptions.x1 = xPixelSized ? newX1 : p2x(newX1));
- modifyItem('y1', shapeOptions.y1 = yPixelSized ? newY1 : p2y(newY1));
- }
- }
- else {
- var newN = (~dragMode.indexOf('n')) ? n0 + dy : n0;
- var newS = (~dragMode.indexOf('s')) ? s0 + dy : s0;
- var newW = (~dragMode.indexOf('w')) ? w0 + dx : w0;
- var newE = (~dragMode.indexOf('e')) ? e0 + dx : e0;
-
- // Do things in opposing direction for y-axis.
- // Hint: for data-sized shapes the reversal of axis direction is done in p2y.
- if(~dragMode.indexOf('n') && yPixelSized) newN = n0 - dy;
- if(~dragMode.indexOf('s') && yPixelSized) newS = s0 - dy;
-
- // Update shape eventually. Again, be aware of the
- // opposing direction of the y-axis of fixed size shapes.
- if((!yPixelSized && newS - newN > MINHEIGHT) ||
- (yPixelSized && newN - newS > MINHEIGHT)) {
- modifyItem(optN, shapeOptions[optN] = yPixelSized ? newN : p2y(newN));
- modifyItem(optS, shapeOptions[optS] = yPixelSized ? newS : p2y(newS));
- }
- if(newE - newW > MINWIDTH) {
- modifyItem(optW, shapeOptions[optW] = xPixelSized ? newW : p2x(newW));
- modifyItem(optE, shapeOptions[optE] = xPixelSized ? newE : p2x(newE));
- }
- }
-
- shapePath.attr('d', getPathString(gd, shapeOptions));
- renderVisualCues(shapeLayer, shapeOptions);
- }
-
- function renderVisualCues(shapeLayer, shapeOptions) {
- if(xPixelSized || yPixelSized) {
- renderAnchor();
- }
-
- function renderAnchor() {
- var isNotPath = shapeOptions.type !== 'path';
-
- // d3 join with dummy data to satisfy d3 data-binding
- var visualCues = shapeLayer.selectAll('.visual-cue').data([0]);
-
- // Enter
- var strokeWidth = 1;
- visualCues.enter()
- .append('path')
- .attr({
- 'fill': '#fff',
- 'fill-rule': 'evenodd',
- 'stroke': '#000',
- 'stroke-width': strokeWidth
- })
- .classed('visual-cue', true);
-
- // Update
- var posX = x2p(
- xPixelSized ?
- shapeOptions.xanchor :
- Lib.midRange(
- isNotPath ?
- [shapeOptions.x0, shapeOptions.x1] :
- helpers.extractPathCoords(shapeOptions.path, constants.paramIsX))
- );
- var posY = y2p(
- yPixelSized ?
- shapeOptions.yanchor :
- Lib.midRange(
- isNotPath ?
- [shapeOptions.y0, shapeOptions.y1] :
- helpers.extractPathCoords(shapeOptions.path, constants.paramIsY))
- );
-
- posX = helpers.roundPositionForSharpStrokeRendering(posX, strokeWidth);
- posY = helpers.roundPositionForSharpStrokeRendering(posY, strokeWidth);
-
- if(xPixelSized && yPixelSized) {
- var crossPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 1 - strokeWidth) +
- 'h-8v2h8 v8h2v-8 h8v-2h-8 v-8h-2 Z';
- visualCues.attr('d', crossPath);
- } else if(xPixelSized) {
- var vBarPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 9 - strokeWidth) +
- 'v18 h2 v-18 Z';
- visualCues.attr('d', vBarPath);
- } else {
- var hBarPath = 'M' + (posX - 9 - strokeWidth) + ',' + (posY - 1 - strokeWidth) +
- 'h18 v2 h-18 Z';
- visualCues.attr('d', hBarPath);
- }
- }
- }
-
- function removeVisualCues(shapeLayer) {
- shapeLayer.selectAll('.visual-cue').remove();
- }
-
- function deactivateClipPathTemporarily(shapePath, shapeOptions, gd) {
- var xref = shapeOptions.xref;
- var yref = shapeOptions.yref;
- var xa = Axes.getFromId(gd, xref);
- var ya = Axes.getFromId(gd, yref);
-
- var clipAxes = '';
- if(xref !== 'paper' && !xa.autorange) clipAxes += xref;
- if(yref !== 'paper' && !ya.autorange) clipAxes += yref;
-
- Drawing.setClipUrl(
- shapePath,
- clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
- gd
- );
- }
- }
-
- function getPathString(gd, options) {
- var type = options.type;
- var xa = Axes.getFromId(gd, options.xref);
- var ya = Axes.getFromId(gd, options.yref);
- var gs = gd._fullLayout._size;
- var x2r, x2p, y2r, y2p;
- var x0, x1, y0, y1;
-
- if(xa) {
- x2r = helpers.shapePositionToRange(xa);
- x2p = function(v) { return xa._offset + xa.r2p(x2r(v, true)); };
- }
- else {
- x2p = function(v) { return gs.l + gs.w * v; };
- }
-
- if(ya) {
- y2r = helpers.shapePositionToRange(ya);
- y2p = function(v) { return ya._offset + ya.r2p(y2r(v, true)); };
- }
- else {
- y2p = function(v) { return gs.t + gs.h * (1 - v); };
- }
-
- if(type === 'path') {
- if(xa && xa.type === 'date') x2p = helpers.decodeDate(x2p);
- if(ya && ya.type === 'date') y2p = helpers.decodeDate(y2p);
- return convertPath(options, x2p, y2p);
- }
-
- if(options.xsizemode === 'pixel') {
- var xAnchorPos = x2p(options.xanchor);
- x0 = xAnchorPos + options.x0;
- x1 = xAnchorPos + options.x1;
- }
- else {
- x0 = x2p(options.x0);
- x1 = x2p(options.x1);
- }
-
- if(options.ysizemode === 'pixel') {
- var yAnchorPos = y2p(options.yanchor);
- y0 = yAnchorPos - options.y0;
- y1 = yAnchorPos - options.y1;
- }
- else {
- y0 = y2p(options.y0);
- y1 = y2p(options.y1);
- }
-
- if(type === 'line') return 'M' + x0 + ',' + y0 + 'L' + x1 + ',' + y1;
- if(type === 'rect') return 'M' + x0 + ',' + y0 + 'H' + x1 + 'V' + y1 + 'H' + x0 + 'Z';
-
- // circle
- var cx = (x0 + x1) / 2;
- var cy = (y0 + y1) / 2;
- var rx = Math.abs(cx - x0);
- var ry = Math.abs(cy - y0);
- var rArc = 'A' + rx + ',' + ry;
- var rightPt = (cx + rx) + ',' + cy;
- var topPt = cx + ',' + (cy - ry);
- return 'M' + rightPt + rArc + ' 0 1,1 ' + topPt +
- rArc + ' 0 0,1 ' + rightPt + 'Z';
- }
-
-
- function convertPath(options, x2p, y2p) {
- var pathIn = options.path;
- var xSizemode = options.xsizemode;
- var ySizemode = options.ysizemode;
- var xAnchor = options.xanchor;
- var yAnchor = options.yanchor;
-
- return pathIn.replace(constants.segmentRE, function(segment) {
- var paramNumber = 0;
- var segmentType = segment.charAt(0);
- var xParams = constants.paramIsX[segmentType];
- var yParams = constants.paramIsY[segmentType];
- var nParams = constants.numParams[segmentType];
-
- var paramString = segment.substr(1).replace(constants.paramRE, function(param) {
- if(xParams[paramNumber]) {
- if(xSizemode === 'pixel') param = x2p(xAnchor) + Number(param);
- else param = x2p(param);
- }
- else if(yParams[paramNumber]) {
- if(ySizemode === 'pixel') param = y2p(yAnchor) - Number(param);
- else param = y2p(param);
- }
- paramNumber++;
-
- if(paramNumber > nParams) param = 'X';
- return param;
- });
-
- if(paramNumber > nParams) {
- paramString = paramString.replace(/[\s,]*X.*/, '');
- Lib.log('Ignoring extra params in segment ' + segment);
- }
-
- return segmentType + paramString;
- });
- }
-
- function movePath(pathIn, moveX, moveY) {
- return pathIn.replace(constants.segmentRE, function(segment) {
- var paramNumber = 0;
- var segmentType = segment.charAt(0);
- var xParams = constants.paramIsX[segmentType];
- var yParams = constants.paramIsY[segmentType];
- var nParams = constants.numParams[segmentType];
-
- var paramString = segment.substr(1).replace(constants.paramRE, function(param) {
- if(paramNumber >= nParams) return param;
-
- if(xParams[paramNumber]) param = moveX(param);
- else if(yParams[paramNumber]) param = moveY(param);
-
- paramNumber++;
-
- return param;
- });
-
- return segmentType + paramString;
- });
- }
-
- },{"../../lib":168,"../../lib/setcursor":187,"../../plot_api/plot_template":202,"../../plots/cartesian/axes":212,"../../registry":257,"../color":51,"../dragelement":69,"../drawing":72,"./constants":129,"./helpers":132}],132:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var constants = _dereq_('./constants');
-
- var Lib = _dereq_('../../lib');
-
- // special position conversion functions... category axis positions can't be
- // specified by their data values, because they don't make a continuous mapping.
- // so these have to be specified in terms of the category serial numbers,
- // but can take fractional values. Other axis types we specify position based on
- // the actual data values.
- // TODO: in V2.0 (when log axis ranges are in data units) range and shape position
- // will be identical, so rangeToShapePosition and shapePositionToRange can be
- // removed entirely.
-
- exports.rangeToShapePosition = function(ax) {
- return (ax.type === 'log') ? ax.r2d : function(v) { return v; };
- };
-
- exports.shapePositionToRange = function(ax) {
- return (ax.type === 'log') ? ax.d2r : function(v) { return v; };
- };
-
- exports.decodeDate = function(convertToPx) {
- return function(v) {
- if(v.replace) v = v.replace('_', ' ');
- return convertToPx(v);
- };
- };
-
- exports.encodeDate = function(convertToDate) {
- return function(v) { return convertToDate(v).replace(' ', '_'); };
- };
-
- exports.extractPathCoords = function(path, paramsToUse) {
- var extractedCoordinates = [];
-
- var segments = path.match(constants.segmentRE);
- segments.forEach(function(segment) {
- var relevantParamIdx = paramsToUse[segment.charAt(0)].drawn;
- if(relevantParamIdx === undefined) return;
-
- var params = segment.substr(1).match(constants.paramRE);
- if(!params || params.length < relevantParamIdx) return;
-
- extractedCoordinates.push(Lib.cleanNumber(params[relevantParamIdx]));
- });
-
- return extractedCoordinates;
- };
-
- exports.getDataToPixel = function(gd, axis, isVertical) {
- var gs = gd._fullLayout._size;
- var dataToPixel;
-
- if(axis) {
- var d2r = exports.shapePositionToRange(axis);
-
- dataToPixel = function(v) {
- return axis._offset + axis.r2p(d2r(v, true));
- };
-
- if(axis.type === 'date') dataToPixel = exports.decodeDate(dataToPixel);
- }
- else if(isVertical) {
- dataToPixel = function(v) { return gs.t + gs.h * (1 - v); };
- }
- else {
- dataToPixel = function(v) { return gs.l + gs.w * v; };
- }
-
- return dataToPixel;
- };
-
- exports.getPixelToData = function(gd, axis, isVertical) {
- var gs = gd._fullLayout._size;
- var pixelToData;
-
- if(axis) {
- var r2d = exports.rangeToShapePosition(axis);
- pixelToData = function(p) { return r2d(axis.p2r(p - axis._offset)); };
- }
- else if(isVertical) {
- pixelToData = function(p) { return 1 - (p - gs.t) / gs.h; };
- }
- else {
- pixelToData = function(p) { return (p - gs.l) / gs.w; };
- }
-
- return pixelToData;
- };
-
- /**
- * Based on the given stroke width, rounds the passed
- * position value to represent either a full or half pixel.
- *
- * In case of an odd stroke width (e.g. 1), this measure ensures
- * that a stroke positioned at the returned position isn't rendered
- * blurry due to anti-aliasing.
- *
- * In case of an even stroke width (e.g. 2), this measure ensures
- * that the position value is transformed to a full pixel value
- * so that anti-aliasing doesn't take effect either.
- *
- * @param {number} pos The raw position value to be transformed
- * @param {number} strokeWidth The stroke width
- * @returns {number} either an integer or a .5 decimal number
- */
- exports.roundPositionForSharpStrokeRendering = function(pos, strokeWidth) {
- var strokeWidthIsOdd = Math.round(strokeWidth % 2) === 1;
- var posValAsInt = Math.round(pos);
-
- return strokeWidthIsOdd ? posValAsInt + 0.5 : posValAsInt;
- };
-
- },{"../../lib":168,"./constants":129}],133:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var drawModule = _dereq_('./draw');
-
- module.exports = {
- moduleType: 'component',
- name: 'shapes',
-
- layoutAttributes: _dereq_('./attributes'),
- supplyLayoutDefaults: _dereq_('./defaults'),
- includeBasePlot: _dereq_('../../plots/cartesian/include_components')('shapes'),
-
- calcAutorange: _dereq_('./calc_autorange'),
- draw: drawModule.draw,
- drawOne: drawModule.drawOne
- };
-
- },{"../../plots/cartesian/include_components":223,"./attributes":127,"./calc_autorange":128,"./defaults":130,"./draw":131}],134:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var fontAttrs = _dereq_('../../plots/font_attributes');
- var padAttrs = _dereq_('../../plots/pad_attributes');
- var extendDeepAll = _dereq_('../../lib/extend').extendDeepAll;
- var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
- var animationAttrs = _dereq_('../../plots/animation_attributes');
- var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
- var constants = _dereq_('./constants');
-
- var stepsAttrs = templatedArray('step', {
- visible: {
- valType: 'boolean',
-
- dflt: true,
-
- },
- method: {
- valType: 'enumerated',
- values: ['restyle', 'relayout', 'animate', 'update', 'skip'],
- dflt: 'restyle',
-
-
- },
- args: {
- valType: 'info_array',
-
- freeLength: true,
- items: [
- { valType: 'any' },
- { valType: 'any' },
- { valType: 'any' }
- ],
-
- },
- label: {
- valType: 'string',
-
-
- },
- value: {
- valType: 'string',
-
-
- },
- execute: {
- valType: 'boolean',
-
- dflt: true,
-
- }
- });
-
- module.exports = overrideAll(templatedArray('slider', {
- visible: {
- valType: 'boolean',
-
- dflt: true,
-
- },
-
- active: {
- valType: 'number',
-
- min: 0,
- dflt: 0,
-
- },
-
- steps: stepsAttrs,
-
- lenmode: {
- valType: 'enumerated',
- values: ['fraction', 'pixels'],
-
- dflt: 'fraction',
-
- },
- len: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
-
- },
- x: {
- valType: 'number',
- min: -2,
- max: 3,
- dflt: 0,
-
-
- },
- pad: extendDeepAll(padAttrs({editType: 'arraydraw'}), {
-
- }, {t: {dflt: 20}}),
- xanchor: {
- valType: 'enumerated',
- values: ['auto', 'left', 'center', 'right'],
- dflt: 'left',
-
-
- },
- y: {
- valType: 'number',
- min: -2,
- max: 3,
- dflt: 0,
-
-
- },
- yanchor: {
- valType: 'enumerated',
- values: ['auto', 'top', 'middle', 'bottom'],
- dflt: 'top',
-
-
- },
-
- transition: {
- duration: {
- valType: 'number',
-
- min: 0,
- dflt: 150,
-
- },
- easing: {
- valType: 'enumerated',
- values: animationAttrs.transition.easing.values,
-
- dflt: 'cubic-in-out',
-
- }
- },
-
- currentvalue: {
- visible: {
- valType: 'boolean',
-
- dflt: true,
-
- },
-
- xanchor: {
- valType: 'enumerated',
- values: ['left', 'center', 'right'],
- dflt: 'left',
-
-
- },
-
- offset: {
- valType: 'number',
- dflt: 10,
-
-
- },
-
- prefix: {
- valType: 'string',
-
-
- },
-
- suffix: {
- valType: 'string',
-
-
- },
-
- font: fontAttrs({
-
- })
- },
-
- font: fontAttrs({
-
- }),
-
- activebgcolor: {
- valType: 'color',
-
- dflt: constants.gripBgActiveColor,
-
- },
- bgcolor: {
- valType: 'color',
-
- dflt: constants.railBgColor,
-
- },
- bordercolor: {
- valType: 'color',
- dflt: constants.railBorderColor,
-
-
- },
- borderwidth: {
- valType: 'number',
- min: 0,
- dflt: constants.railBorderWidth,
-
-
- },
- ticklen: {
- valType: 'number',
- min: 0,
- dflt: constants.tickLength,
-
-
- },
- tickcolor: {
- valType: 'color',
- dflt: constants.tickColor,
-
-
- },
- tickwidth: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
-
- },
- minorticklen: {
- valType: 'number',
- min: 0,
- dflt: constants.minorTickLength,
-
-
- }
- }), 'arraydraw', 'from-root');
-
- },{"../../lib/extend":162,"../../plot_api/edit_types":195,"../../plot_api/plot_template":202,"../../plots/animation_attributes":207,"../../plots/font_attributes":239,"../../plots/pad_attributes":244,"./constants":135}],135:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- module.exports = {
-
- // layout attribute name
- name: 'sliders',
-
- // class names
- containerClassName: 'slider-container',
- groupClassName: 'slider-group',
- inputAreaClass: 'slider-input-area',
- railRectClass: 'slider-rail-rect',
- railTouchRectClass: 'slider-rail-touch-rect',
- gripRectClass: 'slider-grip-rect',
- tickRectClass: 'slider-tick-rect',
- inputProxyClass: 'slider-input-proxy',
- labelsClass: 'slider-labels',
- labelGroupClass: 'slider-label-group',
- labelClass: 'slider-label',
- currentValueClass: 'slider-current-value',
-
- railHeight: 5,
-
- // DOM attribute name in button group keeping track
- // of active update menu
- menuIndexAttrName: 'slider-active-index',
-
- // id root pass to Plots.autoMargin
- autoMarginIdRoot: 'slider-',
-
- // min item width / height
- minWidth: 30,
- minHeight: 30,
-
- // padding around item text
- textPadX: 40,
-
- // arrow offset off right edge
- arrowOffsetX: 4,
-
- railRadius: 2,
- railWidth: 5,
- railBorder: 4,
- railBorderWidth: 1,
- railBorderColor: '#bec8d9',
- railBgColor: '#f8fafc',
-
- // The distance of the rail from the edge of the touchable area
- // Slightly less than the step inset because of the curved edges
- // of the rail
- railInset: 8,
-
- // The distance from the extremal tick marks to the edge of the
- // touchable area. This is basically the same as the grip radius,
- // but for other styles it wouldn't really need to be.
- stepInset: 10,
-
- gripRadius: 10,
- gripWidth: 20,
- gripHeight: 20,
- gripBorder: 20,
- gripBorderWidth: 1,
- gripBorderColor: '#bec8d9',
- gripBgColor: '#f6f8fa',
- gripBgActiveColor: '#dbdde0',
-
- labelPadding: 8,
- labelOffset: 0,
-
- tickWidth: 1,
- tickColor: '#333',
- tickOffset: 25,
- tickLength: 7,
-
- minorTickOffset: 25,
- minorTickColor: '#333',
- minorTickLength: 4,
-
- // Extra space below the current value label:
- currentValuePadding: 8,
- currentValueInset: 0,
- };
-
- },{}],136:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
-
- var attributes = _dereq_('./attributes');
- var constants = _dereq_('./constants');
-
- var name = constants.name;
- var stepAttrs = attributes.steps;
-
-
- module.exports = function slidersDefaults(layoutIn, layoutOut) {
- handleArrayContainerDefaults(layoutIn, layoutOut, {
- name: name,
- handleItemDefaults: sliderDefaults
- });
- };
-
- function sliderDefaults(sliderIn, sliderOut, layoutOut) {
-
- function coerce(attr, dflt) {
- return Lib.coerce(sliderIn, sliderOut, attributes, attr, dflt);
- }
-
- var steps = handleArrayContainerDefaults(sliderIn, sliderOut, {
- name: 'steps',
- handleItemDefaults: stepDefaults
- });
-
- var stepCount = 0;
- for(var i = 0; i < steps.length; i++) {
- if(steps[i].visible) stepCount++;
- }
-
- var visible;
- // If it has fewer than two options, it's not really a slider
- if(stepCount < 2) visible = sliderOut.visible = false;
- else visible = coerce('visible');
- if(!visible) return;
-
- sliderOut._stepCount = stepCount;
- var visSteps = sliderOut._visibleSteps = Lib.filterVisible(steps);
-
- var active = coerce('active');
- if(!(steps[active] || {}).visible) sliderOut.active = visSteps[0]._index;
-
- coerce('x');
- coerce('y');
- Lib.noneOrAll(sliderIn, sliderOut, ['x', 'y']);
-
- coerce('xanchor');
- coerce('yanchor');
-
- coerce('len');
- coerce('lenmode');
-
- coerce('pad.t');
- coerce('pad.r');
- coerce('pad.b');
- coerce('pad.l');
-
- Lib.coerceFont(coerce, 'font', layoutOut.font);
-
- var currentValueIsVisible = coerce('currentvalue.visible');
-
- if(currentValueIsVisible) {
- coerce('currentvalue.xanchor');
- coerce('currentvalue.prefix');
- coerce('currentvalue.suffix');
- coerce('currentvalue.offset');
-
- Lib.coerceFont(coerce, 'currentvalue.font', sliderOut.font);
- }
-
- coerce('transition.duration');
- coerce('transition.easing');
-
- coerce('bgcolor');
- coerce('activebgcolor');
- coerce('bordercolor');
- coerce('borderwidth');
- coerce('ticklen');
- coerce('tickwidth');
- coerce('tickcolor');
- coerce('minorticklen');
- }
-
- function stepDefaults(valueIn, valueOut) {
- function coerce(attr, dflt) {
- return Lib.coerce(valueIn, valueOut, stepAttrs, attr, dflt);
- }
-
- var visible;
- if(valueIn.method !== 'skip' && !Array.isArray(valueIn.args)) {
- visible = valueOut.visible = false;
- }
- else visible = coerce('visible');
-
- if(visible) {
- coerce('method');
- coerce('args');
- var label = coerce('label', 'step-' + valueOut._index);
- coerce('value', label);
- coerce('execute');
- }
- }
-
- },{"../../lib":168,"../../plots/array_container_defaults":208,"./attributes":134,"./constants":135}],137:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Plots = _dereq_('../../plots/plots');
- var Color = _dereq_('../color');
- var Drawing = _dereq_('../drawing');
- var Lib = _dereq_('../../lib');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
-
- var constants = _dereq_('./constants');
- var alignmentConstants = _dereq_('../../constants/alignment');
- var LINE_SPACING = alignmentConstants.LINE_SPACING;
- var FROM_TL = alignmentConstants.FROM_TL;
- var FROM_BR = alignmentConstants.FROM_BR;
-
- module.exports = function draw(gd) {
- var fullLayout = gd._fullLayout;
- var sliderData = makeSliderData(fullLayout, gd);
-
- // draw a container for *all* sliders:
- var sliders = fullLayout._infolayer
- .selectAll('g.' + constants.containerClassName)
- .data(sliderData.length > 0 ? [0] : []);
-
- sliders.enter().append('g')
- .classed(constants.containerClassName, true)
- .style('cursor', 'ew-resize');
-
- function clearSlider(sliderOpts) {
- if(sliderOpts._commandObserver) {
- sliderOpts._commandObserver.remove();
- delete sliderOpts._commandObserver;
- }
-
- // Most components don't need to explicitly remove autoMargin, because
- // marginPushers does this - but slider updates don't go through
- // a full replot so we need to explicitly remove it.
- Plots.autoMargin(gd, autoMarginId(sliderOpts));
- }
-
- sliders.exit().each(function() {
- d3.select(this).selectAll('g.' + constants.groupClassName)
- .each(clearSlider);
- })
- .remove();
-
- // Return early if no menus visible:
- if(sliderData.length === 0) return;
-
- var sliderGroups = sliders.selectAll('g.' + constants.groupClassName)
- .data(sliderData, keyFunction);
-
- sliderGroups.enter().append('g')
- .classed(constants.groupClassName, true);
-
- sliderGroups.exit()
- .each(clearSlider)
- .remove();
-
- // Find the dimensions of the sliders:
- for(var i = 0; i < sliderData.length; i++) {
- var sliderOpts = sliderData[i];
- findDimensions(gd, sliderOpts);
- }
-
- sliderGroups.each(function(sliderOpts) {
- var gSlider = d3.select(this);
-
- computeLabelSteps(sliderOpts);
-
- Plots.manageCommandObserver(gd, sliderOpts, sliderOpts._visibleSteps, function(data) {
- // NB: Same as below. This is *not* always the same as sliderOpts since
- // if a new set of steps comes in, the reference in this callback would
- // be invalid. We need to refetch it from the slider group, which is
- // the join data that creates this slider. So if this slider still exists,
- // the group should be valid, *to the best of my knowledge.* If not,
- // we'd have to look it up by d3 data join index/key.
- var opts = gSlider.data()[0];
-
- if(opts.active === data.index) return;
- if(opts._dragging) return;
-
- setActive(gd, gSlider, opts, data.index, false, true);
- });
-
- drawSlider(gd, d3.select(this), sliderOpts);
- });
- };
-
- function autoMarginId(sliderOpts) {
- return constants.autoMarginIdRoot + sliderOpts._index;
- }
-
- // This really only just filters by visibility:
- function makeSliderData(fullLayout, gd) {
- var contOpts = fullLayout[constants.name];
- var sliderData = [];
-
- for(var i = 0; i < contOpts.length; i++) {
- var item = contOpts[i];
- if(!item.visible) continue;
- item._gd = gd;
- sliderData.push(item);
- }
-
- return sliderData;
- }
-
- // This is set in the defaults step:
- function keyFunction(opts) {
- return opts._index;
- }
-
- // Compute the dimensions (mutates sliderOpts):
- function findDimensions(gd, sliderOpts) {
- var sliderLabels = Drawing.tester.selectAll('g.' + constants.labelGroupClass)
- .data(sliderOpts._visibleSteps);
-
- sliderLabels.enter().append('g')
- .classed(constants.labelGroupClass, true);
-
- // loop over fake buttons to find width / height
- var maxLabelWidth = 0;
- var labelHeight = 0;
- sliderLabels.each(function(stepOpts) {
- var labelGroup = d3.select(this);
-
- var text = drawLabel(labelGroup, {step: stepOpts}, sliderOpts);
-
- var textNode = text.node();
- if(textNode) {
- var bBox = Drawing.bBox(textNode);
- labelHeight = Math.max(labelHeight, bBox.height);
- maxLabelWidth = Math.max(maxLabelWidth, bBox.width);
- }
- });
-
- sliderLabels.remove();
-
- var dims = sliderOpts._dims = {};
-
- dims.inputAreaWidth = Math.max(
- constants.railWidth,
- constants.gripHeight
- );
-
- // calculate some overall dimensions - some of these are needed for
- // calculating the currentValue dimensions
- var graphSize = gd._fullLayout._size;
- dims.lx = graphSize.l + graphSize.w * sliderOpts.x;
- dims.ly = graphSize.t + graphSize.h * (1 - sliderOpts.y);
-
- if(sliderOpts.lenmode === 'fraction') {
- // fraction:
- dims.outerLength = Math.round(graphSize.w * sliderOpts.len);
- } else {
- // pixels:
- dims.outerLength = sliderOpts.len;
- }
-
- // The length of the rail, *excluding* padding on either end:
- dims.inputAreaStart = 0;
- dims.inputAreaLength = Math.round(dims.outerLength - sliderOpts.pad.l - sliderOpts.pad.r);
-
- var textableInputLength = dims.inputAreaLength - 2 * constants.stepInset;
- var availableSpacePerLabel = textableInputLength / (sliderOpts._stepCount - 1);
- var computedSpacePerLabel = maxLabelWidth + constants.labelPadding;
- dims.labelStride = Math.max(1, Math.ceil(computedSpacePerLabel / availableSpacePerLabel));
- dims.labelHeight = labelHeight;
-
- // loop over all possible values for currentValue to find the
- // area we need for it
- dims.currentValueMaxWidth = 0;
- dims.currentValueHeight = 0;
- dims.currentValueTotalHeight = 0;
- dims.currentValueMaxLines = 1;
-
- if(sliderOpts.currentvalue.visible) {
- // Get the dimensions of the current value label:
- var dummyGroup = Drawing.tester.append('g');
-
- sliderLabels.each(function(stepOpts) {
- var curValPrefix = drawCurrentValue(dummyGroup, sliderOpts, stepOpts.label);
- var curValSize = (curValPrefix.node() && Drawing.bBox(curValPrefix.node())) || {width: 0, height: 0};
- var lines = svgTextUtils.lineCount(curValPrefix);
- dims.currentValueMaxWidth = Math.max(dims.currentValueMaxWidth, Math.ceil(curValSize.width));
- dims.currentValueHeight = Math.max(dims.currentValueHeight, Math.ceil(curValSize.height));
- dims.currentValueMaxLines = Math.max(dims.currentValueMaxLines, lines);
- });
-
- dims.currentValueTotalHeight = dims.currentValueHeight + sliderOpts.currentvalue.offset;
-
- dummyGroup.remove();
- }
-
- dims.height = dims.currentValueTotalHeight + constants.tickOffset + sliderOpts.ticklen + constants.labelOffset + dims.labelHeight + sliderOpts.pad.t + sliderOpts.pad.b;
-
- var xanchor = 'left';
- if(Lib.isRightAnchor(sliderOpts)) {
- dims.lx -= dims.outerLength;
- xanchor = 'right';
- }
- if(Lib.isCenterAnchor(sliderOpts)) {
- dims.lx -= dims.outerLength / 2;
- xanchor = 'center';
- }
-
- var yanchor = 'top';
- if(Lib.isBottomAnchor(sliderOpts)) {
- dims.ly -= dims.height;
- yanchor = 'bottom';
- }
- if(Lib.isMiddleAnchor(sliderOpts)) {
- dims.ly -= dims.height / 2;
- yanchor = 'middle';
- }
-
- dims.outerLength = Math.ceil(dims.outerLength);
- dims.height = Math.ceil(dims.height);
- dims.lx = Math.round(dims.lx);
- dims.ly = Math.round(dims.ly);
-
- var marginOpts = {
- y: sliderOpts.y,
- b: dims.height * FROM_BR[yanchor],
- t: dims.height * FROM_TL[yanchor]
- };
-
- if(sliderOpts.lenmode === 'fraction') {
- marginOpts.l = 0;
- marginOpts.xl = sliderOpts.x - sliderOpts.len * FROM_TL[xanchor];
- marginOpts.r = 0;
- marginOpts.xr = sliderOpts.x + sliderOpts.len * FROM_BR[xanchor];
- }
- else {
- marginOpts.x = sliderOpts.x;
- marginOpts.l = dims.outerLength * FROM_TL[xanchor];
- marginOpts.r = dims.outerLength * FROM_BR[xanchor];
- }
-
- Plots.autoMargin(gd, autoMarginId(sliderOpts), marginOpts);
- }
-
- function drawSlider(gd, sliderGroup, sliderOpts) {
- // This is related to the other long notes in this file regarding what happens
- // when slider steps disappear. This particular fix handles what happens when
- // the *current* slider step is removed. The drawing functions will error out
- // when they fail to find it, so the fix for now is that it will just draw the
- // slider in the first position but will not execute the command.
- if(!((sliderOpts.steps[sliderOpts.active] || {}).visible)) {
- sliderOpts.active = sliderOpts._visibleSteps[0]._index;
- }
-
- // These are carefully ordered for proper z-ordering:
- sliderGroup
- .call(drawCurrentValue, sliderOpts)
- .call(drawRail, sliderOpts)
- .call(drawLabelGroup, sliderOpts)
- .call(drawTicks, sliderOpts)
- .call(drawTouchRect, gd, sliderOpts)
- .call(drawGrip, gd, sliderOpts);
-
- var dims = sliderOpts._dims;
-
- // Position the rectangle:
- Drawing.setTranslate(sliderGroup, dims.lx + sliderOpts.pad.l, dims.ly + sliderOpts.pad.t);
-
- sliderGroup.call(setGripPosition, sliderOpts, false);
- sliderGroup.call(drawCurrentValue, sliderOpts);
-
- }
-
- function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
- if(!sliderOpts.currentvalue.visible) return;
-
- var dims = sliderOpts._dims;
- var x0, textAnchor;
-
- switch(sliderOpts.currentvalue.xanchor) {
- case 'right':
- // This is anchored left and adjusted by the width of the longest label
- // so that the prefix doesn't move. The goal of this is to emphasize
- // what's actually changing and make the update less distracting.
- x0 = dims.inputAreaLength - constants.currentValueInset - dims.currentValueMaxWidth;
- textAnchor = 'left';
- break;
- case 'center':
- x0 = dims.inputAreaLength * 0.5;
- textAnchor = 'middle';
- break;
- default:
- x0 = constants.currentValueInset;
- textAnchor = 'left';
- }
-
- var text = Lib.ensureSingle(sliderGroup, 'text', constants.labelClass, function(s) {
- s.classed('user-select-none', true)
- .attr({
- 'text-anchor': textAnchor,
- 'data-notex': 1
- });
- });
-
- var str = sliderOpts.currentvalue.prefix ? sliderOpts.currentvalue.prefix : '';
-
- if(typeof valueOverride === 'string') {
- str += valueOverride;
- } else {
- var curVal = sliderOpts.steps[sliderOpts.active].label;
- str += curVal;
- }
-
- if(sliderOpts.currentvalue.suffix) {
- str += sliderOpts.currentvalue.suffix;
- }
-
- text.call(Drawing.font, sliderOpts.currentvalue.font)
- .text(str)
- .call(svgTextUtils.convertToTspans, sliderOpts._gd);
-
- var lines = svgTextUtils.lineCount(text);
-
- var y0 = (dims.currentValueMaxLines + 1 - lines) *
- sliderOpts.currentvalue.font.size * LINE_SPACING;
-
- svgTextUtils.positionText(text, x0, y0);
-
- return text;
- }
-
- function drawGrip(sliderGroup, gd, sliderOpts) {
- var grip = Lib.ensureSingle(sliderGroup, 'rect', constants.gripRectClass, function(s) {
- s.call(attachGripEvents, gd, sliderGroup, sliderOpts)
- .style('pointer-events', 'all');
- });
-
- grip.attr({
- width: constants.gripWidth,
- height: constants.gripHeight,
- rx: constants.gripRadius,
- ry: constants.gripRadius,
- })
- .call(Color.stroke, sliderOpts.bordercolor)
- .call(Color.fill, sliderOpts.bgcolor)
- .style('stroke-width', sliderOpts.borderwidth + 'px');
- }
-
- function drawLabel(item, data, sliderOpts) {
- var text = Lib.ensureSingle(item, 'text', constants.labelClass, function(s) {
- s.classed('user-select-none', true)
- .attr({
- 'text-anchor': 'middle',
- 'data-notex': 1
- });
- });
-
- text.call(Drawing.font, sliderOpts.font)
- .text(data.step.label)
- .call(svgTextUtils.convertToTspans, sliderOpts._gd);
-
- return text;
- }
-
- function drawLabelGroup(sliderGroup, sliderOpts) {
- var labels = Lib.ensureSingle(sliderGroup, 'g', constants.labelsClass);
- var dims = sliderOpts._dims;
-
- var labelItems = labels.selectAll('g.' + constants.labelGroupClass)
- .data(dims.labelSteps);
-
- labelItems.enter().append('g')
- .classed(constants.labelGroupClass, true);
-
- labelItems.exit().remove();
-
- labelItems.each(function(d) {
- var item = d3.select(this);
-
- item.call(drawLabel, d, sliderOpts);
-
- Drawing.setTranslate(item,
- normalizedValueToPosition(sliderOpts, d.fraction),
- constants.tickOffset +
- sliderOpts.ticklen +
- // position is the baseline of the top line of text only, even
- // if the label spans multiple lines
- sliderOpts.font.size * LINE_SPACING +
- constants.labelOffset +
- dims.currentValueTotalHeight
- );
- });
-
- }
-
- function handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, doTransition) {
- var quantizedPosition = Math.round(normalizedPosition * (sliderOpts._stepCount - 1));
- var quantizedIndex = sliderOpts._visibleSteps[quantizedPosition]._index;
-
- if(quantizedIndex !== sliderOpts.active) {
- setActive(gd, sliderGroup, sliderOpts, quantizedIndex, true, doTransition);
- }
- }
-
- function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition) {
- var previousActive = sliderOpts.active;
- sliderOpts.active = index;
-
- // due to templating, it's possible this slider doesn't even exist yet
- arrayEditor(gd.layout, constants.name, sliderOpts)
- .applyUpdate('active', index);
-
- var step = sliderOpts.steps[sliderOpts.active];
-
- sliderGroup.call(setGripPosition, sliderOpts, doTransition);
- sliderGroup.call(drawCurrentValue, sliderOpts);
-
- gd.emit('plotly_sliderchange', {
- slider: sliderOpts,
- step: sliderOpts.steps[sliderOpts.active],
- interaction: doCallback,
- previousActive: previousActive
- });
-
- if(step && step.method && doCallback) {
- if(sliderGroup._nextMethod) {
- // If we've already queued up an update, just overwrite it with the most recent:
- sliderGroup._nextMethod.step = step;
- sliderGroup._nextMethod.doCallback = doCallback;
- sliderGroup._nextMethod.doTransition = doTransition;
- } else {
- sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
- sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
- var _step = sliderGroup._nextMethod.step;
- if(!_step.method) return;
-
- if(_step.execute) {
- Plots.executeAPICommand(gd, _step.method, _step.args);
- }
-
- sliderGroup._nextMethod = null;
- sliderGroup._nextMethodRaf = null;
- });
- }
- }
- }
-
- function attachGripEvents(item, gd, sliderGroup) {
- var node = sliderGroup.node();
- var $gd = d3.select(gd);
-
- // NB: This is *not* the same as sliderOpts itself! These callbacks
- // are in a closure so this array won't actually be correct if the
- // steps have changed since this was initialized. The sliderGroup,
- // however, has not changed since that *is* the slider, so it must
- // be present to receive mouse events.
- function getSliderOpts() {
- return sliderGroup.data()[0];
- }
-
- item.on('mousedown', function() {
- var sliderOpts = getSliderOpts();
- gd.emit('plotly_sliderstart', {slider: sliderOpts});
-
- var grip = sliderGroup.select('.' + constants.gripRectClass);
-
- d3.event.stopPropagation();
- d3.event.preventDefault();
- grip.call(Color.fill, sliderOpts.activebgcolor);
-
- var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
- handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
- sliderOpts._dragging = true;
-
- $gd.on('mousemove', function() {
- var sliderOpts = getSliderOpts();
- var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
- handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
- });
-
- $gd.on('mouseup', function() {
- var sliderOpts = getSliderOpts();
- sliderOpts._dragging = false;
- grip.call(Color.fill, sliderOpts.bgcolor);
- $gd.on('mouseup', null);
- $gd.on('mousemove', null);
-
- gd.emit('plotly_sliderend', {
- slider: sliderOpts,
- step: sliderOpts.steps[sliderOpts.active]
- });
- });
- });
- }
-
- function drawTicks(sliderGroup, sliderOpts) {
- var tick = sliderGroup.selectAll('rect.' + constants.tickRectClass)
- .data(sliderOpts._visibleSteps);
- var dims = sliderOpts._dims;
-
- tick.enter().append('rect')
- .classed(constants.tickRectClass, true);
-
- tick.exit().remove();
-
- tick.attr({
- width: sliderOpts.tickwidth + 'px',
- 'shape-rendering': 'crispEdges'
- });
-
- tick.each(function(d, i) {
- var isMajor = i % dims.labelStride === 0;
- var item = d3.select(this);
-
- item
- .attr({height: isMajor ? sliderOpts.ticklen : sliderOpts.minorticklen})
- .call(Color.fill, isMajor ? sliderOpts.tickcolor : sliderOpts.tickcolor);
-
- Drawing.setTranslate(item,
- normalizedValueToPosition(sliderOpts, i / (sliderOpts._stepCount - 1)) - 0.5 * sliderOpts.tickwidth,
- (isMajor ? constants.tickOffset : constants.minorTickOffset) + dims.currentValueTotalHeight
- );
- });
-
- }
-
- function computeLabelSteps(sliderOpts) {
- var dims = sliderOpts._dims;
- dims.labelSteps = [];
- var nsteps = sliderOpts._stepCount;
-
- for(var i = 0; i < nsteps; i += dims.labelStride) {
- dims.labelSteps.push({
- fraction: i / (nsteps - 1),
- step: sliderOpts._visibleSteps[i]
- });
- }
- }
-
- function setGripPosition(sliderGroup, sliderOpts, doTransition) {
- var grip = sliderGroup.select('rect.' + constants.gripRectClass);
-
- var quantizedIndex = 0;
- for(var i = 0; i < sliderOpts._stepCount; i++) {
- if(sliderOpts._visibleSteps[i]._index === sliderOpts.active) {
- quantizedIndex = i;
- break;
- }
- }
-
- var x = normalizedValueToPosition(sliderOpts, quantizedIndex / (sliderOpts._stepCount - 1));
-
- // If this is true, then *this component* is already invoking its own command
- // and has triggered its own animation.
- if(sliderOpts._invokingCommand) return;
-
- var el = grip;
- if(doTransition && sliderOpts.transition.duration > 0) {
- el = el.transition()
- .duration(sliderOpts.transition.duration)
- .ease(sliderOpts.transition.easing);
- }
-
- // Drawing.setTranslate doesn't work here becasue of the transition duck-typing.
- // It's also not necessary because there are no other transitions to preserve.
- el.attr('transform', 'translate(' + (x - constants.gripWidth * 0.5) + ',' + (sliderOpts._dims.currentValueTotalHeight) + ')');
- }
-
- // Convert a number from [0-1] to a pixel position relative to the slider group container:
- function normalizedValueToPosition(sliderOpts, normalizedPosition) {
- var dims = sliderOpts._dims;
- return dims.inputAreaStart + constants.stepInset +
- (dims.inputAreaLength - 2 * constants.stepInset) * Math.min(1, Math.max(0, normalizedPosition));
- }
-
- // Convert a position relative to the slider group to a nubmer in [0, 1]
- function positionToNormalizedValue(sliderOpts, position) {
- var dims = sliderOpts._dims;
- return Math.min(1, Math.max(0, (position - constants.stepInset - dims.inputAreaStart) / (dims.inputAreaLength - 2 * constants.stepInset - 2 * dims.inputAreaStart)));
- }
-
- function drawTouchRect(sliderGroup, gd, sliderOpts) {
- var dims = sliderOpts._dims;
- var rect = Lib.ensureSingle(sliderGroup, 'rect', constants.railTouchRectClass, function(s) {
- s.call(attachGripEvents, gd, sliderGroup, sliderOpts)
- .style('pointer-events', 'all');
- });
-
- rect.attr({
- width: dims.inputAreaLength,
- height: Math.max(dims.inputAreaWidth, constants.tickOffset + sliderOpts.ticklen + dims.labelHeight)
- })
- .call(Color.fill, sliderOpts.bgcolor)
- .attr('opacity', 0);
-
- Drawing.setTranslate(rect, 0, dims.currentValueTotalHeight);
- }
-
- function drawRail(sliderGroup, sliderOpts) {
- var dims = sliderOpts._dims;
- var computedLength = dims.inputAreaLength - constants.railInset * 2;
- var rect = Lib.ensureSingle(sliderGroup, 'rect', constants.railRectClass);
-
- rect.attr({
- width: computedLength,
- height: constants.railWidth,
- rx: constants.railRadius,
- ry: constants.railRadius,
- 'shape-rendering': 'crispEdges'
- })
- .call(Color.stroke, sliderOpts.bordercolor)
- .call(Color.fill, sliderOpts.bgcolor)
- .style('stroke-width', sliderOpts.borderwidth + 'px');
-
- Drawing.setTranslate(rect,
- constants.railInset,
- (dims.inputAreaWidth - constants.railWidth) * 0.5 + dims.currentValueTotalHeight
- );
- }
-
- },{"../../constants/alignment":146,"../../lib":168,"../../lib/svg_text_utils":189,"../../plot_api/plot_template":202,"../../plots/plots":245,"../color":51,"../drawing":72,"./constants":135,"d3":16}],138:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var constants = _dereq_('./constants');
-
- module.exports = {
- moduleType: 'component',
- name: constants.name,
-
- layoutAttributes: _dereq_('./attributes'),
- supplyLayoutDefaults: _dereq_('./defaults'),
-
- draw: _dereq_('./draw')
- };
-
- },{"./attributes":134,"./constants":135,"./defaults":136,"./draw":137}],139:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Plots = _dereq_('../../plots/plots');
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var Drawing = _dereq_('../drawing');
- var Color = _dereq_('../color');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var interactConstants = _dereq_('../../constants/interactions');
-
- module.exports = {
- draw: draw
- };
-
- var numStripRE = / [XY][0-9]* /;
-
- /**
- * Titles - (re)draw titles on the axes and plot:
- * @param {DOM element} gd - the graphDiv
- * @param {string} titleClass - the css class of this title
- * @param {object} options - how and what to draw
- * propContainer - the layout object containing `title` and `titlefont`
- * attributes that apply to this title
- * propName - the full name of the title property (for Plotly.relayout)
- * [traceIndex] - include only if this property applies to one trace
- * (such as a colorbar title) - then editing pipes to Plotly.restyle
- * instead of Plotly.relayout
- * placeholder - placeholder text for an empty editable title
- * [avoid] {object} - include if this title should move to avoid other elements
- * selection - d3 selection of elements to avoid
- * side - which direction to move if there is a conflict
- * [offsetLeft] - if these elements are subject to a translation
- * wrt the title element
- * [offsetTop]
- * attributes {object} - position and alignment attributes
- * x - pixels
- * y - pixels
- * text-anchor - start|middle|end
- * transform {object} - how to transform the title after positioning
- * rotate - degrees
- * offset - shift up/down in the rotated frame (unused?)
- * containerGroup - if an svg <g> element already exists to hold this
- * title, include here. Otherwise it will go in fullLayout._infolayer
- *
- * @return {selection} d3 selection of title container group
- */
- function draw(gd, titleClass, options) {
- var cont = options.propContainer;
- var prop = options.propName;
- var placeholder = options.placeholder;
- var traceIndex = options.traceIndex;
- var avoid = options.avoid || {};
- var attributes = options.attributes;
- var transform = options.transform;
- var group = options.containerGroup;
-
- var fullLayout = gd._fullLayout;
-
- var opacity = 1;
- var isplaceholder = false;
- var title = cont.title;
- var txt = (title && title.text ? title.text : '').trim();
-
- var font = title && title.font ? title.font : {};
- var fontFamily = font.family;
- var fontSize = font.size;
- var fontColor = font.color;
-
- // only make this title editable if we positively identify its property
- // as one that has editing enabled.
- var editAttr;
- if(prop === 'title.text') editAttr = 'titleText';
- else if(prop.indexOf('axis') !== -1) editAttr = 'axisTitleText';
- else if(prop.indexOf('colorbar' !== -1)) editAttr = 'colorbarTitleText';
- var editable = gd._context.edits[editAttr];
-
- if(txt === '') opacity = 0;
- // look for placeholder text while stripping out numbers from eg X2, Y3
- // this is just for backward compatibility with the old version that had
- // "Click to enter X2 title" and may have gotten saved in some old plots,
- // we don't want this to show up when these are displayed.
- else if(txt.replace(numStripRE, ' % ') === placeholder.replace(numStripRE, ' % ')) {
- opacity = 0.2;
- isplaceholder = true;
- if(!editable) txt = '';
- }
-
- if(fullLayout.meta) {
- txt = Lib.templateString(txt, {meta: fullLayout.meta});
- }
-
- var elShouldExist = txt || editable;
-
- if(!group) {
- group = Lib.ensureSingle(fullLayout._infolayer, 'g', 'g-' + titleClass);
- }
-
- var el = group.selectAll('text')
- .data(elShouldExist ? [0] : []);
- el.enter().append('text');
- el.text(txt)
- // this is hacky, but convertToTspans uses the class
- // to determine whether to rotate mathJax...
- // so we need to clear out any old class and put the
- // correct one (only relevant for colorbars, at least
- // for now) - ie don't use .classed
- .attr('class', titleClass);
- el.exit().remove();
-
- if(!elShouldExist) return group;
-
- function titleLayout(titleEl) {
- Lib.syncOrAsync([drawTitle, scootTitle], titleEl);
- }
-
- function drawTitle(titleEl) {
- var transformVal;
-
- if(transform) {
- transformVal = '';
- if(transform.rotate) {
- transformVal += 'rotate(' + [transform.rotate, attributes.x, attributes.y] + ')';
- }
- if(transform.offset) {
- transformVal += 'translate(0, ' + transform.offset + ')';
- }
- } else {
- transformVal = null;
- }
-
- titleEl.attr('transform', transformVal);
-
- titleEl.style({
- 'font-family': fontFamily,
- 'font-size': d3.round(fontSize, 2) + 'px',
- fill: Color.rgb(fontColor),
- opacity: opacity * Color.opacity(fontColor),
- 'font-weight': Plots.fontWeight
- })
- .attr(attributes)
- .call(svgTextUtils.convertToTspans, gd);
-
- return Plots.previousPromises(gd);
- }
-
- function scootTitle(titleElIn) {
- var titleGroup = d3.select(titleElIn.node().parentNode);
-
- if(avoid && avoid.selection && avoid.side && txt) {
- titleGroup.attr('transform', null);
-
- // move toward avoid.side (= left, right, top, bottom) if needed
- // can include pad (pixels, default 2)
- var shift = 0;
- var backside = {
- left: 'right',
- right: 'left',
- top: 'bottom',
- bottom: 'top'
- }[avoid.side];
- var shiftSign = (['left', 'top'].indexOf(avoid.side) !== -1) ?
- -1 : 1;
- var pad = isNumeric(avoid.pad) ? avoid.pad : 2;
- var titlebb = Drawing.bBox(titleGroup.node());
- var paperbb = {
- left: 0,
- top: 0,
- right: fullLayout.width,
- bottom: fullLayout.height
- };
- var maxshift = avoid.maxShift || (
- (paperbb[avoid.side] - titlebb[avoid.side]) *
- ((avoid.side === 'left' || avoid.side === 'top') ? -1 : 1));
- // Prevent the title going off the paper
- if(maxshift < 0) shift = maxshift;
- else {
- // so we don't have to offset each avoided element,
- // give the title the opposite offset
- var offsetLeft = avoid.offsetLeft || 0;
- var offsetTop = avoid.offsetTop || 0;
- titlebb.left -= offsetLeft;
- titlebb.right -= offsetLeft;
- titlebb.top -= offsetTop;
- titlebb.bottom -= offsetTop;
-
- // iterate over a set of elements (avoid.selection)
- // to avoid collisions with
- avoid.selection.each(function() {
- var avoidbb = Drawing.bBox(this);
-
- if(Lib.bBoxIntersect(titlebb, avoidbb, pad)) {
- shift = Math.max(shift, shiftSign * (
- avoidbb[avoid.side] - titlebb[backside]) + pad);
- }
- });
- shift = Math.min(maxshift, shift);
- }
- if(shift > 0 || maxshift < 0) {
- var shiftTemplate = {
- left: [-shift, 0],
- right: [shift, 0],
- top: [0, -shift],
- bottom: [0, shift]
- }[avoid.side];
- titleGroup.attr('transform',
- 'translate(' + shiftTemplate + ')');
- }
- }
- }
-
- el.call(titleLayout);
-
- function setPlaceholder() {
- opacity = 0;
- isplaceholder = true;
- el.text(placeholder)
- .on('mouseover.opacity', function() {
- d3.select(this).transition()
- .duration(interactConstants.SHOW_PLACEHOLDER).style('opacity', 1);
- })
- .on('mouseout.opacity', function() {
- d3.select(this).transition()
- .duration(interactConstants.HIDE_PLACEHOLDER).style('opacity', 0);
- });
- }
-
- if(editable) {
- if(!txt) setPlaceholder();
- else el.on('.opacity', null);
-
- el.call(svgTextUtils.makeEditable, {gd: gd})
- .on('edit', function(text) {
- if(traceIndex !== undefined) {
- Registry.call('_guiRestyle', gd, prop, text, traceIndex);
- } else {
- Registry.call('_guiRelayout', gd, prop, text);
- }
- })
- .on('cancel', function() {
- this.text(this.attr('data-unformatted'))
- .call(titleLayout);
- })
- .on('input', function(d) {
- this.text(d || ' ')
- .call(svgTextUtils.positionText, attributes.x, attributes.y);
- });
- }
- el.classed('js-placeholder', isplaceholder);
-
- return group;
- }
-
- },{"../../constants/interactions":148,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/plots":245,"../../registry":257,"../color":51,"../drawing":72,"d3":16,"fast-isnumeric":18}],140:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var fontAttrs = _dereq_('../../plots/font_attributes');
- var colorAttrs = _dereq_('../color/attributes');
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
- var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
- var padAttrs = _dereq_('../../plots/pad_attributes');
- var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
-
- var buttonsAttrs = templatedArray('button', {
- visible: {
- valType: 'boolean',
-
-
- },
- method: {
- valType: 'enumerated',
- values: ['restyle', 'relayout', 'animate', 'update', 'skip'],
- dflt: 'restyle',
-
-
- },
- args: {
- valType: 'info_array',
-
- freeLength: true,
- items: [
- {valType: 'any'},
- {valType: 'any'},
- {valType: 'any'}
- ],
-
- },
- label: {
- valType: 'string',
-
- dflt: '',
-
- },
- execute: {
- valType: 'boolean',
-
- dflt: true,
-
- }
- });
-
- module.exports = overrideAll(templatedArray('updatemenu', {
- _arrayAttrRegexps: [/^updatemenus\[(0|[1-9][0-9]+)\]\.buttons/],
-
- visible: {
- valType: 'boolean',
-
-
- },
-
- type: {
- valType: 'enumerated',
- values: ['dropdown', 'buttons'],
- dflt: 'dropdown',
-
-
- },
-
- direction: {
- valType: 'enumerated',
- values: ['left', 'right', 'up', 'down'],
- dflt: 'down',
-
-
- },
-
- active: {
- valType: 'integer',
-
- min: -1,
- dflt: 0,
-
- },
-
- showactive: {
- valType: 'boolean',
-
- dflt: true,
-
- },
-
- buttons: buttonsAttrs,
-
- x: {
- valType: 'number',
- min: -2,
- max: 3,
- dflt: -0.05,
-
-
- },
- xanchor: {
- valType: 'enumerated',
- values: ['auto', 'left', 'center', 'right'],
- dflt: 'right',
-
-
- },
- y: {
- valType: 'number',
- min: -2,
- max: 3,
- dflt: 1,
-
-
- },
- yanchor: {
- valType: 'enumerated',
- values: ['auto', 'top', 'middle', 'bottom'],
- dflt: 'top',
-
-
- },
-
- pad: extendFlat(padAttrs({editType: 'arraydraw'}), {
-
- }),
-
- font: fontAttrs({
-
- }),
-
- bgcolor: {
- valType: 'color',
-
-
- },
- bordercolor: {
- valType: 'color',
- dflt: colorAttrs.borderLine,
-
-
- },
- borderwidth: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
- editType: 'arraydraw',
-
- }
- }), 'arraydraw', 'from-root');
-
- },{"../../lib/extend":162,"../../plot_api/edit_types":195,"../../plot_api/plot_template":202,"../../plots/font_attributes":239,"../../plots/pad_attributes":244,"../color/attributes":50}],141:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- module.exports = {
-
- // layout attribute name
- name: 'updatemenus',
-
- // class names
- containerClassName: 'updatemenu-container',
- headerGroupClassName: 'updatemenu-header-group',
- headerClassName: 'updatemenu-header',
- headerArrowClassName: 'updatemenu-header-arrow',
- dropdownButtonGroupClassName: 'updatemenu-dropdown-button-group',
- dropdownButtonClassName: 'updatemenu-dropdown-button',
- buttonClassName: 'updatemenu-button',
- itemRectClassName: 'updatemenu-item-rect',
- itemTextClassName: 'updatemenu-item-text',
-
- // DOM attribute name in button group keeping track
- // of active update menu
- menuIndexAttrName: 'updatemenu-active-index',
-
- // id root pass to Plots.autoMargin
- autoMarginIdRoot: 'updatemenu-',
-
- // options when 'active: -1'
- blankHeaderOpts: { label: ' ' },
-
- // min item width / height
- minWidth: 30,
- minHeight: 30,
-
- // padding around item text
- textPadX: 24,
- arrowPadX: 16,
-
- // item rect radii
- rx: 2,
- ry: 2,
-
- // item text x offset off left edge
- textOffsetX: 12,
-
- // item text y offset (w.r.t. middle)
- textOffsetY: 3,
-
- // arrow offset off right edge
- arrowOffsetX: 4,
-
- // gap between header and buttons
- gapButtonHeader: 5,
-
- // gap between between buttons
- gapButton: 2,
-
- // color given to active buttons
- activeColor: '#F4FAFF',
-
- // color given to hovered buttons
- hoverColor: '#F4FAFF',
-
- // symbol for menu open arrow
- arrowSymbol: {
- left: '◄',
- right: '►',
- up: '▲',
- down: '▼'
- }
- };
-
- },{}],142:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
-
- var attributes = _dereq_('./attributes');
- var constants = _dereq_('./constants');
-
- var name = constants.name;
- var buttonAttrs = attributes.buttons;
-
-
- module.exports = function updateMenusDefaults(layoutIn, layoutOut) {
- var opts = {
- name: name,
- handleItemDefaults: menuDefaults
- };
-
- handleArrayContainerDefaults(layoutIn, layoutOut, opts);
- };
-
- function menuDefaults(menuIn, menuOut, layoutOut) {
-
- function coerce(attr, dflt) {
- return Lib.coerce(menuIn, menuOut, attributes, attr, dflt);
- }
-
- var buttons = handleArrayContainerDefaults(menuIn, menuOut, {
- name: 'buttons',
- handleItemDefaults: buttonDefaults
- });
-
- var visible = coerce('visible', buttons.length > 0);
- if(!visible) return;
-
- coerce('active');
- coerce('direction');
- coerce('type');
- coerce('showactive');
-
- coerce('x');
- coerce('y');
- Lib.noneOrAll(menuIn, menuOut, ['x', 'y']);
-
- coerce('xanchor');
- coerce('yanchor');
-
- coerce('pad.t');
- coerce('pad.r');
- coerce('pad.b');
- coerce('pad.l');
-
- Lib.coerceFont(coerce, 'font', layoutOut.font);
-
- coerce('bgcolor', layoutOut.paper_bgcolor);
- coerce('bordercolor');
- coerce('borderwidth');
- }
-
- function buttonDefaults(buttonIn, buttonOut) {
- function coerce(attr, dflt) {
- return Lib.coerce(buttonIn, buttonOut, buttonAttrs, attr, dflt);
- }
-
- var visible = coerce('visible',
- (buttonIn.method === 'skip' || Array.isArray(buttonIn.args)));
- if(visible) {
- coerce('method');
- coerce('args');
- coerce('label');
- coerce('execute');
- }
- }
-
- },{"../../lib":168,"../../plots/array_container_defaults":208,"./attributes":140,"./constants":141}],143:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Plots = _dereq_('../../plots/plots');
- var Color = _dereq_('../color');
- var Drawing = _dereq_('../drawing');
- var Lib = _dereq_('../../lib');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
-
- var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING;
-
- var constants = _dereq_('./constants');
- var ScrollBox = _dereq_('./scrollbox');
-
- module.exports = function draw(gd) {
- var fullLayout = gd._fullLayout;
- var menuData = Lib.filterVisible(fullLayout[constants.name]);
-
- /* Update menu data is bound to the header-group.
- * The items in the header group are always present.
- *
- * Upon clicking on a header its corresponding button
- * data is bound to the button-group.
- *
- * We draw all headers in one group before all buttons
- * so that the buttons *always* appear above the headers.
- *
- * Note that only one set of buttons are visible at once.
- *
- * <g container />
- *
- * <g header-group />
- * <g item header />
- * <text item header-arrow />
- * <g header-group />
- * <g item header />
- * <text item header-arrow />
- * ...
- *
- * <g button-group />
- * <g item button />
- * <g item button />
- * ...
- */
-
- function clearAutoMargin(menuOpts) {
- Plots.autoMargin(gd, autoMarginId(menuOpts));
- }
-
- // draw update menu container
- var menus = fullLayout._menulayer
- .selectAll('g.' + constants.containerClassName)
- .data(menuData.length > 0 ? [0] : []);
-
- menus.enter().append('g')
- .classed(constants.containerClassName, true)
- .style('cursor', 'pointer');
-
- menus.exit().each(function() {
- // Most components don't need to explicitly remove autoMargin, because
- // marginPushers does this - but updatemenu updates don't go through
- // a full replot so we need to explicitly remove it.
- // This is for removing *all* updatemenus, removing individuals is
- // handled below, in headerGroups.exit
- d3.select(this).selectAll('g.' + constants.headerGroupClassName)
- .each(clearAutoMargin);
- }).remove();
-
- // return early if no update menus are visible
- if(menuData.length === 0) return;
-
- // join header group
- var headerGroups = menus.selectAll('g.' + constants.headerGroupClassName)
- .data(menuData, keyFunction);
-
- headerGroups.enter().append('g')
- .classed(constants.headerGroupClassName, true);
-
- // draw dropdown button container
- var gButton = Lib.ensureSingle(menus, 'g', constants.dropdownButtonGroupClassName, function(s) {
- s.style('pointer-events', 'all');
- });
-
- // find dimensions before plotting anything (this mutates menuOpts)
- for(var i = 0; i < menuData.length; i++) {
- var menuOpts = menuData[i];
- findDimensions(gd, menuOpts);
- }
-
- // setup scrollbox
- var scrollBoxId = 'updatemenus' + fullLayout._uid;
- var scrollBox = new ScrollBox(gd, gButton, scrollBoxId);
-
- // remove exiting header, remove dropped buttons and reset margins
- if(headerGroups.enter().size()) {
- // make sure gButton is on top of all headers
- gButton.node().parentNode.appendChild(gButton.node());
- gButton.call(removeAllButtons);
- }
-
- headerGroups.exit().each(function(menuOpts) {
- gButton.call(removeAllButtons);
- clearAutoMargin(menuOpts);
- }).remove();
-
- // draw headers!
- headerGroups.each(function(menuOpts) {
- var gHeader = d3.select(this);
-
- var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
- Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
- setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, scrollBox, data.index, true);
- });
-
- if(menuOpts.type === 'dropdown') {
- drawHeader(gd, gHeader, gButton, scrollBox, menuOpts);
-
- // if this menu is active, update the dropdown container
- if(isActive(gButton, menuOpts)) {
- drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
- }
- } else {
- drawButtons(gd, gHeader, null, null, menuOpts);
- }
-
- });
- };
-
- // Note that '_index' is set at the default step,
- // it corresponds to the menu index in the user layout update menu container.
- // Because a menu can be set invisible,
- // this is a more 'consistent' field than the index in the menuData.
- function keyFunction(menuOpts) {
- return menuOpts._index;
- }
-
- function isFolded(gButton) {
- return +gButton.attr(constants.menuIndexAttrName) === -1;
- }
-
- function isActive(gButton, menuOpts) {
- return +gButton.attr(constants.menuIndexAttrName) === menuOpts._index;
- }
-
- function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex, isSilentUpdate) {
- // update 'active' attribute in menuOpts
- menuOpts.active = buttonIndex;
-
- // due to templating, it's possible this slider doesn't even exist yet
- arrayEditor(gd.layout, constants.name, menuOpts)
- .applyUpdate('active', buttonIndex);
-
- if(menuOpts.type === 'buttons') {
- drawButtons(gd, gHeader, null, null, menuOpts);
- }
- else if(menuOpts.type === 'dropdown') {
- // fold up buttons and redraw header
- gButton.attr(constants.menuIndexAttrName, '-1');
-
- drawHeader(gd, gHeader, gButton, scrollBox, menuOpts);
-
- if(!isSilentUpdate) {
- drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
- }
- }
- }
-
- function drawHeader(gd, gHeader, gButton, scrollBox, menuOpts) {
- var header = Lib.ensureSingle(gHeader, 'g', constants.headerClassName, function(s) {
- s.style('pointer-events', 'all');
- });
-
- var dims = menuOpts._dims;
- var active = menuOpts.active;
- var headerOpts = menuOpts.buttons[active] || constants.blankHeaderOpts;
- var posOpts = { y: menuOpts.pad.t, yPad: 0, x: menuOpts.pad.l, xPad: 0, index: 0 };
- var positionOverrides = {
- width: dims.headerWidth,
- height: dims.headerHeight
- };
-
- header
- .call(drawItem, menuOpts, headerOpts, gd)
- .call(setItemPosition, menuOpts, posOpts, positionOverrides);
-
- // draw drop arrow at the right edge
- var arrow = Lib.ensureSingle(gHeader, 'text', constants.headerArrowClassName, function(s) {
- s.classed('user-select-none', true)
- .attr('text-anchor', 'end')
- .call(Drawing.font, menuOpts.font)
- .text(constants.arrowSymbol[menuOpts.direction]);
- });
-
- arrow.attr({
- x: dims.headerWidth - constants.arrowOffsetX + menuOpts.pad.l,
- y: dims.headerHeight / 2 + constants.textOffsetY + menuOpts.pad.t
- });
-
- header.on('click', function() {
- gButton.call(removeAllButtons,
- String(isActive(gButton, menuOpts) ? -1 : menuOpts._index)
- );
-
- drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
- });
-
- header.on('mouseover', function() {
- header.call(styleOnMouseOver);
- });
-
- header.on('mouseout', function() {
- header.call(styleOnMouseOut, menuOpts);
- });
-
- // translate header group
- Drawing.setTranslate(gHeader, dims.lx, dims.ly);
- }
-
- function drawButtons(gd, gHeader, gButton, scrollBox, menuOpts) {
- // If this is a set of buttons, set pointer events = all since we play
- // some minor games with which container is which in order to simplify
- // the drawing of *either* buttons or menus
- if(!gButton) {
- gButton = gHeader;
- gButton.attr('pointer-events', 'all');
- }
-
- var buttonData = (!isFolded(gButton) || menuOpts.type === 'buttons') ?
- menuOpts.buttons :
- [];
-
- var klass = menuOpts.type === 'dropdown' ? constants.dropdownButtonClassName : constants.buttonClassName;
-
- var buttons = gButton.selectAll('g.' + klass)
- .data(Lib.filterVisible(buttonData));
-
- var enter = buttons.enter().append('g')
- .classed(klass, true);
-
- var exit = buttons.exit();
-
- if(menuOpts.type === 'dropdown') {
- enter.attr('opacity', '0')
- .transition()
- .attr('opacity', '1');
-
- exit.transition()
- .attr('opacity', '0')
- .remove();
- } else {
- exit.remove();
- }
-
- var x0 = 0;
- var y0 = 0;
- var dims = menuOpts._dims;
-
- var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
-
- if(menuOpts.type === 'dropdown') {
- if(isVertical) {
- y0 = dims.headerHeight + constants.gapButtonHeader;
- } else {
- x0 = dims.headerWidth + constants.gapButtonHeader;
- }
- }
-
- if(menuOpts.type === 'dropdown' && menuOpts.direction === 'up') {
- y0 = -constants.gapButtonHeader + constants.gapButton - dims.openHeight;
- }
-
- if(menuOpts.type === 'dropdown' && menuOpts.direction === 'left') {
- x0 = -constants.gapButtonHeader + constants.gapButton - dims.openWidth;
- }
-
- var posOpts = {
- x: dims.lx + x0 + menuOpts.pad.l,
- y: dims.ly + y0 + menuOpts.pad.t,
- yPad: constants.gapButton,
- xPad: constants.gapButton,
- index: 0,
- };
-
- var scrollBoxPosition = {
- l: posOpts.x + menuOpts.borderwidth,
- t: posOpts.y + menuOpts.borderwidth
- };
-
- buttons.each(function(buttonOpts, buttonIndex) {
- var button = d3.select(this);
-
- button
- .call(drawItem, menuOpts, buttonOpts, gd)
- .call(setItemPosition, menuOpts, posOpts);
-
- button.on('click', function() {
- // skip `dragend` events
- if(d3.event.defaultPrevented) return;
-
- setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex);
-
- if(buttonOpts.execute) {
- Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);
- }
-
- gd.emit('plotly_buttonclicked', {menu: menuOpts, button: buttonOpts, active: menuOpts.active});
- });
-
- button.on('mouseover', function() {
- button.call(styleOnMouseOver);
- });
-
- button.on('mouseout', function() {
- button.call(styleOnMouseOut, menuOpts);
- buttons.call(styleButtons, menuOpts);
- });
- });
-
- buttons.call(styleButtons, menuOpts);
-
- if(isVertical) {
- scrollBoxPosition.w = Math.max(dims.openWidth, dims.headerWidth);
- scrollBoxPosition.h = posOpts.y - scrollBoxPosition.t;
- }
- else {
- scrollBoxPosition.w = posOpts.x - scrollBoxPosition.l;
- scrollBoxPosition.h = Math.max(dims.openHeight, dims.headerHeight);
- }
-
- scrollBoxPosition.direction = menuOpts.direction;
-
- if(scrollBox) {
- if(buttons.size()) {
- drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, scrollBoxPosition);
- }
- else {
- hideScrollBox(scrollBox);
- }
- }
- }
-
- function drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, position) {
- // enable the scrollbox
- var direction = menuOpts.direction;
- var isVertical = (direction === 'up' || direction === 'down');
- var dims = menuOpts._dims;
-
- var active = menuOpts.active;
- var translateX, translateY;
- var i;
- if(isVertical) {
- translateY = 0;
- for(i = 0; i < active; i++) {
- translateY += dims.heights[i] + constants.gapButton;
- }
- }
- else {
- translateX = 0;
- for(i = 0; i < active; i++) {
- translateX += dims.widths[i] + constants.gapButton;
- }
- }
-
- scrollBox.enable(position, translateX, translateY);
-
- if(scrollBox.hbar) {
- scrollBox.hbar
- .attr('opacity', '0')
- .transition()
- .attr('opacity', '1');
- }
-
- if(scrollBox.vbar) {
- scrollBox.vbar
- .attr('opacity', '0')
- .transition()
- .attr('opacity', '1');
- }
- }
-
- function hideScrollBox(scrollBox) {
- var hasHBar = !!scrollBox.hbar;
- var hasVBar = !!scrollBox.vbar;
-
- if(hasHBar) {
- scrollBox.hbar
- .transition()
- .attr('opacity', '0')
- .each('end', function() {
- hasHBar = false;
- if(!hasVBar) scrollBox.disable();
- });
- }
-
- if(hasVBar) {
- scrollBox.vbar
- .transition()
- .attr('opacity', '0')
- .each('end', function() {
- hasVBar = false;
- if(!hasHBar) scrollBox.disable();
- });
- }
- }
-
- function drawItem(item, menuOpts, itemOpts, gd) {
- item.call(drawItemRect, menuOpts)
- .call(drawItemText, menuOpts, itemOpts, gd);
- }
-
- function drawItemRect(item, menuOpts) {
- var rect = Lib.ensureSingle(item, 'rect', constants.itemRectClassName, function(s) {
- s.attr({
- rx: constants.rx,
- ry: constants.ry,
- 'shape-rendering': 'crispEdges'
- });
- });
-
- rect.call(Color.stroke, menuOpts.bordercolor)
- .call(Color.fill, menuOpts.bgcolor)
- .style('stroke-width', menuOpts.borderwidth + 'px');
- }
-
- function drawItemText(item, menuOpts, itemOpts, gd) {
- var text = Lib.ensureSingle(item, 'text', constants.itemTextClassName, function(s) {
- s.classed('user-select-none', true)
- .attr({
- 'text-anchor': 'start',
- 'data-notex': 1
- });
- });
-
- text.call(Drawing.font, menuOpts.font)
- .text(itemOpts.label)
- .call(svgTextUtils.convertToTspans, gd);
- }
-
- function styleButtons(buttons, menuOpts) {
- var active = menuOpts.active;
-
- buttons.each(function(buttonOpts, i) {
- var button = d3.select(this);
-
- if(i === active && menuOpts.showactive) {
- button.select('rect.' + constants.itemRectClassName)
- .call(Color.fill, constants.activeColor);
- }
- });
- }
-
- function styleOnMouseOver(item) {
- item.select('rect.' + constants.itemRectClassName)
- .call(Color.fill, constants.hoverColor);
- }
-
- function styleOnMouseOut(item, menuOpts) {
- item.select('rect.' + constants.itemRectClassName)
- .call(Color.fill, menuOpts.bgcolor);
- }
-
- // find item dimensions (this mutates menuOpts)
- function findDimensions(gd, menuOpts) {
- var dims = menuOpts._dims = {
- width1: 0,
- height1: 0,
- heights: [],
- widths: [],
- totalWidth: 0,
- totalHeight: 0,
- openWidth: 0,
- openHeight: 0,
- lx: 0,
- ly: 0
- };
-
- var fakeButtons = Drawing.tester.selectAll('g.' + constants.dropdownButtonClassName)
- .data(Lib.filterVisible(menuOpts.buttons));
-
- fakeButtons.enter().append('g')
- .classed(constants.dropdownButtonClassName, true);
-
- var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
-
- // loop over fake buttons to find width / height
- fakeButtons.each(function(buttonOpts, i) {
- var button = d3.select(this);
-
- button.call(drawItem, menuOpts, buttonOpts, gd);
-
- var text = button.select('.' + constants.itemTextClassName);
-
- // width is given by max width of all buttons
- var tWidth = text.node() && Drawing.bBox(text.node()).width;
- var wEff = Math.max(tWidth + constants.textPadX, constants.minWidth);
-
- // height is determined by item text
- var tHeight = menuOpts.font.size * LINE_SPACING;
- var tLines = svgTextUtils.lineCount(text);
- var hEff = Math.max(tHeight * tLines, constants.minHeight) + constants.textOffsetY;
-
- hEff = Math.ceil(hEff);
- wEff = Math.ceil(wEff);
-
- // Store per-item sizes since a row of horizontal buttons, for example,
- // don't all need to be the same width:
- dims.widths[i] = wEff;
- dims.heights[i] = hEff;
-
- // Height and width of individual element:
- dims.height1 = Math.max(dims.height1, hEff);
- dims.width1 = Math.max(dims.width1, wEff);
-
- if(isVertical) {
- dims.totalWidth = Math.max(dims.totalWidth, wEff);
- dims.openWidth = dims.totalWidth;
- dims.totalHeight += hEff + constants.gapButton;
- dims.openHeight += hEff + constants.gapButton;
- } else {
- dims.totalWidth += wEff + constants.gapButton;
- dims.openWidth += wEff + constants.gapButton;
- dims.totalHeight = Math.max(dims.totalHeight, hEff);
- dims.openHeight = dims.totalHeight;
- }
- });
-
- if(isVertical) {
- dims.totalHeight -= constants.gapButton;
- } else {
- dims.totalWidth -= constants.gapButton;
- }
-
-
- dims.headerWidth = dims.width1 + constants.arrowPadX;
- dims.headerHeight = dims.height1;
-
- if(menuOpts.type === 'dropdown') {
- if(isVertical) {
- dims.width1 += constants.arrowPadX;
- dims.totalHeight = dims.height1;
- } else {
- dims.totalWidth = dims.width1;
- }
- dims.totalWidth += constants.arrowPadX;
- }
-
- fakeButtons.remove();
-
- var paddedWidth = dims.totalWidth + menuOpts.pad.l + menuOpts.pad.r;
- var paddedHeight = dims.totalHeight + menuOpts.pad.t + menuOpts.pad.b;
-
- var graphSize = gd._fullLayout._size;
- dims.lx = graphSize.l + graphSize.w * menuOpts.x;
- dims.ly = graphSize.t + graphSize.h * (1 - menuOpts.y);
-
- var xanchor = 'left';
- if(Lib.isRightAnchor(menuOpts)) {
- dims.lx -= paddedWidth;
- xanchor = 'right';
- }
- if(Lib.isCenterAnchor(menuOpts)) {
- dims.lx -= paddedWidth / 2;
- xanchor = 'center';
- }
-
- var yanchor = 'top';
- if(Lib.isBottomAnchor(menuOpts)) {
- dims.ly -= paddedHeight;
- yanchor = 'bottom';
- }
- if(Lib.isMiddleAnchor(menuOpts)) {
- dims.ly -= paddedHeight / 2;
- yanchor = 'middle';
- }
-
- dims.totalWidth = Math.ceil(dims.totalWidth);
- dims.totalHeight = Math.ceil(dims.totalHeight);
- dims.lx = Math.round(dims.lx);
- dims.ly = Math.round(dims.ly);
-
- Plots.autoMargin(gd, autoMarginId(menuOpts), {
- x: menuOpts.x,
- y: menuOpts.y,
- l: paddedWidth * ({right: 1, center: 0.5}[xanchor] || 0),
- r: paddedWidth * ({left: 1, center: 0.5}[xanchor] || 0),
- b: paddedHeight * ({top: 1, middle: 0.5}[yanchor] || 0),
- t: paddedHeight * ({bottom: 1, middle: 0.5}[yanchor] || 0)
- });
- }
-
- function autoMarginId(menuOpts) {
- return constants.autoMarginIdRoot + menuOpts._index;
- }
-
- // set item positions (mutates posOpts)
- function setItemPosition(item, menuOpts, posOpts, overrideOpts) {
- overrideOpts = overrideOpts || {};
- var rect = item.select('.' + constants.itemRectClassName);
- var text = item.select('.' + constants.itemTextClassName);
- var borderWidth = menuOpts.borderwidth;
- var index = posOpts.index;
- var dims = menuOpts._dims;
-
- Drawing.setTranslate(item, borderWidth + posOpts.x, borderWidth + posOpts.y);
-
- var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
- var finalHeight = overrideOpts.height || (isVertical ? dims.heights[index] : dims.height1);
-
- rect.attr({
- x: 0,
- y: 0,
- width: overrideOpts.width || (isVertical ? dims.width1 : dims.widths[index]),
- height: finalHeight
- });
-
- var tHeight = menuOpts.font.size * LINE_SPACING;
- var tLines = svgTextUtils.lineCount(text);
- var spanOffset = ((tLines - 1) * tHeight / 2);
-
- svgTextUtils.positionText(text, constants.textOffsetX,
- finalHeight / 2 - spanOffset + constants.textOffsetY);
-
- if(isVertical) {
- posOpts.y += dims.heights[index] + posOpts.yPad;
- } else {
- posOpts.x += dims.widths[index] + posOpts.xPad;
- }
-
- posOpts.index++;
- }
-
- function removeAllButtons(gButton, newMenuIndexAttr) {
- gButton
- .attr(constants.menuIndexAttrName, newMenuIndexAttr || '-1')
- .selectAll('g.' + constants.dropdownButtonClassName).remove();
- }
-
- },{"../../constants/alignment":146,"../../lib":168,"../../lib/svg_text_utils":189,"../../plot_api/plot_template":202,"../../plots/plots":245,"../color":51,"../drawing":72,"./constants":141,"./scrollbox":145,"d3":16}],144:[function(_dereq_,module,exports){
- arguments[4][138][0].apply(exports,arguments)
- },{"./attributes":140,"./constants":141,"./defaults":142,"./draw":143,"dup":138}],145:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = ScrollBox;
-
- var d3 = _dereq_('d3');
-
- var Color = _dereq_('../color');
- var Drawing = _dereq_('../drawing');
-
- var Lib = _dereq_('../../lib');
-
- /**
- * Helper class to setup a scroll box
- *
- * @class
- * @param gd Plotly's graph div
- * @param container Container to be scroll-boxed (as a D3 selection)
- * @param {string} id Id for the clip path to implement the scroll box
- */
- function ScrollBox(gd, container, id) {
- this.gd = gd;
- this.container = container;
- this.id = id;
-
- // See ScrollBox.prototype.enable for further definition
- this.position = null; // scrollbox position
- this.translateX = null; // scrollbox horizontal translation
- this.translateY = null; // scrollbox vertical translation
- this.hbar = null; // horizontal scrollbar D3 selection
- this.vbar = null; // vertical scrollbar D3 selection
-
- // <rect> element to capture pointer events
- this.bg = this.container.selectAll('rect.scrollbox-bg').data([0]);
-
- this.bg.exit()
- .on('.drag', null)
- .on('wheel', null)
- .remove();
-
- this.bg.enter().append('rect')
- .classed('scrollbox-bg', true)
- .style('pointer-events', 'all')
- .attr({
- opacity: 0,
- x: 0,
- y: 0,
- width: 0,
- height: 0
- });
- }
-
- // scroll bar dimensions
- ScrollBox.barWidth = 2;
- ScrollBox.barLength = 20;
- ScrollBox.barRadius = 2;
- ScrollBox.barPad = 1;
- ScrollBox.barColor = '#808BA4';
-
- /**
- * If needed, setup a clip path and scrollbars
- *
- * @method
- * @param {Object} position
- * @param {number} position.l Left side position (in pixels)
- * @param {number} position.t Top side (in pixels)
- * @param {number} position.w Width (in pixels)
- * @param {number} position.h Height (in pixels)
- * @param {string} [position.direction='down']
- * Either 'down', 'left', 'right' or 'up'
- * @param {number} [translateX=0] Horizontal offset (in pixels)
- * @param {number} [translateY=0] Vertical offset (in pixels)
- */
- ScrollBox.prototype.enable = function enable(position, translateX, translateY) {
- var fullLayout = this.gd._fullLayout;
- var fullWidth = fullLayout.width;
- var fullHeight = fullLayout.height;
-
- // compute position of scrollbox
- this.position = position;
-
- var l = this.position.l;
- var w = this.position.w;
- var t = this.position.t;
- var h = this.position.h;
- var direction = this.position.direction;
- var isDown = (direction === 'down');
- var isLeft = (direction === 'left');
- var isRight = (direction === 'right');
- var isUp = (direction === 'up');
- var boxW = w;
- var boxH = h;
- var boxL, boxR;
- var boxT, boxB;
-
- if(!isDown && !isLeft && !isRight && !isUp) {
- this.position.direction = 'down';
- isDown = true;
- }
-
- var isVertical = isDown || isUp;
- if(isVertical) {
- boxL = l;
- boxR = boxL + boxW;
-
- if(isDown) {
- // anchor to top side
- boxT = t;
- boxB = Math.min(boxT + boxH, fullHeight);
- boxH = boxB - boxT;
- }
- else {
- // anchor to bottom side
- boxB = t + boxH;
- boxT = Math.max(boxB - boxH, 0);
- boxH = boxB - boxT;
- }
- }
- else {
- boxT = t;
- boxB = boxT + boxH;
-
- if(isLeft) {
- // anchor to right side
- boxR = l + boxW;
- boxL = Math.max(boxR - boxW, 0);
- boxW = boxR - boxL;
- }
- else {
- // anchor to left side
- boxL = l;
- boxR = Math.min(boxL + boxW, fullWidth);
- boxW = boxR - boxL;
- }
- }
-
- this._box = {
- l: boxL,
- t: boxT,
- w: boxW,
- h: boxH
- };
-
- // compute position of horizontal scroll bar
- var needsHorizontalScrollBar = (w > boxW);
- var hbarW = ScrollBox.barLength + 2 * ScrollBox.barPad;
- var hbarH = ScrollBox.barWidth + 2 * ScrollBox.barPad;
- // draw horizontal scrollbar on the bottom side
- var hbarL = l;
- var hbarT = t + h;
-
- if(hbarT + hbarH > fullHeight) hbarT = fullHeight - hbarH;
-
- var hbar = this.container.selectAll('rect.scrollbar-horizontal').data(
- (needsHorizontalScrollBar) ? [0] : []);
-
- hbar.exit()
- .on('.drag', null)
- .remove();
-
- hbar.enter().append('rect')
- .classed('scrollbar-horizontal', true)
- .call(Color.fill, ScrollBox.barColor);
-
- if(needsHorizontalScrollBar) {
- this.hbar = hbar.attr({
- 'rx': ScrollBox.barRadius,
- 'ry': ScrollBox.barRadius,
- 'x': hbarL,
- 'y': hbarT,
- 'width': hbarW,
- 'height': hbarH
- });
-
- // hbar center moves between hbarXMin and hbarXMin + hbarTranslateMax
- this._hbarXMin = hbarL + hbarW / 2;
- this._hbarTranslateMax = boxW - hbarW;
- }
- else {
- delete this.hbar;
- delete this._hbarXMin;
- delete this._hbarTranslateMax;
- }
-
- // compute position of vertical scroll bar
- var needsVerticalScrollBar = (h > boxH);
- var vbarW = ScrollBox.barWidth + 2 * ScrollBox.barPad;
- var vbarH = ScrollBox.barLength + 2 * ScrollBox.barPad;
- // draw vertical scrollbar on the right side
- var vbarL = l + w;
- var vbarT = t;
-
- if(vbarL + vbarW > fullWidth) vbarL = fullWidth - vbarW;
-
- var vbar = this.container.selectAll('rect.scrollbar-vertical').data(
- (needsVerticalScrollBar) ? [0] : []);
-
- vbar.exit()
- .on('.drag', null)
- .remove();
-
- vbar.enter().append('rect')
- .classed('scrollbar-vertical', true)
- .call(Color.fill, ScrollBox.barColor);
-
- if(needsVerticalScrollBar) {
- this.vbar = vbar.attr({
- 'rx': ScrollBox.barRadius,
- 'ry': ScrollBox.barRadius,
- 'x': vbarL,
- 'y': vbarT,
- 'width': vbarW,
- 'height': vbarH
- });
-
- // vbar center moves between vbarYMin and vbarYMin + vbarTranslateMax
- this._vbarYMin = vbarT + vbarH / 2;
- this._vbarTranslateMax = boxH - vbarH;
- }
- else {
- delete this.vbar;
- delete this._vbarYMin;
- delete this._vbarTranslateMax;
- }
-
- // setup a clip path (if scroll bars are needed)
- var clipId = this.id;
- var clipL = boxL - 0.5;
- var clipR = (needsVerticalScrollBar) ? boxR + vbarW + 0.5 : boxR + 0.5;
- var clipT = boxT - 0.5;
- var clipB = (needsHorizontalScrollBar) ? boxB + hbarH + 0.5 : boxB + 0.5;
-
- var clipPath = fullLayout._topdefs.selectAll('#' + clipId)
- .data((needsHorizontalScrollBar || needsVerticalScrollBar) ? [0] : []);
-
- clipPath.exit().remove();
-
- clipPath.enter()
- .append('clipPath').attr('id', clipId)
- .append('rect');
-
- if(needsHorizontalScrollBar || needsVerticalScrollBar) {
- this._clipRect = clipPath.select('rect').attr({
- x: Math.floor(clipL),
- y: Math.floor(clipT),
- width: Math.ceil(clipR) - Math.floor(clipL),
- height: Math.ceil(clipB) - Math.floor(clipT)
- });
-
- this.container.call(Drawing.setClipUrl, clipId, this.gd);
-
- this.bg.attr({
- x: l,
- y: t,
- width: w,
- height: h
- });
- }
- else {
- this.bg.attr({
- width: 0,
- height: 0
- });
- this.container
- .on('wheel', null)
- .on('.drag', null)
- .call(Drawing.setClipUrl, null);
- delete this._clipRect;
- }
-
- // set up drag listeners (if scroll bars are needed)
- if(needsHorizontalScrollBar || needsVerticalScrollBar) {
- var onBoxDrag = d3.behavior.drag()
- .on('dragstart', function() {
- d3.event.sourceEvent.preventDefault();
- })
- .on('drag', this._onBoxDrag.bind(this));
-
- this.container
- .on('wheel', null)
- .on('wheel', this._onBoxWheel.bind(this))
- .on('.drag', null)
- .call(onBoxDrag);
-
- var onBarDrag = d3.behavior.drag()
- .on('dragstart', function() {
- d3.event.sourceEvent.preventDefault();
- d3.event.sourceEvent.stopPropagation();
- })
- .on('drag', this._onBarDrag.bind(this));
-
- if(needsHorizontalScrollBar) {
- this.hbar
- .on('.drag', null)
- .call(onBarDrag);
- }
-
- if(needsVerticalScrollBar) {
- this.vbar
- .on('.drag', null)
- .call(onBarDrag);
- }
- }
-
- // set scrollbox translation
- this.setTranslate(translateX, translateY);
- };
-
- /**
- * If present, remove clip-path and scrollbars
- *
- * @method
- */
- ScrollBox.prototype.disable = function disable() {
- if(this.hbar || this.vbar) {
- this.bg.attr({
- width: 0,
- height: 0
- });
- this.container
- .on('wheel', null)
- .on('.drag', null)
- .call(Drawing.setClipUrl, null);
- delete this._clipRect;
- }
-
- if(this.hbar) {
- this.hbar.on('.drag', null);
- this.hbar.remove();
- delete this.hbar;
- delete this._hbarXMin;
- delete this._hbarTranslateMax;
- }
-
- if(this.vbar) {
- this.vbar.on('.drag', null);
- this.vbar.remove();
- delete this.vbar;
- delete this._vbarYMin;
- delete this._vbarTranslateMax;
- }
- };
-
- /**
- * Handles scroll box drag events
- *
- * @method
- */
- ScrollBox.prototype._onBoxDrag = function onBarDrag() {
- var translateX = this.translateX;
- var translateY = this.translateY;
-
- if(this.hbar) {
- translateX -= d3.event.dx;
- }
-
- if(this.vbar) {
- translateY -= d3.event.dy;
- }
-
- this.setTranslate(translateX, translateY);
- };
-
- /**
- * Handles scroll box wheel events
- *
- * @method
- */
- ScrollBox.prototype._onBoxWheel = function onBarWheel() {
- var translateX = this.translateX;
- var translateY = this.translateY;
-
- if(this.hbar) {
- translateX += d3.event.deltaY;
- }
-
- if(this.vbar) {
- translateY += d3.event.deltaY;
- }
-
- this.setTranslate(translateX, translateY);
- };
-
- /**
- * Handles scroll bar drag events
- *
- * @method
- */
- ScrollBox.prototype._onBarDrag = function onBarDrag() {
- var translateX = this.translateX;
- var translateY = this.translateY;
-
- if(this.hbar) {
- var xMin = translateX + this._hbarXMin;
- var xMax = xMin + this._hbarTranslateMax;
- var x = Lib.constrain(d3.event.x, xMin, xMax);
- var xf = (x - xMin) / (xMax - xMin);
-
- var translateXMax = this.position.w - this._box.w;
-
- translateX = xf * translateXMax;
- }
-
- if(this.vbar) {
- var yMin = translateY + this._vbarYMin;
- var yMax = yMin + this._vbarTranslateMax;
- var y = Lib.constrain(d3.event.y, yMin, yMax);
- var yf = (y - yMin) / (yMax - yMin);
-
- var translateYMax = this.position.h - this._box.h;
-
- translateY = yf * translateYMax;
- }
-
- this.setTranslate(translateX, translateY);
- };
-
- /**
- * Set clip path and scroll bar translate transform
- *
- * @method
- * @param {number} [translateX=0] Horizontal offset (in pixels)
- * @param {number} [translateY=0] Vertical offset (in pixels)
- */
- ScrollBox.prototype.setTranslate = function setTranslate(translateX, translateY) {
- // store translateX and translateY (needed by mouse event handlers)
- var translateXMax = this.position.w - this._box.w;
- var translateYMax = this.position.h - this._box.h;
-
- translateX = Lib.constrain(translateX || 0, 0, translateXMax);
- translateY = Lib.constrain(translateY || 0, 0, translateYMax);
-
- this.translateX = translateX;
- this.translateY = translateY;
-
- this.container.call(Drawing.setTranslate,
- this._box.l - this.position.l - translateX,
- this._box.t - this.position.t - translateY);
-
- if(this._clipRect) {
- this._clipRect.attr({
- x: Math.floor(this.position.l + translateX - 0.5),
- y: Math.floor(this.position.t + translateY - 0.5)
- });
- }
-
- if(this.hbar) {
- var xf = translateX / translateXMax;
-
- this.hbar.call(Drawing.setTranslate,
- translateX + xf * this._hbarTranslateMax,
- translateY);
- }
-
- if(this.vbar) {
- var yf = translateY / translateYMax;
-
- this.vbar.call(Drawing.setTranslate,
- translateX,
- translateY + yf * this._vbarTranslateMax);
- }
- };
-
- },{"../../lib":168,"../color":51,"../drawing":72,"d3":16}],146:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- // fraction of some size to get to a named position
- module.exports = {
- // from bottom left: this is the origin of our paper-reference
- // positioning system
- FROM_BL: {
- left: 0,
- center: 0.5,
- right: 1,
- bottom: 0,
- middle: 0.5,
- top: 1
- },
- // from top left: this is the screen pixel positioning origin
- FROM_TL: {
- left: 0,
- center: 0.5,
- right: 1,
- bottom: 1,
- middle: 0.5,
- top: 0
- },
- // from bottom right: sometimes you just need the opposite of ^^
- FROM_BR: {
- left: 1,
- center: 0.5,
- right: 0,
- bottom: 0,
- middle: 0.5,
- top: 1
- },
- // multiple of fontSize to get the vertical offset between lines
- LINE_SPACING: 1.3,
-
- // multiple of fontSize to shift from the baseline
- // to the cap (captical letter) line
- // (to use when we don't calculate this shift from Drawing.bBox)
- // This is an approximation since in reality cap height can differ
- // from font to font. However, according to Wikipedia
- // an "average" font might have a cap height of 70% of the em
- // https://en.wikipedia.org/wiki/Em_(typography)#History
- CAP_SHIFT: 0.70,
-
- // half the cap height (distance between baseline and cap line)
- // of an "average" font (for more info see above).
- MID_SHIFT: 0.35,
-
- OPPOSITE_SIDE: {
- left: 'right',
- right: 'left',
- top: 'bottom',
- bottom: 'top'
- }
- };
-
- },{}],147:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- COMPARISON_OPS: ['=', '!=', '<', '>=', '>', '<='],
- COMPARISON_OPS2: ['=', '<', '>=', '>', '<='],
- INTERVAL_OPS: ['[]', '()', '[)', '(]', '][', ')(', '](', ')['],
- SET_OPS: ['{}', '}{'],
- CONSTRAINT_REDUCTION: {
- // for contour constraints, open/closed endpoints are equivalent
- '=': '=',
-
- '<': '<',
- '<=': '<',
-
- '>': '>',
- '>=': '>',
-
- '[]': '[]',
- '()': '[]',
- '[)': '[]',
- '(]': '[]',
-
- '][': '][',
- ')(': '][',
- '](': '][',
- ')[': ']['
- }
- };
-
- },{}],148:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
-
- module.exports = {
- /**
- * Timing information for interactive elements
- */
- SHOW_PLACEHOLDER: 100,
- HIDE_PLACEHOLDER: 1000,
-
- // ms between first mousedown and 2nd mouseup to constitute dblclick...
- // we don't seem to have access to the system setting
- DBLCLICKDELAY: 300,
-
- // opacity dimming fraction for points that are not in selection
- DESELECTDIM: 0.2
- };
-
- },{}],149:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
-
- module.exports = {
- /**
- * Standardize all missing data in calcdata to use undefined
- * never null or NaN.
- * That way we can use !==undefined, or !== BADNUM,
- * to test for real data
- */
- BADNUM: undefined,
-
- /*
- * Limit certain operations to well below floating point max value
- * to avoid glitches: Make sure that even when you multiply it by the
- * number of pixels on a giant screen it still works
- */
- FP_SAFE: Number.MAX_VALUE / 10000,
-
- /*
- * conversion of date units to milliseconds
- * year and month constants are marked "AVG"
- * to remind us that not all years and months
- * have the same length
- */
- ONEAVGYEAR: 31557600000, // 365.25 days
- ONEAVGMONTH: 2629800000, // 1/12 of ONEAVGYEAR
- ONEDAY: 86400000,
- ONEHOUR: 3600000,
- ONEMIN: 60000,
- ONESEC: 1000,
-
- /*
- * For fast conversion btwn world calendars and epoch ms, the Julian Day Number
- * of the unix epoch. From calendars.instance().newDate(1970, 1, 1).toJD()
- */
- EPOCHJD: 2440587.5,
-
- /*
- * Are two values nearly equal? Compare to 1PPM
- */
- ALMOST_EQUAL: 1 - 1e-6,
-
- /*
- * If we're asked to clip a non-positive log value, how far off-screen
- * do we put it?
- */
- LOG_CLIP: 10,
-
- /*
- * not a number, but for displaying numbers: the "minus sign" symbol is
- * wider than the regular ascii dash "-"
- */
- MINUS_SIGN: '\u2212'
- };
-
- },{}],150:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- exports.xmlns = 'http://www.w3.org/2000/xmlns/';
- exports.svg = 'http://www.w3.org/2000/svg';
- exports.xlink = 'http://www.w3.org/1999/xlink';
-
- // the 'old' d3 quirk got fix in v3.5.7
- // https://github.com/mbostock/d3/commit/a6f66e9dd37f764403fc7c1f26be09ab4af24fed
- exports.svgAttrs = {
- xmlns: exports.svg,
- 'xmlns:xlink': exports.xlink
- };
-
- },{}],151:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- // package version injected by `npm run preprocess`
- exports.version = '1.44.4';
-
- // inject promise polyfill
- _dereq_('es6-promise').polyfill();
-
- // inject plot css
- _dereq_('../build/plotcss');
-
- // inject default MathJax config
- _dereq_('./fonts/mathjax_config')();
-
- // include registry module and expose register method
- var Registry = _dereq_('./registry');
- var register = exports.register = Registry.register;
-
- // expose plot api methods
- var plotApi = _dereq_('./plot_api');
- var methodNames = Object.keys(plotApi);
- for(var i = 0; i < methodNames.length; i++) {
- var name = methodNames[i];
- // _ -> private API methods, but still registered for internal use
- if(name.charAt(0) !== '_') exports[name] = plotApi[name];
- register({
- moduleType: 'apiMethod',
- name: name,
- fn: plotApi[name]
- });
- }
-
- // scatter is the only trace included by default
- register(_dereq_('./traces/scatter'));
-
- // register all registrable components modules
- register([
- _dereq_('./components/fx'),
- _dereq_('./components/legend'),
- _dereq_('./components/annotations'),
- _dereq_('./components/annotations3d'),
- _dereq_('./components/shapes'),
- _dereq_('./components/images'),
- _dereq_('./components/updatemenus'),
- _dereq_('./components/sliders'),
- _dereq_('./components/rangeslider'),
- _dereq_('./components/rangeselector'),
- _dereq_('./components/grid'),
- _dereq_('./components/errorbars'),
- _dereq_('./components/colorscale')
- ]);
-
- // locales en and en-US are required for default behavior
- register([
- _dereq_('./locale-en'),
- _dereq_('./locale-en-us')
- ]);
-
- // plot icons
- exports.Icons = _dereq_('../build/ploticon');
-
- // unofficial 'beta' plot methods, use at your own risk
- exports.Plots = _dereq_('./plots/plots');
- exports.Fx = _dereq_('./components/fx');
- exports.Snapshot = _dereq_('./snapshot');
- exports.PlotSchema = _dereq_('./plot_api/plot_schema');
- exports.Queue = _dereq_('./lib/queue');
-
- // export d3 used in the bundle
- exports.d3 = _dereq_('d3');
-
- },{"../build/plotcss":1,"../build/ploticon":2,"./components/annotations":44,"./components/annotations3d":49,"./components/colorscale":63,"./components/errorbars":78,"./components/fx":90,"./components/grid":94,"./components/images":99,"./components/legend":107,"./components/rangeselector":118,"./components/rangeslider":125,"./components/shapes":133,"./components/sliders":138,"./components/updatemenus":144,"./fonts/mathjax_config":152,"./lib/queue":182,"./locale-en":193,"./locale-en-us":192,"./plot_api":197,"./plot_api/plot_schema":201,"./plots/plots":245,"./registry":257,"./snapshot":262,"./traces/scatter":379,"d3":16,"es6-promise":17}],152:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /* global MathJax:false */
-
- module.exports = function() {
- if(typeof MathJax !== 'undefined') {
- var globalConfig = (window.PlotlyConfig || {}).MathJaxConfig !== 'local';
-
- if(globalConfig) {
- MathJax.Hub.Config({
- messageStyle: 'none',
- skipStartupTypeset: true,
- displayAlign: 'left',
- tex2jax: {
- inlineMath: [['$', '$'], ['\\(', '\\)']]
- }
- });
- MathJax.Hub.Configured();
- }
- }
- };
-
- },{}],153:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- /**
- * Determine the position anchor property of x/y xanchor/yanchor components.
- *
- * - values < 1/3 align the low side at that fraction,
- * - values [1/3, 2/3] align the center at that fraction,
- * - values > 2/3 align the right at that fraction.
- */
-
-
- exports.isLeftAnchor = function isLeftAnchor(opts) {
- return (
- opts.xanchor === 'left' ||
- (opts.xanchor === 'auto' && opts.x <= 1 / 3)
- );
- };
-
- exports.isCenterAnchor = function isCenterAnchor(opts) {
- return (
- opts.xanchor === 'center' ||
- (opts.xanchor === 'auto' && opts.x > 1 / 3 && opts.x < 2 / 3)
- );
- };
-
- exports.isRightAnchor = function isRightAnchor(opts) {
- return (
- opts.xanchor === 'right' ||
- (opts.xanchor === 'auto' && opts.x >= 2 / 3)
- );
- };
-
- exports.isTopAnchor = function isTopAnchor(opts) {
- return (
- opts.yanchor === 'top' ||
- (opts.yanchor === 'auto' && opts.y >= 2 / 3)
- );
- };
-
- exports.isMiddleAnchor = function isMiddleAnchor(opts) {
- return (
- opts.yanchor === 'middle' ||
- (opts.yanchor === 'auto' && opts.y > 1 / 3 && opts.y < 2 / 3)
- );
- };
-
- exports.isBottomAnchor = function isBottomAnchor(opts) {
- return (
- opts.yanchor === 'bottom' ||
- (opts.yanchor === 'auto' && opts.y <= 1 / 3)
- );
- };
-
- },{}],154:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var modModule = _dereq_('./mod');
- var mod = modModule.mod;
- var modHalf = modModule.modHalf;
-
- var PI = Math.PI;
- var twoPI = 2 * PI;
-
- function deg2rad(deg) { return deg / 180 * PI; }
-
- function rad2deg(rad) { return rad / PI * 180; }
-
- /**
- * is sector a full circle?
- * ... this comes up a lot in SVG path-drawing routines
- *
- * N.B. we consider all sectors that span more that 2pi 'full' circles
- *
- * @param {2-item array} aBnds : angular bounds in *radians*
- * @return {boolean}
- */
- function isFullCircle(aBnds) {
- return Math.abs(aBnds[1] - aBnds[0]) > twoPI - 1e-15;
- }
-
- /**
- * angular delta between angle 'a' and 'b'
- * solution taken from: https://stackoverflow.com/a/2007279
- *
- * @param {number} a : first angle in *radians*
- * @param {number} b : second angle in *radians*
- * @return {number} angular delta in *radians*
- */
- function angleDelta(a, b) {
- return modHalf(b - a, twoPI);
- }
-
- /**
- * angular distance between angle 'a' and 'b'
- *
- * @param {number} a : first angle in *radians*
- * @param {number} b : second angle in *radians*
- * @return {number} angular distance in *radians*
- */
- function angleDist(a, b) {
- return Math.abs(angleDelta(a, b));
- }
-
- /**
- * is angle inside sector?
- *
- * @param {number} a : angle to test in *radians*
- * @param {2-item array} aBnds : sector's angular bounds in *radians*
- * @param {boolean}
- */
- function isAngleInsideSector(a, aBnds) {
- if(isFullCircle(aBnds)) return true;
-
- var s0, s1;
-
- if(aBnds[0] < aBnds[1]) {
- s0 = aBnds[0];
- s1 = aBnds[1];
- } else {
- s0 = aBnds[1];
- s1 = aBnds[0];
- }
-
- s0 = mod(s0, twoPI);
- s1 = mod(s1, twoPI);
- if(s0 > s1) s1 += twoPI;
-
- var a0 = mod(a, twoPI);
- var a1 = a0 + twoPI;
-
- return (a0 >= s0 && a0 <= s1) || (a1 >= s0 && a1 <= s1);
- }
-
- /**
- * is pt (r,a) inside sector?
- *
- * @param {number} r : pt's radial coordinate
- * @param {number} a : pt's angular coordinate in *radians*
- * @param {2-item array} rBnds : sector's radial bounds
- * @param {2-item array} aBnds : sector's angular bounds in *radians*
- * @return {boolean}
- */
- function isPtInsideSector(r, a, rBnds, aBnds) {
- if(!isAngleInsideSector(a, aBnds)) return false;
-
- var r0, r1;
-
- if(rBnds[0] < rBnds[1]) {
- r0 = rBnds[0];
- r1 = rBnds[1];
- } else {
- r0 = rBnds[1];
- r1 = rBnds[0];
- }
-
- return r >= r0 && r <= r1;
- }
-
- // common to pathArc, pathSector and pathAnnulus
- function _path(r0, r1, a0, a1, cx, cy, isClosed) {
- cx = cx || 0;
- cy = cy || 0;
-
- var isCircle = isFullCircle([a0, a1]);
- var aStart, aMid, aEnd;
- var rStart, rEnd;
-
- if(isCircle) {
- aStart = 0;
- aMid = PI;
- aEnd = twoPI;
- } else {
- if(a0 < a1) {
- aStart = a0;
- aEnd = a1;
- } else {
- aStart = a1;
- aEnd = a0;
- }
- }
-
- if(r0 < r1) {
- rStart = r0;
- rEnd = r1;
- } else {
- rStart = r1;
- rEnd = r0;
- }
-
- // N.B. svg coordinates here, where y increases downward
- function pt(r, a) {
- return [r * Math.cos(a) + cx, cy - r * Math.sin(a)];
- }
-
- var largeArc = Math.abs(aEnd - aStart) <= PI ? 0 : 1;
- function arc(r, a, cw) {
- return 'A' + [r, r] + ' ' + [0, largeArc, cw] + ' ' + pt(r, a);
- }
-
- var p;
-
- if(isCircle) {
- if(rStart === null) {
- p = 'M' + pt(rEnd, aStart) +
- arc(rEnd, aMid, 0) +
- arc(rEnd, aEnd, 0) + 'Z';
- } else {
- p = 'M' + pt(rStart, aStart) +
- arc(rStart, aMid, 0) +
- arc(rStart, aEnd, 0) + 'Z' +
- 'M' + pt(rEnd, aStart) +
- arc(rEnd, aMid, 1) +
- arc(rEnd, aEnd, 1) + 'Z';
- }
- } else {
- if(rStart === null) {
- p = 'M' + pt(rEnd, aStart) + arc(rEnd, aEnd, 0);
- if(isClosed) p += 'L0,0Z';
- } else {
- p = 'M' + pt(rStart, aStart) +
- 'L' + pt(rEnd, aStart) +
- arc(rEnd, aEnd, 0) +
- 'L' + pt(rStart, aEnd) +
- arc(rStart, aStart, 1) + 'Z';
- }
- }
-
- return p;
- }
-
- /**
- * path an arc
- *
- * @param {number} r : radius
- * @param {number} a0 : first angular coordinate in *radians*
- * @param {number} a1 : second angular coordinate in *radians*
- * @param {number (optional)} cx : x coordinate of center
- * @param {number (optional)} cy : y coordinate of center
- * @return {string} svg path
- */
- function pathArc(r, a0, a1, cx, cy) {
- return _path(null, r, a0, a1, cx, cy, 0);
- }
-
- /**
- * path a sector
- *
- * @param {number} r : radius
- * @param {number} a0 : first angular coordinate in *radians*
- * @param {number} a1 : second angular coordinate in *radians*
- * @param {number (optional)} cx : x coordinate of center
- * @param {number (optional)} cy : y coordinate of center
- * @return {string} svg path
- */
- function pathSector(r, a0, a1, cx, cy) {
- return _path(null, r, a0, a1, cx, cy, 1);
- }
-
- /**
- * path an annulus
- *
- * @param {number} r0 : first radial coordinate
- * @param {number} r1 : second radial coordinate
- * @param {number} a0 : first angular coordinate in *radians*
- * @param {number} a1 : second angular coordinate in *radians*
- * @param {number (optional)} cx : x coordinate of center
- * @param {number (optional)} cy : y coordinate of center
- * @return {string} svg path
- */
- function pathAnnulus(r0, r1, a0, a1, cx, cy) {
- return _path(r0, r1, a0, a1, cx, cy, 1);
- }
-
- module.exports = {
- deg2rad: deg2rad,
- rad2deg: rad2deg,
- angleDelta: angleDelta,
- angleDist: angleDist,
- isFullCircle: isFullCircle,
- isAngleInsideSector: isAngleInsideSector,
- isPtInsideSector: isPtInsideSector,
- pathArc: pathArc,
- pathSector: pathSector,
- pathAnnulus: pathAnnulus
- };
-
- },{"./mod":175}],155:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isArray = Array.isArray;
-
- // IE9 fallbacks
-
- var ab = (typeof ArrayBuffer === 'undefined' || !ArrayBuffer.isView) ?
- {isView: function() { return false; }} :
- ArrayBuffer;
-
- var dv = (typeof DataView === 'undefined') ?
- function() {} :
- DataView;
-
- function isTypedArray(a) {
- return ab.isView(a) && !(a instanceof dv);
- }
- exports.isTypedArray = isTypedArray;
-
- function isArrayOrTypedArray(a) {
- return isArray(a) || isTypedArray(a);
- }
- exports.isArrayOrTypedArray = isArrayOrTypedArray;
-
- /*
- * Test whether an input object is 1D.
- *
- * Assumes we already know the object is an array.
- *
- * Looks only at the first element, if the dimensionality is
- * not consistent we won't figure that out here.
- */
- function isArray1D(a) {
- return !isArrayOrTypedArray(a[0]);
- }
- exports.isArray1D = isArray1D;
-
- /*
- * Ensures an array has the right amount of storage space. If it doesn't
- * exist, it creates an array. If it does exist, it returns it if too
- * short or truncates it in-place.
- *
- * The goal is to just reuse memory to avoid a bit of excessive garbage
- * collection.
- */
- exports.ensureArray = function(out, n) {
- // TODO: typed array support here? This is only used in
- // traces/carpet/compute_control_points
- if(!isArray(out)) out = [];
-
- // If too long, truncate. (If too short, it will grow
- // automatically so we don't care about that case)
- out.length = n;
-
- return out;
- };
-
- /*
- * TypedArray-compatible concatenation of n arrays
- * if all arrays are the same type it will preserve that type,
- * otherwise it falls back on Array.
- * Also tries to avoid copying, in case one array has zero length
- * But never mutates an existing array
- */
- exports.concat = function() {
- var args = [];
- var allArray = true;
- var totalLen = 0;
-
- var _constructor, arg0, i, argi, posi, leni, out, j;
-
- for(i = 0; i < arguments.length; i++) {
- argi = arguments[i];
- leni = argi.length;
- if(leni) {
- if(arg0) args.push(argi);
- else {
- arg0 = argi;
- posi = leni;
- }
-
- if(isArray(argi)) {
- _constructor = false;
- }
- else {
- allArray = false;
- if(!totalLen) {
- _constructor = argi.constructor;
- }
- else if(_constructor !== argi.constructor) {
- // TODO: in principle we could upgrade here,
- // ie keep typed array but convert all to Float64Array?
- _constructor = false;
- }
- }
-
- totalLen += leni;
- }
- }
-
- if(!totalLen) return [];
- if(!args.length) return arg0;
-
- if(allArray) return arg0.concat.apply(arg0, args);
- if(_constructor) {
- // matching typed arrays
- out = new _constructor(totalLen);
- out.set(arg0);
- for(i = 0; i < args.length; i++) {
- argi = args[i];
- out.set(argi, posi);
- posi += argi.length;
- }
- return out;
- }
-
- // mismatched types or Array + typed
- out = new Array(totalLen);
- for(j = 0; j < arg0.length; j++) out[j] = arg0[j];
- for(i = 0; i < args.length; i++) {
- argi = args[i];
- for(j = 0; j < argi.length; j++) out[posi + j] = argi[j];
- posi += j;
- }
- return out;
- };
-
- exports.maxRowLength = function(z) {
- return _rowLength(z, Math.max, 0);
- };
-
- exports.minRowLength = function(z) {
- return _rowLength(z, Math.min, Infinity);
- };
-
- function _rowLength(z, fn, len0) {
- if(isArrayOrTypedArray(z)) {
- if(isArrayOrTypedArray(z[0])) {
- var len = len0;
- for(var i = 0; i < z.length; i++) {
- len = fn(len, z[i].length);
- }
- return len;
- } else {
- return z.length;
- }
- }
- return 0;
- }
-
- },{}],156:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var BADNUM = _dereq_('../constants/numerical').BADNUM;
-
- // precompile for speed
- var JUNK = /^['"%,$#\s']+|[, ]|['"%,$#\s']+$/g;
-
- /**
- * cleanNumber: remove common leading and trailing cruft
- * Always returns either a number or BADNUM.
- */
- module.exports = function cleanNumber(v) {
- if(typeof v === 'string') {
- v = v.replace(JUNK, '');
- }
-
- if(isNumeric(v)) return Number(v);
-
- return BADNUM;
- };
-
- },{"../constants/numerical":149,"fast-isnumeric":18}],157:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /**
- * Clear gl frame (if any). This is a common pattern as
- * we usually set `preserveDrawingBuffer: true` during
- * gl context creation (e.g. via `reglUtils.prepare`).
- *
- * @param {DOM node or object} gd : graph div object
- */
- module.exports = function clearGlCanvases(gd) {
- var fullLayout = gd._fullLayout;
-
- if(fullLayout._glcanvas && fullLayout._glcanvas.size()) {
- fullLayout._glcanvas.each(function(d) {
- if(d.regl) d.regl.clear({color: true, depth: true});
- });
- }
- };
-
- },{}],158:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /**
- * Clear responsive handlers (if any).
- *
- * @param {DOM node or object} gd : graph div object
- */
- module.exports = function clearResponsive(gd) {
- if(gd._responsiveChartHandler) {
- window.removeEventListener('resize', gd._responsiveChartHandler);
- delete gd._responsiveChartHandler;
- }
- };
-
- },{}],159:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var tinycolor = _dereq_('tinycolor2');
-
- var baseTraceAttrs = _dereq_('../plots/attributes');
- var scales = _dereq_('../components/colorscale/scales');
- var DESELECTDIM = _dereq_('../constants/interactions').DESELECTDIM;
-
- var nestedProperty = _dereq_('./nested_property');
- var counterRegex = _dereq_('./regex').counter;
- var modHalf = _dereq_('./mod').modHalf;
- var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
-
- exports.valObjectMeta = {
- data_array: {
- // You can use *dflt=[] to force said array to exist though.
-
-
-
- coerceFunction: function(v, propOut, dflt) {
- // TODO maybe `v: {type: 'float32', vals: [/* ... */]}` also
- if(isArrayOrTypedArray(v)) propOut.set(v);
- else if(dflt !== undefined) propOut.set(dflt);
- }
- },
- enumerated: {
-
-
-
- coerceFunction: function(v, propOut, dflt, opts) {
- if(opts.coerceNumber) v = +v;
- if(opts.values.indexOf(v) === -1) propOut.set(dflt);
- else propOut.set(v);
- },
- validateFunction: function(v, opts) {
- if(opts.coerceNumber) v = +v;
-
- var values = opts.values;
- for(var i = 0; i < values.length; i++) {
- var k = String(values[i]);
-
- if((k.charAt(0) === '/' && k.charAt(k.length - 1) === '/')) {
- var regex = new RegExp(k.substr(1, k.length - 2));
- if(regex.test(v)) return true;
- } else if(v === values[i]) return true;
- }
- return false;
- }
- },
- 'boolean': {
-
-
-
- coerceFunction: function(v, propOut, dflt) {
- if(v === true || v === false) propOut.set(v);
- else propOut.set(dflt);
- }
- },
- number: {
-
-
-
- coerceFunction: function(v, propOut, dflt, opts) {
- if(!isNumeric(v) ||
- (opts.min !== undefined && v < opts.min) ||
- (opts.max !== undefined && v > opts.max)) {
- propOut.set(dflt);
- }
- else propOut.set(+v);
- }
- },
- integer: {
-
-
-
- coerceFunction: function(v, propOut, dflt, opts) {
- if(v % 1 || !isNumeric(v) ||
- (opts.min !== undefined && v < opts.min) ||
- (opts.max !== undefined && v > opts.max)) {
- propOut.set(dflt);
- }
- else propOut.set(+v);
- }
- },
- string: {
-
-
- // TODO 'values shouldn't be in there (edge case: 'dash' in Scatter)
-
- coerceFunction: function(v, propOut, dflt, opts) {
- if(typeof v !== 'string') {
- var okToCoerce = (typeof v === 'number');
-
- if(opts.strict === true || !okToCoerce) propOut.set(dflt);
- else propOut.set(String(v));
- }
- else if(opts.noBlank && !v) propOut.set(dflt);
- else propOut.set(v);
- }
- },
- color: {
-
-
-
- coerceFunction: function(v, propOut, dflt) {
- if(tinycolor(v).isValid()) propOut.set(v);
- else propOut.set(dflt);
- }
- },
- colorlist: {
-
-
-
- coerceFunction: function(v, propOut, dflt) {
- function isColor(color) {
- return tinycolor(color).isValid();
- }
- if(!Array.isArray(v) || !v.length) propOut.set(dflt);
- else if(v.every(isColor)) propOut.set(v);
- else propOut.set(dflt);
- }
- },
- colorscale: {
-
-
-
- coerceFunction: function(v, propOut, dflt) {
- propOut.set(scales.get(v, dflt));
- }
- },
- angle: {
-
-
-
- coerceFunction: function(v, propOut, dflt) {
- if(v === 'auto') propOut.set('auto');
- else if(!isNumeric(v)) propOut.set(dflt);
- else propOut.set(modHalf(+v, 360));
- }
- },
- subplotid: {
-
-
-
- coerceFunction: function(v, propOut, dflt, opts) {
- var regex = opts.regex || counterRegex(dflt);
- if(typeof v === 'string' && regex.test(v)) {
- propOut.set(v);
- return;
- }
- propOut.set(dflt);
- },
- validateFunction: function(v, opts) {
- var dflt = opts.dflt;
-
- if(v === dflt) return true;
- if(typeof v !== 'string') return false;
- if(counterRegex(dflt).test(v)) return true;
-
- return false;
- }
- },
- flaglist: {
-
-
-
- coerceFunction: function(v, propOut, dflt, opts) {
- if(typeof v !== 'string') {
- propOut.set(dflt);
- return;
- }
- if((opts.extras || []).indexOf(v) !== -1) {
- propOut.set(v);
- return;
- }
- var vParts = v.split('+');
- var i = 0;
- while(i < vParts.length) {
- var vi = vParts[i];
- if(opts.flags.indexOf(vi) === -1 || vParts.indexOf(vi) < i) {
- vParts.splice(i, 1);
- }
- else i++;
- }
- if(!vParts.length) propOut.set(dflt);
- else propOut.set(vParts.join('+'));
- }
- },
- any: {
-
-
-
- coerceFunction: function(v, propOut, dflt) {
- if(v === undefined) propOut.set(dflt);
- else propOut.set(v);
- }
- },
- info_array: {
-
-
- // set `dimensions=2` for a 2D array or '1-2' for either
- // `items` may be a single object instead of an array, in which case
- // `freeLength` must be true.
- // if `dimensions='1-2'` and items is a 1D array, then the value can
- // either be a matching 1D array or an array of such matching 1D arrays
-
- coerceFunction: function(v, propOut, dflt, opts) {
-
- // simplified coerce function just for array items
- function coercePart(v, opts, dflt) {
- var out;
- var propPart = {set: function(v) { out = v; }};
-
- if(dflt === undefined) dflt = opts.dflt;
-
- exports.valObjectMeta[opts.valType].coerceFunction(v, propPart, dflt, opts);
-
- return out;
- }
-
- var twoD = opts.dimensions === 2 || (opts.dimensions === '1-2' && Array.isArray(v) && Array.isArray(v[0]));
-
- if(!Array.isArray(v)) {
- propOut.set(dflt);
- return;
- }
-
- var items = opts.items;
- var vOut = [];
- var arrayItems = Array.isArray(items);
- var arrayItems2D = arrayItems && twoD && Array.isArray(items[0]);
- var innerItemsOnly = twoD && arrayItems && !arrayItems2D;
- var len = (arrayItems && !innerItemsOnly) ? items.length : v.length;
-
- var i, j, row, item, len2, vNew;
-
- dflt = Array.isArray(dflt) ? dflt : [];
-
- if(twoD) {
- for(i = 0; i < len; i++) {
- vOut[i] = [];
- row = Array.isArray(v[i]) ? v[i] : [];
- if(innerItemsOnly) len2 = items.length;
- else if(arrayItems) len2 = items[i].length;
- else len2 = row.length;
-
- for(j = 0; j < len2; j++) {
- if(innerItemsOnly) item = items[j];
- else if(arrayItems) item = items[i][j];
- else item = items;
-
- vNew = coercePart(row[j], item, (dflt[i] || [])[j]);
- if(vNew !== undefined) vOut[i][j] = vNew;
- }
- }
- }
- else {
- for(i = 0; i < len; i++) {
- vNew = coercePart(v[i], arrayItems ? items[i] : items, dflt[i]);
- if(vNew !== undefined) vOut[i] = vNew;
- }
- }
-
- propOut.set(vOut);
- },
- validateFunction: function(v, opts) {
- if(!Array.isArray(v)) return false;
-
- var items = opts.items;
- var arrayItems = Array.isArray(items);
- var twoD = opts.dimensions === 2;
-
- // when free length is off, input and declared lengths must match
- if(!opts.freeLength && v.length !== items.length) return false;
-
- // valid when all input items are valid
- for(var i = 0; i < v.length; i++) {
- if(twoD) {
- if(!Array.isArray(v[i]) || (!opts.freeLength && v[i].length !== items[i].length)) {
- return false;
- }
- for(var j = 0; j < v[i].length; j++) {
- if(!validate(v[i][j], arrayItems ? items[i][j] : items)) {
- return false;
- }
- }
- }
- else if(!validate(v[i], arrayItems ? items[i] : items)) return false;
- }
-
- return true;
- }
- }
- };
-
- /**
- * Ensures that container[attribute] has a valid value.
- *
- * attributes[attribute] is an object with possible keys:
- * - valType: data_array, enumerated, boolean, ... as in valObjectMeta
- * - values: (enumerated only) array of allowed vals
- * - min, max: (number, integer only) inclusive bounds on allowed vals
- * either or both may be omitted
- * - dflt: if attribute is invalid or missing, use this default
- * if dflt is provided as an argument to lib.coerce it takes precedence
- * as a convenience, returns the value it finally set
- */
- exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt) {
- var opts = nestedProperty(attributes, attribute).get();
- var propIn = nestedProperty(containerIn, attribute);
- var propOut = nestedProperty(containerOut, attribute);
- var v = propIn.get();
-
- var template = containerOut._template;
- if(v === undefined && template) {
- v = nestedProperty(template, attribute).get();
- // already used the template value, so short-circuit the second check
- template = 0;
- }
-
- if(dflt === undefined) dflt = opts.dflt;
-
- /**
- * arrayOk: value MAY be an array, then we do no value checking
- * at this point, because it can be more complicated than the
- * individual form (eg. some array vals can be numbers, even if the
- * single values must be color strings)
- */
- if(opts.arrayOk && isArrayOrTypedArray(v)) {
- propOut.set(v);
- return v;
- }
-
- var coerceFunction = exports.valObjectMeta[opts.valType].coerceFunction;
- coerceFunction(v, propOut, dflt, opts);
-
- var out = propOut.get();
- // in case v was provided but invalid, try the template again so it still
- // overrides the regular default
- if(template && out === dflt && !validate(v, opts)) {
- v = nestedProperty(template, attribute).get();
- coerceFunction(v, propOut, dflt, opts);
- out = propOut.get();
- }
- return out;
- };
-
- /**
- * Variation on coerce
- *
- * Uses coerce to get attribute value if user input is valid,
- * returns attribute default if user input it not valid or
- * returns false if there is no user input.
- */
- exports.coerce2 = function(containerIn, containerOut, attributes, attribute, dflt) {
- var propIn = nestedProperty(containerIn, attribute);
- var propOut = exports.coerce(containerIn, containerOut, attributes, attribute, dflt);
- var valIn = propIn.get();
-
- return (valIn !== undefined && valIn !== null) ? propOut : false;
- };
-
- /*
- * Shortcut to coerce the three font attributes
- *
- * 'coerce' is a lib.coerce wrapper with implied first three arguments
- */
- exports.coerceFont = function(coerce, attr, dfltObj) {
- var out = {};
-
- dfltObj = dfltObj || {};
-
- out.family = coerce(attr + '.family', dfltObj.family);
- out.size = coerce(attr + '.size', dfltObj.size);
- out.color = coerce(attr + '.color', dfltObj.color);
-
- return out;
- };
-
- /** Coerce shortcut for 'hoverinfo'
- * handling 1-vs-multi-trace dflt logic
- *
- * @param {object} traceIn : user trace object
- * @param {object} traceOut : full trace object (requires _module ref)
- * @param {object} layoutOut : full layout object (require _dataLength ref)
- * @return {any} : the coerced value
- */
- exports.coerceHoverinfo = function(traceIn, traceOut, layoutOut) {
- var moduleAttrs = traceOut._module.attributes;
- var attrs = moduleAttrs.hoverinfo ? moduleAttrs : baseTraceAttrs;
-
- var valObj = attrs.hoverinfo;
- var dflt;
-
- if(layoutOut._dataLength === 1) {
- var flags = valObj.dflt === 'all' ?
- valObj.flags.slice() :
- valObj.dflt.split('+');
-
- flags.splice(flags.indexOf('name'), 1);
- dflt = flags.join('+');
- }
-
- return exports.coerce(traceIn, traceOut, attrs, 'hoverinfo', dflt);
- };
-
- /** Coerce shortcut for [un]selected.marker.opacity,
- * which has special default logic, to ensure that it corresponds to the
- * default selection behavior while allowing to be overtaken by any other
- * [un]selected attribute.
- *
- * N.B. This must be called *after* coercing all the other [un]selected attrs,
- * to give the intended result.
- *
- * @param {object} traceOut : fullData item
- * @param {function} coerce : lib.coerce wrapper with implied first three arguments
- */
- exports.coerceSelectionMarkerOpacity = function(traceOut, coerce) {
- if(!traceOut.marker) return;
-
- var mo = traceOut.marker.opacity;
- // you can still have a `marker` container with no markers if there's text
- if(mo === undefined) return;
-
- var smoDflt;
- var usmoDflt;
-
- // Don't give [un]selected.marker.opacity a default value if
- // marker.opacity is an array: handle this during style step.
- //
- // Only give [un]selected.marker.opacity a default value if you don't
- // set any other [un]selected attributes.
- if(!isArrayOrTypedArray(mo) && !traceOut.selected && !traceOut.unselected) {
- smoDflt = mo;
- usmoDflt = DESELECTDIM * mo;
- }
-
- coerce('selected.marker.opacity', smoDflt);
- coerce('unselected.marker.opacity', usmoDflt);
- };
-
- function validate(value, opts) {
- var valObjectDef = exports.valObjectMeta[opts.valType];
-
- if(opts.arrayOk && isArrayOrTypedArray(value)) return true;
-
- if(valObjectDef.validateFunction) {
- return valObjectDef.validateFunction(value, opts);
- }
-
- var failed = {};
- var out = failed;
- var propMock = { set: function(v) { out = v; } };
-
- // 'failed' just something mutable that won't be === anything else
-
- valObjectDef.coerceFunction(value, propMock, failed, opts);
- return out !== failed;
- }
- exports.validate = validate;
-
- },{"../components/colorscale/scales":66,"../constants/interactions":148,"../plots/attributes":209,"./array":155,"./mod":175,"./nested_property":176,"./regex":183,"fast-isnumeric":18,"tinycolor2":34}],160:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Loggers = _dereq_('./loggers');
- var mod = _dereq_('./mod').mod;
-
- var constants = _dereq_('../constants/numerical');
- var BADNUM = constants.BADNUM;
- var ONEDAY = constants.ONEDAY;
- var ONEHOUR = constants.ONEHOUR;
- var ONEMIN = constants.ONEMIN;
- var ONESEC = constants.ONESEC;
- var EPOCHJD = constants.EPOCHJD;
-
- var Registry = _dereq_('../registry');
-
- var utcFormat = d3.time.format.utc;
-
- var DATETIME_REGEXP = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\d)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m;
- // special regex for chinese calendars to support yyyy-mmi-dd etc for intercalary months
- var DATETIME_REGEXP_CN = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\di?)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m;
-
- // for 2-digit years, the first year we map them onto
- var YFIRST = new Date().getFullYear() - 70;
-
- function isWorldCalendar(calendar) {
- return (
- calendar &&
- Registry.componentsRegistry.calendars &&
- typeof calendar === 'string' && calendar !== 'gregorian'
- );
- }
-
- /*
- * dateTick0: get the canonical tick for this calendar
- *
- * bool sunday is for week ticks, shift it to a Sunday.
- */
- exports.dateTick0 = function(calendar, sunday) {
- if(isWorldCalendar(calendar)) {
- return sunday ?
- Registry.getComponentMethod('calendars', 'CANONICAL_SUNDAY')[calendar] :
- Registry.getComponentMethod('calendars', 'CANONICAL_TICK')[calendar];
- }
- else {
- return sunday ? '2000-01-02' : '2000-01-01';
- }
- };
-
- /*
- * dfltRange: for each calendar, give a valid default range
- */
- exports.dfltRange = function(calendar) {
- if(isWorldCalendar(calendar)) {
- return Registry.getComponentMethod('calendars', 'DFLTRANGE')[calendar];
- }
- else {
- return ['2000-01-01', '2001-01-01'];
- }
- };
-
- // is an object a javascript date?
- exports.isJSDate = function(v) {
- return typeof v === 'object' && v !== null && typeof v.getTime === 'function';
- };
-
- // The absolute limits of our date-time system
- // This is a little weird: we use MIN_MS and MAX_MS in dateTime2ms
- // but we use dateTime2ms to calculate them (after defining it!)
- var MIN_MS, MAX_MS;
-
- /**
- * dateTime2ms - turn a date object or string s into milliseconds
- * (relative to 1970-01-01, per javascript standard)
- * optional calendar (string) to use a non-gregorian calendar
- *
- * Returns BADNUM if it doesn't find a date
- *
- * strings should have the form:
- *
- * -?YYYY-mm-dd<sep>HH:MM:SS.sss<tzInfo>?
- *
- * <sep>: space (our normal standard) or T or t (ISO-8601)
- * <tzInfo>: Z, z, or [+\-]HH:?MM and we THROW IT AWAY
- * this format comes from https://tools.ietf.org/html/rfc3339#section-5.6
- * but we allow it even with a space as the separator
- *
- * May truncate after any full field, and sss can be any length
- * even >3 digits, though javascript dates truncate to milliseconds,
- * we keep as much as javascript numeric precision can hold, but we only
- * report back up to 100 microsecond precision, because most dates support
- * this precision (close to 1970 support more, very far away support less)
- *
- * Expanded to support negative years to -9999 but you must always
- * give 4 digits, except for 2-digit positive years which we assume are
- * near the present time.
- * Note that we follow ISO 8601:2004: there *is* a year 0, which
- * is 1BC/BCE, and -1===2BC etc.
- *
- * World calendars: not all of these *have* agreed extensions to this full range,
- * if you have another calendar system but want a date range outside its validity,
- * you can use a gregorian date string prefixed with 'G' or 'g'.
- *
- * Where to cut off 2-digit years between 1900s and 2000s?
- * from http://support.microsoft.com/kb/244664:
- * 1930-2029 (the most retro of all...)
- * but in my mac chrome from eg. d=new Date(Date.parse('8/19/50')):
- * 1950-2049
- * by Java, from http://stackoverflow.com/questions/2024273/:
- * now-80 - now+19
- * or FileMaker Pro, from
- * http://www.filemaker.com/12help/html/add_view_data.4.21.html:
- * now-70 - now+29
- * but python strptime etc, via
- * http://docs.python.org/py3k/library/time.html:
- * 1969-2068 (super forward-looking, but static, not sliding!)
- *
- * lets go with now-70 to now+29, and if anyone runs into this problem
- * they can learn the hard way not to use 2-digit years, as no choice we
- * make now will cover all possibilities. mostly this will all be taken
- * care of in initial parsing, should only be an issue for hand-entered data
- * currently (2016) this range is:
- * 1946-2045
- */
- exports.dateTime2ms = function(s, calendar) {
- // first check if s is a date object
- if(exports.isJSDate(s)) {
- // Convert to the UTC milliseconds that give the same
- // hours as this date has in the local timezone
- var tzOffset = s.getTimezoneOffset() * ONEMIN;
- var offsetTweak = (s.getUTCMinutes() - s.getMinutes()) * ONEMIN +
- (s.getUTCSeconds() - s.getSeconds()) * ONESEC +
- (s.getUTCMilliseconds() - s.getMilliseconds());
-
- if(offsetTweak) {
- var comb = 3 * ONEMIN;
- tzOffset = tzOffset - comb / 2 + mod(offsetTweak - tzOffset + comb / 2, comb);
- }
- s = Number(s) - tzOffset;
- if(s >= MIN_MS && s <= MAX_MS) return s;
- return BADNUM;
- }
- // otherwise only accept strings and numbers
- if(typeof s !== 'string' && typeof s !== 'number') return BADNUM;
-
- s = String(s);
-
- var isWorld = isWorldCalendar(calendar);
-
- // to handle out-of-range dates in international calendars, accept
- // 'G' as a prefix to force the built-in gregorian calendar.
- var s0 = s.charAt(0);
- if(isWorld && (s0 === 'G' || s0 === 'g')) {
- s = s.substr(1);
- calendar = '';
- }
-
- var isChinese = isWorld && calendar.substr(0, 7) === 'chinese';
-
- var match = s.match(isChinese ? DATETIME_REGEXP_CN : DATETIME_REGEXP);
- if(!match) return BADNUM;
- var y = match[1];
- var m = match[3] || '1';
- var d = Number(match[5] || 1);
- var H = Number(match[7] || 0);
- var M = Number(match[9] || 0);
- var S = Number(match[11] || 0);
-
- if(isWorld) {
- // disallow 2-digit years for world calendars
- if(y.length === 2) return BADNUM;
- y = Number(y);
-
- var cDate;
- try {
- var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
- if(isChinese) {
- var isIntercalary = m.charAt(m.length - 1) === 'i';
- m = parseInt(m, 10);
- cDate = calInstance.newDate(y, calInstance.toMonthIndex(y, m, isIntercalary), d);
- }
- else {
- cDate = calInstance.newDate(y, Number(m), d);
- }
- }
- catch(e) { return BADNUM; } // Invalid ... date
-
- if(!cDate) return BADNUM;
-
- return ((cDate.toJD() - EPOCHJD) * ONEDAY) +
- (H * ONEHOUR) + (M * ONEMIN) + (S * ONESEC);
- }
-
- if(y.length === 2) {
- y = (Number(y) + 2000 - YFIRST) % 100 + YFIRST;
- }
- else y = Number(y);
-
- // new Date uses months from 0; subtract 1 here just so we
- // don't have to do it again during the validity test below
- m -= 1;
-
- // javascript takes new Date(0..99,m,d) to mean 1900-1999, so
- // to support years 0-99 we need to use setFullYear explicitly
- // Note that 2000 is a leap year.
- var date = new Date(Date.UTC(2000, m, d, H, M));
- date.setUTCFullYear(y);
-
- if(date.getUTCMonth() !== m) return BADNUM;
- if(date.getUTCDate() !== d) return BADNUM;
-
- return date.getTime() + S * ONESEC;
- };
-
- MIN_MS = exports.MIN_MS = exports.dateTime2ms('-9999');
- MAX_MS = exports.MAX_MS = exports.dateTime2ms('9999-12-31 23:59:59.9999');
-
- // is string s a date? (see above)
- exports.isDateTime = function(s, calendar) {
- return (exports.dateTime2ms(s, calendar) !== BADNUM);
- };
-
- // pad a number with zeroes, to given # of digits before the decimal point
- function lpad(val, digits) {
- return String(val + Math.pow(10, digits)).substr(1);
- }
-
- /**
- * Turn ms into string of the form YYYY-mm-dd HH:MM:SS.ssss
- * Crop any trailing zeros in time, except never stop right after hours
- * (we could choose to crop '-01' from date too but for now we always
- * show the whole date)
- * Optional range r is the data range that applies, also in ms.
- * If rng is big, the later parts of time will be omitted
- */
- var NINETYDAYS = 90 * ONEDAY;
- var THREEHOURS = 3 * ONEHOUR;
- var FIVEMIN = 5 * ONEMIN;
- exports.ms2DateTime = function(ms, r, calendar) {
- if(typeof ms !== 'number' || !(ms >= MIN_MS && ms <= MAX_MS)) return BADNUM;
-
- if(!r) r = 0;
-
- var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10);
- var msRounded = Math.round(ms - msecTenths / 10);
- var dateStr, h, m, s, msec10, d;
-
- if(isWorldCalendar(calendar)) {
- var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD;
- var timeMs = Math.floor(mod(ms, ONEDAY));
- try {
- dateStr = Registry.getComponentMethod('calendars', 'getCal')(calendar)
- .fromJD(dateJD).formatDate('yyyy-mm-dd');
- } catch(e) {
- // invalid date in this calendar - fall back to Gyyyy-mm-dd
- dateStr = utcFormat('G%Y-%m-%d')(new Date(msRounded));
- }
-
- // yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does
- // other things for a few calendars, so we can't trust it. Just pad
- // it manually (after the '-' if there is one)
- if(dateStr.charAt(0) === '-') {
- while(dateStr.length < 11) dateStr = '-0' + dateStr.substr(1);
- } else {
- while(dateStr.length < 10) dateStr = '0' + dateStr;
- }
-
- // TODO: if this is faster, we could use this block for extracting
- // the time components of regular gregorian too
- h = (r < NINETYDAYS) ? Math.floor(timeMs / ONEHOUR) : 0;
- m = (r < NINETYDAYS) ? Math.floor((timeMs % ONEHOUR) / ONEMIN) : 0;
- s = (r < THREEHOURS) ? Math.floor((timeMs % ONEMIN) / ONESEC) : 0;
- msec10 = (r < FIVEMIN) ? (timeMs % ONESEC) * 10 + msecTenths : 0;
- }
- else {
- d = new Date(msRounded);
-
- dateStr = utcFormat('%Y-%m-%d')(d);
-
- // <90 days: add hours and minutes - never *only* add hours
- h = (r < NINETYDAYS) ? d.getUTCHours() : 0;
- m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0;
- // <3 hours: add seconds
- s = (r < THREEHOURS) ? d.getUTCSeconds() : 0;
- // <5 minutes: add ms (plus one extra digit, this is msec*10)
- msec10 = (r < FIVEMIN) ? d.getUTCMilliseconds() * 10 + msecTenths : 0;
- }
-
- return includeTime(dateStr, h, m, s, msec10);
- };
-
- // For converting old-style milliseconds to date strings,
- // we use the local timezone rather than UTC like we use
- // everywhere else, both for backward compatibility and
- // because that's how people mostly use javasript date objects.
- // Clip one extra day off our date range though so we can't get
- // thrown beyond the range by the timezone shift.
- exports.ms2DateTimeLocal = function(ms) {
- if(!(ms >= MIN_MS + ONEDAY && ms <= MAX_MS - ONEDAY)) return BADNUM;
-
- var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10);
- var d = new Date(Math.round(ms - msecTenths / 10));
- var dateStr = d3.time.format('%Y-%m-%d')(d);
- var h = d.getHours();
- var m = d.getMinutes();
- var s = d.getSeconds();
- var msec10 = d.getUTCMilliseconds() * 10 + msecTenths;
-
- return includeTime(dateStr, h, m, s, msec10);
- };
-
- function includeTime(dateStr, h, m, s, msec10) {
- // include each part that has nonzero data in or after it
- if(h || m || s || msec10) {
- dateStr += ' ' + lpad(h, 2) + ':' + lpad(m, 2);
- if(s || msec10) {
- dateStr += ':' + lpad(s, 2);
- if(msec10) {
- var digits = 4;
- while(msec10 % 10 === 0) {
- digits -= 1;
- msec10 /= 10;
- }
- dateStr += '.' + lpad(msec10, digits);
- }
- }
- }
- return dateStr;
- }
-
- // normalize date format to date string, in case it starts as
- // a Date object or milliseconds
- // optional dflt is the return value if cleaning fails
- exports.cleanDate = function(v, dflt, calendar) {
- // let us use cleanDate to provide a missing default without an error
- if(v === BADNUM) return dflt;
- if(exports.isJSDate(v) || (typeof v === 'number' && isFinite(v))) {
- // do not allow milliseconds (old) or jsdate objects (inherently
- // described as gregorian dates) with world calendars
- if(isWorldCalendar(calendar)) {
- Loggers.error('JS Dates and milliseconds are incompatible with world calendars', v);
- return dflt;
- }
-
- // NOTE: if someone puts in a year as a number rather than a string,
- // this will mistakenly convert it thinking it's milliseconds from 1970
- // that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
- v = exports.ms2DateTimeLocal(+v);
- if(!v && dflt !== undefined) return dflt;
- }
- else if(!exports.isDateTime(v, calendar)) {
- Loggers.error('unrecognized date', v);
- return dflt;
- }
- return v;
- };
-
- /*
- * Date formatting for ticks and hovertext
- */
-
- /*
- * modDateFormat: Support world calendars, and add one item to
- * d3's vocabulary:
- * %{n}f where n is the max number of digits of fractional seconds
- */
- var fracMatch = /%\d?f/g;
- function modDateFormat(fmt, x, formatter, calendar) {
-
- fmt = fmt.replace(fracMatch, function(match) {
- var digits = Math.min(+(match.charAt(1)) || 6, 6);
- var fracSecs = ((x / 1000 % 1) + 2)
- .toFixed(digits)
- .substr(2).replace(/0+$/, '') || '0';
- return fracSecs;
- });
-
- var d = new Date(Math.floor(x + 0.05));
-
- if(isWorldCalendar(calendar)) {
- try {
- fmt = Registry.getComponentMethod('calendars', 'worldCalFmt')(fmt, x, calendar);
- }
- catch(e) {
- return 'Invalid';
- }
- }
- return formatter(fmt)(d);
- }
-
- /*
- * formatTime: create a time string from:
- * x: milliseconds
- * tr: tickround ('M', 'S', or # digits)
- * only supports UTC times (where every day is 24 hours and 0 is at midnight)
- */
- var MAXSECONDS = [59, 59.9, 59.99, 59.999, 59.9999];
- function formatTime(x, tr) {
- var timePart = mod(x + 0.05, ONEDAY);
-
- var timeStr = lpad(Math.floor(timePart / ONEHOUR), 2) + ':' +
- lpad(mod(Math.floor(timePart / ONEMIN), 60), 2);
-
- if(tr !== 'M') {
- if(!isNumeric(tr)) tr = 0; // should only be 'S'
-
- /*
- * this is a weird one - and shouldn't come up unless people
- * monkey with tick0 in weird ways, but we need to do something!
- * IN PARTICULAR we had better not display garbage (see below)
- * for numbers we always round to the nearest increment of the
- * precision we're showing, and this seems like the right way to
- * handle seconds and milliseconds, as they have a decimal point
- * and people will interpret that to mean rounding like numbers.
- * but for larger increments we floor the value: it's always
- * 2013 until the ball drops on the new year. We could argue about
- * which field it is where we start rounding (should 12:08:59
- * round to 12:09 if we're stopping at minutes?) but for now I'll
- * say we round seconds but floor everything else. BUT that means
- * we need to never round up to 60 seconds, ie 23:59:60
- */
- var sec = Math.min(mod(x / ONESEC, 60), MAXSECONDS[tr]);
-
- var secStr = (100 + sec).toFixed(tr).substr(1);
- if(tr > 0) {
- secStr = secStr.replace(/0+$/, '').replace(/[\.]$/, '');
- }
-
- timeStr += ':' + secStr;
- }
- return timeStr;
- }
-
- /*
- * formatDate: turn a date into tick or hover label text.
- *
- * x: milliseconds, the value to convert
- * fmt: optional, an explicit format string (d3 format, even for world calendars)
- * tr: tickround ('y', 'm', 'd', 'M', 'S', or # digits)
- * used if no explicit fmt is provided
- * formatter: locale-aware d3 date formatter for standard gregorian calendars
- * should be the result of exports.getD3DateFormat(gd)
- * calendar: optional string, the world calendar system to use
- *
- * returns the date/time as a string, potentially with the leading portion
- * on a separate line (after '\n')
- * Note that this means if you provide an explicit format which includes '\n'
- * the axis may choose to strip things after it when they don't change from
- * one tick to the next (as it does with automatic formatting)
- */
- exports.formatDate = function(x, fmt, tr, formatter, calendar, extraFormat) {
- calendar = isWorldCalendar(calendar) && calendar;
-
- if(!fmt) {
- if(tr === 'y') fmt = extraFormat.year;
- else if(tr === 'm') fmt = extraFormat.month;
- else if(tr === 'd') {
- fmt = extraFormat.dayMonth + '\n' + extraFormat.year;
- }
- else {
- return formatTime(x, tr) + '\n' + modDateFormat(extraFormat.dayMonthYear, x, formatter, calendar);
- }
- }
-
- return modDateFormat(fmt, x, formatter, calendar);
- };
-
- /*
- * incrementMonth: make a new milliseconds value from the given one,
- * having changed the month
- *
- * special case for world calendars: multiples of 12 are treated as years,
- * even for calendar systems that don't have (always or ever) 12 months/year
- * TODO: perhaps we need a different code for year increments to support this?
- *
- * ms (number): the initial millisecond value
- * dMonth (int): the (signed) number of months to shift
- * calendar (string): the calendar system to use
- *
- * changing month does not (and CANNOT) always preserve day, since
- * months have different lengths. The worst example of this is:
- * d = new Date(1970,0,31); d.setMonth(1) -> Feb 31 turns into Mar 3
- *
- * But we want to be able to iterate over the last day of each month,
- * regardless of what its number is.
- * So shift 3 days forward, THEN set the new month, then unshift:
- * 1/31 -> 2/28 (or 29) -> 3/31 -> 4/30 -> ...
- *
- * Note that odd behavior still exists if you start from the 26th-28th:
- * 1/28 -> 2/28 -> 3/31
- * but at least you can't shift any dates into the wrong month,
- * and ticks on these days incrementing by month would be very unusual
- */
- var THREEDAYS = 3 * ONEDAY;
- exports.incrementMonth = function(ms, dMonth, calendar) {
- calendar = isWorldCalendar(calendar) && calendar;
-
- // pull time out and operate on pure dates, then add time back at the end
- // this gives maximum precision - not that we *normally* care if we're
- // incrementing by month, but better to be safe!
- var timeMs = mod(ms, ONEDAY);
- ms = Math.round(ms - timeMs);
-
- if(calendar) {
- try {
- var dateJD = Math.round(ms / ONEDAY) + EPOCHJD;
- var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
- var cDate = calInstance.fromJD(dateJD);
-
- if(dMonth % 12) calInstance.add(cDate, dMonth, 'm');
- else calInstance.add(cDate, dMonth / 12, 'y');
-
- return (cDate.toJD() - EPOCHJD) * ONEDAY + timeMs;
- } catch(e) {
- Loggers.error('invalid ms ' + ms + ' in calendar ' + calendar);
- // then keep going in gregorian even though the result will be 'Invalid'
- }
- }
-
- var y = new Date(ms + THREEDAYS);
- return y.setUTCMonth(y.getUTCMonth() + dMonth) + timeMs - THREEDAYS;
- };
-
- /*
- * findExactDates: what fraction of data is exact days, months, or years?
- *
- * data: array of millisecond values
- * calendar (string) the calendar to test against
- */
- exports.findExactDates = function(data, calendar) {
- var exactYears = 0;
- var exactMonths = 0;
- var exactDays = 0;
- var blankCount = 0;
- var d;
- var di;
-
- var calInstance = (
- isWorldCalendar(calendar) &&
- Registry.getComponentMethod('calendars', 'getCal')(calendar)
- );
-
- for(var i = 0; i < data.length; i++) {
- di = data[i];
-
- // not date data at all
- if(!isNumeric(di)) {
- blankCount ++;
- continue;
- }
-
- // not an exact date
- if(di % ONEDAY) continue;
-
- if(calInstance) {
- try {
- d = calInstance.fromJD(di / ONEDAY + EPOCHJD);
- if(d.day() === 1) {
- if(d.month() === 1) exactYears++;
- else exactMonths++;
- }
- else exactDays++;
- }
- catch(e) {
- // invalid date in this calendar - ignore it here.
- }
- }
- else {
- d = new Date(di);
- if(d.getUTCDate() === 1) {
- if(d.getUTCMonth() === 0) exactYears++;
- else exactMonths++;
- }
- else exactDays++;
- }
- }
- exactMonths += exactYears;
- exactDays += exactMonths;
-
- var dataCount = data.length - blankCount;
-
- return {
- exactYears: exactYears / dataCount,
- exactMonths: exactMonths / dataCount,
- exactDays: exactDays / dataCount
- };
- };
-
- },{"../constants/numerical":149,"../registry":257,"./loggers":172,"./mod":175,"d3":16,"fast-isnumeric":18}],161:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- /* global jQuery:false */
-
- var EventEmitter = _dereq_('events').EventEmitter;
-
- var Events = {
-
- init: function(plotObj) {
-
- /*
- * If we have already instantiated an emitter for this plot
- * return early.
- */
- if(plotObj._ev instanceof EventEmitter) return plotObj;
-
- var ev = new EventEmitter();
- var internalEv = new EventEmitter();
-
- /*
- * Assign to plot._ev while we still live in a land
- * where plot is a DOM element with stuff attached to it.
- * In the future we can make plot the event emitter itself.
- */
- plotObj._ev = ev;
-
- /*
- * Create a second event handler that will manage events *internally*.
- * This allows parts of plotly to respond to thing like relayout without
- * having to use the user-facing event handler. They cannot peacefully
- * coexist on the same handler because a user invoking
- * plotObj.removeAllListeners() would detach internal events, breaking
- * plotly.
- */
- plotObj._internalEv = internalEv;
-
- /*
- * Assign bound methods from the ev to the plot object. These methods
- * will reference the 'this' of plot._ev even though they are methods
- * of plot. This will keep the event machinery away from the plot object
- * which currently is often a DOM element but presents an API that will
- * continue to function when plot becomes an emitter. Not all EventEmitter
- * methods have been bound to `plot` as some do not currently add value to
- * the Plotly event API.
- */
- plotObj.on = ev.on.bind(ev);
- plotObj.once = ev.once.bind(ev);
- plotObj.removeListener = ev.removeListener.bind(ev);
- plotObj.removeAllListeners = ev.removeAllListeners.bind(ev);
-
- /*
- * Create functions for managing internal events. These are *only* triggered
- * by the mirroring of external events via the emit function.
- */
- plotObj._internalOn = internalEv.on.bind(internalEv);
- plotObj._internalOnce = internalEv.once.bind(internalEv);
- plotObj._removeInternalListener = internalEv.removeListener.bind(internalEv);
- plotObj._removeAllInternalListeners = internalEv.removeAllListeners.bind(internalEv);
-
- /*
- * We must wrap emit to continue to support JQuery events. The idea
- * is to check to see if the user is using JQuery events, if they are
- * we emit JQuery events to trigger user handlers as well as the EventEmitter
- * events.
- */
- plotObj.emit = function(event, data) {
- if(typeof jQuery !== 'undefined') {
- jQuery(plotObj).trigger(event, data);
- }
-
- ev.emit(event, data);
- internalEv.emit(event, data);
- };
-
- return plotObj;
- },
-
- /*
- * This function behaves like jQuery's triggerHandler. It calls
- * all handlers for a particular event and returns the return value
- * of the LAST handler. This function also triggers jQuery's
- * triggerHandler for backwards compatibility.
- */
- triggerHandler: function(plotObj, event, data) {
- var jQueryHandlerValue;
- var nodeEventHandlerValue;
-
- /*
- * If jQuery exists run all its handlers for this event and
- * collect the return value of the LAST handler function
- */
- if(typeof jQuery !== 'undefined') {
- jQueryHandlerValue = jQuery(plotObj).triggerHandler(event, data);
- }
-
- /*
- * Now run all the node style event handlers
- */
- var ev = plotObj._ev;
- if(!ev) return jQueryHandlerValue;
-
- var handlers = ev._events[event];
- if(!handlers) return jQueryHandlerValue;
-
- // making sure 'this' is the EventEmitter instance
- function apply(handler) {
- // The 'once' case, we can't just call handler() as we need
- // the return value here. So,
- // - remove handler
- // - call listener and grab return value!
- // - stash 'fired' key to not call handler twice
- if(handler.listener) {
- ev.removeListener(event, handler.listener);
- if(!handler.fired) {
- handler.fired = true;
- return handler.listener.apply(ev, [data]);
- }
- } else {
- return handler.apply(ev, [data]);
- }
- }
-
- // handlers can be function or an array of functions
- handlers = Array.isArray(handlers) ? handlers : [handlers];
-
- var i;
- for(i = 0; i < handlers.length - 1; i++) {
- apply(handlers[i]);
- }
- // now call the final handler and collect its value
- nodeEventHandlerValue = apply(handlers[i]);
-
- /*
- * Return either the jQuery handler value if it exists or the
- * nodeEventHandler value. jQuery event value supersedes nodejs
- * events for backwards compatibility reasons.
- */
- return jQueryHandlerValue !== undefined ?
- jQueryHandlerValue :
- nodeEventHandlerValue;
- },
-
- purge: function(plotObj) {
- delete plotObj._ev;
- delete plotObj.on;
- delete plotObj.once;
- delete plotObj.removeListener;
- delete plotObj.removeAllListeners;
- delete plotObj.emit;
-
- delete plotObj._ev;
- delete plotObj._internalEv;
- delete plotObj._internalOn;
- delete plotObj._internalOnce;
- delete plotObj._removeInternalListener;
- delete plotObj._removeAllInternalListeners;
-
- return plotObj;
- }
-
- };
-
- module.exports = Events;
-
- },{"events":15}],162:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isPlainObject = _dereq_('./is_plain_object.js');
- var isArray = Array.isArray;
-
- function primitivesLoopSplice(source, target) {
- var i, value;
- for(i = 0; i < source.length; i++) {
- value = source[i];
- if(value !== null && typeof(value) === 'object') {
- return false;
- }
- if(value !== void(0)) {
- target[i] = value;
- }
- }
- return true;
- }
-
- exports.extendFlat = function() {
- return _extend(arguments, false, false, false);
- };
-
- exports.extendDeep = function() {
- return _extend(arguments, true, false, false);
- };
-
- exports.extendDeepAll = function() {
- return _extend(arguments, true, true, false);
- };
-
- exports.extendDeepNoArrays = function() {
- return _extend(arguments, true, false, true);
- };
-
- /*
- * Inspired by https://github.com/justmoon/node-extend/blob/master/index.js
- * All credit to the jQuery authors for perfecting this amazing utility.
- *
- * API difference with jQuery version:
- * - No optional boolean (true -> deep extend) first argument,
- * use `extendFlat` for first-level only extend and
- * use `extendDeep` for a deep extend.
- *
- * Other differences with jQuery version:
- * - Uses a modern (and faster) isPlainObject routine.
- * - Expected to work with object {} and array [] arguments only.
- * - Does not check for circular structure.
- * FYI: jQuery only does a check across one level.
- * Warning: this might result in infinite loops.
- *
- */
- function _extend(inputs, isDeep, keepAllKeys, noArrayCopies) {
- var target = inputs[0];
- var length = inputs.length;
-
- var input, key, src, copy, copyIsArray, clone, allPrimitives;
-
- // TODO does this do the right thing for typed arrays?
-
- if(length === 2 && isArray(target) && isArray(inputs[1]) && target.length === 0) {
-
- allPrimitives = primitivesLoopSplice(inputs[1], target);
-
- if(allPrimitives) {
- return target;
- } else {
- target.splice(0, target.length); // reset target and continue to next block
- }
- }
-
- for(var i = 1; i < length; i++) {
- input = inputs[i];
-
- for(key in input) {
- src = target[key];
- copy = input[key];
-
- // Stop early and just transfer the array if array copies are disallowed:
- if(noArrayCopies && isArray(copy)) {
- target[key] = copy;
- }
-
- // recurse if we're merging plain objects or arrays
- else if(isDeep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
- if(copyIsArray) {
- copyIsArray = false;
- clone = src && isArray(src) ? src : [];
- } else {
- clone = src && isPlainObject(src) ? src : {};
- }
-
- // never move original objects, clone them
- target[key] = _extend([clone, copy], isDeep, keepAllKeys, noArrayCopies);
- }
-
- // don't bring in undefined values, except for extendDeepAll
- else if(typeof copy !== 'undefined' || keepAllKeys) {
- target[key] = copy;
- }
- }
- }
-
- return target;
- }
-
- },{"./is_plain_object.js":169}],163:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- /**
- * Return news array containing only the unique items
- * found in input array.
- *
- * IMPORTANT: Note that items are considered unique
- * if `String({})` is unique. For example;
- *
- * Lib.filterUnique([ { a: 1 }, { b: 2 } ])
- *
- * returns [{ a: 1 }]
- *
- * and
- *
- * Lib.filterUnique([ '1', 1 ])
- *
- * returns ['1']
- *
- *
- * @param {array} array base array
- * @return {array} new filtered array
- */
- module.exports = function filterUnique(array) {
- var seen = {};
- var out = [];
- var j = 0;
-
- for(var i = 0; i < array.length; i++) {
- var item = array[i];
-
- if(seen[item] !== 1) {
- seen[item] = 1;
- out[j++] = item;
- }
- }
-
- return out;
- };
-
- },{}],164:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /** Filter out object items with visible !== true
- * insider array container.
- *
- * @param {array of objects} container
- * @return {array of objects} of length <= container
- *
- */
- module.exports = function filterVisible(container) {
- var filterFn = isCalcData(container) ? calcDataFilter : baseFilter;
- var out = [];
-
- for(var i = 0; i < container.length; i++) {
- var item = container[i];
- if(filterFn(item)) out.push(item);
- }
-
- return out;
- };
-
- function baseFilter(item) {
- return item.visible === true;
- }
-
- function calcDataFilter(item) {
- return item[0].trace.visible === true;
- }
-
- function isCalcData(cont) {
- return (
- Array.isArray(cont) &&
- Array.isArray(cont[0]) &&
- cont[0][0] &&
- cont[0][0].trace
- );
- }
-
- },{}],165:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var mod = _dereq_('./mod').mod;
-
- /*
- * look for intersection of two line segments
- * (1->2 and 3->4) - returns array [x,y] if they do, null if not
- */
- exports.segmentsIntersect = segmentsIntersect;
- function segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4) {
- var a = x2 - x1;
- var b = x3 - x1;
- var c = x4 - x3;
- var d = y2 - y1;
- var e = y3 - y1;
- var f = y4 - y3;
- var det = a * f - c * d;
- // parallel lines? intersection is undefined
- // ignore the case where they are colinear
- if(det === 0) return null;
- var t = (b * f - c * e) / det;
- var u = (b * d - a * e) / det;
- // segments do not intersect?
- if(u < 0 || u > 1 || t < 0 || t > 1) return null;
-
- return {x: x1 + a * t, y: y1 + d * t};
- }
-
- /*
- * find the minimum distance between two line segments (1->2 and 3->4)
- */
- exports.segmentDistance = function segmentDistance(x1, y1, x2, y2, x3, y3, x4, y4) {
- if(segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) return 0;
-
- // the two segments and their lengths squared
- var x12 = x2 - x1;
- var y12 = y2 - y1;
- var x34 = x4 - x3;
- var y34 = y4 - y3;
- var ll12 = x12 * x12 + y12 * y12;
- var ll34 = x34 * x34 + y34 * y34;
-
- // calculate distance squared, then take the sqrt at the very end
- var dist2 = Math.min(
- perpDistance2(x12, y12, ll12, x3 - x1, y3 - y1),
- perpDistance2(x12, y12, ll12, x4 - x1, y4 - y1),
- perpDistance2(x34, y34, ll34, x1 - x3, y1 - y3),
- perpDistance2(x34, y34, ll34, x2 - x3, y2 - y3)
- );
-
- return Math.sqrt(dist2);
- };
-
- /*
- * distance squared from segment ab to point c
- * [xab, yab] is the vector b-a
- * [xac, yac] is the vector c-a
- * llab is the length squared of (b-a), just to simplify calculation
- */
- function perpDistance2(xab, yab, llab, xac, yac) {
- var fcAB = (xac * xab + yac * yab);
- if(fcAB < 0) {
- // point c is closer to point a
- return xac * xac + yac * yac;
- }
- else if(fcAB > llab) {
- // point c is closer to point b
- var xbc = xac - xab;
- var ybc = yac - yab;
- return xbc * xbc + ybc * ybc;
- }
- else {
- // perpendicular distance is the shortest
- var crossProduct = xac * yab - yac * xab;
- return crossProduct * crossProduct / llab;
- }
- }
-
- // a very short-term cache for getTextLocation, just because
- // we're often looping over the same locations multiple times
- // invalidated as soon as we look at a different path
- var locationCache, workingPath, workingTextWidth;
-
- // turn a path and position along it into x, y, and angle for the given text
- exports.getTextLocation = function getTextLocation(path, totalPathLen, positionOnPath, textWidth) {
- if(path !== workingPath || textWidth !== workingTextWidth) {
- locationCache = {};
- workingPath = path;
- workingTextWidth = textWidth;
- }
- if(locationCache[positionOnPath]) {
- return locationCache[positionOnPath];
- }
-
- // for the angle, use points on the path separated by the text width
- // even though due to curvature, the text will cover a bit more than that
- var p0 = path.getPointAtLength(mod(positionOnPath - textWidth / 2, totalPathLen));
- var p1 = path.getPointAtLength(mod(positionOnPath + textWidth / 2, totalPathLen));
- // note: atan handles 1/0 nicely
- var theta = Math.atan((p1.y - p0.y) / (p1.x - p0.x));
- // center the text at 2/3 of the center position plus 1/3 the p0/p1 midpoint
- // that's the average position of this segment, assuming it's roughly quadratic
- var pCenter = path.getPointAtLength(mod(positionOnPath, totalPathLen));
- var x = (pCenter.x * 4 + p0.x + p1.x) / 6;
- var y = (pCenter.y * 4 + p0.y + p1.y) / 6;
-
- var out = {x: x, y: y, theta: theta};
- locationCache[positionOnPath] = out;
- return out;
- };
-
- exports.clearLocationCache = function() {
- workingPath = null;
- };
-
- /*
- * Find the segment of `path` that's within the visible area
- * given by `bounds` {left, right, top, bottom}, to within a
- * precision of `buffer` px
- *
- * returns: undefined if nothing is visible, else object:
- * {
- * min: position where the path first enters bounds, or 0 if it
- * starts within bounds
- * max: position where the path last exits bounds, or the path length
- * if it finishes within bounds
- * len: max - min, ie the length of visible path
- * total: the total path length - just included so the caller doesn't
- * need to call path.getTotalLength() again
- * isClosed: true iff the start and end points of the path are both visible
- * and are at the same point
- * }
- *
- * Works by starting from either end and repeatedly finding the distance from
- * that point to the plot area, and if it's outside the plot, moving along the
- * path by that distance (because the plot must be at least that far away on
- * the path). Note that if a path enters, exits, and re-enters the plot, we
- * will not capture this behavior.
- */
- exports.getVisibleSegment = function getVisibleSegment(path, bounds, buffer) {
- var left = bounds.left;
- var right = bounds.right;
- var top = bounds.top;
- var bottom = bounds.bottom;
-
- var pMin = 0;
- var pTotal = path.getTotalLength();
- var pMax = pTotal;
-
- var pt0, ptTotal;
-
- function getDistToPlot(len) {
- var pt = path.getPointAtLength(len);
-
- // hold on to the start and end points for `closed`
- if(len === 0) pt0 = pt;
- else if(len === pTotal) ptTotal = pt;
-
- var dx = (pt.x < left) ? left - pt.x : (pt.x > right ? pt.x - right : 0);
- var dy = (pt.y < top) ? top - pt.y : (pt.y > bottom ? pt.y - bottom : 0);
- return Math.sqrt(dx * dx + dy * dy);
- }
-
- var distToPlot = getDistToPlot(pMin);
- while(distToPlot) {
- pMin += distToPlot + buffer;
- if(pMin > pMax) return;
- distToPlot = getDistToPlot(pMin);
- }
-
- distToPlot = getDistToPlot(pMax);
- while(distToPlot) {
- pMax -= distToPlot + buffer;
- if(pMin > pMax) return;
- distToPlot = getDistToPlot(pMax);
- }
-
- return {
- min: pMin,
- max: pMax,
- len: pMax - pMin,
- total: pTotal,
- isClosed: pMin === 0 && pMax === pTotal &&
- Math.abs(pt0.x - ptTotal.x) < 0.1 &&
- Math.abs(pt0.y - ptTotal.y) < 0.1
- };
- };
-
- /**
- * Find point on SVG path corresponding to a given constraint coordinate
- *
- * @param {SVGPathElement} path
- * @param {Number} val : constraint coordinate value
- * @param {String} coord : 'x' or 'y' the constraint coordinate
- * @param {Object} opts :
- * - {Number} pathLength : supply total path length before hand
- * - {Number} tolerance
- * - {Number} iterationLimit
- * @return {SVGPoint}
- */
- exports.findPointOnPath = function findPointOnPath(path, val, coord, opts) {
- opts = opts || {};
-
- var pathLength = opts.pathLength || path.getTotalLength();
- var tolerance = opts.tolerance || 1e-3;
- var iterationLimit = opts.iterationLimit || 30;
-
- // if path starts at a val greater than the path tail (like on vertical violins),
- // we must flip the sign of the computed diff.
- var mul = path.getPointAtLength(0)[coord] > path.getPointAtLength(pathLength)[coord] ? -1 : 1;
-
- var i = 0;
- var b0 = 0;
- var b1 = pathLength;
- var mid;
- var pt;
- var diff;
-
- while(i < iterationLimit) {
- mid = (b0 + b1) / 2;
- pt = path.getPointAtLength(mid);
- diff = pt[coord] - val;
-
- if(Math.abs(diff) < tolerance) {
- return pt;
- } else {
- if(mul * diff > 0) {
- b1 = mid;
- } else {
- b0 = mid;
- }
- i++;
- }
- }
- return pt;
- };
-
- },{"./mod":175}],166:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /**
- * Allow referencing a graph DOM element either directly
- * or by its id string
- *
- * @param {HTMLDivElement|string} gd: a graph element or its id
- *
- * @returns {HTMLDivElement} the DOM element of the graph
- */
- module.exports = function(gd) {
- var gdElement;
-
- if(typeof gd === 'string') {
- gdElement = document.getElementById(gd);
-
- if(gdElement === null) {
- throw new Error('No DOM element with id \'' + gd + '\' exists on the page.');
- }
-
- return gdElement;
- }
- else if(gd === null || gd === undefined) {
- throw new Error('DOM element provided is null or undefined');
- }
-
- return gd; // otherwise assume that gd is a DOM element
- };
-
- },{}],167:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- // Simple helper functions
- // none of these need any external deps
-
- module.exports = function identity(d) { return d; };
-
- },{}],168:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var numConstants = _dereq_('../constants/numerical');
- var FP_SAFE = numConstants.FP_SAFE;
- var BADNUM = numConstants.BADNUM;
-
- var lib = module.exports = {};
-
- lib.nestedProperty = _dereq_('./nested_property');
- lib.keyedContainer = _dereq_('./keyed_container');
- lib.relativeAttr = _dereq_('./relative_attr');
- lib.isPlainObject = _dereq_('./is_plain_object');
- lib.toLogRange = _dereq_('./to_log_range');
- lib.relinkPrivateKeys = _dereq_('./relink_private');
-
- var arrayModule = _dereq_('./array');
- lib.isTypedArray = arrayModule.isTypedArray;
- lib.isArrayOrTypedArray = arrayModule.isArrayOrTypedArray;
- lib.isArray1D = arrayModule.isArray1D;
- lib.ensureArray = arrayModule.ensureArray;
- lib.concat = arrayModule.concat;
- lib.maxRowLength = arrayModule.maxRowLength;
- lib.minRowLength = arrayModule.minRowLength;
-
- var modModule = _dereq_('./mod');
- lib.mod = modModule.mod;
- lib.modHalf = modModule.modHalf;
-
- var coerceModule = _dereq_('./coerce');
- lib.valObjectMeta = coerceModule.valObjectMeta;
- lib.coerce = coerceModule.coerce;
- lib.coerce2 = coerceModule.coerce2;
- lib.coerceFont = coerceModule.coerceFont;
- lib.coerceHoverinfo = coerceModule.coerceHoverinfo;
- lib.coerceSelectionMarkerOpacity = coerceModule.coerceSelectionMarkerOpacity;
- lib.validate = coerceModule.validate;
-
- var datesModule = _dereq_('./dates');
- lib.dateTime2ms = datesModule.dateTime2ms;
- lib.isDateTime = datesModule.isDateTime;
- lib.ms2DateTime = datesModule.ms2DateTime;
- lib.ms2DateTimeLocal = datesModule.ms2DateTimeLocal;
- lib.cleanDate = datesModule.cleanDate;
- lib.isJSDate = datesModule.isJSDate;
- lib.formatDate = datesModule.formatDate;
- lib.incrementMonth = datesModule.incrementMonth;
- lib.dateTick0 = datesModule.dateTick0;
- lib.dfltRange = datesModule.dfltRange;
- lib.findExactDates = datesModule.findExactDates;
- lib.MIN_MS = datesModule.MIN_MS;
- lib.MAX_MS = datesModule.MAX_MS;
-
- var searchModule = _dereq_('./search');
- lib.findBin = searchModule.findBin;
- lib.sorterAsc = searchModule.sorterAsc;
- lib.sorterDes = searchModule.sorterDes;
- lib.distinctVals = searchModule.distinctVals;
- lib.roundUp = searchModule.roundUp;
- lib.sort = searchModule.sort;
- lib.findIndexOfMin = searchModule.findIndexOfMin;
-
- var statsModule = _dereq_('./stats');
- lib.aggNums = statsModule.aggNums;
- lib.len = statsModule.len;
- lib.mean = statsModule.mean;
- lib.midRange = statsModule.midRange;
- lib.variance = statsModule.variance;
- lib.stdev = statsModule.stdev;
- lib.interp = statsModule.interp;
-
- var matrixModule = _dereq_('./matrix');
- lib.init2dArray = matrixModule.init2dArray;
- lib.transposeRagged = matrixModule.transposeRagged;
- lib.dot = matrixModule.dot;
- lib.translationMatrix = matrixModule.translationMatrix;
- lib.rotationMatrix = matrixModule.rotationMatrix;
- lib.rotationXYMatrix = matrixModule.rotationXYMatrix;
- lib.apply2DTransform = matrixModule.apply2DTransform;
- lib.apply2DTransform2 = matrixModule.apply2DTransform2;
-
- var anglesModule = _dereq_('./angles');
- lib.deg2rad = anglesModule.deg2rad;
- lib.rad2deg = anglesModule.rad2deg;
- lib.angleDelta = anglesModule.angleDelta;
- lib.angleDist = anglesModule.angleDist;
- lib.isFullCircle = anglesModule.isFullCircle;
- lib.isAngleInsideSector = anglesModule.isAngleInsideSector;
- lib.isPtInsideSector = anglesModule.isPtInsideSector;
- lib.pathArc = anglesModule.pathArc;
- lib.pathSector = anglesModule.pathSector;
- lib.pathAnnulus = anglesModule.pathAnnulus;
-
- var anchorUtils = _dereq_('./anchor_utils');
- lib.isLeftAnchor = anchorUtils.isLeftAnchor;
- lib.isCenterAnchor = anchorUtils.isCenterAnchor;
- lib.isRightAnchor = anchorUtils.isRightAnchor;
- lib.isTopAnchor = anchorUtils.isTopAnchor;
- lib.isMiddleAnchor = anchorUtils.isMiddleAnchor;
- lib.isBottomAnchor = anchorUtils.isBottomAnchor;
-
- var geom2dModule = _dereq_('./geometry2d');
- lib.segmentsIntersect = geom2dModule.segmentsIntersect;
- lib.segmentDistance = geom2dModule.segmentDistance;
- lib.getTextLocation = geom2dModule.getTextLocation;
- lib.clearLocationCache = geom2dModule.clearLocationCache;
- lib.getVisibleSegment = geom2dModule.getVisibleSegment;
- lib.findPointOnPath = geom2dModule.findPointOnPath;
-
- var extendModule = _dereq_('./extend');
- lib.extendFlat = extendModule.extendFlat;
- lib.extendDeep = extendModule.extendDeep;
- lib.extendDeepAll = extendModule.extendDeepAll;
- lib.extendDeepNoArrays = extendModule.extendDeepNoArrays;
-
- var loggersModule = _dereq_('./loggers');
- lib.log = loggersModule.log;
- lib.warn = loggersModule.warn;
- lib.error = loggersModule.error;
-
- var regexModule = _dereq_('./regex');
- lib.counterRegex = regexModule.counter;
-
- var throttleModule = _dereq_('./throttle');
- lib.throttle = throttleModule.throttle;
- lib.throttleDone = throttleModule.done;
- lib.clearThrottle = throttleModule.clear;
-
- lib.getGraphDiv = _dereq_('./get_graph_div');
-
- lib.clearResponsive = _dereq_('./clear_responsive');
-
- lib.makeTraceGroups = _dereq_('./make_trace_groups');
-
- lib._ = _dereq_('./localize');
-
- lib.notifier = _dereq_('./notifier');
-
- lib.filterUnique = _dereq_('./filter_unique');
- lib.filterVisible = _dereq_('./filter_visible');
- lib.pushUnique = _dereq_('./push_unique');
-
- lib.cleanNumber = _dereq_('./clean_number');
-
- lib.ensureNumber = function num(v) {
- if(!isNumeric(v)) return BADNUM;
- v = Number(v);
- if(v < -FP_SAFE || v > FP_SAFE) return BADNUM;
- return isNumeric(v) ? Number(v) : BADNUM;
- };
-
- /**
- * Is v a valid array index? Accepts numeric strings as well as numbers.
- *
- * @param {any} v: the value to test
- * @param {Optional[integer]} len: the array length we are indexing
- *
- * @return {bool}: v is a valid array index
- */
- lib.isIndex = function(v, len) {
- if(len !== undefined && v >= len) return false;
- return isNumeric(v) && (v >= 0) && (v % 1 === 0);
- };
-
- lib.noop = _dereq_('./noop');
- lib.identity = _dereq_('./identity');
-
- /**
- * create an array of length 'cnt' filled with 'v' at all indices
- *
- * @param {any} v
- * @param {number} cnt
- * @return {array}
- */
- lib.repeat = function(v, cnt) {
- var out = new Array(cnt);
- for(var i = 0; i < cnt; i++) {
- out[i] = v;
- }
- return out;
- };
-
- /**
- * swap x and y of the same attribute in container cont
- * specify attr with a ? in place of x/y
- * you can also swap other things than x/y by providing part1 and part2
- */
- lib.swapAttrs = function(cont, attrList, part1, part2) {
- if(!part1) part1 = 'x';
- if(!part2) part2 = 'y';
- for(var i = 0; i < attrList.length; i++) {
- var attr = attrList[i];
- var xp = lib.nestedProperty(cont, attr.replace('?', part1));
- var yp = lib.nestedProperty(cont, attr.replace('?', part2));
- var temp = xp.get();
- xp.set(yp.get());
- yp.set(temp);
- }
- };
-
- /**
- * SVG painter's algo worked around with reinsertion
- */
- lib.raiseToTop = function raiseToTop(elem) {
- elem.parentNode.appendChild(elem);
- };
-
- /**
- * cancel a possibly pending transition; returned selection may be used by caller
- */
- lib.cancelTransition = function(selection) {
- return selection.transition().duration(0);
- };
-
- // constrain - restrict a number v to be between v0 and v1
- lib.constrain = function(v, v0, v1) {
- if(v0 > v1) return Math.max(v1, Math.min(v0, v));
- return Math.max(v0, Math.min(v1, v));
- };
-
- /**
- * do two bounding boxes from getBoundingClientRect,
- * ie {left,right,top,bottom,width,height}, overlap?
- * takes optional padding pixels
- */
- lib.bBoxIntersect = function(a, b, pad) {
- pad = pad || 0;
- return (a.left <= b.right + pad &&
- b.left <= a.right + pad &&
- a.top <= b.bottom + pad &&
- b.top <= a.bottom + pad);
- };
-
- /*
- * simpleMap: alternative to Array.map that only
- * passes on the element and up to 2 extra args you
- * provide (but not the array index or the whole array)
- *
- * array: the array to map it to
- * func: the function to apply
- * x1, x2: optional extra args
- */
- lib.simpleMap = function(array, func, x1, x2) {
- var len = array.length;
- var out = new Array(len);
- for(var i = 0; i < len; i++) out[i] = func(array[i], x1, x2);
- return out;
- };
-
- /**
- * Random string generator
- *
- * @param {object} existing
- * pass in strings to avoid as keys with truthy values
- * @param {int} bits
- * bits of information in the output string, default 24
- * @param {int} base
- * base of string representation, default 16. Should be a power of 2.
- */
- lib.randstr = function randstr(existing, bits, base, _recursion) {
- if(!base) base = 16;
- if(bits === undefined) bits = 24;
- if(bits <= 0) return '0';
-
- var digits = Math.log(Math.pow(2, bits)) / Math.log(base);
- var res = '';
- var i, b, x;
-
- for(i = 2; digits === Infinity; i *= 2) {
- digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i;
- }
-
- var rem = digits - Math.floor(digits);
-
- for(i = 0; i < Math.floor(digits); i++) {
- x = Math.floor(Math.random() * base).toString(base);
- res = x + res;
- }
-
- if(rem) {
- b = Math.pow(base, rem);
- x = Math.floor(Math.random() * b).toString(base);
- res = x + res;
- }
-
- var parsed = parseInt(res, base);
- if((existing && existing[res]) ||
- (parsed !== Infinity && parsed >= Math.pow(2, bits))) {
- if(_recursion > 10) {
- lib.warn('randstr failed uniqueness');
- return res;
- }
- return randstr(existing, bits, base, (_recursion || 0) + 1);
- }
- else return res;
- };
-
- lib.OptionControl = function(opt, optname) {
- /*
- * An environment to contain all option setters and
- * getters that collectively modify opts.
- *
- * You can call up opts from any function in new object
- * as this.optname || this.opt
- *
- * See FitOpts for example of usage
- */
- if(!opt) opt = {};
- if(!optname) optname = 'opt';
-
- var self = {};
- self.optionList = [];
-
- self._newoption = function(optObj) {
- optObj[optname] = opt;
- self[optObj.name] = optObj;
- self.optionList.push(optObj);
- };
-
- self['_' + optname] = opt;
- return self;
- };
-
- /**
- * lib.smooth: smooth arrayIn by convolving with
- * a hann window with given full width at half max
- * bounce the ends in, so the output has the same length as the input
- */
- lib.smooth = function(arrayIn, FWHM) {
- FWHM = Math.round(FWHM) || 0; // only makes sense for integers
- if(FWHM < 2) return arrayIn;
-
- var alen = arrayIn.length;
- var alen2 = 2 * alen;
- var wlen = 2 * FWHM - 1;
- var w = new Array(wlen);
- var arrayOut = new Array(alen);
- var i;
- var j;
- var k;
- var v;
-
- // first make the window array
- for(i = 0; i < wlen; i++) {
- w[i] = (1 - Math.cos(Math.PI * (i + 1) / FWHM)) / (2 * FWHM);
- }
-
- // now do the convolution
- for(i = 0; i < alen; i++) {
- v = 0;
- for(j = 0; j < wlen; j++) {
- k = i + j + 1 - FWHM;
-
- // multibounce
- if(k < -alen) k -= alen2 * Math.round(k / alen2);
- else if(k >= alen2) k -= alen2 * Math.floor(k / alen2);
-
- // single bounce
- if(k < 0) k = - 1 - k;
- else if(k >= alen) k = alen2 - 1 - k;
-
- v += arrayIn[k] * w[j];
- }
- arrayOut[i] = v;
- }
-
- return arrayOut;
- };
-
- /**
- * syncOrAsync: run a sequence of functions synchronously
- * as long as its returns are not promises (ie have no .then)
- * includes one argument arg to send to all functions...
- * this is mainly just to prevent us having to make wrapper functions
- * when the only purpose of the wrapper is to reference gd
- * and a final step to be executed at the end
- * TODO: if there's an error and everything is sync,
- * this doesn't happen yet because we want to make sure
- * that it gets reported
- */
- lib.syncOrAsync = function(sequence, arg, finalStep) {
- var ret, fni;
-
- function continueAsync() {
- return lib.syncOrAsync(sequence, arg, finalStep);
- }
-
- while(sequence.length) {
- fni = sequence.splice(0, 1)[0];
- ret = fni(arg);
-
- if(ret && ret.then) {
- return ret.then(continueAsync)
- .then(undefined, lib.promiseError);
- }
- }
-
- return finalStep && finalStep(arg);
- };
-
-
- /**
- * Helper to strip trailing slash, from
- * http://stackoverflow.com/questions/6680825/return-string-without-trailing-slash
- */
- lib.stripTrailingSlash = function(str) {
- if(str.substr(-1) === '/') return str.substr(0, str.length - 1);
- return str;
- };
-
- lib.noneOrAll = function(containerIn, containerOut, attrList) {
- /**
- * some attributes come together, so if you have one of them
- * in the input, you should copy the default values of the others
- * to the input as well.
- */
- if(!containerIn) return;
-
- var hasAny = false;
- var hasAll = true;
- var i;
- var val;
-
- for(i = 0; i < attrList.length; i++) {
- val = containerIn[attrList[i]];
- if(val !== undefined && val !== null) hasAny = true;
- else hasAll = false;
- }
-
- if(hasAny && !hasAll) {
- for(i = 0; i < attrList.length; i++) {
- containerIn[attrList[i]] = containerOut[attrList[i]];
- }
- }
- };
-
- /** merges calcdata field (given by cdAttr) with traceAttr values
- *
- * N.B. Loop over minimum of cd.length and traceAttr.length
- * i.e. it does not try to fill in beyond traceAttr.length-1
- *
- * @param {array} traceAttr : trace attribute
- * @param {object} cd : calcdata trace
- * @param {string} cdAttr : calcdata key
- */
- lib.mergeArray = function(traceAttr, cd, cdAttr) {
- if(lib.isArrayOrTypedArray(traceAttr)) {
- var imax = Math.min(traceAttr.length, cd.length);
- for(var i = 0; i < imax; i++) cd[i][cdAttr] = traceAttr[i];
- }
- };
-
- /** fills calcdata field (given by cdAttr) with traceAttr values
- * or function of traceAttr values (e.g. some fallback)
- *
- * N.B. Loops over all cd items.
- *
- * @param {array} traceAttr : trace attribute
- * @param {object} cd : calcdata trace
- * @param {string} cdAttr : calcdata key
- * @param {function} [fn] : optional function to apply to each array item
- */
- lib.fillArray = function(traceAttr, cd, cdAttr, fn) {
- fn = fn || lib.identity;
-
- if(lib.isArrayOrTypedArray(traceAttr)) {
- for(var i = 0; i < cd.length; i++) {
- cd[i][cdAttr] = fn(traceAttr[i]);
- }
- }
- };
-
- /** Handler for trace-wide vs per-point options
- *
- * @param {object} trace : (full) trace object
- * @param {number} ptNumber : index of the point in question
- * @param {string} astr : attribute string
- * @param {function} [fn] : optional function to apply to each array item
- *
- * @return {any}
- */
- lib.castOption = function(trace, ptNumber, astr, fn) {
- fn = fn || lib.identity;
-
- var val = lib.nestedProperty(trace, astr).get();
-
- if(lib.isArrayOrTypedArray(val)) {
- if(Array.isArray(ptNumber) && lib.isArrayOrTypedArray(val[ptNumber[0]])) {
- return fn(val[ptNumber[0]][ptNumber[1]]);
- } else {
- return fn(val[ptNumber]);
- }
- } else {
- return val;
- }
- };
-
- /** Extract option from calcdata item, correctly falling back to
- * trace value if not found.
- *
- * @param {object} calcPt : calcdata[i][j] item
- * @param {object} trace : (full) trace object
- * @param {string} calcKey : calcdata key
- * @param {string} traceKey : aka trace attribute string
- * @return {any}
- */
- lib.extractOption = function(calcPt, trace, calcKey, traceKey) {
- if(calcKey in calcPt) return calcPt[calcKey];
-
- // fallback to trace value,
- // must check if value isn't itself an array
- // which means the trace attribute has a corresponding
- // calcdata key, but its value is falsy
- var traceVal = lib.nestedProperty(trace, traceKey).get();
- if(!Array.isArray(traceVal)) return traceVal;
- };
-
- function makePtIndex2PtNumber(indexToPoints) {
- var ptIndex2ptNumber = {};
- for(var k in indexToPoints) {
- var pts = indexToPoints[k];
- for(var j = 0; j < pts.length; j++) {
- ptIndex2ptNumber[pts[j]] = +k;
- }
- }
- return ptIndex2ptNumber;
- }
-
- /** Tag selected calcdata items
- *
- * N.B. note that point 'index' corresponds to input data array index
- * whereas 'number' is its post-transform version.
- *
- * @param {array} calcTrace
- * @param {object} trace
- * - selectedpoints {array}
- * - _indexToPoints {object}
- * @param {ptNumber2cdIndex} ptNumber2cdIndex (optional)
- * optional map object for trace types that do not have 1-to-1 point number to
- * calcdata item index correspondence (e.g. histogram)
- */
- lib.tagSelected = function(calcTrace, trace, ptNumber2cdIndex) {
- var selectedpoints = trace.selectedpoints;
- var indexToPoints = trace._indexToPoints;
- var ptIndex2ptNumber;
-
- // make pt index-to-number map object, which takes care of transformed traces
- if(indexToPoints) {
- ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints);
- }
-
- function isCdIndexValid(v) {
- return v !== undefined && v < calcTrace.length;
- }
-
- for(var i = 0; i < selectedpoints.length; i++) {
- var ptIndex = selectedpoints[i];
-
- if(lib.isIndex(ptIndex)) {
- var ptNumber = ptIndex2ptNumber ? ptIndex2ptNumber[ptIndex] : ptIndex;
- var cdIndex = ptNumber2cdIndex ? ptNumber2cdIndex[ptNumber] : ptNumber;
-
- if(isCdIndexValid(cdIndex)) {
- calcTrace[cdIndex].selected = 1;
- }
- }
- }
- };
-
- lib.selIndices2selPoints = function(trace) {
- var selectedpoints = trace.selectedpoints;
- var indexToPoints = trace._indexToPoints;
-
- if(indexToPoints) {
- var ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints);
- var out = [];
-
- for(var i = 0; i < selectedpoints.length; i++) {
- var ptIndex = selectedpoints[i];
- if(lib.isIndex(ptIndex)) {
- var ptNumber = ptIndex2ptNumber[ptIndex];
- if(lib.isIndex(ptNumber)) {
- out.push(ptNumber);
- }
- }
- }
-
- return out;
- } else {
- return selectedpoints;
- }
- };
-
- /** Returns target as set by 'target' transform attribute
- *
- * @param {object} trace : full trace object
- * @param {object} transformOpts : transform option object
- * - target (string} :
- * either an attribute string referencing an array in the trace object, or
- * a set array.
- *
- * @return {array or false} : the target array (NOT a copy!!) or false if invalid
- */
- lib.getTargetArray = function(trace, transformOpts) {
- var target = transformOpts.target;
-
- if(typeof target === 'string' && target) {
- var array = lib.nestedProperty(trace, target).get();
- return Array.isArray(array) ? array : false;
- } else if(Array.isArray(target)) {
- return target;
- }
-
- return false;
- };
-
- /**
- * modified version of jQuery's extend to strip out private objs and functions,
- * and cut arrays down to first <arraylen> or 1 elements
- * because extend-like algorithms are hella slow
- * obj2 is assumed to already be clean of these things (including no arrays)
- */
- lib.minExtend = function(obj1, obj2) {
- var objOut = {};
- if(typeof obj2 !== 'object') obj2 = {};
- var arrayLen = 3;
- var keys = Object.keys(obj1);
- var i, k, v;
-
- for(i = 0; i < keys.length; i++) {
- k = keys[i];
- v = obj1[k];
- if(k.charAt(0) === '_' || typeof v === 'function') continue;
- else if(k === 'module') objOut[k] = v;
- else if(Array.isArray(v)) {
- if(k === 'colorscale') {
- objOut[k] = v.slice();
- } else {
- objOut[k] = v.slice(0, arrayLen);
- }
- }
- else if(v && (typeof v === 'object')) objOut[k] = lib.minExtend(obj1[k], obj2[k]);
- else objOut[k] = v;
- }
-
- keys = Object.keys(obj2);
- for(i = 0; i < keys.length; i++) {
- k = keys[i];
- v = obj2[k];
- if(typeof v !== 'object' || !(k in objOut) || typeof objOut[k] !== 'object') {
- objOut[k] = v;
- }
- }
-
- return objOut;
- };
-
- lib.titleCase = function(s) {
- return s.charAt(0).toUpperCase() + s.substr(1);
- };
-
- lib.containsAny = function(s, fragments) {
- for(var i = 0; i < fragments.length; i++) {
- if(s.indexOf(fragments[i]) !== -1) return true;
- }
- return false;
- };
-
- lib.isPlotDiv = function(el) {
- var el3 = d3.select(el);
- return el3.node() instanceof HTMLElement &&
- el3.size() &&
- el3.classed('js-plotly-plot');
- };
-
- lib.removeElement = function(el) {
- var elParent = el && el.parentNode;
- if(elParent) elParent.removeChild(el);
- };
-
- /**
- * for dynamically adding style rules
- * makes one stylesheet that contains all rules added
- * by all calls to this function
- */
- lib.addStyleRule = function(selector, styleString) {
- lib.addRelatedStyleRule('global', selector, styleString);
- };
-
- /**
- * for dynamically adding style rules
- * to a stylesheet uniquely identified by a uid
- */
- lib.addRelatedStyleRule = function(uid, selector, styleString) {
- var id = 'plotly.js-style-' + uid;
- var style = document.getElementById(id);
- if(!style) {
- style = document.createElement('style');
- style.setAttribute('id', id);
- // WebKit hack :(
- style.appendChild(document.createTextNode(''));
- document.head.appendChild(style);
- }
- var styleSheet = style.sheet;
-
- if(styleSheet.insertRule) {
- styleSheet.insertRule(selector + '{' + styleString + '}', 0);
- }
- else if(styleSheet.addRule) {
- styleSheet.addRule(selector, styleString, 0);
- }
- else lib.warn('addStyleRule failed');
- };
-
- /**
- * to remove from the page a stylesheet identified by a given uid
- */
- lib.deleteRelatedStyleRule = function(uid) {
- var id = 'plotly.js-style-' + uid;
- var style = document.getElementById(id);
- if(style) lib.removeElement(style);
- };
-
- lib.isIE = function() {
- return typeof window.navigator.msSaveBlob !== 'undefined';
- };
-
- /**
- * Duck typing to recognize a d3 selection, mostly for IE9's benefit
- * because it doesn't handle instanceof like modern browsers
- */
- lib.isD3Selection = function(obj) {
- return obj && (typeof obj.classed === 'function');
- };
-
- /**
- * Append element to DOM only if not present.
- *
- * @param {d3 selection} parent : parent selection of the element in question
- * @param {string} nodeType : node type of element to append
- * @param {string} className (optional) : class name of element in question
- * @param {fn} enterFn (optional) : optional fn applied to entering elements only
- * @return {d3 selection} selection of new layer
- *
- * Previously, we were using the following pattern:
- *
- * ```
- * var sel = parent.selectAll('.' + className)
- * .data([0]);
- *
- * sel.enter().append(nodeType)
- * .classed(className, true);
- *
- * return sel;
- * ```
- *
- * in numerous places in our codebase to achieve the same behavior.
- *
- * The logic below performs much better, mostly as we are using
- * `.select` instead `.selectAll` that is `querySelector` instead of
- * `querySelectorAll`.
- *
- */
- lib.ensureSingle = function(parent, nodeType, className, enterFn) {
- var sel = parent.select(nodeType + (className ? '.' + className : ''));
- if(sel.size()) return sel;
-
- var layer = parent.append(nodeType);
- if(className) layer.classed(className, true);
- if(enterFn) layer.call(enterFn);
-
- return layer;
- };
-
- /**
- * Same as Lib.ensureSingle, but using id as selector.
- * This version is mostly used for clipPath nodes.
- *
- * @param {d3 selection} parent : parent selection of the element in question
- * @param {string} nodeType : node type of element to append
- * @param {string} id : id of element in question
- * @param {fn} enterFn (optional) : optional fn applied to entering elements only
- * @return {d3 selection} selection of new layer
- */
- lib.ensureSingleById = function(parent, nodeType, id, enterFn) {
- var sel = parent.select(nodeType + '#' + id);
- if(sel.size()) return sel;
-
- var layer = parent.append(nodeType).attr('id', id);
- if(enterFn) layer.call(enterFn);
-
- return layer;
- };
-
- /**
- * Converts a string path to an object.
- *
- * When given a string containing an array element, it will create a `null`
- * filled array of the given size.
- *
- * @example
- * lib.objectFromPath('nested.test[2].path', 'value');
- * // returns { nested: { test: [null, null, { path: 'value' }]}
- *
- * @param {string} path to nested value
- * @param {*} any value to be set
- *
- * @return {Object} the constructed object with a full nested path
- */
- lib.objectFromPath = function(path, value) {
- var keys = path.split('.');
- var tmpObj;
- var obj = tmpObj = {};
-
- for(var i = 0; i < keys.length; i++) {
- var key = keys[i];
- var el = null;
-
- var parts = keys[i].match(/(.*)\[([0-9]+)\]/);
-
- if(parts) {
- key = parts[1];
- el = parts[2];
-
- tmpObj = tmpObj[key] = [];
-
- if(i === keys.length - 1) {
- tmpObj[el] = value;
- } else {
- tmpObj[el] = {};
- }
-
- tmpObj = tmpObj[el];
- } else {
-
- if(i === keys.length - 1) {
- tmpObj[key] = value;
- } else {
- tmpObj[key] = {};
- }
-
- tmpObj = tmpObj[key];
- }
- }
-
- return obj;
- };
-
- /**
- * Iterate through an object in-place, converting dotted properties to objects.
- *
- * Examples:
- *
- * lib.expandObjectPaths({'nested.test.path': 'value'});
- * => { nested: { test: {path: 'value'}}}
- *
- * It also handles array notation, e.g.:
- *
- * lib.expandObjectPaths({'foo[1].bar': 'value'});
- * => { foo: [null, {bar: value}] }
- *
- * It handles merges the results when two properties are specified in parallel:
- *
- * lib.expandObjectPaths({'foo[1].bar': 10, 'foo[0].bar': 20});
- * => { foo: [{bar: 10}, {bar: 20}] }
- *
- * It does NOT, however, merge mulitple mutliply-nested arrays::
- *
- * lib.expandObjectPaths({'marker[1].range[1]': 5, 'marker[1].range[0]': 4})
- * => { marker: [null, {range: 4}] }
- */
-
- // Store this to avoid recompiling regex on *every* prop since this may happen many
- // many times for animations. Could maybe be inside the function. Not sure about
- // scoping vs. recompilation tradeoff, but at least it's not just inlining it into
- // the inner loop.
- var dottedPropertyRegex = /^([^\[\.]+)\.(.+)?/;
- var indexedPropertyRegex = /^([^\.]+)\[([0-9]+)\](\.)?(.+)?/;
-
- lib.expandObjectPaths = function(data) {
- var match, key, prop, datum, idx, dest, trailingPath;
- if(typeof data === 'object' && !Array.isArray(data)) {
- for(key in data) {
- if(data.hasOwnProperty(key)) {
- if((match = key.match(dottedPropertyRegex))) {
- datum = data[key];
- prop = match[1];
-
- delete data[key];
-
- data[prop] = lib.extendDeepNoArrays(data[prop] || {}, lib.objectFromPath(key, lib.expandObjectPaths(datum))[prop]);
- } else if((match = key.match(indexedPropertyRegex))) {
- datum = data[key];
-
- prop = match[1];
- idx = parseInt(match[2]);
-
- delete data[key];
-
- data[prop] = data[prop] || [];
-
- if(match[3] === '.') {
- // This is the case where theere are subsequent properties into which
- // we must recurse, e.g. transforms[0].value
- trailingPath = match[4];
- dest = data[prop][idx] = data[prop][idx] || {};
-
- // NB: Extend deep no arrays prevents this from working on multiple
- // nested properties in the same object, e.g.
- //
- // {
- // foo[0].bar[1].range
- // foo[0].bar[0].range
- // }
- //
- // In this case, the extendDeepNoArrays will overwrite one array with
- // the other, so that both properties *will not* be present in the
- // result. Fixing this would require a more intelligent tracking
- // of changes and merging than extendDeepNoArrays currently accomplishes.
- lib.extendDeepNoArrays(dest, lib.objectFromPath(trailingPath, lib.expandObjectPaths(datum)));
- } else {
- // This is the case where this property is the end of the line,
- // e.g. xaxis.range[0]
- data[prop][idx] = lib.expandObjectPaths(datum);
- }
- } else {
- data[key] = lib.expandObjectPaths(data[key]);
- }
- }
- }
- }
-
- return data;
- };
-
- /**
- * Converts value to string separated by the provided separators.
- *
- * @example
- * lib.numSeparate(2016, '.,');
- * // returns '2016'
- *
- * @example
- * lib.numSeparate(3000, '.,', true);
- * // returns '3,000'
- *
- * @example
- * lib.numSeparate(1234.56, '|,')
- * // returns '1,234|56'
- *
- * @param {string|number} value the value to be converted
- * @param {string} separators string of decimal, then thousands separators
- * @param {boolean} separatethousands boolean, 4-digit integers are separated if true
- *
- * @return {string} the value that has been separated
- */
- lib.numSeparate = function(value, separators, separatethousands) {
- if(!separatethousands) separatethousands = false;
-
- if(typeof separators !== 'string' || separators.length === 0) {
- throw new Error('Separator string required for formatting!');
- }
-
- if(typeof value === 'number') {
- value = String(value);
- }
-
- var thousandsRe = /(\d+)(\d{3})/;
- var decimalSep = separators.charAt(0);
- var thouSep = separators.charAt(1);
-
- var x = value.split('.');
- var x1 = x[0];
- var x2 = x.length > 1 ? decimalSep + x[1] : '';
-
- // Years are ignored for thousands separators
- if(thouSep && (x.length > 1 || x1.length > 4 || separatethousands)) {
- while(thousandsRe.test(x1)) {
- x1 = x1.replace(thousandsRe, '$1' + thouSep + '$2');
- }
- }
-
- return x1 + x2;
- };
-
- var TEMPLATE_STRING_REGEX = /%{([^\s%{}:]*)(:[^}]*)?}/g;
- var SIMPLE_PROPERTY_REGEX = /^\w*$/;
-
- /**
- * Substitute values from an object into a string
- *
- * Examples:
- * Lib.templateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
- * Lib.templateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
- *
- * @param {string} input string containing %{...} template strings
- * @param {obj} data object containing substitution values
- *
- * @return {string} templated string
- */
- lib.templateString = function(string, obj) {
- // Not all that useful, but cache nestedProperty instantiation
- // just in case it speeds things up *slightly*:
- var getterCache = {};
-
- return string.replace(TEMPLATE_STRING_REGEX, function(dummy, key) {
- if(SIMPLE_PROPERTY_REGEX.test(key)) {
- return obj[key] || '';
- }
- getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get;
- return getterCache[key]() || '';
- });
- };
-
- var TEMPLATE_STRING_FORMAT_SEPARATOR = /^:/;
- var numberOfHoverTemplateWarnings = 0;
- var maximumNumberOfHoverTemplateWarnings = 10;
- /**
- * Substitute values from an object into a string and optionally formats them using d3-format,
- * or fallback to associated labels.
- *
- * Examples:
- * Lib.templateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
- * Lib.templateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
- * Lib.templateString('price: %{y:$.2f}', {y: 1}) --> 'price: $1.00'
- *
- * @param {string} input string containing %{...:...} template strings
- * @param {obj} data object containing fallback text when no formatting is specified, ex.: {yLabel: 'formattedYValue'}
- * @param {obj} data objects containing substitution values
- *
- * @return {string} templated string
- */
- lib.hovertemplateString = function(string, labels) {
- var args = arguments;
- // Not all that useful, but cache nestedProperty instantiation
- // just in case it speeds things up *slightly*:
- var getterCache = {};
-
- return string.replace(TEMPLATE_STRING_REGEX, function(match, key, format) {
- var obj, value, i;
- for(i = 2; i < args.length; i++) {
- obj = args[i];
- if(obj.hasOwnProperty(key)) {
- value = obj[key];
- break;
- }
-
- if(!SIMPLE_PROPERTY_REGEX.test(key)) {
- value = getterCache[key] || lib.nestedProperty(obj, key).get();
- if(value) getterCache[key] = value;
- }
- if(value !== undefined) break;
- }
-
- if(value === undefined) {
- if(numberOfHoverTemplateWarnings < maximumNumberOfHoverTemplateWarnings) {
- lib.warn('Variable \'' + key + '\' in hovertemplate could not be found!');
- value = match;
- }
-
- if(numberOfHoverTemplateWarnings === maximumNumberOfHoverTemplateWarnings) {
- lib.warn('Too many hovertemplate warnings - additional warnings will be suppressed');
- }
- numberOfHoverTemplateWarnings++;
- }
-
- if(format) {
- value = d3.format(format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''))(value);
- } else {
- if(labels.hasOwnProperty(key + 'Label')) value = labels[key + 'Label'];
- }
- return value;
- });
- };
-
- /*
- * alphanumeric string sort, tailored for subplot IDs like scene2, scene10, x10y13 etc
- */
- var char0 = 48;
- var char9 = 57;
- lib.subplotSort = function(a, b) {
- var l = Math.min(a.length, b.length) + 1;
- var numA = 0;
- var numB = 0;
- for(var i = 0; i < l; i++) {
- var charA = a.charCodeAt(i) || 0;
- var charB = b.charCodeAt(i) || 0;
- var isNumA = charA >= char0 && charA <= char9;
- var isNumB = charB >= char0 && charB <= char9;
-
- if(isNumA) numA = 10 * numA + charA - char0;
- if(isNumB) numB = 10 * numB + charB - char0;
-
- if(!isNumA || !isNumB) {
- if(numA !== numB) return numA - numB;
- if(charA !== charB) return charA - charB;
- }
- }
- return numB - numA;
- };
-
- // repeatable pseudorandom generator
- var randSeed = 2000000000;
-
- lib.seedPseudoRandom = function() {
- randSeed = 2000000000;
- };
-
- lib.pseudoRandom = function() {
- var lastVal = randSeed;
- randSeed = (69069 * randSeed + 1) % 4294967296;
- // don't let consecutive vals be too close together
- // gets away from really trying to be random, in favor of better local uniformity
- if(Math.abs(randSeed - lastVal) < 429496729) return lib.pseudoRandom();
- return randSeed / 4294967296;
- };
-
- },{"../constants/numerical":149,"./anchor_utils":153,"./angles":154,"./array":155,"./clean_number":156,"./clear_responsive":158,"./coerce":159,"./dates":160,"./extend":162,"./filter_unique":163,"./filter_visible":164,"./geometry2d":165,"./get_graph_div":166,"./identity":167,"./is_plain_object":169,"./keyed_container":170,"./localize":171,"./loggers":172,"./make_trace_groups":173,"./matrix":174,"./mod":175,"./nested_property":176,"./noop":177,"./notifier":178,"./push_unique":181,"./regex":183,"./relative_attr":184,"./relink_private":185,"./search":186,"./stats":188,"./throttle":190,"./to_log_range":191,"d3":16,"fast-isnumeric":18}],169:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- // more info: http://stackoverflow.com/questions/18531624/isplainobject-thing
- module.exports = function isPlainObject(obj) {
-
- // We need to be a little less strict in the `imagetest` container because
- // of how async image requests are handled.
- //
- // N.B. isPlainObject(new Constructor()) will return true in `imagetest`
- if(window && window.process && window.process.versions) {
- return Object.prototype.toString.call(obj) === '[object Object]';
- }
-
- return (
- Object.prototype.toString.call(obj) === '[object Object]' &&
- Object.getPrototypeOf(obj) === Object.prototype
- );
- };
-
- },{}],170:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var nestedProperty = _dereq_('./nested_property');
-
- var SIMPLE_PROPERTY_REGEX = /^\w*$/;
-
- // bitmask for deciding what's updated. Sometimes the name needs to be updated,
- // sometimes the value needs to be updated, and sometimes both do. This is just
- // a simple way to track what's updated such that it's a simple OR operation to
- // assimilate new updates.
- //
- // The only exception is the UNSET bit that tracks when we need to explicitly
- // unset and remove the property. This concrn arises because of the special
- // way in which nestedProperty handles null/undefined. When you specify `null`,
- // it prunes any unused items in the tree. I ran into some issues with it getting
- // null vs undefined confused, so UNSET is just a bit that forces the property
- // update to send `null`, removing the property explicitly rather than setting
- // it to undefined.
- var NONE = 0;
- var NAME = 1;
- var VALUE = 2;
- var BOTH = 3;
- var UNSET = 4;
-
- module.exports = function keyedContainer(baseObj, path, keyName, valueName) {
- keyName = keyName || 'name';
- valueName = valueName || 'value';
- var i, arr, baseProp;
- var changeTypes = {};
-
- if(path && path.length) {
- baseProp = nestedProperty(baseObj, path);
- arr = baseProp.get();
- } else {
- arr = baseObj;
- }
-
- path = path || '';
-
- // Construct an index:
- var indexLookup = {};
- if(arr) {
- for(i = 0; i < arr.length; i++) {
- indexLookup[arr[i][keyName]] = i;
- }
- }
-
- var isSimpleValueProp = SIMPLE_PROPERTY_REGEX.test(valueName);
-
- var obj = {
- set: function(name, value) {
- var changeType = value === null ? UNSET : NONE;
-
- // create the base array if necessary
- if(!arr) {
- if(!baseProp || changeType === UNSET) return;
-
- arr = [];
- baseProp.set(arr);
- }
-
- var idx = indexLookup[name];
- if(idx === undefined) {
- if(changeType === UNSET) return;
-
- changeType = changeType | BOTH;
- idx = arr.length;
- indexLookup[name] = idx;
- } else if(value !== (isSimpleValueProp ? arr[idx][valueName] : nestedProperty(arr[idx], valueName).get())) {
- changeType = changeType | VALUE;
- }
-
- var newValue = arr[idx] = arr[idx] || {};
- newValue[keyName] = name;
-
- if(isSimpleValueProp) {
- newValue[valueName] = value;
- } else {
- nestedProperty(newValue, valueName).set(value);
- }
-
- // If it's not an unset, force that bit to be unset. This is all related to the fact
- // that undefined and null are a bit specially implemented in nestedProperties.
- if(value !== null) {
- changeType = changeType & ~UNSET;
- }
-
- changeTypes[idx] = changeTypes[idx] | changeType;
-
- return obj;
- },
- get: function(name) {
- if(!arr) return;
-
- var idx = indexLookup[name];
-
- if(idx === undefined) {
- return undefined;
- } else if(isSimpleValueProp) {
- return arr[idx][valueName];
- } else {
- return nestedProperty(arr[idx], valueName).get();
- }
- },
- rename: function(name, newName) {
- var idx = indexLookup[name];
-
- if(idx === undefined) return obj;
- changeTypes[idx] = changeTypes[idx] | NAME;
-
- indexLookup[newName] = idx;
- delete indexLookup[name];
-
- arr[idx][keyName] = newName;
-
- return obj;
- },
- remove: function(name) {
- var idx = indexLookup[name];
-
- if(idx === undefined) return obj;
-
- var object = arr[idx];
- if(Object.keys(object).length > 2) {
- // This object contains more than just the key/value, so unset
- // the value without modifying the entry otherwise:
- changeTypes[idx] = changeTypes[idx] | VALUE;
- return obj.set(name, null);
- }
-
- if(isSimpleValueProp) {
- for(i = idx; i < arr.length; i++) {
- changeTypes[i] = changeTypes[i] | BOTH;
- }
- for(i = idx; i < arr.length; i++) {
- indexLookup[arr[i][keyName]]--;
- }
- arr.splice(idx, 1);
- delete(indexLookup[name]);
- } else {
- // Perform this update *strictly* so we can check whether the result's
- // been pruned. If so, it's a removal. If not, it's a value unset only.
- nestedProperty(object, valueName).set(null);
-
- // Now check if the top level nested property has any keys left. If so,
- // the object still has values so we only want to unset the key. If not,
- // the entire object can be removed since there's no other data.
- // var topLevelKeys = Object.keys(object[valueName.split('.')[0]] || []);
-
- changeTypes[idx] = changeTypes[idx] | VALUE | UNSET;
- }
-
- return obj;
- },
- constructUpdate: function() {
- var astr, idx;
- var update = {};
- var changed = Object.keys(changeTypes);
- for(var i = 0; i < changed.length; i++) {
- idx = changed[i];
- astr = path + '[' + idx + ']';
- if(arr[idx]) {
- if(changeTypes[idx] & NAME) {
- update[astr + '.' + keyName] = arr[idx][keyName];
- }
- if(changeTypes[idx] & VALUE) {
- if(isSimpleValueProp) {
- update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : arr[idx][valueName];
- } else {
- update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : nestedProperty(arr[idx], valueName).get();
- }
- }
- } else {
- update[astr] = null;
- }
- }
-
- return update;
- }
- };
-
- return obj;
- };
-
- },{"./nested_property":176}],171:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../registry');
-
- /**
- * localize: translate a string for the current locale
- *
- * @param {object} gd: the graphDiv for context
- * gd._context.locale determines the language (& optional region/country)
- * the dictionary for each locale may either be supplied in
- * gd._context.locales or globally via Plotly.register
- * @param {string} s: the string to translate
- */
- module.exports = function localize(gd, s) {
- var locale = gd._context.locale;
-
- /*
- * Priority of lookup:
- * contextDicts[locale],
- * registeredDicts[locale],
- * contextDicts[baseLocale], (if baseLocale is distinct)
- * registeredDicts[baseLocale]
- * Return the first translation we find.
- * This way if you have a regionalization you are allowed to specify
- * only what's different from the base locale, everything else will
- * fall back on the base.
- */
- for(var i = 0; i < 2; i++) {
- var locales = gd._context.locales;
- for(var j = 0; j < 2; j++) {
- var dict = (locales[locale] || {}).dictionary;
- if(dict) {
- var out = dict[s];
- if(out) return out;
- }
- locales = Registry.localeRegistry;
- }
-
- var baseLocale = locale.split('-')[0];
- if(baseLocale === locale) break;
- locale = baseLocale;
- }
-
- return s;
- };
-
- },{"../registry":257}],172:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /* eslint-disable no-console */
-
- var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig;
-
- var loggers = module.exports = {};
-
- /**
- * ------------------------------------------
- * debugging tools
- * ------------------------------------------
- */
-
- loggers.log = function() {
- if(dfltConfig.logging > 1) {
- var messages = ['LOG:'];
-
- for(var i = 0; i < arguments.length; i++) {
- messages.push(arguments[i]);
- }
-
- apply(console.trace || console.log, messages);
- }
- };
-
- loggers.warn = function() {
- if(dfltConfig.logging > 0) {
- var messages = ['WARN:'];
-
- for(var i = 0; i < arguments.length; i++) {
- messages.push(arguments[i]);
- }
-
- apply(console.trace || console.log, messages);
- }
- };
-
- loggers.error = function() {
- if(dfltConfig.logging > 0) {
- var messages = ['ERROR:'];
-
- for(var i = 0; i < arguments.length; i++) {
- messages.push(arguments[i]);
- }
-
- apply(console.error, messages);
- }
- };
-
- /*
- * Robust apply, for IE9 where console.log doesn't support
- * apply like other functions do
- */
- function apply(f, args) {
- if(f && f.apply) {
- try {
- // `this` should always be console, since here we're always
- // applying a method of the console object.
- f.apply(console, args);
- return;
- }
- catch(e) { /* in case apply failed, fall back on the code below */ }
- }
-
- // no apply - just try calling the function on each arg independently
- for(var i = 0; i < args.length; i++) {
- try {
- f(args[i]);
- }
- catch(e) {
- // still fails - last resort simple console.log
- console.log(args[i]);
- }
- }
- }
-
- },{"../plot_api/plot_config":200}],173:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- /**
- * General helper to manage trace groups based on calcdata
- *
- * @param {d3.selection} traceLayer: a selection containing a single group
- * to draw these traces into
- * @param {array} cdModule: array of calcdata items for this
- * module and subplot combination. Assumes the calcdata item for each
- * trace is an array with the fullData trace attached to the first item.
- * @param {string} cls: the class attribute to give each trace group
- * so you can give multiple classes separated by spaces
- */
- module.exports = function makeTraceGroups(traceLayer, cdModule, cls) {
- var traces = traceLayer.selectAll('g.' + cls.replace(/\s/g, '.'))
- .data(cdModule, function(cd) { return cd[0].trace.uid; });
-
- traces.exit().remove();
-
- traces.enter().append('g')
- .attr('class', cls);
-
- traces.order();
-
- return traces;
- };
-
- },{}],174:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- exports.init2dArray = function(rowLength, colLength) {
- var array = new Array(rowLength);
- for(var i = 0; i < rowLength; i++) array[i] = new Array(colLength);
- return array;
- };
-
- /**
- * transpose a (possibly ragged) 2d array z. inspired by
- * http://stackoverflow.com/questions/17428587/
- * transposing-a-2d-array-in-javascript
- */
- exports.transposeRagged = function(z) {
- var maxlen = 0;
- var zlen = z.length;
- var i, j;
- // Maximum row length:
- for(i = 0; i < zlen; i++) maxlen = Math.max(maxlen, z[i].length);
-
- var t = new Array(maxlen);
- for(i = 0; i < maxlen; i++) {
- t[i] = new Array(zlen);
- for(j = 0; j < zlen; j++) t[i][j] = z[j][i];
- }
-
- return t;
- };
-
- // our own dot function so that we don't need to include numeric
- exports.dot = function(x, y) {
- if(!(x.length && y.length) || x.length !== y.length) return null;
-
- var len = x.length;
- var out;
- var i;
-
- if(x[0].length) {
- // mat-vec or mat-mat
- out = new Array(len);
- for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y);
- }
- else if(y[0].length) {
- // vec-mat
- var yTranspose = exports.transposeRagged(y);
- out = new Array(yTranspose.length);
- for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]);
- }
- else {
- // vec-vec
- out = 0;
- for(i = 0; i < len; i++) out += x[i] * y[i];
- }
-
- return out;
- };
-
- // translate by (x,y)
- exports.translationMatrix = function(x, y) {
- return [[1, 0, x], [0, 1, y], [0, 0, 1]];
- };
-
- // rotate by alpha around (0,0)
- exports.rotationMatrix = function(alpha) {
- var a = alpha * Math.PI / 180;
- return [[Math.cos(a), -Math.sin(a), 0],
- [Math.sin(a), Math.cos(a), 0],
- [0, 0, 1]];
- };
-
- // rotate by alpha around (x,y)
- exports.rotationXYMatrix = function(a, x, y) {
- return exports.dot(
- exports.dot(exports.translationMatrix(x, y),
- exports.rotationMatrix(a)),
- exports.translationMatrix(-x, -y));
- };
-
- // applies a 2D transformation matrix to either x and y params or an [x,y] array
- exports.apply2DTransform = function(transform) {
- return function() {
- var args = arguments;
- if(args.length === 3) {
- args = args[0];
- }// from map
- var xy = arguments.length === 1 ? args[0] : [args[0], args[1]];
- return exports.dot(transform, [xy[0], xy[1], 1]).slice(0, 2);
- };
- };
-
- // applies a 2D transformation matrix to an [x1,y1,x2,y2] array (to transform a segment)
- exports.apply2DTransform2 = function(transform) {
- var at = exports.apply2DTransform(transform);
- return function(xys) {
- return at(xys.slice(0, 2)).concat(at(xys.slice(2, 4)));
- };
- };
-
- },{}],175:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /**
- * sanitized modulus function that always returns in the range [0, d)
- * rather than (-d, 0] if v is negative
- */
- function mod(v, d) {
- var out = v % d;
- return out < 0 ? out + d : out;
- }
-
- /**
- * sanitized modulus function that always returns in the range [-d/2, d/2]
- * rather than (-d, 0] if v is negative
- */
- function modHalf(v, d) {
- return Math.abs(v) > (d / 2) ?
- v - Math.round(v / d) * d :
- v;
- }
-
- module.exports = {
- mod: mod,
- modHalf: modHalf
- };
-
- },{}],176:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
-
- /**
- * convert a string s (such as 'xaxis.range[0]')
- * representing a property of nested object into set and get methods
- * also return the string and object so we don't have to keep track of them
- * allows [-1] for an array index, to set a property inside all elements
- * of an array
- * eg if obj = {arr: [{a: 1}, {a: 2}]}
- * you can do p = nestedProperty(obj, 'arr[-1].a')
- * but you cannot set the array itself this way, to do that
- * just set the whole array.
- * eg if obj = {arr: [1, 2, 3]}
- * you can't do nestedProperty(obj, 'arr[-1]').set(5)
- * but you can do nestedProperty(obj, 'arr').set([5, 5, 5])
- */
- module.exports = function nestedProperty(container, propStr) {
- if(isNumeric(propStr)) propStr = String(propStr);
- else if(typeof propStr !== 'string' ||
- propStr.substr(propStr.length - 4) === '[-1]') {
- throw 'bad property string';
- }
-
- var j = 0;
- var propParts = propStr.split('.');
- var indexed;
- var indices;
- var i;
-
- // check for parts of the nesting hierarchy that are numbers (ie array elements)
- while(j < propParts.length) {
- // look for non-bracket chars, then any number of [##] blocks
- indexed = String(propParts[j]).match(/^([^\[\]]*)((\[\-?[0-9]*\])+)$/);
- if(indexed) {
- if(indexed[1]) propParts[j] = indexed[1];
- // allow propStr to start with bracketed array indices
- else if(j === 0) propParts.splice(0, 1);
- else throw 'bad property string';
-
- indices = indexed[2]
- .substr(1, indexed[2].length - 2)
- .split('][');
-
- for(i = 0; i < indices.length; i++) {
- j++;
- propParts.splice(j, 0, Number(indices[i]));
- }
- }
- j++;
- }
-
- if(typeof container !== 'object') {
- return badContainer(container, propStr, propParts);
- }
-
- return {
- set: npSet(container, propParts, propStr),
- get: npGet(container, propParts),
- astr: propStr,
- parts: propParts,
- obj: container
- };
- };
-
- function npGet(cont, parts) {
- return function() {
- var curCont = cont;
- var curPart;
- var allSame;
- var out;
- var i;
- var j;
-
- for(i = 0; i < parts.length - 1; i++) {
- curPart = parts[i];
- if(curPart === -1) {
- allSame = true;
- out = [];
- for(j = 0; j < curCont.length; j++) {
- out[j] = npGet(curCont[j], parts.slice(i + 1))();
- if(out[j] !== out[0]) allSame = false;
- }
- return allSame ? out[0] : out;
- }
- if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) {
- return undefined;
- }
- curCont = curCont[curPart];
- if(typeof curCont !== 'object' || curCont === null) {
- return undefined;
- }
- }
-
- // only hit this if parts.length === 1
- if(typeof curCont !== 'object' || curCont === null) return undefined;
-
- out = curCont[parts[i]];
- if(out === null) return undefined;
- return out;
- };
- }
-
- /*
- * Can this value be deleted? We can delete `undefined`, and `null` except INSIDE an
- * *args* array.
- *
- * Previously we also deleted some `{}` and `[]`, in order to try and make set/unset
- * a net noop; but this causes far more complication than it's worth, and still had
- * lots of exceptions. See https://github.com/plotly/plotly.js/issues/1410
- *
- * *args* arrays get passed directly to API methods and we should respect null if
- * the user put it there, but otherwise null is deleted as we use it as code
- * in restyle/relayout/update for "delete this value" whereas undefined means
- * "ignore this edit"
- */
- var ARGS_PATTERN = /(^|\.)args\[/;
- function isDeletable(val, propStr) {
- return (val === undefined) || (val === null && !propStr.match(ARGS_PATTERN));
- }
-
- function npSet(cont, parts, propStr) {
- return function(val) {
- var curCont = cont;
- var propPart = '';
- var containerLevels = [[cont, propPart]];
- var toDelete = isDeletable(val, propStr);
- var curPart;
- var i;
-
- for(i = 0; i < parts.length - 1; i++) {
- curPart = parts[i];
-
- if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) {
- throw 'array index but container is not an array';
- }
-
- // handle special -1 array index
- if(curPart === -1) {
- toDelete = !setArrayAll(curCont, parts.slice(i + 1), val, propStr);
- if(toDelete) break;
- else return;
- }
-
- if(!checkNewContainer(curCont, curPart, parts[i + 1], toDelete)) {
- break;
- }
-
- curCont = curCont[curPart];
-
- if(typeof curCont !== 'object' || curCont === null) {
- throw 'container is not an object';
- }
-
- propPart = joinPropStr(propPart, curPart);
-
- containerLevels.push([curCont, propPart]);
- }
-
- if(toDelete) {
- if(i === parts.length - 1) {
- delete curCont[parts[i]];
-
- // The one bit of pruning we still do: drop `undefined` from the end of arrays.
- // In case someone has already unset previous items, continue until we hit a
- // non-undefined value.
- if(Array.isArray(curCont) && +parts[i] === curCont.length - 1) {
- while(curCont.length && curCont[curCont.length - 1] === undefined) {
- curCont.pop();
- }
- }
- }
- }
- else curCont[parts[i]] = val;
- };
- }
-
- function joinPropStr(propStr, newPart) {
- var toAdd = newPart;
- if(isNumeric(newPart)) toAdd = '[' + newPart + ']';
- else if(propStr) toAdd = '.' + newPart;
-
- return propStr + toAdd;
- }
-
- // handle special -1 array index
- function setArrayAll(containerArray, innerParts, val, propStr) {
- var arrayVal = isArrayOrTypedArray(val);
- var allSet = true;
- var thisVal = val;
- var thisPropStr = propStr.replace('-1', 0);
- var deleteThis = arrayVal ? false : isDeletable(val, thisPropStr);
- var firstPart = innerParts[0];
- var i;
-
- for(i = 0; i < containerArray.length; i++) {
- thisPropStr = propStr.replace('-1', i);
- if(arrayVal) {
- thisVal = val[i % val.length];
- deleteThis = isDeletable(thisVal, thisPropStr);
- }
- if(deleteThis) allSet = false;
- if(!checkNewContainer(containerArray, i, firstPart, deleteThis)) {
- continue;
- }
- npSet(containerArray[i], innerParts, propStr.replace('-1', i))(thisVal);
- }
- return allSet;
- }
-
- /**
- * make new sub-container as needed.
- * returns false if there's no container and none is needed
- * because we're only deleting an attribute
- */
- function checkNewContainer(container, part, nextPart, toDelete) {
- if(container[part] === undefined) {
- if(toDelete) return false;
-
- if(typeof nextPart === 'number') container[part] = [];
- else container[part] = {};
- }
- return true;
- }
-
- function badContainer(container, propStr, propParts) {
- return {
- set: function() { throw 'bad container'; },
- get: function() {},
- astr: propStr,
- parts: propParts,
- obj: container
- };
- }
-
- },{"./array":155,"fast-isnumeric":18}],177:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- // Simple helper functions
- // none of these need any external deps
-
- module.exports = function noop() {};
-
- },{}],178:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var NOTEDATA = [];
-
- /**
- * notifier
- * @param {String} text The person's user name
- * @param {Number} [delay=1000] The delay time in milliseconds
- * or 'long' which provides 2000 ms delay time.
- * @return {undefined} this function does not return a value
- */
- module.exports = function(text, displayLength) {
- if(NOTEDATA.indexOf(text) !== -1) return;
-
- NOTEDATA.push(text);
-
- var ts = 1000;
- if(isNumeric(displayLength)) ts = displayLength;
- else if(displayLength === 'long') ts = 3000;
-
- var notifierContainer = d3.select('body')
- .selectAll('.plotly-notifier')
- .data([0]);
- notifierContainer.enter()
- .append('div')
- .classed('plotly-notifier', true);
-
- var notes = notifierContainer.selectAll('.notifier-note').data(NOTEDATA);
-
- function killNote(transition) {
- transition
- .duration(700)
- .style('opacity', 0)
- .each('end', function(thisText) {
- var thisIndex = NOTEDATA.indexOf(thisText);
- if(thisIndex !== -1) NOTEDATA.splice(thisIndex, 1);
- d3.select(this).remove();
- });
- }
-
- notes.enter().append('div')
- .classed('notifier-note', true)
- .style('opacity', 0)
- .each(function(thisText) {
- var note = d3.select(this);
-
- note.append('button')
- .classed('notifier-close', true)
- .html('×')
- .on('click', function() {
- note.transition().call(killNote);
- });
-
- var p = note.append('p');
- var lines = thisText.split(/<br\s*\/?>/g);
- for(var i = 0; i < lines.length; i++) {
- if(i) p.append('br');
- p.append('span').text(lines[i]);
- }
-
- note.transition()
- .duration(700)
- .style('opacity', 1)
- .transition()
- .delay(ts)
- .call(killNote);
- });
- };
-
- },{"d3":16,"fast-isnumeric":18}],179:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var setCursor = _dereq_('./setcursor');
-
- var STASHATTR = 'data-savedcursor';
- var NO_CURSOR = '!!';
-
- /*
- * works with our CSS cursor classes (see css/_cursor.scss)
- * to override a previous cursor set on d3 single-element selections,
- * by moving the name of the original cursor to the data-savedcursor attr.
- * omit cursor to revert to the previously set value.
- */
- module.exports = function overrideCursor(el3, csr) {
- var savedCursor = el3.attr(STASHATTR);
- if(csr) {
- if(!savedCursor) {
- var classes = (el3.attr('class') || '').split(' ');
- for(var i = 0; i < classes.length; i++) {
- var cls = classes[i];
- if(cls.indexOf('cursor-') === 0) {
- el3.attr(STASHATTR, cls.substr(7))
- .classed(cls, false);
- }
- }
- if(!el3.attr(STASHATTR)) {
- el3.attr(STASHATTR, NO_CURSOR);
- }
- }
- setCursor(el3, csr);
- }
- else if(savedCursor) {
- el3.attr(STASHATTR, null);
-
- if(savedCursor === NO_CURSOR) setCursor(el3);
- else setCursor(el3, savedCursor);
- }
- };
-
- },{"./setcursor":187}],180:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var dot = _dereq_('./matrix').dot;
- var BADNUM = _dereq_('../constants/numerical').BADNUM;
-
- var polygon = module.exports = {};
-
- /**
- * Turn an array of [x, y] pairs into a polygon object
- * that can test if points are inside it
- *
- * @param ptsIn Array of [x, y] pairs
- *
- * @returns polygon Object {xmin, xmax, ymin, ymax, pts, contains}
- * (x|y)(min|max) are the bounding rect of the polygon
- * pts is the original array, with the first pair repeated at the end
- * contains is a function: (pt, omitFirstEdge)
- * pt is the [x, y] pair to test
- * omitFirstEdge truthy means points exactly on the first edge don't
- * count. This is for use adding one polygon to another so we
- * don't double-count the edge where they meet.
- * returns boolean: is pt inside the polygon (including on its edges)
- */
- polygon.tester = function tester(ptsIn) {
- var pts = ptsIn.slice();
- var xmin = pts[0][0];
- var xmax = xmin;
- var ymin = pts[0][1];
- var ymax = ymin;
- var i;
-
- pts.push(pts[0]);
- for(i = 1; i < pts.length; i++) {
- xmin = Math.min(xmin, pts[i][0]);
- xmax = Math.max(xmax, pts[i][0]);
- ymin = Math.min(ymin, pts[i][1]);
- ymax = Math.max(ymax, pts[i][1]);
- }
-
- // do we have a rectangle? Handle this here, so we can use the same
- // tester for the rectangular case without sacrificing speed
-
- var isRect = false;
- var rectFirstEdgeTest;
-
- if(pts.length === 5) {
- if(pts[0][0] === pts[1][0]) { // vert, horz, vert, horz
- if(pts[2][0] === pts[3][0] &&
- pts[0][1] === pts[3][1] &&
- pts[1][1] === pts[2][1]) {
- isRect = true;
- rectFirstEdgeTest = function(pt) { return pt[0] === pts[0][0]; };
- }
- }
- else if(pts[0][1] === pts[1][1]) { // horz, vert, horz, vert
- if(pts[2][1] === pts[3][1] &&
- pts[0][0] === pts[3][0] &&
- pts[1][0] === pts[2][0]) {
- isRect = true;
- rectFirstEdgeTest = function(pt) { return pt[1] === pts[0][1]; };
- }
- }
- }
-
- function rectContains(pt, omitFirstEdge) {
- var x = pt[0];
- var y = pt[1];
-
- if(x === BADNUM || x < xmin || x > xmax || y === BADNUM || y < ymin || y > ymax) {
- // pt is outside the bounding box of polygon
- return false;
- }
- if(omitFirstEdge && rectFirstEdgeTest(pt)) return false;
-
- return true;
- }
-
- function contains(pt, omitFirstEdge) {
- var x = pt[0];
- var y = pt[1];
-
- if(x === BADNUM || x < xmin || x > xmax || y === BADNUM || y < ymin || y > ymax) {
- // pt is outside the bounding box of polygon
- return false;
- }
-
- var imax = pts.length;
- var x1 = pts[0][0];
- var y1 = pts[0][1];
- var crossings = 0;
- var i;
- var x0;
- var y0;
- var xmini;
- var ycross;
-
- for(i = 1; i < imax; i++) {
- // find all crossings of a vertical line upward from pt with
- // polygon segments
- // crossings exactly at xmax don't count, unless the point is
- // exactly on the segment, then it counts as inside.
- x0 = x1;
- y0 = y1;
- x1 = pts[i][0];
- y1 = pts[i][1];
- xmini = Math.min(x0, x1);
-
- // outside the bounding box of this segment, it's only a crossing
- // if it's below the box.
- if(x < xmini || x > Math.max(x0, x1) || y > Math.max(y0, y1)) {
- continue;
- }
- else if(y < Math.min(y0, y1)) {
- // don't count the left-most point of the segment as a crossing
- // because we don't want to double-count adjacent crossings
- // UNLESS the polygon turns past vertical at exactly this x
- // Note that this is repeated below, but we can't factor it out
- // because
- if(x !== xmini) crossings++;
- }
- // inside the bounding box, check the actual line intercept
- else {
- // vertical segment - we know already that the point is exactly
- // on the segment, so mark the crossing as exactly at the point.
- if(x1 === x0) ycross = y;
- // any other angle
- else ycross = y0 + (x - x0) * (y1 - y0) / (x1 - x0);
-
- // exactly on the edge: counts as inside the polygon, unless it's the
- // first edge and we're omitting it.
- if(y === ycross) {
- if(i === 1 && omitFirstEdge) return false;
- return true;
- }
-
- if(y <= ycross && x !== xmini) crossings++;
- }
- }
-
- // if we've gotten this far, odd crossings means inside, even is outside
- return crossings % 2 === 1;
- }
-
- // detect if poly is degenerate
- var degenerate = true;
- var lastPt = pts[0];
- for(i = 1; i < pts.length; i++) {
- if(lastPt[0] !== pts[i][0] || lastPt[1] !== pts[i][1]) {
- degenerate = false;
- break;
- }
- }
-
- return {
- xmin: xmin,
- xmax: xmax,
- ymin: ymin,
- ymax: ymax,
- pts: pts,
- contains: isRect ? rectContains : contains,
- isRect: isRect,
- degenerate: degenerate
- };
- };
-
- /**
- * Test if a segment of a points array is bent or straight
- *
- * @param pts Array of [x, y] pairs
- * @param start the index of the proposed start of the straight section
- * @param end the index of the proposed end point
- * @param tolerance the max distance off the line connecting start and end
- * before the line counts as bent
- * @returns boolean: true means this segment is bent, false means straight
- */
- var isBent = polygon.isSegmentBent = function isBent(pts, start, end, tolerance) {
- var startPt = pts[start];
- var segment = [pts[end][0] - startPt[0], pts[end][1] - startPt[1]];
- var segmentSquared = dot(segment, segment);
- var segmentLen = Math.sqrt(segmentSquared);
- var unitPerp = [-segment[1] / segmentLen, segment[0] / segmentLen];
- var i;
- var part;
- var partParallel;
-
- for(i = start + 1; i < end; i++) {
- part = [pts[i][0] - startPt[0], pts[i][1] - startPt[1]];
- partParallel = dot(part, segment);
-
- if(partParallel < 0 || partParallel > segmentSquared ||
- Math.abs(dot(part, unitPerp)) > tolerance) return true;
- }
- return false;
- };
-
- /**
- * Make a filtering polygon, to minimize the number of segments
- *
- * @param pts Array of [x, y] pairs (must start with at least 1 pair)
- * @param tolerance the maximum deviation from straight allowed for
- * removing points to simplify the polygon
- *
- * @returns Object {addPt, raw, filtered}
- * addPt is a function(pt: [x, y] pair) to add a raw point and
- * continue filtering
- * raw is all the input points
- * filtered is the resulting filtered Array of [x, y] pairs
- */
- polygon.filter = function filter(pts, tolerance) {
- var ptsFiltered = [pts[0]];
- var doneRawIndex = 0;
- var doneFilteredIndex = 0;
-
- function addPt(pt) {
- pts.push(pt);
- var prevFilterLen = ptsFiltered.length;
- var iLast = doneRawIndex;
- ptsFiltered.splice(doneFilteredIndex + 1);
-
- for(var i = iLast + 1; i < pts.length; i++) {
- if(i === pts.length - 1 || isBent(pts, iLast, i + 1, tolerance)) {
- ptsFiltered.push(pts[i]);
- if(ptsFiltered.length < prevFilterLen - 2) {
- doneRawIndex = i;
- doneFilteredIndex = ptsFiltered.length - 1;
- }
- iLast = i;
- }
- }
- }
-
- if(pts.length > 1) {
- var lastPt = pts.pop();
- addPt(lastPt);
- }
-
- return {
- addPt: addPt,
- raw: pts,
- filtered: ptsFiltered
- };
- };
-
- },{"../constants/numerical":149,"./matrix":174}],181:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /**
- * Push array with unique items
- *
- * Ignores falsy items, except 0 so we can use it to construct arrays of indices.
- *
- * @param {array} array
- * array to be filled
- * @param {any} item
- * item to be or not to be inserted
- * @return {array}
- * ref to array (now possibly containing one more item)
- *
- */
- module.exports = function pushUnique(array, item) {
- if(item instanceof RegExp) {
- var itemStr = item.toString();
- for(var i = 0; i < array.length; i++) {
- if(array[i] instanceof RegExp && array[i].toString() === itemStr) {
- return array;
- }
- }
- array.push(item);
- }
- else if((item || item === 0) && array.indexOf(item) === -1) array.push(item);
-
- return array;
- };
-
- },{}],182:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../lib');
- var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig;
-
- /**
- * Copy arg array *without* removing `undefined` values from objects.
- *
- * @param gd
- * @param args
- * @returns {Array}
- */
- function copyArgArray(gd, args) {
- var copy = [];
- var arg;
-
- for(var i = 0; i < args.length; i++) {
- arg = args[i];
-
- if(arg === gd) copy[i] = arg;
- else if(typeof arg === 'object') {
- copy[i] = Array.isArray(arg) ?
- Lib.extendDeep([], arg) :
- Lib.extendDeepAll({}, arg);
- }
- else copy[i] = arg;
- }
-
- return copy;
- }
-
-
- // -----------------------------------------------------
- // Undo/Redo queue for plots
- // -----------------------------------------------------
-
-
- var queue = {};
-
- // TODO: disable/enable undo and redo buttons appropriately
-
- /**
- * Add an item to the undoQueue for a graphDiv
- *
- * @param gd
- * @param undoFunc Function undo this operation
- * @param undoArgs Args to supply undoFunc with
- * @param redoFunc Function to redo this operation
- * @param redoArgs Args to supply redoFunc with
- */
- queue.add = function(gd, undoFunc, undoArgs, redoFunc, redoArgs) {
- var queueObj,
- queueIndex;
-
- // make sure we have the queue and our position in it
- gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
- queueIndex = gd.undoQueue.index;
-
- // if we're already playing an undo or redo, or if this is an auto operation
- // (like pane resize... any others?) then we don't save this to the undo queue
- if(gd.autoplay) {
- if(!gd.undoQueue.inSequence) gd.autoplay = false;
- return;
- }
-
- // if we're not in a sequence or are just starting, we need a new queue item
- if(!gd.undoQueue.sequence || gd.undoQueue.beginSequence) {
- queueObj = {undo: {calls: [], args: []}, redo: {calls: [], args: []}};
- gd.undoQueue.queue.splice(queueIndex, gd.undoQueue.queue.length - queueIndex, queueObj);
- gd.undoQueue.index += 1;
- } else {
- queueObj = gd.undoQueue.queue[queueIndex - 1];
- }
- gd.undoQueue.beginSequence = false;
-
- // we unshift to handle calls for undo in a forward for loop later
- if(queueObj) {
- queueObj.undo.calls.unshift(undoFunc);
- queueObj.undo.args.unshift(undoArgs);
- queueObj.redo.calls.push(redoFunc);
- queueObj.redo.args.push(redoArgs);
- }
-
- if(gd.undoQueue.queue.length > dfltConfig.queueLength) {
- gd.undoQueue.queue.shift();
- gd.undoQueue.index--;
- }
- };
-
- /**
- * Begin a sequence of undoQueue changes
- *
- * @param gd
- */
- queue.startSequence = function(gd) {
- gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
- gd.undoQueue.sequence = true;
- gd.undoQueue.beginSequence = true;
- };
-
- /**
- * Stop a sequence of undoQueue changes
- *
- * Call this *after* you're sure your undo chain has ended
- *
- * @param gd
- */
- queue.stopSequence = function(gd) {
- gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
- gd.undoQueue.sequence = false;
- gd.undoQueue.beginSequence = false;
- };
-
- /**
- * Move one step back in the undo queue, and undo the object there.
- *
- * @param gd
- */
- queue.undo = function undo(gd) {
- var queueObj, i;
-
- if(gd.framework && gd.framework.isPolar) {
- gd.framework.undo();
- return;
- }
- if(gd.undoQueue === undefined ||
- isNaN(gd.undoQueue.index) ||
- gd.undoQueue.index <= 0) {
- return;
- }
-
- // index is pointing to next *forward* queueObj, point to the one we're undoing
- gd.undoQueue.index--;
-
- // get the queueObj for instructions on how to undo
- queueObj = gd.undoQueue.queue[gd.undoQueue.index];
-
- // this sequence keeps things from adding to the queue during undo/redo
- gd.undoQueue.inSequence = true;
- for(i = 0; i < queueObj.undo.calls.length; i++) {
- queue.plotDo(gd, queueObj.undo.calls[i], queueObj.undo.args[i]);
- }
- gd.undoQueue.inSequence = false;
- gd.autoplay = false;
- };
-
- /**
- * Redo the current object in the undo, then move forward in the queue.
- *
- * @param gd
- */
- queue.redo = function redo(gd) {
- var queueObj, i;
-
- if(gd.framework && gd.framework.isPolar) {
- gd.framework.redo();
- return;
- }
- if(gd.undoQueue === undefined ||
- isNaN(gd.undoQueue.index) ||
- gd.undoQueue.index >= gd.undoQueue.queue.length) {
- return;
- }
-
- // get the queueObj for instructions on how to undo
- queueObj = gd.undoQueue.queue[gd.undoQueue.index];
-
- // this sequence keeps things from adding to the queue during undo/redo
- gd.undoQueue.inSequence = true;
- for(i = 0; i < queueObj.redo.calls.length; i++) {
- queue.plotDo(gd, queueObj.redo.calls[i], queueObj.redo.args[i]);
- }
- gd.undoQueue.inSequence = false;
- gd.autoplay = false;
-
- // index is pointing to the thing we just redid, move it
- gd.undoQueue.index++;
- };
-
- /**
- * Called by undo/redo to make the actual changes.
- *
- * Not meant to be called publically, but included for mocking out in tests.
- *
- * @param gd
- * @param func
- * @param args
- */
- queue.plotDo = function(gd, func, args) {
- gd.autoplay = true;
-
- // this *won't* copy gd and it preserves `undefined` properties!
- args = copyArgArray(gd, args);
-
- // call the supplied function
- func.apply(null, args);
- };
-
- module.exports = queue;
-
- },{"../lib":168,"../plot_api/plot_config":200}],183:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /*
- * make a regex for matching counter ids/names ie xaxis, xaxis2, xaxis10...
- *
- * @param {string} head: the head of the pattern, eg 'x' matches 'x', 'x2', 'x10' etc.
- * 'xy' is a special case for cartesian subplots: it matches 'x2y3' etc
- * @param {Optional(string)} tail: a fixed piece after the id
- * eg counterRegex('scene', '.annotations') for scene2.annotations etc.
- * @param {boolean} openEnded: if true, the string may continue past the match.
- * @param {boolean} matchBeginning: if false, the string may start before the match.
- */
- exports.counter = function(head, tail, openEnded, matchBeginning) {
- var fullTail = (tail || '') + (openEnded ? '' : '$');
- var startWithPrefix = matchBeginning === false ? '' : '^';
- if(head === 'xy') {
- return new RegExp(startWithPrefix + 'x([2-9]|[1-9][0-9]+)?y([2-9]|[1-9][0-9]+)?' + fullTail);
- }
- return new RegExp(startWithPrefix + head + '([2-9]|[1-9][0-9]+)?' + fullTail);
- };
-
- },{}],184:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- // ASCEND: chop off the last nesting level - either [<n>] or .<key> - to ascend
- // the attribute tree. the remaining attrString is in match[1]
- var ASCEND = /^(.*)(\.[^\.\[\]]+|\[\d\])$/;
-
- // SIMPLEATTR: is this an un-nested attribute? (no dots or brackets)
- var SIMPLEATTR = /^[^\.\[\]]+$/;
-
- /*
- * calculate a relative attribute string, similar to a relative path
- *
- * @param {string} baseAttr:
- * an attribute string, such as 'annotations[3].x'. The "current location"
- * is the attribute string minus the last component ('annotations[3]')
- * @param {string} relativeAttr:
- * a route to the desired attribute string, using '^' to ascend
- *
- * @return {string} attrString:
- * for example:
- * relativeAttr('annotations[3].x', 'y') = 'annotations[3].y'
- * relativeAttr('annotations[3].x', '^[2].z') = 'annotations[2].z'
- * relativeAttr('annotations[3].x', '^^margin') = 'margin'
- * relativeAttr('annotations[3].x', '^^margin.r') = 'margin.r'
- */
- module.exports = function(baseAttr, relativeAttr) {
- while(relativeAttr) {
- var match = baseAttr.match(ASCEND);
-
- if(match) baseAttr = match[1];
- else if(baseAttr.match(SIMPLEATTR)) baseAttr = '';
- else throw new Error('bad relativeAttr call:' + [baseAttr, relativeAttr]);
-
- if(relativeAttr.charAt(0) === '^') relativeAttr = relativeAttr.slice(1);
- else break;
- }
-
- if(baseAttr && relativeAttr.charAt(0) !== '[') {
- return baseAttr + '.' + relativeAttr;
- }
- return baseAttr + relativeAttr;
- };
-
- },{}],185:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
- var isPlainObject = _dereq_('./is_plain_object');
-
- /**
- * Relink private _keys and keys with a function value from one container
- * to the new container.
- * Relink means copying if object is pass-by-value and adding a reference
- * if object is pass-by-ref.
- * This prevents deepCopying massive structures like a webgl context.
- */
- module.exports = function relinkPrivateKeys(toContainer, fromContainer) {
- for(var k in fromContainer) {
- var fromVal = fromContainer[k];
- var toVal = toContainer[k];
-
- if(toVal === fromVal) {
- continue;
- }
- if(k.charAt(0) === '_' || typeof fromVal === 'function') {
-
- // if it already exists at this point, it's something
- // that we recreate each time around, so ignore it
- if(k in toContainer) continue;
-
- toContainer[k] = fromVal;
- }
- else if(isArrayOrTypedArray(fromVal) && isArrayOrTypedArray(toVal) && isPlainObject(fromVal[0])) {
-
- // filter out data_array items that can contain user objects
- // most of the time the toVal === fromVal check will catch these early
- // but if the user makes new ones we also don't want to recurse in.
- if(k === 'customdata' || k === 'ids') continue;
-
- // recurse into arrays containers
- var minLen = Math.min(fromVal.length, toVal.length);
- for(var j = 0; j < minLen; j++) {
- if((toVal[j] !== fromVal[j]) && isPlainObject(fromVal[j]) && isPlainObject(toVal[j])) {
- relinkPrivateKeys(toVal[j], fromVal[j]);
- }
- }
- }
- else if(isPlainObject(fromVal) && isPlainObject(toVal)) {
-
- // recurse into objects, but only if they still exist
- relinkPrivateKeys(toVal, fromVal);
-
- if(!Object.keys(toVal).length) delete toContainer[k];
- }
- }
- };
-
- },{"./array":155,"./is_plain_object":169}],186:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var loggers = _dereq_('./loggers');
- var identity = _dereq_('./identity');
-
- // don't trust floating point equality - fraction of bin size to call
- // "on the line" and ensure that they go the right way specified by
- // linelow
- var roundingError = 1e-9;
-
-
- /**
- * findBin - find the bin for val - note that it can return outside the
- * bin range any pos. or neg. integer for linear bins, or -1 or
- * bins.length-1 for explicit.
- * bins is either an object {start,size,end} or an array length #bins+1
- * bins can be either increasing or decreasing but must be monotonic
- * for linear bins, we can just calculate. For listed bins, run a binary
- * search linelow (truthy) says the bin boundary should be attributed to
- * the lower bin rather than the default upper bin
- */
- exports.findBin = function(val, bins, linelow) {
- if(isNumeric(bins.start)) {
- return linelow ?
- Math.ceil((val - bins.start) / bins.size - roundingError) - 1 :
- Math.floor((val - bins.start) / bins.size + roundingError);
- }
- else {
- var n1 = 0;
- var n2 = bins.length;
- var c = 0;
- var binSize = (n2 > 1) ? (bins[n2 - 1] - bins[0]) / (n2 - 1) : 1;
- var n, test;
- if(binSize >= 0) {
- test = linelow ? lessThan : lessOrEqual;
- } else {
- test = linelow ? greaterOrEqual : greaterThan;
- }
- val += binSize * roundingError * (linelow ? -1 : 1) * (binSize >= 0 ? 1 : -1);
- // c is just to avoid infinite loops if there's an error
- while(n1 < n2 && c++ < 100) {
- n = Math.floor((n1 + n2) / 2);
- if(test(bins[n], val)) n1 = n + 1;
- else n2 = n;
- }
- if(c > 90) loggers.log('Long binary search...');
- return n1 - 1;
- }
- };
-
- function lessThan(a, b) { return a < b; }
- function lessOrEqual(a, b) { return a <= b; }
- function greaterThan(a, b) { return a > b; }
- function greaterOrEqual(a, b) { return a >= b; }
-
- exports.sorterAsc = function(a, b) { return a - b; };
- exports.sorterDes = function(a, b) { return b - a; };
-
- /**
- * find distinct values in an array, lumping together ones that appear to
- * just be off by a rounding error
- * return the distinct values and the minimum difference between any two
- */
- exports.distinctVals = function(valsIn) {
- var vals = valsIn.slice(); // otherwise we sort the original array...
- vals.sort(exports.sorterAsc);
-
- var l = vals.length - 1;
- var minDiff = (vals[l] - vals[0]) || 1;
- var errDiff = minDiff / (l || 1) / 10000;
- var v2 = [vals[0]];
-
- for(var i = 0; i < l; i++) {
- // make sure values aren't just off by a rounding error
- if(vals[i + 1] > vals[i] + errDiff) {
- minDiff = Math.min(minDiff, vals[i + 1] - vals[i]);
- v2.push(vals[i + 1]);
- }
- }
-
- return {vals: v2, minDiff: minDiff};
- };
-
- /**
- * return the smallest element from (sorted) array arrayIn that's bigger than val,
- * or (reverse) the largest element smaller than val
- * used to find the best tick given the minimum (non-rounded) tick
- * particularly useful for date/time where things are not powers of 10
- * binary search is probably overkill here...
- */
- exports.roundUp = function(val, arrayIn, reverse) {
- var low = 0;
- var high = arrayIn.length - 1;
- var mid;
- var c = 0;
- var dlow = reverse ? 0 : 1;
- var dhigh = reverse ? 1 : 0;
- var rounded = reverse ? Math.ceil : Math.floor;
- // c is just to avoid infinite loops if there's an error
- while(low < high && c++ < 100) {
- mid = rounded((low + high) / 2);
- if(arrayIn[mid] <= val) low = mid + dlow;
- else high = mid - dhigh;
- }
- return arrayIn[low];
- };
-
- /**
- * Tweak to Array.sort(sortFn) that improves performance for pre-sorted arrays
- *
- * Note that newer browsers (such as Chrome v70+) are starting to pick up
- * on pre-sorted arrays which may render the following optimization unnecessary
- * in the future.
- *
- * Motivation: sometimes we need to sort arrays but the input is likely to
- * already be sorted. Browsers don't seem to pick up on pre-sorted arrays,
- * and in fact Chrome is actually *slower* sorting pre-sorted arrays than purely
- * random arrays. FF is at least faster if the array is pre-sorted, but still
- * not as fast as it could be.
- * Here's how this plays out sorting a length-1e6 array:
- *
- * Calls to Sort FN | Chrome bare | FF bare | Chrome tweak | FF tweak
- * | v68.0 Mac | v61.0 Mac| |
- * ------------------+---------------+-----------+----------------+------------
- * ordered | 30.4e6 | 10.1e6 | 1e6 | 1e6
- * reversed | 29.4e6 | 9.9e6 | 1e6 + reverse | 1e6 + reverse
- * random | ~21e6 | ~18.7e6 | ~21e6 | ~18.7e6
- *
- * So this is a substantial win for pre-sorted (ordered or exactly reversed)
- * arrays. Including this wrapper on an unsorted array adds a penalty that will
- * in general be only a few calls to the sort function. The only case this
- * penalty will be significant is if the array is mostly sorted but there are
- * a few unsorted items near the end, but the penalty is still at most N calls
- * out of (for N=1e6) ~20N total calls
- *
- * @param {Array} array: the array, to be sorted in place
- * @param {function} sortFn: As in Array.sort, function(a, b) that puts
- * item a before item b if the return is negative, a after b if positive,
- * and no change if zero.
- * @return {Array}: the original array, sorted in place.
- */
- exports.sort = function(array, sortFn) {
- var notOrdered = 0;
- var notReversed = 0;
- for(var i = 1; i < array.length; i++) {
- var pairOrder = sortFn(array[i], array[i - 1]);
- if(pairOrder < 0) notOrdered = 1;
- else if(pairOrder > 0) notReversed = 1;
- if(notOrdered && notReversed) return array.sort(sortFn);
- }
- return notReversed ? array : array.reverse();
- };
-
- /**
- * find index in array 'arr' that minimizes 'fn'
- *
- * @param {array} arr : array where to search
- * @param {fn (optional)} fn : function to minimize,
- * if not given, fn is the identity function
- * @return {integer}
- */
- exports.findIndexOfMin = function(arr, fn) {
- fn = fn || identity;
-
- var min = Infinity;
- var ind;
-
- for(var i = 0; i < arr.length; i++) {
- var v = fn(arr[i]);
- if(v < min) {
- min = v;
- ind = i;
- }
- }
- return ind;
- };
-
- },{"./identity":167,"./loggers":172,"fast-isnumeric":18}],187:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- // works with our CSS cursor classes (see css/_cursor.scss)
- // to apply cursors to d3 single-element selections.
- // omit cursor to revert to the default.
- module.exports = function setCursor(el3, csr) {
- (el3.attr('class') || '').split(' ').forEach(function(cls) {
- if(cls.indexOf('cursor-') === 0) el3.classed(cls, false);
- });
-
- if(csr) el3.classed('cursor-' + csr, true);
- };
-
- },{}],188:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
-
- /**
- * aggNums() returns the result of an aggregate function applied to an array of
- * values, where non-numerical values have been tossed out.
- *
- * @param {function} f - aggregation function (e.g., Math.min)
- * @param {Number} v - initial value (continuing from previous calls)
- * if there's no continuing value, use null for selector-type
- * functions (max,min), or 0 for summations
- * @param {Array} a - array to aggregate (may be nested, we will recurse,
- * but all elements must have the same dimension)
- * @param {Number} len - maximum length of a to aggregate
- * @return {Number} - result of f applied to a starting from v
- */
- exports.aggNums = function(f, v, a, len) {
- var i,
- b;
- if(!len || len > a.length) len = a.length;
- if(!isNumeric(v)) v = false;
- if(isArrayOrTypedArray(a[0])) {
- b = new Array(len);
- for(i = 0; i < len; i++) b[i] = exports.aggNums(f, v, a[i]);
- a = b;
- }
-
- for(i = 0; i < len; i++) {
- if(!isNumeric(v)) v = a[i];
- else if(isNumeric(a[i])) v = f(+v, +a[i]);
- }
- return v;
- };
-
- /**
- * mean & std dev functions using aggNums, so it handles non-numerics nicely
- * even need to use aggNums instead of .length, to toss out non-numerics
- */
- exports.len = function(data) {
- return exports.aggNums(function(a) { return a + 1; }, 0, data);
- };
-
- exports.mean = function(data, len) {
- if(!len) len = exports.len(data);
- return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
- };
-
- exports.midRange = function(numArr) {
- if(numArr === undefined || numArr.length === 0) return undefined;
- return (exports.aggNums(Math.max, null, numArr) + exports.aggNums(Math.min, null, numArr)) / 2;
- };
-
- exports.variance = function(data, len, mean) {
- if(!len) len = exports.len(data);
- if(!isNumeric(mean)) mean = exports.mean(data, len);
-
- return exports.aggNums(function(a, b) {
- return a + Math.pow(b - mean, 2);
- }, 0, data) / len;
- };
-
- exports.stdev = function(data, len, mean) {
- return Math.sqrt(exports.variance(data, len, mean));
- };
-
- /**
- * interp() computes a percentile (quantile) for a given distribution.
- * We interpolate the distribution (to compute quantiles, we follow method #10 here:
- * http://www.amstat.org/publications/jse/v14n3/langford.html).
- * Typically the index or rank (n * arr.length) may be non-integer.
- * For reference: ends are clipped to the extreme values in the array;
- * For box plots: index you get is half a point too high (see
- * http://en.wikipedia.org/wiki/Percentile#Nearest_rank) but note that this definition
- * indexes from 1 rather than 0, so we subtract 1/2 (instead of add).
- *
- * @param {Array} arr - This array contains the values that make up the distribution.
- * @param {Number} n - Between 0 and 1, n = p/100 is such that we compute the p^th percentile.
- * For example, the 50th percentile (or median) corresponds to n = 0.5
- * @return {Number} - percentile
- */
- exports.interp = function(arr, n) {
- if(!isNumeric(n)) throw 'n should be a finite number';
- n = n * arr.length - 0.5;
- if(n < 0) return arr[0];
- if(n > arr.length - 1) return arr[arr.length - 1];
- var frac = n % 1;
- return frac * arr[Math.ceil(n)] + (1 - frac) * arr[Math.floor(n)];
- };
-
- },{"./array":155,"fast-isnumeric":18}],189:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- /* global MathJax:false */
-
- var d3 = _dereq_('d3');
-
- var Lib = _dereq_('../lib');
- var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
- var LINE_SPACING = _dereq_('../constants/alignment').LINE_SPACING;
-
- // text converter
-
- function getSize(_selection, _dimension) {
- return _selection.node().getBoundingClientRect()[_dimension];
- }
-
- var FIND_TEX = /([^$]*)([$]+[^$]*[$]+)([^$]*)/;
-
- exports.convertToTspans = function(_context, gd, _callback) {
- var str = _context.text();
-
- // Until we get tex integrated more fully (so it can be used along with non-tex)
- // allow some elements to prohibit it by attaching 'data-notex' to the original
- var tex = (!_context.attr('data-notex')) &&
- (typeof MathJax !== 'undefined') &&
- str.match(FIND_TEX);
-
- var parent = d3.select(_context.node().parentNode);
- if(parent.empty()) return;
- var svgClass = (_context.attr('class')) ? _context.attr('class').split(' ')[0] : 'text';
- svgClass += '-math';
- parent.selectAll('svg.' + svgClass).remove();
- parent.selectAll('g.' + svgClass + '-group').remove();
- _context.style('display', null)
- .attr({
- // some callers use data-unformatted *from the <text> element* in 'cancel'
- // so we need it here even if we're going to turn it into math
- // these two (plus style and text-anchor attributes) form the key we're
- // going to use for Drawing.bBox
- 'data-unformatted': str,
- 'data-math': 'N'
- });
-
- function showText() {
- if(!parent.empty()) {
- svgClass = _context.attr('class') + '-math';
- parent.select('svg.' + svgClass).remove();
- }
- _context.text('')
- .style('white-space', 'pre');
-
- var hasLink = buildSVGText(_context.node(), str);
-
- if(hasLink) {
- // at least in Chrome, pointer-events does not seem
- // to be honored in children of <text> elements
- // so if we have an anchor, we have to make the
- // whole element respond
- _context.style('pointer-events', 'all');
- }
-
- exports.positionText(_context);
-
- if(_callback) _callback.call(_context);
- }
-
- if(tex) {
- ((gd && gd._promises) || []).push(new Promise(function(resolve) {
- _context.style('display', 'none');
- var fontSize = parseInt(_context.node().style.fontSize, 10);
- var config = {fontSize: fontSize};
-
- texToSVG(tex[2], config, function(_svgEl, _glyphDefs, _svgBBox) {
- parent.selectAll('svg.' + svgClass).remove();
- parent.selectAll('g.' + svgClass + '-group').remove();
-
- var newSvg = _svgEl && _svgEl.select('svg');
- if(!newSvg || !newSvg.node()) {
- showText();
- resolve();
- return;
- }
-
- var mathjaxGroup = parent.append('g')
- .classed(svgClass + '-group', true)
- .attr({
- 'pointer-events': 'none',
- 'data-unformatted': str,
- 'data-math': 'Y'
- });
-
- mathjaxGroup.node().appendChild(newSvg.node());
-
- // stitch the glyph defs
- if(_glyphDefs && _glyphDefs.node()) {
- newSvg.node().insertBefore(_glyphDefs.node().cloneNode(true),
- newSvg.node().firstChild);
- }
-
- newSvg.attr({
- 'class': svgClass,
- height: _svgBBox.height,
- preserveAspectRatio: 'xMinYMin meet'
- })
- .style({overflow: 'visible', 'pointer-events': 'none'});
-
- var fill = _context.node().style.fill || 'black';
- newSvg.select('g').attr({fill: fill, stroke: fill});
-
- var newSvgW = getSize(newSvg, 'width');
- var newSvgH = getSize(newSvg, 'height');
- var newX = +_context.attr('x') - newSvgW *
- {start: 0, middle: 0.5, end: 1}[_context.attr('text-anchor') || 'start'];
- // font baseline is about 1/4 fontSize below centerline
- var textHeight = fontSize || getSize(_context, 'height');
- var dy = -textHeight / 4;
-
- if(svgClass[0] === 'y') {
- mathjaxGroup.attr({
- transform: 'rotate(' + [-90, +_context.attr('x'), +_context.attr('y')] +
- ') translate(' + [-newSvgW / 2, dy - newSvgH / 2] + ')'
- });
- newSvg.attr({x: +_context.attr('x'), y: +_context.attr('y')});
- }
- else if(svgClass[0] === 'l') {
- newSvg.attr({x: _context.attr('x'), y: dy - (newSvgH / 2)});
- }
- else if(svgClass[0] === 'a' && svgClass.indexOf('atitle') !== 0) {
- newSvg.attr({x: 0, y: dy});
- }
- else {
- newSvg.attr({x: newX, y: (+_context.attr('y') + dy - newSvgH / 2)});
- }
-
- if(_callback) _callback.call(_context, mathjaxGroup);
- resolve(mathjaxGroup);
- });
- }));
- }
- else showText();
-
- return _context;
- };
-
-
- // MathJax
-
- var LT_MATCH = /(<|<|<)/g;
- var GT_MATCH = /(>|>|>)/g;
-
- function cleanEscapesForTex(s) {
- return s.replace(LT_MATCH, '\\lt ')
- .replace(GT_MATCH, '\\gt ');
- }
-
- function texToSVG(_texString, _config, _callback) {
-
- var originalRenderer,
- originalConfig,
- originalProcessSectionDelay,
- tmpDiv;
-
- MathJax.Hub.Queue(
- function() {
- originalConfig = Lib.extendDeepAll({}, MathJax.Hub.config);
-
- originalProcessSectionDelay = MathJax.Hub.processSectionDelay;
- if(MathJax.Hub.processSectionDelay !== undefined) {
- // MathJax 2.5+
- MathJax.Hub.processSectionDelay = 0;
- }
-
- return MathJax.Hub.Config({
- messageStyle: 'none',
- tex2jax: {
- inlineMath: [['$', '$'], ['\\(', '\\)']]
- },
- displayAlign: 'left',
- });
- },
- function() {
- // Get original renderer
- originalRenderer = MathJax.Hub.config.menuSettings.renderer;
- if(originalRenderer !== 'SVG') {
- return MathJax.Hub.setRenderer('SVG');
- }
- },
- function() {
- var randomID = 'math-output-' + Lib.randstr({}, 64);
- tmpDiv = d3.select('body').append('div')
- .attr({id: randomID})
- .style({visibility: 'hidden', position: 'absolute'})
- .style({'font-size': _config.fontSize + 'px'})
- .text(cleanEscapesForTex(_texString));
-
- return MathJax.Hub.Typeset(tmpDiv.node());
- },
- function() {
- var glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs');
-
- if(tmpDiv.select('.MathJax_SVG').empty() || !tmpDiv.select('svg').node()) {
- Lib.log('There was an error in the tex syntax.', _texString);
- _callback();
- }
- else {
- var svgBBox = tmpDiv.select('svg').node().getBoundingClientRect();
- _callback(tmpDiv.select('.MathJax_SVG'), glyphDefs, svgBBox);
- }
-
- tmpDiv.remove();
-
- if(originalRenderer !== 'SVG') {
- return MathJax.Hub.setRenderer(originalRenderer);
- }
- },
- function() {
- if(originalProcessSectionDelay !== undefined) {
- MathJax.Hub.processSectionDelay = originalProcessSectionDelay;
- }
- return MathJax.Hub.Config(originalConfig);
- });
- }
-
- var TAG_STYLES = {
- // would like to use baseline-shift for sub/sup but FF doesn't support it
- // so we need to use dy along with the uber hacky shift-back-to
- // baseline below
- sup: 'font-size:70%',
- sub: 'font-size:70%',
- b: 'font-weight:bold',
- i: 'font-style:italic',
- a: 'cursor:pointer',
- span: '',
- em: 'font-style:italic;font-weight:bold'
- };
-
- // baseline shifts for sub and sup
- var SHIFT_DY = {
- sub: '0.3em',
- sup: '-0.6em'
- };
- // reset baseline by adding a tspan (empty except for a zero-width space)
- // with dy of -70% * SHIFT_DY (because font-size=70%)
- var RESET_DY = {
- sub: '-0.21em',
- sup: '0.42em'
- };
- var ZERO_WIDTH_SPACE = '\u200b';
-
- /*
- * Whitelist of protocols in user-supplied urls. Mostly we want to avoid javascript
- * and related attack vectors. The empty items are there for IE, that in various
- * versions treats relative paths as having different flavors of no protocol, while
- * other browsers have these explicitly inherit the protocol of the page they're in.
- */
- var PROTOCOLS = ['http:', 'https:', 'mailto:', '', undefined, ':'];
-
- var NEWLINES = /(\r\n?|\n)/g;
-
- var SPLIT_TAGS = /(<[^<>]*>)/;
-
- var ONE_TAG = /<(\/?)([^ >]*)(\s+(.*))?>/i;
-
- var BR_TAG = /<br(\s+.*)?>/i;
-
- /*
- * style and href: pull them out of either single or double quotes. Also
- * - target: (_blank|_self|_parent|_top|framename)
- * note that you can't use target to get a popup but if you use popup,
- * a `framename` will be passed along as the name of the popup window.
- * per the spec, cannot contain whitespace.
- * for backward compatibility we default to '_blank'
- * - popup: a custom one for us to enable popup (new window) links. String
- * for window.open -> strWindowFeatures, like 'menubar=yes,width=500,height=550'
- * note that at least in Chrome, you need to give at least one property
- * in this string or the page will open in a new tab anyway. We follow this
- * convention and will not make a popup if this string is empty.
- * per the spec, cannot contain whitespace.
- *
- * Because we hack in other attributes with style (sub & sup), drop any trailing
- * semicolon in user-supplied styles so we can consistently append the tag-dependent style
- *
- * These are for tag attributes; Chrome anyway will convert entities in
- * attribute values, but not in attribute names
- * you can test this by for example:
- * > p = document.createElement('p')
- * > p.innerHTML = '<span style="font-color:red;">Hi</span>'
- * > p.innerHTML
- * <- '<span style="font-color:red;">Hi</span>'
- */
- var STYLEMATCH = /(^|[\s"'])style\s*=\s*("([^"]*);?"|'([^']*);?')/i;
- var HREFMATCH = /(^|[\s"'])href\s*=\s*("([^"]*)"|'([^']*)')/i;
- var TARGETMATCH = /(^|[\s"'])target\s*=\s*("([^"\s]*)"|'([^'\s]*)')/i;
- var POPUPMATCH = /(^|[\s"'])popup\s*=\s*("([\w=,]*)"|'([\w=,]*)')/i;
-
- // dedicated matcher for these quoted regexes, that can return their results
- // in two different places
- function getQuotedMatch(_str, re) {
- if(!_str) return null;
- var match = _str.match(re);
- var result = match && (match[3] || match[4]);
- return result && convertEntities(result);
- }
-
- var COLORMATCH = /(^|;)\s*color:/;
-
- /**
- * Strip string of tags
- *
- * @param {string} _str : input string
- * @param {object} opts :
- * - maxLen {number} max length of output string
- * - allowedTags {array} list of pseudo-html tags to NOT strip
- * @return {string}
- */
- exports.plainText = function(_str, opts) {
- opts = opts || {};
-
- var len = (opts.len !== undefined && opts.len !== -1) ? opts.len : Infinity;
- var allowedTags = opts.allowedTags !== undefined ? opts.allowedTags : ['br'];
-
- var ellipsis = '...';
- var eLen = ellipsis.length;
-
- var oldParts = _str.split(SPLIT_TAGS);
- var newParts = [];
- var prevTag = '';
- var l = 0;
-
- for(var i = 0; i < oldParts.length; i++) {
- var p = oldParts[i];
- var match = p.match(ONE_TAG);
- var tagType = match && match[2].toLowerCase();
-
- if(tagType) {
- // N.B. tags do not count towards string length
- if(allowedTags.indexOf(tagType) !== -1) {
- newParts.push(p);
- prevTag = tagType;
- }
- } else {
- var pLen = p.length;
-
- if((l + pLen) < len) {
- newParts.push(p);
- l += pLen;
- } else if(l < len) {
- var pLen2 = len - l;
-
- if(prevTag && (prevTag !== 'br' || pLen2 <= eLen || pLen <= eLen)) {
- newParts.pop();
- }
-
- if(len > eLen) {
- newParts.push(p.substr(0, pLen2 - eLen) + ellipsis);
- } else {
- newParts.push(p.substr(0, pLen2));
- }
- break;
- }
-
- prevTag = '';
- }
- }
-
- return newParts.join('');
- };
-
- /*
- * N.B. HTML entities are listed without the leading '&' and trailing ';'
- * https://www.freeformatter.com/html-entities.html
- *
- * FWIW if we wanted to support the full set, it has 2261 entries:
- * https://www.w3.org/TR/html5/entities.json
- * though I notice that some of these are duplicates and/or are missing ";"
- * eg: "&", "&", "&", and "&" all map to "&"
- * We no longer need to include numeric entities here, these are now handled
- * by String.fromCodePoint/fromCharCode
- *
- * Anyway the only ones that are really important to allow are the HTML special
- * chars <, >, and &, because these ones can trigger special processing if not
- * replaced by the corresponding entity.
- */
- var entityToUnicode = {
- mu: 'μ',
- amp: '&',
- lt: '<',
- gt: '>',
- nbsp: ' ',
- times: '×',
- plusmn: '±',
- deg: '°'
- };
-
- // NOTE: in general entities can contain uppercase too (so [a-zA-Z]) but all the
- // ones we support use only lowercase. If we ever change that, update the regex.
- var ENTITY_MATCH = /&(#\d+|#x[\da-fA-F]+|[a-z]+);/g;
- function convertEntities(_str) {
- return _str.replace(ENTITY_MATCH, function(fullMatch, innerMatch) {
- var outChar;
- if(innerMatch.charAt(0) === '#') {
- // cannot use String.fromCodePoint in IE
- outChar = fromCodePoint(
- innerMatch.charAt(1) === 'x' ?
- parseInt(innerMatch.substr(2), 16) :
- parseInt(innerMatch.substr(1), 10)
- );
- }
- else outChar = entityToUnicode[innerMatch];
-
- // as in regular HTML, if we didn't decode the entity just
- // leave the raw text in place.
- return outChar || fullMatch;
- });
- }
- exports.convertEntities = convertEntities;
-
- function fromCodePoint(code) {
- // Don't allow overflow. In Chrome this turns into � but I feel like it's
- // more useful to just not convert it at all.
- if(code > 0x10FFFF) return;
- var stringFromCodePoint = String.fromCodePoint;
- if(stringFromCodePoint) return stringFromCodePoint(code);
-
- // IE doesn't have String.fromCodePoint
- // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint
- var stringFromCharCode = String.fromCharCode;
- if(code <= 0xFFFF) return stringFromCharCode(code);
- return stringFromCharCode(
- (code >> 10) + 0xD7C0,
- (code % 0x400) + 0xDC00
- );
- }
-
- /*
- * buildSVGText: convert our pseudo-html into SVG tspan elements, and attach these
- * to containerNode
- *
- * @param {svg text element} containerNode: the <text> node to insert this text into
- * @param {string} str: the pseudo-html string to convert to svg
- *
- * @returns {bool}: does the result contain any links? We need to handle the text element
- * somewhat differently if it does, so just keep track of this when it happens.
- */
- function buildSVGText(containerNode, str) {
- /*
- * Normalize behavior between IE and others wrt newlines and whitespace:pre
- * this combination makes IE barf https://github.com/plotly/plotly.js/issues/746
- * Chrome and FF display \n, \r, or \r\n as a space in this mode.
- * I feel like at some point we turned these into <br> but currently we don't so
- * I'm just going to cement what we do now in Chrome and FF
- */
- str = str.replace(NEWLINES, ' ');
-
- var hasLink = false;
-
- // as we're building the text, keep track of what elements we're nested inside
- // nodeStack will be an array of {node, type, style, href, target, popup}
- // where only type: 'a' gets the last 3 and node is only added when it's created
- var nodeStack = [];
- var currentNode;
- var currentLine = -1;
-
- function newLine() {
- currentLine++;
-
- var lineNode = document.createElementNS(xmlnsNamespaces.svg, 'tspan');
- d3.select(lineNode).attr({
- class: 'line',
- dy: (currentLine * LINE_SPACING) + 'em'
- });
- containerNode.appendChild(lineNode);
-
- currentNode = lineNode;
-
- var oldNodeStack = nodeStack;
- nodeStack = [{node: lineNode}];
-
- if(oldNodeStack.length > 1) {
- for(var i = 1; i < oldNodeStack.length; i++) {
- enterNode(oldNodeStack[i]);
- }
- }
- }
-
- function enterNode(nodeSpec) {
- var type = nodeSpec.type;
- var nodeAttrs = {};
- var nodeType;
-
- if(type === 'a') {
- nodeType = 'a';
- var target = nodeSpec.target;
- var href = nodeSpec.href;
- var popup = nodeSpec.popup;
- if(href) {
- nodeAttrs = {
- 'xlink:xlink:show': (target === '_blank' || target.charAt(0) !== '_') ? 'new' : 'replace',
- target: target,
- 'xlink:xlink:href': href
- };
- if(popup) {
- // security: href and target are not inserted as code but
- // as attributes. popup is, but limited to /[A-Za-z0-9_=,]/
- nodeAttrs.onclick = 'window.open(this.href.baseVal,this.target.baseVal,"' +
- popup + '");return false;';
- }
- }
- }
- else nodeType = 'tspan';
-
- if(nodeSpec.style) nodeAttrs.style = nodeSpec.style;
-
- var newNode = document.createElementNS(xmlnsNamespaces.svg, nodeType);
-
- if(type === 'sup' || type === 'sub') {
- addTextNode(currentNode, ZERO_WIDTH_SPACE);
- currentNode.appendChild(newNode);
-
- var resetter = document.createElementNS(xmlnsNamespaces.svg, 'tspan');
- addTextNode(resetter, ZERO_WIDTH_SPACE);
- d3.select(resetter).attr('dy', RESET_DY[type]);
- nodeAttrs.dy = SHIFT_DY[type];
-
- currentNode.appendChild(newNode);
- currentNode.appendChild(resetter);
- }
- else {
- currentNode.appendChild(newNode);
- }
-
- d3.select(newNode).attr(nodeAttrs);
-
- currentNode = nodeSpec.node = newNode;
- nodeStack.push(nodeSpec);
- }
-
- function addTextNode(node, text) {
- node.appendChild(document.createTextNode(text));
- }
-
- function exitNode(type) {
- // A bare closing tag can't close the root node. If we encounter this it
- // means there's an extra closing tag that can just be ignored:
- if(nodeStack.length === 1) {
- Lib.log('Ignoring unexpected end tag </' + type + '>.', str);
- return;
- }
-
- var innerNode = nodeStack.pop();
-
- if(type !== innerNode.type) {
- Lib.log('Start tag <' + innerNode.type + '> doesnt match end tag <' +
- type + '>. Pretending it did match.', str);
- }
- currentNode = nodeStack[nodeStack.length - 1].node;
- }
-
- var hasLines = BR_TAG.test(str);
-
- if(hasLines) newLine();
- else {
- currentNode = containerNode;
- nodeStack = [{node: containerNode}];
- }
-
- var parts = str.split(SPLIT_TAGS);
- for(var i = 0; i < parts.length; i++) {
- var parti = parts[i];
- var match = parti.match(ONE_TAG);
- var tagType = match && match[2].toLowerCase();
- var tagStyle = TAG_STYLES[tagType];
-
- if(tagType === 'br') {
- newLine();
- }
- else if(tagStyle === undefined) {
- addTextNode(currentNode, convertEntities(parti));
- }
- else {
- // tag - open or close
- if(match[1]) {
- exitNode(tagType);
- }
- else {
- var extra = match[4];
-
- var nodeSpec = {type: tagType};
-
- // now add style, from both the tag name and any extra css
- // Most of the svg css that users will care about is just like html,
- // but font color is different (uses fill). Let our users ignore this.
- var css = getQuotedMatch(extra, STYLEMATCH);
- if(css) {
- css = css.replace(COLORMATCH, '$1 fill:');
- if(tagStyle) css += ';' + tagStyle;
- }
- else if(tagStyle) css = tagStyle;
-
- if(css) nodeSpec.style = css;
-
- if(tagType === 'a') {
- hasLink = true;
-
- var href = getQuotedMatch(extra, HREFMATCH);
-
- if(href) {
- // check safe protocols
- var dummyAnchor = document.createElement('a');
- dummyAnchor.href = href;
- if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) {
- // Decode href to allow both already encoded and not encoded
- // URIs. Without decoding prior encoding, an already encoded
- // URI would be encoded twice producing a semantically different URI.
- nodeSpec.href = encodeURI(decodeURI(href));
- nodeSpec.target = getQuotedMatch(extra, TARGETMATCH) || '_blank';
- nodeSpec.popup = getQuotedMatch(extra, POPUPMATCH);
- }
- }
- }
-
- enterNode(nodeSpec);
- }
- }
- }
-
- return hasLink;
- }
-
- exports.lineCount = function lineCount(s) {
- return s.selectAll('tspan.line').size() || 1;
- };
-
- exports.positionText = function positionText(s, x, y) {
- return s.each(function() {
- var text = d3.select(this);
-
- function setOrGet(attr, val) {
- if(val === undefined) {
- val = text.attr(attr);
- if(val === null) {
- text.attr(attr, 0);
- val = 0;
- }
- }
- else text.attr(attr, val);
- return val;
- }
-
- var thisX = setOrGet('x', x);
- var thisY = setOrGet('y', y);
-
- if(this.nodeName === 'text') {
- text.selectAll('tspan.line').attr({x: thisX, y: thisY});
- }
- });
- };
-
- function alignHTMLWith(_base, container, options) {
- var alignH = options.horizontalAlign;
- var alignV = options.verticalAlign || 'top';
- var bRect = _base.node().getBoundingClientRect();
- var cRect = container.node().getBoundingClientRect();
- var thisRect;
- var getTop;
- var getLeft;
-
- if(alignV === 'bottom') {
- getTop = function() { return bRect.bottom - thisRect.height; };
- } else if(alignV === 'middle') {
- getTop = function() { return bRect.top + (bRect.height - thisRect.height) / 2; };
- } else { // default: top
- getTop = function() { return bRect.top; };
- }
-
- if(alignH === 'right') {
- getLeft = function() { return bRect.right - thisRect.width; };
- } else if(alignH === 'center') {
- getLeft = function() { return bRect.left + (bRect.width - thisRect.width) / 2; };
- } else { // default: left
- getLeft = function() { return bRect.left; };
- }
-
- return function() {
- thisRect = this.node().getBoundingClientRect();
- this.style({
- top: (getTop() - cRect.top) + 'px',
- left: (getLeft() - cRect.left) + 'px',
- 'z-index': 1000
- });
- return this;
- };
- }
-
- /*
- * Editable title
- * @param {d3.selection} context: the element being edited. Normally text,
- * but if it isn't, you should provide the styling options
- * @param {object} options:
- * @param {div} options.gd: graphDiv
- * @param {d3.selection} options.delegate: item to bind events to if not this
- * @param {boolean} options.immediate: start editing now (true) or on click (false, default)
- * @param {string} options.fill: font color if not as shown
- * @param {string} options.background: background color if not as shown
- * @param {string} options.text: initial text, if not as shown
- * @param {string} options.horizontalAlign: alignment of the edit box wrt. the bound element
- * @param {string} options.verticalAlign: alignment of the edit box wrt. the bound element
- */
-
- exports.makeEditable = function(context, options) {
- var gd = options.gd;
- var _delegate = options.delegate;
- var dispatch = d3.dispatch('edit', 'input', 'cancel');
- var handlerElement = _delegate || context;
-
- context.style({'pointer-events': _delegate ? 'none' : 'all'});
-
- if(context.size() !== 1) throw new Error('boo');
-
- function handleClick() {
- appendEditable();
- context.style({opacity: 0});
- // also hide any mathjax svg
- var svgClass = handlerElement.attr('class');
- var mathjaxClass;
- if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group';
- else mathjaxClass = '[class*=-math-group]';
- if(mathjaxClass) {
- d3.select(context.node().parentNode).select(mathjaxClass).style({opacity: 0});
- }
- }
-
- function selectElementContents(_el) {
- var el = _el.node();
- var range = document.createRange();
- range.selectNodeContents(el);
- var sel = window.getSelection();
- sel.removeAllRanges();
- sel.addRange(range);
- el.focus();
- }
-
- function appendEditable() {
- var plotDiv = d3.select(gd);
- var container = plotDiv.select('.svg-container');
- var div = container.append('div');
- var cStyle = context.node().style;
- var fontSize = parseFloat(cStyle.fontSize || 12);
-
- var initialText = options.text;
- if(initialText === undefined) initialText = context.attr('data-unformatted');
-
- div.classed('plugin-editable editable', true)
- .style({
- position: 'absolute',
- 'font-family': cStyle.fontFamily || 'Arial',
- 'font-size': fontSize,
- color: options.fill || cStyle.fill || 'black',
- opacity: 1,
- 'background-color': options.background || 'transparent',
- outline: '#ffffff33 1px solid',
- margin: [-fontSize / 8 + 1, 0, 0, -1].join('px ') + 'px',
- padding: '0',
- 'box-sizing': 'border-box'
- })
- .attr({contenteditable: true})
- .text(initialText)
- .call(alignHTMLWith(context, container, options))
- .on('blur', function() {
- gd._editing = false;
- context.text(this.textContent)
- .style({opacity: 1});
- var svgClass = d3.select(this).attr('class');
- var mathjaxClass;
- if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group';
- else mathjaxClass = '[class*=-math-group]';
- if(mathjaxClass) {
- d3.select(context.node().parentNode).select(mathjaxClass).style({opacity: 0});
- }
- var text = this.textContent;
- d3.select(this).transition().duration(0).remove();
- d3.select(document).on('mouseup', null);
- dispatch.edit.call(context, text);
- })
- .on('focus', function() {
- var editDiv = this;
- gd._editing = true;
- d3.select(document).on('mouseup', function() {
- if(d3.event.target === editDiv) return false;
- if(document.activeElement === div.node()) div.node().blur();
- });
- })
- .on('keyup', function() {
- if(d3.event.which === 27) {
- gd._editing = false;
- context.style({opacity: 1});
- d3.select(this)
- .style({opacity: 0})
- .on('blur', function() { return false; })
- .transition().remove();
- dispatch.cancel.call(context, this.textContent);
- }
- else {
- dispatch.input.call(context, this.textContent);
- d3.select(this).call(alignHTMLWith(context, container, options));
- }
- })
- .on('keydown', function() {
- if(d3.event.which === 13) this.blur();
- })
- .call(selectElementContents);
- }
-
- if(options.immediate) handleClick();
- else handlerElement.on('click', handleClick);
-
- return d3.rebind(context, dispatch, 'on');
- };
-
- },{"../constants/alignment":146,"../constants/xmlns_namespaces":150,"../lib":168,"d3":16}],190:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var timerCache = {};
-
- /**
- * Throttle a callback. `callback` executes synchronously only if
- * more than `minInterval` milliseconds have already elapsed since the latest
- * call (if any). Otherwise we wait until `minInterval` is over and execute the
- * last callback received while waiting.
- * So the first and last events in a train are always executed (eventually)
- * but some of the events in the middle can be dropped.
- *
- * @param {string} id: an identifier to mark events to throttle together
- * @param {number} minInterval: minimum time, in milliseconds, between
- * invocations of `callback`
- * @param {function} callback: the function to throttle. `callback` itself
- * should be a purely synchronous function.
- */
- exports.throttle = function throttle(id, minInterval, callback) {
- var cache = timerCache[id];
- var now = Date.now();
-
- if(!cache) {
- /*
- * Throw out old items before making a new one, to prevent the cache
- * getting overgrown, for example from old plots that have been replaced.
- * 1 minute age is arbitrary.
- */
- for(var idi in timerCache) {
- if(timerCache[idi].ts < now - 60000) {
- delete timerCache[idi];
- }
- }
- cache = timerCache[id] = {ts: 0, timer: null};
- }
-
- _clearTimeout(cache);
-
- function exec() {
- callback();
- cache.ts = Date.now();
- if(cache.onDone) {
- cache.onDone();
- cache.onDone = null;
- }
- }
-
- if(now > cache.ts + minInterval) {
- exec();
- return;
- }
-
- cache.timer = setTimeout(function() {
- exec();
- cache.timer = null;
- }, minInterval);
- };
-
- exports.done = function(id) {
- var cache = timerCache[id];
- if(!cache || !cache.timer) return Promise.resolve();
-
- return new Promise(function(resolve) {
- var previousOnDone = cache.onDone;
- cache.onDone = function onDone() {
- if(previousOnDone) previousOnDone();
- resolve();
- cache.onDone = null;
- };
- });
- };
-
- /**
- * Clear the throttle cache for one or all timers
- * @param {optional string} id:
- * if provided, clear just this timer
- * if omitted, clear all timers (mainly useful for testing)
- */
- exports.clear = function(id) {
- if(id) {
- _clearTimeout(timerCache[id]);
- delete timerCache[id];
- }
- else {
- for(var idi in timerCache) exports.clear(idi);
- }
- };
-
- function _clearTimeout(cache) {
- if(cache && cache.timer !== null) {
- clearTimeout(cache.timer);
- cache.timer = null;
- }
- }
-
- },{}],191:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- /**
- * convert a linear value into a logged value, folding negative numbers into
- * the given range
- */
- module.exports = function toLogRange(val, range) {
- if(val > 0) return Math.log(val) / Math.LN10;
-
- // move a negative value reference to a log axis - just put the
- // result at the lowest range value on the plot (or if the range also went negative,
- // one millionth of the top of the range)
- var newVal = Math.log(Math.min(range[0], range[1])) / Math.LN10;
- if(!isNumeric(newVal)) newVal = Math.log(Math.max(range[0], range[1])) / Math.LN10 - 6;
- return newVal;
- };
-
- },{"fast-isnumeric":18}],192:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- moduleType: 'locale',
- name: 'en-US',
- dictionary: {
- 'Click to enter Colorscale title': 'Click to enter Colorscale title'
- },
- format: {
- date: '%m/%d/%Y'
- }
- };
-
- },{}],193:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- moduleType: 'locale',
- name: 'en',
- dictionary: {
- 'Click to enter Colorscale title': 'Click to enter Colourscale title'
- },
- format: {
- days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
- shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
- months: [
- 'January', 'February', 'March', 'April', 'May', 'June',
- 'July', 'August', 'September', 'October', 'November', 'December'
- ],
- shortMonths: [
- 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
- ],
- periods: ['AM', 'PM'],
- dateTime: '%a %b %e %X %Y',
- date: '%d/%m/%Y',
- time: '%H:%M:%S',
- decimal: '.',
- thousands: ',',
- grouping: [3],
- currency: ['$', ''],
- year: '%Y',
- month: '%b %Y',
- dayMonth: '%b %-d',
- dayMonthYear: '%b %-d, %Y'
- }
- };
-
- },{}],194:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../registry');
-
- /*
- * containerArrayMatch: does this attribute string point into a
- * layout container array?
- *
- * @param {String} astr: an attribute string, like *annotations[2].text*
- *
- * @returns {Object | false} Returns false if `astr` doesn't match a container
- * array. If it does, returns:
- * {array: {String}, index: {Number}, property: {String}}
- * ie the attribute string for the array, the index within the array (or ''
- * if the whole array) and the property within that (or '' if the whole array
- * or the whole object)
- */
- module.exports = function containerArrayMatch(astr) {
- var rootContainers = Registry.layoutArrayContainers;
- var regexpContainers = Registry.layoutArrayRegexes;
- var rootPart = astr.split('[')[0];
- var arrayStr;
- var match;
-
- // look for regexp matches first, because they may be nested inside root matches
- // eg updatemenus[i].buttons is nested inside updatemenus
- for(var i = 0; i < regexpContainers.length; i++) {
- match = astr.match(regexpContainers[i]);
- if(match && match.index === 0) {
- arrayStr = match[0];
- break;
- }
- }
-
- // now look for root matches
- if(!arrayStr) arrayStr = rootContainers[rootContainers.indexOf(rootPart)];
-
- if(!arrayStr) return false;
-
- var tail = astr.substr(arrayStr.length);
- if(!tail) return {array: arrayStr, index: '', property: ''};
-
- match = tail.match(/^\[(0|[1-9][0-9]*)\](\.(.+))?$/);
- if(!match) return false;
-
- return {array: arrayStr, index: Number(match[1]), property: match[3] || ''};
- };
-
- },{"../registry":257}],195:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../lib');
- var extendFlat = Lib.extendFlat;
- var isPlainObject = Lib.isPlainObject;
-
- var traceOpts = {
- valType: 'flaglist',
- extras: ['none'],
- flags: ['calc', 'clearAxisTypes', 'plot', 'style', 'markerSize', 'colorbars'],
-
- };
-
- var layoutOpts = {
- valType: 'flaglist',
- extras: ['none'],
- flags: [
- 'calc', 'plot', 'legend', 'ticks', 'axrange',
- 'layoutstyle', 'modebar', 'camera', 'arraydraw'
- ],
-
- };
-
- // flags for inside restyle/relayout include a few extras
- // that shouldn't be used in attributes, to deal with certain
- // combinations and conditionals efficiently
- var traceEditTypeFlags = traceOpts.flags.slice()
- .concat(['fullReplot']);
-
- var layoutEditTypeFlags = layoutOpts.flags.slice()
- .concat('layoutReplot');
-
- module.exports = {
- traces: traceOpts,
- layout: layoutOpts,
- /*
- * default (all false) edit flags for restyle (traces)
- * creates a new object each call, so the caller can mutate freely
- */
- traceFlags: function() { return falseObj(traceEditTypeFlags); },
-
- /*
- * default (all false) edit flags for relayout
- * creates a new object each call, so the caller can mutate freely
- */
- layoutFlags: function() { return falseObj(layoutEditTypeFlags); },
-
- /*
- * update `flags` with the `editType` values found in `attr`
- */
- update: function(flags, attr) {
- var editType = attr.editType;
- if(editType && editType !== 'none') {
- var editTypeParts = editType.split('+');
- for(var i = 0; i < editTypeParts.length; i++) {
- flags[editTypeParts[i]] = true;
- }
- }
- },
-
- overrideAll: overrideAll
- };
-
- function falseObj(keys) {
- var out = {};
- for(var i = 0; i < keys.length; i++) out[keys[i]] = false;
- return out;
- }
-
- /**
- * For attributes that are largely copied from elsewhere into a plot type that doesn't
- * support partial redraws - overrides the editType field of all attributes in the object
- *
- * @param {object} attrs: the attributes to override. Will not be mutated.
- * @param {string} editTypeOverride: the new editType to use
- * @param {'nested'|'from-root'} overrideContainers:
- * - 'nested' will override editType for nested containers but not the root.
- * - 'from-root' will also override editType of the root container.
- * Containers below the absolute top level (trace or layout root) DO need an
- * editType even if they are not `valObject`s themselves (eg `scatter.marker`)
- * to handle the case where you edit the whole container.
- *
- * @return {object} a new attributes object with `editType` modified as directed
- */
- function overrideAll(attrs, editTypeOverride, overrideContainers) {
- var out = extendFlat({}, attrs);
- for(var key in out) {
- var attr = out[key];
- if(isPlainObject(attr)) {
- out[key] = overrideOne(attr, editTypeOverride, overrideContainers, key);
- }
- }
- if(overrideContainers === 'from-root') out.editType = editTypeOverride;
-
- return out;
- }
-
- function overrideOne(attr, editTypeOverride, overrideContainers, key) {
- if(attr.valType) {
- var out = extendFlat({}, attr);
- out.editType = editTypeOverride;
-
- if(Array.isArray(attr.items)) {
- out.items = new Array(attr.items.length);
- for(var i = 0; i < attr.items.length; i++) {
- out.items[i] = overrideOne(attr.items[i], editTypeOverride, 'from-root');
- }
- }
- return out;
- }
- else {
- // don't provide an editType for the _deprecated container
- return overrideAll(attr, editTypeOverride,
- (key.charAt(0) === '_') ? 'nested' : 'from-root');
- }
- }
-
- },{"../lib":168}],196:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var m4FromQuat = _dereq_('gl-mat4/fromQuat');
-
- var Registry = _dereq_('../registry');
- var Lib = _dereq_('../lib');
- var Plots = _dereq_('../plots/plots');
- var AxisIds = _dereq_('../plots/cartesian/axis_ids');
- var cleanId = AxisIds.cleanId;
- var getFromTrace = AxisIds.getFromTrace;
- var Color = _dereq_('../components/color');
-
-
- // clear the promise queue if one of them got rejected
- exports.clearPromiseQueue = function(gd) {
- if(Array.isArray(gd._promises) && gd._promises.length > 0) {
- Lib.log('Clearing previous rejected promises from queue.');
- }
-
- gd._promises = [];
- };
-
- // make a few changes to the layout right away
- // before it gets used for anything
- // backward compatibility and cleanup of nonstandard options
- exports.cleanLayout = function(layout) {
- var i, j;
-
- if(!layout) layout = {};
-
- // cannot have (x|y)axis1, numbering goes axis, axis2, axis3...
- if(layout.xaxis1) {
- if(!layout.xaxis) layout.xaxis = layout.xaxis1;
- delete layout.xaxis1;
- }
- if(layout.yaxis1) {
- if(!layout.yaxis) layout.yaxis = layout.yaxis1;
- delete layout.yaxis1;
- }
- if(layout.scene1) {
- if(!layout.scene) layout.scene = layout.scene1;
- delete layout.scene1;
- }
-
- var axisAttrRegex = (Plots.subplotsRegistry.cartesian || {}).attrRegex;
- var polarAttrRegex = (Plots.subplotsRegistry.polar || {}).attrRegex;
- var ternaryAttrRegex = (Plots.subplotsRegistry.ternary || {}).attrRegex;
- var sceneAttrRegex = (Plots.subplotsRegistry.gl3d || {}).attrRegex;
-
- var keys = Object.keys(layout);
- for(i = 0; i < keys.length; i++) {
- var key = keys[i];
-
- // modifications to cartesian axes
- if(axisAttrRegex && axisAttrRegex.test(key)) {
- var ax = layout[key];
- if(ax.anchor && ax.anchor !== 'free') {
- ax.anchor = cleanId(ax.anchor);
- }
- if(ax.overlaying) ax.overlaying = cleanId(ax.overlaying);
-
- // old method of axis type - isdate and islog (before category existed)
- if(!ax.type) {
- if(ax.isdate) ax.type = 'date';
- else if(ax.islog) ax.type = 'log';
- else if(ax.isdate === false && ax.islog === false) ax.type = 'linear';
- }
- if(ax.autorange === 'withzero' || ax.autorange === 'tozero') {
- ax.autorange = true;
- ax.rangemode = 'tozero';
- }
- delete ax.islog;
- delete ax.isdate;
- delete ax.categories; // replaced by _categories
-
- // prune empty domain arrays made before the new nestedProperty
- if(emptyContainer(ax, 'domain')) delete ax.domain;
-
- // autotick -> tickmode
- if(ax.autotick !== undefined) {
- if(ax.tickmode === undefined) {
- ax.tickmode = ax.autotick ? 'auto' : 'linear';
- }
- delete ax.autotick;
- }
-
- cleanTitle(ax);
- }
-
- // modifications for polar
- else if(polarAttrRegex && polarAttrRegex.test(key)) {
- var polar = layout[key];
-
- cleanTitle(polar.radialaxis);
- }
-
- // modifications for ternary
- else if(ternaryAttrRegex && ternaryAttrRegex.test(key)) {
- var ternary = layout[key];
-
- cleanTitle(ternary.aaxis);
- cleanTitle(ternary.baxis);
- cleanTitle(ternary.caxis);
- }
-
- // modifications for 3D scenes
- else if(sceneAttrRegex && sceneAttrRegex.test(key)) {
- var scene = layout[key];
-
- // clean old Camera coords
- var cameraposition = scene.cameraposition;
-
- if(Array.isArray(cameraposition) && cameraposition[0].length === 4) {
- var rotation = cameraposition[0];
- var center = cameraposition[1];
- var radius = cameraposition[2];
- var mat = m4FromQuat([], rotation);
- var eye = [];
-
- for(j = 0; j < 3; ++j) {
- eye[j] = center[j] + radius * mat[2 + 4 * j];
- }
-
- scene.camera = {
- eye: {x: eye[0], y: eye[1], z: eye[2]},
- center: {x: center[0], y: center[1], z: center[2]},
- up: {x: 0, y: 0, z: 1} // we just ignore calculating camera z up in this case
- };
-
- delete scene.cameraposition;
- }
-
- // clean axis titles
- cleanTitle(scene.xaxis);
- cleanTitle(scene.yaxis);
- cleanTitle(scene.zaxis);
- }
- }
-
- var annotationsLen = Array.isArray(layout.annotations) ? layout.annotations.length : 0;
- for(i = 0; i < annotationsLen; i++) {
- var ann = layout.annotations[i];
-
- if(!Lib.isPlainObject(ann)) continue;
-
- if(ann.ref) {
- if(ann.ref === 'paper') {
- ann.xref = 'paper';
- ann.yref = 'paper';
- }
- else if(ann.ref === 'data') {
- ann.xref = 'x';
- ann.yref = 'y';
- }
- delete ann.ref;
- }
-
- cleanAxRef(ann, 'xref');
- cleanAxRef(ann, 'yref');
- }
-
- var shapesLen = Array.isArray(layout.shapes) ? layout.shapes.length : 0;
- for(i = 0; i < shapesLen; i++) {
- var shape = layout.shapes[i];
-
- if(!Lib.isPlainObject(shape)) continue;
-
- cleanAxRef(shape, 'xref');
- cleanAxRef(shape, 'yref');
- }
-
- var legend = layout.legend;
- if(legend) {
- // check for old-style legend positioning (x or y is +/- 100)
- if(legend.x > 3) {
- legend.x = 1.02;
- legend.xanchor = 'left';
- }
- else if(legend.x < -2) {
- legend.x = -0.02;
- legend.xanchor = 'right';
- }
-
- if(legend.y > 3) {
- legend.y = 1.02;
- legend.yanchor = 'bottom';
- }
- else if(legend.y < -2) {
- legend.y = -0.02;
- legend.yanchor = 'top';
- }
- }
-
- // clean plot title
- cleanTitle(layout);
-
- /*
- * Moved from rotate -> orbit for dragmode
- */
- if(layout.dragmode === 'rotate') layout.dragmode = 'orbit';
-
- // sanitize rgb(fractions) and rgba(fractions) that old tinycolor
- // supported, but new tinycolor does not because they're not valid css
- Color.clean(layout);
-
- // clean the layout container in layout.template
- if(layout.template && layout.template.layout) {
- exports.cleanLayout(layout.template.layout);
- }
-
- return layout;
- };
-
- function cleanAxRef(container, attr) {
- var valIn = container[attr];
- var axLetter = attr.charAt(0);
- if(valIn && valIn !== 'paper') {
- container[attr] = cleanId(valIn, axLetter);
- }
- }
-
- /**
- * Cleans up old title attribute structure (flat) in favor of the new one (nested).
- *
- * @param {Object} titleContainer - an object potentially including deprecated title attributes
- */
- function cleanTitle(titleContainer) {
- if(titleContainer) {
-
- // title -> title.text
- // (although title used to be a string attribute,
- // numbers are accepted as well)
- if(typeof titleContainer.title === 'string' || typeof titleContainer.title === 'number') {
- titleContainer.title = {
- text: titleContainer.title
- };
- }
-
- rewireAttr('titlefont', 'font');
- rewireAttr('titleposition', 'position');
- rewireAttr('titleside', 'side');
- rewireAttr('titleoffset', 'offset');
- }
-
- function rewireAttr(oldAttrName, newAttrName) {
- var oldAttrSet = titleContainer[oldAttrName];
- var newAttrSet = titleContainer.title && titleContainer.title[newAttrName];
-
- if(oldAttrSet && !newAttrSet) {
-
- // Ensure title object exists
- if(!titleContainer.title) {
- titleContainer.title = {};
- }
-
- titleContainer.title[newAttrName] = titleContainer[oldAttrName];
- delete titleContainer[oldAttrName];
- }
- }
- }
-
- /*
- * cleanData: Make a few changes to the data for backward compatibility
- * before it gets used for anything. Modifies the data traces users provide.
- *
- * Important: if you're going to add something here that modifies a data array,
- * update it in place so the new array === the old one.
- */
- exports.cleanData = function(data) {
- for(var tracei = 0; tracei < data.length; tracei++) {
- var trace = data[tracei];
- var i;
-
- // use xbins to bin data in x, and ybins to bin data in y
- if(trace.type === 'histogramy' && 'xbins' in trace && !('ybins' in trace)) {
- trace.ybins = trace.xbins;
- delete trace.xbins;
- }
-
- // error_y.opacity is obsolete - merge into color
- if(trace.error_y && 'opacity' in trace.error_y) {
- var dc = Color.defaults;
- var yeColor = trace.error_y.color || (Registry.traceIs(trace, 'bar') ?
- Color.defaultLine :
- dc[tracei % dc.length]);
- trace.error_y.color = Color.addOpacity(
- Color.rgb(yeColor),
- Color.opacity(yeColor) * trace.error_y.opacity);
- delete trace.error_y.opacity;
- }
-
- // convert bardir to orientation, and put the data into
- // the axes it's eventually going to be used with
- if('bardir' in trace) {
- if(trace.bardir === 'h' && (Registry.traceIs(trace, 'bar') ||
- trace.type.substr(0, 9) === 'histogram')) {
- trace.orientation = 'h';
- exports.swapXYData(trace);
- }
- delete trace.bardir;
- }
-
- // now we have only one 1D histogram type, and whether
- // it uses x or y data depends on trace.orientation
- if(trace.type === 'histogramy') exports.swapXYData(trace);
- if(trace.type === 'histogramx' || trace.type === 'histogramy') {
- trace.type = 'histogram';
- }
-
- // scl->scale, reversescl->reversescale
- if('scl' in trace && !('colorscale' in trace)) {
- trace.colorscale = trace.scl;
- delete trace.scl;
- }
- if('reversescl' in trace && !('reversescale' in trace)) {
- trace.reversescale = trace.reversescl;
- delete trace.reversescl;
- }
-
- // axis ids x1 -> x, y1-> y
- if(trace.xaxis) trace.xaxis = cleanId(trace.xaxis, 'x');
- if(trace.yaxis) trace.yaxis = cleanId(trace.yaxis, 'y');
-
- // scene ids scene1 -> scene
- if(Registry.traceIs(trace, 'gl3d') && trace.scene) {
- trace.scene = Plots.subplotsRegistry.gl3d.cleanId(trace.scene);
- }
-
- if(!Registry.traceIs(trace, 'pie') && !Registry.traceIs(trace, 'bar')) {
- if(Array.isArray(trace.textposition)) {
- for(i = 0; i < trace.textposition.length; i++) {
- trace.textposition[i] = cleanTextPosition(trace.textposition[i]);
- }
- }
- else if(trace.textposition) {
- trace.textposition = cleanTextPosition(trace.textposition);
- }
- }
-
- // fix typo in colorscale definition
- var _module = Registry.getModule(trace);
- if(_module && _module.colorbar) {
- var containerName = _module.colorbar.container;
- var container = containerName ? trace[containerName] : trace;
- if(container && container.colorscale) {
- if(container.colorscale === 'YIGnBu') container.colorscale = 'YlGnBu';
- if(container.colorscale === 'YIOrRd') container.colorscale = 'YlOrRd';
- }
- }
-
- // fix typo in surface 'highlight*' definitions
- if(trace.type === 'surface' && Lib.isPlainObject(trace.contours)) {
- var dims = ['x', 'y', 'z'];
-
- for(i = 0; i < dims.length; i++) {
- var opts = trace.contours[dims[i]];
-
- if(!Lib.isPlainObject(opts)) continue;
-
- if(opts.highlightColor) {
- opts.highlightcolor = opts.highlightColor;
- delete opts.highlightColor;
- }
-
- if(opts.highlightWidth) {
- opts.highlightwidth = opts.highlightWidth;
- delete opts.highlightWidth;
- }
- }
- }
-
- // fixes from converting finance from transforms to real trace types
- if(trace.type === 'candlestick' || trace.type === 'ohlc') {
- var increasingShowlegend = (trace.increasing || {}).showlegend !== false;
- var decreasingShowlegend = (trace.decreasing || {}).showlegend !== false;
- var increasingName = cleanFinanceDir(trace.increasing);
- var decreasingName = cleanFinanceDir(trace.decreasing);
-
- // now figure out something smart to do with the separate direction
- // names we removed
- if((increasingName !== false) && (decreasingName !== false)) {
- // both sub-names existed: base name previously had no effect
- // so ignore it and try to find a shared part of the sub-names
-
- var newName = commonPrefix(
- increasingName, decreasingName,
- increasingShowlegend, decreasingShowlegend
- );
- // if no common part, leave whatever name was (or wasn't) there
- if(newName) trace.name = newName;
- }
- else if((increasingName || decreasingName) && !trace.name) {
- // one sub-name existed but not the base name - just use the sub-name
- trace.name = increasingName || decreasingName;
- }
- }
-
- // transforms backward compatibility fixes
- if(Array.isArray(trace.transforms)) {
- var transforms = trace.transforms;
-
- for(i = 0; i < transforms.length; i++) {
- var transform = transforms[i];
-
- if(!Lib.isPlainObject(transform)) continue;
-
- switch(transform.type) {
- case 'filter':
- if(transform.filtersrc) {
- transform.target = transform.filtersrc;
- delete transform.filtersrc;
- }
-
- if(transform.calendar) {
- if(!transform.valuecalendar) {
- transform.valuecalendar = transform.calendar;
- }
- delete transform.calendar;
- }
- break;
-
- case 'groupby':
- // Name has changed from `style` to `styles`, so use `style` but prefer `styles`:
- transform.styles = transform.styles || transform.style;
-
- if(transform.styles && !Array.isArray(transform.styles)) {
- var prevStyles = transform.styles;
- var styleKeys = Object.keys(prevStyles);
-
- transform.styles = [];
- for(var j = 0; j < styleKeys.length; j++) {
- transform.styles.push({
- target: styleKeys[j],
- value: prevStyles[styleKeys[j]]
- });
- }
- }
- break;
- }
- }
- }
-
- // prune empty containers made before the new nestedProperty
- if(emptyContainer(trace, 'line')) delete trace.line;
- if('marker' in trace) {
- if(emptyContainer(trace.marker, 'line')) delete trace.marker.line;
- if(emptyContainer(trace, 'marker')) delete trace.marker;
- }
-
- // sanitize rgb(fractions) and rgba(fractions) that old tinycolor
- // supported, but new tinycolor does not because they're not valid css
- Color.clean(trace);
-
- // remove obsolete autobin(x|y) attributes, but only if true
- // if false, this needs to happen in Histogram.calc because it
- // can be a one-time autobin so we need to know the results before
- // we can push them back into the trace.
- if(trace.autobinx) {
- delete trace.autobinx;
- delete trace.xbins;
- }
- if(trace.autobiny) {
- delete trace.autobiny;
- delete trace.ybins;
- }
-
- cleanTitle(trace);
- if(trace.colorbar) cleanTitle(trace.colorbar);
- if(trace.marker && trace.marker.colorbar) cleanTitle(trace.marker.colorbar);
- if(trace.line && trace.line.colorbar) cleanTitle(trace.line.colorbar);
- if(trace.aaxis) cleanTitle(trace.aaxis);
- if(trace.baxis) cleanTitle(trace.baxis);
- }
- };
-
- function cleanFinanceDir(dirContainer) {
- if(!Lib.isPlainObject(dirContainer)) return false;
-
- var dirName = dirContainer.name;
-
- delete dirContainer.name;
- delete dirContainer.showlegend;
-
- return (typeof dirName === 'string' || typeof dirName === 'number') && String(dirName);
- }
-
- function commonPrefix(name1, name2, show1, show2) {
- // if only one is shown in the legend, use that
- if(show1 && !show2) return name1;
- if(show2 && !show1) return name2;
-
- // if both or neither are in the legend, check if one is blank (or whitespace)
- // and use the other one
- // note that hover labels can still use the name even if the legend doesn't
- if(!name1.trim()) return name2;
- if(!name2.trim()) return name1;
-
- var minLen = Math.min(name1.length, name2.length);
- var i;
- for(i = 0; i < minLen; i++) {
- if(name1.charAt(i) !== name2.charAt(i)) break;
- }
-
- var out = name1.substr(0, i);
- return out.trim();
- }
-
- // textposition - support partial attributes (ie just 'top')
- // and incorrect use of middle / center etc.
- function cleanTextPosition(textposition) {
- var posY = 'middle';
- var posX = 'center';
-
- if(typeof textposition === 'string') {
- if(textposition.indexOf('top') !== -1) posY = 'top';
- else if(textposition.indexOf('bottom') !== -1) posY = 'bottom';
-
- if(textposition.indexOf('left') !== -1) posX = 'left';
- else if(textposition.indexOf('right') !== -1) posX = 'right';
- }
-
- return posY + ' ' + posX;
- }
-
- function emptyContainer(outer, innerStr) {
- return (innerStr in outer) &&
- (typeof outer[innerStr] === 'object') &&
- (Object.keys(outer[innerStr]).length === 0);
- }
-
-
- // swap all the data and data attributes associated with x and y
- exports.swapXYData = function(trace) {
- var i;
- Lib.swapAttrs(trace, ['?', '?0', 'd?', '?bins', 'nbins?', 'autobin?', '?src', 'error_?']);
- if(Array.isArray(trace.z) && Array.isArray(trace.z[0])) {
- if(trace.transpose) delete trace.transpose;
- else trace.transpose = true;
- }
- if(trace.error_x && trace.error_y) {
- var errorY = trace.error_y;
- var copyYstyle = ('copy_ystyle' in errorY) ?
- errorY.copy_ystyle :
- !(errorY.color || errorY.thickness || errorY.width);
- Lib.swapAttrs(trace, ['error_?.copy_ystyle']);
- if(copyYstyle) {
- Lib.swapAttrs(trace, ['error_?.color', 'error_?.thickness', 'error_?.width']);
- }
- }
- if(typeof trace.hoverinfo === 'string') {
- var hoverInfoParts = trace.hoverinfo.split('+');
- for(i = 0; i < hoverInfoParts.length; i++) {
- if(hoverInfoParts[i] === 'x') hoverInfoParts[i] = 'y';
- else if(hoverInfoParts[i] === 'y') hoverInfoParts[i] = 'x';
- }
- trace.hoverinfo = hoverInfoParts.join('+');
- }
- };
-
- // coerce traceIndices input to array of trace indices
- exports.coerceTraceIndices = function(gd, traceIndices) {
- if(isNumeric(traceIndices)) {
- return [traceIndices];
- }
- else if(!Array.isArray(traceIndices) || !traceIndices.length) {
- return gd.data.map(function(_, i) { return i; });
- }
- else if(Array.isArray(traceIndices)) {
- var traceIndicesOut = [];
- for(var i = 0; i < traceIndices.length; i++) {
- if(Lib.isIndex(traceIndices[i], gd.data.length)) {
- traceIndicesOut.push(traceIndices[i]);
- } else {
- Lib.warn('trace index (', traceIndices[i], ') is not a number or is out of bounds');
- }
- }
- return traceIndicesOut;
- }
-
- return traceIndices;
- };
-
- /**
- * Manages logic around array container item creation / deletion / update
- * that nested property alone can't handle.
- *
- * @param {Object} np
- * nested property of update attribute string about trace or layout object
- * @param {*} newVal
- * update value passed to restyle / relayout / update
- * @param {Object} undoit
- * undo hash (N.B. undoit may be mutated here).
- *
- */
- exports.manageArrayContainers = function(np, newVal, undoit) {
- var obj = np.obj;
- var parts = np.parts;
- var pLength = parts.length;
- var pLast = parts[pLength - 1];
-
- var pLastIsNumber = isNumeric(pLast);
-
- // delete item
- if(pLastIsNumber && newVal === null) {
-
- // Clear item in array container when new value is null
- var contPath = parts.slice(0, pLength - 1).join('.');
- var cont = Lib.nestedProperty(obj, contPath).get();
- cont.splice(pLast, 1);
-
- // Note that nested property clears null / undefined at end of
- // array container, but not within them.
- }
- // create item
- else if(pLastIsNumber && np.get() === undefined) {
-
- // When adding a new item, make sure undo command will remove it
- if(np.get() === undefined) undoit[np.astr] = null;
-
- np.set(newVal);
- }
- // update item
- else {
-
- // If the last part of attribute string isn't a number,
- // np.set is all we need.
- np.set(newVal);
- }
- };
-
- /*
- * Match the part to strip off to turn an attribute into its parent
- * really it should be either '.some_characters' or '[number]'
- * but we're a little more permissive here and match either
- * '.not_brackets_or_dot' or '[not_brackets_or_dot]'
- */
- var ATTR_TAIL_RE = /(\.[^\[\]\.]+|\[[^\[\]\.]+\])$/;
-
- function getParent(attr) {
- var tail = attr.search(ATTR_TAIL_RE);
- if(tail > 0) return attr.substr(0, tail);
- }
-
- /*
- * hasParent: does an attribute object contain a parent of the given attribute?
- * for example, given 'images[2].x' do we also have 'images' or 'images[2]'?
- *
- * @param {Object} aobj
- * update object, whose keys are attribute strings and values are their new settings
- * @param {string} attr
- * the attribute string to test against
- * @returns {Boolean}
- * is a parent of attr present in aobj?
- */
- exports.hasParent = function(aobj, attr) {
- var attrParent = getParent(attr);
- while(attrParent) {
- if(attrParent in aobj) return true;
- attrParent = getParent(attrParent);
- }
- return false;
- };
-
- /**
- * Empty out types for all axes containing these traces so we auto-set them again
- *
- * @param {object} gd
- * @param {[integer]} traces: trace indices to search for axes to clear the types of
- * @param {object} layoutUpdate: any update being done concurrently to the layout,
- * which may supercede clearing the axis types
- */
- var axLetters = ['x', 'y', 'z'];
- exports.clearAxisTypes = function(gd, traces, layoutUpdate) {
- for(var i = 0; i < traces.length; i++) {
- var trace = gd._fullData[i];
- for(var j = 0; j < 3; j++) {
- var ax = getFromTrace(gd, trace, axLetters[j]);
-
- // do not clear log type - that's never an auto result so must have been intentional
- if(ax && ax.type !== 'log') {
- var axAttr = ax._name;
- var sceneName = ax._id.substr(1);
- if(sceneName.substr(0, 5) === 'scene') {
- if(layoutUpdate[sceneName] !== undefined) continue;
- axAttr = sceneName + '.' + axAttr;
- }
- var typeAttr = axAttr + '.type';
-
- if(layoutUpdate[axAttr] === undefined && layoutUpdate[typeAttr] === undefined) {
- Lib.nestedProperty(gd.layout, typeAttr).set(null);
- }
- }
- }
- }
- };
-
- },{"../components/color":51,"../lib":168,"../plots/cartesian/axis_ids":215,"../plots/plots":245,"../registry":257,"fast-isnumeric":18,"gl-mat4/fromQuat":19}],197:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var main = _dereq_('./plot_api');
-
- exports.plot = main.plot;
- exports.newPlot = main.newPlot;
- exports.restyle = main.restyle;
- exports.relayout = main.relayout;
- exports.redraw = main.redraw;
- exports.update = main.update;
- exports._guiRestyle = main._guiRestyle;
- exports._guiRelayout = main._guiRelayout;
- exports._guiUpdate = main._guiUpdate;
- exports._storeDirectGUIEdit = main._storeDirectGUIEdit;
- exports.react = main.react;
- exports.extendTraces = main.extendTraces;
- exports.prependTraces = main.prependTraces;
- exports.addTraces = main.addTraces;
- exports.deleteTraces = main.deleteTraces;
- exports.moveTraces = main.moveTraces;
- exports.purge = main.purge;
- exports.addFrames = main.addFrames;
- exports.deleteFrames = main.deleteFrames;
- exports.animate = main.animate;
- exports.setPlotConfig = main.setPlotConfig;
-
- exports.toImage = _dereq_('./to_image');
- exports.validate = _dereq_('./validate');
- exports.downloadImage = _dereq_('../snapshot/download');
-
- var templateApi = _dereq_('./template_api');
- exports.makeTemplate = templateApi.makeTemplate;
- exports.validateTemplate = templateApi.validateTemplate;
-
- },{"../snapshot/download":259,"./plot_api":199,"./template_api":204,"./to_image":205,"./validate":206}],198:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isPlainObject = _dereq_('../lib/is_plain_object');
- var noop = _dereq_('../lib/noop');
- var Loggers = _dereq_('../lib/loggers');
- var sorterAsc = _dereq_('../lib/search').sorterAsc;
- var Registry = _dereq_('../registry');
-
-
- exports.containerArrayMatch = _dereq_('./container_array_match');
-
- var isAddVal = exports.isAddVal = function isAddVal(val) {
- return val === 'add' || isPlainObject(val);
- };
-
- var isRemoveVal = exports.isRemoveVal = function isRemoveVal(val) {
- return val === null || val === 'remove';
- };
-
- /*
- * applyContainerArrayChanges: for managing arrays of layout components in relayout
- * handles them all with a consistent interface.
- *
- * Here are the supported actions -> relayout calls -> edits we get here
- * (as prepared in _relayout):
- *
- * add an empty obj -> {'annotations[2]': 'add'} -> {2: {'': 'add'}}
- * add a specific obj -> {'annotations[2]': {attrs}} -> {2: {'': {attrs}}}
- * delete an obj -> {'annotations[2]': 'remove'} -> {2: {'': 'remove'}}
- * -> {'annotations[2]': null} -> {2: {'': null}}
- * delete the whole array -> {'annotations': 'remove'} -> {'': {'': 'remove'}}
- * -> {'annotations': null} -> {'': {'': null}}
- * edit an object -> {'annotations[2].text': 'boo'} -> {2: {'text': 'boo'}}
- *
- * You can combine many edits to different objects. Objects are added and edited
- * in ascending order, then removed in descending order.
- * For example, starting with [a, b, c], if you want to:
- * - replace b with d:
- * {'annotations[1]': d, 'annotations[2]': null} (b is item 2 after adding d)
- * - add a new item d between a and b, and edit b:
- * {'annotations[1]': d, 'annotations[2].x': newX} (b is item 2 after adding d)
- * - delete b and edit c:
- * {'annotations[1]': null, 'annotations[2].x': newX} (c is edited before b is removed)
- *
- * You CANNOT combine adding/deleting an item at index `i` with edits to the same index `i`
- * You CANNOT combine replacing/deleting the whole array with anything else (for the same array).
- *
- * @param {HTMLDivElement} gd
- * the DOM element of the graph container div
- * @param {Lib.nestedProperty} componentType: the array we are editing
- * @param {Object} edits
- * the changes to make; keys are indices to edit, values are themselves objects:
- * {attr: newValue} of changes to make to that index (with add/remove behavior
- * in special values of the empty attr)
- * @param {Object} flags
- * the flags for which actions we're going to perform to display these (and
- * any other) changes. If we're already `recalc`ing, we don't need to redraw
- * individual items
- * @param {function} _nestedProperty
- * a (possibly modified for gui edits) nestedProperty constructor
- * The modified version takes a 3rd argument, for a prefix to the attribute
- * string necessary for storing GUI edits
- *
- * @returns {bool} `true` if it managed to complete drawing of the changes
- * `false` would mean the parent should replot.
- */
- exports.applyContainerArrayChanges = function applyContainerArrayChanges(gd, np, edits, flags, _nestedProperty) {
- var componentType = np.astr;
- var supplyComponentDefaults = Registry.getComponentMethod(componentType, 'supplyLayoutDefaults');
- var draw = Registry.getComponentMethod(componentType, 'draw');
- var drawOne = Registry.getComponentMethod(componentType, 'drawOne');
- var replotLater = flags.replot || flags.recalc || (supplyComponentDefaults === noop) || (draw === noop);
- var layout = gd.layout;
- var fullLayout = gd._fullLayout;
-
- if(edits['']) {
- if(Object.keys(edits).length > 1) {
- Loggers.warn('Full array edits are incompatible with other edits',
- componentType);
- }
-
- var fullVal = edits[''][''];
-
- if(isRemoveVal(fullVal)) np.set(null);
- else if(Array.isArray(fullVal)) np.set(fullVal);
- else {
- Loggers.warn('Unrecognized full array edit value', componentType, fullVal);
- return true;
- }
-
- if(replotLater) return false;
-
- supplyComponentDefaults(layout, fullLayout);
- draw(gd);
- return true;
- }
-
- var componentNums = Object.keys(edits).map(Number).sort(sorterAsc);
- var componentArrayIn = np.get();
- var componentArray = componentArrayIn || [];
- // componentArrayFull is used just to keep splices in line between
- // full and input arrays, so private keys can be copied over after
- // redoing supplyDefaults
- // TODO: this assumes componentArray is in gd.layout - which will not be
- // true after we extend this to restyle
- var componentArrayFull = _nestedProperty(fullLayout, componentType).get();
-
- var deletes = [];
- var firstIndexChange = -1;
- var maxIndex = componentArray.length;
- var i;
- var j;
- var componentNum;
- var objEdits;
- var objKeys;
- var objVal;
- var adding, prefix;
-
- // first make the add and edit changes
- for(i = 0; i < componentNums.length; i++) {
- componentNum = componentNums[i];
- objEdits = edits[componentNum];
- objKeys = Object.keys(objEdits);
- objVal = objEdits[''],
- adding = isAddVal(objVal);
-
- if(componentNum < 0 || componentNum > componentArray.length - (adding ? 0 : 1)) {
- Loggers.warn('index out of range', componentType, componentNum);
- continue;
- }
-
- if(objVal !== undefined) {
- if(objKeys.length > 1) {
- Loggers.warn(
- 'Insertion & removal are incompatible with edits to the same index.',
- componentType, componentNum);
- }
-
- if(isRemoveVal(objVal)) {
- deletes.push(componentNum);
- }
- else if(adding) {
- if(objVal === 'add') objVal = {};
- componentArray.splice(componentNum, 0, objVal);
- if(componentArrayFull) componentArrayFull.splice(componentNum, 0, {});
- }
- else {
- Loggers.warn('Unrecognized full object edit value',
- componentType, componentNum, objVal);
- }
-
- if(firstIndexChange === -1) firstIndexChange = componentNum;
- }
- else {
- for(j = 0; j < objKeys.length; j++) {
- prefix = componentType + '[' + componentNum + '].';
- _nestedProperty(componentArray[componentNum], objKeys[j], prefix)
- .set(objEdits[objKeys[j]]);
- }
- }
- }
-
- // now do deletes
- for(i = deletes.length - 1; i >= 0; i--) {
- componentArray.splice(deletes[i], 1);
- // TODO: this drops private keys that had been stored in componentArrayFull
- // does this have any ill effects?
- if(componentArrayFull) componentArrayFull.splice(deletes[i], 1);
- }
-
- if(!componentArray.length) np.set(null);
- else if(!componentArrayIn) np.set(componentArray);
-
- if(replotLater) return false;
-
- supplyComponentDefaults(layout, fullLayout);
-
- // finally draw all the components we need to
- // if we added or removed any, redraw all after it
- if(drawOne !== noop) {
- var indicesToDraw;
- if(firstIndexChange === -1) {
- // there's no re-indexing to do, so only redraw components that changed
- indicesToDraw = componentNums;
- }
- else {
- // in case the component array was shortened, we still need do call
- // drawOne on the latter items so they get properly removed
- maxIndex = Math.max(componentArray.length, maxIndex);
- indicesToDraw = [];
- for(i = 0; i < componentNums.length; i++) {
- componentNum = componentNums[i];
- if(componentNum >= firstIndexChange) break;
- indicesToDraw.push(componentNum);
- }
- for(i = firstIndexChange; i < maxIndex; i++) {
- indicesToDraw.push(i);
- }
- }
- for(i = 0; i < indicesToDraw.length; i++) {
- drawOne(gd, indicesToDraw[i]);
- }
- }
- else draw(gd);
-
- return true;
- };
-
- },{"../lib/is_plain_object":169,"../lib/loggers":172,"../lib/noop":177,"../lib/search":186,"../registry":257,"./container_array_match":194}],199:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
- var hasHover = _dereq_('has-hover');
-
- var Lib = _dereq_('../lib');
- var nestedProperty = Lib.nestedProperty;
-
- var Events = _dereq_('../lib/events');
- var Queue = _dereq_('../lib/queue');
-
- var Registry = _dereq_('../registry');
- var PlotSchema = _dereq_('./plot_schema');
- var Plots = _dereq_('../plots/plots');
- var Polar = _dereq_('../plots/polar/legacy');
-
- var Axes = _dereq_('../plots/cartesian/axes');
- var Drawing = _dereq_('../components/drawing');
- var Color = _dereq_('../components/color');
- var connectColorbar = _dereq_('../components/colorbar/connect');
- var initInteractions = _dereq_('../plots/cartesian/graph_interact').initInteractions;
- var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
- var svgTextUtils = _dereq_('../lib/svg_text_utils');
-
- var dfltConfig = _dereq_('./plot_config').dfltConfig;
- var manageArrays = _dereq_('./manage_arrays');
- var helpers = _dereq_('./helpers');
- var subroutines = _dereq_('./subroutines');
- var editTypes = _dereq_('./edit_types');
-
- var AX_NAME_PATTERN = _dereq_('../plots/cartesian/constants').AX_NAME_PATTERN;
-
- var numericNameWarningCount = 0;
- var numericNameWarningCountLimit = 5;
-
- /**
- * Main plot-creation function
- *
- * @param {string id or DOM element} gd
- * the id or DOM element of the graph container div
- * @param {array of objects} data
- * array of traces, containing the data and display information for each trace
- * @param {object} layout
- * object describing the overall display of the plot,
- * all the stuff that doesn't pertain to any individual trace
- * @param {object} config
- * configuration options (see ./plot_config.js for more info)
- *
- * OR
- *
- * @param {string id or DOM element} gd
- * the id or DOM element of the graph container div
- * @param {object} figure
- * object containing `data`, `layout`, `config`, and `frames` members
- *
- */
- exports.plot = function(gd, data, layout, config) {
- var frames;
-
- gd = Lib.getGraphDiv(gd);
-
- // Events.init is idempotent and bails early if gd has already been init'd
- Events.init(gd);
-
- if(Lib.isPlainObject(data)) {
- var obj = data;
- data = obj.data;
- layout = obj.layout;
- config = obj.config;
- frames = obj.frames;
- }
-
- var okToPlot = Events.triggerHandler(gd, 'plotly_beforeplot', [data, layout, config]);
- if(okToPlot === false) return Promise.reject();
-
- // if there's no data or layout, and this isn't yet a plotly plot
- // container, log a warning to help plotly.js users debug
- if(!data && !layout && !Lib.isPlotDiv(gd)) {
- Lib.warn('Calling Plotly.plot as if redrawing ' +
- 'but this container doesn\'t yet have a plot.', gd);
- }
-
- function addFrames() {
- if(frames) {
- return exports.addFrames(gd, frames);
- }
- }
-
- // transfer configuration options to gd until we move over to
- // a more OO like model
- setPlotContext(gd, config);
-
- if(!layout) layout = {};
-
- // hook class for plots main container (in case of plotly.js
- // this won't be #embedded-graph or .js-tab-contents)
- d3.select(gd).classed('js-plotly-plot', true);
-
- // off-screen getBoundingClientRect testing space,
- // in #js-plotly-tester (and stored as Drawing.tester)
- // so we can share cached text across tabs
- Drawing.makeTester();
-
- // collect promises for any async actions during plotting
- // any part of the plotting code can push to gd._promises, then
- // before we move to the next step, we check that they're all
- // complete, and empty out the promise list again.
- if(!Array.isArray(gd._promises)) gd._promises = [];
-
- var graphWasEmpty = ((gd.data || []).length === 0 && Array.isArray(data));
-
- // if there is already data on the graph, append the new data
- // if you only want to redraw, pass a non-array for data
- if(Array.isArray(data)) {
- helpers.cleanData(data);
-
- if(graphWasEmpty) gd.data = data;
- else gd.data.push.apply(gd.data, data);
-
- // for routines outside graph_obj that want a clean tab
- // (rather than appending to an existing one) gd.empty
- // is used to determine whether to make a new tab
- gd.empty = false;
- }
-
- if(!gd.layout || graphWasEmpty) gd.layout = helpers.cleanLayout(layout);
-
- // if the user is trying to drag the axes, allow new data and layout
- // to come in but don't allow a replot.
- if(gd._dragging && !gd._transitioning) {
- // signal to drag handler that after everything else is done
- // we need to replot, because something has changed
- gd._replotPending = true;
- return Promise.reject();
- } else {
- // we're going ahead with a replot now
- gd._replotPending = false;
- }
-
- Plots.supplyDefaults(gd);
-
- var fullLayout = gd._fullLayout;
- var hasCartesian = fullLayout._has('cartesian');
-
- // Legacy polar plots
- if(!fullLayout._has('polar') && data && data[0] && data[0].r) {
- Lib.log('Legacy polar charts are deprecated!');
- return plotLegacyPolar(gd, data, layout);
- }
-
- // so we don't try to re-call Plotly.plot from inside
- // legend and colorbar, if margins changed
- fullLayout._replotting = true;
-
- // make or remake the framework if we need to
- if(graphWasEmpty) makePlotFramework(gd);
-
- // polar need a different framework
- if(gd.framework !== makePlotFramework) {
- gd.framework = makePlotFramework;
- makePlotFramework(gd);
- }
-
- // clear gradient defs on each .plot call, because we know we'll loop through all traces
- Drawing.initGradients(gd);
-
- // save initial show spikes once per graph
- if(graphWasEmpty) Axes.saveShowSpikeInitial(gd);
-
- // prepare the data and find the autorange
-
- // generate calcdata, if we need to
- // to force redoing calcdata, just delete it before calling Plotly.plot
- var recalc = !gd.calcdata || gd.calcdata.length !== (gd._fullData || []).length;
- if(recalc) Plots.doCalcdata(gd);
-
- // in case it has changed, attach fullData traces to calcdata
- for(var i = 0; i < gd.calcdata.length; i++) {
- gd.calcdata[i][0].trace = gd._fullData[i];
- }
-
- // make the figure responsive
- if(gd._context.responsive) {
- if(!gd._responsiveChartHandler) {
- // Keep a reference to the resize handler to purge it down the road
- gd._responsiveChartHandler = function() {Plots.resize(gd);};
-
- // Listen to window resize
- window.addEventListener('resize', gd._responsiveChartHandler);
- }
- } else {
- Lib.clearResponsive(gd);
- }
-
- /*
- * start async-friendly code - now we're actually drawing things
- */
-
- var oldmargins = JSON.stringify(fullLayout._size);
-
- // draw framework first so that margin-pushing
- // components can position themselves correctly
- var drawFrameworkCalls = 0;
- function drawFramework() {
- var basePlotModules = fullLayout._basePlotModules;
-
- for(var i = 0; i < basePlotModules.length; i++) {
- if(basePlotModules[i].drawFramework) {
- basePlotModules[i].drawFramework(gd);
- }
- }
-
- if(!fullLayout._glcanvas && fullLayout._has('gl')) {
- fullLayout._glcanvas = fullLayout._glcontainer.selectAll('.gl-canvas').data([{
- key: 'contextLayer',
- context: true,
- pick: false
- }, {
- key: 'focusLayer',
- context: false,
- pick: false
- }, {
- key: 'pickLayer',
- context: false,
- pick: true
- }], function(d) { return d.key; });
-
- fullLayout._glcanvas.enter().append('canvas')
- .attr('class', function(d) {
- return 'gl-canvas gl-canvas-' + d.key.replace('Layer', '');
- })
- .style({
- 'position': 'absolute',
- 'top': 0,
- 'left': 0,
- 'width': '100%',
- 'height': '100%',
- 'overflow': 'visible',
- 'pointer-events': 'none'
- });
- }
-
- if(fullLayout._glcanvas) {
- fullLayout._glcanvas
- .attr('width', fullLayout.width)
- .attr('height', fullLayout.height);
-
- var regl = fullLayout._glcanvas.data()[0].regl;
- if(regl) {
- // Unfortunately, this can happen when relayouting to large
- // width/height on some browsers.
- if(Math.floor(fullLayout.width) !== regl._gl.drawingBufferWidth ||
- Math.floor(fullLayout.height) !== regl._gl.drawingBufferHeight
- ) {
- var msg = 'WebGL context buffer and canvas dimensions do not match due to browser/WebGL bug.';
- if(drawFrameworkCalls) {
- Lib.error(msg);
- } else {
- Lib.log(msg + ' Clearing graph and plotting again.');
- Plots.cleanPlot([], {}, gd._fullData, fullLayout);
- Plots.supplyDefaults(gd);
- fullLayout = gd._fullLayout;
- Plots.doCalcdata(gd);
- drawFrameworkCalls++;
- return drawFramework();
- }
- }
- }
- }
-
- return Plots.previousPromises(gd);
- }
-
- // draw anything that can affect margins.
- function marginPushers() {
- var calcdata = gd.calcdata;
- var i, cd, trace;
-
- // First reset the list of things that are allowed to change the margins
- // So any deleted traces or components will be wiped out of the
- // automargin calculation.
- // This means *every* margin pusher must be listed here, even if it
- // doesn't actually try to push the margins until later.
- Plots.clearAutoMarginIds(gd);
-
- subroutines.drawMarginPushers(gd);
- Axes.allowAutoMargin(gd);
-
- for(i = 0; i < calcdata.length; i++) {
- cd = calcdata[i];
- trace = cd[0].trace;
- var colorbarOpts = trace._module.colorbar;
- if(trace.visible !== true || !colorbarOpts) {
- Plots.autoMargin(gd, 'cb' + trace.uid);
- }
- else connectColorbar(gd, cd, colorbarOpts);
- }
-
- Plots.doAutoMargin(gd);
- return Plots.previousPromises(gd);
- }
-
- // in case the margins changed, draw margin pushers again
- function marginPushersAgain() {
- if(JSON.stringify(fullLayout._size) === oldmargins) return;
-
- return Lib.syncOrAsync([
- marginPushers,
- subroutines.layoutStyles
- ], gd);
- }
-
- function positionAndAutorange() {
- if(!recalc) {
- doAutoRangeAndConstraints();
- return;
- }
-
- // TODO: autosize extra for text markers and images
- // see https://github.com/plotly/plotly.js/issues/1111
- return Lib.syncOrAsync([
- Registry.getComponentMethod('shapes', 'calcAutorange'),
- Registry.getComponentMethod('annotations', 'calcAutorange'),
- doAutoRangeAndConstraints
- ], gd);
- }
-
- function doAutoRangeAndConstraints() {
- if(gd._transitioning) return;
-
- subroutines.doAutoRangeAndConstraints(gd);
-
- // store initial ranges *after* enforcing constraints, otherwise
- // we will never look like we're at the initial ranges
- if(graphWasEmpty) Axes.saveRangeInitial(gd);
-
- // this one is different from shapes/annotations calcAutorange
- // the others incorporate those components into ax._extremes,
- // this one actually sets the ranges in rangesliders.
- Registry.getComponentMethod('rangeslider', 'calcAutorange')(gd);
- }
-
- // draw ticks, titles, and calculate axis scaling (._b, ._m)
- function drawAxes() {
- return Axes.draw(gd, graphWasEmpty ? '' : 'redraw');
- }
-
- var seq = [
- Plots.previousPromises,
- addFrames,
- drawFramework,
- marginPushers,
- marginPushersAgain
- ];
-
- if(hasCartesian) seq.push(positionAndAutorange);
-
- seq.push(subroutines.layoutStyles);
- if(hasCartesian) seq.push(drawAxes);
-
- seq.push(
- subroutines.drawData,
- subroutines.finalDraw,
- initInteractions,
- Plots.addLinks,
- Plots.rehover,
- // TODO: doAutoMargin is only needed here for axis automargin, which
- // happens outside of marginPushers where all the other automargins are
- // calculated. Would be much better to separate margin calculations from
- // component drawing - see https://github.com/plotly/plotly.js/issues/2704
- Plots.doAutoMargin,
- Plots.previousPromises
- );
-
- // even if everything we did was synchronous, return a promise
- // so that the caller doesn't care which route we took
- var plotDone = Lib.syncOrAsync(seq, gd);
- if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
-
- return plotDone.then(function() {
- emitAfterPlot(gd);
- return gd;
- });
- };
-
- function emitAfterPlot(gd) {
- var fullLayout = gd._fullLayout;
-
- if(fullLayout._redrawFromAutoMarginCount) {
- fullLayout._redrawFromAutoMarginCount--;
- } else {
- gd.emit('plotly_afterplot');
- }
- }
-
- exports.setPlotConfig = function setPlotConfig(obj) {
- return Lib.extendFlat(dfltConfig, obj);
- };
-
- function setBackground(gd, bgColor) {
- try {
- gd._fullLayout._paper.style('background', bgColor);
- } catch(e) {
- Lib.error(e);
- }
- }
-
- function opaqueSetBackground(gd, bgColor) {
- var blend = Color.combine(bgColor, 'white');
- setBackground(gd, blend);
- }
-
- function setPlotContext(gd, config) {
- if(!gd._context) {
- gd._context = Lib.extendDeep({}, dfltConfig);
-
- // stash <base> href, used to make robust clipPath URLs
- var base = d3.select('base');
- gd._context._baseUrl = base.size() && base.attr('href') ?
- window.location.href.split('#')[0] :
- '';
- }
-
- var context = gd._context;
-
- var i, keys, key;
-
- if(config) {
- keys = Object.keys(config);
- for(i = 0; i < keys.length; i++) {
- key = keys[i];
- if(key === 'editable' || key === 'edits') continue;
- if(key in context) {
- if(key === 'setBackground' && config[key] === 'opaque') {
- context[key] = opaqueSetBackground;
- } else {
- context[key] = config[key];
- }
- }
- }
-
- // map plot3dPixelRatio to plotGlPixelRatio for backward compatibility
- if(config.plot3dPixelRatio && !context.plotGlPixelRatio) {
- context.plotGlPixelRatio = context.plot3dPixelRatio;
- }
-
- // now deal with editable and edits - first editable overrides
- // everything, then edits refines
- var editable = config.editable;
- if(editable !== undefined) {
- // we're not going to *use* context.editable, we're only going to
- // use context.edits... but keep it for the record
- context.editable = editable;
-
- keys = Object.keys(context.edits);
- for(i = 0; i < keys.length; i++) {
- context.edits[keys[i]] = editable;
- }
- }
- if(config.edits) {
- keys = Object.keys(config.edits);
- for(i = 0; i < keys.length; i++) {
- key = keys[i];
- if(key in context.edits) {
- context.edits[key] = config.edits[key];
- }
- }
- }
-
- // not part of the user-facing config options
- context._exportedPlot = config._exportedPlot;
- }
-
- // staticPlot forces a bunch of others:
- if(context.staticPlot) {
- context.editable = false;
- context.edits = {};
- context.autosizable = false;
- context.scrollZoom = false;
- context.doubleClick = false;
- context.showTips = false;
- context.showLink = false;
- context.displayModeBar = false;
- }
-
- // make sure hover-only devices have mode bar visible
- if(context.displayModeBar === 'hover' && !hasHover) {
- context.displayModeBar = true;
- }
-
- // default and fallback for setBackground
- if(context.setBackground === 'transparent' || typeof context.setBackground !== 'function') {
- context.setBackground = setBackground;
- }
-
- // Check if gd has a specified widht/height to begin with
- context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
- context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;
-
- // fill context._scrollZoom helper to help manage scrollZoom flaglist
- var szIn = context.scrollZoom;
- var szOut = context._scrollZoom = {};
- if(szIn === true) {
- szOut.cartesian = 1;
- szOut.gl3d = 1;
- szOut.geo = 1;
- szOut.mapbox = 1;
- } else if(typeof szIn === 'string') {
- var parts = szIn.split('+');
- for(i = 0; i < parts.length; i++) {
- szOut[parts[i]] = 1;
- }
- } else if(szIn !== false) {
- szOut.gl3d = 1;
- szOut.geo = 1;
- szOut.mapbox = 1;
- }
- }
-
- function plotLegacyPolar(gd, data, layout) {
- // build or reuse the container skeleton
- var plotContainer = d3.select(gd).selectAll('.plot-container')
- .data([0]);
- plotContainer.enter()
- .insert('div', ':first-child')
- .classed('plot-container plotly', true);
- var paperDiv = plotContainer.selectAll('.svg-container')
- .data([0]);
- paperDiv.enter().append('div')
- .classed('svg-container', true)
- .style('position', 'relative');
-
- // empty it everytime for now
- paperDiv.html('');
-
- // fulfill gd requirements
- if(data) gd.data = data;
- if(layout) gd.layout = layout;
- Polar.manager.fillLayout(gd);
-
- // resize canvas
- paperDiv.style({
- width: gd._fullLayout.width + 'px',
- height: gd._fullLayout.height + 'px'
- });
-
- // instantiate framework
- gd.framework = Polar.manager.framework(gd);
-
- // plot
- gd.framework({data: gd.data, layout: gd.layout}, paperDiv.node());
-
- // set undo point
- gd.framework.setUndoPoint();
-
- // get the resulting svg for extending it
- var polarPlotSVG = gd.framework.svg();
-
- // editable title
- var opacity = 1;
- var txt = gd._fullLayout.title ? gd._fullLayout.title.text : '';
- if(txt === '' || !txt) opacity = 0;
-
- var titleLayout = function() {
- this.call(svgTextUtils.convertToTspans, gd);
- // TODO: html/mathjax
- // TODO: center title
- };
-
- var title = polarPlotSVG.select('.title-group text')
- .call(titleLayout);
-
- if(gd._context.edits.titleText) {
- var placeholderText = Lib._(gd, 'Click to enter Plot title');
- if(!txt || txt === placeholderText) {
- opacity = 0.2;
- // placeholder is not going through convertToTspans
- // so needs explicit data-unformatted
- title.attr({'data-unformatted': placeholderText})
- .text(placeholderText)
- .style({opacity: opacity})
- .on('mouseover.opacity', function() {
- d3.select(this).transition().duration(100)
- .style('opacity', 1);
- })
- .on('mouseout.opacity', function() {
- d3.select(this).transition().duration(1000)
- .style('opacity', 0);
- });
- }
-
- var setContenteditable = function() {
- this.call(svgTextUtils.makeEditable, {gd: gd})
- .on('edit', function(text) {
- gd.framework({layout: {title: {text: text}}});
- this.text(text)
- .call(titleLayout);
- this.call(setContenteditable);
- })
- .on('cancel', function() {
- var txt = this.attr('data-unformatted');
- this.text(txt).call(titleLayout);
- });
- };
- title.call(setContenteditable);
- }
-
- gd._context.setBackground(gd, gd._fullLayout.paper_bgcolor);
- Plots.addLinks(gd);
-
- return Promise.resolve();
- }
-
- // convenience function to force a full redraw, mostly for use by plotly.js
- exports.redraw = function(gd) {
- gd = Lib.getGraphDiv(gd);
-
- if(!Lib.isPlotDiv(gd)) {
- throw new Error('This element is not a Plotly plot: ' + gd);
- }
-
- helpers.cleanData(gd.data);
- helpers.cleanLayout(gd.layout);
-
- gd.calcdata = undefined;
- return exports.plot(gd).then(function() {
- gd.emit('plotly_redraw');
- return gd;
- });
- };
-
- /**
- * Convenience function to make idempotent plot option obvious to users.
- *
- * @param gd
- * @param {Object[]} data
- * @param {Object} layout
- * @param {Object} config
- */
- exports.newPlot = function(gd, data, layout, config) {
- gd = Lib.getGraphDiv(gd);
-
- // remove gl contexts
- Plots.cleanPlot([], {}, gd._fullData || [], gd._fullLayout || {});
-
- Plots.purge(gd);
- return exports.plot(gd, data, layout, config);
- };
-
- /**
- * Wrap negative indicies to their positive counterparts.
- *
- * @param {Number[]} indices An array of indices
- * @param {Number} maxIndex The maximum index allowable (arr.length - 1)
- */
- function positivifyIndices(indices, maxIndex) {
- var parentLength = maxIndex + 1;
- var positiveIndices = [];
- var i;
- var index;
-
- for(i = 0; i < indices.length; i++) {
- index = indices[i];
- if(index < 0) {
- positiveIndices.push(parentLength + index);
- } else {
- positiveIndices.push(index);
- }
- }
- return positiveIndices;
- }
-
- /**
- * Ensures that an index array for manipulating gd.data is valid.
- *
- * Intended for use with addTraces, deleteTraces, and moveTraces.
- *
- * @param gd
- * @param indices
- * @param arrayName
- */
- function assertIndexArray(gd, indices, arrayName) {
- var i,
- index;
-
- for(i = 0; i < indices.length; i++) {
- index = indices[i];
-
- // validate that indices are indeed integers
- if(index !== parseInt(index, 10)) {
- throw new Error('all values in ' + arrayName + ' must be integers');
- }
-
- // check that all indices are in bounds for given gd.data array length
- if(index >= gd.data.length || index < -gd.data.length) {
- throw new Error(arrayName + ' must be valid indices for gd.data.');
- }
-
- // check that indices aren't repeated
- if(indices.indexOf(index, i + 1) > -1 ||
- index >= 0 && indices.indexOf(-gd.data.length + index) > -1 ||
- index < 0 && indices.indexOf(gd.data.length + index) > -1) {
- throw new Error('each index in ' + arrayName + ' must be unique.');
- }
- }
- }
-
- /**
- * Private function used by Plotly.moveTraces to check input args
- *
- * @param gd
- * @param currentIndices
- * @param newIndices
- */
- function checkMoveTracesArgs(gd, currentIndices, newIndices) {
-
- // check that gd has attribute 'data' and 'data' is array
- if(!Array.isArray(gd.data)) {
- throw new Error('gd.data must be an array.');
- }
-
- // validate currentIndices array
- if(typeof currentIndices === 'undefined') {
- throw new Error('currentIndices is a required argument.');
- } else if(!Array.isArray(currentIndices)) {
- currentIndices = [currentIndices];
- }
- assertIndexArray(gd, currentIndices, 'currentIndices');
-
- // validate newIndices array if it exists
- if(typeof newIndices !== 'undefined' && !Array.isArray(newIndices)) {
- newIndices = [newIndices];
- }
- if(typeof newIndices !== 'undefined') {
- assertIndexArray(gd, newIndices, 'newIndices');
- }
-
- // check currentIndices and newIndices are the same length if newIdices exists
- if(typeof newIndices !== 'undefined' && currentIndices.length !== newIndices.length) {
- throw new Error('current and new indices must be of equal length.');
- }
-
- }
- /**
- * A private function to reduce the type checking clutter in addTraces.
- *
- * @param gd
- * @param traces
- * @param newIndices
- */
- function checkAddTracesArgs(gd, traces, newIndices) {
- var i, value;
-
- // check that gd has attribute 'data' and 'data' is array
- if(!Array.isArray(gd.data)) {
- throw new Error('gd.data must be an array.');
- }
-
- // make sure traces exists
- if(typeof traces === 'undefined') {
- throw new Error('traces must be defined.');
- }
-
- // make sure traces is an array
- if(!Array.isArray(traces)) {
- traces = [traces];
- }
-
- // make sure each value in traces is an object
- for(i = 0; i < traces.length; i++) {
- value = traces[i];
- if(typeof value !== 'object' || (Array.isArray(value) || value === null)) {
- throw new Error('all values in traces array must be non-array objects');
- }
- }
-
- // make sure we have an index for each trace
- if(typeof newIndices !== 'undefined' && !Array.isArray(newIndices)) {
- newIndices = [newIndices];
- }
- if(typeof newIndices !== 'undefined' && newIndices.length !== traces.length) {
- throw new Error(
- 'if indices is specified, traces.length must equal indices.length'
- );
- }
- }
-
- /**
- * A private function to reduce the type checking clutter in spliceTraces.
- * Get all update Properties from gd.data. Validate inputs and outputs.
- * Used by prependTrace and extendTraces
- *
- * @param gd
- * @param update
- * @param indices
- * @param maxPoints
- */
- function assertExtendTracesArgs(gd, update, indices, maxPoints) {
-
- var maxPointsIsObject = Lib.isPlainObject(maxPoints);
-
- if(!Array.isArray(gd.data)) {
- throw new Error('gd.data must be an array');
- }
- if(!Lib.isPlainObject(update)) {
- throw new Error('update must be a key:value object');
- }
-
- if(typeof indices === 'undefined') {
- throw new Error('indices must be an integer or array of integers');
- }
-
- assertIndexArray(gd, indices, 'indices');
-
- for(var key in update) {
-
- /*
- * Verify that the attribute to be updated contains as many trace updates
- * as indices. Failure must result in throw and no-op
- */
- if(!Array.isArray(update[key]) || update[key].length !== indices.length) {
- throw new Error('attribute ' + key + ' must be an array of length equal to indices array length');
- }
-
- /*
- * if maxPoints is an object it must match keys and array lengths of 'update' 1:1
- */
- if(maxPointsIsObject &&
- (!(key in maxPoints) || !Array.isArray(maxPoints[key]) ||
- maxPoints[key].length !== update[key].length)) {
- throw new Error('when maxPoints is set as a key:value object it must contain a 1:1 ' +
- 'corrispondence with the keys and number of traces in the update object');
- }
- }
- }
-
- /**
- * A private function to reduce the type checking clutter in spliceTraces.
- *
- * @param {Object|HTMLDivElement} gd
- * @param {Object} update
- * @param {Number[]} indices
- * @param {Number||Object} maxPoints
- * @return {Object[]}
- */
- function getExtendProperties(gd, update, indices, maxPoints) {
-
- var maxPointsIsObject = Lib.isPlainObject(maxPoints);
- var updateProps = [];
- var trace, target, prop, insert, maxp;
-
- // allow scalar index to represent a single trace position
- if(!Array.isArray(indices)) indices = [indices];
-
- // negative indices are wrapped around to their positive value. Equivalent to python indexing.
- indices = positivifyIndices(indices, gd.data.length - 1);
-
- // loop through all update keys and traces and harvest validated data.
- for(var key in update) {
-
- for(var j = 0; j < indices.length; j++) {
-
- /*
- * Choose the trace indexed by the indices map argument and get the prop setter-getter
- * instance that references the key and value for this particular trace.
- */
- trace = gd.data[indices[j]];
- prop = nestedProperty(trace, key);
-
- /*
- * Target is the existing gd.data.trace.dataArray value like "x" or "marker.size"
- * Target must exist as an Array to allow the extend operation to be performed.
- */
- target = prop.get();
- insert = update[key][j];
-
- if(!Lib.isArrayOrTypedArray(insert)) {
- throw new Error('attribute: ' + key + ' index: ' + j + ' must be an array');
- }
- if(!Lib.isArrayOrTypedArray(target)) {
- throw new Error('cannot extend missing or non-array attribute: ' + key);
- }
- if(target.constructor !== insert.constructor) {
- throw new Error('cannot extend array with an array of a different type: ' + key);
- }
-
- /*
- * maxPoints may be an object map or a scalar. If object select the key:value, else
- * Use the scalar maxPoints for all key and trace combinations.
- */
- maxp = maxPointsIsObject ? maxPoints[key][j] : maxPoints;
-
- // could have chosen null here, -1 just tells us to not take a window
- if(!isNumeric(maxp)) maxp = -1;
-
- /*
- * Wrap the nestedProperty in an object containing required data
- * for lengthening and windowing this particular trace - key combination.
- * Flooring maxp mirrors the behaviour of floats in the Array.slice JSnative function.
- */
- updateProps.push({
- prop: prop,
- target: target,
- insert: insert,
- maxp: Math.floor(maxp)
- });
- }
- }
-
- // all target and insertion data now validated
- return updateProps;
- }
-
- /**
- * A private function to key Extend and Prepend traces DRY
- *
- * @param {Object|HTMLDivElement} gd
- * @param {Object} update
- * @param {Number[]} indices
- * @param {Number||Object} maxPoints
- * @param {Function} updateArray
- * @return {Object}
- */
- function spliceTraces(gd, update, indices, maxPoints, updateArray) {
- assertExtendTracesArgs(gd, update, indices, maxPoints);
-
- var updateProps = getExtendProperties(gd, update, indices, maxPoints);
- var undoUpdate = {};
- var undoPoints = {};
-
- for(var i = 0; i < updateProps.length; i++) {
- var prop = updateProps[i].prop;
- var maxp = updateProps[i].maxp;
-
- // return new array and remainder
- var out = updateArray(updateProps[i].target, updateProps[i].insert, maxp);
- prop.set(out[0]);
-
- // build the inverse update object for the undo operation
- if(!Array.isArray(undoUpdate[prop.astr])) undoUpdate[prop.astr] = [];
- undoUpdate[prop.astr].push(out[1]);
-
- // build the matching maxPoints undo object containing original trace lengths
- if(!Array.isArray(undoPoints[prop.astr])) undoPoints[prop.astr] = [];
- undoPoints[prop.astr].push(updateProps[i].target.length);
- }
-
- return {update: undoUpdate, maxPoints: undoPoints};
- }
-
- function concatTypedArray(arr0, arr1) {
- var arr2 = new arr0.constructor(arr0.length + arr1.length);
- arr2.set(arr0);
- arr2.set(arr1, arr0.length);
- return arr2;
- }
-
- /**
- * extend && prepend traces at indices with update arrays, window trace lengths to maxPoints
- *
- * Extend and Prepend have identical APIs. Prepend inserts an array at the head while Extend
- * inserts an array off the tail. Prepend truncates the tail of the array - counting maxPoints
- * from the head, whereas Extend truncates the head of the array, counting backward maxPoints
- * from the tail.
- *
- * If maxPoints is undefined, nonNumeric, negative or greater than extended trace length no
- * truncation / windowing will be performed. If its zero, well the whole trace is truncated.
- *
- * @param {Object|HTMLDivElement} gd The graph div
- * @param {Object} update The key:array map of target attributes to extend
- * @param {Number|Number[]} indices The locations of traces to be extended
- * @param {Number|Object} [maxPoints] Number of points for trace window after lengthening.
- *
- */
- exports.extendTraces = function extendTraces(gd, update, indices, maxPoints) {
- gd = Lib.getGraphDiv(gd);
-
- function updateArray(target, insert, maxp) {
- var newArray, remainder;
-
- if(Lib.isTypedArray(target)) {
- if(maxp < 0) {
- var none = new target.constructor(0);
- var both = concatTypedArray(target, insert);
-
- if(maxp < 0) {
- newArray = both;
- remainder = none;
- } else {
- newArray = none;
- remainder = both;
- }
- } else {
- newArray = new target.constructor(maxp);
- remainder = new target.constructor(target.length + insert.length - maxp);
-
- if(maxp === insert.length) {
- newArray.set(insert);
- remainder.set(target);
- } else if(maxp < insert.length) {
- var numberOfItemsFromInsert = insert.length - maxp;
-
- newArray.set(insert.subarray(numberOfItemsFromInsert));
- remainder.set(target);
- remainder.set(insert.subarray(0, numberOfItemsFromInsert), target.length);
- } else {
- var numberOfItemsFromTarget = maxp - insert.length;
- var targetBegin = target.length - numberOfItemsFromTarget;
-
- newArray.set(target.subarray(targetBegin));
- newArray.set(insert, numberOfItemsFromTarget);
- remainder.set(target.subarray(0, targetBegin));
- }
- }
- } else {
- newArray = target.concat(insert);
- remainder = (maxp >= 0 && maxp < newArray.length) ?
- newArray.splice(0, newArray.length - maxp) :
- [];
- }
-
- return [newArray, remainder];
- }
-
- var undo = spliceTraces(gd, update, indices, maxPoints, updateArray);
- var promise = exports.redraw(gd);
- var undoArgs = [gd, undo.update, indices, undo.maxPoints];
- Queue.add(gd, exports.prependTraces, undoArgs, extendTraces, arguments);
-
- return promise;
- };
-
- exports.prependTraces = function prependTraces(gd, update, indices, maxPoints) {
- gd = Lib.getGraphDiv(gd);
-
- function updateArray(target, insert, maxp) {
- var newArray, remainder;
-
- if(Lib.isTypedArray(target)) {
- if(maxp <= 0) {
- var none = new target.constructor(0);
- var both = concatTypedArray(insert, target);
-
- if(maxp < 0) {
- newArray = both;
- remainder = none;
- } else {
- newArray = none;
- remainder = both;
- }
- } else {
- newArray = new target.constructor(maxp);
- remainder = new target.constructor(target.length + insert.length - maxp);
-
- if(maxp === insert.length) {
- newArray.set(insert);
- remainder.set(target);
- } else if(maxp < insert.length) {
- var numberOfItemsFromInsert = insert.length - maxp;
-
- newArray.set(insert.subarray(0, numberOfItemsFromInsert));
- remainder.set(insert.subarray(numberOfItemsFromInsert));
- remainder.set(target, numberOfItemsFromInsert);
- } else {
- var numberOfItemsFromTarget = maxp - insert.length;
-
- newArray.set(insert);
- newArray.set(target.subarray(0, numberOfItemsFromTarget), insert.length);
- remainder.set(target.subarray(numberOfItemsFromTarget));
- }
- }
- } else {
- newArray = insert.concat(target);
- remainder = (maxp >= 0 && maxp < newArray.length) ?
- newArray.splice(maxp, newArray.length) :
- [];
- }
-
- return [newArray, remainder];
- }
-
- var undo = spliceTraces(gd, update, indices, maxPoints, updateArray);
- var promise = exports.redraw(gd);
- var undoArgs = [gd, undo.update, indices, undo.maxPoints];
- Queue.add(gd, exports.extendTraces, undoArgs, prependTraces, arguments);
-
- return promise;
- };
-
- /**
- * Add data traces to an existing graph div.
- *
- * @param {Object|HTMLDivElement} gd The graph div
- * @param {Object[]} gd.data The array of traces we're adding to
- * @param {Object[]|Object} traces The object or array of objects to add
- * @param {Number[]|Number} [newIndices=[gd.data.length]] Locations to add traces
- *
- */
- exports.addTraces = function addTraces(gd, traces, newIndices) {
- gd = Lib.getGraphDiv(gd);
-
- var currentIndices = [];
- var undoFunc = exports.deleteTraces;
- var redoFunc = addTraces;
- var undoArgs = [gd, currentIndices];
- var redoArgs = [gd, traces]; // no newIndices here
- var i;
- var promise;
-
- // all validation is done elsewhere to remove clutter here
- checkAddTracesArgs(gd, traces, newIndices);
-
- // make sure traces is an array
- if(!Array.isArray(traces)) {
- traces = [traces];
- }
-
- // make sure traces do not repeat existing ones
- traces = traces.map(function(trace) {
- return Lib.extendFlat({}, trace);
- });
-
- helpers.cleanData(traces);
-
- // add the traces to gd.data (no redrawing yet!)
- for(i = 0; i < traces.length; i++) {
- gd.data.push(traces[i]);
- }
-
- // to continue, we need to call moveTraces which requires currentIndices
- for(i = 0; i < traces.length; i++) {
- currentIndices.push(-traces.length + i);
- }
-
- // if the user didn't define newIndices, they just want the traces appended
- // i.e., we can simply redraw and be done
- if(typeof newIndices === 'undefined') {
- promise = exports.redraw(gd);
- Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
- return promise;
- }
-
- // make sure indices is property defined
- if(!Array.isArray(newIndices)) {
- newIndices = [newIndices];
- }
-
- try {
-
- // this is redundant, but necessary to not catch later possible errors!
- checkMoveTracesArgs(gd, currentIndices, newIndices);
- }
- catch(error) {
-
- // something went wrong, reset gd to be safe and rethrow error
- gd.data.splice(gd.data.length - traces.length, traces.length);
- throw error;
- }
-
- // if we're here, the user has defined specific places to place the new traces
- // this requires some extra work that moveTraces will do
- Queue.startSequence(gd);
- Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
- promise = exports.moveTraces(gd, currentIndices, newIndices);
- Queue.stopSequence(gd);
- return promise;
- };
-
- /**
- * Delete traces at `indices` from gd.data array.
- *
- * @param {Object|HTMLDivElement} gd The graph div
- * @param {Object[]} gd.data The array of traces we're removing from
- * @param {Number|Number[]} indices The indices
- */
- exports.deleteTraces = function deleteTraces(gd, indices) {
- gd = Lib.getGraphDiv(gd);
-
- var traces = [];
- var undoFunc = exports.addTraces;
- var redoFunc = deleteTraces;
- var undoArgs = [gd, traces, indices];
- var redoArgs = [gd, indices];
- var i;
- var deletedTrace;
-
- // make sure indices are defined
- if(typeof indices === 'undefined') {
- throw new Error('indices must be an integer or array of integers.');
- } else if(!Array.isArray(indices)) {
- indices = [indices];
- }
- assertIndexArray(gd, indices, 'indices');
-
- // convert negative indices to positive indices
- indices = positivifyIndices(indices, gd.data.length - 1);
-
- // we want descending here so that splicing later doesn't affect indexing
- indices.sort(Lib.sorterDes);
- for(i = 0; i < indices.length; i += 1) {
- deletedTrace = gd.data.splice(indices[i], 1)[0];
- traces.push(deletedTrace);
- }
-
- var promise = exports.redraw(gd);
- Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
-
- return promise;
- };
-
- /**
- * Move traces at currentIndices array to locations in newIndices array.
- *
- * If newIndices is omitted, currentIndices will be moved to the end. E.g.,
- * these are equivalent:
- *
- * Plotly.moveTraces(gd, [1, 2, 3], [-3, -2, -1])
- * Plotly.moveTraces(gd, [1, 2, 3])
- *
- * @param {Object|HTMLDivElement} gd The graph div
- * @param {Object[]} gd.data The array of traces we're removing from
- * @param {Number|Number[]} currentIndices The locations of traces to be moved
- * @param {Number|Number[]} [newIndices] The locations to move traces to
- *
- * Example calls:
- *
- * // move trace i to location x
- * Plotly.moveTraces(gd, i, x)
- *
- * // move trace i to end of array
- * Plotly.moveTraces(gd, i)
- *
- * // move traces i, j, k to end of array (i != j != k)
- * Plotly.moveTraces(gd, [i, j, k])
- *
- * // move traces [i, j, k] to [x, y, z] (i != j != k) (x != y != z)
- * Plotly.moveTraces(gd, [i, j, k], [x, y, z])
- *
- * // reorder all traces (assume there are 5--a, b, c, d, e)
- * Plotly.moveTraces(gd, [b, d, e, a, c]) // same as 'move to end'
- */
- exports.moveTraces = function moveTraces(gd, currentIndices, newIndices) {
- gd = Lib.getGraphDiv(gd);
-
- var newData = [];
- var movingTraceMap = [];
- var undoFunc = moveTraces;
- var redoFunc = moveTraces;
- var undoArgs = [gd, newIndices, currentIndices];
- var redoArgs = [gd, currentIndices, newIndices];
- var i;
-
- // to reduce complexity here, check args elsewhere
- // this throws errors where appropriate
- checkMoveTracesArgs(gd, currentIndices, newIndices);
-
- // make sure currentIndices is an array
- currentIndices = Array.isArray(currentIndices) ? currentIndices : [currentIndices];
-
- // if undefined, define newIndices to point to the end of gd.data array
- if(typeof newIndices === 'undefined') {
- newIndices = [];
- for(i = 0; i < currentIndices.length; i++) {
- newIndices.push(-currentIndices.length + i);
- }
- }
-
- // make sure newIndices is an array if it's user-defined
- newIndices = Array.isArray(newIndices) ? newIndices : [newIndices];
-
- // convert negative indices to positive indices (they're the same length)
- currentIndices = positivifyIndices(currentIndices, gd.data.length - 1);
- newIndices = positivifyIndices(newIndices, gd.data.length - 1);
-
- // at this point, we've coerced the index arrays into predictable forms
-
- // get the traces that aren't being moved around
- for(i = 0; i < gd.data.length; i++) {
-
- // if index isn't in currentIndices, include it in ignored!
- if(currentIndices.indexOf(i) === -1) {
- newData.push(gd.data[i]);
- }
- }
-
- // get a mapping of indices to moving traces
- for(i = 0; i < currentIndices.length; i++) {
- movingTraceMap.push({newIndex: newIndices[i], trace: gd.data[currentIndices[i]]});
- }
-
- // reorder this mapping by newIndex, ascending
- movingTraceMap.sort(function(a, b) {
- return a.newIndex - b.newIndex;
- });
-
- // now, add the moving traces back in, in order!
- for(i = 0; i < movingTraceMap.length; i += 1) {
- newData.splice(movingTraceMap[i].newIndex, 0, movingTraceMap[i].trace);
- }
-
- gd.data = newData;
-
- var promise = exports.redraw(gd);
- Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
-
- return promise;
- };
-
- /**
- * restyle: update trace attributes of an existing plot
- *
- * Can be called two ways.
- *
- * Signature 1:
- * @param {String | HTMLDivElement} gd
- * the id or DOM element of the graph container div
- * @param {String} astr
- * attribute string (like `'marker.symbol'`) to update
- * @param {*} val
- * value to give this attribute
- * @param {Number[] | Number} [traces]
- * integer or array of integers for the traces to alter (all if omitted)
- *
- * Signature 2:
- * @param {String | HTMLDivElement} gd
- * (as in signature 1)
- * @param {Object} aobj
- * attribute object `{astr1: val1, astr2: val2 ...}`
- * allows setting multiple attributes simultaneously
- * @param {Number[] | Number} [traces]
- * (as in signature 1)
- *
- * `val` (or `val1`, `val2` ... in the object form) can be an array,
- * to apply different values to each trace.
- *
- * If the array is too short, it will wrap around (useful for
- * style files that want to specify cyclical default values).
- */
- function restyle(gd, astr, val, _traces) {
- gd = Lib.getGraphDiv(gd);
- helpers.clearPromiseQueue(gd);
-
- var aobj = {};
- if(typeof astr === 'string') aobj[astr] = val;
- else if(Lib.isPlainObject(astr)) {
- // the 3-arg form
- aobj = Lib.extendFlat({}, astr);
- if(_traces === undefined) _traces = val;
- }
- else {
- Lib.warn('Restyle fail.', astr, val, _traces);
- return Promise.reject();
- }
-
- if(Object.keys(aobj).length) gd.changed = true;
-
- var traces = helpers.coerceTraceIndices(gd, _traces);
-
- var specs = _restyle(gd, aobj, traces);
- var flags = specs.flags;
-
- // clear calcdata and/or axis types if required so they get regenerated
- if(flags.calc) gd.calcdata = undefined;
- if(flags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, {});
-
- // fill in redraw sequence
- var seq = [];
-
- if(flags.fullReplot) {
- seq.push(exports.plot);
- } else {
- seq.push(Plots.previousPromises);
-
- // maybe only call Plots.supplyDataDefaults in the splom case,
- // to skip over long and slow axes defaults
- Plots.supplyDefaults(gd);
-
- if(flags.markerSize) {
- Plots.doCalcdata(gd);
- addAxRangeSequence(seq);
-
- // TODO
- // if all axes have autorange:false, then
- // proceed to subroutines.doTraceStyle(),
- // otherwise we must go through addAxRangeSequence,
- // which in general must redraws 'all' axes
- }
-
- if(flags.style) seq.push(subroutines.doTraceStyle);
- if(flags.colorbars) seq.push(subroutines.doColorBars);
-
- seq.push(emitAfterPlot);
- }
-
- seq.push(Plots.rehover);
-
- Queue.add(gd,
- restyle, [gd, specs.undoit, specs.traces],
- restyle, [gd, specs.redoit, specs.traces]
- );
-
- var plotDone = Lib.syncOrAsync(seq, gd);
- if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
-
- return plotDone.then(function() {
- gd.emit('plotly_restyle', specs.eventData);
- return gd;
- });
- }
- exports.restyle = restyle;
-
- // for undo: undefined initial vals must be turned into nulls
- // so that we unset rather than ignore them
- function undefinedToNull(val) {
- if(val === undefined) return null;
- return val;
- }
-
- /**
- * Factory function to wrap nestedProperty with GUI edits if necessary
- * with GUI edits we add an optional prefix to the nestedProperty constructor
- * to prepend to the attribute string in the preGUI store.
- */
- function makeNP(preGUI, guiEditFlag) {
- if(!guiEditFlag) return nestedProperty;
-
- return function(container, attr, prefix) {
- var np = nestedProperty(container, attr);
- var npSet = np.set;
- np.set = function(val) {
- var fullAttr = (prefix || '') + attr;
- storeCurrent(fullAttr, np.get(), val, preGUI);
- npSet(val);
- };
- return np;
- };
- }
-
- function storeCurrent(attr, val, newVal, preGUI) {
- if(Array.isArray(val) || Array.isArray(newVal)) {
- var arrayVal = Array.isArray(val) ? val : [];
- var arrayNew = Array.isArray(newVal) ? newVal : [];
- var maxLen = Math.max(arrayVal.length, arrayNew.length);
- for(var i = 0; i < maxLen; i++) {
- storeCurrent(attr + '[' + i + ']', arrayVal[i], arrayNew[i], preGUI);
- }
- }
- else if(Lib.isPlainObject(val) || Lib.isPlainObject(newVal)) {
- var objVal = Lib.isPlainObject(val) ? val : {};
- var objNew = Lib.isPlainObject(newVal) ? newVal : {};
- var objBoth = Lib.extendFlat({}, objVal, objNew);
- for(var key in objBoth) {
- storeCurrent(attr + '.' + key, objVal[key], objNew[key], preGUI);
- }
- }
- else if(preGUI[attr] === undefined) {
- preGUI[attr] = undefinedToNull(val);
- }
- }
-
- /**
- * storeDirectGUIEdit: for routines that skip restyle/relayout and mock it
- * by emitting a plotly_restyle or plotly_relayout event, this routine
- * keeps track of the initial state in _preGUI for use by uirevision
- * Does *not* apply these changes to data/layout - that's the responsibility
- * of the calling routine.
- *
- * @param {object} container: the input attributes container (eg `layout` or a `trace`)
- * @param {object} preGUI: where original values should be stored, either
- * `layout._preGUI` or `layout._tracePreGUI[uid]`
- * @param {object} edits: the {attr: val} object as normally passed to `relayout` etc
- */
- exports._storeDirectGUIEdit = function(container, preGUI, edits) {
- for(var attr in edits) {
- var np = nestedProperty(container, attr);
- storeCurrent(attr, np.get(), edits[attr], preGUI);
- }
- };
-
- function _restyle(gd, aobj, traces) {
- var fullLayout = gd._fullLayout;
- var fullData = gd._fullData;
- var data = gd.data;
- var guiEditFlag = fullLayout._guiEditing;
- var layoutNP = makeNP(fullLayout._preGUI, guiEditFlag);
- var eventData = Lib.extendDeepAll({}, aobj);
- var i;
-
- cleanDeprecatedAttributeKeys(aobj);
-
- // initialize flags
- var flags = editTypes.traceFlags();
-
- // copies of the change (and previous values of anything affected)
- // for the undo / redo queue
- var redoit = {};
- var undoit = {};
- var axlist;
-
- // make a new empty vals array for undoit
- function a0() { return traces.map(function() { return undefined; }); }
-
- // for autoranging multiple axes
- function addToAxlist(axid) {
- var axName = Axes.id2name(axid);
- if(axlist.indexOf(axName) === -1) axlist.push(axName);
- }
-
- function autorangeAttr(axName) { return 'LAYOUT' + axName + '.autorange'; }
-
- function rangeAttr(axName) { return 'LAYOUT' + axName + '.range'; }
-
- function getFullTrace(traceIndex) {
- // usually fullData maps 1:1 onto data, but with groupby transforms
- // the fullData index can be greater. Take the *first* matching trace.
- for(var j = traceIndex; j < fullData.length; j++) {
- if(fullData[j]._input === data[traceIndex]) return fullData[j];
- }
- // should never get here - and if we *do* it should cause an error
- // later on undefined fullTrace is passed to nestedProperty.
- }
-
- // for attrs that interact (like scales & autoscales), save the
- // old vals before making the change
- // val=undefined will not set a value, just record what the value was.
- // val=null will delete the attribute
- // attr can be an array to set several at once (all to the same val)
- function doextra(attr, val, i) {
- if(Array.isArray(attr)) {
- attr.forEach(function(a) { doextra(a, val, i); });
- return;
- }
- // quit if explicitly setting this elsewhere
- if(attr in aobj || helpers.hasParent(aobj, attr)) return;
-
- var extraparam;
- if(attr.substr(0, 6) === 'LAYOUT') {
- extraparam = layoutNP(gd.layout, attr.replace('LAYOUT', ''));
- } else {
- var tracei = traces[i];
- var preGUI = fullLayout._tracePreGUI[getFullTrace(tracei)._fullInput.uid];
- extraparam = makeNP(preGUI, guiEditFlag)(data[tracei], attr);
- }
-
- if(!(attr in undoit)) {
- undoit[attr] = a0();
- }
- if(undoit[attr][i] === undefined) {
- undoit[attr][i] = undefinedToNull(extraparam.get());
- }
- if(val !== undefined) {
- extraparam.set(val);
- }
- }
-
- function allBins(binAttr) {
- return function(j) {
- return fullData[j][binAttr];
- };
- }
-
- function arrayBins(binAttr) {
- return function(vij, j) {
- return vij === false ? fullData[traces[j]][binAttr] : null;
- };
- }
-
- // now make the changes to gd.data (and occasionally gd.layout)
- // and figure out what kind of graphics update we need to do
- for(var ai in aobj) {
- if(helpers.hasParent(aobj, ai)) {
- throw new Error('cannot set ' + ai + 'and a parent attribute simultaneously');
- }
-
- var vi = aobj[ai];
- var cont;
- var contFull;
- var param;
- var oldVal;
- var newVal;
- var valObject;
-
- // Backward compatibility shim for turning histogram autobin on,
- // or freezing previous autobinned values.
- // Replace obsolete `autobin(x|y): true` with `(x|y)bins: null`
- // and `autobin(x|y): false` with the `(x|y)bins` in `fullData`
- if(ai === 'autobinx' || ai === 'autobiny') {
- ai = ai.charAt(ai.length - 1) + 'bins';
- if(Array.isArray(vi)) vi = vi.map(arrayBins(ai));
- else if(vi === false) vi = traces.map(allBins(ai));
- else vi = null;
- }
-
- redoit[ai] = vi;
-
- if(ai.substr(0, 6) === 'LAYOUT') {
- param = layoutNP(gd.layout, ai.replace('LAYOUT', ''));
- undoit[ai] = [undefinedToNull(param.get())];
- // since we're allowing val to be an array, allow it here too,
- // even though that's meaningless
- param.set(Array.isArray(vi) ? vi[0] : vi);
- // ironically, the layout attrs in restyle only require replot,
- // not relayout
- flags.calc = true;
- continue;
- }
-
- // set attribute in gd.data
- undoit[ai] = a0();
- for(i = 0; i < traces.length; i++) {
- cont = data[traces[i]];
- contFull = getFullTrace(traces[i]);
- var preGUI = fullLayout._tracePreGUI[contFull._fullInput.uid];
- param = makeNP(preGUI, guiEditFlag)(cont, ai);
- oldVal = param.get();
- newVal = Array.isArray(vi) ? vi[i % vi.length] : vi;
-
- if(newVal === undefined) continue;
-
- var finalPart = param.parts[param.parts.length - 1];
- var prefix = ai.substr(0, ai.length - finalPart.length - 1);
- var prefixDot = prefix ? prefix + '.' : '';
- var innerContFull = prefix ?
- nestedProperty(contFull, prefix).get() : contFull;
-
- valObject = PlotSchema.getTraceValObject(contFull, param.parts);
-
- if(valObject && valObject.impliedEdits && newVal !== null) {
- for(var impliedKey in valObject.impliedEdits) {
- doextra(Lib.relativeAttr(ai, impliedKey), valObject.impliedEdits[impliedKey], i);
- }
- }
-
- // changing colorbar size modes,
- // make the resulting size not change
- // note that colorbar fractional sizing is based on the
- // original plot size, before anything (like a colorbar)
- // increases the margins
- else if((finalPart === 'thicknessmode' || finalPart === 'lenmode') &&
- oldVal !== newVal &&
- (newVal === 'fraction' || newVal === 'pixels') &&
- innerContFull
- ) {
- var gs = fullLayout._size;
- var orient = innerContFull.orient;
- var topOrBottom = (orient === 'top') || (orient === 'bottom');
- if(finalPart === 'thicknessmode') {
- var thicknorm = topOrBottom ? gs.h : gs.w;
- doextra(prefixDot + 'thickness', innerContFull.thickness *
- (newVal === 'fraction' ? 1 / thicknorm : thicknorm), i);
- }
- else {
- var lennorm = topOrBottom ? gs.w : gs.h;
- doextra(prefixDot + 'len', innerContFull.len *
- (newVal === 'fraction' ? 1 / lennorm : lennorm), i);
- }
- }
-
- else if(ai === 'type' && (newVal === 'pie') !== (oldVal === 'pie')) {
- var labelsTo = 'x';
- var valuesTo = 'y';
- if((newVal === 'bar' || oldVal === 'bar') && cont.orientation === 'h') {
- labelsTo = 'y';
- valuesTo = 'x';
- }
- Lib.swapAttrs(cont, ['?', '?src'], 'labels', labelsTo);
- Lib.swapAttrs(cont, ['d?', '?0'], 'label', labelsTo);
- Lib.swapAttrs(cont, ['?', '?src'], 'values', valuesTo);
-
- if(oldVal === 'pie') {
- nestedProperty(cont, 'marker.color')
- .set(nestedProperty(cont, 'marker.colors').get());
-
- // super kludgy - but if all pies are gone we won't remove them otherwise
- fullLayout._pielayer.selectAll('g.trace').remove();
- } else if(Registry.traceIs(cont, 'cartesian')) {
- nestedProperty(cont, 'marker.colors')
- .set(nestedProperty(cont, 'marker.color').get());
- }
- }
-
- undoit[ai][i] = undefinedToNull(oldVal);
- // set the new value - if val is an array, it's one el per trace
- // first check for attributes that get more complex alterations
- var swapAttrs = [
- 'swapxy', 'swapxyaxes', 'orientation', 'orientationaxes'
- ];
- if(swapAttrs.indexOf(ai) !== -1) {
- // setting an orientation: make sure it's changing
- // before we swap everything else
- if(ai === 'orientation') {
- param.set(newVal);
- // obnoxious that we need this level of coupling... but in order to
- // properly handle setting orientation to `null` we need to mimic
- // the logic inside Bars.supplyDefaults for default orientation
- var defaultOrientation = (cont.x && !cont.y) ? 'h' : 'v';
- if((param.get() || defaultOrientation) === contFull.orientation) {
- continue;
- }
- }
- // orientationaxes has no value,
- // it flips everything and the axes
- else if(ai === 'orientationaxes') {
- cont.orientation =
- {v: 'h', h: 'v'}[contFull.orientation];
- }
- helpers.swapXYData(cont);
- flags.calc = flags.clearAxisTypes = true;
- }
- else if(Plots.dataArrayContainers.indexOf(param.parts[0]) !== -1) {
- // TODO: use manageArrays.applyContainerArrayChanges here too
- helpers.manageArrayContainers(param, newVal, undoit);
- flags.calc = true;
- }
- else {
- if(valObject) {
- // must redo calcdata when restyling array values of arrayOk attributes
- // ... but no need to this for regl-based traces
- if(valObject.arrayOk &&
- !Registry.traceIs(contFull, 'regl') &&
- (Lib.isArrayOrTypedArray(newVal) || Lib.isArrayOrTypedArray(oldVal))
- ) {
- flags.calc = true;
- }
- else editTypes.update(flags, valObject);
- }
- else {
- /*
- * if we couldn't find valObject, assume a full recalc.
- * This can happen if you're changing type and making
- * some other edits too, so the modules we're
- * looking at don't have these attributes in them.
- */
- flags.calc = true;
- }
-
- // all the other ones, just modify that one attribute
- param.set(newVal);
- }
- }
-
- // swap the data attributes of the relevant x and y axes?
- if(['swapxyaxes', 'orientationaxes'].indexOf(ai) !== -1) {
- Axes.swap(gd, traces);
- }
-
- // swap hovermode if set to "compare x/y data"
- if(ai === 'orientationaxes') {
- var hovermode = nestedProperty(gd.layout, 'hovermode');
- if(hovermode.get() === 'x') {
- hovermode.set('y');
- } else if(hovermode.get() === 'y') {
- hovermode.set('x');
- }
- }
-
- // Major enough changes deserve autoscale and
- // non-reversed axes so people don't get confused
- //
- // Note: autobin (or its new analog bin clearing) is not included here
- // since we're not pushing bins back to gd.data, so if we have bin
- // info it was explicitly provided by the user.
- if(['orientation', 'type'].indexOf(ai) !== -1) {
- axlist = [];
- for(i = 0; i < traces.length; i++) {
- var trace = data[traces[i]];
-
- if(Registry.traceIs(trace, 'cartesian')) {
- addToAxlist(trace.xaxis || 'x');
- addToAxlist(trace.yaxis || 'y');
- }
- }
-
- doextra(axlist.map(autorangeAttr), true, 0);
- doextra(axlist.map(rangeAttr), [0, 1], 0);
- }
- }
-
- if(flags.calc || flags.plot) {
- flags.fullReplot = true;
- }
-
- return {
- flags: flags,
- undoit: undoit,
- redoit: redoit,
- traces: traces,
- eventData: Lib.extendDeepNoArrays([], [eventData, traces])
- };
- }
-
- /**
- * Converts deprecated attribute keys to
- * the current API to ensure backwards compatibility.
- *
- * This is needed for the update mechanism to determine which
- * subroutines to run based on the actual attribute
- * definitions (that don't include the deprecated ones).
- *
- * E.g. Maps {'xaxis.title': 'A chart'} to {'xaxis.title.text': 'A chart'}
- * and {titlefont: {...}} to {'title.font': {...}}.
- *
- * @param aobj
- */
- function cleanDeprecatedAttributeKeys(aobj) {
- var oldAxisTitleRegex = Lib.counterRegex('axis', '\.title', false, false);
- var colorbarRegex = /colorbar\.title$/;
- var keys = Object.keys(aobj);
- var i, key, value;
-
- for(i = 0; i < keys.length; i++) {
- key = keys[i];
- value = aobj[key];
-
- if((key === 'title' || oldAxisTitleRegex.test(key) || colorbarRegex.test(key)) &&
- (typeof value === 'string' || typeof value === 'number')) {
- replace(key, key.replace('title', 'title.text'));
- } else if(key.indexOf('titlefont') > -1) {
- replace(key, key.replace('titlefont', 'title.font'));
- } else if(key.indexOf('titleposition') > -1) {
- replace(key, key.replace('titleposition', 'title.position'));
- } else if(key.indexOf('titleside') > -1) {
- replace(key, key.replace('titleside', 'title.side'));
- } else if(key.indexOf('titleoffset') > -1) {
- replace(key, key.replace('titleoffset', 'title.offset'));
- }
- }
-
- function replace(oldAttrStr, newAttrStr) {
- aobj[newAttrStr] = aobj[oldAttrStr];
- delete aobj[oldAttrStr];
- }
- }
-
- /**
- * relayout: update layout attributes of an existing plot
- *
- * Can be called two ways:
- *
- * Signature 1:
- * @param {String | HTMLDivElement} gd
- * the id or dom element of the graph container div
- * @param {String} astr
- * attribute string (like `'xaxis.range[0]'`) to update
- * @param {*} val
- * value to give this attribute
- *
- * Signature 2:
- * @param {String | HTMLDivElement} gd
- * (as in signature 1)
- * @param {Object} aobj
- * attribute object `{astr1: val1, astr2: val2 ...}`
- * allows setting multiple attributes simultaneously
- */
- function relayout(gd, astr, val) {
- gd = Lib.getGraphDiv(gd);
- helpers.clearPromiseQueue(gd);
-
- if(gd.framework && gd.framework.isPolar) {
- return Promise.resolve(gd);
- }
-
- var aobj = {};
- if(typeof astr === 'string') {
- aobj[astr] = val;
- } else if(Lib.isPlainObject(astr)) {
- aobj = Lib.extendFlat({}, astr);
- } else {
- Lib.warn('Relayout fail.', astr, val);
- return Promise.reject();
- }
-
- if(Object.keys(aobj).length) gd.changed = true;
-
- var specs = _relayout(gd, aobj);
- var flags = specs.flags;
-
- // clear calcdata if required
- if(flags.calc) gd.calcdata = undefined;
-
- // fill in redraw sequence
-
- // even if we don't have anything left in aobj,
- // something may have happened within relayout that we
- // need to wait for
- var seq = [Plots.previousPromises];
-
- if(flags.layoutReplot) {
- seq.push(subroutines.layoutReplot);
- }
- else if(Object.keys(aobj).length) {
- axRangeSupplyDefaultsByPass(gd, flags, specs) || Plots.supplyDefaults(gd);
-
- if(flags.legend) seq.push(subroutines.doLegend);
- if(flags.layoutstyle) seq.push(subroutines.layoutStyles);
- if(flags.axrange) addAxRangeSequence(seq, specs.rangesAltered);
- if(flags.ticks) seq.push(subroutines.doTicksRelayout);
- if(flags.modebar) seq.push(subroutines.doModeBar);
- if(flags.camera) seq.push(subroutines.doCamera);
-
- seq.push(emitAfterPlot);
- }
-
- seq.push(Plots.rehover);
-
- Queue.add(gd,
- relayout, [gd, specs.undoit],
- relayout, [gd, specs.redoit]
- );
-
- var plotDone = Lib.syncOrAsync(seq, gd);
- if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
-
- return plotDone.then(function() {
- gd.emit('plotly_relayout', specs.eventData);
- return gd;
- });
- }
- exports.relayout = relayout;
-
- // Optimization mostly for large splom traces where
- // Plots.supplyDefaults can take > 100ms
- function axRangeSupplyDefaultsByPass(gd, flags, specs) {
- var k;
-
- if(!flags.axrange) return false;
-
- for(k in flags) {
- if(k !== 'axrange' && flags[k]) return false;
- }
-
- for(k in specs.rangesAltered) {
- var axName = Axes.id2name(k);
- var axIn = gd.layout[axName];
- var axOut = gd._fullLayout[axName];
- axOut.autorange = axIn.autorange;
- axOut.range = axIn.range.slice();
- axOut.cleanRange();
- }
- return true;
- }
-
- function addAxRangeSequence(seq, rangesAltered) {
- // N.B. leave as sequence of subroutines (for now) instead of
- // subroutine of its own so that finalDraw always gets
- // executed after drawData
- var drawAxes = rangesAltered ?
- function(gd) {
- var opts = {skipTitle: true};
- for(var id in rangesAltered) {
- if(Axes.getFromId(gd, id).automargin) {
- opts = {};
- break;
- }
- }
- return Axes.draw(gd, Object.keys(rangesAltered), opts);
- } :
- function(gd) {
- return Axes.draw(gd, 'redraw');
- };
-
- seq.push(
- subroutines.doAutoRangeAndConstraints,
- drawAxes,
- subroutines.drawData,
- subroutines.finalDraw
- );
- }
-
- var AX_RANGE_RE = /^[xyz]axis[0-9]*\.range(\[[0|1]\])?$/;
- var AX_AUTORANGE_RE = /^[xyz]axis[0-9]*\.autorange$/;
- var AX_DOMAIN_RE = /^[xyz]axis[0-9]*\.domain(\[[0|1]\])?$/;
-
- function _relayout(gd, aobj) {
- var layout = gd.layout;
- var fullLayout = gd._fullLayout;
- var guiEditFlag = fullLayout._guiEditing;
- var layoutNP = makeNP(fullLayout._preGUI, guiEditFlag);
- var keys = Object.keys(aobj);
- var axes = Axes.list(gd);
- var eventData = Lib.extendDeepAll({}, aobj);
- var arrayEdits = {};
-
- var arrayStr, i, j;
-
- cleanDeprecatedAttributeKeys(aobj);
- keys = Object.keys(aobj);
-
- // look for 'allaxes', split out into all axes
- // in case of 3D the axis are nested within a scene which is held in _id
- for(i = 0; i < keys.length; i++) {
- if(keys[i].indexOf('allaxes') === 0) {
- for(j = 0; j < axes.length; j++) {
- var scene = axes[j]._id.substr(1);
- var axisAttr = (scene.indexOf('scene') !== -1) ? (scene + '.') : '';
- var newkey = keys[i].replace('allaxes', axisAttr + axes[j]._name);
-
- if(!aobj[newkey]) aobj[newkey] = aobj[keys[i]];
- }
-
- delete aobj[keys[i]];
- }
- }
-
- // initialize flags
- var flags = editTypes.layoutFlags();
-
- // copies of the change (and previous values of anything affected)
- // for the undo / redo queue
- var redoit = {};
- var undoit = {};
-
- // for attrs that interact (like scales & autoscales), save the
- // old vals before making the change
- // val=undefined will not set a value, just record what the value was.
- // attr can be an array to set several at once (all to the same val)
- function doextra(attr, val) {
- if(Array.isArray(attr)) {
- attr.forEach(function(a) { doextra(a, val); });
- return;
- }
-
- // if we have another value for this attribute (explicitly or
- // via a parent) do not override with this auto-generated extra
- if(attr in aobj || helpers.hasParent(aobj, attr)) return;
-
- var p = layoutNP(layout, attr);
- if(!(attr in undoit)) {
- undoit[attr] = undefinedToNull(p.get());
- }
- if(val !== undefined) p.set(val);
- }
-
- // for constraint enforcement: keep track of all axes (as {id: name})
- // we're editing the (auto)range of, so we can tell the others constrained
- // to scale with them that it's OK for them to shrink
- var rangesAltered = {};
- var axId;
-
- function recordAlteredAxis(pleafPlus) {
- var axId = Axes.name2id(pleafPlus.split('.')[0]);
- rangesAltered[axId] = 1;
- return axId;
- }
-
- // alter gd.layout
- for(var ai in aobj) {
- if(helpers.hasParent(aobj, ai)) {
- throw new Error('cannot set ' + ai + 'and a parent attribute simultaneously');
- }
-
- var p = layoutNP(layout, ai);
- var vi = aobj[ai];
- var plen = p.parts.length;
- // p.parts may end with an index integer if the property is an array
- var pend = plen - 1;
- while(pend > 0 && typeof p.parts[pend] !== 'string') pend--;
- // last property in chain (leaf node)
- var pleaf = p.parts[pend];
- // leaf plus immediate parent
- var pleafPlus = p.parts[pend - 1] + '.' + pleaf;
- // trunk nodes (everything except the leaf)
- var ptrunk = p.parts.slice(0, pend).join('.');
- var parentIn = nestedProperty(gd.layout, ptrunk).get();
- var parentFull = nestedProperty(fullLayout, ptrunk).get();
- var vOld = p.get();
-
- if(vi === undefined) continue;
-
- redoit[ai] = vi;
-
- // axis reverse is special - it is its own inverse
- // op and has no flag.
- undoit[ai] = (pleaf === 'reverse') ? vi : undefinedToNull(vOld);
-
- var valObject = PlotSchema.getLayoutValObject(fullLayout, p.parts);
-
- if(valObject && valObject.impliedEdits && vi !== null) {
- for(var impliedKey in valObject.impliedEdits) {
- doextra(Lib.relativeAttr(ai, impliedKey), valObject.impliedEdits[impliedKey]);
- }
- }
-
- // Setting width or height to null must reset the graph's width / height
- // back to its initial value as computed during the first pass in Plots.plotAutoSize.
- //
- // To do so, we must manually set them back here using the _initialAutoSize cache.
- // can't use impliedEdits for this because behavior depends on vi
- if(['width', 'height'].indexOf(ai) !== -1) {
- if(vi) {
- doextra('autosize', null);
- // currently we don't support autosize one dim only - so
- // explicitly set the other one. Note that doextra will
- // ignore this if the same relayout call also provides oppositeAttr
- var oppositeAttr = ai === 'height' ? 'width' : 'height';
- doextra(oppositeAttr, fullLayout[oppositeAttr]);
- }
- else {
- fullLayout[ai] = gd._initialAutoSize[ai];
- }
- }
- else if(ai === 'autosize') {
- // depends on vi here too, so again can't use impliedEdits
- doextra('width', vi ? null : fullLayout.width);
- doextra('height', vi ? null : fullLayout.height);
- }
- // check autorange vs range
- else if(pleafPlus.match(AX_RANGE_RE)) {
- recordAlteredAxis(pleafPlus);
- nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
- }
- else if(pleafPlus.match(AX_AUTORANGE_RE)) {
- recordAlteredAxis(pleafPlus);
- nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
- var axFull = nestedProperty(fullLayout, ptrunk).get();
- if(axFull._inputDomain) {
- // if we're autoranging and this axis has a constrained domain,
- // reset it so we don't get locked into a shrunken size
- axFull._input.domain = axFull._inputDomain.slice();
- }
- }
- else if(pleafPlus.match(AX_DOMAIN_RE)) {
- nestedProperty(fullLayout, ptrunk + '._inputDomain').set(null);
- }
-
- // toggling axis type between log and linear: we need to convert
- // positions for components that are still using linearized values,
- // not data values like newer components.
- // previously we did this for log <-> not-log, but now only do it
- // for log <-> linear
- if(pleaf === 'type') {
- var ax = parentIn;
- var toLog = parentFull.type === 'linear' && vi === 'log';
- var fromLog = parentFull.type === 'log' && vi === 'linear';
-
- if(toLog || fromLog) {
- if(!ax || !ax.range) {
- // 2D never gets here, but 3D does
- // I don't think this is needed, but left here in case there
- // are edge cases I'm not thinking of.
- doextra(ptrunk + '.autorange', true);
- }
- else if(!parentFull.autorange) {
- // toggling log without autorange: need to also recalculate ranges
- // because log axes use linearized values for range endpoints
- var r0 = ax.range[0];
- var r1 = ax.range[1];
- if(toLog) {
- // if both limits are negative, autorange
- if(r0 <= 0 && r1 <= 0) {
- doextra(ptrunk + '.autorange', true);
- }
- // if one is negative, set it 6 orders below the other.
- if(r0 <= 0) r0 = r1 / 1e6;
- else if(r1 <= 0) r1 = r0 / 1e6;
- // now set the range values as appropriate
- doextra(ptrunk + '.range[0]', Math.log(r0) / Math.LN10);
- doextra(ptrunk + '.range[1]', Math.log(r1) / Math.LN10);
- }
- else {
- doextra(ptrunk + '.range[0]', Math.pow(10, r0));
- doextra(ptrunk + '.range[1]', Math.pow(10, r1));
- }
- }
- else if(toLog) {
- // just make sure the range is positive and in the right
- // order, it'll get recalculated later
- ax.range = (ax.range[1] > ax.range[0]) ? [1, 2] : [2, 1];
- }
-
- // clear polar view initial stash for radial range so that
- // value get recomputed in correct units
- if(Array.isArray(fullLayout._subplots.polar) &&
- fullLayout._subplots.polar.length &&
- fullLayout[p.parts[0]] &&
- p.parts[1] === 'radialaxis'
- ) {
- delete fullLayout[p.parts[0]]._subplot.viewInitial['radialaxis.range'];
- }
-
- // Annotations and images also need to convert to/from linearized coords
- // Shapes do not need this :)
- Registry.getComponentMethod('annotations', 'convertCoords')(gd, parentFull, vi, doextra);
- Registry.getComponentMethod('images', 'convertCoords')(gd, parentFull, vi, doextra);
- }
- else {
- // any other type changes: the range from the previous type
- // will not make sense, so autorange it.
- doextra(ptrunk + '.autorange', true);
- doextra(ptrunk + '.range', null);
- }
- nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
- }
- else if(pleaf.match(AX_NAME_PATTERN)) {
- var fullProp = nestedProperty(fullLayout, ai).get();
- var newType = (vi || {}).type;
-
- // This can potentially cause strange behavior if the autotype is not
- // numeric (linear, because we don't auto-log) but the previous type
- // was log. That's a very strange edge case though
- if(!newType || newType === '-') newType = 'linear';
- Registry.getComponentMethod('annotations', 'convertCoords')(gd, fullProp, newType, doextra);
- Registry.getComponentMethod('images', 'convertCoords')(gd, fullProp, newType, doextra);
- }
-
- // alter gd.layout
-
- // collect array component edits for execution all together
- // so we can ensure consistent behavior adding/removing items
- // and order-independence for add/remove/edit all together in
- // one relayout call
- var containerArrayMatch = manageArrays.containerArrayMatch(ai);
- if(containerArrayMatch) {
- arrayStr = containerArrayMatch.array;
- i = containerArrayMatch.index;
- var propStr = containerArrayMatch.property;
- var updateValObject = valObject || {editType: 'calc'};
-
- if(i !== '' && propStr === '') {
- // special handling of undoit if we're adding or removing an element
- // ie 'annotations[2]' which can be {...} (add) or null,
- // does not work when replacing the entire array
- if(manageArrays.isAddVal(vi)) {
- undoit[ai] = null;
- } else if(manageArrays.isRemoveVal(vi)) {
- undoit[ai] = (nestedProperty(layout, arrayStr).get() || [])[i];
- } else {
- Lib.warn('unrecognized full object value', aobj);
- }
- }
- editTypes.update(flags, updateValObject);
-
- // prepare the edits object we'll send to applyContainerArrayChanges
- if(!arrayEdits[arrayStr]) arrayEdits[arrayStr] = {};
- var objEdits = arrayEdits[arrayStr][i];
- if(!objEdits) objEdits = arrayEdits[arrayStr][i] = {};
- objEdits[propStr] = vi;
-
- delete aobj[ai];
- }
- // handle axis reversal explicitly, as there's no 'reverse' attribute
- else if(pleaf === 'reverse') {
- if(parentIn.range) parentIn.range.reverse();
- else {
- doextra(ptrunk + '.autorange', true);
- parentIn.range = [1, 0];
- }
-
- if(parentFull.autorange) flags.calc = true;
- else flags.plot = true;
- }
- else {
- if((fullLayout._has('scatter-like') && fullLayout._has('regl')) &&
- (ai === 'dragmode' &&
- (vi === 'lasso' || vi === 'select') &&
- !(vOld === 'lasso' || vOld === 'select'))
- ) {
- flags.plot = true;
- }
- else if(valObject) editTypes.update(flags, valObject);
- else flags.calc = true;
-
- p.set(vi);
- }
- }
-
- // now we've collected component edits - execute them all together
- for(arrayStr in arrayEdits) {
- var finished = manageArrays.applyContainerArrayChanges(gd,
- layoutNP(layout, arrayStr), arrayEdits[arrayStr], flags, layoutNP);
- if(!finished) flags.plot = true;
- }
-
- // figure out if we need to recalculate axis constraints
- var constraints = fullLayout._axisConstraintGroups || [];
- for(axId in rangesAltered) {
- for(i = 0; i < constraints.length; i++) {
- var group = constraints[i];
- if(group[axId]) {
- // Always recalc if we're changing constrained ranges.
- // Otherwise it's possible to violate the constraints by
- // specifying arbitrary ranges for all axes in the group.
- // this way some ranges may expand beyond what's specified,
- // as they do at first draw, to satisfy the constraints.
- flags.calc = true;
- for(var groupAxId in group) {
- if(!rangesAltered[groupAxId]) {
- Axes.getFromId(gd, groupAxId)._constraintShrinkable = true;
- }
- }
- }
- }
- }
-
- // If the autosize changed or height or width was explicitly specified,
- // this triggers a redraw
- // TODO: do we really need special aobj.height/width handling here?
- // couldn't editType do this?
- if(updateAutosize(gd) || aobj.height || aobj.width) flags.plot = true;
-
- if(flags.plot || flags.calc) {
- flags.layoutReplot = true;
- }
-
- // now all attribute mods are done, as are
- // redo and undo so we can save them
-
- return {
- flags: flags,
- rangesAltered: rangesAltered,
- undoit: undoit,
- redoit: redoit,
- eventData: eventData
- };
- }
-
- /*
- * updateAutosize: we made a change, does it change the autosize result?
- * puts the new size into fullLayout
- * returns true if either height or width changed
- */
- function updateAutosize(gd) {
- var fullLayout = gd._fullLayout;
- var oldWidth = fullLayout.width;
- var oldHeight = fullLayout.height;
-
- // calculate autosizing
- if(gd.layout.autosize) Plots.plotAutoSize(gd, gd.layout, fullLayout);
-
- return (fullLayout.width !== oldWidth) || (fullLayout.height !== oldHeight);
- }
-
- /**
- * update: update trace and layout attributes of an existing plot
- *
- * @param {String | HTMLDivElement} gd
- * the id or DOM element of the graph container div
- * @param {Object} traceUpdate
- * attribute object `{astr1: val1, astr2: val2 ...}`
- * corresponding to updates in the plot's traces
- * @param {Object} layoutUpdate
- * attribute object `{astr1: val1, astr2: val2 ...}`
- * corresponding to updates in the plot's layout
- * @param {Number[] | Number} [traces]
- * integer or array of integers for the traces to alter (all if omitted)
- *
- */
- function update(gd, traceUpdate, layoutUpdate, _traces) {
- gd = Lib.getGraphDiv(gd);
- helpers.clearPromiseQueue(gd);
-
- if(gd.framework && gd.framework.isPolar) {
- return Promise.resolve(gd);
- }
-
- if(!Lib.isPlainObject(traceUpdate)) traceUpdate = {};
- if(!Lib.isPlainObject(layoutUpdate)) layoutUpdate = {};
-
- if(Object.keys(traceUpdate).length) gd.changed = true;
- if(Object.keys(layoutUpdate).length) gd.changed = true;
-
- var traces = helpers.coerceTraceIndices(gd, _traces);
-
- var restyleSpecs = _restyle(gd, Lib.extendFlat({}, traceUpdate), traces);
- var restyleFlags = restyleSpecs.flags;
-
- var relayoutSpecs = _relayout(gd, Lib.extendFlat({}, layoutUpdate));
- var relayoutFlags = relayoutSpecs.flags;
-
- // clear calcdata and/or axis types if required
- if(restyleFlags.calc || relayoutFlags.calc) gd.calcdata = undefined;
- if(restyleFlags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, layoutUpdate);
-
- // fill in redraw sequence
- var seq = [];
-
- if(restyleFlags.fullReplot && relayoutFlags.layoutReplot) {
- var data = gd.data;
- var layout = gd.layout;
-
- // clear existing data/layout on gd
- // so that Plotly.plot doesn't try to extend them
- gd.data = undefined;
- gd.layout = undefined;
-
- seq.push(function() { return exports.plot(gd, data, layout); });
- }
- else if(restyleFlags.fullReplot) {
- seq.push(exports.plot);
- }
- else if(relayoutFlags.layoutReplot) {
- seq.push(subroutines.layoutReplot);
- }
- else {
- seq.push(Plots.previousPromises);
- axRangeSupplyDefaultsByPass(gd, relayoutFlags, relayoutSpecs) || Plots.supplyDefaults(gd);
-
- if(restyleFlags.style) seq.push(subroutines.doTraceStyle);
- if(restyleFlags.colorbars) seq.push(subroutines.doColorBars);
- if(relayoutFlags.legend) seq.push(subroutines.doLegend);
- if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles);
- if(relayoutFlags.axrange) addAxRangeSequence(seq, relayoutSpecs.rangesAltered);
- if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout);
- if(relayoutFlags.modebar) seq.push(subroutines.doModeBar);
- if(relayoutFlags.camera) seq.push(subroutines.doCamera);
-
- seq.push(emitAfterPlot);
- }
-
- seq.push(Plots.rehover);
-
- Queue.add(gd,
- update, [gd, restyleSpecs.undoit, relayoutSpecs.undoit, restyleSpecs.traces],
- update, [gd, restyleSpecs.redoit, relayoutSpecs.redoit, restyleSpecs.traces]
- );
-
- var plotDone = Lib.syncOrAsync(seq, gd);
- if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
-
- return plotDone.then(function() {
- gd.emit('plotly_update', {
- data: restyleSpecs.eventData,
- layout: relayoutSpecs.eventData
- });
-
- return gd;
- });
- }
- exports.update = update;
-
- /*
- * internal-use-only restyle/relayout/update variants that record the initial
- * values in (fullLayout|fullTrace)._preGUI so changes can be persisted across
- * Plotly.react data updates, dependent on uirevision attributes
- */
- function guiEdit(func) {
- return function wrappedEdit(gd) {
- gd._fullLayout._guiEditing = true;
- var p = func.apply(null, arguments);
- gd._fullLayout._guiEditing = false;
- return p;
- };
- }
- exports._guiRestyle = guiEdit(restyle);
- exports._guiRelayout = guiEdit(relayout);
- exports._guiUpdate = guiEdit(update);
-
- // For connecting edited layout attributes to uirevision attrs
- // If no `attr` we use `match[1] + '.uirevision'`
- // Ordered by most common edits first, to minimize our search time
- var layoutUIControlPatterns = [
- {pattern: /^hiddenlabels/, attr: 'legend.uirevision'},
- {pattern: /^((x|y)axis\d*)\.((auto)?range|title\.text)/},
-
- // showspikes and modes include those nested inside scenes
- {pattern: /axis\d*\.showspikes$/, attr: 'modebar.uirevision'},
- {pattern: /(hover|drag)mode$/, attr: 'modebar.uirevision'},
-
- {pattern: /^(scene\d*)\.camera/},
- {pattern: /^(geo\d*)\.(projection|center)/},
- {pattern: /^(ternary\d*\.[abc]axis)\.(min|title\.text)$/},
- {pattern: /^(polar\d*\.radialaxis)\.((auto)?range|angle|title\.text)/},
- {pattern: /^(polar\d*\.angularaxis)\.rotation/},
- {pattern: /^(mapbox\d*)\.(center|zoom|bearing|pitch)/},
-
- {pattern: /^legend\.(x|y)$/, attr: 'editrevision'},
- {pattern: /^(shapes|annotations)/, attr: 'editrevision'},
- {pattern: /^title\.text$/, attr: 'editrevision'}
- ];
-
- // same for trace attributes: if `attr` is given it's in layout,
- // or with no `attr` we use `trace.uirevision`
- var traceUIControlPatterns = [
- {pattern: /^selectedpoints$/, attr: 'selectionrevision'},
- // "visible" includes trace.transforms[i].styles[j].value.visible
- {pattern: /(^|value\.)visible$/, attr: 'legend.uirevision'},
- {pattern: /^dimensions\[\d+\]\.constraintrange/},
-
- // below this you must be in editable: true mode
- // TODO: I still put name and title with `trace.uirevision`
- // reasonable or should these be `editrevision`?
- // Also applies to axis titles up in the layout section
-
- // "name" also includes transform.styles
- {pattern: /(^|value\.)name$/},
- // including nested colorbar attributes (ie marker.colorbar)
- {pattern: /colorbar\.title\.text$/},
- {pattern: /colorbar\.(x|y)$/, attr: 'editrevision'}
- ];
-
- function findUIPattern(key, patternSpecs) {
- for(var i = 0; i < patternSpecs.length; i++) {
- var spec = patternSpecs[i];
- var match = key.match(spec.pattern);
- if(match) {
- return {head: match[1], attr: spec.attr};
- }
- }
- }
-
- // We're finding the new uirevision before supplyDefaults, so do the
- // inheritance manually. Note that only `undefined` inherits - other
- // falsy values are returned.
- function getNewRev(revAttr, container) {
- var newRev = nestedProperty(container, revAttr).get();
- if(newRev !== undefined) return newRev;
-
- var parts = revAttr.split('.');
- parts.pop();
- while(parts.length > 1) {
- parts.pop();
- newRev = nestedProperty(container, parts.join('.') + '.uirevision').get();
- if(newRev !== undefined) return newRev;
- }
-
- return container.uirevision;
- }
-
- function getFullTraceIndexFromUid(uid, fullData) {
- for(var i = 0; i < fullData.length; i++) {
- if(fullData[i]._fullInput.uid === uid) return i;
- }
- return -1;
- }
-
- function getTraceIndexFromUid(uid, data, tracei) {
- for(var i = 0; i < data.length; i++) {
- if(data[i].uid === uid) return i;
- }
- // fall back on trace order, but only if user didn't provide a uid for that trace
- return (!data[tracei] || data[tracei].uid) ? -1 : tracei;
- }
-
- function valsMatch(v1, v2) {
- var v1IsObj = Lib.isPlainObject(v1);
- var v1IsArray = Array.isArray(v1);
- if(v1IsObj || v1IsArray) {
- return (
- (v1IsObj && Lib.isPlainObject(v2)) ||
- (v1IsArray && Array.isArray(v2))
- ) && JSON.stringify(v1) === JSON.stringify(v2);
- }
- return v1 === v2;
- }
-
- function applyUIRevisions(data, layout, oldFullData, oldFullLayout) {
- var layoutPreGUI = oldFullLayout._preGUI;
- var key, revAttr, oldRev, newRev, match, preGUIVal, newNP, newVal;
- var bothInheritAutorange = [];
- var newRangeAccepted = {};
- for(key in layoutPreGUI) {
- match = findUIPattern(key, layoutUIControlPatterns);
- if(match) {
- revAttr = match.attr || (match.head + '.uirevision');
- oldRev = nestedProperty(oldFullLayout, revAttr).get();
- newRev = oldRev && getNewRev(revAttr, layout);
- if(newRev && (newRev === oldRev)) {
- preGUIVal = layoutPreGUI[key];
- if(preGUIVal === null) preGUIVal = undefined;
- newNP = nestedProperty(layout, key);
- newVal = newNP.get();
- if(valsMatch(newVal, preGUIVal)) {
- if(newVal === undefined && key.substr(key.length - 9) === 'autorange') {
- bothInheritAutorange.push(key.substr(0, key.length - 10));
- }
- newNP.set(undefinedToNull(nestedProperty(oldFullLayout, key).get()));
- continue;
- }
- }
- }
- else {
- Lib.warn('unrecognized GUI edit: ' + key);
- }
- // if we got this far, the new value was accepted as the new starting
- // point (either because it changed or revision changed)
- // so remove it from _preGUI for next time.
- delete layoutPreGUI[key];
-
- if(key.substr(key.length - 8, 6) === 'range[') {
- newRangeAccepted[key.substr(0, key.length - 9)] = 1;
- }
- }
-
- // Special logic for `autorange`, since it interacts with `range`:
- // If the new figure's matching `range` was kept, and `autorange`
- // wasn't supplied explicitly in either the original or the new figure,
- // we shouldn't alter that - but we may just have done that, so fix it.
- for(var i = 0; i < bothInheritAutorange.length; i++) {
- var axAttr = bothInheritAutorange[i];
- if(newRangeAccepted[axAttr]) {
- var newAx = nestedProperty(layout, axAttr).get();
- if(newAx) delete newAx.autorange;
- }
- }
-
- // Now traces - try to match them up by uid (in case we added/deleted in
- // the middle), then fall back on index.
- var allTracePreGUI = oldFullLayout._tracePreGUI;
- for(var uid in allTracePreGUI) {
- var tracePreGUI = allTracePreGUI[uid];
- var newTrace = null;
- var fullInput;
- for(key in tracePreGUI) {
- // wait until we know we have preGUI values to look for traces
- // but if we don't find both, stop looking at this uid
- if(!newTrace) {
- var fulli = getFullTraceIndexFromUid(uid, oldFullData);
- if(fulli < 0) {
- // Somehow we didn't even have this trace in oldFullData...
- // I guess this could happen with `deleteTraces` or something
- delete allTracePreGUI[uid];
- break;
- }
- var fullTrace = oldFullData[fulli];
- fullInput = fullTrace._fullInput;
-
- var newTracei = getTraceIndexFromUid(uid, data, fullInput.index);
- if(newTracei < 0) {
- // No match in new data
- delete allTracePreGUI[uid];
- break;
- }
- newTrace = data[newTracei];
- }
-
- match = findUIPattern(key, traceUIControlPatterns);
- if(match) {
- if(match.attr) {
- oldRev = nestedProperty(oldFullLayout, match.attr).get();
- newRev = oldRev && getNewRev(match.attr, layout);
- }
- else {
- oldRev = fullInput.uirevision;
- // inheritance for trace.uirevision is simple, just layout.uirevision
- newRev = newTrace.uirevision;
- if(newRev === undefined) newRev = layout.uirevision;
- }
-
- if(newRev && newRev === oldRev) {
- preGUIVal = tracePreGUI[key];
- if(preGUIVal === null) preGUIVal = undefined;
- newNP = nestedProperty(newTrace, key);
- newVal = newNP.get();
- if(valsMatch(newVal, preGUIVal)) {
- newNP.set(undefinedToNull(nestedProperty(fullInput, key).get()));
- continue;
- }
- }
- }
- else {
- Lib.warn('unrecognized GUI edit: ' + key + ' in trace uid ' + uid);
- }
- delete tracePreGUI[key];
- }
- }
- }
-
- /**
- * Plotly.react:
- * A plot/update method that takes the full plot state (same API as plot/newPlot)
- * and diffs to determine the minimal update pathway
- *
- * @param {string id or DOM element} gd
- * the id or DOM element of the graph container div
- * @param {array of objects} data
- * array of traces, containing the data and display information for each trace
- * @param {object} layout
- * object describing the overall display of the plot,
- * all the stuff that doesn't pertain to any individual trace
- * @param {object} config
- * configuration options (see ./plot_config.js for more info)
- *
- * OR
- *
- * @param {string id or DOM element} gd
- * the id or DOM element of the graph container div
- * @param {object} figure
- * object containing `data`, `layout`, `config`, and `frames` members
- *
- */
- exports.react = function(gd, data, layout, config) {
- var frames, plotDone;
-
- function addFrames() { return exports.addFrames(gd, frames); }
-
- gd = Lib.getGraphDiv(gd);
-
- var oldFullData = gd._fullData;
- var oldFullLayout = gd._fullLayout;
-
- // you can use this as the initial draw as well as to update
- if(!Lib.isPlotDiv(gd) || !oldFullData || !oldFullLayout) {
- plotDone = exports.newPlot(gd, data, layout, config);
- }
- else {
-
- if(Lib.isPlainObject(data)) {
- var obj = data;
- data = obj.data;
- layout = obj.layout;
- config = obj.config;
- frames = obj.frames;
- }
-
- var configChanged = false;
- // assume that if there's a config at all, we're reacting to it too,
- // and completely replace the previous config
- if(config) {
- var oldConfig = Lib.extendDeep({}, gd._context);
- gd._context = undefined;
- setPlotContext(gd, config);
- configChanged = diffConfig(oldConfig, gd._context);
- }
-
- gd.data = data || [];
- helpers.cleanData(gd.data);
- gd.layout = layout || {};
- helpers.cleanLayout(gd.layout);
-
- applyUIRevisions(gd.data, gd.layout, oldFullData, oldFullLayout);
-
- // "true" skips updating calcdata and remapping arrays from calcTransforms,
- // which supplyDefaults usually does at the end, but we may need to NOT do
- // if the diff (which we haven't determined yet) says we'll recalc
- Plots.supplyDefaults(gd, {skipUpdateCalc: true});
-
- var newFullData = gd._fullData;
- var newFullLayout = gd._fullLayout;
- var immutable = newFullLayout.datarevision === undefined;
- var transition = newFullLayout.transition;
-
- var relayoutFlags = diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition);
- var newDataRevision = relayoutFlags.newDataRevision;
- var restyleFlags = diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision);
-
- // TODO: how to translate this part of relayout to Plotly.react?
- // // Setting width or height to null must reset the graph's width / height
- // // back to its initial value as computed during the first pass in Plots.plotAutoSize.
- // //
- // // To do so, we must manually set them back here using the _initialAutoSize cache.
- // if(['width', 'height'].indexOf(ai) !== -1 && vi === null) {
- // fullLayout[ai] = gd._initialAutoSize[ai];
- // }
-
- if(updateAutosize(gd)) relayoutFlags.layoutReplot = true;
-
- // clear calcdata if required
- if(restyleFlags.calc || relayoutFlags.calc) gd.calcdata = undefined;
- // otherwise do the calcdata updates and calcTransform array remaps that we skipped earlier
- else Plots.supplyDefaultsUpdateCalc(gd.calcdata, newFullData);
-
- // Note: what restyle/relayout use impliedEdits and clearAxisTypes for
- // must be handled by the user when using Plotly.react.
-
- // fill in redraw sequence
- var seq = [];
-
- if(frames) {
- gd._transitionData = {};
- Plots.createTransitionData(gd);
- seq.push(addFrames);
- }
-
- // Transition pathway,
- // only used when 'transition' is set by user and
- // when at least one animatable attribute has changed,
- // N.B. config changed aren't animatable
- if(newFullLayout.transition && !configChanged && (restyleFlags.anim || relayoutFlags.anim)) {
- Plots.doCalcdata(gd);
- subroutines.doAutoRangeAndConstraints(gd);
-
- seq.push(function() {
- return Plots.transitionFromReact(gd, restyleFlags, relayoutFlags, oldFullLayout);
- });
- }
- else if(restyleFlags.fullReplot || relayoutFlags.layoutReplot || configChanged) {
- gd._fullLayout._skipDefaults = true;
- seq.push(exports.plot);
- }
- else {
- for(var componentType in relayoutFlags.arrays) {
- var indices = relayoutFlags.arrays[componentType];
- if(indices.length) {
- var drawOne = Registry.getComponentMethod(componentType, 'drawOne');
- if(drawOne !== Lib.noop) {
- for(var i = 0; i < indices.length; i++) {
- drawOne(gd, indices[i]);
- }
- }
- else {
- var draw = Registry.getComponentMethod(componentType, 'draw');
- if(draw === Lib.noop) {
- throw new Error('cannot draw components: ' + componentType);
- }
- draw(gd);
- }
- }
- }
-
- seq.push(Plots.previousPromises);
- if(restyleFlags.style) seq.push(subroutines.doTraceStyle);
- if(restyleFlags.colorbars) seq.push(subroutines.doColorBars);
- if(relayoutFlags.legend) seq.push(subroutines.doLegend);
- if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles);
- if(relayoutFlags.axrange) addAxRangeSequence(seq);
- if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout);
- if(relayoutFlags.modebar) seq.push(subroutines.doModeBar);
- if(relayoutFlags.camera) seq.push(subroutines.doCamera);
- seq.push(emitAfterPlot);
- }
-
- seq.push(Plots.rehover);
-
- plotDone = Lib.syncOrAsync(seq, gd);
- if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
- }
-
- return plotDone.then(function() {
- gd.emit('plotly_react', {
- data: data,
- layout: layout
- });
-
- return gd;
- });
-
- };
-
- function diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision) {
- var sameTraceLength = oldFullData.length === newFullData.length;
-
- if(!transition && !sameTraceLength) {
- return {
- fullReplot: true,
- calc: true
- };
- }
-
- var flags = editTypes.traceFlags();
- flags.arrays = {};
- flags.nChanges = 0;
- flags.nChangesAnim = 0;
-
- var i, trace;
-
- function getTraceValObject(parts) {
- return PlotSchema.getTraceValObject(trace, parts);
- }
-
- var diffOpts = {
- getValObject: getTraceValObject,
- flags: flags,
- immutable: immutable,
- transition: transition,
- newDataRevision: newDataRevision,
- gd: gd
- };
-
- var seenUIDs = {};
-
- for(i = 0; i < oldFullData.length; i++) {
- if(newFullData[i]) {
- trace = newFullData[i]._fullInput;
- if(Plots.hasMakesDataTransform(trace)) trace = newFullData[i];
- if(seenUIDs[trace.uid]) continue;
- seenUIDs[trace.uid] = 1;
-
- getDiffFlags(oldFullData[i]._fullInput, trace, [], diffOpts);
- }
- }
-
- if(flags.calc || flags.plot) {
- flags.fullReplot = true;
- }
-
- if(transition && flags.nChanges && flags.nChangesAnim) {
- flags.anim = (flags.nChanges === flags.nChangesAnim) && sameTraceLength ? 'all' : 'some';
- }
-
- return flags;
- }
-
- function diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition) {
- var flags = editTypes.layoutFlags();
- flags.arrays = {};
- flags.rangesAltered = {};
- flags.nChanges = 0;
- flags.nChangesAnim = 0;
-
- function getLayoutValObject(parts) {
- return PlotSchema.getLayoutValObject(newFullLayout, parts);
- }
-
- var diffOpts = {
- getValObject: getLayoutValObject,
- flags: flags,
- immutable: immutable,
- transition: transition,
- gd: gd
- };
-
- getDiffFlags(oldFullLayout, newFullLayout, [], diffOpts);
-
- if(flags.plot || flags.calc) {
- flags.layoutReplot = true;
- }
-
- if(transition && flags.nChanges && flags.nChangesAnim) {
- flags.anim = flags.nChanges === flags.nChangesAnim ? 'all' : 'some';
- }
-
- return flags;
- }
-
- function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
- var valObject, key, astr;
-
- var getValObject = opts.getValObject;
- var flags = opts.flags;
- var immutable = opts.immutable;
- var inArray = opts.inArray;
- var arrayIndex = opts.arrayIndex;
-
- function changed() {
- var editType = valObject.editType;
- if(inArray && editType.indexOf('arraydraw') !== -1) {
- Lib.pushUnique(flags.arrays[inArray], arrayIndex);
- return;
- }
- editTypes.update(flags, valObject);
-
- if(editType !== 'none') {
- flags.nChanges++;
- }
-
- // track animatable changes
- if(opts.transition && valObject.anim) {
- flags.nChangesAnim++;
- }
-
- // track cartesian axes with altered ranges
- if(AX_RANGE_RE.test(astr) || AX_AUTORANGE_RE.test(astr)) {
- flags.rangesAltered[outerparts[0]] = 1;
- }
-
- // clear _inputDomain on cartesian axes with altered domains
- if(AX_DOMAIN_RE.test(astr)) {
- nestedProperty(newContainer, '_inputDomain').set(null);
- }
-
- // track datarevision changes
- if(key === 'datarevision') {
- flags.newDataRevision = 1;
- }
- }
-
- function valObjectCanBeDataArray(valObject) {
- return valObject.valType === 'data_array' || valObject.arrayOk;
- }
-
- for(key in oldContainer) {
- // short-circuit based on previous calls or previous keys that already maximized the pathway
- if(flags.calc && !opts.transition) return;
-
- var oldVal = oldContainer[key];
- var newVal = newContainer[key];
- var parts = outerparts.concat(key);
- astr = parts.join('.');
-
- if(key.charAt(0) === '_' || typeof oldVal === 'function' || oldVal === newVal) continue;
-
- // FIXME: ax.tick0 and dtick get filled in during plotting (except for geo subplots),
- // and unlike other auto values they don't make it back into the input,
- // so newContainer won't have them.
- if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') {
- var tickMode = newContainer.tickmode;
- if(tickMode === 'auto' || tickMode === 'array' || !tickMode) continue;
- }
- // FIXME: Similarly for axis ranges for 3D
- // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them.
- if(key === 'range' && newContainer.autorange) continue;
- if((key === 'zmin' || key === 'zmax') && newContainer.type === 'contourcarpet') continue;
-
- valObject = getValObject(parts);
-
- // in case type changed, we may not even *have* a valObject.
- if(!valObject) continue;
-
- if(valObject._compareAsJSON && JSON.stringify(oldVal) === JSON.stringify(newVal)) continue;
-
- var valType = valObject.valType;
- var i;
-
- var canBeDataArray = valObjectCanBeDataArray(valObject);
- var wasArray = Array.isArray(oldVal);
- var nowArray = Array.isArray(newVal);
-
- // hack for traces that modify the data in supplyDefaults, like
- // converting 1D to 2D arrays, which will always create new objects
- if(wasArray && nowArray) {
- var inputKey = '_input_' + key;
- var oldValIn = oldContainer[inputKey];
- var newValIn = newContainer[inputKey];
- if(Array.isArray(oldValIn) && oldValIn === newValIn) continue;
- }
-
- if(newVal === undefined) {
- if(canBeDataArray && wasArray) flags.calc = true;
- else changed();
- }
- else if(valObject._isLinkedToArray) {
- var arrayEditIndices = [];
- var extraIndices = false;
- if(!inArray) flags.arrays[key] = arrayEditIndices;
-
- var minLen = Math.min(oldVal.length, newVal.length);
- var maxLen = Math.max(oldVal.length, newVal.length);
- if(minLen !== maxLen) {
- if(valObject.editType === 'arraydraw') {
- extraIndices = true;
- }
- else {
- changed();
- continue;
- }
- }
-
- for(i = 0; i < minLen; i++) {
- getDiffFlags(oldVal[i], newVal[i], parts.concat(i),
- // add array indices, but not if we're already in an array
- Lib.extendFlat({inArray: key, arrayIndex: i}, opts));
- }
-
- // put this at the end so that we know our collected array indices are sorted
- // but the check for length changes happens up front so we can short-circuit
- // diffing if appropriate
- if(extraIndices) {
- for(i = minLen; i < maxLen; i++) {
- arrayEditIndices.push(i);
- }
- }
- }
- else if(!valType && Lib.isPlainObject(oldVal)) {
- getDiffFlags(oldVal, newVal, parts, opts);
- }
- else if(canBeDataArray) {
- if(wasArray && nowArray) {
-
- // don't try to diff two data arrays. If immutable we know the data changed,
- // if not, assume it didn't and let `layout.datarevision` tell us if it did
- if(immutable) {
- flags.calc = true;
- }
-
- // look for animatable attributes when the data changed
- if(immutable || opts.newDataRevision) {
- changed();
- }
- }
- else if(wasArray !== nowArray) {
- flags.calc = true;
- }
- else changed();
- }
- else if(wasArray && nowArray) {
- // info array, colorscale, 'any' - these are short, just stringify.
- // I don't *think* that covers up any real differences post-validation, does it?
- // otherwise we need to dive in 1 (info_array) or 2 (colorscale) levels and compare
- // all elements.
- if(oldVal.length !== newVal.length || String(oldVal) !== String(newVal)) {
- changed();
- }
- }
- else {
- changed();
- }
- }
-
- for(key in newContainer) {
- if(!(key in oldContainer || key.charAt(0) === '_' || typeof newContainer[key] === 'function')) {
- valObject = getValObject(outerparts.concat(key));
-
- if(valObjectCanBeDataArray(valObject) && Array.isArray(newContainer[key])) {
- flags.calc = true;
- return;
- }
- else changed();
- }
- }
- }
-
- /*
- * simple diff for config - for now, just treat all changes as equivalent
- */
- function diffConfig(oldConfig, newConfig) {
- var key;
-
- for(key in oldConfig) {
- if(key.charAt(0) === '_') continue;
- var oldVal = oldConfig[key];
- var newVal = newConfig[key];
- if(oldVal !== newVal) {
- if(Lib.isPlainObject(oldVal) && Lib.isPlainObject(newVal)) {
- if(diffConfig(oldVal, newVal)) {
- return true;
- }
- }
- else if(Array.isArray(oldVal) && Array.isArray(newVal)) {
- if(oldVal.length !== newVal.length) {
- return true;
- }
- for(var i = 0; i < oldVal.length; i++) {
- if(oldVal[i] !== newVal[i]) {
- if(Lib.isPlainObject(oldVal[i]) && Lib.isPlainObject(newVal[i])) {
- if(diffConfig(oldVal[i], newVal[i])) {
- return true;
- }
- }
- else {
- return true;
- }
- }
- }
- }
- else {
- return true;
- }
- }
- }
- }
-
- /**
- * Animate to a frame, sequence of frame, frame group, or frame definition
- *
- * @param {string id or DOM element} gd
- * the id or DOM element of the graph container div
- *
- * @param {string or object or array of strings or array of objects} frameOrGroupNameOrFrameList
- * a single frame, array of frames, or group to which to animate. The intent is
- * inferred by the type of the input. Valid inputs are:
- *
- * - string, e.g. 'groupname': animate all frames of a given `group` in the order
- * in which they are defined via `Plotly.addFrames`.
- *
- * - array of strings, e.g. ['frame1', frame2']: a list of frames by name to which
- * to animate in sequence
- *
- * - object: {data: ...}: a frame definition to which to animate. The frame is not
- * and does not need to be added via `Plotly.addFrames`. It may contain any of
- * the properties of a frame, including `data`, `layout`, and `traces`. The
- * frame is used as provided and does not use the `baseframe` property.
- *
- * - array of objects, e.g. [{data: ...}, {data: ...}]: a list of frame objects,
- * each following the same rules as a single `object`.
- *
- * @param {object} animationOpts
- * configuration for the animation
- */
- exports.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
- gd = Lib.getGraphDiv(gd);
-
- if(!Lib.isPlotDiv(gd)) {
- throw new Error(
- 'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' +
- 'to create a plot before animating it. For more details, see ' +
- 'https://plot.ly/javascript/animations/'
- );
- }
-
- var trans = gd._transitionData;
-
- // This is the queue of frames that will be animated as soon as possible. They
- // are popped immediately upon the *start* of a transition:
- if(!trans._frameQueue) {
- trans._frameQueue = [];
- }
-
- animationOpts = Plots.supplyAnimationDefaults(animationOpts);
- var transitionOpts = animationOpts.transition;
- var frameOpts = animationOpts.frame;
-
- // Since frames are popped immediately, an empty queue only means all frames have
- // *started* to transition, not that the animation is complete. To solve that,
- // track a separate counter that increments at the same time as frames are added
- // to the queue, but decrements only when the transition is complete.
- if(trans._frameWaitingCnt === undefined) {
- trans._frameWaitingCnt = 0;
- }
-
- function getTransitionOpts(i) {
- if(Array.isArray(transitionOpts)) {
- if(i >= transitionOpts.length) {
- return transitionOpts[0];
- } else {
- return transitionOpts[i];
- }
- } else {
- return transitionOpts;
- }
- }
-
- function getFrameOpts(i) {
- if(Array.isArray(frameOpts)) {
- if(i >= frameOpts.length) {
- return frameOpts[0];
- } else {
- return frameOpts[i];
- }
- } else {
- return frameOpts;
- }
- }
-
- // Execute a callback after the wrapper function has been called n times.
- // This is used to defer the resolution until a transition has resovled *and*
- // the frame has completed. If it's not done this way, then we get a race
- // condition in which the animation might resolve before a transition is complete
- // or vice versa.
- function callbackOnNthTime(cb, n) {
- var cnt = 0;
- return function() {
- if(cb && ++cnt === n) {
- return cb();
- }
- };
- }
-
- return new Promise(function(resolve, reject) {
- function discardExistingFrames() {
- if(trans._frameQueue.length === 0) {
- return;
- }
-
- while(trans._frameQueue.length) {
- var next = trans._frameQueue.pop();
- if(next.onInterrupt) {
- next.onInterrupt();
- }
- }
-
- gd.emit('plotly_animationinterrupted', []);
- }
-
- function queueFrames(frameList) {
- if(frameList.length === 0) return;
-
- for(var i = 0; i < frameList.length; i++) {
- var computedFrame;
-
- if(frameList[i].type === 'byname') {
- // If it's a named frame, compute it:
- computedFrame = Plots.computeFrame(gd, frameList[i].name);
- } else {
- // Otherwise we must have been given a simple object, so treat
- // the input itself as the computed frame.
- computedFrame = frameList[i].data;
- }
-
- var frameOpts = getFrameOpts(i);
- var transitionOpts = getTransitionOpts(i);
-
- // It doesn't make much sense for the transition duration to be greater than
- // the frame duration, so limit it:
- transitionOpts.duration = Math.min(transitionOpts.duration, frameOpts.duration);
-
- var nextFrame = {
- frame: computedFrame,
- name: frameList[i].name,
- frameOpts: frameOpts,
- transitionOpts: transitionOpts,
- };
- if(i === frameList.length - 1) {
- // The last frame in this .animate call stores the promise resolve
- // and reject callbacks. This is how we ensure that the animation
- // loop (which may exist as a result of a *different* .animate call)
- // still resolves or rejecdts this .animate call's promise. once it's
- // complete.
- nextFrame.onComplete = callbackOnNthTime(resolve, 2);
- nextFrame.onInterrupt = reject;
- }
-
- trans._frameQueue.push(nextFrame);
- }
-
- // Set it as never having transitioned to a frame. This will cause the animation
- // loop to immediately transition to the next frame (which, for immediate mode,
- // is the first frame in the list since all others would have been discarded
- // below)
- if(animationOpts.mode === 'immediate') {
- trans._lastFrameAt = -Infinity;
- }
-
- // Only it's not already running, start a RAF loop. This could be avoided in the
- // case that there's only one frame, but it significantly complicated the logic
- // and only sped things up by about 5% or so for a lorenz attractor simulation.
- // It would be a fine thing to implement, but the benefit of that optimization
- // doesn't seem worth the extra complexity.
- if(!trans._animationRaf) {
- beginAnimationLoop();
- }
- }
-
- function stopAnimationLoop() {
- gd.emit('plotly_animated');
-
- // Be sure to unset also since it's how we know whether a loop is already running:
- window.cancelAnimationFrame(trans._animationRaf);
- trans._animationRaf = null;
- }
-
- function nextFrame() {
- if(trans._currentFrame && trans._currentFrame.onComplete) {
- // Execute the callback and unset it to ensure it doesn't
- // accidentally get called twice
- trans._currentFrame.onComplete();
- }
-
- var newFrame = trans._currentFrame = trans._frameQueue.shift();
-
- if(newFrame) {
- // Since it's sometimes necessary to do deep digging into frame data,
- // we'll consider it not 100% impossible for nulls or numbers to sneak through,
- // so check when casting the name, just to be absolutely certain:
- var stringName = newFrame.name ? newFrame.name.toString() : null;
- gd._fullLayout._currentFrame = stringName;
-
- trans._lastFrameAt = Date.now();
- trans._timeToNext = newFrame.frameOpts.duration;
-
- // This is simply called and it's left to .transition to decide how to manage
- // interrupting current transitions. That means we don't need to worry about
- // how it resolves or what happens after this:
- Plots.transition(gd,
- newFrame.frame.data,
- newFrame.frame.layout,
- helpers.coerceTraceIndices(gd, newFrame.frame.traces),
- newFrame.frameOpts,
- newFrame.transitionOpts
- ).then(function() {
- if(newFrame.onComplete) {
- newFrame.onComplete();
- }
-
- });
-
- gd.emit('plotly_animatingframe', {
- name: stringName,
- frame: newFrame.frame,
- animation: {
- frame: newFrame.frameOpts,
- transition: newFrame.transitionOpts,
- }
- });
- } else {
- // If there are no more frames, then stop the RAF loop:
- stopAnimationLoop();
- }
- }
-
- function beginAnimationLoop() {
- gd.emit('plotly_animating');
-
- // If no timer is running, then set last frame = long ago so that the next
- // frame is immediately transitioned:
- trans._lastFrameAt = -Infinity;
- trans._timeToNext = 0;
- trans._runningTransitions = 0;
- trans._currentFrame = null;
-
- var doFrame = function() {
- // This *must* be requested before nextFrame since nextFrame may decide
- // to cancel it if there's nothing more to animated:
- trans._animationRaf = window.requestAnimationFrame(doFrame);
-
- // Check if we're ready for a new frame:
- if(Date.now() - trans._lastFrameAt > trans._timeToNext) {
- nextFrame();
- }
- };
-
- doFrame();
- }
-
- // This is an animate-local counter that helps match up option input list
- // items with the particular frame.
- var configCounter = 0;
- function setTransitionConfig(frame) {
- if(Array.isArray(transitionOpts)) {
- if(configCounter >= transitionOpts.length) {
- frame.transitionOpts = transitionOpts[configCounter];
- } else {
- frame.transitionOpts = transitionOpts[0];
- }
- } else {
- frame.transitionOpts = transitionOpts;
- }
- configCounter++;
- return frame;
- }
-
- // Disambiguate what's sort of frames have been received
- var i, frame;
- var frameList = [];
- var allFrames = frameOrGroupNameOrFrameList === undefined || frameOrGroupNameOrFrameList === null;
- var isFrameArray = Array.isArray(frameOrGroupNameOrFrameList);
- var isSingleFrame = !allFrames && !isFrameArray && Lib.isPlainObject(frameOrGroupNameOrFrameList);
-
- if(isSingleFrame) {
- // In this case, a simple object has been passed to animate.
- frameList.push({
- type: 'object',
- data: setTransitionConfig(Lib.extendFlat({}, frameOrGroupNameOrFrameList))
- });
- } else if(allFrames || ['string', 'number'].indexOf(typeof frameOrGroupNameOrFrameList) !== -1) {
- // In this case, null or undefined has been passed so that we want to
- // animate *all* currently defined frames
- for(i = 0; i < trans._frames.length; i++) {
- frame = trans._frames[i];
-
- if(!frame) continue;
-
- if(allFrames || String(frame.group) === String(frameOrGroupNameOrFrameList)) {
- frameList.push({
- type: 'byname',
- name: String(frame.name),
- data: setTransitionConfig({name: frame.name})
- });
- }
- }
- } else if(isFrameArray) {
- for(i = 0; i < frameOrGroupNameOrFrameList.length; i++) {
- var frameOrName = frameOrGroupNameOrFrameList[i];
- if(['number', 'string'].indexOf(typeof frameOrName) !== -1) {
- frameOrName = String(frameOrName);
- // In this case, there's an array and this frame is a string name:
- frameList.push({
- type: 'byname',
- name: frameOrName,
- data: setTransitionConfig({name: frameOrName})
- });
- } else if(Lib.isPlainObject(frameOrName)) {
- frameList.push({
- type: 'object',
- data: setTransitionConfig(Lib.extendFlat({}, frameOrName))
- });
- }
- }
- }
-
- // Verify that all of these frames actually exist; return and reject if not:
- for(i = 0; i < frameList.length; i++) {
- frame = frameList[i];
- if(frame.type === 'byname' && !trans._frameHash[frame.data.name]) {
- Lib.warn('animate failure: frame not found: "' + frame.data.name + '"');
- reject();
- return;
- }
- }
-
- // If the mode is either next or immediate, then all currently queued frames must
- // be dumped and the corresponding .animate promises rejected.
- if(['next', 'immediate'].indexOf(animationOpts.mode) !== -1) {
- discardExistingFrames();
- }
-
- if(animationOpts.direction === 'reverse') {
- frameList.reverse();
- }
-
- var currentFrame = gd._fullLayout._currentFrame;
- if(currentFrame && animationOpts.fromcurrent) {
- var idx = -1;
- for(i = 0; i < frameList.length; i++) {
- frame = frameList[i];
- if(frame.type === 'byname' && frame.name === currentFrame) {
- idx = i;
- break;
- }
- }
-
- if(idx > 0 && idx < frameList.length - 1) {
- var filteredFrameList = [];
- for(i = 0; i < frameList.length; i++) {
- frame = frameList[i];
- if(frameList[i].type !== 'byname' || i > idx) {
- filteredFrameList.push(frame);
- }
- }
- frameList = filteredFrameList;
- }
- }
-
- if(frameList.length > 0) {
- queueFrames(frameList);
- } else {
- // This is the case where there were simply no frames. It's a little strange
- // since there's not much to do:
- gd.emit('plotly_animated');
- resolve();
- }
- });
- };
-
- /**
- * Register new frames
- *
- * @param {string id or DOM element} gd
- * the id or DOM element of the graph container div
- *
- * @param {array of objects} frameList
- * list of frame definitions, in which each object includes any of:
- * - name: {string} name of frame to add
- * - data: {array of objects} trace data
- * - layout {object} layout definition
- * - traces {array} trace indices
- * - baseframe {string} name of frame from which this frame gets defaults
- *
- * @param {array of integers} indices
- * an array of integer indices matching the respective frames in `frameList`. If not
- * provided, an index will be provided in serial order. If already used, the frame
- * will be overwritten.
- */
- exports.addFrames = function(gd, frameList, indices) {
- gd = Lib.getGraphDiv(gd);
-
- if(frameList === null || frameList === undefined) {
- return Promise.resolve();
- }
-
- if(!Lib.isPlotDiv(gd)) {
- throw new Error(
- 'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' +
- 'to create a plot before adding frames. For more details, see ' +
- 'https://plot.ly/javascript/animations/'
- );
- }
-
- var i, frame, j, idx;
- var _frames = gd._transitionData._frames;
- var _frameHash = gd._transitionData._frameHash;
-
-
- if(!Array.isArray(frameList)) {
- throw new Error('addFrames failure: frameList must be an Array of frame definitions' + frameList);
- }
-
- // Create a sorted list of insertions since we run into lots of problems if these
- // aren't in ascending order of index:
- //
- // Strictly for sorting. Make sure this is guaranteed to never collide with any
- // already-exisisting indices:
- var bigIndex = _frames.length + frameList.length * 2;
-
- var insertions = [];
- var _frameHashLocal = {};
- for(i = frameList.length - 1; i >= 0; i--) {
- if(!Lib.isPlainObject(frameList[i])) continue;
-
- // The entire logic for checking for this type of name collision can be removed once we migrate to ES6 and
- // use a Map instead of an Object instance, as Map keys aren't converted to strings.
- var lookupName = frameList[i].name;
- var name = (_frameHash[lookupName] || _frameHashLocal[lookupName] || {}).name;
- var newName = frameList[i].name;
- var collisionPresent = _frameHash[name] || _frameHashLocal[name];
-
- if(name && newName && typeof newName === 'number' && collisionPresent && numericNameWarningCount < numericNameWarningCountLimit) {
- numericNameWarningCount++;
-
- Lib.warn('addFrames: overwriting frame "' + (_frameHash[name] || _frameHashLocal[name]).name +
- '" with a frame whose name of type "number" also equates to "' +
- name + '". This is valid but may potentially lead to unexpected ' +
- 'behavior since all plotly.js frame names are stored internally ' +
- 'as strings.');
-
- if(numericNameWarningCount === numericNameWarningCountLimit) {
- Lib.warn('addFrames: This API call has yielded too many of these warnings. ' +
- 'For the rest of this call, further warnings about numeric frame ' +
- 'names will be suppressed.');
- }
- }
-
- _frameHashLocal[lookupName] = {name: lookupName};
-
- insertions.push({
- frame: Plots.supplyFrameDefaults(frameList[i]),
- index: (indices && indices[i] !== undefined && indices[i] !== null) ? indices[i] : bigIndex + i
- });
- }
-
- // Sort this, taking note that undefined insertions end up at the end:
- insertions.sort(function(a, b) {
- if(a.index > b.index) return -1;
- if(a.index < b.index) return 1;
- return 0;
- });
-
- var ops = [];
- var revops = [];
- var frameCount = _frames.length;
-
- for(i = insertions.length - 1; i >= 0; i--) {
- frame = insertions[i].frame;
-
- if(typeof frame.name === 'number') {
- Lib.warn('Warning: addFrames accepts frames with numeric names, but the numbers are' +
- 'implicitly cast to strings');
-
- }
-
- if(!frame.name) {
- // Repeatedly assign a default name, incrementing the counter each time until
- // we get a name that's not in the hashed lookup table:
- while(_frameHash[(frame.name = 'frame ' + gd._transitionData._counter++)]);
- }
-
- if(_frameHash[frame.name]) {
- // If frame is present, overwrite its definition:
- for(j = 0; j < _frames.length; j++) {
- if((_frames[j] || {}).name === frame.name) break;
- }
- ops.push({type: 'replace', index: j, value: frame});
- revops.unshift({type: 'replace', index: j, value: _frames[j]});
- } else {
- // Otherwise insert it at the end of the list:
- idx = Math.max(0, Math.min(insertions[i].index, frameCount));
-
- ops.push({type: 'insert', index: idx, value: frame});
- revops.unshift({type: 'delete', index: idx});
- frameCount++;
- }
- }
-
- var undoFunc = Plots.modifyFrames;
- var redoFunc = Plots.modifyFrames;
- var undoArgs = [gd, revops];
- var redoArgs = [gd, ops];
-
- if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
-
- return Plots.modifyFrames(gd, ops);
- };
-
- /**
- * Delete frame
- *
- * @param {string id or DOM element} gd
- * the id or DOM element of the graph container div
- *
- * @param {array of integers} frameList
- * list of integer indices of frames to be deleted
- */
- exports.deleteFrames = function(gd, frameList) {
- gd = Lib.getGraphDiv(gd);
-
- if(!Lib.isPlotDiv(gd)) {
- throw new Error('This element is not a Plotly plot: ' + gd);
- }
-
- var i, idx;
- var _frames = gd._transitionData._frames;
- var ops = [];
- var revops = [];
-
- if(!frameList) {
- frameList = [];
- for(i = 0; i < _frames.length; i++) {
- frameList.push(i);
- }
- }
-
- frameList = frameList.slice(0);
- frameList.sort();
-
- for(i = frameList.length - 1; i >= 0; i--) {
- idx = frameList[i];
- ops.push({type: 'delete', index: idx});
- revops.unshift({type: 'insert', index: idx, value: _frames[idx]});
- }
-
- var undoFunc = Plots.modifyFrames;
- var redoFunc = Plots.modifyFrames;
- var undoArgs = [gd, revops];
- var redoArgs = [gd, ops];
-
- if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
-
- return Plots.modifyFrames(gd, ops);
- };
-
- /**
- * Purge a graph container div back to its initial pre-Plotly.plot state
- *
- * @param {string id or DOM element} gd
- * the id or DOM element of the graph container div
- */
- exports.purge = function purge(gd) {
- gd = Lib.getGraphDiv(gd);
-
- var fullLayout = gd._fullLayout || {};
- var fullData = gd._fullData || [];
-
- // remove gl contexts
- Plots.cleanPlot([], {}, fullData, fullLayout);
-
- // purge properties
- Plots.purge(gd);
-
- // purge event emitter methods
- Events.purge(gd);
-
- // remove plot container
- if(fullLayout._container) fullLayout._container.remove();
-
- // in contrast to Plotly.Plots.purge which does NOT clear _context!
- delete gd._context;
-
- return gd;
- };
-
- // -------------------------------------------------------
- // makePlotFramework: Create the plot container and axes
- // -------------------------------------------------------
- function makePlotFramework(gd) {
- var gd3 = d3.select(gd);
- var fullLayout = gd._fullLayout;
-
- // Plot container
- fullLayout._container = gd3.selectAll('.plot-container').data([0]);
- fullLayout._container.enter().insert('div', ':first-child')
- .classed('plot-container', true)
- .classed('plotly', true);
-
- // Make the svg container
- fullLayout._paperdiv = fullLayout._container.selectAll('.svg-container').data([0]);
- fullLayout._paperdiv.enter().append('div')
- .classed('svg-container', true)
- .style('position', 'relative');
-
- // Make the graph containers
- // start fresh each time we get here, so we know the order comes out
- // right, rather than enter/exit which can muck up the order
- // TODO: sort out all the ordering so we don't have to
- // explicitly delete anything
- // FIXME: parcoords reuses this object, not the best pattern
- fullLayout._glcontainer = fullLayout._paperdiv.selectAll('.gl-container')
- .data([{}]);
-
- fullLayout._glcontainer.enter().append('div')
- .classed('gl-container', true);
-
- fullLayout._paperdiv.selectAll('.main-svg').remove();
-
- fullLayout._paper = fullLayout._paperdiv.insert('svg', ':first-child')
- .classed('main-svg', true);
-
- fullLayout._toppaper = fullLayout._paperdiv.append('svg')
- .classed('main-svg', true);
-
- if(!fullLayout._uid) {
- var otherUids = {};
- d3.selectAll('defs').each(function() {
- if(this.id) otherUids[this.id.split('-')[1]] = 1;
- });
- fullLayout._uid = Lib.randstr(otherUids);
- }
-
- fullLayout._paperdiv.selectAll('.main-svg')
- .attr(xmlnsNamespaces.svgAttrs);
-
- fullLayout._defs = fullLayout._paper.append('defs')
- .attr('id', 'defs-' + fullLayout._uid);
-
- fullLayout._clips = fullLayout._defs.append('g')
- .classed('clips', true);
-
- fullLayout._topdefs = fullLayout._toppaper.append('defs')
- .attr('id', 'topdefs-' + fullLayout._uid);
-
- fullLayout._topclips = fullLayout._topdefs.append('g')
- .classed('clips', true);
-
- fullLayout._bgLayer = fullLayout._paper.append('g')
- .classed('bglayer', true);
-
- fullLayout._draggers = fullLayout._paper.append('g')
- .classed('draglayer', true);
-
- // lower shape/image layer - note that this is behind
- // all subplots data/grids but above the backgrounds
- // except inset subplots, whose backgrounds are drawn
- // inside their own group so that they appear above
- // the data for the main subplot
- // lower shapes and images which are fully referenced to
- // a subplot still get drawn within the subplot's group
- // so they will work correctly on insets
- var layerBelow = fullLayout._paper.append('g')
- .classed('layer-below', true);
- fullLayout._imageLowerLayer = layerBelow.append('g')
- .classed('imagelayer', true);
- fullLayout._shapeLowerLayer = layerBelow.append('g')
- .classed('shapelayer', true);
-
- // single cartesian layer for the whole plot
- fullLayout._cartesianlayer = fullLayout._paper.append('g').classed('cartesianlayer', true);
-
- // single polar layer for the whole plot
- fullLayout._polarlayer = fullLayout._paper.append('g').classed('polarlayer', true);
-
- // single ternary layer for the whole plot
- fullLayout._ternarylayer = fullLayout._paper.append('g').classed('ternarylayer', true);
-
- // single geo layer for the whole plot
- fullLayout._geolayer = fullLayout._paper.append('g').classed('geolayer', true);
-
- // single pie layer for the whole plot
- fullLayout._pielayer = fullLayout._paper.append('g').classed('pielayer', true);
-
- // fill in image server scrape-svg
- fullLayout._glimages = fullLayout._paper.append('g').classed('glimages', true);
-
- // lastly upper shapes, info (legend, annotations) and hover layers go on top
- // these are in a different svg element normally, but get collapsed into a single
- // svg when exporting (after inserting 3D)
- // upper shapes/images are only those drawn above the whole plot, including subplots
- var layerAbove = fullLayout._toppaper.append('g')
- .classed('layer-above', true);
- fullLayout._imageUpperLayer = layerAbove.append('g')
- .classed('imagelayer', true);
- fullLayout._shapeUpperLayer = layerAbove.append('g')
- .classed('shapelayer', true);
-
- fullLayout._infolayer = fullLayout._toppaper.append('g').classed('infolayer', true);
- fullLayout._menulayer = fullLayout._toppaper.append('g').classed('menulayer', true);
- fullLayout._zoomlayer = fullLayout._toppaper.append('g').classed('zoomlayer', true);
- fullLayout._hoverlayer = fullLayout._toppaper.append('g').classed('hoverlayer', true);
-
- gd.emit('plotly_framework');
- }
-
- },{"../components/color":51,"../components/colorbar/connect":53,"../components/drawing":72,"../constants/xmlns_namespaces":150,"../lib":168,"../lib/events":161,"../lib/queue":182,"../lib/svg_text_utils":189,"../plots/cartesian/axes":212,"../plots/cartesian/constants":218,"../plots/cartesian/graph_interact":222,"../plots/plots":245,"../plots/polar/legacy":248,"../registry":257,"./edit_types":195,"./helpers":196,"./manage_arrays":198,"./plot_config":200,"./plot_schema":201,"./subroutines":203,"d3":16,"fast-isnumeric":18,"has-hover":20}],200:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /**
- * This will be transferred over to gd and overridden by
- * config args to Plotly.plot.
- *
- * The defaults are the appropriate settings for plotly.js,
- * so we get the right experience without any config argument.
- *
- * N.B. the config options are not coerced using Lib.coerce so keys
- * like `valType` and `values` are only set for documentation purposes
- * at the moment.
- */
-
- var configAttributes = {
- staticPlot: {
- valType: 'boolean',
- dflt: false,
-
- },
-
- plotlyServerURL: {
- valType: 'string',
- dflt: 'https://plot.ly',
-
- },
-
- editable: {
- valType: 'boolean',
- dflt: false,
-
- },
- edits: {
- annotationPosition: {
- valType: 'boolean',
- dflt: false,
-
- },
- annotationTail: {
- valType: 'boolean',
- dflt: false,
-
- },
- annotationText: {
- valType: 'boolean',
- dflt: false,
-
- },
- axisTitleText: {
- valType: 'boolean',
- dflt: false,
-
- },
- colorbarPosition: {
- valType: 'boolean',
- dflt: false,
-
- },
- colorbarTitleText: {
- valType: 'boolean',
- dflt: false,
-
- },
- legendPosition: {
- valType: 'boolean',
- dflt: false,
-
- },
- legendText: {
- valType: 'boolean',
- dflt: false,
-
- },
- shapePosition: {
- valType: 'boolean',
- dflt: false,
-
- },
- titleText: {
- valType: 'boolean',
- dflt: false,
-
- }
- },
-
- autosizable: {
- valType: 'boolean',
- dflt: false,
-
- },
- responsive: {
- valType: 'boolean',
- dflt: false,
-
- },
- fillFrame: {
- valType: 'boolean',
- dflt: false,
-
- },
- frameMargins: {
- valType: 'number',
- dflt: 0,
- min: 0,
- max: 0.5,
-
- },
-
- scrollZoom: {
- valType: 'flaglist',
- flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
- extras: [true, false],
- dflt: 'gl3d+geo+mapbox',
-
- },
- doubleClick: {
- valType: 'enumerated',
- values: [false, 'reset', 'autosize', 'reset+autosize'],
- dflt: 'reset+autosize',
-
- },
-
- showAxisDragHandles: {
- valType: 'boolean',
- dflt: true,
-
- },
- showAxisRangeEntryBoxes: {
- valType: 'boolean',
- dflt: true,
-
- },
-
- showTips: {
- valType: 'boolean',
- dflt: true,
-
- },
-
- showLink: {
- valType: 'boolean',
- dflt: false,
-
- },
- linkText: {
- valType: 'string',
- dflt: 'Edit chart',
- noBlank: true,
-
- },
- sendData: {
- valType: 'boolean',
- dflt: true,
-
- },
- showSources: {
- valType: 'any',
- dflt: false,
-
- },
-
- displayModeBar: {
- valType: 'enumerated',
- values: ['hover', true, false],
- dflt: 'hover',
-
- },
- showSendToCloud: {
- valType: 'boolean',
- dflt: false,
-
- },
- modeBarButtonsToRemove: {
- valType: 'any',
- dflt: [],
-
- },
- modeBarButtonsToAdd: {
- valType: 'any',
- dflt: [],
-
- },
- modeBarButtons: {
- valType: 'any',
- dflt: false,
-
- },
- toImageButtonOptions: {
- valType: 'any',
- dflt: {},
-
- },
- displaylogo: {
- valType: 'boolean',
- dflt: true,
-
- },
- watermark: {
- valType: 'boolean',
- dflt: false,
-
- },
-
- plotGlPixelRatio: {
- valType: 'number',
- dflt: 2,
- min: 1,
- max: 4,
-
- },
-
- setBackground: {
- valType: 'any',
- dflt: 'transparent',
-
- },
-
- topojsonURL: {
- valType: 'string',
- noBlank: true,
- dflt: 'https://cdn.plot.ly/',
-
- },
-
- mapboxAccessToken: {
- valType: 'string',
- dflt: null,
-
- },
-
- logging: {
- valType: 'boolean',
- dflt: 1,
-
- },
-
- queueLength: {
- valType: 'integer',
- min: 0,
- dflt: 0,
-
- },
-
- globalTransforms: {
- valType: 'any',
- dflt: [],
-
- },
-
- locale: {
- valType: 'string',
- dflt: 'en-US',
-
- },
-
- locales: {
- valType: 'any',
- dflt: {},
-
- }
- };
-
- var dfltConfig = {};
-
- function crawl(src, target) {
- for(var k in src) {
- var obj = src[k];
- if(obj.valType) {
- target[k] = obj.dflt;
- } else {
- if(!target[k]) {
- target[k] = {};
- }
- crawl(obj, target[k]);
- }
- }
- }
-
- crawl(configAttributes, dfltConfig);
-
- module.exports = {
- configAttributes: configAttributes,
- dfltConfig: dfltConfig
- };
-
- },{}],201:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../registry');
- var Lib = _dereq_('../lib');
-
- var baseAttributes = _dereq_('../plots/attributes');
- var baseLayoutAttributes = _dereq_('../plots/layout_attributes');
- var frameAttributes = _dereq_('../plots/frame_attributes');
- var animationAttributes = _dereq_('../plots/animation_attributes');
- var configAttributes = _dereq_('./plot_config').configAttributes;
-
- // polar attributes are not part of the Registry yet
- var polarAreaAttrs = _dereq_('../plots/polar/legacy/area_attributes');
- var polarAxisAttrs = _dereq_('../plots/polar/legacy/axis_attributes');
-
- var editTypes = _dereq_('./edit_types');
-
- var extendFlat = Lib.extendFlat;
- var extendDeepAll = Lib.extendDeepAll;
- var isPlainObject = Lib.isPlainObject;
-
- var IS_SUBPLOT_OBJ = '_isSubplotObj';
- var IS_LINKED_TO_ARRAY = '_isLinkedToArray';
- var ARRAY_ATTR_REGEXPS = '_arrayAttrRegexps';
- var DEPRECATED = '_deprecated';
- var UNDERSCORE_ATTRS = [IS_SUBPLOT_OBJ, IS_LINKED_TO_ARRAY, ARRAY_ATTR_REGEXPS, DEPRECATED];
-
- exports.IS_SUBPLOT_OBJ = IS_SUBPLOT_OBJ;
- exports.IS_LINKED_TO_ARRAY = IS_LINKED_TO_ARRAY;
- exports.DEPRECATED = DEPRECATED;
- exports.UNDERSCORE_ATTRS = UNDERSCORE_ATTRS;
-
- /** Outputs the full plotly.js plot schema
- *
- * @return {object}
- * - defs
- * - traces
- * - layout
- * - transforms
- * - frames
- * - animations
- * - config
- */
- exports.get = function() {
- var traces = {};
-
- Registry.allTypes.concat('area').forEach(function(type) {
- traces[type] = getTraceAttributes(type);
- });
-
- var transforms = {};
-
- Object.keys(Registry.transformsRegistry).forEach(function(type) {
- transforms[type] = getTransformAttributes(type);
- });
-
- return {
- defs: {
- valObjects: Lib.valObjectMeta,
- metaKeys: UNDERSCORE_ATTRS.concat(['description', 'role', 'editType', 'impliedEdits']),
- editType: {
- traces: editTypes.traces,
- layout: editTypes.layout
- },
- impliedEdits: {
-
- }
- },
-
- traces: traces,
- layout: getLayoutAttributes(),
-
- transforms: transforms,
-
- frames: getFramesAttributes(),
- animation: formatAttributes(animationAttributes),
-
- config: formatAttributes(configAttributes)
- };
- };
-
- /**
- * Crawl the attribute tree, recursively calling a callback function
- *
- * @param {object} attrs
- * The node of the attribute tree (e.g. the root) from which recursion originates
- * @param {Function} callback
- * A callback function with the signature:
- * @callback callback
- * @param {object} attr an attribute
- * @param {String} attrName name string
- * @param {object[]} attrs all the attributes
- * @param {Number} level the recursion level, 0 at the root
- * @param {String} fullAttrString full attribute name (ie 'marker.line')
- * @param {Number} [specifiedLevel]
- * The level in the tree, in order to let the callback function detect descend or backtrack,
- * typically unsupplied (implied 0), just used by the self-recursive call.
- * The necessity arises because the tree traversal is not controlled by callback return values.
- * The decision to not use callback return values for controlling tree pruning arose from
- * the goal of keeping the crawler backwards compatible. Observe that one of the pruning conditions
- * precedes the callback call.
- * @param {string} [attrString]
- * the path to the current attribute, as an attribute string (ie 'marker.line')
- * typically unsupplied, but you may supply it if you want to disambiguate which attrs tree you
- * are starting from
- *
- * @return {object} transformOut
- * copy of transformIn that contains attribute defaults
- */
- exports.crawl = function(attrs, callback, specifiedLevel, attrString) {
- var level = specifiedLevel || 0;
- attrString = attrString || '';
-
- Object.keys(attrs).forEach(function(attrName) {
- var attr = attrs[attrName];
-
- if(UNDERSCORE_ATTRS.indexOf(attrName) !== -1) return;
-
- var fullAttrString = (attrString ? attrString + '.' : '') + attrName;
- callback(attr, attrName, attrs, level, fullAttrString);
-
- if(exports.isValObject(attr)) return;
-
- if(isPlainObject(attr) && attrName !== 'impliedEdits') {
- exports.crawl(attr, callback, level + 1, fullAttrString);
- }
- });
- };
-
- /** Is object a value object (or a container object)?
- *
- * @param {object} obj
- * @return {boolean}
- * returns true for a valid value object and
- * false for tree nodes in the attribute hierarchy
- */
- exports.isValObject = function(obj) {
- return obj && obj.valType !== undefined;
- };
-
- /**
- * Find all data array attributes in a given trace object - including
- * `arrayOk` attributes.
- *
- * @param {object} trace
- * full trace object that contains a reference to `_module.attributes`
- *
- * @return {array} arrayAttributes
- * list of array attributes for the given trace
- */
- exports.findArrayAttributes = function(trace) {
- var arrayAttributes = [];
- var stack = [];
- var isArrayStack = [];
- var baseContainer, baseAttrName;
-
- function callback(attr, attrName, attrs, level) {
- stack = stack.slice(0, level).concat([attrName]);
- isArrayStack = isArrayStack.slice(0, level).concat([attr && attr._isLinkedToArray]);
-
- var splittableAttr = (
- attr &&
- (attr.valType === 'data_array' || attr.arrayOk === true) &&
- !(stack[level - 1] === 'colorbar' && (attrName === 'ticktext' || attrName === 'tickvals'))
- );
-
- // Manually exclude 'colorbar.tickvals' and 'colorbar.ticktext' for now
- // which are declared as `valType: 'data_array'` but scale independently of
- // the coordinate arrays.
- //
- // Down the road, we might want to add a schema field (e.g `uncorrelatedArray: true`)
- // to distinguish attributes of the likes.
-
- if(!splittableAttr) return;
-
- crawlIntoTrace(baseContainer, 0, '');
- }
-
- function crawlIntoTrace(container, i, astrPartial) {
- var item = container[stack[i]];
- var newAstrPartial = astrPartial + stack[i];
- if(i === stack.length - 1) {
- if(Lib.isArrayOrTypedArray(item)) {
- arrayAttributes.push(baseAttrName + newAstrPartial);
- }
- }
- else {
- if(isArrayStack[i]) {
- if(Array.isArray(item)) {
- for(var j = 0; j < item.length; j++) {
- if(Lib.isPlainObject(item[j])) {
- crawlIntoTrace(item[j], i + 1, newAstrPartial + '[' + j + '].');
- }
- }
- }
- }
- else if(Lib.isPlainObject(item)) {
- crawlIntoTrace(item, i + 1, newAstrPartial + '.');
- }
- }
- }
-
- baseContainer = trace;
- baseAttrName = '';
- exports.crawl(baseAttributes, callback);
- if(trace._module && trace._module.attributes) {
- exports.crawl(trace._module.attributes, callback);
- }
-
- var transforms = trace.transforms;
- if(transforms) {
- for(var i = 0; i < transforms.length; i++) {
- var transform = transforms[i];
- var module = transform._module;
-
- if(module) {
- baseAttrName = 'transforms[' + i + '].';
- baseContainer = transform;
-
- exports.crawl(module.attributes, callback);
- }
- }
- }
-
- return arrayAttributes;
- };
-
- /*
- * Find the valObject for one attribute in an existing trace
- *
- * @param {object} trace
- * full trace object that contains a reference to `_module.attributes`
- * @param {object} parts
- * an array of parts, like ['transforms', 1, 'value']
- * typically from nestedProperty(...).parts
- *
- * @return {object|false}
- * the valObject for this attribute, or the last found parent
- * in some cases the innermost valObject will not exist, for example
- * `valType: 'any'` attributes where we might set a part of the attribute.
- * In that case, stop at the deepest valObject we *do* find.
- */
- exports.getTraceValObject = function(trace, parts) {
- var head = parts[0];
- var i = 1; // index to start recursing from
- var moduleAttrs, valObject;
-
- if(head === 'transforms') {
- if(parts.length === 1) {
- return baseAttributes.transforms;
- }
- var transforms = trace.transforms;
- if(!Array.isArray(transforms) || !transforms.length) return false;
- var tNum = parts[1];
- if(!isIndex(tNum) || tNum >= transforms.length) {
- return false;
- }
- moduleAttrs = (Registry.transformsRegistry[transforms[tNum].type] || {}).attributes;
- valObject = moduleAttrs && moduleAttrs[parts[2]];
- i = 3; // start recursing only inside the transform
- }
- else if(trace.type === 'area') {
- valObject = polarAreaAttrs[head];
- }
- else {
- // first look in the module for this trace
- // components have already merged their trace attributes in here
- var _module = trace._module;
- if(!_module) _module = (Registry.modules[trace.type || baseAttributes.type.dflt] || {})._module;
- if(!_module) return false;
-
- moduleAttrs = _module.attributes;
- valObject = moduleAttrs && moduleAttrs[head];
-
- // then look in the subplot attributes
- if(!valObject) {
- var subplotModule = _module.basePlotModule;
- if(subplotModule && subplotModule.attributes) {
- valObject = subplotModule.attributes[head];
- }
- }
-
- // finally look in the global attributes
- if(!valObject) valObject = baseAttributes[head];
- }
-
- return recurseIntoValObject(valObject, parts, i);
- };
-
- /*
- * Find the valObject for one layout attribute
- *
- * @param {array} parts
- * an array of parts, like ['annotations', 1, 'x']
- * typically from nestedProperty(...).parts
- *
- * @return {object|false}
- * the valObject for this attribute, or the last found parent
- * in some cases the innermost valObject will not exist, for example
- * `valType: 'any'` attributes where we might set a part of the attribute.
- * In that case, stop at the deepest valObject we *do* find.
- */
- exports.getLayoutValObject = function(fullLayout, parts) {
- var valObject = layoutHeadAttr(fullLayout, parts[0]);
-
- return recurseIntoValObject(valObject, parts, 1);
- };
-
- function layoutHeadAttr(fullLayout, head) {
- var i, key, _module, attributes;
-
- // look for attributes of the subplot types used on the plot
- var basePlotModules = fullLayout._basePlotModules;
- if(basePlotModules) {
- var out;
- for(i = 0; i < basePlotModules.length; i++) {
- _module = basePlotModules[i];
- if(_module.attrRegex && _module.attrRegex.test(head)) {
- // if a module defines overrides, these take precedence
- // initially this is to allow gl2d different editTypes from svg cartesian
- if(_module.layoutAttrOverrides) return _module.layoutAttrOverrides;
-
- // otherwise take the first attributes we find
- if(!out && _module.layoutAttributes) out = _module.layoutAttributes;
- }
-
- // a module can also override the behavior of base (and component) module layout attrs
- // again see gl2d for initial use case
- var baseOverrides = _module.baseLayoutAttrOverrides;
- if(baseOverrides && head in baseOverrides) return baseOverrides[head];
- }
- if(out) return out;
- }
-
- // look for layout attributes contributed by traces on the plot
- var modules = fullLayout._modules;
- if(modules) {
- for(i = 0; i < modules.length; i++) {
- attributes = modules[i].layoutAttributes;
- if(attributes && head in attributes) {
- return attributes[head];
- }
- }
- }
-
- /*
- * Next look in components.
- * Components that define a schema have already merged this into
- * base and subplot attribute defs, so ignore these.
- * Others (older style) all put all their attributes
- * inside a container matching the module `name`
- * eg `attributes` (array) or `legend` (object)
- */
- for(key in Registry.componentsRegistry) {
- _module = Registry.componentsRegistry[key];
- if(!_module.schema && (head === _module.name)) {
- return _module.layoutAttributes;
- }
- }
-
- if(head in baseLayoutAttributes) return baseLayoutAttributes[head];
-
- // Polar doesn't populate _modules or _basePlotModules
- // just fall back on these when the others fail
- if(head === 'radialaxis' || head === 'angularaxis') {
- return polarAxisAttrs[head];
- }
- return polarAxisAttrs.layout[head] || false;
- }
-
- function recurseIntoValObject(valObject, parts, i) {
- if(!valObject) return false;
-
- if(valObject._isLinkedToArray) {
- // skip array index, abort if we try to dive into an array without an index
- if(isIndex(parts[i])) i++;
- else if(i < parts.length) return false;
- }
-
- // now recurse as far as we can. Occasionally we have an attribute
- // setting an internal part below what's in the schema; just return
- // the innermost schema item we find.
- for(; i < parts.length; i++) {
- var newValObject = valObject[parts[i]];
- if(isPlainObject(newValObject)) valObject = newValObject;
- else break;
-
- if(i === parts.length - 1) break;
-
- if(valObject._isLinkedToArray) {
- i++;
- if(!isIndex(parts[i])) return false;
- }
- else if(valObject.valType === 'info_array') {
- i++;
- var index = parts[i];
- if(!isIndex(index)) return false;
-
- var items = valObject.items;
- if(Array.isArray(items)) {
- if(index >= items.length) return false;
- if(valObject.dimensions === 2) {
- i++;
- if(parts.length === i) return valObject;
- var index2 = parts[i];
- if(!isIndex(index2)) return false;
- valObject = items[index][index2];
- }
- else valObject = items[index];
- }
- else {
- valObject = items;
- }
- }
- }
-
- return valObject;
- }
-
- // note: this is different from Lib.isIndex, this one doesn't accept numeric
- // strings, only actual numbers.
- function isIndex(val) {
- return val === Math.round(val) && val >= 0;
- }
-
- function getTraceAttributes(type) {
- var _module, basePlotModule;
-
- if(type === 'area') {
- _module = { attributes: polarAreaAttrs };
- basePlotModule = {};
- }
- else {
- _module = Registry.modules[type]._module,
- basePlotModule = _module.basePlotModule;
- }
-
- var attributes = {};
-
- // make 'type' the first attribute in the object
- attributes.type = null;
-
-
- var copyBaseAttributes = extendDeepAll({}, baseAttributes);
- var copyModuleAttributes = extendDeepAll({}, _module.attributes);
-
- // prune global-level trace attributes that are already defined in a trace
- exports.crawl(copyModuleAttributes, function(attr, attrName, attrs, level, fullAttrString) {
- Lib.nestedProperty(copyBaseAttributes, fullAttrString).set(undefined);
- // Prune undefined attributes
- if(attr === undefined) Lib.nestedProperty(copyModuleAttributes, fullAttrString).set(undefined);
- });
-
- // base attributes (same for all trace types)
- extendDeepAll(attributes, copyBaseAttributes);
-
- // module attributes
- extendDeepAll(attributes, copyModuleAttributes);
-
- // subplot attributes
- if(basePlotModule.attributes) {
- extendDeepAll(attributes, basePlotModule.attributes);
- }
-
- // 'type' gets overwritten by baseAttributes; reset it here
- attributes.type = type;
-
- var out = {
- meta: _module.meta || {},
- attributes: formatAttributes(attributes),
- };
-
- // trace-specific layout attributes
- if(_module.layoutAttributes) {
- var layoutAttributes = {};
-
- extendDeepAll(layoutAttributes, _module.layoutAttributes);
- out.layoutAttributes = formatAttributes(layoutAttributes);
- }
-
- return out;
- }
-
- function getLayoutAttributes() {
- var layoutAttributes = {};
- var key, _module;
-
- // global layout attributes
- extendDeepAll(layoutAttributes, baseLayoutAttributes);
-
- // add base plot module layout attributes
- for(key in Registry.subplotsRegistry) {
- _module = Registry.subplotsRegistry[key];
-
- if(!_module.layoutAttributes) continue;
-
- if(Array.isArray(_module.attr)) {
- for(var i = 0; i < _module.attr.length; i++) {
- handleBasePlotModule(layoutAttributes, _module, _module.attr[i]);
- }
- } else {
- var astr = _module.attr === 'subplot' ? _module.name : _module.attr;
- handleBasePlotModule(layoutAttributes, _module, astr);
- }
- }
-
- // polar layout attributes
- layoutAttributes = assignPolarLayoutAttrs(layoutAttributes);
-
- // add registered components layout attributes
- for(key in Registry.componentsRegistry) {
- _module = Registry.componentsRegistry[key];
- var schema = _module.schema;
-
- /*
- * Components with defined schema have already been merged in at register time
- * but a few components define attributes that apply only to xaxis
- * not yaxis (rangeselector, rangeslider) - delete from y schema.
- * Note that the input attributes for xaxis/yaxis are the same object
- * so it's not possible to only add them to xaxis from the start.
- * If we ever have such asymmetry the other way, or anywhere else,
- * we will need to extend both this code and mergeComponentAttrsToSubplot
- * (which will not find yaxis only for example)
- */
- if(schema && (schema.subplots || schema.layout)) {
- var subplots = schema.subplots;
- if(subplots && subplots.xaxis && !subplots.yaxis) {
- for(var xkey in subplots.xaxis) delete layoutAttributes.yaxis[xkey];
- }
- }
- // older style without schema need to be explicitly merged in now
- else if(_module.layoutAttributes) {
- insertAttrs(layoutAttributes, _module.layoutAttributes, _module.name);
- }
- }
-
- return {
- layoutAttributes: formatAttributes(layoutAttributes)
- };
- }
-
- function getTransformAttributes(type) {
- var _module = Registry.transformsRegistry[type];
- var attributes = extendDeepAll({}, _module.attributes);
-
- // add registered components transform attributes
- Object.keys(Registry.componentsRegistry).forEach(function(k) {
- var _module = Registry.componentsRegistry[k];
-
- if(_module.schema && _module.schema.transforms && _module.schema.transforms[type]) {
- Object.keys(_module.schema.transforms[type]).forEach(function(v) {
- insertAttrs(attributes, _module.schema.transforms[type][v], v);
- });
- }
- });
-
- return {
- attributes: formatAttributes(attributes)
- };
- }
-
- function getFramesAttributes() {
- var attrs = {
- frames: Lib.extendDeepAll({}, frameAttributes)
- };
-
- formatAttributes(attrs);
-
- return attrs.frames;
- }
-
- function formatAttributes(attrs) {
- mergeValTypeAndRole(attrs);
- formatArrayContainers(attrs);
- stringify(attrs);
-
- return attrs;
- }
-
- function mergeValTypeAndRole(attrs) {
-
- function makeSrcAttr(attrName) {
- return {
- valType: 'string',
-
-
- editType: 'none'
- };
- }
-
- function callback(attr, attrName, attrs) {
- if(exports.isValObject(attr)) {
- if(attr.valType === 'data_array') {
- // all 'data_array' attrs have role 'data'
- attr.role = 'data';
- // all 'data_array' attrs have a corresponding 'src' attr
- attrs[attrName + 'src'] = makeSrcAttr(attrName);
- }
- else if(attr.arrayOk === true) {
- // all 'arrayOk' attrs have a corresponding 'src' attr
- attrs[attrName + 'src'] = makeSrcAttr(attrName);
- }
- }
- else if(isPlainObject(attr)) {
- // all attrs container objects get role 'object'
- attr.role = 'object';
- }
- }
-
- exports.crawl(attrs, callback);
- }
-
- function formatArrayContainers(attrs) {
-
- function callback(attr, attrName, attrs) {
- if(!attr) return;
-
- var itemName = attr[IS_LINKED_TO_ARRAY];
-
- if(!itemName) return;
-
- delete attr[IS_LINKED_TO_ARRAY];
-
- attrs[attrName] = { items: {} };
- attrs[attrName].items[itemName] = attr;
- attrs[attrName].role = 'object';
- }
-
- exports.crawl(attrs, callback);
- }
-
- // this can take around 10ms and should only be run from PlotSchema.get(),
- // to ensure JSON.stringify(PlotSchema.get()) gives the intended result.
- function stringify(attrs) {
- function walk(attr) {
- for(var k in attr) {
- if(isPlainObject(attr[k])) {
- walk(attr[k]);
- } else if(Array.isArray(attr[k])) {
- for(var i = 0; i < attr[k].length; i++) {
- walk(attr[k][i]);
- }
- } else {
- // as JSON.stringify(/test/) // => {}
- if(attr[k] instanceof RegExp) {
- attr[k] = attr[k].toString();
- }
- }
- }
- }
-
- walk(attrs);
- }
-
- function assignPolarLayoutAttrs(layoutAttributes) {
- extendFlat(layoutAttributes, {
- radialaxis: polarAxisAttrs.radialaxis,
- angularaxis: polarAxisAttrs.angularaxis
- });
-
- extendFlat(layoutAttributes, polarAxisAttrs.layout);
-
- return layoutAttributes;
- }
-
- function handleBasePlotModule(layoutAttributes, _module, astr) {
- var np = Lib.nestedProperty(layoutAttributes, astr);
- var attrs = extendDeepAll({}, _module.layoutAttributes);
-
- attrs[IS_SUBPLOT_OBJ] = true;
- np.set(attrs);
- }
-
- function insertAttrs(baseAttrs, newAttrs, astr) {
- var np = Lib.nestedProperty(baseAttrs, astr);
-
- np.set(extendDeepAll(np.get() || {}, newAttrs));
- }
-
- },{"../lib":168,"../plots/animation_attributes":207,"../plots/attributes":209,"../plots/frame_attributes":240,"../plots/layout_attributes":243,"../plots/polar/legacy/area_attributes":246,"../plots/polar/legacy/axis_attributes":247,"../registry":257,"./edit_types":195,"./plot_config":200}],202:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../lib');
- var plotAttributes = _dereq_('../plots/attributes');
-
- var TEMPLATEITEMNAME = 'templateitemname';
-
- var templateAttrs = {
- name: {
- valType: 'string',
-
- editType: 'none',
-
- }
- };
- templateAttrs[TEMPLATEITEMNAME] = {
- valType: 'string',
-
- editType: 'calc',
-
- };
-
- /**
- * templatedArray: decorate an attributes object with templating (and array)
- * properties.
- *
- * @param {string} name: the singular form of the array name. Sets
- * `_isLinkedToArray` to this, so the schema knows to treat this as an array.
- * @param {object} attrs: the item attributes. Since all callers are expected
- * to be constructing this object on the spot, we mutate it here for
- * performance, rather than extending a new object with it.
- *
- * @returns {object}: the decorated `attrs` object
- */
- exports.templatedArray = function(name, attrs) {
- attrs._isLinkedToArray = name;
- attrs.name = templateAttrs.name;
- attrs[TEMPLATEITEMNAME] = templateAttrs[TEMPLATEITEMNAME];
- return attrs;
- };
-
- /**
- * traceTemplater: logic for matching traces to trace templates
- *
- * @param {object} dataTemplate: collection of {traceType: [{template}, ...]}
- * ie each type the template applies to contains a list of template objects,
- * to be provided cyclically to data traces of that type.
- *
- * @returns {object}: {newTrace}, a function:
- * newTrace(traceIn): that takes the input traceIn, coerces its type, then
- * uses that type to find the next template to apply. returns the output
- * traceOut with template attached, ready to continue supplyDefaults.
- */
- exports.traceTemplater = function(dataTemplate) {
- var traceCounts = {};
- var traceType, typeTemplates;
-
- for(traceType in dataTemplate) {
- typeTemplates = dataTemplate[traceType];
- if(Array.isArray(typeTemplates) && typeTemplates.length) {
- traceCounts[traceType] = 0;
- }
- }
-
- function newTrace(traceIn) {
- traceType = Lib.coerce(traceIn, {}, plotAttributes, 'type');
- var traceOut = {type: traceType, _template: null};
- if(traceType in traceCounts) {
- typeTemplates = dataTemplate[traceType];
- // cycle through traces in the template set for this type
- var typei = traceCounts[traceType] % typeTemplates.length;
- traceCounts[traceType]++;
- traceOut._template = typeTemplates[typei];
- }
- else {
- // TODO: anything we should do for types missing from the template?
- // try to apply some other type? Or just bail as we do here?
- // Actually I think yes, we should apply other types; would be nice
- // if all scatter* could inherit from each other, and if histogram
- // could inherit from bar, etc... but how to specify this? And do we
- // compose them, or if a type is present require it to be complete?
- // Actually this could apply to layout too - 3D annotations
- // inheriting from 2D, axes of different types inheriting from each
- // other...
- }
- return traceOut;
- }
-
- return {
- newTrace: newTrace
- // TODO: function to figure out what's left & what didn't work
- };
- };
-
- /**
- * newContainer: Create a new sub-container inside `container` and propagate any
- * applicable template to it. If there's no template, still propagates
- * `undefined` so relinkPrivate will not retain an old template!
- *
- * @param {object} container: the outer container, should already have _template
- * if there *is* a template for this plot
- * @param {string} name: the key of the new container to make
- * @param {string} baseName: if applicable, a base attribute to take the
- * template from, ie for xaxis3 the base would be xaxis
- *
- * @returns {object}: an object for inclusion _full*, empty except for the
- * appropriate template piece
- */
- exports.newContainer = function(container, name, baseName) {
- var template = container._template;
- var part = template && (template[name] || (baseName && template[baseName]));
- if(!Lib.isPlainObject(part)) part = null;
-
- var out = container[name] = {_template: part};
- return out;
- };
-
- /**
- * arrayTemplater: special logic for templating both defaults and specific items
- * in a container array (annotations etc)
- *
- * @param {object} container: the outer container, should already have _template
- * if there *is* a template for this plot
- * @param {string} name: the name of the array to template (ie 'annotations')
- * will be used to find default ('annotationdefaults' object) and specific
- * ('annotations' array) template specs.
- * @param {string} inclusionAttr: the attribute determining this item's
- * inclusion in the output, usually 'visible' or 'enabled'
- *
- * @returns {object}: {newItem, defaultItems}, both functions:
- * newItem(itemIn): create an output item, bare except for the correct
- * template and name(s), as the base for supplyDefaults
- * defaultItems(): to be called after all newItem calls, return any
- * specific template items that have not already beeen included,
- * also as bare output items ready for supplyDefaults.
- */
- exports.arrayTemplater = function(container, name, inclusionAttr) {
- var template = container._template;
- var defaultsTemplate = template && template[arrayDefaultKey(name)];
- var templateItems = template && template[name];
- if(!Array.isArray(templateItems) || !templateItems.length) {
- templateItems = [];
- }
-
- var usedNames = {};
-
- function newItem(itemIn) {
- // include name and templateitemname in the output object for ALL
- // container array items. Note: you could potentially use different
- // name and templateitemname, if you're using one template to make
- // another template. templateitemname would be the name in the original
- // template, and name is the new "subclassed" item name.
- var out = {name: itemIn.name, _input: itemIn};
- var templateItemName = out[TEMPLATEITEMNAME] = itemIn[TEMPLATEITEMNAME];
-
- // no itemname: use the default template
- if(!validItemName(templateItemName)) {
- out._template = defaultsTemplate;
- return out;
- }
-
- // look for an item matching this itemname
- // note these do not inherit from the default template, only the item.
- for(var i = 0; i < templateItems.length; i++) {
- var templateItem = templateItems[i];
- if(templateItem.name === templateItemName) {
- // Note: it's OK to use a template item more than once
- // but using it at least once will stop it from generating
- // a default item at the end.
- usedNames[templateItemName] = 1;
- out._template = templateItem;
- return out;
- }
- }
-
- // Didn't find a matching template item, so since this item is intended
- // to only be modifications it's most likely broken. Hide it unless
- // it's explicitly marked visible - in which case it gets NO template,
- // not even the default.
- out[inclusionAttr] = itemIn[inclusionAttr] || false;
- // special falsy value we can look for in validateTemplate
- out._template = false;
- return out;
- }
-
- function defaultItems() {
- var out = [];
- for(var i = 0; i < templateItems.length; i++) {
- var templateItem = templateItems[i];
- var name = templateItem.name;
- // only allow named items to be added as defaults,
- // and only allow each name once
- if(validItemName(name) && !usedNames[name]) {
- var outi = {
- _template: templateItem,
- name: name,
- _input: {_templateitemname: name}
- };
- outi[TEMPLATEITEMNAME] = templateItem[TEMPLATEITEMNAME];
- out.push(outi);
- usedNames[name] = 1;
- }
- }
- return out;
- }
-
- return {
- newItem: newItem,
- defaultItems: defaultItems
- };
- };
-
- function validItemName(name) {
- return name && typeof name === 'string';
- }
-
- function arrayDefaultKey(name) {
- var lastChar = name.length - 1;
- if(name.charAt(lastChar) !== 's') {
- Lib.warn('bad argument to arrayDefaultKey: ' + name);
- }
- return name.substr(0, name.length - 1) + 'defaults';
- }
- exports.arrayDefaultKey = arrayDefaultKey;
-
- /**
- * arrayEditor: helper for editing array items that may have come from
- * template defaults (in which case they will not exist in the input yet)
- *
- * @param {object} parentIn: the input container (eg gd.layout)
- * @param {string} containerStr: the attribute string for the container inside
- * `parentIn`.
- * @param {object} itemOut: the _full* item (eg gd._fullLayout.annotations[0])
- * that we'll be editing. Assumed to have been created by `arrayTemplater`.
- *
- * @returns {object}: {modifyBase, modifyItem, getUpdateObj, applyUpdate}, all functions:
- * modifyBase(attr, value): Add an update that's *not* related to the item.
- * `attr` is the full attribute string.
- * modifyItem(attr, value): Add an update to the item. `attr` is just the
- * portion of the attribute string inside the item.
- * getUpdateObj(): Get the final constructed update object, to use in
- * `restyle` or `relayout`. Also resets the update object in case this
- * update was canceled.
- * applyUpdate(attr, value): optionally add an update `attr: value`,
- * then apply it to `parent` which should be the parent of `containerIn`,
- * ie the object to which `containerStr` is the attribute string.
- */
- exports.arrayEditor = function(parentIn, containerStr, itemOut) {
- var lengthIn = (Lib.nestedProperty(parentIn, containerStr).get() || []).length;
- var index = itemOut._index;
- // Check that we are indeed off the end of this container.
- // Otherwise a devious user could put a key `_templateitemname` in their
- // own input and break lots of things.
- var templateItemName = (index >= lengthIn) && (itemOut._input || {})._templateitemname;
- if(templateItemName) index = lengthIn;
- var itemStr = containerStr + '[' + index + ']';
-
- var update;
- function resetUpdate() {
- update = {};
- if(templateItemName) {
- update[itemStr] = {};
- update[itemStr][TEMPLATEITEMNAME] = templateItemName;
- }
- }
- resetUpdate();
-
- function modifyBase(attr, value) {
- update[attr] = value;
- }
-
- function modifyItem(attr, value) {
- if(templateItemName) {
- // we're making a new object: edit that object
- Lib.nestedProperty(update[itemStr], attr).set(value);
- }
- else {
- // we're editing an existing object: include *just* the edit
- update[itemStr + '.' + attr] = value;
- }
- }
-
- function getUpdateObj() {
- var updateOut = update;
- resetUpdate();
- return updateOut;
- }
-
- function applyUpdate(attr, value) {
- if(attr) modifyItem(attr, value);
- var updateToApply = getUpdateObj();
- for(var key in updateToApply) {
- Lib.nestedProperty(parentIn, key).set(updateToApply[key]);
- }
- }
-
- return {
- modifyBase: modifyBase,
- modifyItem: modifyItem,
- getUpdateObj: getUpdateObj,
- applyUpdate: applyUpdate
- };
- };
-
- },{"../lib":168,"../plots/attributes":209}],203:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var Registry = _dereq_('../registry');
- var Plots = _dereq_('../plots/plots');
-
- var Lib = _dereq_('../lib');
- var clearGlCanvases = _dereq_('../lib/clear_gl_canvases');
-
- var Color = _dereq_('../components/color');
- var Drawing = _dereq_('../components/drawing');
- var Titles = _dereq_('../components/titles');
- var ModeBar = _dereq_('../components/modebar');
-
- var Axes = _dereq_('../plots/cartesian/axes');
- var alignmentConstants = _dereq_('../constants/alignment');
- var axisConstraints = _dereq_('../plots/cartesian/constraints');
- var enforceAxisConstraints = axisConstraints.enforce;
- var cleanAxisConstraints = axisConstraints.clean;
- var doAutoRange = _dereq_('../plots/cartesian/autorange').doAutoRange;
-
- var SVG_TEXT_ANCHOR_START = 'start';
- var SVG_TEXT_ANCHOR_MIDDLE = 'middle';
- var SVG_TEXT_ANCHOR_END = 'end';
-
- exports.layoutStyles = function(gd) {
- return Lib.syncOrAsync([Plots.doAutoMargin, lsInner], gd);
- };
-
- function overlappingDomain(xDomain, yDomain, domains) {
- for(var i = 0; i < domains.length; i++) {
- var existingX = domains[i][0];
- var existingY = domains[i][1];
-
- if(existingX[0] >= xDomain[1] || existingX[1] <= xDomain[0]) {
- continue;
- }
- if(existingY[0] < yDomain[1] && existingY[1] > yDomain[0]) {
- return true;
- }
- }
- return false;
- }
-
- function lsInner(gd) {
- var fullLayout = gd._fullLayout;
- var gs = fullLayout._size;
- var pad = gs.p;
- var axList = Axes.list(gd, '', true);
- var i, subplot, plotinfo, ax, xa, ya;
-
- fullLayout._paperdiv.style({
- width: (gd._context.responsive && fullLayout.autosize && !gd._context._hasZeroWidth && !gd.layout.width) ? '100%' : fullLayout.width + 'px',
- height: (gd._context.responsive && fullLayout.autosize && !gd._context._hasZeroHeight && !gd.layout.height) ? '100%' : fullLayout.height + 'px'
- })
- .selectAll('.main-svg')
- .call(Drawing.setSize, fullLayout.width, fullLayout.height);
- gd._context.setBackground(gd, fullLayout.paper_bgcolor);
-
- exports.drawMainTitle(gd);
- ModeBar.manage(gd);
-
- // _has('cartesian') means SVG specifically, not GL2D - but GL2D
- // can still get here because it makes some of the SVG structure
- // for shared features like selections.
- if(!fullLayout._has('cartesian')) {
- return gd._promises.length && Promise.all(gd._promises);
- }
-
- function getLinePosition(ax, counterAx, side) {
- var lwHalf = ax._lw / 2;
-
- if(ax._id.charAt(0) === 'x') {
- if(!counterAx) return gs.t + gs.h * (1 - (ax.position || 0)) + (lwHalf % 1);
- else if(side === 'top') return counterAx._offset - pad - lwHalf;
- return counterAx._offset + counterAx._length + pad + lwHalf;
- }
-
- if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1);
- else if(side === 'right') return counterAx._offset + counterAx._length + pad + lwHalf;
- return counterAx._offset - pad - lwHalf;
- }
-
- // some preparation of axis position info
- for(i = 0; i < axList.length; i++) {
- ax = axList[i];
-
- var counterAx = ax._anchorAxis;
-
- // clear axis line positions, to be set in the subplot loop below
- ax._linepositions = {};
-
- // stash crispRounded linewidth so we don't need to pass gd all over the place
- ax._lw = Drawing.crispRound(gd, ax.linewidth, 1);
-
- // figure out the main axis line and main mirror line position.
- // it's easier to follow the logic if we handle these separately from
- // ax._linepositions, which are only used by mirror=allticks
- // for non-main-subplot ticks, and mirror=all(ticks)? for zero line
- // hiding logic
- ax._mainLinePosition = getLinePosition(ax, counterAx, ax.side);
- ax._mainMirrorPosition = (ax.mirror && counterAx) ?
- getLinePosition(ax, counterAx,
- alignmentConstants.OPPOSITE_SIDE[ax.side]) : null;
- }
-
- // figure out which backgrounds we need to draw,
- // and in which layers to put them
- var lowerBackgroundIDs = [];
- var backgroundIds = [];
- var lowerDomains = [];
- // no need to draw background when paper and plot color are the same color,
- // activate mode just for large splom (which benefit the most from this
- // optimization), but this could apply to all cartesian subplots.
- var noNeedForBg = (
- Color.opacity(fullLayout.paper_bgcolor) === 1 &&
- Color.opacity(fullLayout.plot_bgcolor) === 1 &&
- fullLayout.paper_bgcolor === fullLayout.plot_bgcolor
- );
-
- for(subplot in fullLayout._plots) {
- plotinfo = fullLayout._plots[subplot];
-
- if(plotinfo.mainplot) {
- // mainplot is a reference to the main plot this one is overlaid on
- // so if it exists, this is an overlaid plot and we don't need to
- // give it its own background
- if(plotinfo.bg) {
- plotinfo.bg.remove();
- }
- plotinfo.bg = undefined;
- } else {
- var xDomain = plotinfo.xaxis.domain;
- var yDomain = plotinfo.yaxis.domain;
- var plotgroup = plotinfo.plotgroup;
-
- if(overlappingDomain(xDomain, yDomain, lowerDomains)) {
- var pgNode = plotgroup.node();
- var plotgroupBg = plotinfo.bg = Lib.ensureSingle(plotgroup, 'rect', 'bg');
- pgNode.insertBefore(plotgroupBg.node(), pgNode.childNodes[0]);
- backgroundIds.push(subplot);
- } else {
- plotgroup.select('rect.bg').remove();
- lowerDomains.push([xDomain, yDomain]);
- if(!noNeedForBg) {
- lowerBackgroundIDs.push(subplot);
- backgroundIds.push(subplot);
- }
- }
- }
- }
-
- // now create all the lower-layer backgrounds at once now that
- // we have the list of subplots that need them
- var lowerBackgrounds = fullLayout._bgLayer.selectAll('.bg')
- .data(lowerBackgroundIDs);
-
- lowerBackgrounds.enter().append('rect')
- .classed('bg', true);
-
- lowerBackgrounds.exit().remove();
-
- lowerBackgrounds.each(function(subplot) {
- fullLayout._plots[subplot].bg = d3.select(this);
- });
-
- // style all backgrounds
- for(i = 0; i < backgroundIds.length; i++) {
- plotinfo = fullLayout._plots[backgroundIds[i]];
- xa = plotinfo.xaxis;
- ya = plotinfo.yaxis;
-
- if(plotinfo.bg) {
- plotinfo.bg
- .call(Drawing.setRect,
- xa._offset - pad, ya._offset - pad,
- xa._length + 2 * pad, ya._length + 2 * pad)
- .call(Color.fill, fullLayout.plot_bgcolor)
- .style('stroke-width', 0);
- }
- }
-
- if(!fullLayout._hasOnlyLargeSploms) {
- for(subplot in fullLayout._plots) {
- plotinfo = fullLayout._plots[subplot];
- xa = plotinfo.xaxis;
- ya = plotinfo.yaxis;
-
- // Clip so that data only shows up on the plot area.
- var clipId = plotinfo.clipId = 'clip' + fullLayout._uid + subplot + 'plot';
-
- var plotClip = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipId, function(s) {
- s.classed('plotclip', true)
- .append('rect');
- });
-
- plotinfo.clipRect = plotClip.select('rect').attr({
- width: xa._length,
- height: ya._length
- });
-
- Drawing.setTranslate(plotinfo.plot, xa._offset, ya._offset);
-
- var plotClipId;
- var layerClipId;
-
- if(plotinfo._hasClipOnAxisFalse) {
- plotClipId = null;
- layerClipId = clipId;
- } else {
- plotClipId = clipId;
- layerClipId = null;
- }
-
- Drawing.setClipUrl(plotinfo.plot, plotClipId, gd);
-
- // stash layer clipId value (null or same as clipId)
- // to DRY up Drawing.setClipUrl calls on trace-module and trace layers
- // downstream
- plotinfo.layerClipId = layerClipId;
- }
- }
-
- var xLinesXLeft, xLinesXRight, xLinesYBottom, xLinesYTop,
- leftYLineWidth, rightYLineWidth;
- var yLinesYBottom, yLinesYTop, yLinesXLeft, yLinesXRight,
- connectYBottom, connectYTop;
- var extraSubplot;
-
- function xLinePath(y) {
- return 'M' + xLinesXLeft + ',' + y + 'H' + xLinesXRight;
- }
-
- function xLinePathFree(y) {
- return 'M' + xa._offset + ',' + y + 'h' + xa._length;
- }
-
- function yLinePath(x) {
- return 'M' + x + ',' + yLinesYTop + 'V' + yLinesYBottom;
- }
-
- function yLinePathFree(x) {
- return 'M' + x + ',' + ya._offset + 'v' + ya._length;
- }
-
- function mainPath(ax, pathFn, pathFnFree) {
- if(!ax.showline || subplot !== ax._mainSubplot) return '';
- if(!ax._anchorAxis) return pathFnFree(ax._mainLinePosition);
- var out = pathFn(ax._mainLinePosition);
- if(ax.mirror) out += pathFn(ax._mainMirrorPosition);
- return out;
- }
-
- for(subplot in fullLayout._plots) {
- plotinfo = fullLayout._plots[subplot];
- xa = plotinfo.xaxis;
- ya = plotinfo.yaxis;
-
- /*
- * x lines get longer where they meet y lines, to make a crisp corner.
- * The x lines get the padding (margin.pad) plus the y line width to
- * fill up the corner nicely. Free x lines are excluded - they always
- * span exactly the data area of the plot
- *
- * | XXXXX
- * | XXXXX
- * |
- * +------
- * x1
- * -----
- * x2
- */
- var xPath = 'M0,0';
- if(shouldShowLinesOrTicks(xa, subplot)) {
- leftYLineWidth = findCounterAxisLineWidth(xa, 'left', ya, axList);
- xLinesXLeft = xa._offset - (leftYLineWidth ? (pad + leftYLineWidth) : 0);
- rightYLineWidth = findCounterAxisLineWidth(xa, 'right', ya, axList);
- xLinesXRight = xa._offset + xa._length + (rightYLineWidth ? (pad + rightYLineWidth) : 0);
- xLinesYBottom = getLinePosition(xa, ya, 'bottom');
- xLinesYTop = getLinePosition(xa, ya, 'top');
-
- // save axis line positions for extra ticks to reference
- // each subplot that gets ticks from "allticks" gets an entry:
- // [left or bottom, right or top]
- extraSubplot = (!xa._anchorAxis || subplot !== xa._mainSubplot);
- if(extraSubplot && (xa.mirror === 'allticks' || xa.mirror === 'all')) {
- xa._linepositions[subplot] = [xLinesYBottom, xLinesYTop];
- }
-
- xPath = mainPath(xa, xLinePath, xLinePathFree);
- if(extraSubplot && xa.showline && (xa.mirror === 'all' || xa.mirror === 'allticks')) {
- xPath += xLinePath(xLinesYBottom) + xLinePath(xLinesYTop);
- }
-
- plotinfo.xlines
- .style('stroke-width', xa._lw + 'px')
- .call(Color.stroke, xa.showline ?
- xa.linecolor : 'rgba(0,0,0,0)');
- }
- plotinfo.xlines.attr('d', xPath);
-
- /*
- * y lines that meet x axes get longer only by margin.pad, because
- * the x axes fill in the corner space. Free y axes, like free x axes,
- * always span exactly the data area of the plot
- *
- * | | XXXX
- * y2| y1| XXXX
- * | | XXXX
- * |
- * +-----
- */
- var yPath = 'M0,0';
- if(shouldShowLinesOrTicks(ya, subplot)) {
- connectYBottom = findCounterAxisLineWidth(ya, 'bottom', xa, axList);
- yLinesYBottom = ya._offset + ya._length + (connectYBottom ? pad : 0);
- connectYTop = findCounterAxisLineWidth(ya, 'top', xa, axList);
- yLinesYTop = ya._offset - (connectYTop ? pad : 0);
- yLinesXLeft = getLinePosition(ya, xa, 'left');
- yLinesXRight = getLinePosition(ya, xa, 'right');
-
- extraSubplot = (!ya._anchorAxis || subplot !== ya._mainSubplot);
- if(extraSubplot && (ya.mirror === 'allticks' || ya.mirror === 'all')) {
- ya._linepositions[subplot] = [yLinesXLeft, yLinesXRight];
- }
-
- yPath = mainPath(ya, yLinePath, yLinePathFree);
- if(extraSubplot && ya.showline && (ya.mirror === 'all' || ya.mirror === 'allticks')) {
- yPath += yLinePath(yLinesXLeft) + yLinePath(yLinesXRight);
- }
-
- plotinfo.ylines
- .style('stroke-width', ya._lw + 'px')
- .call(Color.stroke, ya.showline ?
- ya.linecolor : 'rgba(0,0,0,0)');
- }
- plotinfo.ylines.attr('d', yPath);
- }
-
- Axes.makeClipPaths(gd);
-
- return gd._promises.length && Promise.all(gd._promises);
- }
-
- function shouldShowLinesOrTicks(ax, subplot) {
- return (ax.ticks || ax.showline) &&
- (subplot === ax._mainSubplot || ax.mirror === 'all' || ax.mirror === 'allticks');
- }
-
- /*
- * should we draw a line on counterAx at this side of ax?
- * It's assumed that counterAx is known to overlay the subplot we're working on
- * but it may not be its main axis.
- */
- function shouldShowLineThisSide(ax, side, counterAx) {
- // does counterAx get a line at all?
- if(!counterAx.showline || !counterAx._lw) return false;
-
- // are we drawing *all* lines for counterAx?
- if(counterAx.mirror === 'all' || counterAx.mirror === 'allticks') return true;
-
- var anchorAx = counterAx._anchorAxis;
-
- // is this a free axis? free axes can only have a subplot side-line with all(ticks)? mirroring
- if(!anchorAx) return false;
-
- // in order to handle cases where the user forgot to anchor this axis correctly
- // (because its default anchor has the same domain on the relevant end)
- // check whether the relevant position is the same.
- var sideIndex = alignmentConstants.FROM_BL[side];
- if(counterAx.side === side) {
- return anchorAx.domain[sideIndex] === ax.domain[sideIndex];
- }
- return counterAx.mirror && anchorAx.domain[1 - sideIndex] === ax.domain[1 - sideIndex];
- }
-
- /*
- * Is there another axis intersecting `side` end of `ax`?
- * First look at `counterAx` (the axis for this subplot),
- * then at all other potential counteraxes on or overlaying this subplot.
- * Take the line width from the first one that has a line.
- */
- function findCounterAxisLineWidth(ax, side, counterAx, axList) {
- if(shouldShowLineThisSide(ax, side, counterAx)) {
- return counterAx._lw;
- }
- for(var i = 0; i < axList.length; i++) {
- var axi = axList[i];
- if(axi._mainAxis === counterAx._mainAxis && shouldShowLineThisSide(ax, side, axi)) {
- return axi._lw;
- }
- }
- return 0;
- }
-
- exports.drawMainTitle = function(gd) {
- var fullLayout = gd._fullLayout;
-
- var textAnchor = getMainTitleTextAnchor(fullLayout);
- var dy = getMainTitleDy(fullLayout);
-
- Titles.draw(gd, 'gtitle', {
- propContainer: fullLayout,
- propName: 'title.text',
- placeholder: fullLayout._dfltTitle.plot,
- attributes: {
- x: getMainTitleX(fullLayout, textAnchor),
- y: getMainTitleY(fullLayout, dy),
- 'text-anchor': textAnchor,
- dy: dy
- }
- });
- };
-
- function getMainTitleX(fullLayout, textAnchor) {
- var title = fullLayout.title;
- var gs = fullLayout._size;
- var hPadShift = 0;
-
- if(textAnchor === SVG_TEXT_ANCHOR_START) {
- hPadShift = title.pad.l;
- } else if(textAnchor === SVG_TEXT_ANCHOR_END) {
- hPadShift = -title.pad.r;
- }
-
- switch(title.xref) {
- case 'paper':
- return gs.l + gs.w * title.x + hPadShift;
- case 'container':
- default:
- return fullLayout.width * title.x + hPadShift;
- }
- }
-
- function getMainTitleY(fullLayout, dy) {
- var title = fullLayout.title;
- var gs = fullLayout._size;
- var vPadShift = 0;
-
- if(dy === '0em' || !dy) {
- vPadShift = -title.pad.b;
- } else if(dy === alignmentConstants.CAP_SHIFT + 'em') {
- vPadShift = title.pad.t;
- }
-
- if(title.y === 'auto') {
- return gs.t / 2;
- } else {
- switch(title.yref) {
- case 'paper':
- return gs.t + gs.h - gs.h * title.y + vPadShift;
- case 'container':
- default:
- return fullLayout.height - fullLayout.height * title.y + vPadShift;
- }
- }
- }
-
- function getMainTitleTextAnchor(fullLayout) {
- var title = fullLayout.title;
-
- var textAnchor = SVG_TEXT_ANCHOR_MIDDLE;
- if(Lib.isRightAnchor(title)) {
- textAnchor = SVG_TEXT_ANCHOR_END;
- } else if(Lib.isLeftAnchor(title)) {
- textAnchor = SVG_TEXT_ANCHOR_START;
- }
-
- return textAnchor;
- }
-
- function getMainTitleDy(fullLayout) {
- var title = fullLayout.title;
-
- var dy = '0em';
- if(Lib.isTopAnchor(title)) {
- dy = alignmentConstants.CAP_SHIFT + 'em';
- } else if(Lib.isMiddleAnchor(title)) {
- dy = alignmentConstants.MID_SHIFT + 'em';
- }
-
- return dy;
- }
-
- exports.doTraceStyle = function(gd) {
- var calcdata = gd.calcdata;
- var editStyleCalls = [];
- var i;
-
- for(i = 0; i < calcdata.length; i++) {
- var cd = calcdata[i];
- var cd0 = cd[0] || {};
- var trace = cd0.trace || {};
- var _module = trace._module || {};
-
- // See if we need to do arraysToCalcdata
- // call it regardless of what change we made, in case
- // supplyDefaults brought in an array that was already
- // in gd.data but not in gd._fullData previously
- var arraysToCalcdata = _module.arraysToCalcdata;
- if(arraysToCalcdata) arraysToCalcdata(cd, trace);
-
- var editStyle = _module.editStyle;
- if(editStyle) editStyleCalls.push({fn: editStyle, cd0: cd0});
- }
-
- if(editStyleCalls.length) {
- for(i = 0; i < editStyleCalls.length; i++) {
- var edit = editStyleCalls[i];
- edit.fn(gd, edit.cd0);
- }
- clearGlCanvases(gd);
- exports.redrawReglTraces(gd);
- }
-
- Plots.style(gd);
- Registry.getComponentMethod('legend', 'draw')(gd);
-
- return Plots.previousPromises(gd);
- };
-
- exports.doColorBars = function(gd) {
- for(var i = 0; i < gd.calcdata.length; i++) {
- var cdi0 = gd.calcdata[i][0];
-
- if((cdi0.t || {}).cb) {
- var trace = cdi0.trace;
- var cb = cdi0.t.cb;
-
- if(Registry.traceIs(trace, 'contour')) {
- cb.line({
- width: trace.contours.showlines !== false ?
- trace.line.width : 0,
- dash: trace.line.dash,
- color: trace.contours.coloring === 'line' ?
- cb._opts.line.color : trace.line.color
- });
- }
- var moduleOpts = trace._module.colorbar;
- var containerName = moduleOpts.container;
- var opts = (containerName ? trace[containerName] : trace).colorbar;
- cb.options(opts)();
- }
- }
-
- return Plots.previousPromises(gd);
- };
-
- // force plot() to redo the layout and replot with the modified layout
- exports.layoutReplot = function(gd) {
- var layout = gd.layout;
- gd.layout = undefined;
- return Registry.call('plot', gd, '', layout);
- };
-
- exports.doLegend = function(gd) {
- Registry.getComponentMethod('legend', 'draw')(gd);
- return Plots.previousPromises(gd);
- };
-
- exports.doTicksRelayout = function(gd) {
- Axes.draw(gd, 'redraw');
-
- if(gd._fullLayout._hasOnlyLargeSploms) {
- Registry.subplotsRegistry.splom.updateGrid(gd);
- clearGlCanvases(gd);
- exports.redrawReglTraces(gd);
- }
-
- exports.drawMainTitle(gd);
- return Plots.previousPromises(gd);
- };
-
- exports.doModeBar = function(gd) {
- var fullLayout = gd._fullLayout;
-
- ModeBar.manage(gd);
-
- for(var i = 0; i < fullLayout._basePlotModules.length; i++) {
- var updateFx = fullLayout._basePlotModules[i].updateFx;
- if(updateFx) updateFx(gd);
- }
-
- return Plots.previousPromises(gd);
- };
-
- exports.doCamera = function(gd) {
- var fullLayout = gd._fullLayout;
- var sceneIds = fullLayout._subplots.gl3d;
-
- for(var i = 0; i < sceneIds.length; i++) {
- var sceneLayout = fullLayout[sceneIds[i]];
- var scene = sceneLayout._scene;
-
- scene.setCamera(sceneLayout.camera);
- }
- };
-
- exports.drawData = function(gd) {
- var fullLayout = gd._fullLayout;
- var calcdata = gd.calcdata;
- var i;
-
- // remove old colorbars explicitly
- for(i = 0; i < calcdata.length; i++) {
- var trace = calcdata[i][0].trace;
- if(trace.visible !== true || !trace._module.colorbar) {
- fullLayout._infolayer.select('.cb' + trace.uid).remove();
- }
- }
-
- clearGlCanvases(gd);
-
- // loop over the base plot modules present on graph
- var basePlotModules = fullLayout._basePlotModules;
- for(i = 0; i < basePlotModules.length; i++) {
- basePlotModules[i].plot(gd);
- }
-
- exports.redrawReglTraces(gd);
-
- // styling separate from drawing
- Plots.style(gd);
-
- // show annotations and shapes
- Registry.getComponentMethod('shapes', 'draw')(gd);
- Registry.getComponentMethod('annotations', 'draw')(gd);
-
- // Mark the first render as complete
- fullLayout._replotting = false;
-
- return Plots.previousPromises(gd);
- };
-
- // Draw (or redraw) all regl-based traces in one go,
- // useful during drag and selection where buffers of targeted traces are updated,
- // but all traces need to be redrawn following clearGlCanvases.
- //
- // Note that _module.plot for regl trace does NOT draw things
- // on the canvas, they only update the buffers.
- // Drawing is perform here.
- //
- // TODO try adding per-subplot option using gl.SCISSOR_TEST for
- // non-overlaying, disjoint subplots.
- //
- // TODO try to include parcoords in here.
- // https://github.com/plotly/plotly.js/issues/3069
- exports.redrawReglTraces = function(gd) {
- var fullLayout = gd._fullLayout;
-
- if(fullLayout._has('regl')) {
- var fullData = gd._fullData;
- var cartesianIds = [];
- var polarIds = [];
- var i, sp;
-
- if(fullLayout._hasOnlyLargeSploms) {
- fullLayout._splomGrid.draw();
- }
-
- // N.B.
- // - Loop over fullData (not _splomScenes) to preserve splom trace-to-trace ordering
- // - Fill list if subplot ids (instead of fullLayout._subplots) to handle cases where all traces
- // of a given module are `visible !== true`
- for(i = 0; i < fullData.length; i++) {
- var trace = fullData[i];
-
- if(trace.visible === true) {
- if(trace.type === 'splom') {
- fullLayout._splomScenes[trace.uid].draw();
- } else if(trace.type === 'scattergl') {
- Lib.pushUnique(cartesianIds, trace.xaxis + trace.yaxis);
- } else if(trace.type === 'scatterpolargl') {
- Lib.pushUnique(polarIds, trace.subplot);
- }
- }
- }
-
- for(i = 0; i < cartesianIds.length; i++) {
- sp = fullLayout._plots[cartesianIds[i]];
- if(sp._scene) sp._scene.draw();
- }
-
- for(i = 0; i < polarIds.length; i++) {
- sp = fullLayout[polarIds[i]]._subplot;
- if(sp._scene) sp._scene.draw();
- }
- }
- };
-
- exports.doAutoRangeAndConstraints = function(gd) {
- var axList = Axes.list(gd, '', true);
-
- for(var i = 0; i < axList.length; i++) {
- var ax = axList[i];
- cleanAxisConstraints(gd, ax);
- // in case margins changed, update scale
- ax.setScale();
- doAutoRange(gd, ax);
- }
-
- enforceAxisConstraints(gd);
- };
-
- // An initial paint must be completed before these components can be
- // correctly sized and the whole plot re-margined. fullLayout._replotting must
- // be set to false before these will work properly.
- exports.finalDraw = function(gd) {
- Registry.getComponentMethod('shapes', 'draw')(gd);
- Registry.getComponentMethod('images', 'draw')(gd);
- Registry.getComponentMethod('annotations', 'draw')(gd);
- // TODO: rangesliders really belong in marginPushers but they need to be
- // drawn after data - can we at least get the margin pushing part separated
- // out and done earlier?
- Registry.getComponentMethod('rangeslider', 'draw')(gd);
- // TODO: rangeselector only needs to be here (in addition to drawMarginPushers)
- // because the margins need to be fully determined before we can call
- // autorange and update axis ranges (which rangeselector needs to know which
- // button is active). Can we break out its automargin step from its draw step?
- Registry.getComponentMethod('rangeselector', 'draw')(gd);
- };
-
- exports.drawMarginPushers = function(gd) {
- Registry.getComponentMethod('legend', 'draw')(gd);
- Registry.getComponentMethod('rangeselector', 'draw')(gd);
- Registry.getComponentMethod('sliders', 'draw')(gd);
- Registry.getComponentMethod('updatemenus', 'draw')(gd);
- };
-
- },{"../components/color":51,"../components/drawing":72,"../components/modebar":110,"../components/titles":139,"../constants/alignment":146,"../lib":168,"../lib/clear_gl_canvases":157,"../plots/cartesian/autorange":211,"../plots/cartesian/axes":212,"../plots/cartesian/constraints":220,"../plots/plots":245,"../registry":257,"d3":16}],204:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../lib');
- var isPlainObject = Lib.isPlainObject;
- var PlotSchema = _dereq_('./plot_schema');
- var Plots = _dereq_('../plots/plots');
- var plotAttributes = _dereq_('../plots/attributes');
- var Template = _dereq_('./plot_template');
- var dfltConfig = _dereq_('./plot_config').dfltConfig;
-
- /**
- * Plotly.makeTemplate: create a template off an existing figure to reuse
- * style attributes on other figures.
- *
- * Note: separated from the rest of templates because otherwise we get circular
- * references due to PlotSchema.
- *
- * @param {object|DOM element|string} figure: The figure to base the template on
- * should contain a trace array `figure.data`
- * and a layout object `figure.layout`
- * @returns {object} template: the extracted template - can then be used as
- * `layout.template` in another figure.
- */
- exports.makeTemplate = function(figure) {
- figure = Lib.isPlainObject(figure) ? figure : Lib.getGraphDiv(figure);
- figure = Lib.extendDeep({_context: dfltConfig}, {data: figure.data, layout: figure.layout});
- Plots.supplyDefaults(figure);
- var data = figure.data || [];
- var layout = figure.layout || {};
- // copy over a few items to help follow the schema
- layout._basePlotModules = figure._fullLayout._basePlotModules;
- layout._modules = figure._fullLayout._modules;
-
- var template = {
- data: {},
- layout: {}
- };
-
- /*
- * Note: we do NOT validate template values, we just take what's in the
- * user inputs data and layout, not the validated values in fullData and
- * fullLayout. Even if we were to validate here, there's no guarantee that
- * these values would still be valid when applied to a new figure, which
- * may contain different trace modes, different axes, etc. So it's
- * important that when applying a template we still validate the template
- * values, rather than just using them as defaults.
- */
-
- data.forEach(function(trace) {
- // TODO: What if no style info is extracted for this trace. We may
- // not want an empty object as the null value.
- // TODO: allow transforms to contribute to templates?
- // as it stands they are ignored, which may be for the best...
-
- var traceTemplate = {};
- walkStyleKeys(trace, traceTemplate, getTraceInfo.bind(null, trace));
-
- var traceType = Lib.coerce(trace, {}, plotAttributes, 'type');
- var typeTemplates = template.data[traceType];
- if(!typeTemplates) typeTemplates = template.data[traceType] = [];
- typeTemplates.push(traceTemplate);
- });
-
- walkStyleKeys(layout, template.layout, getLayoutInfo.bind(null, layout));
-
- /*
- * Compose the new template with an existing one to the same effect
- *
- * NOTE: there's a possibility of slightly different behavior: if the plot
- * has an invalid value and the old template has a valid value for the same
- * attribute, the plot will use the old template value but this routine
- * will pull the invalid value (resulting in the original default).
- * In the general case it's not possible to solve this with a single value,
- * since valid options can be context-dependent. It could be solved with
- * a *list* of values, but that would be huge complexity for little gain.
- */
- delete template.layout.template;
- var oldTemplate = layout.template;
- if(isPlainObject(oldTemplate)) {
- var oldLayoutTemplate = oldTemplate.layout;
-
- var i, traceType, oldTypeTemplates, oldTypeLen, typeTemplates, typeLen;
-
- if(isPlainObject(oldLayoutTemplate)) {
- mergeTemplates(oldLayoutTemplate, template.layout);
- }
- var oldDataTemplate = oldTemplate.data;
- if(isPlainObject(oldDataTemplate)) {
- for(traceType in template.data) {
- oldTypeTemplates = oldDataTemplate[traceType];
- if(Array.isArray(oldTypeTemplates)) {
- typeTemplates = template.data[traceType];
- typeLen = typeTemplates.length;
- oldTypeLen = oldTypeTemplates.length;
- for(i = 0; i < typeLen; i++) {
- mergeTemplates(oldTypeTemplates[i % oldTypeLen], typeTemplates[i]);
- }
- for(i = typeLen; i < oldTypeLen; i++) {
- typeTemplates.push(Lib.extendDeep({}, oldTypeTemplates[i]));
- }
- }
- }
- for(traceType in oldDataTemplate) {
- if(!(traceType in template.data)) {
- template.data[traceType] = Lib.extendDeep([], oldDataTemplate[traceType]);
- }
- }
- }
- }
-
- return template;
- };
-
- function mergeTemplates(oldTemplate, newTemplate) {
- // we don't care about speed here, just make sure we have a totally
- // distinct object from the previous template
- oldTemplate = Lib.extendDeep({}, oldTemplate);
-
- // sort keys so we always get annotationdefaults before annotations etc
- // so arrayTemplater will work right
- var oldKeys = Object.keys(oldTemplate).sort();
- var i, j;
-
- function mergeOne(oldVal, newVal, key) {
- if(isPlainObject(newVal) && isPlainObject(oldVal)) {
- mergeTemplates(oldVal, newVal);
- }
- else if(Array.isArray(newVal) && Array.isArray(oldVal)) {
- // Note: omitted `inclusionAttr` from arrayTemplater here,
- // it's irrelevant as we only want the resulting `_template`.
- var templater = Template.arrayTemplater({_template: oldTemplate}, key);
- for(j = 0; j < newVal.length; j++) {
- var item = newVal[j];
- var oldItem = templater.newItem(item)._template;
- if(oldItem) mergeTemplates(oldItem, item);
- }
- var defaultItems = templater.defaultItems();
- for(j = 0; j < defaultItems.length; j++) newVal.push(defaultItems[j]._template);
-
- // templateitemname only applies to receiving plots
- for(j = 0; j < newVal.length; j++) delete newVal[j].templateitemname;
- }
- }
-
- for(i = 0; i < oldKeys.length; i++) {
- var key = oldKeys[i];
- var oldVal = oldTemplate[key];
- if(key in newTemplate) {
- mergeOne(oldVal, newTemplate[key], key);
- }
- else newTemplate[key] = oldVal;
-
- // if this is a base key from the old template (eg xaxis), look for
- // extended keys (eg xaxis2) in the new template to merge into
- if(getBaseKey(key) === key) {
- for(var key2 in newTemplate) {
- var baseKey2 = getBaseKey(key2);
- if(key2 !== baseKey2 && baseKey2 === key && !(key2 in oldTemplate)) {
- mergeOne(oldVal, newTemplate[key2], key);
- }
- }
- }
- }
- }
-
- function getBaseKey(key) {
- return key.replace(/[0-9]+$/, '');
- }
-
- function walkStyleKeys(parent, templateOut, getAttributeInfo, path, basePath) {
- var pathAttr = basePath && getAttributeInfo(basePath);
- for(var key in parent) {
- var child = parent[key];
- var nextPath = getNextPath(parent, key, path);
- var nextBasePath = getNextPath(parent, key, basePath);
- var attr = getAttributeInfo(nextBasePath);
- if(!attr) {
- var baseKey = getBaseKey(key);
- if(baseKey !== key) {
- nextBasePath = getNextPath(parent, baseKey, basePath);
- attr = getAttributeInfo(nextBasePath);
- }
- }
-
- // we'll get an attr if path starts with a valid part, then has an
- // invalid ending. Make sure we got all the way to the end.
- if(pathAttr && (pathAttr === attr)) continue;
-
- if(!attr || attr._noTemplating ||
- attr.valType === 'data_array' ||
- (attr.arrayOk && Array.isArray(child))
- ) {
- continue;
- }
-
- if(!attr.valType && isPlainObject(child)) {
- walkStyleKeys(child, templateOut, getAttributeInfo, nextPath, nextBasePath);
- }
- else if(attr._isLinkedToArray && Array.isArray(child)) {
- var dfltDone = false;
- var namedIndex = 0;
- var usedNames = {};
- for(var i = 0; i < child.length; i++) {
- var item = child[i];
- if(isPlainObject(item)) {
- var name = item.name;
- if(name) {
- if(!usedNames[name]) {
- // named array items: allow all attributes except data arrays
- walkStyleKeys(item, templateOut, getAttributeInfo,
- getNextPath(child, namedIndex, nextPath),
- getNextPath(child, namedIndex, nextBasePath));
- namedIndex++;
- usedNames[name] = 1;
- }
- }
- else if(!dfltDone) {
- var dfltKey = Template.arrayDefaultKey(key);
- var dfltPath = getNextPath(parent, dfltKey, path);
-
- // getAttributeInfo will fail if we try to use dfltKey directly.
- // Instead put this item into the next array element, then
- // pull it out and move it to dfltKey.
- var pathInArray = getNextPath(child, namedIndex, nextPath);
- walkStyleKeys(item, templateOut, getAttributeInfo, pathInArray,
- getNextPath(child, namedIndex, nextBasePath));
- var itemPropInArray = Lib.nestedProperty(templateOut, pathInArray);
- var dfltProp = Lib.nestedProperty(templateOut, dfltPath);
- dfltProp.set(itemPropInArray.get());
- itemPropInArray.set(null);
-
- dfltDone = true;
- }
- }
- }
- }
- else {
- var templateProp = Lib.nestedProperty(templateOut, nextPath);
- templateProp.set(child);
- }
- }
- }
-
- function getLayoutInfo(layout, path) {
- return PlotSchema.getLayoutValObject(
- layout, Lib.nestedProperty({}, path).parts
- );
- }
-
- function getTraceInfo(trace, path) {
- return PlotSchema.getTraceValObject(
- trace, Lib.nestedProperty({}, path).parts
- );
- }
-
- function getNextPath(parent, key, path) {
- var nextPath;
- if(!path) nextPath = key;
- else if(Array.isArray(parent)) nextPath = path + '[' + key + ']';
- else nextPath = path + '.' + key;
-
- return nextPath;
- }
-
- /**
- * validateTemplate: Test for consistency between the given figure and
- * a template, either already included in the figure or given separately.
- * Note that not every issue we identify here is necessarily a problem,
- * it depends on what you're using the template for.
- *
- * @param {object|DOM element} figure: the plot, with {data, layout} members,
- * to test the template against
- * @param {Optional(object)} template: the template, with its own {data, layout},
- * to test. If omitted, we will look for a template already attached as the
- * plot's `layout.template` attribute.
- *
- * @returns {array} array of error objects each containing:
- * - {string} code
- * error code ('missing', 'unused', 'reused', 'noLayout', 'noData')
- * - {string} msg
- * a full readable description of the issue.
- */
- exports.validateTemplate = function(figureIn, template) {
- var figure = Lib.extendDeep({}, {
- _context: dfltConfig,
- data: figureIn.data,
- layout: figureIn.layout
- });
- var layout = figure.layout || {};
- if(!isPlainObject(template)) template = layout.template || {};
- var layoutTemplate = template.layout;
- var dataTemplate = template.data;
- var errorList = [];
-
- figure.layout = layout;
- figure.layout.template = template;
- Plots.supplyDefaults(figure);
-
- var fullLayout = figure._fullLayout;
- var fullData = figure._fullData;
-
- var layoutPaths = {};
- function crawlLayoutForContainers(obj, paths) {
- for(var key in obj) {
- if(key.charAt(0) !== '_' && isPlainObject(obj[key])) {
- var baseKey = getBaseKey(key);
- var nextPaths = [];
- var i;
- for(i = 0; i < paths.length; i++) {
- nextPaths.push(getNextPath(obj, key, paths[i]));
- if(baseKey !== key) nextPaths.push(getNextPath(obj, baseKey, paths[i]));
- }
- for(i = 0; i < nextPaths.length; i++) {
- layoutPaths[nextPaths[i]] = 1;
- }
- crawlLayoutForContainers(obj[key], nextPaths);
- }
- }
- }
-
- function crawlLayoutTemplateForContainers(obj, path) {
- for(var key in obj) {
- if(key.indexOf('defaults') === -1 && isPlainObject(obj[key])) {
- var nextPath = getNextPath(obj, key, path);
- if(layoutPaths[nextPath]) {
- crawlLayoutTemplateForContainers(obj[key], nextPath);
- }
- else {
- errorList.push({code: 'unused', path: nextPath});
- }
- }
- }
- }
-
- if(!isPlainObject(layoutTemplate)) {
- errorList.push({code: 'layout'});
- }
- else {
- crawlLayoutForContainers(fullLayout, ['layout']);
- crawlLayoutTemplateForContainers(layoutTemplate, 'layout');
- }
-
- if(!isPlainObject(dataTemplate)) {
- errorList.push({code: 'data'});
- }
- else {
- var typeCount = {};
- var traceType;
- for(var i = 0; i < fullData.length; i++) {
- var fullTrace = fullData[i];
- traceType = fullTrace.type;
- typeCount[traceType] = (typeCount[traceType] || 0) + 1;
- if(!fullTrace._fullInput._template) {
- // this takes care of the case of traceType in the data but not
- // the template
- errorList.push({
- code: 'missing',
- index: fullTrace._fullInput.index,
- traceType: traceType
- });
- }
- }
- for(traceType in dataTemplate) {
- var templateCount = dataTemplate[traceType].length;
- var dataCount = typeCount[traceType] || 0;
- if(templateCount > dataCount) {
- errorList.push({
- code: 'unused',
- traceType: traceType,
- templateCount: templateCount,
- dataCount: dataCount
- });
- }
- else if(dataCount > templateCount) {
- errorList.push({
- code: 'reused',
- traceType: traceType,
- templateCount: templateCount,
- dataCount: dataCount
- });
- }
- }
- }
-
- // _template: false is when someone tried to modify an array item
- // but there was no template with matching name
- function crawlForMissingTemplates(obj, path) {
- for(var key in obj) {
- if(key.charAt(0) === '_') continue;
- var val = obj[key];
- var nextPath = getNextPath(obj, key, path);
- if(isPlainObject(val)) {
- if(Array.isArray(obj) && val._template === false && val.templateitemname) {
- errorList.push({
- code: 'missing',
- path: nextPath,
- templateitemname: val.templateitemname
- });
- }
- crawlForMissingTemplates(val, nextPath);
- }
- else if(Array.isArray(val) && hasPlainObject(val)) {
- crawlForMissingTemplates(val, nextPath);
- }
- }
- }
- crawlForMissingTemplates({data: fullData, layout: fullLayout}, '');
-
- if(errorList.length) return errorList.map(format);
- };
-
- function hasPlainObject(arr) {
- for(var i = 0; i < arr.length; i++) {
- if(isPlainObject(arr[i])) return true;
- }
- }
-
- function format(opts) {
- var msg;
- switch(opts.code) {
- case 'data':
- msg = 'The template has no key data.';
- break;
- case 'layout':
- msg = 'The template has no key layout.';
- break;
- case 'missing':
- if(opts.path) {
- msg = 'There are no templates for item ' + opts.path +
- ' with name ' + opts.templateitemname;
- }
- else {
- msg = 'There are no templates for trace ' + opts.index +
- ', of type ' + opts.traceType + '.';
- }
- break;
- case 'unused':
- if(opts.path) {
- msg = 'The template item at ' + opts.path +
- ' was not used in constructing the plot.';
- }
- else if(opts.dataCount) {
- msg = 'Some of the templates of type ' + opts.traceType +
- ' were not used. The template has ' + opts.templateCount +
- ' traces, the data only has ' + opts.dataCount +
- ' of this type.';
- }
- else {
- msg = 'The template has ' + opts.templateCount +
- ' traces of type ' + opts.traceType +
- ' but there are none in the data.';
- }
- break;
- case 'reused':
- msg = 'Some of the templates of type ' + opts.traceType +
- ' were used more than once. The template has ' +
- opts.templateCount + ' traces, the data has ' +
- opts.dataCount + ' of this type.';
- break;
- }
- opts.msg = msg;
-
- return opts;
- }
-
- },{"../lib":168,"../plots/attributes":209,"../plots/plots":245,"./plot_config":200,"./plot_schema":201,"./plot_template":202}],205:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var plotApi = _dereq_('./plot_api');
- var Lib = _dereq_('../lib');
-
- var helpers = _dereq_('../snapshot/helpers');
- var toSVG = _dereq_('../snapshot/tosvg');
- var svgToImg = _dereq_('../snapshot/svgtoimg');
-
- var attrs = {
- format: {
- valType: 'enumerated',
- values: ['png', 'jpeg', 'webp', 'svg'],
- dflt: 'png',
-
- },
- width: {
- valType: 'number',
- min: 1,
-
- },
- height: {
- valType: 'number',
- min: 1,
-
- },
- scale: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
- },
- setBackground: {
- valType: 'any',
- dflt: false,
-
- },
- imageDataOnly: {
- valType: 'boolean',
- dflt: false,
-
- }
- };
-
- var IMAGE_URL_PREFIX = /^data:image\/\w+;base64,/;
-
- /** Plotly.toImage
- *
- * @param {object | string | HTML div} gd
- * can either be a data/layout/config object
- * or an existing graph <div>
- * or an id to an existing graph <div>
- * @param {object} opts (see above)
- * @return {promise}
- */
- function toImage(gd, opts) {
- opts = opts || {};
-
- var data;
- var layout;
- var config;
-
- if(Lib.isPlainObject(gd)) {
- data = gd.data || [];
- layout = gd.layout || {};
- config = gd.config || {};
- } else {
- gd = Lib.getGraphDiv(gd);
- data = Lib.extendDeep([], gd.data);
- layout = Lib.extendDeep({}, gd.layout);
- config = gd._context;
- }
-
- function isImpliedOrValid(attr) {
- return !(attr in opts) || Lib.validate(opts[attr], attrs[attr]);
- }
-
- if(!isImpliedOrValid('width') || !isImpliedOrValid('height')) {
- throw new Error('Height and width should be pixel values.');
- }
-
- if(!isImpliedOrValid('format')) {
- throw new Error('Image format is not jpeg, png, svg or webp.');
- }
-
- var fullOpts = {};
-
- function coerce(attr, dflt) {
- return Lib.coerce(opts, fullOpts, attrs, attr, dflt);
- }
-
- var format = coerce('format');
- var width = coerce('width');
- var height = coerce('height');
- var scale = coerce('scale');
- var setBackground = coerce('setBackground');
- var imageDataOnly = coerce('imageDataOnly');
-
- // put the cloned div somewhere off screen before attaching to DOM
- var clonedGd = document.createElement('div');
- clonedGd.style.position = 'absolute';
- clonedGd.style.left = '-5000px';
- document.body.appendChild(clonedGd);
-
- // extend layout with image options
- var layoutImage = Lib.extendFlat({}, layout);
- if(width) layoutImage.width = width;
- if(height) layoutImage.height = height;
-
- // extend config for static plot
- var configImage = Lib.extendFlat({}, config, {
- _exportedPlot: true,
- staticPlot: true,
- setBackground: setBackground
- });
-
- var redrawFunc = helpers.getRedrawFunc(clonedGd);
-
- function wait() {
- return new Promise(function(resolve) {
- setTimeout(resolve, helpers.getDelay(clonedGd._fullLayout));
- });
- }
-
- function convert() {
- return new Promise(function(resolve, reject) {
- var svg = toSVG(clonedGd, format, scale);
- var width = clonedGd._fullLayout.width;
- var height = clonedGd._fullLayout.height;
-
- plotApi.purge(clonedGd);
- document.body.removeChild(clonedGd);
-
- if(format === 'svg') {
- if(imageDataOnly) {
- return resolve(svg);
- } else {
- return resolve('data:image/svg+xml,' + encodeURIComponent(svg));
- }
- }
-
- var canvas = document.createElement('canvas');
- canvas.id = Lib.randstr();
-
- svgToImg({
- format: format,
- width: width,
- height: height,
- scale: scale,
- canvas: canvas,
- svg: svg,
- // ask svgToImg to return a Promise
- // rather than EventEmitter
- // leave EventEmitter for backward
- // compatibility
- promise: true
- })
- .then(resolve)
- .catch(reject);
- });
- }
-
- function urlToImageData(url) {
- if(imageDataOnly) {
- return url.replace(IMAGE_URL_PREFIX, '');
- } else {
- return url;
- }
- }
-
- return new Promise(function(resolve, reject) {
- plotApi.plot(clonedGd, data, layoutImage, configImage)
- .then(redrawFunc)
- .then(wait)
- .then(convert)
- .then(function(url) { resolve(urlToImageData(url)); })
- .catch(function(err) { reject(err); });
- });
- }
-
- module.exports = toImage;
-
- },{"../lib":168,"../snapshot/helpers":261,"../snapshot/svgtoimg":263,"../snapshot/tosvg":265,"./plot_api":199}],206:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../lib');
- var Plots = _dereq_('../plots/plots');
- var PlotSchema = _dereq_('./plot_schema');
- var dfltConfig = _dereq_('./plot_config').dfltConfig;
-
- var isPlainObject = Lib.isPlainObject;
- var isArray = Array.isArray;
- var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
-
- /**
- * Validate a data array and layout object.
- *
- * @param {array} data
- * @param {object} layout
- *
- * @return {array} array of error objects each containing:
- * - {string} code
- * error code ('object', 'array', 'schema', 'unused', 'invisible' or 'value')
- * - {string} container
- * container where the error occurs ('data' or 'layout')
- * - {number} trace
- * trace index of the 'data' container where the error occurs
- * - {array} path
- * nested path to the key that causes the error
- * - {string} astr
- * attribute string variant of 'path' compatible with Plotly.restyle and
- * Plotly.relayout.
- * - {string} msg
- * error message (shown in console in logger config argument is enable)
- */
- module.exports = function validate(data, layout) {
- var schema = PlotSchema.get();
- var errorList = [];
- var gd = {_context: Lib.extendFlat({}, dfltConfig)};
-
- var dataIn, layoutIn;
-
- if(isArray(data)) {
- gd.data = Lib.extendDeep([], data);
- dataIn = data;
- }
- else {
- gd.data = [];
- dataIn = [];
- errorList.push(format('array', 'data'));
- }
-
- if(isPlainObject(layout)) {
- gd.layout = Lib.extendDeep({}, layout);
- layoutIn = layout;
- }
- else {
- gd.layout = {};
- layoutIn = {};
- if(arguments.length > 1) {
- errorList.push(format('object', 'layout'));
- }
- }
-
- // N.B. dataIn and layoutIn are in general not the same as
- // gd.data and gd.layout after supplyDefaults as some attributes
- // in gd.data and gd.layout (still) get mutated during this step.
-
- Plots.supplyDefaults(gd);
-
- var dataOut = gd._fullData;
- var len = dataIn.length;
-
- for(var i = 0; i < len; i++) {
- var traceIn = dataIn[i];
- var base = ['data', i];
-
- if(!isPlainObject(traceIn)) {
- errorList.push(format('object', base));
- continue;
- }
-
- var traceOut = dataOut[i];
- var traceType = traceOut.type;
- var traceSchema = schema.traces[traceType].attributes;
-
- // PlotSchema does something fancy with trace 'type', reset it here
- // to make the trace schema compatible with Lib.validate.
- traceSchema.type = {
- valType: 'enumerated',
- values: [traceType]
- };
-
- if(traceOut.visible === false && traceIn.visible !== false) {
- errorList.push(format('invisible', base));
- }
-
- crawl(traceIn, traceOut, traceSchema, errorList, base);
-
- var transformsIn = traceIn.transforms;
- var transformsOut = traceOut.transforms;
-
- if(transformsIn) {
- if(!isArray(transformsIn)) {
- errorList.push(format('array', base, ['transforms']));
- }
-
- base.push('transforms');
-
- for(var j = 0; j < transformsIn.length; j++) {
- var path = ['transforms', j];
- var transformType = transformsIn[j].type;
-
- if(!isPlainObject(transformsIn[j])) {
- errorList.push(format('object', base, path));
- continue;
- }
-
- var transformSchema = schema.transforms[transformType] ?
- schema.transforms[transformType].attributes :
- {};
-
- // add 'type' to transform schema to validate the transform type
- transformSchema.type = {
- valType: 'enumerated',
- values: Object.keys(schema.transforms)
- };
-
- crawl(transformsIn[j], transformsOut[j], transformSchema, errorList, base, path);
- }
- }
- }
-
- var layoutOut = gd._fullLayout;
- var layoutSchema = fillLayoutSchema(schema, dataOut);
-
- crawl(layoutIn, layoutOut, layoutSchema, errorList, 'layout');
-
- // return undefined if no validation errors were found
- return (errorList.length === 0) ? void(0) : errorList;
- };
-
- function crawl(objIn, objOut, schema, list, base, path) {
- path = path || [];
-
- var keys = Object.keys(objIn);
-
- for(var i = 0; i < keys.length; i++) {
- var k = keys[i];
-
- // transforms are handled separately
- if(k === 'transforms') continue;
-
- var p = path.slice();
- p.push(k);
-
- var valIn = objIn[k];
- var valOut = objOut[k];
-
- var nestedSchema = getNestedSchema(schema, k);
- var isInfoArray = (nestedSchema || {}).valType === 'info_array';
- var isColorscale = (nestedSchema || {}).valType === 'colorscale';
- var items = (nestedSchema || {}).items;
-
- if(!isInSchema(schema, k)) {
- list.push(format('schema', base, p));
- }
- else if(isPlainObject(valIn) && isPlainObject(valOut)) {
- crawl(valIn, valOut, nestedSchema, list, base, p);
- }
- else if(isInfoArray && isArray(valIn)) {
- if(valIn.length > valOut.length) {
- list.push(format('unused', base, p.concat(valOut.length)));
- }
- var len = valOut.length;
- var arrayItems = Array.isArray(items);
- if(arrayItems) len = Math.min(len, items.length);
- var m, n, item, valInPart, valOutPart;
- if(nestedSchema.dimensions === 2) {
- for(n = 0; n < len; n++) {
- if(isArray(valIn[n])) {
- if(valIn[n].length > valOut[n].length) {
- list.push(format('unused', base, p.concat(n, valOut[n].length)));
- }
- var len2 = valOut[n].length;
- for(m = 0; m < (arrayItems ? Math.min(len2, items[n].length) : len2); m++) {
- item = arrayItems ? items[n][m] : items;
- valInPart = valIn[n][m];
- valOutPart = valOut[n][m];
- if(!Lib.validate(valInPart, item)) {
- list.push(format('value', base, p.concat(n, m), valInPart));
- }
- else if(valOutPart !== valInPart && valOutPart !== +valInPart) {
- list.push(format('dynamic', base, p.concat(n, m), valInPart, valOutPart));
- }
- }
- }
- else {
- list.push(format('array', base, p.concat(n), valIn[n]));
- }
- }
- }
- else {
- for(n = 0; n < len; n++) {
- item = arrayItems ? items[n] : items;
- valInPart = valIn[n];
- valOutPart = valOut[n];
- if(!Lib.validate(valInPart, item)) {
- list.push(format('value', base, p.concat(n), valInPart));
- }
- else if(valOutPart !== valInPart && valOutPart !== +valInPart) {
- list.push(format('dynamic', base, p.concat(n), valInPart, valOutPart));
- }
- }
- }
- }
- else if(nestedSchema.items && !isInfoArray && isArray(valIn)) {
- var _nestedSchema = items[Object.keys(items)[0]];
- var indexList = [];
-
- var j, _p;
-
- // loop over valOut items while keeping track of their
- // corresponding input container index (given by _index)
- for(j = 0; j < valOut.length; j++) {
- var _index = valOut[j]._index || j;
-
- _p = p.slice();
- _p.push(_index);
-
- if(isPlainObject(valIn[_index]) && isPlainObject(valOut[j])) {
- indexList.push(_index);
- var valInj = valIn[_index];
- var valOutj = valOut[j];
- if(isPlainObject(valInj) && valInj.visible !== false && valOutj.visible === false) {
- list.push(format('invisible', base, _p));
- }
- else crawl(valInj, valOutj, _nestedSchema, list, base, _p);
- }
- }
-
- // loop over valIn to determine where it went wrong for some items
- for(j = 0; j < valIn.length; j++) {
- _p = p.slice();
- _p.push(j);
-
- if(!isPlainObject(valIn[j])) {
- list.push(format('object', base, _p, valIn[j]));
- }
- else if(indexList.indexOf(j) === -1) {
- list.push(format('unused', base, _p));
- }
- }
- }
- else if(!isPlainObject(valIn) && isPlainObject(valOut)) {
- list.push(format('object', base, p, valIn));
- }
- else if(!isArrayOrTypedArray(valIn) && isArrayOrTypedArray(valOut) && !isInfoArray && !isColorscale) {
- list.push(format('array', base, p, valIn));
- }
- else if(!(k in objOut)) {
- list.push(format('unused', base, p, valIn));
- }
- else if(!Lib.validate(valIn, nestedSchema)) {
- list.push(format('value', base, p, valIn));
- }
- else if(nestedSchema.valType === 'enumerated' &&
- ((nestedSchema.coerceNumber && valIn !== +valOut) || valIn !== valOut)
- ) {
- list.push(format('dynamic', base, p, valIn, valOut));
- }
- }
-
- return list;
- }
-
- // the 'full' layout schema depends on the traces types presents
- function fillLayoutSchema(schema, dataOut) {
- var layoutSchema = schema.layout.layoutAttributes;
-
- for(var i = 0; i < dataOut.length; i++) {
- var traceOut = dataOut[i];
- var traceSchema = schema.traces[traceOut.type];
- var traceLayoutAttr = traceSchema.layoutAttributes;
-
- if(traceLayoutAttr) {
- if(traceOut.subplot) {
- Lib.extendFlat(layoutSchema[traceSchema.attributes.subplot.dflt], traceLayoutAttr);
- } else {
- Lib.extendFlat(layoutSchema, traceLayoutAttr);
- }
- }
- }
-
- return layoutSchema;
- }
-
- // validation error codes
- var code2msgFunc = {
- object: function(base, astr) {
- var prefix;
-
- if(base === 'layout' && astr === '') prefix = 'The layout argument';
- else if(base[0] === 'data' && astr === '') {
- prefix = 'Trace ' + base[1] + ' in the data argument';
- }
- else prefix = inBase(base) + 'key ' + astr;
-
- return prefix + ' must be linked to an object container';
- },
- array: function(base, astr) {
- var prefix;
-
- if(base === 'data') prefix = 'The data argument';
- else prefix = inBase(base) + 'key ' + astr;
-
- return prefix + ' must be linked to an array container';
- },
- schema: function(base, astr) {
- return inBase(base) + 'key ' + astr + ' is not part of the schema';
- },
- unused: function(base, astr, valIn) {
- var target = isPlainObject(valIn) ? 'container' : 'key';
-
- return inBase(base) + target + ' ' + astr + ' did not get coerced';
- },
- dynamic: function(base, astr, valIn, valOut) {
- return [
- inBase(base) + 'key',
- astr,
- '(set to \'' + valIn + '\')',
- 'got reset to',
- '\'' + valOut + '\'',
- 'during defaults.'
- ].join(' ');
- },
- invisible: function(base, astr) {
- return (
- astr ? (inBase(base) + 'item ' + astr) : ('Trace ' + base[1])
- ) + ' got defaulted to be not visible';
- },
- value: function(base, astr, valIn) {
- return [
- inBase(base) + 'key ' + astr,
- 'is set to an invalid value (' + valIn + ')'
- ].join(' ');
- }
- };
-
- function inBase(base) {
- if(isArray(base)) return 'In data trace ' + base[1] + ', ';
-
- return 'In ' + base + ', ';
- }
-
- function format(code, base, path, valIn, valOut) {
- path = path || '';
-
- var container, trace;
-
- // container is either 'data' or 'layout
- // trace is the trace index if 'data', null otherwise
-
- if(isArray(base)) {
- container = base[0];
- trace = base[1];
- }
- else {
- container = base;
- trace = null;
- }
-
- var astr = convertPathToAttributeString(path);
- var msg = code2msgFunc[code](base, astr, valIn, valOut);
-
- // log to console if logger config option is enabled
- Lib.log(msg);
-
- return {
- code: code,
- container: container,
- trace: trace,
- path: path,
- astr: astr,
- msg: msg
- };
- }
-
- function isInSchema(schema, key) {
- var parts = splitKey(key);
- var keyMinusId = parts.keyMinusId;
- var id = parts.id;
-
- if((keyMinusId in schema) && schema[keyMinusId]._isSubplotObj && id) {
- return true;
- }
-
- return (key in schema);
- }
-
- function getNestedSchema(schema, key) {
- if(key in schema) return schema[key];
-
- var parts = splitKey(key);
-
- return schema[parts.keyMinusId];
- }
-
- var idRegex = Lib.counterRegex('([a-z]+)');
-
- function splitKey(key) {
- var idMatch = key.match(idRegex);
-
- return {
- keyMinusId: idMatch && idMatch[1],
- id: idMatch && idMatch[2]
- };
- }
-
- function convertPathToAttributeString(path) {
- if(!isArray(path)) return String(path);
-
- var astr = '';
-
- for(var i = 0; i < path.length; i++) {
- var p = path[i];
-
- if(typeof p === 'number') {
- astr = astr.substr(0, astr.length - 1) + '[' + p + ']';
- }
- else {
- astr += p;
- }
-
- if(i < path.length - 1) astr += '.';
- }
-
- return astr;
- }
-
- },{"../lib":168,"../plots/plots":245,"./plot_config":200,"./plot_schema":201}],207:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- mode: {
- valType: 'enumerated',
- dflt: 'afterall',
-
- values: ['immediate', 'next', 'afterall'],
-
- },
- direction: {
- valType: 'enumerated',
-
- values: ['forward', 'reverse'],
- dflt: 'forward',
-
- },
- fromcurrent: {
- valType: 'boolean',
- dflt: false,
-
-
- },
- frame: {
- duration: {
- valType: 'number',
-
- min: 0,
- dflt: 500,
-
- },
- redraw: {
- valType: 'boolean',
-
- dflt: true,
-
- },
- },
- transition: {
- duration: {
- valType: 'number',
-
- min: 0,
- dflt: 500,
- editType: 'none',
-
- },
- easing: {
- valType: 'enumerated',
- dflt: 'cubic-in-out',
- values: [
- 'linear',
- 'quad',
- 'cubic',
- 'sin',
- 'exp',
- 'circle',
- 'elastic',
- 'back',
- 'bounce',
- 'linear-in',
- 'quad-in',
- 'cubic-in',
- 'sin-in',
- 'exp-in',
- 'circle-in',
- 'elastic-in',
- 'back-in',
- 'bounce-in',
- 'linear-out',
- 'quad-out',
- 'cubic-out',
- 'sin-out',
- 'exp-out',
- 'circle-out',
- 'elastic-out',
- 'back-out',
- 'bounce-out',
- 'linear-in-out',
- 'quad-in-out',
- 'cubic-in-out',
- 'sin-in-out',
- 'exp-in-out',
- 'circle-in-out',
- 'elastic-in-out',
- 'back-in-out',
- 'bounce-in-out'
- ],
-
- editType: 'none',
-
- },
- ordering: {
- valType: 'enumerated',
- values: ['layout first', 'traces first'],
- dflt: 'layout first',
-
- editType: 'none',
-
- }
- }
- };
-
- },{}],208:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../lib');
- var Template = _dereq_('../plot_api/plot_template');
-
- /** Convenience wrapper for making array container logic DRY and consistent
- *
- * @param {object} parentObjIn
- * user input object where the container in question is linked
- * (i.e. either a user trace object or the user layout object)
- *
- * @param {object} parentObjOut
- * full object where the coerced container will be linked
- * (i.e. either a full trace object or the full layout object)
- *
- * @param {object} opts
- * options object:
- * - name {string}
- * name of the key linking the container in question
- * - inclusionAttr {string}
- * name of the item attribute for inclusion/exclusion. Default is 'visible'.
- * Since inclusion is true, use eg 'enabled' instead of 'disabled'.
- * - handleItemDefaults {function}
- * defaults method to be called on each item in the array container in question
- *
- * Its arguments are:
- * - itemIn {object} item in user layout
- * - itemOut {object} item in full layout
- * - parentObj {object} (as in closure)
- * - opts {object} (as in closure)
- * N.B.
- *
- * - opts is passed to handleItemDefaults so it can also store
- * links to supplementary data (e.g. fullData for layout components)
- *
- */
- module.exports = function handleArrayContainerDefaults(parentObjIn, parentObjOut, opts) {
- var name = opts.name;
- var inclusionAttr = opts.inclusionAttr || 'visible';
-
- var previousContOut = parentObjOut[name];
-
- var contIn = Lib.isArrayOrTypedArray(parentObjIn[name]) ? parentObjIn[name] : [];
- var contOut = parentObjOut[name] = [];
- var templater = Template.arrayTemplater(parentObjOut, name, inclusionAttr);
- var i, itemOut;
-
- for(i = 0; i < contIn.length; i++) {
- var itemIn = contIn[i];
-
- if(!Lib.isPlainObject(itemIn)) {
- itemOut = templater.newItem({});
- itemOut[inclusionAttr] = false;
- }
- else {
- itemOut = templater.newItem(itemIn);
- }
-
- itemOut._index = i;
-
- if(itemOut[inclusionAttr] !== false) {
- opts.handleItemDefaults(itemIn, itemOut, parentObjOut, opts);
- }
-
- contOut.push(itemOut);
- }
-
- var defaultItems = templater.defaultItems();
- for(i = 0; i < defaultItems.length; i++) {
- itemOut = defaultItems[i];
- itemOut._index = contOut.length;
- opts.handleItemDefaults({}, itemOut, parentObjOut, opts, {});
- contOut.push(itemOut);
- }
-
- // in case this array gets its defaults rebuilt independent of the whole layout,
- // relink the private keys just for this array.
- if(Lib.isArrayOrTypedArray(previousContOut)) {
- var len = Math.min(previousContOut.length, contOut.length);
- for(i = 0; i < len; i++) {
- Lib.relinkPrivateKeys(contOut[i], previousContOut[i]);
- }
- }
-
- return contOut;
- };
-
- },{"../lib":168,"../plot_api/plot_template":202}],209:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var fxAttrs = _dereq_('../components/fx/attributes');
-
- module.exports = {
- type: {
- valType: 'enumerated',
-
- values: [], // listed dynamically
- dflt: 'scatter',
- editType: 'calc+clearAxisTypes',
- _noTemplating: true // we handle this at a higher level
- },
- visible: {
- valType: 'enumerated',
- values: [true, false, 'legendonly'],
-
- dflt: true,
- editType: 'calc',
-
- },
- showlegend: {
- valType: 'boolean',
-
- dflt: true,
- editType: 'style',
-
- },
- legendgroup: {
- valType: 'string',
-
- dflt: '',
- editType: 'style',
-
- },
- opacity: {
- valType: 'number',
-
- min: 0,
- max: 1,
- dflt: 1,
- editType: 'style',
-
- },
- name: {
- valType: 'string',
-
- editType: 'style',
-
- },
- uid: {
- valType: 'string',
-
- editType: 'plot',
- anim: true,
-
- },
- ids: {
- valType: 'data_array',
- editType: 'calc',
- anim: true,
-
- },
- customdata: {
- valType: 'data_array',
- editType: 'calc',
-
- },
-
- // N.B. these cannot be 'data_array' as they do not have the same length as
- // other data arrays and arrayOk attributes in general
- //
- // Maybe add another valType:
- // https://github.com/plotly/plotly.js/issues/1894
- selectedpoints: {
- valType: 'any',
-
- editType: 'calc',
-
- },
-
- hoverinfo: {
- valType: 'flaglist',
-
- flags: ['x', 'y', 'z', 'text', 'name'],
- extras: ['all', 'none', 'skip'],
- arrayOk: true,
- dflt: 'all',
- editType: 'none',
-
- },
- hoverlabel: fxAttrs.hoverlabel,
- stream: {
- token: {
- valType: 'string',
- noBlank: true,
- strict: true,
-
- editType: 'calc',
-
- },
- maxpoints: {
- valType: 'number',
- min: 0,
- max: 10000,
- dflt: 500,
-
- editType: 'calc',
-
- },
- editType: 'calc'
- },
- transforms: {
- _isLinkedToArray: 'transform',
- editType: 'calc',
-
- },
- uirevision: {
- valType: 'any',
-
- editType: 'none',
-
- }
- };
-
- },{"../components/fx/attributes":81}],210:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
-
- module.exports = {
- xaxis: {
- valType: 'subplotid',
-
- dflt: 'x',
- editType: 'calc+clearAxisTypes',
-
- },
- yaxis: {
- valType: 'subplotid',
-
- dflt: 'y',
- editType: 'calc+clearAxisTypes',
-
- }
- };
-
- },{}],211:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Lib = _dereq_('../../lib');
- var FP_SAFE = _dereq_('../../constants/numerical').FP_SAFE;
- var Registry = _dereq_('../../registry');
-
- module.exports = {
- getAutoRange: getAutoRange,
- makePadFn: makePadFn,
- doAutoRange: doAutoRange,
- findExtremes: findExtremes,
- concatExtremes: concatExtremes
- };
-
- /**
- * getAutoRange
- *
- * Collects all _extremes values corresponding to a given axis
- * and computes its auto range.
- *
- * Note that getAutoRange uses return values from findExtremes.
- *
- * @param {object} gd:
- * graph div object with filled-in fullData and fullLayout, in particular
- * with filled-in '_extremes' containers:
- * {
- * val: calcdata value,
- * pad: extra pixels beyond this value,
- * extrapad: bool, does this point want 5% extra padding
- * }
- * @param {object} ax:
- * full axis object, in particular with filled-in '_traceIndices'
- * and '_annIndices' / '_shapeIndices' if applicable
- * @return {array}
- * an array of [min, max]. These are calcdata for log and category axes
- * and data for linear and date axes.
- *
- * TODO: we want to change log to data as well, but it's hard to do this
- * maintaining backward compatibility. category will always have to use calcdata
- * though, because otherwise values between categories (or outside all categories)
- * would be impossible.
- */
- function getAutoRange(gd, ax) {
- var i, j;
- var newRange = [];
-
- var getPad = makePadFn(ax);
- var extremes = concatExtremes(gd, ax);
- var minArray = extremes.min;
- var maxArray = extremes.max;
-
- if(minArray.length === 0 || maxArray.length === 0) {
- return Lib.simpleMap(ax.range, ax.r2l);
- }
-
- var minmin = minArray[0].val;
- var maxmax = maxArray[0].val;
-
- for(i = 1; i < minArray.length; i++) {
- if(minmin !== maxmax) break;
- minmin = Math.min(minmin, minArray[i].val);
- }
- for(i = 1; i < maxArray.length; i++) {
- if(minmin !== maxmax) break;
- maxmax = Math.max(maxmax, maxArray[i].val);
- }
-
- var axReverse = false;
-
- if(ax.range) {
- var rng = Lib.simpleMap(ax.range, ax.r2l);
- axReverse = rng[1] < rng[0];
- }
- // one-time setting to easily reverse the axis
- // when plotting from code
- if(ax.autorange === 'reversed') {
- axReverse = true;
- ax.autorange = true;
- }
-
- var rangeMode = ax.rangemode;
- var toZero = rangeMode === 'tozero';
- var nonNegative = rangeMode === 'nonnegative';
- var axLen = ax._length;
- // don't allow padding to reduce the data to < 10% of the length
- var minSpan = axLen / 10;
-
- var mbest = 0;
- var minpt, maxpt, minbest, maxbest, dp, dv;
-
- for(i = 0; i < minArray.length; i++) {
- minpt = minArray[i];
- for(j = 0; j < maxArray.length; j++) {
- maxpt = maxArray[j];
- dv = maxpt.val - minpt.val;
- if(dv > 0) {
- dp = axLen - getPad(minpt) - getPad(maxpt);
- if(dp > minSpan) {
- if(dv / dp > mbest) {
- minbest = minpt;
- maxbest = maxpt;
- mbest = dv / dp;
- }
- }
- else if(dv / axLen > mbest) {
- // in case of padding longer than the axis
- // at least include the unpadded data values.
- minbest = {val: minpt.val, pad: 0};
- maxbest = {val: maxpt.val, pad: 0};
- mbest = dv / axLen;
- }
- }
- }
- }
-
- function getMaxPad(prev, pt) {
- return Math.max(prev, getPad(pt));
- }
-
- if(minmin === maxmax) {
- var lower = minmin - 1;
- var upper = minmin + 1;
- if(toZero) {
- if(minmin === 0) {
- // The only value we have on this axis is 0, and we want to
- // autorange so zero is one end.
- // In principle this could be [0, 1] or [-1, 0] but usually
- // 'tozero' pins 0 to the low end, so follow that.
- newRange = [0, 1];
- }
- else {
- var maxPad = (minmin > 0 ? maxArray : minArray).reduce(getMaxPad, 0);
- // we're pushing a single value away from the edge due to its
- // padding, with the other end clamped at zero
- // 0.5 means don't push it farther than the center.
- var rangeEnd = minmin / (1 - Math.min(0.5, maxPad / axLen));
- newRange = minmin > 0 ? [0, rangeEnd] : [rangeEnd, 0];
- }
- } else if(nonNegative) {
- newRange = [Math.max(0, lower), Math.max(1, upper)];
- } else {
- newRange = [lower, upper];
- }
- }
- else {
- if(toZero) {
- if(minbest.val >= 0) {
- minbest = {val: 0, pad: 0};
- }
- if(maxbest.val <= 0) {
- maxbest = {val: 0, pad: 0};
- }
- }
- else if(nonNegative) {
- if(minbest.val - mbest * getPad(minbest) < 0) {
- minbest = {val: 0, pad: 0};
- }
- if(maxbest.val <= 0) {
- maxbest = {val: 1, pad: 0};
- }
- }
-
- // in case it changed again...
- mbest = (maxbest.val - minbest.val) /
- (axLen - getPad(minbest) - getPad(maxbest));
-
- newRange = [
- minbest.val - mbest * getPad(minbest),
- maxbest.val + mbest * getPad(maxbest)
- ];
- }
-
- // maintain reversal
- if(axReverse) newRange.reverse();
-
- return Lib.simpleMap(newRange, ax.l2r || Number);
- }
-
- /*
- * calculate the pixel padding for ax._min and ax._max entries with
- * optional extrapad as 5% of the total axis length
- */
- function makePadFn(ax) {
- // 5% padding for points that specify extrapad: true
- var extrappad = ax._length / 20;
-
- // domain-constrained axes: base extrappad on the unconstrained
- // domain so it's consistent as the domain changes
- if((ax.constrain === 'domain') && ax._inputDomain) {
- extrappad *= (ax._inputDomain[1] - ax._inputDomain[0]) /
- (ax.domain[1] - ax.domain[0]);
- }
-
- return function getPad(pt) { return pt.pad + (pt.extrapad ? extrappad : 0); };
- }
-
- function concatExtremes(gd, ax) {
- var axId = ax._id;
- var fullData = gd._fullData;
- var fullLayout = gd._fullLayout;
- var minArray = [];
- var maxArray = [];
- var i, j, d;
-
- function _concat(cont, indices) {
- for(i = 0; i < indices.length; i++) {
- var item = cont[indices[i]];
- var extremes = (item._extremes || {})[axId];
- if(item.visible === true && extremes) {
- for(j = 0; j < extremes.min.length; j++) {
- d = extremes.min[j];
- collapseMinArray(minArray, d.val, d.pad, {extrapad: d.extrapad});
- }
- for(j = 0; j < extremes.max.length; j++) {
- d = extremes.max[j];
- collapseMaxArray(maxArray, d.val, d.pad, {extrapad: d.extrapad});
- }
- }
- }
- }
-
- _concat(fullData, ax._traceIndices);
- _concat(fullLayout.annotations || [], ax._annIndices || []);
- _concat(fullLayout.shapes || [], ax._shapeIndices || []);
-
- return {min: minArray, max: maxArray};
- }
-
- function doAutoRange(gd, ax) {
- if(ax.autorange) {
- ax.range = getAutoRange(gd, ax);
-
- ax._r = ax.range.slice();
- ax._rl = Lib.simpleMap(ax._r, ax.r2l);
-
- // doAutoRange will get called on fullLayout,
- // but we want to report its results back to layout
-
- var axIn = ax._input;
-
- // before we edit _input, store preGUI values
- var edits = {};
- edits[ax._attr + '.range'] = ax.range;
- edits[ax._attr + '.autorange'] = ax.autorange;
- Registry.call('_storeDirectGUIEdit', gd.layout, gd._fullLayout._preGUI, edits);
-
- axIn.range = ax.range.slice();
- axIn.autorange = ax.autorange;
- }
-
- var anchorAx = ax._anchorAxis;
-
- if(anchorAx && anchorAx.rangeslider) {
- var axeRangeOpts = anchorAx.rangeslider[ax._name];
- if(axeRangeOpts) {
- if(axeRangeOpts.rangemode === 'auto') {
- axeRangeOpts.range = getAutoRange(gd, ax);
- }
- }
- anchorAx._input.rangeslider[ax._name] = Lib.extendFlat({}, axeRangeOpts);
- }
- }
-
- /**
- * findExtremes
- *
- * Find min/max extremes of an array of coordinates on a given axis.
- *
- * Note that findExtremes is called during `calc`, when we don't yet know the axis
- * length; all the inputs should be based solely on the trace data, nothing
- * about the axis layout.
- *
- * Note that `ppad` and `vpad` as well as their asymmetric variants refer to
- * the before and after padding of the passed `data` array, not to the whole axis.
- *
- * @param {object} ax: full axis object
- * relies on
- * - ax.type
- * - ax._m (just its sign)
- * - ax.d2l
- * @param {array} data:
- * array of numbers (i.e. already run though ax.d2c)
- * @param {object} opts:
- * available keys are:
- * vpad: (number or number array) pad values (data value +-vpad)
- * ppad: (number or number array) pad pixels (pixel location +-ppad)
- * ppadplus, ppadminus, vpadplus, vpadminus:
- * separate padding for each side, overrides symmetric
- * padded: (boolean) add 5% padding to both ends
- * (unless one end is overridden by tozero)
- * tozero: (boolean) make sure to include zero if axis is linear,
- * and make it a tight bound if possible
- *
- * @return {object}
- * - min {array of objects}
- * - max {array of objects}
- * each object item has fields:
- * - val {number}
- * - pad {number}
- * - extrappad {number}
- * - opts {object}: a ref to the passed "options" object
- */
- function findExtremes(ax, data, opts) {
- if(!opts) opts = {};
- if(!ax._m) ax.setScale();
-
- var minArray = [];
- var maxArray = [];
-
- var len = data.length;
- var extrapad = opts.padded || false;
- var tozero = opts.tozero && (ax.type === 'linear' || ax.type === '-');
- var isLog = ax.type === 'log';
- var hasArrayOption = false;
- var i, v, di, dmin, dmax, ppadiplus, ppadiminus, vmin, vmax;
-
- function makePadAccessor(item) {
- if(Array.isArray(item)) {
- hasArrayOption = true;
- return function(i) { return Math.max(Number(item[i]||0), 0); };
- }
- else {
- var v = Math.max(Number(item||0), 0);
- return function() { return v; };
- }
- }
-
- var ppadplus = makePadAccessor((ax._m > 0 ?
- opts.ppadplus : opts.ppadminus) || opts.ppad || 0);
- var ppadminus = makePadAccessor((ax._m > 0 ?
- opts.ppadminus : opts.ppadplus) || opts.ppad || 0);
- var vpadplus = makePadAccessor(opts.vpadplus || opts.vpad);
- var vpadminus = makePadAccessor(opts.vpadminus || opts.vpad);
-
- if(!hasArrayOption) {
- // with no arrays other than `data` we don't need to consider
- // every point, only the extreme data points
- vmin = Infinity;
- vmax = -Infinity;
-
- if(isLog) {
- for(i = 0; i < len; i++) {
- v = data[i];
- // data is not linearized yet so we still have to filter out negative logs
- if(v < vmin && v > 0) vmin = v;
- if(v > vmax && v < FP_SAFE) vmax = v;
- }
- } else {
- for(i = 0; i < len; i++) {
- v = data[i];
- if(v < vmin && v > -FP_SAFE) vmin = v;
- if(v > vmax && v < FP_SAFE) vmax = v;
- }
- }
-
- data = [vmin, vmax];
- len = 2;
- }
-
- var collapseOpts = {tozero: tozero, extrapad: extrapad};
-
- function addItem(i) {
- di = data[i];
- if(!isNumeric(di)) return;
- ppadiplus = ppadplus(i);
- ppadiminus = ppadminus(i);
- vmin = di - vpadminus(i);
- vmax = di + vpadplus(i);
- // special case for log axes: if vpad makes this object span
- // more than an order of mag, clip it to one order. This is so
- // we don't have non-positive errors or absurdly large lower
- // range due to rounding errors
- if(isLog && vmin < vmax / 10) vmin = vmax / 10;
-
- dmin = ax.c2l(vmin);
- dmax = ax.c2l(vmax);
-
- if(tozero) {
- dmin = Math.min(0, dmin);
- dmax = Math.max(0, dmax);
- }
- if(goodNumber(dmin)) {
- collapseMinArray(minArray, dmin, ppadiminus, collapseOpts);
- }
- if(goodNumber(dmax)) {
- collapseMaxArray(maxArray, dmax, ppadiplus, collapseOpts);
- }
- }
-
- // For efficiency covering monotonic or near-monotonic data,
- // check a few points at both ends first and then sweep
- // through the middle
- var iMax = Math.min(6, len);
- for(i = 0; i < iMax; i++) addItem(i);
- for(i = len - 1; i >= iMax; i--) addItem(i);
-
- return {
- min: minArray,
- max: maxArray,
- opts: opts
- };
- }
-
- function collapseMinArray(array, newVal, newPad, opts) {
- collapseArray(array, newVal, newPad, opts, lessOrEqual);
- }
-
- function collapseMaxArray(array, newVal, newPad, opts) {
- collapseArray(array, newVal, newPad, opts, greaterOrEqual);
- }
-
- /**
- * collapseArray
- *
- * Takes items from 'array' and compares them to 'newVal', 'newPad'.
- *
- * @param {array} array:
- * current set of min or max extremes
- * @param {number} newVal:
- * new value to compare against
- * @param {number} newPad:
- * pad value associated with 'newVal'
- * @param {object} opts:
- * - tozero {boolean}
- * - extrapad {number}
- * @param {function} atLeastAsExtreme:
- * comparison function, use
- * - lessOrEqual for min 'array' and
- * - greaterOrEqual for max 'array'
- *
- * In practice, 'array' is either
- * - 'extremes[ax._id].min' or
- * - 'extremes[ax._id].max
- * found in traces and layout items that affect autorange.
- *
- * Since we don't yet know the relationship between pixels and values
- * (that's what we're trying to figure out!) AND we don't yet know how
- * many pixels `extrapad` represents (it's going to be 5% of the length,
- * but we don't want to have to redo calc just because length changed)
- * two point must satisfy three criteria simultaneously for one to supersede the other:
- * - at least as extreme a `val`
- * - at least as big a `pad`
- * - an unpadded point cannot supersede a padded point, but any other combination can
- *
- * Then:
- * - If the item supersedes the new point, set includeThis false
- * - If the new pt supersedes the item, delete it from 'array'
- */
- function collapseArray(array, newVal, newPad, opts, atLeastAsExtreme) {
- var tozero = opts.tozero;
- var extrapad = opts.extrapad;
- var includeThis = true;
-
- for(var j = 0; j < array.length && includeThis; j++) {
- var v = array[j];
- if(atLeastAsExtreme(v.val, newVal) && v.pad >= newPad && (v.extrapad || !extrapad)) {
- includeThis = false;
- break;
- } else if(atLeastAsExtreme(newVal, v.val) && v.pad <= newPad && (extrapad || !v.extrapad)) {
- array.splice(j, 1);
- j--;
- }
- }
- if(includeThis) {
- var clipAtZero = (tozero && newVal === 0);
- array.push({
- val: newVal,
- pad: clipAtZero ? 0 : newPad,
- extrapad: clipAtZero ? false : extrapad
- });
- }
- }
-
- // In order to stop overflow errors, don't consider points
- // too close to the limits of js floating point
- function goodNumber(v) {
- return isNumeric(v) && Math.abs(v) < FP_SAFE;
- }
-
- function lessOrEqual(v0, v1) { return v0 <= v1; }
- function greaterOrEqual(v0, v1) { return v0 >= v1; }
-
- },{"../../constants/numerical":149,"../../lib":168,"../../registry":257,"fast-isnumeric":18}],212:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
- var Plots = _dereq_('../../plots/plots');
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var Titles = _dereq_('../../components/titles');
- var Color = _dereq_('../../components/color');
- var Drawing = _dereq_('../../components/drawing');
-
- var axAttrs = _dereq_('./layout_attributes');
- var cleanTicks = _dereq_('./clean_ticks');
-
- var constants = _dereq_('../../constants/numerical');
- var ONEAVGYEAR = constants.ONEAVGYEAR;
- var ONEAVGMONTH = constants.ONEAVGMONTH;
- var ONEDAY = constants.ONEDAY;
- var ONEHOUR = constants.ONEHOUR;
- var ONEMIN = constants.ONEMIN;
- var ONESEC = constants.ONESEC;
- var MINUS_SIGN = constants.MINUS_SIGN;
- var BADNUM = constants.BADNUM;
-
- var MID_SHIFT = _dereq_('../../constants/alignment').MID_SHIFT;
- var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING;
-
- var axes = module.exports = {};
-
- axes.setConvert = _dereq_('./set_convert');
- var autoType = _dereq_('./axis_autotype');
-
- var axisIds = _dereq_('./axis_ids');
- axes.id2name = axisIds.id2name;
- axes.name2id = axisIds.name2id;
- axes.cleanId = axisIds.cleanId;
- axes.list = axisIds.list;
- axes.listIds = axisIds.listIds;
- axes.getFromId = axisIds.getFromId;
- axes.getFromTrace = axisIds.getFromTrace;
-
- var autorange = _dereq_('./autorange');
- axes.getAutoRange = autorange.getAutoRange;
- axes.findExtremes = autorange.findExtremes;
-
- /*
- * find the list of possible axes to reference with an xref or yref attribute
- * and coerce it to that list
- *
- * attr: the attribute we're generating a reference for. Should end in 'x' or 'y'
- * but can be prefixed, like 'ax' for annotation's arrow x
- * dflt: the default to coerce to, or blank to use the first axis (falling back on
- * extraOption if there is no axis)
- * extraOption: aside from existing axes with this letter, what non-axis value is allowed?
- * Only required if it's different from `dflt`
- */
- axes.coerceRef = function(containerIn, containerOut, gd, attr, dflt, extraOption) {
- var axLetter = attr.charAt(attr.length - 1);
- var axlist = gd._fullLayout._subplots[axLetter + 'axis'];
- var refAttr = attr + 'ref';
- var attrDef = {};
-
- if(!dflt) dflt = axlist[0] || extraOption;
- if(!extraOption) extraOption = dflt;
-
- // data-ref annotations are not supported in gl2d yet
-
- attrDef[refAttr] = {
- valType: 'enumerated',
- values: axlist.concat(extraOption ? [extraOption] : []),
- dflt: dflt
- };
-
- // xref, yref
- return Lib.coerce(containerIn, containerOut, attrDef, refAttr);
- };
-
- /*
- * coerce position attributes (range-type) that can be either on axes or absolute
- * (paper or pixel) referenced. The biggest complication here is that we don't know
- * before looking at the axis whether the value must be a number or not (it may be
- * a date string), so we can't use the regular valType='number' machinery
- *
- * axRef (string): the axis this position is referenced to, or:
- * paper: fraction of the plot area
- * pixel: pixels relative to some starting position
- * attr (string): the attribute in containerOut we are coercing
- * dflt (number): the default position, as a fraction or pixels. If the attribute
- * is to be axis-referenced, this will be converted to an axis data value
- *
- * Also cleans the values, since the attribute definition itself has to say
- * valType: 'any' to handle date axes. This allows us to accept:
- * - for category axes: category names, and convert them here into serial numbers.
- * Note that this will NOT work for axis range endpoints, because we don't know
- * the category list yet (it's set by ax.makeCalcdata during calc)
- * but it works for component (note, shape, images) positions.
- * - for date axes: JS Dates or milliseconds, and convert to date strings
- * - for other types: coerce them to numbers
- */
- axes.coercePosition = function(containerOut, gd, coerce, axRef, attr, dflt) {
- var cleanPos, pos;
-
- if(axRef === 'paper' || axRef === 'pixel') {
- cleanPos = Lib.ensureNumber;
- pos = coerce(attr, dflt);
- } else {
- var ax = axes.getFromId(gd, axRef);
- dflt = ax.fraction2r(dflt);
- pos = coerce(attr, dflt);
- cleanPos = ax.cleanPos;
- }
-
- containerOut[attr] = cleanPos(pos);
- };
-
- axes.cleanPosition = function(pos, gd, axRef) {
- var cleanPos = (axRef === 'paper' || axRef === 'pixel') ?
- Lib.ensureNumber :
- axes.getFromId(gd, axRef).cleanPos;
-
- return cleanPos(pos);
- };
-
- axes.redrawComponents = function(gd, axIds) {
- axIds = axIds ? axIds : axes.listIds(gd);
-
- var fullLayout = gd._fullLayout;
-
- function _redrawOneComp(moduleName, methodName, stashName, shortCircuit) {
- var method = Registry.getComponentMethod(moduleName, methodName);
- var stash = {};
-
- for(var i = 0; i < axIds.length; i++) {
- var ax = fullLayout[axes.id2name(axIds[i])];
- var indices = ax[stashName];
-
- for(var j = 0; j < indices.length; j++) {
- var ind = indices[j];
-
- if(!stash[ind]) {
- method(gd, ind);
- stash[ind] = 1;
- // once is enough for images (which doesn't use the `i` arg anyway)
- if(shortCircuit) return;
- }
- }
- }
- }
-
- // annotations and shapes 'draw' method is slow,
- // use the finer-grained 'drawOne' method instead
- _redrawOneComp('annotations', 'drawOne', '_annIndices');
- _redrawOneComp('shapes', 'drawOne', '_shapeIndices');
- _redrawOneComp('images', 'draw', '_imgIndices', true);
- };
-
- var getDataConversions = axes.getDataConversions = function(gd, trace, target, targetArray) {
- var ax;
-
- // If target points to an axis, use the type we already have for that
- // axis to find the data type. Otherwise use the values to autotype.
- var d2cTarget = (target === 'x' || target === 'y' || target === 'z') ?
- target :
- targetArray;
-
- // In the case of an array target, make a mock data array
- // and call supplyDefaults to the data type and
- // setup the data-to-calc method.
- if(Array.isArray(d2cTarget)) {
- ax = {
- type: autoType(targetArray),
- _categories: []
- };
- axes.setConvert(ax);
-
- // build up ax._categories (usually done during ax.makeCalcdata()
- if(ax.type === 'category') {
- for(var i = 0; i < targetArray.length; i++) {
- ax.d2c(targetArray[i]);
- }
- }
- // TODO what to do for transforms?
- } else {
- ax = axes.getFromTrace(gd, trace, d2cTarget);
- }
-
- // if 'target' has corresponding axis
- // -> use setConvert method
- if(ax) return {d2c: ax.d2c, c2d: ax.c2d};
-
- // special case for 'ids'
- // -> cast to String
- if(d2cTarget === 'ids') return {d2c: toString, c2d: toString};
-
- // otherwise (e.g. numeric-array of 'marker.color' or 'marker.size')
- // -> cast to Number
-
- return {d2c: toNum, c2d: toNum};
- };
-
- function toNum(v) { return +v; }
- function toString(v) { return String(v); }
-
- axes.getDataToCoordFunc = function(gd, trace, target, targetArray) {
- return getDataConversions(gd, trace, target, targetArray).d2c;
- };
-
- // get counteraxis letter for this axis (name or id)
- // this can also be used as the id for default counter axis
- axes.counterLetter = function(id) {
- var axLetter = id.charAt(0);
- if(axLetter === 'x') return 'y';
- if(axLetter === 'y') return 'x';
- };
-
- // incorporate a new minimum difference and first tick into
- // forced
- // note that _forceTick0 is linearized, so needs to be turned into
- // a range value for setting tick0
- axes.minDtick = function(ax, newDiff, newFirst, allow) {
- // doesn't make sense to do forced min dTick on log or category axes,
- // and the plot itself may decide to cancel (ie non-grouped bars)
- if(['log', 'category', 'multicategory'].indexOf(ax.type) !== -1 || !allow) {
- ax._minDtick = 0;
- }
- // undefined means there's nothing there yet
- else if(ax._minDtick === undefined) {
- ax._minDtick = newDiff;
- ax._forceTick0 = newFirst;
- }
- else if(ax._minDtick) {
- // existing minDtick is an integer multiple of newDiff
- // (within rounding err)
- // and forceTick0 can be shifted to newFirst
- if((ax._minDtick / newDiff + 1e-6) % 1 < 2e-6 &&
- (((newFirst - ax._forceTick0) / newDiff % 1) +
- 1.000001) % 1 < 2e-6) {
- ax._minDtick = newDiff;
- ax._forceTick0 = newFirst;
- }
- // if the converse is true (newDiff is a multiple of minDtick and
- // newFirst can be shifted to forceTick0) then do nothing - same
- // forcing stands. Otherwise, cancel forced minimum
- else if((newDiff / ax._minDtick + 1e-6) % 1 > 2e-6 ||
- (((newFirst - ax._forceTick0) / ax._minDtick % 1) +
- 1.000001) % 1 > 2e-6) {
- ax._minDtick = 0;
- }
- }
- };
-
- // save a copy of the initial axis ranges in fullLayout
- // use them in mode bar and dblclick events
- axes.saveRangeInitial = function(gd, overwrite) {
- var axList = axes.list(gd, '', true);
- var hasOneAxisChanged = false;
-
- for(var i = 0; i < axList.length; i++) {
- var ax = axList[i];
- var isNew = (ax._rangeInitial === undefined);
- var hasChanged = isNew || !(
- ax.range[0] === ax._rangeInitial[0] &&
- ax.range[1] === ax._rangeInitial[1]
- );
-
- if((isNew && ax.autorange === false) || (overwrite && hasChanged)) {
- ax._rangeInitial = ax.range.slice();
- hasOneAxisChanged = true;
- }
- }
-
- return hasOneAxisChanged;
- };
-
- // save a copy of the initial spike visibility
- axes.saveShowSpikeInitial = function(gd, overwrite) {
- var axList = axes.list(gd, '', true);
- var hasOneAxisChanged = false;
- var allSpikesEnabled = 'on';
-
- for(var i = 0; i < axList.length; i++) {
- var ax = axList[i];
- var isNew = (ax._showSpikeInitial === undefined);
- var hasChanged = isNew || !(ax.showspikes === ax._showspikes);
-
- if(isNew || (overwrite && hasChanged)) {
- ax._showSpikeInitial = ax.showspikes;
- hasOneAxisChanged = true;
- }
-
- if(allSpikesEnabled === 'on' && !ax.showspikes) {
- allSpikesEnabled = 'off';
- }
- }
- gd._fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
- return hasOneAxisChanged;
- };
-
- axes.autoBin = function(data, ax, nbins, is2d, calendar, size) {
- var dataMin = Lib.aggNums(Math.min, null, data);
- var dataMax = Lib.aggNums(Math.max, null, data);
-
- if(ax.type === 'category' || ax.type === 'multicategory') {
- return {
- start: dataMin - 0.5,
- end: dataMax + 0.5,
- size: Math.max(1, Math.round(size) || 1),
- _dataSpan: dataMax - dataMin,
- };
- }
-
- if(!calendar) calendar = ax.calendar;
-
- // piggyback off tick code to make "nice" bin sizes and edges
- var dummyAx;
- if(ax.type === 'log') {
- dummyAx = {
- type: 'linear',
- range: [dataMin, dataMax]
- };
- } else {
- dummyAx = {
- type: ax.type,
- range: Lib.simpleMap([dataMin, dataMax], ax.c2r, 0, calendar),
- calendar: calendar
- };
- }
- axes.setConvert(dummyAx);
-
- size = size && cleanTicks.dtick(size, dummyAx.type);
-
- if(size) {
- dummyAx.dtick = size;
- dummyAx.tick0 = cleanTicks.tick0(undefined, dummyAx.type, calendar);
- }
- else {
- var size0;
- if(nbins) size0 = ((dataMax - dataMin) / nbins);
- else {
- // totally auto: scale off std deviation so the highest bin is
- // somewhat taller than the total number of bins, but don't let
- // the size get smaller than the 'nice' rounded down minimum
- // difference between values
- var distinctData = Lib.distinctVals(data);
- var msexp = Math.pow(10, Math.floor(
- Math.log(distinctData.minDiff) / Math.LN10));
- var minSize = msexp * Lib.roundUp(
- distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
- size0 = Math.max(minSize, 2 * Lib.stdev(data) /
- Math.pow(data.length, is2d ? 0.25 : 0.4));
-
- // fallback if ax.d2c output BADNUMs
- // e.g. when user try to plot categorical bins
- // on a layout.xaxis.type: 'linear'
- if(!isNumeric(size0)) size0 = 1;
- }
-
- axes.autoTicks(dummyAx, size0);
- }
-
-
- var finalSize = dummyAx.dtick;
- var binStart = axes.tickIncrement(
- axes.tickFirst(dummyAx), finalSize, 'reverse', calendar);
- var binEnd, bincount;
-
- // check for too many data points right at the edges of bins
- // (>50% within 1% of bin edges) or all data points integral
- // and offset the bins accordingly
- if(typeof finalSize === 'number') {
- binStart = autoShiftNumericBins(binStart, data, dummyAx, dataMin, dataMax);
-
- bincount = 1 + Math.floor((dataMax - binStart) / finalSize);
- binEnd = binStart + bincount * finalSize;
- }
- else {
- // month ticks - should be the only nonlinear kind we have at this point.
- // dtick (as supplied by axes.autoTick) only has nonlinear values on
- // date and log axes, but even if you display a histogram on a log axis
- // we bin it on a linear axis (which one could argue against, but that's
- // a separate issue)
- if(dummyAx.dtick.charAt(0) === 'M') {
- binStart = autoShiftMonthBins(binStart, data, finalSize, dataMin, calendar);
- }
-
- // calculate the endpoint for nonlinear ticks - you have to
- // just increment until you're done
- binEnd = binStart;
- bincount = 0;
- while(binEnd <= dataMax) {
- binEnd = axes.tickIncrement(binEnd, finalSize, false, calendar);
- bincount++;
- }
- }
-
- return {
- start: ax.c2r(binStart, 0, calendar),
- end: ax.c2r(binEnd, 0, calendar),
- size: finalSize,
- _dataSpan: dataMax - dataMin
- };
- };
-
-
- function autoShiftNumericBins(binStart, data, ax, dataMin, dataMax) {
- var edgecount = 0;
- var midcount = 0;
- var intcount = 0;
- var blankCount = 0;
-
- function nearEdge(v) {
- // is a value within 1% of a bin edge?
- return (1 + (v - binStart) * 100 / ax.dtick) % 100 < 2;
- }
-
- for(var i = 0; i < data.length; i++) {
- if(data[i] % 1 === 0) intcount++;
- else if(!isNumeric(data[i])) blankCount++;
-
- if(nearEdge(data[i])) edgecount++;
- if(nearEdge(data[i] + ax.dtick / 2)) midcount++;
- }
- var dataCount = data.length - blankCount;
-
- if(intcount === dataCount && ax.type !== 'date') {
- // all integers: if bin size is <1, it's because
- // that was specifically requested (large nbins)
- // so respect that... but center the bins containing
- // integers on those integers
- if(ax.dtick < 1) {
- binStart = dataMin - 0.5 * ax.dtick;
- }
- // otherwise start half an integer down regardless of
- // the bin size, just enough to clear up endpoint
- // ambiguity about which integers are in which bins.
- else {
- binStart -= 0.5;
- if(binStart + ax.dtick < dataMin) binStart += ax.dtick;
- }
- }
- else if(midcount < dataCount * 0.1) {
- if(edgecount > dataCount * 0.3 ||
- nearEdge(dataMin) || nearEdge(dataMax)) {
- // lots of points at the edge, not many in the middle
- // shift half a bin
- var binshift = ax.dtick / 2;
- binStart += (binStart + binshift < dataMin) ? binshift : -binshift;
- }
- }
- return binStart;
- }
-
-
- function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
- var stats = Lib.findExactDates(data, calendar);
- // number of data points that needs to be an exact value
- // to shift that increment to (near) the bin center
- var threshold = 0.8;
-
- if(stats.exactDays > threshold) {
- var numMonths = Number(dtick.substr(1));
-
- if((stats.exactYears > threshold) && (numMonths % 12 === 0)) {
- // The exact middle of a non-leap-year is 1.5 days into July
- // so if we start the bins here, all but leap years will
- // get hover-labeled as exact years.
- binStart = axes.tickIncrement(binStart, 'M6', 'reverse') + ONEDAY * 1.5;
- }
- else if(stats.exactMonths > threshold) {
- // Months are not as clean, but if we shift half the *longest*
- // month (31/2 days) then 31-day months will get labeled exactly
- // and shorter months will get labeled with the correct month
- // but shifted 12-36 hours into it.
- binStart = axes.tickIncrement(binStart, 'M1', 'reverse') + ONEDAY * 15.5;
- }
- else {
- // Shifting half a day is exact, but since these are month bins it
- // will always give a somewhat odd-looking label, until we do something
- // smarter like showing the bin boundaries (or the bounds of the actual
- // data in each bin)
- binStart -= ONEDAY / 2;
- }
- var nextBinStart = axes.tickIncrement(binStart, dtick);
-
- if(nextBinStart <= dataMin) return nextBinStart;
- }
- return binStart;
- }
-
- // ----------------------------------------------------
- // Ticks and grids
- // ----------------------------------------------------
-
- // ensure we have tick0, dtick, and tick rounding calculated
- axes.prepTicks = function(ax) {
- var rng = Lib.simpleMap(ax.range, ax.r2l);
-
- // calculate max number of (auto) ticks to display based on plot size
- if(ax.tickmode === 'auto' || !ax.dtick) {
- var nt = ax.nticks;
- var minPx;
-
- if(!nt) {
- if(ax.type === 'category' || ax.type === 'multicategory') {
- minPx = ax.tickfont ? (ax.tickfont.size || 12) * 1.2 : 15;
- nt = ax._length / minPx;
- } else {
- minPx = ax._id.charAt(0) === 'y' ? 40 : 80;
- nt = Lib.constrain(ax._length / minPx, 4, 9) + 1;
- }
-
- // radial axes span half their domain,
- // multiply nticks value by two to get correct number of auto ticks.
- if(ax._name === 'radialaxis') nt *= 2;
- }
-
- // add a couple of extra digits for filling in ticks when we
- // have explicit tickvals without tick text
- if(ax.tickmode === 'array') nt *= 100;
-
- axes.autoTicks(ax, Math.abs(rng[1] - rng[0]) / nt);
- // check for a forced minimum dtick
- if(ax._minDtick > 0 && ax.dtick < ax._minDtick * 2) {
- ax.dtick = ax._minDtick;
- ax.tick0 = ax.l2r(ax._forceTick0);
- }
- }
-
- // check for missing tick0
- if(!ax.tick0) {
- ax.tick0 = (ax.type === 'date') ? '2000-01-01' : 0;
- }
-
- // ensure we don't try to make ticks below our minimum precision
- // see https://github.com/plotly/plotly.js/issues/2892
- if(ax.type === 'date' && ax.dtick < 0.1) ax.dtick = 0.1;
-
- // now figure out rounding of tick values
- autoTickRound(ax);
- };
-
- // calculate the ticks: text, values, positioning
- // if ticks are set to automatic, determine the right values (tick0,dtick)
- // in any case, set tickround to # of digits to round tick labels to,
- // or codes to this effect for log and date scales
- axes.calcTicks = function calcTicks(ax) {
- axes.prepTicks(ax);
- var rng = Lib.simpleMap(ax.range, ax.r2l);
-
- // now that we've figured out the auto values for formatting
- // in case we're missing some ticktext, we can break out for array ticks
- if(ax.tickmode === 'array') return arrayTicks(ax);
-
- // find the first tick
- ax._tmin = axes.tickFirst(ax);
-
- // add a tiny bit so we get ticks which may have rounded out
- var startTick = rng[0] * 1.0001 - rng[1] * 0.0001;
- var endTick = rng[1] * 1.0001 - rng[0] * 0.0001;
- // check for reversed axis
- var axrev = (rng[1] < rng[0]);
-
- // No visible ticks? Quit.
- // I've only seen this on category axes with all categories off the edge.
- if((ax._tmin < startTick) !== axrev) return [];
-
- // return the full set of tick vals
- var vals = [];
- if(ax.type === 'category' || ax.type === 'multicategory') {
- endTick = (axrev) ? Math.max(-0.5, endTick) :
- Math.min(ax._categories.length - 0.5, endTick);
- }
-
- var xPrevious = null;
- var maxTicks = Math.max(1000, ax._length || 0);
- for(var x = ax._tmin;
- (axrev) ? (x >= endTick) : (x <= endTick);
- x = axes.tickIncrement(x, ax.dtick, axrev, ax.calendar)) {
- // prevent infinite loops - no more than one tick per pixel,
- // and make sure each value is different from the previous
- if(vals.length > maxTicks || x === xPrevious) break;
- xPrevious = x;
-
- vals.push(x);
- }
-
- // If same angle over a full circle, the last tick vals is a duplicate.
- // TODO must do something similar for angular date axes.
- if(isAngular(ax) && Math.abs(rng[1] - rng[0]) === 360) {
- vals.pop();
- }
-
- // save the last tick as well as first, so we can
- // show the exponent only on the last one
- ax._tmax = vals[vals.length - 1];
-
- // for showing the rest of a date when the main tick label is only the
- // latter part: ax._prevDateHead holds what we showed most recently.
- // Start with it cleared and mark that we're in calcTicks (ie calculating a
- // whole string of these so we should care what the previous date head was!)
- ax._prevDateHead = '';
- ax._inCalcTicks = true;
-
- var ticksOut = new Array(vals.length);
- for(var i = 0; i < vals.length; i++) ticksOut[i] = axes.tickText(ax, vals[i]);
-
- ax._inCalcTicks = false;
-
- return ticksOut;
- };
-
- function arrayTicks(ax) {
- var vals = ax.tickvals;
- var text = ax.ticktext;
- var ticksOut = new Array(vals.length);
- var rng = Lib.simpleMap(ax.range, ax.r2l);
- var r0expanded = rng[0] * 1.0001 - rng[1] * 0.0001;
- var r1expanded = rng[1] * 1.0001 - rng[0] * 0.0001;
- var tickMin = Math.min(r0expanded, r1expanded);
- var tickMax = Math.max(r0expanded, r1expanded);
- var j = 0;
-
- // without a text array, just format the given values as any other ticks
- // except with more precision to the numbers
- if(!Array.isArray(text)) text = [];
-
- // make sure showing ticks doesn't accidentally add new categories
- // TODO multicategory, if we allow ticktext / tickvals
- var tickVal2l = ax.type === 'category' ? ax.d2l_noadd : ax.d2l;
-
- // array ticks on log axes always show the full number
- // (if no explicit ticktext overrides it)
- if(ax.type === 'log' && String(ax.dtick).charAt(0) !== 'L') {
- ax.dtick = 'L' + Math.pow(10, Math.floor(Math.min(ax.range[0], ax.range[1])) - 1);
- }
-
- for(var i = 0; i < vals.length; i++) {
- var vali = tickVal2l(vals[i]);
- if(vali > tickMin && vali < tickMax) {
- if(text[i] === undefined) ticksOut[j] = axes.tickText(ax, vali);
- else ticksOut[j] = tickTextObj(ax, vali, String(text[i]));
- j++;
- }
- }
-
- if(j < vals.length) ticksOut.splice(j, vals.length - j);
-
- return ticksOut;
- }
-
- var roundBase10 = [2, 5, 10];
- var roundBase24 = [1, 2, 3, 6, 12];
- var roundBase60 = [1, 2, 5, 10, 15, 30];
- // 2&3 day ticks are weird, but need something btwn 1&7
- var roundDays = [1, 2, 3, 7, 14];
- // approx. tick positions for log axes, showing all (1) and just 1, 2, 5 (2)
- // these don't have to be exact, just close enough to round to the right value
- var roundLog1 = [-0.046, 0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1];
- var roundLog2 = [-0.301, 0, 0.301, 0.699, 1];
- // N.B. `thetaunit; 'radians' angular axes must be converted to degrees
- var roundAngles = [15, 30, 45, 90, 180];
-
- function roundDTick(roughDTick, base, roundingSet) {
- return base * Lib.roundUp(roughDTick / base, roundingSet);
- }
-
- // autoTicks: calculate best guess at pleasant ticks for this axis
- // inputs:
- // ax - an axis object
- // roughDTick - rough tick spacing (to be turned into a nice round number)
- // outputs (into ax):
- // tick0: starting point for ticks (not necessarily on the graph)
- // usually 0 for numeric (=10^0=1 for log) or jan 1, 2000 for dates
- // dtick: the actual, nice round tick spacing, usually a little larger than roughDTick
- // if the ticks are spaced linearly (linear scale, categories,
- // log with only full powers, date ticks < month),
- // this will just be a number
- // months: M#
- // years: M# where # is 12*number of years
- // log with linear ticks: L# where # is the linear tick spacing
- // log showing powers plus some intermediates:
- // D1 shows all digits, D2 shows 2 and 5
- axes.autoTicks = function(ax, roughDTick) {
- var base;
-
- function getBase(v) {
- return Math.pow(v, Math.floor(Math.log(roughDTick) / Math.LN10));
- }
-
- if(ax.type === 'date') {
- ax.tick0 = Lib.dateTick0(ax.calendar);
- // the criteria below are all based on the rough spacing we calculate
- // being > half of the final unit - so precalculate twice the rough val
- var roughX2 = 2 * roughDTick;
-
- if(roughX2 > ONEAVGYEAR) {
- roughDTick /= ONEAVGYEAR;
- base = getBase(10);
- ax.dtick = 'M' + (12 * roundDTick(roughDTick, base, roundBase10));
- }
- else if(roughX2 > ONEAVGMONTH) {
- roughDTick /= ONEAVGMONTH;
- ax.dtick = 'M' + roundDTick(roughDTick, 1, roundBase24);
- }
- else if(roughX2 > ONEDAY) {
- ax.dtick = roundDTick(roughDTick, ONEDAY, roundDays);
- // get week ticks on sunday
- // this will also move the base tick off 2000-01-01 if dtick is
- // 2 or 3 days... but that's a weird enough case that we'll ignore it.
- ax.tick0 = Lib.dateTick0(ax.calendar, true);
- }
- else if(roughX2 > ONEHOUR) {
- ax.dtick = roundDTick(roughDTick, ONEHOUR, roundBase24);
- }
- else if(roughX2 > ONEMIN) {
- ax.dtick = roundDTick(roughDTick, ONEMIN, roundBase60);
- }
- else if(roughX2 > ONESEC) {
- ax.dtick = roundDTick(roughDTick, ONESEC, roundBase60);
- }
- else {
- // milliseconds
- base = getBase(10);
- ax.dtick = roundDTick(roughDTick, base, roundBase10);
- }
- }
- else if(ax.type === 'log') {
- ax.tick0 = 0;
- var rng = Lib.simpleMap(ax.range, ax.r2l);
-
- if(roughDTick > 0.7) {
- // only show powers of 10
- ax.dtick = Math.ceil(roughDTick);
- }
- else if(Math.abs(rng[1] - rng[0]) < 1) {
- // span is less than one power of 10
- var nt = 1.5 * Math.abs((rng[1] - rng[0]) / roughDTick);
-
- // ticks on a linear scale, labeled fully
- roughDTick = Math.abs(Math.pow(10, rng[1]) -
- Math.pow(10, rng[0])) / nt;
- base = getBase(10);
- ax.dtick = 'L' + roundDTick(roughDTick, base, roundBase10);
- }
- else {
- // include intermediates between powers of 10,
- // labeled with small digits
- // ax.dtick = "D2" (show 2 and 5) or "D1" (show all digits)
- ax.dtick = (roughDTick > 0.3) ? 'D2' : 'D1';
- }
- }
- else if(ax.type === 'category' || ax.type === 'multicategory') {
- ax.tick0 = 0;
- ax.dtick = Math.ceil(Math.max(roughDTick, 1));
- }
- else if(isAngular(ax)) {
- ax.tick0 = 0;
- base = 1;
- ax.dtick = roundDTick(roughDTick, base, roundAngles);
- }
- else {
- // auto ticks always start at 0
- ax.tick0 = 0;
- base = getBase(10);
- ax.dtick = roundDTick(roughDTick, base, roundBase10);
- }
-
- // prevent infinite loops
- if(ax.dtick === 0) ax.dtick = 1;
-
- // TODO: this is from log axis histograms with autorange off
- if(!isNumeric(ax.dtick) && typeof ax.dtick !== 'string') {
- var olddtick = ax.dtick;
- ax.dtick = 1;
- throw 'ax.dtick error: ' + String(olddtick);
- }
- };
-
- // after dtick is already known, find tickround = precision
- // to display in tick labels
- // for numeric ticks, integer # digits after . to round to
- // for date ticks, the last date part to show (y,m,d,H,M,S)
- // or an integer # digits past seconds
- function autoTickRound(ax) {
- var dtick = ax.dtick;
-
- ax._tickexponent = 0;
- if(!isNumeric(dtick) && typeof dtick !== 'string') {
- dtick = 1;
- }
-
- if(ax.type === 'category' || ax.type === 'multicategory') {
- ax._tickround = null;
- }
- if(ax.type === 'date') {
- // If tick0 is unusual, give tickround a bit more information
- // not necessarily *all* the information in tick0 though, if it's really odd
- // minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19
- // take off a leading minus (year < 0) and i (intercalary month) so length is consistent
- var tick0ms = ax.r2l(ax.tick0);
- var tick0str = ax.l2r(tick0ms).replace(/(^-|i)/g, '');
- var tick0len = tick0str.length;
-
- if(String(dtick).charAt(0) === 'M') {
- // any tick0 more specific than a year: alway show the full date
- if(tick0len > 10 || tick0str.substr(5) !== '01-01') ax._tickround = 'd';
- // show the month unless ticks are full multiples of a year
- else ax._tickround = (+(dtick.substr(1)) % 12 === 0) ? 'y' : 'm';
- }
- else if((dtick >= ONEDAY && tick0len <= 10) || (dtick >= ONEDAY * 15)) ax._tickround = 'd';
- else if((dtick >= ONEMIN && tick0len <= 16) || (dtick >= ONEHOUR)) ax._tickround = 'M';
- else if((dtick >= ONESEC && tick0len <= 19) || (dtick >= ONEMIN)) ax._tickround = 'S';
- else {
- // tickround is a number of digits of fractional seconds
- // of any two adjacent ticks, at least one will have the maximum fractional digits
- // of all possible ticks - so take the max. length of tick0 and the next one
- var tick1len = ax.l2r(tick0ms + dtick).replace(/^-/, '').length;
- ax._tickround = Math.max(tick0len, tick1len) - 20;
-
- // We shouldn't get here... but in case there's a situation I'm
- // not thinking of where tick0str and tick1str are identical or
- // something, fall back on maximum precision
- if(ax._tickround < 0) ax._tickround = 4;
- }
- }
- else if(isNumeric(dtick) || dtick.charAt(0) === 'L') {
- // linear or log (except D1, D2)
- var rng = ax.range.map(ax.r2d || Number);
- if(!isNumeric(dtick)) dtick = Number(dtick.substr(1));
- // 2 digits past largest digit of dtick
- ax._tickround = 2 - Math.floor(Math.log(dtick) / Math.LN10 + 0.01);
-
- var maxend = Math.max(Math.abs(rng[0]), Math.abs(rng[1]));
-
- var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01);
- if(Math.abs(rangeexp) > 3) {
- if(isSIFormat(ax.exponentformat) && !beyondSI(rangeexp)) {
- ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3);
- }
- else ax._tickexponent = rangeexp;
- }
- }
- // D1 or D2 (log)
- else ax._tickround = null;
- }
-
- // months and years don't have constant millisecond values
- // (but a year is always 12 months so we only need months)
- // log-scale ticks are also not consistently spaced, except
- // for pure powers of 10
- // numeric ticks always have constant differences, other datetime ticks
- // can all be calculated as constant number of milliseconds
- axes.tickIncrement = function(x, dtick, axrev, calendar) {
- var axSign = axrev ? -1 : 1;
-
- // includes linear, all dates smaller than month, and pure 10^n in log
- if(isNumeric(dtick)) return x + axSign * dtick;
-
- // everything else is a string, one character plus a number
- var tType = dtick.charAt(0);
- var dtSigned = axSign * Number(dtick.substr(1));
-
- // Dates: months (or years - see Lib.incrementMonth)
- if(tType === 'M') return Lib.incrementMonth(x, dtSigned, calendar);
-
- // Log scales: Linear, Digits
- else if(tType === 'L') return Math.log(Math.pow(10, x) + dtSigned) / Math.LN10;
-
- // log10 of 2,5,10, or all digits (logs just have to be
- // close enough to round)
- else if(tType === 'D') {
- var tickset = (dtick === 'D2') ? roundLog2 : roundLog1;
- var x2 = x + axSign * 0.01;
- var frac = Lib.roundUp(Lib.mod(x2, 1), tickset, axrev);
-
- return Math.floor(x2) +
- Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
- }
- else throw 'unrecognized dtick ' + String(dtick);
- };
-
- // calculate the first tick on an axis
- axes.tickFirst = function(ax) {
- var r2l = ax.r2l || Number;
- var rng = Lib.simpleMap(ax.range, r2l);
- var axrev = rng[1] < rng[0];
- var sRound = axrev ? Math.floor : Math.ceil;
- // add a tiny extra bit to make sure we get ticks
- // that may have been rounded out
- var r0 = rng[0] * 1.0001 - rng[1] * 0.0001;
- var dtick = ax.dtick;
- var tick0 = r2l(ax.tick0);
-
- if(isNumeric(dtick)) {
- var tmin = sRound((r0 - tick0) / dtick) * dtick + tick0;
-
- // make sure no ticks outside the category list
- if(ax.type === 'category' || ax.type === 'multicategory') {
- tmin = Lib.constrain(tmin, 0, ax._categories.length - 1);
- }
- return tmin;
- }
-
- var tType = dtick.charAt(0);
- var dtNum = Number(dtick.substr(1));
-
- // Dates: months (or years)
- if(tType === 'M') {
- var cnt = 0;
- var t0 = tick0;
- var t1, mult, newDTick;
-
- // This algorithm should work for *any* nonlinear (but close to linear!)
- // tick spacing. Limit to 10 iterations, for gregorian months it's normally <=3.
- while(cnt < 10) {
- t1 = axes.tickIncrement(t0, dtick, axrev, ax.calendar);
- if((t1 - r0) * (t0 - r0) <= 0) {
- // t1 and t0 are on opposite sides of r0! we've succeeded!
- if(axrev) return Math.min(t0, t1);
- return Math.max(t0, t1);
- }
- mult = (r0 - ((t0 + t1) / 2)) / (t1 - t0);
- newDTick = tType + ((Math.abs(Math.round(mult)) || 1) * dtNum);
- t0 = axes.tickIncrement(t0, newDTick, mult < 0 ? !axrev : axrev, ax.calendar);
- cnt++;
- }
- Lib.error('tickFirst did not converge', ax);
- return t0;
- }
-
- // Log scales: Linear, Digits
- else if(tType === 'L') {
- return Math.log(sRound(
- (Math.pow(10, r0) - tick0) / dtNum) * dtNum + tick0) / Math.LN10;
- }
- else if(tType === 'D') {
- var tickset = (dtick === 'D2') ? roundLog2 : roundLog1;
- var frac = Lib.roundUp(Lib.mod(r0, 1), tickset, axrev);
-
- return Math.floor(r0) +
- Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
- }
- else throw 'unrecognized dtick ' + String(dtick);
- };
-
- // draw the text for one tick.
- // px,py are the location on gd.paper
- // prefix is there so the x axis ticks can be dropped a line
- // ax is the axis layout, x is the tick value
- // hover is a (truthy) flag for whether to show numbers with a bit
- // more precision for hovertext
- axes.tickText = function(ax, x, hover) {
- var out = tickTextObj(ax, x);
- var arrayMode = ax.tickmode === 'array';
- var extraPrecision = hover || arrayMode;
- var axType = ax.type;
- // TODO multicategory, if we allow ticktext / tickvals
- var tickVal2l = axType === 'category' ? ax.d2l_noadd : ax.d2l;
- var i;
-
- if(arrayMode && Array.isArray(ax.ticktext)) {
- var rng = Lib.simpleMap(ax.range, ax.r2l);
- var minDiff = Math.abs(rng[1] - rng[0]) / 10000;
-
- for(i = 0; i < ax.ticktext.length; i++) {
- if(Math.abs(x - tickVal2l(ax.tickvals[i])) < minDiff) break;
- }
- if(i < ax.ticktext.length) {
- out.text = String(ax.ticktext[i]);
- return out;
- }
- }
-
- function isHidden(showAttr) {
- if(showAttr === undefined) return true;
- if(hover) return showAttr === 'none';
-
- var firstOrLast = {
- first: ax._tmin,
- last: ax._tmax
- }[showAttr];
-
- return showAttr !== 'all' && x !== firstOrLast;
- }
-
- var hideexp = hover ?
- 'never' :
- ax.exponentformat !== 'none' && isHidden(ax.showexponent) ? 'hide' : '';
-
- if(axType === 'date') formatDate(ax, out, hover, extraPrecision);
- else if(axType === 'log') formatLog(ax, out, hover, extraPrecision, hideexp);
- else if(axType === 'category') formatCategory(ax, out);
- else if(axType === 'multicategory') formatMultiCategory(ax, out, hover);
- else if(isAngular(ax)) formatAngle(ax, out, hover, extraPrecision, hideexp);
- else formatLinear(ax, out, hover, extraPrecision, hideexp);
-
- // add prefix and suffix
- if(ax.tickprefix && !isHidden(ax.showtickprefix)) out.text = ax.tickprefix + out.text;
- if(ax.ticksuffix && !isHidden(ax.showticksuffix)) out.text += ax.ticksuffix;
-
- // Setup ticks and grid lines boundaries
- // at 1/2 a 'category' to the left/bottom
- if(ax.tickson === 'boundaries' || ax.showdividers) {
- var inbounds = function(v) {
- var p = ax.l2p(v);
- return p >= 0 && p <= ax._length ? v : null;
- };
-
- out.xbnd = [
- inbounds(out.x - 0.5),
- inbounds(out.x + ax.dtick - 0.5)
- ];
- }
-
- return out;
- };
-
- /**
- * create text for a hover label on this axis, with special handling of
- * log axes (where negative values can't be displayed but can appear in hover text)
- *
- * @param {object} ax: the axis to format text for
- * @param {number} val: calcdata value to format
- * @param {Optional(number)} val2: a second value to display
- *
- * @returns {string} `val` formatted as a string appropriate to this axis, or
- * `val` and `val2` as a range (ie '<val> - <val2>') if `val2` is provided and
- * it's different from `val`.
- */
- axes.hoverLabelText = function(ax, val, val2) {
- if(val2 !== BADNUM && val2 !== val) {
- return axes.hoverLabelText(ax, val) + ' - ' + axes.hoverLabelText(ax, val2);
- }
-
- var logOffScale = (ax.type === 'log' && val <= 0);
- var tx = axes.tickText(ax, ax.c2l(logOffScale ? -val : val), 'hover').text;
-
- if(logOffScale) {
- return val === 0 ? '0' : MINUS_SIGN + tx;
- }
-
- // TODO: should we do something special if the axis calendar and
- // the data calendar are different? Somehow display both dates with
- // their system names? Right now it will just display in the axis calendar
- // but users could add the other one as text.
- return tx;
- };
-
- function tickTextObj(ax, x, text) {
- var tf = ax.tickfont || {};
-
- return {
- x: x,
- dx: 0,
- dy: 0,
- text: text || '',
- fontSize: tf.size,
- font: tf.family,
- fontColor: tf.color
- };
- }
-
- function formatDate(ax, out, hover, extraPrecision) {
- var tr = ax._tickround;
- var fmt = (hover && ax.hoverformat) || axes.getTickFormat(ax);
-
- if(extraPrecision) {
- // second or sub-second precision: extra always shows max digits.
- // for other fields, extra precision just adds one field.
- if(isNumeric(tr)) tr = 4;
- else tr = {y: 'm', m: 'd', d: 'M', M: 'S', S: 4}[tr];
- }
-
- var dateStr = Lib.formatDate(out.x, fmt, tr, ax._dateFormat, ax.calendar, ax._extraFormat);
- var headStr;
-
- var splitIndex = dateStr.indexOf('\n');
- if(splitIndex !== -1) {
- headStr = dateStr.substr(splitIndex + 1);
- dateStr = dateStr.substr(0, splitIndex);
- }
-
- if(extraPrecision) {
- // if extraPrecision led to trailing zeros, strip them off
- // actually, this can lead to removing even more zeros than
- // in the original rounding, but that's fine because in these
- // contexts uniformity is not so important (if there's even
- // anything to be uniform with!)
-
- // can we remove the whole time part?
- if(dateStr === '00:00:00' || dateStr === '00:00') {
- dateStr = headStr;
- headStr = '';
- }
- else if(dateStr.length === 8) {
- // strip off seconds if they're zero (zero fractional seconds
- // are already omitted)
- // but we never remove minutes and leave just hours
- dateStr = dateStr.replace(/:00$/, '');
- }
- }
-
- if(headStr) {
- if(hover) {
- // hover puts it all on one line, so headPart works best up front
- // except for year headPart: turn this into "Jan 1, 2000" etc.
- if(tr === 'd') dateStr += ', ' + headStr;
- else dateStr = headStr + (dateStr ? ', ' + dateStr : '');
- }
- else if(!ax._inCalcTicks || (headStr !== ax._prevDateHead)) {
- dateStr += '<br>' + headStr;
- ax._prevDateHead = headStr;
- }
- }
-
- out.text = dateStr;
- }
-
- function formatLog(ax, out, hover, extraPrecision, hideexp) {
- var dtick = ax.dtick;
- var x = out.x;
- var tickformat = ax.tickformat;
- var dtChar0 = typeof dtick === 'string' && dtick.charAt(0);
-
- if(hideexp === 'never') {
- // If this is a hover label, then we must *never* hide the exponent
- // for the sake of display, which could give the wrong value by
- // potentially many orders of magnitude. If hideexp was 'never', then
- // it's now succeeded by preventing the other condition from automating
- // this choice. Thus we can unset it so that the axis formatting takes
- // precedence.
- hideexp = '';
- }
-
- if(extraPrecision && (dtChar0 !== 'L')) {
- dtick = 'L3';
- dtChar0 = 'L';
- }
-
- if(tickformat || (dtChar0 === 'L')) {
- out.text = numFormat(Math.pow(10, x), ax, hideexp, extraPrecision);
- }
- else if(isNumeric(dtick) || ((dtChar0 === 'D') && (Lib.mod(x + 0.01, 1) < 0.1))) {
- var p = Math.round(x);
- var absP = Math.abs(p);
- var exponentFormat = ax.exponentformat;
- if(exponentFormat === 'power' || (isSIFormat(exponentFormat) && beyondSI(p))) {
- if(p === 0) out.text = 1;
- else if(p === 1) out.text = '10';
- else out.text = '10<sup>' + (p > 1 ? '' : MINUS_SIGN) + absP + '</sup>';
-
- out.fontSize *= 1.25;
- }
- else if((exponentFormat === 'e' || exponentFormat === 'E') && absP > 2) {
- out.text = '1' + exponentFormat + (p > 0 ? '+' : MINUS_SIGN) + absP;
- }
- else {
- out.text = numFormat(Math.pow(10, x), ax, '', 'fakehover');
- if(dtick === 'D1' && ax._id.charAt(0) === 'y') {
- out.dy -= out.fontSize / 6;
- }
- }
- }
- else if(dtChar0 === 'D') {
- out.text = String(Math.round(Math.pow(10, Lib.mod(x, 1))));
- out.fontSize *= 0.75;
- }
- else throw 'unrecognized dtick ' + String(dtick);
-
- // if 9's are printed on log scale, move the 10's away a bit
- if(ax.dtick === 'D1') {
- var firstChar = String(out.text).charAt(0);
- if(firstChar === '0' || firstChar === '1') {
- if(ax._id.charAt(0) === 'y') {
- out.dx -= out.fontSize / 4;
- }
- else {
- out.dy += out.fontSize / 2;
- out.dx += (ax.range[1] > ax.range[0] ? 1 : -1) *
- out.fontSize * (x < 0 ? 0.5 : 0.25);
- }
- }
- }
- }
-
- function formatCategory(ax, out) {
- var tt = ax._categories[Math.round(out.x)];
- if(tt === undefined) tt = '';
- out.text = String(tt);
- }
-
- function formatMultiCategory(ax, out, hover) {
- var v = Math.round(out.x);
- var cats = ax._categories[v] || [];
- var tt = cats[1] === undefined ? '' : String(cats[1]);
- var tt2 = cats[0] === undefined ? '' : String(cats[0]);
-
- if(hover) {
- // TODO is this what we want?
- out.text = tt2 + ' - ' + tt;
- } else {
- // setup for secondary labels
- out.text = tt;
- out.text2 = tt2;
- }
- }
-
- function formatLinear(ax, out, hover, extraPrecision, hideexp) {
- if(hideexp === 'never') {
- // If this is a hover label, then we must *never* hide the exponent
- // for the sake of display, which could give the wrong value by
- // potentially many orders of magnitude. If hideexp was 'never', then
- // it's now succeeded by preventing the other condition from automating
- // this choice. Thus we can unset it so that the axis formatting takes
- // precedence.
- hideexp = '';
- } else if(ax.showexponent === 'all' && Math.abs(out.x / ax.dtick) < 1e-6) {
- // don't add an exponent to zero if we're showing all exponents
- // so the only reason you'd show an exponent on zero is if it's the
- // ONLY tick to get an exponent (first or last)
- hideexp = 'hide';
- }
- out.text = numFormat(out.x, ax, hideexp, extraPrecision);
- }
-
- function formatAngle(ax, out, hover, extraPrecision, hideexp) {
- if(ax.thetaunit === 'radians' && !hover) {
- var num = out.x / 180;
-
- if(num === 0) {
- out.text = '0';
- } else {
- var frac = num2frac(num);
-
- if(frac[1] >= 100) {
- out.text = numFormat(Lib.deg2rad(out.x), ax, hideexp, extraPrecision);
- } else {
- var isNeg = out.x < 0;
-
- if(frac[1] === 1) {
- if(frac[0] === 1) out.text = 'π';
- else out.text = frac[0] + 'π';
- } else {
- out.text = [
- '<sup>', frac[0], '</sup>',
- '⁄',
- '<sub>', frac[1], '</sub>',
- 'π'
- ].join('');
- }
-
- if(isNeg) out.text = MINUS_SIGN + out.text;
- }
- }
- } else {
- out.text = numFormat(out.x, ax, hideexp, extraPrecision);
- }
- }
-
- // inspired by
- // https://github.com/yisibl/num2fraction/blob/master/index.js
- function num2frac(num) {
- function almostEq(a, b) {
- return Math.abs(a - b) <= 1e-6;
- }
-
- function findGCD(a, b) {
- return almostEq(b, 0) ? a : findGCD(b, a % b);
- }
-
- function findPrecision(n) {
- var e = 1;
- while(!almostEq(Math.round(n * e) / e, n)) {
- e *= 10;
- }
- return e;
- }
-
- var precision = findPrecision(num);
- var number = num * precision;
- var gcd = Math.abs(findGCD(number, precision));
-
- return [
- // numerator
- Math.round(number / gcd),
- // denominator
- Math.round(precision / gcd)
- ];
- }
-
- // format a number (tick value) according to the axis settings
- // new, more reliable procedure than d3.round or similar:
- // add half the rounding increment, then stringify and truncate
- // also automatically switch to sci. notation
- var SIPREFIXES = ['f', 'p', 'n', 'μ', 'm', '', 'k', 'M', 'G', 'T'];
-
- function isSIFormat(exponentFormat) {
- return exponentFormat === 'SI' || exponentFormat === 'B';
- }
-
- // are we beyond the range of common SI prefixes?
- // 10^-16 -> 1x10^-16
- // 10^-15 -> 1f
- // ...
- // 10^14 -> 100T
- // 10^15 -> 1x10^15
- // 10^16 -> 1x10^16
- function beyondSI(exponent) {
- return exponent > 14 || exponent < -15;
- }
-
- function numFormat(v, ax, fmtoverride, hover) {
- var isNeg = v < 0;
- // max number of digits past decimal point to show
- var tickRound = ax._tickround;
- var exponentFormat = fmtoverride || ax.exponentformat || 'B';
- var exponent = ax._tickexponent;
- var tickformat = axes.getTickFormat(ax);
- var separatethousands = ax.separatethousands;
-
- // special case for hover: set exponent just for this value, and
- // add a couple more digits of precision over tick labels
- if(hover) {
- // make a dummy axis obj to get the auto rounding and exponent
- var ah = {
- exponentformat: exponentFormat,
- dtick: ax.showexponent === 'none' ? ax.dtick :
- (isNumeric(v) ? Math.abs(v) || 1 : 1),
- // if not showing any exponents, don't change the exponent
- // from what we calculate
- range: ax.showexponent === 'none' ? ax.range.map(ax.r2d) : [0, v || 1]
- };
- autoTickRound(ah);
- tickRound = (Number(ah._tickround) || 0) + 4;
- exponent = ah._tickexponent;
- if(ax.hoverformat) tickformat = ax.hoverformat;
- }
-
- if(tickformat) return ax._numFormat(tickformat)(v).replace(/-/g, MINUS_SIGN);
-
- // 'epsilon' - rounding increment
- var e = Math.pow(10, -tickRound) / 2;
-
- // exponentFormat codes:
- // 'e' (1.2e+6, default)
- // 'E' (1.2E+6)
- // 'SI' (1.2M)
- // 'B' (same as SI except 10^9=B not G)
- // 'none' (1200000)
- // 'power' (1.2x10^6)
- // 'hide' (1.2, use 3rd argument=='hide' to eg
- // only show exponent on last tick)
- if(exponentFormat === 'none') exponent = 0;
-
- // take the sign out, put it back manually at the end
- // - makes cases easier
- v = Math.abs(v);
- if(v < e) {
- // 0 is just 0, but may get exponent if it's the last tick
- v = '0';
- isNeg = false;
- }
- else {
- v += e;
- // take out a common exponent, if any
- if(exponent) {
- v *= Math.pow(10, -exponent);
- tickRound += exponent;
- }
- // round the mantissa
- if(tickRound === 0) v = String(Math.floor(v));
- else if(tickRound < 0) {
- v = String(Math.round(v));
- v = v.substr(0, v.length + tickRound);
- for(var i = tickRound; i < 0; i++) v += '0';
- }
- else {
- v = String(v);
- var dp = v.indexOf('.') + 1;
- if(dp) v = v.substr(0, dp + tickRound).replace(/\.?0+$/, '');
- }
- // insert appropriate decimal point and thousands separator
- v = Lib.numSeparate(v, ax._separators, separatethousands);
- }
-
- // add exponent
- if(exponent && exponentFormat !== 'hide') {
- if(isSIFormat(exponentFormat) && beyondSI(exponent)) exponentFormat = 'power';
-
- var signedExponent;
- if(exponent < 0) signedExponent = MINUS_SIGN + -exponent;
- else if(exponentFormat !== 'power') signedExponent = '+' + exponent;
- else signedExponent = String(exponent);
-
- if(exponentFormat === 'e' || exponentFormat === 'E') {
- v += exponentFormat + signedExponent;
- }
- else if(exponentFormat === 'power') {
- v += '×10<sup>' + signedExponent + '</sup>';
- }
- else if(exponentFormat === 'B' && exponent === 9) {
- v += 'B';
- }
- else if(isSIFormat(exponentFormat)) {
- v += SIPREFIXES[exponent / 3 + 5];
- }
- }
-
- // put sign back in and return
- // replace standard minus character (which is technically a hyphen)
- // with a true minus sign
- if(isNeg) return MINUS_SIGN + v;
- return v;
- }
-
- axes.getTickFormat = function(ax) {
- var i;
-
- function convertToMs(dtick) {
- return typeof dtick !== 'string' ? dtick : Number(dtick.replace('M', '')) * ONEAVGMONTH;
- }
-
- function compareLogTicks(left, right) {
- var priority = ['L', 'D'];
- if(typeof left === typeof right) {
- if(typeof left === 'number') {
- return left - right;
- } else {
- var leftPriority = priority.indexOf(left.charAt(0));
- var rightPriority = priority.indexOf(right.charAt(0));
- if(leftPriority === rightPriority) {
- return Number(left.replace(/(L|D)/g, '')) - Number(right.replace(/(L|D)/g, ''));
- } else {
- return leftPriority - rightPriority;
- }
- }
- } else {
- return typeof left === 'number' ? 1 : -1;
- }
- }
-
- function isProperStop(dtick, range, convert) {
- var convertFn = convert || function(x) { return x;};
- var leftDtick = range[0];
- var rightDtick = range[1];
- return ((!leftDtick && typeof leftDtick !== 'number') || convertFn(leftDtick) <= convertFn(dtick)) &&
- ((!rightDtick && typeof rightDtick !== 'number') || convertFn(rightDtick) >= convertFn(dtick));
- }
-
- function isProperLogStop(dtick, range) {
- var isLeftDtickNull = range[0] === null;
- var isRightDtickNull = range[1] === null;
- var isDtickInRangeLeft = compareLogTicks(dtick, range[0]) >= 0;
- var isDtickInRangeRight = compareLogTicks(dtick, range[1]) <= 0;
- return (isLeftDtickNull || isDtickInRangeLeft) && (isRightDtickNull || isDtickInRangeRight);
- }
-
- var tickstop, stopi;
- if(ax.tickformatstops && ax.tickformatstops.length > 0) {
- switch(ax.type) {
- case 'date':
- case 'linear': {
- for(i = 0; i < ax.tickformatstops.length; i++) {
- stopi = ax.tickformatstops[i];
- if(stopi.enabled && isProperStop(ax.dtick, stopi.dtickrange, convertToMs)) {
- tickstop = stopi;
- break;
- }
- }
- break;
- }
- case 'log': {
- for(i = 0; i < ax.tickformatstops.length; i++) {
- stopi = ax.tickformatstops[i];
- if(stopi.enabled && isProperLogStop(ax.dtick, stopi.dtickrange)) {
- tickstop = stopi;
- break;
- }
- }
- break;
- }
- default:
- }
- }
- return tickstop ? tickstop.value : ax.tickformat;
- };
-
- // getSubplots - extract all subplot IDs we need
- // as an array of items like 'xy', 'x2y', 'x2y2'...
- // sorted by x (x,x2,x3...) then y
- // optionally restrict to only subplots containing axis object ax
- //
- // NOTE: this is currently only used OUTSIDE plotly.js (toolpanel, webapp)
- // ideally we get rid of it there (or just copy this there) and remove it here
- axes.getSubplots = function(gd, ax) {
- var subplotObj = gd._fullLayout._subplots;
- var allSubplots = subplotObj.cartesian.concat(subplotObj.gl2d || []);
-
- var out = ax ? axes.findSubplotsWithAxis(allSubplots, ax) : allSubplots;
-
- out.sort(function(a, b) {
- var aParts = a.substr(1).split('y');
- var bParts = b.substr(1).split('y');
-
- if(aParts[0] === bParts[0]) return +aParts[1] - +bParts[1];
- return +aParts[0] - +bParts[0];
- });
-
- return out;
- };
-
- // find all subplots with axis 'ax'
- // NOTE: this is only used in axes.getSubplots (only used outside plotly.js) and
- // gl2d/convert (where it restricts axis subplots to only those with gl2d)
- axes.findSubplotsWithAxis = function(subplots, ax) {
- var axMatch = new RegExp(
- (ax._id.charAt(0) === 'x') ? ('^' + ax._id + 'y') : (ax._id + '$')
- );
- var subplotsWithAx = [];
-
- for(var i = 0; i < subplots.length; i++) {
- var sp = subplots[i];
- if(axMatch.test(sp)) subplotsWithAx.push(sp);
- }
-
- return subplotsWithAx;
- };
-
- // makeClipPaths: prepare clipPaths for all single axes and all possible xy pairings
- axes.makeClipPaths = function(gd) {
- var fullLayout = gd._fullLayout;
-
- // for more info: https://github.com/plotly/plotly.js/issues/2595
- if(fullLayout._hasOnlyLargeSploms) return;
-
- var fullWidth = {_offset: 0, _length: fullLayout.width, _id: ''};
- var fullHeight = {_offset: 0, _length: fullLayout.height, _id: ''};
- var xaList = axes.list(gd, 'x', true);
- var yaList = axes.list(gd, 'y', true);
- var clipList = [];
- var i, j;
-
- for(i = 0; i < xaList.length; i++) {
- clipList.push({x: xaList[i], y: fullHeight});
- for(j = 0; j < yaList.length; j++) {
- if(i === 0) clipList.push({x: fullWidth, y: yaList[j]});
- clipList.push({x: xaList[i], y: yaList[j]});
- }
- }
-
- // selectors don't work right with camelCase tags,
- // have to use class instead
- // https://groups.google.com/forum/#!topic/d3-js/6EpAzQ2gU9I
- var axClips = fullLayout._clips.selectAll('.axesclip')
- .data(clipList, function(d) { return d.x._id + d.y._id; });
-
- axClips.enter().append('clipPath')
- .classed('axesclip', true)
- .attr('id', function(d) { return 'clip' + fullLayout._uid + d.x._id + d.y._id; })
- .append('rect');
-
- axClips.exit().remove();
-
- axClips.each(function(d) {
- d3.select(this).select('rect').attr({
- x: d.x._offset || 0,
- y: d.y._offset || 0,
- width: d.x._length || 1,
- height: d.y._length || 1
- });
- });
- };
-
- /**
- * Main multi-axis drawing routine!
- *
- * @param {DOM element} gd : graph div
- * @param {string or array of strings} arg : polymorphic argument
- * @param {object} opts:
- * - @param {boolean} skipTitle : optional flag to skip axis title draw/update
- *
- * Signature 1: Axes.draw(gd, 'redraw')
- * use this to clear and redraw all axes on graph
- *
- * Signature 2: Axes.draw(gd, '')
- * use this to draw all axes on graph w/o the selectAll().remove()
- * of the 'redraw' signature
- *
- * Signature 3: Axes.draw(gd, [axId, axId2, ...])
- * where the items are axis id string,
- * use this to update multiple axes in one call
- *
- * N.B draw updates:
- * - ax._r (stored range for use by zoom/pan)
- * - ax._rl (stored linearized range for use by zoom/pan)
- */
- axes.draw = function(gd, arg, opts) {
- var fullLayout = gd._fullLayout;
-
- if(arg === 'redraw') {
- fullLayout._paper.selectAll('g.subplot').each(function(d) {
- var id = d[0];
- var plotinfo = fullLayout._plots[id];
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick').remove();
- plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick').remove();
- plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick2').remove();
- plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick2').remove();
- plotinfo.xaxislayer.selectAll('.' + xa._id + 'divider').remove();
- plotinfo.yaxislayer.selectAll('.' + ya._id + 'divider').remove();
-
- if(plotinfo.gridlayer) plotinfo.gridlayer.selectAll('path').remove();
- if(plotinfo.zerolinelayer) plotinfo.zerolinelayer.selectAll('path').remove();
-
- fullLayout._infolayer.select('.g-' + xa._id + 'title').remove();
- fullLayout._infolayer.select('.g-' + ya._id + 'title').remove();
- });
- }
-
- var axList = (!arg || arg === 'redraw') ? axes.listIds(gd) : arg;
-
- return Lib.syncOrAsync(axList.map(function(axId) {
- return function() {
- if(!axId) return;
-
- var ax = axes.getFromId(gd, axId);
- var axDone = axes.drawOne(gd, ax, opts);
-
- ax._r = ax.range.slice();
- ax._rl = Lib.simpleMap(ax._r, ax.r2l);
-
- return axDone;
- };
- }));
- };
-
- /**
- * Draw one cartesian axis
- *
- * @param {DOM element} gd
- * @param {object} ax (full) axis object
- * @param {object} opts
- * - @param {boolean} skipTitle (set to true to skip axis title draw call)
- */
- axes.drawOne = function(gd, ax, opts) {
- opts = opts || {};
-
- var i, sp, plotinfo;
-
- ax.setScale();
-
- var fullLayout = gd._fullLayout;
- var axId = ax._id;
- var axLetter = axId.charAt(0);
- var counterLetter = axes.counterLetter(axId);
- var mainSubplot = ax._mainSubplot;
- var mainLinePosition = ax._mainLinePosition;
- var mainMirrorPosition = ax._mainMirrorPosition;
- var mainPlotinfo = fullLayout._plots[mainSubplot];
- var mainAxLayer = mainPlotinfo[axLetter + 'axislayer'];
- var subplotsWithAx = ax._subplotsWith;
-
- var vals = ax._vals = axes.calcTicks(ax);
-
- // Add a couple of axis properties that should cause us to recreate
- // elements. Used in d3 data function.
- var axInfo = [ax.mirror, mainLinePosition, mainMirrorPosition].join('_');
- for(i = 0; i < vals.length; i++) {
- vals[i].axInfo = axInfo;
- }
-
- if(!ax.visible) return;
-
- // stash selections to avoid DOM queries e.g.
- // - stash tickLabels selection, so that drawTitle can use it to scoot title
- ax._selections = {};
- // stash tick angle (including the computed 'auto' values) per tick-label class
- ax._tickAngles = {};
-
- var transFn = axes.makeTransFn(ax);
- var tickVals;
- // We remove zero lines, grid lines, and inside ticks if they're within 1px of the end
- // The key case here is removing zero lines when the axis bound is zero
- var valsClipped;
-
- if(ax.tickson === 'boundaries') {
- var boundaryVals = getBoundaryVals(ax, vals);
- valsClipped = axes.clipEnds(ax, boundaryVals);
- tickVals = ax.ticks === 'inside' ? valsClipped : boundaryVals;
- } else {
- valsClipped = axes.clipEnds(ax, vals);
- tickVals = ax.ticks === 'inside' ? valsClipped : vals;
- }
-
- var gridVals = ax._gridVals = valsClipped;
- var dividerVals = getDividerVals(ax, vals);
-
- if(!fullLayout._hasOnlyLargeSploms) {
- // keep track of which subplots (by main conteraxis) we've already
- // drawn grids for, so we don't overdraw overlaying subplots
- var finishedGrids = {};
-
- for(i = 0; i < subplotsWithAx.length; i++) {
- sp = subplotsWithAx[i];
- plotinfo = fullLayout._plots[sp];
-
- var counterAxis = plotinfo[counterLetter + 'axis'];
- var mainCounterID = counterAxis._mainAxis._id;
- if(finishedGrids[mainCounterID]) continue;
- finishedGrids[mainCounterID] = 1;
-
- var gridPath = axLetter === 'x' ?
- 'M0,' + counterAxis._offset + 'v' + counterAxis._length :
- 'M' + counterAxis._offset + ',0h' + counterAxis._length;
-
- axes.drawGrid(gd, ax, {
- vals: gridVals,
- counterAxis: counterAxis,
- layer: plotinfo.gridlayer.select('.' + axId),
- path: gridPath,
- transFn: transFn
- });
- axes.drawZeroLine(gd, ax, {
- counterAxis: counterAxis,
- layer: plotinfo.zerolinelayer,
- path: gridPath,
- transFn: transFn
- });
- }
- }
-
- var tickSigns = axes.getTickSigns(ax);
- var tickSubplots = [];
-
- if(ax.ticks) {
- var mainTickPath = axes.makeTickPath(ax, mainLinePosition, tickSigns[2]);
- var mirrorTickPath;
- var fullTickPath;
- if(ax._anchorAxis && ax.mirror && ax.mirror !== true) {
- mirrorTickPath = axes.makeTickPath(ax, mainMirrorPosition, tickSigns[3]);
- fullTickPath = mainTickPath + mirrorTickPath;
- } else {
- mirrorTickPath = '';
- fullTickPath = mainTickPath;
- }
-
- var tickPath;
- if(ax.showdividers && ax.ticks === 'outside' && ax.tickson === 'boundaries') {
- var dividerLookup = {};
- for(i = 0; i < dividerVals.length; i++) {
- dividerLookup[dividerVals[i].x] = 1;
- }
- tickPath = function(d) {
- return dividerLookup[d.x] ? mirrorTickPath : fullTickPath;
- };
- } else {
- tickPath = fullTickPath;
- }
-
- axes.drawTicks(gd, ax, {
- vals: tickVals,
- layer: mainAxLayer,
- path: tickPath,
- transFn: transFn
- });
-
- tickSubplots = Object.keys(ax._linepositions || {});
- }
-
- for(i = 0; i < tickSubplots.length; i++) {
- sp = tickSubplots[i];
- plotinfo = fullLayout._plots[sp];
- // [bottom or left, top or right], free and main are handled above
- var linepositions = ax._linepositions[sp] || [];
- var spTickPath = axes.makeTickPath(ax, linepositions[0], tickSigns[0]) +
- axes.makeTickPath(ax, linepositions[1], tickSigns[1]);
-
- axes.drawTicks(gd, ax, {
- vals: tickVals,
- layer: plotinfo[axLetter + 'axislayer'],
- path: spTickPath,
- transFn: transFn
- });
- }
-
- var seq = [];
-
- // tick labels - for now just the main labels.
- // TODO: mirror labels, esp for subplots
-
- seq.push(function() {
- var labelFns = axes.makeLabelFns(ax, mainLinePosition);
- return axes.drawLabels(gd, ax, {
- vals: vals,
- layer: mainAxLayer,
- transFn: transFn,
- labelXFn: labelFns.labelXFn,
- labelYFn: labelFns.labelYFn,
- labelAnchorFn: labelFns.labelAnchorFn,
- });
- });
-
- if(ax.type === 'multicategory') {
- var labelLength = 0;
- var pad = {x: 2, y: 10}[axLetter];
- var sgn = tickSigns[2] * (ax.ticks === 'inside' ? -1 : 1);
-
- seq.push(function() {
- labelLength += getLabelLevelSpan(ax, axId + 'tick') + pad;
- labelLength += ax._tickAngles[axId + 'tick'] ? ax.tickfont.size * LINE_SPACING : 0;
- var secondaryPosition = mainLinePosition + labelLength * sgn;
- var secondaryLabelFns = axes.makeLabelFns(ax, secondaryPosition);
-
- return axes.drawLabels(gd, ax, {
- vals: getSecondaryLabelVals(ax, vals),
- layer: mainAxLayer,
- cls: axId + 'tick2',
- repositionOnUpdate: true,
- secondary: true,
- transFn: transFn,
- labelXFn: secondaryLabelFns.labelXFn,
- labelYFn: secondaryLabelFns.labelYFn,
- labelAnchorFn: secondaryLabelFns.labelAnchorFn,
- });
- });
-
- seq.push(function() {
- labelLength += getLabelLevelSpan(ax, axId + 'tick2');
- ax._labelLength = labelLength;
-
- return drawDividers(gd, ax, {
- vals: dividerVals,
- layer: mainAxLayer,
- path: axes.makeTickPath(ax, mainLinePosition, sgn, labelLength),
- transFn: transFn
- });
- });
- }
-
- function extendRange(range, newRange) {
- range[0] = Math.min(range[0], newRange[0]);
- range[1] = Math.max(range[1], newRange[1]);
- }
-
- function calcBoundingBox() {
- if(ax.showticklabels) {
- var gdBB = gd.getBoundingClientRect();
- var bBox = mainAxLayer.node().getBoundingClientRect();
-
- /*
- * the way we're going to use this, the positioning that matters
- * is relative to the origin of gd. This is important particularly
- * if gd is scrollable, and may have been scrolled between the time
- * we calculate this and the time we use it
- */
-
- ax._boundingBox = {
- width: bBox.width,
- height: bBox.height,
- left: bBox.left - gdBB.left,
- right: bBox.right - gdBB.left,
- top: bBox.top - gdBB.top,
- bottom: bBox.bottom - gdBB.top
- };
- } else {
- var gs = fullLayout._size;
- var pos;
-
- // set dummy bbox for ticklabel-less axes
-
- if(axLetter === 'x') {
- pos = ax.anchor === 'free' ?
- gs.t + gs.h * (1 - ax.position) :
- gs.t + gs.h * (1 - ax._anchorAxis.domain[{bottom: 0, top: 1}[ax.side]]);
-
- ax._boundingBox = {
- top: pos,
- bottom: pos,
- left: ax._offset,
- right: ax._offset + ax._length,
- width: ax._length,
- height: 0
- };
- } else {
- pos = ax.anchor === 'free' ?
- gs.l + gs.w * ax.position :
- gs.l + gs.w * ax._anchorAxis.domain[{left: 0, right: 1}[ax.side]];
-
- ax._boundingBox = {
- left: pos,
- right: pos,
- bottom: ax._offset + ax._length,
- top: ax._offset,
- height: ax._length,
- width: 0
- };
- }
- }
-
- /*
- * for spikelines: what's the full domain of positions in the
- * opposite direction that are associated with this axis?
- * This means any axes that we make a subplot with, plus the
- * position of the axis itself if it's free.
- */
- if(subplotsWithAx) {
- var fullRange = ax._counterSpan = [Infinity, -Infinity];
-
- for(var i = 0; i < subplotsWithAx.length; i++) {
- var plotinfo = fullLayout._plots[subplotsWithAx[i]];
- var counterAxis = plotinfo[(axLetter === 'x') ? 'yaxis' : 'xaxis'];
-
- extendRange(fullRange, [
- counterAxis._offset,
- counterAxis._offset + counterAxis._length
- ]);
- }
-
- if(ax.anchor === 'free') {
- extendRange(fullRange, (axLetter === 'x') ?
- [ax._boundingBox.bottom, ax._boundingBox.top] :
- [ax._boundingBox.right, ax._boundingBox.left]);
- }
- }
- }
-
- var hasRangeSlider = Registry.getComponentMethod('rangeslider', 'isVisible')(ax);
-
- function doAutoMargins() {
- var s = ax.side.charAt(0);
- var push;
- var rangeSliderPush;
-
- if(hasRangeSlider) {
- rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax);
- }
- Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush);
-
- if(ax.automargin && (!hasRangeSlider || s !== 'b')) {
- push = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0};
-
- var bbox = ax._boundingBox;
- var counterAx = mainPlotinfo[counterLetter + 'axis'];
- var anchorAxDomainIndex;
- var offset;
-
- switch(axLetter + s) {
- case 'xb':
- anchorAxDomainIndex = 0;
- offset = bbox.top - counterAx._length - counterAx._offset;
- push[s] = bbox.height;
- break;
- case 'xt':
- anchorAxDomainIndex = 1;
- offset = counterAx._offset - bbox.bottom;
- push[s] = bbox.height;
- break;
- case 'yl':
- anchorAxDomainIndex = 0;
- offset = counterAx._offset - bbox.right;
- push[s] = bbox.width;
- break;
- case 'yr':
- anchorAxDomainIndex = 1;
- offset = bbox.left - counterAx._length - counterAx._offset;
- push[s] = bbox.width;
- break;
- }
-
- push[counterLetter] = ax.anchor === 'free' ?
- ax.position :
- ax._anchorAxis.domain[anchorAxDomainIndex];
-
- if(push[s] > 0) {
- push[s] += offset;
- }
- if(ax.title.text !== fullLayout._dfltTitle[axLetter]) {
- push[s] += ax.title.font.size;
- }
- }
-
- Plots.autoMargin(gd, axAutoMarginID(ax), push);
- }
-
- seq.push(calcBoundingBox, doAutoMargins);
-
- if(!opts.skipTitle &&
- !(hasRangeSlider && ax._boundingBox && ax.side === 'bottom')
- ) {
- seq.push(function() { return drawTitle(gd, ax); });
- }
-
- return Lib.syncOrAsync(seq);
- };
-
- function getBoundaryVals(ax, vals) {
- var out = [];
- var i;
-
- // boundaryVals are never used for labels;
- // no need to worry about the other tickTextObj keys
- var _push = function(d, bndIndex) {
- var xb = d.xbnd[bndIndex];
- if(xb !== null) {
- out.push(Lib.extendFlat({}, d, {x: xb}));
- }
- };
-
- if(vals.length) {
- for(i = 0; i < vals.length; i++) {
- _push(vals[i], 0);
- }
- _push(vals[i - 1], 1);
- }
-
- return out;
- }
-
- function getSecondaryLabelVals(ax, vals) {
- var out = [];
- var lookup = {};
-
- for(var i = 0; i < vals.length; i++) {
- var d = vals[i];
- if(lookup[d.text2]) {
- lookup[d.text2].push(d.x);
- } else {
- lookup[d.text2] = [d.x];
- }
- }
-
- for(var k in lookup) {
- out.push(tickTextObj(ax, Lib.interp(lookup[k], 0.5), k));
- }
-
- return out;
- }
-
- function getDividerVals(ax, vals) {
- var out = [];
- var i, current;
-
- // never used for labels;
- // no need to worry about the other tickTextObj keys
- var _push = function(d, bndIndex) {
- var xb = d.xbnd[bndIndex];
- if(xb !== null) {
- out.push(Lib.extendFlat({}, d, {x: xb}));
- }
- };
-
- if(ax.showdividers && vals.length) {
- for(i = 0; i < vals.length; i++) {
- var d = vals[i];
- if(d.text2 !== current) {
- _push(d, 0);
- }
- current = d.text2;
- }
- _push(vals[i - 1], 1);
- }
-
- return out;
- }
-
- function getLabelLevelSpan(ax, cls) {
- var axLetter = ax._id.charAt(0);
- var angle = ax._tickAngles[cls] || 0;
- var rad = Lib.deg2rad(angle);
- var sinA = Math.sin(rad);
- var cosA = Math.cos(rad);
- var maxX = 0;
- var maxY = 0;
-
- // N.B. Drawing.bBox does not take into account rotate transforms
-
- ax._selections[cls].each(function() {
- var thisLabel = selectTickLabel(this);
- var bb = Drawing.bBox(thisLabel.node());
- var w = bb.width;
- var h = bb.height;
- maxX = Math.max(maxX, cosA * w, sinA * h);
- maxY = Math.max(maxY, sinA * w, cosA * h);
- });
-
- return {x: maxY, y: maxX}[axLetter];
- }
-
- /**
- * Which direction do the 'ax.side' values, and free ticks go?
- *
- * @param {object} ax (full) axis object
- * - {string} _id (starting with 'x' or 'y')
- * - {string} side
- * - {string} ticks
- * @return {array} all entries are either -1 or 1
- * - [0]: sign for top/right ticks (i.e. negative SVG direction)
- * - [1]: sign for bottom/left ticks (i.e. positive SVG direction)
- * - [2]: sign for ticks corresponding to 'ax.side'
- * - [3]: sign for ticks mirroring 'ax.side'
- */
- axes.getTickSigns = function(ax) {
- var axLetter = ax._id.charAt(0);
- var sideOpposite = {x: 'top', y: 'right'}[axLetter];
- var main = ax.side === sideOpposite ? 1 : -1;
- var out = [-1, 1, main, -main];
- // then we flip if outside XOR y axis
- if((ax.ticks !== 'inside') === (axLetter === 'x')) {
- out = out.map(function(v) { return -v; });
- }
- return out;
- };
-
- /**
- * Make axis translate transform function
- *
- * @param {object} ax (full) axis object
- * - {string} _id
- * - {number} _offset
- * - {fn} l2p
- * @return {fn} function of calcTicks items
- */
- axes.makeTransFn = function(ax) {
- var axLetter = ax._id.charAt(0);
- var offset = ax._offset;
- return axLetter === 'x' ?
- function(d) { return 'translate(' + (offset + ax.l2p(d.x)) + ',0)'; } :
- function(d) { return 'translate(0,' + (offset + ax.l2p(d.x)) + ')'; };
- };
-
- /**
- * Make axis tick path string
- *
- * @param {object} ax (full) axis object
- * - {string} _id
- * - {number} ticklen
- * - {number} linewidth
- * @param {number} shift along direction of ticklen
- * @param {1 or -1} sng tick sign
- * @param {number (optional)} len tick length
- * @return {string}
- */
- axes.makeTickPath = function(ax, shift, sgn, len) {
- len = len !== undefined ? len : ax.ticklen;
-
- var axLetter = ax._id.charAt(0);
- var pad = (ax.linewidth || 1) / 2;
-
- return axLetter === 'x' ?
- 'M0,' + (shift + pad * sgn) + 'v' + (len * sgn) :
- 'M' + (shift + pad * sgn) + ',0h' + (len * sgn);
- };
-
- /**
- * Make axis tick label x, y and anchor functions
- *
- * @param {object} ax (full) axis object
- * - {string} _id
- * - {string} ticks
- * - {number} ticklen
- * - {string} side
- * - {number} linewidth
- * - {number} tickfont.size
- * - {boolean} showline
- * @param {number} shift
- * @param {number} angle [in degrees] ...
- * @return {object}
- * - {fn} labelXFn
- * - {fn} labelYFn
- * - {fn} labelAnchorFn
- * - {number} labelStandoff
- * - {number} labelShift
- */
- axes.makeLabelFns = function(ax, shift, angle) {
- var axLetter = ax._id.charAt(0);
- var pad = (ax.linewidth || 1) / 2;
- var ticksOnOutsideLabels = ax.tickson !== 'boundaries' && ax.ticks === 'outside';
-
- var labelStandoff = ticksOnOutsideLabels ? ax.ticklen : 0;
- var labelShift = 0;
-
- if(angle && ax.ticks === 'outside') {
- var rad = Lib.deg2rad(angle);
- labelStandoff = ax.ticklen * Math.cos(rad) + 1;
- labelShift = ax.ticklen * Math.sin(rad);
- }
-
- if(ax.showticklabels && (ticksOnOutsideLabels || ax.showline)) {
- labelStandoff += 0.2 * ax.tickfont.size;
- }
-
- // Used in polar angular label x/y functions
- // TODO generalize makeLabelFns so that it just work for angular axes
- var out = {
- labelStandoff: labelStandoff,
- labelShift: labelShift
- };
-
- var x0, y0, ff, flipIt;
- if(axLetter === 'x') {
- flipIt = ax.side === 'bottom' ? 1 : -1;
- x0 = labelShift * flipIt;
- y0 = shift + (labelStandoff + pad) * flipIt;
- ff = ax.side === 'bottom' ? 1 : -0.2;
-
- out.labelXFn = function(d) { return d.dx + x0; };
- out.labelYFn = function(d) { return d.dy + y0 + d.fontSize * ff; };
- out.labelAnchorFn = function(a) {
- if(!isNumeric(a) || a === 0 || a === 180) {
- return 'middle';
- }
- return (a * flipIt < 0) ? 'end' : 'start';
- };
- } else if(axLetter === 'y') {
- flipIt = ax.side === 'right' ? 1 : -1;
- x0 = labelStandoff + pad;
- y0 = -labelShift * flipIt;
- ff = Math.abs(ax.tickangle) === 90 ? 0.5 : 0;
-
- out.labelXFn = function(d) { return d.dx + shift + (x0 + d.fontSize * ff) * flipIt; };
- out.labelYFn = function(d) { return d.dy + y0 + d.fontSize * MID_SHIFT; };
- out.labelAnchorFn = function(a) {
- if(isNumeric(a) && Math.abs(a) === 90) {
- return 'middle';
- }
- return ax.side === 'right' ? 'start' : 'end';
- };
- }
-
- return out;
- };
-
- function tickDataFn(d) {
- return [d.text, d.x, d.axInfo, d.font, d.fontSize, d.fontColor].join('_');
- }
-
- /**
- * Draw axis ticks
- *
- * @param {DOM element} gd
- * @param {object} ax (full) axis object
- * - {string} _id
- * - {string} ticks
- * - {number} linewidth
- * - {string} tickcolor
- * @param {object} opts
- * - {array of object} vals (calcTicks output-like)
- * - {d3 selection} layer
- * - {string or fn} path
- * - {fn} transFn
- * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
- */
- axes.drawTicks = function(gd, ax, opts) {
- opts = opts || {};
-
- var cls = ax._id + 'tick';
-
- var ticks = opts.layer.selectAll('path.' + cls)
- .data(ax.ticks ? opts.vals : [], tickDataFn);
-
- ticks.exit().remove();
-
- ticks.enter().append('path')
- .classed(cls, 1)
- .classed('ticks', 1)
- .classed('crisp', opts.crisp !== false)
- .call(Color.stroke, ax.tickcolor)
- .style('stroke-width', Drawing.crispRound(gd, ax.tickwidth, 1) + 'px')
- .attr('d', opts.path);
-
- ticks.attr('transform', opts.transFn);
- };
-
- /**
- * Draw axis grid
- *
- * @param {DOM element} gd
- * @param {object} ax (full) axis object
- * - {string} _id
- * - {boolean} showgrid
- * - {string} gridcolor
- * - {string} gridwidth
- * - {boolean} zeroline
- * - {string} type
- * - {string} dtick
- * @param {object} opts
- * - {array of object} vals (calcTicks output-like)
- * - {d3 selection} layer
- * - {object} counterAxis (full axis object corresponding to counter axis)
- * optional - only required if this axis supports zero lines
- * - {string or fn} path
- * - {fn} transFn
- * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
- */
- axes.drawGrid = function(gd, ax, opts) {
- opts = opts || {};
-
- var cls = ax._id + 'grid';
- var vals = opts.vals;
- var counterAx = opts.counterAxis;
- if(ax.showgrid === false) {
- vals = [];
- }
- else if(counterAx && axes.shouldShowZeroLine(gd, ax, counterAx)) {
- var isArrayMode = ax.tickmode === 'array';
- for(var i = 0; i < vals.length; i++) {
- var xi = vals[i].x;
- if(isArrayMode ? !xi : (Math.abs(xi) < ax.dtick / 100)) {
- vals = vals.slice(0, i).concat(vals.slice(i + 1));
- // In array mode you can in principle have multiple
- // ticks at 0, so test them all. Otherwise once we found
- // one we can stop.
- if(isArrayMode) i--;
- else break;
- }
- }
- }
-
- var grid = opts.layer.selectAll('path.' + cls)
- .data(vals, tickDataFn);
-
- grid.exit().remove();
-
- grid.enter().append('path')
- .classed(cls, 1)
- .classed('crisp', opts.crisp !== false);
-
- ax._gw = Drawing.crispRound(gd, ax.gridwidth, 1);
-
- grid.attr('transform', opts.transFn)
- .attr('d', opts.path)
- .call(Color.stroke, ax.gridcolor || '#ddd')
- .style('stroke-width', ax._gw + 'px');
-
- if(typeof opts.path === 'function') grid.attr('d', opts.path);
- };
-
- /**
- * Draw axis zero-line
- *
- * @param {DOM element} gd
- * @param {object} ax (full) axis object
- * - {string} _id
- * - {boolean} zeroline
- * - {number} zerolinewidth
- * - {string} zerolinecolor
- * - {number (optional)} _gridWidthCrispRound
- * @param {object} opts
- * - {d3 selection} layer
- * - {object} counterAxis (full axis object corresponding to counter axis)
- * - {string or fn} path
- * - {fn} transFn
- * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
- */
- axes.drawZeroLine = function(gd, ax, opts) {
- opts = opts || opts;
-
- var cls = ax._id + 'zl';
- var show = axes.shouldShowZeroLine(gd, ax, opts.counterAxis);
-
- var zl = opts.layer.selectAll('path.' + cls)
- .data(show ? [{x: 0, id: ax._id}] : []);
-
- zl.exit().remove();
-
- zl.enter().append('path')
- .classed(cls, 1)
- .classed('zl', 1)
- .classed('crisp', opts.crisp !== false)
- .each(function() {
- // use the fact that only one element can enter to trigger a sort.
- // If several zerolines enter at the same time we will sort once per,
- // but generally this should be a minimal overhead.
- opts.layer.selectAll('path').sort(function(da, db) {
- return axisIds.idSort(da.id, db.id);
- });
- });
-
- zl.attr('transform', opts.transFn)
- .attr('d', opts.path)
- .call(Color.stroke, ax.zerolinecolor || Color.defaultLine)
- .style('stroke-width', Drawing.crispRound(gd, ax.zerolinewidth, ax._gw || 1) + 'px');
- };
-
- /**
- * Draw axis tick labels
- *
- * @param {DOM element} gd
- * @param {object} ax (full) axis object
- * - {string} _id
- * - {boolean} showticklabels
- * - {number} tickangle
- * - {object (optional)} _selections
- * - {object} (optional)} _tickAngles
- * @param {object} opts
- * - {array of object} vals (calcTicks output-like)
- * - {d3 selection} layer
- * - {string (optional)} cls (node className)
- * - {boolean} repositionOnUpdate (set to true to reposition update selection)
- * - {boolean} secondary
- * - {fn} transFn
- * - {fn} labelXFn
- * - {fn} labelYFn
- * - {fn} labelAnchorFn
- */
- axes.drawLabels = function(gd, ax, opts) {
- opts = opts || {};
-
- var axId = ax._id;
- var axLetter = axId.charAt(0);
- var cls = opts.cls || axId + 'tick';
- var vals = opts.vals;
- var labelXFn = opts.labelXFn;
- var labelYFn = opts.labelYFn;
- var labelAnchorFn = opts.labelAnchorFn;
- var tickAngle = opts.secondary ? 0 : ax.tickangle;
- var lastAngle = (ax._tickAngles || {})[cls];
-
- var tickLabels = opts.layer.selectAll('g.' + cls)
- .data(ax.showticklabels ? vals : [], tickDataFn);
-
- var labelsReady = [];
-
- tickLabels.enter().append('g')
- .classed(cls, 1)
- .append('text')
- // only so tex has predictable alignment that we can
- // alter later
- .attr('text-anchor', 'middle')
- .each(function(d) {
- var thisLabel = d3.select(this);
- var newPromise = gd._promises.length;
-
- thisLabel
- .call(svgTextUtils.positionText, labelXFn(d), labelYFn(d))
- .call(Drawing.font, d.font, d.fontSize, d.fontColor)
- .text(d.text)
- .call(svgTextUtils.convertToTspans, gd);
-
- if(gd._promises[newPromise]) {
- // if we have an async label, we'll deal with that
- // all here so take it out of gd._promises and
- // instead position the label and promise this in
- // labelsReady
- labelsReady.push(gd._promises.pop().then(function() {
- positionLabels(thisLabel, tickAngle);
- }));
- } else {
- // sync label: just position it now.
- positionLabels(thisLabel, tickAngle);
- }
- });
-
- tickLabels.exit().remove();
-
- if(opts.repositionOnUpdate) {
- tickLabels.each(function(d) {
- d3.select(this).select('text')
- .call(svgTextUtils.positionText, labelXFn(d), labelYFn(d));
- });
- }
-
- // How much to shift a multi-line label to center it vertically.
- function getAnchorHeight(lineCount, lineHeight, angle) {
- var h = (lineCount - 1) * lineHeight;
- if(axLetter === 'x') {
- if(angle < -60 || 60 < angle) {
- return -0.5 * h;
- } else if(ax.side === 'top') {
- return -h;
- }
- } else {
- angle *= ax.side === 'left' ? 1 : -1;
- if(angle < -30) {
- return -h;
- } else if(angle < 30) {
- return -0.5 * h;
- }
- }
- return 0;
- }
-
- function positionLabels(s, angle) {
- s.each(function(d) {
- var thisLabel = d3.select(this);
- var mathjaxGroup = thisLabel.select('.text-math-group');
- var anchor = labelAnchorFn(angle, d);
-
- var transform = opts.transFn.call(thisLabel.node(), d) +
- ((isNumeric(angle) && +angle !== 0) ?
- (' rotate(' + angle + ',' + labelXFn(d) + ',' +
- (labelYFn(d) - d.fontSize / 2) + ')') :
- '');
-
- var anchorHeight = getAnchorHeight(
- svgTextUtils.lineCount(thisLabel),
- LINE_SPACING * d.fontSize,
- isNumeric(angle) ? +angle : 0
- );
-
- if(anchorHeight) {
- transform += ' translate(0, ' + anchorHeight + ')';
- }
-
- if(mathjaxGroup.empty()) {
- thisLabel.select('text').attr({
- transform: transform,
- 'text-anchor': anchor
- });
- } else {
- var mjWidth = Drawing.bBox(mathjaxGroup.node()).width;
- var mjShift = mjWidth * {end: -0.5, start: 0.5}[anchor];
- mathjaxGroup.attr('transform', transform + (mjShift ? 'translate(' + mjShift + ',0)' : ''));
- }
- });
- }
-
- // make sure all labels are correctly positioned at their base angle
- // the positionLabels call above is only for newly drawn labels.
- // do this without waiting, using the last calculated angle to
- // minimize flicker, then do it again when we know all labels are
- // there, putting back the prescribed angle to check for overlaps.
- positionLabels(tickLabels, lastAngle || tickAngle);
-
- function allLabelsReady() {
- return labelsReady.length && Promise.all(labelsReady);
- }
-
- function fixLabelOverlaps() {
- positionLabels(tickLabels, tickAngle);
-
- var autoangle = null;
-
- // check for auto-angling if x labels overlap
- // don't auto-angle at all for log axes with
- // base and digit format
- if(vals.length && axLetter === 'x' && !isNumeric(tickAngle) &&
- (ax.type !== 'log' || String(ax.dtick).charAt(0) !== 'D')
- ) {
- autoangle = 0;
-
- var maxFontSize = 0;
- var lbbArray = [];
- var i;
-
- tickLabels.each(function(d) {
- maxFontSize = Math.max(maxFontSize, d.fontSize);
-
- var x = ax.l2p(d.x);
- var thisLabel = selectTickLabel(this);
- var bb = Drawing.bBox(thisLabel.node());
-
- lbbArray.push({
- // ignore about y, just deal with x overlaps
- top: 0,
- bottom: 10,
- height: 10,
- left: x - bb.width / 2,
- // impose a 2px gap
- right: x + bb.width / 2 + 2,
- width: bb.width + 2
- });
- });
-
- if((ax.tickson === 'boundaries' || ax.showdividers) && !opts.secondary) {
- var gap = 2;
- if(ax.ticks) gap += ax.tickwidth / 2;
-
- // TODO should secondary labels also fall into this fix-overlap regime?
-
- for(i = 0; i < lbbArray.length; i++) {
- var xbnd = vals[i].xbnd;
- var lbb = lbbArray[i];
- if(
- (xbnd[0] !== null && (lbb.left - ax.l2p(xbnd[0])) < gap) ||
- (xbnd[1] !== null && (ax.l2p(xbnd[1]) - lbb.right) < gap)
- ) {
- autoangle = 90;
- break;
- }
- }
- } else {
- var vLen = vals.length;
- var tickSpacing = Math.abs((vals[vLen - 1].x - vals[0].x) * ax._m) / (vLen - 1);
- var rotate90 = (tickSpacing < maxFontSize * 2.5) || ax.type === 'multicategory';
-
- // any overlap at all - set 30 degrees or 90 degrees
- for(i = 0; i < lbbArray.length - 1; i++) {
- if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1])) {
- autoangle = rotate90 ? 90 : 30;
- break;
- }
- }
- }
-
- if(autoangle) {
- positionLabels(tickLabels, autoangle);
- }
- }
-
- if(ax._tickAngles) {
- ax._tickAngles[cls] = autoangle === null ?
- (isNumeric(tickAngle) ? tickAngle : 0) :
- autoangle;
- }
- }
-
- if(ax._selections) {
- ax._selections[cls] = tickLabels;
- }
-
- var done = Lib.syncOrAsync([allLabelsReady, fixLabelOverlaps]);
- if(done && done.then) gd._promises.push(done);
- return done;
- };
-
- /**
- * Draw axis dividers
- *
- * @param {DOM element} gd
- * @param {object} ax (full) axis object
- * - {string} _id
- * - {string} showdividers
- * - {number} dividerwidth
- * - {string} dividercolor
- * @param {object} opts
- * - {array of object} vals (calcTicks output-like)
- * - {d3 selection} layer
- * - {fn} path
- * - {fn} transFn
- */
- function drawDividers(gd, ax, opts) {
- var cls = ax._id + 'divider';
- var vals = opts.vals;
-
- var dividers = opts.layer.selectAll('path.' + cls)
- .data(vals, tickDataFn);
-
- dividers.exit().remove();
-
- dividers.enter().insert('path', ':first-child')
- .classed(cls, 1)
- .classed('crisp', 1)
- .call(Color.stroke, ax.dividercolor)
- .style('stroke-width', Drawing.crispRound(gd, ax.dividerwidth, 1) + 'px');
-
- dividers
- .attr('transform', opts.transFn)
- .attr('d', opts.path);
- }
-
- function drawTitle(gd, ax) {
- var fullLayout = gd._fullLayout;
- var axId = ax._id;
- var axLetter = axId.charAt(0);
- var gs = fullLayout._size;
- var fontSize = ax.title.font.size;
-
- var titleStandoff;
- if(ax.type === 'multicategory') {
- titleStandoff = ax._labelLength;
- } else {
- var offsetBase = 1.5;
- titleStandoff = 10 + fontSize * offsetBase + (ax.linewidth ? ax.linewidth - 1 : 0);
- }
-
- var transform, counterAxis, x, y;
-
- if(axLetter === 'x') {
- counterAxis = (ax.anchor === 'free') ?
- {_offset: gs.t + (1 - (ax.position || 0)) * gs.h, _length: 0} :
- axisIds.getFromId(gd, ax.anchor);
-
- x = ax._offset + ax._length / 2;
-
- if(ax.side === 'top') {
- y = -titleStandoff - fontSize * (ax.showticklabels ? 1 : 0);
- } else {
- y = counterAxis._length + titleStandoff +
- fontSize * (ax.showticklabels ? 1.5 : 0.5);
- }
- y += counterAxis._offset;
- } else {
- counterAxis = (ax.anchor === 'free') ?
- {_offset: gs.l + (ax.position || 0) * gs.w, _length: 0} :
- axisIds.getFromId(gd, ax.anchor);
-
- y = ax._offset + ax._length / 2;
-
- if(ax.side === 'right') {
- x = counterAxis._length + titleStandoff +
- fontSize * (ax.showticklabels ? 1 : 0.5);
- } else {
- x = -titleStandoff - fontSize * (ax.showticklabels ? 0.5 : 0);
- }
- x += counterAxis._offset;
-
- transform = {rotate: '-90', offset: 0};
- }
-
- var avoid;
-
- if(ax.type !== 'multicategory') {
- var tickLabels = ax._selections[ax._id + 'tick'];
-
- avoid = {
- selection: tickLabels,
- side: ax.side
- };
-
- if(tickLabels && tickLabels.node() && tickLabels.node().parentNode) {
- var translation = Drawing.getTranslate(tickLabels.node().parentNode);
- avoid.offsetLeft = translation.x;
- avoid.offsetTop = translation.y;
- }
- }
-
- return Titles.draw(gd, axId + 'title', {
- propContainer: ax,
- propName: ax._name + '.title.text',
- placeholder: fullLayout._dfltTitle[axLetter],
- avoid: avoid,
- transform: transform,
- attributes: {x: x, y: y, 'text-anchor': 'middle'}
- });
- }
-
- axes.shouldShowZeroLine = function(gd, ax, counterAxis) {
- var rng = Lib.simpleMap(ax.range, ax.r2l);
- return (
- (rng[0] * rng[1] <= 0) &&
- ax.zeroline &&
- (ax.type === 'linear' || ax.type === '-') &&
- ax._gridVals.length &&
- (
- clipEnds(ax, 0) ||
- !anyCounterAxLineAtZero(gd, ax, counterAxis, rng) ||
- hasBarsOrFill(gd, ax)
- )
- );
- };
-
- axes.clipEnds = function(ax, vals) {
- return vals.filter(function(d) { return clipEnds(ax, d.x); });
- };
-
- function clipEnds(ax, l) {
- var p = ax.l2p(l);
- return (p > 1 && p < ax._length - 1);
- }
-
- function anyCounterAxLineAtZero(gd, ax, counterAxis, rng) {
- var mainCounterAxis = counterAxis._mainAxis;
- if(!mainCounterAxis) return;
-
- var fullLayout = gd._fullLayout;
- var axLetter = ax._id.charAt(0);
- var counterLetter = axes.counterLetter(ax._id);
-
- var zeroPosition = ax._offset + (
- ((Math.abs(rng[0]) < Math.abs(rng[1])) === (axLetter === 'x')) ?
- 0 : ax._length
- );
-
- function lineNearZero(ax2) {
- if(!ax2.showline || !ax2.linewidth) return false;
- var tolerance = Math.max((ax2.linewidth + ax.zerolinewidth) / 2, 1);
-
- function closeEnough(pos2) {
- return typeof pos2 === 'number' && Math.abs(pos2 - zeroPosition) < tolerance;
- }
-
- if(closeEnough(ax2._mainLinePosition) || closeEnough(ax2._mainMirrorPosition)) {
- return true;
- }
- var linePositions = ax2._linepositions || {};
- for(var k in linePositions) {
- if(closeEnough(linePositions[k][0]) || closeEnough(linePositions[k][1])) {
- return true;
- }
- }
- }
-
- var plotinfo = fullLayout._plots[counterAxis._mainSubplot];
- if(!(plotinfo.mainplotinfo || plotinfo).overlays.length) {
- return lineNearZero(counterAxis, zeroPosition);
- }
-
- var counterLetterAxes = axes.list(gd, counterLetter);
- for(var i = 0; i < counterLetterAxes.length; i++) {
- var counterAxis2 = counterLetterAxes[i];
- if(
- counterAxis2._mainAxis === mainCounterAxis &&
- lineNearZero(counterAxis2, zeroPosition)
- ) {
- return true;
- }
- }
- }
-
- function hasBarsOrFill(gd, ax) {
- var fullData = gd._fullData;
- var subplot = ax._mainSubplot;
- var axLetter = ax._id.charAt(0);
-
- for(var i = 0; i < fullData.length; i++) {
- var trace = fullData[i];
-
- if(trace.visible === true &&
- (trace.xaxis + trace.yaxis) === subplot &&
- (
- Registry.traceIs(trace, 'bar') && trace.orientation === {x: 'h', y: 'v'}[axLetter] ||
- trace.fill && trace.fill.charAt(trace.fill.length - 1) === axLetter
- )
- ) {
- return true;
- }
- }
- return false;
- }
-
- function selectTickLabel(gTick) {
- var s = d3.select(gTick);
- var mj = s.select('.text-math-group');
- return mj.empty() ? s.select('text') : mj;
- }
-
- /**
- * Find all margin pushers for 2D axes and reserve them for later use
- * Both label and rangeslider automargin calculations happen later so
- * we need to explicitly allow their ids in order to not delete them.
- *
- * TODO: can we pull the actual automargin calls forward to avoid this hack?
- * We're probably also doing multiple redraws in this case, would be faster
- * if we can just do the whole calculation ahead of time and draw once.
- */
- axes.allowAutoMargin = function(gd) {
- var axList = axes.list(gd, '', true);
- for(var i = 0; i < axList.length; i++) {
- var ax = axList[i];
- if(ax.automargin) {
- Plots.allowAutoMargin(gd, axAutoMarginID(ax));
- }
- if(Registry.getComponentMethod('rangeslider', 'isVisible')(ax)) {
- Plots.allowAutoMargin(gd, rangeSliderAutoMarginID(ax));
- }
- }
- };
-
- function axAutoMarginID(ax) { return ax._id + '.automargin'; }
- function rangeSliderAutoMarginID(ax) { return ax._id + '.rangeslider'; }
-
- // swap all the presentation attributes of the axes showing these traces
- axes.swap = function(gd, traces) {
- var axGroups = makeAxisGroups(gd, traces);
-
- for(var i = 0; i < axGroups.length; i++) {
- swapAxisGroup(gd, axGroups[i].x, axGroups[i].y);
- }
- };
-
- function makeAxisGroups(gd, traces) {
- var groups = [];
- var i, j;
-
- for(i = 0; i < traces.length; i++) {
- var groupsi = [];
- var xi = gd._fullData[traces[i]].xaxis;
- var yi = gd._fullData[traces[i]].yaxis;
- if(!xi || !yi) continue; // not a 2D cartesian trace?
-
- for(j = 0; j < groups.length; j++) {
- if(groups[j].x.indexOf(xi) !== -1 || groups[j].y.indexOf(yi) !== -1) {
- groupsi.push(j);
- }
- }
-
- if(!groupsi.length) {
- groups.push({x: [xi], y: [yi]});
- continue;
- }
-
- var group0 = groups[groupsi[0]];
- var groupj;
-
- if(groupsi.length > 1) {
- for(j = 1; j < groupsi.length; j++) {
- groupj = groups[groupsi[j]];
- mergeAxisGroups(group0.x, groupj.x);
- mergeAxisGroups(group0.y, groupj.y);
- }
- }
- mergeAxisGroups(group0.x, [xi]);
- mergeAxisGroups(group0.y, [yi]);
- }
-
- return groups;
- }
-
- function mergeAxisGroups(intoSet, fromSet) {
- for(var i = 0; i < fromSet.length; i++) {
- if(intoSet.indexOf(fromSet[i]) === -1) intoSet.push(fromSet[i]);
- }
- }
-
- function swapAxisGroup(gd, xIds, yIds) {
- var xFullAxes = [];
- var yFullAxes = [];
- var layout = gd.layout;
- var i, j;
-
- for(i = 0; i < xIds.length; i++) xFullAxes.push(axes.getFromId(gd, xIds[i]));
- for(i = 0; i < yIds.length; i++) yFullAxes.push(axes.getFromId(gd, yIds[i]));
-
- var allAxKeys = Object.keys(axAttrs);
-
- var noSwapAttrs = [
- 'anchor', 'domain', 'overlaying', 'position', 'side', 'tickangle', 'editType'
- ];
- var numericTypes = ['linear', 'log'];
-
- for(i = 0; i < allAxKeys.length; i++) {
- var keyi = allAxKeys[i];
- var xVal = xFullAxes[0][keyi];
- var yVal = yFullAxes[0][keyi];
- var allEqual = true;
- var coerceLinearX = false;
- var coerceLinearY = false;
- if(keyi.charAt(0) === '_' || typeof xVal === 'function' ||
- noSwapAttrs.indexOf(keyi) !== -1) {
- continue;
- }
- for(j = 1; j < xFullAxes.length && allEqual; j++) {
- var xVali = xFullAxes[j][keyi];
- if(keyi === 'type' && numericTypes.indexOf(xVal) !== -1 &&
- numericTypes.indexOf(xVali) !== -1 && xVal !== xVali) {
- // type is special - if we find a mixture of linear and log,
- // coerce them all to linear on flipping
- coerceLinearX = true;
- }
- else if(xVali !== xVal) allEqual = false;
- }
- for(j = 1; j < yFullAxes.length && allEqual; j++) {
- var yVali = yFullAxes[j][keyi];
- if(keyi === 'type' && numericTypes.indexOf(yVal) !== -1 &&
- numericTypes.indexOf(yVali) !== -1 && yVal !== yVali) {
- // type is special - if we find a mixture of linear and log,
- // coerce them all to linear on flipping
- coerceLinearY = true;
- }
- else if(yFullAxes[j][keyi] !== yVal) allEqual = false;
- }
- if(allEqual) {
- if(coerceLinearX) layout[xFullAxes[0]._name].type = 'linear';
- if(coerceLinearY) layout[yFullAxes[0]._name].type = 'linear';
- swapAxisAttrs(layout, keyi, xFullAxes, yFullAxes, gd._fullLayout._dfltTitle);
- }
- }
-
- // now swap x&y for any annotations anchored to these x & y
- for(i = 0; i < gd._fullLayout.annotations.length; i++) {
- var ann = gd._fullLayout.annotations[i];
- if(xIds.indexOf(ann.xref) !== -1 &&
- yIds.indexOf(ann.yref) !== -1) {
- Lib.swapAttrs(layout.annotations[i], ['?']);
- }
- }
- }
-
- function swapAxisAttrs(layout, key, xFullAxes, yFullAxes, dfltTitle) {
- // in case the value is the default for either axis,
- // look at the first axis in each list and see if
- // this key's value is undefined
- var np = Lib.nestedProperty;
- var xVal = np(layout[xFullAxes[0]._name], key).get();
- var yVal = np(layout[yFullAxes[0]._name], key).get();
- var i;
-
- if(key === 'title') {
- // special handling of placeholder titles
- if(xVal && xVal.text === dfltTitle.x) {
- xVal.text = dfltTitle.y;
- }
- if(yVal && yVal.text === dfltTitle.y) {
- yVal.text = dfltTitle.x;
- }
- }
-
- for(i = 0; i < xFullAxes.length; i++) {
- np(layout, xFullAxes[i]._name + '.' + key).set(yVal);
- }
- for(i = 0; i < yFullAxes.length; i++) {
- np(layout, yFullAxes[i]._name + '.' + key).set(xVal);
- }
- }
-
- function isAngular(ax) {
- return ax._id === 'angularaxis';
- }
-
- },{"../../components/color":51,"../../components/drawing":72,"../../components/titles":139,"../../constants/alignment":146,"../../constants/numerical":149,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/plots":245,"../../registry":257,"./autorange":211,"./axis_autotype":213,"./axis_ids":215,"./clean_ticks":217,"./layout_attributes":225,"./set_convert":231,"d3":16,"fast-isnumeric":18}],213:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Lib = _dereq_('../../lib');
- var BADNUM = _dereq_('../../constants/numerical').BADNUM;
-
- module.exports = function autoType(array, calendar, opts) {
- opts = opts || {};
-
- if(!opts.noMultiCategory && multiCategory(array)) return 'multicategory';
- if(moreDates(array, calendar)) return 'date';
- if(category(array)) return 'category';
- if(linearOK(array)) return 'linear';
- else return '-';
- };
-
- // is there at least one number in array? If not, we should leave
- // ax.type empty so it can be autoset later
- function linearOK(array) {
- if(!array) return false;
-
- for(var i = 0; i < array.length; i++) {
- if(isNumeric(array[i])) return true;
- }
-
- return false;
- }
-
- // does the array a have mostly dates rather than numbers?
- // note: some values can be neither (such as blanks, text)
- // 2- or 4-digit integers can be both, so require twice as many
- // dates as non-dates, to exclude cases with mostly 2 & 4 digit
- // numbers and a few dates
- // as with categories, consider DISTINCT values only.
- function moreDates(a, calendar) {
- // test at most 1000 points, evenly spaced
- var inc = Math.max(1, (a.length - 1) / 1000);
- var dcnt = 0;
- var ncnt = 0;
- var seen = {};
-
- for(var i = 0; i < a.length; i += inc) {
- var ai = a[Math.round(i)];
- var stri = String(ai);
- if(seen[stri]) continue;
- seen[stri] = 1;
-
- if(Lib.isDateTime(ai, calendar)) dcnt += 1;
- if(isNumeric(ai)) ncnt += 1;
- }
-
- return (dcnt > ncnt * 2);
- }
-
- // are the (x,y)-values in gd.data mostly text?
- // require twice as many DISTINCT categories as distinct numbers
- function category(a) {
- // test at most 1000 points
- var inc = Math.max(1, (a.length - 1) / 1000);
- var curvenums = 0;
- var curvecats = 0;
- var seen = {};
-
- for(var i = 0; i < a.length; i += inc) {
- var ai = a[Math.round(i)];
- var stri = String(ai);
- if(seen[stri]) continue;
- seen[stri] = 1;
-
- if(typeof ai === 'boolean') curvecats++;
- else if(Lib.cleanNumber(ai) !== BADNUM) curvenums++;
- else if(typeof ai === 'string') curvecats++;
- }
-
- return curvecats > curvenums * 2;
- }
-
- // very-loose requirements for multicategory,
- // trace modules that should never auto-type to multicategory
- // should be declared with 'noMultiCategory'
- function multiCategory(a) {
- return Lib.isArrayOrTypedArray(a[0]) && Lib.isArrayOrTypedArray(a[1]);
- }
-
- },{"../../constants/numerical":149,"../../lib":168,"fast-isnumeric":18}],214:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
-
- var layoutAttributes = _dereq_('./layout_attributes');
- var handleTickValueDefaults = _dereq_('./tick_value_defaults');
- var handleTickMarkDefaults = _dereq_('./tick_mark_defaults');
- var handleTickLabelDefaults = _dereq_('./tick_label_defaults');
- var handleCategoryOrderDefaults = _dereq_('./category_order_defaults');
- var handleLineGridDefaults = _dereq_('./line_grid_defaults');
- var setConvert = _dereq_('./set_convert');
-
- /**
- * options: object containing:
- *
- * letter: 'x' or 'y'
- * title: name of the axis (ie 'Colorbar') to go in default title
- * font: the default font to inherit
- * outerTicks: boolean, should ticks default to outside?
- * showGrid: boolean, should gridlines be shown by default?
- * noHover: boolean, this axis doesn't support hover effects?
- * noTickson: boolean, this axis doesn't support 'tickson'
- * data: the plot data, used to manage categories
- * bgColor: the plot background color, to calculate default gridline colors
- */
- module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, options, layoutOut) {
- var letter = options.letter;
- var font = options.font || {};
- var splomStash = options.splomStash || {};
-
- var visible = coerce('visible', !options.cheateronly);
-
- var axType = containerOut.type;
-
- if(axType === 'date') {
- var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults');
- handleCalendarDefaults(containerIn, containerOut, 'calendar', options.calendar);
- }
-
- setConvert(containerOut, layoutOut);
-
- var autoRange = coerce('autorange', !containerOut.isValidRange(containerIn.range));
- if(autoRange && (axType === 'linear' || axType === '-')) coerce('rangemode');
-
- coerce('range');
- containerOut.cleanRange();
-
- handleCategoryOrderDefaults(containerIn, containerOut, coerce, options);
-
- if(axType !== 'category' && !options.noHover) coerce('hoverformat');
-
- if(!visible) return containerOut;
-
- var dfltColor = coerce('color');
- // if axis.color was provided, use it for fonts too; otherwise,
- // inherit from global font color in case that was provided.
- // Compare to dflt rather than to containerIn, so we can provide color via
- // template too.
- var dfltFontColor = (dfltColor !== layoutAttributes.color.dflt) ? dfltColor : font.color;
- // try to get default title from splom trace, fallback to graph-wide value
- var dfltTitle = splomStash.label || layoutOut._dfltTitle[letter];
-
- coerce('title.text', dfltTitle);
- Lib.coerceFont(coerce, 'title.font', {
- family: font.family,
- size: Math.round(font.size * 1.2),
- color: dfltFontColor
- });
-
- handleTickValueDefaults(containerIn, containerOut, coerce, axType);
- handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options);
- handleTickMarkDefaults(containerIn, containerOut, coerce, options);
- handleLineGridDefaults(containerIn, containerOut, coerce, {
- dfltColor: dfltColor,
- bgColor: options.bgColor,
- showGrid: options.showGrid,
- attributes: layoutAttributes
- });
-
- if(containerOut.showline || containerOut.ticks) coerce('mirror');
-
- if(options.automargin) coerce('automargin');
-
- var isMultiCategory = containerOut.type === 'multicategory';
-
- if(!options.noTickson &&
- (containerOut.type === 'category' || isMultiCategory) &&
- (containerOut.ticks || containerOut.showgrid)
- ) {
- var ticksonDflt;
- if(isMultiCategory) ticksonDflt = 'boundaries';
- coerce('tickson', ticksonDflt);
- }
-
- if(isMultiCategory) {
- var showDividers = coerce('showdividers');
- if(showDividers) {
- coerce('dividercolor');
- coerce('dividerwidth');
- }
- }
-
- return containerOut;
- };
-
- },{"../../lib":168,"../../registry":257,"./category_order_defaults":216,"./layout_attributes":225,"./line_grid_defaults":227,"./set_convert":231,"./tick_label_defaults":232,"./tick_mark_defaults":233,"./tick_value_defaults":234}],215:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
-
- var constants = _dereq_('./constants');
-
-
- // convert between axis names (xaxis, xaxis2, etc, elements of gd.layout)
- // and axis id's (x, x2, etc). Would probably have ditched 'xaxis'
- // completely in favor of just 'x' if it weren't ingrained in the API etc.
- exports.id2name = function id2name(id) {
- if(typeof id !== 'string' || !id.match(constants.AX_ID_PATTERN)) return;
- var axNum = id.substr(1);
- if(axNum === '1') axNum = '';
- return id.charAt(0) + 'axis' + axNum;
- };
-
- exports.name2id = function name2id(name) {
- if(!name.match(constants.AX_NAME_PATTERN)) return;
- var axNum = name.substr(5);
- if(axNum === '1') axNum = '';
- return name.charAt(0) + axNum;
- };
-
- exports.cleanId = function cleanId(id, axLetter) {
- if(!id.match(constants.AX_ID_PATTERN)) return;
- if(axLetter && id.charAt(0) !== axLetter) return;
-
- var axNum = id.substr(1).replace(/^0+/, '');
- if(axNum === '1') axNum = '';
- return id.charAt(0) + axNum;
- };
-
- // get all axis objects, as restricted in listNames
- exports.list = function(gd, axLetter, only2d) {
- var fullLayout = gd._fullLayout;
- if(!fullLayout) return [];
-
- var idList = exports.listIds(gd, axLetter);
- var out = new Array(idList.length);
- var i;
-
- for(i = 0; i < idList.length; i++) {
- var idi = idList[i];
- out[i] = fullLayout[idi.charAt(0) + 'axis' + idi.substr(1)];
- }
-
- if(!only2d) {
- var sceneIds3D = fullLayout._subplots.gl3d || [];
-
- for(i = 0; i < sceneIds3D.length; i++) {
- var scene = fullLayout[sceneIds3D[i]];
-
- if(axLetter) out.push(scene[axLetter + 'axis']);
- else out.push(scene.xaxis, scene.yaxis, scene.zaxis);
- }
- }
-
- return out;
- };
-
- // get all axis ids, optionally restricted by letter
- // this only makes sense for 2d axes
- exports.listIds = function(gd, axLetter) {
- var fullLayout = gd._fullLayout;
- if(!fullLayout) return [];
-
- var subplotLists = fullLayout._subplots;
- if(axLetter) return subplotLists[axLetter + 'axis'];
- return subplotLists.xaxis.concat(subplotLists.yaxis);
- };
-
- // get an axis object from its id 'x','x2' etc
- // optionally, id can be a subplot (ie 'x2y3') and type gets x or y from it
- exports.getFromId = function(gd, id, type) {
- var fullLayout = gd._fullLayout;
-
- if(type === 'x') id = id.replace(/y[0-9]*/, '');
- else if(type === 'y') id = id.replace(/x[0-9]*/, '');
-
- return fullLayout[exports.id2name(id)];
- };
-
- // get an axis object of specified type from the containing trace
- exports.getFromTrace = function(gd, fullTrace, type) {
- var fullLayout = gd._fullLayout;
- var ax = null;
-
- if(Registry.traceIs(fullTrace, 'gl3d')) {
- var scene = fullTrace.scene;
- if(scene.substr(0, 5) === 'scene') {
- ax = fullLayout[scene][type + 'axis'];
- }
- }
- else {
- ax = exports.getFromId(gd, fullTrace[type + 'axis'] || type);
- }
-
- return ax;
- };
-
- // sort x, x2, x10, y, y2, y10...
- exports.idSort = function(id1, id2) {
- var letter1 = id1.charAt(0);
- var letter2 = id2.charAt(0);
- if(letter1 !== letter2) return letter1 > letter2 ? 1 : -1;
- return +(id1.substr(1) || 1) - +(id2.substr(1) || 1);
- };
-
- },{"../../registry":257,"./constants":218}],216:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- function findCategories(ax, opts) {
- var dataAttr = opts.dataAttr || ax._id.charAt(0);
- var lookup = {};
- var axData;
- var i, j;
-
- if(opts.axData) {
- // non-x/y case
- axData = opts.axData;
- } else {
- // x/y case
- axData = [];
- for(i = 0; i < opts.data.length; i++) {
- var trace = opts.data[i];
- if(trace[dataAttr + 'axis'] === ax._id) {
- axData.push(trace);
- }
- }
- }
-
- for(i = 0; i < axData.length; i++) {
- var vals = axData[i][dataAttr];
- for(j = 0; j < vals.length; j++) {
- var v = vals[j];
- if(v !== null && v !== undefined) {
- lookup[v] = 1;
- }
- }
- }
-
- return Object.keys(lookup);
- }
-
- /**
- * Fills in category* default and initial categories.
- *
- * @param {object} containerIn : input axis object
- * @param {object} containerOut : full axis object
- * @param {function} coerce : Lib.coerce fn wrapper
- * @param {object} opts :
- * - data {array} : (full) data trace
- * OR
- * - axData {array} : (full) data associated with axis being coerced here
- * - dataAttr {string} : attribute name corresponding to coordinate array
- */
- module.exports = function handleCategoryOrderDefaults(containerIn, containerOut, coerce, opts) {
- if(containerOut.type !== 'category') return;
-
- var arrayIn = containerIn.categoryarray;
- var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0);
-
- // override default 'categoryorder' value when non-empty array is supplied
- var orderDefault;
- if(isValidArray) orderDefault = 'array';
-
- var order = coerce('categoryorder', orderDefault);
- var array;
-
- // coerce 'categoryarray' only in array order case
- if(order === 'array') {
- array = coerce('categoryarray');
- }
-
- // cannot set 'categoryorder' to 'array' with an invalid 'categoryarray'
- if(!isValidArray && order === 'array') {
- order = containerOut.categoryorder = 'trace';
- }
-
- // set up things for makeCalcdata
- if(order === 'trace') {
- containerOut._initialCategories = [];
- } else if(order === 'array') {
- containerOut._initialCategories = array.slice();
- } else {
- array = findCategories(containerOut, opts).sort();
- if(order === 'category ascending') {
- containerOut._initialCategories = array;
- } else if(order === 'category descending') {
- containerOut._initialCategories = array.reverse();
- }
- }
- };
-
- },{}],217:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var Lib = _dereq_('../../lib');
- var ONEDAY = _dereq_('../../constants/numerical').ONEDAY;
-
- /**
- * Return a validated dtick value for this axis
- *
- * @param {any} dtick: the candidate dtick. valid values are numbers and strings,
- * and further constrained depending on the axis type.
- * @param {string} axType: the axis type
- */
- exports.dtick = function(dtick, axType) {
- var isLog = axType === 'log';
- var isDate = axType === 'date';
- var isCat = axType === 'category';
- var dtickDflt = isDate ? ONEDAY : 1;
-
- if(!dtick) return dtickDflt;
-
- if(isNumeric(dtick)) {
- dtick = Number(dtick);
- if(dtick <= 0) return dtickDflt;
- if(isCat) {
- // category dtick must be positive integers
- return Math.max(1, Math.round(dtick));
- }
- if(isDate) {
- // date dtick must be at least 0.1ms (our current precision)
- return Math.max(0.1, dtick);
- }
- return dtick;
- }
-
- if(typeof dtick !== 'string' || !(isDate || isLog)) {
- return dtickDflt;
- }
-
- var prefix = dtick.charAt(0);
- var dtickNum = dtick.substr(1);
- dtickNum = isNumeric(dtickNum) ? Number(dtickNum) : 0;
-
- if((dtickNum <= 0) || !(
- // "M<n>" gives ticks every (integer) n months
- (isDate && prefix === 'M' && dtickNum === Math.round(dtickNum)) ||
- // "L<f>" gives ticks linearly spaced in data (not in position) every (float) f
- (isLog && prefix === 'L') ||
- // "D1" gives powers of 10 with all small digits between, "D2" gives only 2 and 5
- (isLog && prefix === 'D' && (dtickNum === 1 || dtickNum === 2))
- )) {
- return dtickDflt;
- }
-
- return dtick;
- };
-
- /**
- * Return a validated tick0 for this axis
- *
- * @param {any} tick0: the candidate tick0. Valid values are numbers and strings,
- * further constrained depending on the axis type
- * @param {string} axType: the axis type
- * @param {string} calendar: for date axes, the calendar to validate/convert with
- * @param {any} dtick: an already valid dtick. Only used for D1 and D2 log dticks,
- * which do not support tick0 at all.
- */
- exports.tick0 = function(tick0, axType, calendar, dtick) {
- if(axType === 'date') {
- return Lib.cleanDate(tick0, Lib.dateTick0(calendar));
- }
- if(dtick === 'D1' || dtick === 'D2') {
- // D1 and D2 modes ignore tick0 entirely
- return undefined;
- }
- // Aside from date axes, tick0 must be numeric
- return isNumeric(tick0) ? Number(tick0) : 0;
- };
-
- },{"../../constants/numerical":149,"../../lib":168,"fast-isnumeric":18}],218:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
- var counterRegex = _dereq_('../../lib/regex').counter;
-
-
- module.exports = {
-
- idRegex: {
- x: counterRegex('x'),
- y: counterRegex('y')
- },
-
- attrRegex: counterRegex('[xy]axis'),
-
- // axis match regular expression
- xAxisMatch: counterRegex('xaxis'),
- yAxisMatch: counterRegex('yaxis'),
-
- // pattern matching axis ids and names
- // note that this is more permissive than counterRegex, as
- // id2name, name2id, and cleanId accept "x1" etc
- AX_ID_PATTERN: /^[xyz][0-9]*$/,
- AX_NAME_PATTERN: /^[xyz]axis[0-9]*$/,
-
- // and for 2D subplots
- SUBPLOT_PATTERN: /^x([0-9]*)y([0-9]*)$/,
-
- // pixels to move mouse before you stop clamping to starting point
- MINDRAG: 8,
-
- // smallest dimension allowed for a select box
- MINSELECT: 12,
-
- // smallest dimension allowed for a zoombox
- MINZOOM: 20,
-
- // width of axis drag regions
- DRAGGERSIZE: 20,
-
- // max pixels off straight before a lasso select line counts as bent
- BENDPX: 1.5,
-
- // delay before a redraw (relayout) after smooth panning and zooming
- REDRAWDELAY: 50,
-
- // throttling limit (ms) for selectPoints calls
- SELECTDELAY: 100,
-
- // cache ID suffix for throttle
- SELECTID: '-select',
-
- // last resort axis ranges for x and y axes if we have no data
- DFLTRANGEX: [-1, 6],
- DFLTRANGEY: [-1, 4],
-
- // Layers to keep trace types in the right order
- // N.B. each 'unique' plot method must have its own layer
- traceLayerClasses: [
- 'heatmaplayer',
- 'contourcarpetlayer', 'contourlayer',
- 'barlayer',
- 'carpetlayer',
- 'violinlayer',
- 'boxlayer',
- 'ohlclayer',
- 'scattercarpetlayer', 'scatterlayer'
- ],
-
- layerValue2layerClass: {
- 'above traces': 'above',
- 'below traces': 'below'
- }
- };
-
- },{"../../lib/regex":183}],219:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var id2name = _dereq_('./axis_ids').id2name;
-
-
- module.exports = function handleConstraintDefaults(containerIn, containerOut, coerce, allAxisIds, layoutOut) {
- var constraintGroups = layoutOut._axisConstraintGroups;
- var thisID = containerOut._id;
- var letter = thisID.charAt(0);
-
- if(containerOut.fixedrange) return;
-
- // coerce the constraint mechanics even if this axis has no scaleanchor
- // because it may be the anchor of another axis.
- coerce('constrain');
- Lib.coerce(containerIn, containerOut, {
- constraintoward: {
- valType: 'enumerated',
- values: letter === 'x' ? ['left', 'center', 'right'] : ['bottom', 'middle', 'top'],
- dflt: letter === 'x' ? 'center' : 'middle'
- }
- }, 'constraintoward');
-
- if(!containerIn.scaleanchor) return;
-
- var constraintOpts = getConstraintOpts(constraintGroups, thisID, allAxisIds, layoutOut);
-
- var scaleanchor = Lib.coerce(containerIn, containerOut, {
- scaleanchor: {
- valType: 'enumerated',
- values: constraintOpts.linkableAxes
- }
- }, 'scaleanchor');
-
- if(scaleanchor) {
- var scaleratio = coerce('scaleratio');
- // TODO: I suppose I could do attribute.min: Number.MIN_VALUE to avoid zero,
- // but that seems hacky. Better way to say "must be a positive number"?
- // Of course if you use several super-tiny values you could eventually
- // force a product of these to zero and all hell would break loose...
- // Likewise with super-huge values.
- if(!scaleratio) scaleratio = containerOut.scaleratio = 1;
-
- updateConstraintGroups(constraintGroups, constraintOpts.thisGroup,
- thisID, scaleanchor, scaleratio);
- }
- else if(allAxisIds.indexOf(containerIn.scaleanchor) !== -1) {
- Lib.warn('ignored ' + containerOut._name + '.scaleanchor: "' +
- containerIn.scaleanchor + '" to avoid either an infinite loop ' +
- 'and possibly inconsistent scaleratios, or because the target' +
- 'axis has fixed range.');
- }
- };
-
- function getConstraintOpts(constraintGroups, thisID, allAxisIds, layoutOut) {
- // If this axis is already part of a constraint group, we can't
- // scaleanchor any other axis in that group, or we'd make a loop.
- // Filter allAxisIds to enforce this, also matching axis types.
-
- var thisType = layoutOut[id2name(thisID)].type;
-
- var i, j, idj, axj;
-
- var linkableAxes = [];
- for(j = 0; j < allAxisIds.length; j++) {
- idj = allAxisIds[j];
- if(idj === thisID) continue;
-
- axj = layoutOut[id2name(idj)];
- if(axj.type === thisType && !axj.fixedrange) linkableAxes.push(idj);
- }
-
- for(i = 0; i < constraintGroups.length; i++) {
- if(constraintGroups[i][thisID]) {
- var thisGroup = constraintGroups[i];
-
- var linkableAxesNoLoops = [];
- for(j = 0; j < linkableAxes.length; j++) {
- idj = linkableAxes[j];
- if(!thisGroup[idj]) linkableAxesNoLoops.push(idj);
- }
- return {linkableAxes: linkableAxesNoLoops, thisGroup: thisGroup};
- }
- }
-
- return {linkableAxes: linkableAxes, thisGroup: null};
- }
-
-
- /*
- * Add this axis to the axis constraint groups, which is the collection
- * of axes that are all constrained together on scale.
- *
- * constraintGroups: a list of objects. each object is
- * {axis_id: scale_within_group}, where scale_within_group is
- * only important relative to the rest of the group, and defines
- * the relative scales between all axes in the group
- *
- * thisGroup: the group the current axis is already in
- * thisID: the id if the current axis
- * scaleanchor: the id of the axis to scale it with
- * scaleratio: the ratio of this axis to the scaleanchor axis
- */
- function updateConstraintGroups(constraintGroups, thisGroup, thisID, scaleanchor, scaleratio) {
- var i, j, groupi, keyj, thisGroupIndex;
-
- if(thisGroup === null) {
- thisGroup = {};
- thisGroup[thisID] = 1;
- thisGroupIndex = constraintGroups.length;
- constraintGroups.push(thisGroup);
- }
- else {
- thisGroupIndex = constraintGroups.indexOf(thisGroup);
- }
-
- var thisGroupKeys = Object.keys(thisGroup);
-
- // we know that this axis isn't in any other groups, but we don't know
- // about the scaleanchor axis. If it is, we need to merge the groups.
- for(i = 0; i < constraintGroups.length; i++) {
- groupi = constraintGroups[i];
- if(i !== thisGroupIndex && groupi[scaleanchor]) {
- var baseScale = groupi[scaleanchor];
- for(j = 0; j < thisGroupKeys.length; j++) {
- keyj = thisGroupKeys[j];
- groupi[keyj] = baseScale * scaleratio * thisGroup[keyj];
- }
- constraintGroups.splice(thisGroupIndex, 1);
- return;
- }
- }
-
- // otherwise, we insert the new scaleanchor axis as the base scale (1)
- // in its group, and scale the rest of the group to it
- if(scaleratio !== 1) {
- for(j = 0; j < thisGroupKeys.length; j++) {
- thisGroup[thisGroupKeys[j]] *= scaleratio;
- }
- }
- thisGroup[scaleanchor] = 1;
- }
-
- },{"../../lib":168,"./axis_ids":215}],220:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var id2name = _dereq_('./axis_ids').id2name;
- var scaleZoom = _dereq_('./scale_zoom');
- var makePadFn = _dereq_('./autorange').makePadFn;
- var concatExtremes = _dereq_('./autorange').concatExtremes;
-
- var ALMOST_EQUAL = _dereq_('../../constants/numerical').ALMOST_EQUAL;
-
- var FROM_BL = _dereq_('../../constants/alignment').FROM_BL;
-
-
- exports.enforce = function enforceAxisConstraints(gd) {
- var fullLayout = gd._fullLayout;
- var constraintGroups = fullLayout._axisConstraintGroups || [];
-
- var i, j, axisID, ax, normScale, mode, factor;
-
- for(i = 0; i < constraintGroups.length; i++) {
- var group = constraintGroups[i];
- var axisIDs = Object.keys(group);
-
- var minScale = Infinity;
- var maxScale = 0;
- // mostly matchScale will be the same as minScale
- // ie we expand axis ranges to encompass *everything*
- // that's currently in any of their ranges, but during
- // autorange of a subset of axes we will ignore other
- // axes for this purpose.
- var matchScale = Infinity;
- var normScales = {};
- var axes = {};
- var hasAnyDomainConstraint = false;
-
- // find the (normalized) scale of each axis in the group
- for(j = 0; j < axisIDs.length; j++) {
- axisID = axisIDs[j];
- axes[axisID] = ax = fullLayout[id2name(axisID)];
-
- if(ax._inputDomain) ax.domain = ax._inputDomain.slice();
- else ax._inputDomain = ax.domain.slice();
-
- if(!ax._inputRange) ax._inputRange = ax.range.slice();
-
- // set axis scale here so we can use _m rather than
- // having to calculate it from length and range
- ax.setScale();
-
- // abs: inverted scales still satisfy the constraint
- normScales[axisID] = normScale = Math.abs(ax._m) / group[axisID];
- minScale = Math.min(minScale, normScale);
- if(ax.constrain === 'domain' || !ax._constraintShrinkable) {
- matchScale = Math.min(matchScale, normScale);
- }
-
- // this has served its purpose, so remove it
- delete ax._constraintShrinkable;
- maxScale = Math.max(maxScale, normScale);
-
- if(ax.constrain === 'domain') hasAnyDomainConstraint = true;
- }
-
- // Do we have a constraint mismatch? Give a small buffer for rounding errors
- if(minScale > ALMOST_EQUAL * maxScale && !hasAnyDomainConstraint) continue;
-
- // now increase any ranges we need to until all normalized scales are equal
- for(j = 0; j < axisIDs.length; j++) {
- axisID = axisIDs[j];
- normScale = normScales[axisID];
- ax = axes[axisID];
- mode = ax.constrain;
-
- // even if the scale didn't change, if we're shrinking domain
- // we need to recalculate in case `constraintoward` changed
- if(normScale !== matchScale || mode === 'domain') {
- factor = normScale / matchScale;
-
- if(mode === 'range') {
- scaleZoom(ax, factor);
- }
- else {
- // mode === 'domain'
-
- var inputDomain = ax._inputDomain;
- var domainShrunk = (ax.domain[1] - ax.domain[0]) /
- (inputDomain[1] - inputDomain[0]);
- var rangeShrunk = (ax.r2l(ax.range[1]) - ax.r2l(ax.range[0])) /
- (ax.r2l(ax._inputRange[1]) - ax.r2l(ax._inputRange[0]));
-
- factor /= domainShrunk;
-
- if(factor * rangeShrunk < 1) {
- // we've asked to magnify the axis more than we can just by
- // enlarging the domain - so we need to constrict range
- ax.domain = ax._input.domain = inputDomain.slice();
- scaleZoom(ax, factor);
- continue;
- }
-
- if(rangeShrunk < 1) {
- // the range has previously been constricted by ^^, but we've
- // switched to the domain-constricted regime, so reset range
- ax.range = ax._input.range = ax._inputRange.slice();
- factor *= rangeShrunk;
- }
-
- if(ax.autorange) {
- /*
- * range & factor may need to change because range was
- * calculated for the larger scaling, so some pixel
- * paddings may get cut off when we reduce the domain.
- *
- * This is easier than the regular autorange calculation
- * because we already know the scaling `m`, but we still
- * need to cut out impossible constraints (like
- * annotations with super-long arrows). That's what
- * outerMin/Max are for - if the expansion was going to
- * go beyond the original domain, it must be impossible
- */
- var rl0 = ax.r2l(ax.range[0]);
- var rl1 = ax.r2l(ax.range[1]);
- var rangeCenter = (rl0 + rl1) / 2;
- var rangeMin = rangeCenter;
- var rangeMax = rangeCenter;
- var halfRange = Math.abs(rl1 - rangeCenter);
- // extra tiny bit for rounding errors, in case we actually
- // *are* expanding to the full domain
- var outerMin = rangeCenter - halfRange * factor * 1.0001;
- var outerMax = rangeCenter + halfRange * factor * 1.0001;
- var getPad = makePadFn(ax);
-
- updateDomain(ax, factor);
- var m = Math.abs(ax._m);
- var extremes = concatExtremes(gd, ax);
- var minArray = extremes.min;
- var maxArray = extremes.max;
- var newVal;
- var k;
-
- for(k = 0; k < minArray.length; k++) {
- newVal = minArray[k].val - getPad(minArray[k]) / m;
- if(newVal > outerMin && newVal < rangeMin) {
- rangeMin = newVal;
- }
- }
-
- for(k = 0; k < maxArray.length; k++) {
- newVal = maxArray[k].val + getPad(maxArray[k]) / m;
- if(newVal < outerMax && newVal > rangeMax) {
- rangeMax = newVal;
- }
- }
-
- var domainExpand = (rangeMax - rangeMin) / (2 * halfRange);
- factor /= domainExpand;
-
- rangeMin = ax.l2r(rangeMin);
- rangeMax = ax.l2r(rangeMax);
- ax.range = ax._input.range = (rl0 < rl1) ?
- [rangeMin, rangeMax] : [rangeMax, rangeMin];
- }
-
- updateDomain(ax, factor);
- }
- }
- }
- }
- };
-
- // For use before autoranging, check if this axis was previously constrained
- // by domain but no longer is
- exports.clean = function cleanConstraints(gd, ax) {
- if(ax._inputDomain) {
- var isConstrained = false;
- var axId = ax._id;
- var constraintGroups = gd._fullLayout._axisConstraintGroups;
- for(var j = 0; j < constraintGroups.length; j++) {
- if(constraintGroups[j][axId]) {
- isConstrained = true;
- break;
- }
- }
- if(!isConstrained || ax.constrain !== 'domain') {
- ax._input.domain = ax.domain = ax._inputDomain;
- delete ax._inputDomain;
- }
- }
- };
-
- function updateDomain(ax, factor) {
- var inputDomain = ax._inputDomain;
- var centerFraction = FROM_BL[ax.constraintoward];
- var center = inputDomain[0] + (inputDomain[1] - inputDomain[0]) * centerFraction;
-
- ax.domain = ax._input.domain = [
- center + (inputDomain[0] - center) / factor,
- center + (inputDomain[1] - center) / factor
- ];
- ax.setScale();
- }
-
- },{"../../constants/alignment":146,"../../constants/numerical":149,"./autorange":211,"./axis_ids":215,"./scale_zoom":229}],221:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var tinycolor = _dereq_('tinycolor2');
- var supportsPassive = _dereq_('has-passive-events');
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var Color = _dereq_('../../components/color');
- var Drawing = _dereq_('../../components/drawing');
- var Fx = _dereq_('../../components/fx');
- var Axes = _dereq_('./axes');
- var setCursor = _dereq_('../../lib/setcursor');
- var dragElement = _dereq_('../../components/dragelement');
- var FROM_TL = _dereq_('../../constants/alignment').FROM_TL;
- var clearGlCanvases = _dereq_('../../lib/clear_gl_canvases');
- var redrawReglTraces = _dereq_('../../plot_api/subroutines').redrawReglTraces;
-
- var Plots = _dereq_('../plots');
-
- var getFromId = _dereq_('./axis_ids').getFromId;
- var prepSelect = _dereq_('./select').prepSelect;
- var clearSelect = _dereq_('./select').clearSelect;
- var selectOnClick = _dereq_('./select').selectOnClick;
- var scaleZoom = _dereq_('./scale_zoom');
-
- var constants = _dereq_('./constants');
- var MINDRAG = constants.MINDRAG;
- var MINZOOM = constants.MINZOOM;
-
-
- // flag for showing "doubleclick to zoom out" only at the beginning
- var SHOWZOOMOUTTIP = true;
-
- // dragBox: create an element to drag one or more axis ends
- // inputs:
- // plotinfo - which subplot are we making dragboxes on?
- // x,y,w,h - left, top, width, height of the box
- // ns - how does this drag the vertical axis?
- // 'n' - top only
- // 's' - bottom only
- // 'ns' - top and bottom together, difference unchanged
- // ew - same for horizontal axis
- function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
- // mouseDown stores ms of first mousedown event in the last
- // DBLCLICKDELAY ms on the drag bars
- // numClicks stores how many mousedowns have been seen
- // within DBLCLICKDELAY so we can check for click or doubleclick events
- // dragged stores whether a drag has occurred, so we don't have to
- // redraw unnecessarily, ie if no move bigger than MINDRAG or MINZOOM px
- var zoomlayer = gd._fullLayout._zoomlayer;
- var isMainDrag = (ns + ew === 'nsew');
- var singleEnd = (ns + ew).length === 1;
-
- // main subplot x and y (i.e. found in plotinfo - the main ones)
- var xa0, ya0;
- // {ax._id: ax} hash objects
- var xaHash, yaHash;
- // xaHash/yaHash values (arrays)
- var xaxes, yaxes;
- // main axis offsets
- var xs, ys;
- // main axis lengths
- var pw, ph;
- // contains keys 'xaHash', 'yaHash', 'xaxes', and 'yaxes'
- // which are the x/y {ax._id: ax} hash objects and their values
- // for linked axis relative to this subplot
- var links;
- // set to ew/ns val when active, set to '' when inactive
- var xActive, yActive;
- // are all axes in this subplot are fixed?
- var allFixedRanges;
- // is subplot constrained?
- var isSubplotConstrained;
- // do we need to edit x/y ranges?
- var editX, editY;
- // graph-wide optimization flags
- var hasScatterGl, hasSplom, hasSVG;
- // collected changes to be made to the plot by relayout at the end
- var updates;
-
- function recomputeAxisLists() {
- xa0 = plotinfo.xaxis;
- ya0 = plotinfo.yaxis;
- pw = xa0._length;
- ph = ya0._length;
- xs = xa0._offset;
- ys = ya0._offset;
-
- xaHash = {};
- xaHash[xa0._id] = xa0;
- yaHash = {};
- yaHash[ya0._id] = ya0;
-
- // if we're dragging two axes at once, also drag overlays
- if(ns && ew) {
- var overlays = plotinfo.overlays;
- for(var i = 0; i < overlays.length; i++) {
- var xa = overlays[i].xaxis;
- xaHash[xa._id] = xa;
- var ya = overlays[i].yaxis;
- yaHash[ya._id] = ya;
- }
- }
-
- xaxes = hashValues(xaHash);
- yaxes = hashValues(yaHash);
- xActive = isDirectionActive(xaxes, ew);
- yActive = isDirectionActive(yaxes, ns);
- allFixedRanges = !yActive && !xActive;
-
- links = calcLinks(gd, xaHash, yaHash);
- isSubplotConstrained = links.isSubplotConstrained;
- editX = ew || isSubplotConstrained;
- editY = ns || isSubplotConstrained;
-
- var fullLayout = gd._fullLayout;
- hasScatterGl = fullLayout._has('scattergl');
- hasSplom = fullLayout._has('splom');
- hasSVG = fullLayout._has('svg');
- }
-
- recomputeAxisLists();
-
- var cursor = getDragCursor(yActive + xActive, gd._fullLayout.dragmode, isMainDrag);
- var dragger = makeRectDragger(plotinfo, ns + ew + 'drag', cursor, x, y, w, h);
-
- // still need to make the element if the axes are disabled
- // but nuke its events (except for maindrag which needs them for hover)
- // and stop there
- if(allFixedRanges && !isMainDrag) {
- dragger.onmousedown = null;
- dragger.style.pointerEvents = 'none';
- return dragger;
- }
-
- var dragOptions = {
- element: dragger,
- gd: gd,
- plotinfo: plotinfo
- };
-
- dragOptions.prepFn = function(e, startX, startY) {
- var dragModePrev = dragOptions.dragmode;
- var dragModeNow = gd._fullLayout.dragmode;
- if(dragModeNow !== dragModePrev) {
- dragOptions.dragmode = dragModeNow;
- }
-
- recomputeAxisLists();
-
- if(!allFixedRanges) {
- if(isMainDrag) {
- // main dragger handles all drag modes, and changes
- // to pan (or to zoom if it already is pan) on shift
- if(e.shiftKey) {
- if(dragModeNow === 'pan') dragModeNow = 'zoom';
- else if(!isSelectOrLasso(dragModeNow)) dragModeNow = 'pan';
- }
- else if(e.ctrlKey) {
- dragModeNow = 'pan';
- }
- }
- // all other draggers just pan
- else dragModeNow = 'pan';
- }
-
- if(dragModeNow === 'lasso') dragOptions.minDrag = 1;
- else dragOptions.minDrag = undefined;
-
- if(isSelectOrLasso(dragModeNow)) {
- dragOptions.xaxes = xaxes;
- dragOptions.yaxes = yaxes;
- // this attaches moveFn, clickFn, doneFn on dragOptions
- prepSelect(e, startX, startY, dragOptions, dragModeNow);
- } else {
- dragOptions.clickFn = clickFn;
- if(isSelectOrLasso(dragModePrev)) {
- // TODO Fix potential bug
- // Note: clearing / resetting selection state only happens, when user
- // triggers at least one interaction in pan/zoom mode. Otherwise, the
- // select/lasso outlines are deleted (in plots.js.cleanPlot) but the selection
- // cache isn't cleared. So when the user switches back to select/lasso and
- // 'adds to a selection' with Shift, the "old", seemingly removed outlines
- // are redrawn again because the selection cache still holds their coordinates.
- // However, this isn't easily solved, since plots.js would need
- // to have a reference to the dragOptions object (which holds the
- // selection cache).
- clearAndResetSelect();
- }
-
- if(!allFixedRanges) {
- if(dragModeNow === 'zoom') {
- dragOptions.moveFn = zoomMove;
- dragOptions.doneFn = zoomDone;
-
- // zoomMove takes care of the threshold, but we need to
- // minimize this so that constrained zoom boxes will flip
- // orientation at the right place
- dragOptions.minDrag = 1;
-
- zoomPrep(e, startX, startY);
- } else if(dragModeNow === 'pan') {
- dragOptions.moveFn = plotDrag;
- dragOptions.doneFn = dragTail;
- }
- }
- }
- };
-
- function clearAndResetSelect() {
- // clear selection polygon cache (if any)
- dragOptions.plotinfo.selection = false;
- // clear selection outlines
- clearSelect(zoomlayer);
- }
-
- function clickFn(numClicks, evt) {
- var clickmode = gd._fullLayout.clickmode;
-
- removeZoombox(gd);
-
- if(numClicks === 2 && !singleEnd) doubleClick();
-
- if(isMainDrag) {
- if(clickmode.indexOf('select') > -1) {
- selectOnClick(evt, gd, xaxes, yaxes, plotinfo.id, dragOptions);
- }
-
- if(clickmode.indexOf('event') > -1) {
- Fx.click(gd, evt, plotinfo.id);
- }
- }
- else if(numClicks === 1 && singleEnd) {
- var ax = ns ? ya0 : xa0;
- var end = (ns === 's' || ew === 'w') ? 0 : 1;
- var attrStr = ax._name + '.range[' + end + ']';
- var initialText = getEndText(ax, end);
- var hAlign = 'left';
- var vAlign = 'middle';
-
- if(ax.fixedrange) return;
-
- if(ns) {
- vAlign = (ns === 'n') ? 'top' : 'bottom';
- if(ax.side === 'right') hAlign = 'right';
- }
- else if(ew === 'e') hAlign = 'right';
-
- if(gd._context.showAxisRangeEntryBoxes) {
- d3.select(dragger)
- .call(svgTextUtils.makeEditable, {
- gd: gd,
- immediate: true,
- background: gd._fullLayout.paper_bgcolor,
- text: String(initialText),
- fill: ax.tickfont ? ax.tickfont.color : '#444',
- horizontalAlign: hAlign,
- verticalAlign: vAlign
- })
- .on('edit', function(text) {
- var v = ax.d2r(text);
- if(v !== undefined) {
- Registry.call('_guiRelayout', gd, attrStr, v);
- }
- });
- }
- }
- }
-
- dragElement.init(dragOptions);
-
- var x0,
- y0,
- box,
- lum,
- path0,
- dimmed,
- zoomMode,
- zb,
- corners;
-
- // zoom takes over minDrag, so it also has to take over gd._dragged
- var zoomDragged;
-
- function zoomPrep(e, startX, startY) {
- var dragBBox = dragger.getBoundingClientRect();
- x0 = startX - dragBBox.left;
- y0 = startY - dragBBox.top;
- box = {l: x0, r: x0, w: 0, t: y0, b: y0, h: 0};
- lum = gd._hmpixcount ?
- (gd._hmlumcount / gd._hmpixcount) :
- tinycolor(gd._fullLayout.plot_bgcolor).getLuminance();
- path0 = 'M0,0H' + pw + 'V' + ph + 'H0V0';
- dimmed = false;
- zoomMode = 'xy';
- zoomDragged = false;
-
- zb = makeZoombox(zoomlayer, lum, xs, ys, path0);
-
- corners = makeCorners(zoomlayer, xs, ys);
- }
-
- function zoomMove(dx0, dy0) {
- if(gd._transitioningWithDuration) {
- return false;
- }
-
- var x1 = Math.max(0, Math.min(pw, dx0 + x0));
- var y1 = Math.max(0, Math.min(ph, dy0 + y0));
- var dx = Math.abs(x1 - x0);
- var dy = Math.abs(y1 - y0);
-
- box.l = Math.min(x0, x1);
- box.r = Math.max(x0, x1);
- box.t = Math.min(y0, y1);
- box.b = Math.max(y0, y1);
-
- function noZoom() {
- zoomMode = '';
- box.r = box.l;
- box.t = box.b;
- corners.attr('d', 'M0,0Z');
- }
-
- if(isSubplotConstrained) {
- if(dx > MINZOOM || dy > MINZOOM) {
- zoomMode = 'xy';
- if(dx / pw > dy / ph) {
- dy = dx * ph / pw;
- if(y0 > y1) box.t = y0 - dy;
- else box.b = y0 + dy;
- }
- else {
- dx = dy * pw / ph;
- if(x0 > x1) box.l = x0 - dx;
- else box.r = x0 + dx;
- }
- corners.attr('d', xyCorners(box));
- }
- else {
- noZoom();
- }
- }
- // look for small drags in one direction or the other,
- // and only drag the other axis
- else if(!yActive || dy < Math.min(Math.max(dx * 0.6, MINDRAG), MINZOOM)) {
- if(dx < MINDRAG || !xActive) {
- noZoom();
- } else {
- box.t = 0;
- box.b = ph;
- zoomMode = 'x';
- corners.attr('d', xCorners(box, y0));
- }
- }
- else if(!xActive || dx < Math.min(dy * 0.6, MINZOOM)) {
- box.l = 0;
- box.r = pw;
- zoomMode = 'y';
- corners.attr('d', yCorners(box, x0));
- }
- else {
- zoomMode = 'xy';
- corners.attr('d', xyCorners(box));
- }
- box.w = box.r - box.l;
- box.h = box.b - box.t;
-
- if(zoomMode) zoomDragged = true;
- gd._dragged = zoomDragged;
-
- updateZoombox(zb, corners, box, path0, dimmed, lum);
- dimmed = true;
- }
-
- function zoomDone() {
- updates = {};
-
- // more strict than dragged, which allows you to come back to where you started
- // and still count as dragged
- if(Math.min(box.h, box.w) < MINDRAG * 2) {
- return removeZoombox(gd);
- }
-
- // TODO: edit linked axes in zoomAxRanges and in dragTail
- if(zoomMode === 'xy' || zoomMode === 'x') {
- zoomAxRanges(xaxes, box.l / pw, box.r / pw, updates, links.xaxes);
- }
- if(zoomMode === 'xy' || zoomMode === 'y') {
- zoomAxRanges(yaxes, (ph - box.b) / ph, (ph - box.t) / ph, updates, links.yaxes);
- }
-
- removeZoombox(gd);
- dragTail();
- showDoubleClickNotifier(gd);
- }
-
- // scroll zoom, on all draggers except corners
- var scrollViewBox = [0, 0, pw, ph];
- // wait a little after scrolling before redrawing
- var redrawTimer = null;
- var REDRAWDELAY = constants.REDRAWDELAY;
- var mainplot = plotinfo.mainplot ? gd._fullLayout._plots[plotinfo.mainplot] : plotinfo;
-
- function zoomWheel(e) {
- // deactivate mousewheel scrolling on embedded graphs
- // devs can override this with layout._enablescrollzoom,
- // but _ ensures this setting won't leave their page
- if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
- return;
- }
-
- clearAndResetSelect();
-
- // If a transition is in progress, then disable any behavior:
- if(gd._transitioningWithDuration) {
- e.preventDefault();
- e.stopPropagation();
- return;
- }
-
- recomputeAxisLists();
-
- clearTimeout(redrawTimer);
-
- var wheelDelta = -e.deltaY;
- if(!isFinite(wheelDelta)) wheelDelta = e.wheelDelta / 10;
- if(!isFinite(wheelDelta)) {
- Lib.log('Did not find wheel motion attributes: ', e);
- return;
- }
-
- var zoom = Math.exp(-Math.min(Math.max(wheelDelta, -20), 20) / 200);
- var gbb = mainplot.draglayer.select('.nsewdrag').node().getBoundingClientRect();
- var xfrac = (e.clientX - gbb.left) / gbb.width;
- var yfrac = (gbb.bottom - e.clientY) / gbb.height;
- var i;
-
- function zoomWheelOneAxis(ax, centerFraction, zoom) {
- if(ax.fixedrange) return;
-
- var axRange = Lib.simpleMap(ax.range, ax.r2l);
- var v0 = axRange[0] + (axRange[1] - axRange[0]) * centerFraction;
- function doZoom(v) { return ax.l2r(v0 + (v - v0) * zoom); }
- ax.range = axRange.map(doZoom);
- }
-
- if(editX) {
- // if we're only zooming this axis because of constraints,
- // zoom it about the center
- if(!ew) xfrac = 0.5;
-
- for(i = 0; i < xaxes.length; i++) {
- zoomWheelOneAxis(xaxes[i], xfrac, zoom);
- }
-
- scrollViewBox[2] *= zoom;
- scrollViewBox[0] += scrollViewBox[2] * xfrac * (1 / zoom - 1);
- }
- if(editY) {
- if(!ns) yfrac = 0.5;
-
- for(i = 0; i < yaxes.length; i++) {
- zoomWheelOneAxis(yaxes[i], yfrac, zoom);
- }
-
- scrollViewBox[3] *= zoom;
- scrollViewBox[1] += scrollViewBox[3] * (1 - yfrac) * (1 / zoom - 1);
- }
-
- // viewbox redraw at first
- updateSubplots(scrollViewBox);
- ticksAndAnnotations();
-
- // then replot after a delay to make sure
- // no more scrolling is coming
- redrawTimer = setTimeout(function() {
- scrollViewBox = [0, 0, pw, ph];
- dragTail();
- }, REDRAWDELAY);
-
- e.preventDefault();
- return;
- }
-
- // everything but the corners gets wheel zoom
- if(ns.length * ew.length !== 1) {
- attachWheelEventHandler(dragger, zoomWheel);
- }
-
- // plotDrag: move the plot in response to a drag
- function plotDrag(dx, dy) {
- // If a transition is in progress, then disable any behavior:
- if(gd._transitioningWithDuration) {
- return;
- }
-
- // prevent axis drawing from monkeying with margins until we're done
- gd._fullLayout._replotting = true;
-
- if(xActive === 'ew' || yActive === 'ns') {
- if(xActive) dragAxList(xaxes, dx);
- if(yActive) dragAxList(yaxes, dy);
- updateSubplots([xActive ? -dx : 0, yActive ? -dy : 0, pw, ph]);
- ticksAndAnnotations();
- return;
- }
-
- // dz: set a new value for one end (0 or 1) of an axis array axArray,
- // and return a pixel shift for that end for the viewbox
- // based on pixel drag distance d
- // TODO: this makes (generally non-fatal) errors when you get
- // near floating point limits
- function dz(axArray, end, d) {
- var otherEnd = 1 - end;
- var movedAx;
- var newLinearizedEnd;
- for(var i = 0; i < axArray.length; i++) {
- var axi = axArray[i];
- if(axi.fixedrange) continue;
- movedAx = axi;
- newLinearizedEnd = axi._rl[otherEnd] +
- (axi._rl[end] - axi._rl[otherEnd]) / dZoom(d / axi._length);
- var newEnd = axi.l2r(newLinearizedEnd);
-
- // if l2r comes back false or undefined, it means we've dragged off
- // the end of valid ranges - so stop.
- if(newEnd !== false && newEnd !== undefined) axi.range[end] = newEnd;
- }
- return movedAx._length * (movedAx._rl[end] - newLinearizedEnd) /
- (movedAx._rl[end] - movedAx._rl[otherEnd]);
- }
-
- if(isSubplotConstrained && xActive && yActive) {
- // dragging a corner of a constrained subplot:
- // respect the fixed corner, but harmonize dx and dy
- var dxySign = ((xActive === 'w') === (yActive === 'n')) ? 1 : -1;
- var dxyFraction = (dx / pw + dxySign * dy / ph) / 2;
- dx = dxyFraction * pw;
- dy = dxySign * dxyFraction * ph;
- }
-
- if(xActive === 'w') dx = dz(xaxes, 0, dx);
- else if(xActive === 'e') dx = dz(xaxes, 1, -dx);
- else if(!xActive) dx = 0;
-
- if(yActive === 'n') dy = dz(yaxes, 1, dy);
- else if(yActive === 's') dy = dz(yaxes, 0, -dy);
- else if(!yActive) dy = 0;
-
- var x0 = (xActive === 'w') ? dx : 0;
- var y0 = (yActive === 'n') ? dy : 0;
-
- if(isSubplotConstrained) {
- var i;
- if(!xActive && yActive.length === 1) {
- // dragging one end of the y axis of a constrained subplot
- // scale the other axis the same about its middle
- for(i = 0; i < xaxes.length; i++) {
- xaxes[i].range = xaxes[i]._r.slice();
- scaleZoom(xaxes[i], 1 - dy / ph);
- }
- dx = dy * pw / ph;
- x0 = dx / 2;
- }
- if(!yActive && xActive.length === 1) {
- for(i = 0; i < yaxes.length; i++) {
- yaxes[i].range = yaxes[i]._r.slice();
- scaleZoom(yaxes[i], 1 - dx / pw);
- }
- dy = dx * ph / pw;
- y0 = dy / 2;
- }
- }
-
- updateSubplots([x0, y0, pw - dx, ph - dy]);
- ticksAndAnnotations();
- }
-
- // Draw ticks and annotations (and other components) when ranges change.
- // Also records the ranges that have changed for use by update at the end.
- function ticksAndAnnotations() {
- var activeAxIds = [];
- var i;
-
- function pushActiveAxIds(axList) {
- for(i = 0; i < axList.length; i++) {
- if(!axList[i].fixedrange) activeAxIds.push(axList[i]._id);
- }
- }
-
- if(editX) {
- pushActiveAxIds(xaxes);
- pushActiveAxIds(links.xaxes);
- }
- if(editY) {
- pushActiveAxIds(yaxes);
- pushActiveAxIds(links.yaxes);
- }
-
- updates = {};
- for(i = 0; i < activeAxIds.length; i++) {
- var axId = activeAxIds[i];
- var ax = getFromId(gd, axId);
- Axes.drawOne(gd, ax, {skipTitle: true});
- updates[ax._name + '.range[0]'] = ax.range[0];
- updates[ax._name + '.range[1]'] = ax.range[1];
- }
-
- Axes.redrawComponents(gd, activeAxIds);
- }
-
- function doubleClick() {
- if(gd._transitioningWithDuration) return;
-
- var doubleClickConfig = gd._context.doubleClick;
- var axList = (xActive ? xaxes : []).concat(yActive ? yaxes : []);
- var attrs = {};
-
- var ax, i, rangeInitial;
-
- // For reset+autosize mode:
- // If *any* of the main axes is not at its initial range
- // (or autoranged, if we have no initial range, to match the logic in
- // doubleClickConfig === 'reset' below), we reset.
- // If they are *all* at their initial ranges, then we autosize.
- if(doubleClickConfig === 'reset+autosize') {
-
- doubleClickConfig = 'autosize';
-
- for(i = 0; i < axList.length; i++) {
- ax = axList[i];
- if((ax._rangeInitial && (
- ax.range[0] !== ax._rangeInitial[0] ||
- ax.range[1] !== ax._rangeInitial[1]
- )) ||
- (!ax._rangeInitial && !ax.autorange)
- ) {
- doubleClickConfig = 'reset';
- break;
- }
- }
- }
-
- if(doubleClickConfig === 'autosize') {
- // don't set the linked axes here, so relayout marks them as shrinkable
- // and we autosize just to the requested axis/axes
- for(i = 0; i < axList.length; i++) {
- ax = axList[i];
- if(!ax.fixedrange) attrs[ax._name + '.autorange'] = true;
- }
- }
- else if(doubleClickConfig === 'reset') {
- // when we're resetting, reset all linked axes too, so we get back
- // to the fully-auto-with-constraints situation
- if(xActive || isSubplotConstrained) axList = axList.concat(links.xaxes);
- if(yActive && !isSubplotConstrained) axList = axList.concat(links.yaxes);
-
- if(isSubplotConstrained) {
- if(!xActive) axList = axList.concat(xaxes);
- else if(!yActive) axList = axList.concat(yaxes);
- }
-
- for(i = 0; i < axList.length; i++) {
- ax = axList[i];
-
- if(!ax.fixedrange) {
- if(!ax._rangeInitial) {
- attrs[ax._name + '.autorange'] = true;
- } else {
- rangeInitial = ax._rangeInitial;
- attrs[ax._name + '.range[0]'] = rangeInitial[0];
- attrs[ax._name + '.range[1]'] = rangeInitial[1];
- }
- }
- }
- }
-
- gd.emit('plotly_doubleclick', null);
- Registry.call('_guiRelayout', gd, attrs);
- }
-
- // dragTail - finish a drag event with a redraw
- function dragTail() {
- // put the subplot viewboxes back to default (Because we're going to)
- // be repositioning the data in the relayout. But DON'T call
- // ticksAndAnnotations again - it's unnecessary and would overwrite `updates`
- updateSubplots([0, 0, pw, ph]);
-
- // since we may have been redrawing some things during the drag, we may have
- // accumulated MathJax promises - wait for them before we relayout.
- Lib.syncOrAsync([
- Plots.previousPromises,
- function() {
- gd._fullLayout._replotting = false;
- Registry.call('_guiRelayout', gd, updates);
- }
- ], gd);
- }
-
- // x/y scaleFactor stash,
- // minimizes number of per-point DOM updates in updateSubplots below
- var xScaleFactorOld, yScaleFactorOld;
-
- // updateSubplots - find all plot viewboxes that should be
- // affected by this drag, and update them. look for all plots
- // sharing an affected axis (including the one being dragged),
- // includes also scattergl and splom logic.
- function updateSubplots(viewBox) {
- var fullLayout = gd._fullLayout;
- var plotinfos = fullLayout._plots;
- var subplots = fullLayout._subplots.cartesian;
- var i, sp, xa, ya;
-
- if(hasSplom) {
- Registry.subplotsRegistry.splom.drag(gd);
- }
-
- if(hasScatterGl) {
- for(i = 0; i < subplots.length; i++) {
- sp = plotinfos[subplots[i]];
- xa = sp.xaxis;
- ya = sp.yaxis;
-
- if(sp._scene) {
- var xrng = Lib.simpleMap(xa.range, xa.r2l);
- var yrng = Lib.simpleMap(ya.range, ya.r2l);
- sp._scene.update({range: [xrng[0], yrng[0], xrng[1], yrng[1]]});
- }
- }
- }
-
- if(hasSplom || hasScatterGl) {
- clearGlCanvases(gd);
- redrawReglTraces(gd);
- }
-
- if(hasSVG) {
- var xScaleFactor = viewBox[2] / xa0._length;
- var yScaleFactor = viewBox[3] / ya0._length;
-
- for(i = 0; i < subplots.length; i++) {
- sp = plotinfos[subplots[i]];
- xa = sp.xaxis;
- ya = sp.yaxis;
-
- var editX2 = editX && !xa.fixedrange && xaHash[xa._id];
- var editY2 = editY && !ya.fixedrange && yaHash[ya._id];
-
- var xScaleFactor2, yScaleFactor2;
- var clipDx, clipDy;
-
- if(editX2) {
- xScaleFactor2 = xScaleFactor;
- clipDx = ew ? viewBox[0] : getShift(xa, xScaleFactor2);
- } else {
- xScaleFactor2 = getLinkedScaleFactor(xa, xScaleFactor, yScaleFactor);
- clipDx = scaleAndGetShift(xa, xScaleFactor2);
- }
-
- if(editY2) {
- yScaleFactor2 = yScaleFactor;
- clipDy = ns ? viewBox[1] : getShift(ya, yScaleFactor2);
- } else {
- yScaleFactor2 = getLinkedScaleFactor(ya, xScaleFactor, yScaleFactor);
- clipDy = scaleAndGetShift(ya, yScaleFactor2);
- }
-
- // don't scale at all if neither axis is scalable here
- if(!xScaleFactor2 && !yScaleFactor2) {
- continue;
- }
-
- // but if only one is, reset the other axis scaling
- if(!xScaleFactor2) xScaleFactor2 = 1;
- if(!yScaleFactor2) yScaleFactor2 = 1;
-
- var plotDx = xa._offset - clipDx / xScaleFactor2;
- var plotDy = ya._offset - clipDy / yScaleFactor2;
-
- // TODO could be more efficient here:
- // setTranslate and setScale do a lot of extra work
- // when working independently, should perhaps combine
- // them into a single routine.
- sp.clipRect
- .call(Drawing.setTranslate, clipDx, clipDy)
- .call(Drawing.setScale, xScaleFactor2, yScaleFactor2);
-
- sp.plot
- .call(Drawing.setTranslate, plotDx, plotDy)
- .call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2);
-
- // apply an inverse scale to individual points to counteract
- // the scale of the trace group.
- // apply only when scale changes, as adjusting the scale of
- // all the points can be expansive.
- if(xScaleFactor2 !== xScaleFactorOld || yScaleFactor2 !== yScaleFactorOld) {
- Drawing.setPointGroupScale(sp.zoomScalePts, xScaleFactor2, yScaleFactor2);
- Drawing.setTextPointsScale(sp.zoomScaleTxt, xScaleFactor2, yScaleFactor2);
- }
-
- Drawing.hideOutsideRangePoints(sp.clipOnAxisFalseTraces, sp);
-
- // update x/y scaleFactor stash
- xScaleFactorOld = xScaleFactor2;
- yScaleFactorOld = yScaleFactor2;
- }
- }
- }
-
- // Find the appropriate scaling for this axis, if it's linked to the
- // dragged axes by constraints. 0 is special, it means this axis shouldn't
- // ever be scaled (will be converted to 1 if the other axis is scaled)
- function getLinkedScaleFactor(ax, xScaleFactor, yScaleFactor) {
- if(ax.fixedrange) return 0;
-
- if(editX && links.xaHash[ax._id]) {
- return xScaleFactor;
- }
- if(editY && (isSubplotConstrained ? links.xaHash : links.yaHash)[ax._id]) {
- return yScaleFactor;
- }
- return 0;
- }
-
- function scaleAndGetShift(ax, scaleFactor) {
- if(scaleFactor) {
- ax.range = ax._r.slice();
- scaleZoom(ax, scaleFactor);
- return getShift(ax, scaleFactor);
- }
- return 0;
- }
-
- function getShift(ax, scaleFactor) {
- return ax._length * (1 - scaleFactor) * FROM_TL[ax.constraintoward || 'middle'];
- }
-
- return dragger;
- }
-
- function makeDragger(plotinfo, nodeName, dragClass, cursor) {
- var dragger3 = Lib.ensureSingle(plotinfo.draglayer, nodeName, dragClass, function(s) {
- s.classed('drag', true)
- .style({fill: 'transparent', 'stroke-width': 0})
- .attr('data-subplot', plotinfo.id);
- });
-
- dragger3.call(setCursor, cursor);
-
- return dragger3.node();
- }
-
- function makeRectDragger(plotinfo, dragClass, cursor, x, y, w, h) {
- var dragger = makeDragger(plotinfo, 'rect', dragClass, cursor);
- d3.select(dragger).call(Drawing.setRect, x, y, w, h);
- return dragger;
- }
-
- function isDirectionActive(axList, activeVal) {
- for(var i = 0; i < axList.length; i++) {
- if(!axList[i].fixedrange) return activeVal;
- }
- return '';
- }
-
- function getEndText(ax, end) {
- var initialVal = ax.range[end];
- var diff = Math.abs(initialVal - ax.range[1 - end]);
- var dig;
-
- // TODO: this should basically be ax.r2d but we're doing extra
- // rounding here... can we clean up at all?
- if(ax.type === 'date') {
- return initialVal;
- }
- else if(ax.type === 'log') {
- dig = Math.ceil(Math.max(0, -Math.log(diff) / Math.LN10)) + 3;
- return d3.format('.' + dig + 'g')(Math.pow(10, initialVal));
- }
- else { // linear numeric (or category... but just show numbers here)
- dig = Math.floor(Math.log(Math.abs(initialVal)) / Math.LN10) -
- Math.floor(Math.log(diff) / Math.LN10) + 4;
- return d3.format('.' + String(dig) + 'g')(initialVal);
- }
- }
-
- function zoomAxRanges(axList, r0Fraction, r1Fraction, updates, linkedAxes) {
- var i,
- axi,
- axRangeLinear0,
- axRangeLinearSpan;
-
- for(i = 0; i < axList.length; i++) {
- axi = axList[i];
- if(axi.fixedrange) continue;
-
- axRangeLinear0 = axi._rl[0];
- axRangeLinearSpan = axi._rl[1] - axRangeLinear0;
- axi.range = [
- axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction),
- axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction)
- ];
-
- updates[axi._name + '.range[0]'] = axi.range[0];
- updates[axi._name + '.range[1]'] = axi.range[1];
- }
-
- // zoom linked axes about their centers
- if(linkedAxes && linkedAxes.length) {
- var linkedR0Fraction = (r0Fraction + (1 - r1Fraction)) / 2;
-
- zoomAxRanges(linkedAxes, linkedR0Fraction, 1 - linkedR0Fraction, updates);
- }
- }
-
- function dragAxList(axList, pix) {
- for(var i = 0; i < axList.length; i++) {
- var axi = axList[i];
- if(!axi.fixedrange) {
- axi.range = [
- axi.l2r(axi._rl[0] - pix / axi._m),
- axi.l2r(axi._rl[1] - pix / axi._m)
- ];
- }
- }
- }
-
- // common transform for dragging one end of an axis
- // d>0 is compressing scale (cursor is over the plot,
- // the axis end should move with the cursor)
- // d<0 is expanding (cursor is off the plot, axis end moves
- // nonlinearly so you can expand far)
- function dZoom(d) {
- return 1 - ((d >= 0) ? Math.min(d, 0.9) :
- 1 / (1 / Math.max(d, -0.3) + 3.222));
- }
-
- function getDragCursor(nsew, dragmode, isMainDrag) {
- if(!nsew) return 'pointer';
- if(nsew === 'nsew') {
- // in this case here, clear cursor and
- // use the cursor style set on <g .draglayer>
- if(isMainDrag) return '';
- if(dragmode === 'pan') return 'move';
- return 'crosshair';
- }
- return nsew.toLowerCase() + '-resize';
- }
-
- function makeZoombox(zoomlayer, lum, xs, ys, path0) {
- return zoomlayer.append('path')
- .attr('class', 'zoombox')
- .style({
- 'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
- 'stroke-width': 0
- })
- .attr('transform', 'translate(' + xs + ', ' + ys + ')')
- .attr('d', path0 + 'Z');
- }
-
- function makeCorners(zoomlayer, xs, ys) {
- return zoomlayer.append('path')
- .attr('class', 'zoombox-corners')
- .style({
- fill: Color.background,
- stroke: Color.defaultLine,
- 'stroke-width': 1,
- opacity: 0
- })
- .attr('transform', 'translate(' + xs + ', ' + ys + ')')
- .attr('d', 'M0,0Z');
- }
-
- function updateZoombox(zb, corners, box, path0, dimmed, lum) {
- zb.attr('d',
- path0 + 'M' + (box.l) + ',' + (box.t) + 'v' + (box.h) +
- 'h' + (box.w) + 'v-' + (box.h) + 'h-' + (box.w) + 'Z');
- transitionZoombox(zb, corners, dimmed, lum);
- }
-
- function transitionZoombox(zb, corners, dimmed, lum) {
- if(!dimmed) {
- zb.transition()
- .style('fill', lum > 0.2 ? 'rgba(0,0,0,0.4)' :
- 'rgba(255,255,255,0.3)')
- .duration(200);
- corners.transition()
- .style('opacity', 1)
- .duration(200);
- }
- }
-
- function removeZoombox(gd) {
- d3.select(gd)
- .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners')
- .remove();
- }
-
- function showDoubleClickNotifier(gd) {
- if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) {
- Lib.notifier(Lib._(gd, 'Double-click to zoom back out'), 'long');
- SHOWZOOMOUTTIP = false;
- }
- }
-
- function isSelectOrLasso(dragmode) {
- return dragmode === 'lasso' || dragmode === 'select';
- }
-
- function xCorners(box, y0) {
- return 'M' +
- (box.l - 0.5) + ',' + (y0 - MINZOOM - 0.5) +
- 'h-3v' + (2 * MINZOOM + 1) + 'h3ZM' +
- (box.r + 0.5) + ',' + (y0 - MINZOOM - 0.5) +
- 'h3v' + (2 * MINZOOM + 1) + 'h-3Z';
- }
-
- function yCorners(box, x0) {
- return 'M' +
- (x0 - MINZOOM - 0.5) + ',' + (box.t - 0.5) +
- 'v-3h' + (2 * MINZOOM + 1) + 'v3ZM' +
- (x0 - MINZOOM - 0.5) + ',' + (box.b + 0.5) +
- 'v3h' + (2 * MINZOOM + 1) + 'v-3Z';
- }
-
- function xyCorners(box) {
- var clen = Math.floor(Math.min(box.b - box.t, box.r - box.l, MINZOOM) / 2);
- return 'M' +
- (box.l - 3.5) + ',' + (box.t - 0.5 + clen) + 'h3v' + (-clen) +
- 'h' + clen + 'v-3h-' + (clen + 3) + 'ZM' +
- (box.r + 3.5) + ',' + (box.t - 0.5 + clen) + 'h-3v' + (-clen) +
- 'h' + (-clen) + 'v-3h' + (clen + 3) + 'ZM' +
- (box.r + 3.5) + ',' + (box.b + 0.5 - clen) + 'h-3v' + clen +
- 'h' + (-clen) + 'v3h' + (clen + 3) + 'ZM' +
- (box.l - 3.5) + ',' + (box.b + 0.5 - clen) + 'h3v' + clen +
- 'h' + clen + 'v3h-' + (clen + 3) + 'Z';
- }
-
- function calcLinks(gd, xaHash, yaHash) {
- var constraintGroups = gd._fullLayout._axisConstraintGroups;
- var isSubplotConstrained = false;
- var xLinks = {};
- var yLinks = {};
- var xID, yID, xLinkID, yLinkID;
-
- for(var i = 0; i < constraintGroups.length; i++) {
- var group = constraintGroups[i];
- // check if any of the x axes we're dragging is in this constraint group
- for(xID in xaHash) {
- if(group[xID]) {
- // put the rest of these axes into xLinks, if we're not already
- // dragging them, so we know to scale these axes automatically too
- // to match the changes in the dragged x axes
- for(xLinkID in group) {
- if(!(xLinkID.charAt(0) === 'x' ? xaHash : yaHash)[xLinkID]) {
- xLinks[xLinkID] = 1;
- }
- }
-
- // check if the x and y axes of THIS drag are linked
- for(yID in yaHash) {
- if(group[yID]) isSubplotConstrained = true;
- }
- }
- }
-
- // now check if any of the y axes we're dragging is in this constraint group
- // only look for outside links, as we've already checked for links within the dragger
- for(yID in yaHash) {
- if(group[yID]) {
- for(yLinkID in group) {
- if(!(yLinkID.charAt(0) === 'x' ? xaHash : yaHash)[yLinkID]) {
- yLinks[yLinkID] = 1;
- }
- }
- }
- }
- }
-
- if(isSubplotConstrained) {
- // merge xLinks and yLinks if the subplot is constrained,
- // since we'll always apply both anyway and the two will contain
- // duplicates
- Lib.extendFlat(xLinks, yLinks);
- yLinks = {};
- }
-
- var xaHashLinked = {};
- var xaxesLinked = [];
- for(xLinkID in xLinks) {
- var xa = getFromId(gd, xLinkID);
- xaxesLinked.push(xa);
- xaHashLinked[xa._id] = xa;
- }
-
- var yaHashLinked = {};
- var yaxesLinked = [];
- for(yLinkID in yLinks) {
- var ya = getFromId(gd, yLinkID);
- yaxesLinked.push(ya);
- yaHashLinked[ya._id] = ya;
- }
-
- return {
- xaHash: xaHashLinked,
- yaHash: yaHashLinked,
- xaxes: xaxesLinked,
- yaxes: yaxesLinked,
- isSubplotConstrained: isSubplotConstrained
- };
- }
-
- // still seems to be some confusion about onwheel vs onmousewheel...
- function attachWheelEventHandler(element, handler) {
- if(!supportsPassive) {
- if(element.onwheel !== undefined) element.onwheel = handler;
- else if(element.onmousewheel !== undefined) element.onmousewheel = handler;
- }
- else {
- var wheelEventName = element.onwheel !== undefined ? 'wheel' : 'mousewheel';
-
- if(element._onwheel) {
- element.removeEventListener(wheelEventName, element._onwheel);
- }
- element._onwheel = handler;
-
- element.addEventListener(wheelEventName, handler, {passive: false});
- }
- }
-
- function hashValues(hash) {
- var out = [];
- for(var k in hash) out.push(hash[k]);
- return out;
- }
-
- module.exports = {
- makeDragBox: makeDragBox,
-
- makeDragger: makeDragger,
- makeRectDragger: makeRectDragger,
- makeZoombox: makeZoombox,
- makeCorners: makeCorners,
-
- updateZoombox: updateZoombox,
- xyCorners: xyCorners,
- transitionZoombox: transitionZoombox,
- removeZoombox: removeZoombox,
- showDoubleClickNotifier: showDoubleClickNotifier,
-
- attachWheelEventHandler: attachWheelEventHandler
- };
-
- },{"../../components/color":51,"../../components/dragelement":69,"../../components/drawing":72,"../../components/fx":90,"../../constants/alignment":146,"../../lib":168,"../../lib/clear_gl_canvases":157,"../../lib/setcursor":187,"../../lib/svg_text_utils":189,"../../plot_api/subroutines":203,"../../registry":257,"../plots":245,"./axes":212,"./axis_ids":215,"./constants":218,"./scale_zoom":229,"./select":230,"d3":16,"has-passive-events":21,"tinycolor2":34}],222:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Fx = _dereq_('../../components/fx');
- var dragElement = _dereq_('../../components/dragelement');
- var setCursor = _dereq_('../../lib/setcursor');
-
- var makeDragBox = _dereq_('./dragbox').makeDragBox;
- var DRAGGERSIZE = _dereq_('./constants').DRAGGERSIZE;
-
- exports.initInteractions = function initInteractions(gd) {
- var fullLayout = gd._fullLayout;
-
- if(gd._context.staticPlot) {
- // this sweeps up more than just cartesian drag elements...
- d3.select(gd).selectAll('.drag').remove();
- return;
- }
-
- if(!fullLayout._has('cartesian') && !fullLayout._has('splom')) return;
-
- var subplots = Object.keys(fullLayout._plots || {}).sort(function(a, b) {
- // sort overlays last, then by x axis number, then y axis number
- if((fullLayout._plots[a].mainplot && true) ===
- (fullLayout._plots[b].mainplot && true)) {
- var aParts = a.split('y');
- var bParts = b.split('y');
- return (aParts[0] === bParts[0]) ?
- (Number(aParts[1] || 1) - Number(bParts[1] || 1)) :
- (Number(aParts[0] || 1) - Number(bParts[0] || 1));
- }
- return fullLayout._plots[a].mainplot ? 1 : -1;
- });
-
- subplots.forEach(function(subplot) {
- var plotinfo = fullLayout._plots[subplot];
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- // main and corner draggers need not be repeated for
- // overlaid subplots - these draggers drag them all
- if(!plotinfo.mainplot) {
- // main dragger goes over the grids and data, so we use its
- // mousemove events for all data hover effects
- var maindrag = makeDragBox(gd, plotinfo, xa._offset, ya._offset,
- xa._length, ya._length, 'ns', 'ew');
-
- maindrag.onmousemove = function(evt) {
- // This is on `gd._fullLayout`, *not* fullLayout because the reference
- // changes by the time this is called again.
- gd._fullLayout._rehover = function() {
- if(gd._fullLayout._hoversubplot === subplot) {
- Fx.hover(gd, evt, subplot);
- }
- };
-
- Fx.hover(gd, evt, subplot);
-
- // Note that we have *not* used the cached fullLayout variable here
- // since that may be outdated when this is called as a callback later on
- gd._fullLayout._lasthover = maindrag;
- gd._fullLayout._hoversubplot = subplot;
- };
-
- /*
- * IMPORTANT:
- * We must check for the presence of the drag cover here.
- * If we don't, a 'mouseout' event is triggered on the
- * maindrag before each 'click' event, which has the effect
- * of clearing the hoverdata; thus, cancelling the click event.
- */
- maindrag.onmouseout = function(evt) {
- if(gd._dragging) return;
-
- // When the mouse leaves this maindrag, unset the hovered subplot.
- // This may cause problems if it leaves the subplot directly *onto*
- // another subplot, but that's a tiny corner case at the moment.
- gd._fullLayout._hoversubplot = null;
-
- dragElement.unhover(gd, evt);
- };
-
- // corner draggers
- if(gd._context.showAxisDragHandles) {
- makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset - DRAGGERSIZE,
- DRAGGERSIZE, DRAGGERSIZE, 'n', 'w');
- makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset - DRAGGERSIZE,
- DRAGGERSIZE, DRAGGERSIZE, 'n', 'e');
- makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset + ya._length,
- DRAGGERSIZE, DRAGGERSIZE, 's', 'w');
- makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset + ya._length,
- DRAGGERSIZE, DRAGGERSIZE, 's', 'e');
- }
- }
- if(gd._context.showAxisDragHandles) {
- // x axis draggers - if you have overlaid plots,
- // these drag each axis separately
- if(subplot === xa._mainSubplot) {
- // the y position of the main x axis line
- var y0 = xa._mainLinePosition;
- if(xa.side === 'top') y0 -= DRAGGERSIZE;
- makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.1, y0,
- xa._length * 0.8, DRAGGERSIZE, '', 'ew');
- makeDragBox(gd, plotinfo, xa._offset, y0,
- xa._length * 0.1, DRAGGERSIZE, '', 'w');
- makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.9, y0,
- xa._length * 0.1, DRAGGERSIZE, '', 'e');
- }
- // y axis draggers
- if(subplot === ya._mainSubplot) {
- // the x position of the main y axis line
- var x0 = ya._mainLinePosition;
- if(ya.side !== 'right') x0 -= DRAGGERSIZE;
- makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.1,
- DRAGGERSIZE, ya._length * 0.8, 'ns', '');
- makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.9,
- DRAGGERSIZE, ya._length * 0.1, 's', '');
- makeDragBox(gd, plotinfo, x0, ya._offset,
- DRAGGERSIZE, ya._length * 0.1, 'n', '');
- }
- }
- });
-
- // In case you mousemove over some hovertext, send it to Fx.hover too
- // we do this so that we can put the hover text in front of everything,
- // but still be able to interact with everything as if it isn't there
- var hoverLayer = fullLayout._hoverlayer.node();
-
- hoverLayer.onmousemove = function(evt) {
- evt.target = gd._fullLayout._lasthover;
- Fx.hover(gd, evt, fullLayout._hoversubplot);
- };
-
- hoverLayer.onclick = function(evt) {
- evt.target = gd._fullLayout._lasthover;
- Fx.click(gd, evt);
- };
-
- // also delegate mousedowns... TODO: does this actually work?
- hoverLayer.onmousedown = function(evt) {
- gd._fullLayout._lasthover.onmousedown(evt);
- };
-
- exports.updateFx(gd);
- };
-
- // Minimal set of update needed on 'modebar' edits.
- // We only need to update the <g .draglayer> cursor style.
- //
- // Note that changing the axis configuration and/or the fixedrange attribute
- // should trigger a full initInteractions.
- exports.updateFx = function(gd) {
- var fullLayout = gd._fullLayout;
- var cursor = fullLayout.dragmode === 'pan' ? 'move' : 'crosshair';
- setCursor(fullLayout._draggers, cursor);
- };
-
- },{"../../components/dragelement":69,"../../components/fx":90,"../../lib/setcursor":187,"./constants":218,"./dragbox":221,"d3":16}],223:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
-
- /**
- * Factory function for checking component arrays for subplot references.
- *
- * @param {string} containerArrayName: the top-level array in gd.layout to check
- * If an item in this container is found that references a cartesian x and/or y axis,
- * ensure cartesian is marked as a base plot module and record the axes (and subplot
- * if both refs are axes) in gd._fullLayout
- *
- * @return {function}: with args layoutIn (gd.layout) and layoutOut (gd._fullLayout)
- * as expected of a component includeBasePlot method
- */
- module.exports = function makeIncludeComponents(containerArrayName) {
- return function includeComponents(layoutIn, layoutOut) {
- var array = layoutIn[containerArrayName];
- if(!Array.isArray(array)) return;
-
- var Cartesian = Registry.subplotsRegistry.cartesian;
- var idRegex = Cartesian.idRegex;
- var subplots = layoutOut._subplots;
- var xaList = subplots.xaxis;
- var yaList = subplots.yaxis;
- var cartesianList = subplots.cartesian;
- var hasCartesianOrGL2D = layoutOut._has('cartesian') || layoutOut._has('gl2d');
-
- for(var i = 0; i < array.length; i++) {
- var itemi = array[i];
- if(!Lib.isPlainObject(itemi)) continue;
-
- var xref = itemi.xref;
- var yref = itemi.yref;
-
- var hasXref = idRegex.x.test(xref);
- var hasYref = idRegex.y.test(yref);
- if(hasXref || hasYref) {
- if(!hasCartesianOrGL2D) Lib.pushUnique(layoutOut._basePlotModules, Cartesian);
-
- var newAxis = false;
- if(hasXref && xaList.indexOf(xref) === -1) {
- xaList.push(xref);
- newAxis = true;
- }
- if(hasYref && yaList.indexOf(yref) === -1) {
- yaList.push(yref);
- newAxis = true;
- }
-
- /*
- * Notice the logic here: only add a subplot for a component if
- * it's referencing both x and y axes AND it's creating a new axis
- * so for example if your plot already has xy and x2y2, an annotation
- * on x2y or xy2 will not create a new subplot.
- */
- if(newAxis && hasXref && hasYref) {
- cartesianList.push(xref + yref);
- }
- }
- }
- };
- };
-
- },{"../../lib":168,"../../registry":257}],224:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var Plots = _dereq_('../plots');
- var Drawing = _dereq_('../../components/drawing');
-
- var getModuleCalcData = _dereq_('../get_data').getModuleCalcData;
- var axisIds = _dereq_('./axis_ids');
- var constants = _dereq_('./constants');
- var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
-
- var ensureSingle = Lib.ensureSingle;
-
- function ensureSingleAndAddDatum(parent, nodeType, className) {
- return Lib.ensureSingle(parent, nodeType, className, function(s) {
- s.datum(className);
- });
- }
-
- exports.name = 'cartesian';
-
- exports.attr = ['xaxis', 'yaxis'];
-
- exports.idRoot = ['x', 'y'];
-
- exports.idRegex = constants.idRegex;
-
- exports.attrRegex = constants.attrRegex;
-
- exports.attributes = _dereq_('./attributes');
-
- exports.layoutAttributes = _dereq_('./layout_attributes');
-
- exports.supplyLayoutDefaults = _dereq_('./layout_defaults');
-
- exports.transitionAxes = _dereq_('./transition_axes');
-
- exports.finalizeSubplots = function(layoutIn, layoutOut) {
- var subplots = layoutOut._subplots;
- var xList = subplots.xaxis;
- var yList = subplots.yaxis;
- var spSVG = subplots.cartesian;
- var spAll = spSVG.concat(subplots.gl2d || []);
- var allX = {};
- var allY = {};
- var i, xi, yi;
-
- for(i = 0; i < spAll.length; i++) {
- var parts = spAll[i].split('y');
- allX[parts[0]] = 1;
- allY['y' + parts[1]] = 1;
- }
-
- // check for x axes with no subplot, and make one from the anchor of that x axis
- for(i = 0; i < xList.length; i++) {
- xi = xList[i];
- if(!allX[xi]) {
- yi = (layoutIn[axisIds.id2name(xi)] || {}).anchor;
- if(!constants.idRegex.y.test(yi)) yi = 'y';
- spSVG.push(xi + yi);
- spAll.push(xi + yi);
-
- if(!allY[yi]) {
- allY[yi] = 1;
- Lib.pushUnique(yList, yi);
- }
- }
- }
-
- // same for y axes with no subplot
- for(i = 0; i < yList.length; i++) {
- yi = yList[i];
- if(!allY[yi]) {
- xi = (layoutIn[axisIds.id2name(yi)] || {}).anchor;
- if(!constants.idRegex.x.test(xi)) xi = 'x';
- spSVG.push(xi + yi);
- spAll.push(xi + yi);
-
- if(!allX[xi]) {
- allX[xi] = 1;
- Lib.pushUnique(xList, xi);
- }
- }
- }
-
- // finally, if we've gotten here we're supposed to show cartesian...
- // so if there are NO subplots at all, make one from the first
- // x & y axes in the input layout
- if(!spAll.length) {
- xi = '';
- yi = '';
- for(var ki in layoutIn) {
- if(constants.attrRegex.test(ki)) {
- var axLetter = ki.charAt(0);
- if(axLetter === 'x') {
- if(!xi || (+ki.substr(5) < +xi.substr(5))) {
- xi = ki;
- }
- }
- else if(!yi || (+ki.substr(5) < +yi.substr(5))) {
- yi = ki;
- }
- }
- }
- xi = xi ? axisIds.name2id(xi) : 'x';
- yi = yi ? axisIds.name2id(yi) : 'y';
- xList.push(xi);
- yList.push(yi);
- spSVG.push(xi + yi);
- }
- };
-
- /**
- * Cartesian.plot
- *
- * @param {DOM div | object} gd
- * @param {array | null} (optional) traces
- * array of traces indices to plot
- * if undefined, plots all cartesian traces,
- * if null, plots no traces
- * @param {object} (optional) transitionOpts
- * transition option object
- * @param {function} (optional) makeOnCompleteCallback
- * transition make callback function from Plots.transition
- */
- exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
- var fullLayout = gd._fullLayout;
- var subplots = fullLayout._subplots.cartesian;
- var calcdata = gd.calcdata;
- var i;
-
- if(traces === null) {
- // this means no updates required, must return here
- // so that plotOne doesn't remove the trace layers
- return;
- } else if(!Array.isArray(traces)) {
- // If traces is not provided, then it's a complete replot and missing
- // traces are removed
- traces = [];
- for(i = 0; i < calcdata.length; i++) traces.push(i);
- }
-
- for(i = 0; i < subplots.length; i++) {
- var subplot = subplots[i];
- var subplotInfo = fullLayout._plots[subplot];
-
- // Get all calcdata for this subplot:
- var cdSubplot = [];
- var pcd;
-
- for(var j = 0; j < calcdata.length; j++) {
- var cd = calcdata[j];
- var trace = cd[0].trace;
-
- // Skip trace if whitelist provided and it's not whitelisted:
- // if (Array.isArray(traces) && traces.indexOf(i) === -1) continue;
- if(trace.xaxis + trace.yaxis === subplot) {
- // XXX: Should trace carpet dependencies. Only replot all carpet plots if the carpet
- // axis has actually changed:
- //
- // If this trace is specifically requested, add it to the list:
- if(traces.indexOf(trace.index) !== -1 || trace.carpet) {
- // Okay, so example: traces 0, 1, and 2 have fill = tonext. You animate
- // traces 0 and 2. Trace 1 also needs to be updated, otherwise its fill
- // is outdated. So this retroactively adds the previous trace if the
- // traces are interdependent.
- if(
- pcd &&
- pcd[0].trace.xaxis + pcd[0].trace.yaxis === subplot &&
- ['tonextx', 'tonexty', 'tonext'].indexOf(trace.fill) !== -1 &&
- cdSubplot.indexOf(pcd) === -1
- ) {
- cdSubplot.push(pcd);
- }
-
- cdSubplot.push(cd);
- }
-
- // Track the previous trace on this subplot for the retroactive-add step
- // above:
- pcd = cd;
- }
- }
-
- plotOne(gd, subplotInfo, cdSubplot, transitionOpts, makeOnCompleteCallback);
- }
- };
-
- function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback) {
- var traceLayerClasses = constants.traceLayerClasses;
- var fullLayout = gd._fullLayout;
- var modules = fullLayout._modules;
- var _module, cdModuleAndOthers, cdModule;
-
- var layerData = [];
- var zoomScaleQueryParts = [];
-
- for(var i = 0; i < modules.length; i++) {
- _module = modules[i];
- var name = _module.name;
- var categories = Registry.modules[name].categories;
-
- if(categories.svg) {
- var className = (_module.layerName || name + 'layer');
- var plotMethod = _module.plot;
-
- // plot all visible traces of this type on this subplot at once
- cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod);
- cdModule = cdModuleAndOthers[0];
- // don't need to search the found traces again - in fact we need to NOT
- // so that if two modules share the same plotter we don't double-plot
- cdSubplot = cdModuleAndOthers[1];
-
- if(cdModule.length) {
- layerData.push({
- i: traceLayerClasses.indexOf(className),
- className: className,
- plotMethod: plotMethod,
- cdModule: cdModule
- });
- }
-
- if(categories.zoomScale) {
- zoomScaleQueryParts.push('.' + className);
- }
- }
- }
-
- layerData.sort(function(a, b) { return a.i - b.i; });
-
- var layers = plotinfo.plot.selectAll('g.mlayer')
- .data(layerData, function(d) { return d.className; });
-
- layers.enter().append('g')
- .attr('class', function(d) { return d.className; })
- .classed('mlayer', true);
-
- layers.exit().remove();
-
- layers.order();
-
- layers.each(function(d) {
- var sel = d3.select(this);
- var className = d.className;
-
- d.plotMethod(
- gd, plotinfo, d.cdModule, sel,
- transitionOpts, makeOnCompleteCallback
- );
-
- // layers that allow `cliponaxis: false`
- if(className !== 'scatterlayer' && className !== 'barlayer') {
- Drawing.setClipUrl(sel, plotinfo.layerClipId, gd);
- }
- });
-
- // call Scattergl.plot separately
- if(fullLayout._has('scattergl')) {
- _module = Registry.getModule('scattergl');
- cdModule = getModuleCalcData(cdSubplot, _module)[0];
- _module.plot(gd, plotinfo, cdModule);
- }
-
- // stash "hot" selections for faster interaction on drag and scroll
- if(!gd._context.staticPlot) {
- if(plotinfo._hasClipOnAxisFalse) {
- plotinfo.clipOnAxisFalseTraces = plotinfo.plot
- .selectAll('.scatterlayer, .barlayer')
- .selectAll('.trace');
- }
-
- if(zoomScaleQueryParts.length) {
- var traces = plotinfo.plot
- .selectAll(zoomScaleQueryParts.join(','))
- .selectAll('.trace');
-
- plotinfo.zoomScalePts = traces.selectAll('path.point');
- plotinfo.zoomScaleTxt = traces.selectAll('.textpoint');
- }
- }
- }
-
- exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
- var oldPlots = oldFullLayout._plots || {};
- var newPlots = newFullLayout._plots || {};
- var oldSubplotList = oldFullLayout._subplots || {};
- var plotinfo;
- var i, k;
-
- // when going from a large splom graph to something else,
- // we need to clear <g subplot> so that the new cartesian subplot
- // can have the correct layer ordering
- if(oldFullLayout._hasOnlyLargeSploms && !newFullLayout._hasOnlyLargeSploms) {
- for(k in oldPlots) {
- plotinfo = oldPlots[k];
- if(plotinfo.plotgroup) plotinfo.plotgroup.remove();
- }
- }
-
- var hadGl = (oldFullLayout._has && oldFullLayout._has('gl'));
- var hasGl = (newFullLayout._has && newFullLayout._has('gl'));
-
- if(hadGl && !hasGl) {
- for(k in oldPlots) {
- plotinfo = oldPlots[k];
- if(plotinfo._scene) plotinfo._scene.destroy();
- }
- }
-
- // delete any titles we don't need anymore
- // check if axis list has changed, and if so clear old titles
- if(oldSubplotList.xaxis && oldSubplotList.yaxis) {
- var oldAxIDs = axisIds.listIds({_fullLayout: oldFullLayout});
- for(i = 0; i < oldAxIDs.length; i++) {
- var oldAxId = oldAxIDs[i];
- if(!newFullLayout[axisIds.id2name(oldAxId)]) {
- oldFullLayout._infolayer.selectAll('.g-' + oldAxId + 'title').remove();
- }
- }
- }
-
- // if we've gotten rid of all cartesian traces, remove all the subplot svg items
- var hadCartesian = (oldFullLayout._has && oldFullLayout._has('cartesian'));
- var hasCartesian = (newFullLayout._has && newFullLayout._has('cartesian'));
-
- if(hadCartesian && !hasCartesian) {
- purgeSubplotLayers(oldFullLayout._cartesianlayer.selectAll('.subplot'), oldFullLayout);
- oldFullLayout._defs.selectAll('.axesclip').remove();
- delete oldFullLayout._axisConstraintGroups;
- }
- // otherwise look for subplots we need to remove
- else if(oldSubplotList.cartesian) {
- for(i = 0; i < oldSubplotList.cartesian.length; i++) {
- var oldSubplotId = oldSubplotList.cartesian[i];
- if(!newPlots[oldSubplotId]) {
- var selector = '.' + oldSubplotId + ',.' + oldSubplotId + '-x,.' + oldSubplotId + '-y';
- oldFullLayout._cartesianlayer.selectAll(selector).remove();
- removeSubplotExtras(oldSubplotId, oldFullLayout);
- }
- }
- }
- };
-
- exports.drawFramework = function(gd) {
- var fullLayout = gd._fullLayout;
- var subplotData = makeSubplotData(gd);
-
- var subplotLayers = fullLayout._cartesianlayer.selectAll('.subplot')
- .data(subplotData, String);
-
- subplotLayers.enter().append('g')
- .attr('class', function(d) { return 'subplot ' + d[0]; });
-
- subplotLayers.order();
-
- subplotLayers.exit()
- .call(purgeSubplotLayers, fullLayout);
-
- subplotLayers.each(function(d) {
- var id = d[0];
- var plotinfo = fullLayout._plots[id];
-
- plotinfo.plotgroup = d3.select(this);
- makeSubplotLayer(gd, plotinfo);
-
- // make separate drag layers for each subplot,
- // but append them to paper rather than the plot groups,
- // so they end up on top of the rest
- plotinfo.draglayer = ensureSingle(fullLayout._draggers, 'g', id);
- });
- };
-
- exports.rangePlot = function(gd, plotinfo, cdSubplot) {
- makeSubplotLayer(gd, plotinfo);
- plotOne(gd, plotinfo, cdSubplot);
- Plots.style(gd);
- };
-
- function makeSubplotData(gd) {
- var fullLayout = gd._fullLayout;
- var ids = fullLayout._subplots.cartesian;
- var len = ids.length;
- var i, j, id, plotinfo, xa, ya;
-
- // split 'regular' and 'overlaying' subplots
- var regulars = [];
- var overlays = [];
-
- for(i = 0; i < len; i++) {
- id = ids[i];
- plotinfo = fullLayout._plots[id];
- xa = plotinfo.xaxis;
- ya = plotinfo.yaxis;
-
- var xa2 = xa._mainAxis;
- var ya2 = ya._mainAxis;
- var mainplot = xa2._id + ya2._id;
- var mainplotinfo = fullLayout._plots[mainplot];
- plotinfo.overlays = [];
-
- if(mainplot !== id && mainplotinfo) {
- plotinfo.mainplot = mainplot;
- plotinfo.mainplotinfo = mainplotinfo;
- overlays.push(id);
- } else {
- plotinfo.mainplot = undefined;
- plotinfo.mainPlotinfo = undefined;
- regulars.push(id);
- }
- }
-
- // fill in list of overlaying subplots in 'main plot'
- for(i = 0; i < overlays.length; i++) {
- id = overlays[i];
- plotinfo = fullLayout._plots[id];
- plotinfo.mainplotinfo.overlays.push(plotinfo);
- }
-
- // put 'regular' subplot data before 'overlaying'
- var subplotIds = regulars.concat(overlays);
- var subplotData = new Array(len);
-
- for(i = 0; i < len; i++) {
- id = subplotIds[i];
- plotinfo = fullLayout._plots[id];
- xa = plotinfo.xaxis;
- ya = plotinfo.yaxis;
-
- // use info about axis layer and overlaying pattern
- // to clean what need to be cleaned up in exit selection
- var d = [id, xa.layer, ya.layer, xa.overlaying || '', ya.overlaying || ''];
- for(j = 0; j < plotinfo.overlays.length; j++) {
- d.push(plotinfo.overlays[j].id);
- }
- subplotData[i] = d;
- }
-
- return subplotData;
- }
-
- function makeSubplotLayer(gd, plotinfo) {
- var plotgroup = plotinfo.plotgroup;
- var id = plotinfo.id;
- var xLayer = constants.layerValue2layerClass[plotinfo.xaxis.layer];
- var yLayer = constants.layerValue2layerClass[plotinfo.yaxis.layer];
- var hasOnlyLargeSploms = gd._fullLayout._hasOnlyLargeSploms;
-
- if(!plotinfo.mainplot) {
- if(hasOnlyLargeSploms) {
- // TODO could do even better
- // - we don't need plot (but we would have to mock it in lsInner
- // and other places
- // - we don't (x|y)lines and (x|y)axislayer for most subplots
- // usually just the bottom x and left y axes.
- plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above');
- plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above');
- plotinfo.xaxislayer = ensureSingle(plotgroup, 'g', 'xaxislayer-above');
- plotinfo.yaxislayer = ensureSingle(plotgroup, 'g', 'yaxislayer-above');
- }
- else {
- var backLayer = ensureSingle(plotgroup, 'g', 'layer-subplot');
- plotinfo.shapelayer = ensureSingle(backLayer, 'g', 'shapelayer');
- plotinfo.imagelayer = ensureSingle(backLayer, 'g', 'imagelayer');
-
- plotinfo.gridlayer = ensureSingle(plotgroup, 'g', 'gridlayer');
- plotinfo.zerolinelayer = ensureSingle(plotgroup, 'g', 'zerolinelayer');
-
- ensureSingle(plotgroup, 'path', 'xlines-below');
- ensureSingle(plotgroup, 'path', 'ylines-below');
- plotinfo.overlinesBelow = ensureSingle(plotgroup, 'g', 'overlines-below');
-
- ensureSingle(plotgroup, 'g', 'xaxislayer-below');
- ensureSingle(plotgroup, 'g', 'yaxislayer-below');
- plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below');
-
- plotinfo.plot = ensureSingle(plotgroup, 'g', 'plot');
- plotinfo.overplot = ensureSingle(plotgroup, 'g', 'overplot');
-
- plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above');
- plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above');
- plotinfo.overlinesAbove = ensureSingle(plotgroup, 'g', 'overlines-above');
-
- ensureSingle(plotgroup, 'g', 'xaxislayer-above');
- ensureSingle(plotgroup, 'g', 'yaxislayer-above');
- plotinfo.overaxesAbove = ensureSingle(plotgroup, 'g', 'overaxes-above');
-
- // set refs to correct layers as determined by 'axis.layer'
- plotinfo.xlines = plotgroup.select('.xlines-' + xLayer);
- plotinfo.ylines = plotgroup.select('.ylines-' + yLayer);
- plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer);
- plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer);
- }
- }
- else {
- var mainplotinfo = plotinfo.mainplotinfo;
- var mainplotgroup = mainplotinfo.plotgroup;
- var xId = id + '-x';
- var yId = id + '-y';
-
- // now make the components of overlaid subplots
- // overlays don't have backgrounds, and append all
- // their other components to the corresponding
- // extra groups of their main plots.
-
- plotinfo.gridlayer = mainplotinfo.gridlayer;
- plotinfo.zerolinelayer = mainplotinfo.zerolinelayer;
-
- ensureSingle(mainplotinfo.overlinesBelow, 'path', xId);
- ensureSingle(mainplotinfo.overlinesBelow, 'path', yId);
- ensureSingle(mainplotinfo.overaxesBelow, 'g', xId);
- ensureSingle(mainplotinfo.overaxesBelow, 'g', yId);
-
- plotinfo.plot = ensureSingle(mainplotinfo.overplot, 'g', id);
-
- ensureSingle(mainplotinfo.overlinesAbove, 'path', xId);
- ensureSingle(mainplotinfo.overlinesAbove, 'path', yId);
- ensureSingle(mainplotinfo.overaxesAbove, 'g', xId);
- ensureSingle(mainplotinfo.overaxesAbove, 'g', yId);
-
- // set refs to correct layers as determined by 'abovetraces'
- plotinfo.xlines = mainplotgroup.select('.overlines-' + xLayer).select('.' + xId);
- plotinfo.ylines = mainplotgroup.select('.overlines-' + yLayer).select('.' + yId);
- plotinfo.xaxislayer = mainplotgroup.select('.overaxes-' + xLayer).select('.' + xId);
- plotinfo.yaxislayer = mainplotgroup.select('.overaxes-' + yLayer).select('.' + yId);
- }
-
- // common attributes for all subplots, overlays or not
-
- if(!hasOnlyLargeSploms) {
- ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id);
- ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id);
- plotinfo.gridlayer.selectAll('g')
- .map(function(d) { return d[0]; })
- .sort(axisIds.idSort);
- }
-
- plotinfo.xlines
- .style('fill', 'none')
- .classed('crisp', true);
-
- plotinfo.ylines
- .style('fill', 'none')
- .classed('crisp', true);
- }
-
- function purgeSubplotLayers(layers, fullLayout) {
- if(!layers) return;
-
- var overlayIdsToRemove = {};
-
- layers.each(function(d) {
- var id = d[0];
- var plotgroup = d3.select(this);
-
- plotgroup.remove();
- removeSubplotExtras(id, fullLayout);
- overlayIdsToRemove[id] = true;
-
- // do not remove individual axis <clipPath>s here
- // as other subplots may need them
- });
-
- // must remove overlaid subplot trace layers 'manually'
-
- for(var k in fullLayout._plots) {
- var subplotInfo = fullLayout._plots[k];
- var overlays = subplotInfo.overlays || [];
-
- for(var j = 0; j < overlays.length; j++) {
- var overlayInfo = overlays[j];
-
- if(overlayIdsToRemove[overlayInfo.id]) {
- overlayInfo.plot.selectAll('.trace').remove();
- }
- }
- }
- }
-
- function removeSubplotExtras(subplotId, fullLayout) {
- fullLayout._draggers.selectAll('g.' + subplotId).remove();
- fullLayout._defs.select('#clip' + fullLayout._uid + subplotId + 'plot').remove();
- }
-
- exports.toSVG = function(gd) {
- var imageRoot = gd._fullLayout._glimages;
- var root = d3.select(gd).selectAll('.svg-container');
- var canvases = root.filter(function(d, i) {return i === root.size() - 1;})
- .selectAll('.gl-canvas-context, .gl-canvas-focus');
-
- function canvasToImage() {
- var canvas = this;
- var imageData = canvas.toDataURL('image/png');
- var image = imageRoot.append('svg:image');
-
- image.attr({
- xmlns: xmlnsNamespaces.svg,
- 'xlink:href': imageData,
- preserveAspectRatio: 'none',
- x: 0,
- y: 0,
- width: canvas.width,
- height: canvas.height
- });
- }
-
- canvases.each(canvasToImage);
- };
-
- exports.updateFx = _dereq_('./graph_interact').updateFx;
-
- },{"../../components/drawing":72,"../../constants/xmlns_namespaces":150,"../../lib":168,"../../registry":257,"../get_data":241,"../plots":245,"./attributes":210,"./axis_ids":215,"./constants":218,"./graph_interact":222,"./layout_attributes":225,"./layout_defaults":226,"./transition_axes":235,"d3":16}],225:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var fontAttrs = _dereq_('../font_attributes');
- var colorAttrs = _dereq_('../../components/color/attributes');
- var dash = _dereq_('../../components/drawing/attributes').dash;
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
- var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
-
- var constants = _dereq_('./constants');
-
-
- module.exports = {
- visible: {
- valType: 'boolean',
-
- editType: 'plot',
-
- },
- color: {
- valType: 'color',
- dflt: colorAttrs.defaultLine,
-
- editType: 'ticks',
-
- },
- title: {
- text: {
- valType: 'string',
-
- editType: 'ticks',
-
- },
- font: fontAttrs({
- editType: 'ticks',
-
- }),
- editType: 'ticks'
- },
- type: {
- valType: 'enumerated',
- // '-' means we haven't yet run autotype or couldn't find any data
- // it gets turned into linear in gd._fullLayout but not copied back
- // to gd.data like the others are.
- values: ['-', 'linear', 'log', 'date', 'category', 'multicategory'],
- dflt: '-',
-
- editType: 'calc',
- // we forget when an axis has been autotyped, just writing the auto
- // value back to the input - so it doesn't make sense to template this.
- // Note: we do NOT prohibit this in `coerce`, so if someone enters a
- // type in the template explicitly it will be honored as the default.
- _noTemplating: true,
-
- },
- autorange: {
- valType: 'enumerated',
- values: [true, false, 'reversed'],
- dflt: true,
-
- editType: 'axrange',
- impliedEdits: {'range[0]': undefined, 'range[1]': undefined},
-
- },
- rangemode: {
- valType: 'enumerated',
- values: ['normal', 'tozero', 'nonnegative'],
- dflt: 'normal',
-
- editType: 'plot',
-
- },
- range: {
- valType: 'info_array',
-
- items: [
- {valType: 'any', editType: 'axrange', impliedEdits: {'^autorange': false}, anim: true},
- {valType: 'any', editType: 'axrange', impliedEdits: {'^autorange': false}, anim: true}
- ],
- editType: 'axrange',
- impliedEdits: {'autorange': false},
- anim: true,
-
- },
- fixedrange: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'calc',
-
- },
- // scaleanchor: not used directly, just put here for reference
- // values are any opposite-letter axis id
- scaleanchor: {
- valType: 'enumerated',
- values: [
- constants.idRegex.x.toString(),
- constants.idRegex.y.toString()
- ],
-
- editType: 'plot',
-
- },
- scaleratio: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
- editType: 'plot',
-
- },
- constrain: {
- valType: 'enumerated',
- values: ['range', 'domain'],
- dflt: 'range',
-
- editType: 'plot',
-
- },
- // constraintoward: not used directly, just put here for reference
- constraintoward: {
- valType: 'enumerated',
- values: ['left', 'center', 'right', 'top', 'middle', 'bottom'],
-
- editType: 'plot',
-
- },
- // ticks
- tickmode: {
- valType: 'enumerated',
- values: ['auto', 'linear', 'array'],
-
- editType: 'ticks',
- impliedEdits: {tick0: undefined, dtick: undefined},
-
- },
- nticks: {
- valType: 'integer',
- min: 0,
- dflt: 0,
-
- editType: 'ticks',
-
- },
- tick0: {
- valType: 'any',
-
- editType: 'ticks',
- impliedEdits: {tickmode: 'linear'},
-
- },
- dtick: {
- valType: 'any',
-
- editType: 'ticks',
- impliedEdits: {tickmode: 'linear'},
-
- },
- tickvals: {
- valType: 'data_array',
- editType: 'ticks',
-
- },
- ticktext: {
- valType: 'data_array',
- editType: 'ticks',
-
- },
- ticks: {
- valType: 'enumerated',
- values: ['outside', 'inside', ''],
-
- editType: 'ticks',
-
- },
- tickson: {
- valType: 'enumerated',
- values: ['labels', 'boundaries'],
-
- dflt: 'labels',
- editType: 'ticks',
-
- },
- mirror: {
- valType: 'enumerated',
- values: [true, 'ticks', false, 'all', 'allticks'],
- dflt: false,
-
- editType: 'ticks+layoutstyle',
-
- },
- ticklen: {
- valType: 'number',
- min: 0,
- dflt: 5,
-
- editType: 'ticks',
-
- },
- tickwidth: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
- editType: 'ticks',
-
- },
- tickcolor: {
- valType: 'color',
- dflt: colorAttrs.defaultLine,
-
- editType: 'ticks',
-
- },
- showticklabels: {
- valType: 'boolean',
- dflt: true,
-
- editType: 'ticks',
-
- },
- automargin: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'ticks',
-
- },
- showspikes: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'modebar',
-
- },
- spikecolor: {
- valType: 'color',
- dflt: null,
-
- editType: 'none',
-
- },
- spikethickness: {
- valType: 'number',
- dflt: 3,
-
- editType: 'none',
-
- },
- spikedash: extendFlat({}, dash, {dflt: 'dash', editType: 'none'}),
- spikemode: {
- valType: 'flaglist',
- flags: ['toaxis', 'across', 'marker'],
-
- dflt: 'toaxis',
- editType: 'none',
-
- },
- spikesnap: {
- valType: 'enumerated',
- values: ['data', 'cursor'],
- dflt: 'data',
-
- editType: 'none',
-
- },
- tickfont: fontAttrs({
- editType: 'ticks',
-
- }),
- tickangle: {
- valType: 'angle',
- dflt: 'auto',
-
- editType: 'ticks',
-
- },
- tickprefix: {
- valType: 'string',
- dflt: '',
-
- editType: 'ticks',
-
- },
- showtickprefix: {
- valType: 'enumerated',
- values: ['all', 'first', 'last', 'none'],
- dflt: 'all',
-
- editType: 'ticks',
-
- },
- ticksuffix: {
- valType: 'string',
- dflt: '',
-
- editType: 'ticks',
-
- },
- showticksuffix: {
- valType: 'enumerated',
- values: ['all', 'first', 'last', 'none'],
- dflt: 'all',
-
- editType: 'ticks',
-
- },
- showexponent: {
- valType: 'enumerated',
- values: ['all', 'first', 'last', 'none'],
- dflt: 'all',
-
- editType: 'ticks',
-
- },
- exponentformat: {
- valType: 'enumerated',
- values: ['none', 'e', 'E', 'power', 'SI', 'B'],
- dflt: 'B',
-
- editType: 'ticks',
-
- },
- separatethousands: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'ticks',
-
- },
- tickformat: {
- valType: 'string',
- dflt: '',
-
- editType: 'ticks',
-
- },
- tickformatstops: templatedArray('tickformatstop', {
- enabled: {
- valType: 'boolean',
-
- dflt: true,
- editType: 'ticks',
-
- },
- dtickrange: {
- valType: 'info_array',
-
- items: [
- {valType: 'any', editType: 'ticks'},
- {valType: 'any', editType: 'ticks'}
- ],
- editType: 'ticks',
-
- },
- value: {
- valType: 'string',
- dflt: '',
-
- editType: 'ticks',
-
- },
- editType: 'ticks'
- }),
- hoverformat: {
- valType: 'string',
- dflt: '',
-
- editType: 'none',
-
- },
- // lines and grids
- showline: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'ticks+layoutstyle',
-
- },
- linecolor: {
- valType: 'color',
- dflt: colorAttrs.defaultLine,
-
- editType: 'layoutstyle',
-
- },
- linewidth: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
- editType: 'ticks+layoutstyle',
-
- },
- showgrid: {
- valType: 'boolean',
-
- editType: 'ticks',
-
- },
- gridcolor: {
- valType: 'color',
- dflt: colorAttrs.lightLine,
-
- editType: 'ticks',
-
- },
- gridwidth: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
- editType: 'ticks',
-
- },
- zeroline: {
- valType: 'boolean',
-
- editType: 'ticks',
-
- },
- zerolinecolor: {
- valType: 'color',
- dflt: colorAttrs.defaultLine,
-
- editType: 'ticks',
-
- },
- zerolinewidth: {
- valType: 'number',
- dflt: 1,
-
- editType: 'ticks',
-
- },
-
- showdividers: {
- valType: 'boolean',
- dflt: true,
-
- editType: 'ticks',
-
- },
- dividercolor: {
- valType: 'color',
- dflt: colorAttrs.defaultLine,
-
- editType: 'ticks',
-
- },
- dividerwidth: {
- valType: 'number',
- dflt: 1,
-
- editType: 'ticks',
-
- },
- // TODO dividerlen: that would override "to label base" length?
-
- // positioning attributes
- // anchor: not used directly, just put here for reference
- // values are any opposite-letter axis id
- anchor: {
- valType: 'enumerated',
- values: [
- 'free',
- constants.idRegex.x.toString(),
- constants.idRegex.y.toString()
- ],
-
- editType: 'plot',
-
- },
- // side: not used directly, as values depend on direction
- // values are top, bottom for x axes, and left, right for y
- side: {
- valType: 'enumerated',
- values: ['top', 'bottom', 'left', 'right'],
-
- editType: 'plot',
-
- },
- // overlaying: not used directly, just put here for reference
- // values are false and any other same-letter axis id that's not
- // itself overlaying anything
- overlaying: {
- valType: 'enumerated',
- values: [
- 'free',
- constants.idRegex.x.toString(),
- constants.idRegex.y.toString()
- ],
-
- editType: 'plot',
-
- },
- layer: {
- valType: 'enumerated',
- values: ['above traces', 'below traces'],
- dflt: 'above traces',
-
- editType: 'plot',
-
- },
- domain: {
- valType: 'info_array',
-
- items: [
- {valType: 'number', min: 0, max: 1, editType: 'plot'},
- {valType: 'number', min: 0, max: 1, editType: 'plot'}
- ],
- dflt: [0, 1],
- editType: 'plot',
-
- },
- position: {
- valType: 'number',
- min: 0,
- max: 1,
- dflt: 0,
-
- editType: 'plot',
-
- },
- categoryorder: {
- valType: 'enumerated',
- values: [
- 'trace', 'category ascending', 'category descending', 'array'
- /* , 'value ascending', 'value descending'*/ // value ascending / descending to be implemented later
- ],
- dflt: 'trace',
-
- editType: 'calc',
-
- },
- categoryarray: {
- valType: 'data_array',
-
- editType: 'calc',
-
- },
- uirevision: {
- valType: 'any',
-
- editType: 'none',
-
- },
- editType: 'calc',
-
- _deprecated: {
- autotick: {
- valType: 'boolean',
-
- editType: 'ticks',
-
- },
- title: {
- valType: 'string',
-
- editType: 'ticks',
-
- },
- titlefont: fontAttrs({
- editType: 'ticks',
-
- })
- }
- };
-
- },{"../../components/color/attributes":50,"../../components/drawing/attributes":71,"../../lib/extend":162,"../../plot_api/plot_template":202,"../font_attributes":239,"./constants":218}],226:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Color = _dereq_('../../components/color');
- var Template = _dereq_('../../plot_api/plot_template');
- var basePlotLayoutAttributes = _dereq_('../layout_attributes');
-
- var layoutAttributes = _dereq_('./layout_attributes');
- var handleTypeDefaults = _dereq_('./type_defaults');
- var handleAxisDefaults = _dereq_('./axis_defaults');
- var handleConstraintDefaults = _dereq_('./constraint_defaults');
- var handlePositionDefaults = _dereq_('./position_defaults');
-
- var axisIds = _dereq_('./axis_ids');
- var id2name = axisIds.id2name;
- var name2id = axisIds.name2id;
-
- var Registry = _dereq_('../../registry');
- var traceIs = Registry.traceIs;
- var getComponentMethod = Registry.getComponentMethod;
-
- function appendList(cont, k, item) {
- if(Array.isArray(cont[k])) cont[k].push(item);
- else cont[k] = [item];
- }
-
- module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
- var ax2traces = {};
- var xaCheater = {};
- var xaNonCheater = {};
- var outerTicks = {};
- var noGrids = {};
- var i, j;
-
- // look for axes in the data
- for(i = 0; i < fullData.length; i++) {
- var trace = fullData[i];
- if(!traceIs(trace, 'cartesian') && !traceIs(trace, 'gl2d')) continue;
-
- var xaName;
- if(trace.xaxis) {
- xaName = id2name(trace.xaxis);
- appendList(ax2traces, xaName, trace);
- } else if(trace.xaxes) {
- for(j = 0; j < trace.xaxes.length; j++) {
- appendList(ax2traces, id2name(trace.xaxes[j]), trace);
- }
- }
-
- var yaName;
- if(trace.yaxis) {
- yaName = id2name(trace.yaxis);
- appendList(ax2traces, yaName, trace);
- } else if(trace.yaxes) {
- for(j = 0; j < trace.yaxes.length; j++) {
- appendList(ax2traces, id2name(trace.yaxes[j]), trace);
- }
- }
-
- // Two things trigger axis visibility:
- // 1. is not carpet
- // 2. carpet that's not cheater
- if(!traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) {
- if(xaName) xaNonCheater[xaName] = 1;
- }
-
- // The above check for definitely-not-cheater is not adequate. This
- // second list tracks which axes *could* be a cheater so that the
- // full condition triggering hiding is:
- // *could* be a cheater and *is not definitely visible*
- if(trace.type === 'carpet' && trace._cheater) {
- if(xaName) xaCheater[xaName] = 1;
- }
-
- // check for default formatting tweaks
- if(traceIs(trace, '2dMap')) {
- outerTicks[xaName] = 1;
- outerTicks[yaName] = 1;
- }
-
- if(traceIs(trace, 'oriented')) {
- var positionAxis = trace.orientation === 'h' ? yaName : xaName;
- noGrids[positionAxis] = 1;
- }
- }
-
- var subplots = layoutOut._subplots;
- var xIds = subplots.xaxis;
- var yIds = subplots.yaxis;
- var xNames = Lib.simpleMap(xIds, id2name);
- var yNames = Lib.simpleMap(yIds, id2name);
- var axNames = xNames.concat(yNames);
-
- // plot_bgcolor only makes sense if there's a (2D) plot!
- // TODO: bgcolor for each subplot, to inherit from the main one
- var plotBgColor = Color.background;
- if(xIds.length && yIds.length) {
- plotBgColor = Lib.coerce(layoutIn, layoutOut, basePlotLayoutAttributes, 'plot_bgcolor');
- }
-
- var bgColor = Color.combine(plotBgColor, layoutOut.paper_bgcolor);
-
- var axName, axLetter, axLayoutIn, axLayoutOut;
-
- function coerce(attr, dflt) {
- return Lib.coerce(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
- }
-
- function coerce2(attr, dflt) {
- return Lib.coerce2(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
- }
-
- function getCounterAxes(axLetter) {
- return (axLetter === 'x') ? yIds : xIds;
- }
-
- var counterAxes = {x: getCounterAxes('x'), y: getCounterAxes('y')};
-
- function getOverlayableAxes(axLetter, axName) {
- var list = (axLetter === 'x') ? xNames : yNames;
- var out = [];
-
- for(var j = 0; j < list.length; j++) {
- var axName2 = list[j];
-
- if(axName2 !== axName && !(layoutIn[axName2] || {}).overlaying) {
- out.push(name2id(axName2));
- }
- }
-
- return out;
- }
-
- // first pass creates the containers, determines types, and handles most of the settings
- for(i = 0; i < axNames.length; i++) {
- axName = axNames[i];
- axLetter = axName.charAt(0);
-
- if(!Lib.isPlainObject(layoutIn[axName])) {
- layoutIn[axName] = {};
- }
-
- axLayoutIn = layoutIn[axName];
- axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis');
-
- var traces = ax2traces[axName] || [];
- axLayoutOut._traceIndices = traces.map(function(t) { return t._expandedIndex; });
- axLayoutOut._annIndices = [];
- axLayoutOut._shapeIndices = [];
- axLayoutOut._imgIndices = [];
- axLayoutOut._subplotsWith = [];
- axLayoutOut._counterAxes = [];
-
- // set up some private properties
- axLayoutOut._name = axLayoutOut._attr = axName;
- var id = axLayoutOut._id = name2id(axName);
-
- var overlayableAxes = getOverlayableAxes(axLetter, axName);
-
- var defaultOptions = {
- letter: axLetter,
- font: layoutOut.font,
- outerTicks: outerTicks[axName],
- showGrid: !noGrids[axName],
- data: traces,
- bgColor: bgColor,
- calendar: layoutOut.calendar,
- automargin: true,
- cheateronly: axLetter === 'x' && xaCheater[axName] && !xaNonCheater[axName],
- splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[id]
- };
-
- coerce('uirevision', layoutOut.uirevision);
-
- handleTypeDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions);
- handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions, layoutOut);
-
- var spikecolor = coerce2('spikecolor');
- var spikethickness = coerce2('spikethickness');
- var spikedash = coerce2('spikedash');
- var spikemode = coerce2('spikemode');
- var spikesnap = coerce2('spikesnap');
- var showSpikes = coerce('showspikes', !!spikecolor || !!spikethickness || !!spikedash || !!spikemode || !!spikesnap);
-
- if(!showSpikes) {
- delete axLayoutOut.spikecolor;
- delete axLayoutOut.spikethickness;
- delete axLayoutOut.spikedash;
- delete axLayoutOut.spikemode;
- delete axLayoutOut.spikesnap;
- }
-
- var positioningOptions = {
- letter: axLetter,
- counterAxes: counterAxes[axLetter],
- overlayableAxes: overlayableAxes,
- grid: layoutOut.grid
- };
-
- handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, positioningOptions);
-
- axLayoutOut._input = axLayoutIn;
- }
-
- // quick second pass for range slider and selector defaults
- var rangeSliderDefaults = getComponentMethod('rangeslider', 'handleDefaults');
- var rangeSelectorDefaults = getComponentMethod('rangeselector', 'handleDefaults');
-
- for(i = 0; i < xNames.length; i++) {
- axName = xNames[i];
- axLayoutIn = layoutIn[axName];
- axLayoutOut = layoutOut[axName];
-
- rangeSliderDefaults(layoutIn, layoutOut, axName);
-
- if(axLayoutOut.type === 'date') {
- rangeSelectorDefaults(
- axLayoutIn,
- axLayoutOut,
- layoutOut,
- yNames,
- axLayoutOut.calendar
- );
- }
-
- coerce('fixedrange');
- }
-
- for(i = 0; i < yNames.length; i++) {
- axName = yNames[i];
- axLayoutIn = layoutIn[axName];
- axLayoutOut = layoutOut[axName];
-
- var anchoredAxis = layoutOut[id2name(axLayoutOut.anchor)];
-
- var fixedRangeDflt = getComponentMethod('rangeslider', 'isVisible')(anchoredAxis);
-
- coerce('fixedrange', fixedRangeDflt);
- }
-
- // Finally, handle scale constraints. We need to do this after all axes have
- // coerced both `type` (so we link only axes of the same type) and
- // `fixedrange` (so we can avoid linking from OR TO a fixed axis).
-
- // sets of axes linked by `scaleanchor` along with the scaleratios compounded
- // together, populated in handleConstraintDefaults
- layoutOut._axisConstraintGroups = [];
- var allAxisIds = counterAxes.x.concat(counterAxes.y);
-
- for(i = 0; i < axNames.length; i++) {
- axName = axNames[i];
- axLetter = axName.charAt(0);
-
- axLayoutIn = layoutIn[axName];
- axLayoutOut = layoutOut[axName];
-
- handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, allAxisIds, layoutOut);
- }
- };
-
- },{"../../components/color":51,"../../lib":168,"../../plot_api/plot_template":202,"../../registry":257,"../layout_attributes":243,"./axis_defaults":214,"./axis_ids":215,"./constraint_defaults":219,"./layout_attributes":225,"./position_defaults":228,"./type_defaults":236}],227:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var colorMix = _dereq_('tinycolor2').mix;
- var lightFraction = _dereq_('../../components/color/attributes').lightFraction;
- var Lib = _dereq_('../../lib');
-
- /**
- * @param {object} opts :
- * - dfltColor {string} : default axis color
- * - bgColor {string} : combined subplot bg color
- * - blend {number, optional} : blend percentage (to compute dflt grid color)
- * - showLine {boolean} : show line by default
- * - showGrid {boolean} : show grid by default
- * - noZeroLine {boolean} : don't coerce zeroline* attributes
- * - attributes {object} : attribute object associated with input containers
- */
- module.exports = function handleLineGridDefaults(containerIn, containerOut, coerce, opts) {
- opts = opts || {};
-
- var dfltColor = opts.dfltColor;
-
- function coerce2(attr, dflt) {
- return Lib.coerce2(containerIn, containerOut, opts.attributes, attr, dflt);
- }
-
- var lineColor = coerce2('linecolor', dfltColor);
- var lineWidth = coerce2('linewidth');
- var showLine = coerce('showline', opts.showLine || !!lineColor || !!lineWidth);
-
- if(!showLine) {
- delete containerOut.linecolor;
- delete containerOut.linewidth;
- }
-
- var gridColorDflt = colorMix(dfltColor, opts.bgColor, opts.blend || lightFraction).toRgbString();
- var gridColor = coerce2('gridcolor', gridColorDflt);
- var gridWidth = coerce2('gridwidth');
- var showGridLines = coerce('showgrid', opts.showGrid || !!gridColor || !!gridWidth);
-
- if(!showGridLines) {
- delete containerOut.gridcolor;
- delete containerOut.gridwidth;
- }
-
- if(!opts.noZeroLine) {
- var zeroLineColor = coerce2('zerolinecolor', dfltColor);
- var zeroLineWidth = coerce2('zerolinewidth');
- var showZeroLine = coerce('zeroline', opts.showGrid || !!zeroLineColor || !!zeroLineWidth);
-
- if(!showZeroLine) {
- delete containerOut.zerolinecolor;
- delete containerOut.zerolinewidth;
- }
- }
- };
-
- },{"../../components/color/attributes":50,"../../lib":168,"tinycolor2":34}],228:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Lib = _dereq_('../../lib');
-
-
- module.exports = function handlePositionDefaults(containerIn, containerOut, coerce, options) {
- var counterAxes = options.counterAxes || [];
- var overlayableAxes = options.overlayableAxes || [];
- var letter = options.letter;
- var grid = options.grid;
-
- var dfltAnchor, dfltDomain, dfltSide, dfltPosition;
-
- if(grid) {
- dfltDomain = grid._domains[letter][grid._axisMap[containerOut._id]];
- dfltAnchor = grid._anchors[containerOut._id];
- if(dfltDomain) {
- dfltSide = grid[letter + 'side'].split(' ')[0];
- dfltPosition = grid.domain[letter][dfltSide === 'right' || dfltSide === 'top' ? 1 : 0];
- }
- }
-
- // Even if there's a grid, this axis may not be in it - fall back on non-grid defaults
- dfltDomain = dfltDomain || [0, 1];
- dfltAnchor = dfltAnchor || (isNumeric(containerIn.position) ? 'free' : (counterAxes[0] || 'free'));
- dfltSide = dfltSide || (letter === 'x' ? 'bottom' : 'left');
- dfltPosition = dfltPosition || 0;
-
- var anchor = Lib.coerce(containerIn, containerOut, {
- anchor: {
- valType: 'enumerated',
- values: ['free'].concat(counterAxes),
- dflt: dfltAnchor
- }
- }, 'anchor');
-
- if(anchor === 'free') coerce('position', dfltPosition);
-
- Lib.coerce(containerIn, containerOut, {
- side: {
- valType: 'enumerated',
- values: letter === 'x' ? ['bottom', 'top'] : ['left', 'right'],
- dflt: dfltSide
- }
- }, 'side');
-
- var overlaying = false;
- if(overlayableAxes.length) {
- overlaying = Lib.coerce(containerIn, containerOut, {
- overlaying: {
- valType: 'enumerated',
- values: [false].concat(overlayableAxes),
- dflt: false
- }
- }, 'overlaying');
- }
-
- if(!overlaying) {
- // TODO: right now I'm copying this domain over to overlaying axes
- // in ax.setscale()... but this means we still need (imperfect) logic
- // in the axes popover to hide domain for the overlaying axis.
- // perhaps I should make a private version _domain that all axes get???
- var domain = coerce('domain', dfltDomain);
-
- // according to https://www.npmjs.com/package/canvas-size
- // the minimum value of max canvas width across browsers and devices is 4096
- // which applied in the calculation below:
- if(domain[0] > domain[1] - 1 / 4096) containerOut.domain = dfltDomain;
- Lib.noneOrAll(containerIn.domain, containerOut.domain, dfltDomain);
- }
-
- coerce('layer');
-
- return containerOut;
- };
-
- },{"../../lib":168,"fast-isnumeric":18}],229:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var FROM_BL = _dereq_('../../constants/alignment').FROM_BL;
-
- module.exports = function scaleZoom(ax, factor, centerFraction) {
- if(centerFraction === undefined) {
- centerFraction = FROM_BL[ax.constraintoward || 'center'];
- }
-
- var rangeLinear = [ax.r2l(ax.range[0]), ax.r2l(ax.range[1])];
- var center = rangeLinear[0] + (rangeLinear[1] - rangeLinear[0]) * centerFraction;
-
- ax.range = ax._input.range = [
- ax.l2r(center + (rangeLinear[0] - center) * factor),
- ax.l2r(center + (rangeLinear[1] - center) * factor)
- ];
- };
-
- },{"../../constants/alignment":146}],230:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var polybool = _dereq_('polybooljs');
-
- var Registry = _dereq_('../../registry');
- var Color = _dereq_('../../components/color');
- var Fx = _dereq_('../../components/fx');
-
- var polygon = _dereq_('../../lib/polygon');
- var throttle = _dereq_('../../lib/throttle');
- var makeEventData = _dereq_('../../components/fx/helpers').makeEventData;
- var getFromId = _dereq_('./axis_ids').getFromId;
- var clearGlCanvases = _dereq_('../../lib/clear_gl_canvases');
- var redrawReglTraces = _dereq_('../../plot_api/subroutines').redrawReglTraces;
-
- var constants = _dereq_('./constants');
- var MINSELECT = constants.MINSELECT;
-
- var filteredPolygon = polygon.filter;
- var polygonTester = polygon.tester;
-
- function getAxId(ax) { return ax._id; }
-
- function prepSelect(e, startX, startY, dragOptions, mode) {
- var gd = dragOptions.gd;
- var fullLayout = gd._fullLayout;
- var zoomLayer = fullLayout._zoomlayer;
- var dragBBox = dragOptions.element.getBoundingClientRect();
- var plotinfo = dragOptions.plotinfo;
- var xs = plotinfo.xaxis._offset;
- var ys = plotinfo.yaxis._offset;
- var x0 = startX - dragBBox.left;
- var y0 = startY - dragBBox.top;
- var x1 = x0;
- var y1 = y0;
- var path0 = 'M' + x0 + ',' + y0;
- var pw = dragOptions.xaxes[0]._length;
- var ph = dragOptions.yaxes[0]._length;
- var allAxes = dragOptions.xaxes.concat(dragOptions.yaxes);
- var subtract = e.altKey;
-
- var filterPoly, selectionTester, mergedPolygons, currentPolygon;
- var i, searchInfo, eventData;
-
- coerceSelectionsCache(e, gd, dragOptions);
-
- if(mode === 'lasso') {
- filterPoly = filteredPolygon([[x0, y0]], constants.BENDPX);
- }
-
- var outlines = zoomLayer.selectAll('path.select-outline-' + plotinfo.id).data([1, 2]);
-
- outlines.enter()
- .append('path')
- .attr('class', function(d) { return 'select-outline select-outline-' + d + ' select-outline-' + plotinfo.id; })
- .attr('transform', 'translate(' + xs + ', ' + ys + ')')
- .attr('d', path0 + 'Z');
-
- var corners = zoomLayer.append('path')
- .attr('class', 'zoombox-corners')
- .style({
- fill: Color.background,
- stroke: Color.defaultLine,
- 'stroke-width': 1
- })
- .attr('transform', 'translate(' + xs + ', ' + ys + ')')
- .attr('d', 'M0,0Z');
-
-
- var throttleID = fullLayout._uid + constants.SELECTID;
- var selection = [];
-
- // find the traces to search for selection points
- var searchTraces = determineSearchTraces(gd, dragOptions.xaxes,
- dragOptions.yaxes, dragOptions.subplot);
-
- function axValue(ax) {
- var index = (ax._id.charAt(0) === 'y') ? 1 : 0;
- return function(v) { return ax.p2d(v[index]); };
- }
-
- function ascending(a, b) { return a - b; }
-
- // allow subplots to override fillRangeItems routine
- var fillRangeItems;
-
- if(plotinfo.fillRangeItems) {
- fillRangeItems = plotinfo.fillRangeItems;
- } else {
- if(mode === 'select') {
- fillRangeItems = function(eventData, poly) {
- var ranges = eventData.range = {};
-
- for(i = 0; i < allAxes.length; i++) {
- var ax = allAxes[i];
- var axLetter = ax._id.charAt(0);
-
- ranges[ax._id] = [
- ax.p2d(poly[axLetter + 'min']),
- ax.p2d(poly[axLetter + 'max'])
- ].sort(ascending);
- }
- };
- } else {
- fillRangeItems = function(eventData, poly, filterPoly) {
- var dataPts = eventData.lassoPoints = {};
-
- for(i = 0; i < allAxes.length; i++) {
- var ax = allAxes[i];
- dataPts[ax._id] = filterPoly.filtered.map(axValue(ax));
- }
- };
- }
- }
-
- dragOptions.moveFn = function(dx0, dy0) {
- x1 = Math.max(0, Math.min(pw, dx0 + x0));
- y1 = Math.max(0, Math.min(ph, dy0 + y0));
-
- var dx = Math.abs(x1 - x0);
- var dy = Math.abs(y1 - y0);
-
- if(mode === 'select') {
- var direction = fullLayout.selectdirection;
-
- if(fullLayout.selectdirection === 'any') {
- if(dy < Math.min(dx * 0.6, MINSELECT)) direction = 'h';
- else if(dx < Math.min(dy * 0.6, MINSELECT)) direction = 'v';
- else direction = 'd';
- }
- else {
- direction = fullLayout.selectdirection;
- }
-
- if(direction === 'h') {
- // horizontal motion: make a vertical box
- currentPolygon = [[x0, 0], [x0, ph], [x1, ph], [x1, 0]];
- currentPolygon.xmin = Math.min(x0, x1);
- currentPolygon.xmax = Math.max(x0, x1);
- currentPolygon.ymin = Math.min(0, ph);
- currentPolygon.ymax = Math.max(0, ph);
- // extras to guide users in keeping a straight selection
- corners.attr('d', 'M' + currentPolygon.xmin + ',' + (y0 - MINSELECT) +
- 'h-4v' + (2 * MINSELECT) + 'h4Z' +
- 'M' + (currentPolygon.xmax - 1) + ',' + (y0 - MINSELECT) +
- 'h4v' + (2 * MINSELECT) + 'h-4Z');
-
- }
- else if(direction === 'v') {
- // vertical motion: make a horizontal box
- currentPolygon = [[0, y0], [0, y1], [pw, y1], [pw, y0]];
- currentPolygon.xmin = Math.min(0, pw);
- currentPolygon.xmax = Math.max(0, pw);
- currentPolygon.ymin = Math.min(y0, y1);
- currentPolygon.ymax = Math.max(y0, y1);
- corners.attr('d', 'M' + (x0 - MINSELECT) + ',' + currentPolygon.ymin +
- 'v-4h' + (2 * MINSELECT) + 'v4Z' +
- 'M' + (x0 - MINSELECT) + ',' + (currentPolygon.ymax - 1) +
- 'v4h' + (2 * MINSELECT) + 'v-4Z');
- }
- else if(direction === 'd') {
- // diagonal motion
- currentPolygon = [[x0, y0], [x0, y1], [x1, y1], [x1, y0]];
- currentPolygon.xmin = Math.min(x0, x1);
- currentPolygon.xmax = Math.max(x0, x1);
- currentPolygon.ymin = Math.min(y0, y1);
- currentPolygon.ymax = Math.max(y0, y1);
- corners.attr('d', 'M0,0Z');
- }
- }
- else if(mode === 'lasso') {
- filterPoly.addPt([x1, y1]);
- currentPolygon = filterPoly.filtered;
- }
-
- // create outline & tester
- if(dragOptions.selectionDefs && dragOptions.selectionDefs.length) {
- mergedPolygons = mergePolygons(dragOptions.mergedPolygons, currentPolygon, subtract);
- currentPolygon.subtract = subtract;
- selectionTester = multiTester(dragOptions.selectionDefs.concat([currentPolygon]));
- }
- else {
- mergedPolygons = [currentPolygon];
- selectionTester = polygonTester(currentPolygon);
- }
-
- // draw selection
- drawSelection(mergedPolygons, outlines);
-
-
- throttle.throttle(
- throttleID,
- constants.SELECTDELAY,
- function() {
- selection = [];
-
- var thisSelection;
- var traceSelections = [];
- var traceSelection;
- for(i = 0; i < searchTraces.length; i++) {
- searchInfo = searchTraces[i];
-
- traceSelection = searchInfo._module.selectPoints(searchInfo, selectionTester);
- traceSelections.push(traceSelection);
-
- thisSelection = fillSelectionItem(traceSelection, searchInfo);
-
- if(selection.length) {
- for(var j = 0; j < thisSelection.length; j++) {
- selection.push(thisSelection[j]);
- }
- }
- else selection = thisSelection;
- }
-
- eventData = {points: selection};
- updateSelectedState(gd, searchTraces, eventData);
- fillRangeItems(eventData, currentPolygon, filterPoly);
- dragOptions.gd.emit('plotly_selecting', eventData);
- }
- );
- };
-
- dragOptions.clickFn = function(numClicks, evt) {
- var clickmode = fullLayout.clickmode;
-
- corners.remove();
-
- throttle.done(throttleID).then(function() {
- throttle.clear(throttleID);
- if(numClicks === 2) {
- // clear selection on doubleclick
- outlines.remove();
- for(i = 0; i < searchTraces.length; i++) {
- searchInfo = searchTraces[i];
- searchInfo._module.selectPoints(searchInfo, false);
- }
-
- updateSelectedState(gd, searchTraces);
-
- clearSelectionsCache(dragOptions);
-
- gd.emit('plotly_deselect', null);
- } else {
- if(clickmode.indexOf('select') > -1) {
- selectOnClick(evt, gd, dragOptions.xaxes, dragOptions.yaxes,
- dragOptions.subplot, dragOptions, outlines);
- }
-
- if(clickmode === 'event') {
- // TODO: remove in v2 - this was probably never intended to work as it does,
- // but in case anyone depends on it we don't want to break it now.
- // Note that click-to-select introduced pre v2 also emitts proper
- // event data when clickmode is having 'select' in its flag list.
- gd.emit('plotly_selected', undefined);
- }
- }
-
- Fx.click(gd, evt);
- });
- };
-
- dragOptions.doneFn = function() {
- corners.remove();
-
- throttle.done(throttleID).then(function() {
- throttle.clear(throttleID);
- dragOptions.gd.emit('plotly_selected', eventData);
-
- if(currentPolygon && dragOptions.selectionDefs) {
- // save last polygons
- currentPolygon.subtract = subtract;
- dragOptions.selectionDefs.push(currentPolygon);
-
- // we have to keep reference to arrays container
- dragOptions.mergedPolygons.length = 0;
- [].push.apply(dragOptions.mergedPolygons, mergedPolygons);
- }
- });
- };
- }
-
- function selectOnClick(evt, gd, xAxes, yAxes, subplot, dragOptions, polygonOutlines) {
- var hoverData = gd._hoverdata;
- var clickmode = gd._fullLayout.clickmode;
- var sendEvents = clickmode.indexOf('event') > -1;
- var selection = [];
- var searchTraces, searchInfo, currentSelectionDef, selectionTester, traceSelection;
- var thisTracesSelection, pointOrBinSelected, subtract, eventData, i;
-
- if(isHoverDataSet(hoverData)) {
- coerceSelectionsCache(evt, gd, dragOptions);
- searchTraces = determineSearchTraces(gd, xAxes, yAxes, subplot);
- var clickedPtInfo = extractClickedPtInfo(hoverData, searchTraces);
- var isBinnedTrace = clickedPtInfo.pointNumbers.length > 0;
-
-
- // Note: potentially costly operation isPointOrBinSelected is
- // called as late as possible through the use of an assignment
- // in an if condition.
- if(isBinnedTrace ?
- isOnlyThisBinSelected(searchTraces, clickedPtInfo) :
- isOnlyOnePointSelected(searchTraces) &&
- (pointOrBinSelected = isPointOrBinSelected(clickedPtInfo)))
- {
- if(polygonOutlines) polygonOutlines.remove();
- for(i = 0; i < searchTraces.length; i++) {
- searchInfo = searchTraces[i];
- searchInfo._module.selectPoints(searchInfo, false);
- }
-
- updateSelectedState(gd, searchTraces);
-
- clearSelectionsCache(dragOptions);
-
- if(sendEvents) {
- gd.emit('plotly_deselect', null);
- }
- } else {
- subtract = evt.shiftKey &&
- (pointOrBinSelected !== undefined ?
- pointOrBinSelected :
- isPointOrBinSelected(clickedPtInfo));
- currentSelectionDef = newPointSelectionDef(clickedPtInfo.pointNumber, clickedPtInfo.searchInfo, subtract);
-
- var allSelectionDefs = dragOptions.selectionDefs.concat([currentSelectionDef]);
- selectionTester = multiTester(allSelectionDefs);
-
- for(i = 0; i < searchTraces.length; i++) {
- traceSelection = searchTraces[i]._module.selectPoints(searchTraces[i], selectionTester);
- thisTracesSelection = fillSelectionItem(traceSelection, searchTraces[i]);
-
- if(selection.length) {
- for(var j = 0; j < thisTracesSelection.length; j++) {
- selection.push(thisTracesSelection[j]);
- }
- }
- else selection = thisTracesSelection;
- }
-
- eventData = {points: selection};
- updateSelectedState(gd, searchTraces, eventData);
-
- if(currentSelectionDef && dragOptions) {
- dragOptions.selectionDefs.push(currentSelectionDef);
- }
-
- if(polygonOutlines) drawSelection(dragOptions.mergedPolygons, polygonOutlines);
-
- if(sendEvents) {
- gd.emit('plotly_selected', eventData);
- }
- }
- }
- }
-
- /**
- * Constructs a new point selection definition object.
- */
- function newPointSelectionDef(pointNumber, searchInfo, subtract) {
- return {
- pointNumber: pointNumber,
- searchInfo: searchInfo,
- subtract: subtract
- };
- }
-
- function isPointSelectionDef(o) {
- return 'pointNumber' in o && 'searchInfo' in o;
- }
-
- /*
- * Constructs a new point number tester.
- */
- function newPointNumTester(pointSelectionDef) {
- return {
- xmin: 0,
- xmax: 0,
- ymin: 0,
- ymax: 0,
- pts: [],
- contains: function(pt, omitFirstEdge, pointNumber, searchInfo) {
- var idxWantedTrace = pointSelectionDef.searchInfo.cd[0].trace._expandedIndex;
- var idxActualTrace = searchInfo.cd[0].trace._expandedIndex;
- return idxActualTrace === idxWantedTrace &&
- pointNumber === pointSelectionDef.pointNumber;
- },
- isRect: false,
- degenerate: false,
- subtract: pointSelectionDef.subtract
- };
- }
-
- /**
- * Wraps multiple selection testers.
- *
- * @param {Array} list - An array of selection testers.
- *
- * @return a selection tester object with a contains function
- * that can be called to evaluate a point against all wrapped
- * selection testers that were passed in list.
- */
- function multiTester(list) {
- var testers = [];
- var xmin = isPointSelectionDef(list[0]) ? 0 : list[0][0][0];
- var xmax = xmin;
- var ymin = isPointSelectionDef(list[0]) ? 0 : list[0][0][1];
- var ymax = ymin;
-
- for(var i = 0; i < list.length; i++) {
- if(isPointSelectionDef(list[i])) {
- testers.push(newPointNumTester(list[i]));
- } else {
- var tester = polygon.tester(list[i]);
- tester.subtract = list[i].subtract;
- testers.push(tester);
- xmin = Math.min(xmin, tester.xmin);
- xmax = Math.max(xmax, tester.xmax);
- ymin = Math.min(ymin, tester.ymin);
- ymax = Math.max(ymax, tester.ymax);
- }
- }
-
- /**
- * Tests if the given point is within this tester.
- *
- * @param {Array} pt - [0] is the x coordinate, [1] is the y coordinate of the point.
- * @param {*} arg - An optional parameter to pass down to wrapped testers.
- * @param {number} pointNumber - The point number of the point within the underlying data array.
- * @param {number} searchInfo - An object identifying the trace the point is contained in.
- *
- * @return {boolean} true if point is considered to be selected, false otherwise.
- */
- function contains(pt, arg, pointNumber, searchInfo) {
- var contained = false;
- for(var i = 0; i < testers.length; i++) {
- if(testers[i].contains(pt, arg, pointNumber, searchInfo)) {
- // if contained by subtract tester - exclude the point
- contained = testers[i].subtract === false;
- }
- }
-
- return contained;
- }
-
- return {
- xmin: xmin,
- xmax: xmax,
- ymin: ymin,
- ymax: ymax,
- pts: [],
- contains: contains,
- isRect: false,
- degenerate: false
- };
- }
-
- function coerceSelectionsCache(evt, gd, dragOptions) {
- var fullLayout = gd._fullLayout;
- var zoomLayer = fullLayout._zoomlayer;
- var plotinfo = dragOptions.plotinfo;
-
- var selectingOnSameSubplot = (
- fullLayout._lastSelectedSubplot &&
- fullLayout._lastSelectedSubplot === plotinfo.id
- );
- var hasModifierKey = evt.shiftKey || evt.altKey;
- if(selectingOnSameSubplot && hasModifierKey &&
- (plotinfo.selection && plotinfo.selection.selectionDefs) && !dragOptions.selectionDefs) {
- // take over selection definitions from prev mode, if any
- dragOptions.selectionDefs = plotinfo.selection.selectionDefs;
- dragOptions.mergedPolygons = plotinfo.selection.mergedPolygons;
- } else if(!hasModifierKey || !plotinfo.selection) {
- clearSelectionsCache(dragOptions);
- }
-
- // clear selection outline when selecting a different subplot
- if(!selectingOnSameSubplot) {
- clearSelect(zoomLayer);
- fullLayout._lastSelectedSubplot = plotinfo.id;
- }
- }
-
- function clearSelectionsCache(dragOptions) {
- var plotinfo = dragOptions.plotinfo;
-
- plotinfo.selection = {};
- plotinfo.selection.selectionDefs = dragOptions.selectionDefs = [];
- plotinfo.selection.mergedPolygons = dragOptions.mergedPolygons = [];
- }
-
- function determineSearchTraces(gd, xAxes, yAxes, subplot) {
- var searchTraces = [];
- var xAxisIds = xAxes.map(getAxId);
- var yAxisIds = yAxes.map(getAxId);
- var cd, trace, i;
-
- for(i = 0; i < gd.calcdata.length; i++) {
- cd = gd.calcdata[i];
- trace = cd[0].trace;
-
- if(trace.visible !== true || !trace._module || !trace._module.selectPoints) continue;
-
- if(subplot && (trace.subplot === subplot || trace.geo === subplot)) {
- searchTraces.push(createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]));
- } else if(
- trace.type === 'splom' &&
- // FIXME: make sure we don't have more than single axis for splom
- trace._xaxes[xAxisIds[0]] && trace._yaxes[yAxisIds[0]]
- ) {
- var info = createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]);
- info.scene = gd._fullLayout._splomScenes[trace.uid];
- searchTraces.push(info);
- } else {
- if(xAxisIds.indexOf(trace.xaxis) === -1) continue;
- if(yAxisIds.indexOf(trace.yaxis) === -1) continue;
-
- searchTraces.push(createSearchInfo(trace._module, cd,
- getFromId(gd, trace.xaxis), getFromId(gd, trace.yaxis)));
- }
- }
-
- return searchTraces;
-
- function createSearchInfo(module, calcData, xaxis, yaxis) {
- return {
- _module: module,
- cd: calcData,
- xaxis: xaxis,
- yaxis: yaxis
- };
- }
- }
-
- function drawSelection(polygons, outlines) {
- var paths = [];
- var i, d;
-
- for(i = 0; i < polygons.length; i++) {
- var ppts = polygons[i];
- paths.push(ppts.join('L') + 'L' + ppts[0]);
- }
-
- d = polygons.length > 0 ?
- 'M' + paths.join('M') + 'Z' :
- 'M0,0Z';
- outlines.attr('d', d);
- }
-
- function isHoverDataSet(hoverData) {
- return hoverData &&
- Array.isArray(hoverData) &&
- hoverData[0].hoverOnBox !== true;
- }
-
- function extractClickedPtInfo(hoverData, searchTraces) {
- var hoverDatum = hoverData[0];
- var pointNumber = -1;
- var pointNumbers = [];
- var searchInfo, i;
-
- for(i = 0; i < searchTraces.length; i++) {
- searchInfo = searchTraces[i];
- if(hoverDatum.fullData._expandedIndex === searchInfo.cd[0].trace._expandedIndex) {
-
- // Special case for box (and violin)
- if(hoverDatum.hoverOnBox === true) {
- break;
- }
-
- // Hint: in some traces like histogram, one graphical element
- // doesn't correspond to one particular data point, but to
- // bins of data points. Thus, hoverDatum can have a binNumber
- // property instead of pointNumber.
- if(hoverDatum.pointNumber !== undefined) {
- pointNumber = hoverDatum.pointNumber;
- } else if(hoverDatum.binNumber !== undefined) {
- pointNumber = hoverDatum.binNumber;
- pointNumbers = hoverDatum.pointNumbers;
- }
-
- break;
- }
- }
-
- return {
- pointNumber: pointNumber,
- pointNumbers: pointNumbers,
- searchInfo: searchInfo
- };
- }
-
- function isPointOrBinSelected(clickedPtInfo) {
- var trace = clickedPtInfo.searchInfo.cd[0].trace;
- var ptNum = clickedPtInfo.pointNumber;
- var ptNums = clickedPtInfo.pointNumbers;
- var ptNumsSet = ptNums.length > 0;
-
- // When pointsNumbers is set (e.g. histogram's binning),
- // it is assumed that when the first point of
- // a bin is selected, all others are as well
- var ptNumToTest = ptNumsSet ? ptNums[0] : ptNum;
-
- // TODO potential performance improvement
- // Primarily we need this function to determine if a click adds
- // or subtracts from a selection.
- // In cases `trace.selectedpoints` is a huge array, indexOf
- // might be slow. One remedy would be to introduce a hash somewhere.
- return trace.selectedpoints ? trace.selectedpoints.indexOf(ptNumToTest) > -1 : false;
- }
-
- function isOnlyThisBinSelected(searchTraces, clickedPtInfo) {
- var tracesWithSelectedPts = [];
- var searchInfo, trace, isSameTrace, i;
-
- for(i = 0; i < searchTraces.length; i++) {
- searchInfo = searchTraces[i];
- if(searchInfo.cd[0].trace.selectedpoints && searchInfo.cd[0].trace.selectedpoints.length > 0) {
- tracesWithSelectedPts.push(searchInfo);
- }
- }
-
- if(tracesWithSelectedPts.length === 1) {
- isSameTrace = tracesWithSelectedPts[0] === clickedPtInfo.searchInfo;
- if(isSameTrace) {
- trace = clickedPtInfo.searchInfo.cd[0].trace;
- if(trace.selectedpoints.length === clickedPtInfo.pointNumbers.length) {
- for(i = 0; i < clickedPtInfo.pointNumbers.length; i++) {
- if(trace.selectedpoints.indexOf(clickedPtInfo.pointNumbers[i]) < 0) {
- return false;
- }
- }
- return true;
- }
- }
- }
-
- return false;
- }
-
- function isOnlyOnePointSelected(searchTraces) {
- var len = 0;
- var searchInfo, trace, i;
-
- for(i = 0; i < searchTraces.length; i++) {
- searchInfo = searchTraces[i];
- trace = searchInfo.cd[0].trace;
- if(trace.selectedpoints) {
- if(trace.selectedpoints.length > 1) return false;
-
- len += trace.selectedpoints.length;
- if(len > 1) return false;
- }
- }
-
- return len === 1;
- }
-
- function updateSelectedState(gd, searchTraces, eventData) {
- var i, searchInfo, cd, trace;
-
- // before anything else, update preGUI if necessary
- for(i = 0; i < searchTraces.length; i++) {
- var fullInputTrace = searchTraces[i].cd[0].trace._fullInput;
- var tracePreGUI = gd._fullLayout._tracePreGUI[fullInputTrace.uid];
- if(tracePreGUI.selectedpoints === undefined) {
- tracePreGUI.selectedpoints = fullInputTrace._input.selectedpoints || null;
- }
- }
-
- if(eventData) {
- var pts = eventData.points || [];
-
- for(i = 0; i < searchTraces.length; i++) {
- trace = searchTraces[i].cd[0].trace;
- trace._input.selectedpoints = trace._fullInput.selectedpoints = [];
- if(trace._fullInput !== trace) trace.selectedpoints = [];
- }
-
- for(i = 0; i < pts.length; i++) {
- var pt = pts[i];
- var data = pt.data;
- var fullData = pt.fullData;
-
- if(pt.pointIndices) {
- [].push.apply(data.selectedpoints, pt.pointIndices);
- if(trace._fullInput !== trace) {
- [].push.apply(fullData.selectedpoints, pt.pointIndices);
- }
- } else {
- data.selectedpoints.push(pt.pointIndex);
- if(trace._fullInput !== trace) {
- fullData.selectedpoints.push(pt.pointIndex);
- }
- }
- }
- }
- else {
- for(i = 0; i < searchTraces.length; i++) {
- trace = searchTraces[i].cd[0].trace;
- delete trace.selectedpoints;
- delete trace._input.selectedpoints;
- if(trace._fullInput !== trace) {
- delete trace._fullInput.selectedpoints;
- }
- }
- }
-
- var hasRegl = false;
-
- for(i = 0; i < searchTraces.length; i++) {
- searchInfo = searchTraces[i];
- cd = searchInfo.cd;
- trace = cd[0].trace;
-
- if(Registry.traceIs(trace, 'regl')) {
- hasRegl = true;
- }
-
- var _module = searchInfo._module;
- var fn = _module.styleOnSelect || _module.style;
- if(fn) fn(gd, cd);
- }
-
- if(hasRegl) {
- clearGlCanvases(gd);
- redrawReglTraces(gd);
- }
- }
-
- function mergePolygons(list, poly, subtract) {
- var res;
-
- if(subtract) {
- res = polybool.difference({
- regions: list,
- inverted: false
- }, {
- regions: [poly],
- inverted: false
- });
-
- return res.regions;
- }
-
- res = polybool.union({
- regions: list,
- inverted: false
- }, {
- regions: [poly],
- inverted: false
- });
-
- return res.regions;
- }
-
- function fillSelectionItem(selection, searchInfo) {
- if(Array.isArray(selection)) {
- var cd = searchInfo.cd;
- var trace = searchInfo.cd[0].trace;
-
- for(var i = 0; i < selection.length; i++) {
- selection[i] = makeEventData(selection[i], trace, cd);
- }
- }
-
- return selection;
- }
-
- function clearSelect(zoomlayer) {
- // until we get around to persistent selections, remove the outline
- // here. The selection itself will be removed when the plot redraws
- // at the end.
- zoomlayer.selectAll('.select-outline').remove();
- }
-
- module.exports = {
- prepSelect: prepSelect,
- clearSelect: clearSelect,
- selectOnClick: selectOnClick
- };
-
- },{"../../components/color":51,"../../components/fx":90,"../../components/fx/helpers":86,"../../lib/clear_gl_canvases":157,"../../lib/polygon":180,"../../lib/throttle":190,"../../plot_api/subroutines":203,"../../registry":257,"./axis_ids":215,"./constants":218,"polybooljs":25}],231:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Lib = _dereq_('../../lib');
- var cleanNumber = Lib.cleanNumber;
- var ms2DateTime = Lib.ms2DateTime;
- var dateTime2ms = Lib.dateTime2ms;
- var ensureNumber = Lib.ensureNumber;
- var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
-
- var numConstants = _dereq_('../../constants/numerical');
- var FP_SAFE = numConstants.FP_SAFE;
- var BADNUM = numConstants.BADNUM;
- var LOG_CLIP = numConstants.LOG_CLIP;
-
- var constants = _dereq_('./constants');
- var axisIds = _dereq_('./axis_ids');
-
- function fromLog(v) {
- return Math.pow(10, v);
- }
-
- function isValidCategory(v) {
- return v !== null && v !== undefined;
- }
-
- /**
- * Define the conversion functions for an axis data is used in 5 ways:
- *
- * d: data, in whatever form it's provided
- * c: calcdata: turned into numbers, but not linearized
- * l: linearized - same as c except for log axes (and other nonlinear
- * mappings later?) this is used when we need to know if it's
- * *possible* to show some data on this axis, without caring about
- * the current range
- * p: pixel value - mapped to the screen with current size and zoom
- * r: ranges, tick0, and annotation positions match one of the above
- * but are handled differently for different types:
- * - linear and date: data format (d)
- * - category: calcdata format (c), and will stay that way because
- * the data format has no continuous mapping
- * - log: linearized (l) format
- * TODO: in v2.0 we plan to change it to data format. At that point
- * shapes will work the same way as ranges, tick0, and annotations
- * so they can use this conversion too.
- *
- * Creates/updates these conversion functions, and a few more utilities
- * like cleanRange, and makeCalcdata
- *
- * also clears the autotick constraints ._minDtick, ._forceTick0
- */
- module.exports = function setConvert(ax, fullLayout) {
- fullLayout = fullLayout || {};
-
- var axLetter = (ax._id || 'x').charAt(0);
-
- function toLog(v, clip) {
- if(v > 0) return Math.log(v) / Math.LN10;
-
- else if(v <= 0 && clip && ax.range && ax.range.length === 2) {
- // clip NaN (ie past negative infinity) to LOG_CLIP axis
- // length past the negative edge
- var r0 = ax.range[0];
- var r1 = ax.range[1];
- return 0.5 * (r0 + r1 - 2 * LOG_CLIP * Math.abs(r0 - r1));
- }
-
- else return BADNUM;
- }
-
- /*
- * wrapped dateTime2ms that:
- * - accepts ms numbers for backward compatibility
- * - inserts a dummy arg so calendar is the 3rd arg (see notes below).
- * - defaults to ax.calendar
- */
- function dt2ms(v, _, calendar) {
- // NOTE: Changed this behavior: previously we took any numeric value
- // to be a ms, even if it was a string that could be a bare year.
- // Now we convert it as a date if at all possible, and only try
- // as (local) ms if that fails.
- var ms = dateTime2ms(v, calendar || ax.calendar);
- if(ms === BADNUM) {
- if(isNumeric(v)) {
- v = +v;
- // keep track of tenths of ms, that `new Date` will drop
- // same logic as in Lib.ms2DateTime
- var msecTenths = Math.floor(Lib.mod(v + 0.05, 1) * 10);
- var msRounded = Math.round(v - msecTenths / 10);
- ms = dateTime2ms(new Date(msRounded)) + msecTenths / 10;
- }
- else return BADNUM;
- }
- return ms;
- }
-
- // wrapped ms2DateTime to insert default ax.calendar
- function ms2dt(v, r, calendar) {
- return ms2DateTime(v, r, calendar || ax.calendar);
- }
-
- function getCategoryName(v) {
- return ax._categories[Math.round(v)];
- }
-
- /*
- * setCategoryIndex: return the index of category v,
- * inserting it in the list if it's not already there
- *
- * this will enter the categories in the order it
- * encounters them, ie all the categories from the
- * first data set, then all the ones from the second
- * that aren't in the first etc.
- *
- * it is assumed that this function is being invoked in the
- * already sorted category order; otherwise there would be
- * a disconnect between the array and the index returned
- */
- function setCategoryIndex(v) {
- if(isValidCategory(v)) {
- if(ax._categoriesMap === undefined) {
- ax._categoriesMap = {};
- }
-
- if(ax._categoriesMap[v] !== undefined) {
- return ax._categoriesMap[v];
- } else {
- ax._categories.push(v);
-
- var curLength = ax._categories.length - 1;
- ax._categoriesMap[v] = curLength;
-
- return curLength;
- }
- }
- return BADNUM;
- }
-
- function setMultiCategoryIndex(arrayIn, len) {
- var arrayOut = new Array(len);
-
- for(var i = 0; i < len; i++) {
- var v0 = (arrayIn[0] || [])[i];
- var v1 = (arrayIn[1] || [])[i];
- arrayOut[i] = getCategoryIndex([v0, v1]);
- }
-
- return arrayOut;
- }
-
- function getCategoryIndex(v) {
- if(ax._categoriesMap) {
- return ax._categoriesMap[v];
- }
- }
-
- function getCategoryPosition(v) {
- // d2l/d2c variant that that won't add categories but will also
- // allow numbers to be mapped to the linearized axis positions
- var index = getCategoryIndex(v);
- if(index !== undefined) return index;
- if(isNumeric(v)) return +v;
- }
-
- function l2p(v) {
- if(!isNumeric(v)) return BADNUM;
-
- // include 2 fractional digits on pixel, for PDF zooming etc
- return d3.round(ax._b + ax._m * v, 2);
- }
-
- function p2l(px) { return (px - ax._b) / ax._m; }
-
- // conversions among c/l/p are fairly simple - do them together for all axis types
- ax.c2l = (ax.type === 'log') ? toLog : ensureNumber;
- ax.l2c = (ax.type === 'log') ? fromLog : ensureNumber;
-
- ax.l2p = l2p;
- ax.p2l = p2l;
-
- ax.c2p = (ax.type === 'log') ? function(v, clip) { return l2p(toLog(v, clip)); } : l2p;
- ax.p2c = (ax.type === 'log') ? function(px) { return fromLog(p2l(px)); } : p2l;
-
- /*
- * now type-specific conversions for **ALL** other combinations
- * they're all written out, instead of being combinations of each other, for
- * both clarity and speed.
- */
- if(['linear', '-'].indexOf(ax.type) !== -1) {
- // all are data vals, but d and r need cleaning
- ax.d2r = ax.r2d = ax.d2c = ax.r2c = ax.d2l = ax.r2l = cleanNumber;
- ax.c2d = ax.c2r = ax.l2d = ax.l2r = ensureNumber;
-
- ax.d2p = ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); };
- ax.p2d = ax.p2r = p2l;
-
- ax.cleanPos = ensureNumber;
- }
- else if(ax.type === 'log') {
- // d and c are data vals, r and l are logged (but d and r need cleaning)
- ax.d2r = ax.d2l = function(v, clip) { return toLog(cleanNumber(v), clip); };
- ax.r2d = ax.r2c = function(v) { return fromLog(cleanNumber(v)); };
-
- ax.d2c = ax.r2l = cleanNumber;
- ax.c2d = ax.l2r = ensureNumber;
-
- ax.c2r = toLog;
- ax.l2d = fromLog;
-
- ax.d2p = function(v, clip) { return ax.l2p(ax.d2r(v, clip)); };
- ax.p2d = function(px) { return fromLog(p2l(px)); };
-
- ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); };
- ax.p2r = p2l;
-
- ax.cleanPos = ensureNumber;
- }
- else if(ax.type === 'date') {
- // r and d are date strings, l and c are ms
-
- /*
- * Any of these functions with r and d on either side, calendar is the
- * **3rd** argument. log has reserved the second argument.
- *
- * Unless you need the special behavior of the second arg (ms2DateTime
- * uses this to limit precision, toLog uses true to clip negatives
- * to offscreen low rather than undefined), it's safe to pass 0.
- */
- ax.d2r = ax.r2d = Lib.identity;
-
- ax.d2c = ax.r2c = ax.d2l = ax.r2l = dt2ms;
- ax.c2d = ax.c2r = ax.l2d = ax.l2r = ms2dt;
-
- ax.d2p = ax.r2p = function(v, _, calendar) { return ax.l2p(dt2ms(v, 0, calendar)); };
- ax.p2d = ax.p2r = function(px, r, calendar) { return ms2dt(p2l(px), r, calendar); };
-
- ax.cleanPos = function(v) { return Lib.cleanDate(v, BADNUM, ax.calendar); };
- }
- else if(ax.type === 'category') {
- // d is categories (string)
- // c and l are indices (numbers)
- // r is categories or numbers
-
- ax.d2c = ax.d2l = setCategoryIndex;
- ax.r2d = ax.c2d = ax.l2d = getCategoryName;
-
- ax.d2r = ax.d2l_noadd = getCategoryPosition;
-
- ax.r2c = function(v) {
- var index = getCategoryPosition(v);
- return index !== undefined ? index : ax.fraction2r(0.5);
- };
-
- ax.l2r = ax.c2r = ensureNumber;
- ax.r2l = getCategoryPosition;
-
- ax.d2p = function(v) { return ax.l2p(ax.r2c(v)); };
- ax.p2d = function(px) { return getCategoryName(p2l(px)); };
- ax.r2p = ax.d2p;
- ax.p2r = p2l;
-
- ax.cleanPos = function(v) {
- if(typeof v === 'string' && v !== '') return v;
- return ensureNumber(v);
- };
- }
- else if(ax.type === 'multicategory') {
- // N.B. multicategory axes don't define d2c and d2l,
- // as 'data-to-calcdata' conversion needs to take into
- // account all data array items as in ax.makeCalcdata.
-
- ax.r2d = ax.c2d = ax.l2d = getCategoryName;
- ax.d2r = ax.d2l_noadd = getCategoryPosition;
-
- ax.r2c = function(v) {
- var index = getCategoryPosition(v);
- return index !== undefined ? index : ax.fraction2r(0.5);
- };
-
- ax.r2c_just_indices = getCategoryIndex;
-
- ax.l2r = ax.c2r = ensureNumber;
- ax.r2l = getCategoryPosition;
-
- ax.d2p = function(v) { return ax.l2p(ax.r2c(v)); };
- ax.p2d = function(px) { return getCategoryName(p2l(px)); };
- ax.r2p = ax.d2p;
- ax.p2r = p2l;
-
- ax.cleanPos = function(v) {
- if(Array.isArray(v) || (typeof v === 'string' && v !== '')) return v;
- return ensureNumber(v);
- };
-
- ax.setupMultiCategory = function(fullData) {
- var traceIndices = ax._traceIndices;
- var i, j;
-
- // [ [cnt, {$cat: index}], for 1,2 ]
- var seen = ax._multicatSeen = [[0, {}], [0, {}]];
- // [ [arrayIn[0][i], arrayIn[1][i]], for i .. N ]
- var list = ax._multicatList = [];
-
- for(i = 0; i < traceIndices.length; i++) {
- var trace = fullData[traceIndices[i]];
-
- if(axLetter in trace) {
- var arrayIn = trace[axLetter];
- var len = trace._length || Lib.minRowLength(arrayIn);
-
- if(isArrayOrTypedArray(arrayIn[0]) && isArrayOrTypedArray(arrayIn[1])) {
- for(j = 0; j < len; j++) {
- var v0 = arrayIn[0][j];
- var v1 = arrayIn[1][j];
-
- if(isValidCategory(v0) && isValidCategory(v1)) {
- list.push([v0, v1]);
-
- if(!(v0 in seen[0][1])) {
- seen[0][1][v0] = seen[0][0]++;
- }
- if(!(v1 in seen[1][1])) {
- seen[1][1][v1] = seen[1][0]++;
- }
- }
- }
- }
- }
- }
-
- list.sort(function(a, b) {
- var ind0 = seen[0][1];
- var d = ind0[a[0]] - ind0[b[0]];
- if(d) return d;
-
- var ind1 = seen[1][1];
- return ind1[a[1]] - ind1[b[1]];
- });
-
- for(i = 0; i < list.length; i++) {
- setCategoryIndex(list[i]);
- }
- };
- }
-
- // find the range value at the specified (linear) fraction of the axis
- ax.fraction2r = function(v) {
- var rl0 = ax.r2l(ax.range[0]);
- var rl1 = ax.r2l(ax.range[1]);
- return ax.l2r(rl0 + v * (rl1 - rl0));
- };
-
- // find the fraction of the range at the specified range value
- ax.r2fraction = function(v) {
- var rl0 = ax.r2l(ax.range[0]);
- var rl1 = ax.r2l(ax.range[1]);
- return (ax.r2l(v) - rl0) / (rl1 - rl0);
- };
-
- /*
- * cleanRange: make sure range is a couplet of valid & distinct values
- * keep numbers away from the limits of floating point numbers,
- * and dates away from the ends of our date system (+/- 9999 years)
- *
- * optional param rangeAttr: operate on a different attribute, like
- * ax._r, rather than ax.range
- */
- ax.cleanRange = function(rangeAttr, opts) {
- if(!opts) opts = {};
- if(!rangeAttr) rangeAttr = 'range';
-
- var range = Lib.nestedProperty(ax, rangeAttr).get();
- var i, dflt;
-
- if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar);
- else if(axLetter === 'y') dflt = constants.DFLTRANGEY;
- else dflt = opts.dfltRange || constants.DFLTRANGEX;
-
- // make sure we don't later mutate the defaults
- dflt = dflt.slice();
-
- if(!range || range.length !== 2) {
- Lib.nestedProperty(ax, rangeAttr).set(dflt);
- return;
- }
-
- if(ax.type === 'date') {
- // check if milliseconds or js date objects are provided for range
- // and convert to date strings
- range[0] = Lib.cleanDate(range[0], BADNUM, ax.calendar);
- range[1] = Lib.cleanDate(range[1], BADNUM, ax.calendar);
- }
-
- for(i = 0; i < 2; i++) {
- if(ax.type === 'date') {
- if(!Lib.isDateTime(range[i], ax.calendar)) {
- ax[rangeAttr] = dflt;
- break;
- }
-
- if(ax.r2l(range[0]) === ax.r2l(range[1])) {
- // split by +/- 1 second
- var linCenter = Lib.constrain(ax.r2l(range[0]),
- Lib.MIN_MS + 1000, Lib.MAX_MS - 1000);
- range[0] = ax.l2r(linCenter - 1000);
- range[1] = ax.l2r(linCenter + 1000);
- break;
- }
- }
- else {
- if(!isNumeric(range[i])) {
- if(isNumeric(range[1 - i])) {
- range[i] = range[1 - i] * (i ? 10 : 0.1);
- }
- else {
- ax[rangeAttr] = dflt;
- break;
- }
- }
-
- if(range[i] < -FP_SAFE) range[i] = -FP_SAFE;
- else if(range[i] > FP_SAFE) range[i] = FP_SAFE;
-
- if(range[0] === range[1]) {
- // somewhat arbitrary: split by 1 or 1ppm, whichever is bigger
- var inc = Math.max(1, Math.abs(range[0] * 1e-6));
- range[0] -= inc;
- range[1] += inc;
- }
- }
- }
- };
-
- // set scaling to pixels
- ax.setScale = function(usePrivateRange) {
- var gs = fullLayout._size;
-
- // make sure we have a domain (pull it in from the axis
- // this one is overlaying if necessary)
- if(ax.overlaying) {
- var ax2 = axisIds.getFromId({ _fullLayout: fullLayout }, ax.overlaying);
- ax.domain = ax2.domain;
- }
-
- // While transitions are occuring, occurring, we get a double-transform
- // issue if we transform the drawn layer *and* use the new axis range to
- // draw the data. This allows us to construct setConvert using the pre-
- // interaction values of the range:
- var rangeAttr = (usePrivateRange && ax._r) ? '_r' : 'range';
- var calendar = ax.calendar;
- ax.cleanRange(rangeAttr);
-
- var rl0 = ax.r2l(ax[rangeAttr][0], calendar);
- var rl1 = ax.r2l(ax[rangeAttr][1], calendar);
-
- if(axLetter === 'y') {
- ax._offset = gs.t + (1 - ax.domain[1]) * gs.h;
- ax._length = gs.h * (ax.domain[1] - ax.domain[0]);
- ax._m = ax._length / (rl0 - rl1);
- ax._b = -ax._m * rl1;
- }
- else {
- ax._offset = gs.l + ax.domain[0] * gs.w;
- ax._length = gs.w * (ax.domain[1] - ax.domain[0]);
- ax._m = ax._length / (rl1 - rl0);
- ax._b = -ax._m * rl0;
- }
-
- if(!isFinite(ax._m) || !isFinite(ax._b)) {
- fullLayout._replotting = false;
- throw new Error('Something went wrong with axis scaling');
- }
- };
-
- // makeCalcdata: takes an x or y array and converts it
- // to a position on the axis object "ax"
- // inputs:
- // trace - a data object from gd.data
- // axLetter - a string, either 'x' or 'y', for which item
- // to convert (TODO: is this now always the same as
- // the first letter of ax._id?)
- // in case the expected data isn't there, make a list of
- // integers based on the opposite data
- ax.makeCalcdata = function(trace, axLetter) {
- var arrayIn, arrayOut, i, len;
-
- var axType = ax.type;
- var cal = axType === 'date' && trace[axLetter + 'calendar'];
-
- if(axLetter in trace) {
- arrayIn = trace[axLetter];
- len = trace._length || Lib.minRowLength(arrayIn);
-
- if(Lib.isTypedArray(arrayIn) && (axType === 'linear' || axType === 'log')) {
- if(len === arrayIn.length) {
- return arrayIn;
- } else if(arrayIn.subarray) {
- return arrayIn.subarray(0, len);
- }
- }
-
- if(axType === 'multicategory') {
- return setMultiCategoryIndex(arrayIn, len);
- }
-
- arrayOut = new Array(len);
- for(i = 0; i < len; i++) {
- arrayOut[i] = ax.d2c(arrayIn[i], 0, cal);
- }
- }
- else {
- var v0 = ((axLetter + '0') in trace) ? ax.d2c(trace[axLetter + '0'], 0, cal) : 0;
- var dv = (trace['d' + axLetter]) ? Number(trace['d' + axLetter]) : 1;
-
- // the opposing data, for size if we have x and dx etc
- arrayIn = trace[{x: 'y', y: 'x'}[axLetter]];
- len = trace._length || arrayIn.length;
- arrayOut = new Array(len);
-
- for(i = 0; i < len; i++) {
- arrayOut[i] = v0 + i * dv;
- }
- }
-
- return arrayOut;
- };
-
- ax.isValidRange = function(range) {
- return (
- Array.isArray(range) &&
- range.length === 2 &&
- isNumeric(ax.r2l(range[0])) &&
- isNumeric(ax.r2l(range[1]))
- );
- };
-
- ax.isPtWithinRange = function(d, calendar) {
- var coord = ax.c2l(d[axLetter], null, calendar);
- var r0 = ax.r2l(ax.range[0]);
- var r1 = ax.r2l(ax.range[1]);
-
- if(r0 < r1) {
- return r0 <= coord && coord <= r1;
- } else {
- // Reversed axis case.
- return r1 <= coord && coord <= r0;
- }
- };
-
- ax.clearCalc = function() {
- // initialize the category list, if there is one, so we start over
- // to be filled in later by ax.d2c
- ax._categories = (ax._initialCategories || []).slice();
-
- // Build the lookup map for initialized categories
- ax._categoriesMap = {};
- for(var j = 0; j < ax._categories.length; j++) {
- ax._categoriesMap[ax._categories[j]] = j;
- }
- };
-
- // Propagate localization into the axis so that
- // methods in Axes can use it w/o having to pass fullLayout
- // Default (non-d3) number formatting uses separators directly
- // dates and d3-formatted numbers use the d3 locale
- // Fall back on default format for dummy axes that don't care about formatting
- var locale = fullLayout._d3locale;
- if(ax.type === 'date') {
- ax._dateFormat = locale ? locale.timeFormat.utc : d3.time.format.utc;
- ax._extraFormat = fullLayout._extraFormat;
- }
- // occasionally we need _numFormat to pass through
- // even though it won't be needed by this axis
- ax._separators = fullLayout.separators;
- ax._numFormat = locale ? locale.numberFormat : d3.format;
-
- // and for bar charts and box plots: reset forced minimum tick spacing
- delete ax._minDtick;
- delete ax._forceTick0;
- };
-
- },{"../../constants/numerical":149,"../../lib":168,"./axis_ids":215,"./constants":218,"d3":16,"fast-isnumeric":18}],232:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var layoutAttributes = _dereq_('./layout_attributes');
- var handleArrayContainerDefaults = _dereq_('../array_container_defaults');
-
- module.exports = function handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options) {
- var showAttrDflt = getShowAttrDflt(containerIn);
-
- var tickPrefix = coerce('tickprefix');
- if(tickPrefix) coerce('showtickprefix', showAttrDflt);
-
- var tickSuffix = coerce('ticksuffix', options.tickSuffixDflt);
- if(tickSuffix) coerce('showticksuffix', showAttrDflt);
-
- var showTickLabels = coerce('showticklabels');
- if(showTickLabels) {
- var font = options.font || {};
- var contColor = containerOut.color;
- // as with titlefont.color, inherit axis.color only if one was
- // explicitly provided
- var dfltFontColor = (contColor && contColor !== layoutAttributes.color.dflt) ?
- contColor : font.color;
- Lib.coerceFont(coerce, 'tickfont', {
- family: font.family,
- size: font.size,
- color: dfltFontColor
- });
- coerce('tickangle');
-
- if(axType !== 'category') {
- var tickFormat = coerce('tickformat');
- var tickformatStops = containerIn.tickformatstops;
- if(Array.isArray(tickformatStops) && tickformatStops.length) {
- handleArrayContainerDefaults(containerIn, containerOut, {
- name: 'tickformatstops',
- inclusionAttr: 'enabled',
- handleItemDefaults: tickformatstopDefaults
- });
- }
- if(!tickFormat && axType !== 'date') {
- coerce('showexponent', showAttrDflt);
- coerce('exponentformat');
- coerce('separatethousands');
- }
- }
- }
- };
-
- /*
- * Attributes 'showexponent', 'showtickprefix' and 'showticksuffix'
- * share values.
- *
- * If only 1 attribute is set,
- * the remaining attributes inherit that value.
- *
- * If 2 attributes are set to the same value,
- * the remaining attribute inherits that value.
- *
- * If 2 attributes are set to different values,
- * the remaining is set to its dflt value.
- *
- */
- function getShowAttrDflt(containerIn) {
- var showAttrsAll = ['showexponent', 'showtickprefix', 'showticksuffix'];
- var showAttrs = showAttrsAll.filter(function(a) {
- return containerIn[a] !== undefined;
- });
- var sameVal = function(a) {
- return containerIn[a] === containerIn[showAttrs[0]];
- };
-
- if(showAttrs.every(sameVal) || showAttrs.length === 1) {
- return containerIn[showAttrs[0]];
- }
- }
-
- function tickformatstopDefaults(valueIn, valueOut) {
- function coerce(attr, dflt) {
- return Lib.coerce(valueIn, valueOut, layoutAttributes.tickformatstops, attr, dflt);
- }
-
- var enabled = coerce('enabled');
- if(enabled) {
- coerce('dtickrange');
- coerce('value');
- }
- }
-
- },{"../../lib":168,"../array_container_defaults":208,"./layout_attributes":225}],233:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- var layoutAttributes = _dereq_('./layout_attributes');
-
-
- /**
- * options: inherits outerTicks from axes.handleAxisDefaults
- */
- module.exports = function handleTickDefaults(containerIn, containerOut, coerce, options) {
- var tickLen = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'ticklen');
- var tickWidth = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'tickwidth');
- var tickColor = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'tickcolor', containerOut.color);
- var showTicks = coerce('ticks', (options.outerTicks || tickLen || tickWidth || tickColor) ? 'outside' : '');
-
- if(!showTicks) {
- delete containerOut.ticklen;
- delete containerOut.tickwidth;
- delete containerOut.tickcolor;
- }
- };
-
- },{"../../lib":168,"./layout_attributes":225}],234:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var cleanTicks = _dereq_('./clean_ticks');
-
- module.exports = function handleTickValueDefaults(containerIn, containerOut, coerce, axType) {
- var tickmode;
-
- if(containerIn.tickmode === 'array' &&
- (axType === 'log' || axType === 'date')) {
- tickmode = containerOut.tickmode = 'auto';
- } else {
- var tickmodeDefault = Array.isArray(containerIn.tickvals) ? 'array' :
- containerIn.dtick ? 'linear' :
- 'auto';
- tickmode = coerce('tickmode', tickmodeDefault);
- }
-
- if(tickmode === 'auto') coerce('nticks');
- else if(tickmode === 'linear') {
- // dtick is usually a positive number, but there are some
- // special strings available for log or date axes
- // tick0 also has special logic
- var dtick = containerOut.dtick = cleanTicks.dtick(
- containerIn.dtick, axType);
- containerOut.tick0 = cleanTicks.tick0(
- containerIn.tick0, axType, containerOut.calendar, dtick);
- } else if(axType !== 'multicategory') {
- var tickvals = coerce('tickvals');
- if(tickvals === undefined) containerOut.tickmode = 'auto';
- else coerce('ticktext');
- }
- };
-
- },{"./clean_ticks":217}],235:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Registry = _dereq_('../../registry');
- var Drawing = _dereq_('../../components/drawing');
- var Axes = _dereq_('./axes');
-
- /**
- * transitionAxes
- *
- * transition axes from one set of ranges to another, using a svg
- * transformations, similar to during panning.
- *
- * @param {DOM element | object} gd
- * @param {array} edits : array of 'edits', each item with
- * - plotinfo {object} subplot object
- * - xr0 {array} initial x-range
- * - xr1 {array} end x-range
- * - yr0 {array} initial y-range
- * - yr1 {array} end y-range
- * @param {object} transitionOpts
- * @param {function} makeOnCompleteCallback
- */
- module.exports = function transitionAxes(gd, edits, transitionOpts, makeOnCompleteCallback) {
- var fullLayout = gd._fullLayout;
-
- // special case for redraw:false Plotly.animate that relies on this
- // to update axis-referenced layout components
- if(edits.length === 0) {
- Axes.redrawComponents(gd);
- return;
- }
-
- function unsetSubplotTransform(subplot) {
- var xa = subplot.xaxis;
- var ya = subplot.yaxis;
-
- fullLayout._defs.select('#' + subplot.clipId + '> rect')
- .call(Drawing.setTranslate, 0, 0)
- .call(Drawing.setScale, 1, 1);
-
- subplot.plot
- .call(Drawing.setTranslate, xa._offset, ya._offset)
- .call(Drawing.setScale, 1, 1);
-
- var traceGroups = subplot.plot.selectAll('.scatterlayer .trace');
-
- // This is specifically directed at scatter traces, applying an inverse
- // scale to individual points to counteract the scale of the trace
- // as a whole:
- traceGroups.selectAll('.point')
- .call(Drawing.setPointGroupScale, 1, 1);
- traceGroups.selectAll('.textpoint')
- .call(Drawing.setTextPointsScale, 1, 1);
- traceGroups
- .call(Drawing.hideOutsideRangePoints, subplot);
- }
-
- function updateSubplot(edit, progress) {
- var plotinfo = edit.plotinfo;
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- var xr0 = edit.xr0;
- var xr1 = edit.xr1;
- var xlen = xa._length;
- var yr0 = edit.yr0;
- var yr1 = edit.yr1;
- var ylen = ya._length;
-
- var editX = !!xr1;
- var editY = !!yr1;
- var viewBox = [];
-
- if(editX) {
- var dx0 = xr0[1] - xr0[0];
- var dx1 = xr1[1] - xr1[0];
- viewBox[0] = (xr0[0] * (1 - progress) + progress * xr1[0] - xr0[0]) / (xr0[1] - xr0[0]) * xlen;
- viewBox[2] = xlen * ((1 - progress) + progress * dx1 / dx0);
- xa.range[0] = xr0[0] * (1 - progress) + progress * xr1[0];
- xa.range[1] = xr0[1] * (1 - progress) + progress * xr1[1];
- } else {
- viewBox[0] = 0;
- viewBox[2] = xlen;
- }
-
- if(editY) {
- var dy0 = yr0[1] - yr0[0];
- var dy1 = yr1[1] - yr1[0];
- viewBox[1] = (yr0[1] * (1 - progress) + progress * yr1[1] - yr0[1]) / (yr0[0] - yr0[1]) * ylen;
- viewBox[3] = ylen * ((1 - progress) + progress * dy1 / dy0);
- ya.range[0] = yr0[0] * (1 - progress) + progress * yr1[0];
- ya.range[1] = yr0[1] * (1 - progress) + progress * yr1[1];
- } else {
- viewBox[1] = 0;
- viewBox[3] = ylen;
- }
-
- Axes.drawOne(gd, xa, {skipTitle: true});
- Axes.drawOne(gd, ya, {skipTitle: true});
- Axes.redrawComponents(gd, [xa._id, ya._id]);
-
- var xScaleFactor = editX ? xlen / viewBox[2] : 1;
- var yScaleFactor = editY ? ylen / viewBox[3] : 1;
- var clipDx = editX ? viewBox[0] : 0;
- var clipDy = editY ? viewBox[1] : 0;
- var fracDx = editX ? (viewBox[0] / viewBox[2] * xlen) : 0;
- var fracDy = editY ? (viewBox[1] / viewBox[3] * ylen) : 0;
- var plotDx = xa._offset - fracDx;
- var plotDy = ya._offset - fracDy;
-
- plotinfo.clipRect
- .call(Drawing.setTranslate, clipDx, clipDy)
- .call(Drawing.setScale, 1 / xScaleFactor, 1 / yScaleFactor);
-
- plotinfo.plot
- .call(Drawing.setTranslate, plotDx, plotDy)
- .call(Drawing.setScale, xScaleFactor, yScaleFactor);
-
- // apply an inverse scale to individual points to counteract
- // the scale of the trace group.
- Drawing.setPointGroupScale(plotinfo.zoomScalePts, 1 / xScaleFactor, 1 / yScaleFactor);
- Drawing.setTextPointsScale(plotinfo.zoomScaleTxt, 1 / xScaleFactor, 1 / yScaleFactor);
- }
-
- var onComplete;
- if(makeOnCompleteCallback) {
- // This module makes the choice whether or not it notifies Plotly.transition
- // about completion:
- onComplete = makeOnCompleteCallback();
- }
-
- function transitionComplete() {
- var aobj = {};
-
- for(var i = 0; i < edits.length; i++) {
- var edit = edits[i];
- if(edit.xr1) aobj[edit.plotinfo.xaxis._name + '.range'] = edit.xr1.slice();
- if(edit.yr1) aobj[edit.plotinfo.yaxis._name + '.range'] = edit.yr1.slice();
- }
-
- // Signal that this transition has completed:
- onComplete && onComplete();
-
- return Registry.call('relayout', gd, aobj).then(function() {
- for(var i = 0; i < edits.length; i++) {
- unsetSubplotTransform(edits[i].plotinfo);
- }
- });
- }
-
- function transitionInterrupt() {
- var aobj = {};
-
- for(var i = 0; i < edits.length; i++) {
- var edit = edits[i];
- if(edit.xr0) aobj[edit.plotinfo.xaxis._name + '.range'] = edit.xr0.slice();
- if(edit.yr0) aobj[edit.plotinfo.yaxis._name + '.range'] = edit.yr0.slice();
- }
-
- return Registry.call('relayout', gd, aobj).then(function() {
- for(var i = 0; i < edits.length; i++) {
- unsetSubplotTransform(edits[i].plotinfo);
- }
- });
- }
-
- var t1, t2, raf;
- var easeFn = d3.ease(transitionOpts.easing);
-
- gd._transitionData._interruptCallbacks.push(function() {
- window.cancelAnimationFrame(raf);
- raf = null;
- return transitionInterrupt();
- });
-
- function doFrame() {
- t2 = Date.now();
-
- var tInterp = Math.min(1, (t2 - t1) / transitionOpts.duration);
- var progress = easeFn(tInterp);
-
- for(var i = 0; i < edits.length; i++) {
- updateSubplot(edits[i], progress);
- }
-
- if(t2 - t1 > transitionOpts.duration) {
- transitionComplete();
- raf = window.cancelAnimationFrame(doFrame);
- } else {
- raf = window.requestAnimationFrame(doFrame);
- }
- }
-
- t1 = Date.now();
- raf = window.requestAnimationFrame(doFrame);
-
- return Promise.resolve();
- };
-
- },{"../../components/drawing":72,"../../registry":257,"./axes":212,"d3":16}],236:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var traceIs = _dereq_('../../registry').traceIs;
- var autoType = _dereq_('./axis_autotype');
-
- /*
- * data: the plot data to use in choosing auto type
- * name: axis object name (ie 'xaxis') if one should be stored
- */
- module.exports = function handleTypeDefaults(containerIn, containerOut, coerce, options) {
- var axType = coerce('type', (options.splomStash || {}).type);
-
- if(axType === '-') {
- setAutoType(containerOut, options.data);
-
- if(containerOut.type === '-') {
- containerOut.type = 'linear';
- } else {
- // copy autoType back to input axis
- // note that if this object didn't exist
- // in the input layout, we have to put it in
- // this happens in the main supplyDefaults function
- containerIn.type = containerOut.type;
- }
- }
- };
-
- function setAutoType(ax, data) {
- // new logic: let people specify any type they want,
- // only autotype if type is '-'
- if(ax.type !== '-') return;
-
- var id = ax._id;
- var axLetter = id.charAt(0);
-
- // support 3d
- if(id.indexOf('scene') !== -1) id = axLetter;
-
- var d0 = getFirstNonEmptyTrace(data, id, axLetter);
- if(!d0) return;
-
- // first check for histograms, as the count direction
- // should always default to a linear axis
- if(d0.type === 'histogram' &&
- axLetter === {v: 'y', h: 'x'}[d0.orientation || 'v']) {
- ax.type = 'linear';
- return;
- }
-
- var calAttr = axLetter + 'calendar';
- var calendar = d0[calAttr];
- var opts = {noMultiCategory: !traceIs(d0, 'cartesian') || traceIs(d0, 'noMultiCategory')};
- var i;
-
- // check all boxes on this x axis to see
- // if they're dates, numbers, or categories
- if(isBoxWithoutPositionCoords(d0, axLetter)) {
- var posLetter = getBoxPosLetter(d0);
- var boxPositions = [];
-
- for(i = 0; i < data.length; i++) {
- var trace = data[i];
- if(!traceIs(trace, 'box-violin') || (trace[axLetter + 'axis'] || axLetter) !== id) continue;
-
- if(trace[posLetter] !== undefined) boxPositions.push(trace[posLetter][0]);
- else if(trace.name !== undefined) boxPositions.push(trace.name);
- else boxPositions.push('text');
-
- if(trace[calAttr] !== calendar) calendar = undefined;
- }
-
- ax.type = autoType(boxPositions, calendar, opts);
- }
- else if(d0.type === 'splom') {
- var dimensions = d0.dimensions;
- var diag = d0._diag;
- for(i = 0; i < dimensions.length; i++) {
- var dim = dimensions[i];
- if(dim.visible && (diag[i][0] === id || diag[i][1] === id)) {
- ax.type = autoType(dim.values, calendar, opts);
- break;
- }
- }
- }
- else {
- ax.type = autoType(d0[axLetter] || [d0[axLetter + '0']], calendar, opts);
- }
- }
-
- function getFirstNonEmptyTrace(data, id, axLetter) {
- for(var i = 0; i < data.length; i++) {
- var trace = data[i];
-
- if(trace.type === 'splom' &&
- trace._length > 0 &&
- (trace['_' + axLetter + 'axes'] || {})[id]
- ) {
- return trace;
- }
-
- if((trace[axLetter + 'axis'] || axLetter) === id) {
- if(isBoxWithoutPositionCoords(trace, axLetter)) {
- return trace;
- }
- else if((trace[axLetter] || []).length || trace[axLetter + '0']) {
- return trace;
- }
- }
- }
- }
-
- function getBoxPosLetter(trace) {
- return {v: 'x', h: 'y'}[trace.orientation || 'v'];
- }
-
- function isBoxWithoutPositionCoords(trace, axLetter) {
- var posLetter = getBoxPosLetter(trace);
- var isBox = traceIs(trace, 'box-violin');
- var isCandlestick = traceIs(trace._fullInput || {}, 'candlestick');
-
- return (
- isBox &&
- !isCandlestick &&
- axLetter === posLetter &&
- trace[posLetter] === undefined &&
- trace[posLetter + '0'] === undefined
- );
- }
-
- },{"../../registry":257,"./axis_autotype":213}],237:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Registry = _dereq_('../registry');
- var Lib = _dereq_('../lib');
-
- /*
- * Create or update an observer. This function is designed to be
- * idempotent so that it can be called over and over as the component
- * updates, and will attach and detach listeners as needed.
- *
- * @param {optional object} container
- * An object on which the observer is stored. This is the mechanism
- * by which it is idempotent. If it already exists, another won't be
- * added. Each time it's called, the value lookup table is updated.
- * @param {array} commandList
- * An array of commands, following either `buttons` of `updatemenus`
- * or `steps` of `sliders`.
- * @param {function} onchange
- * A listener called when the value is changed. Receives data object
- * with information about the new state.
- */
- exports.manageCommandObserver = function(gd, container, commandList, onchange) {
- var ret = {};
- var enabled = true;
-
- if(container && container._commandObserver) {
- ret = container._commandObserver;
- }
-
- if(!ret.cache) {
- ret.cache = {};
- }
-
- // Either create or just recompute this:
- ret.lookupTable = {};
-
- var binding = exports.hasSimpleAPICommandBindings(gd, commandList, ret.lookupTable);
-
- if(container && container._commandObserver) {
- if(!binding) {
- // If container exists and there are no longer any bindings,
- // remove existing:
- if(container._commandObserver.remove) {
- container._commandObserver.remove();
- container._commandObserver = null;
- return ret;
- }
- } else {
- // If container exists and there *are* bindings, then the lookup
- // table should have been updated and check is already attached,
- // so there's nothing to be done:
- return ret;
-
-
- }
- }
-
- // Determine whether there's anything to do for this binding:
-
- if(binding) {
- // Build the cache:
- bindingValueHasChanged(gd, binding, ret.cache);
-
- ret.check = function check() {
- if(!enabled) return;
-
- var update = bindingValueHasChanged(gd, binding, ret.cache);
-
- if(update.changed && onchange) {
- // Disable checks for the duration of this command in order to avoid
- // infinite loops:
- if(ret.lookupTable[update.value] !== undefined) {
- ret.disable();
- Promise.resolve(onchange({
- value: update.value,
- type: binding.type,
- prop: binding.prop,
- traces: binding.traces,
- index: ret.lookupTable[update.value]
- })).then(ret.enable, ret.enable);
- }
- }
-
- return update.changed;
- };
-
- var checkEvents = [
- 'plotly_relayout',
- 'plotly_redraw',
- 'plotly_restyle',
- 'plotly_update',
- 'plotly_animatingframe',
- 'plotly_afterplot'
- ];
-
- for(var i = 0; i < checkEvents.length; i++) {
- gd._internalOn(checkEvents[i], ret.check);
- }
-
- ret.remove = function() {
- for(var i = 0; i < checkEvents.length; i++) {
- gd._removeInternalListener(checkEvents[i], ret.check);
- }
- };
- } else {
- // TODO: It'd be really neat to actually give a *reason* for this, but at least a warning
- // is a start
- Lib.log('Unable to automatically bind plot updates to API command');
-
- ret.lookupTable = {};
- ret.remove = function() {};
- }
-
- ret.disable = function disable() {
- enabled = false;
- };
-
- ret.enable = function enable() {
- enabled = true;
- };
-
- if(container) {
- container._commandObserver = ret;
- }
-
- return ret;
- };
-
- /*
- * This function checks to see if an array of objects containing
- * method and args properties is compatible with automatic two-way
- * binding. The criteria right now are that
- *
- * 1. multiple traces may be affected
- * 2. only one property may be affected
- * 3. the same property must be affected by all commands
- */
- exports.hasSimpleAPICommandBindings = function(gd, commandList, bindingsByValue) {
- var i;
- var n = commandList.length;
-
- var refBinding;
-
- for(i = 0; i < n; i++) {
- var binding;
- var command = commandList[i];
- var method = command.method;
- var args = command.args;
-
- if(!Array.isArray(args)) args = [];
-
- // If any command has no method, refuse to bind:
- if(!method) {
- return false;
- }
- var bindings = exports.computeAPICommandBindings(gd, method, args);
-
- // Right now, handle one and *only* one property being set:
- if(bindings.length !== 1) {
- return false;
- }
-
- if(!refBinding) {
- refBinding = bindings[0];
- if(Array.isArray(refBinding.traces)) {
- refBinding.traces.sort();
- }
- } else {
- binding = bindings[0];
- if(binding.type !== refBinding.type) {
- return false;
- }
- if(binding.prop !== refBinding.prop) {
- return false;
- }
- if(Array.isArray(refBinding.traces)) {
- if(Array.isArray(binding.traces)) {
- binding.traces.sort();
- for(var j = 0; j < refBinding.traces.length; j++) {
- if(refBinding.traces[j] !== binding.traces[j]) {
- return false;
- }
- }
- } else {
- return false;
- }
- } else {
- if(binding.prop !== refBinding.prop) {
- return false;
- }
- }
- }
-
- binding = bindings[0];
- var value = binding.value;
- if(Array.isArray(value)) {
- if(value.length === 1) {
- value = value[0];
- } else {
- return false;
- }
- }
- if(bindingsByValue) {
- bindingsByValue[value] = i;
- }
- }
-
- return refBinding;
- };
-
- function bindingValueHasChanged(gd, binding, cache) {
- var container, value, obj;
- var changed = false;
-
- if(binding.type === 'data') {
- // If it's data, we need to get a trace. Based on the limited scope
- // of what we cover, we can just take the first trace from the list,
- // or otherwise just the first trace:
- container = gd._fullData[binding.traces !== null ? binding.traces[0] : 0];
- } else if(binding.type === 'layout') {
- container = gd._fullLayout;
- } else {
- return false;
- }
-
- value = Lib.nestedProperty(container, binding.prop).get();
-
- obj = cache[binding.type] = cache[binding.type] || {};
-
- if(obj.hasOwnProperty(binding.prop)) {
- if(obj[binding.prop] !== value) {
- changed = true;
- }
- }
-
- obj[binding.prop] = value;
-
- return {
- changed: changed,
- value: value
- };
- }
-
- /*
- * Execute an API command. There's really not much to this; it just provides
- * a common hook so that implementations don't need to be synchronized across
- * multiple components with the ability to invoke API commands.
- *
- * @param {string} method
- * The name of the plotly command to execute. Must be one of 'animate',
- * 'restyle', 'relayout', 'update'.
- * @param {array} args
- * A list of arguments passed to the API command
- */
- exports.executeAPICommand = function(gd, method, args) {
- if(method === 'skip') return Promise.resolve();
-
- var _method = Registry.apiMethodRegistry[method];
- var allArgs = [gd];
- if(!Array.isArray(args)) args = [];
-
- for(var i = 0; i < args.length; i++) {
- allArgs.push(args[i]);
- }
-
- return _method.apply(null, allArgs).catch(function(err) {
- Lib.warn('API call to Plotly.' + method + ' rejected.', err);
- return Promise.reject(err);
- });
- };
-
- exports.computeAPICommandBindings = function(gd, method, args) {
- var bindings;
-
- if(!Array.isArray(args)) args = [];
-
- switch(method) {
- case 'restyle':
- bindings = computeDataBindings(gd, args);
- break;
- case 'relayout':
- bindings = computeLayoutBindings(gd, args);
- break;
- case 'update':
- bindings = computeDataBindings(gd, [args[0], args[2]])
- .concat(computeLayoutBindings(gd, [args[1]]));
- break;
- case 'animate':
- bindings = computeAnimateBindings(gd, args);
- break;
- default:
- // This is the case where intelligent logic about what affects
- // this command is not implemented. It causes no ill effects.
- // For example, addFrames simply won't bind to a control component.
- bindings = [];
- }
- return bindings;
- };
-
- function computeAnimateBindings(gd, args) {
- // We'll assume that the only relevant modification an animation
- // makes that's meaningfully tracked is the frame:
- if(Array.isArray(args[0]) && args[0].length === 1 && ['string', 'number'].indexOf(typeof args[0][0]) !== -1) {
- return [{type: 'layout', prop: '_currentFrame', value: args[0][0].toString()}];
- } else {
- return [];
- }
- }
-
- function computeLayoutBindings(gd, args) {
- var bindings = [];
-
- var astr = args[0];
- var aobj = {};
- if(typeof astr === 'string') {
- aobj[astr] = args[1];
- } else if(Lib.isPlainObject(astr)) {
- aobj = astr;
- } else {
- return bindings;
- }
-
- crawl(aobj, function(path, attrName, attr) {
- bindings.push({type: 'layout', prop: path, value: attr});
- }, '', 0);
-
- return bindings;
- }
-
- function computeDataBindings(gd, args) {
- var traces, astr, val, aobj;
- var bindings = [];
-
- // Logic copied from Plotly.restyle:
- astr = args[0];
- val = args[1];
- traces = args[2];
- aobj = {};
- if(typeof astr === 'string') {
- aobj[astr] = val;
- } else if(Lib.isPlainObject(astr)) {
- // the 3-arg form
- aobj = astr;
-
- if(traces === undefined) {
- traces = val;
- }
- } else {
- return bindings;
- }
-
- if(traces === undefined) {
- // Explicitly assign this to null instead of undefined:
- traces = null;
- }
-
- crawl(aobj, function(path, attrName, attr) {
- var thisTraces;
- if(Array.isArray(attr)) {
- var nAttr = Math.min(attr.length, gd.data.length);
- if(traces) {
- nAttr = Math.min(nAttr, traces.length);
- }
- thisTraces = [];
- for(var j = 0; j < nAttr; j++) {
- thisTraces[j] = traces ? traces[j] : j;
- }
- } else {
- thisTraces = traces ? traces.slice(0) : null;
- }
-
- // Convert [7] to just 7 when traces is null:
- if(thisTraces === null) {
- if(Array.isArray(attr)) {
- attr = attr[0];
- }
- } else if(Array.isArray(thisTraces)) {
- if(!Array.isArray(attr)) {
- var tmp = attr;
- attr = [];
- for(var i = 0; i < thisTraces.length; i++) {
- attr[i] = tmp;
- }
- }
- attr.length = Math.min(thisTraces.length, attr.length);
- }
-
- bindings.push({
- type: 'data',
- prop: path,
- traces: thisTraces,
- value: attr
- });
- }, '', 0);
-
- return bindings;
- }
-
- function crawl(attrs, callback, path, depth) {
- Object.keys(attrs).forEach(function(attrName) {
- var attr = attrs[attrName];
-
- if(attrName[0] === '_') return;
-
- var thisPath = path + (depth > 0 ? '.' : '') + attrName;
-
- if(Lib.isPlainObject(attr)) {
- crawl(attr, callback, thisPath, depth + 1);
- } else {
- // Only execute the callback on leaf nodes:
- callback(thisPath, attrName, attr);
- }
- });
- }
-
- },{"../lib":168,"../registry":257}],238:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var extendFlat = _dereq_('../lib/extend').extendFlat;
-
- /**
- * Make a xy domain attribute group
- *
- * @param {object} opts
- * @param {string}
- * opts.name: name to be inserted in the default description
- * @param {boolean}
- * opts.trace: set to true for trace containers
- * @param {string}
- * opts.editType: editType for all pieces
- * @param {boolean}
- * opts.noGridCell: set to true to omit `row` and `column`
- *
- * @param {object} extra
- * @param {string}
- * extra.description: extra description. N.B we use
- * a separate extra container to make it compatible with
- * the compress_attributes transform.
- *
- * @return {object} attributes object containing {x,y} as specified
- */
- exports.attributes = function(opts, extra) {
- opts = opts || {};
- extra = extra || {};
-
- var base = {
- valType: 'info_array',
-
- editType: opts.editType,
- items: [
- {valType: 'number', min: 0, max: 1, editType: opts.editType},
- {valType: 'number', min: 0, max: 1, editType: opts.editType}
- ],
- dflt: [0, 1]
- };
-
- var namePart = opts.name ? opts.name + ' ' : '';
- var contPart = opts.trace ? 'trace ' : 'subplot ';
- var descPart = extra.description ? ' ' + extra.description : '';
-
- var out = {
- x: extendFlat({}, base, {
-
- }),
- y: extendFlat({}, base, {
-
- }),
- editType: opts.editType
- };
-
- if(!opts.noGridCell) {
- out.row = {
- valType: 'integer',
- min: 0,
- dflt: 0,
-
- editType: opts.editType,
-
- };
- out.column = {
- valType: 'integer',
- min: 0,
- dflt: 0,
-
- editType: opts.editType,
-
- };
- }
-
- return out;
- };
-
- exports.defaults = function(containerOut, layout, coerce, dfltDomains) {
- var dfltX = (dfltDomains && dfltDomains.x) || [0, 1];
- var dfltY = (dfltDomains && dfltDomains.y) || [0, 1];
-
- var grid = layout.grid;
- if(grid) {
- var column = coerce('domain.column');
- if(column !== undefined) {
- if(column < grid.columns) dfltX = grid._domains.x[column];
- else delete containerOut.domain.column;
- }
-
- var row = coerce('domain.row');
- if(row !== undefined) {
- if(row < grid.rows) dfltY = grid._domains.y[row];
- else delete containerOut.domain.row;
- }
- }
-
- coerce('domain.x', dfltX);
- coerce('domain.y', dfltY);
- };
-
- },{"../lib/extend":162}],239:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /*
- * make a font attribute group
- *
- * @param {object} opts
- * @param {string}
- * opts.description: where & how this font is used
- * @param {optional bool} arrayOk:
- * should each part (family, size, color) be arrayOk? default false.
- * @param {string} editType:
- * the editType for all pieces of this font
- * @param {optional string} colorEditType:
- * a separate editType just for color
- *
- * @return {object} attributes object containing {family, size, color} as specified
- */
- module.exports = function(opts) {
- var editType = opts.editType;
- var colorEditType = opts.colorEditType;
- if(colorEditType === undefined) colorEditType = editType;
- var attrs = {
- family: {
- valType: 'string',
-
- noBlank: true,
- strict: true,
- editType: editType,
-
- },
- size: {
- valType: 'number',
-
- min: 1,
- editType: editType
- },
- color: {
- valType: 'color',
-
- editType: colorEditType
- },
- editType: editType,
- // blank strings so compress_attributes can remove
- // TODO - that's uber hacky... better solution?
-
- };
-
- if(opts.arrayOk) {
- attrs.family.arrayOk = true;
- attrs.size.arrayOk = true;
- attrs.color.arrayOk = true;
- }
-
- return attrs;
- };
-
- },{}],240:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- _isLinkedToArray: 'frames_entry',
-
- group: {
- valType: 'string',
-
-
- },
- name: {
- valType: 'string',
-
-
- },
- traces: {
- valType: 'any',
-
-
- },
- baseframe: {
- valType: 'string',
-
-
- },
- data: {
- valType: 'any',
-
-
- },
- layout: {
- valType: 'any',
-
-
- }
- };
-
- },{}],241:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Registry = _dereq_('../registry');
- var SUBPLOT_PATTERN = _dereq_('./cartesian/constants').SUBPLOT_PATTERN;
-
- /**
- * Get calcdata trace(s) associated with a given subplot
- *
- * @param {array} calcData: as in gd.calcdata
- * @param {string} type: subplot type
- * @param {string} subplotId: subplot id to look for
- *
- * @return {array} array of calcdata traces
- */
- exports.getSubplotCalcData = function(calcData, type, subplotId) {
- var basePlotModule = Registry.subplotsRegistry[type];
- if(!basePlotModule) return [];
-
- var attr = basePlotModule.attr;
- var subplotCalcData = [];
-
- for(var i = 0; i < calcData.length; i++) {
- var calcTrace = calcData[i];
- var trace = calcTrace[0].trace;
-
- if(trace[attr] === subplotId) subplotCalcData.push(calcTrace);
- }
-
- return subplotCalcData;
- };
- /**
- * Get calcdata trace(s) that can be plotted with a given module
- * NOTE: this isn't necessarily just exactly matching trace type,
- * if multiple trace types use the same plotting routine, they will be
- * collected here.
- * In order to not plot the same thing multiple times, we return two arrays,
- * the calcdata we *will* plot with this module, and the ones we *won't*
- *
- * @param {array} calcdata: as in gd.calcdata
- * @param {object|string|fn} arg1:
- * the plotting module, or its name, or its plot method
- *
- * @return {array[array]} [foundCalcdata, remainingCalcdata]
- */
- exports.getModuleCalcData = function(calcdata, arg1) {
- var moduleCalcData = [];
- var remainingCalcData = [];
-
- var plotMethod;
- if(typeof arg1 === 'string') {
- plotMethod = Registry.getModule(arg1).plot;
- } else if(typeof arg1 === 'function') {
- plotMethod = arg1;
- } else {
- plotMethod = arg1.plot;
- }
- if(!plotMethod) {
- return [moduleCalcData, calcdata];
- }
-
- for(var i = 0; i < calcdata.length; i++) {
- var cd = calcdata[i];
- var trace = cd[0].trace;
- // N.B. 'legendonly' traces do not make it past here
- if(trace.visible !== true) continue;
-
- // group calcdata trace not by 'module' (as the name of this function
- // would suggest), but by 'module plot method' so that if some traces
- // share the same module plot method (e.g. bar and histogram), we
- // only call it one!
- if(trace._module.plot === plotMethod) {
- moduleCalcData.push(cd);
- } else {
- remainingCalcData.push(cd);
- }
- }
-
- return [moduleCalcData, remainingCalcData];
- };
-
- /**
- * Get the data trace(s) associated with a given subplot.
- *
- * @param {array} data plotly full data array.
- * @param {string} type subplot type to look for.
- * @param {string} subplotId subplot id to look for.
- *
- * @return {array} list of trace objects.
- *
- */
- exports.getSubplotData = function getSubplotData(data, type, subplotId) {
- if(!Registry.subplotsRegistry[type]) return [];
-
- var attr = Registry.subplotsRegistry[type].attr;
- var subplotData = [];
- var trace, subplotX, subplotY;
-
- if(type === 'gl2d') {
- var spmatch = subplotId.match(SUBPLOT_PATTERN);
- subplotX = 'x' + spmatch[1];
- subplotY = 'y' + spmatch[2];
- }
-
- for(var i = 0; i < data.length; i++) {
- trace = data[i];
-
- if(type === 'gl2d' && Registry.traceIs(trace, 'gl2d')) {
- if(trace[attr[0]] === subplotX && trace[attr[1]] === subplotY) {
- subplotData.push(trace);
- }
- }
- else {
- if(trace[attr] === subplotId) subplotData.push(trace);
- }
- }
-
- return subplotData;
- };
-
- },{"../registry":257,"./cartesian/constants":218}],242:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- function xformMatrix(m, v) {
- var out = [0, 0, 0, 0];
- var i, j;
-
- for(i = 0; i < 4; ++i) {
- for(j = 0; j < 4; ++j) {
- out[j] += m[4 * i + j] * v[i];
- }
- }
-
- return out;
- }
-
- function project(camera, v) {
- var p = xformMatrix(camera.projection,
- xformMatrix(camera.view,
- xformMatrix(camera.model, [v[0], v[1], v[2], 1])));
- return p;
- }
-
- module.exports = project;
-
- },{}],243:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var fontAttrs = _dereq_('./font_attributes');
- var animationAttrs = _dereq_('./animation_attributes');
- var colorAttrs = _dereq_('../components/color/attributes');
- var colorscaleAttrs = _dereq_('../components/colorscale/layout_attributes');
- var padAttrs = _dereq_('./pad_attributes');
- var extendFlat = _dereq_('../lib/extend').extendFlat;
-
- var globalFont = fontAttrs({
- editType: 'calc',
-
- });
- globalFont.family.dflt = '"Open Sans", verdana, arial, sans-serif';
- globalFont.size.dflt = 12;
- globalFont.color.dflt = colorAttrs.defaultLine;
-
- module.exports = {
- font: globalFont,
- title: {
- text: {
- valType: 'string',
-
- editType: 'layoutstyle',
-
- },
- font: fontAttrs({
- editType: 'layoutstyle',
-
- }),
- xref: {
- valType: 'enumerated',
- dflt: 'container',
- values: ['container', 'paper'],
-
- editType: 'layoutstyle',
-
- },
- yref: {
- valType: 'enumerated',
- dflt: 'container',
- values: ['container', 'paper'],
-
- editType: 'layoutstyle',
-
- },
- x: {
- valType: 'number',
- min: 0,
- max: 1,
- dflt: 0.5,
-
- editType: 'layoutstyle',
-
- },
- y: {
- valType: 'number',
- min: 0,
- max: 1,
- dflt: 'auto',
-
- editType: 'layoutstyle',
-
- },
- xanchor: {
- valType: 'enumerated',
- dflt: 'auto',
- values: ['auto', 'left', 'center', 'right'],
-
- editType: 'layoutstyle',
-
- },
- yanchor: {
- valType: 'enumerated',
- dflt: 'auto',
- values: ['auto', 'top', 'middle', 'bottom'],
-
- editType: 'layoutstyle',
-
- },
- pad: extendFlat(padAttrs({editType: 'layoutstyle'}), {
-
- }),
- editType: 'layoutstyle'
- },
- autosize: {
- valType: 'boolean',
-
- dflt: false,
- // autosize, width, and height get special editType treatment in _relayout
- // so we can handle noop resizes more efficiently
- editType: 'none',
-
- },
- width: {
- valType: 'number',
-
- min: 10,
- dflt: 700,
- editType: 'plot',
-
- },
- height: {
- valType: 'number',
-
- min: 10,
- dflt: 450,
- editType: 'plot',
-
- },
- margin: {
- l: {
- valType: 'number',
-
- min: 0,
- dflt: 80,
- editType: 'plot',
-
- },
- r: {
- valType: 'number',
-
- min: 0,
- dflt: 80,
- editType: 'plot',
-
- },
- t: {
- valType: 'number',
-
- min: 0,
- dflt: 100,
- editType: 'plot',
-
- },
- b: {
- valType: 'number',
-
- min: 0,
- dflt: 80,
- editType: 'plot',
-
- },
- pad: {
- valType: 'number',
-
- min: 0,
- dflt: 0,
- editType: 'plot',
-
- },
- autoexpand: {
- valType: 'boolean',
-
- dflt: true,
- editType: 'plot'
- },
- editType: 'plot'
- },
- paper_bgcolor: {
- valType: 'color',
-
- dflt: colorAttrs.background,
- editType: 'plot',
-
- },
- plot_bgcolor: {
- // defined here, but set in cartesian.supplyLayoutDefaults
- // because it needs to know if there are (2D) axes or not
- valType: 'color',
-
- dflt: colorAttrs.background,
- editType: 'layoutstyle',
-
- },
- separators: {
- valType: 'string',
-
- editType: 'plot',
-
- },
- hidesources: {
- valType: 'boolean',
-
- dflt: false,
- editType: 'plot',
-
- },
- showlegend: {
- // handled in legend.supplyLayoutDefaults
- // but included here because it's not in the legend object
- valType: 'boolean',
-
- editType: 'legend',
-
- },
- colorway: {
- valType: 'colorlist',
- dflt: colorAttrs.defaults,
-
- editType: 'calc',
-
- },
- colorscale: colorscaleAttrs,
- datarevision: {
- valType: 'any',
-
- editType: 'calc',
-
- },
- uirevision: {
- valType: 'any',
-
- editType: 'none',
-
- },
- editrevision: {
- valType: 'any',
-
- editType: 'none',
-
- },
- selectionrevision: {
- valType: 'any',
-
- editType: 'none',
-
- },
- template: {
- valType: 'any',
-
- editType: 'calc',
-
- },
- modebar: {
- orientation: {
- valType: 'enumerated',
- values: ['v', 'h'],
- dflt: 'h',
-
- editType: 'modebar',
-
- },
- bgcolor: {
- valType: 'color',
-
- editType: 'modebar',
-
- },
- color: {
- valType: 'color',
-
- editType: 'modebar',
-
- },
- activecolor: {
- valType: 'color',
-
- editType: 'modebar',
-
- },
- uirevision: {
- valType: 'any',
-
- editType: 'none',
-
- },
- editType: 'modebar'
- },
-
- meta: {
- valType: 'data_array',
- editType: 'plot',
-
- },
-
- transition: extendFlat({}, animationAttrs.transition, {
-
- editType: 'none'
- }),
-
- _deprecated: {
- title: {
- valType: 'string',
-
- editType: 'layoutstyle',
-
- },
- titlefont: fontAttrs({
- editType: 'layoutstyle',
-
- })
- }
- };
-
- },{"../components/color/attributes":50,"../components/colorscale/layout_attributes":64,"../lib/extend":162,"./animation_attributes":207,"./font_attributes":239,"./pad_attributes":244}],244:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- /**
- * Creates a set of padding attributes.
- *
- * @param {object} opts
- * @param {string} editType:
- * the editType for all pieces of this padding definition
- *
- * @return {object} attributes object containing {t, r, b, l} as specified
- */
- module.exports = function(opts) {
- var editType = opts.editType;
- return {
- t: {
- valType: 'number',
- dflt: 0,
-
- editType: editType,
-
- },
- r: {
- valType: 'number',
- dflt: 0,
-
- editType: editType,
-
- },
- b: {
- valType: 'number',
- dflt: 0,
-
- editType: editType,
-
- },
- l: {
- valType: 'number',
- dflt: 0,
-
- editType: editType,
-
- },
- editType: editType
- };
- };
-
- },{}],245:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Registry = _dereq_('../registry');
- var PlotSchema = _dereq_('../plot_api/plot_schema');
- var Template = _dereq_('../plot_api/plot_template');
- var Lib = _dereq_('../lib');
- var Color = _dereq_('../components/color');
- var BADNUM = _dereq_('../constants/numerical').BADNUM;
-
- var axisIDs = _dereq_('../plots/cartesian/axis_ids');
-
- var animationAttrs = _dereq_('./animation_attributes');
- var frameAttrs = _dereq_('./frame_attributes');
-
- var relinkPrivateKeys = Lib.relinkPrivateKeys;
- var _ = Lib._;
-
- var plots = module.exports = {};
-
- // Expose registry methods on Plots for backward-compatibility
- Lib.extendFlat(plots, Registry);
-
- plots.attributes = _dereq_('./attributes');
- plots.attributes.type.values = plots.allTypes;
- plots.fontAttrs = _dereq_('./font_attributes');
- plots.layoutAttributes = _dereq_('./layout_attributes');
-
- // TODO make this a plot attribute?
- plots.fontWeight = 'normal';
-
- var transformsRegistry = plots.transformsRegistry;
-
- var commandModule = _dereq_('./command');
- plots.executeAPICommand = commandModule.executeAPICommand;
- plots.computeAPICommandBindings = commandModule.computeAPICommandBindings;
- plots.manageCommandObserver = commandModule.manageCommandObserver;
- plots.hasSimpleAPICommandBindings = commandModule.hasSimpleAPICommandBindings;
-
- // in some cases the browser doesn't seem to know how big
- // the text is at first, so it needs to draw it,
- // then wait a little, then draw it again
- plots.redrawText = function(gd) {
- gd = Lib.getGraphDiv(gd);
-
- // do not work if polar is present
- if((gd.data && gd.data[0] && gd.data[0].r)) return;
-
- return new Promise(function(resolve) {
- setTimeout(function() {
- Registry.getComponentMethod('annotations', 'draw')(gd);
- Registry.getComponentMethod('legend', 'draw')(gd);
-
- (gd.calcdata || []).forEach(function(d) {
- if(d[0] && d[0].t && d[0].t.cb) d[0].t.cb();
- });
-
- resolve(plots.previousPromises(gd));
- }, 300);
- });
- };
-
- // resize plot about the container size
- plots.resize = function(gd) {
- gd = Lib.getGraphDiv(gd);
-
- return new Promise(function(resolve, reject) {
-
- function isHidden(gd) {
- var display = window.getComputedStyle(gd).display;
- return !display || display === 'none';
- }
-
- if(!gd || isHidden(gd)) {
- reject(new Error('Resize must be passed a displayed plot div element.'));
- }
-
- if(gd._redrawTimer) clearTimeout(gd._redrawTimer);
-
- gd._redrawTimer = setTimeout(function() {
- // return if there is nothing to resize or is hidden
- if(!gd.layout || (gd.layout.width && gd.layout.height) || isHidden(gd)) {
- resolve(gd);
- return;
- }
-
- delete gd.layout.width;
- delete gd.layout.height;
-
- // autosizing doesn't count as a change that needs saving
- var oldchanged = gd.changed;
-
- // nor should it be included in the undo queue
- gd.autoplay = true;
-
- Registry.call('relayout', gd, {autosize: true}).then(function() {
- gd.changed = oldchanged;
- resolve(gd);
- });
- }, 100);
- });
- };
-
-
- // for use in Lib.syncOrAsync, check if there are any
- // pending promises in this plot and wait for them
- plots.previousPromises = function(gd) {
- if((gd._promises || []).length) {
- return Promise.all(gd._promises)
- .then(function() { gd._promises = []; });
- }
- };
-
- /**
- * Adds the 'Edit chart' link.
- * Note that now Plotly.plot() calls this so it can regenerate whenever it replots
- *
- * Add source links to your graph inside the 'showSources' config argument.
- */
- plots.addLinks = function(gd) {
- // Do not do anything if showLink and showSources are not set to true in config
- if(!gd._context.showLink && !gd._context.showSources) return;
-
- var fullLayout = gd._fullLayout;
-
- var linkContainer = Lib.ensureSingle(fullLayout._paper, 'text', 'js-plot-link-container', function(s) {
- s.style({
- 'font-family': '"Open Sans", Arial, sans-serif',
- 'font-size': '12px',
- 'fill': Color.defaultLine,
- 'pointer-events': 'all'
- })
- .each(function() {
- var links = d3.select(this);
- links.append('tspan').classed('js-link-to-tool', true);
- links.append('tspan').classed('js-link-spacer', true);
- links.append('tspan').classed('js-sourcelinks', true);
- });
- });
-
- // The text node inside svg
- var text = linkContainer.node();
- var attrs = {y: fullLayout._paper.attr('height') - 9};
-
- // If text's width is bigger than the layout
- // Check that text is a child node or document.body
- // because otherwise IE/Edge might throw an exception
- // when calling getComputedTextLength().
- // Apparently offsetParent is null for invisibles.
- if(document.body.contains(text) && text.getComputedTextLength() >= (fullLayout.width - 20)) {
- // Align the text at the left
- attrs['text-anchor'] = 'start';
- attrs.x = 5;
- }
- else {
- // Align the text at the right
- attrs['text-anchor'] = 'end';
- attrs.x = fullLayout._paper.attr('width') - 7;
- }
-
- linkContainer.attr(attrs);
-
- var toolspan = linkContainer.select('.js-link-to-tool');
- var spacespan = linkContainer.select('.js-link-spacer');
- var sourcespan = linkContainer.select('.js-sourcelinks');
-
- if(gd._context.showSources) gd._context.showSources(gd);
-
- // 'view in plotly' link for embedded plots
- if(gd._context.showLink) positionPlayWithData(gd, toolspan);
-
- // separator if we have both sources and tool link
- spacespan.text((toolspan.text() && sourcespan.text()) ? ' - ' : '');
- };
-
- // note that now this function is only adding the brand in
- // iframes and 3rd-party apps
- function positionPlayWithData(gd, container) {
- container.text('');
- var link = container.append('a')
- .attr({
- 'xlink:xlink:href': '#',
- 'class': 'link--impt link--embedview',
- 'font-weight': 'bold'
- })
- .text(gd._context.linkText + ' ' + String.fromCharCode(187));
-
- if(gd._context.sendData) {
- link.on('click', function() {
- plots.sendDataToCloud(gd);
- });
- }
- else {
- var path = window.location.pathname.split('/');
- var query = window.location.search;
- link.attr({
- 'xlink:xlink:show': 'new',
- 'xlink:xlink:href': '/' + path[2].split('.')[0] + '/' + path[1] + query
- });
- }
- }
-
- plots.sendDataToCloud = function(gd) {
- gd.emit('plotly_beforeexport');
-
- var baseUrl = (window.PLOTLYENV || {}).BASE_URL || gd._context.plotlyServerURL;
-
- var hiddenformDiv = d3.select(gd)
- .append('div')
- .attr('id', 'hiddenform')
- .style('display', 'none');
-
- var hiddenform = hiddenformDiv
- .append('form')
- .attr({
- action: baseUrl + '/external',
- method: 'post',
- target: '_blank'
- });
-
- var hiddenformInput = hiddenform
- .append('input')
- .attr({
- type: 'text',
- name: 'data'
- });
-
- hiddenformInput.node().value = plots.graphJson(gd, false, 'keepdata');
- hiddenform.node().submit();
- hiddenformDiv.remove();
-
- gd.emit('plotly_afterexport');
- return false;
- };
-
- var d3FormatKeys = [
- 'days', 'shortDays', 'months', 'shortMonths', 'periods',
- 'dateTime', 'date', 'time',
- 'decimal', 'thousands', 'grouping', 'currency'
- ];
-
- var extraFormatKeys = [
- 'year', 'month', 'dayMonth', 'dayMonthYear'
- ];
-
- /*
- * Fill in default values
- * @param {DOM element} gd
- * @param {object} opts
- * @param {boolean} opts.skipUpdateCalc: normally if the existing gd.calcdata looks
- * compatible with the new gd._fullData we finish by linking the new _fullData traces
- * to the old gd.calcdata, so it's correctly set if we're not going to recalc. But also,
- * if there are calcTransforms on the trace, we first remap data arrays from the old full
- * trace into the new one. Use skipUpdateCalc to defer this (needed by Plotly.react)
- *
- * gd.data, gd.layout:
- * are precisely what the user specified (except as modified by cleanData/cleanLayout),
- * these fields shouldn't be modified (except for filling in some auto values)
- * nor used directly after the supply defaults step.
- *
- * gd._fullData, gd._fullLayout:
- * are complete descriptions of how to draw the plot,
- * use these fields in all required computations.
- *
- * gd._fullLayout._modules
- * is a list of all the trace modules required to draw the plot.
- *
- * gd._fullLayout._visibleModules
- * subset of _modules, a list of modules corresponding to visible:true traces.
- *
- * gd._fullLayout._basePlotModules
- * is a list of all the plot modules required to draw the plot.
- *
- * gd._fullLayout._transformModules
- * is a list of all the transform modules invoked.
- *
- */
- plots.supplyDefaults = function(gd, opts) {
- var skipUpdateCalc = opts && opts.skipUpdateCalc;
- var oldFullLayout = gd._fullLayout || {};
-
- if(oldFullLayout._skipDefaults) {
- delete oldFullLayout._skipDefaults;
- return;
- }
-
- var newFullLayout = gd._fullLayout = {};
- var newLayout = gd.layout || {};
-
- var oldFullData = gd._fullData || [];
- var newFullData = gd._fullData = [];
- var newData = gd.data || [];
-
- var oldCalcdata = gd.calcdata || [];
-
- var context = gd._context || {};
-
- var i;
-
- // Create all the storage space for frames, but only if doesn't already exist
- if(!gd._transitionData) plots.createTransitionData(gd);
-
- // So we only need to do this once (and since we have gd here)
- // get the translated placeholder titles.
- // These ones get used as default values so need to be known at supplyDefaults
- // others keep their blank defaults but render the placeholder as desired later
- // TODO: make these work the same way, only inserting the placeholder text at draw time?
- // The challenge is that this has slightly different behavior right now in editable mode:
- // using the placeholder as default makes this text permanently (but lightly) visible,
- // but explicit '' for these titles gives you a placeholder that's hidden until you mouse
- // over it - so you're not distracted by it if you really don't want a title, but if you do
- // and you're new to plotly you may not be able to find it.
- // When editable=false the two behave the same, no title is drawn.
- newFullLayout._dfltTitle = {
- plot: _(gd, 'Click to enter Plot title'),
- x: _(gd, 'Click to enter X axis title'),
- y: _(gd, 'Click to enter Y axis title'),
- colorbar: _(gd, 'Click to enter Colorscale title'),
- annotation: _(gd, 'new text')
- };
- newFullLayout._traceWord = _(gd, 'trace');
-
- var formatObj = getFormatObj(gd, d3FormatKeys);
-
- // stash the token from context so mapbox subplots can use it as default
- newFullLayout._mapboxAccessToken = context.mapboxAccessToken;
-
- // first fill in what we can of layout without looking at data
- // because fullData needs a few things from layout
- if(oldFullLayout._initialAutoSizeIsDone) {
-
- // coerce the updated layout while preserving width and height
- var oldWidth = oldFullLayout.width;
- var oldHeight = oldFullLayout.height;
-
- plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout, formatObj);
-
- if(!newLayout.width) newFullLayout.width = oldWidth;
- if(!newLayout.height) newFullLayout.height = oldHeight;
- plots.sanitizeMargins(newFullLayout);
- }
- else {
-
- // coerce the updated layout and autosize if needed
- plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout, formatObj);
-
- var missingWidthOrHeight = (!newLayout.width || !newLayout.height);
- var autosize = newFullLayout.autosize;
- var autosizable = context.autosizable;
- var initialAutoSize = missingWidthOrHeight && (autosize || autosizable);
-
- if(initialAutoSize) plots.plotAutoSize(gd, newLayout, newFullLayout);
- else if(missingWidthOrHeight) plots.sanitizeMargins(newFullLayout);
-
- // for backwards-compatibility with Plotly v1.x.x
- if(!autosize && missingWidthOrHeight) {
- newLayout.width = newFullLayout.width;
- newLayout.height = newFullLayout.height;
- }
- }
-
- newFullLayout._d3locale = getFormatter(formatObj, newFullLayout.separators);
- newFullLayout._extraFormat = getFormatObj(gd, extraFormatKeys);
-
- newFullLayout._initialAutoSizeIsDone = true;
-
- // keep track of how many traces are inputted
- newFullLayout._dataLength = newData.length;
-
- // clear the lists of trace and baseplot modules, and subplots
- newFullLayout._modules = [];
- newFullLayout._visibleModules = [];
- newFullLayout._basePlotModules = [];
- var subplots = newFullLayout._subplots = emptySubplotLists();
-
- // initialize axis and subplot hash objects for splom-generated grids
- var splomAxes = newFullLayout._splomAxes = {x: {}, y: {}};
- var splomSubplots = newFullLayout._splomSubplots = {};
- // initialize splom grid defaults
- newFullLayout._splomGridDflt = {};
-
- // for stacked area traces to share config across traces
- newFullLayout._scatterStackOpts = {};
- // for the first scatter trace on each subplot (so it knows tonext->tozero)
- newFullLayout._firstScatter = {};
-
- // for traces to request a default rangeslider on their x axes
- // eg set `_requestRangeslider.x2 = true` for xaxis2
- newFullLayout._requestRangeslider = {};
-
- // pull uids from old data to use as new defaults
- newFullLayout._traceUids = getTraceUids(oldFullData, newData);
-
- // then do the data
- newFullLayout._globalTransforms = (gd._context || {}).globalTransforms;
- plots.supplyDataDefaults(newData, newFullData, newLayout, newFullLayout);
-
- // redo grid size defaults with info about splom x/y axes,
- // and fill in generated cartesian axes and subplots
- var splomXa = Object.keys(splomAxes.x);
- var splomYa = Object.keys(splomAxes.y);
- if(splomXa.length > 1 && splomYa.length > 1) {
- Registry.getComponentMethod('grid', 'sizeDefaults')(newLayout, newFullLayout);
-
- for(i = 0; i < splomXa.length; i++) {
- Lib.pushUnique(subplots.xaxis, splomXa[i]);
- }
- for(i = 0; i < splomYa.length; i++) {
- Lib.pushUnique(subplots.yaxis, splomYa[i]);
- }
- for(var k in splomSubplots) {
- Lib.pushUnique(subplots.cartesian, k);
- }
- }
-
- // attach helper method to check whether a plot type is present on graph
- newFullLayout._has = plots._hasPlotType.bind(newFullLayout);
-
- if(oldFullData.length === newFullData.length) {
- for(i = 0; i < newFullData.length; i++) {
- relinkPrivateKeys(newFullData[i], oldFullData[i]);
- }
- }
-
- // finally, fill in the pieces of layout that may need to look at data
- plots.supplyLayoutModuleDefaults(newLayout, newFullLayout, newFullData, gd._transitionData);
-
- // Special cases that introduce interactions between traces.
- // This is after relinkPrivateKeys so we can use those in crossTraceDefaults
- // and after layout module defaults, so we can use eg barmode
- var _modules = newFullLayout._visibleModules;
- var crossTraceDefaultsFuncs = [];
- for(i = 0; i < _modules.length; i++) {
- var funci = _modules[i].crossTraceDefaults;
- // some trace types share crossTraceDefaults (ie histogram2d, histogram2dcontour)
- if(funci) Lib.pushUnique(crossTraceDefaultsFuncs, funci);
- }
- for(i = 0; i < crossTraceDefaultsFuncs.length; i++) {
- crossTraceDefaultsFuncs[i](newFullData, newFullLayout);
- }
- Registry.getComponentMethod('colorscale', 'crossTraceDefaults')(newFullData, newFullLayout);
-
- // turn on flag to optimize large splom-only graphs
- // mostly by omitting SVG layers during Cartesian.drawFramework
- newFullLayout._hasOnlyLargeSploms = (
- newFullLayout._basePlotModules.length === 1 &&
- newFullLayout._basePlotModules[0].name === 'splom' &&
- splomXa.length > 15 &&
- splomYa.length > 15 &&
- newFullLayout.shapes.length === 0 &&
- newFullLayout.images.length === 0
- );
-
- // TODO remove in v2.0.0
- // add has-plot-type refs to fullLayout for backward compatibility
- newFullLayout._hasCartesian = newFullLayout._has('cartesian');
- newFullLayout._hasGeo = newFullLayout._has('geo');
- newFullLayout._hasGL3D = newFullLayout._has('gl3d');
- newFullLayout._hasGL2D = newFullLayout._has('gl2d');
- newFullLayout._hasTernary = newFullLayout._has('ternary');
- newFullLayout._hasPie = newFullLayout._has('pie');
-
- // relink / initialize subplot axis objects
- plots.linkSubplots(newFullData, newFullLayout, oldFullData, oldFullLayout);
-
- // clean subplots and other artifacts from previous plot calls
- plots.cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout);
-
- // relink functions and _ attributes to promote consistency between plots
- relinkPrivateKeys(newFullLayout, oldFullLayout);
-
- // For persisting GUI-driven changes in layout
- // _preGUI and _tracePreGUI were already copied over in relinkPrivateKeys
- if(!newFullLayout._preGUI) newFullLayout._preGUI = {};
- // track trace GUI changes by uid rather than by trace index
- if(!newFullLayout._tracePreGUI) newFullLayout._tracePreGUI = {};
- var tracePreGUI = newFullLayout._tracePreGUI;
- var uids = {};
- var uid;
- for(uid in tracePreGUI) uids[uid] = 'old';
- for(i = 0; i < newFullData.length; i++) {
- uid = newFullData[i]._fullInput.uid;
- if(!uids[uid]) tracePreGUI[uid] = {};
- uids[uid] = 'new';
- }
- for(uid in uids) {
- if(uids[uid] === 'old') delete tracePreGUI[uid];
- }
-
- // set up containers for margin calculations
- initMargins(newFullLayout);
-
- // collect and do some initial calculations for rangesliders
- Registry.getComponentMethod('rangeslider', 'makeData')(newFullLayout);
-
- // update object references in calcdata
- if(!skipUpdateCalc && oldCalcdata.length === newFullData.length) {
- plots.supplyDefaultsUpdateCalc(oldCalcdata, newFullData);
- }
- };
-
- plots.supplyDefaultsUpdateCalc = function(oldCalcdata, newFullData) {
- for(var i = 0; i < newFullData.length; i++) {
- var newTrace = newFullData[i];
- var cd0 = (oldCalcdata[i] || [])[0];
- if(cd0 && cd0.trace) {
- var oldTrace = cd0.trace;
- if(oldTrace._hasCalcTransform) {
- var arrayAttrs = oldTrace._arrayAttrs;
- var j, astr, oldArrayVal;
-
- for(j = 0; j < arrayAttrs.length; j++) {
- astr = arrayAttrs[j];
- oldArrayVal = Lib.nestedProperty(oldTrace, astr).get().slice();
- Lib.nestedProperty(newTrace, astr).set(oldArrayVal);
- }
- }
- cd0.trace = newTrace;
- }
- }
- };
-
- /**
- * Create a list of uid strings satisfying (in this order of importance):
- * 1. all unique, all strings
- * 2. matches input uids if provided
- * 3. matches previous data uids
- */
- function getTraceUids(oldFullData, newData) {
- var len = newData.length;
- var oldFullInput = [];
- var i, prevFullInput;
- for(i = 0; i < oldFullData.length; i++) {
- var thisFullInput = oldFullData[i]._fullInput;
- if(thisFullInput !== prevFullInput) oldFullInput.push(thisFullInput);
- prevFullInput = thisFullInput;
- }
- var oldLen = oldFullInput.length;
- var out = new Array(len);
- var seenUids = {};
-
- function setUid(uid, i) {
- out[i] = uid;
- seenUids[uid] = 1;
- }
-
- function tryUid(uid, i) {
- if(uid && typeof uid === 'string' && !seenUids[uid]) {
- setUid(uid, i);
- return true;
- }
- }
-
- for(i = 0; i < len; i++) {
- var newUid = newData[i].uid;
- if(typeof newUid === 'number') newUid = String(newUid);
-
- if(tryUid(newUid, i)) continue;
- if(i < oldLen && tryUid(oldFullInput[i].uid, i)) continue;
- setUid(Lib.randstr(seenUids), i);
- }
-
- return out;
- }
-
- /**
- * Make a container for collecting subplots we need to display.
- *
- * Finds all subplot types we need to enumerate once and caches it,
- * but makes a new output object each time.
- * Single-trace subplots (which have no `id`) such as pie, table, etc
- * do not need to be collected because we just draw all visible traces.
- */
- function emptySubplotLists() {
- var collectableSubplotTypes = Registry.collectableSubplotTypes;
- var out = {};
- var i, j;
-
- if(!collectableSubplotTypes) {
- collectableSubplotTypes = [];
-
- var subplotsRegistry = Registry.subplotsRegistry;
-
- for(var subplotType in subplotsRegistry) {
- var subplotModule = subplotsRegistry[subplotType];
- var subplotAttr = subplotModule.attr;
-
- if(subplotAttr) {
- collectableSubplotTypes.push(subplotType);
-
- // special case, currently just for cartesian:
- // we need to enumerate axes, not just subplots
- if(Array.isArray(subplotAttr)) {
- for(j = 0; j < subplotAttr.length; j++) {
- Lib.pushUnique(collectableSubplotTypes, subplotAttr[j]);
- }
- }
- }
- }
- }
-
- for(i = 0; i < collectableSubplotTypes.length; i++) {
- out[collectableSubplotTypes[i]] = [];
- }
- return out;
- }
-
- /**
- * getFormatObj: use _context to get the format object from locale.
- * Used to get d3.locale argument object and extraFormat argument object
- *
- * Regarding d3.locale argument :
- * decimal and thousands can be overridden later by layout.separators
- * grouping and currency are not presently used by our automatic number
- * formatting system but can be used by custom formats.
- *
- * @returns {object} d3.locale format object
- */
- function getFormatObj(gd, formatKeys) {
- var locale = gd._context.locale;
- if(!locale) locale === 'en-US';
-
- var formatDone = false;
- var formatObj = {};
-
- function includeFormat(newFormat) {
- var formatFinished = true;
- for(var i = 0; i < formatKeys.length; i++) {
- var formatKey = formatKeys[i];
- if(!formatObj[formatKey]) {
- if(newFormat[formatKey]) {
- formatObj[formatKey] = newFormat[formatKey];
- }
- else formatFinished = false;
- }
- }
- if(formatFinished) formatDone = true;
- }
-
- // same as localize, look for format parts in each format spec in the chain
- for(var i = 0; i < 2; i++) {
- var locales = gd._context.locales;
- for(var j = 0; j < 2; j++) {
- var formatj = (locales[locale] || {}).format;
- if(formatj) {
- includeFormat(formatj);
- if(formatDone) break;
- }
- locales = Registry.localeRegistry;
- }
-
- var baseLocale = locale.split('-')[0];
- if(formatDone || baseLocale === locale) break;
- locale = baseLocale;
- }
-
- // lastly pick out defaults from english (non-US, as DMY is so much more common)
- if(!formatDone) includeFormat(Registry.localeRegistry.en.format);
-
- return formatObj;
- }
-
- /**
- * getFormatter: combine the final separators with the locale formatting object
- * we pulled earlier to generate number and time formatters
- * TODO: remove separators in v2, only use locale, so we don't need this step?
- *
- * @param {object} formatObj: d3.locale format object
- * @param {string} separators: length-2 string to override decimal and thousands
- * separators in number formatting
- *
- * @returns {object} {numberFormat, timeFormat} d3 formatter factory functions
- * for numbers and time
- */
- function getFormatter(formatObj, separators) {
- formatObj.decimal = separators.charAt(0);
- formatObj.thousands = separators.charAt(1);
-
- return d3.locale(formatObj);
- }
-
- // Create storage for all of the data related to frames and transitions:
- plots.createTransitionData = function(gd) {
- // Set up the default keyframe if it doesn't exist:
- if(!gd._transitionData) {
- gd._transitionData = {};
- }
-
- if(!gd._transitionData._frames) {
- gd._transitionData._frames = [];
- }
-
- if(!gd._transitionData._frameHash) {
- gd._transitionData._frameHash = {};
- }
-
- if(!gd._transitionData._counter) {
- gd._transitionData._counter = 0;
- }
-
- if(!gd._transitionData._interruptCallbacks) {
- gd._transitionData._interruptCallbacks = [];
- }
- };
-
- // helper function to be bound to fullLayout to check
- // whether a certain plot type is present on plot
- // or trace has a category
- plots._hasPlotType = function(category) {
- var i;
-
- // check base plot modules
- var basePlotModules = this._basePlotModules || [];
- for(i = 0; i < basePlotModules.length; i++) {
- if(basePlotModules[i].name === category) return true;
- }
-
- // check trace modules (including non-visible:true)
- var modules = this._modules || [];
- for(i = 0; i < modules.length; i++) {
- var name = modules[i].name;
- if(name === category) return true;
- // N.B. this is modules[i] along with 'categories' as a hash object
- var _module = Registry.modules[name];
- if(_module && _module.categories[category]) return true;
- }
-
- return false;
- };
-
- plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
- var i, j;
-
- var basePlotModules = oldFullLayout._basePlotModules || [];
- for(i = 0; i < basePlotModules.length; i++) {
- var _module = basePlotModules[i];
-
- if(_module.clean) {
- _module.clean(newFullData, newFullLayout, oldFullData, oldFullLayout);
- }
- }
-
- var hadGl = oldFullLayout._has && oldFullLayout._has('gl');
- var hasGl = newFullLayout._has && newFullLayout._has('gl');
-
- if(hadGl && !hasGl) {
- if(oldFullLayout._glcontainer !== undefined) {
- oldFullLayout._glcontainer.selectAll('.gl-canvas').remove();
- oldFullLayout._glcontainer.selectAll('.no-webgl').remove();
- oldFullLayout._glcanvas = null;
- }
- }
-
- var hasInfoLayer = !!oldFullLayout._infolayer;
-
- oldLoop:
- for(i = 0; i < oldFullData.length; i++) {
- var oldTrace = oldFullData[i];
- var oldUid = oldTrace.uid;
-
- for(j = 0; j < newFullData.length; j++) {
- var newTrace = newFullData[j];
-
- if(oldUid === newTrace.uid) continue oldLoop;
- }
-
- // clean old colorbars
- if(hasInfoLayer) {
- oldFullLayout._infolayer.select('.cb' + oldUid).remove();
- }
- }
-
- if(oldFullLayout._zoomlayer) {
- oldFullLayout._zoomlayer.selectAll('.select-outline').remove();
- }
- };
-
- plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
- var i, j;
-
- var oldSubplots = oldFullLayout._plots || {};
- var newSubplots = newFullLayout._plots = {};
- var newSubplotList = newFullLayout._subplots;
-
- var mockGd = {
- _fullData: newFullData,
- _fullLayout: newFullLayout
- };
-
- var ids = newSubplotList.cartesian.concat(newSubplotList.gl2d || []);
-
- for(i = 0; i < ids.length; i++) {
- var id = ids[i];
- var oldSubplot = oldSubplots[id];
- var xaxis = axisIDs.getFromId(mockGd, id, 'x');
- var yaxis = axisIDs.getFromId(mockGd, id, 'y');
- var plotinfo;
-
- // link or create subplot object
- if(oldSubplot) {
- plotinfo = newSubplots[id] = oldSubplot;
- } else {
- plotinfo = newSubplots[id] = {};
- plotinfo.id = id;
- }
-
- // add these axis ids to each others' subplot lists
- xaxis._counterAxes.push(yaxis._id);
- yaxis._counterAxes.push(xaxis._id);
- xaxis._subplotsWith.push(id);
- yaxis._subplotsWith.push(id);
-
- // update x and y axis layout object refs
- plotinfo.xaxis = xaxis;
- plotinfo.yaxis = yaxis;
-
- // By default, we clip at the subplot level,
- // but if one trace on a given subplot has *cliponaxis* set to false,
- // we need to clip at the trace module layer level;
- // find this out here, once of for all.
- plotinfo._hasClipOnAxisFalse = false;
-
- for(j = 0; j < newFullData.length; j++) {
- var trace = newFullData[j];
-
- if(
- trace.xaxis === plotinfo.xaxis._id &&
- trace.yaxis === plotinfo.yaxis._id &&
- trace.cliponaxis === false
- ) {
- plotinfo._hasClipOnAxisFalse = true;
- break;
- }
- }
- }
-
- // while we're at it, link overlaying axes to their main axes and
- // anchored axes to the axes they're anchored to
- var axList = axisIDs.list(mockGd, null, true);
- var ax;
- for(i = 0; i < axList.length; i++) {
- ax = axList[i];
- var mainAx = null;
-
- if(ax.overlaying) {
- mainAx = axisIDs.getFromId(mockGd, ax.overlaying);
-
- // you cannot overlay an axis that's already overlaying another
- if(mainAx && mainAx.overlaying) {
- ax.overlaying = false;
- mainAx = null;
- }
- }
- ax._mainAxis = mainAx || ax;
-
- /*
- * For now force overlays to overlay completely... so they
- * can drag together correctly and share backgrounds.
- * Later perhaps we make separate axis domain and
- * tick/line domain or something, so they can still share
- * the (possibly larger) dragger and background but don't
- * have to both be drawn over that whole domain
- */
- if(mainAx) ax.domain = mainAx.domain.slice();
-
- ax._anchorAxis = ax.anchor === 'free' ?
- null :
- axisIDs.getFromId(mockGd, ax.anchor);
- }
-
- // finally, we can find the main subplot for each axis
- // (on which the ticks & labels are drawn)
- for(i = 0; i < axList.length; i++) {
- ax = axList[i];
- ax._counterAxes.sort(axisIDs.idSort);
- ax._subplotsWith.sort(Lib.subplotSort);
- ax._mainSubplot = findMainSubplot(ax, newFullLayout);
- }
- };
-
- function findMainSubplot(ax, fullLayout) {
- var mockGd = {_fullLayout: fullLayout};
-
- var isX = ax._id.charAt(0) === 'x';
- var anchorAx = ax._mainAxis._anchorAxis;
- var mainSubplotID = '';
- var nextBestMainSubplotID = '';
- var anchorID = '';
-
- // First try the main ID with the anchor
- if(anchorAx) {
- anchorID = anchorAx._mainAxis._id;
- mainSubplotID = isX ? (ax._id + anchorID) : (anchorID + ax._id);
- }
-
- // Then look for a subplot with the counteraxis overlaying the anchor
- // If that fails just use the first subplot including this axis
- if(!mainSubplotID || !fullLayout._plots[mainSubplotID]) {
- mainSubplotID = '';
-
- var counterIDs = ax._counterAxes;
- for(var j = 0; j < counterIDs.length; j++) {
- var counterPart = counterIDs[j];
- var id = isX ? (ax._id + counterPart) : (counterPart + ax._id);
- if(!nextBestMainSubplotID) nextBestMainSubplotID = id;
- var counterAx = axisIDs.getFromId(mockGd, counterPart);
- if(anchorID && counterAx.overlaying === anchorID) {
- mainSubplotID = id;
- break;
- }
- }
- }
-
- return mainSubplotID || nextBestMainSubplotID;
- }
-
- // This function clears any trace attributes with valType: color and
- // no set dflt filed in the plot schema. This is needed because groupby (which
- // is the only transform for which this currently applies) supplies parent
- // trace defaults, then expanded trace defaults. The result is that `null`
- // colors are default-supplied and inherited as a color instead of a null.
- // The result is that expanded trace default colors have no effect, with
- // the final result that groups are indistinguishable. This function clears
- // those colors so that individual groupby groups get unique colors.
- plots.clearExpandedTraceDefaultColors = function(trace) {
- var colorAttrs, path, i;
-
- // This uses weird closure state in order to satisfy the linter rule
- // that we can't create functions in a loop.
- function locateColorAttrs(attr, attrName, attrs, level) {
- path[level] = attrName;
- path.length = level + 1;
- if(attr.valType === 'color' && attr.dflt === undefined) {
- colorAttrs.push(path.join('.'));
- }
- }
-
- path = [];
-
- // Get the cached colorAttrs:
- colorAttrs = trace._module._colorAttrs;
-
- // Or else compute and cache the colorAttrs on the module:
- if(!colorAttrs) {
- trace._module._colorAttrs = colorAttrs = [];
- PlotSchema.crawl(
- trace._module.attributes,
- locateColorAttrs
- );
- }
-
- for(i = 0; i < colorAttrs.length; i++) {
- var origprop = Lib.nestedProperty(trace, '_input.' + colorAttrs[i]);
-
- if(!origprop.get()) {
- Lib.nestedProperty(trace, colorAttrs[i]).set(null);
- }
- }
- };
-
-
- plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) {
- var modules = fullLayout._modules;
- var visibleModules = fullLayout._visibleModules;
- var basePlotModules = fullLayout._basePlotModules;
- var cnt = 0;
- var colorCnt = 0;
-
- var i, fullTrace, trace;
-
- fullLayout._transformModules = [];
-
- function pushModule(fullTrace) {
- dataOut.push(fullTrace);
-
- var _module = fullTrace._module;
- if(!_module) return;
-
- Lib.pushUnique(modules, _module);
- if(fullTrace.visible === true) Lib.pushUnique(visibleModules, _module);
- Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule);
- cnt++;
-
- // TODO: do we really want color not to increment for explicitly invisible traces?
- // This logic is weird, but matches previous behavior: traces that you explicitly
- // set to visible:false do not increment the color, but traces WE determine to be
- // empty or invalid (and thus set to visible:false) DO increment color.
- // I kind of think we should just let all traces increment color, visible or not.
- // see mock: axes-autotype-empty vs. a test of restyling visible: false that
- // I can't find right now...
- if(fullTrace._input.visible !== false) colorCnt++;
- }
-
- var carpetIndex = {};
- var carpetDependents = [];
- var dataTemplate = (layout.template || {}).data || {};
- var templater = Template.traceTemplater(dataTemplate);
-
- for(i = 0; i < dataIn.length; i++) {
- trace = dataIn[i];
-
- // reuse uid we may have pulled out of oldFullData
- // Note: templater supplies trace type
- fullTrace = templater.newTrace(trace);
- fullTrace.uid = fullLayout._traceUids[i];
- plots.supplyTraceDefaults(trace, fullTrace, colorCnt, fullLayout, i);
-
- fullTrace.index = i;
- fullTrace._input = trace;
- fullTrace._expandedIndex = cnt;
-
- if(fullTrace.transforms && fullTrace.transforms.length) {
- var sdInvisible = trace.visible !== false && fullTrace.visible === false;
-
- var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout);
-
- for(var j = 0; j < expandedTraces.length; j++) {
- var expandedTrace = expandedTraces[j];
-
- // No further templating during transforms.
- var fullExpandedTrace = {
- _template: fullTrace._template,
- type: fullTrace.type,
- // set uid using parent uid and expanded index
- // to promote consistency between update calls
- uid: fullTrace.uid + j
- };
-
- // If the first supplyDefaults created `visible: false`,
- // clear it before running supplyDefaults a second time,
- // because sometimes there are items we still want to coerce
- // inside trace modules before determining that the trace is
- // again `visible: false`, for example partial visibilities
- // in `splom` traces.
- if(sdInvisible && expandedTrace.visible === false) {
- delete expandedTrace.visible;
- }
-
- plots.supplyTraceDefaults(expandedTrace, fullExpandedTrace, cnt, fullLayout, i);
-
- // relink private (i.e. underscore) keys expanded trace to full expanded trace so
- // that transform supply-default methods can set _ keys for future use.
- relinkPrivateKeys(fullExpandedTrace, expandedTrace);
-
- // add info about parent data trace
- fullExpandedTrace.index = i;
- fullExpandedTrace._input = trace;
- fullExpandedTrace._fullInput = fullTrace;
-
- // add info about the expanded data
- fullExpandedTrace._expandedIndex = cnt;
- fullExpandedTrace._expandedInput = expandedTrace;
-
- pushModule(fullExpandedTrace);
- }
- }
- else {
- // add identify refs for consistency with transformed traces
- fullTrace._fullInput = fullTrace;
- fullTrace._expandedInput = fullTrace;
-
- pushModule(fullTrace);
- }
-
- if(Registry.traceIs(fullTrace, 'carpetAxis')) {
- carpetIndex[fullTrace.carpet] = fullTrace;
- }
-
- if(Registry.traceIs(fullTrace, 'carpetDependent')) {
- carpetDependents.push(i);
- }
- }
-
- for(i = 0; i < carpetDependents.length; i++) {
- fullTrace = dataOut[carpetDependents[i]];
-
- if(!fullTrace.visible) continue;
-
- var carpetAxis = carpetIndex[fullTrace.carpet];
- fullTrace._carpet = carpetAxis;
-
- if(!carpetAxis || !carpetAxis.visible) {
- fullTrace.visible = false;
- continue;
- }
-
- fullTrace.xaxis = carpetAxis.xaxis;
- fullTrace.yaxis = carpetAxis.yaxis;
- }
- };
-
- plots.supplyAnimationDefaults = function(opts) {
- opts = opts || {};
- var i;
- var optsOut = {};
-
- function coerce(attr, dflt) {
- return Lib.coerce(opts || {}, optsOut, animationAttrs, attr, dflt);
- }
-
- coerce('mode');
- coerce('direction');
- coerce('fromcurrent');
-
- if(Array.isArray(opts.frame)) {
- optsOut.frame = [];
- for(i = 0; i < opts.frame.length; i++) {
- optsOut.frame[i] = plots.supplyAnimationFrameDefaults(opts.frame[i] || {});
- }
- } else {
- optsOut.frame = plots.supplyAnimationFrameDefaults(opts.frame || {});
- }
-
- if(Array.isArray(opts.transition)) {
- optsOut.transition = [];
- for(i = 0; i < opts.transition.length; i++) {
- optsOut.transition[i] = plots.supplyAnimationTransitionDefaults(opts.transition[i] || {});
- }
- } else {
- optsOut.transition = plots.supplyAnimationTransitionDefaults(opts.transition || {});
- }
-
- return optsOut;
- };
-
- plots.supplyAnimationFrameDefaults = function(opts) {
- var optsOut = {};
-
- function coerce(attr, dflt) {
- return Lib.coerce(opts || {}, optsOut, animationAttrs.frame, attr, dflt);
- }
-
- coerce('duration');
- coerce('redraw');
-
- return optsOut;
- };
-
- plots.supplyAnimationTransitionDefaults = function(opts) {
- var optsOut = {};
-
- function coerce(attr, dflt) {
- return Lib.coerce(opts || {}, optsOut, animationAttrs.transition, attr, dflt);
- }
-
- coerce('duration');
- coerce('easing');
-
- return optsOut;
- };
-
- plots.supplyFrameDefaults = function(frameIn) {
- var frameOut = {};
-
- function coerce(attr, dflt) {
- return Lib.coerce(frameIn, frameOut, frameAttrs, attr, dflt);
- }
-
- coerce('group');
- coerce('name');
- coerce('traces');
- coerce('baseframe');
- coerce('data');
- coerce('layout');
-
- return frameOut;
- };
-
- plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, traceInIndex) {
- var colorway = layout.colorway || Color.defaults;
- var defaultColor = colorway[colorIndex % colorway.length];
-
- var i;
-
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, plots.attributes, attr, dflt);
- }
-
- var visible = coerce('visible');
-
- coerce('type');
- coerce('name', layout._traceWord + ' ' + traceInIndex);
-
- coerce('uirevision', layout.uirevision);
-
- // we want even invisible traces to make their would-be subplots visible
- // so coerce the subplot id(s) now no matter what
- var _module = plots.getModule(traceOut);
-
- traceOut._module = _module;
- if(_module) {
- var basePlotModule = _module.basePlotModule;
- var subplotAttr = basePlotModule.attr;
- var subplotAttrs = basePlotModule.attributes;
- if(subplotAttr && subplotAttrs) {
- var subplots = layout._subplots;
- var subplotId = '';
-
- // TODO - currently if we draw an empty gl2d subplot, it draws
- // nothing then gets stuck and you can't get it back without newPlot
- // sort this out in the regl refactor? but for now just drop empty gl2d subplots
- if(basePlotModule.name !== 'gl2d' || visible) {
- if(Array.isArray(subplotAttr)) {
- for(i = 0; i < subplotAttr.length; i++) {
- var attri = subplotAttr[i];
- var vali = Lib.coerce(traceIn, traceOut, subplotAttrs, attri);
-
- if(subplots[attri]) Lib.pushUnique(subplots[attri], vali);
- subplotId += vali;
- }
- }
- else {
- subplotId = Lib.coerce(traceIn, traceOut, subplotAttrs, subplotAttr);
- }
-
- if(subplots[basePlotModule.name]) {
- Lib.pushUnique(subplots[basePlotModule.name], subplotId);
- }
- }
- }
- }
-
- function coerceUnlessPruned(attr, dflt, cb) {
- if(_module && (attr in _module.attributes) && _module.attributes[attr] === undefined) {
- // Pruned
- } else {
- if(cb && typeof cb === 'function') {
- cb();
- } else {
- coerce(attr, dflt);
- }
- }
- }
-
- if(visible) {
- coerce('customdata');
- coerce('ids');
-
- if(Registry.traceIs(traceOut, 'showLegend')) {
- traceOut._dfltShowLegend = true;
- coerce('showlegend');
- coerce('legendgroup');
- }
- else {
- traceOut._dfltShowLegend = false;
- }
-
- coerceUnlessPruned('hoverlabel', '', function() {
- Registry.getComponentMethod(
- 'fx',
- 'supplyDefaults'
- )(traceIn, traceOut, defaultColor, layout);
- });
-
- // TODO add per-base-plot-module trace defaults step
-
- if(_module) {
- _module.supplyDefaults(traceIn, traceOut, defaultColor, layout);
- if(!traceOut.hovertemplate) Lib.coerceHoverinfo(traceIn, traceOut, layout);
- }
-
- if(!Registry.traceIs(traceOut, 'noOpacity')) coerce('opacity');
-
- if(Registry.traceIs(traceOut, 'notLegendIsolatable')) {
- // This clears out the legendonly state for traces like carpet that
- // cannot be isolated in the legend
- traceOut.visible = !!traceOut.visible;
- }
-
- if(_module && _module.selectPoints) {
- coerce('selectedpoints');
- }
-
- plots.supplyTransformDefaults(traceIn, traceOut, layout);
- }
-
- return traceOut;
- };
-
- /**
- * hasMakesDataTransform: does this trace have a transform that makes its own
- * data, either by grabbing it from somewhere else or by creating it from input
- * parameters? If so, we should still keep going with supplyDefaults
- * even if the trace is invisible, which may just be because it has no data yet.
- */
- function hasMakesDataTransform(trace) {
- var transforms = trace.transforms;
- if(Array.isArray(transforms) && transforms.length) {
- for(var i = 0; i < transforms.length; i++) {
- var ti = transforms[i];
- var _module = ti._module || transformsRegistry[ti.type];
- if(_module && _module.makesData) return true;
- }
- }
- return false;
- }
-
- plots.hasMakesDataTransform = hasMakesDataTransform;
-
- plots.supplyTransformDefaults = function(traceIn, traceOut, layout) {
- // For now we only allow transforms on 1D traces, ie those that specify a _length.
- // If we were to implement 2D transforms, we'd need to have each transform
- // describe its own applicability and disable itself when it doesn't apply.
- // Also allow transforms that make their own data, but not in globalTransforms
- if(!(traceOut._length || hasMakesDataTransform(traceIn))) return;
-
- var globalTransforms = layout._globalTransforms || [];
- var transformModules = layout._transformModules || [];
-
- if(!Array.isArray(traceIn.transforms) && globalTransforms.length === 0) return;
-
- var containerIn = traceIn.transforms || [];
- var transformList = globalTransforms.concat(containerIn);
- var containerOut = traceOut.transforms = [];
-
- for(var i = 0; i < transformList.length; i++) {
- var transformIn = transformList[i];
- var type = transformIn.type;
- var _module = transformsRegistry[type];
- var transformOut;
-
- /*
- * Supply defaults may run twice. First pass runs all supply defaults steps
- * and adds the _module to any output transforms.
- * If transforms exist another pass is run so that any generated traces also
- * go through supply defaults. This has the effect of rerunning
- * supplyTransformDefaults. If the transform does not have a `transform`
- * function it could not have generated any new traces and the second stage
- * is unnecessary. We detect this case with the following variables.
- */
- var isFirstStage = !(transformIn._module && transformIn._module === _module);
- var doLaterStages = _module && typeof _module.transform === 'function';
-
- if(!_module) Lib.warn('Unrecognized transform type ' + type + '.');
-
- if(_module && _module.supplyDefaults && (isFirstStage || doLaterStages)) {
- transformOut = _module.supplyDefaults(transformIn, traceOut, layout, traceIn);
- transformOut.type = type;
- transformOut._module = _module;
-
- Lib.pushUnique(transformModules, _module);
- }
- else {
- transformOut = Lib.extendFlat({}, transformIn);
- }
-
- containerOut.push(transformOut);
- }
- };
-
- function applyTransforms(fullTrace, fullData, layout, fullLayout) {
- var container = fullTrace.transforms;
- var dataOut = [fullTrace];
-
- for(var i = 0; i < container.length; i++) {
- var transform = container[i];
- var _module = transformsRegistry[transform.type];
-
- if(_module && _module.transform) {
- dataOut = _module.transform(dataOut, {
- transform: transform,
- fullTrace: fullTrace,
- fullData: fullData,
- layout: layout,
- fullLayout: fullLayout,
- transformIndex: i
- });
- }
- }
-
- return dataOut;
- }
-
- plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) {
- function coerce(attr, dflt) {
- return Lib.coerce(layoutIn, layoutOut, plots.layoutAttributes, attr, dflt);
- }
-
- var template = layoutIn.template;
- if(Lib.isPlainObject(template)) {
- layoutOut.template = template;
- layoutOut._template = template.layout;
- layoutOut._dataTemplate = template.data;
- }
-
- var globalFont = Lib.coerceFont(coerce, 'font');
-
- coerce('title.text', layoutOut._dfltTitle.plot);
-
- Lib.coerceFont(coerce, 'title.font', {
- family: globalFont.family,
- size: Math.round(globalFont.size * 1.4),
- color: globalFont.color
- });
-
- coerce('title.xref');
- coerce('title.yref');
- coerce('title.x');
- coerce('title.y');
- coerce('title.xanchor');
- coerce('title.yanchor');
- coerce('title.pad.t');
- coerce('title.pad.r');
- coerce('title.pad.b');
- coerce('title.pad.l');
-
- // Make sure that autosize is defaulted to *true*
- // on layouts with no set width and height for backward compatibly,
- // in particular https://plot.ly/javascript/responsive-fluid-layout/
- //
- // Before https://github.com/plotly/plotly.js/pull/635 ,
- // layouts with no set width and height were set temporary set to 'initial'
- // to pass through the autosize routine
- //
- // This behavior is subject to change in v2.
- coerce('autosize', !(layoutIn.width && layoutIn.height));
-
- coerce('width');
- coerce('height');
- coerce('margin.l');
- coerce('margin.r');
- coerce('margin.t');
- coerce('margin.b');
- coerce('margin.pad');
- coerce('margin.autoexpand');
-
- if(layoutIn.width && layoutIn.height) plots.sanitizeMargins(layoutOut);
-
- Registry.getComponentMethod('grid', 'sizeDefaults')(layoutIn, layoutOut);
-
- coerce('paper_bgcolor');
-
- coerce('separators', formatObj.decimal + formatObj.thousands);
- coerce('hidesources');
-
- coerce('colorway');
-
- coerce('datarevision');
- var uirevision = coerce('uirevision');
- coerce('editrevision', uirevision);
- coerce('selectionrevision', uirevision);
-
- coerce('modebar.orientation');
- coerce('modebar.bgcolor', Color.addOpacity(layoutOut.paper_bgcolor, 0.5));
- var modebarDefaultColor = Color.contrast(Color.rgb(layoutOut.modebar.bgcolor));
- coerce('modebar.color', Color.addOpacity(modebarDefaultColor, 0.3));
- coerce('modebar.activecolor', Color.addOpacity(modebarDefaultColor, 0.7));
- coerce('modebar.uirevision', uirevision);
-
- coerce('meta');
-
- // do not include defaults in fullLayout when users do not set transition
- if(Lib.isPlainObject(layoutIn.transition)) {
- coerce('transition.duration');
- coerce('transition.easing');
- coerce('transition.ordering');
- }
-
- Registry.getComponentMethod(
- 'calendars',
- 'handleDefaults'
- )(layoutIn, layoutOut, 'calendar');
-
- Registry.getComponentMethod(
- 'fx',
- 'supplyLayoutGlobalDefaults'
- )(layoutIn, layoutOut, coerce);
- };
-
- plots.plotAutoSize = function plotAutoSize(gd, layout, fullLayout) {
- var context = gd._context || {};
- var frameMargins = context.frameMargins;
- var newWidth;
- var newHeight;
-
- var isPlotDiv = Lib.isPlotDiv(gd);
-
- if(isPlotDiv) gd.emit('plotly_autosize');
-
- // embedded in an iframe - just take the full iframe size
- // if we get to this point, with no aspect ratio restrictions
- if(context.fillFrame) {
- newWidth = window.innerWidth;
- newHeight = window.innerHeight;
-
- // somehow we get a few extra px height sometimes...
- // just hide it
- document.body.style.overflow = 'hidden';
- }
- else {
- // plotly.js - let the developers do what they want, either
- // provide height and width for the container div,
- // specify size in layout, or take the defaults,
- // but don't enforce any ratio restrictions
- var computedStyle = isPlotDiv ? window.getComputedStyle(gd) : {};
-
- newWidth = parseFloat(computedStyle.width) || parseFloat(computedStyle.maxWidth) || fullLayout.width;
- newHeight = parseFloat(computedStyle.height) || parseFloat(computedStyle.maxHeight) || fullLayout.height;
-
- if(isNumeric(frameMargins) && frameMargins > 0) {
- var factor = 1 - 2 * frameMargins;
- newWidth = Math.round(factor * newWidth);
- newHeight = Math.round(factor * newHeight);
- }
- }
-
- var minWidth = plots.layoutAttributes.width.min;
- var minHeight = plots.layoutAttributes.height.min;
- if(newWidth < minWidth) newWidth = minWidth;
- if(newHeight < minHeight) newHeight = minHeight;
-
- var widthHasChanged = !layout.width &&
- (Math.abs(fullLayout.width - newWidth) > 1);
- var heightHasChanged = !layout.height &&
- (Math.abs(fullLayout.height - newHeight) > 1);
-
- if(heightHasChanged || widthHasChanged) {
- if(widthHasChanged) fullLayout.width = newWidth;
- if(heightHasChanged) fullLayout.height = newHeight;
- }
-
- // cache initial autosize value, used in relayout when
- // width or height values are set to null
- if(!gd._initialAutoSize) {
- gd._initialAutoSize = { width: newWidth, height: newHeight };
- }
-
- plots.sanitizeMargins(fullLayout);
- };
-
- plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, transitionData) {
- var componentsRegistry = Registry.componentsRegistry;
- var basePlotModules = layoutOut._basePlotModules;
- var component, i, _module;
-
- var Cartesian = Registry.subplotsRegistry.cartesian;
-
- // check if any components need to add more base plot modules
- // that weren't captured by traces
- for(component in componentsRegistry) {
- _module = componentsRegistry[component];
-
- if(_module.includeBasePlot) {
- _module.includeBasePlot(layoutIn, layoutOut);
- }
- }
-
- // make sure we *at least* have some cartesian axes
- if(!basePlotModules.length) {
- basePlotModules.push(Cartesian);
- }
-
- // ensure all cartesian axes have at least one subplot
- if(layoutOut._has('cartesian')) {
- Registry.getComponentMethod('grid', 'contentDefaults')(layoutIn, layoutOut);
- Cartesian.finalizeSubplots(layoutIn, layoutOut);
- }
-
- // sort subplot lists
- for(var subplotType in layoutOut._subplots) {
- layoutOut._subplots[subplotType].sort(Lib.subplotSort);
- }
-
- // base plot module layout defaults
- for(i = 0; i < basePlotModules.length; i++) {
- _module = basePlotModules[i];
-
- // e.g. pie does not have a layout-defaults step
- if(_module.supplyLayoutDefaults) {
- _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- }
- }
-
- // trace module layout defaults
- // use _modules rather than _visibleModules so that even
- // legendonly traces can include settings - eg barmode, which affects
- // legend.traceorder default value.
- var modules = layoutOut._modules;
- for(i = 0; i < modules.length; i++) {
- _module = modules[i];
-
- if(_module.supplyLayoutDefaults) {
- _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- }
- }
-
- // transform module layout defaults
- var transformModules = layoutOut._transformModules;
- for(i = 0; i < transformModules.length; i++) {
- _module = transformModules[i];
-
- if(_module.supplyLayoutDefaults) {
- _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData, transitionData);
- }
- }
-
- for(component in componentsRegistry) {
- _module = componentsRegistry[component];
-
- if(_module.supplyLayoutDefaults) {
- _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
- }
- }
- };
-
- // Remove all plotly attributes from a div so it can be replotted fresh
- // TODO: these really need to be encapsulated into a much smaller set...
- plots.purge = function(gd) {
- // note: we DO NOT remove _context because it doesn't change when we insert
- // a new plot, and may have been set outside of our scope.
-
- var fullLayout = gd._fullLayout || {};
- if(fullLayout._glcontainer !== undefined) {
- fullLayout._glcontainer.selectAll('.gl-canvas').remove();
- fullLayout._glcontainer.remove();
- fullLayout._glcanvas = null;
- }
- if(fullLayout._geocontainer !== undefined) fullLayout._geocontainer.remove();
-
- // remove modebar
- if(fullLayout._modeBar) fullLayout._modeBar.destroy();
-
- if(gd._transitionData) {
- // Ensure any dangling callbacks are simply dropped if the plot is purged.
- // This is more or less only actually important for testing.
- if(gd._transitionData._interruptCallbacks) {
- gd._transitionData._interruptCallbacks.length = 0;
- }
-
- if(gd._transitionData._animationRaf) {
- window.cancelAnimationFrame(gd._transitionData._animationRaf);
- }
- }
-
- // remove any planned throttles
- Lib.clearThrottle();
-
- // remove responsive handler
- Lib.clearResponsive(gd);
-
- // data and layout
- delete gd.data;
- delete gd.layout;
- delete gd._fullData;
- delete gd._fullLayout;
- delete gd.calcdata;
- delete gd.framework;
- delete gd.empty;
-
- delete gd.fid;
-
- delete gd.undoqueue; // action queue
- delete gd.undonum;
- delete gd.autoplay; // are we doing an action that doesn't go in undo queue?
- delete gd.changed;
-
- // these get recreated on Plotly.plot anyway, but just to be safe
- // (and to have a record of them...)
- delete gd._promises;
- delete gd._redrawTimer;
- delete gd._hmlumcount;
- delete gd._hmpixcount;
- delete gd._transitionData;
- delete gd._transitioning;
- delete gd._initialAutoSize;
- delete gd._transitioningWithDuration;
-
- // created during certain events, that *should* clean them up
- // themselves, but may not if there was an error
- delete gd._dragging;
- delete gd._dragged;
- delete gd._hoverdata;
- delete gd._snapshotInProgress;
- delete gd._editing;
- delete gd._replotPending;
- delete gd._mouseDownTime;
- delete gd._legendMouseDownTime;
-
- // remove all event listeners
- if(gd.removeAllListeners) gd.removeAllListeners();
- };
-
- plots.style = function(gd) {
- var _modules = gd._fullLayout._visibleModules;
- var styleModules = [];
- var i;
-
- // some trace modules reuse the same style method,
- // make sure to not unnecessary call them multiple times.
-
- for(i = 0; i < _modules.length; i++) {
- var _module = _modules[i];
- if(_module.style) {
- Lib.pushUnique(styleModules, _module.style);
- }
- }
-
- for(i = 0; i < styleModules.length; i++) {
- styleModules[i](gd);
- }
- };
-
- plots.sanitizeMargins = function(fullLayout) {
- // polar doesn't do margins...
- if(!fullLayout || !fullLayout.margin) return;
-
- var width = fullLayout.width;
- var height = fullLayout.height;
- var margin = fullLayout.margin;
- var plotWidth = width - (margin.l + margin.r);
- var plotHeight = height - (margin.t + margin.b);
- var correction;
-
- // if margin.l + margin.r = 0 then plotWidth > 0
- // as width >= 10 by supplyDefaults
- // similarly for margin.t + margin.b
-
- if(plotWidth < 0) {
- correction = (width - 1) / (margin.l + margin.r);
- margin.l = Math.floor(correction * margin.l);
- margin.r = Math.floor(correction * margin.r);
- }
-
- if(plotHeight < 0) {
- correction = (height - 1) / (margin.t + margin.b);
- margin.t = Math.floor(correction * margin.t);
- margin.b = Math.floor(correction * margin.b);
- }
- };
-
- plots.clearAutoMarginIds = function(gd) {
- gd._fullLayout._pushmarginIds = {};
- };
-
- plots.allowAutoMargin = function(gd, id) {
- gd._fullLayout._pushmarginIds[id] = 1;
- };
-
- function initMargins(fullLayout) {
- var margin = fullLayout.margin;
-
- if(!fullLayout._size) {
- var gs = fullLayout._size = {
- l: Math.round(margin.l),
- r: Math.round(margin.r),
- t: Math.round(margin.t),
- b: Math.round(margin.b),
- p: Math.round(margin.pad)
- };
- gs.w = Math.round(fullLayout.width) - gs.l - gs.r;
- gs.h = Math.round(fullLayout.height) - gs.t - gs.b;
- }
- if(!fullLayout._pushmargin) fullLayout._pushmargin = {};
- if(!fullLayout._pushmarginIds) fullLayout._pushmarginIds = {};
- }
-
- /**
- * autoMargin: called by components that may need to expand the margins to
- * be rendered on-plot.
- *
- * @param {DOM element} gd
- * @param {string} id - an identifier unique (within this plot) to this object,
- * so we can remove a previous margin expansion from the same object.
- * @param {object} o - the margin requirements of this object, or omit to delete
- * this entry (like if it's hidden). Keys are:
- * x, y: plot fraction of the anchor point.
- * xl, xr, yt, yb: if the object has an extent defined in plot fraction,
- * you can specify both edges as plot fractions in each dimension
- * l, r, t, b: the pixels to pad past the plot fraction x[l|r] and y[t|b]
- * pad: extra pixels to add in all directions, default 12 (why?)
- */
- plots.autoMargin = function(gd, id, o) {
- var fullLayout = gd._fullLayout;
-
- var pushMargin = fullLayout._pushmargin;
- var pushMarginIds = fullLayout._pushmarginIds;
-
- if(fullLayout.margin.autoexpand !== false) {
- if(!o) {
- delete pushMargin[id];
- delete pushMarginIds[id];
- }
- else {
- var pad = o.pad;
- if(pad === undefined) {
- var margin = fullLayout.margin;
- // if no explicit pad is given, use 12px unless there's a
- // specified margin that's smaller than that
- pad = Math.min(12, margin.l, margin.r, margin.t, margin.b);
- }
-
- // if the item is too big, just give it enough automargin to
- // make sure you can still grab it and bring it back
- if(o.l + o.r > fullLayout.width * 0.5) o.l = o.r = 0;
- if(o.b + o.t > fullLayout.height * 0.5) o.b = o.t = 0;
-
- var xl = o.xl !== undefined ? o.xl : o.x;
- var xr = o.xr !== undefined ? o.xr : o.x;
- var yt = o.yt !== undefined ? o.yt : o.y;
- var yb = o.yb !== undefined ? o.yb : o.y;
-
- pushMargin[id] = {
- l: {val: xl, size: o.l + pad},
- r: {val: xr, size: o.r + pad},
- b: {val: yb, size: o.b + pad},
- t: {val: yt, size: o.t + pad}
- };
- pushMarginIds[id] = 1;
- }
-
- if(!fullLayout._replotting) plots.doAutoMargin(gd);
- }
- };
-
- plots.doAutoMargin = function(gd) {
- var fullLayout = gd._fullLayout;
- if(!fullLayout._size) fullLayout._size = {};
- initMargins(fullLayout);
-
- var gs = fullLayout._size;
- var oldmargins = JSON.stringify(gs);
- var margin = fullLayout.margin;
-
- // adjust margins for outside components
- // fullLayout.margin is the requested margin,
- // fullLayout._size has margins and plotsize after adjustment
- var ml = margin.l;
- var mr = margin.r;
- var mt = margin.t;
- var mb = margin.b;
- var pushMargin = fullLayout._pushmargin;
- var pushMarginIds = fullLayout._pushmarginIds;
-
- if(fullLayout.margin.autoexpand !== false) {
-
- for(var k in pushMargin) {
- if(!pushMarginIds[k]) delete pushMargin[k];
- }
-
- // fill in the requested margins
- pushMargin.base = {
- l: {val: 0, size: ml},
- r: {val: 1, size: mr},
- t: {val: 1, size: mt},
- b: {val: 0, size: mb}
- };
-
- // now cycle through all the combinations of l and r
- // (and t and b) to find the required margins
-
- for(var k1 in pushMargin) {
-
- var pushleft = pushMargin[k1].l || {};
- var pushbottom = pushMargin[k1].b || {};
- var fl = pushleft.val;
- var pl = pushleft.size;
- var fb = pushbottom.val;
- var pb = pushbottom.size;
-
- for(var k2 in pushMargin) {
- if(isNumeric(pl) && pushMargin[k2].r) {
- var fr = pushMargin[k2].r.val;
- var pr = pushMargin[k2].r.size;
-
- if(fr > fl) {
- var newl = (pl * fr +
- (pr - fullLayout.width) * fl) / (fr - fl);
- var newr = (pr * (1 - fl) +
- (pl - fullLayout.width) * (1 - fr)) / (fr - fl);
- if(newl >= 0 && newr >= 0 && newl + newr > ml + mr) {
- ml = newl;
- mr = newr;
- }
- }
- }
-
- if(isNumeric(pb) && pushMargin[k2].t) {
- var ft = pushMargin[k2].t.val;
- var pt = pushMargin[k2].t.size;
-
- if(ft > fb) {
- var newb = (pb * ft +
- (pt - fullLayout.height) * fb) / (ft - fb);
- var newt = (pt * (1 - fb) +
- (pb - fullLayout.height) * (1 - ft)) / (ft - fb);
- if(newb >= 0 && newt >= 0 && newb + newt > mb + mt) {
- mb = newb;
- mt = newt;
- }
- }
- }
- }
- }
- }
-
- gs.l = Math.round(ml);
- gs.r = Math.round(mr);
- gs.t = Math.round(mt);
- gs.b = Math.round(mb);
- gs.p = Math.round(margin.pad);
- gs.w = Math.round(fullLayout.width) - gs.l - gs.r;
- gs.h = Math.round(fullLayout.height) - gs.t - gs.b;
-
- // if things changed and we're not already redrawing, trigger a redraw
- if(!fullLayout._replotting &&
- oldmargins !== '{}' &&
- oldmargins !== JSON.stringify(fullLayout._size)
- ) {
- if('_redrawFromAutoMarginCount' in fullLayout) {
- fullLayout._redrawFromAutoMarginCount++;
- } else {
- fullLayout._redrawFromAutoMarginCount = 1;
- }
- return Registry.call('plot', gd);
- }
- };
-
- /**
- * JSONify the graph data and layout
- *
- * This function needs to recurse because some src can be inside
- * sub-objects.
- *
- * It also strips out functions and private (starts with _) elements.
- * Therefore, we can add temporary things to data and layout that don't
- * get saved.
- *
- * @param gd The graphDiv
- * @param {Boolean} dataonly If true, don't return layout.
- * @param {'keepref'|'keepdata'|'keepall'} [mode='keepref'] Filter what's kept
- * keepref: remove data for which there's a src present
- * eg if there's xsrc present (and xsrc is well-formed,
- * ie has : and some chars before it), strip out x
- * keepdata: remove all src tags, don't remove the data itself
- * keepall: keep data and src
- * @param {String} output If you specify 'object', the result will not be stringified
- * @param {Boolean} useDefaults If truthy, use _fullLayout and _fullData
- * @returns {Object|String}
- */
- plots.graphJson = function(gd, dataonly, mode, output, useDefaults) {
- // if the defaults aren't supplied yet, we need to do that...
- if((useDefaults && dataonly && !gd._fullData) ||
- (useDefaults && !dataonly && !gd._fullLayout)) {
- plots.supplyDefaults(gd);
- }
-
- var data = (useDefaults) ? gd._fullData : gd.data;
- var layout = (useDefaults) ? gd._fullLayout : gd.layout;
- var frames = (gd._transitionData || {})._frames;
-
- function stripObj(d) {
- if(typeof d === 'function') {
- return null;
- }
- if(Lib.isPlainObject(d)) {
- var o = {};
- var v, src;
- for(v in d) {
- // remove private elements and functions
- // _ is for private, [ is a mistake ie [object Object]
- if(typeof d[v] === 'function' ||
- ['_', '['].indexOf(v.charAt(0)) !== -1) {
- continue;
- }
-
- // look for src/data matches and remove the appropriate one
- if(mode === 'keepdata') {
- // keepdata: remove all ...src tags
- if(v.substr(v.length - 3) === 'src') {
- continue;
- }
- }
- else if(mode === 'keepstream') {
- // keep sourced data if it's being streamed.
- // similar to keepref, but if the 'stream' object exists
- // in a trace, we will keep the data array.
- src = d[v + 'src'];
- if(typeof src === 'string' && src.indexOf(':') > 0) {
- if(!Lib.isPlainObject(d.stream)) {
- continue;
- }
- }
- }
- else if(mode !== 'keepall') {
- // keepref: remove sourced data but only
- // if the source tag is well-formed
- src = d[v + 'src'];
- if(typeof src === 'string' && src.indexOf(':') > 0) {
- continue;
- }
- }
-
- // OK, we're including this... recurse into it
- o[v] = stripObj(d[v]);
- }
- return o;
- }
-
- if(Array.isArray(d)) {
- return d.map(stripObj);
- }
-
- if(Lib.isTypedArray(d)) {
- return Lib.simpleMap(d, Lib.identity);
- }
-
- // convert native dates to date strings...
- // mostly for external users exporting to plotly
- if(Lib.isJSDate(d)) return Lib.ms2DateTimeLocal(+d);
-
- return d;
- }
-
- var obj = {
- data: (data || []).map(function(v) {
- var d = stripObj(v);
- // fit has some little arrays in it that don't contain data,
- // just fit params and meta
- if(dataonly) { delete d.fit; }
- return d;
- })
- };
- if(!dataonly) { obj.layout = stripObj(layout); }
-
- if(gd.framework && gd.framework.isPolar) obj = gd.framework.getConfig();
-
- if(frames) obj.frames = stripObj(frames);
-
- return (output === 'object') ? obj : JSON.stringify(obj);
- };
-
- /**
- * Modify a keyframe using a list of operations:
- *
- * @param {array of objects} operations
- * Sequence of operations to be performed on the keyframes
- */
- plots.modifyFrames = function(gd, operations) {
- var i, op, frame;
- var _frames = gd._transitionData._frames;
- var _frameHash = gd._transitionData._frameHash;
-
- for(i = 0; i < operations.length; i++) {
- op = operations[i];
-
- switch(op.type) {
- // No reason this couldn't exist, but is currently unused/untested:
- /* case 'rename':
- frame = _frames[op.index];
- delete _frameHash[frame.name];
- _frameHash[op.name] = frame;
- frame.name = op.name;
- break;*/
- case 'replace':
- frame = op.value;
- var oldName = (_frames[op.index] || {}).name;
- var newName = frame.name;
- _frames[op.index] = _frameHash[newName] = frame;
-
- if(newName !== oldName) {
- // If name has changed in addition to replacement, then update
- // the lookup table:
- delete _frameHash[oldName];
- _frameHash[newName] = frame;
- }
-
- break;
- case 'insert':
- frame = op.value;
- _frameHash[frame.name] = frame;
- _frames.splice(op.index, 0, frame);
- break;
- case 'delete':
- frame = _frames[op.index];
- delete _frameHash[frame.name];
- _frames.splice(op.index, 1);
- break;
- }
- }
-
- return Promise.resolve();
- };
-
- /*
- * Compute a keyframe. Merge a keyframe into its base frame(s) and
- * expand properties.
- *
- * @param {object} frameLookup
- * An object containing frames keyed by name (i.e. gd._transitionData._frameHash)
- * @param {string} frame
- * The name of the keyframe to be computed
- *
- * Returns: a new object with the merged content
- */
- plots.computeFrame = function(gd, frameName) {
- var frameLookup = gd._transitionData._frameHash;
- var i, traceIndices, traceIndex, destIndex;
-
- // Null or undefined will fail on .toString(). We'll allow numbers since we
- // make it clear frames must be given string names, but we'll allow numbers
- // here since they're otherwise fine for looking up frames as long as they're
- // properly cast to strings. We really just want to ensure here that this
- // 1) doesn't fail, and
- // 2) doens't give an incorrect answer (which String(frameName) would)
- if(!frameName) {
- throw new Error('computeFrame must be given a string frame name');
- }
-
- var framePtr = frameLookup[frameName.toString()];
-
- // Return false if the name is invalid:
- if(!framePtr) {
- return false;
- }
-
- var frameStack = [framePtr];
- var frameNameStack = [framePtr.name];
-
- // Follow frame pointers:
- while(framePtr.baseframe && (framePtr = frameLookup[framePtr.baseframe.toString()])) {
- // Avoid infinite loops:
- if(frameNameStack.indexOf(framePtr.name) !== -1) break;
-
- frameStack.push(framePtr);
- frameNameStack.push(framePtr.name);
- }
-
- // A new object for the merged result:
- var result = {};
-
- // Merge, starting with the last and ending with the desired frame:
- while((framePtr = frameStack.pop())) {
- if(framePtr.layout) {
- result.layout = plots.extendLayout(result.layout, framePtr.layout);
- }
-
- if(framePtr.data) {
- if(!result.data) {
- result.data = [];
- }
- traceIndices = framePtr.traces;
-
- if(!traceIndices) {
- // If not defined, assume serial order starting at zero
- traceIndices = [];
- for(i = 0; i < framePtr.data.length; i++) {
- traceIndices[i] = i;
- }
- }
-
- if(!result.traces) {
- result.traces = [];
- }
-
- for(i = 0; i < framePtr.data.length; i++) {
- // Loop through this frames data, find out where it should go,
- // and merge it!
- traceIndex = traceIndices[i];
- if(traceIndex === undefined || traceIndex === null) {
- continue;
- }
-
- destIndex = result.traces.indexOf(traceIndex);
- if(destIndex === -1) {
- destIndex = result.data.length;
- result.traces[destIndex] = traceIndex;
- }
-
- result.data[destIndex] = plots.extendTrace(result.data[destIndex], framePtr.data[i]);
- }
- }
- }
-
- return result;
- };
-
- /*
- * Recompute the lookup table that maps frame name -> frame object. addFrames/
- * deleteFrames already manages this data one at a time, so the only time this
- * is necessary is if you poke around manually in `gd._transitionData._frames`
- * and create and haven't updated the lookup table.
- */
- plots.recomputeFrameHash = function(gd) {
- var hash = gd._transitionData._frameHash = {};
- var frames = gd._transitionData._frames;
- for(var i = 0; i < frames.length; i++) {
- var frame = frames[i];
- if(frame && frame.name) {
- hash[frame.name] = frame;
- }
- }
- };
-
- /**
- * Extend an object, treating container arrays very differently by extracting
- * their contents and merging them separately.
- *
- * This exists so that we can extendDeepNoArrays and avoid stepping into data
- * arrays without knowledge of the plot schema, but so that we may also manually
- * recurse into known container arrays, such as transforms.
- *
- * See extendTrace and extendLayout below for usage.
- */
- plots.extendObjectWithContainers = function(dest, src, containerPaths) {
- var containerProp, containerVal, i, j, srcProp, destProp, srcContainer, destContainer;
- var copy = Lib.extendDeepNoArrays({}, src || {});
- var expandedObj = Lib.expandObjectPaths(copy);
- var containerObj = {};
-
- // Step through and extract any container properties. Otherwise extendDeepNoArrays
- // will clobber any existing properties with an empty array and then supplyDefaults
- // will reset everything to defaults.
- if(containerPaths && containerPaths.length) {
- for(i = 0; i < containerPaths.length; i++) {
- containerProp = Lib.nestedProperty(expandedObj, containerPaths[i]);
- containerVal = containerProp.get();
-
- if(containerVal === undefined) {
- Lib.nestedProperty(containerObj, containerPaths[i]).set(null);
- }
- else {
- containerProp.set(null);
- Lib.nestedProperty(containerObj, containerPaths[i]).set(containerVal);
- }
- }
- }
-
- dest = Lib.extendDeepNoArrays(dest || {}, expandedObj);
-
- if(containerPaths && containerPaths.length) {
- for(i = 0; i < containerPaths.length; i++) {
- srcProp = Lib.nestedProperty(containerObj, containerPaths[i]);
- srcContainer = srcProp.get();
-
- if(!srcContainer) continue;
-
- destProp = Lib.nestedProperty(dest, containerPaths[i]);
- destContainer = destProp.get();
-
- if(!Array.isArray(destContainer)) {
- destContainer = [];
- destProp.set(destContainer);
- }
-
- for(j = 0; j < srcContainer.length; j++) {
- var srcObj = srcContainer[j];
-
- if(srcObj === null) destContainer[j] = null;
- else {
- destContainer[j] = plots.extendObjectWithContainers(destContainer[j], srcObj);
- }
- }
-
- destProp.set(destContainer);
- }
- }
-
- return dest;
- };
-
- plots.dataArrayContainers = ['transforms', 'dimensions'];
- plots.layoutArrayContainers = Registry.layoutArrayContainers;
-
- /*
- * Extend a trace definition. This method:
- *
- * 1. directly transfers any array references
- * 2. manually recurses into container arrays like transforms
- *
- * The result is the original object reference with the new contents merged in.
- */
- plots.extendTrace = function(destTrace, srcTrace) {
- return plots.extendObjectWithContainers(destTrace, srcTrace, plots.dataArrayContainers);
- };
-
- /*
- * Extend a layout definition. This method:
- *
- * 1. directly transfers any array references (not critically important for
- * layout since there aren't really data arrays)
- * 2. manually recurses into container arrays like annotations
- *
- * The result is the original object reference with the new contents merged in.
- */
- plots.extendLayout = function(destLayout, srcLayout) {
- return plots.extendObjectWithContainers(destLayout, srcLayout, plots.layoutArrayContainers);
- };
-
- /**
- * Transition to a set of new data and layout properties from Plotly.animate
- *
- * @param {DOM element} gd
- * @param {Object[]} data
- * an array of data objects following the normal Plotly data definition format
- * @param {Object} layout
- * a layout object, following normal Plotly layout format
- * @param {Number[]} traces
- * indices of the corresponding traces specified in `data`
- * @param {Object} frameOpts
- * options for the frame (i.e. whether to redraw post-transition)
- * @param {Object} transitionOpts
- * options for the transition
- */
- plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) {
- var opts = {redraw: frameOpts.redraw};
- var transitionedTraces = [];
- var axEdits = [];
-
- opts.prepareFn = function() {
- var dataLength = Array.isArray(data) ? data.length : 0;
- var traceIndices = traces.slice(0, dataLength);
-
- for(var i = 0; i < traceIndices.length; i++) {
- var traceIdx = traceIndices[i];
- var trace = gd._fullData[traceIdx];
- var module = trace._module;
-
- // There's nothing to do if this module is not defined:
- if(!module) continue;
-
- // Don't register the trace as transitioned if it doesn't know what to do.
- // If it *is* registered, it will receive a callback that it's responsible
- // for calling in order to register the transition as having completed.
- if(module.animatable) {
- transitionedTraces.push(traceIdx);
- }
-
- gd.data[traceIndices[i]] = plots.extendTrace(gd.data[traceIndices[i]], data[i]);
- }
-
- // Follow the same procedure. Clone it so we don't mangle the input, then
- // expand any object paths so we can merge deep into gd.layout:
- var layoutUpdate = Lib.expandObjectPaths(Lib.extendDeepNoArrays({}, layout));
-
- // Before merging though, we need to modify the incoming layout. We only
- // know how to *transition* layout ranges, so it's imperative that a new
- // range not be sent to the layout before the transition has started. So
- // we must remove the things we can transition:
- var axisAttrRe = /^[xy]axis[0-9]*$/;
- for(var attr in layoutUpdate) {
- if(!axisAttrRe.test(attr)) continue;
- delete layoutUpdate[attr].range;
- }
-
- plots.extendLayout(gd.layout, layoutUpdate);
-
- // Supply defaults after applying the incoming properties. Note that any attempt
- // to simplify this step and reduce the amount of work resulted in the reconstruction
- // of essentially the whole supplyDefaults step, so that it seems sensible to just use
- // supplyDefaults even though it's heavier than would otherwise be desired for
- // transitions:
-
- // first delete calcdata so supplyDefaults knows a calc step is coming
- delete gd.calcdata;
-
- plots.supplyDefaults(gd);
- plots.doCalcdata(gd);
-
- var newLayout = Lib.expandObjectPaths(layout);
-
- if(newLayout) {
- var subplots = gd._fullLayout._plots;
-
- for(var k in subplots) {
- var plotinfo = subplots[k];
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
- var xr0 = xa.range.slice();
- var yr0 = ya.range.slice();
-
- var xr1;
- if(Array.isArray(newLayout[xa._name + '.range'])) {
- xr1 = newLayout[xa._name + '.range'].slice();
- } else if(Array.isArray((newLayout[xa._name] || {}).range)) {
- xr1 = newLayout[xa._name].range.slice();
- }
-
- var yr1;
- if(Array.isArray(newLayout[ya._name + '.range'])) {
- yr1 = newLayout[ya._name + '.range'].slice();
- } else if(Array.isArray((newLayout[ya._name] || {}).range)) {
- yr1 = newLayout[ya._name].range.slice();
- }
-
- var editX;
- if(xr0 && xr1 && (xr0[0] !== xr1[0] || xr0[1] !== xr1[1])) {
- editX = {xr0: xr0, xr1: xr1};
- }
-
- var editY;
- if(yr0 && yr1 && (yr0[0] !== yr1[0] || yr0[1] !== yr1[1])) {
- editY = {yr0: yr0, yr1: yr1};
- }
-
- if(editX || editY) {
- axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY));
- }
- }
- }
-
- return Promise.resolve();
- };
-
- opts.runFn = function(makeCallback) {
- var traceTransitionOpts;
- var basePlotModules = gd._fullLayout._basePlotModules;
- var hasAxisTransition = axEdits.length;
- var i;
-
- if(layout) {
- for(i = 0; i < basePlotModules.length; i++) {
- if(basePlotModules[i].transitionAxes) {
- basePlotModules[i].transitionAxes(gd, axEdits, transitionOpts, makeCallback);
- }
- }
- }
-
- // Here handle the exception that we refuse to animate scales and axes at the same
- // time. In other words, if there's an axis transition, then set the data transition
- // to instantaneous.
- if(hasAxisTransition) {
- traceTransitionOpts = Lib.extendFlat({}, transitionOpts);
- traceTransitionOpts.duration = 0;
- // This means do not transition traces,
- // this happens on layout-only (e.g. axis range) animations
- transitionedTraces = null;
- } else {
- traceTransitionOpts = transitionOpts;
- }
-
- for(i = 0; i < basePlotModules.length; i++) {
- // Note that we pass a callback to *create* the callback that must be invoked on completion.
- // This is since not all traces know about transitions, so it greatly simplifies matters if
- // the trace is responsible for creating a callback, if needed, and then executing it when
- // the time is right.
- basePlotModules[i].plot(gd, transitionedTraces, traceTransitionOpts, makeCallback);
- }
- };
-
- return _transition(gd, transitionOpts, opts);
- };
-
- /**
- * Transition to a set of new data and layout properties from Plotly.react
- *
- * @param {DOM element} gd
- * @param {object} restyleFlags
- * - anim {'all'|'some'}
- * @param {object} relayoutFlags
- * - anim {'all'|'some'}
- * @param {object} oldFullLayout : old (pre Plotly.react) fullLayout
- */
- plots.transitionFromReact = function(gd, restyleFlags, relayoutFlags, oldFullLayout) {
- var fullLayout = gd._fullLayout;
- var transitionOpts = fullLayout.transition;
- var opts = {};
- var axEdits = [];
-
- opts.prepareFn = function() {
- var subplots = fullLayout._plots;
-
- // no need to redraw at end of transition,
- // if all changes are animatable
- opts.redraw = false;
- if(restyleFlags.anim === 'some') opts.redraw = true;
- if(relayoutFlags.anim === 'some') opts.redraw = true;
-
- for(var k in subplots) {
- var plotinfo = subplots[k];
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
- var xr0 = oldFullLayout[xa._name].range.slice();
- var yr0 = oldFullLayout[ya._name].range.slice();
- var xr1 = xa.range.slice();
- var yr1 = ya.range.slice();
-
- xa.setScale();
- ya.setScale();
-
- var editX;
- if(xr0[0] !== xr1[0] || xr0[1] !== xr1[1]) {
- editX = {xr0: xr0, xr1: xr1};
- }
-
- var editY;
- if(yr0[0] !== yr1[0] || yr0[1] !== yr1[1]) {
- editY = {yr0: yr0, yr1: yr1};
- }
-
- if(editX || editY) {
- axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY));
- }
- }
-
- return Promise.resolve();
- };
-
- opts.runFn = function(makeCallback) {
- var fullData = gd._fullData;
- var fullLayout = gd._fullLayout;
- var basePlotModules = fullLayout._basePlotModules;
-
- var axisTransitionOpts;
- var traceTransitionOpts;
- var transitionedTraces;
-
- var allTraceIndices = [];
- for(var i = 0; i < fullData.length; i++) {
- allTraceIndices.push(i);
- }
-
- function transitionAxes() {
- for(var j = 0; j < basePlotModules.length; j++) {
- if(basePlotModules[j].transitionAxes) {
- basePlotModules[j].transitionAxes(gd, axEdits, axisTransitionOpts, makeCallback);
- }
- }
- }
-
- function transitionTraces() {
- for(var j = 0; j < basePlotModules.length; j++) {
- basePlotModules[j].plot(gd, transitionedTraces, traceTransitionOpts, makeCallback);
- }
- }
-
- if(axEdits.length && restyleFlags.anim) {
- if(transitionOpts.ordering === 'traces first') {
- axisTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0});
- transitionedTraces = allTraceIndices;
- traceTransitionOpts = transitionOpts;
- transitionTraces();
- setTimeout(transitionAxes, transitionOpts.duration);
- } else {
- axisTransitionOpts = transitionOpts;
- transitionedTraces = null;
- traceTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0});
- transitionAxes();
- transitionTraces();
- }
- } else if(axEdits.length) {
- axisTransitionOpts = transitionOpts;
- transitionAxes();
- } else if(restyleFlags.anim) {
- transitionedTraces = allTraceIndices;
- traceTransitionOpts = transitionOpts;
- transitionTraces();
- }
- };
-
- return _transition(gd, transitionOpts, opts);
- };
-
- /**
- * trace/layout transition wrapper that works
- * for transitions initiated by Plotly.animate and Plotly.react.
- *
- * @param {DOM element} gd
- * @param {object} transitionOpts
- * @param {object} opts
- * - redraw {boolean}
- * - prepareFn {function} *should return a Promise*
- * - runFn {function} ran inside executeTransitions
- */
- function _transition(gd, transitionOpts, opts) {
- var aborted = false;
-
- function executeCallbacks(list) {
- var p = Promise.resolve();
- if(!list) return p;
- while(list.length) {
- p = p.then((list.shift()));
- }
- return p;
- }
-
- function flushCallbacks(list) {
- if(!list) return;
- while(list.length) {
- list.shift();
- }
- }
-
- function executeTransitions() {
- gd.emit('plotly_transitioning', []);
-
- return new Promise(function(resolve) {
- // This flag is used to disabled things like autorange:
- gd._transitioning = true;
-
- // When instantaneous updates are coming through quickly, it's too much to simply disable
- // all interaction, so store this flag so we can disambiguate whether mouse interactions
- // should be fully disabled or not:
- if(transitionOpts.duration > 0) {
- gd._transitioningWithDuration = true;
- }
-
- // If another transition is triggered, this callback will be executed simply because it's
- // in the interruptCallbacks queue. If this transition completes, it will instead flush
- // that queue and forget about this callback.
- gd._transitionData._interruptCallbacks.push(function() {
- aborted = true;
- });
-
- if(opts.redraw) {
- gd._transitionData._interruptCallbacks.push(function() {
- return Registry.call('redraw', gd);
- });
- }
-
- // Emit this and make sure it happens last:
- gd._transitionData._interruptCallbacks.push(function() {
- gd.emit('plotly_transitioninterrupted', []);
- });
-
- // Construct callbacks that are executed on transition end. This ensures the d3 transitions
- // are *complete* before anything else is done.
- var numCallbacks = 0;
- var numCompleted = 0;
- function makeCallback() {
- numCallbacks++;
- return function() {
- numCompleted++;
- // When all are complete, perform a redraw:
- if(!aborted && numCompleted === numCallbacks) {
- completeTransition(resolve);
- }
- };
- }
-
- opts.runFn(makeCallback);
-
- // If nothing else creates a callback, then this will trigger the completion in the next tick:
- setTimeout(makeCallback());
- });
- }
-
- function completeTransition(callback) {
- // This a simple workaround for tests which purge the graph before animations
- // have completed. That's not a very common case, so this is the simplest
- // fix.
- if(!gd._transitionData) return;
-
- flushCallbacks(gd._transitionData._interruptCallbacks);
-
- return Promise.resolve().then(function() {
- if(opts.redraw) {
- return Registry.call('redraw', gd);
- }
- }).then(function() {
- // Set transitioning false again once the redraw has occurred. This is used, for example,
- // to prevent the trailing redraw from autoranging:
- gd._transitioning = false;
- gd._transitioningWithDuration = false;
-
- gd.emit('plotly_transitioned', []);
- }).then(callback);
- }
-
- function interruptPreviousTransitions() {
- // Fail-safe against purged plot:
- if(!gd._transitionData) return;
-
- // If a transition is interrupted, set this to false. At the moment, the only thing that would
- // interrupt a transition is another transition, so that it will momentarily be set to true
- // again, but this determines whether autorange or dragbox work, so it's for the sake of
- // cleanliness:
- gd._transitioning = false;
-
- return executeCallbacks(gd._transitionData._interruptCallbacks);
- }
-
- var seq = [
- plots.previousPromises,
- interruptPreviousTransitions,
- opts.prepareFn,
- plots.rehover,
- executeTransitions
- ];
-
- var transitionStarting = Lib.syncOrAsync(seq, gd);
-
- if(!transitionStarting || !transitionStarting.then) {
- transitionStarting = Promise.resolve();
- }
-
- return transitionStarting.then(function() { return gd; });
- }
-
- plots.doCalcdata = function(gd, traces) {
- var axList = axisIDs.list(gd);
- var fullData = gd._fullData;
- var fullLayout = gd._fullLayout;
-
- var trace, _module, i, j;
-
- // XXX: Is this correct? Needs a closer look so that *some* traces can be recomputed without
- // *all* needing doCalcdata:
- var calcdata = new Array(fullData.length);
- var oldCalcdata = (gd.calcdata || []).slice(0);
- gd.calcdata = calcdata;
-
- // extra helper variables
-
- // how many box/violins plots do we have (in case they're grouped)
- fullLayout._numBoxes = 0;
- fullLayout._numViolins = 0;
-
- // initialize violin per-scale-group stats container
- fullLayout._violinScaleGroupStats = {};
-
- // for calculating avg luminosity of heatmaps
- gd._hmpixcount = 0;
- gd._hmlumcount = 0;
-
- // for sharing colors across pies (and for legend)
- fullLayout._piecolormap = {};
-
- // If traces were specified and this trace was not included,
- // then transfer it over from the old calcdata:
- for(i = 0; i < fullData.length; i++) {
- if(Array.isArray(traces) && traces.indexOf(i) === -1) {
- calcdata[i] = oldCalcdata[i];
- continue;
- }
- }
-
- for(i = 0; i < fullData.length; i++) {
- trace = fullData[i];
-
- trace._arrayAttrs = PlotSchema.findArrayAttributes(trace);
-
- // keep track of trace extremes (for autorange) in here
- trace._extremes = {};
- }
-
- // add polar axes to axis list
- var polarIds = fullLayout._subplots.polar || [];
- for(i = 0; i < polarIds.length; i++) {
- axList.push(
- fullLayout[polarIds[i]].radialaxis,
- fullLayout[polarIds[i]].angularaxis
- );
- }
-
- setupAxisCategories(axList, fullData);
-
- var hasCalcTransform = false;
-
- // transform loop
- for(i = 0; i < fullData.length; i++) {
- trace = fullData[i];
-
- if(trace.visible === true && trace.transforms) {
- _module = trace._module;
-
- // we need one round of trace module calc before
- // the calc transform to 'fill in' the categories list
- // used for example in the data-to-coordinate method
- if(_module && _module.calc) {
- var cdi = _module.calc(gd, trace);
-
- // must clear scene 'batches', so that 2nd
- // _module.calc call starts from scratch
- if(cdi[0] && cdi[0].t && cdi[0].t._scene) {
- delete cdi[0].t._scene.dirty;
- }
- }
-
- for(j = 0; j < trace.transforms.length; j++) {
- var transform = trace.transforms[j];
-
- _module = transformsRegistry[transform.type];
- if(_module && _module.calcTransform) {
- trace._hasCalcTransform = true;
- hasCalcTransform = true;
- _module.calcTransform(gd, trace, transform);
- }
- }
- }
- }
-
- // clear stuff that should recomputed in 'regular' loop
- if(hasCalcTransform) setupAxisCategories(axList, fullData);
-
- function calci(i, isContainer) {
- trace = fullData[i];
- _module = trace._module;
-
- if(!!_module.isContainer !== isContainer) return;
-
- var cd = [];
-
- if(trace.visible === true) {
-
- // clear existing ref in case it got relinked
- delete trace._indexToPoints;
- // keep ref of index-to-points map object of the *last* enabled transform,
- // this index-to-points map object is required to determine the calcdata indices
- // that correspond to input indices (e.g. from 'selectedpoints')
- var transforms = trace.transforms || [];
- for(j = transforms.length - 1; j >= 0; j--) {
- if(transforms[j].enabled) {
- trace._indexToPoints = transforms[j]._indexToPoints;
- break;
- }
- }
-
- if(_module && _module.calc) {
- cd = _module.calc(gd, trace);
- }
- }
-
- // Make sure there is a first point.
- //
- // This ensures there is a calcdata item for every trace,
- // even if cartesian logic doesn't handle it (for things like legends).
- if(!Array.isArray(cd) || !cd[0]) {
- cd = [{x: BADNUM, y: BADNUM}];
- }
-
- // add the trace-wide properties to the first point,
- // per point properties to every point
- // t is the holder for trace-wide properties
- if(!cd[0].t) cd[0].t = {};
- cd[0].trace = trace;
-
- calcdata[i] = cd;
- }
-
- // 'regular' loop - make sure container traces (eg carpet) calc before
- // contained traces (eg contourcarpet)
- for(i = 0; i < fullData.length; i++) calci(i, true);
- for(i = 0; i < fullData.length; i++) calci(i, false);
-
- doCrossTraceCalc(gd);
-
- Registry.getComponentMethod('fx', 'calc')(gd);
- Registry.getComponentMethod('errorbars', 'calc')(gd);
- };
-
- function setupAxisCategories(axList, fullData) {
- for(var i = 0; i < axList.length; i++) {
- var ax = axList[i];
- ax.clearCalc();
- if(ax.type === 'multicategory') {
- ax.setupMultiCategory(fullData);
- }
- }
- }
-
- function doCrossTraceCalc(gd) {
- var fullLayout = gd._fullLayout;
- var modules = fullLayout._visibleModules;
- var hash = {};
- var i, j, k;
-
- // position and range calculations for traces that
- // depend on each other ie bars (stacked or grouped)
- // and boxes (grouped) push each other out of the way
-
- for(j = 0; j < modules.length; j++) {
- var _module = modules[j];
- var fn = _module.crossTraceCalc;
- if(fn) {
- var spType = _module.basePlotModule.name;
- if(hash[spType]) {
- Lib.pushUnique(hash[spType], fn);
- } else {
- hash[spType] = [fn];
- }
- }
- }
-
- for(k in hash) {
- var methods = hash[k];
- var subplots = fullLayout._subplots[k];
-
- if(Array.isArray(subplots)) {
- for(i = 0; i < subplots.length; i++) {
- var sp = subplots[i];
- var spInfo = k === 'cartesian' ?
- fullLayout._plots[sp] :
- fullLayout[sp];
-
- for(j = 0; j < methods.length; j++) {
- methods[j](gd, spInfo, sp);
- }
- }
- }
- else {
- for(j = 0; j < methods.length; j++) {
- methods[j](gd);
- }
- }
- }
- }
-
- plots.rehover = function(gd) {
- if(gd._fullLayout._rehover) {
- gd._fullLayout._rehover();
- }
- };
-
- plots.generalUpdatePerTraceModule = function(gd, subplot, subplotCalcData, subplotLayout) {
- var traceHashOld = subplot.traceHash;
- var traceHash = {};
- var i;
-
- // build up moduleName -> calcData hash
- for(i = 0; i < subplotCalcData.length; i++) {
- var calcTraces = subplotCalcData[i];
- var trace = calcTraces[0].trace;
-
- // skip over visible === false traces
- // as they don't have `_module` ref
- if(trace.visible) {
- traceHash[trace.type] = traceHash[trace.type] || [];
- traceHash[trace.type].push(calcTraces);
- }
- }
-
- // when a trace gets deleted, make sure that its module's
- // plot method is called so that it is properly
- // removed from the DOM.
- for(var moduleNameOld in traceHashOld) {
- if(!traceHash[moduleNameOld]) {
- var fakeCalcTrace = traceHashOld[moduleNameOld][0];
- var fakeTrace = fakeCalcTrace[0].trace;
-
- fakeTrace.visible = false;
- traceHash[moduleNameOld] = [fakeCalcTrace];
- }
- }
-
- // call module plot method
- for(var moduleName in traceHash) {
- var moduleCalcData = traceHash[moduleName];
- var _module = moduleCalcData[0][0].trace._module;
-
- _module.plot(gd, subplot, Lib.filterVisible(moduleCalcData), subplotLayout);
- }
-
- // update moduleName -> calcData hash
- subplot.traceHash = traceHash;
- };
-
- },{"../components/color":51,"../constants/numerical":149,"../lib":168,"../plot_api/plot_schema":201,"../plot_api/plot_template":202,"../plots/cartesian/axis_ids":215,"../registry":257,"./animation_attributes":207,"./attributes":209,"./command":237,"./font_attributes":239,"./frame_attributes":240,"./layout_attributes":243,"d3":16,"fast-isnumeric":18}],246:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var scatterAttrs = _dereq_('../../../traces/scatter/attributes');
- var scatterMarkerAttrs = scatterAttrs.marker;
- var extendFlat = _dereq_('../../../lib/extend').extendFlat;
-
- var deprecationWarning = [
- 'Area traces are deprecated!',
- 'Please switch to the *barpolar* trace type.'
- ].join(' ');
-
- module.exports = {
- r: extendFlat({}, scatterAttrs.r, {
-
- }),
- t: extendFlat({}, scatterAttrs.t, {
-
- }),
- marker: {
- color: extendFlat({}, scatterMarkerAttrs.color, {
-
- }),
- size: extendFlat({}, scatterMarkerAttrs.size, {
-
- }),
- symbol: extendFlat({}, scatterMarkerAttrs.symbol, {
-
- }),
- opacity: extendFlat({}, scatterMarkerAttrs.opacity, {
-
- }),
- editType: 'calc'
- }
- };
-
- },{"../../../lib/extend":162,"../../../traces/scatter/attributes":367}],247:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var axesAttrs = _dereq_('../../cartesian/layout_attributes');
- var extendFlat = _dereq_('../../../lib/extend').extendFlat;
- var overrideAll = _dereq_('../../../plot_api/edit_types').overrideAll;
-
- var deprecationWarning = [
- 'Legacy polar charts are deprecated!',
- 'Please switch to *polar* subplots.'
- ].join(' ');
-
- var domainAttr = extendFlat({}, axesAttrs.domain, {
-
- });
-
- function mergeAttrs(axisName, nonCommonAttrs) {
- var commonAttrs = {
- showline: {
- valType: 'boolean',
-
-
- },
- showticklabels: {
- valType: 'boolean',
-
-
- },
- tickorientation: {
- valType: 'enumerated',
- values: ['horizontal', 'vertical'],
-
-
- },
- ticklen: {
- valType: 'number',
- min: 0,
-
-
- },
- tickcolor: {
- valType: 'color',
-
-
- },
- ticksuffix: {
- valType: 'string',
-
-
- },
- endpadding: {
- valType: 'number',
-
- description: deprecationWarning,
- },
- visible: {
- valType: 'boolean',
-
-
- }
- };
-
- return extendFlat({}, nonCommonAttrs, commonAttrs);
- }
-
- module.exports = overrideAll({
- radialaxis: mergeAttrs('radial', {
- range: {
- valType: 'info_array',
-
- items: [
- { valType: 'number' },
- { valType: 'number' }
- ],
-
- },
- domain: domainAttr,
- orientation: {
- valType: 'number',
-
-
- }
- }),
-
- angularaxis: mergeAttrs('angular', {
- range: {
- valType: 'info_array',
-
- items: [
- { valType: 'number', dflt: 0 },
- { valType: 'number', dflt: 360 }
- ],
-
- },
- domain: domainAttr
- }),
-
- // attributes that appear at layout root
- layout: {
- direction: {
- valType: 'enumerated',
- values: ['clockwise', 'counterclockwise'],
-
-
- },
- orientation: {
- valType: 'angle',
-
-
- }
- }
- }, 'plot', 'nested');
-
- },{"../../../lib/extend":162,"../../../plot_api/edit_types":195,"../../cartesian/layout_attributes":225}],248:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Polar = module.exports = _dereq_('./micropolar');
-
- Polar.manager = _dereq_('./micropolar_manager');
-
- },{"./micropolar":249,"./micropolar_manager":250}],249:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- var d3 = _dereq_('d3');
- var Lib = _dereq_('../../../lib');
- var extendDeepAll = Lib.extendDeepAll;
- var MID_SHIFT = _dereq_('../../../constants/alignment').MID_SHIFT;
-
- var µ = module.exports = { version: '0.2.2' };
-
- µ.Axis = function module() {
- var config = {
- data: [],
- layout: {}
- }, inputConfig = {}, liveConfig = {};
- var svg, container, dispatch = d3.dispatch('hover'), radialScale, angularScale;
- var exports = {};
- function render(_container) {
- container = _container || container;
- var data = config.data;
- var axisConfig = config.layout;
- if (typeof container == 'string' || container.nodeName) container = d3.select(container);
- container.datum(data).each(function(_data, _index) {
- var dataOriginal = _data.slice();
- liveConfig = {
- data: µ.util.cloneJson(dataOriginal),
- layout: µ.util.cloneJson(axisConfig)
- };
- var colorIndex = 0;
- dataOriginal.forEach(function(d, i) {
- if (!d.color) {
- d.color = axisConfig.defaultColorRange[colorIndex];
- colorIndex = (colorIndex + 1) % axisConfig.defaultColorRange.length;
- }
- if (!d.strokeColor) {
- d.strokeColor = d.geometry === 'LinePlot' ? d.color : d3.rgb(d.color).darker().toString();
- }
- liveConfig.data[i].color = d.color;
- liveConfig.data[i].strokeColor = d.strokeColor;
- liveConfig.data[i].strokeDash = d.strokeDash;
- liveConfig.data[i].strokeSize = d.strokeSize;
- });
- var data = dataOriginal.filter(function(d, i) {
- var visible = d.visible;
- return typeof visible === 'undefined' || visible === true;
- });
- var isStacked = false;
- var dataWithGroupId = data.map(function(d, i) {
- isStacked = isStacked || typeof d.groupId !== 'undefined';
- return d;
- });
- if (isStacked) {
- var grouped = d3.nest().key(function(d, i) {
- return typeof d.groupId != 'undefined' ? d.groupId : 'unstacked';
- }).entries(dataWithGroupId);
- var dataYStack = [];
- var stacked = grouped.map(function(d, i) {
- if (d.key === 'unstacked') return d.values; else {
- var prevArray = d.values[0].r.map(function(d, i) {
- return 0;
- });
- d.values.forEach(function(d, i, a) {
- d.yStack = [ prevArray ];
- dataYStack.push(prevArray);
- prevArray = µ.util.sumArrays(d.r, prevArray);
- });
- return d.values;
- }
- });
- data = d3.merge(stacked);
- }
- data.forEach(function(d, i) {
- d.t = Array.isArray(d.t[0]) ? d.t : [ d.t ];
- d.r = Array.isArray(d.r[0]) ? d.r : [ d.r ];
- });
- var radius = Math.min(axisConfig.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig.margin.top - axisConfig.margin.bottom) / 2;
- radius = Math.max(10, radius);
- var chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ];
- var extent;
- if (isStacked) {
- var highestStackedValue = d3.max(µ.util.sumArrays(µ.util.arrayLast(data).r[0], µ.util.arrayLast(dataYStack)));
- extent = [ 0, highestStackedValue ];
- } else extent = d3.extent(µ.util.flattenArray(data.map(function(d, i) {
- return d.r;
- })));
- if (axisConfig.radialAxis.domain != µ.DATAEXTENT) extent[0] = 0;
- radialScale = d3.scale.linear().domain(axisConfig.radialAxis.domain != µ.DATAEXTENT && axisConfig.radialAxis.domain ? axisConfig.radialAxis.domain : extent).range([ 0, radius ]);
- liveConfig.layout.radialAxis.domain = radialScale.domain();
- var angularDataMerged = µ.util.flattenArray(data.map(function(d, i) {
- return d.t;
- }));
- var isOrdinal = typeof angularDataMerged[0] === 'string';
- var ticks;
- if (isOrdinal) {
- angularDataMerged = µ.util.deduplicate(angularDataMerged);
- ticks = angularDataMerged.slice();
- angularDataMerged = d3.range(angularDataMerged.length);
- data = data.map(function(d, i) {
- var result = d;
- d.t = [ angularDataMerged ];
- if (isStacked) result.yStack = d.yStack;
- return result;
- });
- }
- var hasOnlyLineOrDotPlot = data.filter(function(d, i) {
- return d.geometry === 'LinePlot' || d.geometry === 'DotPlot';
- }).length === data.length;
- var needsEndSpacing = axisConfig.needsEndSpacing === null ? isOrdinal || !hasOnlyLineOrDotPlot : axisConfig.needsEndSpacing;
- var useProvidedDomain = axisConfig.angularAxis.domain && axisConfig.angularAxis.domain != µ.DATAEXTENT && !isOrdinal && axisConfig.angularAxis.domain[0] >= 0;
- var angularDomain = useProvidedDomain ? axisConfig.angularAxis.domain : d3.extent(angularDataMerged);
- var angularDomainStep = Math.abs(angularDataMerged[1] - angularDataMerged[0]);
- if (hasOnlyLineOrDotPlot && !isOrdinal) angularDomainStep = 0;
- var angularDomainWithPadding = angularDomain.slice();
- if (needsEndSpacing && isOrdinal) angularDomainWithPadding[1] += angularDomainStep;
- var tickCount = axisConfig.angularAxis.ticksCount || 4;
- if (tickCount > 8) tickCount = tickCount / (tickCount / 8) + tickCount % 8;
- if (axisConfig.angularAxis.ticksStep) {
- tickCount = (angularDomainWithPadding[1] - angularDomainWithPadding[0]) / tickCount;
- }
- var angularTicksStep = axisConfig.angularAxis.ticksStep || (angularDomainWithPadding[1] - angularDomainWithPadding[0]) / (tickCount * (axisConfig.minorTicks + 1));
- if (ticks) angularTicksStep = Math.max(Math.round(angularTicksStep), 1);
- if (!angularDomainWithPadding[2]) angularDomainWithPadding[2] = angularTicksStep;
- var angularAxisRange = d3.range.apply(this, angularDomainWithPadding);
- angularAxisRange = angularAxisRange.map(function(d, i) {
- return parseFloat(d.toPrecision(12));
- });
- angularScale = d3.scale.linear().domain(angularDomainWithPadding.slice(0, 2)).range(axisConfig.direction === 'clockwise' ? [ 0, 360 ] : [ 360, 0 ]);
- liveConfig.layout.angularAxis.domain = angularScale.domain();
- liveConfig.layout.angularAxis.endPadding = needsEndSpacing ? angularDomainStep : 0;
- svg = d3.select(this).select('svg.chart-root');
- if (typeof svg === 'undefined' || svg.empty()) {
- var skeleton = "<svg xmlns='http://www.w3.org/2000/svg' class='chart-root'>' + '<g class='outer-group'>' + '<g class='chart-group'>' + '<circle class='background-circle'></circle>' + '<g class='geometry-group'></g>' + '<g class='radial axis-group'>' + '<circle class='outside-circle'></circle>' + '</g>' + '<g class='angular axis-group'></g>' + '<g class='guides-group'><line></line><circle r='0'></circle></g>' + '</g>' + '<g class='legend-group'></g>' + '<g class='tooltips-group'></g>' + '<g class='title-group'><text></text></g>' + '</g>' + '</svg>";
- var doc = new DOMParser().parseFromString(skeleton, 'application/xml');
- var newSvg = this.appendChild(this.ownerDocument.importNode(doc.documentElement, true));
- svg = d3.select(newSvg);
- }
- svg.select('.guides-group').style({
- 'pointer-events': 'none'
- });
- svg.select('.angular.axis-group').style({
- 'pointer-events': 'none'
- });
- svg.select('.radial.axis-group').style({
- 'pointer-events': 'none'
- });
- var chartGroup = svg.select('.chart-group');
- var lineStyle = {
- fill: 'none',
- stroke: axisConfig.tickColor
- };
- var fontStyle = {
- 'font-size': axisConfig.font.size,
- 'font-family': axisConfig.font.family,
- fill: axisConfig.font.color,
- 'text-shadow': [ '-1px 0px', '1px -1px', '-1px 1px', '1px 1px' ].map(function(d, i) {
- return ' ' + d + ' 0 ' + axisConfig.font.outlineColor;
- }).join(',')
- };
- var legendContainer;
- if (axisConfig.showLegend) {
- legendContainer = svg.select('.legend-group').attr({
- transform: 'translate(' + [ radius, axisConfig.margin.top ] + ')'
- }).style({
- display: 'block'
- });
- var elements = data.map(function(d, i) {
- var datumClone = µ.util.cloneJson(d);
- datumClone.symbol = d.geometry === 'DotPlot' ? d.dotType || 'circle' : d.geometry != 'LinePlot' ? 'square' : 'line';
- datumClone.visibleInLegend = typeof d.visibleInLegend === 'undefined' || d.visibleInLegend;
- datumClone.color = d.geometry === 'LinePlot' ? d.strokeColor : d.color;
- return datumClone;
- });
-
- µ.Legend().config({
- data: data.map(function(d, i) {
- return d.name || 'Element' + i;
- }),
- legendConfig: extendDeepAll({},
- µ.Legend.defaultConfig().legendConfig,
- {
- container: legendContainer,
- elements: elements,
- reverseOrder: axisConfig.legend.reverseOrder
- }
- )
- })();
-
- var legendBBox = legendContainer.node().getBBox();
- radius = Math.min(axisConfig.width - legendBBox.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig.margin.top - axisConfig.margin.bottom) / 2;
- radius = Math.max(10, radius);
- chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ];
- radialScale.range([ 0, radius ]);
- liveConfig.layout.radialAxis.domain = radialScale.domain();
- legendContainer.attr('transform', 'translate(' + [ chartCenter[0] + radius, chartCenter[1] - radius ] + ')');
- } else {
- legendContainer = svg.select('.legend-group').style({
- display: 'none'
- });
- }
- svg.attr({
- width: axisConfig.width,
- height: axisConfig.height
- }).style({
- opacity: axisConfig.opacity
- });
- chartGroup.attr('transform', 'translate(' + chartCenter + ')').style({
- cursor: 'crosshair'
- });
- var centeringOffset = [ (axisConfig.width - (axisConfig.margin.left + axisConfig.margin.right + radius * 2 + (legendBBox ? legendBBox.width : 0))) / 2, (axisConfig.height - (axisConfig.margin.top + axisConfig.margin.bottom + radius * 2)) / 2 ];
- centeringOffset[0] = Math.max(0, centeringOffset[0]);
- centeringOffset[1] = Math.max(0, centeringOffset[1]);
- svg.select('.outer-group').attr('transform', 'translate(' + centeringOffset + ')');
- if (axisConfig.title && axisConfig.title.text) {
- var title = svg.select('g.title-group text').style(fontStyle).text(axisConfig.title.text);
- var titleBBox = title.node().getBBox();
- title.attr({
- x: chartCenter[0] - titleBBox.width / 2,
- y: chartCenter[1] - radius - 20
- });
- }
- var radialAxis = svg.select('.radial.axis-group');
- if (axisConfig.radialAxis.gridLinesVisible) {
- var gridCircles = radialAxis.selectAll('circle.grid-circle').data(radialScale.ticks(5));
- gridCircles.enter().append('circle').attr({
- 'class': 'grid-circle'
- }).style(lineStyle);
- gridCircles.attr('r', radialScale);
- gridCircles.exit().remove();
- }
- radialAxis.select('circle.outside-circle').attr({
- r: radius
- }).style(lineStyle);
- var backgroundCircle = svg.select('circle.background-circle').attr({
- r: radius
- }).style({
- fill: axisConfig.backgroundColor,
- stroke: axisConfig.stroke
- });
- function currentAngle(d, i) {
- return angularScale(d) % 360 + axisConfig.orientation;
- }
- if (axisConfig.radialAxis.visible) {
- var axis = d3.svg.axis().scale(radialScale).ticks(5).tickSize(5);
- radialAxis.call(axis).attr({
- transform: 'rotate(' + axisConfig.radialAxis.orientation + ')'
- });
- radialAxis.selectAll('.domain').style(lineStyle);
- radialAxis.selectAll('g>text').text(function(d, i) {
- return this.textContent + axisConfig.radialAxis.ticksSuffix;
- }).style(fontStyle).style({
- 'text-anchor': 'start'
- }).attr({
- x: 0,
- y: 0,
- dx: 0,
- dy: 0,
- transform: function(d, i) {
- if (axisConfig.radialAxis.tickOrientation === 'horizontal') {
- return 'rotate(' + -axisConfig.radialAxis.orientation + ') translate(' + [ 0, fontStyle['font-size'] ] + ')';
- } else return 'translate(' + [ 0, fontStyle['font-size'] ] + ')';
- }
- });
- radialAxis.selectAll('g>line').style({
- stroke: 'black'
- });
- }
- var angularAxis = svg.select('.angular.axis-group').selectAll('g.angular-tick').data(angularAxisRange);
- var angularAxisEnter = angularAxis.enter().append('g').classed('angular-tick', true);
- angularAxis.attr({
- transform: function(d, i) {
- return 'rotate(' + currentAngle(d, i) + ')';
- }
- }).style({
- display: axisConfig.angularAxis.visible ? 'block' : 'none'
- });
- angularAxis.exit().remove();
- angularAxisEnter.append('line').classed('grid-line', true).classed('major', function(d, i) {
- return i % (axisConfig.minorTicks + 1) == 0;
- }).classed('minor', function(d, i) {
- return !(i % (axisConfig.minorTicks + 1) == 0);
- }).style(lineStyle);
- angularAxisEnter.selectAll('.minor').style({
- stroke: axisConfig.minorTickColor
- });
- angularAxis.select('line.grid-line').attr({
- x1: axisConfig.tickLength ? radius - axisConfig.tickLength : 0,
- x2: radius
- }).style({
- display: axisConfig.angularAxis.gridLinesVisible ? 'block' : 'none'
- });
- angularAxisEnter.append('text').classed('axis-text', true).style(fontStyle);
- var ticksText = angularAxis.select('text.axis-text').attr({
- x: radius + axisConfig.labelOffset,
- dy: MID_SHIFT + 'em',
- transform: function(d, i) {
- var angle = currentAngle(d, i);
- var rad = radius + axisConfig.labelOffset;
- var orient = axisConfig.angularAxis.tickOrientation;
- if (orient == 'horizontal') return 'rotate(' + -angle + ' ' + rad + ' 0)'; else if (orient == 'radial') return angle < 270 && angle > 90 ? 'rotate(180 ' + rad + ' 0)' : null; else return 'rotate(' + (angle <= 180 && angle > 0 ? -90 : 90) + ' ' + rad + ' 0)';
- }
- }).style({
- 'text-anchor': 'middle',
- display: axisConfig.angularAxis.labelsVisible ? 'block' : 'none'
- }).text(function(d, i) {
- if (i % (axisConfig.minorTicks + 1) != 0) return '';
- if (ticks) {
- return ticks[d] + axisConfig.angularAxis.ticksSuffix;
- } else return d + axisConfig.angularAxis.ticksSuffix;
- }).style(fontStyle);
- if (axisConfig.angularAxis.rewriteTicks) ticksText.text(function(d, i) {
- if (i % (axisConfig.minorTicks + 1) != 0) return '';
- return axisConfig.angularAxis.rewriteTicks(this.textContent, i);
- });
- var rightmostTickEndX = d3.max(chartGroup.selectAll('.angular-tick text')[0].map(function(d, i) {
- return d.getCTM().e + d.getBBox().width;
- }));
- legendContainer.attr({
- transform: 'translate(' + [ radius + rightmostTickEndX, axisConfig.margin.top ] + ')'
- });
- var hasGeometry = svg.select('g.geometry-group').selectAll('g').size() > 0;
- var geometryContainer = svg.select('g.geometry-group').selectAll('g.geometry').data(data);
- geometryContainer.enter().append('g').attr({
- 'class': function(d, i) {
- return 'geometry geometry' + i;
- }
- });
- geometryContainer.exit().remove();
- if (data[0] || hasGeometry) {
- var geometryConfigs = [];
- data.forEach(function(d, i) {
- var geometryConfig = {};
- geometryConfig.radialScale = radialScale;
- geometryConfig.angularScale = angularScale;
- geometryConfig.container = geometryContainer.filter(function(dB, iB) {
- return iB == i;
- });
- geometryConfig.geometry = d.geometry;
- geometryConfig.orientation = axisConfig.orientation;
- geometryConfig.direction = axisConfig.direction;
- geometryConfig.index = i;
- geometryConfigs.push({
- data: d,
- geometryConfig: geometryConfig
- });
- });
- var geometryConfigsGrouped = d3.nest().key(function(d, i) {
- return typeof d.data.groupId != 'undefined' || 'unstacked';
- }).entries(geometryConfigs);
- var geometryConfigsGrouped2 = [];
- geometryConfigsGrouped.forEach(function(d, i) {
- if (d.key === 'unstacked') geometryConfigsGrouped2 = geometryConfigsGrouped2.concat(d.values.map(function(d, i) {
- return [ d ];
- })); else geometryConfigsGrouped2.push(d.values);
- });
- geometryConfigsGrouped2.forEach(function(d, i) {
- var geometry;
- if (Array.isArray(d)) geometry = d[0].geometryConfig.geometry; else geometry = d.geometryConfig.geometry;
- var finalGeometryConfig = d.map(function(dB, iB) {
- return extendDeepAll(µ[geometry].defaultConfig(), dB);
- });
- µ[geometry]().config(finalGeometryConfig)();
- });
- }
- var guides = svg.select('.guides-group');
- var tooltipContainer = svg.select('.tooltips-group');
- var angularTooltip = µ.tooltipPanel().config({
- container: tooltipContainer,
- fontSize: 8
- })();
- var radialTooltip = µ.tooltipPanel().config({
- container: tooltipContainer,
- fontSize: 8
- })();
- var geometryTooltip = µ.tooltipPanel().config({
- container: tooltipContainer,
- hasTick: true
- })();
- var angularValue, radialValue;
- if (!isOrdinal) {
- var angularGuideLine = guides.select('line').attr({
- x1: 0,
- y1: 0,
- y2: 0
- }).style({
- stroke: 'grey',
- 'pointer-events': 'none'
- });
- chartGroup.on('mousemove.angular-guide', function(d, i) {
- var mouseAngle = µ.util.getMousePos(backgroundCircle).angle;
- angularGuideLine.attr({
- x2: -radius,
- transform: 'rotate(' + mouseAngle + ')'
- }).style({
- opacity: .5
- });
- var angleWithOriginOffset = (mouseAngle + 180 + 360 - axisConfig.orientation) % 360;
- angularValue = angularScale.invert(angleWithOriginOffset);
- var pos = µ.util.convertToCartesian(radius + 12, mouseAngle + 180);
- angularTooltip.text(µ.util.round(angularValue)).move([ pos[0] + chartCenter[0], pos[1] + chartCenter[1] ]);
- }).on('mouseout.angular-guide', function(d, i) {
- guides.select('line').style({
- opacity: 0
- });
- });
- }
- var angularGuideCircle = guides.select('circle').style({
- stroke: 'grey',
- fill: 'none'
- });
- chartGroup.on('mousemove.radial-guide', function(d, i) {
- var r = µ.util.getMousePos(backgroundCircle).radius;
- angularGuideCircle.attr({
- r: r
- }).style({
- opacity: .5
- });
- radialValue = radialScale.invert(µ.util.getMousePos(backgroundCircle).radius);
- var pos = µ.util.convertToCartesian(r, axisConfig.radialAxis.orientation);
- radialTooltip.text(µ.util.round(radialValue)).move([ pos[0] + chartCenter[0], pos[1] + chartCenter[1] ]);
- }).on('mouseout.radial-guide', function(d, i) {
- angularGuideCircle.style({
- opacity: 0
- });
- geometryTooltip.hide();
- angularTooltip.hide();
- radialTooltip.hide();
- });
- svg.selectAll('.geometry-group .mark').on('mouseover.tooltip', function(d, i) {
- var el = d3.select(this);
- var color = this.style.fill;
- var newColor = 'black';
- var opacity = this.style.opacity || 1;
- el.attr({
- 'data-opacity': opacity
- });
- if (color && color !== 'none') {
- el.attr({
- 'data-fill': color
- });
- newColor = d3.hsl(color).darker().toString();
- el.style({
- fill: newColor,
- opacity: 1
- });
- var textData = {
- t: µ.util.round(d[0]),
- r: µ.util.round(d[1])
- };
- if (isOrdinal) textData.t = ticks[d[0]];
- var text = 't: ' + textData.t + ', r: ' + textData.r;
- var bbox = this.getBoundingClientRect();
- var svgBBox = svg.node().getBoundingClientRect();
- var pos = [ bbox.left + bbox.width / 2 - centeringOffset[0] - svgBBox.left, bbox.top + bbox.height / 2 - centeringOffset[1] - svgBBox.top ];
- geometryTooltip.config({
- color: newColor
- }).text(text);
- geometryTooltip.move(pos);
- } else {
- color = this.style.stroke || 'black';
- el.attr({
- 'data-stroke': color
- });
- newColor = d3.hsl(color).darker().toString();
- el.style({
- stroke: newColor,
- opacity: 1
- });
- }
- }).on('mousemove.tooltip', function(d, i) {
- if (d3.event.which != 0) return false;
- if (d3.select(this).attr('data-fill')) geometryTooltip.show();
- }).on('mouseout.tooltip', function(d, i) {
- geometryTooltip.hide();
- var el = d3.select(this);
- var fillColor = el.attr('data-fill');
- if (fillColor) el.style({
- fill: fillColor,
- opacity: el.attr('data-opacity')
- }); else el.style({
- stroke: el.attr('data-stroke'),
- opacity: el.attr('data-opacity')
- });
- });
- });
- return exports;
- }
- exports.render = function(_container) {
- render(_container);
- return this;
- };
- exports.config = function(_x) {
- if (!arguments.length) return config;
- var xClone = µ.util.cloneJson(_x);
- xClone.data.forEach(function(d, i) {
- if (!config.data[i]) config.data[i] = {};
- extendDeepAll(config.data[i], µ.Axis.defaultConfig().data[0]);
- extendDeepAll(config.data[i], d);
- });
- extendDeepAll(config.layout, µ.Axis.defaultConfig().layout);
- extendDeepAll(config.layout, xClone.layout);
- return this;
- };
- exports.getLiveConfig = function() {
- return liveConfig;
- };
- exports.getinputConfig = function() {
- return inputConfig;
- };
- exports.radialScale = function(_x) {
- return radialScale;
- };
- exports.angularScale = function(_x) {
- return angularScale;
- };
- exports.svg = function() {
- return svg;
- };
- d3.rebind(exports, dispatch, 'on');
- return exports;
- };
-
- µ.Axis.defaultConfig = function(d, i) {
- var config = {
- data: [ {
- t: [ 1, 2, 3, 4 ],
- r: [ 10, 11, 12, 13 ],
- name: 'Line1',
- geometry: 'LinePlot',
- color: null,
- strokeDash: 'solid',
- strokeColor: null,
- strokeSize: '1',
- visibleInLegend: true,
- opacity: 1
- } ],
- layout: {
- defaultColorRange: d3.scale.category10().range(),
- title: null,
- height: 450,
- width: 500,
- margin: {
- top: 40,
- right: 40,
- bottom: 40,
- left: 40
- },
- font: {
- size: 12,
- color: 'gray',
- outlineColor: 'white',
- family: 'Tahoma, sans-serif'
- },
- direction: 'clockwise',
- orientation: 0,
- labelOffset: 10,
- radialAxis: {
- domain: null,
- orientation: -45,
- ticksSuffix: '',
- visible: true,
- gridLinesVisible: true,
- tickOrientation: 'horizontal',
- rewriteTicks: null
- },
- angularAxis: {
- domain: [ 0, 360 ],
- ticksSuffix: '',
- visible: true,
- gridLinesVisible: true,
- labelsVisible: true,
- tickOrientation: 'horizontal',
- rewriteTicks: null,
- ticksCount: null,
- ticksStep: null
- },
- minorTicks: 0,
- tickLength: null,
- tickColor: 'silver',
- minorTickColor: '#eee',
- backgroundColor: 'none',
- needsEndSpacing: null,
- showLegend: true,
- legend: {
- reverseOrder: false
- },
- opacity: 1
- }
- };
- return config;
- };
-
- µ.util = {};
-
- µ.DATAEXTENT = 'dataExtent';
-
- µ.AREA = 'AreaChart';
-
- µ.LINE = 'LinePlot';
-
- µ.DOT = 'DotPlot';
-
- µ.BAR = 'BarChart';
-
- µ.util._override = function(_objA, _objB) {
- for (var x in _objA) if (x in _objB) _objB[x] = _objA[x];
- };
-
- µ.util._extend = function(_objA, _objB) {
- for (var x in _objA) _objB[x] = _objA[x];
- };
-
- µ.util._rndSnd = function() {
- return Math.random() * 2 - 1 + (Math.random() * 2 - 1) + (Math.random() * 2 - 1);
- };
-
- µ.util.dataFromEquation2 = function(_equation, _step) {
- var step = _step || 6;
- var data = d3.range(0, 360 + step, step).map(function(deg, index) {
- var theta = deg * Math.PI / 180;
- var radius = _equation(theta);
- return [ deg, radius ];
- });
- return data;
- };
-
- µ.util.dataFromEquation = function(_equation, _step, _name) {
- var step = _step || 6;
- var t = [], r = [];
- d3.range(0, 360 + step, step).forEach(function(deg, index) {
- var theta = deg * Math.PI / 180;
- var radius = _equation(theta);
- t.push(deg);
- r.push(radius);
- });
- var result = {
- t: t,
- r: r
- };
- if (_name) result.name = _name;
- return result;
- };
-
- µ.util.ensureArray = function(_val, _count) {
- if (typeof _val === 'undefined') return null;
- var arr = [].concat(_val);
- return d3.range(_count).map(function(d, i) {
- return arr[i] || arr[0];
- });
- };
-
- µ.util.fillArrays = function(_obj, _valueNames, _count) {
- _valueNames.forEach(function(d, i) {
- _obj[d] = µ.util.ensureArray(_obj[d], _count);
- });
- return _obj;
- };
-
- µ.util.cloneJson = function(json) {
- return JSON.parse(JSON.stringify(json));
- };
-
- µ.util.validateKeys = function(obj, keys) {
- if (typeof keys === 'string') keys = keys.split('.');
- var next = keys.shift();
- return obj[next] && (!keys.length || objHasKeys(obj[next], keys));
- };
-
- µ.util.sumArrays = function(a, b) {
- return d3.zip(a, b).map(function(d, i) {
- return d3.sum(d);
- });
- };
-
- µ.util.arrayLast = function(a) {
- return a[a.length - 1];
- };
-
- µ.util.arrayEqual = function(a, b) {
- var i = Math.max(a.length, b.length, 1);
- while (i-- >= 0 && a[i] === b[i]) ;
- return i === -2;
- };
-
- µ.util.flattenArray = function(arr) {
- var r = [];
- while (!µ.util.arrayEqual(r, arr)) {
- r = arr;
- arr = [].concat.apply([], arr);
- }
- return arr;
- };
-
- µ.util.deduplicate = function(arr) {
- return arr.filter(function(v, i, a) {
- return a.indexOf(v) == i;
- });
- };
-
- µ.util.convertToCartesian = function(radius, theta) {
- var thetaRadians = theta * Math.PI / 180;
- var x = radius * Math.cos(thetaRadians);
- var y = radius * Math.sin(thetaRadians);
- return [ x, y ];
- };
-
- µ.util.round = function(_value, _digits) {
- var digits = _digits || 2;
- var mult = Math.pow(10, digits);
- return Math.round(_value * mult) / mult;
- };
-
- µ.util.getMousePos = function(_referenceElement) {
- var mousePos = d3.mouse(_referenceElement.node());
- var mouseX = mousePos[0];
- var mouseY = mousePos[1];
- var mouse = {};
- mouse.x = mouseX;
- mouse.y = mouseY;
- mouse.pos = mousePos;
- mouse.angle = (Math.atan2(mouseY, mouseX) + Math.PI) * 180 / Math.PI;
- mouse.radius = Math.sqrt(mouseX * mouseX + mouseY * mouseY);
- return mouse;
- };
-
- µ.util.duplicatesCount = function(arr) {
- var uniques = {}, val;
- var dups = {};
- for (var i = 0, len = arr.length; i < len; i++) {
- val = arr[i];
- if (val in uniques) {
- uniques[val]++;
- dups[val] = uniques[val];
- } else {
- uniques[val] = 1;
- }
- }
- return dups;
- };
-
- µ.util.duplicates = function(arr) {
- return Object.keys(µ.util.duplicatesCount(arr));
- };
-
- µ.util.translator = function(obj, sourceBranch, targetBranch, reverse) {
- if (reverse) {
- var targetBranchCopy = targetBranch.slice();
- targetBranch = sourceBranch;
- sourceBranch = targetBranchCopy;
- }
- var value = sourceBranch.reduce(function(previousValue, currentValue) {
- if (typeof previousValue != 'undefined') return previousValue[currentValue];
- }, obj);
- if (typeof value === 'undefined') return;
- sourceBranch.reduce(function(previousValue, currentValue, index) {
- if (typeof previousValue == 'undefined') return;
- if (index === sourceBranch.length - 1) delete previousValue[currentValue];
- return previousValue[currentValue];
- }, obj);
- targetBranch.reduce(function(previousValue, currentValue, index) {
- if (typeof previousValue[currentValue] === 'undefined') previousValue[currentValue] = {};
- if (index === targetBranch.length - 1) previousValue[currentValue] = value;
- return previousValue[currentValue];
- }, obj);
- };
-
- µ.PolyChart = function module() {
- var config = [ µ.PolyChart.defaultConfig() ];
- var dispatch = d3.dispatch('hover');
- var dashArray = {
- solid: 'none',
- dash: [ 5, 2 ],
- dot: [ 2, 5 ]
- };
- var colorScale;
- function exports() {
- var geometryConfig = config[0].geometryConfig;
- var container = geometryConfig.container;
- if (typeof container == 'string') container = d3.select(container);
- container.datum(config).each(function(_config, _index) {
- var isStack = !!_config[0].data.yStack;
- var data = _config.map(function(d, i) {
- if (isStack) return d3.zip(d.data.t[0], d.data.r[0], d.data.yStack[0]); else return d3.zip(d.data.t[0], d.data.r[0]);
- });
- var angularScale = geometryConfig.angularScale;
- var domainMin = geometryConfig.radialScale.domain()[0];
- var generator = {};
- generator.bar = function(d, i, pI) {
- var dataConfig = _config[pI].data;
- var h = geometryConfig.radialScale(d[1]) - geometryConfig.radialScale(0);
- var stackTop = geometryConfig.radialScale(d[2] || 0);
- var w = dataConfig.barWidth;
- d3.select(this).attr({
- 'class': 'mark bar',
- d: 'M' + [ [ h + stackTop, -w / 2 ], [ h + stackTop, w / 2 ], [ stackTop, w / 2 ], [ stackTop, -w / 2 ] ].join('L') + 'Z',
- transform: function(d, i) {
- return 'rotate(' + (geometryConfig.orientation + angularScale(d[0])) + ')';
- }
- });
- };
- generator.dot = function(d, i, pI) {
- var stackedData = d[2] ? [ d[0], d[1] + d[2] ] : d;
- var symbol = d3.svg.symbol().size(_config[pI].data.dotSize).type(_config[pI].data.dotType)(d, i);
- d3.select(this).attr({
- 'class': 'mark dot',
- d: symbol,
- transform: function(d, i) {
- var coord = convertToCartesian(getPolarCoordinates(stackedData));
- return 'translate(' + [ coord.x, coord.y ] + ')';
- }
- });
- };
- var line = d3.svg.line.radial().interpolate(_config[0].data.lineInterpolation).radius(function(d) {
- return geometryConfig.radialScale(d[1]);
- }).angle(function(d) {
- return geometryConfig.angularScale(d[0]) * Math.PI / 180;
- });
- generator.line = function(d, i, pI) {
- var lineData = d[2] ? data[pI].map(function(d, i) {
- return [ d[0], d[1] + d[2] ];
- }) : data[pI];
- d3.select(this).each(generator['dot']).style({
- opacity: function(dB, iB) {
- return +_config[pI].data.dotVisible;
- },
- fill: markStyle.stroke(d, i, pI)
- }).attr({
- 'class': 'mark dot'
- });
- if (i > 0) return;
- var lineSelection = d3.select(this.parentNode).selectAll('path.line').data([ 0 ]);
- lineSelection.enter().insert('path');
- lineSelection.attr({
- 'class': 'line',
- d: line(lineData),
- transform: function(dB, iB) {
- return 'rotate(' + (geometryConfig.orientation + 90) + ')';
- },
- 'pointer-events': 'none'
- }).style({
- fill: function(dB, iB) {
- return markStyle.fill(d, i, pI);
- },
- 'fill-opacity': 0,
- stroke: function(dB, iB) {
- return markStyle.stroke(d, i, pI);
- },
- 'stroke-width': function(dB, iB) {
- return markStyle['stroke-width'](d, i, pI);
- },
- 'stroke-dasharray': function(dB, iB) {
- return markStyle['stroke-dasharray'](d, i, pI);
- },
- opacity: function(dB, iB) {
- return markStyle.opacity(d, i, pI);
- },
- display: function(dB, iB) {
- return markStyle.display(d, i, pI);
- }
- });
- };
- var angularRange = geometryConfig.angularScale.range();
- var triangleAngle = Math.abs(angularRange[1] - angularRange[0]) / data[0].length * Math.PI / 180;
- var arc = d3.svg.arc().startAngle(function(d) {
- return -triangleAngle / 2;
- }).endAngle(function(d) {
- return triangleAngle / 2;
- }).innerRadius(function(d) {
- return geometryConfig.radialScale(domainMin + (d[2] || 0));
- }).outerRadius(function(d) {
- return geometryConfig.radialScale(domainMin + (d[2] || 0)) + geometryConfig.radialScale(d[1]);
- });
- generator.arc = function(d, i, pI) {
- d3.select(this).attr({
- 'class': 'mark arc',
- d: arc,
- transform: function(d, i) {
- return 'rotate(' + (geometryConfig.orientation + angularScale(d[0]) + 90) + ')';
- }
- });
- };
- var markStyle = {
- fill: function(d, i, pI) {
- return _config[pI].data.color;
- },
- stroke: function(d, i, pI) {
- return _config[pI].data.strokeColor;
- },
- 'stroke-width': function(d, i, pI) {
- return _config[pI].data.strokeSize + 'px';
- },
- 'stroke-dasharray': function(d, i, pI) {
- return dashArray[_config[pI].data.strokeDash];
- },
- opacity: function(d, i, pI) {
- return _config[pI].data.opacity;
- },
- display: function(d, i, pI) {
- return typeof _config[pI].data.visible === 'undefined' || _config[pI].data.visible ? 'block' : 'none';
- }
- };
- var geometryLayer = d3.select(this).selectAll('g.layer').data(data);
- geometryLayer.enter().append('g').attr({
- 'class': 'layer'
- });
- var geometry = geometryLayer.selectAll('path.mark').data(function(d, i) {
- return d;
- });
- geometry.enter().append('path').attr({
- 'class': 'mark'
- });
- geometry.style(markStyle).each(generator[geometryConfig.geometryType]);
- geometry.exit().remove();
- geometryLayer.exit().remove();
- function getPolarCoordinates(d, i) {
- var r = geometryConfig.radialScale(d[1]);
- var t = (geometryConfig.angularScale(d[0]) + geometryConfig.orientation) * Math.PI / 180;
- return {
- r: r,
- t: t
- };
- }
- function convertToCartesian(polarCoordinates) {
- var x = polarCoordinates.r * Math.cos(polarCoordinates.t);
- var y = polarCoordinates.r * Math.sin(polarCoordinates.t);
- return {
- x: x,
- y: y
- };
- }
- });
- }
- exports.config = function(_x) {
- if (!arguments.length) return config;
- _x.forEach(function(d, i) {
- if (!config[i]) config[i] = {};
- extendDeepAll(config[i], µ.PolyChart.defaultConfig());
- extendDeepAll(config[i], d);
- });
- return this;
- };
- exports.getColorScale = function() {
- return colorScale;
- };
- d3.rebind(exports, dispatch, 'on');
- return exports;
- };
-
- µ.PolyChart.defaultConfig = function() {
- var config = {
- data: {
- name: 'geom1',
- t: [ [ 1, 2, 3, 4 ] ],
- r: [ [ 1, 2, 3, 4 ] ],
- dotType: 'circle',
- dotSize: 64,
- dotVisible: false,
- barWidth: 20,
- color: '#ffa500',
- strokeSize: 1,
- strokeColor: 'silver',
- strokeDash: 'solid',
- opacity: 1,
- index: 0,
- visible: true,
- visibleInLegend: true
- },
- geometryConfig: {
- geometry: 'LinePlot',
- geometryType: 'arc',
- direction: 'clockwise',
- orientation: 0,
- container: 'body',
- radialScale: null,
- angularScale: null,
- colorScale: d3.scale.category20()
- }
- };
- return config;
- };
-
- µ.BarChart = function module() {
- return µ.PolyChart();
- };
-
- µ.BarChart.defaultConfig = function() {
- var config = {
- geometryConfig: {
- geometryType: 'bar'
- }
- };
- return config;
- };
-
- µ.AreaChart = function module() {
- return µ.PolyChart();
- };
-
- µ.AreaChart.defaultConfig = function() {
- var config = {
- geometryConfig: {
- geometryType: 'arc'
- }
- };
- return config;
- };
-
- µ.DotPlot = function module() {
- return µ.PolyChart();
- };
-
- µ.DotPlot.defaultConfig = function() {
- var config = {
- geometryConfig: {
- geometryType: 'dot',
- dotType: 'circle'
- }
- };
- return config;
- };
-
- µ.LinePlot = function module() {
- return µ.PolyChart();
- };
-
- µ.LinePlot.defaultConfig = function() {
- var config = {
- geometryConfig: {
- geometryType: 'line'
- }
- };
- return config;
- };
-
- µ.Legend = function module() {
- var config = µ.Legend.defaultConfig();
- var dispatch = d3.dispatch('hover');
- function exports() {
- var legendConfig = config.legendConfig;
- var flattenData = config.data.map(function(d, i) {
- return [].concat(d).map(function(dB, iB) {
- var element = extendDeepAll({}, legendConfig.elements[i]);
- element.name = dB;
- element.color = [].concat(legendConfig.elements[i].color)[iB];
- return element;
- });
- });
- var data = d3.merge(flattenData);
- data = data.filter(function(d, i) {
- return legendConfig.elements[i] && (legendConfig.elements[i].visibleInLegend || typeof legendConfig.elements[i].visibleInLegend === 'undefined');
- });
- if (legendConfig.reverseOrder) data = data.reverse();
- var container = legendConfig.container;
- if (typeof container == 'string' || container.nodeName) container = d3.select(container);
- var colors = data.map(function(d, i) {
- return d.color;
- });
- var lineHeight = legendConfig.fontSize;
- var isContinuous = legendConfig.isContinuous == null ? typeof data[0] === 'number' : legendConfig.isContinuous;
- var height = isContinuous ? legendConfig.height : lineHeight * data.length;
- var legendContainerGroup = container.classed('legend-group', true);
- var svg = legendContainerGroup.selectAll('svg').data([ 0 ]);
- var svgEnter = svg.enter().append('svg').attr({
- width: 300,
- height: height + lineHeight,
- xmlns: 'http://www.w3.org/2000/svg',
- 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
- version: '1.1'
- });
- svgEnter.append('g').classed('legend-axis', true);
- svgEnter.append('g').classed('legend-marks', true);
- var dataNumbered = d3.range(data.length);
- var colorScale = d3.scale[isContinuous ? 'linear' : 'ordinal']().domain(dataNumbered).range(colors);
- var dataScale = d3.scale[isContinuous ? 'linear' : 'ordinal']().domain(dataNumbered)[isContinuous ? 'range' : 'rangePoints']([ 0, height ]);
- var shapeGenerator = function(_type, _size) {
- var squareSize = _size * 3;
- if (_type === 'line') {
- return 'M' + [ [ -_size / 2, -_size / 12 ], [ _size / 2, -_size / 12 ], [ _size / 2, _size / 12 ], [ -_size / 2, _size / 12 ] ] + 'Z';
- } else if (d3.svg.symbolTypes.indexOf(_type) != -1) return d3.svg.symbol().type(_type).size(squareSize)(); else return d3.svg.symbol().type('square').size(squareSize)();
- };
- if (isContinuous) {
- var gradient = svg.select('.legend-marks').append('defs').append('linearGradient').attr({
- id: 'grad1',
- x1: '0%',
- y1: '0%',
- x2: '0%',
- y2: '100%'
- }).selectAll('stop').data(colors);
- gradient.enter().append('stop');
- gradient.attr({
- offset: function(d, i) {
- return i / (colors.length - 1) * 100 + '%';
- }
- }).style({
- 'stop-color': function(d, i) {
- return d;
- }
- });
- svg.append('rect').classed('legend-mark', true).attr({
- height: legendConfig.height,
- width: legendConfig.colorBandWidth,
- fill: 'url(#grad1)'
- });
- } else {
- var legendElement = svg.select('.legend-marks').selectAll('path.legend-mark').data(data);
- legendElement.enter().append('path').classed('legend-mark', true);
- legendElement.attr({
- transform: function(d, i) {
- return 'translate(' + [ lineHeight / 2, dataScale(i) + lineHeight / 2 ] + ')';
- },
- d: function(d, i) {
- var symbolType = d.symbol;
- return shapeGenerator(symbolType, lineHeight);
- },
- fill: function(d, i) {
- return colorScale(i);
- }
- });
- legendElement.exit().remove();
- }
- var legendAxis = d3.svg.axis().scale(dataScale).orient('right');
- var axis = svg.select('g.legend-axis').attr({
- transform: 'translate(' + [ isContinuous ? legendConfig.colorBandWidth : lineHeight, lineHeight / 2 ] + ')'
- }).call(legendAxis);
- axis.selectAll('.domain').style({
- fill: 'none',
- stroke: 'none'
- });
- axis.selectAll('line').style({
- fill: 'none',
- stroke: isContinuous ? legendConfig.textColor : 'none'
- });
- axis.selectAll('text').style({
- fill: legendConfig.textColor,
- 'font-size': legendConfig.fontSize
- }).text(function(d, i) {
- return data[i].name;
- });
- return exports;
- }
- exports.config = function(_x) {
- if (!arguments.length) return config;
- extendDeepAll(config, _x);
- return this;
- };
- d3.rebind(exports, dispatch, 'on');
- return exports;
- };
-
- µ.Legend.defaultConfig = function(d, i) {
- var config = {
- data: [ 'a', 'b', 'c' ],
- legendConfig: {
- elements: [ {
- symbol: 'line',
- color: 'red'
- }, {
- symbol: 'square',
- color: 'yellow'
- }, {
- symbol: 'diamond',
- color: 'limegreen'
- } ],
- height: 150,
- colorBandWidth: 30,
- fontSize: 12,
- container: 'body',
- isContinuous: null,
- textColor: 'grey',
- reverseOrder: false
- }
- };
- return config;
- };
-
- µ.tooltipPanel = function() {
- var tooltipEl, tooltipTextEl, backgroundEl;
- var config = {
- container: null,
- hasTick: false,
- fontSize: 12,
- color: 'white',
- padding: 5
- };
- var id = 'tooltip-' + µ.tooltipPanel.uid++;
- var tickSize = 10;
- var exports = function() {
- tooltipEl = config.container.selectAll('g.' + id).data([ 0 ]);
- var tooltipEnter = tooltipEl.enter().append('g').classed(id, true).style({
- 'pointer-events': 'none',
- display: 'none'
- });
- backgroundEl = tooltipEnter.append('path').style({
- fill: 'white',
- 'fill-opacity': .9
- }).attr({
- d: 'M0 0'
- });
- tooltipTextEl = tooltipEnter.append('text').attr({
- dx: config.padding + tickSize,
- dy: +config.fontSize * .3
- });
- return exports;
- };
- exports.text = function(_text) {
- var l = d3.hsl(config.color).l;
- var strokeColor = l >= .5 ? '#aaa' : 'white';
- var fillColor = l >= .5 ? 'black' : 'white';
- var text = _text || '';
- tooltipTextEl.style({
- fill: fillColor,
- 'font-size': config.fontSize + 'px'
- }).text(text);
- var padding = config.padding;
- var bbox = tooltipTextEl.node().getBBox();
- var boxStyle = {
- fill: config.color,
- stroke: strokeColor,
- 'stroke-width': '2px'
- };
- var backGroundW = bbox.width + padding * 2 + tickSize;
- var backGroundH = bbox.height + padding * 2;
- backgroundEl.attr({
- d: 'M' + [ [ tickSize, -backGroundH / 2 ], [ tickSize, -backGroundH / 4 ], [ config.hasTick ? 0 : tickSize, 0 ], [ tickSize, backGroundH / 4 ], [ tickSize, backGroundH / 2 ], [ backGroundW, backGroundH / 2 ], [ backGroundW, -backGroundH / 2 ] ].join('L') + 'Z'
- }).style(boxStyle);
- tooltipEl.attr({
- transform: 'translate(' + [ tickSize, -backGroundH / 2 + padding * 2 ] + ')'
- });
- tooltipEl.style({
- display: 'block'
- });
- return exports;
- };
- exports.move = function(_pos) {
- if (!tooltipEl) return;
- tooltipEl.attr({
- transform: 'translate(' + [ _pos[0], _pos[1] ] + ')'
- }).style({
- display: 'block'
- });
- return exports;
- };
- exports.hide = function() {
- if (!tooltipEl) return;
- tooltipEl.style({
- display: 'none'
- });
- return exports;
- };
- exports.show = function() {
- if (!tooltipEl) return;
- tooltipEl.style({
- display: 'block'
- });
- return exports;
- };
- exports.config = function(_x) {
- extendDeepAll(config, _x);
- return exports;
- };
- return exports;
- };
-
- µ.tooltipPanel.uid = 1;
-
- µ.adapter = {};
-
- µ.adapter.plotly = function module() {
- var exports = {};
- exports.convert = function(_inputConfig, reverse) {
- var outputConfig = {};
- if (_inputConfig.data) {
- outputConfig.data = _inputConfig.data.map(function(d, i) {
- var r = extendDeepAll({}, d);
- var toTranslate = [
- [ r, [ 'marker', 'color' ], [ 'color' ] ],
- [ r, [ 'marker', 'opacity' ], [ 'opacity' ] ],
- [ r, [ 'marker', 'line', 'color' ], [ 'strokeColor' ] ],
- [ r, [ 'marker', 'line', 'dash' ], [ 'strokeDash' ] ],
- [ r, [ 'marker', 'line', 'width' ], [ 'strokeSize' ] ],
- [ r, [ 'marker', 'symbol' ], [ 'dotType' ] ],
- [ r, [ 'marker', 'size' ], [ 'dotSize' ] ],
- [ r, [ 'marker', 'barWidth' ], [ 'barWidth' ] ],
- [ r, [ 'line', 'interpolation' ], [ 'lineInterpolation' ] ],
- [ r, [ 'showlegend' ], [ 'visibleInLegend' ] ]
- ];
- toTranslate.forEach(function(d, i) {
- µ.util.translator.apply(null, d.concat(reverse));
- });
-
- if (!reverse) delete r.marker;
- if (reverse) delete r.groupId;
- if (!reverse) {
- if (r.type === 'scatter') {
- if (r.mode === 'lines') r.geometry = 'LinePlot'; else if (r.mode === 'markers') r.geometry = 'DotPlot'; else if (r.mode === 'lines+markers') {
- r.geometry = 'LinePlot';
- r.dotVisible = true;
- }
- } else if (r.type === 'area') r.geometry = 'AreaChart'; else if (r.type === 'bar') r.geometry = 'BarChart';
- delete r.mode;
- delete r.type;
- } else {
- if (r.geometry === 'LinePlot') {
- r.type = 'scatter';
- if (r.dotVisible === true) {
- delete r.dotVisible;
- r.mode = 'lines+markers';
- } else r.mode = 'lines';
- } else if (r.geometry === 'DotPlot') {
- r.type = 'scatter';
- r.mode = 'markers';
- } else if (r.geometry === 'AreaChart') r.type = 'area'; else if (r.geometry === 'BarChart') r.type = 'bar';
- delete r.geometry;
- }
- return r;
- });
- if (!reverse && _inputConfig.layout && _inputConfig.layout.barmode === 'stack') {
- var duplicates = µ.util.duplicates(outputConfig.data.map(function(d, i) {
- return d.geometry;
- }));
- outputConfig.data.forEach(function(d, i) {
- var idx = duplicates.indexOf(d.geometry);
- if (idx != -1) outputConfig.data[i].groupId = idx;
- });
- }
- }
- if (_inputConfig.layout) {
- var r = extendDeepAll({}, _inputConfig.layout);
- var toTranslate = [
- [ r, [ 'plot_bgcolor' ], [ 'backgroundColor' ] ],
- [ r, [ 'showlegend' ], [ 'showLegend' ] ],
- [ r, [ 'radialaxis' ], [ 'radialAxis' ] ],
- [ r, [ 'angularaxis' ], [ 'angularAxis' ] ],
- [ r.angularaxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
- [ r.angularaxis, [ 'showticklabels' ], [ 'labelsVisible' ] ],
- [ r.angularaxis, [ 'nticks' ], [ 'ticksCount' ] ],
- [ r.angularaxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
- [ r.angularaxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
- [ r.angularaxis, [ 'range' ], [ 'domain' ] ],
- [ r.angularaxis, [ 'endpadding' ], [ 'endPadding' ] ],
- [ r.radialaxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
- [ r.radialaxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
- [ r.radialaxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
- [ r.radialaxis, [ 'range' ], [ 'domain' ] ],
- [ r.angularAxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
- [ r.angularAxis, [ 'showticklabels' ], [ 'labelsVisible' ] ],
- [ r.angularAxis, [ 'nticks' ], [ 'ticksCount' ] ],
- [ r.angularAxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
- [ r.angularAxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
- [ r.angularAxis, [ 'range' ], [ 'domain' ] ],
- [ r.angularAxis, [ 'endpadding' ], [ 'endPadding' ] ],
- [ r.radialAxis, [ 'showline' ], [ 'gridLinesVisible' ] ],
- [ r.radialAxis, [ 'tickorientation' ], [ 'tickOrientation' ] ],
- [ r.radialAxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ],
- [ r.radialAxis, [ 'range' ], [ 'domain' ] ],
- [ r.font, [ 'outlinecolor' ], [ 'outlineColor' ] ],
- [ r.legend, [ 'traceorder' ], [ 'reverseOrder' ] ],
- [ r, [ 'labeloffset' ], [ 'labelOffset' ] ],
- [ r, [ 'defaultcolorrange' ], [ 'defaultColorRange' ] ]
- ];
- toTranslate.forEach(function(d, i) {
- µ.util.translator.apply(null, d.concat(reverse));
- });
-
- if (!reverse) {
- if (r.angularAxis && typeof r.angularAxis.ticklen !== 'undefined') r.tickLength = r.angularAxis.ticklen;
- if (r.angularAxis && typeof r.angularAxis.tickcolor !== 'undefined') r.tickColor = r.angularAxis.tickcolor;
- } else {
- if (typeof r.tickLength !== 'undefined') {
- r.angularaxis.ticklen = r.tickLength;
- delete r.tickLength;
- }
- if (r.tickColor) {
- r.angularaxis.tickcolor = r.tickColor;
- delete r.tickColor;
- }
- }
- if (r.legend && typeof r.legend.reverseOrder != 'boolean') {
- r.legend.reverseOrder = r.legend.reverseOrder != 'normal';
- }
- if (r.legend && typeof r.legend.traceorder == 'boolean') {
- r.legend.traceorder = r.legend.traceorder ? 'reversed' : 'normal';
- delete r.legend.reverseOrder;
- }
- if (r.margin && typeof r.margin.t != 'undefined') {
- var source = [ 't', 'r', 'b', 'l', 'pad' ];
- var target = [ 'top', 'right', 'bottom', 'left', 'pad' ];
- var margin = {};
- d3.entries(r.margin).forEach(function(dB, iB) {
- margin[target[source.indexOf(dB.key)]] = dB.value;
- });
- r.margin = margin;
- }
- if (reverse) {
- delete r.needsEndSpacing;
- delete r.minorTickColor;
- delete r.minorTicks;
- delete r.angularaxis.ticksCount;
- delete r.angularaxis.ticksCount;
- delete r.angularaxis.ticksStep;
- delete r.angularaxis.rewriteTicks;
- delete r.angularaxis.nticks;
- delete r.radialaxis.ticksCount;
- delete r.radialaxis.ticksCount;
- delete r.radialaxis.ticksStep;
- delete r.radialaxis.rewriteTicks;
- delete r.radialaxis.nticks;
- }
- outputConfig.layout = r;
- }
- return outputConfig;
- };
- return exports;
- };
-
- },{"../../../constants/alignment":146,"../../../lib":168,"d3":16}],250:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- /* eslint-disable new-cap */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var Lib = _dereq_('../../../lib');
- var Color = _dereq_('../../../components/color');
-
- var micropolar = _dereq_('./micropolar');
- var UndoManager = _dereq_('./undo_manager');
- var extendDeepAll = Lib.extendDeepAll;
-
- var manager = module.exports = {};
-
- manager.framework = function(_gd) {
- var config, previousConfigClone, plot, convertedInput, container;
- var undoManager = new UndoManager();
-
- function exports(_inputConfig, _container) {
- if(_container) container = _container;
- d3.select(d3.select(container).node().parentNode).selectAll('.svg-container>*:not(.chart-root)').remove();
-
- config = (!config) ?
- _inputConfig :
- extendDeepAll(config, _inputConfig);
-
- if(!plot) plot = micropolar.Axis();
- convertedInput = micropolar.adapter.plotly().convert(config);
- plot.config(convertedInput).render(container);
- _gd.data = config.data;
- _gd.layout = config.layout;
- manager.fillLayout(_gd);
- return config;
- }
- exports.isPolar = true;
- exports.svg = function() { return plot.svg(); };
- exports.getConfig = function() { return config; };
- exports.getLiveConfig = function() {
- return micropolar.adapter.plotly().convert(plot.getLiveConfig(), true);
- };
- exports.getLiveScales = function() { return {t: plot.angularScale(), r: plot.radialScale()}; };
- exports.setUndoPoint = function() {
- var that = this;
- var configClone = micropolar.util.cloneJson(config);
- (function(_configClone, _previousConfigClone) {
- undoManager.add({
- undo: function() {
- if(_previousConfigClone) that(_previousConfigClone);
- },
- redo: function() {
- that(_configClone);
- }
- });
- })(configClone, previousConfigClone);
- previousConfigClone = micropolar.util.cloneJson(configClone);
- };
- exports.undo = function() { undoManager.undo(); };
- exports.redo = function() { undoManager.redo(); };
- return exports;
- };
-
- manager.fillLayout = function(_gd) {
- var container = d3.select(_gd).selectAll('.plot-container');
- var paperDiv = container.selectAll('.svg-container');
- var paper = _gd.framework && _gd.framework.svg && _gd.framework.svg();
- var dflts = {
- width: 800,
- height: 600,
- paper_bgcolor: Color.background,
- _container: container,
- _paperdiv: paperDiv,
- _paper: paper
- };
-
- _gd._fullLayout = extendDeepAll(dflts, _gd.layout);
- };
-
- },{"../../../components/color":51,"../../../lib":168,"./micropolar":249,"./undo_manager":251,"d3":16}],251:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- // Modified from https://github.com/ArthurClemens/Javascript-Undo-Manager
- // Copyright (c) 2010-2013 Arthur Clemens, arthur@visiblearea.com
- module.exports = function UndoManager() {
- var undoCommands = [];
- var index = -1;
- var isExecuting = false;
- var callback;
-
- function execute(command, action) {
- if(!command) return this;
-
- isExecuting = true;
- command[action]();
- isExecuting = false;
-
- return this;
- }
-
- return {
- add: function(command) {
- if(isExecuting) return this;
- undoCommands.splice(index + 1, undoCommands.length - index);
- undoCommands.push(command);
- index = undoCommands.length - 1;
- return this;
- },
- setCallback: function(callbackFunc) { callback = callbackFunc; },
- undo: function() {
- var command = undoCommands[index];
- if(!command) return this;
- execute(command, 'undo');
- index -= 1;
- if(callback) callback(command.undo);
- return this;
- },
- redo: function() {
- var command = undoCommands[index + 1];
- if(!command) return this;
- execute(command, 'redo');
- index += 1;
- if(callback) callback(command.redo);
- return this;
- },
- clear: function() {
- undoCommands = [];
- index = -1;
- },
- hasUndo: function() { return index !== -1; },
- hasRedo: function() { return index < (undoCommands.length - 1); },
- getCommands: function() { return undoCommands; },
- getPreviousCommand: function() { return undoCommands[index - 1]; },
- getIndex: function() { return index; }
- };
- };
-
- },{}],252:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../lib');
- var Template = _dereq_('../plot_api/plot_template');
- var handleDomainDefaults = _dereq_('./domain').defaults;
-
-
- /**
- * Find and supply defaults to all subplots of a given type
- * This handles subplots that are contained within one container - so
- * gl3d, geo, ternary... but not 2d axes which have separate x and y axes
- * finds subplots, coerces their `domain` attributes, then calls the
- * given handleDefaults function to fill in everything else.
- *
- * layoutIn: the complete user-supplied input layout
- * layoutOut: the complete finished layout
- * fullData: the finished data array, used only to find subplots
- * opts: {
- * type: subplot type string
- * attributes: subplot attributes object
- * partition: 'x' or 'y', which direction to divide domain space by default
- * (default 'x', ie side-by-side subplots)
- * TODO: this option is only here because 3D and geo made opposite
- * choices in this regard previously and I didn't want to change it.
- * Instead we should do:
- * - something consistent
- * - something more square (4 cuts 2x2, 5/6 cuts 2x3, etc.)
- * - something that includes all subplot types in one arrangement,
- * now that we can have them together!
- * handleDefaults: function of (subplotLayoutIn, subplotLayoutOut, coerce, opts)
- * this opts object is passed through to handleDefaults, so attach any
- * additional items needed by this function here as well
- * }
- */
- module.exports = function handleSubplotDefaults(layoutIn, layoutOut, fullData, opts) {
- var subplotType = opts.type;
- var subplotAttributes = opts.attributes;
- var handleDefaults = opts.handleDefaults;
- var partition = opts.partition || 'x';
-
- var ids = layoutOut._subplots[subplotType];
- var idsLength = ids.length;
-
- var baseId = idsLength && ids[0].replace(/\d+$/, '');
-
- var subplotLayoutIn, subplotLayoutOut;
-
- function coerce(attr, dflt) {
- return Lib.coerce(subplotLayoutIn, subplotLayoutOut, subplotAttributes, attr, dflt);
- }
-
- for(var i = 0; i < idsLength; i++) {
- var id = ids[i];
-
- // ternary traces get a layout ternary for free!
- if(layoutIn[id]) subplotLayoutIn = layoutIn[id];
- else subplotLayoutIn = layoutIn[id] = {};
-
- subplotLayoutOut = Template.newContainer(layoutOut, id, baseId);
-
- // All subplot containers get a `uirevision` inheriting from the base.
- // Currently all subplots containers have some user interaction
- // attributes, but if we ever add one that doesn't, we would need an
- // option to skip this step.
- coerce('uirevision', layoutOut.uirevision);
-
- var dfltDomains = {};
- dfltDomains[partition] = [i / idsLength, (i + 1) / idsLength];
- handleDomainDefaults(subplotLayoutOut, layoutOut, coerce, dfltDomains);
-
- opts.id = id;
- handleDefaults(subplotLayoutIn, subplotLayoutOut, coerce, opts);
- }
- };
-
- },{"../lib":168,"../plot_api/plot_template":202,"./domain":238}],253:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Ternary = _dereq_('./ternary');
-
- var getSubplotCalcData = _dereq_('../../plots/get_data').getSubplotCalcData;
- var counterRegex = _dereq_('../../lib').counterRegex;
- var TERNARY = 'ternary';
-
- exports.name = TERNARY;
-
- var attr = exports.attr = 'subplot';
-
- exports.idRoot = TERNARY;
-
- exports.idRegex = exports.attrRegex = counterRegex(TERNARY);
-
- var attributes = exports.attributes = {};
- attributes[attr] = {
- valType: 'subplotid',
-
- dflt: 'ternary',
- editType: 'calc',
-
- };
-
- exports.layoutAttributes = _dereq_('./layout_attributes');
-
- exports.supplyLayoutDefaults = _dereq_('./layout_defaults');
-
- exports.plot = function plotTernary(gd) {
- var fullLayout = gd._fullLayout;
- var calcData = gd.calcdata;
- var ternaryIds = fullLayout._subplots[TERNARY];
-
- for(var i = 0; i < ternaryIds.length; i++) {
- var ternaryId = ternaryIds[i];
- var ternaryCalcData = getSubplotCalcData(calcData, TERNARY, ternaryId);
- var ternary = fullLayout[ternaryId]._subplot;
-
- // If ternary is not instantiated, create one!
- if(!ternary) {
- ternary = new Ternary({
- id: ternaryId,
- graphDiv: gd,
- container: fullLayout._ternarylayer.node()
- },
- fullLayout
- );
-
- fullLayout[ternaryId]._subplot = ternary;
- }
-
- ternary.plot(ternaryCalcData, fullLayout, gd._promises);
- }
- };
-
- exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
- var oldTernaryKeys = oldFullLayout._subplots[TERNARY] || [];
-
- for(var i = 0; i < oldTernaryKeys.length; i++) {
- var oldTernaryKey = oldTernaryKeys[i];
- var oldTernary = oldFullLayout[oldTernaryKey]._subplot;
-
- if(!newFullLayout[oldTernaryKey] && !!oldTernary) {
- oldTernary.plotContainer.remove();
- oldTernary.clipDef.remove();
- oldTernary.clipDefRelative.remove();
- oldTernary.layers['a-title'].remove();
- oldTernary.layers['b-title'].remove();
- oldTernary.layers['c-title'].remove();
- }
- }
- };
-
- },{"../../lib":168,"../../plots/get_data":241,"./layout_attributes":254,"./layout_defaults":255,"./ternary":256}],254:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var colorAttrs = _dereq_('../../components/color/attributes');
- var domainAttrs = _dereq_('../domain').attributes;
- var axesAttrs = _dereq_('../cartesian/layout_attributes');
-
- var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- var ternaryAxesAttrs = {
- title: axesAttrs.title,
- color: axesAttrs.color,
- // ticks
- tickmode: axesAttrs.tickmode,
- nticks: extendFlat({}, axesAttrs.nticks, {dflt: 6, min: 1}),
- tick0: axesAttrs.tick0,
- dtick: axesAttrs.dtick,
- tickvals: axesAttrs.tickvals,
- ticktext: axesAttrs.ticktext,
- ticks: axesAttrs.ticks,
- ticklen: axesAttrs.ticklen,
- tickwidth: axesAttrs.tickwidth,
- tickcolor: axesAttrs.tickcolor,
- showticklabels: axesAttrs.showticklabels,
- showtickprefix: axesAttrs.showtickprefix,
- tickprefix: axesAttrs.tickprefix,
- showticksuffix: axesAttrs.showticksuffix,
- ticksuffix: axesAttrs.ticksuffix,
- showexponent: axesAttrs.showexponent,
- exponentformat: axesAttrs.exponentformat,
- separatethousands: axesAttrs.separatethousands,
- tickfont: axesAttrs.tickfont,
- tickangle: axesAttrs.tickangle,
- tickformat: axesAttrs.tickformat,
- tickformatstops: axesAttrs.tickformatstops,
- hoverformat: axesAttrs.hoverformat,
- // lines and grids
- showline: extendFlat({}, axesAttrs.showline, {dflt: true}),
- linecolor: axesAttrs.linecolor,
- linewidth: axesAttrs.linewidth,
- showgrid: extendFlat({}, axesAttrs.showgrid, {dflt: true}),
- gridcolor: axesAttrs.gridcolor,
- gridwidth: axesAttrs.gridwidth,
- layer: axesAttrs.layer,
- // range
- min: {
- valType: 'number',
- dflt: 0,
-
- min: 0,
-
- },
- _deprecated: {
- title: axesAttrs._deprecated.title,
- titlefont: axesAttrs._deprecated.titlefont
- }
- };
-
- var attrs = module.exports = overrideAll({
- domain: domainAttrs({name: 'ternary'}),
-
- bgcolor: {
- valType: 'color',
-
- dflt: colorAttrs.background,
-
- },
- sum: {
- valType: 'number',
-
- dflt: 1,
- min: 0,
-
- },
- aaxis: ternaryAxesAttrs,
- baxis: ternaryAxesAttrs,
- caxis: ternaryAxesAttrs
- }, 'plot', 'from-root');
-
- // set uirevisions outside of `overrideAll` so we can get `editType: none`
- attrs.uirevision = {
- valType: 'any',
-
- editType: 'none',
-
- };
-
- attrs.aaxis.uirevision = attrs.baxis.uirevision = attrs.caxis.uirevision = {
- valType: 'any',
-
- editType: 'none',
-
- };
-
- },{"../../components/color/attributes":50,"../../lib/extend":162,"../../plot_api/edit_types":195,"../cartesian/layout_attributes":225,"../domain":238}],255:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Color = _dereq_('../../components/color');
- var Template = _dereq_('../../plot_api/plot_template');
- var Lib = _dereq_('../../lib');
-
- var handleSubplotDefaults = _dereq_('../subplot_defaults');
- var handleTickLabelDefaults = _dereq_('../cartesian/tick_label_defaults');
- var handleTickMarkDefaults = _dereq_('../cartesian/tick_mark_defaults');
- var handleTickValueDefaults = _dereq_('../cartesian/tick_value_defaults');
- var handleLineGridDefaults = _dereq_('../cartesian/line_grid_defaults');
- var layoutAttributes = _dereq_('./layout_attributes');
-
- var axesNames = ['aaxis', 'baxis', 'caxis'];
-
- module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
- handleSubplotDefaults(layoutIn, layoutOut, fullData, {
- type: 'ternary',
- attributes: layoutAttributes,
- handleDefaults: handleTernaryDefaults,
- font: layoutOut.font,
- paper_bgcolor: layoutOut.paper_bgcolor
- });
- };
-
- function handleTernaryDefaults(ternaryLayoutIn, ternaryLayoutOut, coerce, options) {
- var bgColor = coerce('bgcolor');
- var sum = coerce('sum');
- options.bgColor = Color.combine(bgColor, options.paper_bgcolor);
- var axName, containerIn, containerOut;
-
- // TODO: allow most (if not all) axis attributes to be set
- // in the outer container and used as defaults in the individual axes?
-
- for(var j = 0; j < axesNames.length; j++) {
- axName = axesNames[j];
- containerIn = ternaryLayoutIn[axName] || {};
- containerOut = Template.newContainer(ternaryLayoutOut, axName);
- containerOut._name = axName;
-
- handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut);
- }
-
- // if the min values contradict each other, set them all to default (0)
- // and delete *all* the inputs so the user doesn't get confused later by
- // changing one and having them all change.
- var aaxis = ternaryLayoutOut.aaxis;
- var baxis = ternaryLayoutOut.baxis;
- var caxis = ternaryLayoutOut.caxis;
- if(aaxis.min + baxis.min + caxis.min >= sum) {
- aaxis.min = 0;
- baxis.min = 0;
- caxis.min = 0;
- if(ternaryLayoutIn.aaxis) delete ternaryLayoutIn.aaxis.min;
- if(ternaryLayoutIn.baxis) delete ternaryLayoutIn.baxis.min;
- if(ternaryLayoutIn.caxis) delete ternaryLayoutIn.caxis.min;
- }
- }
-
- function handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut) {
- var axAttrs = layoutAttributes[containerOut._name];
-
- function coerce(attr, dflt) {
- return Lib.coerce(containerIn, containerOut, axAttrs, attr, dflt);
- }
-
- coerce('uirevision', ternaryLayoutOut.uirevision);
-
- containerOut.type = 'linear'; // no other types allowed for ternary
-
- var dfltColor = coerce('color');
- // if axis.color was provided, use it for fonts too; otherwise,
- // inherit from global font color in case that was provided.
- var dfltFontColor = (dfltColor !== axAttrs.color.dflt) ? dfltColor : options.font.color;
-
- var axName = containerOut._name;
- var letterUpper = axName.charAt(0).toUpperCase();
- var dfltTitle = 'Component ' + letterUpper;
-
- var title = coerce('title.text', dfltTitle);
- containerOut._hovertitle = title === dfltTitle ? title : letterUpper;
-
- Lib.coerceFont(coerce, 'title.font', {
- family: options.font.family,
- size: Math.round(options.font.size * 1.2),
- color: dfltFontColor
- });
-
- // range is just set by 'min' - max is determined by the other axes mins
- coerce('min');
-
- handleTickValueDefaults(containerIn, containerOut, coerce, 'linear');
- handleTickLabelDefaults(containerIn, containerOut, coerce, 'linear', {});
- handleTickMarkDefaults(containerIn, containerOut, coerce,
- { outerTicks: true });
-
- var showTickLabels = coerce('showticklabels');
- if(showTickLabels) {
- Lib.coerceFont(coerce, 'tickfont', {
- family: options.font.family,
- size: options.font.size,
- color: dfltFontColor
- });
- coerce('tickangle');
- coerce('tickformat');
- }
-
- handleLineGridDefaults(containerIn, containerOut, coerce, {
- dfltColor: dfltColor,
- bgColor: options.bgColor,
- // default grid color is darker here (60%, vs cartesian default ~91%)
- // because the grid is not square so the eye needs heavier cues to follow
- blend: 60,
- showLine: true,
- showGrid: true,
- noZeroLine: true,
- attributes: axAttrs
- });
-
- coerce('hoverformat');
- coerce('layer');
- }
-
- },{"../../components/color":51,"../../lib":168,"../../plot_api/plot_template":202,"../cartesian/line_grid_defaults":227,"../cartesian/tick_label_defaults":232,"../cartesian/tick_mark_defaults":233,"../cartesian/tick_value_defaults":234,"../subplot_defaults":252,"./layout_attributes":254}],256:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var tinycolor = _dereq_('tinycolor2');
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var _ = Lib._;
- var Color = _dereq_('../../components/color');
- var Drawing = _dereq_('../../components/drawing');
- var setConvert = _dereq_('../cartesian/set_convert');
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
- var Plots = _dereq_('../plots');
- var Axes = _dereq_('../cartesian/axes');
- var dragElement = _dereq_('../../components/dragelement');
- var Fx = _dereq_('../../components/fx');
- var Titles = _dereq_('../../components/titles');
- var prepSelect = _dereq_('../cartesian/select').prepSelect;
- var selectOnClick = _dereq_('../cartesian/select').selectOnClick;
- var clearSelect = _dereq_('../cartesian/select').clearSelect;
- var constants = _dereq_('../cartesian/constants');
-
- function Ternary(options, fullLayout) {
- this.id = options.id;
- this.graphDiv = options.graphDiv;
- this.init(fullLayout);
- this.makeFramework(fullLayout);
-
- // unfortunately, we have to keep track of some axis tick settings
- // as ternary subplots do not implement the 'ticks' editType
- this.aTickLayout = null;
- this.bTickLayout = null;
- this.cTickLayout = null;
- }
-
- module.exports = Ternary;
-
- var proto = Ternary.prototype;
-
- proto.init = function(fullLayout) {
- this.container = fullLayout._ternarylayer;
- this.defs = fullLayout._defs;
- this.layoutId = fullLayout._uid;
- this.traceHash = {};
- this.layers = {};
- };
-
- proto.plot = function(ternaryCalcData, fullLayout) {
- var _this = this;
- var ternaryLayout = fullLayout[_this.id];
- var graphSize = fullLayout._size;
-
- _this._hasClipOnAxisFalse = false;
- for(var i = 0; i < ternaryCalcData.length; i++) {
- var trace = ternaryCalcData[i][0].trace;
-
- if(trace.cliponaxis === false) {
- _this._hasClipOnAxisFalse = true;
- break;
- }
- }
-
- _this.updateLayers(ternaryLayout);
- _this.adjustLayout(ternaryLayout, graphSize);
- Plots.generalUpdatePerTraceModule(_this.graphDiv, _this, ternaryCalcData, ternaryLayout);
- _this.layers.plotbg.select('path').call(Color.fill, ternaryLayout.bgcolor);
- };
-
- proto.makeFramework = function(fullLayout) {
- var _this = this;
- var gd = _this.graphDiv;
- var ternaryLayout = fullLayout[_this.id];
-
- var clipId = _this.clipId = 'clip' + _this.layoutId + _this.id;
- var clipIdRelative = _this.clipIdRelative = 'clip-relative' + _this.layoutId + _this.id;
-
- // clippath for this ternary subplot
- _this.clipDef = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipId, function(s) {
- s.append('path').attr('d', 'M0,0Z');
- });
-
- // 'relative' clippath (i.e. no translation) for this ternary subplot
- _this.clipDefRelative = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipIdRelative, function(s) {
- s.append('path').attr('d', 'M0,0Z');
- });
-
- // container for everything in this ternary subplot
- _this.plotContainer = Lib.ensureSingle(_this.container, 'g', _this.id);
- _this.updateLayers(ternaryLayout);
-
- Drawing.setClipUrl(_this.layers.backplot, clipId, gd);
- Drawing.setClipUrl(_this.layers.grids, clipId, gd);
- };
-
- proto.updateLayers = function(ternaryLayout) {
- var _this = this;
- var layers = _this.layers;
-
- // inside that container, we have one container for the data, and
- // one each for the three axes around it.
-
- var plotLayers = ['draglayer', 'plotbg', 'backplot', 'grids'];
-
- if(ternaryLayout.aaxis.layer === 'below traces') {
- plotLayers.push('aaxis', 'aline');
- }
- if(ternaryLayout.baxis.layer === 'below traces') {
- plotLayers.push('baxis', 'bline');
- }
- if(ternaryLayout.caxis.layer === 'below traces') {
- plotLayers.push('caxis', 'cline');
- }
-
- plotLayers.push('frontplot');
-
- if(ternaryLayout.aaxis.layer === 'above traces') {
- plotLayers.push('aaxis', 'aline');
- }
- if(ternaryLayout.baxis.layer === 'above traces') {
- plotLayers.push('baxis', 'bline');
- }
- if(ternaryLayout.caxis.layer === 'above traces') {
- plotLayers.push('caxis', 'cline');
- }
-
- var toplevel = _this.plotContainer.selectAll('g.toplevel')
- .data(plotLayers, String);
-
- var grids = ['agrid', 'bgrid', 'cgrid'];
-
- toplevel.enter().append('g')
- .attr('class', function(d) { return 'toplevel ' + d; })
- .each(function(d) {
- var s = d3.select(this);
- layers[d] = s;
-
- // containers for different trace types.
- // NOTE - this is different from cartesian, where all traces
- // are in front of grids. Here I'm putting maps behind the grids
- // so the grids will always be visible if they're requested.
- // Perhaps we want that for cartesian too?
- if(d === 'frontplot') {
- s.append('g').classed('scatterlayer', true);
- } else if(d === 'backplot') {
- s.append('g').classed('maplayer', true);
- } else if(d === 'plotbg') {
- s.append('path').attr('d', 'M0,0Z');
- } else if(d === 'aline' || d === 'bline' || d === 'cline') {
- s.append('path');
- } else if(d === 'grids') {
- grids.forEach(function(d) {
- layers[d] = s.append('g').classed('grid ' + d, true);
- });
- }
- });
-
- toplevel.order();
- };
-
- var whRatio = Math.sqrt(4 / 3);
-
- proto.adjustLayout = function(ternaryLayout, graphSize) {
- var _this = this;
- var domain = ternaryLayout.domain;
- var xDomainCenter = (domain.x[0] + domain.x[1]) / 2;
- var yDomainCenter = (domain.y[0] + domain.y[1]) / 2;
- var xDomain = domain.x[1] - domain.x[0];
- var yDomain = domain.y[1] - domain.y[0];
- var wmax = xDomain * graphSize.w;
- var hmax = yDomain * graphSize.h;
- var sum = ternaryLayout.sum;
- var amin = ternaryLayout.aaxis.min;
- var bmin = ternaryLayout.baxis.min;
- var cmin = ternaryLayout.caxis.min;
-
- var x0, y0, w, h, xDomainFinal, yDomainFinal;
-
- if(wmax > whRatio * hmax) {
- h = hmax;
- w = h * whRatio;
- }
- else {
- w = wmax;
- h = w / whRatio;
- }
-
- xDomainFinal = xDomain * w / wmax;
- yDomainFinal = yDomain * h / hmax;
-
- x0 = graphSize.l + graphSize.w * xDomainCenter - w / 2;
- y0 = graphSize.t + graphSize.h * (1 - yDomainCenter) - h / 2;
-
- _this.x0 = x0;
- _this.y0 = y0;
- _this.w = w;
- _this.h = h;
- _this.sum = sum;
-
- // set up the x and y axis objects we'll use to lay out the points
- _this.xaxis = {
- type: 'linear',
- range: [amin + 2 * cmin - sum, sum - amin - 2 * bmin],
- domain: [
- xDomainCenter - xDomainFinal / 2,
- xDomainCenter + xDomainFinal / 2
- ],
- _id: 'x'
- };
- setConvert(_this.xaxis, _this.graphDiv._fullLayout);
- _this.xaxis.setScale();
- _this.xaxis.isPtWithinRange = function(d) {
- return (
- d.a >= _this.aaxis.range[0] &&
- d.a <= _this.aaxis.range[1] &&
- d.b >= _this.baxis.range[1] &&
- d.b <= _this.baxis.range[0] &&
- d.c >= _this.caxis.range[1] &&
- d.c <= _this.caxis.range[0]
- );
- };
-
- _this.yaxis = {
- type: 'linear',
- range: [amin, sum - bmin - cmin],
- domain: [
- yDomainCenter - yDomainFinal / 2,
- yDomainCenter + yDomainFinal / 2
- ],
- _id: 'y'
- };
- setConvert(_this.yaxis, _this.graphDiv._fullLayout);
- _this.yaxis.setScale();
- _this.yaxis.isPtWithinRange = function() { return true; };
-
- // set up the modified axes for tick drawing
- var yDomain0 = _this.yaxis.domain[0];
-
- // aaxis goes up the left side. Set it up as a y axis, but with
- // fictitious angles and domain, but then rotate and translate
- // it into place at the end
- var aaxis = _this.aaxis = extendFlat({}, ternaryLayout.aaxis, {
- range: [amin, sum - bmin - cmin],
- side: 'left',
- // tickangle = 'auto' means 0 anyway for a y axis, need to coerce to 0 here
- // so we can shift by 30.
- tickangle: (+ternaryLayout.aaxis.tickangle || 0) - 30,
- domain: [yDomain0, yDomain0 + yDomainFinal * whRatio],
- anchor: 'free',
- position: 0,
- _id: 'y',
- _length: w
- });
- setConvert(aaxis, _this.graphDiv._fullLayout);
- aaxis.setScale();
-
- // baxis goes across the bottom (backward). We can set it up as an x axis
- // without any enclosing transformation.
- var baxis = _this.baxis = extendFlat({}, ternaryLayout.baxis, {
- range: [sum - amin - cmin, bmin],
- side: 'bottom',
- domain: _this.xaxis.domain,
- anchor: 'free',
- position: 0,
- _id: 'x',
- _length: w
- });
- setConvert(baxis, _this.graphDiv._fullLayout);
- baxis.setScale();
-
- // caxis goes down the right side. Set it up as a y axis, with
- // post-transformation similar to aaxis
- var caxis = _this.caxis = extendFlat({}, ternaryLayout.caxis, {
- range: [sum - amin - bmin, cmin],
- side: 'right',
- tickangle: (+ternaryLayout.caxis.tickangle || 0) + 30,
- domain: [yDomain0, yDomain0 + yDomainFinal * whRatio],
- anchor: 'free',
- position: 0,
- _id: 'y',
- _length: w
- });
- setConvert(caxis, _this.graphDiv._fullLayout);
- caxis.setScale();
-
- var triangleClip = 'M' + x0 + ',' + (y0 + h) + 'h' + w + 'l-' + (w / 2) + ',-' + h + 'Z';
- _this.clipDef.select('path').attr('d', triangleClip);
- _this.layers.plotbg.select('path').attr('d', triangleClip);
-
- var triangleClipRelative = 'M0,' + h + 'h' + w + 'l-' + (w / 2) + ',-' + h + 'Z';
- _this.clipDefRelative.select('path').attr('d', triangleClipRelative);
-
- var plotTransform = 'translate(' + x0 + ',' + y0 + ')';
- _this.plotContainer.selectAll('.scatterlayer,.maplayer')
- .attr('transform', plotTransform);
-
- _this.clipDefRelative.select('path').attr('transform', null);
-
- // TODO: shift axes to accommodate linewidth*sin(30) tick mark angle
-
- // TODO: there's probably an easier way to handle these translations/offsets now...
- var bTransform = 'translate(' + (x0 - baxis._offset) + ',' + (y0 + h) + ')';
-
- _this.layers.baxis.attr('transform', bTransform);
- _this.layers.bgrid.attr('transform', bTransform);
-
- var aTransform = 'translate(' + (x0 + w / 2) + ',' + y0 +
- ')rotate(30)translate(0,' + -aaxis._offset + ')';
- _this.layers.aaxis.attr('transform', aTransform);
- _this.layers.agrid.attr('transform', aTransform);
-
- var cTransform = 'translate(' + (x0 + w / 2) + ',' + y0 +
- ')rotate(-30)translate(0,' + -caxis._offset + ')';
- _this.layers.caxis.attr('transform', cTransform);
- _this.layers.cgrid.attr('transform', cTransform);
-
- _this.drawAxes(true);
-
- _this.layers.aline.select('path')
- .attr('d', aaxis.showline ?
- 'M' + x0 + ',' + (y0 + h) + 'l' + (w / 2) + ',-' + h : 'M0,0')
- .call(Color.stroke, aaxis.linecolor || '#000')
- .style('stroke-width', (aaxis.linewidth || 0) + 'px');
- _this.layers.bline.select('path')
- .attr('d', baxis.showline ?
- 'M' + x0 + ',' + (y0 + h) + 'h' + w : 'M0,0')
- .call(Color.stroke, baxis.linecolor || '#000')
- .style('stroke-width', (baxis.linewidth || 0) + 'px');
- _this.layers.cline.select('path')
- .attr('d', caxis.showline ?
- 'M' + (x0 + w / 2) + ',' + y0 + 'l' + (w / 2) + ',' + h : 'M0,0')
- .call(Color.stroke, caxis.linecolor || '#000')
- .style('stroke-width', (caxis.linewidth || 0) + 'px');
-
- if(!_this.graphDiv._context.staticPlot) {
- _this.initInteractions();
- }
-
- Drawing.setClipUrl(
- _this.layers.frontplot,
- _this._hasClipOnAxisFalse ? null : _this.clipId,
- _this.graphDiv
- );
- };
-
- proto.drawAxes = function(doTitles) {
- var _this = this;
- var gd = _this.graphDiv;
- var titlesuffix = _this.id.substr(7) + 'title';
- var layers = _this.layers;
- var aaxis = _this.aaxis;
- var baxis = _this.baxis;
- var caxis = _this.caxis;
-
- _this.drawAx(aaxis);
- _this.drawAx(baxis);
- _this.drawAx(caxis);
-
- if(doTitles) {
- var apad = Math.max(aaxis.showticklabels ? aaxis.tickfont.size / 2 : 0,
- (caxis.showticklabels ? caxis.tickfont.size * 0.75 : 0) +
- (caxis.ticks === 'outside' ? caxis.ticklen * 0.87 : 0));
- var bpad = (baxis.showticklabels ? baxis.tickfont.size : 0) +
- (baxis.ticks === 'outside' ? baxis.ticklen : 0) + 3;
-
- layers['a-title'] = Titles.draw(gd, 'a' + titlesuffix, {
- propContainer: aaxis,
- propName: _this.id + '.aaxis.title',
- placeholder: _(gd, 'Click to enter Component A title'),
- attributes: {
- x: _this.x0 + _this.w / 2,
- y: _this.y0 - aaxis.title.font.size / 3 - apad,
- 'text-anchor': 'middle'
- }
- });
- layers['b-title'] = Titles.draw(gd, 'b' + titlesuffix, {
- propContainer: baxis,
- propName: _this.id + '.baxis.title',
- placeholder: _(gd, 'Click to enter Component B title'),
- attributes: {
- x: _this.x0 - bpad,
- y: _this.y0 + _this.h + baxis.title.font.size * 0.83 + bpad,
- 'text-anchor': 'middle'
- }
- });
- layers['c-title'] = Titles.draw(gd, 'c' + titlesuffix, {
- propContainer: caxis,
- propName: _this.id + '.caxis.title',
- placeholder: _(gd, 'Click to enter Component C title'),
- attributes: {
- x: _this.x0 + _this.w + bpad,
- y: _this.y0 + _this.h + caxis.title.font.size * 0.83 + bpad,
- 'text-anchor': 'middle'
- }
- });
- }
- };
-
- proto.drawAx = function(ax) {
- var _this = this;
- var gd = _this.graphDiv;
- var axName = ax._name;
- var axLetter = axName.charAt(0);
- var axId = ax._id;
- var axLayer = _this.layers[axName];
- var counterAngle = 30;
-
- var stashKey = axLetter + 'tickLayout';
- var newTickLayout = strTickLayout(ax);
- if(_this[stashKey] !== newTickLayout) {
- axLayer.selectAll('.' + axId + 'tick').remove();
- _this[stashKey] = newTickLayout;
- }
-
- ax.setScale();
-
- var vals = Axes.calcTicks(ax);
- var valsClipped = Axes.clipEnds(ax, vals);
- var transFn = Axes.makeTransFn(ax);
- var tickSign = Axes.getTickSigns(ax)[2];
-
- var caRad = Lib.deg2rad(counterAngle);
- var pad = tickSign * (ax.linewidth || 1) / 2;
- var len = tickSign * ax.ticklen;
- var w = _this.w;
- var h = _this.h;
-
- var tickPath = axLetter === 'b' ?
- 'M0,' + pad + 'l' + (Math.sin(caRad) * len) + ',' + (Math.cos(caRad) * len) :
- 'M' + pad + ',0l' + (Math.cos(caRad) * len) + ',' + (-Math.sin(caRad) * len);
-
- var gridPath = {
- a: 'M0,0l' + h + ',-' + (w / 2),
- b: 'M0,0l-' + (w / 2) + ',-' + h,
- c: 'M0,0l-' + h + ',' + (w / 2)
- }[axLetter];
-
- Axes.drawTicks(gd, ax, {
- vals: ax.ticks === 'inside' ? valsClipped : vals,
- layer: axLayer,
- path: tickPath,
- transFn: transFn,
- crisp: false
- });
-
- Axes.drawGrid(gd, ax, {
- vals: valsClipped,
- layer: _this.layers[axLetter + 'grid'],
- path: gridPath,
- transFn: transFn,
- crisp: false
- });
-
- var labelFns = Axes.makeLabelFns(ax, 0, counterAngle);
-
- Axes.drawLabels(gd, ax, {
- vals: vals,
- layer: axLayer,
- transFn: transFn,
- labelXFn: labelFns.labelXFn,
- labelYFn: labelFns.labelYFn,
- labelAnchorFn: labelFns.labelAnchorFn
- });
- };
-
- function strTickLayout(axLayout) {
- return axLayout.ticks + String(axLayout.ticklen) + String(axLayout.showticklabels);
- }
-
- // hard coded paths for zoom corners
- // uses the same sizing as cartesian, length is MINZOOM/2, width is 3px
- var CLEN = constants.MINZOOM / 2 + 0.87;
- var BLPATH = 'm-0.87,.5h' + CLEN + 'v3h-' + (CLEN + 5.2) +
- 'l' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) +
- 'l2.6,1.5l-' + (CLEN / 2) + ',' + (CLEN * 0.87) + 'Z';
- var BRPATH = 'm0.87,.5h-' + CLEN + 'v3h' + (CLEN + 5.2) +
- 'l-' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) +
- 'l-2.6,1.5l' + (CLEN / 2) + ',' + (CLEN * 0.87) + 'Z';
- var TOPPATH = 'm0,1l' + (CLEN / 2) + ',' + (CLEN * 0.87) +
- 'l2.6,-1.5l-' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) +
- 'l-' + (CLEN / 2 + 2.6) + ',' + (CLEN * 0.87 + 4.5) +
- 'l2.6,1.5l' + (CLEN / 2) + ',-' + (CLEN * 0.87) + 'Z';
- var STARTMARKER = 'm0.5,0.5h5v-2h-5v-5h-2v5h-5v2h5v5h2Z';
-
- // I guess this could be shared with cartesian... but for now it's separate.
- var SHOWZOOMOUTTIP = true;
-
- proto.initInteractions = function() {
- var _this = this;
- var dragger = _this.layers.plotbg.select('path').node();
- var gd = _this.graphDiv;
- var zoomContainer = gd._fullLayout._zoomlayer;
-
- // use plotbg for the main interactions
- var dragOptions = {
- element: dragger,
- gd: gd,
- plotinfo: {
- id: _this.id,
- xaxis: _this.xaxis,
- yaxis: _this.yaxis
- },
- subplot: _this.id,
- prepFn: function(e, startX, startY) {
- // these aren't available yet when initInteractions
- // is called
- dragOptions.xaxes = [_this.xaxis];
- dragOptions.yaxes = [_this.yaxis];
- var dragModeNow = gd._fullLayout.dragmode;
-
- if(dragModeNow === 'lasso') dragOptions.minDrag = 1;
- else dragOptions.minDrag = undefined;
-
- if(dragModeNow === 'zoom') {
- dragOptions.moveFn = zoomMove;
- dragOptions.clickFn = clickZoomPan;
- dragOptions.doneFn = zoomDone;
- zoomPrep(e, startX, startY);
- }
- else if(dragModeNow === 'pan') {
- dragOptions.moveFn = plotDrag;
- dragOptions.clickFn = clickZoomPan;
- dragOptions.doneFn = dragDone;
- panPrep();
- clearSelect(zoomContainer);
- }
- else if(dragModeNow === 'select' || dragModeNow === 'lasso') {
- prepSelect(e, startX, startY, dragOptions, dragModeNow);
- }
- }
- };
-
- var x0, y0, mins0, span0, mins, lum, path0, dimmed, zb, corners;
-
- function makeUpdate(_mins) {
- var attrs = {};
- attrs[_this.id + '.aaxis.min'] = _mins.a;
- attrs[_this.id + '.baxis.min'] = _mins.b;
- attrs[_this.id + '.caxis.min'] = _mins.c;
- return attrs;
- }
-
- function clickZoomPan(numClicks, evt) {
- var clickMode = gd._fullLayout.clickmode;
-
- removeZoombox(gd);
-
- if(numClicks === 2) {
- gd.emit('plotly_doubleclick', null);
- Registry.call('_guiRelayout', gd, makeUpdate({a: 0, b: 0, c: 0}));
- }
-
- if(clickMode.indexOf('select') > -1 && numClicks === 1) {
- selectOnClick(evt, gd, [_this.xaxis], [_this.yaxis], _this.id, dragOptions);
- }
-
- if(clickMode.indexOf('event') > -1) {
- Fx.click(gd, evt, _this.id);
- }
- }
-
- function zoomPrep(e, startX, startY) {
- var dragBBox = dragger.getBoundingClientRect();
- x0 = startX - dragBBox.left;
- y0 = startY - dragBBox.top;
- mins0 = {
- a: _this.aaxis.range[0],
- b: _this.baxis.range[1],
- c: _this.caxis.range[1]
- };
- mins = mins0;
- span0 = _this.aaxis.range[1] - mins0.a;
- lum = tinycolor(_this.graphDiv._fullLayout[_this.id].bgcolor).getLuminance();
- path0 = 'M0,' + _this.h + 'L' + (_this.w / 2) + ', 0L' + _this.w + ',' + _this.h + 'Z';
- dimmed = false;
-
- zb = zoomContainer.append('path')
- .attr('class', 'zoombox')
- .attr('transform', 'translate(' + _this.x0 + ', ' + _this.y0 + ')')
- .style({
- 'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
- 'stroke-width': 0
- })
- .attr('d', path0);
-
- corners = zoomContainer.append('path')
- .attr('class', 'zoombox-corners')
- .attr('transform', 'translate(' + _this.x0 + ', ' + _this.y0 + ')')
- .style({
- fill: Color.background,
- stroke: Color.defaultLine,
- 'stroke-width': 1,
- opacity: 0
- })
- .attr('d', 'M0,0Z');
-
- clearSelect(zoomContainer);
- }
-
- function getAFrac(x, y) { return 1 - (y / _this.h); }
- function getBFrac(x, y) { return 1 - ((x + (_this.h - y) / Math.sqrt(3)) / _this.w); }
- function getCFrac(x, y) { return ((x - (_this.h - y) / Math.sqrt(3)) / _this.w); }
-
- function zoomMove(dx0, dy0) {
- var x1 = x0 + dx0;
- var y1 = y0 + dy0;
- var afrac = Math.max(0, Math.min(1, getAFrac(x0, y0), getAFrac(x1, y1)));
- var bfrac = Math.max(0, Math.min(1, getBFrac(x0, y0), getBFrac(x1, y1)));
- var cfrac = Math.max(0, Math.min(1, getCFrac(x0, y0), getCFrac(x1, y1)));
- var xLeft = ((afrac / 2) + cfrac) * _this.w;
- var xRight = (1 - (afrac / 2) - bfrac) * _this.w;
- var xCenter = (xLeft + xRight) / 2;
- var xSpan = xRight - xLeft;
- var yBottom = (1 - afrac) * _this.h;
- var yTop = yBottom - xSpan / whRatio;
-
- if(xSpan < constants.MINZOOM) {
- mins = mins0;
- zb.attr('d', path0);
- corners.attr('d', 'M0,0Z');
- }
- else {
- mins = {
- a: mins0.a + afrac * span0,
- b: mins0.b + bfrac * span0,
- c: mins0.c + cfrac * span0
- };
- zb.attr('d', path0 + 'M' + xLeft + ',' + yBottom +
- 'H' + xRight + 'L' + xCenter + ',' + yTop +
- 'L' + xLeft + ',' + yBottom + 'Z');
- corners.attr('d', 'M' + x0 + ',' + y0 + STARTMARKER +
- 'M' + xLeft + ',' + yBottom + BLPATH +
- 'M' + xRight + ',' + yBottom + BRPATH +
- 'M' + xCenter + ',' + yTop + TOPPATH);
- }
-
- if(!dimmed) {
- zb.transition()
- .style('fill', lum > 0.2 ? 'rgba(0,0,0,0.4)' :
- 'rgba(255,255,255,0.3)')
- .duration(200);
- corners.transition()
- .style('opacity', 1)
- .duration(200);
- dimmed = true;
- }
- }
-
- function zoomDone() {
- removeZoombox(gd);
-
- if(mins === mins0) return;
-
- Registry.call('_guiRelayout', gd, makeUpdate(mins));
-
- if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) {
- Lib.notifier(_(gd, 'Double-click to zoom back out'), 'long');
- SHOWZOOMOUTTIP = false;
- }
- }
-
- function panPrep() {
- mins0 = {
- a: _this.aaxis.range[0],
- b: _this.baxis.range[1],
- c: _this.caxis.range[1]
- };
- mins = mins0;
- }
-
- function plotDrag(dx, dy) {
- var dxScaled = dx / _this.xaxis._m;
- var dyScaled = dy / _this.yaxis._m;
- mins = {
- a: mins0.a - dyScaled,
- b: mins0.b + (dxScaled + dyScaled) / 2,
- c: mins0.c - (dxScaled - dyScaled) / 2
- };
- var minsorted = [mins.a, mins.b, mins.c].sort();
- var minindices = {
- a: minsorted.indexOf(mins.a),
- b: minsorted.indexOf(mins.b),
- c: minsorted.indexOf(mins.c)
- };
- if(minsorted[0] < 0) {
- if(minsorted[1] + minsorted[0] / 2 < 0) {
- minsorted[2] += minsorted[0] + minsorted[1];
- minsorted[0] = minsorted[1] = 0;
- }
- else {
- minsorted[2] += minsorted[0] / 2;
- minsorted[1] += minsorted[0] / 2;
- minsorted[0] = 0;
- }
- mins = {
- a: minsorted[minindices.a],
- b: minsorted[minindices.b],
- c: minsorted[minindices.c]
- };
- dy = (mins0.a - mins.a) * _this.yaxis._m;
- dx = (mins0.c - mins.c - mins0.b + mins.b) * _this.xaxis._m;
- }
-
- // move the data (translate, don't redraw)
- var plotTransform = 'translate(' + (_this.x0 + dx) + ',' + (_this.y0 + dy) + ')';
- _this.plotContainer.selectAll('.scatterlayer,.maplayer')
- .attr('transform', plotTransform);
-
- var plotTransform2 = 'translate(' + -dx + ',' + -dy + ')';
- _this.clipDefRelative.select('path').attr('transform', plotTransform2);
-
- // move the ticks
- _this.aaxis.range = [mins.a, _this.sum - mins.b - mins.c];
- _this.baxis.range = [_this.sum - mins.a - mins.c, mins.b];
- _this.caxis.range = [_this.sum - mins.a - mins.b, mins.c];
-
- _this.drawAxes(false);
-
- if(_this._hasClipOnAxisFalse) {
- _this.plotContainer
- .select('.scatterlayer').selectAll('.trace')
- .call(Drawing.hideOutsideRangePoints, _this);
- }
- }
-
- function dragDone() {
- Registry.call('_guiRelayout', gd, makeUpdate(mins));
- }
-
- // finally, set up hover and click
- // these event handlers must already be set before dragElement.init
- // so it can stash them and override them.
- dragger.onmousemove = function(evt) {
- Fx.hover(gd, evt, _this.id);
- gd._fullLayout._lasthover = dragger;
- gd._fullLayout._hoversubplot = _this.id;
- };
-
- dragger.onmouseout = function(evt) {
- if(gd._dragging) return;
-
- dragElement.unhover(gd, evt);
- };
-
- dragElement.init(dragOptions);
- };
-
- function removeZoombox(gd) {
- d3.select(gd)
- .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners')
- .remove();
- }
-
- },{"../../components/color":51,"../../components/dragelement":69,"../../components/drawing":72,"../../components/fx":90,"../../components/titles":139,"../../lib":168,"../../lib/extend":162,"../../registry":257,"../cartesian/axes":212,"../cartesian/constants":218,"../cartesian/select":230,"../cartesian/set_convert":231,"../plots":245,"d3":16,"tinycolor2":34}],257:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Loggers = _dereq_('./lib/loggers');
- var noop = _dereq_('./lib/noop');
- var pushUnique = _dereq_('./lib/push_unique');
- var isPlainObject = _dereq_('./lib/is_plain_object');
- var ExtendModule = _dereq_('./lib/extend');
-
- var basePlotAttributes = _dereq_('./plots/attributes');
- var baseLayoutAttributes = _dereq_('./plots/layout_attributes');
-
- var extendFlat = ExtendModule.extendFlat;
- var extendDeepAll = ExtendModule.extendDeepAll;
-
- exports.modules = {};
- exports.allCategories = {};
- exports.allTypes = [];
- exports.subplotsRegistry = {};
- exports.transformsRegistry = {};
- exports.componentsRegistry = {};
- exports.layoutArrayContainers = [];
- exports.layoutArrayRegexes = [];
- exports.traceLayoutAttributes = {};
- exports.localeRegistry = {};
- exports.apiMethodRegistry = {};
- exports.collectableSubplotTypes = null;
-
- /**
- * Top-level register routine, exported as Plotly.register
- *
- * @param {object array or array of objects} _modules :
- * module object or list of module object to register.
- *
- * A valid `moduleType: 'trace'` module has fields:
- * - name {string} : the trace type
- * - categories {array} : categories associated with this trace type,
- * tested with Register.traceIs()
- * - meta {object} : meta info (mostly for plot-schema)
- *
- * A valid `moduleType: 'locale'` module has fields:
- * - name {string} : the locale name. Should be a 2-digit language string ('en', 'de')
- * optionally with a country/region code ('en-GB', 'de-CH'). If a country
- * code is used but the base language locale has not yet been supplied,
- * we will use this locale for the base as well.
- * - dictionary {object} : the dictionary mapping input strings to localized strings
- * generally the keys should be the literal input strings, but
- * if default translations are provided you can use any string as a key.
- * - format {object} : a `d3.locale` format specifier for this locale
- * any omitted keys we'll fall back on en-US.
- *
- * A valid `moduleType: 'transform'` module has fields:
- * - name {string} : transform name
- * - transform {function} : default-level transform function
- * - calcTransform {function} : calc-level transform function
- * - attributes {object} : transform attributes declarations
- * - supplyDefaults {function} : attributes default-supply function
- *
- * A valid `moduleType: 'component'` module has fields:
- * - name {string} : the component name, used it with Register.getComponentMethod()
- * to employ component method.
- *
- * A valid `moduleType: 'apiMethod'` module has fields:
- * - name {string} : the api method name.
- * - fn {function} : the api method called with Register.call();
- *
- */
- exports.register = function register(_modules) {
- exports.collectableSubplotTypes = null;
-
- if(!_modules) {
- throw new Error('No argument passed to Plotly.register.');
- } else if(_modules && !Array.isArray(_modules)) {
- _modules = [_modules];
- }
-
- for(var i = 0; i < _modules.length; i++) {
- var newModule = _modules[i];
-
- if(!newModule) {
- throw new Error('Invalid module was attempted to be registered!');
- }
-
- switch(newModule.moduleType) {
- case 'trace':
- registerTraceModule(newModule);
- break;
- case 'transform':
- registerTransformModule(newModule);
- break;
- case 'component':
- registerComponentModule(newModule);
- break;
- case 'locale':
- registerLocale(newModule);
- break;
- case 'apiMethod':
- var name = newModule.name;
- exports.apiMethodRegistry[name] = newModule.fn;
- break;
- default:
- throw new Error('Invalid module was attempted to be registered!');
- }
- }
- };
-
- /**
- * Get registered module using trace object or trace type
- *
- * @param {object||string} trace
- * trace object with prop 'type' or trace type as a string
- * @return {object}
- * module object corresponding to trace type
- */
- exports.getModule = function(trace) {
- var _module = exports.modules[getTraceType(trace)];
- if(!_module) return false;
- return _module._module;
- };
-
- /**
- * Determine if this trace type is in a given category
- *
- * @param {object||string} traceType
- * a trace (object) or trace type (string)
- * @param {string} category
- * category in question
- * @return {boolean}
- */
- exports.traceIs = function(traceType, category) {
- traceType = getTraceType(traceType);
-
- // old plot.ly workspace hack, nothing to see here
- if(traceType === 'various') return false;
-
- var _module = exports.modules[traceType];
-
- if(!_module) {
- if(traceType && traceType !== 'area') {
- Loggers.log('Unrecognized trace type ' + traceType + '.');
- }
-
- _module = exports.modules[basePlotAttributes.type.dflt];
- }
-
- return !!_module.categories[category];
- };
-
- /**
- * Determine if this trace has a transform of the given type and return
- * array of matching indices.
- *
- * @param {object} data
- * a trace object (member of data or fullData)
- * @param {string} type
- * type of trace to test
- * @return {array}
- * array of matching indices. If none found, returns []
- */
- exports.getTransformIndices = function(data, type) {
- var indices = [];
- var transforms = data.transforms || [];
- for(var i = 0; i < transforms.length; i++) {
- if(transforms[i].type === type) {
- indices.push(i);
- }
- }
- return indices;
- };
-
- /**
- * Determine if this trace has a transform of the given type
- *
- * @param {object} data
- * a trace object (member of data or fullData)
- * @param {string} type
- * type of trace to test
- * @return {boolean}
- */
- exports.hasTransform = function(data, type) {
- var transforms = data.transforms || [];
- for(var i = 0; i < transforms.length; i++) {
- if(transforms[i].type === type) {
- return true;
- }
- }
- return false;
- };
-
- /**
- * Retrieve component module method. Falls back on noop if either the
- * module or the method is missing, so the result can always be safely called
- *
- * @param {string} name
- * name of component (as declared in component module)
- * @param {string} method
- * name of component module method
- * @return {function}
- */
- exports.getComponentMethod = function(name, method) {
- var _module = exports.componentsRegistry[name];
-
- if(!_module) return noop;
- return _module[method] || noop;
- };
-
- /**
- * Call registered api method.
- *
- * @param {string} name : api method name
- * @param {...array} args : arguments passed to api method
- * @return {any} : returns api method output
- */
- exports.call = function() {
- var name = arguments[0];
- var args = [].slice.call(arguments, 1);
- return exports.apiMethodRegistry[name].apply(null, args);
- };
-
- function registerTraceModule(_module) {
- var thisType = _module.name;
- var categoriesIn = _module.categories;
- var meta = _module.meta;
-
- if(exports.modules[thisType]) {
- Loggers.log('Type ' + thisType + ' already registered');
- return;
- }
-
- if(!exports.subplotsRegistry[_module.basePlotModule.name]) {
- registerSubplot(_module.basePlotModule);
- }
-
- var categoryObj = {};
- for(var i = 0; i < categoriesIn.length; i++) {
- categoryObj[categoriesIn[i]] = true;
- exports.allCategories[categoriesIn[i]] = true;
- }
-
- exports.modules[thisType] = {
- _module: _module,
- categories: categoryObj
- };
-
- if(meta && Object.keys(meta).length) {
- exports.modules[thisType].meta = meta;
- }
-
- exports.allTypes.push(thisType);
-
- for(var componentName in exports.componentsRegistry) {
- mergeComponentAttrsToTrace(componentName, thisType);
- }
-
- /*
- * Collect all trace layout attributes in one place for easier lookup later
- * but don't merge them into the base schema as it would confuse the docs
- * (at least after https://github.com/plotly/documentation/issues/202 gets done!)
- */
- if(_module.layoutAttributes) {
- extendFlat(exports.traceLayoutAttributes, _module.layoutAttributes);
- }
- }
-
- function registerSubplot(_module) {
- var plotType = _module.name;
-
- if(exports.subplotsRegistry[plotType]) {
- Loggers.log('Plot type ' + plotType + ' already registered.');
- return;
- }
-
- // relayout array handling will look for component module methods with this
- // name and won't find them because this is a subplot module... but that
- // should be fine, it will just fall back on redrawing the plot.
- findArrayRegexps(_module);
-
- // not sure what's best for the 'cartesian' type at this point
- exports.subplotsRegistry[plotType] = _module;
-
- for(var componentName in exports.componentsRegistry) {
- mergeComponentAttrsToSubplot(componentName, _module.name);
- }
- }
-
- function registerComponentModule(_module) {
- if(typeof _module.name !== 'string') {
- throw new Error('Component module *name* must be a string.');
- }
-
- var name = _module.name;
- exports.componentsRegistry[name] = _module;
-
- if(_module.layoutAttributes) {
- if(_module.layoutAttributes._isLinkedToArray) {
- pushUnique(exports.layoutArrayContainers, name);
- }
- findArrayRegexps(_module);
- }
-
- for(var traceType in exports.modules) {
- mergeComponentAttrsToTrace(name, traceType);
- }
-
- for(var subplotName in exports.subplotsRegistry) {
- mergeComponentAttrsToSubplot(name, subplotName);
- }
-
- for(var transformType in exports.transformsRegistry) {
- mergeComponentAttrsToTransform(name, transformType);
- }
-
- if(_module.schema && _module.schema.layout) {
- extendDeepAll(baseLayoutAttributes, _module.schema.layout);
- }
- }
-
- function registerTransformModule(_module) {
- if(typeof _module.name !== 'string') {
- throw new Error('Transform module *name* must be a string.');
- }
-
- var prefix = 'Transform module ' + _module.name;
- var hasTransform = typeof _module.transform === 'function';
- var hasCalcTransform = typeof _module.calcTransform === 'function';
-
- if(!hasTransform && !hasCalcTransform) {
- throw new Error(prefix + ' is missing a *transform* or *calcTransform* method.');
- }
- if(hasTransform && hasCalcTransform) {
- Loggers.log([
- prefix + ' has both a *transform* and *calcTransform* methods.',
- 'Please note that all *transform* methods are executed',
- 'before all *calcTransform* methods.'
- ].join(' '));
- }
- if(!isPlainObject(_module.attributes)) {
- Loggers.log(prefix + ' registered without an *attributes* object.');
- }
- if(typeof _module.supplyDefaults !== 'function') {
- Loggers.log(prefix + ' registered without a *supplyDefaults* method.');
- }
-
- exports.transformsRegistry[_module.name] = _module;
-
- for(var componentName in exports.componentsRegistry) {
- mergeComponentAttrsToTransform(componentName, _module.name);
- }
- }
-
- function registerLocale(_module) {
- var locale = _module.name;
- var baseLocale = locale.split('-')[0];
-
- var newDict = _module.dictionary;
- var newFormat = _module.format;
- var hasDict = newDict && Object.keys(newDict).length;
- var hasFormat = newFormat && Object.keys(newFormat).length;
-
- var locales = exports.localeRegistry;
-
- var localeObj = locales[locale];
- if(!localeObj) locales[locale] = localeObj = {};
-
- // Should we use this dict for the base locale?
- // In case we're overwriting a previous dict for this locale, check
- // whether the base matches the full locale dict now. If we're not
- // overwriting, locales[locale] is undefined so this just checks if
- // baseLocale already had a dict or not.
- // Same logic for dateFormats
- if(baseLocale !== locale) {
- var baseLocaleObj = locales[baseLocale];
- if(!baseLocaleObj) locales[baseLocale] = baseLocaleObj = {};
-
- if(hasDict && baseLocaleObj.dictionary === localeObj.dictionary) {
- baseLocaleObj.dictionary = newDict;
- }
- if(hasFormat && baseLocaleObj.format === localeObj.format) {
- baseLocaleObj.format = newFormat;
- }
- }
-
- if(hasDict) localeObj.dictionary = newDict;
- if(hasFormat) localeObj.format = newFormat;
- }
-
- function findArrayRegexps(_module) {
- if(_module.layoutAttributes) {
- var arrayAttrRegexps = _module.layoutAttributes._arrayAttrRegexps;
- if(arrayAttrRegexps) {
- for(var i = 0; i < arrayAttrRegexps.length; i++) {
- pushUnique(exports.layoutArrayRegexes, arrayAttrRegexps[i]);
- }
- }
- }
- }
-
- function mergeComponentAttrsToTrace(componentName, traceType) {
- var componentSchema = exports.componentsRegistry[componentName].schema;
- if(!componentSchema || !componentSchema.traces) return;
-
- var traceAttrs = componentSchema.traces[traceType];
- if(traceAttrs) {
- extendDeepAll(exports.modules[traceType]._module.attributes, traceAttrs);
- }
- }
-
- function mergeComponentAttrsToTransform(componentName, transformType) {
- var componentSchema = exports.componentsRegistry[componentName].schema;
- if(!componentSchema || !componentSchema.transforms) return;
-
- var transformAttrs = componentSchema.transforms[transformType];
- if(transformAttrs) {
- extendDeepAll(exports.transformsRegistry[transformType].attributes, transformAttrs);
- }
- }
-
- function mergeComponentAttrsToSubplot(componentName, subplotName) {
- var componentSchema = exports.componentsRegistry[componentName].schema;
- if(!componentSchema || !componentSchema.subplots) return;
-
- var subplotModule = exports.subplotsRegistry[subplotName];
- var subplotAttrs = subplotModule.layoutAttributes;
- var subplotAttr = subplotModule.attr === 'subplot' ? subplotModule.name : subplotModule.attr;
- if(Array.isArray(subplotAttr)) subplotAttr = subplotAttr[0];
-
- var componentLayoutAttrs = componentSchema.subplots[subplotAttr];
- if(subplotAttrs && componentLayoutAttrs) {
- extendDeepAll(subplotAttrs, componentLayoutAttrs);
- }
- }
-
- function getTraceType(traceType) {
- if(typeof traceType === 'object') traceType = traceType.type;
- return traceType;
- }
-
- },{"./lib/extend":162,"./lib/is_plain_object":169,"./lib/loggers":172,"./lib/noop":177,"./lib/push_unique":181,"./plots/attributes":209,"./plots/layout_attributes":243}],258:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../lib');
-
- var extendFlat = Lib.extendFlat;
- var extendDeep = Lib.extendDeep;
-
- // Put default plotTile layouts here
- function cloneLayoutOverride(tileClass) {
- var override;
-
- switch(tileClass) {
- case 'themes__thumb':
- override = {
- autosize: true,
- width: 150,
- height: 150,
- title: {text: ''},
- showlegend: false,
- margin: {l: 5, r: 5, t: 5, b: 5, pad: 0},
- annotations: []
- };
- break;
-
- case 'thumbnail':
- override = {
- title: {text: ''},
- hidesources: true,
- showlegend: false,
- borderwidth: 0,
- bordercolor: '',
- margin: {l: 1, r: 1, t: 1, b: 1, pad: 0},
- annotations: []
- };
- break;
-
- default:
- override = {};
- }
-
-
- return override;
- }
-
- function keyIsAxis(keyName) {
- var types = ['xaxis', 'yaxis', 'zaxis'];
- return (types.indexOf(keyName.slice(0, 5)) > -1);
- }
-
-
- module.exports = function clonePlot(graphObj, options) {
-
- // Polar plot compatibility
- if(graphObj.framework && graphObj.framework.isPolar) {
- graphObj = graphObj.framework.getConfig();
- }
-
- var i;
- var oldData = graphObj.data;
- var oldLayout = graphObj.layout;
- var newData = extendDeep([], oldData);
- var newLayout = extendDeep({}, oldLayout, cloneLayoutOverride(options.tileClass));
- var context = graphObj._context || {};
-
- if(options.width) newLayout.width = options.width;
- if(options.height) newLayout.height = options.height;
-
- if(options.tileClass === 'thumbnail' || options.tileClass === 'themes__thumb') {
- // kill annotations
- newLayout.annotations = [];
- var keys = Object.keys(newLayout);
-
- for(i = 0; i < keys.length; i++) {
- if(keyIsAxis(keys[i])) {
- newLayout[keys[i]].title = {text: ''};
- }
- }
-
- // kill colorbar and pie labels
- for(i = 0; i < newData.length; i++) {
- var trace = newData[i];
- trace.showscale = false;
- if(trace.marker) trace.marker.showscale = false;
- if(trace.type === 'pie') trace.textposition = 'none';
- }
- }
-
- if(Array.isArray(options.annotations)) {
- for(i = 0; i < options.annotations.length; i++) {
- newLayout.annotations.push(options.annotations[i]);
- }
- }
-
- // TODO: does this scene modification really belong here?
- // If we still need it, can it move into the gl3d module?
- var sceneIds = Object.keys(newLayout).filter(function(key) {
- return key.match(/^scene\d*$/);
- });
- if(sceneIds.length) {
- var axesImageOverride = {};
- if(options.tileClass === 'thumbnail') {
- axesImageOverride = {
- title: {text: ''},
- showaxeslabels: false,
- showticklabels: false,
- linetickenable: false
- };
- }
- for(i = 0; i < sceneIds.length; i++) {
- var scene = newLayout[sceneIds[i]];
-
- if(!scene.xaxis) {
- scene.xaxis = {};
- }
-
- if(!scene.yaxis) {
- scene.yaxis = {};
- }
-
- if(!scene.zaxis) {
- scene.zaxis = {};
- }
-
- extendFlat(scene.xaxis, axesImageOverride);
- extendFlat(scene.yaxis, axesImageOverride);
- extendFlat(scene.zaxis, axesImageOverride);
-
- // TODO what does this do?
- scene._scene = null;
- }
- }
-
- var gd = document.createElement('div');
- if(options.tileClass) gd.className = options.tileClass;
-
- var plotTile = {
- gd: gd,
- td: gd, // for external (image server) compatibility
- layout: newLayout,
- data: newData,
- config: {
- staticPlot: (options.staticPlot === undefined) ?
- true :
- options.staticPlot,
- plotGlPixelRatio: (options.plotGlPixelRatio === undefined) ?
- 2 :
- options.plotGlPixelRatio,
- displaylogo: options.displaylogo || false,
- showLink: options.showLink || false,
- showTips: options.showTips || false,
- mapboxAccessToken: context.mapboxAccessToken
- }
- };
-
- if(options.setBackground !== 'transparent') {
- plotTile.config.setBackground = options.setBackground || 'opaque';
- }
-
- // attaching the default Layout the gd, so you can grab it later
- plotTile.gd.defaultLayout = cloneLayoutOverride(options.tileClass);
-
- return plotTile;
- };
-
- },{"../lib":168}],259:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var toImage = _dereq_('../plot_api/to_image');
- var Lib = _dereq_('../lib'); // for isIE
- var fileSaver = _dereq_('./filesaver');
-
- /** Plotly.downloadImage
- *
- * @param {object | string | HTML div} gd
- * can either be a data/layout/config object
- * or an existing graph <div>
- * or an id to an existing graph <div>
- * @param {object} opts (see ../plot_api/to_image)
- * @return {promise}
- */
- function downloadImage(gd, opts) {
- var _gd;
- if(!Lib.isPlainObject(gd)) _gd = Lib.getGraphDiv(gd);
-
- // check for undefined opts
- opts = opts || {};
- // default to png
- opts.format = opts.format || 'png';
-
- return new Promise(function(resolve, reject) {
- if(_gd && _gd._snapshotInProgress) {
- reject(new Error('Snapshotting already in progress.'));
- }
-
- // see comments within svgtoimg for additional
- // discussion of problems with IE
- // can now draw to canvas, but CORS tainted canvas
- // does not allow toDataURL
- // svg format will work though
- if(Lib.isIE() && opts.format !== 'svg') {
- reject(new Error('Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.'));
- }
-
- if(_gd) _gd._snapshotInProgress = true;
- var promise = toImage(gd, opts);
-
- var filename = opts.filename || gd.fn || 'newplot';
- filename += '.' + opts.format;
-
- promise.then(function(result) {
- if(_gd) _gd._snapshotInProgress = false;
- return fileSaver(result, filename);
- }).then(function(name) {
- resolve(name);
- }).catch(function(err) {
- if(_gd) _gd._snapshotInProgress = false;
- reject(err);
- });
- });
- }
-
- module.exports = downloadImage;
-
- },{"../lib":168,"../plot_api/to_image":205,"./filesaver":260}],260:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- /*
- * substantial portions of this code from FileSaver.js
- * https://github.com/eligrey/FileSaver.js
- * License: https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
- * FileSaver.js
- * A saveAs() FileSaver implementation.
- * 1.1.20160328
- *
- * By Eli Grey, http://eligrey.com
- * License: MIT
- * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
- */
-
- 'use strict';
-
- var fileSaver = function(url, name) {
- var saveLink = document.createElement('a');
- var canUseSaveLink = 'download' in saveLink;
- var isSafari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent);
- var promise = new Promise(function(resolve, reject) {
- // IE <10 is explicitly unsupported
- if(typeof navigator !== 'undefined' && /MSIE [1-9]\./.test(navigator.userAgent)) {
- reject(new Error('IE < 10 unsupported'));
- }
-
- // First try a.download, then web filesystem, then object URLs
- if(isSafari) {
- // Safari doesn't allow downloading of blob urls
- document.location.href = 'data:application/octet-stream' + url.slice(url.search(/[,;]/));
- resolve(name);
- }
-
- if(!name) {
- name = 'download';
- }
-
- if(canUseSaveLink) {
- saveLink.href = url;
- saveLink.download = name;
- document.body.appendChild(saveLink);
- saveLink.click();
- document.body.removeChild(saveLink);
- resolve(name);
- }
-
- // IE 10+ (native saveAs)
- if(typeof navigator !== 'undefined' && navigator.msSaveBlob) {
- // At this point we are only dealing with a SVG encoded as
- // a data URL (since IE only supports SVG)
- var encoded = url.split(/^data:image\/svg\+xml,/)[1];
- var svg = decodeURIComponent(encoded);
- navigator.msSaveBlob(new Blob([svg]), name);
- resolve(name);
- }
-
- reject(new Error('download error'));
- });
-
- return promise;
- };
-
- module.exports = fileSaver;
-
- },{}],261:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- exports.getDelay = function(fullLayout) {
- if(!fullLayout._has) return 0;
-
- return (
- fullLayout._has('gl3d') ||
- fullLayout._has('gl2d') ||
- fullLayout._has('mapbox')
- ) ? 500 : 0;
- };
-
- exports.getRedrawFunc = function(gd) {
- var fullLayout = gd._fullLayout || {};
- var hasPolar = fullLayout._has && fullLayout._has('polar');
- var hasLegacyPolar = !hasPolar && gd.data && gd.data[0] && gd.data[0].r;
-
- // do not work for legacy polar
- if(hasLegacyPolar) return;
-
- return function() {
- (gd.calcdata || []).forEach(function(d) {
- if(d[0] && d[0].t && d[0].t.cb) d[0].t.cb();
- });
- };
- };
-
- },{}],262:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var helpers = _dereq_('./helpers');
-
- var Snapshot = {
- getDelay: helpers.getDelay,
- getRedrawFunc: helpers.getRedrawFunc,
- clone: _dereq_('./cloneplot'),
- toSVG: _dereq_('./tosvg'),
- svgToImg: _dereq_('./svgtoimg'),
- toImage: _dereq_('./toimage'),
- downloadImage: _dereq_('./download')
- };
-
- module.exports = Snapshot;
-
- },{"./cloneplot":258,"./download":259,"./helpers":261,"./svgtoimg":263,"./toimage":264,"./tosvg":265}],263:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../lib');
- var EventEmitter = _dereq_('events').EventEmitter;
-
- function svgToImg(opts) {
- var ev = opts.emitter || new EventEmitter();
-
- var promise = new Promise(function(resolve, reject) {
- var Image = window.Image;
- var svg = opts.svg;
- var format = opts.format || 'png';
-
- // IE only support svg
- if(Lib.isIE() && format !== 'svg') {
- var ieSvgError = new Error('Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.');
- reject(ieSvgError);
- // eventually remove the ev
- // in favor of promises
- if(!opts.promise) {
- return ev.emit('error', ieSvgError);
- } else {
- return promise;
- }
- }
-
- var canvas = opts.canvas;
- var scale = opts.scale || 1;
- var w0 = opts.width || 300;
- var h0 = opts.height || 150;
- var w1 = scale * w0;
- var h1 = scale * h0;
-
- var ctx = canvas.getContext('2d');
- var img = new Image();
-
- // for Safari support, eliminate createObjectURL
- // this decision could cause problems if content
- // is not restricted to svg
- var url = 'data:image/svg+xml,' + encodeURIComponent(svg);
-
- canvas.width = w1;
- canvas.height = h1;
-
- img.onload = function() {
- var imgData;
-
- // don't need to draw to canvas if svg
- // save some time and also avoid failure on IE
- if(format !== 'svg') {
- ctx.drawImage(img, 0, 0, w1, h1);
- }
-
- switch(format) {
- case 'jpeg':
- imgData = canvas.toDataURL('image/jpeg');
- break;
- case 'png':
- imgData = canvas.toDataURL('image/png');
- break;
- case 'webp':
- imgData = canvas.toDataURL('image/webp');
- break;
- case 'svg':
- imgData = url;
- break;
- default:
- var errorMsg = 'Image format is not jpeg, png, svg or webp.';
- reject(new Error(errorMsg));
- // eventually remove the ev
- // in favor of promises
- if(!opts.promise) {
- return ev.emit('error', errorMsg);
- }
- }
- resolve(imgData);
- // eventually remove the ev
- // in favor of promises
- if(!opts.promise) {
- ev.emit('success', imgData);
- }
- };
-
- img.onerror = function(err) {
- reject(err);
- // eventually remove the ev
- // in favor of promises
- if(!opts.promise) {
- return ev.emit('error', err);
- }
- };
-
- img.src = url;
- });
-
- // temporary for backward compatibility
- // move to only Promise in 2.0.0
- // and eliminate the EventEmitter
- if(opts.promise) {
- return promise;
- }
-
- return ev;
- }
-
- module.exports = svgToImg;
-
- },{"../lib":168,"events":15}],264:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var EventEmitter = _dereq_('events').EventEmitter;
-
- var Registry = _dereq_('../registry');
- var Lib = _dereq_('../lib');
-
- var helpers = _dereq_('./helpers');
- var clonePlot = _dereq_('./cloneplot');
- var toSVG = _dereq_('./tosvg');
- var svgToImg = _dereq_('./svgtoimg');
-
- /**
- * @param {object} gd figure Object
- * @param {object} opts option object
- * @param opts.format 'jpeg' | 'png' | 'webp' | 'svg'
- */
- function toImage(gd, opts) {
-
- // first clone the GD so we can operate in a clean environment
- var ev = new EventEmitter();
-
- var clone = clonePlot(gd, {format: 'png'});
- var clonedGd = clone.gd;
-
- // put the cloned div somewhere off screen before attaching to DOM
- clonedGd.style.position = 'absolute';
- clonedGd.style.left = '-5000px';
- document.body.appendChild(clonedGd);
-
- function wait() {
- var delay = helpers.getDelay(clonedGd._fullLayout);
-
- setTimeout(function() {
- var svg = toSVG(clonedGd);
-
- var canvas = document.createElement('canvas');
- canvas.id = Lib.randstr();
-
- ev = svgToImg({
- format: opts.format,
- width: clonedGd._fullLayout.width,
- height: clonedGd._fullLayout.height,
- canvas: canvas,
- emitter: ev,
- svg: svg
- });
-
- ev.clean = function() {
- if(clonedGd) document.body.removeChild(clonedGd);
- };
-
- }, delay);
- }
-
- var redrawFunc = helpers.getRedrawFunc(clonedGd);
-
- Registry.call('plot', clonedGd, clone.data, clone.layout, clone.config)
- .then(redrawFunc)
- .then(wait)
- .catch(function(err) {
- ev.emit('error', err);
- });
-
-
- return ev;
- }
-
- module.exports = toImage;
-
- },{"../lib":168,"../registry":257,"./cloneplot":258,"./helpers":261,"./svgtoimg":263,"./tosvg":265,"events":15}],265:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Lib = _dereq_('../lib');
- var Drawing = _dereq_('../components/drawing');
- var Color = _dereq_('../components/color');
-
- var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
- var DOUBLEQUOTE_REGEX = /"/g;
- var DUMMY_SUB = 'TOBESTRIPPED';
- var DUMMY_REGEX = new RegExp('("' + DUMMY_SUB + ')|(' + DUMMY_SUB + '")', 'g');
-
- function htmlEntityDecode(s) {
- var hiddenDiv = d3.select('body').append('div').style({display: 'none'}).html('');
- var replaced = s.replace(/(&[^;]*;)/gi, function(d) {
- if(d === '<') { return '<'; } // special handling for brackets
- if(d === '&rt;') { return '>'; }
- if(d.indexOf('<') !== -1 || d.indexOf('>') !== -1) { return ''; }
- return hiddenDiv.html(d).text(); // everything else, let the browser decode it to unicode
- });
- hiddenDiv.remove();
- return replaced;
- }
-
- function xmlEntityEncode(str) {
- return str.replace(/&(?!\w+;|\#[0-9]+;| \#x[0-9A-F]+;)/g, '&');
- }
-
- module.exports = function toSVG(gd, format, scale) {
- var fullLayout = gd._fullLayout;
- var svg = fullLayout._paper;
- var toppaper = fullLayout._toppaper;
- var width = fullLayout.width;
- var height = fullLayout.height;
- var i;
-
- // make background color a rect in the svg, then revert after scraping
- // all other alterations have been dealt with by properly preparing the svg
- // in the first place... like setting cursors with css classes so we don't
- // have to remove them, and providing the right namespaces in the svg to
- // begin with
- svg.insert('rect', ':first-child')
- .call(Drawing.setRect, 0, 0, width, height)
- .call(Color.fill, fullLayout.paper_bgcolor);
-
- // subplot-specific to-SVG methods
- // which notably add the contents of the gl-container
- // into the main svg node
- var basePlotModules = fullLayout._basePlotModules || [];
- for(i = 0; i < basePlotModules.length; i++) {
- var _module = basePlotModules[i];
-
- if(_module.toSVG) _module.toSVG(gd);
- }
-
- // add top items above them assumes everything in toppaper is either
- // a group or a defs, and if it's empty (like hoverlayer) we can ignore it.
- if(toppaper) {
- var nodes = toppaper.node().childNodes;
-
- // make copy of nodes as childNodes prop gets mutated in loop below
- var topGroups = Array.prototype.slice.call(nodes);
-
- for(i = 0; i < topGroups.length; i++) {
- var topGroup = topGroups[i];
-
- if(topGroup.childNodes.length) svg.node().appendChild(topGroup);
- }
- }
-
- // remove draglayer for Adobe Illustrator compatibility
- if(fullLayout._draggers) {
- fullLayout._draggers.remove();
- }
-
- // in case the svg element had an explicit background color, remove this
- // we want the rect to get the color so it's the right size; svg bg will
- // fill whatever container it's displayed in regardless of plot size.
- svg.node().style.background = '';
-
- svg.selectAll('text')
- .attr({'data-unformatted': null, 'data-math': null})
- .each(function() {
- var txt = d3.select(this);
-
- // hidden text is pre-formatting mathjax, the browser ignores it
- // but in a static plot it's useless and it can confuse batik
- // we've tried to standardize on display:none but make sure we still
- // catch visibility:hidden if it ever arises
- if(this.style.visibility === 'hidden' || this.style.display === 'none') {
- txt.remove();
- return;
- }
- else {
- // clear other visibility/display values to default
- // to not potentially confuse non-browser SVG implementations
- txt.style({visibility: null, display: null});
- }
-
- // Font family styles break things because of quotation marks,
- // so we must remove them *after* the SVG DOM has been serialized
- // to a string (browsers convert singles back)
- var ff = this.style.fontFamily;
- if(ff && ff.indexOf('"') !== -1) {
- txt.style('font-family', ff.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
- }
- });
-
- svg.selectAll('.point, .scatterpts, .legendfill>path, .legendlines>path, .cbfill').each(function() {
- var pt = d3.select(this);
-
- // similar to font family styles above,
- // we must remove " after the SVG DOM has been serialized
- var fill = this.style.fill;
- if(fill && fill.indexOf('url(') !== -1) {
- pt.style('fill', fill.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
- }
-
- var stroke = this.style.stroke;
- if(stroke && stroke.indexOf('url(') !== -1) {
- pt.style('stroke', stroke.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
- }
- });
-
- if(format === 'pdf' || format === 'eps') {
- // these formats make the extra line MathJax adds around symbols look super thick in some cases
- // it looks better if this is removed entirely.
- svg.selectAll('#MathJax_SVG_glyphs path')
- .attr('stroke-width', 0);
- }
-
- // fix for IE namespacing quirk?
- // http://stackoverflow.com/questions/19610089/unwanted-namespaces-on-svg-markup-when-using-xmlserializer-in-javascript-with-ie
- svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns', xmlnsNamespaces.svg);
- svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns:xlink', xmlnsNamespaces.xlink);
-
- if(format === 'svg' && scale) {
- svg.attr('width', scale * width);
- svg.attr('height', scale * height);
- svg.attr('viewBox', '0 0 ' + width + ' ' + height);
- }
-
- var s = new window.XMLSerializer().serializeToString(svg.node());
- s = htmlEntityDecode(s);
- s = xmlEntityEncode(s);
-
- // Fix quotations around font strings and gradient URLs
- s = s.replace(DUMMY_REGEX, '\'');
-
- // IE is very strict, so we will need to clean
- // svg with the following regex
- // yes this is messy, but do not know a better way
- // Even with this IE will not work due to tainted canvas
- // see https://github.com/kangax/fabric.js/issues/1957
- // http://stackoverflow.com/questions/18112047/canvas-todataurl-working-in-all-browsers-except-ie10
- // Leave here just in case the CORS/tainted IE issue gets resolved
- if(Lib.isIE()) {
- // replace double quote with single quote
- s = s.replace(/"/gi, '\'');
- // url in svg are single quoted
- // since we changed double to single
- // we'll need to change these to double-quoted
- s = s.replace(/(\('#)([^']*)('\))/gi, '(\"#$2\")');
- // font names with spaces will be escaped single-quoted
- // we'll need to change these to double-quoted
- s = s.replace(/(\\')/gi, '\"');
- }
-
- return s;
- };
-
- },{"../components/color":51,"../components/drawing":72,"../constants/xmlns_namespaces":150,"../lib":168,"d3":16}],266:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var mergeArray = _dereq_('../../lib').mergeArray;
-
-
- // arrayOk attributes, merge them into calcdata array
- module.exports = function arraysToCalcdata(cd, trace) {
- for(var i = 0; i < cd.length; i++) cd[i].i = i;
-
- mergeArray(trace.text, cd, 'tx');
- mergeArray(trace.hovertext, cd, 'htx');
-
- var marker = trace.marker;
- if(marker) {
- mergeArray(marker.opacity, cd, 'mo');
- mergeArray(marker.color, cd, 'mc');
-
- var markerLine = marker.line;
- if(markerLine) {
- mergeArray(markerLine.color, cd, 'mlc');
- mergeArray(markerLine.width, cd, 'mlw');
- }
- }
- };
-
- },{"../../lib":168}],267:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var scatterAttrs = _dereq_('../scatter/attributes');
- var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes');
- var colorAttributes = _dereq_('../../components/colorscale/attributes');
- var colorbarAttrs = _dereq_('../../components/colorbar/attributes');
- var fontAttrs = _dereq_('../../plots/font_attributes');
- var constants = _dereq_('./constants.js');
-
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- var textFontAttrs = fontAttrs({
- editType: 'calc',
- arrayOk: true,
- colorEditType: 'style',
-
- });
-
- var scatterMarkerAttrs = scatterAttrs.marker;
- var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
-
- var markerLineWidth = extendFlat({},
- scatterMarkerLineAttrs.width, { dflt: 0 });
-
- var markerLine = extendFlat({
- width: markerLineWidth,
- editType: 'calc'
- }, colorAttributes('marker.line'));
-
- var marker = extendFlat({
- line: markerLine,
- editType: 'calc'
- }, colorAttributes('marker'), {
- colorbar: colorbarAttrs,
- opacity: {
- valType: 'number',
- arrayOk: true,
- dflt: 1,
- min: 0,
- max: 1,
-
- editType: 'style',
-
- }
- });
-
- module.exports = {
- x: scatterAttrs.x,
- x0: scatterAttrs.x0,
- dx: scatterAttrs.dx,
- y: scatterAttrs.y,
- y0: scatterAttrs.y0,
- dy: scatterAttrs.dy,
-
- text: scatterAttrs.text,
- hovertext: scatterAttrs.hovertext,
- hovertemplate: hovertemplateAttrs({}, {
- keys: constants.eventDataKeys
- }),
-
- textposition: {
- valType: 'enumerated',
-
- values: ['inside', 'outside', 'auto', 'none'],
- dflt: 'none',
- arrayOk: true,
- editType: 'calc',
-
- },
-
- textfont: extendFlat({}, textFontAttrs, {
-
- }),
-
- insidetextfont: extendFlat({}, textFontAttrs, {
-
- }),
-
- outsidetextfont: extendFlat({}, textFontAttrs, {
-
- }),
-
- constraintext: {
- valType: 'enumerated',
- values: ['inside', 'outside', 'both', 'none'],
-
- dflt: 'both',
- editType: 'calc',
-
- },
-
- cliponaxis: extendFlat({}, scatterAttrs.cliponaxis, {
-
- }),
-
- orientation: {
- valType: 'enumerated',
-
- values: ['v', 'h'],
- editType: 'calc+clearAxisTypes',
-
- },
-
- base: {
- valType: 'any',
- dflt: null,
- arrayOk: true,
-
- editType: 'calc',
-
- },
-
- offset: {
- valType: 'number',
- dflt: null,
- arrayOk: true,
-
- editType: 'calc',
-
- },
-
- width: {
- valType: 'number',
- dflt: null,
- min: 0,
- arrayOk: true,
-
- editType: 'calc',
-
- },
-
- marker: marker,
-
- selected: {
- marker: {
- opacity: scatterAttrs.selected.marker.opacity,
- color: scatterAttrs.selected.marker.color,
- editType: 'style'
- },
- textfont: scatterAttrs.selected.textfont,
- editType: 'style'
- },
- unselected: {
- marker: {
- opacity: scatterAttrs.unselected.marker.opacity,
- color: scatterAttrs.unselected.marker.color,
- editType: 'style'
- },
- textfont: scatterAttrs.unselected.textfont,
- editType: 'style'
- },
-
- r: scatterAttrs.r,
- t: scatterAttrs.t,
-
- _deprecated: {
- bardir: {
- valType: 'enumerated',
-
- editType: 'calc',
- values: ['v', 'h'],
-
- }
- }
- };
-
- },{"../../components/colorbar/attributes":52,"../../components/colorscale/attributes":58,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/font_attributes":239,"../scatter/attributes":367,"./constants.js":269}],268:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Axes = _dereq_('../../plots/cartesian/axes');
- var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
- var colorscaleCalc = _dereq_('../../components/colorscale/calc');
- var arraysToCalcdata = _dereq_('./arrays_to_calcdata');
- var calcSelection = _dereq_('../scatter/calc_selection');
-
- module.exports = function calc(gd, trace) {
- var xa = Axes.getFromId(gd, trace.xaxis || 'x');
- var ya = Axes.getFromId(gd, trace.yaxis || 'y');
- var size, pos;
-
- if(trace.orientation === 'h') {
- size = xa.makeCalcdata(trace, 'x');
- pos = ya.makeCalcdata(trace, 'y');
- } else {
- size = ya.makeCalcdata(trace, 'y');
- pos = xa.makeCalcdata(trace, 'x');
- }
-
- // create the "calculated data" to plot
- var serieslen = Math.min(pos.length, size.length);
- var cd = new Array(serieslen);
-
- // set position and size
- for(var i = 0; i < serieslen; i++) {
- cd[i] = { p: pos[i], s: size[i] };
-
- if(trace.ids) {
- cd[i].id = String(trace.ids[i]);
- }
- }
-
- // auto-z and autocolorscale if applicable
- if(hasColorscale(trace, 'marker')) {
- colorscaleCalc(gd, trace, {
- vals: trace.marker.color,
- containerStr: 'marker',
- cLetter: 'c'
- });
- }
- if(hasColorscale(trace, 'marker.line')) {
- colorscaleCalc(gd, trace, {
- vals: trace.marker.line.color,
- containerStr: 'marker.line',
- cLetter: 'c'
- });
- }
-
- arraysToCalcdata(cd, trace);
- calcSelection(cd, trace);
-
- return cd;
- };
-
- },{"../../components/colorscale/calc":59,"../../components/colorscale/helpers":62,"../../plots/cartesian/axes":212,"../scatter/calc_selection":369,"./arrays_to_calcdata":266}],269:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- module.exports = {
- eventDataKeys: []
- };
-
- },{}],270:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
- var BADNUM = _dereq_('../../constants/numerical').BADNUM;
-
- var Registry = _dereq_('../../registry');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var Sieve = _dereq_('./sieve.js');
-
- /*
- * Bar chart stacking/grouping positioning and autoscaling calculations
- * for each direction separately calculate the ranges and positions
- * note that this handles histograms too
- * now doing this one subplot at a time
- */
-
- function crossTraceCalc(gd, plotinfo) {
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- var fullTraces = gd._fullData;
- var calcTraces = gd.calcdata;
- var calcTracesHorizontal = [];
- var calcTracesVertical = [];
-
- for(var i = 0; i < fullTraces.length; i++) {
- var fullTrace = fullTraces[i];
- if(
- fullTrace.visible === true &&
- Registry.traceIs(fullTrace, 'bar') &&
- fullTrace.xaxis === xa._id &&
- fullTrace.yaxis === ya._id
- ) {
- if(fullTrace.orientation === 'h') {
- calcTracesHorizontal.push(calcTraces[i]);
- } else {
- calcTracesVertical.push(calcTraces[i]);
- }
- }
- }
-
- setGroupPositions(gd, xa, ya, calcTracesVertical);
- setGroupPositions(gd, ya, xa, calcTracesHorizontal);
- }
-
- function setGroupPositions(gd, pa, sa, calcTraces) {
- if(!calcTraces.length) return;
-
- var barmode = gd._fullLayout.barmode;
- var overlay = (barmode === 'overlay');
- var group = (barmode === 'group');
- var excluded;
- var included;
- var i, calcTrace, fullTrace;
-
- initBase(gd, pa, sa, calcTraces);
-
- if(overlay) {
- setGroupPositionsInOverlayMode(gd, pa, sa, calcTraces);
- } else if(group) {
- // exclude from the group those traces for which the user set an offset
- excluded = [];
- included = [];
- for(i = 0; i < calcTraces.length; i++) {
- calcTrace = calcTraces[i];
- fullTrace = calcTrace[0].trace;
-
- if(fullTrace.offset === undefined) included.push(calcTrace);
- else excluded.push(calcTrace);
- }
-
- if(included.length) {
- setGroupPositionsInGroupMode(gd, pa, sa, included);
- }
- if(excluded.length) {
- setGroupPositionsInOverlayMode(gd, pa, sa, excluded);
- }
- } else {
- // exclude from the stack those traces for which the user set a base
- excluded = [];
- included = [];
- for(i = 0; i < calcTraces.length; i++) {
- calcTrace = calcTraces[i];
- fullTrace = calcTrace[0].trace;
-
- if(fullTrace.base === undefined) included.push(calcTrace);
- else excluded.push(calcTrace);
- }
-
- if(included.length) {
- setGroupPositionsInStackOrRelativeMode(gd, pa, sa, included);
- }
- if(excluded.length) {
- setGroupPositionsInOverlayMode(gd, pa, sa, excluded);
- }
- }
-
- collectExtents(calcTraces, pa);
- }
-
- function initBase(gd, pa, sa, calcTraces) {
- var i, j;
-
- for(i = 0; i < calcTraces.length; i++) {
- var cd = calcTraces[i];
- var trace = cd[0].trace;
- var base = trace.base;
- var b;
-
- // not sure if it really makes sense to have dates for bar size data...
- // ideally if we want to make gantt charts or something we'd treat
- // the actual size (trace.x or y) as time delta but base as absolute
- // time. But included here for completeness.
- var scalendar = trace.orientation === 'h' ? trace.xcalendar : trace.ycalendar;
-
- // 'base' on categorical axes makes no sense
- var d2c = sa.type === 'category' || sa.type === 'multicategory' ?
- function() { return null; } :
- sa.d2c;
-
- if(isArrayOrTypedArray(base)) {
- for(j = 0; j < Math.min(base.length, cd.length); j++) {
- b = d2c(base[j], 0, scalendar);
- if(isNumeric(b)) {
- cd[j].b = +b;
- cd[j].hasB = 1;
- }
- else cd[j].b = 0;
- }
- for(; j < cd.length; j++) {
- cd[j].b = 0;
- }
- } else {
- b = d2c(base, 0, scalendar);
- var hasBase = isNumeric(b);
- b = hasBase ? b : 0;
- for(j = 0; j < cd.length; j++) {
- cd[j].b = b;
- if(hasBase) cd[j].hasB = 1;
- }
- }
- }
- }
-
- function setGroupPositionsInOverlayMode(gd, pa, sa, calcTraces) {
- var barnorm = gd._fullLayout.barnorm;
- var separateNegativeValues = false;
- var dontMergeOverlappingData = !barnorm;
-
- // update position axis and set bar offsets and widths
- for(var i = 0; i < calcTraces.length; i++) {
- var calcTrace = calcTraces[i];
- var sieve = new Sieve([calcTrace], separateNegativeValues, dontMergeOverlappingData);
-
- // set bar offsets and widths, and update position axis
- setOffsetAndWidth(gd, pa, sieve);
-
- // set bar bases and sizes, and update size axis
- //
- // (note that `setGroupPositionsInOverlayMode` handles the case barnorm
- // is defined, because this function is also invoked for traces that
- // can't be grouped or stacked)
- if(barnorm) {
- sieveBars(gd, sa, sieve);
- normalizeBars(gd, sa, sieve);
- } else {
- setBaseAndTop(gd, sa, sieve);
- }
- }
- }
-
- function setGroupPositionsInGroupMode(gd, pa, sa, calcTraces) {
- var fullLayout = gd._fullLayout;
- var barnorm = fullLayout.barnorm;
- var separateNegativeValues = false;
- var dontMergeOverlappingData = !barnorm;
- var sieve = new Sieve(calcTraces, separateNegativeValues, dontMergeOverlappingData);
-
- // set bar offsets and widths, and update position axis
- setOffsetAndWidthInGroupMode(gd, pa, sieve);
-
- // set bar bases and sizes, and update size axis
- if(barnorm) {
- sieveBars(gd, sa, sieve);
- normalizeBars(gd, sa, sieve);
- } else {
- setBaseAndTop(gd, sa, sieve);
- }
- }
-
- function setGroupPositionsInStackOrRelativeMode(gd, pa, sa, calcTraces) {
- var fullLayout = gd._fullLayout;
- var barmode = fullLayout.barmode;
- var stack = barmode === 'stack';
- var relative = barmode === 'relative';
- var barnorm = fullLayout.barnorm;
- var separateNegativeValues = relative;
- var dontMergeOverlappingData = !(barnorm || stack || relative);
- var sieve = new Sieve(calcTraces, separateNegativeValues, dontMergeOverlappingData);
-
- // set bar offsets and widths, and update position axis
- setOffsetAndWidth(gd, pa, sieve);
-
- // set bar bases and sizes, and update size axis
- stackBars(gd, sa, sieve);
-
- // flag the outmost bar (for text display purposes)
- for(var i = 0; i < calcTraces.length; i++) {
- var calcTrace = calcTraces[i];
-
- for(var j = 0; j < calcTrace.length; j++) {
- var bar = calcTrace[j];
-
- if(bar.s !== BADNUM) {
- var isOutmostBar = ((bar.b + bar.s) === sieve.get(bar.p, bar.s));
- if(isOutmostBar) bar._outmost = true;
- }
- }
- }
-
- // Note that marking the outmost bars has to be done
- // before `normalizeBars` changes `bar.b` and `bar.s`.
- if(barnorm) normalizeBars(gd, sa, sieve);
- }
-
- function setOffsetAndWidth(gd, pa, sieve) {
- var fullLayout = gd._fullLayout;
- var bargap = fullLayout.bargap;
- var bargroupgap = fullLayout.bargroupgap || 0;
- var minDiff = sieve.minDiff;
- var calcTraces = sieve.traces;
-
- // set bar offsets and widths
- var barGroupWidth = minDiff * (1 - bargap);
- var barWidthPlusGap = barGroupWidth;
- var barWidth = barWidthPlusGap * (1 - bargroupgap);
-
- // computer bar group center and bar offset
- var offsetFromCenter = -barWidth / 2;
-
- for(var i = 0; i < calcTraces.length; i++) {
- var calcTrace = calcTraces[i];
- var t = calcTrace[0].t;
-
- // store bar width and offset for this trace
- t.barwidth = barWidth;
- t.poffset = offsetFromCenter;
- t.bargroupwidth = barGroupWidth;
- t.bardelta = minDiff;
- }
-
- // stack bars that only differ by rounding
- sieve.binWidth = calcTraces[0][0].t.barwidth / 100;
-
- // if defined, apply trace offset and width
- applyAttributes(sieve);
-
- // store the bar center in each calcdata item
- setBarCenterAndWidth(gd, pa, sieve);
-
- // update position axes
- updatePositionAxis(gd, pa, sieve);
- }
-
- function setOffsetAndWidthInGroupMode(gd, pa, sieve) {
- var fullLayout = gd._fullLayout;
- var bargap = fullLayout.bargap;
- var bargroupgap = fullLayout.bargroupgap || 0;
- var positions = sieve.positions;
- var distinctPositions = sieve.distinctPositions;
- var minDiff = sieve.minDiff;
- var calcTraces = sieve.traces;
-
- // if there aren't any overlapping positions,
- // let them have full width even if mode is group
- var overlap = (positions.length !== distinctPositions.length);
-
- var nTraces = calcTraces.length;
- var barGroupWidth = minDiff * (1 - bargap);
- var barWidthPlusGap = (overlap) ? barGroupWidth / nTraces : barGroupWidth;
- var barWidth = barWidthPlusGap * (1 - bargroupgap);
-
- for(var i = 0; i < nTraces; i++) {
- var calcTrace = calcTraces[i];
- var t = calcTrace[0].t;
-
- // computer bar group center and bar offset
- var offsetFromCenter = overlap ?
- ((2 * i + 1 - nTraces) * barWidthPlusGap - barWidth) / 2 :
- -barWidth / 2;
-
- // store bar width and offset for this trace
- t.barwidth = barWidth;
- t.poffset = offsetFromCenter;
- t.bargroupwidth = barGroupWidth;
- t.bardelta = minDiff;
- }
-
- // stack bars that only differ by rounding
- sieve.binWidth = calcTraces[0][0].t.barwidth / 100;
-
- // if defined, apply trace width
- applyAttributes(sieve);
-
- // store the bar center in each calcdata item
- setBarCenterAndWidth(gd, pa, sieve);
-
- // update position axes
- updatePositionAxis(gd, pa, sieve, overlap);
- }
-
- function applyAttributes(sieve) {
- var calcTraces = sieve.traces;
- var i, j;
-
- for(i = 0; i < calcTraces.length; i++) {
- var calcTrace = calcTraces[i];
- var calcTrace0 = calcTrace[0];
- var fullTrace = calcTrace0.trace;
- var t = calcTrace0.t;
- var offset = fullTrace._offset || fullTrace.offset;
- var initialPoffset = t.poffset;
- var newPoffset;
-
- if(isArrayOrTypedArray(offset)) {
- // if offset is an array, then clone it into t.poffset.
- newPoffset = Array.prototype.slice.call(offset, 0, calcTrace.length);
-
- // guard against non-numeric items
- for(j = 0; j < newPoffset.length; j++) {
- if(!isNumeric(newPoffset[j])) {
- newPoffset[j] = initialPoffset;
- }
- }
-
- // if the length of the array is too short,
- // then extend it with the initial value of t.poffset
- for(j = newPoffset.length; j < calcTrace.length; j++) {
- newPoffset.push(initialPoffset);
- }
-
- t.poffset = newPoffset;
- } else if(offset !== undefined) {
- t.poffset = offset;
- }
-
- var width = fullTrace._width || fullTrace.width;
- var initialBarwidth = t.barwidth;
-
- if(isArrayOrTypedArray(width)) {
- // if width is an array, then clone it into t.barwidth.
- var newBarwidth = Array.prototype.slice.call(width, 0, calcTrace.length);
-
- // guard against non-numeric items
- for(j = 0; j < newBarwidth.length; j++) {
- if(!isNumeric(newBarwidth[j])) newBarwidth[j] = initialBarwidth;
- }
-
- // if the length of the array is too short,
- // then extend it with the initial value of t.barwidth
- for(j = newBarwidth.length; j < calcTrace.length; j++) {
- newBarwidth.push(initialBarwidth);
- }
-
- t.barwidth = newBarwidth;
-
- // if user didn't set offset,
- // then correct t.poffset to ensure bars remain centered
- if(offset === undefined) {
- newPoffset = [];
- for(j = 0; j < calcTrace.length; j++) {
- newPoffset.push(
- initialPoffset + (initialBarwidth - newBarwidth[j]) / 2
- );
- }
- t.poffset = newPoffset;
- }
- } else if(width !== undefined) {
- t.barwidth = width;
-
- // if user didn't set offset,
- // then correct t.poffset to ensure bars remain centered
- if(offset === undefined) {
- t.poffset = initialPoffset + (initialBarwidth - width) / 2;
- }
- }
- }
- }
-
- function setBarCenterAndWidth(gd, pa, sieve) {
- var calcTraces = sieve.traces;
- var pLetter = getAxisLetter(pa);
-
- for(var i = 0; i < calcTraces.length; i++) {
- var calcTrace = calcTraces[i];
- var t = calcTrace[0].t;
- var poffset = t.poffset;
- var poffsetIsArray = Array.isArray(poffset);
- var barwidth = t.barwidth;
- var barwidthIsArray = Array.isArray(barwidth);
-
- for(var j = 0; j < calcTrace.length; j++) {
- var calcBar = calcTrace[j];
-
- // store the actual bar width and position, for use by hover
- var width = calcBar.w = barwidthIsArray ? barwidth[j] : barwidth;
- calcBar[pLetter] = calcBar.p + (poffsetIsArray ? poffset[j] : poffset) + width / 2;
- }
- }
- }
-
- function updatePositionAxis(gd, pa, sieve, allowMinDtick) {
- var calcTraces = sieve.traces;
- var minDiff = sieve.minDiff;
- var vpad = minDiff / 2;
-
- Axes.minDtick(pa, sieve.minDiff, sieve.distinctPositions[0], allowMinDtick);
-
- for(var i = 0; i < calcTraces.length; i++) {
- var calcTrace = calcTraces[i];
- var calcTrace0 = calcTrace[0];
- var fullTrace = calcTrace0.trace;
- var pts = [];
- var bar, l, r, j;
-
- for(j = 0; j < calcTrace.length; j++) {
- bar = calcTrace[j];
- l = bar.p - vpad;
- r = bar.p + vpad;
- pts.push(l, r);
- }
-
- if(fullTrace.width || fullTrace.offset) {
- var t = calcTrace0.t;
- var poffset = t.poffset;
- var barwidth = t.barwidth;
- var poffsetIsArray = Array.isArray(poffset);
- var barwidthIsArray = Array.isArray(barwidth);
-
- for(j = 0; j < calcTrace.length; j++) {
- bar = calcTrace[j];
- var calcBarOffset = poffsetIsArray ? poffset[j] : poffset;
- var calcBarWidth = barwidthIsArray ? barwidth[j] : barwidth;
- l = bar.p + calcBarOffset;
- r = l + calcBarWidth;
- pts.push(l, r);
- }
- }
-
- fullTrace._extremes[pa._id] = Axes.findExtremes(pa, pts, {padded: false});
- }
- }
-
- // store these bar bases and tops in calcdata
- // and make sure the size axis includes zero,
- // along with the bases and tops of each bar.
- function setBaseAndTop(gd, sa, sieve) {
- var calcTraces = sieve.traces;
- var sLetter = getAxisLetter(sa);
-
- for(var i = 0; i < calcTraces.length; i++) {
- var calcTrace = calcTraces[i];
- var fullTrace = calcTrace[0].trace;
- var pts = [];
- var allBarBaseAboveZero = true;
-
- for(var j = 0; j < calcTrace.length; j++) {
- var bar = calcTrace[j];
- var barBase = bar.b;
- var barTop = barBase + bar.s;
-
- bar[sLetter] = barTop;
- pts.push(barTop);
- if(bar.hasB) pts.push(barBase);
-
- if(!bar.hasB || !(bar.b > 0 && bar.s > 0)) {
- allBarBaseAboveZero = false;
- }
- }
-
- fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, {
- tozero: !allBarBaseAboveZero,
- padded: true
- });
- }
- }
-
- function stackBars(gd, sa, sieve) {
- var fullLayout = gd._fullLayout;
- var barnorm = fullLayout.barnorm;
- var sLetter = getAxisLetter(sa);
- var calcTraces = sieve.traces;
-
- for(var i = 0; i < calcTraces.length; i++) {
- var calcTrace = calcTraces[i];
- var fullTrace = calcTrace[0].trace;
- var pts = [];
-
- for(var j = 0; j < calcTrace.length; j++) {
- var bar = calcTrace[j];
-
- if(bar.s !== BADNUM) {
- // stack current bar and get previous sum
- var barBase = sieve.put(bar.p, bar.b + bar.s);
- var barTop = barBase + bar.b + bar.s;
-
- // store the bar base and top in each calcdata item
- bar.b = barBase;
- bar[sLetter] = barTop;
-
- if(!barnorm) {
- pts.push(barTop);
- if(bar.hasB) pts.push(barBase);
- }
- }
- }
-
- // if barnorm is set, let normalizeBars update the axis range
- if(!barnorm) {
- fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, {
- // N.B. we don't stack base with 'base',
- // so set tozero:true always!
- tozero: true,
- padded: true
- });
- }
- }
- }
-
- function sieveBars(gd, sa, sieve) {
- var calcTraces = sieve.traces;
-
- for(var i = 0; i < calcTraces.length; i++) {
- var calcTrace = calcTraces[i];
-
- for(var j = 0; j < calcTrace.length; j++) {
- var bar = calcTrace[j];
-
- if(bar.s !== BADNUM) sieve.put(bar.p, bar.b + bar.s);
- }
- }
- }
-
- // Note:
- //
- // normalizeBars requires that either sieveBars or stackBars has been
- // previously invoked.
- function normalizeBars(gd, sa, sieve) {
- var fullLayout = gd._fullLayout;
- var calcTraces = sieve.traces;
- var sLetter = getAxisLetter(sa);
- var sTop = fullLayout.barnorm === 'fraction' ? 1 : 100;
- var sTiny = sTop / 1e9; // in case of rounding error in sum
- var sMin = sa.l2c(sa.c2l(0));
- var sMax = fullLayout.barmode === 'stack' ? sTop : sMin;
-
- function needsPadding(v) {
- return (
- isNumeric(sa.c2l(v)) &&
- ((v < sMin - sTiny) || (v > sMax + sTiny) || !isNumeric(sMin))
- );
- }
-
- for(var i = 0; i < calcTraces.length; i++) {
- var calcTrace = calcTraces[i];
- var fullTrace = calcTrace[0].trace;
- var pts = [];
- var allBarBaseAboveZero = true;
- var padded = false;
-
- for(var j = 0; j < calcTrace.length; j++) {
- var bar = calcTrace[j];
-
- if(bar.s !== BADNUM) {
- var scale = Math.abs(sTop / sieve.get(bar.p, bar.s));
- bar.b *= scale;
- bar.s *= scale;
-
- var barBase = bar.b;
- var barTop = barBase + bar.s;
-
- bar[sLetter] = barTop;
- pts.push(barTop);
- padded = padded || needsPadding(barTop);
-
- if(bar.hasB) {
- pts.push(barBase);
- padded = padded || needsPadding(barBase);
- }
-
- if(!bar.hasB || !(bar.b > 0 && bar.s > 0)) {
- allBarBaseAboveZero = false;
- }
- }
- }
-
- fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, {
- tozero: !allBarBaseAboveZero,
- padded: padded
- });
- }
- }
-
- // find the full position span of bars at each position
- // for use by hover, to ensure labels move in if bars are
- // narrower than the space they're in.
- // run once per trace group (subplot & direction) and
- // the same mapping is attached to all calcdata traces
- function collectExtents(calcTraces, pa) {
- var pLetter = getAxisLetter(pa);
- var extents = {};
- var i, j, cd;
-
- var pMin = Infinity;
- var pMax = -Infinity;
-
- for(i = 0; i < calcTraces.length; i++) {
- cd = calcTraces[i];
- for(j = 0; j < cd.length; j++) {
- var p = cd[j].p;
- if(isNumeric(p)) {
- pMin = Math.min(pMin, p);
- pMax = Math.max(pMax, p);
- }
- }
- }
-
- // this is just for positioning of hover labels, and nobody will care if
- // the label is 1px too far out; so round positions to 1/10K in case
- // position values don't exactly match from trace to trace
- var roundFactor = 10000 / (pMax - pMin);
- var round = extents.round = function(p) {
- return String(Math.round(roundFactor * (p - pMin)));
- };
-
- for(i = 0; i < calcTraces.length; i++) {
- cd = calcTraces[i];
- cd[0].t.extents = extents;
-
- var poffset = cd[0].t.poffset;
- var poffsetIsArray = Array.isArray(poffset);
-
- for(j = 0; j < cd.length; j++) {
- var di = cd[j];
- var p0 = di[pLetter] - di.w / 2;
-
- if(isNumeric(p0)) {
- var p1 = di[pLetter] + di.w / 2;
- var pVal = round(di.p);
- if(extents[pVal]) {
- extents[pVal] = [Math.min(p0, extents[pVal][0]), Math.max(p1, extents[pVal][1])];
- } else {
- extents[pVal] = [p0, p1];
- }
- }
-
- di.p0 = di.p + (poffsetIsArray ? poffset[j] : poffset);
- di.p1 = di.p0 + di.w;
- di.s0 = di.b;
- di.s1 = di.s0 + di.s;
- }
- }
- }
-
- function getAxisLetter(ax) {
- return ax._id.charAt(0);
- }
-
- module.exports = {
- crossTraceCalc: crossTraceCalc,
- setGroupPositions: setGroupPositions
- };
-
- },{"../../constants/numerical":149,"../../lib":168,"../../plots/cartesian/axes":212,"../../registry":257,"./sieve.js":279,"fast-isnumeric":18}],271:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Color = _dereq_('../../components/color');
- var Registry = _dereq_('../../registry');
-
- var handleXYDefaults = _dereq_('../scatter/xy_defaults');
- var handleStyleDefaults = _dereq_('../bar/style_defaults');
- var attributes = _dereq_('./attributes');
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- var coerceFont = Lib.coerceFont;
-
- var len = handleXYDefaults(traceIn, traceOut, layout, coerce);
- if(!len) {
- traceOut.visible = false;
- return;
- }
-
- coerce('orientation', (traceOut.x && !traceOut.y) ? 'h' : 'v');
- coerce('base');
- coerce('offset');
- coerce('width');
-
- coerce('text');
- coerce('hovertext');
- coerce('hovertemplate');
-
- var textPosition = coerce('textposition');
-
- var hasBoth = Array.isArray(textPosition) || textPosition === 'auto';
- var hasInside = hasBoth || textPosition === 'inside';
- var hasOutside = hasBoth || textPosition === 'outside';
-
- if(hasInside || hasOutside) {
- var textFont = coerceFont(coerce, 'textfont', layout.font);
-
- // Note that coercing `insidetextfont` is always needed –
- // even if `textposition` is `outside` for each trace – since
- // an outside label can become an inside one, for example because
- // of a bar being stacked on top of it.
- var insideTextFontDefault = Lib.extendFlat({}, textFont);
- var isTraceTextfontColorSet = traceIn.textfont && traceIn.textfont.color;
- var isColorInheritedFromLayoutFont = !isTraceTextfontColorSet;
- if(isColorInheritedFromLayoutFont) {
- delete insideTextFontDefault.color;
- }
- coerceFont(coerce, 'insidetextfont', insideTextFontDefault);
-
- if(hasOutside) coerceFont(coerce, 'outsidetextfont', textFont);
-
- coerce('constraintext');
- coerce('selected.textfont.color');
- coerce('unselected.textfont.color');
- coerce('cliponaxis');
- }
-
- handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout);
-
- var lineColor = (traceOut.marker.line || {}).color;
-
- // override defaultColor for error bars with defaultLine
- var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
- errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'y'});
- errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'});
-
- Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
- };
-
- },{"../../components/color":51,"../../lib":168,"../../registry":257,"../bar/style_defaults":281,"../scatter/xy_defaults":393,"./attributes":267}],272:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var tinycolor = _dereq_('tinycolor2');
-
- exports.coerceString = function(attributeDefinition, value, defaultValue) {
- if(typeof value === 'string') {
- if(value || !attributeDefinition.noBlank) return value;
- } else if(typeof value === 'number') {
- if(!attributeDefinition.strict) return String(value);
- }
-
- return (defaultValue !== undefined) ?
- defaultValue :
- attributeDefinition.dflt;
- };
-
- exports.coerceNumber = function(attributeDefinition, value, defaultValue) {
- if(isNumeric(value)) {
- value = +value;
-
- var min = attributeDefinition.min;
- var max = attributeDefinition.max;
- var isOutOfBounds = (min !== undefined && value < min) ||
- (max !== undefined && value > max);
-
- if(!isOutOfBounds) return value;
- }
-
- return (defaultValue !== undefined) ?
- defaultValue :
- attributeDefinition.dflt;
- };
-
- exports.coerceColor = function(attributeDefinition, value, defaultValue) {
- if(tinycolor(value).isValid()) return value;
-
- return (defaultValue !== undefined) ?
- defaultValue :
- attributeDefinition.dflt;
- };
-
- exports.coerceEnumerated = function(attributeDefinition, value, defaultValue) {
- if(attributeDefinition.coerceNumber) value = +value;
-
- if(attributeDefinition.values.indexOf(value) !== -1) return value;
-
- return (defaultValue !== undefined) ?
- defaultValue :
- attributeDefinition.dflt;
- };
-
- exports.getValue = function(arrayOrScalar, index) {
- var value;
- if(!Array.isArray(arrayOrScalar)) value = arrayOrScalar;
- else if(index < arrayOrScalar.length) value = arrayOrScalar[index];
- return value;
- };
-
- },{"fast-isnumeric":18,"tinycolor2":34}],273:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Fx = _dereq_('../../components/fx');
- var Registry = _dereq_('../../registry');
- var Color = _dereq_('../../components/color');
- var fillHoverText = _dereq_('../scatter/fill_hover_text');
-
- function hoverPoints(pointData, xval, yval, hovermode) {
- var cd = pointData.cd;
- var trace = cd[0].trace;
- var t = cd[0].t;
- var isClosest = (hovermode === 'closest');
- var maxHoverDistance = pointData.maxHoverDistance;
- var maxSpikeDistance = pointData.maxSpikeDistance;
-
- var posVal, sizeVal, posLetter, sizeLetter, dx, dy, pRangeCalc;
-
- function thisBarMinPos(di) { return di[posLetter] - di.w / 2; }
- function thisBarMaxPos(di) { return di[posLetter] + di.w / 2; }
-
- var minPos = isClosest ?
- thisBarMinPos :
- function(di) {
- /*
- * In compare mode, accept a bar if you're on it *or* its group.
- * Nearly always it's the group that matters, but in case the bar
- * was explicitly set wider than its group we'd better accept the
- * whole bar.
- *
- * use `bardelta` instead of `bargroupwidth` so we accept hover
- * in the gap. That way hover doesn't flash on and off as you
- * mouse over the plot in compare modes.
- * In 'closest' mode though the flashing seems inevitable,
- * without far more complex logic
- */
- return Math.min(thisBarMinPos(di), di.p - t.bardelta / 2);
- };
-
- var maxPos = isClosest ?
- thisBarMaxPos :
- function(di) {
- return Math.max(thisBarMaxPos(di), di.p + t.bardelta / 2);
- };
-
- function _positionFn(_minPos, _maxPos) {
- // add a little to the pseudo-distance for wider bars, so that like scatter,
- // if you are over two overlapping bars, the narrower one wins.
- return Fx.inbox(_minPos - posVal, _maxPos - posVal,
- maxHoverDistance + Math.min(1, Math.abs(_maxPos - _minPos) / pRangeCalc) - 1);
- }
-
- function positionFn(di) {
- return _positionFn(minPos(di), maxPos(di));
- }
-
- function thisBarPositionFn(di) {
- return _positionFn(thisBarMinPos(di), thisBarMaxPos(di));
- }
-
- function sizeFn(di) {
- // add a gradient so hovering near the end of a
- // bar makes it a little closer match
- return Fx.inbox(di.b - sizeVal, di[sizeLetter] - sizeVal,
- maxHoverDistance + (di[sizeLetter] - sizeVal) / (di[sizeLetter] - di.b) - 1);
- }
-
- if(trace.orientation === 'h') {
- posVal = yval;
- sizeVal = xval;
- posLetter = 'y';
- sizeLetter = 'x';
- dx = sizeFn;
- dy = positionFn;
- }
- else {
- posVal = xval;
- sizeVal = yval;
- posLetter = 'x';
- sizeLetter = 'y';
- dy = sizeFn;
- dx = positionFn;
- }
-
- var pa = pointData[posLetter + 'a'];
- var sa = pointData[sizeLetter + 'a'];
-
- pRangeCalc = Math.abs(pa.r2c(pa.range[1]) - pa.r2c(pa.range[0]));
-
- function dxy(di) { return (dx(di) + dy(di)) / 2; }
- var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy);
- Fx.getClosest(cd, distfn, pointData);
-
- // skip the rest (for this trace) if we didn't find a close point
- if(pointData.index === false) return;
-
- // if we get here and we're not in 'closest' mode, push min/max pos back
- // onto the group - even though that means occasionally the mouse will be
- // over the hover label.
- if(!isClosest) {
- minPos = function(di) {
- return Math.min(thisBarMinPos(di), di.p - t.bargroupwidth / 2);
- };
- maxPos = function(di) {
- return Math.max(thisBarMaxPos(di), di.p + t.bargroupwidth / 2);
- };
- }
-
- // the closest data point
- var index = pointData.index;
- var di = cd[index];
-
- var size = (trace.base) ? di.b + di.s : di.s;
- pointData[sizeLetter + '0'] = pointData[sizeLetter + '1'] = sa.c2p(di[sizeLetter], true);
- pointData[sizeLetter + 'LabelVal'] = size;
-
- var extent = t.extents[t.extents.round(di.p)];
- pointData[posLetter + '0'] = pa.c2p(isClosest ? minPos(di) : extent[0], true);
- pointData[posLetter + '1'] = pa.c2p(isClosest ? maxPos(di) : extent[1], true);
- pointData[posLetter + 'LabelVal'] = di.p;
-
- // spikelines always want "closest" distance regardless of hovermode
- pointData.spikeDistance = (sizeFn(di) + thisBarPositionFn(di)) / 2 + maxSpikeDistance - maxHoverDistance;
- // they also want to point to the data value, regardless of where the label goes
- // in case of bars shifted within groups
- pointData[posLetter + 'Spike'] = pa.c2p(di.p, true);
-
- pointData.color = getTraceColor(trace, di);
- fillHoverText(di, trace, pointData);
- Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, pointData);
-
- pointData.hovertemplate = trace.hovertemplate;
- return [pointData];
- }
-
- function getTraceColor(trace, di) {
- var mc = di.mcc || trace.marker.color;
- var mlc = di.mlcc || trace.marker.line.color;
- var mlw = di.mlw || trace.marker.line.width;
-
- if(Color.opacity(mc)) return mc;
- else if(Color.opacity(mlc) && mlw) return mlc;
- }
-
- module.exports = {
- hoverPoints: hoverPoints,
- getTraceColor: getTraceColor
- };
-
- },{"../../components/color":51,"../../components/fx":90,"../../registry":257,"../scatter/fill_hover_text":375}],274:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Bar = {};
-
- Bar.attributes = _dereq_('./attributes');
- Bar.layoutAttributes = _dereq_('./layout_attributes');
- Bar.supplyDefaults = _dereq_('./defaults');
- Bar.supplyLayoutDefaults = _dereq_('./layout_defaults');
- Bar.calc = _dereq_('./calc');
- Bar.crossTraceCalc = _dereq_('./cross_trace_calc').crossTraceCalc;
- Bar.colorbar = _dereq_('../scatter/marker_colorbar');
- Bar.arraysToCalcdata = _dereq_('./arrays_to_calcdata');
- Bar.plot = _dereq_('./plot');
- Bar.style = _dereq_('./style').style;
- Bar.styleOnSelect = _dereq_('./style').styleOnSelect;
- Bar.hoverPoints = _dereq_('./hover').hoverPoints;
- Bar.selectPoints = _dereq_('./select');
-
- Bar.moduleType = 'trace';
- Bar.name = 'bar';
- Bar.basePlotModule = _dereq_('../../plots/cartesian');
- Bar.categories = ['cartesian', 'svg', 'bar', 'oriented', 'errorBarsOK', 'showLegend', 'zoomScale'];
- Bar.meta = {
-
- };
-
- module.exports = Bar;
-
- },{"../../plots/cartesian":224,"../scatter/marker_colorbar":385,"./arrays_to_calcdata":266,"./attributes":267,"./calc":268,"./cross_trace_calc":270,"./defaults":271,"./hover":273,"./layout_attributes":275,"./layout_defaults":276,"./plot":277,"./select":278,"./style":280}],275:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
-
- module.exports = {
- barmode: {
- valType: 'enumerated',
- values: ['stack', 'group', 'overlay', 'relative'],
- dflt: 'group',
-
- editType: 'calc',
-
- },
- barnorm: {
- valType: 'enumerated',
- values: ['', 'fraction', 'percent'],
- dflt: '',
-
- editType: 'calc',
-
- },
- bargap: {
- valType: 'number',
- min: 0,
- max: 1,
-
- editType: 'calc',
-
- },
- bargroupgap: {
- valType: 'number',
- min: 0,
- max: 1,
- dflt: 0,
-
- editType: 'calc',
-
- }
- };
-
- },{}],276:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var Lib = _dereq_('../../lib');
-
- var layoutAttributes = _dereq_('./layout_attributes');
-
-
- module.exports = function(layoutIn, layoutOut, fullData) {
- function coerce(attr, dflt) {
- return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
- }
-
- var hasBars = false;
- var shouldBeGapless = false;
- var gappedAnyway = false;
- var usedSubplots = {};
-
- for(var i = 0; i < fullData.length; i++) {
- var trace = fullData[i];
- if(Registry.traceIs(trace, 'bar') && trace.visible) hasBars = true;
- else continue;
-
- // if we have at least 2 grouped bar traces on the same subplot,
- // we should default to a gap anyway, even if the data is histograms
- if(layoutIn.barmode !== 'overlay' && layoutIn.barmode !== 'stack') {
- var subploti = trace.xaxis + trace.yaxis;
- if(usedSubplots[subploti]) gappedAnyway = true;
- usedSubplots[subploti] = true;
- }
-
- if(trace.visible && trace.type === 'histogram') {
- var pa = Axes.getFromId({_fullLayout: layoutOut},
- trace[trace.orientation === 'v' ? 'xaxis' : 'yaxis']);
- if(pa.type !== 'category') shouldBeGapless = true;
- }
- }
-
- if(!hasBars) return;
-
- var mode = coerce('barmode');
- if(mode !== 'overlay') coerce('barnorm');
-
- coerce('bargap', (shouldBeGapless && !gappedAnyway) ? 0 : 0.2);
- coerce('bargroupgap');
- };
-
- },{"../../lib":168,"../../plots/cartesian/axes":212,"../../registry":257,"./layout_attributes":275}],277:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Lib = _dereq_('../../lib');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
-
- var Color = _dereq_('../../components/color');
- var Drawing = _dereq_('../../components/drawing');
- var Registry = _dereq_('../../registry');
-
- var attributes = _dereq_('./attributes');
- var attributeText = attributes.text;
- var attributeTextPosition = attributes.textposition;
- var helpers = _dereq_('./helpers');
- var style = _dereq_('./style');
-
- // padding in pixels around text
- var TEXTPAD = 3;
-
- module.exports = function plot(gd, plotinfo, cdbar, barLayer) {
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
- var fullLayout = gd._fullLayout;
-
- var bartraces = Lib.makeTraceGroups(barLayer, cdbar, 'trace bars').each(function(cd) {
- var plotGroup = d3.select(this);
- var cd0 = cd[0];
- var trace = cd0.trace;
-
- if(!plotinfo.isRangePlot) cd0.node3 = plotGroup;
-
- var pointGroup = Lib.ensureSingle(plotGroup, 'g', 'points');
-
- var bars = pointGroup.selectAll('g.point').data(Lib.identity);
-
- bars.enter().append('g')
- .classed('point', true);
-
- bars.exit().remove();
-
- bars.each(function(di, i) {
- var bar = d3.select(this);
-
- // now display the bar
- // clipped xf/yf (2nd arg true): non-positive
- // log values go off-screen by plotwidth
- // so you see them continue if you drag the plot
- var x0, x1, y0, y1;
- if(trace.orientation === 'h') {
- y0 = ya.c2p(di.p0, true);
- y1 = ya.c2p(di.p1, true);
- x0 = xa.c2p(di.s0, true);
- x1 = xa.c2p(di.s1, true);
-
- // for selections
- di.ct = [x1, (y0 + y1) / 2];
- }
- else {
- x0 = xa.c2p(di.p0, true);
- x1 = xa.c2p(di.p1, true);
- y0 = ya.c2p(di.s0, true);
- y1 = ya.c2p(di.s1, true);
-
- // for selections
- di.ct = [(x0 + x1) / 2, y1];
- }
-
- if(!isNumeric(x0) || !isNumeric(x1) ||
- !isNumeric(y0) || !isNumeric(y1) ||
- x0 === x1 || y0 === y1) {
- bar.remove();
- return;
- }
-
- var lw = (di.mlw + 1 || trace.marker.line.width + 1 ||
- (di.trace ? di.trace.marker.line.width : 0) + 1) - 1;
- var offset = d3.round((lw / 2) % 1, 2);
-
- function roundWithLine(v) {
- // if there are explicit gaps, don't round,
- // it can make the gaps look crappy
- return (fullLayout.bargap === 0 && fullLayout.bargroupgap === 0) ?
- d3.round(Math.round(v) - offset, 2) : v;
- }
-
- function expandToVisible(v, vc) {
- // if it's not in danger of disappearing entirely,
- // round more precisely
- return Math.abs(v - vc) >= 2 ? roundWithLine(v) :
- // but if it's very thin, expand it so it's
- // necessarily visible, even if it might overlap
- // its neighbor
- (v > vc ? Math.ceil(v) : Math.floor(v));
- }
-
- if(!gd._context.staticPlot) {
- // if bars are not fully opaque or they have a line
- // around them, round to integer pixels, mainly for
- // safari so we prevent overlaps from its expansive
- // pixelation. if the bars ARE fully opaque and have
- // no line, expand to a full pixel to make sure we
- // can see them
- var op = Color.opacity(di.mc || trace.marker.color);
- var fixpx = (op < 1 || lw > 0.01) ? roundWithLine : expandToVisible;
- x0 = fixpx(x0, x1);
- x1 = fixpx(x1, x0);
- y0 = fixpx(y0, y1);
- y1 = fixpx(y1, y0);
- }
-
- Lib.ensureSingle(bar, 'path')
- .style('vector-effect', 'non-scaling-stroke')
- .attr('d',
- 'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z')
- .call(Drawing.setClipUrl, plotinfo.layerClipId, gd);
-
- appendBarText(gd, bar, cd, i, x0, x1, y0, y1);
-
- if(plotinfo.layerClipId) {
- Drawing.hideOutsideRangePoint(di, bar.select('text'), xa, ya, trace.xcalendar, trace.ycalendar);
- }
- });
-
- // lastly, clip points groups of `cliponaxis !== false` traces
- // on `plotinfo._hasClipOnAxisFalse === true` subplots
- var hasClipOnAxisFalse = cd0.trace.cliponaxis === false;
- Drawing.setClipUrl(plotGroup, hasClipOnAxisFalse ? null : plotinfo.layerClipId, gd);
- });
-
- // error bars are on the top
- Registry.getComponentMethod('errorbars', 'plot')(gd, bartraces, plotinfo);
- };
-
- function appendBarText(gd, bar, calcTrace, i, x0, x1, y0, y1) {
- var textPosition;
-
- function appendTextNode(bar, text, textFont) {
- var textSelection = Lib.ensureSingle(bar, 'text')
- .text(text)
- .attr({
- 'class': 'bartext bartext-' + textPosition,
- transform: '',
- 'text-anchor': 'middle',
- // prohibit tex interpretation until we can handle
- // tex and regular text together
- 'data-notex': 1
- })
- .call(Drawing.font, textFont)
- .call(svgTextUtils.convertToTspans, gd);
-
- return textSelection;
- }
-
- // get trace attributes
- var trace = calcTrace[0].trace;
- var orientation = trace.orientation;
-
- var text = getText(trace, i);
- textPosition = getTextPosition(trace, i);
-
- if(!text || textPosition === 'none') {
- bar.select('text').remove();
- return;
- }
-
- var layoutFont = gd._fullLayout.font;
- var barColor = style.getBarColor(calcTrace[i], trace);
- var insideTextFont = style.getInsideTextFont(trace, i, layoutFont, barColor);
- var outsideTextFont = style.getOutsideTextFont(trace, i, layoutFont);
-
- // compute text position
- var barmode = gd._fullLayout.barmode;
- var inStackMode = (barmode === 'stack');
- var inRelativeMode = (barmode === 'relative');
- var inStackOrRelativeMode = inStackMode || inRelativeMode;
-
- var calcBar = calcTrace[i];
- var isOutmostBar = !inStackOrRelativeMode || calcBar._outmost;
-
- var barWidth = Math.abs(x1 - x0) - 2 * TEXTPAD; // padding excluded
- var barHeight = Math.abs(y1 - y0) - 2 * TEXTPAD; // padding excluded
-
- var textSelection;
- var textBB;
- var textWidth;
- var textHeight;
-
- if(textPosition === 'outside') {
- if(!isOutmostBar && !calcBar.hasB) textPosition = 'inside';
- }
-
- if(textPosition === 'auto') {
- if(isOutmostBar) {
- // draw text using insideTextFont and check if it fits inside bar
- textPosition = 'inside';
- textSelection = appendTextNode(bar, text, insideTextFont);
-
- textBB = Drawing.bBox(textSelection.node()),
- textWidth = textBB.width,
- textHeight = textBB.height;
-
- var textHasSize = (textWidth > 0 && textHeight > 0);
- var fitsInside = (textWidth <= barWidth && textHeight <= barHeight);
- var fitsInsideIfRotated = (textWidth <= barHeight && textHeight <= barWidth);
- var fitsInsideIfShrunk = (orientation === 'h') ?
- (barWidth >= textWidth * (barHeight / textHeight)) :
- (barHeight >= textHeight * (barWidth / textWidth));
-
- if(textHasSize &&
- (fitsInside || fitsInsideIfRotated || fitsInsideIfShrunk)) {
- textPosition = 'inside';
- }
- else {
- textPosition = 'outside';
- textSelection.remove();
- textSelection = null;
- }
- }
- else textPosition = 'inside';
- }
-
- if(!textSelection) {
- textSelection = appendTextNode(bar, text,
- (textPosition === 'outside') ?
- outsideTextFont : insideTextFont);
-
- textBB = Drawing.bBox(textSelection.node()),
- textWidth = textBB.width,
- textHeight = textBB.height;
-
- if(textWidth <= 0 || textHeight <= 0) {
- textSelection.remove();
- return;
- }
- }
-
- // compute text transform
- var transform, constrained;
- if(textPosition === 'outside') {
- constrained = trace.constraintext === 'both' || trace.constraintext === 'outside';
- transform = getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB,
- orientation, constrained);
- }
- else {
- constrained = trace.constraintext === 'both' || trace.constraintext === 'inside';
- transform = getTransformToMoveInsideBar(x0, x1, y0, y1, textBB,
- orientation, constrained);
- }
-
- textSelection.attr('transform', transform);
- }
-
- function getTransformToMoveInsideBar(x0, x1, y0, y1, textBB, orientation, constrained) {
- // compute text and target positions
- var textWidth = textBB.width;
- var textHeight = textBB.height;
- var textX = (textBB.left + textBB.right) / 2;
- var textY = (textBB.top + textBB.bottom) / 2;
- var barWidth = Math.abs(x1 - x0);
- var barHeight = Math.abs(y1 - y0);
- var targetWidth;
- var targetHeight;
- var targetX;
- var targetY;
-
- // apply text padding
- var textpad;
- if(barWidth > (2 * TEXTPAD) && barHeight > (2 * TEXTPAD)) {
- textpad = TEXTPAD;
- barWidth -= 2 * textpad;
- barHeight -= 2 * textpad;
- }
- else textpad = 0;
-
- // compute rotation and scale
- var rotate,
- scale;
-
- if(textWidth <= barWidth && textHeight <= barHeight) {
- // no scale or rotation is required
- rotate = false;
- scale = 1;
- }
- else if(textWidth <= barHeight && textHeight <= barWidth) {
- // only rotation is required
- rotate = true;
- scale = 1;
- }
- else if((textWidth < textHeight) === (barWidth < barHeight)) {
- // only scale is required
- rotate = false;
- scale = constrained ? Math.min(barWidth / textWidth, barHeight / textHeight) : 1;
- }
- else {
- // both scale and rotation are required
- rotate = true;
- scale = constrained ? Math.min(barHeight / textWidth, barWidth / textHeight) : 1;
- }
-
- if(rotate) rotate = 90; // rotate clockwise
-
- // compute text and target positions
- if(rotate) {
- targetWidth = scale * textHeight;
- targetHeight = scale * textWidth;
- }
- else {
- targetWidth = scale * textWidth;
- targetHeight = scale * textHeight;
- }
-
- if(orientation === 'h') {
- if(x1 < x0) {
- // bar end is on the left hand side
- targetX = x1 + textpad + targetWidth / 2;
- targetY = (y0 + y1) / 2;
- }
- else {
- targetX = x1 - textpad - targetWidth / 2;
- targetY = (y0 + y1) / 2;
- }
- }
- else {
- if(y1 > y0) {
- // bar end is on the bottom
- targetX = (x0 + x1) / 2;
- targetY = y1 - textpad - targetHeight / 2;
- }
- else {
- targetX = (x0 + x1) / 2;
- targetY = y1 + textpad + targetHeight / 2;
- }
- }
-
- return getTransform(textX, textY, targetX, targetY, scale, rotate);
- }
-
- function getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB, orientation, constrained) {
- var barWidth = (orientation === 'h') ?
- Math.abs(y1 - y0) :
- Math.abs(x1 - x0);
- var textpad;
-
- // Keep the padding so the text doesn't sit right against
- // the bars, but don't factor it into barWidth
- if(barWidth > 2 * TEXTPAD) {
- textpad = TEXTPAD;
- }
-
- // compute rotation and scale
- var scale = 1;
- if(constrained) {
- scale = (orientation === 'h') ?
- Math.min(1, barWidth / textBB.height) :
- Math.min(1, barWidth / textBB.width);
- }
-
- // compute text and target positions
- var textX = (textBB.left + textBB.right) / 2;
- var textY = (textBB.top + textBB.bottom) / 2;
- var targetWidth;
- var targetHeight;
- var targetX;
- var targetY;
-
- targetWidth = scale * textBB.width;
- targetHeight = scale * textBB.height;
-
- if(orientation === 'h') {
- if(x1 < x0) {
- // bar end is on the left hand side
- targetX = x1 - textpad - targetWidth / 2;
- targetY = (y0 + y1) / 2;
- }
- else {
- targetX = x1 + textpad + targetWidth / 2;
- targetY = (y0 + y1) / 2;
- }
- }
- else {
- if(y1 > y0) {
- // bar end is on the bottom
- targetX = (x0 + x1) / 2;
- targetY = y1 + textpad + targetHeight / 2;
- }
- else {
- targetX = (x0 + x1) / 2;
- targetY = y1 - textpad - targetHeight / 2;
- }
- }
-
- return getTransform(textX, textY, targetX, targetY, scale, false);
- }
-
- function getTransform(textX, textY, targetX, targetY, scale, rotate) {
- var transformScale;
- var transformRotate;
- var transformTranslate;
-
- if(scale < 1) transformScale = 'scale(' + scale + ') ';
- else {
- scale = 1;
- transformScale = '';
- }
-
- transformRotate = (rotate) ?
- 'rotate(' + rotate + ' ' + textX + ' ' + textY + ') ' : '';
-
- // Note that scaling also affects the center of the text box
- var translateX = (targetX - scale * textX);
- var translateY = (targetY - scale * textY);
- transformTranslate = 'translate(' + translateX + ' ' + translateY + ')';
-
- return transformTranslate + transformScale + transformRotate;
- }
-
- function getText(trace, index) {
- var value = helpers.getValue(trace.text, index);
- return helpers.coerceString(attributeText, value);
- }
-
- function getTextPosition(trace, index) {
- var value = helpers.getValue(trace.textposition, index);
- return helpers.coerceEnumerated(attributeTextPosition, value);
- }
-
- },{"../../components/color":51,"../../components/drawing":72,"../../lib":168,"../../lib/svg_text_utils":189,"../../registry":257,"./attributes":267,"./helpers":272,"./style":280,"d3":16,"fast-isnumeric":18}],278:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = function selectPoints(searchInfo, selectionTester) {
- var cd = searchInfo.cd;
- var xa = searchInfo.xaxis;
- var ya = searchInfo.yaxis;
- var selection = [];
- var i;
-
- if(selectionTester === false) {
- // clear selection
- for(i = 0; i < cd.length; i++) {
- cd[i].selected = 0;
- }
- } else {
- for(i = 0; i < cd.length; i++) {
- var di = cd[i];
-
- if(selectionTester.contains(di.ct, false, i, searchInfo)) {
- selection.push({
- pointNumber: i,
- x: xa.c2d(di.x),
- y: ya.c2d(di.y)
- });
- di.selected = 1;
- } else {
- di.selected = 0;
- }
- }
- }
-
- return selection;
- };
-
- },{}],279:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = Sieve;
-
- var Lib = _dereq_('../../lib');
- var BADNUM = _dereq_('../../constants/numerical').BADNUM;
-
- /**
- * Helper class to sieve data from traces into bins
- *
- * @class
- * @param {Array} traces
- * Array of calculated traces
- * @param {boolean} [separateNegativeValues]
- * If true, then split data at the same position into a bar
- * for positive values and another for negative values
- * @param {boolean} [dontMergeOverlappingData]
- * If true, then don't merge overlapping bars into a single bar
- */
- function Sieve(traces, separateNegativeValues, dontMergeOverlappingData) {
- this.traces = traces;
- this.separateNegativeValues = separateNegativeValues;
- this.dontMergeOverlappingData = dontMergeOverlappingData;
-
- // for single-bin histograms - see histogram/calc
- var width1 = Infinity;
-
- var positions = [];
- for(var i = 0; i < traces.length; i++) {
- var trace = traces[i];
- for(var j = 0; j < trace.length; j++) {
- var bar = trace[j];
- if(bar.p !== BADNUM) positions.push(bar.p);
- }
- if(trace[0] && trace[0].width1) {
- width1 = Math.min(trace[0].width1, width1);
- }
- }
- this.positions = positions;
-
- var dv = Lib.distinctVals(positions);
- this.distinctPositions = dv.vals;
- if(dv.vals.length === 1 && width1 !== Infinity) this.minDiff = width1;
- else this.minDiff = Math.min(dv.minDiff, width1);
-
- this.binWidth = this.minDiff;
-
- this.bins = {};
- }
-
- /**
- * Sieve datum
- *
- * @method
- * @param {number} position
- * @param {number} value
- * @returns {number} Previous bin value
- */
- Sieve.prototype.put = function put(position, value) {
- var label = this.getLabel(position, value);
- var oldValue = this.bins[label] || 0;
-
- this.bins[label] = oldValue + value;
-
- return oldValue;
- };
-
- /**
- * Get current bin value for a given datum
- *
- * @method
- * @param {number} position Position of datum
- * @param {number} [value] Value of datum
- * (required if this.separateNegativeValues is true)
- * @returns {number} Current bin value
- */
- Sieve.prototype.get = function put(position, value) {
- var label = this.getLabel(position, value);
- return this.bins[label] || 0;
- };
-
- /**
- * Get bin label for a given datum
- *
- * @method
- * @param {number} position Position of datum
- * @param {number} [value] Value of datum
- * (required if this.separateNegativeValues is true)
- * @returns {string} Bin label
- * (prefixed with a 'v' if value is negative and this.separateNegativeValues is
- * true; otherwise prefixed with '^')
- */
- Sieve.prototype.getLabel = function getLabel(position, value) {
- var prefix = (value < 0 && this.separateNegativeValues) ? 'v' : '^';
- var label = (this.dontMergeOverlappingData) ?
- position :
- Math.round(position / this.binWidth);
- return prefix + label;
- };
-
- },{"../../constants/numerical":149,"../../lib":168}],280:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var Color = _dereq_('../../components/color');
- var Drawing = _dereq_('../../components/drawing');
- var Lib = _dereq_('../../lib');
- var Registry = _dereq_('../../registry');
-
- var attributes = _dereq_('./attributes');
- var attributeTextFont = attributes.textfont;
- var attributeInsideTextFont = attributes.insidetextfont;
- var attributeOutsideTextFont = attributes.outsidetextfont;
- var helpers = _dereq_('./helpers');
-
- function style(gd, cd) {
- var s = cd ? cd[0].node3 : d3.select(gd).selectAll('g.trace.bars');
- var barcount = s.size();
- var fullLayout = gd._fullLayout;
-
- // trace styling
- s.style('opacity', function(d) { return d[0].trace.opacity; })
-
- // for gapless (either stacked or neighboring grouped) bars use
- // crispEdges to turn off antialiasing so an artificial gap
- // isn't introduced.
- .each(function(d) {
- if((fullLayout.barmode === 'stack' && barcount > 1) ||
- (fullLayout.bargap === 0 &&
- fullLayout.bargroupgap === 0 &&
- !d[0].trace.marker.line.width)) {
- d3.select(this).attr('shape-rendering', 'crispEdges');
- }
- });
-
- s.selectAll('g.points').each(function(d) {
- var sel = d3.select(this);
- var trace = d[0].trace;
- stylePoints(sel, trace, gd);
- });
-
- Registry.getComponentMethod('errorbars', 'style')(s);
- }
-
- function stylePoints(sel, trace, gd) {
- var pts = sel.selectAll('path');
- var txs = sel.selectAll('text');
-
- Drawing.pointStyle(pts, trace, gd);
-
- txs.each(function(d) {
- var tx = d3.select(this);
- var font = determineFont(tx, d, trace, gd);
- Drawing.font(tx, font);
- });
- }
-
- function styleOnSelect(gd, cd) {
- var s = cd[0].node3;
- var trace = cd[0].trace;
-
- if(trace.selectedpoints) {
- stylePointsInSelectionMode(s, trace, gd);
- } else {
- stylePoints(s, trace, gd);
- }
- }
-
- function stylePointsInSelectionMode(s, trace, gd) {
- Drawing.selectedPointStyle(s.selectAll('path'), trace);
- styleTextInSelectionMode(s.selectAll('text'), trace, gd);
- }
-
- function styleTextInSelectionMode(txs, trace, gd) {
- txs.each(function(d) {
- var tx = d3.select(this);
- var font;
-
- if(d.selected) {
- font = Lib.extendFlat({}, determineFont(tx, d, trace, gd));
-
- var selectedFontColor = trace.selected.textfont && trace.selected.textfont.color;
- if(selectedFontColor) {
- font.color = selectedFontColor;
- }
-
- Drawing.font(tx, font);
- } else {
- Drawing.selectedTextStyle(tx, trace);
- }
- });
- }
-
- function determineFont(tx, d, trace, gd) {
- var layoutFont = gd._fullLayout.font;
- var textFont = trace.textfont;
-
- if(tx.classed('bartext-inside')) {
- var barColor = getBarColor(d, trace);
- textFont = getInsideTextFont(trace, d.i, layoutFont, barColor);
- } else if(tx.classed('bartext-outside')) {
- textFont = getOutsideTextFont(trace, d.i, layoutFont);
- }
-
- return textFont;
- }
-
- function getTextFont(trace, index, defaultValue) {
- return getFontValue(
- attributeTextFont, trace.textfont, index, defaultValue);
- }
-
- function getInsideTextFont(trace, index, layoutFont, barColor) {
- var defaultFont = getTextFont(trace, index, layoutFont);
-
- var wouldFallBackToLayoutFont =
- (trace._input.textfont === undefined || trace._input.textfont.color === undefined) ||
- (Array.isArray(trace.textfont.color) && trace.textfont.color[index] === undefined);
- if(wouldFallBackToLayoutFont) {
- defaultFont = {
- color: Color.contrast(barColor),
- family: defaultFont.family,
- size: defaultFont.size
- };
- }
-
- return getFontValue(
- attributeInsideTextFont, trace.insidetextfont, index, defaultFont);
- }
-
- function getOutsideTextFont(trace, index, layoutFont) {
- var defaultFont = getTextFont(trace, index, layoutFont);
- return getFontValue(
- attributeOutsideTextFont, trace.outsidetextfont, index, defaultFont);
- }
-
- function getFontValue(attributeDefinition, attributeValue, index, defaultValue) {
- attributeValue = attributeValue || {};
-
- var familyValue = helpers.getValue(attributeValue.family, index);
- var sizeValue = helpers.getValue(attributeValue.size, index);
- var colorValue = helpers.getValue(attributeValue.color, index);
-
- return {
- family: helpers.coerceString(
- attributeDefinition.family, familyValue, defaultValue.family),
- size: helpers.coerceNumber(
- attributeDefinition.size, sizeValue, defaultValue.size),
- color: helpers.coerceColor(
- attributeDefinition.color, colorValue, defaultValue.color)
- };
- }
-
- function getBarColor(cd, trace) {
- return cd.mc || trace.marker.color;
- }
-
- module.exports = {
- style: style,
- styleOnSelect: styleOnSelect,
- getInsideTextFont: getInsideTextFont,
- getOutsideTextFont: getOutsideTextFont,
- getBarColor: getBarColor
- };
-
- },{"../../components/color":51,"../../components/drawing":72,"../../lib":168,"../../registry":257,"./attributes":267,"./helpers":272,"d3":16}],281:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Color = _dereq_('../../components/color');
- var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
- var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
-
- module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout) {
- coerce('marker.color', defaultColor);
-
- if(hasColorscale(traceIn, 'marker')) {
- colorscaleDefaults(
- traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'}
- );
- }
-
- coerce('marker.line.color', Color.defaultLine);
-
- if(hasColorscale(traceIn, 'marker.line')) {
- colorscaleDefaults(
- traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'}
- );
- }
-
- coerce('marker.line.width');
- coerce('marker.opacity');
- coerce('selected.marker.color');
- coerce('unselected.marker.color');
- };
-
- },{"../../components/color":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62}],282:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var scatterAttrs = _dereq_('../scatter/attributes');
- var colorAttrs = _dereq_('../../components/color/attributes');
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- var scatterMarkerAttrs = scatterAttrs.marker;
- var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
-
- module.exports = {
- y: {
- valType: 'data_array',
- editType: 'calc+clearAxisTypes',
-
- },
- x: {
- valType: 'data_array',
- editType: 'calc+clearAxisTypes',
-
- },
- x0: {
- valType: 'any',
-
- editType: 'calc+clearAxisTypes',
-
- },
- y0: {
- valType: 'any',
-
- editType: 'calc+clearAxisTypes',
-
- },
- name: {
- valType: 'string',
-
- editType: 'calc+clearAxisTypes',
-
- },
- text: extendFlat({}, scatterAttrs.text, {
-
- }),
- whiskerwidth: {
- valType: 'number',
- min: 0,
- max: 1,
- dflt: 0.5,
-
- editType: 'calc',
-
- },
- notched: {
- valType: 'boolean',
-
- editType: 'calc',
-
- },
- notchwidth: {
- valType: 'number',
- min: 0,
- max: 0.5,
- dflt: 0.25,
-
- editType: 'calc',
-
- },
- boxpoints: {
- valType: 'enumerated',
- values: ['all', 'outliers', 'suspectedoutliers', false],
- dflt: 'outliers',
-
- editType: 'calc',
-
- },
- boxmean: {
- valType: 'enumerated',
- values: [true, 'sd', false],
- dflt: false,
-
- editType: 'calc',
-
- },
- jitter: {
- valType: 'number',
- min: 0,
- max: 1,
-
- editType: 'calc',
-
- },
- pointpos: {
- valType: 'number',
- min: -2,
- max: 2,
-
- editType: 'calc',
-
- },
- orientation: {
- valType: 'enumerated',
- values: ['v', 'h'],
-
- editType: 'calc+clearAxisTypes',
-
- },
-
- width: {
- valType: 'number',
- min: 0,
-
- dflt: 0,
- editType: 'calc',
-
- },
-
- marker: {
- outliercolor: {
- valType: 'color',
- dflt: 'rgba(0, 0, 0, 0)',
-
- editType: 'style',
-
- },
- symbol: extendFlat({}, scatterMarkerAttrs.symbol,
- {arrayOk: false, editType: 'plot'}),
- opacity: extendFlat({}, scatterMarkerAttrs.opacity,
- {arrayOk: false, dflt: 1, editType: 'style'}),
- size: extendFlat({}, scatterMarkerAttrs.size,
- {arrayOk: false, editType: 'calc'}),
- color: extendFlat({}, scatterMarkerAttrs.color,
- {arrayOk: false, editType: 'style'}),
- line: {
- color: extendFlat({}, scatterMarkerLineAttrs.color,
- {arrayOk: false, dflt: colorAttrs.defaultLine, editType: 'style'}
- ),
- width: extendFlat({}, scatterMarkerLineAttrs.width,
- {arrayOk: false, dflt: 0, editType: 'style'}
- ),
- outliercolor: {
- valType: 'color',
-
- editType: 'style',
-
- },
- outlierwidth: {
- valType: 'number',
- min: 0,
- dflt: 1,
-
- editType: 'style',
-
- },
- editType: 'style'
- },
- editType: 'plot'
- },
- line: {
- color: {
- valType: 'color',
-
- editType: 'style',
-
- },
- width: {
- valType: 'number',
-
- min: 0,
- dflt: 2,
- editType: 'style',
-
- },
- editType: 'plot'
- },
- fillcolor: scatterAttrs.fillcolor,
-
- selected: {
- marker: scatterAttrs.selected.marker,
- editType: 'style'
- },
- unselected: {
- marker: scatterAttrs.unselected.marker,
- editType: 'style'
- },
- hoveron: {
- valType: 'flaglist',
- flags: ['boxes', 'points'],
- dflt: 'boxes+points',
-
- editType: 'style',
-
- }
- };
-
- },{"../../components/color/attributes":50,"../../lib/extend":162,"../scatter/attributes":367}],283:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Lib = _dereq_('../../lib');
- var _ = Lib._;
- var Axes = _dereq_('../../plots/cartesian/axes');
-
- // outlier definition based on http://www.physics.csbsju.edu/stats/box2.html
- module.exports = function calc(gd, trace) {
- var fullLayout = gd._fullLayout;
- var xa = Axes.getFromId(gd, trace.xaxis || 'x');
- var ya = Axes.getFromId(gd, trace.yaxis || 'y');
- var cd = [];
-
- // N.B. violin reuses same Box.calc
- var numKey = trace.type === 'violin' ? '_numViolins' : '_numBoxes';
-
- var i;
- var valAxis, valLetter;
- var posAxis, posLetter;
-
- if(trace.orientation === 'h') {
- valAxis = xa;
- valLetter = 'x';
- posAxis = ya;
- posLetter = 'y';
- } else {
- valAxis = ya;
- valLetter = 'y';
- posAxis = xa;
- posLetter = 'x';
- }
-
- var val = valAxis.makeCalcdata(trace, valLetter);
- var pos = getPos(trace, posLetter, posAxis, val, fullLayout[numKey]);
-
- var dv = Lib.distinctVals(pos);
- var posDistinct = dv.vals;
- var dPos = dv.minDiff / 2;
- var posBins = makeBins(posDistinct, dPos);
-
- var pLen = posDistinct.length;
- var ptsPerBin = initNestedArray(pLen);
-
- // bin pts info per position bins
- for(i = 0; i < trace._length; i++) {
- var v = val[i];
- if(!isNumeric(v)) continue;
-
- var n = Lib.findBin(pos[i], posBins);
- if(n >= 0 && n < pLen) {
- var pt = {v: v, i: i};
- arraysToCalcdata(pt, trace, i);
- ptsPerBin[n].push(pt);
- }
- }
-
- var cdi;
- var ptFilterFn = (trace.boxpoints || trace.points) === 'all' ?
- Lib.identity :
- function(pt) { return (pt.v < cdi.lf || pt.v > cdi.uf); };
-
- // build calcdata trace items, one item per distinct position
- for(i = 0; i < pLen; i++) {
- if(ptsPerBin[i].length > 0) {
- var pts = ptsPerBin[i].sort(sortByVal);
- var boxVals = pts.map(extractVal);
- var bvLen = boxVals.length;
-
- cdi = {};
- cdi.pos = posDistinct[i];
- cdi.pts = pts;
-
- cdi.min = boxVals[0];
- cdi.max = boxVals[bvLen - 1];
- cdi.mean = Lib.mean(boxVals, bvLen);
- cdi.sd = Lib.stdev(boxVals, bvLen, cdi.mean);
-
- // first quartile
- cdi.q1 = Lib.interp(boxVals, 0.25);
- // median
- cdi.med = Lib.interp(boxVals, 0.5);
- // third quartile
- cdi.q3 = Lib.interp(boxVals, 0.75);
-
- // lower and upper fences - last point inside
- // 1.5 interquartile ranges from quartiles
- cdi.lf = Math.min(
- cdi.q1,
- boxVals[Math.min(
- Lib.findBin(2.5 * cdi.q1 - 1.5 * cdi.q3, boxVals, true) + 1,
- bvLen - 1
- )]
- );
- cdi.uf = Math.max(
- cdi.q3,
- boxVals[Math.max(
- Lib.findBin(2.5 * cdi.q3 - 1.5 * cdi.q1, boxVals),
- 0
- )]
- );
-
- // lower and upper outliers - 3 IQR out (don't clip to max/min,
- // this is only for discriminating suspected & far outliers)
- cdi.lo = 4 * cdi.q1 - 3 * cdi.q3;
- cdi.uo = 4 * cdi.q3 - 3 * cdi.q1;
-
- // lower and upper notches ~95% Confidence Intervals for median
- var iqr = cdi.q3 - cdi.q1;
- var mci = 1.57 * iqr / Math.sqrt(bvLen);
- cdi.ln = cdi.med - mci;
- cdi.un = cdi.med + mci;
-
- cdi.pts2 = pts.filter(ptFilterFn);
-
- cd.push(cdi);
- }
- }
-
- calcSelection(cd, trace);
- var extremes = Axes.findExtremes(valAxis, val, {padded: true});
- trace._extremes[valAxis._id] = extremes;
-
- if(cd.length > 0) {
- cd[0].t = {
- num: fullLayout[numKey],
- dPos: dPos,
- posLetter: posLetter,
- valLetter: valLetter,
- labels: {
- med: _(gd, 'median:'),
- min: _(gd, 'min:'),
- q1: _(gd, 'q1:'),
- q3: _(gd, 'q3:'),
- max: _(gd, 'max:'),
- mean: trace.boxmean === 'sd' ? _(gd, 'mean ± σ:') : _(gd, 'mean:'),
- lf: _(gd, 'lower fence:'),
- uf: _(gd, 'upper fence:')
- }
- };
-
- fullLayout[numKey]++;
- return cd;
- } else {
- return [{t: {empty: true}}];
- }
- };
-
- // In vertical (horizontal) box plots:
- // if no x (y) data, use x0 (y0), or name
- // so if you want one box
- // per trace, set x0 (y0) to the x (y) value or category for this trace
- // (or set x (y) to a constant array matching y (x))
- function getPos(trace, posLetter, posAxis, val, num) {
- if(posLetter in trace) {
- return posAxis.makeCalcdata(trace, posLetter);
- }
-
- var pos0;
-
- if(posLetter + '0' in trace) {
- pos0 = trace[posLetter + '0'];
- } else if('name' in trace && (
- posAxis.type === 'category' || (
- isNumeric(trace.name) &&
- ['linear', 'log'].indexOf(posAxis.type) !== -1
- ) || (
- Lib.isDateTime(trace.name) &&
- posAxis.type === 'date'
- )
- )) {
- pos0 = trace.name;
- } else {
- pos0 = num;
- }
-
- var pos0c = posAxis.type === 'multicategory' ?
- posAxis.r2c_just_indices(pos0) :
- posAxis.d2c(pos0, 0, trace[posLetter + 'calendar']);
-
- return val.map(function() { return pos0c; });
- }
-
- function makeBins(x, dx) {
- var len = x.length;
- var bins = new Array(len + 1);
-
- for(var i = 0; i < len; i++) {
- bins[i] = x[i] - dx;
- }
- bins[len] = x[len - 1] + dx;
-
- return bins;
- }
-
- function initNestedArray(len) {
- var arr = new Array(len);
- for(var i = 0; i < len; i++) {
- arr[i] = [];
- }
- return arr;
- }
-
- function arraysToCalcdata(pt, trace, i) {
- var trace2calc = {
- text: 'tx'
- };
-
- for(var k in trace2calc) {
- if(Array.isArray(trace[k])) {
- pt[trace2calc[k]] = trace[k][i];
- }
- }
- }
-
- function calcSelection(cd, trace) {
- if(Lib.isArrayOrTypedArray(trace.selectedpoints)) {
- for(var i = 0; i < cd.length; i++) {
- var pts = cd[i].pts || [];
- var ptNumber2cdIndex = {};
-
- for(var j = 0; j < pts.length; j++) {
- ptNumber2cdIndex[pts[j].i] = j;
- }
-
- Lib.tagSelected(pts, trace, ptNumber2cdIndex);
- }
- }
- }
-
- function sortByVal(a, b) { return a.v - b.v; }
-
- function extractVal(o) { return o.v; }
-
- },{"../../lib":168,"../../plots/cartesian/axes":212,"fast-isnumeric":18}],284:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Axes = _dereq_('../../plots/cartesian/axes');
- var Lib = _dereq_('../../lib');
-
- var orientations = ['v', 'h'];
-
- function crossTraceCalc(gd, plotinfo) {
- var calcdata = gd.calcdata;
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- for(var i = 0; i < orientations.length; i++) {
- var orientation = orientations[i];
- var posAxis = orientation === 'h' ? ya : xa;
- var boxList = [];
-
- // make list of boxes / candlesticks
- // For backward compatibility, candlesticks are treated as if they *are* box traces here
- for(var j = 0; j < calcdata.length; j++) {
- var cd = calcdata[j];
- var t = cd[0].t;
- var trace = cd[0].trace;
-
- if(trace.visible === true &&
- (trace.type === 'box' || trace.type === 'candlestick') &&
- !t.empty &&
- (trace.orientation || 'v') === orientation &&
- trace.xaxis === xa._id &&
- trace.yaxis === ya._id
- ) {
- boxList.push(j);
- }
- }
-
- setPositionOffset('box', gd, boxList, posAxis);
- }
- }
-
- function setPositionOffset(traceType, gd, boxList, posAxis) {
- var calcdata = gd.calcdata;
- var fullLayout = gd._fullLayout;
- var axId = posAxis._id;
- var axLetter = axId.charAt(0);
-
- // N.B. reused in violin
- var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes';
-
- var i, j, calcTrace;
- var pointList = [];
- var shownPts = 0;
-
- // make list of box points
- for(i = 0; i < boxList.length; i++) {
- calcTrace = calcdata[boxList[i]];
- for(j = 0; j < calcTrace.length; j++) {
- pointList.push(calcTrace[j].pos);
- shownPts += (calcTrace[j].pts2 || []).length;
- }
- }
-
- if(!pointList.length) return;
-
- // box plots - update dPos based on multiple traces
- var boxdv = Lib.distinctVals(pointList);
- var dPos0 = boxdv.minDiff / 2;
-
- // check for forced minimum dtick
- Axes.minDtick(posAxis, boxdv.minDiff, boxdv.vals[0], true);
-
- var num = fullLayout[numKey];
- var group = (fullLayout[traceType + 'mode'] === 'group' && num > 1);
- var groupFraction = 1 - fullLayout[traceType + 'gap'];
- var groupGapFraction = 1 - fullLayout[traceType + 'groupgap'];
-
- for(i = 0; i < boxList.length; i++) {
- calcTrace = calcdata[boxList[i]];
-
- var trace = calcTrace[0].trace;
- var t = calcTrace[0].t;
- var width = trace.width;
- var side = trace.side;
-
- // position coordinate delta
- var dPos;
- // box half width;
- var bdPos;
- // box center offset
- var bPos;
- // half-width within which to accept hover for this box/violin
- // always split the distance to the closest box/violin
- var wHover;
-
- if(width) {
- dPos = bdPos = wHover = width / 2;
- bPos = 0;
- } else {
- dPos = dPos0;
- bdPos = dPos * groupFraction * groupGapFraction / (group ? num : 1);
- bPos = group ? 2 * dPos * (-0.5 + (t.num + 0.5) / num) * groupFraction : 0;
- wHover = dPos * (group ? groupFraction / num : 1);
- }
- t.dPos = dPos;
- t.bPos = bPos;
- t.bdPos = bdPos;
- t.wHover = wHover;
-
- // box/violin-only value-space push value
- var pushplus;
- var pushminus;
- // edge of box/violin
- var edge = bPos + bdPos;
- var edgeplus;
- var edgeminus;
-
- if(side === 'positive') {
- pushplus = dPos * (width ? 1 : 0.5);
- edgeplus = edge;
- pushminus = edgeplus = bPos;
- } else if(side === 'negative') {
- pushplus = edgeplus = bPos;
- pushminus = dPos * (width ? 1 : 0.5);
- edgeminus = edge;
- } else {
- pushplus = pushminus = dPos;
- edgeplus = edgeminus = edge;
- }
-
- // value-space padding
- var vpadplus;
- var vpadminus;
- // pixel-space padding
- var ppadplus;
- var ppadminus;
- // do we add 5% of both sides (for points beyond box/violin)
- var padded = false;
- // does this trace show points?
- var hasPts = (trace.boxpoints || trace.points) && (shownPts > 0);
-
- if(hasPts) {
- var pointpos = trace.pointpos;
- var jitter = trace.jitter;
- var ms = trace.marker.size / 2;
-
- var pp = 0;
- if((pointpos + jitter) >= 0) {
- pp = edge * (pointpos + jitter);
- if(pp > pushplus) {
- // (++) beyond plus-value, use pp
- padded = true;
- ppadplus = ms;
- vpadplus = pp;
- } else if(pp > edgeplus) {
- // (+), use push-value (it's bigger), but add px-pad
- ppadplus = ms;
- vpadplus = pushplus;
- }
- }
- if(pp <= pushplus) {
- // (->) fallback to push value
- vpadplus = pushplus;
- }
-
- var pm = 0;
- if((pointpos - jitter) <= 0) {
- pm = -edge * (pointpos - jitter);
- if(pm > pushminus) {
- // (--) beyond plus-value, use pp
- padded = true;
- ppadminus = ms;
- vpadminus = pm;
- } else if(pm > edgeminus) {
- // (-), use push-value (it's bigger), but add px-pad
- ppadminus = ms;
- vpadminus = pushminus;
- }
- }
- if(pm <= pushminus) {
- // (<-) fallback to push value
- vpadminus = pushminus;
- }
- } else {
- vpadplus = pushplus;
- vpadminus = pushminus;
- }
-
- // calcdata[i][j] are in ascending order
- var firstPos = calcTrace[0].pos;
- var lastPos = calcTrace[calcTrace.length - 1].pos;
-
- trace._extremes[axId] = Axes.findExtremes(posAxis, [firstPos, lastPos], {
- padded: padded,
- vpadminus: vpadminus,
- vpadplus: vpadplus,
- // N.B. SVG px-space positive/negative
- ppadminus: {x: ppadminus, y: ppadplus}[axLetter],
- ppadplus: {x: ppadplus, y: ppadminus}[axLetter],
- });
- }
- }
-
- module.exports = {
- crossTraceCalc: crossTraceCalc,
- setPositionOffset: setPositionOffset
- };
-
- },{"../../lib":168,"../../plots/cartesian/axes":212}],285:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Registry = _dereq_('../../registry');
- var Color = _dereq_('../../components/color');
-
- var attributes = _dereq_('./attributes');
-
- function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- handleSampleDefaults(traceIn, traceOut, coerce, layout);
- if(traceOut.visible === false) return;
-
- coerce('line.color', (traceIn.marker || {}).color || defaultColor);
- coerce('line.width');
- coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5));
-
- coerce('whiskerwidth');
- coerce('boxmean');
- coerce('width');
-
- var notched = coerce('notched', traceIn.notchwidth !== undefined);
- if(notched) coerce('notchwidth');
-
- handlePointsDefaults(traceIn, traceOut, coerce, {prefix: 'box'});
- }
-
- function handleSampleDefaults(traceIn, traceOut, coerce, layout) {
- var y = coerce('y');
- var x = coerce('x');
- var hasX = x && x.length;
-
- var defaultOrientation, len;
-
- if(y && y.length) {
- defaultOrientation = 'v';
- if(hasX) {
- len = Math.min(Lib.minRowLength(x), Lib.minRowLength(y));
- } else {
- coerce('x0');
- len = Lib.minRowLength(y);
- }
- } else if(hasX) {
- defaultOrientation = 'h';
- coerce('y0');
- len = Lib.minRowLength(x);
- } else {
- traceOut.visible = false;
- return;
- }
- traceOut._length = len;
-
- var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
- handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
-
- coerce('orientation', defaultOrientation);
- }
-
- function handlePointsDefaults(traceIn, traceOut, coerce, opts) {
- var prefix = opts.prefix;
-
- var outlierColorDflt = Lib.coerce2(traceIn, traceOut, attributes, 'marker.outliercolor');
- var lineoutliercolor = coerce('marker.line.outliercolor');
-
- var points = coerce(
- prefix + 'points',
- (outlierColorDflt || lineoutliercolor) ? 'suspectedoutliers' : undefined
- );
-
- if(points) {
- coerce('jitter', points === 'all' ? 0.3 : 0);
- coerce('pointpos', points === 'all' ? -1.5 : 0);
-
- coerce('marker.symbol');
- coerce('marker.opacity');
- coerce('marker.size');
- coerce('marker.color', traceOut.line.color);
- coerce('marker.line.color');
- coerce('marker.line.width');
-
- if(points === 'suspectedoutliers') {
- coerce('marker.line.outliercolor', traceOut.marker.color);
- coerce('marker.line.outlierwidth');
- }
-
- coerce('selected.marker.color');
- coerce('unselected.marker.color');
- coerce('selected.marker.size');
- coerce('unselected.marker.size');
-
- coerce('text');
- } else {
- delete traceOut.marker;
- }
-
- coerce('hoveron');
-
- Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
- }
-
- module.exports = {
- supplyDefaults: supplyDefaults,
- handleSampleDefaults: handleSampleDefaults,
- handlePointsDefaults: handlePointsDefaults
- };
-
- },{"../../components/color":51,"../../lib":168,"../../registry":257,"./attributes":282}],286:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = function eventData(out, pt) {
-
- // Note: hoverOnBox property is needed for click-to-select
- // to ignore when a box was clicked. This is the reason box
- // implements this custom eventData function.
- if(pt.hoverOnBox) out.hoverOnBox = pt.hoverOnBox;
-
- if('xVal' in pt) out.x = pt.xVal;
- if('yVal' in pt) out.y = pt.yVal;
- if(pt.xa) out.xaxis = pt.xa;
- if(pt.ya) out.yaxis = pt.ya;
-
- return out;
- };
-
- },{}],287:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Axes = _dereq_('../../plots/cartesian/axes');
- var Lib = _dereq_('../../lib');
- var Fx = _dereq_('../../components/fx');
- var Color = _dereq_('../../components/color');
- var fillHoverText = _dereq_('../scatter/fill_hover_text');
-
- function hoverPoints(pointData, xval, yval, hovermode) {
- var cd = pointData.cd;
- var trace = cd[0].trace;
- var hoveron = trace.hoveron;
- var closeBoxData = [];
- var closePtData;
-
- if(hoveron.indexOf('boxes') !== -1) {
- closeBoxData = closeBoxData.concat(hoverOnBoxes(pointData, xval, yval, hovermode));
- }
-
- if(hoveron.indexOf('points') !== -1) {
- closePtData = hoverOnPoints(pointData, xval, yval);
- }
-
- // If there's a point in range and hoveron has points, show the best single point only.
- // If hoveron has boxes and there's no point in range (or hoveron doesn't have points), show the box stats.
- if(hovermode === 'closest') {
- if(closePtData) return [closePtData];
- return closeBoxData;
- }
-
- // Otherwise in compare mode, allow a point AND the box stats to be labeled
- // If there are multiple boxes in range (ie boxmode = 'overlay') we'll see stats for all of them.
- if(closePtData) {
- closeBoxData.push(closePtData);
- return closeBoxData;
- }
- return closeBoxData;
- }
-
- function hoverOnBoxes(pointData, xval, yval, hovermode) {
- var cd = pointData.cd;
- var xa = pointData.xa;
- var ya = pointData.ya;
- var trace = cd[0].trace;
- var t = cd[0].t;
- var isViolin = trace.type === 'violin';
- var closeBoxData = [];
-
- var pLetter, vLetter, pAxis, vAxis, vVal, pVal, dx, dy, dPos,
- hoverPseudoDistance, spikePseudoDistance;
-
- var boxDelta = t.bdPos;
- var posAcceptance = t.wHover;
- var shiftPos = function(di) { return di.pos + t.bPos - pVal; };
-
- if(isViolin && trace.side !== 'both') {
- if(trace.side === 'positive') {
- dPos = function(di) {
- var pos = shiftPos(di);
- return Fx.inbox(pos, pos + posAcceptance, hoverPseudoDistance);
- };
- }
- if(trace.side === 'negative') {
- dPos = function(di) {
- var pos = shiftPos(di);
- return Fx.inbox(pos - posAcceptance, pos, hoverPseudoDistance);
- };
- }
- } else {
- dPos = function(di) {
- var pos = shiftPos(di);
- return Fx.inbox(pos - posAcceptance, pos + posAcceptance, hoverPseudoDistance);
- };
- }
-
- var dVal;
-
- if(isViolin) {
- dVal = function(di) {
- return Fx.inbox(di.span[0] - vVal, di.span[1] - vVal, hoverPseudoDistance);
- };
- } else {
- dVal = function(di) {
- return Fx.inbox(di.min - vVal, di.max - vVal, hoverPseudoDistance);
- };
- }
-
- if(trace.orientation === 'h') {
- vVal = xval;
- pVal = yval;
- dx = dVal;
- dy = dPos;
- pLetter = 'y';
- pAxis = ya;
- vLetter = 'x';
- vAxis = xa;
- } else {
- vVal = yval;
- pVal = xval;
- dx = dPos;
- dy = dVal;
- pLetter = 'x';
- pAxis = xa;
- vLetter = 'y';
- vAxis = ya;
- }
-
- // if two boxes are overlaying, let the narrowest one win
- var pseudoDistance = Math.min(1, boxDelta / Math.abs(pAxis.r2c(pAxis.range[1]) - pAxis.r2c(pAxis.range[0])));
- hoverPseudoDistance = pointData.maxHoverDistance - pseudoDistance;
- spikePseudoDistance = pointData.maxSpikeDistance - pseudoDistance;
-
- function dxy(di) { return (dx(di) + dy(di)) / 2; }
- var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy);
- Fx.getClosest(cd, distfn, pointData);
-
- // skip the rest (for this trace) if we didn't find a close point
- // and create the item(s) in closedata for this point
- if(pointData.index === false) return [];
-
- var di = cd[pointData.index];
- var lc = trace.line.color;
- var mc = (trace.marker || {}).color;
-
- if(Color.opacity(lc) && trace.line.width) pointData.color = lc;
- else if(Color.opacity(mc) && trace.boxpoints) pointData.color = mc;
- else pointData.color = trace.fillcolor;
-
- pointData[pLetter + '0'] = pAxis.c2p(di.pos + t.bPos - boxDelta, true);
- pointData[pLetter + '1'] = pAxis.c2p(di.pos + t.bPos + boxDelta, true);
-
- pointData[pLetter + 'LabelVal'] = di.pos;
-
- var spikePosAttr = pLetter + 'Spike';
- pointData.spikeDistance = dxy(di) * spikePseudoDistance / hoverPseudoDistance;
- pointData[spikePosAttr] = pAxis.c2p(di.pos, true);
-
- // box plots: each "point" gets many labels
- var usedVals = {};
- var attrs = ['med', 'min', 'q1', 'q3', 'max'];
-
- if(trace.boxmean || (trace.meanline || {}).visible) {
- attrs.push('mean');
- }
- if(trace.boxpoints || trace.points) {
- attrs.push('lf', 'uf');
- }
-
- for(var i = 0; i < attrs.length; i++) {
- var attr = attrs[i];
-
- if(!(attr in di) || (di[attr] in usedVals)) continue;
- usedVals[di[attr]] = true;
-
- // copy out to a new object for each value to label
- var val = di[attr];
- var valPx = vAxis.c2p(val, true);
- var pointData2 = Lib.extendFlat({}, pointData);
-
- pointData2[vLetter + '0'] = pointData2[vLetter + '1'] = valPx;
- pointData2[vLetter + 'LabelVal'] = val;
- pointData2[vLetter + 'Label'] = (t.labels ? t.labels[attr] + ' ' : '') + Axes.hoverLabelText(vAxis, val);
-
- // Note: introduced to be able to distinguish a
- // clicked point from a box during click-to-select
- pointData2.hoverOnBox = true;
-
- if(attr === 'mean' && ('sd' in di) && trace.boxmean === 'sd') {
- pointData2[vLetter + 'err'] = di.sd;
- }
- // only keep name and spikes on the first item (median)
- pointData.name = '';
- pointData.spikeDistance = undefined;
- pointData[spikePosAttr] = undefined;
-
- closeBoxData.push(pointData2);
- }
-
- return closeBoxData;
- }
-
- function hoverOnPoints(pointData, xval, yval) {
- var cd = pointData.cd;
- var xa = pointData.xa;
- var ya = pointData.ya;
- var trace = cd[0].trace;
- var xPx = xa.c2p(xval);
- var yPx = ya.c2p(yval);
- var closePtData;
-
- var dx = function(di) {
- var rad = Math.max(3, di.mrc || 0);
- return Math.max(Math.abs(xa.c2p(di.x) - xPx) - rad, 1 - 3 / rad);
- };
- var dy = function(di) {
- var rad = Math.max(3, di.mrc || 0);
- return Math.max(Math.abs(ya.c2p(di.y) - yPx) - rad, 1 - 3 / rad);
- };
- var distfn = Fx.quadrature(dx, dy);
-
- // show one point per trace
- var ijClosest = false;
- var di, pt;
-
- for(var i = 0; i < cd.length; i++) {
- di = cd[i];
-
- for(var j = 0; j < (di.pts || []).length; j++) {
- pt = di.pts[j];
-
- var newDistance = distfn(pt);
- if(newDistance <= pointData.distance) {
- pointData.distance = newDistance;
- ijClosest = [i, j];
- }
- }
- }
-
- if(!ijClosest) return false;
-
- di = cd[ijClosest[0]];
- pt = di.pts[ijClosest[1]];
-
- var xc = xa.c2p(pt.x, true);
- var yc = ya.c2p(pt.y, true);
- var rad = pt.mrc || 1;
-
- closePtData = Lib.extendFlat({}, pointData, {
- // corresponds to index in x/y input data array
- index: pt.i,
- color: (trace.marker || {}).color,
- name: trace.name,
- x0: xc - rad,
- x1: xc + rad,
- y0: yc - rad,
- y1: yc + rad,
- spikeDistance: pointData.distance
- });
-
- var pa;
- if(trace.orientation === 'h') {
- pa = ya;
- closePtData.xLabelVal = pt.x;
- closePtData.yLabelVal = di.pos;
- } else {
- pa = xa;
- closePtData.xLabelVal = di.pos;
- closePtData.yLabelVal = pt.y;
- }
-
- var pLetter = pa._id.charAt(0);
- closePtData[pLetter + 'Spike'] = pa.c2p(di.pos, true);
-
- fillHoverText(pt, trace, closePtData);
-
- return closePtData;
- }
-
- module.exports = {
- hoverPoints: hoverPoints,
- hoverOnBoxes: hoverOnBoxes,
- hoverOnPoints: hoverOnPoints
- };
-
- },{"../../components/color":51,"../../components/fx":90,"../../lib":168,"../../plots/cartesian/axes":212,"../scatter/fill_hover_text":375}],288:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Box = {};
-
- Box.attributes = _dereq_('./attributes');
- Box.layoutAttributes = _dereq_('./layout_attributes');
- Box.supplyDefaults = _dereq_('./defaults').supplyDefaults;
- Box.supplyLayoutDefaults = _dereq_('./layout_defaults').supplyLayoutDefaults;
- Box.calc = _dereq_('./calc');
- Box.crossTraceCalc = _dereq_('./cross_trace_calc').crossTraceCalc;
- Box.plot = _dereq_('./plot').plot;
- Box.style = _dereq_('./style').style;
- Box.styleOnSelect = _dereq_('./style').styleOnSelect;
- Box.hoverPoints = _dereq_('./hover').hoverPoints;
- Box.eventData = _dereq_('./event_data');
- Box.selectPoints = _dereq_('./select');
-
- Box.moduleType = 'trace';
- Box.name = 'box';
- Box.basePlotModule = _dereq_('../../plots/cartesian');
- Box.categories = ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'boxLayout', 'zoomScale'];
- Box.meta = {
-
- };
-
- module.exports = Box;
-
- },{"../../plots/cartesian":224,"./attributes":282,"./calc":283,"./cross_trace_calc":284,"./defaults":285,"./event_data":286,"./hover":287,"./layout_attributes":289,"./layout_defaults":290,"./plot":291,"./select":292,"./style":293}],289:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
-
- module.exports = {
- boxmode: {
- valType: 'enumerated',
- values: ['group', 'overlay'],
- dflt: 'overlay',
-
- editType: 'calc',
-
- },
- boxgap: {
- valType: 'number',
- min: 0,
- max: 1,
- dflt: 0.3,
-
- editType: 'calc',
-
- },
- boxgroupgap: {
- valType: 'number',
- min: 0,
- max: 1,
- dflt: 0.3,
-
- editType: 'calc',
-
- }
- };
-
- },{}],290:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var layoutAttributes = _dereq_('./layout_attributes');
-
- function _supply(layoutIn, layoutOut, fullData, coerce, traceType) {
- var hasTraceType;
- var category = traceType + 'Layout';
- for(var i = 0; i < fullData.length; i++) {
- if(Registry.traceIs(fullData[i], category)) {
- hasTraceType = true;
- break;
- }
- }
- if(!hasTraceType) return;
-
- coerce(traceType + 'mode');
- coerce(traceType + 'gap');
- coerce(traceType + 'groupgap');
- }
-
- function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
- function coerce(attr, dflt) {
- return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
- }
- _supply(layoutIn, layoutOut, fullData, coerce, 'box');
- }
-
- module.exports = {
- supplyLayoutDefaults: supplyLayoutDefaults,
- _supply: _supply
- };
-
- },{"../../lib":168,"../../registry":257,"./layout_attributes":289}],291:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Lib = _dereq_('../../lib');
- var Drawing = _dereq_('../../components/drawing');
-
- // constants for dynamic jitter (ie less jitter for sparser points)
- var JITTERCOUNT = 5; // points either side of this to include
- var JITTERSPREAD = 0.01; // fraction of IQR to count as "dense"
-
- function plot(gd, plotinfo, cdbox, boxLayer) {
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- Lib.makeTraceGroups(boxLayer, cdbox, 'trace boxes').each(function(cd) {
- var plotGroup = d3.select(this);
- var cd0 = cd[0];
- var t = cd0.t;
- var trace = cd0.trace;
- if(!plotinfo.isRangePlot) cd0.node3 = plotGroup;
-
- // whisker width
- t.wdPos = t.bdPos * trace.whiskerwidth;
-
- if(trace.visible !== true || t.empty) {
- plotGroup.remove();
- return;
- }
-
- var posAxis, valAxis;
-
- if(trace.orientation === 'h') {
- posAxis = ya;
- valAxis = xa;
- } else {
- posAxis = xa;
- valAxis = ya;
- }
-
- plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
- plotPoints(plotGroup, {x: xa, y: ya}, trace, t);
- plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
- });
- }
-
- function plotBoxAndWhiskers(sel, axes, trace, t) {
- var posAxis = axes.pos;
- var valAxis = axes.val;
- var bPos = t.bPos;
- var wdPos = t.wdPos || 0;
- var bPosPxOffset = t.bPosPxOffset || 0;
- var whiskerWidth = trace.whiskerwidth || 0;
- var notched = trace.notched || false;
- var nw = notched ? 1 - 2 * trace.notchwidth : 1;
-
- // to support for one-sided box
- var bdPos0;
- var bdPos1;
- if(Array.isArray(t.bdPos)) {
- bdPos0 = t.bdPos[0];
- bdPos1 = t.bdPos[1];
- } else {
- bdPos0 = t.bdPos;
- bdPos1 = t.bdPos;
- }
-
- var paths = sel.selectAll('path.box').data((
- trace.type !== 'violin' ||
- trace.box.visible
- ) ? Lib.identity : []);
-
- paths.enter().append('path')
- .style('vector-effect', 'non-scaling-stroke')
- .attr('class', 'box');
-
- paths.exit().remove();
-
- paths.each(function(d) {
- if(d.empty) return 'M0,0Z';
-
- var pos = d.pos;
- var posc = posAxis.c2p(pos + bPos, true) + bPosPxOffset;
- var pos0 = posAxis.c2p(pos + bPos - bdPos0, true) + bPosPxOffset;
- var pos1 = posAxis.c2p(pos + bPos + bdPos1, true) + bPosPxOffset;
- var posw0 = posAxis.c2p(pos + bPos - wdPos, true) + bPosPxOffset;
- var posw1 = posAxis.c2p(pos + bPos + wdPos, true) + bPosPxOffset;
- var posm0 = posAxis.c2p(pos + bPos - bdPos0 * nw, true) + bPosPxOffset;
- var posm1 = posAxis.c2p(pos + bPos + bdPos1 * nw, true) + bPosPxOffset;
- var q1 = valAxis.c2p(d.q1, true);
- var q3 = valAxis.c2p(d.q3, true);
- // make sure median isn't identical to either of the
- // quartiles, so we can see it
- var m = Lib.constrain(
- valAxis.c2p(d.med, true),
- Math.min(q1, q3) + 1, Math.max(q1, q3) - 1
- );
-
- // for compatibility with box, violin, and candlestick
- // perhaps we should put this into cd0.t instead so it's more explicit,
- // but what we have now is:
- // - box always has d.lf, but boxpoints can be anything
- // - violin has d.lf and should always use it (boxpoints is undefined)
- // - candlestick has only min/max
- var useExtremes = (d.lf === undefined) || (trace.boxpoints === false);
- var lf = valAxis.c2p(useExtremes ? d.min : d.lf, true);
- var uf = valAxis.c2p(useExtremes ? d.max : d.uf, true);
- var ln = valAxis.c2p(d.ln, true);
- var un = valAxis.c2p(d.un, true);
-
- if(trace.orientation === 'h') {
- d3.select(this).attr('d',
- 'M' + m + ',' + posm0 + 'V' + posm1 + // median line
- 'M' + q1 + ',' + pos0 + 'V' + pos1 + // left edge
- (notched ? 'H' + ln + 'L' + m + ',' + posm1 + 'L' + un + ',' + pos1 : '') + // top notched edge
- 'H' + q3 + // end of the top edge
- 'V' + pos0 + // right edge
- (notched ? 'H' + un + 'L' + m + ',' + posm0 + 'L' + ln + ',' + pos0 : '') + // bottom notched edge
- 'Z' + // end of the box
- 'M' + q1 + ',' + posc + 'H' + lf + 'M' + q3 + ',' + posc + 'H' + uf + // whiskers
- ((whiskerWidth === 0) ? '' : // whisker caps
- 'M' + lf + ',' + posw0 + 'V' + posw1 + 'M' + uf + ',' + posw0 + 'V' + posw1));
- } else {
- d3.select(this).attr('d',
- 'M' + posm0 + ',' + m + 'H' + posm1 + // median line
- 'M' + pos0 + ',' + q1 + 'H' + pos1 + // top of the box
- (notched ? 'V' + ln + 'L' + posm1 + ',' + m + 'L' + pos1 + ',' + un : '') + // notched right edge
- 'V' + q3 + // end of the right edge
- 'H' + pos0 + // bottom of the box
- (notched ? 'V' + un + 'L' + posm0 + ',' + m + 'L' + pos0 + ',' + ln : '') + // notched left edge
- 'Z' + // end of the box
- 'M' + posc + ',' + q1 + 'V' + lf + 'M' + posc + ',' + q3 + 'V' + uf + // whiskers
- ((whiskerWidth === 0) ? '' : // whisker caps
- 'M' + posw0 + ',' + lf + 'H' + posw1 + 'M' + posw0 + ',' + uf + 'H' + posw1));
- }
- });
- }
-
- function plotPoints(sel, axes, trace, t) {
- var xa = axes.x;
- var ya = axes.y;
- var bdPos = t.bdPos;
- var bPos = t.bPos;
-
- // to support violin points
- var mode = trace.boxpoints || trace.points;
-
- // repeatable pseudo-random number generator
- Lib.seedPseudoRandom();
-
- // since box plot points get an extra level of nesting, each
- // box needs the trace styling info
- var fn = function(d) {
- d.forEach(function(v) {
- v.t = t;
- v.trace = trace;
- });
- return d;
- };
-
- var gPoints = sel.selectAll('g.points')
- .data(mode ? fn : []);
-
- gPoints.enter().append('g')
- .attr('class', 'points');
-
- gPoints.exit().remove();
-
- var paths = gPoints.selectAll('path')
- .data(function(d) {
- var i;
- var pts = d.pts2;
-
- // normally use IQR, but if this is 0 or too small, use max-min
- var typicalSpread = Math.max((d.max - d.min) / 10, d.q3 - d.q1);
- var minSpread = typicalSpread * 1e-9;
- var spreadLimit = typicalSpread * JITTERSPREAD;
- var jitterFactors = [];
- var maxJitterFactor = 0;
- var newJitter;
-
- // dynamic jitter
- if(trace.jitter) {
- if(typicalSpread === 0) {
- // edge case of no spread at all: fall back to max jitter
- maxJitterFactor = 1;
- jitterFactors = new Array(pts.length);
- for(i = 0; i < pts.length; i++) {
- jitterFactors[i] = 1;
- }
- } else {
- for(i = 0; i < pts.length; i++) {
- var i0 = Math.max(0, i - JITTERCOUNT);
- var pmin = pts[i0].v;
- var i1 = Math.min(pts.length - 1, i + JITTERCOUNT);
- var pmax = pts[i1].v;
-
- if(mode !== 'all') {
- if(pts[i].v < d.lf) pmax = Math.min(pmax, d.lf);
- else pmin = Math.max(pmin, d.uf);
- }
-
- var jitterFactor = Math.sqrt(spreadLimit * (i1 - i0) / (pmax - pmin + minSpread)) || 0;
- jitterFactor = Lib.constrain(Math.abs(jitterFactor), 0, 1);
-
- jitterFactors.push(jitterFactor);
- maxJitterFactor = Math.max(jitterFactor, maxJitterFactor);
- }
- }
- newJitter = trace.jitter * 2 / (maxJitterFactor || 1);
- }
-
- // fills in 'x' and 'y' in calcdata 'pts' item
- for(i = 0; i < pts.length; i++) {
- var pt = pts[i];
- var v = pt.v;
-
- var jitterOffset = trace.jitter ?
- (newJitter * jitterFactors[i] * (Lib.pseudoRandom() - 0.5)) :
- 0;
-
- var posPx = d.pos + bPos + bdPos * (trace.pointpos + jitterOffset);
-
- if(trace.orientation === 'h') {
- pt.y = posPx;
- pt.x = v;
- } else {
- pt.x = posPx;
- pt.y = v;
- }
-
- // tag suspected outliers
- if(mode === 'suspectedoutliers' && v < d.uo && v > d.lo) {
- pt.so = true;
- }
- }
-
- return pts;
- });
-
- paths.enter().append('path')
- .classed('point', true);
-
- paths.exit().remove();
-
- paths.call(Drawing.translatePoints, xa, ya);
- }
-
- function plotBoxMean(sel, axes, trace, t) {
- var posAxis = axes.pos;
- var valAxis = axes.val;
- var bPos = t.bPos;
- var bPosPxOffset = t.bPosPxOffset || 0;
-
- // to support violin mean lines
- var mode = trace.boxmean || (trace.meanline || {}).visible;
-
- // to support for one-sided box
- var bdPos0;
- var bdPos1;
- if(Array.isArray(t.bdPos)) {
- bdPos0 = t.bdPos[0];
- bdPos1 = t.bdPos[1];
- } else {
- bdPos0 = t.bdPos;
- bdPos1 = t.bdPos;
- }
-
- var paths = sel.selectAll('path.mean').data((
- (trace.type === 'box' && trace.boxmean) ||
- (trace.type === 'violin' && trace.box.visible && trace.meanline.visible)
- ) ? Lib.identity : []);
-
- paths.enter().append('path')
- .attr('class', 'mean')
- .style({
- fill: 'none',
- 'vector-effect': 'non-scaling-stroke'
- });
-
- paths.exit().remove();
-
- paths.each(function(d) {
- var posc = posAxis.c2p(d.pos + bPos, true) + bPosPxOffset;
- var pos0 = posAxis.c2p(d.pos + bPos - bdPos0, true) + bPosPxOffset;
- var pos1 = posAxis.c2p(d.pos + bPos + bdPos1, true) + bPosPxOffset;
- var m = valAxis.c2p(d.mean, true);
- var sl = valAxis.c2p(d.mean - d.sd, true);
- var sh = valAxis.c2p(d.mean + d.sd, true);
-
- if(trace.orientation === 'h') {
- d3.select(this).attr('d',
- 'M' + m + ',' + pos0 + 'V' + pos1 +
- (mode === 'sd' ?
- 'm0,0L' + sl + ',' + posc + 'L' + m + ',' + pos0 + 'L' + sh + ',' + posc + 'Z' :
- '')
- );
- } else {
- d3.select(this).attr('d',
- 'M' + pos0 + ',' + m + 'H' + pos1 +
- (mode === 'sd' ?
- 'm0,0L' + posc + ',' + sl + 'L' + pos0 + ',' + m + 'L' + posc + ',' + sh + 'Z' :
- '')
- );
- }
- });
- }
-
- module.exports = {
- plot: plot,
- plotBoxAndWhiskers: plotBoxAndWhiskers,
- plotPoints: plotPoints,
- plotBoxMean: plotBoxMean
- };
-
- },{"../../components/drawing":72,"../../lib":168,"d3":16}],292:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = function selectPoints(searchInfo, selectionTester) {
- var cd = searchInfo.cd;
- var xa = searchInfo.xaxis;
- var ya = searchInfo.yaxis;
- var selection = [];
- var i, j;
-
- if(selectionTester === false) {
- for(i = 0; i < cd.length; i++) {
- for(j = 0; j < (cd[i].pts || []).length; j++) {
- // clear selection
- cd[i].pts[j].selected = 0;
- }
- }
- } else {
- for(i = 0; i < cd.length; i++) {
- for(j = 0; j < (cd[i].pts || []).length; j++) {
- var pt = cd[i].pts[j];
- var x = xa.c2p(pt.x);
- var y = ya.c2p(pt.y);
-
- if(selectionTester.contains([x, y], null, pt.i, searchInfo)) {
- selection.push({
- pointNumber: pt.i,
- x: xa.c2d(pt.x),
- y: ya.c2d(pt.y)
- });
- pt.selected = 1;
- } else {
- pt.selected = 0;
- }
- }
- }
- }
-
- return selection;
- };
-
- },{}],293:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var Color = _dereq_('../../components/color');
- var Drawing = _dereq_('../../components/drawing');
-
- function style(gd, cd) {
- var s = cd ? cd[0].node3 : d3.select(gd).selectAll('g.trace.boxes');
-
- s.style('opacity', function(d) { return d[0].trace.opacity; });
-
- s.each(function(d) {
- var el = d3.select(this);
- var trace = d[0].trace;
- var lineWidth = trace.line.width;
-
- function styleBox(boxSel, lineWidth, lineColor, fillColor) {
- boxSel.style('stroke-width', lineWidth + 'px')
- .call(Color.stroke, lineColor)
- .call(Color.fill, fillColor);
- }
-
- var allBoxes = el.selectAll('path.box');
-
- if(trace.type === 'candlestick') {
- allBoxes.each(function(boxData) {
- if(boxData.empty) return;
-
- var thisBox = d3.select(this);
- var container = trace[boxData.dir]; // dir = 'increasing' or 'decreasing'
- styleBox(thisBox, container.line.width, container.line.color, container.fillcolor);
- // TODO: custom selection style for candlesticks
- thisBox.style('opacity', trace.selectedpoints && !boxData.selected ? 0.3 : 1);
- });
- } else {
- styleBox(allBoxes, lineWidth, trace.line.color, trace.fillcolor);
- el.selectAll('path.mean')
- .style({
- 'stroke-width': lineWidth,
- 'stroke-dasharray': (2 * lineWidth) + 'px,' + lineWidth + 'px'
- })
- .call(Color.stroke, trace.line.color);
-
- var pts = el.selectAll('path.point');
- Drawing.pointStyle(pts, trace, gd);
- }
- });
- }
-
- function styleOnSelect(gd, cd) {
- var s = cd[0].node3;
- var trace = cd[0].trace;
- var pts = s.selectAll('path.point');
-
- if(trace.selectedpoints) {
- Drawing.selectedPointStyle(pts, trace);
- } else {
- Drawing.pointStyle(pts, trace, gd);
- }
- }
-
- module.exports = {
- style: style,
- styleOnSelect: styleOnSelect
- };
-
- },{"../../components/color":51,"../../components/drawing":72,"d3":16}],294:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var heatmapAttrs = _dereq_('../heatmap/attributes');
- var scatterAttrs = _dereq_('../scatter/attributes');
- var colorscaleAttrs = _dereq_('../../components/colorscale/attributes');
- var colorbarAttrs = _dereq_('../../components/colorbar/attributes');
- var dash = _dereq_('../../components/drawing/attributes').dash;
- var fontAttrs = _dereq_('../../plots/font_attributes');
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- var filterOps = _dereq_('../../constants/filter_ops');
- var COMPARISON_OPS2 = filterOps.COMPARISON_OPS2;
- var INTERVAL_OPS = filterOps.INTERVAL_OPS;
-
- var scatterLineAttrs = scatterAttrs.line;
-
- module.exports = extendFlat({
- z: heatmapAttrs.z,
- x: heatmapAttrs.x,
- x0: heatmapAttrs.x0,
- dx: heatmapAttrs.dx,
- y: heatmapAttrs.y,
- y0: heatmapAttrs.y0,
- dy: heatmapAttrs.dy,
- text: heatmapAttrs.text,
- transpose: heatmapAttrs.transpose,
- xtype: heatmapAttrs.xtype,
- ytype: heatmapAttrs.ytype,
- zhoverformat: heatmapAttrs.zhoverformat,
-
- connectgaps: heatmapAttrs.connectgaps,
-
- fillcolor: {
- valType: 'color',
-
- editType: 'calc',
-
- },
-
- autocontour: {
- valType: 'boolean',
- dflt: true,
-
- editType: 'calc',
- impliedEdits: {
- 'contours.start': undefined,
- 'contours.end': undefined,
- 'contours.size': undefined
- },
-
- },
- ncontours: {
- valType: 'integer',
- dflt: 15,
- min: 1,
-
- editType: 'calc',
-
- },
-
- contours: {
- type: {
- valType: 'enumerated',
- values: ['levels', 'constraint'],
- dflt: 'levels',
-
- editType: 'calc',
-
- },
- start: {
- valType: 'number',
- dflt: null,
-
- editType: 'plot',
- impliedEdits: {'^autocontour': false},
-
- },
- end: {
- valType: 'number',
- dflt: null,
-
- editType: 'plot',
- impliedEdits: {'^autocontour': false},
-
- },
- size: {
- valType: 'number',
- dflt: null,
- min: 0,
-
- editType: 'plot',
- impliedEdits: {'^autocontour': false},
-
- },
- coloring: {
- valType: 'enumerated',
- values: ['fill', 'heatmap', 'lines', 'none'],
- dflt: 'fill',
-
- editType: 'calc',
-
- },
- showlines: {
- valType: 'boolean',
- dflt: true,
-
- editType: 'plot',
-
- },
- showlabels: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'plot',
-
- },
- labelfont: fontAttrs({
- editType: 'plot',
- colorEditType: 'style',
-
- }),
- labelformat: {
- valType: 'string',
- dflt: '',
-
- editType: 'plot',
-
- },
- operation: {
- valType: 'enumerated',
- values: [].concat(COMPARISON_OPS2).concat(INTERVAL_OPS),
-
- dflt: '=',
- editType: 'calc',
-
- },
- value: {
- valType: 'any',
- dflt: 0,
-
- editType: 'calc',
-
- },
- editType: 'calc',
- impliedEdits: {'autocontour': false}
- },
-
- line: {
- color: extendFlat({}, scatterLineAttrs.color, {
- editType: 'style+colorbars',
-
- }),
- width: extendFlat({}, scatterLineAttrs.width, {
- editType: 'style+colorbars'
- }),
- dash: dash,
- smoothing: extendFlat({}, scatterLineAttrs.smoothing, {
-
- }),
- editType: 'plot'
- }
- },
- colorscaleAttrs('', {
- cLetter: 'z',
- autoColorDflt: false,
- editTypeOverride: 'calc'
- }),
- { colorbar: colorbarAttrs }
- );
-
- },{"../../components/colorbar/attributes":52,"../../components/colorscale/attributes":58,"../../components/drawing/attributes":71,"../../constants/filter_ops":147,"../../lib/extend":162,"../../plots/font_attributes":239,"../heatmap/attributes":316,"../scatter/attributes":367}],295:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var heatmapCalc = _dereq_('../heatmap/calc');
- var setContours = _dereq_('./set_contours');
-
- // most is the same as heatmap calc, then adjust it
- // though a few things inside heatmap calc still look for
- // contour maps, because the makeBoundArray calls are too entangled
- module.exports = function calc(gd, trace) {
- var cd = heatmapCalc(gd, trace);
- setContours(trace);
- return cd;
- };
-
- },{"../heatmap/calc":317,"./set_contours":313}],296:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = function(pathinfo, operation, perimeter, trace) {
- // Abandon all hope, ye who enter here.
- var i, v1, v2;
- var pi0 = pathinfo[0];
- var na = pi0.x.length;
- var nb = pi0.y.length;
- var z = pi0.z;
- var contours = trace.contours;
-
- var boundaryMax = -Infinity;
- var boundaryMin = Infinity;
-
- for(i = 0; i < nb; i++) {
- boundaryMin = Math.min(boundaryMin, z[i][0]);
- boundaryMin = Math.min(boundaryMin, z[i][na - 1]);
- boundaryMax = Math.max(boundaryMax, z[i][0]);
- boundaryMax = Math.max(boundaryMax, z[i][na - 1]);
- }
-
- for(i = 1; i < na - 1; i++) {
- boundaryMin = Math.min(boundaryMin, z[0][i]);
- boundaryMin = Math.min(boundaryMin, z[nb - 1][i]);
- boundaryMax = Math.max(boundaryMax, z[0][i]);
- boundaryMax = Math.max(boundaryMax, z[nb - 1][i]);
- }
-
- pi0.prefixBoundary = false;
-
- switch(operation) {
- case '>':
- if(contours.value > boundaryMax) {
- pi0.prefixBoundary = true;
- }
- break;
- case '<':
- if(contours.value < boundaryMin) {
- pi0.prefixBoundary = true;
- }
- break;
- case '[]':
- v1 = Math.min.apply(null, contours.value);
- v2 = Math.max.apply(null, contours.value);
- if(v2 < boundaryMin || v1 > boundaryMax) {
- pi0.prefixBoundary = true;
- }
- break;
- case '][':
- v1 = Math.min.apply(null, contours.value);
- v2 = Math.max.apply(null, contours.value);
- if(v1 < boundaryMin && v2 > boundaryMax) {
- pi0.prefixBoundary = true;
- }
- break;
- }
- };
-
- },{}],297:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var drawColorbar = _dereq_('../../components/colorbar/draw');
-
- var makeColorMap = _dereq_('./make_color_map');
- var endPlus = _dereq_('./end_plus');
-
-
- module.exports = function colorbar(gd, cd) {
- var trace = cd[0].trace;
- var cbId = 'cb' + trace.uid;
-
- gd._fullLayout._infolayer.selectAll('.' + cbId).remove();
-
- if(!trace.showscale) return;
-
- var cb = cd[0].t.cb = drawColorbar(gd, cbId);
-
- var contours = trace.contours;
- var line = trace.line;
- var cs = contours.size || 1;
- var coloring = contours.coloring;
-
- var colorMap = makeColorMap(trace, {isColorbar: true});
-
- cb.fillgradient(coloring === 'heatmap' ? trace.colorscale : '')
- .zrange(coloring === 'heatmap' ? [trace.zmin, trace.zmax] : '')
- .fillcolor((coloring === 'fill') ? colorMap : '')
- .line({
- color: coloring === 'lines' ? colorMap : line.color,
- width: contours.showlines !== false ? line.width : 0,
- dash: line.dash
- })
- .levels({
- start: contours.start,
- end: endPlus(contours),
- size: cs
- })
- .options(trace.colorbar)();
- };
-
- },{"../../components/colorbar/draw":56,"./end_plus":305,"./make_color_map":310}],298:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
- module.exports = {
- // some constants to help with marching squares algorithm
- // where does the path start for each index?
- BOTTOMSTART: [1, 9, 13, 104, 713],
- TOPSTART: [4, 6, 7, 104, 713],
- LEFTSTART: [8, 12, 14, 208, 1114],
- RIGHTSTART: [2, 3, 11, 208, 1114],
-
- // which way [dx,dy] do we leave a given index?
- // saddles are already disambiguated
- NEWDELTA: [
- null, [-1, 0], [0, -1], [-1, 0],
- [1, 0], null, [0, -1], [-1, 0],
- [0, 1], [0, 1], null, [0, 1],
- [1, 0], [1, 0], [0, -1]
- ],
-
- // for each saddle, the first index here is used
- // for dx||dy<0, the second for dx||dy>0
- CHOOSESADDLE: {
- 104: [4, 1],
- 208: [2, 8],
- 713: [7, 13],
- 1114: [11, 14]
- },
-
- // after one index has been used for a saddle, which do we
- // substitute to be used up later?
- SADDLEREMAINDER: {1: 4, 2: 8, 4: 1, 7: 13, 8: 2, 11: 14, 13: 7, 14: 11},
-
- // length of a contour, as a multiple of the plot area diagonal, per label
- LABELDISTANCE: 2,
-
- // number of contour levels after which we start increasing the number of
- // labels we draw. Many contours means they will generally be close
- // together, so it will be harder to follow a long way to find a label
- LABELINCREASE: 10,
-
- // minimum length of a contour line, as a multiple of the label length,
- // at which we draw *any* labels
- LABELMIN: 3,
-
- // max number of labels to draw on a single contour path, no matter how long
- LABELMAX: 10,
-
- // constants for the label position cost function
- LABELOPTIMIZER: {
- // weight given to edge proximity
- EDGECOST: 1,
- // weight given to the angle off horizontal
- ANGLECOST: 1,
- // weight given to distance from already-placed labels
- NEIGHBORCOST: 5,
- // cost multiplier for labels on the same level
- SAMELEVELFACTOR: 10,
- // minimum distance (as a multiple of the label length)
- // for labels on the same level
- SAMELEVELDISTANCE: 5,
- // maximum cost before we won't even place the label
- MAXCOST: 100,
- // number of evenly spaced points to look at in the first
- // iteration of the search
- INITIALSEARCHPOINTS: 10,
- // number of binary search iterations after the initial wide search
- ITERATIONS: 5
- }
- };
-
- },{}],299:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
- var isNumeric = _dereq_('fast-isnumeric');
-
- var handleLabelDefaults = _dereq_('./label_defaults');
-
- var Color = _dereq_('../../components/color');
- var addOpacity = Color.addOpacity;
- var opacity = Color.opacity;
-
- var filterOps = _dereq_('../../constants/filter_ops');
- var CONSTRAINT_REDUCTION = filterOps.CONSTRAINT_REDUCTION;
- var COMPARISON_OPS2 = filterOps.COMPARISON_OPS2;
-
- module.exports = function handleConstraintDefaults(traceIn, traceOut, coerce, layout, defaultColor, opts) {
- var contours = traceOut.contours;
- var showLines, lineColor, fillColor;
-
- var operation = coerce('contours.operation');
- contours._operation = CONSTRAINT_REDUCTION[operation];
-
- handleConstraintValueDefaults(coerce, contours);
-
- if(operation === '=') {
- showLines = contours.showlines = true;
- }
- else {
- showLines = coerce('contours.showlines');
- fillColor = coerce('fillcolor', addOpacity(
- (traceIn.line || {}).color || defaultColor, 0.5
- ));
- }
-
- if(showLines) {
- var lineDfltColor = fillColor && opacity(fillColor) ?
- addOpacity(traceOut.fillcolor, 1) :
- defaultColor;
- lineColor = coerce('line.color', lineDfltColor);
- coerce('line.width', 2);
- coerce('line.dash');
- }
-
- coerce('line.smoothing');
-
- handleLabelDefaults(coerce, layout, lineColor, opts);
- };
-
- function handleConstraintValueDefaults(coerce, contours) {
- var zvalue;
-
- if(COMPARISON_OPS2.indexOf(contours.operation) === -1) {
- // Requires an array of two numbers:
- coerce('contours.value', [0, 1]);
-
- if(!Array.isArray(contours.value)) {
- if(isNumeric(contours.value)) {
- zvalue = parseFloat(contours.value);
- contours.value = [zvalue, zvalue + 1];
- }
- } else if(contours.value.length > 2) {
- contours.value = contours.value.slice(2);
- } else if(contours.length === 0) {
- contours.value = [0, 1];
- } else if(contours.length < 2) {
- zvalue = parseFloat(contours.value[0]);
- contours.value = [zvalue, zvalue + 1];
- } else {
- contours.value = [
- parseFloat(contours.value[0]),
- parseFloat(contours.value[1])
- ];
- }
- } else {
- // Requires a single scalar:
- coerce('contours.value', 0);
-
- if(!isNumeric(contours.value)) {
- if(Array.isArray(contours.value)) {
- contours.value = parseFloat(contours.value[0]);
- } else {
- contours.value = 0;
- }
- }
- }
- }
-
- },{"../../components/color":51,"../../constants/filter_ops":147,"./label_defaults":309,"fast-isnumeric":18}],300:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var filterOps = _dereq_('../../constants/filter_ops');
- var isNumeric = _dereq_('fast-isnumeric');
-
- // This syntax conforms to the existing filter transform syntax, but we don't care
- // about open vs. closed intervals for simply drawing contours constraints:
- module.exports = {
- '[]': makeRangeSettings('[]'),
- '][': makeRangeSettings(']['),
- '>': makeInequalitySettings('>'),
- '<': makeInequalitySettings('<'),
- '=': makeInequalitySettings('=')
- };
-
- // This does not in any way shape or form support calendars. It's adapted from
- // transforms/filter.js.
- function coerceValue(operation, value) {
- var hasArrayValue = Array.isArray(value);
-
- var coercedValue;
-
- function coerce(value) {
- return isNumeric(value) ? (+value) : null;
- }
-
- if(filterOps.COMPARISON_OPS2.indexOf(operation) !== -1) {
- coercedValue = hasArrayValue ? coerce(value[0]) : coerce(value);
- } else if(filterOps.INTERVAL_OPS.indexOf(operation) !== -1) {
- coercedValue = hasArrayValue ?
- [coerce(value[0]), coerce(value[1])] :
- [coerce(value), coerce(value)];
- } else if(filterOps.SET_OPS.indexOf(operation) !== -1) {
- coercedValue = hasArrayValue ? value.map(coerce) : [coerce(value)];
- }
-
- return coercedValue;
- }
-
- // Returns a parabola scaled so that the min/max is either +/- 1 and zero at the two values
- // provided. The data is mapped by this function when constructing intervals so that it's
- // very easy to construct contours as normal.
- function makeRangeSettings(operation) {
- return function(value) {
- value = coerceValue(operation, value);
-
- // Ensure proper ordering:
- var min = Math.min(value[0], value[1]);
- var max = Math.max(value[0], value[1]);
-
- return {
- start: min,
- end: max,
- size: max - min
- };
- };
- }
-
- function makeInequalitySettings(operation) {
- return function(value) {
- value = coerceValue(operation, value);
-
- return {
- start: value,
- end: Infinity,
- size: Infinity
- };
- };
- }
-
- },{"../../constants/filter_ops":147,"fast-isnumeric":18}],301:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = function handleContourDefaults(traceIn, traceOut, coerce, coerce2) {
- var contourStart = coerce2('contours.start');
- var contourEnd = coerce2('contours.end');
- var missingEnd = (contourStart === false) || (contourEnd === false);
-
- // normally we only need size if autocontour is off. But contour.calc
- // pushes its calculated contour size back to the input trace, so for
- // things like restyle that can call supplyDefaults without calc
- // after the initial draw, we can just reuse the previous calculation
- var contourSize = coerce('contours.size');
- var autoContour;
-
- if(missingEnd) autoContour = traceOut.autocontour = true;
- else autoContour = coerce('autocontour', false);
-
- if(autoContour || !contourSize) coerce('ncontours');
- };
-
- },{}],302:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- // The contour extraction is great, except it totally fails for constraints because we
- // need weird range loops and flipped contours instead of the usual format. This function
- // does some weird manipulation of the extracted pathinfo data such that it magically
- // draws contours correctly *as* constraints.
- module.exports = function(pathinfo, operation) {
- var i, pi0, pi1;
-
- var op0 = function(arr) { return arr.reverse(); };
- var op1 = function(arr) { return arr; };
-
- switch(operation) {
- case '=':
- case '<':
- return pathinfo;
- case '>':
- if(pathinfo.length !== 1) {
- Lib.warn('Contour data invalid for the specified inequality operation.');
- }
-
- // In this case there should be exactly two contour levels in pathinfo. We
- // simply concatenate the info into one pathinfo and flip all of the data
- // in one. This will draw the contour as closed.
- pi0 = pathinfo[0];
-
- for(i = 0; i < pi0.edgepaths.length; i++) {
- pi0.edgepaths[i] = op0(pi0.edgepaths[i]);
- }
-
- for(i = 0; i < pi0.paths.length; i++) {
- pi0.paths[i] = op0(pi0.paths[i]);
- }
- return pathinfo;
- case '][':
- var tmp = op0;
- op0 = op1;
- op1 = tmp;
- // It's a nice rule, except this definitely *is* what's intended here.
- /* eslint-disable: no-fallthrough */
- case '[]':
- /* eslint-enable: no-fallthrough */
- if(pathinfo.length !== 2) {
- Lib.warn('Contour data invalid for the specified inequality range operation.');
- }
-
- // In this case there should be exactly two contour levels in pathinfo. We
- // simply concatenate the info into one pathinfo and flip all of the data
- // in one. This will draw the contour as closed.
- pi0 = copyPathinfo(pathinfo[0]);
- pi1 = copyPathinfo(pathinfo[1]);
-
- for(i = 0; i < pi0.edgepaths.length; i++) {
- pi0.edgepaths[i] = op0(pi0.edgepaths[i]);
- }
-
- for(i = 0; i < pi0.paths.length; i++) {
- pi0.paths[i] = op0(pi0.paths[i]);
- }
-
- while(pi1.edgepaths.length) {
- pi0.edgepaths.push(op1(pi1.edgepaths.shift()));
- }
- while(pi1.paths.length) {
- pi0.paths.push(op1(pi1.paths.shift()));
- }
- return [pi0];
- }
- };
-
- function copyPathinfo(pi) {
- return Lib.extendFlat({}, pi, {
- edgepaths: Lib.extendDeep([], pi.edgepaths),
- paths: Lib.extendDeep([], pi.paths)
- });
- }
-
- },{"../../lib":168}],303:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- var handleXYZDefaults = _dereq_('../heatmap/xyz_defaults');
- var handleConstraintDefaults = _dereq_('./constraint_defaults');
- var handleContoursDefaults = _dereq_('./contours_defaults');
- var handleStyleDefaults = _dereq_('./style_defaults');
- var attributes = _dereq_('./attributes');
-
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- function coerce2(attr) {
- return Lib.coerce2(traceIn, traceOut, attributes, attr);
- }
-
- var len = handleXYZDefaults(traceIn, traceOut, coerce, layout);
- if(!len) {
- traceOut.visible = false;
- return;
- }
-
- coerce('text');
- var isConstraint = (coerce('contours.type') === 'constraint');
- coerce('connectgaps', Lib.isArray1D(traceOut.z));
-
- if(isConstraint) {
- handleConstraintDefaults(traceIn, traceOut, coerce, layout, defaultColor);
- }
- else {
- handleContoursDefaults(traceIn, traceOut, coerce, coerce2);
- handleStyleDefaults(traceIn, traceOut, coerce, layout);
- }
- };
-
- },{"../../lib":168,"../heatmap/xyz_defaults":330,"./attributes":294,"./constraint_defaults":299,"./contours_defaults":301,"./style_defaults":315}],304:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var constraintMapping = _dereq_('./constraint_mapping');
- var endPlus = _dereq_('./end_plus');
-
- module.exports = function emptyPathinfo(contours, plotinfo, cd0) {
- var contoursFinal = (contours.type === 'constraint') ?
- constraintMapping[contours._operation](contours.value) :
- contours;
-
- var cs = contoursFinal.size;
- var pathinfo = [];
- var end = endPlus(contoursFinal);
-
- var carpet = cd0.trace._carpetTrace;
-
- var basePathinfo = carpet ? {
- // store axes so we can convert to px
- xaxis: carpet.aaxis,
- yaxis: carpet.baxis,
- // full data arrays to use for interpolation
- x: cd0.a,
- y: cd0.b
- } : {
- xaxis: plotinfo.xaxis,
- yaxis: plotinfo.yaxis,
- x: cd0.x,
- y: cd0.y
- };
-
- for(var ci = contoursFinal.start; ci < end; ci += cs) {
- pathinfo.push(Lib.extendFlat({
- level: ci,
- // all the cells with nontrivial marching index
- crossings: {},
- // starting points on the edges of the lattice for each contour
- starts: [],
- // all unclosed paths (may have less items than starts,
- // if a path is closed by rounding)
- edgepaths: [],
- // all closed paths
- paths: [],
- z: cd0.z,
- smoothing: cd0.trace.line.smoothing
- }, basePathinfo));
-
- if(pathinfo.length > 1000) {
- Lib.warn('Too many contours, clipping at 1000', contours);
- break;
- }
- }
- return pathinfo;
- };
-
- },{"../../lib":168,"./constraint_mapping":300,"./end_plus":305}],305:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- /*
- * tiny helper to move the end of the contours a little to prevent
- * losing the last contour to rounding errors
- */
- module.exports = function endPlus(contours) {
- return contours.end + contours.size / 1e6;
- };
-
- },{}],306:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var constants = _dereq_('./constants');
-
- module.exports = function findAllPaths(pathinfo, xtol, ytol) {
- var cnt,
- startLoc,
- i,
- pi,
- j;
-
- // Default just passes these values through as they were before:
- xtol = xtol || 0.01;
- ytol = ytol || 0.01;
-
- for(i = 0; i < pathinfo.length; i++) {
- pi = pathinfo[i];
-
- for(j = 0; j < pi.starts.length; j++) {
- startLoc = pi.starts[j];
- makePath(pi, startLoc, 'edge', xtol, ytol);
- }
-
- cnt = 0;
- while(Object.keys(pi.crossings).length && cnt < 10000) {
- cnt++;
- startLoc = Object.keys(pi.crossings)[0].split(',').map(Number);
- makePath(pi, startLoc, undefined, xtol, ytol);
- }
- if(cnt === 10000) Lib.log('Infinite loop in contour?');
- }
- };
-
- function equalPts(pt1, pt2, xtol, ytol) {
- return Math.abs(pt1[0] - pt2[0]) < xtol &&
- Math.abs(pt1[1] - pt2[1]) < ytol;
- }
-
- // distance in index units - uses the 3rd and 4th items in points
- function ptDist(pt1, pt2) {
- var dx = pt1[2] - pt2[2];
- var dy = pt1[3] - pt2[3];
- return Math.sqrt(dx * dx + dy * dy);
- }
-
- function makePath(pi, loc, edgeflag, xtol, ytol) {
- var startLocStr = loc.join(',');
- var locStr = startLocStr;
- var mi = pi.crossings[locStr];
- var marchStep = startStep(mi, edgeflag, loc);
- // start by going backward a half step and finding the crossing point
- var pts = [getInterpPx(pi, loc, [-marchStep[0], -marchStep[1]])];
- var startStepStr = marchStep.join(',');
- var m = pi.z.length;
- var n = pi.z[0].length;
- var cnt;
-
- // now follow the path
- for(cnt = 0; cnt < 10000; cnt++) { // just to avoid infinite loops
- if(mi > 20) {
- mi = constants.CHOOSESADDLE[mi][(marchStep[0] || marchStep[1]) < 0 ? 0 : 1];
- pi.crossings[locStr] = constants.SADDLEREMAINDER[mi];
- }
- else {
- delete pi.crossings[locStr];
- }
-
- marchStep = constants.NEWDELTA[mi];
- if(!marchStep) {
- Lib.log('Found bad marching index:', mi, loc, pi.level);
- break;
- }
-
- // find the crossing a half step forward, and then take the full step
- pts.push(getInterpPx(pi, loc, marchStep));
- loc[0] += marchStep[0];
- loc[1] += marchStep[1];
-
- // don't include the same point multiple times
- if(equalPts(pts[pts.length - 1], pts[pts.length - 2], xtol, ytol)) pts.pop();
- locStr = loc.join(',');
-
- var atEdge = (marchStep[0] && (loc[0] < 0 || loc[0] > n - 2)) ||
- (marchStep[1] && (loc[1] < 0 || loc[1] > m - 2));
- var closedLoop = (locStr === startLocStr) && (marchStep.join(',') === startStepStr);
-
- // have we completed a loop, or reached an edge?
- if((closedLoop) || (edgeflag && atEdge)) break;
-
- mi = pi.crossings[locStr];
- }
-
- if(cnt === 10000) {
- Lib.log('Infinite loop in contour?');
- }
- var closedpath = equalPts(pts[0], pts[pts.length - 1], xtol, ytol);
- var totaldist = 0;
- var distThresholdFactor = 0.2 * pi.smoothing;
- var alldists = [];
- var cropstart = 0;
- var distgroup, cnt2, cnt3, newpt, ptcnt, ptavg, thisdist,
- i, j, edgepathi, edgepathj;
-
- /*
- * Check for points that are too close together (<1/5 the average dist
- * *in grid index units* (important for log axes and nonuniform grids),
- * less if less smoothed) and just take the center (or avg of center 2).
- * This cuts down on funny behavior when a point is very close to a
- * contour level.
- */
- for(cnt = 1; cnt < pts.length; cnt++) {
- thisdist = ptDist(pts[cnt], pts[cnt - 1]);
- totaldist += thisdist;
- alldists.push(thisdist);
- }
-
- var distThreshold = totaldist / alldists.length * distThresholdFactor;
-
- function getpt(i) { return pts[i % pts.length]; }
-
- for(cnt = pts.length - 2; cnt >= cropstart; cnt--) {
- distgroup = alldists[cnt];
- if(distgroup < distThreshold) {
- cnt3 = 0;
- for(cnt2 = cnt - 1; cnt2 >= cropstart; cnt2--) {
- if(distgroup + alldists[cnt2] < distThreshold) {
- distgroup += alldists[cnt2];
- }
- else break;
- }
-
- // closed path with close points wrapping around the boundary?
- if(closedpath && cnt === pts.length - 2) {
- for(cnt3 = 0; cnt3 < cnt2; cnt3++) {
- if(distgroup + alldists[cnt3] < distThreshold) {
- distgroup += alldists[cnt3];
- }
- else break;
- }
- }
- ptcnt = cnt - cnt2 + cnt3 + 1;
- ptavg = Math.floor((cnt + cnt2 + cnt3 + 2) / 2);
-
- // either endpoint included: keep the endpoint
- if(!closedpath && cnt === pts.length - 2) newpt = pts[pts.length - 1];
- else if(!closedpath && cnt2 === -1) newpt = pts[0];
-
- // odd # of points - just take the central one
- else if(ptcnt % 2) newpt = getpt(ptavg);
-
- // even # of pts - average central two
- else {
- newpt = [(getpt(ptavg)[0] + getpt(ptavg + 1)[0]) / 2,
- (getpt(ptavg)[1] + getpt(ptavg + 1)[1]) / 2];
- }
-
- pts.splice(cnt2 + 1, cnt - cnt2 + 1, newpt);
- cnt = cnt2 + 1;
- if(cnt3) cropstart = cnt3;
- if(closedpath) {
- if(cnt === pts.length - 2) pts[cnt3] = pts[pts.length - 1];
- else if(cnt === 0) pts[pts.length - 1] = pts[0];
- }
- }
- }
- pts.splice(0, cropstart);
-
- // done with the index parts - remove them so path generation works right
- // because it depends on only having [xpx, ypx]
- for(cnt = 0; cnt < pts.length; cnt++) pts[cnt].length = 2;
-
- // don't return single-point paths (ie all points were the same
- // so they got deleted?)
- if(pts.length < 2) return;
- else if(closedpath) {
- pts.pop();
- pi.paths.push(pts);
- }
- else {
- if(!edgeflag) {
- Lib.log('Unclosed interior contour?',
- pi.level, startLocStr, pts.join('L'));
- }
-
- // edge path - does it start where an existing edge path ends, or vice versa?
- var merged = false;
- for(i = 0; i < pi.edgepaths.length; i++) {
- edgepathi = pi.edgepaths[i];
- if(!merged && equalPts(edgepathi[0], pts[pts.length - 1], xtol, ytol)) {
- pts.pop();
- merged = true;
-
- // now does it ALSO meet the end of another (or the same) path?
- var doublemerged = false;
- for(j = 0; j < pi.edgepaths.length; j++) {
- edgepathj = pi.edgepaths[j];
- if(equalPts(edgepathj[edgepathj.length - 1], pts[0], xtol, ytol)) {
- doublemerged = true;
- pts.shift();
- pi.edgepaths.splice(i, 1);
- if(j === i) {
- // the path is now closed
- pi.paths.push(pts.concat(edgepathj));
- }
- else {
- if(j > i) j--;
- pi.edgepaths[j] = edgepathj.concat(pts, edgepathi);
- }
- break;
- }
- }
- if(!doublemerged) {
- pi.edgepaths[i] = pts.concat(edgepathi);
- }
- }
- }
- for(i = 0; i < pi.edgepaths.length; i++) {
- if(merged) break;
- edgepathi = pi.edgepaths[i];
- if(equalPts(edgepathi[edgepathi.length - 1], pts[0], xtol, ytol)) {
- pts.shift();
- pi.edgepaths[i] = edgepathi.concat(pts);
- merged = true;
- }
- }
-
- if(!merged) pi.edgepaths.push(pts);
- }
- }
-
- // special function to get the marching step of the
- // first point in the path (leading to loc)
- function startStep(mi, edgeflag, loc) {
- var dx = 0;
- var dy = 0;
- if(mi > 20 && edgeflag) {
- // these saddles start at +/- x
- if(mi === 208 || mi === 1114) {
- // if we're starting at the left side, we must be going right
- dx = loc[0] === 0 ? 1 : -1;
- }
- else {
- // if we're starting at the bottom, we must be going up
- dy = loc[1] === 0 ? 1 : -1;
- }
- }
- else if(constants.BOTTOMSTART.indexOf(mi) !== -1) dy = 1;
- else if(constants.LEFTSTART.indexOf(mi) !== -1) dx = 1;
- else if(constants.TOPSTART.indexOf(mi) !== -1) dy = -1;
- else dx = -1;
- return [dx, dy];
- }
-
- /*
- * Find the pixel coordinates of a particular crossing
- *
- * @param {object} pi: the pathinfo object at this level
- * @param {array} loc: the grid index [x, y] of the crossing
- * @param {array} step: the direction [dx, dy] we're moving on the grid
- *
- * @return {array} [xpx, ypx, xi, yi]: the first two are the pixel location,
- * the next two are the interpolated grid indices, which we use for
- * distance calculations to delete points that are too close together.
- * This is important when the grid is nonuniform (and most dramatically when
- * we're on log axes and include invalid (0 or negative) values.
- * It's crucial to delete these extra two before turning an array of these
- * points into a path, because those routines require length-2 points.
- */
- function getInterpPx(pi, loc, step) {
- var locx = loc[0] + Math.max(step[0], 0);
- var locy = loc[1] + Math.max(step[1], 0);
- var zxy = pi.z[locy][locx];
- var xa = pi.xaxis;
- var ya = pi.yaxis;
-
- if(step[1]) {
- var dx = (pi.level - zxy) / (pi.z[locy][locx + 1] - zxy);
-
- return [xa.c2p((1 - dx) * pi.x[locx] + dx * pi.x[locx + 1], true),
- ya.c2p(pi.y[locy], true),
- locx + dx, locy];
- }
- else {
- var dy = (pi.level - zxy) / (pi.z[locy + 1][locx] - zxy);
- return [xa.c2p(pi.x[locx], true),
- ya.c2p((1 - dy) * pi.y[locy] + dy * pi.y[locy + 1], true),
- locx, locy + dy];
- }
- }
-
- },{"../../lib":168,"./constants":298}],307:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Color = _dereq_('../../components/color');
-
- var heatmapHoverPoints = _dereq_('../heatmap/hover');
-
- module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer) {
- var hoverData = heatmapHoverPoints(pointData, xval, yval, hovermode, hoverLayer, true);
-
- if(hoverData) {
- hoverData.forEach(function(hoverPt) {
- var trace = hoverPt.trace;
- if(trace.contours.type === 'constraint') {
- if(trace.fillcolor && Color.opacity(trace.fillcolor)) {
- hoverPt.color = Color.addOpacity(trace.fillcolor, 1);
- }
- else if(trace.contours.showlines && Color.opacity(trace.line.color)) {
- hoverPt.color = Color.addOpacity(trace.line.color, 1);
- }
- }
- });
- }
-
- return hoverData;
- };
-
- },{"../../components/color":51,"../heatmap/hover":323}],308:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Contour = {};
-
- Contour.attributes = _dereq_('./attributes');
- Contour.supplyDefaults = _dereq_('./defaults');
- Contour.calc = _dereq_('./calc');
- Contour.plot = _dereq_('./plot').plot;
- Contour.style = _dereq_('./style');
- Contour.colorbar = _dereq_('./colorbar');
- Contour.hoverPoints = _dereq_('./hover');
-
- Contour.moduleType = 'trace';
- Contour.name = 'contour';
- Contour.basePlotModule = _dereq_('../../plots/cartesian');
- Contour.categories = ['cartesian', 'svg', '2dMap', 'contour', 'showLegend'];
- Contour.meta = {
-
- };
-
- module.exports = Contour;
-
- },{"../../plots/cartesian":224,"./attributes":294,"./calc":295,"./colorbar":297,"./defaults":303,"./hover":307,"./plot":312,"./style":314}],309:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- module.exports = function handleLabelDefaults(coerce, layout, lineColor, opts) {
- if(!opts) opts = {};
- var showLabels = coerce('contours.showlabels');
- if(showLabels) {
- var globalFont = layout.font;
- Lib.coerceFont(coerce, 'contours.labelfont', {
- family: globalFont.family,
- size: globalFont.size,
- color: lineColor
- });
- coerce('contours.labelformat');
- }
-
- if(opts.hasHover !== false) coerce('zhoverformat');
- };
-
- },{"../../lib":168}],310:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var Colorscale = _dereq_('../../components/colorscale');
- var endPlus = _dereq_('./end_plus');
-
- module.exports = function makeColorMap(trace) {
- var contours = trace.contours;
- var start = contours.start;
- var end = endPlus(contours);
- var cs = contours.size || 1;
- var nc = Math.floor((end - start) / cs) + 1;
- var extra = contours.coloring === 'lines' ? 0 : 1;
-
- if(!isFinite(cs)) {
- cs = 1;
- nc = 1;
- }
-
- var scl = trace.reversescale ?
- Colorscale.flipScale(trace.colorscale) :
- trace.colorscale;
-
- var len = scl.length;
- var domain = new Array(len);
- var range = new Array(len);
-
- var si, i;
-
- if(contours.coloring === 'heatmap') {
- if(trace.zauto && trace.autocontour === false) {
- trace.zmin = start - cs / 2;
- trace.zmax = trace.zmin + nc * cs;
- }
-
- for(i = 0; i < len; i++) {
- si = scl[i];
-
- domain[i] = si[0] * (trace.zmax - trace.zmin) + trace.zmin;
- range[i] = si[1];
- }
-
- // do the contours extend beyond the colorscale?
- // if so, extend the colorscale with constants
- var zRange = d3.extent([
- trace.zmin,
- trace.zmax,
- contours.start,
- contours.start + cs * (nc - 1)
- ]);
- var zmin = zRange[trace.zmin < trace.zmax ? 0 : 1];
- var zmax = zRange[trace.zmin < trace.zmax ? 1 : 0];
-
- if(zmin !== trace.zmin) {
- domain.splice(0, 0, zmin);
- range.splice(0, 0, Range[0]);
- }
-
- if(zmax !== trace.zmax) {
- domain.push(zmax);
- range.push(range[range.length - 1]);
- }
- }
- else {
- for(i = 0; i < len; i++) {
- si = scl[i];
-
- domain[i] = (si[0] * (nc + extra - 1) - (extra / 2)) * cs + start;
- range[i] = si[1];
- }
- }
-
- return Colorscale.makeColorScaleFunc({
- domain: domain,
- range: range,
- }, {
- noNumericCheck: true
- });
- };
-
- },{"../../components/colorscale":63,"./end_plus":305,"d3":16}],311:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var constants = _dereq_('./constants');
-
- // Calculate all the marching indices, for ALL levels at once.
- // since we want to be exhaustive we'll check for contour crossings
- // at every intersection, rather than just following a path
- // TODO: shorten the inner loop to only the relevant levels
- module.exports = function makeCrossings(pathinfo) {
- var z = pathinfo[0].z;
- var m = z.length;
- var n = z[0].length; // we already made sure z isn't ragged in interp2d
- var twoWide = m === 2 || n === 2;
- var xi;
- var yi;
- var startIndices;
- var ystartIndices;
- var label;
- var corners;
- var mi;
- var pi;
- var i;
-
- for(yi = 0; yi < m - 1; yi++) {
- ystartIndices = [];
- if(yi === 0) ystartIndices = ystartIndices.concat(constants.BOTTOMSTART);
- if(yi === m - 2) ystartIndices = ystartIndices.concat(constants.TOPSTART);
-
- for(xi = 0; xi < n - 1; xi++) {
- startIndices = ystartIndices.slice();
- if(xi === 0) startIndices = startIndices.concat(constants.LEFTSTART);
- if(xi === n - 2) startIndices = startIndices.concat(constants.RIGHTSTART);
-
- label = xi + ',' + yi;
- corners = [[z[yi][xi], z[yi][xi + 1]],
- [z[yi + 1][xi], z[yi + 1][xi + 1]]];
- for(i = 0; i < pathinfo.length; i++) {
- pi = pathinfo[i];
- mi = getMarchingIndex(pi.level, corners);
- if(!mi) continue;
-
- pi.crossings[label] = mi;
- if(startIndices.indexOf(mi) !== -1) {
- pi.starts.push([xi, yi]);
- if(twoWide && startIndices.indexOf(mi,
- startIndices.indexOf(mi) + 1) !== -1) {
- // the same square has starts from opposite sides
- // it's not possible to have starts on opposite edges
- // of a corner, only a start and an end...
- // but if the array is only two points wide (either way)
- // you can have starts on opposite sides.
- pi.starts.push([xi, yi]);
- }
- }
- }
- }
- }
- };
-
- // modified marching squares algorithm,
- // so we disambiguate the saddle points from the start
- // and we ignore the cases with no crossings
- // the index I'm using is based on:
- // http://en.wikipedia.org/wiki/Marching_squares
- // except that the saddles bifurcate and I represent them
- // as the decimal combination of the two appropriate
- // non-saddle indices
- function getMarchingIndex(val, corners) {
- var mi = (corners[0][0] > val ? 0 : 1) +
- (corners[0][1] > val ? 0 : 2) +
- (corners[1][1] > val ? 0 : 4) +
- (corners[1][0] > val ? 0 : 8);
- if(mi === 5 || mi === 10) {
- var avg = (corners[0][0] + corners[0][1] +
- corners[1][0] + corners[1][1]) / 4;
- // two peaks with a big valley
- if(val > avg) return (mi === 5) ? 713 : 1114;
- // two valleys with a big ridge
- return (mi === 5) ? 104 : 208;
- }
- return (mi === 15) ? 0 : mi;
- }
-
- },{"./constants":298}],312:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Lib = _dereq_('../../lib');
- var Drawing = _dereq_('../../components/drawing');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var setConvert = _dereq_('../../plots/cartesian/set_convert');
-
- var heatmapPlot = _dereq_('../heatmap/plot');
- var makeCrossings = _dereq_('./make_crossings');
- var findAllPaths = _dereq_('./find_all_paths');
- var emptyPathinfo = _dereq_('./empty_pathinfo');
- var convertToConstraints = _dereq_('./convert_to_constraints');
- var closeBoundaries = _dereq_('./close_boundaries');
- var constants = _dereq_('./constants');
- var costConstants = constants.LABELOPTIMIZER;
-
- exports.plot = function plot(gd, plotinfo, cdcontours, contourLayer) {
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- Lib.makeTraceGroups(contourLayer, cdcontours, 'contour').each(function(cd) {
- var plotGroup = d3.select(this);
- var cd0 = cd[0];
- var trace = cd0.trace;
- var x = cd0.x;
- var y = cd0.y;
- var contours = trace.contours;
- var pathinfo = emptyPathinfo(contours, plotinfo, cd0);
-
- // use a heatmap to fill - draw it behind the lines
- var heatmapColoringLayer = Lib.ensureSingle(plotGroup, 'g', 'heatmapcoloring');
- var cdheatmaps = [];
- if(contours.coloring === 'heatmap') {
- if(trace.zauto && (trace.autocontour === false)) {
- trace._input.zmin = trace.zmin =
- contours.start - contours.size / 2;
- trace._input.zmax = trace.zmax =
- trace.zmin + pathinfo.length * contours.size;
- }
- cdheatmaps = [cd];
- }
- heatmapPlot(gd, plotinfo, cdheatmaps, heatmapColoringLayer);
-
- makeCrossings(pathinfo);
- findAllPaths(pathinfo);
-
- var leftedge = xa.c2p(x[0], true);
- var rightedge = xa.c2p(x[x.length - 1], true);
- var bottomedge = ya.c2p(y[0], true);
- var topedge = ya.c2p(y[y.length - 1], true);
- var perimeter = [
- [leftedge, topedge],
- [rightedge, topedge],
- [rightedge, bottomedge],
- [leftedge, bottomedge]
- ];
-
- var fillPathinfo = pathinfo;
- if(contours.type === 'constraint') {
- fillPathinfo = convertToConstraints(pathinfo, contours._operation);
- closeBoundaries(fillPathinfo, contours._operation, perimeter, trace);
- }
-
- // draw everything
- makeBackground(plotGroup, perimeter, contours);
- makeFills(plotGroup, fillPathinfo, perimeter, contours);
- makeLinesAndLabels(plotGroup, pathinfo, gd, cd0, contours, perimeter);
- clipGaps(plotGroup, plotinfo, gd, cd0, perimeter);
- });
- };
-
- function makeBackground(plotgroup, perimeter, contours) {
- var bggroup = Lib.ensureSingle(plotgroup, 'g', 'contourbg');
-
- var bgfill = bggroup.selectAll('path')
- .data(contours.coloring === 'fill' ? [0] : []);
- bgfill.enter().append('path');
- bgfill.exit().remove();
- bgfill
- .attr('d', 'M' + perimeter.join('L') + 'Z')
- .style('stroke', 'none');
- }
-
- function makeFills(plotgroup, pathinfo, perimeter, contours) {
- var fillgroup = Lib.ensureSingle(plotgroup, 'g', 'contourfill');
-
- var fillitems = fillgroup.selectAll('path')
- .data(contours.coloring === 'fill' || (contours.type === 'constraint' && contours._operation !== '=') ? pathinfo : []);
- fillitems.enter().append('path');
- fillitems.exit().remove();
- fillitems.each(function(pi) {
- // join all paths for this level together into a single path
- // first follow clockwise around the perimeter to close any open paths
- // if the whole perimeter is above this level, start with a path
- // enclosing the whole thing. With all that, the parity should mean
- // that we always fill everything above the contour, nothing below
- var fullpath = joinAllPaths(pi, perimeter);
-
- if(!fullpath) d3.select(this).remove();
- else d3.select(this).attr('d', fullpath).style('stroke', 'none');
- });
- }
-
- function initFullPath(pi, perimeter) {
- var prefixBoundary = pi.prefixBoundary;
- if(prefixBoundary === undefined) {
- var edgeVal2 = Math.min(pi.z[0][0], pi.z[0][1]);
- prefixBoundary = (!pi.edgepaths.length && edgeVal2 > pi.level);
- }
-
- if(prefixBoundary) {
- // TODO: why does ^^ not work for constraints?
- // pi.prefixBoundary gets set by closeBoundaries
- return 'M' + perimeter.join('L') + 'Z';
- }
- return '';
- }
-
- function joinAllPaths(pi, perimeter) {
- var fullpath = initFullPath(pi, perimeter);
- var i = 0;
- var startsleft = pi.edgepaths.map(function(v, i) { return i; });
- var newloop = true;
- var endpt;
- var newendpt;
- var cnt;
- var nexti;
- var possiblei;
- var addpath;
-
- function istop(pt) { return Math.abs(pt[1] - perimeter[0][1]) < 0.01; }
- function isbottom(pt) { return Math.abs(pt[1] - perimeter[2][1]) < 0.01; }
- function isleft(pt) { return Math.abs(pt[0] - perimeter[0][0]) < 0.01; }
- function isright(pt) { return Math.abs(pt[0] - perimeter[2][0]) < 0.01; }
-
- while(startsleft.length) {
- addpath = Drawing.smoothopen(pi.edgepaths[i], pi.smoothing);
- fullpath += newloop ? addpath : addpath.replace(/^M/, 'L');
- startsleft.splice(startsleft.indexOf(i), 1);
- endpt = pi.edgepaths[i][pi.edgepaths[i].length - 1];
- nexti = -1;
-
- // now loop through sides, moving our endpoint until we find a new start
- for(cnt = 0; cnt < 4; cnt++) { // just to prevent infinite loops
- if(!endpt) {
- Lib.log('Missing end?', i, pi);
- break;
- }
-
- if(istop(endpt) && !isright(endpt)) newendpt = perimeter[1]; // right top
- else if(isleft(endpt)) newendpt = perimeter[0]; // left top
- else if(isbottom(endpt)) newendpt = perimeter[3]; // right bottom
- else if(isright(endpt)) newendpt = perimeter[2]; // left bottom
-
- for(possiblei = 0; possiblei < pi.edgepaths.length; possiblei++) {
- var ptNew = pi.edgepaths[possiblei][0];
- // is ptNew on the (horz. or vert.) segment from endpt to newendpt?
- if(Math.abs(endpt[0] - newendpt[0]) < 0.01) {
- if(Math.abs(endpt[0] - ptNew[0]) < 0.01 &&
- (ptNew[1] - endpt[1]) * (newendpt[1] - ptNew[1]) >= 0) {
- newendpt = ptNew;
- nexti = possiblei;
- }
- }
- else if(Math.abs(endpt[1] - newendpt[1]) < 0.01) {
- if(Math.abs(endpt[1] - ptNew[1]) < 0.01 &&
- (ptNew[0] - endpt[0]) * (newendpt[0] - ptNew[0]) >= 0) {
- newendpt = ptNew;
- nexti = possiblei;
- }
- }
- else {
- Lib.log('endpt to newendpt is not vert. or horz.',
- endpt, newendpt, ptNew);
- }
- }
-
- endpt = newendpt;
-
- if(nexti >= 0) break;
- fullpath += 'L' + newendpt;
- }
-
- if(nexti === pi.edgepaths.length) {
- Lib.log('unclosed perimeter path');
- break;
- }
-
- i = nexti;
-
- // if we closed back on a loop we already included,
- // close it and start a new loop
- newloop = (startsleft.indexOf(i) === -1);
- if(newloop) {
- i = startsleft[0];
- fullpath += 'Z';
- }
- }
-
- // finally add the interior paths
- for(i = 0; i < pi.paths.length; i++) {
- fullpath += Drawing.smoothclosed(pi.paths[i], pi.smoothing);
- }
-
- return fullpath;
- }
-
- function makeLinesAndLabels(plotgroup, pathinfo, gd, cd0, contours, perimeter) {
- var lineContainer = Lib.ensureSingle(plotgroup, 'g', 'contourlines');
- var showLines = contours.showlines !== false;
- var showLabels = contours.showlabels;
- var clipLinesForLabels = showLines && showLabels;
-
- // Even if we're not going to show lines, we need to create them
- // if we're showing labels, because the fill paths include the perimeter
- // so can't be used to position the labels correctly.
- // In this case we'll remove the lines after making the labels.
- var linegroup = exports.createLines(lineContainer, showLines || showLabels, pathinfo);
-
- var lineClip = exports.createLineClip(lineContainer, clipLinesForLabels, gd, cd0.trace.uid);
-
- var labelGroup = plotgroup.selectAll('g.contourlabels')
- .data(showLabels ? [0] : []);
-
- labelGroup.exit().remove();
-
- labelGroup.enter().append('g')
- .classed('contourlabels', true);
-
- if(showLabels) {
- var labelClipPathData = [];
- var labelData = [];
-
- // invalidate the getTextLocation cache in case paths changed
- Lib.clearLocationCache();
-
- var contourFormat = exports.labelFormatter(contours, cd0.t.cb, gd._fullLayout);
-
- var dummyText = Drawing.tester.append('text')
- .attr('data-notex', 1)
- .call(Drawing.font, contours.labelfont);
-
- var xa = pathinfo[0].xaxis;
- var ya = pathinfo[0].yaxis;
- var xLen = xa._length;
- var yLen = ya._length;
- var xRng = xa.range;
- var yRng = ya.range;
- var x0 = Math.max(perimeter[0][0], 0);
- var x1 = Math.min(perimeter[2][0], xLen);
- var y0 = Math.max(perimeter[0][1], 0);
- var y1 = Math.min(perimeter[2][1], yLen);
-
- // visible bounds of the contour trace (and the midpoints, to
- // help with cost calculations)
- var bounds = {};
-
- if(xRng[0] < xRng[1]) {
- bounds.left = x0;
- bounds.right = x1;
- } else {
- bounds.left = x1;
- bounds.right = x0;
- }
-
- if(yRng[0] < yRng[1]) {
- bounds.top = y0;
- bounds.bottom = y1;
- } else {
- bounds.top = y1;
- bounds.bottom = y0;
- }
-
- bounds.middle = (bounds.top + bounds.bottom) / 2;
- bounds.center = (bounds.left + bounds.right) / 2;
-
- labelClipPathData.push([
- [bounds.left, bounds.top],
- [bounds.right, bounds.top],
- [bounds.right, bounds.bottom],
- [bounds.left, bounds.bottom]
- ]);
-
- var plotDiagonal = Math.sqrt(xLen * xLen + yLen * yLen);
-
- // the path length to use to scale the number of labels to draw:
- var normLength = constants.LABELDISTANCE * plotDiagonal /
- Math.max(1, pathinfo.length / constants.LABELINCREASE);
-
- linegroup.each(function(d) {
- var textOpts = exports.calcTextOpts(d.level, contourFormat, dummyText, gd);
-
- d3.select(this).selectAll('path').each(function() {
- var path = this;
- var pathBounds = Lib.getVisibleSegment(path, bounds, textOpts.height / 2);
- if(!pathBounds) return;
-
- if(pathBounds.len < (textOpts.width + textOpts.height) * constants.LABELMIN) return;
-
- var maxLabels = Math.min(Math.ceil(pathBounds.len / normLength),
- constants.LABELMAX);
-
- for(var i = 0; i < maxLabels; i++) {
- var loc = exports.findBestTextLocation(path, pathBounds, textOpts,
- labelData, bounds);
-
- if(!loc) break;
-
- exports.addLabelData(loc, textOpts, labelData, labelClipPathData);
- }
- });
- });
-
- dummyText.remove();
-
- exports.drawLabels(labelGroup, labelData, gd, lineClip,
- clipLinesForLabels ? labelClipPathData : null);
- }
-
- if(showLabels && !showLines) linegroup.remove();
- }
-
- exports.createLines = function(lineContainer, makeLines, pathinfo) {
- var smoothing = pathinfo[0].smoothing;
-
- var linegroup = lineContainer.selectAll('g.contourlevel')
- .data(makeLines ? pathinfo : []);
-
- linegroup.exit().remove();
- linegroup.enter().append('g')
- .classed('contourlevel', true);
-
- if(makeLines) {
- // pedgepaths / ppaths are used by contourcarpet, for the paths transformed from a/b to x/y
- // edgepaths / paths are used by contour since it's in x/y from the start
- var opencontourlines = linegroup.selectAll('path.openline')
- .data(function(d) { return d.pedgepaths || d.edgepaths; });
-
- opencontourlines.exit().remove();
- opencontourlines.enter().append('path')
- .classed('openline', true);
-
- opencontourlines
- .attr('d', function(d) {
- return Drawing.smoothopen(d, smoothing);
- })
- .style('stroke-miterlimit', 1)
- .style('vector-effect', 'non-scaling-stroke');
-
- var closedcontourlines = linegroup.selectAll('path.closedline')
- .data(function(d) { return d.ppaths || d.paths; });
-
- closedcontourlines.exit().remove();
- closedcontourlines.enter().append('path')
- .classed('closedline', true);
-
- closedcontourlines
- .attr('d', function(d) {
- return Drawing.smoothclosed(d, smoothing);
- })
- .style('stroke-miterlimit', 1)
- .style('vector-effect', 'non-scaling-stroke');
- }
-
- return linegroup;
- };
-
- exports.createLineClip = function(lineContainer, clipLinesForLabels, gd, uid) {
- var clips = gd._fullLayout._clips;
- var clipId = clipLinesForLabels ? ('clipline' + uid) : null;
-
- var lineClip = clips.selectAll('#' + clipId)
- .data(clipLinesForLabels ? [0] : []);
- lineClip.exit().remove();
-
- lineClip.enter().append('clipPath')
- .classed('contourlineclip', true)
- .attr('id', clipId);
-
- Drawing.setClipUrl(lineContainer, clipId, gd);
-
- return lineClip;
- };
-
- exports.labelFormatter = function(contours, colorbar, fullLayout) {
- if(contours.labelformat) {
- return fullLayout._d3locale.numberFormat(contours.labelformat);
- }
- else {
- var formatAxis;
- if(colorbar) {
- formatAxis = colorbar.axis;
- }
- else {
- formatAxis = {
- type: 'linear',
- _id: 'ycontour',
- showexponent: 'all',
- exponentformat: 'B'
- };
-
- if(contours.type === 'constraint') {
- var value = contours.value;
- if(Array.isArray(value)) {
- formatAxis.range = [value[0], value[value.length - 1]];
- }
- else formatAxis.range = [value, value];
- }
- else {
- formatAxis.range = [contours.start, contours.end];
- formatAxis.nticks = (contours.end - contours.start) / contours.size;
- }
-
- if(formatAxis.range[0] === formatAxis.range[1]) {
- formatAxis.range[1] += formatAxis.range[0] || 1;
- }
- if(!formatAxis.nticks) formatAxis.nticks = 1000;
-
- setConvert(formatAxis, fullLayout);
- Axes.prepTicks(formatAxis);
- formatAxis._tmin = null;
- formatAxis._tmax = null;
- }
- return function(v) {
- return Axes.tickText(formatAxis, v).text;
- };
- }
- };
-
- exports.calcTextOpts = function(level, contourFormat, dummyText, gd) {
- var text = contourFormat(level);
- dummyText.text(text)
- .call(svgTextUtils.convertToTspans, gd);
- var bBox = Drawing.bBox(dummyText.node(), true);
-
- return {
- text: text,
- width: bBox.width,
- height: bBox.height,
- level: level,
- dy: (bBox.top + bBox.bottom) / 2
- };
- };
-
- exports.findBestTextLocation = function(path, pathBounds, textOpts, labelData, plotBounds) {
- var textWidth = textOpts.width;
-
- var p0, dp, pMax, pMin, loc;
- if(pathBounds.isClosed) {
- dp = pathBounds.len / costConstants.INITIALSEARCHPOINTS;
- p0 = pathBounds.min + dp / 2;
- pMax = pathBounds.max;
- }
- else {
- dp = (pathBounds.len - textWidth) / (costConstants.INITIALSEARCHPOINTS + 1);
- p0 = pathBounds.min + dp + textWidth / 2;
- pMax = pathBounds.max - (dp + textWidth) / 2;
- }
-
- var cost = Infinity;
- for(var j = 0; j < costConstants.ITERATIONS; j++) {
- for(var p = p0; p < pMax; p += dp) {
- var newLocation = Lib.getTextLocation(path, pathBounds.total, p, textWidth);
- var newCost = locationCost(newLocation, textOpts, labelData, plotBounds);
- if(newCost < cost) {
- cost = newCost;
- loc = newLocation;
- pMin = p;
- }
- }
- if(cost > costConstants.MAXCOST * 2) break;
-
- // subsequent iterations just look half steps away from the
- // best we found in the previous iteration
- if(j) dp /= 2;
- p0 = pMin - dp / 2;
- pMax = p0 + dp * 1.5;
- }
- if(cost <= costConstants.MAXCOST) return loc;
- };
-
- /*
- * locationCost: a cost function for label locations
- * composed of three kinds of penalty:
- * - for open paths, being close to the end of the path
- * - the angle away from horizontal
- * - being too close to already placed neighbors
- */
- function locationCost(loc, textOpts, labelData, bounds) {
- var halfWidth = textOpts.width / 2;
- var halfHeight = textOpts.height / 2;
- var x = loc.x;
- var y = loc.y;
- var theta = loc.theta;
- var dx = Math.cos(theta) * halfWidth;
- var dy = Math.sin(theta) * halfWidth;
-
- // cost for being near an edge
- var normX = ((x > bounds.center) ? (bounds.right - x) : (x - bounds.left)) /
- (dx + Math.abs(Math.sin(theta) * halfHeight));
- var normY = ((y > bounds.middle) ? (bounds.bottom - y) : (y - bounds.top)) /
- (Math.abs(dy) + Math.cos(theta) * halfHeight);
- if(normX < 1 || normY < 1) return Infinity;
- var cost = costConstants.EDGECOST * (1 / (normX - 1) + 1 / (normY - 1));
-
- // cost for not being horizontal
- cost += costConstants.ANGLECOST * theta * theta;
-
- // cost for being close to other labels
- var x1 = x - dx;
- var y1 = y - dy;
- var x2 = x + dx;
- var y2 = y + dy;
- for(var i = 0; i < labelData.length; i++) {
- var labeli = labelData[i];
- var dxd = Math.cos(labeli.theta) * labeli.width / 2;
- var dyd = Math.sin(labeli.theta) * labeli.width / 2;
- var dist = Lib.segmentDistance(
- x1, y1,
- x2, y2,
- labeli.x - dxd, labeli.y - dyd,
- labeli.x + dxd, labeli.y + dyd
- ) * 2 / (textOpts.height + labeli.height);
-
- var sameLevel = labeli.level === textOpts.level;
- var distOffset = sameLevel ? costConstants.SAMELEVELDISTANCE : 1;
-
- if(dist <= distOffset) return Infinity;
-
- var distFactor = costConstants.NEIGHBORCOST *
- (sameLevel ? costConstants.SAMELEVELFACTOR : 1);
-
- cost += distFactor / (dist - distOffset);
- }
-
- return cost;
- }
-
- exports.addLabelData = function(loc, textOpts, labelData, labelClipPathData) {
- var halfWidth = textOpts.width / 2;
- var halfHeight = textOpts.height / 2;
-
- var x = loc.x;
- var y = loc.y;
- var theta = loc.theta;
-
- var sin = Math.sin(theta);
- var cos = Math.cos(theta);
- var dxw = halfWidth * cos;
- var dxh = halfHeight * sin;
- var dyw = halfWidth * sin;
- var dyh = -halfHeight * cos;
- var bBoxPts = [
- [x - dxw - dxh, y - dyw - dyh],
- [x + dxw - dxh, y + dyw - dyh],
- [x + dxw + dxh, y + dyw + dyh],
- [x - dxw + dxh, y - dyw + dyh],
- ];
-
- labelData.push({
- text: textOpts.text,
- x: x,
- y: y,
- dy: textOpts.dy,
- theta: theta,
- level: textOpts.level,
- width: textOpts.width,
- height: textOpts.height
- });
-
- labelClipPathData.push(bBoxPts);
- };
-
- exports.drawLabels = function(labelGroup, labelData, gd, lineClip, labelClipPathData) {
- var labels = labelGroup.selectAll('text')
- .data(labelData, function(d) {
- return d.text + ',' + d.x + ',' + d.y + ',' + d.theta;
- });
-
- labels.exit().remove();
-
- labels.enter().append('text')
- .attr({
- 'data-notex': 1,
- 'text-anchor': 'middle'
- })
- .each(function(d) {
- var x = d.x + Math.sin(d.theta) * d.dy;
- var y = d.y - Math.cos(d.theta) * d.dy;
- d3.select(this)
- .text(d.text)
- .attr({
- x: x,
- y: y,
- transform: 'rotate(' + (180 * d.theta / Math.PI) + ' ' + x + ' ' + y + ')'
- })
- .call(svgTextUtils.convertToTspans, gd);
- });
-
- if(labelClipPathData) {
- var clipPath = '';
- for(var i = 0; i < labelClipPathData.length; i++) {
- clipPath += 'M' + labelClipPathData[i].join('L') + 'Z';
- }
-
- var lineClipPath = Lib.ensureSingle(lineClip, 'path', '');
- lineClipPath.attr('d', clipPath);
- }
- };
-
- function clipGaps(plotGroup, plotinfo, gd, cd0, perimeter) {
- var clips = gd._fullLayout._clips;
- var clipId = 'clip' + cd0.trace.uid;
-
- var clipPath = clips.selectAll('#' + clipId)
- .data(cd0.trace.connectgaps ? [] : [0]);
- clipPath.enter().append('clipPath')
- .classed('contourclip', true)
- .attr('id', clipId);
- clipPath.exit().remove();
-
- if(cd0.trace.connectgaps === false) {
- var clipPathInfo = {
- // fraction of the way from missing to present point
- // to draw the boundary.
- // if you make this 1 (or 1-epsilon) then a point in
- // a sea of missing data will disappear entirely.
- level: 0.9,
- crossings: {},
- starts: [],
- edgepaths: [],
- paths: [],
- xaxis: plotinfo.xaxis,
- yaxis: plotinfo.yaxis,
- x: cd0.x,
- y: cd0.y,
- // 0 = no data, 1 = data
- z: makeClipMask(cd0),
- smoothing: 0
- };
-
- makeCrossings([clipPathInfo]);
- findAllPaths([clipPathInfo]);
- var fullpath = joinAllPaths(clipPathInfo, perimeter);
-
- var path = Lib.ensureSingle(clipPath, 'path', '');
- path.attr('d', fullpath);
- }
- else clipId = null;
-
- Drawing.setClipUrl(plotGroup, clipId, gd);
- }
-
- function makeClipMask(cd0) {
- var empties = cd0.trace._emptypoints;
- var z = [];
- var m = cd0.z.length;
- var n = cd0.z[0].length;
- var i;
- var row = [];
- var emptyPoint;
-
- for(i = 0; i < n; i++) row.push(1);
- for(i = 0; i < m; i++) z.push(row.slice());
- for(i = 0; i < empties.length; i++) {
- emptyPoint = empties[i];
- z[emptyPoint[0]][emptyPoint[1]] = 0;
- }
- // save this mask to determine whether to show this data in hover
- cd0.zmask = z;
- return z;
- }
-
- },{"../../components/drawing":72,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../plots/cartesian/set_convert":231,"../heatmap/plot":327,"./close_boundaries":296,"./constants":298,"./convert_to_constraints":302,"./empty_pathinfo":304,"./find_all_paths":306,"./make_crossings":311,"d3":16}],313:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Axes = _dereq_('../../plots/cartesian/axes');
- var Lib = _dereq_('../../lib');
-
-
- module.exports = function setContours(trace) {
- var contours = trace.contours;
-
- // check if we need to auto-choose contour levels
- if(trace.autocontour) {
- var zmin = trace.zmin;
- var zmax = trace.zmax;
- if(zmin === undefined || zmax === undefined) {
- zmin = Lib.aggNums(Math.min, null, trace._z);
- zmax = Lib.aggNums(Math.max, null, trace._z);
- }
- var dummyAx = autoContours(zmin, zmax, trace.ncontours);
-
- contours.size = dummyAx.dtick;
-
- contours.start = Axes.tickFirst(dummyAx);
- dummyAx.range.reverse();
- contours.end = Axes.tickFirst(dummyAx);
-
- if(contours.start === zmin) contours.start += contours.size;
- if(contours.end === zmax) contours.end -= contours.size;
-
- // if you set a small ncontours, *and* the ends are exactly on zmin/zmax
- // there's an edge case where start > end now. Make sure there's at least
- // one meaningful contour, put it midway between the crossed values
- if(contours.start > contours.end) {
- contours.start = contours.end = (contours.start + contours.end) / 2;
- }
-
- // copy auto-contour info back to the source data.
- // previously we copied the whole contours object back, but that had
- // other info (coloring, showlines) that should be left to supplyDefaults
- if(!trace._input.contours) trace._input.contours = {};
- Lib.extendFlat(trace._input.contours, {
- start: contours.start,
- end: contours.end,
- size: contours.size
- });
- trace._input.autocontour = true;
- }
- else if(contours.type !== 'constraint') {
- // sanity checks on manually-supplied start/end/size
- var start = contours.start;
- var end = contours.end;
- var inputContours = trace._input.contours;
-
- if(start > end) {
- contours.start = inputContours.start = end;
- end = contours.end = inputContours.end = start;
- start = contours.start;
- }
-
- if(!(contours.size > 0)) {
- var sizeOut;
- if(start === end) sizeOut = 1;
- else sizeOut = autoContours(start, end, trace.ncontours).dtick;
-
- inputContours.size = contours.size = sizeOut;
- }
- }
- };
-
-
- /*
- * autoContours: make a dummy axis object with dtick we can use
- * as contours.size, and if needed we can use Axes.tickFirst
- * with this axis object to calculate the start and end too
- *
- * start: the value to start the contours at
- * end: the value to end at (must be > start)
- * ncontours: max number of contours to make, like roughDTick
- *
- * returns: an axis object
- */
- function autoContours(start, end, ncontours) {
- var dummyAx = {
- type: 'linear',
- range: [start, end]
- };
-
- Axes.autoTicks(
- dummyAx,
- (end - start) / (ncontours || 15)
- );
-
- return dummyAx;
- }
-
- },{"../../lib":168,"../../plots/cartesian/axes":212}],314:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Drawing = _dereq_('../../components/drawing');
- var heatmapStyle = _dereq_('../heatmap/style');
-
- var makeColorMap = _dereq_('./make_color_map');
-
-
- module.exports = function style(gd) {
- var contours = d3.select(gd).selectAll('g.contour');
-
- contours.style('opacity', function(d) {
- return d[0].trace.opacity;
- });
-
- contours.each(function(d) {
- var c = d3.select(this);
- var trace = d[0].trace;
- var contours = trace.contours;
- var line = trace.line;
- var cs = contours.size || 1;
- var start = contours.start;
-
- // for contourcarpet only - is this a constraint-type contour trace?
- var isConstraintType = contours.type === 'constraint';
- var colorLines = !isConstraintType && contours.coloring === 'lines';
- var colorFills = !isConstraintType && contours.coloring === 'fill';
-
- var colorMap = (colorLines || colorFills) ? makeColorMap(trace) : null;
-
- c.selectAll('g.contourlevel').each(function(d) {
- d3.select(this).selectAll('path')
- .call(Drawing.lineGroupStyle,
- line.width,
- colorLines ? colorMap(d.level) : line.color,
- line.dash);
- });
-
- var labelFont = contours.labelfont;
- c.selectAll('g.contourlabels text').each(function(d) {
- Drawing.font(d3.select(this), {
- family: labelFont.family,
- size: labelFont.size,
- color: labelFont.color || (colorLines ? colorMap(d.level) : line.color)
- });
- });
-
- if(isConstraintType) {
- c.selectAll('g.contourfill path')
- .style('fill', trace.fillcolor);
- }
- else if(colorFills) {
- var firstFill;
-
- c.selectAll('g.contourfill path')
- .style('fill', function(d) {
- if(firstFill === undefined) firstFill = d.level;
- return colorMap(d.level + 0.5 * cs);
- });
-
- if(firstFill === undefined) firstFill = start;
-
- c.selectAll('g.contourbg path')
- .style('fill', colorMap(firstFill - 0.5 * cs));
- }
- });
-
- heatmapStyle(gd);
- };
-
- },{"../../components/drawing":72,"../heatmap/style":328,"./make_color_map":310,"d3":16}],315:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
- var handleLabelDefaults = _dereq_('./label_defaults');
-
-
- module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout, opts) {
- var coloring = coerce('contours.coloring');
-
- var showLines;
- var lineColor = '';
- if(coloring === 'fill') showLines = coerce('contours.showlines');
-
- if(showLines !== false) {
- if(coloring !== 'lines') lineColor = coerce('line.color', '#000');
- coerce('line.width', 0.5);
- coerce('line.dash');
- }
-
- if(coloring !== 'none') {
- // plots/plots always coerces showlegend to true, but in this case
- // we default to false and (by default) show a colorbar instead
- if(traceIn.showlegend !== true) traceOut.showlegend = false;
- traceOut._dfltShowLegend = false;
-
- colorscaleDefaults(
- traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}
- );
- }
-
- coerce('line.smoothing');
-
- handleLabelDefaults(coerce, layout, lineColor, opts);
- };
-
- },{"../../components/colorscale/defaults":61,"./label_defaults":309}],316:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var scatterAttrs = _dereq_('../scatter/attributes');
- var colorscaleAttrs = _dereq_('../../components/colorscale/attributes');
- var colorbarAttrs = _dereq_('../../components/colorbar/attributes');
-
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- module.exports = extendFlat({
- z: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- x: extendFlat({}, scatterAttrs.x, {impliedEdits: {xtype: 'array'}}),
- x0: extendFlat({}, scatterAttrs.x0, {impliedEdits: {xtype: 'scaled'}}),
- dx: extendFlat({}, scatterAttrs.dx, {impliedEdits: {xtype: 'scaled'}}),
- y: extendFlat({}, scatterAttrs.y, {impliedEdits: {ytype: 'array'}}),
- y0: extendFlat({}, scatterAttrs.y0, {impliedEdits: {ytype: 'scaled'}}),
- dy: extendFlat({}, scatterAttrs.dy, {impliedEdits: {ytype: 'scaled'}}),
-
- text: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- transpose: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'calc',
-
- },
- xtype: {
- valType: 'enumerated',
- values: ['array', 'scaled'],
-
- editType: 'calc+clearAxisTypes',
-
- },
- ytype: {
- valType: 'enumerated',
- values: ['array', 'scaled'],
-
- editType: 'calc+clearAxisTypes',
-
- },
- zsmooth: {
- valType: 'enumerated',
- values: ['fast', 'best', false],
- dflt: false,
-
- editType: 'calc',
-
- },
- connectgaps: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'calc',
-
- },
- xgap: {
- valType: 'number',
- dflt: 0,
- min: 0,
-
- editType: 'plot',
-
- },
- ygap: {
- valType: 'number',
- dflt: 0,
- min: 0,
-
- editType: 'plot',
-
- },
- zhoverformat: {
- valType: 'string',
- dflt: '',
-
- editType: 'none',
-
- },
- transforms: undefined
- },
- colorscaleAttrs('', {
- cLetter: 'z',
- autoColorDflt: false
- }),
- { colorbar: colorbarAttrs }
- );
-
- },{"../../components/colorbar/attributes":52,"../../components/colorscale/attributes":58,"../../lib/extend":162,"../scatter/attributes":367}],317:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
-
- var histogram2dCalc = _dereq_('../histogram2d/calc');
- var colorscaleCalc = _dereq_('../../components/colorscale/calc');
- var convertColumnData = _dereq_('./convert_column_xyz');
- var clean2dArray = _dereq_('./clean_2d_array');
- var interp2d = _dereq_('./interp2d');
- var findEmpties = _dereq_('./find_empties');
- var makeBoundArray = _dereq_('./make_bound_array');
-
-
- module.exports = function calc(gd, trace) {
- // prepare the raw data
- // run makeCalcdata on x and y even for heatmaps, in case of category mappings
- var xa = Axes.getFromId(gd, trace.xaxis || 'x');
- var ya = Axes.getFromId(gd, trace.yaxis || 'y');
- var isContour = Registry.traceIs(trace, 'contour');
- var isHist = Registry.traceIs(trace, 'histogram');
- var isGL2D = Registry.traceIs(trace, 'gl2d');
- var zsmooth = isContour ? 'best' : trace.zsmooth;
- var x;
- var x0;
- var dx;
- var y;
- var y0;
- var dy;
- var z;
- var i;
- var binned;
-
- // cancel minimum tick spacings (only applies to bars and boxes)
- xa._minDtick = 0;
- ya._minDtick = 0;
-
- if(isHist) {
- binned = histogram2dCalc(gd, trace);
- x = binned.x;
- x0 = binned.x0;
- dx = binned.dx;
- y = binned.y;
- y0 = binned.y0;
- dy = binned.dy;
- z = binned.z;
- }
- else {
- var zIn = trace.z;
- if(Lib.isArray1D(zIn)) {
- convertColumnData(trace, xa, ya, 'x', 'y', ['z']);
- x = trace._x;
- y = trace._y;
- zIn = trace._z;
- } else {
- x = trace.x ? xa.makeCalcdata(trace, 'x') : [];
- y = trace.y ? ya.makeCalcdata(trace, 'y') : [];
- }
-
- x0 = trace.x0 || 0;
- dx = trace.dx || 1;
- y0 = trace.y0 || 0;
- dy = trace.dy || 1;
-
- z = clean2dArray(zIn, trace.transpose);
-
- if(isContour || trace.connectgaps) {
- trace._emptypoints = findEmpties(z);
- interp2d(z, trace._emptypoints);
- }
- }
-
- function noZsmooth(msg) {
- zsmooth = trace._input.zsmooth = trace.zsmooth = false;
- Lib.warn('cannot use zsmooth: "fast": ' + msg);
- }
-
- // check whether we really can smooth (ie all boxes are about the same size)
- if(zsmooth === 'fast') {
- if(xa.type === 'log' || ya.type === 'log') {
- noZsmooth('log axis found');
- }
- else if(!isHist) {
- if(x.length) {
- var avgdx = (x[x.length - 1] - x[0]) / (x.length - 1);
- var maxErrX = Math.abs(avgdx / 100);
- for(i = 0; i < x.length - 1; i++) {
- if(Math.abs(x[i + 1] - x[i] - avgdx) > maxErrX) {
- noZsmooth('x scale is not linear');
- break;
- }
- }
- }
- if(y.length && zsmooth === 'fast') {
- var avgdy = (y[y.length - 1] - y[0]) / (y.length - 1);
- var maxErrY = Math.abs(avgdy / 100);
- for(i = 0; i < y.length - 1; i++) {
- if(Math.abs(y[i + 1] - y[i] - avgdy) > maxErrY) {
- noZsmooth('y scale is not linear');
- break;
- }
- }
- }
- }
- }
-
- // create arrays of brick boundaries, to be used by autorange and heatmap.plot
- var xlen = Lib.maxRowLength(z);
- var xIn = trace.xtype === 'scaled' ? '' : x;
- var xArray = makeBoundArray(trace, xIn, x0, dx, xlen, xa);
- var yIn = trace.ytype === 'scaled' ? '' : y;
- var yArray = makeBoundArray(trace, yIn, y0, dy, z.length, ya);
-
- // handled in gl2d convert step
- if(!isGL2D) {
- trace._extremes[xa._id] = Axes.findExtremes(xa, xArray);
- trace._extremes[ya._id] = Axes.findExtremes(ya, yArray);
- }
-
- var cd0 = {
- x: xArray,
- y: yArray,
- z: z,
- text: trace._text || trace.text
- };
-
- if(xIn && xIn.length === xArray.length - 1) cd0.xCenter = xIn;
- if(yIn && yIn.length === yArray.length - 1) cd0.yCenter = yIn;
-
- if(isHist) {
- cd0.xRanges = binned.xRanges;
- cd0.yRanges = binned.yRanges;
- cd0.pts = binned.pts;
- }
-
- // auto-z and autocolorscale if applicable
- if(!isContour || trace.contours.type !== 'constraint') {
- colorscaleCalc(gd, trace, {
- vals: z,
- containerStr: '',
- cLetter: 'z'
- });
- }
-
- if(isContour && trace.contours && trace.contours.coloring === 'heatmap') {
- var dummyTrace = {
- type: trace.type === 'contour' ? 'heatmap' : 'histogram2d',
- xcalendar: trace.xcalendar,
- ycalendar: trace.ycalendar
- };
- cd0.xfill = makeBoundArray(dummyTrace, xIn, x0, dx, xlen, xa);
- cd0.yfill = makeBoundArray(dummyTrace, yIn, y0, dy, z.length, ya);
- }
-
- return [cd0];
- };
-
- },{"../../components/colorscale/calc":59,"../../lib":168,"../../plots/cartesian/axes":212,"../../registry":257,"../histogram2d/calc":345,"./clean_2d_array":318,"./convert_column_xyz":320,"./find_empties":322,"./interp2d":325,"./make_bound_array":326}],318:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- module.exports = function clean2dArray(zOld, transpose) {
- var rowlen, collen, getCollen, old2new, i, j;
-
- function cleanZvalue(v) {
- if(!isNumeric(v)) return undefined;
- return +v;
- }
-
- if(transpose) {
- rowlen = 0;
- for(i = 0; i < zOld.length; i++) rowlen = Math.max(rowlen, zOld[i].length);
- if(rowlen === 0) return false;
- getCollen = function(zOld) { return zOld.length; };
- old2new = function(zOld, i, j) { return zOld[j][i]; };
- }
- else {
- rowlen = zOld.length;
- getCollen = function(zOld, i) { return zOld[i].length; };
- old2new = function(zOld, i, j) { return zOld[i][j]; };
- }
-
- var zNew = new Array(rowlen);
-
- for(i = 0; i < rowlen; i++) {
- collen = getCollen(zOld, i);
- zNew[i] = new Array(collen);
- for(j = 0; j < collen; j++) zNew[i][j] = cleanZvalue(old2new(zOld, i, j));
- }
-
- return zNew;
- };
-
- },{"fast-isnumeric":18}],319:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- min: 'zmin',
- max: 'zmax'
- };
-
- },{}],320:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var BADNUM = _dereq_('../../constants/numerical').BADNUM;
-
- module.exports = function convertColumnData(trace, ax1, ax2, var1Name, var2Name, arrayVarNames) {
- var colLen = trace._length;
- var col1 = ax1.makeCalcdata(trace, var1Name);
- var col2 = ax2.makeCalcdata(trace, var2Name);
- var textCol = trace.text;
- var hasColumnText = (textCol !== undefined && Lib.isArray1D(textCol));
- var i, j;
-
- var col1dv = Lib.distinctVals(col1);
- var col1vals = col1dv.vals;
- var col2dv = Lib.distinctVals(col2);
- var col2vals = col2dv.vals;
- var newArrays = [];
- var text;
-
- for(i = 0; i < arrayVarNames.length; i++) {
- newArrays[i] = Lib.init2dArray(col2vals.length, col1vals.length);
- }
-
- if(hasColumnText) {
- text = Lib.init2dArray(col2vals.length, col1vals.length);
- }
-
- for(i = 0; i < colLen; i++) {
- if(col1[i] !== BADNUM && col2[i] !== BADNUM) {
- var i1 = Lib.findBin(col1[i] + col1dv.minDiff / 2, col1vals);
- var i2 = Lib.findBin(col2[i] + col2dv.minDiff / 2, col2vals);
-
- for(j = 0; j < arrayVarNames.length; j++) {
- var arrayVarName = arrayVarNames[j];
- var arrayVar = trace[arrayVarName];
- var newArray = newArrays[j];
- newArray[i2][i1] = arrayVar[i];
- }
-
- if(hasColumnText) text[i2][i1] = textCol[i];
- }
- }
-
- trace['_' + var1Name] = col1vals;
- trace['_' + var2Name] = col2vals;
- for(j = 0; j < arrayVarNames.length; j++) {
- trace['_' + arrayVarNames[j]] = newArrays[j];
- }
- if(hasColumnText) trace._text = text;
- };
-
- },{"../../constants/numerical":149,"../../lib":168}],321:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- var handleXYZDefaults = _dereq_('./xyz_defaults');
- var handleStyleDefaults = _dereq_('./style_defaults');
- var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
- var attributes = _dereq_('./attributes');
-
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- var validData = handleXYZDefaults(traceIn, traceOut, coerce, layout);
- if(!validData) {
- traceOut.visible = false;
- return;
- }
-
- coerce('text');
-
- handleStyleDefaults(traceIn, traceOut, coerce, layout);
-
- coerce('connectgaps', Lib.isArray1D(traceOut.z) && (traceOut.zsmooth !== false));
-
- colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'});
- };
-
- },{"../../components/colorscale/defaults":61,"../../lib":168,"./attributes":316,"./style_defaults":329,"./xyz_defaults":330}],322:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var maxRowLength = _dereq_('../../lib').maxRowLength;
-
- /* Return a list of empty points in 2D array z
- * each empty point z[i][j] gives an array [i, j, neighborCount]
- * neighborCount is the count of 4 nearest neighbors that DO exist
- * this is to give us an order of points to evaluate for interpolation.
- * if no neighbors exist, we iteratively look for neighbors that HAVE
- * neighbors, and add a fractional neighborCount
- */
- module.exports = function findEmpties(z) {
- var empties = [];
- var neighborHash = {};
- var noNeighborList = [];
- var nextRow = z[0];
- var row = [];
- var blank = [0, 0, 0];
- var rowLength = maxRowLength(z);
- var prevRow;
- var i;
- var j;
- var thisPt;
- var p;
- var neighborCount;
- var newNeighborHash;
- var foundNewNeighbors;
-
- for(i = 0; i < z.length; i++) {
- prevRow = row;
- row = nextRow;
- nextRow = z[i + 1] || [];
- for(j = 0; j < rowLength; j++) {
- if(row[j] === undefined) {
- neighborCount = (row[j - 1] !== undefined ? 1 : 0) +
- (row[j + 1] !== undefined ? 1 : 0) +
- (prevRow[j] !== undefined ? 1 : 0) +
- (nextRow[j] !== undefined ? 1 : 0);
-
- if(neighborCount) {
- // for this purpose, don't count off-the-edge points
- // as undefined neighbors
- if(i === 0) neighborCount++;
- if(j === 0) neighborCount++;
- if(i === z.length - 1) neighborCount++;
- if(j === row.length - 1) neighborCount++;
-
- // if all neighbors that could exist do, we don't
- // need this for finding farther neighbors
- if(neighborCount < 4) {
- neighborHash[[i, j]] = [i, j, neighborCount];
- }
-
- empties.push([i, j, neighborCount]);
- }
- else noNeighborList.push([i, j]);
- }
- }
- }
-
- while(noNeighborList.length) {
- newNeighborHash = {};
- foundNewNeighbors = false;
-
- // look for cells that now have neighbors but didn't before
- for(p = noNeighborList.length - 1; p >= 0; p--) {
- thisPt = noNeighborList[p];
- i = thisPt[0];
- j = thisPt[1];
-
- neighborCount = ((neighborHash[[i - 1, j]] || blank)[2] +
- (neighborHash[[i + 1, j]] || blank)[2] +
- (neighborHash[[i, j - 1]] || blank)[2] +
- (neighborHash[[i, j + 1]] || blank)[2]) / 20;
-
- if(neighborCount) {
- newNeighborHash[thisPt] = [i, j, neighborCount];
- noNeighborList.splice(p, 1);
- foundNewNeighbors = true;
- }
- }
-
- if(!foundNewNeighbors) {
- throw 'findEmpties iterated with no new neighbors';
- }
-
- // put these new cells into the main neighbor list
- for(thisPt in newNeighborHash) {
- neighborHash[thisPt] = newNeighborHash[thisPt];
- empties.push(newNeighborHash[thisPt]);
- }
- }
-
- // sort the full list in descending order of neighbor count
- return empties.sort(function(a, b) { return b[2] - a[2]; });
- };
-
- },{"../../lib":168}],323:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Fx = _dereq_('../../components/fx');
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
-
- module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer, contour) {
- var cd0 = pointData.cd[0];
- var trace = cd0.trace;
- var xa = pointData.xa;
- var ya = pointData.ya;
- var x = cd0.x;
- var y = cd0.y;
- var z = cd0.z;
- var xc = cd0.xCenter;
- var yc = cd0.yCenter;
- var zmask = cd0.zmask;
- var range = [trace.zmin, trace.zmax];
- var zhoverformat = trace.zhoverformat;
- var x2 = x;
- var y2 = y;
-
- var xl, yl, nx, ny;
-
- if(pointData.index !== false) {
- try {
- nx = Math.round(pointData.index[1]);
- ny = Math.round(pointData.index[0]);
- }
- catch(e) {
- Lib.error('Error hovering on heatmap, ' +
- 'pointNumber must be [row,col], found:', pointData.index);
- return;
- }
- if(nx < 0 || nx >= z[0].length || ny < 0 || ny > z.length) {
- return;
- }
- }
- else if(Fx.inbox(xval - x[0], xval - x[x.length - 1], 0) > 0 ||
- Fx.inbox(yval - y[0], yval - y[y.length - 1], 0) > 0) {
- return;
- }
- else {
- if(contour) {
- var i2;
- x2 = [2 * x[0] - x[1]];
-
- for(i2 = 1; i2 < x.length; i2++) {
- x2.push((x[i2] + x[i2 - 1]) / 2);
- }
- x2.push([2 * x[x.length - 1] - x[x.length - 2]]);
-
- y2 = [2 * y[0] - y[1]];
- for(i2 = 1; i2 < y.length; i2++) {
- y2.push((y[i2] + y[i2 - 1]) / 2);
- }
- y2.push([2 * y[y.length - 1] - y[y.length - 2]]);
- }
- nx = Math.max(0, Math.min(x2.length - 2, Lib.findBin(xval, x2)));
- ny = Math.max(0, Math.min(y2.length - 2, Lib.findBin(yval, y2)));
- }
-
- var x0 = xa.c2p(x[nx]);
- var x1 = xa.c2p(x[nx + 1]);
- var y0 = ya.c2p(y[ny]);
- var y1 = ya.c2p(y[ny + 1]);
-
- if(contour) {
- x1 = x0;
- xl = x[nx];
- y1 = y0;
- yl = y[ny];
- }
- else {
- xl = xc ? xc[nx] : ((x[nx] + x[nx + 1]) / 2);
- yl = yc ? yc[ny] : ((y[ny] + y[ny + 1]) / 2);
- if(trace.zsmooth) {
- x0 = x1 = xa.c2p(xl);
- y0 = y1 = ya.c2p(yl);
- }
- }
-
- var zVal = z[ny][nx];
- if(zmask && !zmask[ny][nx]) zVal = undefined;
-
- var text;
- if(Array.isArray(cd0.text) && Array.isArray(cd0.text[ny])) {
- text = cd0.text[ny][nx];
- }
-
- var zLabel;
- // dummy axis for formatting the z value
- var dummyAx = {
- type: 'linear',
- range: range,
- hoverformat: zhoverformat,
- _separators: xa._separators,
- _numFormat: xa._numFormat
- };
- var zLabelObj = Axes.tickText(dummyAx, zVal, 'hover');
- zLabel = zLabelObj.text;
-
- return [Lib.extendFlat(pointData, {
- index: [ny, nx],
- // never let a 2D override 1D type as closest point
- distance: pointData.maxHoverDistance,
- spikeDistance: pointData.maxSpikeDistance,
- x0: x0,
- x1: x1,
- y0: y0,
- y1: y1,
- xLabelVal: xl,
- yLabelVal: yl,
- zLabelVal: zVal,
- zLabel: zLabel,
- text: text
- })];
- };
-
- },{"../../components/fx":90,"../../lib":168,"../../plots/cartesian/axes":212}],324:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Heatmap = {};
-
- Heatmap.attributes = _dereq_('./attributes');
- Heatmap.supplyDefaults = _dereq_('./defaults');
- Heatmap.calc = _dereq_('./calc');
- Heatmap.plot = _dereq_('./plot');
- Heatmap.colorbar = _dereq_('./colorbar');
- Heatmap.style = _dereq_('./style');
- Heatmap.hoverPoints = _dereq_('./hover');
-
- Heatmap.moduleType = 'trace';
- Heatmap.name = 'heatmap';
- Heatmap.basePlotModule = _dereq_('../../plots/cartesian');
- Heatmap.categories = ['cartesian', 'svg', '2dMap'];
- Heatmap.meta = {
-
- };
-
- module.exports = Heatmap;
-
- },{"../../plots/cartesian":224,"./attributes":316,"./calc":317,"./colorbar":319,"./defaults":321,"./hover":323,"./plot":327,"./style":328}],325:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- var INTERPTHRESHOLD = 1e-2;
- var NEIGHBORSHIFTS = [[-1, 0], [1, 0], [0, -1], [0, 1]];
-
- function correctionOvershoot(maxFractionalChange) {
- // start with less overshoot, until we know it's converging,
- // then ramp up the overshoot for faster convergence
- return 0.5 - 0.25 * Math.min(1, maxFractionalChange * 0.5);
- }
-
- /*
- * interp2d: Fill in missing data from a 2D array using an iterative
- * poisson equation solver with zero-derivative BC at edges.
- * Amazingly, this just amounts to repeatedly averaging all the existing
- * nearest neighbors, at least if we don't take x/y scaling into account,
- * which is the right approach here where x and y may not even have the
- * same units.
- *
- * @param {array of arrays} z
- * The 2D array to fill in. Will be mutated here. Assumed to already be
- * cleaned, so all entries are numbers except gaps, which are `undefined`.
- * @param {array of arrays} emptyPoints
- * Each entry [i, j, neighborCount] for empty points z[i][j] and the number
- * of neighbors that are *not* missing. Assumed to be sorted from most to
- * least neighbors, as produced by heatmap/find_empties.
- */
- module.exports = function interp2d(z, emptyPoints) {
- var maxFractionalChange = 1;
- var i;
-
- // one pass to fill in a starting value for all the empties
- iterateInterp2d(z, emptyPoints);
-
- // we're don't need to iterate lone empties - remove them
- for(i = 0; i < emptyPoints.length; i++) {
- if(emptyPoints[i][2] < 4) break;
- }
- // but don't remove these points from the original array,
- // we'll use them for masking, so make a copy.
- emptyPoints = emptyPoints.slice(i);
-
- for(i = 0; i < 100 && maxFractionalChange > INTERPTHRESHOLD; i++) {
- maxFractionalChange = iterateInterp2d(z, emptyPoints,
- correctionOvershoot(maxFractionalChange));
- }
- if(maxFractionalChange > INTERPTHRESHOLD) {
- Lib.log('interp2d didn\'t converge quickly', maxFractionalChange);
- }
-
- return z;
- };
-
- function iterateInterp2d(z, emptyPoints, overshoot) {
- var maxFractionalChange = 0;
- var thisPt;
- var i;
- var j;
- var p;
- var q;
- var neighborShift;
- var neighborRow;
- var neighborVal;
- var neighborCount;
- var neighborSum;
- var initialVal;
- var minNeighbor;
- var maxNeighbor;
-
- for(p = 0; p < emptyPoints.length; p++) {
- thisPt = emptyPoints[p];
- i = thisPt[0];
- j = thisPt[1];
- initialVal = z[i][j];
- neighborSum = 0;
- neighborCount = 0;
-
- for(q = 0; q < 4; q++) {
- neighborShift = NEIGHBORSHIFTS[q];
- neighborRow = z[i + neighborShift[0]];
- if(!neighborRow) continue;
- neighborVal = neighborRow[j + neighborShift[1]];
- if(neighborVal !== undefined) {
- if(neighborSum === 0) {
- minNeighbor = maxNeighbor = neighborVal;
- }
- else {
- minNeighbor = Math.min(minNeighbor, neighborVal);
- maxNeighbor = Math.max(maxNeighbor, neighborVal);
- }
- neighborCount++;
- neighborSum += neighborVal;
- }
- }
-
- if(neighborCount === 0) {
- throw 'iterateInterp2d order is wrong: no defined neighbors';
- }
-
- // this is the laplace equation interpolation:
- // each point is just the average of its neighbors
- // note that this ignores differential x/y scaling
- // which I think is the right approach, since we
- // don't know what that scaling means
- z[i][j] = neighborSum / neighborCount;
-
- if(initialVal === undefined) {
- if(neighborCount < 4) maxFractionalChange = 1;
- }
- else {
- // we can make large empty regions converge faster
- // if we overshoot the change vs the previous value
- z[i][j] = (1 + overshoot) * z[i][j] - overshoot * initialVal;
-
- if(maxNeighbor > minNeighbor) {
- maxFractionalChange = Math.max(maxFractionalChange,
- Math.abs(z[i][j] - initialVal) / (maxNeighbor - minNeighbor));
- }
- }
- }
-
- return maxFractionalChange;
- }
-
- },{"../../lib":168}],326:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
-
- module.exports = function makeBoundArray(trace, arrayIn, v0In, dvIn, numbricks, ax) {
- var arrayOut = [];
- var isContour = Registry.traceIs(trace, 'contour');
- var isHist = Registry.traceIs(trace, 'histogram');
- var isGL2D = Registry.traceIs(trace, 'gl2d');
- var v0;
- var dv;
- var i;
-
- var isArrayOfTwoItemsOrMore = isArrayOrTypedArray(arrayIn) && arrayIn.length > 1;
-
- if(isArrayOfTwoItemsOrMore && !isHist && (ax.type !== 'category')) {
- var len = arrayIn.length;
-
- // given vals are brick centers
- // hopefully length === numbricks, but use this method even if too few are supplied
- // and extend it linearly based on the last two points
- if(len <= numbricks) {
- // contour plots only want the centers
- if(isContour || isGL2D) arrayOut = arrayIn.slice(0, numbricks);
- else if(numbricks === 1) {
- arrayOut = [arrayIn[0] - 0.5, arrayIn[0] + 0.5];
- }
- else {
- arrayOut = [1.5 * arrayIn[0] - 0.5 * arrayIn[1]];
-
- for(i = 1; i < len; i++) {
- arrayOut.push((arrayIn[i - 1] + arrayIn[i]) * 0.5);
- }
-
- arrayOut.push(1.5 * arrayIn[len - 1] - 0.5 * arrayIn[len - 2]);
- }
-
- if(len < numbricks) {
- var lastPt = arrayOut[arrayOut.length - 1];
- var delta = lastPt - arrayOut[arrayOut.length - 2];
-
- for(i = len; i < numbricks; i++) {
- lastPt += delta;
- arrayOut.push(lastPt);
- }
- }
- }
- else {
- // hopefully length === numbricks+1, but do something regardless:
- // given vals are brick boundaries
- return isContour ?
- arrayIn.slice(0, numbricks) : // we must be strict for contours
- arrayIn.slice(0, numbricks + 1);
- }
- }
- else {
- dv = dvIn || 1;
-
- var calendar = trace[ax._id.charAt(0) + 'calendar'];
-
- if(isHist || ax.type === 'category' || ax.type === 'multicategory') {
- v0 = ax.r2c(v0In, 0, calendar) || 0;
- } else if(isArrayOrTypedArray(arrayIn) && arrayIn.length === 1) {
- v0 = arrayIn[0];
- } else if(v0In === undefined) {
- v0 = 0;
- } else {
- v0 = ax.d2c(v0In, 0, calendar);
- }
-
- for(i = (isContour || isGL2D) ? 0 : -0.5; i < numbricks; i++) {
- arrayOut.push(v0 + dv * i);
- }
- }
-
- return arrayOut;
- };
-
- },{"../../lib":168,"../../registry":257}],327:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var tinycolor = _dereq_('tinycolor2');
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var Colorscale = _dereq_('../../components/colorscale');
- var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
-
- module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- Lib.makeTraceGroups(heatmapLayer, cdheatmaps, 'hm').each(function(cd) {
- var plotGroup = d3.select(this);
- var cd0 = cd[0];
- var trace = cd0.trace;
-
- var z = cd0.z;
- var x = cd0.x;
- var y = cd0.y;
- var xc = cd0.xCenter;
- var yc = cd0.yCenter;
- var isContour = Registry.traceIs(trace, 'contour');
- var zsmooth = isContour ? 'best' : trace.zsmooth;
-
- // get z dims
- var m = z.length;
- var n = Lib.maxRowLength(z);
- var xrev = false;
- var yrev = false;
-
- var left, right, temp, top, bottom, i;
-
- // TODO: if there are multiple overlapping categorical heatmaps,
- // or if we allow category sorting, then the categories may not be
- // sequential... may need to reorder and/or expand z
-
- // Get edges of png in pixels (xa.c2p() maps axes coordinates to pixel coordinates)
- // figure out if either axis is reversed (y is usually reversed, in pixel coords)
- // also clip the image to maximum 50% outside the visible plot area
- // bigger image lets you pan more naturally, but slows performance.
- // TODO: use low-resolution images outside the visible plot for panning
- // these while loops find the first and last brick bounds that are defined
- // (in case of log of a negative)
- i = 0;
- while(left === undefined && i < x.length - 1) {
- left = xa.c2p(x[i]);
- i++;
- }
- i = x.length - 1;
- while(right === undefined && i > 0) {
- right = xa.c2p(x[i]);
- i--;
- }
-
- if(right < left) {
- temp = right;
- right = left;
- left = temp;
- xrev = true;
- }
-
- i = 0;
- while(top === undefined && i < y.length - 1) {
- top = ya.c2p(y[i]);
- i++;
- }
- i = y.length - 1;
- while(bottom === undefined && i > 0) {
- bottom = ya.c2p(y[i]);
- i--;
- }
-
- if(bottom < top) {
- temp = top;
- top = bottom;
- bottom = temp;
- yrev = true;
- }
-
- // for contours with heatmap fill, we generate the boundaries based on
- // brick centers but then use the brick edges for drawing the bricks
- if(isContour) {
- xc = x;
- yc = y;
- x = cd0.xfill;
- y = cd0.yfill;
- }
-
- // make an image that goes at most half a screen off either side, to keep
- // time reasonable when you zoom in. if zsmooth is true/fast, don't worry
- // about this, because zooming doesn't increase number of pixels
- // if zsmooth is best, don't include anything off screen because it takes too long
- if(zsmooth !== 'fast') {
- var extra = zsmooth === 'best' ? 0 : 0.5;
- left = Math.max(-extra * xa._length, left);
- right = Math.min((1 + extra) * xa._length, right);
- top = Math.max(-extra * ya._length, top);
- bottom = Math.min((1 + extra) * ya._length, bottom);
- }
-
- var imageWidth = Math.round(right - left);
- var imageHeight = Math.round(bottom - top);
-
- // setup image nodes
-
- // if image is entirely off-screen, don't even draw it
- var isOffScreen = (imageWidth <= 0 || imageHeight <= 0);
-
- if(isOffScreen) {
- var noImage = plotGroup.selectAll('image').data([]);
- noImage.exit().remove();
- return;
- }
-
- // generate image data
-
- var canvasW, canvasH;
- if(zsmooth === 'fast') {
- canvasW = n;
- canvasH = m;
- } else {
- canvasW = imageWidth;
- canvasH = imageHeight;
- }
-
- var canvas = document.createElement('canvas');
- canvas.width = canvasW;
- canvas.height = canvasH;
- var context = canvas.getContext('2d');
-
- var sclFunc = Colorscale.makeColorScaleFunc(
- Colorscale.extractScale(trace, {cLetter: 'z'}),
- { noNumericCheck: true, returnArray: true }
- );
-
- // map brick boundaries to image pixels
- var xpx,
- ypx;
- if(zsmooth === 'fast') {
- xpx = xrev ?
- function(index) { return n - 1 - index; } :
- Lib.identity;
- ypx = yrev ?
- function(index) { return m - 1 - index; } :
- Lib.identity;
- }
- else {
- xpx = function(index) {
- return Lib.constrain(Math.round(xa.c2p(x[index]) - left),
- 0, imageWidth);
- };
- ypx = function(index) {
- return Lib.constrain(Math.round(ya.c2p(y[index]) - top),
- 0, imageHeight);
- };
- }
-
- // build the pixel map brick-by-brick
- // cruise through z-matrix row-by-row
- // build a brick at each z-matrix value
- var yi = ypx(0);
- var yb = [yi, yi];
- var xbi = xrev ? 0 : 1;
- var ybi = yrev ? 0 : 1;
- // for collecting an average luminosity of the heatmap
- var pixcount = 0;
- var rcount = 0;
- var gcount = 0;
- var bcount = 0;
-
- var xb, j, xi, v, row, c;
-
- function setColor(v, pixsize) {
- if(v !== undefined) {
- var c = sclFunc(v);
- c[0] = Math.round(c[0]);
- c[1] = Math.round(c[1]);
- c[2] = Math.round(c[2]);
-
- pixcount += pixsize;
- rcount += c[0] * pixsize;
- gcount += c[1] * pixsize;
- bcount += c[2] * pixsize;
- return c;
- }
- return [0, 0, 0, 0];
- }
-
- function interpColor(r0, r1, xinterp, yinterp) {
- var z00 = r0[xinterp.bin0];
- if(z00 === undefined) return setColor(undefined, 1);
-
- var z01 = r0[xinterp.bin1];
- var z10 = r1[xinterp.bin0];
- var z11 = r1[xinterp.bin1];
- var dx = (z01 - z00) || 0;
- var dy = (z10 - z00) || 0;
- var dxy;
-
- // the bilinear interpolation term needs different calculations
- // for all the different permutations of missing data
- // among the neighbors of the main point, to ensure
- // continuity across brick boundaries.
- if(z01 === undefined) {
- if(z11 === undefined) dxy = 0;
- else if(z10 === undefined) dxy = 2 * (z11 - z00);
- else dxy = (2 * z11 - z10 - z00) * 2 / 3;
- }
- else if(z11 === undefined) {
- if(z10 === undefined) dxy = 0;
- else dxy = (2 * z00 - z01 - z10) * 2 / 3;
- }
- else if(z10 === undefined) dxy = (2 * z11 - z01 - z00) * 2 / 3;
- else dxy = (z11 + z00 - z01 - z10);
-
- return setColor(z00 + xinterp.frac * dx + yinterp.frac * (dy + xinterp.frac * dxy));
- }
-
- if(zsmooth) { // best or fast, works fastest with imageData
- var pxIndex = 0;
- var pixels;
-
- try {
- pixels = new Uint8Array(imageWidth * imageHeight * 4);
- } catch(e) {
- pixels = new Array(imageWidth * imageHeight * 4);
- }
-
- if(zsmooth === 'best') {
- var xForPx = xc || x;
- var yForPx = yc || y;
- var xPixArray = new Array(xForPx.length);
- var yPixArray = new Array(yForPx.length);
- var xinterpArray = new Array(imageWidth);
- var findInterpX = xc ? findInterpFromCenters : findInterp;
- var findInterpY = yc ? findInterpFromCenters : findInterp;
- var yinterp, r0, r1;
-
- // first make arrays of x and y pixel locations of brick boundaries
- for(i = 0; i < xForPx.length; i++) xPixArray[i] = Math.round(xa.c2p(xForPx[i]) - left);
- for(i = 0; i < yForPx.length; i++) yPixArray[i] = Math.round(ya.c2p(yForPx[i]) - top);
-
- // then make arrays of interpolations
- // (bin0=closest, bin1=next, frac=fractional dist.)
- for(i = 0; i < imageWidth; i++) xinterpArray[i] = findInterpX(i, xPixArray);
-
- // now do the interpolations and fill the png
- for(j = 0; j < imageHeight; j++) {
- yinterp = findInterpY(j, yPixArray);
- r0 = z[yinterp.bin0];
- r1 = z[yinterp.bin1];
- for(i = 0; i < imageWidth; i++, pxIndex += 4) {
- c = interpColor(r0, r1, xinterpArray[i], yinterp);
- putColor(pixels, pxIndex, c);
- }
- }
- }
- else { // zsmooth = fast
- for(j = 0; j < m; j++) {
- row = z[j];
- yb = ypx(j);
- for(i = 0; i < imageWidth; i++) {
- c = setColor(row[i], 1);
- pxIndex = (yb * imageWidth + xpx(i)) * 4;
- putColor(pixels, pxIndex, c);
- }
- }
- }
-
- var imageData = context.createImageData(imageWidth, imageHeight);
- try {
- imageData.data.set(pixels);
- } catch(e) {
- var pxArray = imageData.data;
- var dlen = pxArray.length;
- for(j = 0; j < dlen; j ++) {
- pxArray[j] = pixels[j];
- }
- }
-
- context.putImageData(imageData, 0, 0);
- } else { // zsmooth = false -> filling potentially large bricks works fastest with fillRect
-
- // gaps do not need to be exact integers, but if they *are* we will get
- // cleaner edges by rounding at least one edge
- var xGap = trace.xgap;
- var yGap = trace.ygap;
- var xGapLeft = Math.floor(xGap / 2);
- var yGapTop = Math.floor(yGap / 2);
-
- for(j = 0; j < m; j++) {
- row = z[j];
- yb.reverse();
- yb[ybi] = ypx(j + 1);
- if(yb[0] === yb[1] || yb[0] === undefined || yb[1] === undefined) {
- continue;
- }
- xi = xpx(0);
- xb = [xi, xi];
- for(i = 0; i < n; i++) {
- // build one color brick!
- xb.reverse();
- xb[xbi] = xpx(i + 1);
- if(xb[0] === xb[1] || xb[0] === undefined || xb[1] === undefined) {
- continue;
- }
- v = row[i];
- c = setColor(v, (xb[1] - xb[0]) * (yb[1] - yb[0]));
- context.fillStyle = 'rgba(' + c.join(',') + ')';
-
- context.fillRect(xb[0] + xGapLeft, yb[0] + yGapTop,
- xb[1] - xb[0] - xGap, yb[1] - yb[0] - yGap);
- }
- }
- }
-
- rcount = Math.round(rcount / pixcount);
- gcount = Math.round(gcount / pixcount);
- bcount = Math.round(bcount / pixcount);
- var avgColor = tinycolor('rgb(' + rcount + ',' + gcount + ',' + bcount + ')');
-
- gd._hmpixcount = (gd._hmpixcount||0) + pixcount;
- gd._hmlumcount = (gd._hmlumcount||0) + pixcount * avgColor.getLuminance();
-
- var image3 = plotGroup.selectAll('image')
- .data(cd);
-
- image3.enter().append('svg:image').attr({
- xmlns: xmlnsNamespaces.svg,
- preserveAspectRatio: 'none'
- });
-
- image3.attr({
- height: imageHeight,
- width: imageWidth,
- x: left,
- y: top,
- 'xlink:href': canvas.toDataURL('image/png')
- });
- });
- };
-
- // get interpolated bin value. Returns {bin0:closest bin, frac:fractional dist to next, bin1:next bin}
- function findInterp(pixel, pixArray) {
- var maxBin = pixArray.length - 2;
- var bin = Lib.constrain(Lib.findBin(pixel, pixArray), 0, maxBin);
- var pix0 = pixArray[bin];
- var pix1 = pixArray[bin + 1];
- var interp = Lib.constrain(bin + (pixel - pix0) / (pix1 - pix0) - 0.5, 0, maxBin);
- var bin0 = Math.round(interp);
- var frac = Math.abs(interp - bin0);
-
- if(!interp || interp === maxBin || !frac) {
- return {
- bin0: bin0,
- bin1: bin0,
- frac: 0
- };
- }
- return {
- bin0: bin0,
- frac: frac,
- bin1: Math.round(bin0 + frac / (interp - bin0))
- };
- }
-
- function findInterpFromCenters(pixel, centerPixArray) {
- var maxBin = centerPixArray.length - 1;
- var bin = Lib.constrain(Lib.findBin(pixel, centerPixArray), 0, maxBin);
- var pix0 = centerPixArray[bin];
- var pix1 = centerPixArray[bin + 1];
- var frac = ((pixel - pix0) / (pix1 - pix0)) || 0;
- if(frac <= 0) {
- return {
- bin0: bin,
- bin1: bin,
- frac: 0
- };
- }
- if(frac < 0.5) {
- return {
- bin0: bin,
- bin1: bin + 1,
- frac: frac
- };
- }
- return {
- bin0: bin + 1,
- bin1: bin,
- frac: 1 - frac
- };
- }
-
- function putColor(pixels, pxIndex, c) {
- pixels[pxIndex] = c[0];
- pixels[pxIndex + 1] = c[1];
- pixels[pxIndex + 2] = c[2];
- pixels[pxIndex + 3] = Math.round(c[3] * 255);
- }
-
- },{"../../components/colorscale":63,"../../constants/xmlns_namespaces":150,"../../lib":168,"../../registry":257,"d3":16,"tinycolor2":34}],328:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- module.exports = function style(gd) {
- d3.select(gd).selectAll('.hm image')
- .style('opacity', function(d) {
- return d.trace.opacity;
- });
- };
-
- },{"d3":16}],329:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- module.exports = function handleStyleDefaults(traceIn, traceOut, coerce) {
- var zsmooth = coerce('zsmooth');
- if(zsmooth === false) {
- // ensure that xgap and ygap are coerced only when zsmooth allows them to have an effect.
- coerce('xgap');
- coerce('ygap');
- }
-
- coerce('zhoverformat');
- };
-
- },{}],330:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var Lib = _dereq_('../../lib');
-
- var Registry = _dereq_('../../registry');
-
- module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout, xName, yName) {
- var z = coerce('z');
- xName = xName || 'x';
- yName = yName || 'y';
- var x, y;
-
- if(z === undefined || !z.length) return 0;
-
- if(Lib.isArray1D(traceIn.z)) {
- x = coerce(xName);
- y = coerce(yName);
-
- var xlen = Lib.minRowLength(x);
- var ylen = Lib.minRowLength(y);
-
- // column z must be accompanied by xName and yName arrays
- if(xlen === 0 || ylen === 0) return 0;
-
- traceOut._length = Math.min(xlen, ylen, z.length);
- }
- else {
- x = coordDefaults(xName, coerce);
- y = coordDefaults(yName, coerce);
-
- // TODO put z validation elsewhere
- if(!isValidZ(z)) return 0;
-
- coerce('transpose');
-
- traceOut._length = null;
- }
-
- var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
- handleCalendarDefaults(traceIn, traceOut, [xName, yName], layout);
-
- return true;
- };
-
- function coordDefaults(coordStr, coerce) {
- var coord = coerce(coordStr);
- var coordType = coord ? coerce(coordStr + 'type', 'array') : 'scaled';
-
- if(coordType === 'scaled') {
- coerce(coordStr + '0');
- coerce('d' + coordStr);
- }
-
- return coord;
- }
-
- function isValidZ(z) {
- var allRowsAreArrays = true;
- var oneRowIsFilled = false;
- var hasOneNumber = false;
- var zi;
-
- /*
- * Without this step:
- *
- * hasOneNumber = false breaks contour but not heatmap
- * allRowsAreArrays = false breaks contour but not heatmap
- * oneRowIsFilled = false breaks both
- */
-
- for(var i = 0; i < z.length; i++) {
- zi = z[i];
- if(!Lib.isArrayOrTypedArray(zi)) {
- allRowsAreArrays = false;
- break;
- }
- if(zi.length > 0) oneRowIsFilled = true;
- for(var j = 0; j < zi.length; j++) {
- if(isNumeric(zi[j])) {
- hasOneNumber = true;
- break;
- }
- }
- }
-
- return (allRowsAreArrays && oneRowIsFilled && hasOneNumber);
- }
-
- },{"../../lib":168,"../../registry":257,"fast-isnumeric":18}],331:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var barAttrs = _dereq_('../bar/attributes');
- var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes');
- var makeBinAttrs = _dereq_('./bin_attributes');
- var constants = _dereq_('./constants');
-
- module.exports = {
- x: {
- valType: 'data_array',
- editType: 'calc+clearAxisTypes',
-
- },
- y: {
- valType: 'data_array',
- editType: 'calc+clearAxisTypes',
-
- },
-
- text: barAttrs.text,
- orientation: barAttrs.orientation,
-
- histfunc: {
- valType: 'enumerated',
- values: ['count', 'sum', 'avg', 'min', 'max'],
-
- dflt: 'count',
- editType: 'calc',
-
- },
- histnorm: {
- valType: 'enumerated',
- values: ['', 'percent', 'probability', 'density', 'probability density'],
- dflt: '',
-
- editType: 'calc',
-
- },
-
- cumulative: {
- enabled: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'calc',
-
- },
-
- direction: {
- valType: 'enumerated',
- values: ['increasing', 'decreasing'],
- dflt: 'increasing',
-
- editType: 'calc',
-
- },
-
- currentbin: {
- valType: 'enumerated',
- values: ['include', 'exclude', 'half'],
- dflt: 'include',
-
- editType: 'calc',
-
- },
- editType: 'calc'
- },
- nbinsx: {
- valType: 'integer',
- min: 0,
- dflt: 0,
-
- editType: 'calc',
-
- },
- xbins: makeBinAttrs('x', true),
-
- nbinsy: {
- valType: 'integer',
- min: 0,
- dflt: 0,
-
- editType: 'calc',
-
- },
- ybins: makeBinAttrs('y', true),
- autobinx: {
- valType: 'boolean',
- dflt: null,
-
- editType: 'calc',
-
- },
- autobiny: {
- valType: 'boolean',
- dflt: null,
-
- editType: 'calc',
-
- },
-
- hovertemplate: hovertemplateAttrs({}, {
- keys: constants.eventDataKeys
- }),
-
- marker: barAttrs.marker,
-
- selected: barAttrs.selected,
- unselected: barAttrs.unselected,
-
- _deprecated: {
- bardir: barAttrs._deprecated.bardir
- }
- };
-
- },{"../../components/fx/hovertemplate_attributes":89,"../bar/attributes":267,"./bin_attributes":333,"./constants":337}],332:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- module.exports = function doAvg(size, counts) {
- var nMax = size.length;
- var total = 0;
- for(var i = 0; i < nMax; i++) {
- if(counts[i]) {
- size[i] /= counts[i];
- total += size[i];
- }
- else size[i] = null;
- }
- return total;
- };
-
- },{}],333:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = function makeBinAttrs(axLetter, match) {
- return {
- start: {
- valType: 'any', // for date axes
-
- editType: 'calc',
-
- },
- end: {
- valType: 'any', // for date axes
-
- editType: 'calc',
-
- },
- size: {
- valType: 'any', // for date axes
-
- editType: 'calc',
-
- },
- editType: 'calc'
- };
- };
-
- },{}],334:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
-
- module.exports = {
- count: function(n, i, size) {
- size[n]++;
- return 1;
- },
-
- sum: function(n, i, size, counterData) {
- var v = counterData[i];
- if(isNumeric(v)) {
- v = Number(v);
- size[n] += v;
- return v;
- }
- return 0;
- },
-
- avg: function(n, i, size, counterData, counts) {
- var v = counterData[i];
- if(isNumeric(v)) {
- v = Number(v);
- size[n] += v;
- counts[n]++;
- }
- return 0;
- },
-
- min: function(n, i, size, counterData) {
- var v = counterData[i];
- if(isNumeric(v)) {
- v = Number(v);
- if(!isNumeric(size[n])) {
- size[n] = v;
- return v;
- }
- else if(size[n] > v) {
- var delta = v - size[n];
- size[n] = v;
- return delta;
- }
- }
- return 0;
- },
-
- max: function(n, i, size, counterData) {
- var v = counterData[i];
- if(isNumeric(v)) {
- v = Number(v);
- if(!isNumeric(size[n])) {
- size[n] = v;
- return v;
- }
- else if(size[n] < v) {
- var delta = v - size[n];
- size[n] = v;
- return delta;
- }
- }
- return 0;
- }
- };
-
- },{"fast-isnumeric":18}],335:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var numConstants = _dereq_('../../constants/numerical');
- var oneYear = numConstants.ONEAVGYEAR;
- var oneMonth = numConstants.ONEAVGMONTH;
- var oneDay = numConstants.ONEDAY;
- var oneHour = numConstants.ONEHOUR;
- var oneMin = numConstants.ONEMIN;
- var oneSec = numConstants.ONESEC;
- var tickIncrement = _dereq_('../../plots/cartesian/axes').tickIncrement;
-
-
- /*
- * make a function that will find rounded bin edges
- * @param {number} leftGap: how far from the left edge of any bin is the closest data value?
- * @param {number} rightGap: how far from the right edge of any bin is the closest data value?
- * @param {Array[number]} binEdges: the actual edge values used in binning
- * @param {object} pa: the position axis
- * @param {string} calendar: the data calendar
- *
- * @return {function(v, isRightEdge)}:
- * find the start (isRightEdge is falsy) or end (truthy) label value for a bin edge `v`
- */
- module.exports = function getBinSpanLabelRound(leftGap, rightGap, binEdges, pa, calendar) {
- // the rounding digit is the largest digit that changes in *all* of 4 regions:
- // - inside the rightGap before binEdges[0] (shifted 10% to the left)
- // - inside the leftGap after binEdges[0] (expanded by 10% of rightGap on each end)
- // - same for binEdges[1]
- var dv0 = -1.1 * rightGap;
- var dv1 = -0.1 * rightGap;
- var dv2 = leftGap - dv1;
- var edge0 = binEdges[0];
- var edge1 = binEdges[1];
- var leftDigit = Math.min(
- biggestDigitChanged(edge0 + dv1, edge0 + dv2, pa, calendar),
- biggestDigitChanged(edge1 + dv1, edge1 + dv2, pa, calendar)
- );
- var rightDigit = Math.min(
- biggestDigitChanged(edge0 + dv0, edge0 + dv1, pa, calendar),
- biggestDigitChanged(edge1 + dv0, edge1 + dv1, pa, calendar)
- );
-
- // normally we try to make the label for the right edge different from
- // the left edge label, so it's unambiguous which bin gets data on the edge.
- // but if this results in more than 3 extra digits (or for dates, more than
- // 2 fields ie hr&min or min&sec, which is 3600x), it'll be more clutter than
- // useful so keep the label cleaner instead
- var digit, disambiguateEdges;
- if(leftDigit > rightDigit && rightDigit < Math.abs(edge1 - edge0) / 4000) {
- digit = leftDigit;
- disambiguateEdges = false;
- }
- else {
- digit = Math.min(leftDigit, rightDigit);
- disambiguateEdges = true;
- }
-
- if(pa.type === 'date' && digit > oneDay) {
- var dashExclude = (digit === oneYear) ? 1 : 6;
- var increment = (digit === oneYear) ? 'M12' : 'M1';
-
- return function(v, isRightEdge) {
- var dateStr = pa.c2d(v, oneYear, calendar);
- var dashPos = dateStr.indexOf('-', dashExclude);
- if(dashPos > 0) dateStr = dateStr.substr(0, dashPos);
- var roundedV = pa.d2c(dateStr, 0, calendar);
-
- if(roundedV < v) {
- var nextV = tickIncrement(roundedV, increment, false, calendar);
- if((roundedV + nextV) / 2 < v + leftGap) roundedV = nextV;
- }
-
- if(isRightEdge && disambiguateEdges) {
- return tickIncrement(roundedV, increment, true, calendar);
- }
-
- return roundedV;
- };
- }
-
- return function(v, isRightEdge) {
- var roundedV = digit * Math.round(v / digit);
- // if we rounded down and we could round up and still be < leftGap
- // (or what leftGap values round to), do that
- if(roundedV + (digit / 10) < v && roundedV + (digit * 0.9) < v + leftGap) {
- roundedV += digit;
- }
- // finally for the right edge back off one digit - but only if we can do that
- // and not clip off any data that's potentially in the bin
- if(isRightEdge && disambiguateEdges) {
- roundedV -= digit;
- }
- return roundedV;
- };
- };
-
- /*
- * Find the largest digit that changes within a (calcdata) region [v1, v2]
- * if dates, "digit" means date/time part when it's bigger than a second
- * returns the unit value to round to this digit, eg 0.01 to round to hundredths, or
- * 100 to round to hundreds. returns oneMonth or oneYear for month or year rounding,
- * so that Math.min will work, rather than 'M1' and 'M12'
- */
- function biggestDigitChanged(v1, v2, pa, calendar) {
- // are we crossing zero? can't say anything.
- // in principle this doesn't apply to dates but turns out this doesn't matter.
- if(v1 * v2 <= 0) return Infinity;
-
- var dv = Math.abs(v2 - v1);
- var isDate = pa.type === 'date';
- var digit = biggestGuaranteedDigitChanged(dv, isDate);
- // see if a larger digit also changed
- for(var i = 0; i < 10; i++) {
- // numbers: next digit needs to be >10x but <100x then gets rounded down.
- // dates: next digit can be as much as 60x (then rounded down)
- var nextDigit = biggestGuaranteedDigitChanged(digit * 80, isDate);
- // if we get to years, the chain stops
- if(digit === nextDigit) break;
- if(didDigitChange(nextDigit, v1, v2, isDate, pa, calendar)) digit = nextDigit;
- else break;
- }
- return digit;
- }
-
- /*
- * Find the largest digit that *definitely* changes in a region [v, v + dv] for any v
- * for nonuniform date regions (months/years) pick the largest
- */
- function biggestGuaranteedDigitChanged(dv, isDate) {
- if(isDate && dv > oneSec) {
- // this is supposed to be the biggest *guaranteed* change
- // so compare to the longest month and year across any calendar,
- // and we'll iterate back up later
- // note: does not support rounding larger than one year. We could add
- // that if anyone wants it, but seems unusual and not strictly necessary.
- if(dv > oneDay) {
- if(dv > oneYear * 1.1) return oneYear;
- if(dv > oneMonth * 1.1) return oneMonth;
- return oneDay;
- }
-
- if(dv > oneHour) return oneHour;
- if(dv > oneMin) return oneMin;
- return oneSec;
- }
- return Math.pow(10, Math.floor(Math.log(dv) / Math.LN10));
- }
-
- function didDigitChange(digit, v1, v2, isDate, pa, calendar) {
- if(isDate && digit > oneDay) {
- var dateParts1 = dateParts(v1, pa, calendar);
- var dateParts2 = dateParts(v2, pa, calendar);
- var parti = (digit === oneYear) ? 0 : 1;
- return dateParts1[parti] !== dateParts2[parti];
-
- }
- return Math.floor(v2 / digit) - Math.floor(v1 / digit) > 0.1;
- }
-
- function dateParts(v, pa, calendar) {
- var parts = pa.c2d(v, oneYear, calendar).split('-');
- if(parts[0] === '') {
- parts.unshift();
- parts[0] = '-' + parts[0];
- }
- return parts;
- }
-
- },{"../../constants/numerical":149,"../../plots/cartesian/axes":212}],336:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
-
- var arraysToCalcdata = _dereq_('../bar/arrays_to_calcdata');
- var binFunctions = _dereq_('./bin_functions');
- var normFunctions = _dereq_('./norm_functions');
- var doAvg = _dereq_('./average');
- var getBinSpanLabelRound = _dereq_('./bin_label_vals');
-
- module.exports = function calc(gd, trace) {
- // ignore as much processing as possible (and including in autorange) if not visible
- if(trace.visible !== true) return;
-
- // depending on orientation, set position and size axes and data ranges
- // note: this logic for choosing orientation is duplicated in graph_obj->setstyles
- var pos = [];
- var size = [];
- var pa = Axes.getFromId(gd, trace.orientation === 'h' ?
- (trace.yaxis || 'y') : (trace.xaxis || 'x'));
- var mainData = trace.orientation === 'h' ? 'y' : 'x';
- var counterData = {x: 'y', y: 'x'}[mainData];
- var calendar = trace[mainData + 'calendar'];
- var cumulativeSpec = trace.cumulative;
- var i;
-
- var binsAndPos = calcAllAutoBins(gd, trace, pa, mainData);
- var binSpec = binsAndPos[0];
- var pos0 = binsAndPos[1];
-
- var nonuniformBins = typeof binSpec.size === 'string';
- var binEdges = [];
- var bins = nonuniformBins ? binEdges : binSpec;
- // make the empty bin array
- var inc = [];
- var counts = [];
- var inputPoints = [];
- var total = 0;
- var norm = trace.histnorm;
- var func = trace.histfunc;
- var densityNorm = norm.indexOf('density') !== -1;
- var i2, binEnd, n;
-
- if(cumulativeSpec.enabled && densityNorm) {
- // we treat "cumulative" like it means "integral" if you use a density norm,
- // which in the end means it's the same as without "density"
- norm = norm.replace(/ ?density$/, '');
- densityNorm = false;
- }
-
- var extremeFunc = func === 'max' || func === 'min';
- var sizeInit = extremeFunc ? null : 0;
- var binFunc = binFunctions.count;
- var normFunc = normFunctions[norm];
- var isAvg = false;
- var pr2c = function(v) { return pa.r2c(v, 0, calendar); };
- var rawCounterData;
-
- if(Lib.isArrayOrTypedArray(trace[counterData]) && func !== 'count') {
- rawCounterData = trace[counterData];
- isAvg = func === 'avg';
- binFunc = binFunctions[func];
- }
-
- // create the bins (and any extra arrays needed)
- // assume more than 1e6 bins is an error, so we don't crash the browser
- i = pr2c(binSpec.start);
-
- // decrease end a little in case of rounding errors
- binEnd = pr2c(binSpec.end) + (i - Axes.tickIncrement(i, binSpec.size, false, calendar)) / 1e6;
-
- while(i < binEnd && pos.length < 1e6) {
- i2 = Axes.tickIncrement(i, binSpec.size, false, calendar);
- pos.push((i + i2) / 2);
- size.push(sizeInit);
- inputPoints.push([]);
- // nonuniform bins (like months) we need to search,
- // rather than straight calculate the bin we're in
- binEdges.push(i);
- // nonuniform bins also need nonuniform normalization factors
- if(densityNorm) inc.push(1 / (i2 - i));
- if(isAvg) counts.push(0);
- // break to avoid infinite loops
- if(i2 <= i) break;
- i = i2;
- }
- binEdges.push(i);
-
- // for date axes we need bin bounds to be calcdata. For nonuniform bins
- // we already have this, but uniform with start/end/size they're still strings.
- if(!nonuniformBins && pa.type === 'date') {
- bins = {
- start: pr2c(bins.start),
- end: pr2c(bins.end),
- size: bins.size
- };
- }
-
- // bin the data
- // and make histogram-specific pt-number-to-cd-index map object
- var nMax = size.length;
- var uniqueValsPerBin = true;
- var leftGap = Infinity;
- var rightGap = Infinity;
- var ptNumber2cdIndex = {};
- for(i = 0; i < pos0.length; i++) {
- var posi = pos0[i];
- n = Lib.findBin(posi, bins);
- if(n >= 0 && n < nMax) {
- total += binFunc(n, i, size, rawCounterData, counts);
- if(uniqueValsPerBin && inputPoints[n].length && posi !== pos0[inputPoints[n][0]]) {
- uniqueValsPerBin = false;
- }
- inputPoints[n].push(i);
- ptNumber2cdIndex[i] = n;
-
- leftGap = Math.min(leftGap, posi - binEdges[n]);
- rightGap = Math.min(rightGap, binEdges[n + 1] - posi);
- }
- }
-
- var roundFn;
- if(!uniqueValsPerBin) {
- roundFn = getBinSpanLabelRound(leftGap, rightGap, binEdges, pa, calendar);
- }
-
- // average and/or normalize the data, if needed
- if(isAvg) total = doAvg(size, counts);
- if(normFunc) normFunc(size, total, inc);
-
- // after all normalization etc, now we can accumulate if desired
- if(cumulativeSpec.enabled) cdf(size, cumulativeSpec.direction, cumulativeSpec.currentbin);
-
-
- var seriesLen = Math.min(pos.length, size.length);
- var cd = [];
- var firstNonzero = 0;
- var lastNonzero = seriesLen - 1;
-
- // look for empty bins at the ends to remove, so autoscale omits them
- for(i = 0; i < seriesLen; i++) {
- if(size[i]) {
- firstNonzero = i;
- break;
- }
- }
- for(i = seriesLen - 1; i >= firstNonzero; i--) {
- if(size[i]) {
- lastNonzero = i;
- break;
- }
- }
-
- // create the "calculated data" to plot
- for(i = firstNonzero; i <= lastNonzero; i++) {
- if((isNumeric(pos[i]) && isNumeric(size[i]))) {
- var cdi = {
- p: pos[i],
- s: size[i],
- b: 0
- };
-
- // setup hover and event data fields,
- // N.B. pts and "hover" positions ph0/ph1 don't seem to make much sense
- // for cumulative distributions
- if(!cumulativeSpec.enabled) {
- cdi.pts = inputPoints[i];
- if(uniqueValsPerBin) {
- cdi.ph0 = cdi.ph1 = (inputPoints[i].length) ? pos0[inputPoints[i][0]] : pos[i];
- }
- else {
- cdi.ph0 = roundFn(binEdges[i]);
- cdi.ph1 = roundFn(binEdges[i + 1], true);
- }
- }
- cd.push(cdi);
- }
- }
-
- if(cd.length === 1) {
- // when we collapse to a single bin, calcdata no longer describes bin size
- // so we need to explicitly specify it
- cd[0].width1 = Axes.tickIncrement(cd[0].p, binSpec.size, false, calendar) - cd[0].p;
- }
-
- arraysToCalcdata(cd, trace);
-
- if(Lib.isArrayOrTypedArray(trace.selectedpoints)) {
- Lib.tagSelected(cd, trace, ptNumber2cdIndex);
- }
-
- return cd;
- };
-
- /*
- * calcAllAutoBins: we want all histograms on the same axes to share bin specs
- * if they're grouped or stacked. If the user has explicitly specified differing
- * bin specs, there's nothing we can do, but if possible we will try to use the
- * smallest bins of any of the auto values for all histograms grouped/stacked
- * together.
- */
- function calcAllAutoBins(gd, trace, pa, mainData, _overlayEdgeCase) {
- var binAttr = mainData + 'bins';
- var fullLayout = gd._fullLayout;
- var isOverlay = fullLayout.barmode === 'overlay';
- var i, traces, tracei, calendar, pos0, autoVals, cumulativeSpec;
-
- var cleanBound = (pa.type === 'date') ?
- function(v) { return (v || v === 0) ? Lib.cleanDate(v, null, pa.calendar) : null; } :
- function(v) { return isNumeric(v) ? Number(v) : null; };
-
- function setBound(attr, bins, newBins) {
- if(bins[attr + 'Found']) {
- bins[attr] = cleanBound(bins[attr]);
- if(bins[attr] === null) bins[attr] = newBins[attr];
- }
- else {
- autoVals[attr] = bins[attr] = newBins[attr];
- Lib.nestedProperty(traces[0], binAttr + '.' + attr).set(newBins[attr]);
- }
- }
-
- var binOpts = fullLayout._histogramBinOpts[trace._groupName];
-
- // all but the first trace in this group has already been marked finished
- // clear this flag, so next time we run calc we will run autobin again
- if(trace._autoBinFinished) {
- delete trace._autoBinFinished;
- }
- else {
- traces = binOpts.traces;
- var sizeFound = binOpts.sizeFound;
- var allPos = [];
- autoVals = traces[0]._autoBin = {};
- // Note: we're including `legendonly` traces here for autobin purposes,
- // so that showing & hiding from the legend won't affect bins.
- // But this complicates things a bit since those traces don't `calc`,
- // hence `isFirstVisible`.
- var isFirstVisible = true;
- for(i = 0; i < traces.length; i++) {
- tracei = traces[i];
- if(tracei.visible) {
- pos0 = tracei._pos0 = pa.makeCalcdata(tracei, mainData);
- allPos = Lib.concat(allPos, pos0);
- delete tracei._autoBinFinished;
- if(trace.visible === true) {
- if(isFirstVisible) {
- isFirstVisible = false;
- }
- else {
- delete tracei._autoBin;
- tracei._autoBinFinished = 1;
- }
- }
- }
- }
- calendar = traces[0][mainData + 'calendar'];
- var newBinSpec = Axes.autoBin(
- allPos, pa, binOpts.nbins, false, calendar, sizeFound && binOpts.size);
-
- // Edge case: single-valued histogram overlaying others
- // Use them all together to calculate the bin size for the single-valued one
- if(isOverlay && newBinSpec._dataSpan === 0 &&
- pa.type !== 'category' && pa.type !== 'multicategory') {
- // Several single-valued histograms! Stop infinite recursion,
- // just return an extra flag that tells handleSingleValueOverlays
- // to sort out this trace too
- if(_overlayEdgeCase) return [newBinSpec, pos0, true];
-
- newBinSpec = handleSingleValueOverlays(gd, trace, pa, mainData, binAttr);
- }
-
- // adjust for CDF edge cases
- cumulativeSpec = tracei.cumulative;
- if(cumulativeSpec.enabled && (cumulativeSpec.currentbin !== 'include')) {
- if(cumulativeSpec.direction === 'decreasing') {
- newBinSpec.start = pa.c2r(Axes.tickIncrement(
- pa.r2c(newBinSpec.start, 0, calendar),
- newBinSpec.size, true, calendar
- ));
- }
- else {
- newBinSpec.end = pa.c2r(Axes.tickIncrement(
- pa.r2c(newBinSpec.end, 0, calendar),
- newBinSpec.size, false, calendar
- ));
- }
- }
-
- binOpts.size = newBinSpec.size;
- if(!sizeFound) {
- autoVals.size = newBinSpec.size;
- Lib.nestedProperty(traces[0], binAttr + '.size').set(newBinSpec.size);
- }
-
- setBound('start', binOpts, newBinSpec);
- setBound('end', binOpts, newBinSpec);
- }
-
- pos0 = trace._pos0;
- delete trace._pos0;
-
- // Each trace can specify its own start/end, or if omitted
- // we ensure they're beyond the bounds of this trace's data,
- // and we need to make sure start is aligned with the main start
- var traceInputBins = trace._input[binAttr] || {};
- var traceBinOptsCalc = Lib.extendFlat({}, binOpts);
- var mainStart = binOpts.start;
- var startIn = pa.r2l(traceInputBins.start);
- var hasStart = startIn !== undefined;
- if((binOpts.startFound || hasStart) && startIn !== pa.r2l(mainStart)) {
- // We have an explicit start to reconcile across traces
- // if this trace has an explicit start, shift it down to a bin edge
- // if another trace had an explicit start, shift it down to a
- // bin edge past our data
- var traceStart = hasStart ?
- startIn :
- Lib.aggNums(Math.min, null, pos0);
-
- var dummyAx = {
- type: (pa.type === 'category' || pa.type === 'multicategory') ? 'linear' : pa.type,
- r2l: pa.r2l,
- dtick: binOpts.size,
- tick0: mainStart,
- calendar: calendar,
- range: ([traceStart, Axes.tickIncrement(traceStart, binOpts.size, false, calendar)]).map(pa.l2r)
- };
- var newStart = Axes.tickFirst(dummyAx);
- if(newStart > pa.r2l(traceStart)) {
- newStart = Axes.tickIncrement(newStart, binOpts.size, true, calendar);
- }
- traceBinOptsCalc.start = pa.l2r(newStart);
- if(!hasStart) Lib.nestedProperty(trace, binAttr + '.start').set(traceBinOptsCalc.start);
- }
-
- var mainEnd = binOpts.end;
- var endIn = pa.r2l(traceInputBins.end);
- var hasEnd = endIn !== undefined;
- if((binOpts.endFound || hasEnd) && endIn !== pa.r2l(mainEnd)) {
- // Reconciling an explicit end is easier, as it doesn't need to
- // match bin edges
- var traceEnd = hasEnd ?
- endIn :
- Lib.aggNums(Math.max, null, pos0);
-
- traceBinOptsCalc.end = pa.l2r(traceEnd);
- if(!hasEnd) Lib.nestedProperty(trace, binAttr + '.start').set(traceBinOptsCalc.end);
- }
-
- // Backward compatibility for one-time autobinning.
- // autobin: true is handled in cleanData, but autobin: false
- // needs to be here where we have determined the values.
- var autoBinAttr = 'autobin' + mainData;
- if(trace._input[autoBinAttr] === false) {
- trace._input[binAttr] = Lib.extendFlat({}, trace[binAttr] || {});
- delete trace._input[autoBinAttr];
- delete trace[autoBinAttr];
- }
-
- return [traceBinOptsCalc, pos0];
- }
-
- /*
- * Adjust single-value histograms in overlay mode to make as good a
- * guess as we can at autobin values the user would like.
- *
- * Returns the binSpec for the trace that sparked all this
- */
- function handleSingleValueOverlays(gd, trace, pa, mainData, binAttr) {
- var overlaidTraceGroup = getConnectedHistograms(gd, trace);
- var pastThisTrace = false;
- var minSize = Infinity;
- var singleValuedTraces = [trace];
- var i, tracei;
-
- // first collect all the:
- // - min bin size from all multi-valued traces
- // - single-valued traces
- for(i = 0; i < overlaidTraceGroup.length; i++) {
- tracei = overlaidTraceGroup[i];
- if(tracei === trace) pastThisTrace = true;
- else if(!pastThisTrace) {
- // This trace has already had its autobins calculated
- // (so must not have been single-valued).
- minSize = Math.min(minSize, tracei[binAttr].size);
- }
- else {
- var resulti = calcAllAutoBins(gd, tracei, pa, mainData, true);
- var binSpeci = resulti[0];
- var isSingleValued = resulti[2];
-
- // so we can use this result when we get to tracei in the normal
- // course of events, mark it as done and put _pos0 back
- tracei._autoBinFinished = 1;
- tracei._pos0 = resulti[1];
-
- if(isSingleValued) {
- singleValuedTraces.push(tracei);
- }
- else {
- minSize = Math.min(minSize, binSpeci.size);
- }
- }
- }
-
- // find the real data values for each single-valued trace
- // hunt through pos0 for the first valid value
- var dataVals = new Array(singleValuedTraces.length);
- for(i = 0; i < singleValuedTraces.length; i++) {
- var pos0 = singleValuedTraces[i]._pos0;
- for(var j = 0; j < pos0.length; j++) {
- if(pos0[j] !== undefined) {
- dataVals[i] = pos0[j];
- break;
- }
- }
- }
-
- // are ALL traces are single-valued? use the min difference between
- // all of their values (which defaults to 1 if there's still only one)
- if(!isFinite(minSize)) {
- minSize = Lib.distinctVals(dataVals).minDiff;
- }
-
- // now apply the min size we found to all single-valued traces
- for(i = 0; i < singleValuedTraces.length; i++) {
- tracei = singleValuedTraces[i];
- var calendar = tracei[mainData + 'calendar'];
-
- tracei._input[binAttr] = tracei[binAttr] = {
- start: pa.c2r(dataVals[i] - minSize / 2, 0, calendar),
- end: pa.c2r(dataVals[i] + minSize / 2, 0, calendar),
- size: minSize
- };
- }
-
- return trace[binAttr];
- }
-
- /*
- * Return an array of histograms that share axes and orientation.
- *
- * Only considers histograms. In principle we could include bars in a
- * similar way to how we do manually binned histograms, though this
- * would have tons of edge cases and value judgments to make.
- */
- function getConnectedHistograms(gd, trace) {
- var xid = trace.xaxis;
- var yid = trace.yaxis;
- var orientation = trace.orientation;
-
- var out = [];
- var fullData = gd._fullData;
- for(var i = 0; i < fullData.length; i++) {
- var tracei = fullData[i];
- if(tracei.type === 'histogram' &&
- tracei.visible === true &&
- tracei.orientation === orientation &&
- tracei.xaxis === xid && tracei.yaxis === yid
- ) {
- out.push(tracei);
- }
- }
-
- return out;
- }
-
-
- function cdf(size, direction, currentBin) {
- var i, vi, prevSum;
-
- function firstHalfPoint(i) {
- prevSum = size[i];
- size[i] /= 2;
- }
-
- function nextHalfPoint(i) {
- vi = size[i];
- size[i] = prevSum + vi / 2;
- prevSum += vi;
- }
-
- if(currentBin === 'half') {
-
- if(direction === 'increasing') {
- firstHalfPoint(0);
- for(i = 1; i < size.length; i++) {
- nextHalfPoint(i);
- }
- }
- else {
- firstHalfPoint(size.length - 1);
- for(i = size.length - 2; i >= 0; i--) {
- nextHalfPoint(i);
- }
- }
- }
- else if(direction === 'increasing') {
- for(i = 1; i < size.length; i++) {
- size[i] += size[i - 1];
- }
-
- // 'exclude' is identical to 'include' just shifted one bin over
- if(currentBin === 'exclude') {
- size.unshift(0);
- size.pop();
- }
- }
- else {
- for(i = size.length - 2; i >= 0; i--) {
- size[i] += size[i + 1];
- }
-
- if(currentBin === 'exclude') {
- size.push(0);
- size.shift();
- }
- }
- }
-
- },{"../../lib":168,"../../plots/cartesian/axes":212,"../bar/arrays_to_calcdata":266,"./average":332,"./bin_functions":334,"./bin_label_vals":335,"./norm_functions":343,"fast-isnumeric":18}],337:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- module.exports = {
- eventDataKeys: ['binNumber']
- };
-
- },{}],338:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var nestedProperty = Lib.nestedProperty;
-
- var attributes = _dereq_('./attributes');
-
- var BINATTRS = {
- x: [
- {aStr: 'xbins.start', name: 'start'},
- {aStr: 'xbins.end', name: 'end'},
- {aStr: 'xbins.size', name: 'size'},
- {aStr: 'nbinsx', name: 'nbins'}
- ],
- y: [
- {aStr: 'ybins.start', name: 'start'},
- {aStr: 'ybins.end', name: 'end'},
- {aStr: 'ybins.size', name: 'size'},
- {aStr: 'nbinsy', name: 'nbins'}
- ]
- };
-
- // handle bin attrs and relink auto-determined values so fullData is complete
- module.exports = function crossTraceDefaults(fullData, fullLayout) {
- var allBinOpts = fullLayout._histogramBinOpts = {};
- var isOverlay = fullLayout.barmode === 'overlay';
- var i, j, traceOut, traceIn, binDirection, group, binOpts;
-
- function coerce(attr) {
- return Lib.coerce(traceOut._input, traceOut, attributes, attr);
- }
-
- for(i = 0; i < fullData.length; i++) {
- traceOut = fullData[i];
- if(traceOut.type !== 'histogram') continue;
-
- // TODO: this shouldn't be relinked as it's only used within calc
- // https://github.com/plotly/plotly.js/issues/749
- delete traceOut._autoBinFinished;
-
- binDirection = traceOut.orientation === 'v' ? 'x' : 'y';
- // in overlay mode make a separate group for each trace
- // otherwise collect all traces of the same subplot & orientation
- group = isOverlay ? traceOut.uid : (traceOut.xaxis + traceOut.yaxis + binDirection);
- traceOut._groupName = group;
-
- binOpts = allBinOpts[group];
-
- if(binOpts) {
- binOpts.traces.push(traceOut);
- }
- else {
- binOpts = allBinOpts[group] = {
- traces: [traceOut],
- direction: binDirection
- };
- }
- }
-
- for(group in allBinOpts) {
- binOpts = allBinOpts[group];
- binDirection = binOpts.direction;
- var attrs = BINATTRS[binDirection];
- for(j = 0; j < attrs.length; j++) {
- var attrSpec = attrs[j];
- var attr = attrSpec.name;
-
- // nbins(x|y) is moot if we have a size. This depends on
- // nbins coming after size in binAttrs.
- if(attr === 'nbins' && binOpts.sizeFound) continue;
-
- var aStr = attrSpec.aStr;
- for(i = 0; i < binOpts.traces.length; i++) {
- traceOut = binOpts.traces[i];
- traceIn = traceOut._input;
- if(nestedProperty(traceIn, aStr).get() !== undefined) {
- binOpts[attr] = coerce(aStr);
- binOpts[attr + 'Found'] = true;
- break;
- }
- var autoVals = traceOut._autoBin;
- if(autoVals && autoVals[attr]) {
- // if this is the *first* autoval
- nestedProperty(traceOut, aStr).set(autoVals[attr]);
- }
- }
- // start and end we need to coerce anyway, after having collected the
- // first of each into binOpts, in case a trace wants to restrict its
- // data to a certain range
- if(attr === 'start' || attr === 'end') {
- for(; i < binOpts.traces.length; i++) {
- traceOut = binOpts.traces[i];
- coerce(aStr, (traceOut._autoBin || {})[attr]);
- }
- }
-
- if(attr === 'nbins' && !binOpts.sizeFound && !binOpts.nbinsFound) {
- traceOut = binOpts.traces[0];
- binOpts[attr] = coerce(aStr);
- }
- }
- }
- };
-
- },{"../../lib":168,"./attributes":331}],339:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var Color = _dereq_('../../components/color');
-
- var handleStyleDefaults = _dereq_('../bar/style_defaults');
- var attributes = _dereq_('./attributes');
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- var x = coerce('x');
- var y = coerce('y');
-
- var cumulative = coerce('cumulative.enabled');
- if(cumulative) {
- coerce('cumulative.direction');
- coerce('cumulative.currentbin');
- }
-
- coerce('text');
-
- var orientation = coerce('orientation', (y && !x) ? 'h' : 'v');
- var sampleLetter = orientation === 'v' ? 'x' : 'y';
- var aggLetter = orientation === 'v' ? 'y' : 'x';
-
- var len = (x && y) ?
- Math.min(Lib.minRowLength(x) && Lib.minRowLength(y)) :
- Lib.minRowLength(traceOut[sampleLetter] || []);
-
- if(!len) {
- traceOut.visible = false;
- return;
- }
-
- traceOut._length = len;
-
- var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
- handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
-
- var hasAggregationData = traceOut[aggLetter];
- if(hasAggregationData) coerce('histfunc');
- coerce('histnorm');
-
- // Note: bin defaults are now handled in Histogram.crossTraceDefaults
- // autobin(x|y) are only included here to appease Plotly.validate
- coerce('autobin' + sampleLetter);
-
- coerce('hovertemplate');
-
- handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout);
-
- Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
-
- var lineColor = (traceOut.marker.line || {}).color;
-
- // override defaultColor for error bars with defaultLine
- var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
- errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'y'});
- errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'});
- };
-
- },{"../../components/color":51,"../../lib":168,"../../registry":257,"../bar/style_defaults":281,"./attributes":331}],340:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = function eventData(out, pt, trace, cd, pointNumber) {
- // standard cartesian event data
- out.x = 'xVal' in pt ? pt.xVal : pt.x;
- out.y = 'yVal' in pt ? pt.yVal : pt.y;
-
- if(pt.xa) out.xaxis = pt.xa;
- if(pt.ya) out.yaxis = pt.ya;
-
- // specific to histogram - CDFs do not have pts (yet?)
- if(!(trace.cumulative || {}).enabled) {
- var pts = Array.isArray(pointNumber) ?
- cd[0].pts[pointNumber[0]][pointNumber[1]] :
- cd[pointNumber].pts;
-
- out.pointNumbers = pts;
- out.binNumber = out.pointNumber;
- delete out.pointNumber;
- delete out.pointIndex;
-
- var pointIndices;
- if(trace._indexToPoints) {
- pointIndices = [];
- for(var i = 0; i < pts.length; i++) {
- pointIndices = pointIndices.concat(trace._indexToPoints[pts[i]]);
- }
- } else {
- pointIndices = pts;
- }
-
- out.pointIndices = pointIndices;
- }
-
- return out;
- };
-
- },{}],341:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var barHover = _dereq_('../bar/hover').hoverPoints;
- var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText;
-
- module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
- var pts = barHover(pointData, xval, yval, hovermode);
-
- if(!pts) return;
-
- pointData = pts[0];
- var di = pointData.cd[pointData.index];
- var trace = pointData.cd[0].trace;
-
- if(!trace.cumulative.enabled) {
- var posLetter = trace.orientation === 'h' ? 'y' : 'x';
-
- pointData[posLetter + 'Label'] = hoverLabelText(pointData[posLetter + 'a'], di.ph0, di.ph1);
- }
-
- if(trace.hovermplate) pointData.hovertemplate = trace.hovertemplate;
-
- return pts;
- };
-
- },{"../../plots/cartesian/axes":212,"../bar/hover":273}],342:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- /**
- * Histogram has its own attribute, defaults and calc steps,
- * but uses bar's plot to display
- * and bar's crossTraceCalc (formerly known as setPositions) for stacking and grouping
- */
-
- /**
- * histogram errorBarsOK is debatable, but it's put in for backward compat.
- * there are use cases for it - sqrt for a simple histogram works right now,
- * constant and % work but they're not so meaningful. I guess it could be cool
- * to allow quadrature combination of errors in summed histograms...
- */
-
-
- var Histogram = {};
-
- Histogram.attributes = _dereq_('./attributes');
- Histogram.layoutAttributes = _dereq_('../bar/layout_attributes');
- Histogram.supplyDefaults = _dereq_('./defaults');
- Histogram.crossTraceDefaults = _dereq_('./cross_trace_defaults');
- Histogram.supplyLayoutDefaults = _dereq_('../bar/layout_defaults');
- Histogram.calc = _dereq_('./calc');
- Histogram.crossTraceCalc = _dereq_('../bar/cross_trace_calc').crossTraceCalc;
- Histogram.plot = _dereq_('../bar/plot');
- Histogram.layerName = 'barlayer';
- Histogram.style = _dereq_('../bar/style').style;
- Histogram.styleOnSelect = _dereq_('../bar/style').styleOnSelect;
- Histogram.colorbar = _dereq_('../scatter/marker_colorbar');
- Histogram.hoverPoints = _dereq_('./hover');
- Histogram.selectPoints = _dereq_('../bar/select');
- Histogram.eventData = _dereq_('./event_data');
-
- Histogram.moduleType = 'trace';
- Histogram.name = 'histogram';
- Histogram.basePlotModule = _dereq_('../../plots/cartesian');
- Histogram.categories = ['cartesian', 'svg', 'bar', 'histogram', 'oriented', 'errorBarsOK', 'showLegend'];
- Histogram.meta = {
-
- };
-
- module.exports = Histogram;
-
- },{"../../plots/cartesian":224,"../bar/cross_trace_calc":270,"../bar/layout_attributes":275,"../bar/layout_defaults":276,"../bar/plot":277,"../bar/select":278,"../bar/style":280,"../scatter/marker_colorbar":385,"./attributes":331,"./calc":336,"./cross_trace_defaults":338,"./defaults":339,"./event_data":340,"./hover":341}],343:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- module.exports = {
- percent: function(size, total) {
- var nMax = size.length;
- var norm = 100 / total;
- for(var n = 0; n < nMax; n++) size[n] *= norm;
- },
- probability: function(size, total) {
- var nMax = size.length;
- for(var n = 0; n < nMax; n++) size[n] /= total;
- },
- density: function(size, total, inc, yinc) {
- var nMax = size.length;
- yinc = yinc || 1;
- for(var n = 0; n < nMax; n++) size[n] *= inc[n] * yinc;
- },
- 'probability density': function(size, total, inc, yinc) {
- var nMax = size.length;
- if(yinc) total /= yinc;
- for(var n = 0; n < nMax; n++) size[n] *= inc[n] / total;
- }
- };
-
- },{}],344:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var histogramAttrs = _dereq_('../histogram/attributes');
- var makeBinAttrs = _dereq_('../histogram/bin_attributes');
- var heatmapAttrs = _dereq_('../heatmap/attributes');
- var colorscaleAttrs = _dereq_('../../components/colorscale/attributes');
- var colorbarAttrs = _dereq_('../../components/colorbar/attributes');
-
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- module.exports = extendFlat(
- {
- x: histogramAttrs.x,
- y: histogramAttrs.y,
-
- z: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- marker: {
- color: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- editType: 'calc'
- },
-
- histnorm: histogramAttrs.histnorm,
- histfunc: histogramAttrs.histfunc,
- nbinsx: histogramAttrs.nbinsx,
- xbins: makeBinAttrs('x'),
- nbinsy: histogramAttrs.nbinsy,
- ybins: makeBinAttrs('y'),
- autobinx: histogramAttrs.autobinx,
- autobiny: histogramAttrs.autobiny,
-
- xgap: heatmapAttrs.xgap,
- ygap: heatmapAttrs.ygap,
- zsmooth: heatmapAttrs.zsmooth,
- zhoverformat: heatmapAttrs.zhoverformat
- },
- colorscaleAttrs('', {
- cLetter: 'z',
- autoColorDflt: false
- }),
- { colorbar: colorbarAttrs }
- );
-
- },{"../../components/colorbar/attributes":52,"../../components/colorscale/attributes":58,"../../lib/extend":162,"../heatmap/attributes":316,"../histogram/attributes":331,"../histogram/bin_attributes":333}],345:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
-
- var binFunctions = _dereq_('../histogram/bin_functions');
- var normFunctions = _dereq_('../histogram/norm_functions');
- var doAvg = _dereq_('../histogram/average');
- var getBinSpanLabelRound = _dereq_('../histogram/bin_label_vals');
-
-
- module.exports = function calc(gd, trace) {
- var xa = Axes.getFromId(gd, trace.xaxis || 'x');
- var x = trace.x ? xa.makeCalcdata(trace, 'x') : [];
- var ya = Axes.getFromId(gd, trace.yaxis || 'y');
- var y = trace.y ? ya.makeCalcdata(trace, 'y') : [];
- var xcalendar = trace.xcalendar;
- var ycalendar = trace.ycalendar;
- var xr2c = function(v) { return xa.r2c(v, 0, xcalendar); };
- var yr2c = function(v) { return ya.r2c(v, 0, ycalendar); };
- var xc2r = function(v) { return xa.c2r(v, 0, xcalendar); };
- var yc2r = function(v) { return ya.c2r(v, 0, ycalendar); };
-
- var i, j, n, m;
-
- var serieslen = trace._length;
- if(x.length > serieslen) x.splice(serieslen, x.length - serieslen);
- if(y.length > serieslen) y.splice(serieslen, y.length - serieslen);
-
- // calculate the bins
- doAutoBin(trace, 'x', x, xa, xr2c, xc2r, xcalendar);
- doAutoBin(trace, 'y', y, ya, yr2c, yc2r, ycalendar);
-
- // make the empty bin array & scale the map
- var z = [];
- var onecol = [];
- var zerocol = [];
- var nonuniformBinsX = (typeof(trace.xbins.size) === 'string');
- var nonuniformBinsY = (typeof(trace.ybins.size) === 'string');
- var xEdges = [];
- var yEdges = [];
- var xbins = nonuniformBinsX ? xEdges : trace.xbins;
- var ybins = nonuniformBinsY ? yEdges : trace.ybins;
- var total = 0;
- var counts = [];
- var inputPoints = [];
- var norm = trace.histnorm;
- var func = trace.histfunc;
- var densitynorm = (norm.indexOf('density') !== -1);
- var extremefunc = (func === 'max' || func === 'min');
- var sizeinit = (extremefunc ? null : 0);
- var binfunc = binFunctions.count;
- var normfunc = normFunctions[norm];
- var doavg = false;
- var xinc = [];
- var yinc = [];
-
- // set a binning function other than count?
- // for binning functions: check first for 'z',
- // then 'mc' in case we had a colored scatter plot
- // and want to transfer these colors to the 2D histo
- // TODO: axe this, make it the responsibility of the app changing type? or an impliedEdit?
- var rawCounterData = ('z' in trace) ?
- trace.z :
- (('marker' in trace && Array.isArray(trace.marker.color)) ?
- trace.marker.color : '');
- if(rawCounterData && func !== 'count') {
- doavg = func === 'avg';
- binfunc = binFunctions[func];
- }
-
- // decrease end a little in case of rounding errors
- var binSpec = trace.xbins;
- var binStart = xr2c(binSpec.start);
- var binEnd = xr2c(binSpec.end) +
- (binStart - Axes.tickIncrement(binStart, binSpec.size, false, xcalendar)) / 1e6;
-
- for(i = binStart; i < binEnd; i = Axes.tickIncrement(i, binSpec.size, false, xcalendar)) {
- onecol.push(sizeinit);
- xEdges.push(i);
- if(doavg) zerocol.push(0);
- }
- xEdges.push(i);
-
- var nx = onecol.length;
- var x0c = xr2c(trace.xbins.start);
- var dx = (i - x0c) / nx;
- var x0 = xc2r(x0c + dx / 2);
-
- binSpec = trace.ybins;
- binStart = yr2c(binSpec.start);
- binEnd = yr2c(binSpec.end) +
- (binStart - Axes.tickIncrement(binStart, binSpec.size, false, ycalendar)) / 1e6;
-
- for(i = binStart; i < binEnd; i = Axes.tickIncrement(i, binSpec.size, false, ycalendar)) {
- z.push(onecol.slice());
- yEdges.push(i);
- var ipCol = new Array(nx);
- for(j = 0; j < nx; j++) ipCol[j] = [];
- inputPoints.push(ipCol);
- if(doavg) counts.push(zerocol.slice());
- }
- yEdges.push(i);
-
- var ny = z.length;
- var y0c = yr2c(trace.ybins.start);
- var dy = (i - y0c) / ny;
- var y0 = yc2r(y0c + dy / 2);
-
- if(densitynorm) {
- xinc = makeIncrements(onecol.length, xbins, dx, nonuniformBinsX);
- yinc = makeIncrements(z.length, ybins, dy, nonuniformBinsY);
- }
-
- // for date axes we need bin bounds to be calcdata. For nonuniform bins
- // we already have this, but uniform with start/end/size they're still strings.
- if(!nonuniformBinsX && xa.type === 'date') xbins = binsToCalc(xr2c, xbins);
- if(!nonuniformBinsY && ya.type === 'date') ybins = binsToCalc(yr2c, ybins);
-
- // put data into bins
- var uniqueValsPerX = true;
- var uniqueValsPerY = true;
- var xVals = new Array(nx);
- var yVals = new Array(ny);
- var xGapLow = Infinity;
- var xGapHigh = Infinity;
- var yGapLow = Infinity;
- var yGapHigh = Infinity;
- for(i = 0; i < serieslen; i++) {
- var xi = x[i];
- var yi = y[i];
- n = Lib.findBin(xi, xbins);
- m = Lib.findBin(yi, ybins);
- if(n >= 0 && n < nx && m >= 0 && m < ny) {
- total += binfunc(n, i, z[m], rawCounterData, counts[m]);
- inputPoints[m][n].push(i);
-
- if(uniqueValsPerX) {
- if(xVals[n] === undefined) xVals[n] = xi;
- else if(xVals[n] !== xi) uniqueValsPerX = false;
- }
- if(uniqueValsPerY) {
- if(yVals[n] === undefined) yVals[n] = yi;
- else if(yVals[n] !== yi) uniqueValsPerY = false;
- }
-
- xGapLow = Math.min(xGapLow, xi - xEdges[n]);
- xGapHigh = Math.min(xGapHigh, xEdges[n + 1] - xi);
- yGapLow = Math.min(yGapLow, yi - yEdges[m]);
- yGapHigh = Math.min(yGapHigh, yEdges[m + 1] - yi);
- }
- }
- // normalize, if needed
- if(doavg) {
- for(m = 0; m < ny; m++) total += doAvg(z[m], counts[m]);
- }
- if(normfunc) {
- for(m = 0; m < ny; m++) normfunc(z[m], total, xinc, yinc[m]);
- }
-
- return {
- x: x,
- xRanges: getRanges(xEdges, uniqueValsPerX && xVals, xGapLow, xGapHigh, xa, xcalendar),
- x0: x0,
- dx: dx,
- y: y,
- yRanges: getRanges(yEdges, uniqueValsPerY && yVals, yGapLow, yGapHigh, ya, ycalendar),
- y0: y0,
- dy: dy,
- z: z,
- pts: inputPoints
- };
- };
-
- function doAutoBin(trace, axLetter, data, ax, r2c, c2r, calendar) {
- var binAttr = axLetter + 'bins';
- var binSpec = trace[binAttr];
- if(!binSpec) binSpec = trace[binAttr] = {};
- var inputBinSpec = trace._input[binAttr] || {};
- var autoBin = trace._autoBin = {};
-
- // clear out any previously added autobin info
- if(!inputBinSpec.size) delete binSpec.size;
- if(inputBinSpec.start === undefined) delete binSpec.start;
- if(inputBinSpec.end === undefined) delete binSpec.end;
-
- var autoSize = !binSpec.size;
- var autoStart = binSpec.start === undefined;
- var autoEnd = binSpec.end === undefined;
-
- if(autoSize || autoStart || autoEnd) {
- var newBinSpec = Axes.autoBin(data, ax, trace['nbins' + axLetter], '2d', calendar, binSpec.size);
- if(trace.type === 'histogram2dcontour') {
- // the "true" 2nd argument reverses the tick direction (which we can't
- // just do with a minus sign because of month bins)
- if(autoStart) {
- newBinSpec.start = c2r(Axes.tickIncrement(
- r2c(newBinSpec.start), newBinSpec.size, true, calendar));
- }
- if(autoEnd) {
- newBinSpec.end = c2r(Axes.tickIncrement(
- r2c(newBinSpec.end), newBinSpec.size, false, calendar));
- }
- }
- if(autoSize) binSpec.size = autoBin.size = newBinSpec.size;
- if(autoStart) binSpec.start = autoBin.start = newBinSpec.start;
- if(autoEnd) binSpec.end = autoBin.end = newBinSpec.end;
- }
-
- // Backward compatibility for one-time autobinning.
- // autobin: true is handled in cleanData, but autobin: false
- // needs to be here where we have determined the values.
- var autoBinAttr = 'autobin' + axLetter;
- if(trace._input[autoBinAttr] === false) {
- trace._input[binAttr] = Lib.extendFlat({}, binSpec);
- delete trace._input[autoBinAttr];
- delete trace[autoBinAttr];
- }
- }
-
- function makeIncrements(len, bins, dv, nonuniform) {
- var out = new Array(len);
- var i;
- if(nonuniform) {
- for(i = 0; i < len; i++) out[i] = 1 / (bins[i + 1] - bins[i]);
- }
- else {
- var inc = 1 / dv;
- for(i = 0; i < len; i++) out[i] = inc;
- }
- return out;
- }
-
- function binsToCalc(r2c, bins) {
- return {
- start: r2c(bins.start),
- end: r2c(bins.end),
- size: bins.size
- };
- }
-
- function getRanges(edges, uniqueVals, gapLow, gapHigh, ax, calendar) {
- var i;
- var len = edges.length - 1;
- var out = new Array(len);
- if(uniqueVals) {
- for(i = 0; i < len; i++) out[i] = [uniqueVals[i], uniqueVals[i]];
- }
- else {
- var roundFn = getBinSpanLabelRound(gapLow, gapHigh, edges, ax, calendar);
- for(i = 0; i < len; i++) out[i] = [roundFn(edges[i]), roundFn(edges[i + 1], true)];
- }
- return out;
- }
-
- },{"../../lib":168,"../../plots/cartesian/axes":212,"../histogram/average":332,"../histogram/bin_functions":334,"../histogram/bin_label_vals":335,"../histogram/norm_functions":343}],346:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var BADNUM = _dereq_('../../constants/numerical').BADNUM;
- var axisIds = _dereq_('../../plots/cartesian/axis_ids');
- var Lib = _dereq_('../../lib');
-
- var attributes = _dereq_('./attributes');
-
- var BINDIRECTIONS = ['x', 'y'];
-
- // Handle bin attrs and relink auto-determined values so fullData is complete
- // does not have cross-trace coupling, but moved out here so we have axis types
- // and relinked trace._autoBin
- module.exports = function crossTraceDefaults(fullData, fullLayout) {
- var i, j, traceOut, binDirection;
-
- function coerce(attr) {
- return Lib.coerce(traceOut._input, traceOut, attributes, attr);
- }
-
- for(i = 0; i < fullData.length; i++) {
- traceOut = fullData[i];
- var type = traceOut.type;
- if(type !== 'histogram2d' && type !== 'histogram2dcontour') continue;
-
- for(j = 0; j < BINDIRECTIONS.length; j++) {
- binDirection = BINDIRECTIONS[j];
- var binAttr = binDirection + 'bins';
- var autoBins = (traceOut._autoBin || {})[binDirection] || {};
- coerce(binAttr + '.start', autoBins.start);
- coerce(binAttr + '.end', autoBins.end);
- coerce(binAttr + '.size', autoBins.size);
-
- cleanBins(traceOut, binDirection, fullLayout, autoBins);
-
- if(!(traceOut[binAttr] || {}).size) coerce('nbins' + binDirection);
- }
- }
- };
-
- function cleanBins(trace, binDirection, fullLayout, autoBins) {
- var ax = fullLayout[axisIds.id2name(trace[binDirection + 'axis'])];
- var axType = ax.type;
- var binAttr = binDirection + 'bins';
- var bins = trace[binAttr];
- var calendar = trace[binDirection + 'calendar'];
-
- if(!bins) bins = trace[binAttr] = {};
-
- var cleanBound = (axType === 'date') ?
- function(v, dflt) { return (v || v === 0) ? Lib.cleanDate(v, BADNUM, calendar) : dflt; } :
- function(v, dflt) { return isNumeric(v) ? Number(v) : dflt; };
-
- bins.start = cleanBound(bins.start, autoBins.start);
- bins.end = cleanBound(bins.end, autoBins.end);
-
- // logic for bin size is very similar to dtick (cartesian/tick_value_defaults)
- // but without the extra string options for log axes
- // ie the only strings we accept are M<n> for months
- var sizeDflt = autoBins.size;
- var binSize = bins.size;
-
- if(isNumeric(binSize)) {
- bins.size = (binSize > 0) ? Number(binSize) : sizeDflt;
- }
- else if(typeof binSize !== 'string') {
- bins.size = sizeDflt;
- }
- else {
- // date special case: "M<n>" gives bins every (integer) n months
- var prefix = binSize.charAt(0);
- var sizeNum = binSize.substr(1);
-
- sizeNum = isNumeric(sizeNum) ? Number(sizeNum) : 0;
- if((sizeNum <= 0) || !(
- axType === 'date' && prefix === 'M' && sizeNum === Math.round(sizeNum)
- )) {
- bins.size = sizeDflt;
- }
- }
- }
-
- },{"../../constants/numerical":149,"../../lib":168,"../../plots/cartesian/axis_ids":215,"./attributes":344,"fast-isnumeric":18}],347:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- var handleSampleDefaults = _dereq_('./sample_defaults');
- var handleStyleDefaults = _dereq_('../heatmap/style_defaults');
- var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
- var attributes = _dereq_('./attributes');
-
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- handleSampleDefaults(traceIn, traceOut, coerce, layout);
- if(traceOut.visible === false) return;
-
- handleStyleDefaults(traceIn, traceOut, coerce, layout);
- colorscaleDefaults(
- traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}
- );
- };
-
- },{"../../components/colorscale/defaults":61,"../../lib":168,"../heatmap/style_defaults":329,"./attributes":344,"./sample_defaults":350}],348:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var heatmapHover = _dereq_('../heatmap/hover');
- var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText;
-
- module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer, contour) {
- var pts = heatmapHover(pointData, xval, yval, hovermode, hoverLayer, contour);
-
- if(!pts) return;
-
- pointData = pts[0];
- var indices = pointData.index;
- var ny = indices[0];
- var nx = indices[1];
- var cd0 = pointData.cd[0];
- var xRange = cd0.xRanges[nx];
- var yRange = cd0.yRanges[ny];
-
- pointData.xLabel = hoverLabelText(pointData.xa, xRange[0], xRange[1]);
- pointData.yLabel = hoverLabelText(pointData.ya, yRange[0], yRange[1]);
-
- return pts;
- };
-
- },{"../../plots/cartesian/axes":212,"../heatmap/hover":323}],349:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Histogram2D = {};
-
- Histogram2D.attributes = _dereq_('./attributes');
- Histogram2D.supplyDefaults = _dereq_('./defaults');
- Histogram2D.crossTraceDefaults = _dereq_('./cross_trace_defaults');
- Histogram2D.calc = _dereq_('../heatmap/calc');
- Histogram2D.plot = _dereq_('../heatmap/plot');
- Histogram2D.layerName = 'heatmaplayer';
- Histogram2D.colorbar = _dereq_('../heatmap/colorbar');
- Histogram2D.style = _dereq_('../heatmap/style');
- Histogram2D.hoverPoints = _dereq_('./hover');
- Histogram2D.eventData = _dereq_('../histogram/event_data');
-
- Histogram2D.moduleType = 'trace';
- Histogram2D.name = 'histogram2d';
- Histogram2D.basePlotModule = _dereq_('../../plots/cartesian');
- Histogram2D.categories = ['cartesian', 'svg', '2dMap', 'histogram'];
- Histogram2D.meta = {
-
-
- };
-
- module.exports = Histogram2D;
-
- },{"../../plots/cartesian":224,"../heatmap/calc":317,"../heatmap/colorbar":319,"../heatmap/plot":327,"../heatmap/style":328,"../histogram/event_data":340,"./attributes":344,"./cross_trace_defaults":346,"./defaults":347,"./hover":348}],350:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
-
- module.exports = function handleSampleDefaults(traceIn, traceOut, coerce, layout) {
- var x = coerce('x');
- var y = coerce('y');
- var xlen = Lib.minRowLength(x);
- var ylen = Lib.minRowLength(y);
-
- // we could try to accept x0 and dx, etc...
- // but that's a pretty weird use case.
- // for now require both x and y explicitly specified.
- if(!xlen || !ylen) {
- traceOut.visible = false;
- return;
- }
-
- traceOut._length = Math.min(xlen, ylen);
-
- var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
- handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
-
- // if marker.color is an array, we can use it in aggregation instead of z
- var hasAggregationData = coerce('z') || coerce('marker.color');
-
- if(hasAggregationData) coerce('histfunc');
- coerce('histnorm');
-
- // Note: bin defaults are now handled in Histogram2D.crossTraceDefaults
- // autobin(x|y) are only included here to appease Plotly.validate
- coerce('autobinx');
- coerce('autobiny');
- };
-
- },{"../../lib":168,"../../registry":257}],351:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var histogram2dAttrs = _dereq_('../histogram2d/attributes');
- var contourAttrs = _dereq_('../contour/attributes');
- var colorscaleAttrs = _dereq_('../../components/colorscale/attributes');
- var colorbarAttrs = _dereq_('../../components/colorbar/attributes');
-
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- module.exports = extendFlat({
- x: histogram2dAttrs.x,
- y: histogram2dAttrs.y,
- z: histogram2dAttrs.z,
- marker: histogram2dAttrs.marker,
-
- histnorm: histogram2dAttrs.histnorm,
- histfunc: histogram2dAttrs.histfunc,
- nbinsx: histogram2dAttrs.nbinsx,
- xbins: histogram2dAttrs.xbins,
- nbinsy: histogram2dAttrs.nbinsy,
- ybins: histogram2dAttrs.ybins,
- autobinx: histogram2dAttrs.autobinx,
- autobiny: histogram2dAttrs.autobiny,
-
- autocontour: contourAttrs.autocontour,
- ncontours: contourAttrs.ncontours,
- contours: contourAttrs.contours,
- line: contourAttrs.line,
- zhoverformat: histogram2dAttrs.zhoverformat
- },
- colorscaleAttrs('', {
- cLetter: 'z',
- editTypeOverride: 'calc'
- }),
- { colorbar: colorbarAttrs }
- );
-
- },{"../../components/colorbar/attributes":52,"../../components/colorscale/attributes":58,"../../lib/extend":162,"../contour/attributes":294,"../histogram2d/attributes":344}],352:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- var handleSampleDefaults = _dereq_('../histogram2d/sample_defaults');
- var handleContoursDefaults = _dereq_('../contour/contours_defaults');
- var handleStyleDefaults = _dereq_('../contour/style_defaults');
- var attributes = _dereq_('./attributes');
-
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- function coerce2(attr) {
- return Lib.coerce2(traceIn, traceOut, attributes, attr);
- }
-
- handleSampleDefaults(traceIn, traceOut, coerce, layout);
- if(traceOut.visible === false) return;
-
- handleContoursDefaults(traceIn, traceOut, coerce, coerce2);
- handleStyleDefaults(traceIn, traceOut, coerce, layout);
- };
-
- },{"../../lib":168,"../contour/contours_defaults":301,"../contour/style_defaults":315,"../histogram2d/sample_defaults":350,"./attributes":351}],353:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Histogram2dContour = {};
-
- Histogram2dContour.attributes = _dereq_('./attributes');
- Histogram2dContour.supplyDefaults = _dereq_('./defaults');
- Histogram2dContour.crossTraceDefaults = _dereq_('../histogram2d/cross_trace_defaults');
- Histogram2dContour.calc = _dereq_('../contour/calc');
- Histogram2dContour.plot = _dereq_('../contour/plot').plot;
- Histogram2dContour.layerName = 'contourlayer';
- Histogram2dContour.style = _dereq_('../contour/style');
- Histogram2dContour.colorbar = _dereq_('../contour/colorbar');
- Histogram2dContour.hoverPoints = _dereq_('../contour/hover');
-
- Histogram2dContour.moduleType = 'trace';
- Histogram2dContour.name = 'histogram2dcontour';
- Histogram2dContour.basePlotModule = _dereq_('../../plots/cartesian');
- Histogram2dContour.categories = ['cartesian', 'svg', '2dMap', 'contour', 'histogram', 'showLegend'];
- Histogram2dContour.meta = {
-
-
- };
-
- module.exports = Histogram2dContour;
-
- },{"../../plots/cartesian":224,"../contour/calc":295,"../contour/colorbar":297,"../contour/hover":307,"../contour/plot":312,"../contour/style":314,"../histogram2d/cross_trace_defaults":346,"./attributes":351,"./defaults":352}],354:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var colorAttrs = _dereq_('../../components/color/attributes');
- var fontAttrs = _dereq_('../../plots/font_attributes');
- var plotAttrs = _dereq_('../../plots/attributes');
- var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes');
- var domainAttrs = _dereq_('../../plots/domain').attributes;
-
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- var textFontAttrs = fontAttrs({
- editType: 'calc',
- arrayOk: true,
- colorEditType: 'plot',
-
- });
-
- module.exports = {
- labels: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- // equivalent of x0 and dx, if label is missing
- label0: {
- valType: 'number',
-
- dflt: 0,
- editType: 'calc',
-
- },
- dlabel: {
- valType: 'number',
-
- dflt: 1,
- editType: 'calc',
-
- },
-
- values: {
- valType: 'data_array',
- editType: 'calc',
-
- },
-
- marker: {
- colors: {
- valType: 'data_array', // TODO 'color_array' ?
- editType: 'calc',
-
- },
-
- line: {
- color: {
- valType: 'color',
-
- dflt: colorAttrs.defaultLine,
- arrayOk: true,
- editType: 'style',
-
- },
- width: {
- valType: 'number',
-
- min: 0,
- dflt: 0,
- arrayOk: true,
- editType: 'style',
-
- },
- editType: 'calc'
- },
- editType: 'calc'
- },
-
- text: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- hovertext: {
- valType: 'string',
-
- dflt: '',
- arrayOk: true,
- editType: 'style',
-
- },
-
- // 'see eg:'
- // 'https://www.e-education.psu.edu/natureofgeoinfo/sites/www.e-education.psu.edu.natureofgeoinfo/files/image/hisp_pies.gif',
- // '(this example involves a map too - may someday be a whole trace type',
- // 'of its own. but the point is the size of the whole pie is important.)'
- scalegroup: {
- valType: 'string',
-
- dflt: '',
- editType: 'calc',
-
- },
-
- // labels (legend is handled by plots.attributes.showlegend and layout.hiddenlabels)
- textinfo: {
- valType: 'flaglist',
-
- flags: ['label', 'text', 'value', 'percent'],
- extras: ['none'],
- editType: 'calc',
-
- },
- hoverinfo: extendFlat({}, plotAttrs.hoverinfo, {
- flags: ['label', 'text', 'value', 'percent', 'name']
- }),
- hovertemplate: hovertemplateAttrs({}, {
- keys: ['label', 'color', 'value', 'percent', 'text']
- }),
- textposition: {
- valType: 'enumerated',
-
- values: ['inside', 'outside', 'auto', 'none'],
- dflt: 'auto',
- arrayOk: true,
- editType: 'calc',
-
- },
- textfont: extendFlat({}, textFontAttrs, {
-
- }),
- insidetextfont: extendFlat({}, textFontAttrs, {
-
- }),
- outsidetextfont: extendFlat({}, textFontAttrs, {
-
- }),
-
- title: {
- text: {
- valType: 'string',
- dflt: '',
-
- editType: 'calc',
-
- },
- font: extendFlat({}, textFontAttrs, {
-
- }),
- position: {
- valType: 'enumerated',
- values: [
- 'top left', 'top center', 'top right',
- 'middle center',
- 'bottom left', 'bottom center', 'bottom right'
- ],
-
- editType: 'calc',
-
- },
-
- editType: 'calc'
- },
-
- // position and shape
- domain: domainAttrs({name: 'pie', trace: true, editType: 'calc'}),
-
- hole: {
- valType: 'number',
-
- min: 0,
- max: 1,
- dflt: 0,
- editType: 'calc',
-
- },
-
- // ordering and direction
- sort: {
- valType: 'boolean',
-
- dflt: true,
- editType: 'calc',
-
- },
- direction: {
- /**
- * there are two common conventions, both of which place the first
- * (largest, if sorted) slice with its left edge at 12 o'clock but
- * succeeding slices follow either cw or ccw from there.
- *
- * see http://visage.co/data-visualization-101-pie-charts/
- */
- valType: 'enumerated',
- values: ['clockwise', 'counterclockwise'],
-
- dflt: 'counterclockwise',
- editType: 'calc',
-
- },
- rotation: {
- valType: 'number',
-
- min: -360,
- max: 360,
- dflt: 0,
- editType: 'calc',
-
- },
-
- pull: {
- valType: 'number',
-
- min: 0,
- max: 1,
- dflt: 0,
- arrayOk: true,
- editType: 'calc',
-
- },
-
- _deprecated: {
- title: {
- valType: 'string',
- dflt: '',
-
- editType: 'calc',
-
- },
- titlefont: extendFlat({}, textFontAttrs, {
-
- }),
- titleposition: {
- valType: 'enumerated',
- values: [
- 'top left', 'top center', 'top right',
- 'middle center',
- 'bottom left', 'bottom center', 'bottom right'
- ],
-
- editType: 'calc',
-
- }
- }
- };
-
- },{"../../components/color/attributes":50,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/attributes":209,"../../plots/domain":238,"../../plots/font_attributes":239}],355:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Registry = _dereq_('../../registry');
- var getModuleCalcData = _dereq_('../../plots/get_data').getModuleCalcData;
-
- exports.name = 'pie';
-
- exports.plot = function(gd) {
- var Pie = Registry.getModule('pie');
- var cdPie = getModuleCalcData(gd.calcdata, Pie)[0];
-
- if(cdPie.length) Pie.plot(gd, cdPie);
- };
-
- exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
- var hadPie = (oldFullLayout._has && oldFullLayout._has('pie'));
- var hasPie = (newFullLayout._has && newFullLayout._has('pie'));
-
- if(hadPie && !hasPie) {
- oldFullLayout._pielayer.selectAll('g.trace').remove();
- }
- };
-
- },{"../../plots/get_data":241,"../../registry":257}],356:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
- var tinycolor = _dereq_('tinycolor2');
-
- var Color = _dereq_('../../components/color');
- var helpers = _dereq_('./helpers');
-
- exports.calc = function calc(gd, trace) {
- var vals = trace.values;
- var hasVals = isArrayOrTypedArray(vals) && vals.length;
- var labels = trace.labels;
- var colors = trace.marker.colors || [];
- var cd = [];
- var fullLayout = gd._fullLayout;
- var colorMap = fullLayout._piecolormap;
- var allThisTraceLabels = {};
- var vTotal = 0;
- var hiddenLabels = fullLayout.hiddenlabels || [];
-
- var i, v, label, hidden, pt;
-
- if(trace.dlabel) {
- labels = new Array(vals.length);
- for(i = 0; i < vals.length; i++) {
- labels[i] = String(trace.label0 + i * trace.dlabel);
- }
- }
-
- function pullColor(color, label) {
- if(!color) return false;
-
- color = tinycolor(color);
- if(!color.isValid()) return false;
-
- color = Color.addOpacity(color, color.getAlpha());
- if(!colorMap[label]) colorMap[label] = color;
-
- return color;
- }
-
- var seriesLen = (hasVals ? vals : labels).length;
-
- for(i = 0; i < seriesLen; i++) {
- if(hasVals) {
- v = vals[i];
- if(!isNumeric(v)) continue;
- v = +v;
- if(v < 0) continue;
- }
- else v = 1;
-
- label = labels[i];
- if(label === undefined || label === '') label = i;
- label = String(label);
-
- var thisLabelIndex = allThisTraceLabels[label];
- if(thisLabelIndex === undefined) {
- allThisTraceLabels[label] = cd.length;
-
- hidden = hiddenLabels.indexOf(label) !== -1;
-
- if(!hidden) vTotal += v;
-
- cd.push({
- v: v,
- label: label,
- color: pullColor(colors[i], label),
- i: i,
- pts: [i],
- hidden: hidden
- });
- }
- else {
- pt = cd[thisLabelIndex];
- pt.v += v;
- pt.pts.push(i);
- if(!pt.hidden) vTotal += v;
-
- if(pt.color === false && colors[i]) {
- pt.color = pullColor(colors[i], label);
- }
- }
- }
-
- if(trace.sort) cd.sort(function(a, b) { return b.v - a.v; });
-
- // include the sum of all values in the first point
- if(cd[0]) cd[0].vTotal = vTotal;
-
- // now insert text
- if(trace.textinfo && trace.textinfo !== 'none') {
- var hasLabel = trace.textinfo.indexOf('label') !== -1;
- var hasText = trace.textinfo.indexOf('text') !== -1;
- var hasValue = trace.textinfo.indexOf('value') !== -1;
- var hasPercent = trace.textinfo.indexOf('percent') !== -1;
- var separators = fullLayout.separators;
-
- var thisText;
-
- for(i = 0; i < cd.length; i++) {
- pt = cd[i];
- thisText = hasLabel ? [pt.label] : [];
- if(hasText) {
- var texti = helpers.getFirstFilled(trace.text, pt.pts);
- if(texti) thisText.push(texti);
- }
- if(hasValue) thisText.push(helpers.formatPieValue(pt.v, separators));
- if(hasPercent) thisText.push(helpers.formatPiePercent(pt.v / vTotal, separators));
- pt.text = thisText.join('<br>');
- }
- }
-
- return cd;
- };
-
- /*
- * `calc` filled in (and collated) explicit colors.
- * Now we need to propagate these explicit colors to other traces,
- * and fill in default colors.
- * This is done after sorting, so we pick defaults
- * in the order slices will be displayed
- */
- exports.crossTraceCalc = function(gd) {
- var fullLayout = gd._fullLayout;
- var calcdata = gd.calcdata;
- var pieColorWay = fullLayout.piecolorway;
- var colorMap = fullLayout._piecolormap;
-
- if(fullLayout.extendpiecolors) {
- pieColorWay = generateExtendedColors(pieColorWay);
- }
- var dfltColorCount = 0;
-
- var i, j, cd, pt;
- for(i = 0; i < calcdata.length; i++) {
- cd = calcdata[i];
- if(cd[0].trace.type !== 'pie') continue;
-
- for(j = 0; j < cd.length; j++) {
- pt = cd[j];
- if(pt.color === false) {
- // have we seen this label and assigned a color to it in a previous trace?
- if(colorMap[pt.label]) {
- pt.color = colorMap[pt.label];
- }
- else {
- colorMap[pt.label] = pt.color = pieColorWay[dfltColorCount % pieColorWay.length];
- dfltColorCount++;
- }
- }
- }
- }
- };
-
- /**
- * pick a default color from the main default set, augmented by
- * itself lighter then darker before repeating
- */
- var extendedColorWays = {};
-
- function generateExtendedColors(colorList) {
- var i;
- var colorString = JSON.stringify(colorList);
- var pieColors = extendedColorWays[colorString];
- if(!pieColors) {
- pieColors = colorList.slice();
-
- for(i = 0; i < colorList.length; i++) {
- pieColors.push(tinycolor(colorList[i]).lighten(20).toHexString());
- }
-
- for(i = 0; i < colorList.length; i++) {
- pieColors.push(tinycolor(colorList[i]).darken(20).toHexString());
- }
- extendedColorWays[colorString] = pieColors;
- }
-
- return pieColors;
- }
-
- },{"../../components/color":51,"../../lib":168,"./helpers":359,"fast-isnumeric":18,"tinycolor2":34}],357:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var attributes = _dereq_('./attributes');
- var handleDomainDefaults = _dereq_('../../plots/domain').defaults;
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- var coerceFont = Lib.coerceFont;
- var len;
-
- var vals = coerce('values');
- var hasVals = Lib.isArrayOrTypedArray(vals);
- var labels = coerce('labels');
- if(Array.isArray(labels)) {
- len = labels.length;
- if(hasVals) len = Math.min(len, vals.length);
- }
- else if(hasVals) {
- len = vals.length;
-
- coerce('label0');
- coerce('dlabel');
- }
-
- if(!len) {
- traceOut.visible = false;
- return;
- }
- traceOut._length = len;
-
- var lineWidth = coerce('marker.line.width');
- if(lineWidth) coerce('marker.line.color');
-
- coerce('marker.colors');
-
- coerce('scalegroup');
- // TODO: hole needs to be coerced to the same value within a scaleegroup
-
- var textData = coerce('text');
- var textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent');
- coerce('hovertext');
- coerce('hovertemplate');
-
- if(textInfo && textInfo !== 'none') {
- var textPosition = coerce('textposition');
- var hasBoth = Array.isArray(textPosition) || textPosition === 'auto';
- var hasInside = hasBoth || textPosition === 'inside';
- var hasOutside = hasBoth || textPosition === 'outside';
-
- if(hasInside || hasOutside) {
- var dfltFont = coerceFont(coerce, 'textfont', layout.font);
- if(hasInside) {
- var insideTextFontDefault = Lib.extendFlat({}, dfltFont);
- var isTraceTextfontColorSet = traceIn.textfont && traceIn.textfont.color;
- var isColorInheritedFromLayoutFont = !isTraceTextfontColorSet;
- if(isColorInheritedFromLayoutFont) {
- delete insideTextFontDefault.color;
- }
- coerceFont(coerce, 'insidetextfont', insideTextFontDefault);
- }
- if(hasOutside) coerceFont(coerce, 'outsidetextfont', dfltFont);
- }
- }
-
- handleDomainDefaults(traceOut, layout, coerce);
-
- var hole = coerce('hole');
- var title = coerce('title.text');
- if(title) {
- var titlePosition = coerce('title.position', hole ? 'middle center' : 'top center');
- if(!hole && titlePosition === 'middle center') traceOut.title.position = 'top center';
- coerceFont(coerce, 'title.font', layout.font);
- }
-
- coerce('sort');
- coerce('direction');
- coerce('rotation');
-
- coerce('pull');
- };
-
- },{"../../lib":168,"../../plots/domain":238,"./attributes":354}],358:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var appendArrayMultiPointValues = _dereq_('../../components/fx/helpers').appendArrayMultiPointValues;
-
-
- // Note: like other eventData routines, this creates the data for hover/unhover/click events
- // but it has a different API and goes through a totally different pathway.
- // So to ensure it doesn't get misused, it's not attached to the Pie module.
- module.exports = function eventData(pt, trace) {
- var out = {
- curveNumber: trace.index,
- pointNumbers: pt.pts,
- data: trace._input,
- fullData: trace,
- label: pt.label,
- color: pt.color,
- value: pt.v,
- percent: pt.percent,
- text: pt.text,
-
- // pt.v (and pt.i below) for backward compatibility
- v: pt.v
- };
-
- // Only include pointNumber if it's unambiguous
- if(pt.pts.length === 1) out.pointNumber = out.i = pt.pts[0];
-
- // Add extra data arrays to the output
- // notice that this is the multi-point version ('s' on the end!)
- // so added data will be arrays matching the pointNumbers array.
- appendArrayMultiPointValues(out, trace, pt.pts);
-
- return out;
- };
-
- },{"../../components/fx/helpers":86}],359:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- exports.formatPiePercent = function formatPiePercent(v, separators) {
- var vRounded = (v * 100).toPrecision(3);
- if(vRounded.lastIndexOf('.') !== -1) {
- vRounded = vRounded.replace(/[.]?0+$/, '');
- }
- return Lib.numSeparate(vRounded, separators) + '%';
- };
-
- exports.formatPieValue = function formatPieValue(v, separators) {
- var vRounded = v.toPrecision(10);
- if(vRounded.lastIndexOf('.') !== -1) {
- vRounded = vRounded.replace(/[.]?0+$/, '');
- }
- return Lib.numSeparate(vRounded, separators);
- };
-
- exports.getFirstFilled = function getFirstFilled(array, indices) {
- if(!Array.isArray(array)) return;
- for(var i = 0; i < indices.length; i++) {
- var v = array[indices[i]];
- if(v || v === 0) return v;
- }
- };
-
- exports.castOption = function castOption(item, indices) {
- if(Array.isArray(item)) return exports.getFirstFilled(item, indices);
- else if(item) return item;
- };
-
- },{"../../lib":168}],360:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Pie = {};
-
- Pie.attributes = _dereq_('./attributes');
- Pie.supplyDefaults = _dereq_('./defaults');
- Pie.supplyLayoutDefaults = _dereq_('./layout_defaults');
- Pie.layoutAttributes = _dereq_('./layout_attributes');
-
- var calcModule = _dereq_('./calc');
- Pie.calc = calcModule.calc;
- Pie.crossTraceCalc = calcModule.crossTraceCalc;
-
- Pie.plot = _dereq_('./plot');
- Pie.style = _dereq_('./style');
- Pie.styleOne = _dereq_('./style_one');
-
- Pie.moduleType = 'trace';
- Pie.name = 'pie';
- Pie.basePlotModule = _dereq_('./base_plot');
- Pie.categories = ['pie', 'showLegend'];
- Pie.meta = {
-
- };
-
- module.exports = Pie;
-
- },{"./attributes":354,"./base_plot":355,"./calc":356,"./defaults":357,"./layout_attributes":361,"./layout_defaults":362,"./plot":363,"./style":364,"./style_one":365}],361:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- /**
- * hiddenlabels is the pie chart analog of visible:'legendonly'
- * but it can contain many labels, and can hide slices
- * from several pies simultaneously
- */
- hiddenlabels: {
- valType: 'data_array',
- editType: 'calc'
- },
- piecolorway: {
- valType: 'colorlist',
-
- editType: 'calc',
-
- },
- extendpiecolors: {
- valType: 'boolean',
- dflt: true,
-
- editType: 'calc',
-
- }
- };
-
- },{}],362:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- var layoutAttributes = _dereq_('./layout_attributes');
-
- module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
- function coerce(attr, dflt) {
- return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
- }
- coerce('hiddenlabels');
- coerce('piecolorway', layoutOut.colorway);
- coerce('extendpiecolors');
- };
-
- },{"../../lib":168,"./layout_attributes":361}],363:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Fx = _dereq_('../../components/fx');
- var Color = _dereq_('../../components/color');
- var Drawing = _dereq_('../../components/drawing');
- var Lib = _dereq_('../../lib');
- var svgTextUtils = _dereq_('../../lib/svg_text_utils');
-
- var helpers = _dereq_('./helpers');
- var eventData = _dereq_('./event_data');
-
- module.exports = function plot(gd, cdpie) {
- var fullLayout = gd._fullLayout;
-
- prerenderTitles(cdpie, gd);
- scalePies(cdpie, fullLayout._size);
-
- var pieGroups = Lib.makeTraceGroups(fullLayout._pielayer, cdpie, 'trace').each(function(cd) {
- var pieGroup = d3.select(this);
- var cd0 = cd[0];
- var trace = cd0.trace;
-
- setCoords(cd);
-
- // TODO: miter might look better but can sometimes cause problems
- // maybe miter with a small-ish stroke-miterlimit?
- pieGroup.attr('stroke-linejoin', 'round');
-
- pieGroup.each(function() {
- var slices = d3.select(this).selectAll('g.slice').data(cd);
-
- slices.enter().append('g')
- .classed('slice', true);
- slices.exit().remove();
-
- var quadrants = [
- [[], []], // y<0: x<0, x>=0
- [[], []] // y>=0: x<0, x>=0
- ];
- var hasOutsideText = false;
-
- slices.each(function(pt) {
- if(pt.hidden) {
- d3.select(this).selectAll('path,g').remove();
- return;
- }
-
- // to have consistent event data compared to other traces
- pt.pointNumber = pt.i;
- pt.curveNumber = trace.index;
-
- quadrants[pt.pxmid[1] < 0 ? 0 : 1][pt.pxmid[0] < 0 ? 0 : 1].push(pt);
-
- var cx = cd0.cx;
- var cy = cd0.cy;
- var sliceTop = d3.select(this);
- var slicePath = sliceTop.selectAll('path.surface').data([pt]);
-
- // hover state vars
- // have we drawn a hover label, so it should be cleared later
- var hasHoverLabel = false;
- // have we emitted a hover event, so later an unhover event should be emitted
- // note that click events do not depend on this - you can still get them
- // with hovermode: false or if you were earlier dragging, then clicked
- // in the same slice that you moused up in
- var hasHoverEvent = false;
-
- function handleMouseOver() {
- // in case fullLayout or fullData has changed without a replot
- var fullLayout2 = gd._fullLayout;
- var trace2 = gd._fullData[trace.index];
-
- if(gd._dragging || fullLayout2.hovermode === false) return;
-
- var hoverinfo = trace2.hoverinfo;
- if(Array.isArray(hoverinfo)) {
- // super hacky: we need to pull out the *first* hoverinfo from
- // pt.pts, then put it back into an array in a dummy trace
- // and call castHoverinfo on that.
- // TODO: do we want to have Fx.castHoverinfo somehow handle this?
- // it already takes an array for index, for 2D, so this seems tricky.
- hoverinfo = Fx.castHoverinfo({
- hoverinfo: [helpers.castOption(hoverinfo, pt.pts)],
- _module: trace._module
- }, fullLayout2, 0);
- }
-
- if(hoverinfo === 'all') hoverinfo = 'label+text+value+percent+name';
-
- // in case we dragged over the pie from another subplot,
- // or if hover is turned off
- if(trace2.hovertemplate || (hoverinfo !== 'none' && hoverinfo !== 'skip' && hoverinfo)) {
- var rInscribed = getInscribedRadiusFraction(pt, cd0);
- var hoverCenterX = cx + pt.pxmid[0] * (1 - rInscribed);
- var hoverCenterY = cy + pt.pxmid[1] * (1 - rInscribed);
- var separators = fullLayout.separators;
- var thisText = [];
-
- if(hoverinfo && hoverinfo.indexOf('label') !== -1) thisText.push(pt.label);
- pt.text = helpers.castOption(trace2.hovertext || trace2.text, pt.pts);
- if(hoverinfo && hoverinfo.indexOf('text') !== -1) {
- var texti = pt.text;
- if(texti) thisText.push(texti);
- }
- pt.value = pt.v;
- pt.valueLabel = helpers.formatPieValue(pt.v, separators);
- if(hoverinfo && hoverinfo.indexOf('value') !== -1) thisText.push(pt.valueLabel);
- pt.percent = pt.v / cd0.vTotal;
- pt.percentLabel = helpers.formatPiePercent(pt.percent, separators);
- if(hoverinfo && hoverinfo.indexOf('percent') !== -1) thisText.push(pt.percentLabel);
-
- var hoverLabel = trace.hoverlabel;
- var hoverFont = hoverLabel.font;
-
- Fx.loneHover({
- x0: hoverCenterX - rInscribed * cd0.r,
- x1: hoverCenterX + rInscribed * cd0.r,
- y: hoverCenterY,
- text: thisText.join('<br>'),
- name: (trace2.hovertemplate || hoverinfo.indexOf('name') !== -1) ? trace2.name : undefined,
- idealAlign: pt.pxmid[0] < 0 ? 'left' : 'right',
- color: helpers.castOption(hoverLabel.bgcolor, pt.pts) || pt.color,
- borderColor: helpers.castOption(hoverLabel.bordercolor, pt.pts),
- fontFamily: helpers.castOption(hoverFont.family, pt.pts),
- fontSize: helpers.castOption(hoverFont.size, pt.pts),
- fontColor: helpers.castOption(hoverFont.color, pt.pts),
-
- trace: trace2,
- hovertemplate: helpers.castOption(trace2.hovertemplate, pt.pts),
- hovertemplateLabels: pt,
- eventData: [eventData(pt, trace2)]
- }, {
- container: fullLayout2._hoverlayer.node(),
- outerContainer: fullLayout2._paper.node(),
- gd: gd
- });
-
- hasHoverLabel = true;
- }
-
- gd.emit('plotly_hover', {
- points: [eventData(pt, trace2)],
- event: d3.event
- });
- hasHoverEvent = true;
- }
-
- function handleMouseOut(evt) {
- var fullLayout2 = gd._fullLayout;
- var trace2 = gd._fullData[trace.index];
-
- if(hasHoverEvent) {
- evt.originalEvent = d3.event;
- gd.emit('plotly_unhover', {
- points: [eventData(pt, trace2)],
- event: d3.event
- });
- hasHoverEvent = false;
- }
-
- if(hasHoverLabel) {
- Fx.loneUnhover(fullLayout2._hoverlayer.node());
- hasHoverLabel = false;
- }
- }
-
- function handleClick() {
- // TODO: this does not support right-click. If we want to support it, we
- // would likely need to change pie to use dragElement instead of straight
- // mapbox event binding. Or perhaps better, make a simple wrapper with the
- // right mousedown, mousemove, and mouseup handlers just for a left/right click
- // mapbox would use this too.
- var fullLayout2 = gd._fullLayout;
- var trace2 = gd._fullData[trace.index];
-
- if(gd._dragging || fullLayout2.hovermode === false) return;
-
- gd._hoverdata = [eventData(pt, trace2)];
- Fx.click(gd, d3.event);
- }
-
- slicePath.enter().append('path')
- .classed('surface', true)
- .style({'pointer-events': 'all'});
-
- sliceTop.select('path.textline').remove();
-
- sliceTop
- .on('mouseover', handleMouseOver)
- .on('mouseout', handleMouseOut)
- .on('click', handleClick);
-
- if(trace.pull) {
- var pull = +helpers.castOption(trace.pull, pt.pts) || 0;
- if(pull > 0) {
- cx += pull * pt.pxmid[0];
- cy += pull * pt.pxmid[1];
- }
- }
-
- pt.cxFinal = cx;
- pt.cyFinal = cy;
-
- function arc(start, finish, cw, scale) {
- return 'a' + (scale * cd0.r) + ',' + (scale * cd0.r) + ' 0 ' +
- pt.largeArc + (cw ? ' 1 ' : ' 0 ') +
- (scale * (finish[0] - start[0])) + ',' + (scale * (finish[1] - start[1]));
- }
-
- var hole = trace.hole;
- if(pt.v === cd0.vTotal) { // 100% fails bcs arc start and end are identical
- var outerCircle = 'M' + (cx + pt.px0[0]) + ',' + (cy + pt.px0[1]) +
- arc(pt.px0, pt.pxmid, true, 1) +
- arc(pt.pxmid, pt.px0, true, 1) + 'Z';
- if(hole) {
- slicePath.attr('d',
- 'M' + (cx + hole * pt.px0[0]) + ',' + (cy + hole * pt.px0[1]) +
- arc(pt.px0, pt.pxmid, false, hole) +
- arc(pt.pxmid, pt.px0, false, hole) +
- 'Z' + outerCircle);
- }
- else slicePath.attr('d', outerCircle);
- } else {
-
- var outerArc = arc(pt.px0, pt.px1, true, 1);
-
- if(hole) {
- var rim = 1 - hole;
- slicePath.attr('d',
- 'M' + (cx + hole * pt.px1[0]) + ',' + (cy + hole * pt.px1[1]) +
- arc(pt.px1, pt.px0, false, hole) +
- 'l' + (rim * pt.px0[0]) + ',' + (rim * pt.px0[1]) +
- outerArc +
- 'Z');
- } else {
- slicePath.attr('d',
- 'M' + cx + ',' + cy +
- 'l' + pt.px0[0] + ',' + pt.px0[1] +
- outerArc +
- 'Z');
- }
- }
-
- // add text
- var textPosition = helpers.castOption(trace.textposition, pt.pts);
- var sliceTextGroup = sliceTop.selectAll('g.slicetext')
- .data(pt.text && (textPosition !== 'none') ? [0] : []);
-
- sliceTextGroup.enter().append('g')
- .classed('slicetext', true);
- sliceTextGroup.exit().remove();
-
- sliceTextGroup.each(function() {
- var sliceText = Lib.ensureSingle(d3.select(this), 'text', '', function(s) {
- // prohibit tex interpretation until we can handle
- // tex and regular text together
- s.attr('data-notex', 1);
- });
-
- sliceText.text(pt.text)
- .attr({
- 'class': 'slicetext',
- transform: '',
- 'text-anchor': 'middle'
- })
- .call(Drawing.font, textPosition === 'outside' ?
- determineOutsideTextFont(trace, pt, gd._fullLayout.font) :
- determineInsideTextFont(trace, pt, gd._fullLayout.font))
- .call(svgTextUtils.convertToTspans, gd);
-
- // position the text relative to the slice
- var textBB = Drawing.bBox(sliceText.node());
- var transform;
-
- if(textPosition === 'outside') {
- transform = transformOutsideText(textBB, pt);
- } else {
- transform = transformInsideText(textBB, pt, cd0);
- if(textPosition === 'auto' && transform.scale < 1) {
- sliceText.call(Drawing.font, trace.outsidetextfont);
- if(trace.outsidetextfont.family !== trace.insidetextfont.family ||
- trace.outsidetextfont.size !== trace.insidetextfont.size) {
- textBB = Drawing.bBox(sliceText.node());
- }
- transform = transformOutsideText(textBB, pt);
- }
- }
-
- var translateX = cx + pt.pxmid[0] * transform.rCenter + (transform.x || 0);
- var translateY = cy + pt.pxmid[1] * transform.rCenter + (transform.y || 0);
-
- // save some stuff to use later ensure no labels overlap
- if(transform.outside) {
- pt.yLabelMin = translateY - textBB.height / 2;
- pt.yLabelMid = translateY;
- pt.yLabelMax = translateY + textBB.height / 2;
- pt.labelExtraX = 0;
- pt.labelExtraY = 0;
- hasOutsideText = true;
- }
-
- sliceText.attr('transform',
- 'translate(' + translateX + ',' + translateY + ')' +
- (transform.scale < 1 ? ('scale(' + transform.scale + ')') : '') +
- (transform.rotate ? ('rotate(' + transform.rotate + ')') : '') +
- 'translate(' +
- (-(textBB.left + textBB.right) / 2) + ',' +
- (-(textBB.top + textBB.bottom) / 2) +
- ')');
- });
- });
-
- // add the title
- var titleTextGroup = d3.select(this).selectAll('g.titletext')
- .data(trace.title.text ? [0] : []);
-
- titleTextGroup.enter().append('g')
- .classed('titletext', true);
- titleTextGroup.exit().remove();
-
- titleTextGroup.each(function() {
- var titleText = Lib.ensureSingle(d3.select(this), 'text', '', function(s) {
- // prohibit tex interpretation as above
- s.attr('data-notex', 1);
- });
-
- var txt = fullLayout.meta ?
- Lib.templateString(trace.title.text, {meta: fullLayout.meta}) :
- trace.title.text;
-
- titleText.text(txt)
- .attr({
- 'class': 'titletext',
- transform: '',
- 'text-anchor': 'middle',
- })
- .call(Drawing.font, trace.title.font)
- .call(svgTextUtils.convertToTspans, gd);
-
- var transform;
-
- if(trace.title.position === 'middle center') {
- transform = positionTitleInside(cd0);
- } else {
- transform = positionTitleOutside(cd0, fullLayout._size);
- }
-
- titleText.attr('transform',
- 'translate(' + transform.x + ',' + transform.y + ')' +
- (transform.scale < 1 ? ('scale(' + transform.scale + ')') : '') +
- 'translate(' + transform.tx + ',' + transform.ty + ')');
- });
-
- // now make sure no labels overlap (at least within one pie)
- if(hasOutsideText) scootLabels(quadrants, trace);
- slices.each(function(pt) {
- if(pt.labelExtraX || pt.labelExtraY) {
- // first move the text to its new location
- var sliceTop = d3.select(this);
- var sliceText = sliceTop.select('g.slicetext text');
-
- sliceText.attr('transform', 'translate(' + pt.labelExtraX + ',' + pt.labelExtraY + ')' +
- sliceText.attr('transform'));
-
- // then add a line to the new location
- var lineStartX = pt.cxFinal + pt.pxmid[0];
- var lineStartY = pt.cyFinal + pt.pxmid[1];
- var textLinePath = 'M' + lineStartX + ',' + lineStartY;
- var finalX = (pt.yLabelMax - pt.yLabelMin) * (pt.pxmid[0] < 0 ? -1 : 1) / 4;
-
- if(pt.labelExtraX) {
- var yFromX = pt.labelExtraX * pt.pxmid[1] / pt.pxmid[0];
- var yNet = pt.yLabelMid + pt.labelExtraY - (pt.cyFinal + pt.pxmid[1]);
-
- if(Math.abs(yFromX) > Math.abs(yNet)) {
- textLinePath +=
- 'l' + (yNet * pt.pxmid[0] / pt.pxmid[1]) + ',' + yNet +
- 'H' + (lineStartX + pt.labelExtraX + finalX);
- } else {
- textLinePath += 'l' + pt.labelExtraX + ',' + yFromX +
- 'v' + (yNet - yFromX) +
- 'h' + finalX;
- }
- } else {
- textLinePath +=
- 'V' + (pt.yLabelMid + pt.labelExtraY) +
- 'h' + finalX;
- }
-
- sliceTop.append('path')
- .classed('textline', true)
- .call(Color.stroke, trace.outsidetextfont.color)
- .attr({
- 'stroke-width': Math.min(2, trace.outsidetextfont.size / 8),
- d: textLinePath,
- fill: 'none'
- });
- }
- });
- });
- });
-
- // This is for a bug in Chrome (as of 2015-07-22, and does not affect FF)
- // if insidetextfont and outsidetextfont are different sizes, sometimes the size
- // of an "em" gets taken from the wrong element at first so lines are
- // spaced wrong. You just have to tell it to try again later and it gets fixed.
- // I have no idea why we haven't seen this in other contexts. Also, sometimes
- // it gets the initial draw correct but on redraw it gets confused.
- setTimeout(function() {
- pieGroups.selectAll('tspan').each(function() {
- var s = d3.select(this);
- if(s.attr('dy')) s.attr('dy', s.attr('dy'));
- });
- }, 0);
- };
-
- function determineOutsideTextFont(trace, pt, layoutFont) {
- var color = helpers.castOption(trace.outsidetextfont.color, pt.pts) ||
- helpers.castOption(trace.textfont.color, pt.pts) ||
- layoutFont.color;
-
- var family = helpers.castOption(trace.outsidetextfont.family, pt.pts) ||
- helpers.castOption(trace.textfont.family, pt.pts) ||
- layoutFont.family;
-
- var size = helpers.castOption(trace.outsidetextfont.size, pt.pts) ||
- helpers.castOption(trace.textfont.size, pt.pts) ||
- layoutFont.size;
-
- return {
- color: color,
- family: family,
- size: size
- };
- }
-
- function determineInsideTextFont(trace, pt, layoutFont) {
- var customColor = helpers.castOption(trace.insidetextfont.color, pt.pts);
- if(!customColor && trace._input.textfont) {
-
- // Why not simply using trace.textfont? Because if not set, it
- // defaults to layout.font which has a default color. But if
- // textfont.color and insidetextfont.color don't supply a value,
- // a contrasting color shall be used.
- customColor = helpers.castOption(trace._input.textfont.color, pt.pts);
- }
-
- var family = helpers.castOption(trace.insidetextfont.family, pt.pts) ||
- helpers.castOption(trace.textfont.family, pt.pts) ||
- layoutFont.family;
-
- var size = helpers.castOption(trace.insidetextfont.size, pt.pts) ||
- helpers.castOption(trace.textfont.size, pt.pts) ||
- layoutFont.size;
-
- return {
- color: customColor || Color.contrast(pt.color),
- family: family,
- size: size
- };
- }
-
- function prerenderTitles(cdpie, gd) {
- var fullLayout = gd._fullLayout;
-
- var cd0, trace;
- // Determine the width and height of the title for each pie.
- for(var i = 0; i < cdpie.length; i++) {
- cd0 = cdpie[i][0];
- trace = cd0.trace;
-
- if(trace.title.text) {
- var txt = fullLayout.meta ?
- Lib.templateString(trace.title.text, {meta: fullLayout.meta}) :
- trace.title.text;
-
- var dummyTitle = Drawing.tester.append('text')
- .attr('data-notex', 1)
- .text(txt)
- .call(Drawing.font, trace.title.font)
- .call(svgTextUtils.convertToTspans, gd);
- var bBox = Drawing.bBox(dummyTitle.node(), true);
- cd0.titleBox = {
- width: bBox.width,
- height: bBox.height,
- };
- dummyTitle.remove();
- }
- }
- }
-
- function transformInsideText(textBB, pt, cd0) {
- var textDiameter = Math.sqrt(textBB.width * textBB.width + textBB.height * textBB.height);
- var textAspect = textBB.width / textBB.height;
- var halfAngle = Math.PI * Math.min(pt.v / cd0.vTotal, 0.5);
- var ring = 1 - cd0.trace.hole;
- var rInscribed = getInscribedRadiusFraction(pt, cd0);
-
- // max size text can be inserted inside without rotating it
- // this inscribes the text rectangle in a circle, which is then inscribed
- // in the slice, so it will be an underestimate, which some day we may want
- // to improve so this case can get more use
- var transform = {
- scale: rInscribed * cd0.r * 2 / textDiameter,
-
- // and the center position and rotation in this case
- rCenter: 1 - rInscribed,
- rotate: 0
- };
-
- if(transform.scale >= 1) return transform;
-
- // max size if text is rotated radially
- var Qr = textAspect + 1 / (2 * Math.tan(halfAngle));
- var maxHalfHeightRotRadial = cd0.r * Math.min(
- 1 / (Math.sqrt(Qr * Qr + 0.5) + Qr),
- ring / (Math.sqrt(textAspect * textAspect + ring / 2) + textAspect)
- );
- var radialTransform = {
- scale: maxHalfHeightRotRadial * 2 / textBB.height,
- rCenter: Math.cos(maxHalfHeightRotRadial / cd0.r) -
- maxHalfHeightRotRadial * textAspect / cd0.r,
- rotate: (180 / Math.PI * pt.midangle + 720) % 180 - 90
- };
-
- // max size if text is rotated tangentially
- var aspectInv = 1 / textAspect;
- var Qt = aspectInv + 1 / (2 * Math.tan(halfAngle));
- var maxHalfWidthTangential = cd0.r * Math.min(
- 1 / (Math.sqrt(Qt * Qt + 0.5) + Qt),
- ring / (Math.sqrt(aspectInv * aspectInv + ring / 2) + aspectInv)
- );
- var tangentialTransform = {
- scale: maxHalfWidthTangential * 2 / textBB.width,
- rCenter: Math.cos(maxHalfWidthTangential / cd0.r) -
- maxHalfWidthTangential / textAspect / cd0.r,
- rotate: (180 / Math.PI * pt.midangle + 810) % 180 - 90
- };
- // if we need a rotated transform, pick the biggest one
- // even if both are bigger than 1
- var rotatedTransform = tangentialTransform.scale > radialTransform.scale ?
- tangentialTransform : radialTransform;
-
- if(transform.scale < 1 && rotatedTransform.scale > transform.scale) return rotatedTransform;
- return transform;
- }
-
- function getInscribedRadiusFraction(pt, cd0) {
- if(pt.v === cd0.vTotal && !cd0.trace.hole) return 1;// special case of 100% with no hole
-
- var halfAngle = Math.PI * Math.min(pt.v / cd0.vTotal, 0.5);
- return Math.min(1 / (1 + 1 / Math.sin(halfAngle)), (1 - cd0.trace.hole) / 2);
- }
-
- function transformOutsideText(textBB, pt) {
- var x = pt.pxmid[0];
- var y = pt.pxmid[1];
- var dx = textBB.width / 2;
- var dy = textBB.height / 2;
-
- if(x < 0) dx *= -1;
- if(y < 0) dy *= -1;
-
- return {
- scale: 1,
- rCenter: 1,
- rotate: 0,
- x: dx + Math.abs(dy) * (dx > 0 ? 1 : -1) / 2,
- y: dy / (1 + x * x / (y * y)),
- outside: true
- };
- }
-
- function positionTitleInside(cd0) {
- var textDiameter =
- Math.sqrt(cd0.titleBox.width * cd0.titleBox.width + cd0.titleBox.height * cd0.titleBox.height);
- return {
- x: cd0.cx,
- y: cd0.cy,
- scale: cd0.trace.hole * cd0.r * 2 / textDiameter,
- tx: 0,
- ty: - cd0.titleBox.height / 2 + cd0.trace.title.font.size
- };
- }
-
- function positionTitleOutside(cd0, plotSize) {
- var scaleX = 1;
- var scaleY = 1;
- var maxWidth, maxPull;
-
- var trace = cd0.trace;
- // position of the baseline point of the text box in the plot, before scaling.
- // we anchored the text in the middle, so the baseline is on the bottom middle
- // of the first line of text.
- var topMiddle = {
- x: cd0.cx,
- y: cd0.cy
- };
- // relative translation of the text box after scaling
- var translate = {
- tx: 0,
- ty: 0
- };
-
- // we reason below as if the baseline is the top middle point of the text box.
- // so we must add the font size to approximate the y-coord. of the top.
- // note that this correction must happen after scaling.
- translate.ty += trace.title.font.size;
- maxPull = getMaxPull(trace);
-
- if(trace.title.position.indexOf('top') !== -1) {
- topMiddle.y -= (1 + maxPull) * cd0.r;
- translate.ty -= cd0.titleBox.height;
- }
- else if(trace.title.position.indexOf('bottom') !== -1) {
- topMiddle.y += (1 + maxPull) * cd0.r;
- }
-
- if(trace.title.position.indexOf('left') !== -1) {
- // we start the text at the left edge of the pie
- maxWidth = plotSize.w * (trace.domain.x[1] - trace.domain.x[0]) / 2 + cd0.r;
- topMiddle.x -= (1 + maxPull) * cd0.r;
- translate.tx += cd0.titleBox.width / 2;
- } else if(trace.title.position.indexOf('center') !== -1) {
- maxWidth = plotSize.w * (trace.domain.x[1] - trace.domain.x[0]);
- } else if(trace.title.position.indexOf('right') !== -1) {
- maxWidth = plotSize.w * (trace.domain.x[1] - trace.domain.x[0]) / 2 + cd0.r;
- topMiddle.x += (1 + maxPull) * cd0.r;
- translate.tx -= cd0.titleBox.width / 2;
- }
- scaleX = maxWidth / cd0.titleBox.width;
- scaleY = getTitleSpace(cd0, plotSize) / cd0.titleBox.height;
- return {
- x: topMiddle.x,
- y: topMiddle.y,
- scale: Math.min(scaleX, scaleY),
- tx: translate.tx,
- ty: translate.ty
- };
- }
-
- function getTitleSpace(cd0, plotSize) {
- var trace = cd0.trace;
- var pieBoxHeight = plotSize.h * (trace.domain.y[1] - trace.domain.y[0]);
- // use at most half of the plot for the title
- return Math.min(cd0.titleBox.height, pieBoxHeight / 2);
- }
-
- function getMaxPull(trace) {
- var maxPull = trace.pull;
- var j;
- if(Array.isArray(maxPull)) {
- maxPull = 0;
- for(j = 0; j < trace.pull.length; j++) {
- if(trace.pull[j] > maxPull) maxPull = trace.pull[j];
- }
- }
- return maxPull;
- }
-
- function scootLabels(quadrants, trace) {
- var xHalf, yHalf, equatorFirst, farthestX, farthestY,
- xDiffSign, yDiffSign, thisQuad, oppositeQuad,
- wholeSide, i, thisQuadOutside, firstOppositeOutsidePt;
-
- function topFirst(a, b) { return a.pxmid[1] - b.pxmid[1]; }
- function bottomFirst(a, b) { return b.pxmid[1] - a.pxmid[1]; }
-
- function scootOneLabel(thisPt, prevPt) {
- if(!prevPt) prevPt = {};
-
- var prevOuterY = prevPt.labelExtraY + (yHalf ? prevPt.yLabelMax : prevPt.yLabelMin);
- var thisInnerY = yHalf ? thisPt.yLabelMin : thisPt.yLabelMax;
- var thisOuterY = yHalf ? thisPt.yLabelMax : thisPt.yLabelMin;
- var thisSliceOuterY = thisPt.cyFinal + farthestY(thisPt.px0[1], thisPt.px1[1]);
- var newExtraY = prevOuterY - thisInnerY;
-
- var xBuffer, i, otherPt, otherOuterY, otherOuterX, newExtraX;
-
- // make sure this label doesn't overlap other labels
- // this *only* has us move these labels vertically
- if(newExtraY * yDiffSign > 0) thisPt.labelExtraY = newExtraY;
-
- // make sure this label doesn't overlap any slices
- if(!Array.isArray(trace.pull)) return; // this can only happen with array pulls
-
- for(i = 0; i < wholeSide.length; i++) {
- otherPt = wholeSide[i];
-
- // overlap can only happen if the other point is pulled more than this one
- if(otherPt === thisPt || (
- (helpers.castOption(trace.pull, thisPt.pts) || 0) >=
- (helpers.castOption(trace.pull, otherPt.pts) || 0))
- ) {
- continue;
- }
-
- if((thisPt.pxmid[1] - otherPt.pxmid[1]) * yDiffSign > 0) {
- // closer to the equator - by construction all of these happen first
- // move the text vertically to get away from these slices
- otherOuterY = otherPt.cyFinal + farthestY(otherPt.px0[1], otherPt.px1[1]);
- newExtraY = otherOuterY - thisInnerY - thisPt.labelExtraY;
-
- if(newExtraY * yDiffSign > 0) thisPt.labelExtraY += newExtraY;
-
- } else if((thisOuterY + thisPt.labelExtraY - thisSliceOuterY) * yDiffSign > 0) {
- // farther from the equator - happens after we've done all the
- // vertical moving we're going to do
- // move horizontally to get away from these more polar slices
-
- // if we're moving horz. based on a slice that's several slices away from this one
- // then we need some extra space for the lines to labels between them
- xBuffer = 3 * xDiffSign * Math.abs(i - wholeSide.indexOf(thisPt));
-
- otherOuterX = otherPt.cxFinal + farthestX(otherPt.px0[0], otherPt.px1[0]);
- newExtraX = otherOuterX + xBuffer - (thisPt.cxFinal + thisPt.pxmid[0]) - thisPt.labelExtraX;
-
- if(newExtraX * xDiffSign > 0) thisPt.labelExtraX += newExtraX;
- }
- }
- }
-
- for(yHalf = 0; yHalf < 2; yHalf++) {
- equatorFirst = yHalf ? topFirst : bottomFirst;
- farthestY = yHalf ? Math.max : Math.min;
- yDiffSign = yHalf ? 1 : -1;
-
- for(xHalf = 0; xHalf < 2; xHalf++) {
- farthestX = xHalf ? Math.max : Math.min;
- xDiffSign = xHalf ? 1 : -1;
-
- // first sort the array
- // note this is a copy of cd, so cd itself doesn't get sorted
- // but we can still modify points in place.
- thisQuad = quadrants[yHalf][xHalf];
- thisQuad.sort(equatorFirst);
-
- oppositeQuad = quadrants[1 - yHalf][xHalf];
- wholeSide = oppositeQuad.concat(thisQuad);
-
- thisQuadOutside = [];
- for(i = 0; i < thisQuad.length; i++) {
- if(thisQuad[i].yLabelMid !== undefined) thisQuadOutside.push(thisQuad[i]);
- }
-
- firstOppositeOutsidePt = false;
- for(i = 0; yHalf && i < oppositeQuad.length; i++) {
- if(oppositeQuad[i].yLabelMid !== undefined) {
- firstOppositeOutsidePt = oppositeQuad[i];
- break;
- }
- }
-
- // each needs to avoid the previous
- for(i = 0; i < thisQuadOutside.length; i++) {
- var prevPt = i && thisQuadOutside[i - 1];
- // bottom half needs to avoid the first label of the top half
- // top half we still need to call scootOneLabel on the first slice
- // so we can avoid other slices, but we don't pass a prevPt
- if(firstOppositeOutsidePt && !i) prevPt = firstOppositeOutsidePt;
- scootOneLabel(thisQuadOutside[i], prevPt);
- }
- }
- }
- }
-
- function scalePies(cdpie, plotSize) {
- var scaleGroups = [];
-
- var pieBoxWidth, pieBoxHeight, i, j, cd0, trace,
- maxPull, scaleGroup, minPxPerValUnit;
-
- // first figure out the center and maximum radius for each pie
- for(i = 0; i < cdpie.length; i++) {
- cd0 = cdpie[i][0];
- trace = cd0.trace;
-
- pieBoxWidth = plotSize.w * (trace.domain.x[1] - trace.domain.x[0]);
- pieBoxHeight = plotSize.h * (trace.domain.y[1] - trace.domain.y[0]);
- // leave some space for the title, if it will be displayed outside
- if(trace.title.text && trace.title.position !== 'middle center') {
- pieBoxHeight -= getTitleSpace(cd0, plotSize);
- }
-
- maxPull = getMaxPull(trace);
-
- cd0.r = Math.min(pieBoxWidth, pieBoxHeight) / (2 + 2 * maxPull);
-
- cd0.cx = plotSize.l + plotSize.w * (trace.domain.x[1] + trace.domain.x[0]) / 2;
- cd0.cy = plotSize.t + plotSize.h * (1 - trace.domain.y[0]) - pieBoxHeight / 2;
- if(trace.title.text && trace.title.position.indexOf('bottom') !== -1) {
- cd0.cy -= getTitleSpace(cd0, plotSize);
- }
-
- if(trace.scalegroup && scaleGroups.indexOf(trace.scalegroup) === -1) {
- scaleGroups.push(trace.scalegroup);
- }
- }
-
- // Then scale any pies that are grouped
- for(j = 0; j < scaleGroups.length; j++) {
- minPxPerValUnit = Infinity;
- scaleGroup = scaleGroups[j];
-
- for(i = 0; i < cdpie.length; i++) {
- cd0 = cdpie[i][0];
- if(cd0.trace.scalegroup === scaleGroup) {
- minPxPerValUnit = Math.min(minPxPerValUnit,
- cd0.r * cd0.r / cd0.vTotal);
- }
- }
-
- for(i = 0; i < cdpie.length; i++) {
- cd0 = cdpie[i][0];
- if(cd0.trace.scalegroup === scaleGroup) {
- cd0.r = Math.sqrt(minPxPerValUnit * cd0.vTotal);
- }
- }
- }
-
- }
-
- function setCoords(cd) {
- var cd0 = cd[0];
- var trace = cd0.trace;
- var currentAngle = trace.rotation * Math.PI / 180;
- var angleFactor = 2 * Math.PI / cd0.vTotal;
- var firstPt = 'px0';
- var lastPt = 'px1';
-
- var i, cdi, currentCoords;
-
- if(trace.direction === 'counterclockwise') {
- for(i = 0; i < cd.length; i++) {
- if(!cd[i].hidden) break; // find the first non-hidden slice
- }
- if(i === cd.length) return; // all slices hidden
-
- currentAngle += angleFactor * cd[i].v;
- angleFactor *= -1;
- firstPt = 'px1';
- lastPt = 'px0';
- }
-
- function getCoords(angle) {
- return [cd0.r * Math.sin(angle), -cd0.r * Math.cos(angle)];
- }
-
- currentCoords = getCoords(currentAngle);
-
- for(i = 0; i < cd.length; i++) {
- cdi = cd[i];
- if(cdi.hidden) continue;
-
- cdi[firstPt] = currentCoords;
-
- currentAngle += angleFactor * cdi.v / 2;
- cdi.pxmid = getCoords(currentAngle);
- cdi.midangle = currentAngle;
-
- currentAngle += angleFactor * cdi.v / 2;
- currentCoords = getCoords(currentAngle);
-
- cdi[lastPt] = currentCoords;
-
- cdi.largeArc = (cdi.v > cd0.vTotal / 2) ? 1 : 0;
- }
- }
-
- },{"../../components/color":51,"../../components/drawing":72,"../../components/fx":90,"../../lib":168,"../../lib/svg_text_utils":189,"./event_data":358,"./helpers":359,"d3":16}],364:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var styleOne = _dereq_('./style_one');
-
- module.exports = function style(gd) {
- gd._fullLayout._pielayer.selectAll('.trace').each(function(cd) {
- var cd0 = cd[0];
- var trace = cd0.trace;
- var traceSelection = d3.select(this);
-
- traceSelection.style({opacity: trace.opacity});
-
- traceSelection.selectAll('path.surface').each(function(pt) {
- d3.select(this).call(styleOne, pt, trace);
- });
- });
- };
-
- },{"./style_one":365,"d3":16}],365:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Color = _dereq_('../../components/color');
- var castOption = _dereq_('./helpers').castOption;
-
- module.exports = function styleOne(s, pt, trace) {
- var line = trace.marker.line;
- var lineColor = castOption(line.color, pt.pts) || Color.defaultLine;
- var lineWidth = castOption(line.width, pt.pts) || 0;
-
- s.style({'stroke-width': lineWidth})
- .call(Color.fill, pt.color)
- .call(Color.stroke, lineColor);
- };
-
- },{"../../components/color":51,"./helpers":359}],366:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
-
- // arrayOk attributes, merge them into calcdata array
- module.exports = function arraysToCalcdata(cd, trace) {
-
- // so each point knows which index it originally came from
- for(var i = 0; i < cd.length; i++) cd[i].i = i;
-
- Lib.mergeArray(trace.text, cd, 'tx');
- Lib.mergeArray(trace.hovertext, cd, 'htx');
- Lib.mergeArray(trace.customdata, cd, 'data');
- Lib.mergeArray(trace.textposition, cd, 'tp');
- if(trace.textfont) {
- Lib.mergeArray(trace.textfont.size, cd, 'ts');
- Lib.mergeArray(trace.textfont.color, cd, 'tc');
- Lib.mergeArray(trace.textfont.family, cd, 'tf');
- }
-
- var marker = trace.marker;
- if(marker) {
- Lib.mergeArray(marker.size, cd, 'ms');
- Lib.mergeArray(marker.opacity, cd, 'mo');
- Lib.mergeArray(marker.symbol, cd, 'mx');
- Lib.mergeArray(marker.color, cd, 'mc');
-
- var markerLine = marker.line;
- if(marker.line) {
- Lib.mergeArray(markerLine.color, cd, 'mlc');
- Lib.mergeArray(markerLine.width, cd, 'mlw');
- }
-
- var markerGradient = marker.gradient;
- if(markerGradient && markerGradient.type !== 'none') {
- Lib.mergeArray(markerGradient.type, cd, 'mgt');
- Lib.mergeArray(markerGradient.color, cd, 'mgc');
- }
- }
- };
-
- },{"../../lib":168}],367:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes');
- var colorAttributes = _dereq_('../../components/colorscale/attributes');
- var colorbarAttrs = _dereq_('../../components/colorbar/attributes');
- var fontAttrs = _dereq_('../../plots/font_attributes');
- var dash = _dereq_('../../components/drawing/attributes').dash;
-
- var Drawing = _dereq_('../../components/drawing');
- var constants = _dereq_('./constants');
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- module.exports = {
- x: {
- valType: 'data_array',
- editType: 'calc+clearAxisTypes',
- anim: true,
-
- },
- x0: {
- valType: 'any',
- dflt: 0,
-
- editType: 'calc+clearAxisTypes',
- anim: true,
-
- },
- dx: {
- valType: 'number',
- dflt: 1,
-
- editType: 'calc',
- anim: true,
-
- },
- y: {
- valType: 'data_array',
- editType: 'calc+clearAxisTypes',
- anim: true,
-
- },
- y0: {
- valType: 'any',
- dflt: 0,
-
- editType: 'calc+clearAxisTypes',
- anim: true,
-
- },
- dy: {
- valType: 'number',
- dflt: 1,
-
- editType: 'calc',
- anim: true,
-
- },
-
- stackgroup: {
- valType: 'string',
-
- dflt: '',
- editType: 'calc',
-
- },
- orientation: {
- valType: 'enumerated',
-
- values: ['v', 'h'],
- editType: 'calc',
-
- },
- groupnorm: {
- valType: 'enumerated',
- values: ['', 'fraction', 'percent'],
- dflt: '',
-
- editType: 'calc',
-
- },
- stackgaps: {
- valType: 'enumerated',
- values: ['infer zero', 'interpolate'],
- dflt: 'infer zero',
-
- editType: 'calc',
-
- },
-
- text: {
- valType: 'string',
-
- dflt: '',
- arrayOk: true,
- editType: 'calc',
-
- },
- hovertext: {
- valType: 'string',
-
- dflt: '',
- arrayOk: true,
- editType: 'style',
-
- },
- mode: {
- valType: 'flaglist',
- flags: ['lines', 'markers', 'text'],
- extras: ['none'],
-
- editType: 'calc',
-
- },
- hoveron: {
- valType: 'flaglist',
- flags: ['points', 'fills'],
-
- editType: 'style',
-
- },
- hovertemplate: hovertemplateAttrs({}, {
- keys: constants.eventDataKeys
- }),
- line: {
- color: {
- valType: 'color',
-
- editType: 'style',
- anim: true,
-
- },
- width: {
- valType: 'number',
- min: 0,
- dflt: 2,
-
- editType: 'style',
- anim: true,
-
- },
- shape: {
- valType: 'enumerated',
- values: ['linear', 'spline', 'hv', 'vh', 'hvh', 'vhv'],
- dflt: 'linear',
-
- editType: 'plot',
-
- },
- smoothing: {
- valType: 'number',
- min: 0,
- max: 1.3,
- dflt: 1,
-
- editType: 'plot',
-
- },
- dash: extendFlat({}, dash, {editType: 'style'}),
- simplify: {
- valType: 'boolean',
- dflt: true,
-
- editType: 'plot',
-
- },
- editType: 'plot'
- },
-
- connectgaps: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'calc',
-
- },
- cliponaxis: {
- valType: 'boolean',
- dflt: true,
-
- editType: 'plot',
-
- },
-
- fill: {
- valType: 'enumerated',
- values: ['none', 'tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'],
-
- editType: 'calc',
-
- },
- fillcolor: {
- valType: 'color',
-
- editType: 'style',
- anim: true,
-
- },
- marker: extendFlat({
- symbol: {
- valType: 'enumerated',
- values: Drawing.symbolList,
- dflt: 'circle',
- arrayOk: true,
-
- editType: 'style',
-
- },
- opacity: {
- valType: 'number',
- min: 0,
- max: 1,
- arrayOk: true,
-
- editType: 'style',
- anim: true,
-
- },
- size: {
- valType: 'number',
- min: 0,
- dflt: 6,
- arrayOk: true,
-
- editType: 'calc',
- anim: true,
-
- },
- maxdisplayed: {
- valType: 'number',
- min: 0,
- dflt: 0,
-
- editType: 'plot',
-
- },
- sizeref: {
- valType: 'number',
- dflt: 1,
-
- editType: 'calc',
-
- },
- sizemin: {
- valType: 'number',
- min: 0,
- dflt: 0,
-
- editType: 'calc',
-
- },
- sizemode: {
- valType: 'enumerated',
- values: ['diameter', 'area'],
- dflt: 'diameter',
-
- editType: 'calc',
-
- },
-
- colorbar: colorbarAttrs,
-
- line: extendFlat({
- width: {
- valType: 'number',
- min: 0,
- arrayOk: true,
-
- editType: 'style',
- anim: true,
-
- },
- editType: 'calc'
- },
- colorAttributes('marker.line', {anim: true})
- ),
- gradient: {
- type: {
- valType: 'enumerated',
- values: ['radial', 'horizontal', 'vertical', 'none'],
- arrayOk: true,
- dflt: 'none',
-
- editType: 'calc',
-
- },
- color: {
- valType: 'color',
- arrayOk: true,
-
- editType: 'calc',
-
- },
- editType: 'calc'
- },
- editType: 'calc'
- },
- colorAttributes('marker', {anim: true})
- ),
- selected: {
- marker: {
- opacity: {
- valType: 'number',
- min: 0,
- max: 1,
-
- editType: 'style',
-
- },
- color: {
- valType: 'color',
-
- editType: 'style',
-
- },
- size: {
- valType: 'number',
- min: 0,
-
- editType: 'style',
-
- },
- editType: 'style'
- },
- textfont: {
- color: {
- valType: 'color',
-
- editType: 'style',
-
- },
- editType: 'style'
- },
- editType: 'style'
- },
- unselected: {
- marker: {
- opacity: {
- valType: 'number',
- min: 0,
- max: 1,
-
- editType: 'style',
-
- },
- color: {
- valType: 'color',
-
- editType: 'style',
-
- },
- size: {
- valType: 'number',
- min: 0,
-
- editType: 'style',
-
- },
- editType: 'style'
- },
- textfont: {
- color: {
- valType: 'color',
-
- editType: 'style',
-
- },
- editType: 'style'
- },
- editType: 'style'
- },
-
- textposition: {
- valType: 'enumerated',
- values: [
- 'top left', 'top center', 'top right',
- 'middle left', 'middle center', 'middle right',
- 'bottom left', 'bottom center', 'bottom right'
- ],
- dflt: 'middle center',
- arrayOk: true,
-
- editType: 'calc',
-
- },
- textfont: fontAttrs({
- editType: 'calc',
- colorEditType: 'style',
- arrayOk: true,
-
- }),
-
- r: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- t: {
- valType: 'data_array',
- editType: 'calc',
-
- }
- };
-
- },{"../../components/colorbar/attributes":52,"../../components/colorscale/attributes":58,"../../components/drawing":72,"../../components/drawing/attributes":71,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/font_attributes":239,"./constants":371}],368:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
- var Lib = _dereq_('../../lib');
-
- var Axes = _dereq_('../../plots/cartesian/axes');
- var BADNUM = _dereq_('../../constants/numerical').BADNUM;
-
- var subTypes = _dereq_('./subtypes');
- var calcColorscale = _dereq_('./colorscale_calc');
- var arraysToCalcdata = _dereq_('./arrays_to_calcdata');
- var calcSelection = _dereq_('./calc_selection');
-
- function calc(gd, trace) {
- var fullLayout = gd._fullLayout;
- var xa = Axes.getFromId(gd, trace.xaxis || 'x');
- var ya = Axes.getFromId(gd, trace.yaxis || 'y');
- var x = xa.makeCalcdata(trace, 'x');
- var y = ya.makeCalcdata(trace, 'y');
- var serieslen = trace._length;
- var cd = new Array(serieslen);
- var ids = trace.ids;
- var stackGroupOpts = getStackOpts(trace, fullLayout, xa, ya);
- var interpolateGaps = false;
- var isV, i, j, k, interpolate, vali;
-
- setFirstScatter(fullLayout, trace);
-
- var xAttr = 'x';
- var yAttr = 'y';
- var posAttr;
- if(stackGroupOpts) {
- stackGroupOpts.traceIndices.push(trace.index);
- isV = stackGroupOpts.orientation === 'v';
- // size, like we use for bar
- if(isV) {
- yAttr = 's';
- posAttr = 'x';
- }
- else {
- xAttr = 's';
- posAttr = 'y';
- }
- interpolate = stackGroupOpts.stackgaps === 'interpolate';
- }
- else {
- var ppad = calcMarkerSize(trace, serieslen);
- calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
- }
-
- for(i = 0; i < serieslen; i++) {
- var cdi = cd[i] = {};
- var xValid = isNumeric(x[i]);
- var yValid = isNumeric(y[i]);
- if(xValid && yValid) {
- cdi[xAttr] = x[i];
- cdi[yAttr] = y[i];
- }
- // if we're stacking we need to hold on to all valid positions
- // even with invalid sizes
- else if(stackGroupOpts && (isV ? xValid : yValid)) {
- cdi[posAttr] = isV ? x[i] : y[i];
- cdi.gap = true;
- if(interpolate) {
- cdi.s = BADNUM;
- interpolateGaps = true;
- }
- else {
- cdi.s = 0;
- }
- }
- else {
- cdi[xAttr] = cdi[yAttr] = BADNUM;
- }
-
- if(ids) {
- cdi.id = String(ids[i]);
- }
- }
-
- arraysToCalcdata(cd, trace);
- calcColorscale(gd, trace);
- calcSelection(cd, trace);
-
- if(stackGroupOpts) {
- // remove bad positions and sort
- // note that original indices get added to cd in arraysToCalcdata
- i = 0;
- while(i < cd.length) {
- if(cd[i][posAttr] === BADNUM) {
- cd.splice(i, 1);
- }
- else i++;
- }
-
- Lib.sort(cd, function(a, b) {
- return (a[posAttr] - b[posAttr]) || (a.i - b.i);
- });
-
- if(interpolateGaps) {
- // first fill the beginning with constant from the first point
- i = 0;
- while(i < cd.length - 1 && cd[i].gap) {
- i++;
- }
- vali = cd[i].s;
- if(!vali) vali = cd[i].s = 0; // in case of no data AT ALL in this trace - use 0
- for(j = 0; j < i; j++) {
- cd[j].s = vali;
- }
- // then fill the end with constant from the last point
- k = cd.length - 1;
- while(k > i && cd[k].gap) {
- k--;
- }
- vali = cd[k].s;
- for(j = cd.length - 1; j > k; j--) {
- cd[j].s = vali;
- }
- // now interpolate internal gaps linearly
- while(i < k) {
- i++;
- if(cd[i].gap) {
- j = i + 1;
- while(cd[j].gap) {
- j++;
- }
- var pos0 = cd[i - 1][posAttr];
- var size0 = cd[i - 1].s;
- var m = (cd[j].s - size0) / (cd[j][posAttr] - pos0);
- while(i < j) {
- cd[i].s = size0 + (cd[i][posAttr] - pos0) * m;
- i++;
- }
- }
- }
- }
- }
-
- return cd;
- }
-
- function calcAxisExpansion(gd, trace, xa, ya, x, y, ppad) {
- var serieslen = trace._length;
- var fullLayout = gd._fullLayout;
- var xId = xa._id;
- var yId = ya._id;
- var firstScatter = fullLayout._firstScatter[firstScatterGroup(trace)] === trace.uid;
- var stackOrientation = (getStackOpts(trace, fullLayout, xa, ya) || {}).orientation;
- var fill = trace.fill;
-
- // cancel minimum tick spacings (only applies to bars and boxes)
- xa._minDtick = 0;
- ya._minDtick = 0;
-
- // check whether bounds should be tight, padded, extended to zero...
- // most cases both should be padded on both ends, so start with that.
- var xOptions = {padded: true};
- var yOptions = {padded: true};
-
- if(ppad) {
- xOptions.ppad = yOptions.ppad = ppad;
- }
-
- // TODO: text size
-
- var openEnded = serieslen < 2 || (x[0] !== x[serieslen - 1]) || (y[0] !== y[serieslen - 1]);
-
- // include zero (tight) and extremes (padded) if fill to zero
- // (unless the shape is closed, then it's just filling the shape regardless)
- if(openEnded && (
- (fill === 'tozerox') ||
- ((fill === 'tonextx') && (firstScatter || stackOrientation === 'h'))
- )) {
- xOptions.tozero = true;
- }
-
- // if no error bars, markers or text, or fill to y=0 remove x padding
- else if(!(trace.error_y || {}).visible && (
- (fill === 'tonexty' || fill === 'tozeroy') ||
- (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace))
- )) {
- xOptions.padded = false;
- xOptions.ppad = 0;
- }
-
- // now check for y - rather different logic, though still mostly padded both ends
- // include zero (tight) and extremes (padded) if fill to zero
- // (unless the shape is closed, then it's just filling the shape regardless)
- if(openEnded && (
- (fill === 'tozeroy') ||
- ((fill === 'tonexty') && (firstScatter || stackOrientation === 'v'))
- )) {
- yOptions.tozero = true;
- }
-
- // tight y: any x fill
- else if(fill === 'tonextx' || fill === 'tozerox') {
- yOptions.padded = false;
- }
-
- // N.B. asymmetric splom traces call this with blank {} xa or ya
- if(xId) trace._extremes[xId] = Axes.findExtremes(xa, x, xOptions);
- if(yId) trace._extremes[yId] = Axes.findExtremes(ya, y, yOptions);
- }
-
- function calcMarkerSize(trace, serieslen) {
- if(!subTypes.hasMarkers(trace)) return;
-
- // Treat size like x or y arrays --- Run d2c
- // this needs to go before ppad computation
- var marker = trace.marker;
- var sizeref = 1.6 * (trace.marker.sizeref || 1);
- var markerTrans;
-
- if(trace.marker.sizemode === 'area') {
- markerTrans = function(v) {
- return Math.max(Math.sqrt((v || 0) / sizeref), 3);
- };
- } else {
- markerTrans = function(v) {
- return Math.max((v || 0) / sizeref, 3);
- };
- }
-
- if(Lib.isArrayOrTypedArray(marker.size)) {
- // I tried auto-type but category and dates dont make much sense.
- var ax = {type: 'linear'};
- Axes.setConvert(ax);
-
- var s = ax.makeCalcdata(trace.marker, 'size');
-
- var sizeOut = new Array(serieslen);
- for(var i = 0; i < serieslen; i++) {
- sizeOut[i] = markerTrans(s[i]);
- }
- return sizeOut;
-
- } else {
- return markerTrans(marker.size);
- }
- }
-
- /**
- * mark the first scatter trace for each subplot
- * note that scatter and scattergl each get their own first trace
- * note also that I'm doing this during calc rather than supplyDefaults
- * so I don't need to worry about transforms, but if we ever do
- * per-trace calc this will get confused.
- */
- function setFirstScatter(fullLayout, trace) {
- var group = firstScatterGroup(trace);
- var firstScatter = fullLayout._firstScatter;
- if(!firstScatter[group]) firstScatter[group] = trace.uid;
- }
-
- function firstScatterGroup(trace) {
- var stackGroup = trace.stackgroup;
- return trace.xaxis + trace.yaxis + trace.type +
- (stackGroup ? '-' + stackGroup : '');
- }
-
- function getStackOpts(trace, fullLayout, xa, ya) {
- var stackGroup = trace.stackgroup;
- if(!stackGroup) return;
- var stackOpts = fullLayout._scatterStackOpts[xa._id + ya._id][stackGroup];
- var stackAx = stackOpts.orientation === 'v' ? ya : xa;
- // Allow stacking only on numeric axes
- // calc is a little late to be figuring this out, but during supplyDefaults
- // we don't know the axis type yet
- if(stackAx.type === 'linear' || stackAx.type === 'log') return stackOpts;
- }
-
- module.exports = {
- calc: calc,
- calcMarkerSize: calcMarkerSize,
- calcAxisExpansion: calcAxisExpansion,
- setFirstScatter: setFirstScatter,
- getStackOpts: getStackOpts
- };
-
- },{"../../constants/numerical":149,"../../lib":168,"../../plots/cartesian/axes":212,"./arrays_to_calcdata":366,"./calc_selection":369,"./colorscale_calc":370,"./subtypes":391,"fast-isnumeric":18}],369:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- module.exports = function calcSelection(cd, trace) {
- if(Lib.isArrayOrTypedArray(trace.selectedpoints)) {
- Lib.tagSelected(cd, trace);
- }
- };
-
- },{"../../lib":168}],370:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
- var calcColorscale = _dereq_('../../components/colorscale/calc');
-
- var subTypes = _dereq_('./subtypes');
-
- module.exports = function calcMarkerColorscale(gd, trace) {
- if(subTypes.hasLines(trace) && hasColorscale(trace, 'line')) {
- calcColorscale(gd, trace, {
- vals: trace.line.color,
- containerStr: 'line',
- cLetter: 'c'
- });
- }
-
- if(subTypes.hasMarkers(trace)) {
- if(hasColorscale(trace, 'marker')) {
- calcColorscale(gd, trace, {
- vals: trace.marker.color,
- containerStr: 'marker',
- cLetter: 'c'
- });
- }
- if(hasColorscale(trace, 'marker.line')) {
- calcColorscale(gd, trace, {
- vals: trace.marker.line.color,
- containerStr: 'marker.line',
- cLetter: 'c'
- });
- }
- }
- };
-
- },{"../../components/colorscale/calc":59,"../../components/colorscale/helpers":62,"./subtypes":391}],371:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- module.exports = {
- PTS_LINESONLY: 20,
-
- // fixed parameters of clustering and clipping algorithms
-
- // fraction of clustering tolerance "so close we don't even consider it a new point"
- minTolerance: 0.2,
- // how fast does clustering tolerance increase as you get away from the visible region
- toleranceGrowth: 10,
-
- // number of viewport sizes away from the visible region
- // at which we clip all lines to the perimeter
- maxScreensAway: 20,
-
- eventDataKeys: []
- };
-
- },{}],372:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var calc = _dereq_('./calc');
-
- /*
- * Scatter stacking & normalization calculations
- * runs per subplot, and can handle multiple stacking groups
- */
-
- module.exports = function crossTraceCalc(gd, plotinfo) {
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
- var subplot = xa._id + ya._id;
-
- var subplotStackOpts = gd._fullLayout._scatterStackOpts[subplot];
- if(!subplotStackOpts) return;
-
- var calcTraces = gd.calcdata;
-
- var i, j, k, i2, cd, cd0, posj, sumj, norm;
- var groupOpts, interpolate, groupnorm, posAttr, valAttr;
- var hasAnyBlanks;
-
- for(var stackGroup in subplotStackOpts) {
- groupOpts = subplotStackOpts[stackGroup];
- var indices = groupOpts.traceIndices;
-
- // can get here with no indices if the stack axis is non-numeric
- if(!indices.length) continue;
-
- interpolate = groupOpts.stackgaps === 'interpolate';
- groupnorm = groupOpts.groupnorm;
- if(groupOpts.orientation === 'v') {
- posAttr = 'x';
- valAttr = 'y';
- }
- else {
- posAttr = 'y';
- valAttr = 'x';
- }
- hasAnyBlanks = new Array(indices.length);
- for(i = 0; i < hasAnyBlanks.length; i++) {
- hasAnyBlanks[i] = false;
- }
-
- // Collect the complete set of all positions across ALL traces.
- // Start with the first trace, then interleave items from later traces
- // as needed.
- // Fill in mising items as we go.
- cd0 = calcTraces[indices[0]];
- var allPositions = new Array(cd0.length);
- for(i = 0; i < cd0.length; i++) {
- allPositions[i] = cd0[i][posAttr];
- }
-
- for(i = 1; i < indices.length; i++) {
- cd = calcTraces[indices[i]];
-
- for(j = k = 0; j < cd.length; j++) {
- posj = cd[j][posAttr];
- for(; posj > allPositions[k] && k < allPositions.length; k++) {
- // the current trace is missing a position from some previous trace(s)
- insertBlank(cd, j, allPositions[k], i, hasAnyBlanks, interpolate, posAttr);
- j++;
- }
- if(posj !== allPositions[k]) {
- // previous trace(s) are missing a position from the current trace
- for(i2 = 0; i2 < i; i2++) {
- insertBlank(calcTraces[indices[i2]], k, posj, i2, hasAnyBlanks, interpolate, posAttr);
- }
- allPositions.splice(k, 0, posj);
- }
- k++;
- }
- for(; k < allPositions.length; k++) {
- insertBlank(cd, j, allPositions[k], i, hasAnyBlanks, interpolate, posAttr);
- j++;
- }
- }
-
- var serieslen = allPositions.length;
-
- // stack (and normalize)!
- for(j = 0; j < cd0.length; j++) {
- sumj = cd0[j][valAttr] = cd0[j].s;
- for(i = 1; i < indices.length; i++) {
- cd = calcTraces[indices[i]];
- cd[0].trace._rawLength = cd[0].trace._length;
- cd[0].trace._length = serieslen;
- sumj += cd[j].s;
- cd[j][valAttr] = sumj;
- }
-
- if(groupnorm) {
- norm = ((groupnorm === 'fraction') ? sumj : (sumj / 100)) || 1;
- for(i = 0; i < indices.length; i++) {
- var cdj = calcTraces[indices[i]][j];
- cdj[valAttr] /= norm;
- cdj.sNorm = cdj.s / norm;
- }
- }
- }
-
- // autorange
- for(i = 0; i < indices.length; i++) {
- cd = calcTraces[indices[i]];
- var trace = cd[0].trace;
- var ppad = calc.calcMarkerSize(trace, trace._rawLength);
- var arrayPad = Array.isArray(ppad);
- if((ppad && hasAnyBlanks[i]) || arrayPad) {
- var ppadRaw = ppad;
- ppad = new Array(serieslen);
- for(j = 0; j < serieslen; j++) {
- ppad[j] = cd[j].gap ? 0 : (arrayPad ? ppadRaw[cd[j].i] : ppadRaw);
- }
- }
- var x = new Array(serieslen);
- var y = new Array(serieslen);
- for(j = 0; j < serieslen; j++) {
- x[j] = cd[j].x;
- y[j] = cd[j].y;
- }
- calc.calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
-
- // while we're here (in a loop over all traces in the stack)
- // record the orientation, so hover can find it easily
- cd[0].t.orientation = groupOpts.orientation;
- }
- }
- };
-
- function insertBlank(calcTrace, index, position, traceIndex, hasAnyBlanks, interpolate, posAttr) {
- hasAnyBlanks[traceIndex] = true;
- var newEntry = {
- i: null,
- gap: true,
- s: 0
- };
- newEntry[posAttr] = position;
- calcTrace.splice(index, 0, newEntry);
- // Even if we're not interpolating, if one trace has multiple
- // values at the same position and this trace only has one value there,
- // we just duplicate that one value rather than insert a zero.
- // We also make it look like a real point - because it's ambiguous which
- // one really is the real one!
- if(index && position === calcTrace[index - 1][posAttr]) {
- var prevEntry = calcTrace[index - 1];
- newEntry.s = prevEntry.s;
- // TODO is it going to cause any problems to have multiple
- // calcdata points with the same index?
- newEntry.i = prevEntry.i;
- newEntry.gap = prevEntry.gap;
- }
- else if(interpolate) {
- newEntry.s = getInterp(calcTrace, index, position, posAttr);
- }
- if(!index) {
- // t and trace need to stay on the first cd entry
- calcTrace[0].t = calcTrace[1].t;
- calcTrace[0].trace = calcTrace[1].trace;
- delete calcTrace[1].t;
- delete calcTrace[1].trace;
- }
- }
-
- function getInterp(calcTrace, index, position, posAttr) {
- var pt0 = calcTrace[index - 1];
- var pt1 = calcTrace[index + 1];
- if(!pt1) return pt0.s;
- if(!pt0) return pt1.s;
- return pt0.s + (pt1.s - pt0.s) * (position - pt0[posAttr]) / (pt1[posAttr] - pt0[posAttr]);
- }
-
- },{"./calc":368}],373:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- // remove opacity for any trace that has a fill or is filled to
- module.exports = function crossTraceDefaults(fullData) {
- for(var i = 0; i < fullData.length; i++) {
- var tracei = fullData[i];
- if(tracei.type !== 'scatter') continue;
-
- var filli = tracei.fill;
- if(filli === 'none' || filli === 'toself') continue;
-
- tracei.opacity = undefined;
-
- if(filli === 'tonexty' || filli === 'tonextx') {
- for(var j = i - 1; j >= 0; j--) {
- var tracej = fullData[j];
-
- if((tracej.type === 'scatter') &&
- (tracej.xaxis === tracei.xaxis) &&
- (tracej.yaxis === tracei.yaxis)) {
- tracej.opacity = undefined;
- break;
- }
- }
- }
- }
- };
-
- },{}],374:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Registry = _dereq_('../../registry');
-
- var attributes = _dereq_('./attributes');
- var constants = _dereq_('./constants');
- var subTypes = _dereq_('./subtypes');
- var handleXYDefaults = _dereq_('./xy_defaults');
- var handleStackDefaults = _dereq_('./stack_defaults');
- var handleMarkerDefaults = _dereq_('./marker_defaults');
- var handleLineDefaults = _dereq_('./line_defaults');
- var handleLineShapeDefaults = _dereq_('./line_shape_defaults');
- var handleTextDefaults = _dereq_('./text_defaults');
- var handleFillColorDefaults = _dereq_('./fillcolor_defaults');
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- var len = handleXYDefaults(traceIn, traceOut, layout, coerce);
- if(!len) traceOut.visible = false;
-
- if(!traceOut.visible) return;
-
- var stackGroupOpts = handleStackDefaults(traceIn, traceOut, layout, coerce);
-
- var defaultMode = !stackGroupOpts && (len < constants.PTS_LINESONLY) ?
- 'lines+markers' : 'lines';
- coerce('text');
- coerce('hovertext');
- coerce('mode', defaultMode);
-
- if(subTypes.hasLines(traceOut)) {
- handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
- handleLineShapeDefaults(traceIn, traceOut, coerce);
- coerce('connectgaps');
- coerce('line.simplify');
- }
-
- if(subTypes.hasMarkers(traceOut)) {
- handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true});
- }
-
- if(subTypes.hasText(traceOut)) {
- handleTextDefaults(traceIn, traceOut, layout, coerce);
- }
-
- var dfltHoverOn = [];
-
- if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) {
- coerce('cliponaxis');
- coerce('marker.maxdisplayed');
- dfltHoverOn.push('points');
- }
-
- // It's possible for this default to be changed by a later trace.
- // We handle that case in some hacky code inside handleStackDefaults.
- coerce('fill', stackGroupOpts ? stackGroupOpts.fillDflt : 'none');
- if(traceOut.fill !== 'none') {
- handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce);
- if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce);
- }
-
- var lineColor = (traceOut.line || {}).color;
- var markerColor = (traceOut.marker || {}).color;
-
- if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') {
- dfltHoverOn.push('fills');
- }
- coerce('hoveron', dfltHoverOn.join('+') || 'points');
- if(traceOut.hoveron !== 'fills') coerce('hovertemplate');
- var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
- errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'y'});
- errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'x', inherit: 'y'});
-
- Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
- };
-
- },{"../../lib":168,"../../registry":257,"./attributes":367,"./constants":371,"./fillcolor_defaults":376,"./line_defaults":380,"./line_shape_defaults":382,"./marker_defaults":386,"./stack_defaults":389,"./subtypes":391,"./text_defaults":392,"./xy_defaults":393}],375:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- /** Fill hover 'pointData' container with 'correct' hover text value
- *
- * - If trace hoverinfo contains a 'text' flag and hovertext is not set,
- * the text elements will be seen in the hover labels.
- *
- * - If trace hoverinfo contains a 'text' flag and hovertext is set,
- * hovertext takes precedence over text
- * i.e. the hoverinfo elements will be seen in the hover labels
- *
- * @param {object} calcPt
- * @param {object} trace
- * @param {object || array} contOut (mutated here)
- */
- module.exports = function fillHoverText(calcPt, trace, contOut) {
- var fill = Array.isArray(contOut) ?
- function(v) { contOut.push(v); } :
- function(v) { contOut.text = v; };
-
- var htx = Lib.extractOption(calcPt, trace, 'htx', 'hovertext');
- if(isValid(htx)) return fill(htx);
-
- var tx = Lib.extractOption(calcPt, trace, 'tx', 'text');
- if(isValid(tx)) return fill(tx);
- };
-
- // accept all truthy values and 0 (which gets cast to '0' in the hover labels)
- function isValid(v) {
- return v || v === 0;
- }
-
- },{"../../lib":168}],376:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Color = _dereq_('../../components/color');
- var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
-
- module.exports = function fillColorDefaults(traceIn, traceOut, defaultColor, coerce) {
- var inheritColorFromMarker = false;
-
- if(traceOut.marker) {
- // don't try to inherit a color array
- var markerColor = traceOut.marker.color;
- var markerLineColor = (traceOut.marker.line || {}).color;
-
- if(markerColor && !isArrayOrTypedArray(markerColor)) {
- inheritColorFromMarker = markerColor;
- }
- else if(markerLineColor && !isArrayOrTypedArray(markerLineColor)) {
- inheritColorFromMarker = markerLineColor;
- }
- }
-
- coerce('fillcolor', Color.addOpacity(
- (traceOut.line || {}).color ||
- inheritColorFromMarker ||
- defaultColor, 0.5
- ));
- };
-
- },{"../../components/color":51,"../../lib":168}],377:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Color = _dereq_('../../components/color');
- var subtypes = _dereq_('./subtypes');
-
-
- module.exports = function getTraceColor(trace, di) {
- var lc, tc;
-
- // TODO: text modes
-
- if(trace.mode === 'lines') {
- lc = trace.line.color;
- return (lc && Color.opacity(lc)) ?
- lc : trace.fillcolor;
- }
- else if(trace.mode === 'none') {
- return trace.fill ? trace.fillcolor : '';
- }
- else {
- var mc = di.mcc || (trace.marker || {}).color;
- var mlc = di.mlcc || ((trace.marker || {}).line || {}).color;
-
- tc = (mc && Color.opacity(mc)) ? mc :
- (mlc && Color.opacity(mlc) &&
- (di.mlw || ((trace.marker || {}).line || {}).width)) ? mlc : '';
-
- if(tc) {
- // make sure the points aren't TOO transparent
- if(Color.opacity(tc) < 0.3) {
- return Color.addOpacity(tc, 0.3);
- }
- else return tc;
- }
- else {
- lc = (trace.line || {}).color;
- return (lc && Color.opacity(lc) &&
- subtypes.hasLines(trace) && trace.line.width) ?
- lc : trace.fillcolor;
- }
- }
- };
-
- },{"../../components/color":51,"./subtypes":391}],378:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Fx = _dereq_('../../components/fx');
- var Registry = _dereq_('../../registry');
- var getTraceColor = _dereq_('./get_trace_color');
- var Color = _dereq_('../../components/color');
- var fillHoverText = _dereq_('./fill_hover_text');
-
- module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
- var cd = pointData.cd;
- var trace = cd[0].trace;
- var xa = pointData.xa;
- var ya = pointData.ya;
- var xpx = xa.c2p(xval);
- var ypx = ya.c2p(yval);
- var pt = [xpx, ypx];
- var hoveron = trace.hoveron || '';
- var minRad = (trace.mode.indexOf('markers') !== -1) ? 3 : 0.5;
-
- // look for points to hover on first, then take fills only if we
- // didn't find a point
- if(hoveron.indexOf('points') !== -1) {
- var dx = function(di) {
- // dx and dy are used in compare modes - here we want to always
- // prioritize the closest data point, at least as long as markers are
- // the same size or nonexistent, but still try to prioritize small markers too.
- var rad = Math.max(3, di.mrc || 0);
- var kink = 1 - 1 / rad;
- var dxRaw = Math.abs(xa.c2p(di.x) - xpx);
- var d = (dxRaw < rad) ? (kink * dxRaw / rad) : (dxRaw - rad + kink);
- return d;
- };
- var dy = function(di) {
- var rad = Math.max(3, di.mrc || 0);
- var kink = 1 - 1 / rad;
- var dyRaw = Math.abs(ya.c2p(di.y) - ypx);
- return (dyRaw < rad) ? (kink * dyRaw / rad) : (dyRaw - rad + kink);
- };
- var dxy = function(di) {
- // scatter points: d.mrc is the calculated marker radius
- // adjust the distance so if you're inside the marker it
- // always will show up regardless of point size, but
- // prioritize smaller points
- var rad = Math.max(minRad, di.mrc || 0);
- var dx = xa.c2p(di.x) - xpx;
- var dy = ya.c2p(di.y) - ypx;
- return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - minRad / rad);
- };
- var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy);
-
- Fx.getClosest(cd, distfn, pointData);
-
- // skip the rest (for this trace) if we didn't find a close point
- if(pointData.index !== false) {
-
- // the closest data point
- var di = cd[pointData.index];
- var xc = xa.c2p(di.x, true);
- var yc = ya.c2p(di.y, true);
- var rad = di.mrc || 1;
-
- // now we're done using the whole `calcdata` array, replace the
- // index with the original index (in case of inserted point from
- // stacked area)
- pointData.index = di.i;
-
- var orientation = cd[0].t.orientation;
- // TODO: for scatter and bar, option to show (sub)totals and
- // raw data? Currently stacked and/or normalized bars just show
- // the normalized individual sizes, so that's what I'm doing here
- // for now.
- var sizeVal = orientation && (di.sNorm || di.s);
- var xLabelVal = (orientation === 'h') ? sizeVal : di.x;
- var yLabelVal = (orientation === 'v') ? sizeVal : di.y;
-
- Lib.extendFlat(pointData, {
- color: getTraceColor(trace, di),
-
- x0: xc - rad,
- x1: xc + rad,
- xLabelVal: xLabelVal,
-
- y0: yc - rad,
- y1: yc + rad,
- yLabelVal: yLabelVal,
-
- spikeDistance: dxy(di),
- hovertemplate: trace.hovertemplate
- });
-
- fillHoverText(di, trace, pointData);
- Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, pointData);
-
- return [pointData];
- }
- }
-
- // even if hoveron is 'fills', only use it if we have polygons too
- if(hoveron.indexOf('fills') !== -1 && trace._polygons) {
- var polygons = trace._polygons;
- var polygonsIn = [];
- var inside = false;
- var xmin = Infinity;
- var xmax = -Infinity;
- var ymin = Infinity;
- var ymax = -Infinity;
-
- var i, j, polygon, pts, xCross, x0, x1, y0, y1;
-
- for(i = 0; i < polygons.length; i++) {
- polygon = polygons[i];
- // TODO: this is not going to work right for curved edges, it will
- // act as though they're straight. That's probably going to need
- // the elements themselves to capture the events. Worth it?
- if(polygon.contains(pt)) {
- inside = !inside;
- // TODO: need better than just the overall bounding box
- polygonsIn.push(polygon);
- ymin = Math.min(ymin, polygon.ymin);
- ymax = Math.max(ymax, polygon.ymax);
- }
- }
-
- if(inside) {
- // constrain ymin/max to the visible plot, so the label goes
- // at the middle of the piece you can see
- ymin = Math.max(ymin, 0);
- ymax = Math.min(ymax, ya._length);
-
- // find the overall left-most and right-most points of the
- // polygon(s) we're inside at their combined vertical midpoint.
- // This is where we will draw the hover label.
- // Note that this might not be the vertical midpoint of the
- // whole trace, if it's disjoint.
- var yAvg = (ymin + ymax) / 2;
- for(i = 0; i < polygonsIn.length; i++) {
- pts = polygonsIn[i].pts;
- for(j = 1; j < pts.length; j++) {
- y0 = pts[j - 1][1];
- y1 = pts[j][1];
- if((y0 > yAvg) !== (y1 >= yAvg)) {
- x0 = pts[j - 1][0];
- x1 = pts[j][0];
- if(y1 - y0) {
- xCross = x0 + (x1 - x0) * (yAvg - y0) / (y1 - y0);
- xmin = Math.min(xmin, xCross);
- xmax = Math.max(xmax, xCross);
- }
- }
- }
- }
-
- // constrain xmin/max to the visible plot now too
- xmin = Math.max(xmin, 0);
- xmax = Math.min(xmax, xa._length);
-
- // get only fill or line color for the hover color
- var color = Color.defaultLine;
- if(Color.opacity(trace.fillcolor)) color = trace.fillcolor;
- else if(Color.opacity((trace.line || {}).color)) {
- color = trace.line.color;
- }
-
- Lib.extendFlat(pointData, {
- // never let a 2D override 1D type as closest point
- // also: no spikeDistance, it's not allowed for fills
- distance: pointData.maxHoverDistance,
- x0: xmin,
- x1: xmax,
- y0: yAvg,
- y1: yAvg,
- color: color,
- hovertemplate: '%{name}'
- });
-
- delete pointData.index;
-
- if(trace.text && !Array.isArray(trace.text)) {
- pointData.text = String(trace.text);
- }
- else pointData.text = trace.name;
-
- return [pointData];
- }
- }
- };
-
- },{"../../components/color":51,"../../components/fx":90,"../../lib":168,"../../registry":257,"./fill_hover_text":375,"./get_trace_color":377}],379:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Scatter = {};
-
- var subtypes = _dereq_('./subtypes');
- Scatter.hasLines = subtypes.hasLines;
- Scatter.hasMarkers = subtypes.hasMarkers;
- Scatter.hasText = subtypes.hasText;
- Scatter.isBubble = subtypes.isBubble;
-
- Scatter.attributes = _dereq_('./attributes');
- Scatter.supplyDefaults = _dereq_('./defaults');
- Scatter.crossTraceDefaults = _dereq_('./cross_trace_defaults');
- Scatter.calc = _dereq_('./calc').calc;
- Scatter.crossTraceCalc = _dereq_('./cross_trace_calc');
- Scatter.arraysToCalcdata = _dereq_('./arrays_to_calcdata');
- Scatter.plot = _dereq_('./plot');
- Scatter.colorbar = _dereq_('./marker_colorbar');
- Scatter.style = _dereq_('./style').style;
- Scatter.styleOnSelect = _dereq_('./style').styleOnSelect;
- Scatter.hoverPoints = _dereq_('./hover');
- Scatter.selectPoints = _dereq_('./select');
- Scatter.animatable = true;
-
- Scatter.moduleType = 'trace';
- Scatter.name = 'scatter';
- Scatter.basePlotModule = _dereq_('../../plots/cartesian');
- Scatter.categories = [
- 'cartesian', 'svg', 'symbols', 'errorBarsOK', 'showLegend', 'scatter-like',
- 'zoomScale'
- ];
- Scatter.meta = {
-
- };
-
- module.exports = Scatter;
-
- },{"../../plots/cartesian":224,"./arrays_to_calcdata":366,"./attributes":367,"./calc":368,"./cross_trace_calc":372,"./cross_trace_defaults":373,"./defaults":374,"./hover":378,"./marker_colorbar":385,"./plot":387,"./select":388,"./style":390,"./subtypes":391}],380:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
- var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
- var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
-
- module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) {
- var markerColor = (traceIn.marker || {}).color;
-
- coerce('line.color', defaultColor);
-
- if(hasColorscale(traceIn, 'line')) {
- colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'line.', cLetter: 'c'});
- } else {
- var lineColorDflt = (isArrayOrTypedArray(markerColor) ? false : markerColor) || defaultColor;
- coerce('line.color', lineColorDflt);
- }
-
- coerce('line.width');
- if(!(opts || {}).noDash) coerce('line.dash');
- };
-
- },{"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"../../lib":168}],381:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var numConstants = _dereq_('../../constants/numerical');
- var BADNUM = numConstants.BADNUM;
- var LOG_CLIP = numConstants.LOG_CLIP;
- var LOG_CLIP_PLUS = LOG_CLIP + 0.5;
- var LOG_CLIP_MINUS = LOG_CLIP - 0.5;
- var Lib = _dereq_('../../lib');
- var segmentsIntersect = Lib.segmentsIntersect;
- var constrain = Lib.constrain;
- var constants = _dereq_('./constants');
-
-
- module.exports = function linePoints(d, opts) {
- var xa = opts.xaxis;
- var ya = opts.yaxis;
- var xLog = xa.type === 'log';
- var yLog = ya.type === 'log';
- var xLen = xa._length;
- var yLen = ya._length;
- var connectGaps = opts.connectGaps;
- var baseTolerance = opts.baseTolerance;
- var shape = opts.shape;
- var linear = shape === 'linear';
- var segments = [];
- var minTolerance = constants.minTolerance;
- var pts = new Array(d.length);
- var pti = 0;
-
- var i;
-
- // pt variables are pixel coordinates [x,y] of one point
- // these four are the outputs of clustering on a line
- var clusterStartPt, clusterEndPt, clusterHighPt, clusterLowPt;
-
- // "this" is the next point we're considering adding to the cluster
- var thisPt;
-
- // did we encounter the high point first, then a low point, or vice versa?
- var clusterHighFirst;
-
- // the first two points in the cluster determine its unit vector
- // so the second is always in the "High" direction
- var clusterUnitVector;
-
- // the pixel delta from clusterStartPt
- var thisVector;
-
- // val variables are (signed) pixel distances along the cluster vector
- var clusterRefDist, clusterHighVal, clusterLowVal, thisVal;
-
- // deviation variables are (signed) pixel distances normal to the cluster vector
- var clusterMinDeviation, clusterMaxDeviation, thisDeviation;
-
- // turn one calcdata point into pixel coordinates
- function getPt(index) {
- var di = d[index];
- if(!di) return false;
- var x = xa.c2p(di.x);
- var y = ya.c2p(di.y);
-
- // if non-positive log values, set them VERY far off-screen
- // so the line looks essentially straight from the previous point.
- if(x === BADNUM) {
- if(xLog) x = xa.c2p(di.x, true);
- if(x === BADNUM) return false;
- // If BOTH were bad log values, make the line follow a constant
- // exponent rather than a constant slope
- if(yLog && y === BADNUM) {
- x *= Math.abs(xa._m * yLen * (xa._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS) /
- (ya._m * xLen * (ya._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS)));
- }
- x *= 1000;
- }
- if(y === BADNUM) {
- if(yLog) y = ya.c2p(di.y, true);
- if(y === BADNUM) return false;
- y *= 1000;
- }
- return [x, y];
- }
-
- function crossesViewport(xFrac0, yFrac0, xFrac1, yFrac1) {
- var dx = xFrac1 - xFrac0;
- var dy = yFrac1 - yFrac0;
- var dx0 = 0.5 - xFrac0;
- var dy0 = 0.5 - yFrac0;
- var norm2 = dx * dx + dy * dy;
- var dot = dx * dx0 + dy * dy0;
- if(dot > 0 && dot < norm2) {
- var cross = dx0 * dy - dy0 * dx;
- if(cross * cross < norm2) return true;
- }
- }
-
- var latestXFrac, latestYFrac;
- // if we're off-screen, increase tolerance over baseTolerance
- function getTolerance(pt, nextPt) {
- var xFrac = pt[0] / xLen;
- var yFrac = pt[1] / yLen;
- var offScreenFraction = Math.max(0, -xFrac, xFrac - 1, -yFrac, yFrac - 1);
- if(offScreenFraction && (latestXFrac !== undefined) &&
- crossesViewport(xFrac, yFrac, latestXFrac, latestYFrac)
- ) {
- offScreenFraction = 0;
- }
- if(offScreenFraction && nextPt &&
- crossesViewport(xFrac, yFrac, nextPt[0] / xLen, nextPt[1] / yLen)
- ) {
- offScreenFraction = 0;
- }
-
- return (1 + constants.toleranceGrowth * offScreenFraction) * baseTolerance;
- }
-
- function ptDist(pt1, pt2) {
- var dx = pt1[0] - pt2[0];
- var dy = pt1[1] - pt2[1];
- return Math.sqrt(dx * dx + dy * dy);
- }
-
- // last bit of filtering: clip paths that are VERY far off-screen
- // so we don't get near the browser's hard limit (+/- 2^29 px in Chrome and FF)
-
- var maxScreensAway = constants.maxScreensAway;
-
- // find the intersections between the segment from pt1 to pt2
- // and the large rectangle maxScreensAway around the viewport
- // if one of pt1 and pt2 is inside and the other outside, there
- // will be only one intersection.
- // if both are outside there will be 0 or 2 intersections
- // (or 1 if it's right at a corner - we'll treat that like 0)
- // returns an array of intersection pts
- var xEdge0 = -xLen * maxScreensAway;
- var xEdge1 = xLen * (1 + maxScreensAway);
- var yEdge0 = -yLen * maxScreensAway;
- var yEdge1 = yLen * (1 + maxScreensAway);
- var edges = [
- [xEdge0, yEdge0, xEdge1, yEdge0],
- [xEdge1, yEdge0, xEdge1, yEdge1],
- [xEdge1, yEdge1, xEdge0, yEdge1],
- [xEdge0, yEdge1, xEdge0, yEdge0]
- ];
- var xEdge, yEdge, lastXEdge, lastYEdge, lastFarPt, edgePt;
-
- // for linear line shape, edge intersections should be linearly interpolated
- // spline uses this too, which isn't precisely correct but is actually pretty
- // good, because Catmull-Rom weights far-away points less in creating the curvature
- function getLinearEdgeIntersections(pt1, pt2) {
- var out = [];
- var ptCount = 0;
- for(var i = 0; i < 4; i++) {
- var edge = edges[i];
- var ptInt = segmentsIntersect(pt1[0], pt1[1], pt2[0], pt2[1],
- edge[0], edge[1], edge[2], edge[3]);
- if(ptInt && (!ptCount ||
- Math.abs(ptInt.x - out[0][0]) > 1 ||
- Math.abs(ptInt.y - out[0][1]) > 1
- )) {
- ptInt = [ptInt.x, ptInt.y];
- // if we have 2 intersections, make sure the closest one to pt1 comes first
- if(ptCount && ptDist(ptInt, pt1) < ptDist(out[0], pt1)) out.unshift(ptInt);
- else out.push(ptInt);
- ptCount++;
- }
- }
- return out;
- }
-
- function onlyConstrainedPoint(pt) {
- if(pt[0] < xEdge0 || pt[0] > xEdge1 || pt[1] < yEdge0 || pt[1] > yEdge1) {
- return [constrain(pt[0], xEdge0, xEdge1), constrain(pt[1], yEdge0, yEdge1)];
- }
- }
-
- function sameEdge(pt1, pt2) {
- if(pt1[0] === pt2[0] && (pt1[0] === xEdge0 || pt1[0] === xEdge1)) return true;
- if(pt1[1] === pt2[1] && (pt1[1] === yEdge0 || pt1[1] === yEdge1)) return true;
- }
-
- // for line shapes hv and vh, movement in the two dimensions is decoupled,
- // so all we need to do is constrain each dimension independently
- function getHVEdgeIntersections(pt1, pt2) {
- var out = [];
- var ptInt1 = onlyConstrainedPoint(pt1);
- var ptInt2 = onlyConstrainedPoint(pt2);
- if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out;
-
- if(ptInt1) out.push(ptInt1);
- if(ptInt2) out.push(ptInt2);
- return out;
- }
-
- // hvh and vhv we sometimes have to move one of the intersection points
- // out BEYOND the clipping rect, by a maximum of a factor of 2, so that
- // the midpoint line is drawn in the right place
- function getABAEdgeIntersections(dim, limit0, limit1) {
- return function(pt1, pt2) {
- var ptInt1 = onlyConstrainedPoint(pt1);
- var ptInt2 = onlyConstrainedPoint(pt2);
-
- var out = [];
- if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out;
-
- if(ptInt1) out.push(ptInt1);
- if(ptInt2) out.push(ptInt2);
-
- var midShift = 2 * Lib.constrain((pt1[dim] + pt2[dim]) / 2, limit0, limit1) -
- ((ptInt1 || pt1)[dim] + (ptInt2 || pt2)[dim]);
- if(midShift) {
- var ptToAlter;
- if(ptInt1 && ptInt2) {
- ptToAlter = (midShift > 0 === ptInt1[dim] > ptInt2[dim]) ? ptInt1 : ptInt2;
- }
- else ptToAlter = ptInt1 || ptInt2;
-
- ptToAlter[dim] += midShift;
- }
-
- return out;
- };
- }
-
- var getEdgeIntersections;
- if(shape === 'linear' || shape === 'spline') {
- getEdgeIntersections = getLinearEdgeIntersections;
- }
- else if(shape === 'hv' || shape === 'vh') {
- getEdgeIntersections = getHVEdgeIntersections;
- }
- else if(shape === 'hvh') getEdgeIntersections = getABAEdgeIntersections(0, xEdge0, xEdge1);
- else if(shape === 'vhv') getEdgeIntersections = getABAEdgeIntersections(1, yEdge0, yEdge1);
-
- // a segment pt1->pt2 entirely outside the nearby region:
- // find the corner it gets closest to touching
- function getClosestCorner(pt1, pt2) {
- var dx = pt2[0] - pt1[0];
- var m = (pt2[1] - pt1[1]) / dx;
- var b = (pt1[1] * pt2[0] - pt2[1] * pt1[0]) / dx;
-
- if(b > 0) return [m > 0 ? xEdge0 : xEdge1, yEdge1];
- else return [m > 0 ? xEdge1 : xEdge0, yEdge0];
- }
-
- function updateEdge(pt) {
- var x = pt[0];
- var y = pt[1];
- var xSame = x === pts[pti - 1][0];
- var ySame = y === pts[pti - 1][1];
- // duplicate point?
- if(xSame && ySame) return;
- if(pti > 1) {
- // backtracking along an edge?
- var xSame2 = x === pts[pti - 2][0];
- var ySame2 = y === pts[pti - 2][1];
- if(xSame && (x === xEdge0 || x === xEdge1) && xSame2) {
- if(ySame2) pti--; // backtracking exactly - drop prev pt and don't add
- else pts[pti - 1] = pt; // not exact: replace the prev pt
- }
- else if(ySame && (y === yEdge0 || y === yEdge1) && ySame2) {
- if(xSame2) pti--;
- else pts[pti - 1] = pt;
- }
- else pts[pti++] = pt;
- }
- else pts[pti++] = pt;
- }
-
- function updateEdgesForReentry(pt) {
- // if we're outside the nearby region and going back in,
- // we may need to loop around a corner point
- if(pts[pti - 1][0] !== pt[0] && pts[pti - 1][1] !== pt[1]) {
- updateEdge([lastXEdge, lastYEdge]);
- }
- updateEdge(pt);
- lastFarPt = null;
- lastXEdge = lastYEdge = 0;
- }
-
- function addPt(pt) {
- latestXFrac = pt[0] / xLen;
- latestYFrac = pt[1] / yLen;
- // Are we more than maxScreensAway off-screen any direction?
- // if so, clip to this box, but in such a way that on-screen
- // drawing is unchanged
- xEdge = (pt[0] < xEdge0) ? xEdge0 : (pt[0] > xEdge1) ? xEdge1 : 0;
- yEdge = (pt[1] < yEdge0) ? yEdge0 : (pt[1] > yEdge1) ? yEdge1 : 0;
- if(xEdge || yEdge) {
- // to get fills right - if first point is far, push it toward the
- // screen in whichever direction(s) are far
- if(!pti) {
- pts[pti++] = [xEdge || pt[0], yEdge || pt[1]];
- }
- else if(lastFarPt) {
- // both this point and the last are outside the nearby region
- // check if we're crossing the nearby region
- var intersections = getEdgeIntersections(lastFarPt, pt);
- if(intersections.length > 1) {
- updateEdgesForReentry(intersections[0]);
- pts[pti++] = intersections[1];
- }
- }
- // we're leaving the nearby region - add the point where we left it
- else {
- edgePt = getEdgeIntersections(pts[pti - 1], pt)[0];
- pts[pti++] = edgePt;
- }
-
- var lastPt = pts[pti - 1];
- if(xEdge && yEdge && (lastPt[0] !== xEdge || lastPt[1] !== yEdge)) {
- // we've gone out beyond a new corner: add the corner too
- // so that the next point will take the right winding
- if(lastFarPt) {
- if(lastXEdge !== xEdge && lastYEdge !== yEdge) {
- if(lastXEdge && lastYEdge) {
- // we've gone around to an opposite corner - we
- // need to add the correct extra corner
- // in order to get the right winding
- updateEdge(getClosestCorner(lastFarPt, pt));
- }
- else {
- // we're coming from a far edge - the extra corner
- // we need is determined uniquely by the sectors
- updateEdge([lastXEdge || xEdge, lastYEdge || yEdge]);
- }
- }
- else if(lastXEdge && lastYEdge) {
- updateEdge([lastXEdge, lastYEdge]);
- }
- }
- updateEdge([xEdge, yEdge]);
- }
- else if((lastXEdge - xEdge) && (lastYEdge - yEdge)) {
- // we're coming from an edge or far corner to an edge - again the
- // extra corner we need is uniquely determined by the sectors
- updateEdge([xEdge || lastXEdge, yEdge || lastYEdge]);
- }
- lastFarPt = pt;
- lastXEdge = xEdge;
- lastYEdge = yEdge;
- }
- else {
- if(lastFarPt) {
- // this point is in range but the previous wasn't: add its entry pt first
- updateEdgesForReentry(getEdgeIntersections(lastFarPt, pt)[0]);
- }
-
- pts[pti++] = pt;
- }
- }
-
- // loop over ALL points in this trace
- for(i = 0; i < d.length; i++) {
- clusterStartPt = getPt(i);
- if(!clusterStartPt) continue;
-
- pti = 0;
- lastFarPt = null;
- addPt(clusterStartPt);
-
- // loop over one segment of the trace
- for(i++; i < d.length; i++) {
- clusterHighPt = getPt(i);
- if(!clusterHighPt) {
- if(connectGaps) continue;
- else break;
- }
-
- // can't decimate if nonlinear line shape
- // TODO: we *could* decimate [hv]{2,3} shapes if we restricted clusters to horz or vert again
- // but spline would be verrry awkward to decimate
- if(!linear || !opts.simplify) {
- addPt(clusterHighPt);
- continue;
- }
-
- var nextPt = getPt(i + 1);
-
- clusterRefDist = ptDist(clusterHighPt, clusterStartPt);
-
- if(clusterRefDist < getTolerance(clusterHighPt, nextPt) * minTolerance) continue;
-
- clusterUnitVector = [
- (clusterHighPt[0] - clusterStartPt[0]) / clusterRefDist,
- (clusterHighPt[1] - clusterStartPt[1]) / clusterRefDist
- ];
-
- clusterLowPt = clusterStartPt;
- clusterHighVal = clusterRefDist;
- clusterLowVal = clusterMinDeviation = clusterMaxDeviation = 0;
- clusterHighFirst = false;
- clusterEndPt = clusterHighPt;
-
- // loop over one cluster of points that collapse onto one line
- for(i++; i < d.length; i++) {
- thisPt = nextPt;
- nextPt = getPt(i + 1);
- if(!thisPt) {
- if(connectGaps) continue;
- else break;
- }
- thisVector = [
- thisPt[0] - clusterStartPt[0],
- thisPt[1] - clusterStartPt[1]
- ];
- // cross product (or dot with normal to the cluster vector)
- thisDeviation = thisVector[0] * clusterUnitVector[1] - thisVector[1] * clusterUnitVector[0];
- clusterMinDeviation = Math.min(clusterMinDeviation, thisDeviation);
- clusterMaxDeviation = Math.max(clusterMaxDeviation, thisDeviation);
-
- if(clusterMaxDeviation - clusterMinDeviation > getTolerance(thisPt, nextPt)) break;
-
- clusterEndPt = thisPt;
- thisVal = thisVector[0] * clusterUnitVector[0] + thisVector[1] * clusterUnitVector[1];
-
- if(thisVal > clusterHighVal) {
- clusterHighVal = thisVal;
- clusterHighPt = thisPt;
- clusterHighFirst = false;
- } else if(thisVal < clusterLowVal) {
- clusterLowVal = thisVal;
- clusterLowPt = thisPt;
- clusterHighFirst = true;
- }
- }
-
- // insert this cluster into pts
- // we've already inserted the start pt, now check if we have high and low pts
- if(clusterHighFirst) {
- addPt(clusterHighPt);
- if(clusterEndPt !== clusterLowPt) addPt(clusterLowPt);
- } else {
- if(clusterLowPt !== clusterStartPt) addPt(clusterLowPt);
- if(clusterEndPt !== clusterHighPt) addPt(clusterHighPt);
- }
- // and finally insert the end pt
- addPt(clusterEndPt);
-
- // have we reached the end of this segment?
- if(i >= d.length || !thisPt) break;
-
- // otherwise we have an out-of-cluster point to insert as next clusterStartPt
- addPt(thisPt);
- clusterStartPt = thisPt;
- }
-
- // to get fills right - repeat what we did at the start
- if(lastFarPt) updateEdge([lastXEdge || lastFarPt[0], lastYEdge || lastFarPt[1]]);
-
- segments.push(pts.slice(0, pti));
- }
-
- return segments;
- };
-
- },{"../../constants/numerical":149,"../../lib":168,"./constants":371}],382:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
-
- // common to 'scatter' and 'scatterternary'
- module.exports = function handleLineShapeDefaults(traceIn, traceOut, coerce) {
- var shape = coerce('line.shape');
- if(shape === 'spline') coerce('line.smoothing');
- };
-
- },{}],383:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var LINKEDFILLS = {tonextx: 1, tonexty: 1, tonext: 1};
-
- module.exports = function linkTraces(gd, plotinfo, cdscatter) {
- var trace, i, group, prevtrace, groupIndex;
-
- // first sort traces to keep stacks & filled-together groups together
- var groupIndices = {};
- var needsSort = false;
- var prevGroupIndex = -1;
- var nextGroupIndex = 0;
- var prevUnstackedGroupIndex = -1;
- for(i = 0; i < cdscatter.length; i++) {
- trace = cdscatter[i][0].trace;
- group = trace.stackgroup || '';
- if(group) {
- if(group in groupIndices) {
- groupIndex = groupIndices[group];
- }
- else {
- groupIndex = groupIndices[group] = nextGroupIndex;
- nextGroupIndex++;
- }
- }
- else if(trace.fill in LINKEDFILLS && prevUnstackedGroupIndex >= 0) {
- groupIndex = prevUnstackedGroupIndex;
- }
- else {
- groupIndex = prevUnstackedGroupIndex = nextGroupIndex;
- nextGroupIndex++;
- }
-
- if(groupIndex < prevGroupIndex) needsSort = true;
- trace._groupIndex = prevGroupIndex = groupIndex;
- }
-
- var cdscatterSorted = cdscatter.slice();
- if(needsSort) {
- cdscatterSorted.sort(function(a, b) {
- var traceA = a[0].trace;
- var traceB = b[0].trace;
- return (traceA._groupIndex - traceB._groupIndex) ||
- (traceA.index - traceB.index);
- });
- }
-
- // now link traces to each other
- var prevtraces = {};
- for(i = 0; i < cdscatterSorted.length; i++) {
- trace = cdscatterSorted[i][0].trace;
- group = trace.stackgroup || '';
-
- // Note: The check which ensures all cdscatter here are for the same axis and
- // are either cartesian or scatterternary has been removed. This code assumes
- // the passed scattertraces have been filtered to the proper plot types and
- // the proper subplots.
- if(trace.visible === true) {
- trace._nexttrace = null;
-
- if(trace.fill in LINKEDFILLS) {
- prevtrace = prevtraces[group];
- trace._prevtrace = prevtrace || null;
-
- if(prevtrace) {
- prevtrace._nexttrace = trace;
- }
- }
-
- trace._ownfill = (trace.fill && (
- trace.fill.substr(0, 6) === 'tozero' ||
- trace.fill === 'toself' ||
- (trace.fill.substr(0, 2) === 'to' && !trace._prevtrace)
- ));
-
- prevtraces[group] = trace;
- } else {
- trace._prevtrace = trace._nexttrace = trace._ownfill = null;
- }
- }
-
- return cdscatterSorted;
- };
-
- },{}],384:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
-
- // used in the drawing step for 'scatter' and 'scattegeo' and
- // in the convert step for 'scatter3d'
- module.exports = function makeBubbleSizeFn(trace) {
- var marker = trace.marker;
- var sizeRef = marker.sizeref || 1;
- var sizeMin = marker.sizemin || 0;
-
- // for bubble charts, allow scaling the provided value linearly
- // and by area or diameter.
- // Note this only applies to the array-value sizes
-
- var baseFn = (marker.sizemode === 'area') ?
- function(v) { return Math.sqrt(v / sizeRef); } :
- function(v) { return v / sizeRef; };
-
- // TODO add support for position/negative bubbles?
- // TODO add 'sizeoffset' attribute?
- return function(v) {
- var baseSize = baseFn(v / 2);
-
- // don't show non-numeric and negative sizes
- return (isNumeric(baseSize) && (baseSize > 0)) ?
- Math.max(baseSize, sizeMin) :
- 0;
- };
- };
-
- },{"fast-isnumeric":18}],385:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- module.exports = {
- container: 'marker',
- min: 'cmin',
- max: 'cmax'
- };
-
- },{}],386:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Color = _dereq_('../../components/color');
- var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
- var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
-
- var subTypes = _dereq_('./subtypes');
-
- /*
- * opts: object of flags to control features not all marker users support
- * noLine: caller does not support marker lines
- * gradient: caller supports gradients
- * noSelect: caller does not support selected/unselected attribute containers
- */
- module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) {
- var isBubble = subTypes.isBubble(traceIn);
- var lineColor = (traceIn.line || {}).color;
- var defaultMLC;
-
- opts = opts || {};
-
- // marker.color inherit from line.color (even if line.color is an array)
- if(lineColor) defaultColor = lineColor;
-
- coerce('marker.symbol');
- coerce('marker.opacity', isBubble ? 0.7 : 1);
- coerce('marker.size');
-
- coerce('marker.color', defaultColor);
- if(hasColorscale(traceIn, 'marker')) {
- colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'});
- }
-
- if(!opts.noSelect) {
- coerce('selected.marker.color');
- coerce('unselected.marker.color');
- coerce('selected.marker.size');
- coerce('unselected.marker.size');
- }
-
- if(!opts.noLine) {
- // if there's a line with a different color than the marker, use
- // that line color as the default marker line color
- // (except when it's an array)
- // mostly this is for transparent markers to behave nicely
- if(lineColor && !Array.isArray(lineColor) && (traceOut.marker.color !== lineColor)) {
- defaultMLC = lineColor;
- }
- else if(isBubble) defaultMLC = Color.background;
- else defaultMLC = Color.defaultLine;
-
- coerce('marker.line.color', defaultMLC);
- if(hasColorscale(traceIn, 'marker.line')) {
- colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'});
- }
-
- coerce('marker.line.width', isBubble ? 1 : 0);
- }
-
- if(isBubble) {
- coerce('marker.sizeref');
- coerce('marker.sizemin');
- coerce('marker.sizemode');
- }
-
- if(opts.gradient) {
- var gradientType = coerce('marker.gradient.type');
- if(gradientType !== 'none') {
- coerce('marker.gradient.color');
- }
- }
- };
-
- },{"../../components/color":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"./subtypes":391}],387:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
-
- var Registry = _dereq_('../../registry');
- var Lib = _dereq_('../../lib');
- var ensureSingle = Lib.ensureSingle;
- var identity = Lib.identity;
- var Drawing = _dereq_('../../components/drawing');
-
- var subTypes = _dereq_('./subtypes');
- var linePoints = _dereq_('./line_points');
- var linkTraces = _dereq_('./link_traces');
- var polygonTester = _dereq_('../../lib/polygon').tester;
-
- module.exports = function plot(gd, plotinfo, cdscatter, scatterLayer, transitionOpts, makeOnCompleteCallback) {
- var join, onComplete;
-
- // If transition config is provided, then it is only a partial replot and traces not
- // updated are removed.
- var isFullReplot = !transitionOpts;
- var hasTransition = !!transitionOpts && transitionOpts.duration > 0;
-
- // Link traces so the z-order of fill layers is correct
- var cdscatterSorted = linkTraces(gd, plotinfo, cdscatter);
-
- join = scatterLayer.selectAll('g.trace')
- .data(cdscatterSorted, function(d) { return d[0].trace.uid; });
-
- // Append new traces:
- join.enter().append('g')
- .attr('class', function(d) {
- return 'trace scatter trace' + d[0].trace.uid;
- })
- .style('stroke-miterlimit', 2);
- join.order();
-
- createFills(gd, join, plotinfo);
-
- if(hasTransition) {
- if(makeOnCompleteCallback) {
- // If it was passed a callback to register completion, make a callback. If
- // this is created, then it must be executed on completion, otherwise the
- // pos-transition redraw will not execute:
- onComplete = makeOnCompleteCallback();
- }
-
- var transition = d3.transition()
- .duration(transitionOpts.duration)
- .ease(transitionOpts.easing)
- .each('end', function() {
- onComplete && onComplete();
- })
- .each('interrupt', function() {
- onComplete && onComplete();
- });
-
- transition.each(function() {
- // Must run the selection again since otherwise enters/updates get grouped together
- // and these get executed out of order. Except we need them in order!
- scatterLayer.selectAll('g.trace').each(function(d, i) {
- plotOne(gd, i, plotinfo, d, cdscatterSorted, this, transitionOpts);
- });
- });
- } else {
- join.each(function(d, i) {
- plotOne(gd, i, plotinfo, d, cdscatterSorted, this, transitionOpts);
- });
- }
-
- if(isFullReplot) {
- join.exit().remove();
- }
-
- // remove paths that didn't get used
- scatterLayer.selectAll('path:not([d])').remove();
- };
-
- function createFills(gd, traceJoin, plotinfo) {
- traceJoin.each(function(d) {
- var fills = ensureSingle(d3.select(this), 'g', 'fills');
- Drawing.setClipUrl(fills, plotinfo.layerClipId, gd);
-
- var trace = d[0].trace;
-
- var fillData = [];
- if(trace._ownfill) fillData.push('_ownFill');
- if(trace._nexttrace) fillData.push('_nextFill');
-
- var fillJoin = fills.selectAll('g').data(fillData, identity);
-
- fillJoin.enter().append('g');
-
- fillJoin.exit()
- .each(function(d) { trace[d] = null; })
- .remove();
-
- fillJoin.order().each(function(d) {
- // make a path element inside the fill group, just so
- // we can give it its own data later on and the group can
- // keep its simple '_*Fill' data
- trace[d] = ensureSingle(d3.select(this), 'path', 'js-fill');
- });
- });
- }
-
- function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transitionOpts) {
- var i;
-
- // Since this has been reorganized and we're executing this on individual traces,
- // we need to pass it the full list of cdscatter as well as this trace's index (idx)
- // since it does an internal n^2 loop over comparisons with other traces:
- selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll);
-
- var hasTransition = !!transitionOpts && transitionOpts.duration > 0;
-
- function transition(selection) {
- return hasTransition ? selection.transition() : selection;
- }
-
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- var trace = cdscatter[0].trace;
- var line = trace.line;
- var tr = d3.select(element);
-
- var errorBarGroup = ensureSingle(tr, 'g', 'errorbars');
- var lines = ensureSingle(tr, 'g', 'lines');
- var points = ensureSingle(tr, 'g', 'points');
- var text = ensureSingle(tr, 'g', 'text');
-
- // error bars are at the bottom
- Registry.getComponentMethod('errorbars', 'plot')(gd, errorBarGroup, plotinfo, transitionOpts);
-
- if(trace.visible !== true) return;
-
- transition(tr).style('opacity', trace.opacity);
-
- // BUILD LINES AND FILLS
- var ownFillEl3, tonext;
- var ownFillDir = trace.fill.charAt(trace.fill.length - 1);
- if(ownFillDir !== 'x' && ownFillDir !== 'y') ownFillDir = '';
-
- // store node for tweaking by selectPoints
- if(!plotinfo.isRangePlot) cdscatter[0].node3 = tr;
-
- var prevRevpath = '';
- var prevPolygons = [];
- var prevtrace = trace._prevtrace;
-
- if(prevtrace) {
- prevRevpath = prevtrace._prevRevpath || '';
- tonext = prevtrace._nextFill;
- prevPolygons = prevtrace._polygons;
- }
-
- var thispath;
- var thisrevpath;
- // fullpath is all paths for this curve, joined together straight
- // across gaps, for filling
- var fullpath = '';
- // revpath is fullpath reversed, for fill-to-next
- var revpath = '';
- // functions for converting a point array to a path
- var pathfn, revpathbase, revpathfn;
- // variables used before and after the data join
- var pt0, lastSegment, pt1, thisPolygons;
-
- // initialize line join data / method
- var segments = [];
- var makeUpdate = Lib.noop;
-
- ownFillEl3 = trace._ownFill;
-
- if(subTypes.hasLines(trace) || trace.fill !== 'none') {
-
- if(tonext) {
- // This tells .style which trace to use for fill information:
- tonext.datum(cdscatter);
- }
-
- if(['hv', 'vh', 'hvh', 'vhv'].indexOf(line.shape) !== -1) {
- pathfn = Drawing.steps(line.shape);
- revpathbase = Drawing.steps(
- line.shape.split('').reverse().join('')
- );
- }
- else if(line.shape === 'spline') {
- pathfn = revpathbase = function(pts) {
- var pLast = pts[pts.length - 1];
- if(pts.length > 1 && pts[0][0] === pLast[0] && pts[0][1] === pLast[1]) {
- // identical start and end points: treat it as a
- // closed curve so we don't get a kink
- return Drawing.smoothclosed(pts.slice(1), line.smoothing);
- }
- else {
- return Drawing.smoothopen(pts, line.smoothing);
- }
- };
- }
- else {
- pathfn = revpathbase = function(pts) {
- return 'M' + pts.join('L');
- };
- }
-
- revpathfn = function(pts) {
- // note: this is destructive (reverses pts in place) so can't use pts after this
- return revpathbase(pts.reverse());
- };
-
- segments = linePoints(cdscatter, {
- xaxis: xa,
- yaxis: ya,
- connectGaps: trace.connectgaps,
- baseTolerance: Math.max(line.width || 1, 3) / 4,
- shape: line.shape,
- simplify: line.simplify
- });
-
- // since we already have the pixel segments here, use them to make
- // polygons for hover on fill
- // TODO: can we skip this if hoveron!=fills? That would mean we
- // need to redraw when you change hoveron...
- thisPolygons = trace._polygons = new Array(segments.length);
- for(i = 0; i < segments.length; i++) {
- trace._polygons[i] = polygonTester(segments[i]);
- }
-
- if(segments.length) {
- pt0 = segments[0][0];
- lastSegment = segments[segments.length - 1];
- pt1 = lastSegment[lastSegment.length - 1];
- }
-
- makeUpdate = function(isEnter) {
- return function(pts) {
- thispath = pathfn(pts);
- thisrevpath = revpathfn(pts);
- if(!fullpath) {
- fullpath = thispath;
- revpath = thisrevpath;
- }
- else if(ownFillDir) {
- fullpath += 'L' + thispath.substr(1);
- revpath = thisrevpath + ('L' + revpath.substr(1));
- }
- else {
- fullpath += 'Z' + thispath;
- revpath = thisrevpath + 'Z' + revpath;
- }
-
- if(subTypes.hasLines(trace) && pts.length > 1) {
- var el = d3.select(this);
-
- // This makes the coloring work correctly:
- el.datum(cdscatter);
-
- if(isEnter) {
- transition(el.style('opacity', 0)
- .attr('d', thispath)
- .call(Drawing.lineGroupStyle))
- .style('opacity', 1);
- } else {
- var sel = transition(el);
- sel.attr('d', thispath);
- Drawing.singleLineStyle(cdscatter, sel);
- }
- }
- };
- };
- }
-
- var lineJoin = lines.selectAll('.js-line').data(segments);
-
- transition(lineJoin.exit())
- .style('opacity', 0)
- .remove();
-
- lineJoin.each(makeUpdate(false));
-
- lineJoin.enter().append('path')
- .classed('js-line', true)
- .style('vector-effect', 'non-scaling-stroke')
- .call(Drawing.lineGroupStyle)
- .each(makeUpdate(true));
-
- Drawing.setClipUrl(lineJoin, plotinfo.layerClipId, gd);
-
- function clearFill(selection) {
- transition(selection).attr('d', 'M0,0Z');
- }
-
- if(segments.length) {
- if(ownFillEl3) {
- ownFillEl3.datum(cdscatter);
- if(pt0 && pt1) {
- if(ownFillDir) {
- if(ownFillDir === 'y') {
- pt0[1] = pt1[1] = ya.c2p(0, true);
- }
- else if(ownFillDir === 'x') {
- pt0[0] = pt1[0] = xa.c2p(0, true);
- }
-
- // fill to zero: full trace path, plus extension of
- // the endpoints to the appropriate axis
- // For the sake of animations, wrap the points around so that
- // the points on the axes are the first two points. Otherwise
- // animations get a little crazy if the number of points changes.
- transition(ownFillEl3).attr('d', 'M' + pt1 + 'L' + pt0 + 'L' + fullpath.substr(1))
- .call(Drawing.singleFillStyle);
- } else {
- // fill to self: just join the path to itself
- transition(ownFillEl3).attr('d', fullpath + 'Z')
- .call(Drawing.singleFillStyle);
- }
- }
- }
- else if(tonext) {
- if(trace.fill.substr(0, 6) === 'tonext' && fullpath && prevRevpath) {
- // fill to next: full trace path, plus the previous path reversed
- if(trace.fill === 'tonext') {
- // tonext: for use by concentric shapes, like manually constructed
- // contours, we just add the two paths closed on themselves.
- // This makes strange results if one path is *not* entirely
- // inside the other, but then that is a strange usage.
- transition(tonext).attr('d', fullpath + 'Z' + prevRevpath + 'Z')
- .call(Drawing.singleFillStyle);
- }
- else {
- // tonextx/y: for now just connect endpoints with lines. This is
- // the correct behavior if the endpoints are at the same value of
- // y/x, but if they *aren't*, we should ideally do more complicated
- // things depending on whether the new endpoint projects onto the
- // existing curve or off the end of it
- transition(tonext).attr('d', fullpath + 'L' + prevRevpath.substr(1) + 'Z')
- .call(Drawing.singleFillStyle);
- }
- trace._polygons = trace._polygons.concat(prevPolygons);
- }
- else {
- clearFill(tonext);
- trace._polygons = null;
- }
- }
- trace._prevRevpath = revpath;
- trace._prevPolygons = thisPolygons;
- }
- else {
- if(ownFillEl3) clearFill(ownFillEl3);
- else if(tonext) clearFill(tonext);
- trace._polygons = trace._prevRevpath = trace._prevPolygons = null;
- }
-
-
- function visFilter(d) {
- return d.filter(function(v) { return !v.gap && v.vis; });
- }
-
- function visFilterWithGaps(d) {
- return d.filter(function(v) { return v.vis; });
- }
-
- function gapFilter(d) {
- return d.filter(function(v) { return !v.gap; });
- }
-
- function keyFunc(d) {
- return d.id;
- }
-
- // Returns a function if the trace is keyed, otherwise returns undefined
- function getKeyFunc(trace) {
- if(trace.ids) {
- return keyFunc;
- }
- }
-
- function hideFilter() {
- return false;
- }
-
- function makePoints(points, text, cdscatter) {
- var join, selection, hasNode;
-
- var trace = cdscatter[0].trace;
- var showMarkers = subTypes.hasMarkers(trace);
- var showText = subTypes.hasText(trace);
-
- var keyFunc = getKeyFunc(trace);
- var markerFilter = hideFilter;
- var textFilter = hideFilter;
-
- if(showMarkers || showText) {
- var showFilter = identity;
- // if we're stacking, "infer zero" gap mode gets markers in the
- // gap points - because we've inferred a zero there - but other
- // modes (currently "interpolate", later "interrupt" hopefully)
- // we don't draw generated markers
- var stackGroup = trace.stackgroup;
- var isInferZero = stackGroup && (
- gd._fullLayout._scatterStackOpts[xa._id + ya._id][stackGroup].stackgaps === 'infer zero');
- if(trace.marker.maxdisplayed || trace._needsCull) {
- showFilter = isInferZero ? visFilterWithGaps : visFilter;
- }
- else if(stackGroup && !isInferZero) {
- showFilter = gapFilter;
- }
-
- if(showMarkers) markerFilter = showFilter;
- if(showText) textFilter = showFilter;
- }
-
- // marker points
-
- selection = points.selectAll('path.point');
-
- join = selection.data(markerFilter, keyFunc);
-
- var enter = join.enter().append('path')
- .classed('point', true);
-
- if(hasTransition) {
- enter
- .call(Drawing.pointStyle, trace, gd)
- .call(Drawing.translatePoints, xa, ya)
- .style('opacity', 0)
- .transition()
- .style('opacity', 1);
- }
-
- join.order();
-
- var styleFns;
- if(showMarkers) {
- styleFns = Drawing.makePointStyleFns(trace);
- }
-
- join.each(function(d) {
- var el = d3.select(this);
- var sel = transition(el);
- hasNode = Drawing.translatePoint(d, sel, xa, ya);
-
- if(hasNode) {
- Drawing.singlePointStyle(d, sel, trace, styleFns, gd);
-
- if(plotinfo.layerClipId) {
- Drawing.hideOutsideRangePoint(d, sel, xa, ya, trace.xcalendar, trace.ycalendar);
- }
-
- if(trace.customdata) {
- el.classed('plotly-customdata', d.data !== null && d.data !== undefined);
- }
- } else {
- sel.remove();
- }
- });
-
- if(hasTransition) {
- join.exit().transition()
- .style('opacity', 0)
- .remove();
- } else {
- join.exit().remove();
- }
-
- // text points
- selection = text.selectAll('g');
- join = selection.data(textFilter, keyFunc);
-
- // each text needs to go in its own 'g' in case
- // it gets converted to mathjax
- join.enter().append('g').classed('textpoint', true).append('text');
-
- join.order();
-
- join.each(function(d) {
- var g = d3.select(this);
- var sel = transition(g.select('text'));
- hasNode = Drawing.translatePoint(d, sel, xa, ya);
-
- if(hasNode) {
- if(plotinfo.layerClipId) {
- Drawing.hideOutsideRangePoint(d, g, xa, ya, trace.xcalendar, trace.ycalendar);
- }
- } else {
- g.remove();
- }
- });
-
- join.selectAll('text')
- .call(Drawing.textPointStyle, trace, gd)
- .each(function(d) {
- // This just *has* to be totally custom becuase of SVG text positioning :(
- // It's obviously copied from translatePoint; we just can't use that
- var x = xa.c2p(d.x);
- var y = ya.c2p(d.y);
-
- d3.select(this).selectAll('tspan.line').each(function() {
- transition(d3.select(this)).attr({x: x, y: y});
- });
- });
-
- join.exit().remove();
- }
-
- points.datum(cdscatter);
- text.datum(cdscatter);
- makePoints(points, text, cdscatter);
-
- // lastly, clip points groups of `cliponaxis !== false` traces
- // on `plotinfo._hasClipOnAxisFalse === true` subplots
- var hasClipOnAxisFalse = trace.cliponaxis === false;
- var clipUrl = hasClipOnAxisFalse ? null : plotinfo.layerClipId;
- Drawing.setClipUrl(points, clipUrl, gd);
- Drawing.setClipUrl(text, clipUrl, gd);
- }
-
- function selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll) {
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
- var xr = d3.extent(Lib.simpleMap(xa.range, xa.r2c));
- var yr = d3.extent(Lib.simpleMap(ya.range, ya.r2c));
-
- var trace = cdscatter[0].trace;
- if(!subTypes.hasMarkers(trace)) return;
- // if marker.maxdisplayed is used, select a maximum of
- // mnum markers to show, from the set that are in the viewport
- var mnum = trace.marker.maxdisplayed;
-
- // TODO: remove some as we get away from the viewport?
- if(mnum === 0) return;
-
- var cd = cdscatter.filter(function(v) {
- return v.x >= xr[0] && v.x <= xr[1] && v.y >= yr[0] && v.y <= yr[1];
- });
- var inc = Math.ceil(cd.length / mnum);
- var tnum = 0;
- cdscatterAll.forEach(function(cdj, j) {
- var tracei = cdj[0].trace;
- if(subTypes.hasMarkers(tracei) &&
- tracei.marker.maxdisplayed > 0 && j < idx) {
- tnum++;
- }
- });
-
- // if multiple traces use maxdisplayed, stagger which markers we
- // display this formula offsets successive traces by 1/3 of the
- // increment, adding an extra small amount after each triplet so
- // it's not quite periodic
- var i0 = Math.round(tnum * inc / 3 + Math.floor(tnum / 3) * inc / 7.1);
-
- // for error bars: save in cd which markers to show
- // so we don't have to repeat this
- cdscatter.forEach(function(v) { delete v.vis; });
- cd.forEach(function(v, i) {
- if(Math.round((i + i0) % inc) === 0) v.vis = true;
- });
- }
-
- },{"../../components/drawing":72,"../../lib":168,"../../lib/polygon":180,"../../registry":257,"./line_points":381,"./link_traces":383,"./subtypes":391,"d3":16}],388:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var subtypes = _dereq_('./subtypes');
-
- module.exports = function selectPoints(searchInfo, selectionTester) {
- var cd = searchInfo.cd;
- var xa = searchInfo.xaxis;
- var ya = searchInfo.yaxis;
- var selection = [];
- var trace = cd[0].trace;
- var i;
- var di;
- var x;
- var y;
-
- var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace));
- if(hasOnlyLines) return [];
-
- if(selectionTester === false) { // clear selection
- for(i = 0; i < cd.length; i++) {
- cd[i].selected = 0;
- }
- }
- else {
- for(i = 0; i < cd.length; i++) {
- di = cd[i];
- x = xa.c2p(di.x);
- y = ya.c2p(di.y);
-
- if((di.i !== null) && selectionTester.contains([x, y], false, i, searchInfo)) {
- selection.push({
- pointNumber: di.i,
- x: xa.c2d(di.x),
- y: ya.c2d(di.y)
- });
- di.selected = 1;
- } else {
- di.selected = 0;
- }
- }
- }
-
- return selection;
- };
-
- },{"./subtypes":391}],389:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var perStackAttrs = ['orientation', 'groupnorm', 'stackgaps'];
-
- module.exports = function handleStackDefaults(traceIn, traceOut, layout, coerce) {
- var stackOpts = layout._scatterStackOpts;
-
- var stackGroup = coerce('stackgroup');
- if(stackGroup) {
- // use independent stacking options per subplot
- var subplot = traceOut.xaxis + traceOut.yaxis;
- var subplotStackOpts = stackOpts[subplot];
- if(!subplotStackOpts) subplotStackOpts = stackOpts[subplot] = {};
-
- var groupOpts = subplotStackOpts[stackGroup];
- var firstTrace = false;
- if(groupOpts) {
- groupOpts.traces.push(traceOut);
- }
- else {
- groupOpts = subplotStackOpts[stackGroup] = {
- // keep track of trace indices for use during stacking calculations
- // this will be filled in during `calc` and used during `crossTraceCalc`
- // so it's OK if we don't recreate it during a non-calc edit
- traceIndices: [],
- // Hold on to the whole set of prior traces
- // First one is most important, so we can clear defaults
- // there if we find explicit values only in later traces.
- // We're only going to *use* the values stored in groupOpts,
- // but for the editor and validate we want things self-consistent
- // The full set of traces is used only to fix `fill` default if
- // we find `orientation: 'h'` beyond the first trace
- traces: [traceOut]
- };
- firstTrace = true;
- }
- // TODO: how is this going to work with groupby transforms?
- // in principle it should be OK I guess, as long as explicit group styles
- // don't override explicit base-trace styles?
-
- var dflts = {
- orientation: (traceOut.x && !traceOut.y) ? 'h' : 'v'
- };
-
- for(var i = 0; i < perStackAttrs.length; i++) {
- var attr = perStackAttrs[i];
- var attrFound = attr + 'Found';
- if(!groupOpts[attrFound]) {
- var traceHasAttr = traceIn[attr] !== undefined;
- var isOrientation = attr === 'orientation';
- if(traceHasAttr || firstTrace) {
- groupOpts[attr] = coerce(attr, dflts[attr]);
-
- if(isOrientation) {
- groupOpts.fillDflt = groupOpts[attr] === 'h' ?
- 'tonextx' : 'tonexty';
- }
-
- if(traceHasAttr) {
- // Note: this will show a value here even if it's invalid
- // in which case it will revert to default.
- groupOpts[attrFound] = true;
-
- // Note: only one trace in the stack will get a _fullData
- // entry for a given stack-wide attribute. If no traces
- // (or the first trace) specify that attribute, the
- // first trace will get it. If the first trace does NOT
- // specify it but some later trace does, then it gets
- // removed from the first trace and only included in the
- // one that specified it. This is mostly important for
- // editors (that want to see the full values to know
- // what settings are available) and Plotly.react diffing.
- // Editors may want to use fullLayout._scatterStackOpts
- // directly and make these settings available from all
- // traces in the stack... then set the new value into
- // the first trace, and clear all later traces.
- if(!firstTrace) {
- delete groupOpts.traces[0][attr];
-
- // orientation can affect default fill of previous traces
- if(isOrientation) {
- for(var j = 0; j < groupOpts.traces.length - 1; j++) {
- var trace2 = groupOpts.traces[j];
- if(trace2._input.fill !== trace2.fill) {
- trace2.fill = groupOpts.fillDflt;
- }
- }
- }
- }
- }
- }
- }
- }
- return groupOpts;
- }
- };
-
- },{}],390:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var Drawing = _dereq_('../../components/drawing');
- var Registry = _dereq_('../../registry');
-
- function style(gd, cd) {
- var s = cd ? cd[0].node3 : d3.select(gd).selectAll('g.trace.scatter');
-
- s.style('opacity', function(d) {
- return d[0].trace.opacity;
- });
-
- s.selectAll('g.points').each(function(d) {
- var sel = d3.select(this);
- var trace = d.trace || d[0].trace;
- stylePoints(sel, trace, gd);
- });
-
- s.selectAll('g.text').each(function(d) {
- var sel = d3.select(this);
- var trace = d.trace || d[0].trace;
- styleText(sel, trace, gd);
- });
-
- s.selectAll('g.trace path.js-line')
- .call(Drawing.lineGroupStyle);
-
- s.selectAll('g.trace path.js-fill')
- .call(Drawing.fillGroupStyle);
-
- Registry.getComponentMethod('errorbars', 'style')(s);
- }
-
- function stylePoints(sel, trace, gd) {
- Drawing.pointStyle(sel.selectAll('path.point'), trace, gd);
- }
-
- function styleText(sel, trace, gd) {
- Drawing.textPointStyle(sel.selectAll('text'), trace, gd);
- }
-
- function styleOnSelect(gd, cd) {
- var s = cd[0].node3;
- var trace = cd[0].trace;
-
- if(trace.selectedpoints) {
- Drawing.selectedPointStyle(s.selectAll('path.point'), trace);
- Drawing.selectedTextStyle(s.selectAll('text'), trace);
- } else {
- stylePoints(s, trace, gd);
- styleText(s, trace, gd);
- }
- }
-
- module.exports = {
- style: style,
- stylePoints: stylePoints,
- styleText: styleText,
- styleOnSelect: styleOnSelect
- };
-
- },{"../../components/drawing":72,"../../registry":257,"d3":16}],391:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- module.exports = {
- hasLines: function(trace) {
- return trace.visible && trace.mode &&
- trace.mode.indexOf('lines') !== -1;
- },
-
- hasMarkers: function(trace) {
- return trace.visible && (
- (trace.mode && trace.mode.indexOf('markers') !== -1) ||
- // until splom implements 'mode'
- trace.type === 'splom'
- );
- },
-
- hasText: function(trace) {
- return trace.visible && trace.mode &&
- trace.mode.indexOf('text') !== -1;
- },
-
- isBubble: function(trace) {
- return Lib.isPlainObject(trace.marker) &&
- Lib.isArrayOrTypedArray(trace.marker.size);
- }
- };
-
- },{"../../lib":168}],392:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- /*
- * opts: object of flags to control features not all text users support
- * noSelect: caller does not support selected/unselected attribute containers
- */
- module.exports = function(traceIn, traceOut, layout, coerce, opts) {
- opts = opts || {};
-
- coerce('textposition');
- Lib.coerceFont(coerce, 'textfont', layout.font);
-
- if(!opts.noSelect) {
- coerce('selected.textfont.color');
- coerce('unselected.textfont.color');
- }
- };
-
- },{"../../lib":168}],393:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Registry = _dereq_('../../registry');
-
- module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) {
- var x = coerce('x');
- var y = coerce('y');
- var len;
-
- var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
- handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
-
- if(x) {
- var xlen = Lib.minRowLength(x);
- if(y) {
- len = Math.min(xlen, Lib.minRowLength(y));
- } else {
- len = xlen;
- coerce('y0');
- coerce('dy');
- }
- } else {
- if(!y) return 0;
-
- len = Lib.minRowLength(y);
- coerce('x0');
- coerce('dx');
- }
-
- traceOut._length = len;
-
- return len;
- };
-
- },{"../../lib":168,"../../registry":257}],394:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes');
- var scatterAttrs = _dereq_('../scatter/attributes');
- var plotAttrs = _dereq_('../../plots/attributes');
- var colorAttributes = _dereq_('../../components/colorscale/attributes');
- var colorbarAttrs = _dereq_('../../components/colorbar/attributes');
- var dash = _dereq_('../../components/drawing/attributes').dash;
-
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- var scatterMarkerAttrs = scatterAttrs.marker;
- var scatterLineAttrs = scatterAttrs.line;
- var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
-
- module.exports = {
- a: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- b: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- c: {
- valType: 'data_array',
- editType: 'calc',
-
- },
- sum: {
- valType: 'number',
-
- dflt: 0,
- min: 0,
- editType: 'calc',
-
- },
- mode: extendFlat({}, scatterAttrs.mode, {dflt: 'markers'}),
- text: extendFlat({}, scatterAttrs.text, {
-
- }),
- hovertext: extendFlat({}, scatterAttrs.hovertext, {
-
- }),
- line: {
- color: scatterLineAttrs.color,
- width: scatterLineAttrs.width,
- dash: dash,
- shape: extendFlat({}, scatterLineAttrs.shape,
- {values: ['linear', 'spline']}),
- smoothing: scatterLineAttrs.smoothing,
- editType: 'calc'
- },
- connectgaps: scatterAttrs.connectgaps,
- cliponaxis: scatterAttrs.cliponaxis,
- fill: extendFlat({}, scatterAttrs.fill, {
- values: ['none', 'toself', 'tonext'],
- dflt: 'none',
-
- }),
- fillcolor: scatterAttrs.fillcolor,
- marker: extendFlat({
- symbol: scatterMarkerAttrs.symbol,
- opacity: scatterMarkerAttrs.opacity,
- maxdisplayed: scatterMarkerAttrs.maxdisplayed,
- size: scatterMarkerAttrs.size,
- sizeref: scatterMarkerAttrs.sizeref,
- sizemin: scatterMarkerAttrs.sizemin,
- sizemode: scatterMarkerAttrs.sizemode,
- line: extendFlat({
- width: scatterMarkerLineAttrs.width,
- editType: 'calc'
- },
- colorAttributes('marker.line')
- ),
- gradient: scatterMarkerAttrs.gradient,
- editType: 'calc'
- }, colorAttributes('marker'), {
- colorbar: colorbarAttrs
- }),
-
- textfont: scatterAttrs.textfont,
- textposition: scatterAttrs.textposition,
-
- selected: scatterAttrs.selected,
- unselected: scatterAttrs.unselected,
-
- hoverinfo: extendFlat({}, plotAttrs.hoverinfo, {
- flags: ['a', 'b', 'c', 'text', 'name']
- }),
- hoveron: scatterAttrs.hoveron,
- hovertemplate: hovertemplateAttrs(),
- };
-
- },{"../../components/colorbar/attributes":52,"../../components/colorscale/attributes":58,"../../components/drawing/attributes":71,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/attributes":209,"../scatter/attributes":367}],395:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var isNumeric = _dereq_('fast-isnumeric');
-
- var calcColorscale = _dereq_('../scatter/colorscale_calc');
- var arraysToCalcdata = _dereq_('../scatter/arrays_to_calcdata');
- var calcSelection = _dereq_('../scatter/calc_selection');
- var calcMarkerSize = _dereq_('../scatter/calc').calcMarkerSize;
-
- var dataArrays = ['a', 'b', 'c'];
- var arraysToFill = {a: ['b', 'c'], b: ['a', 'c'], c: ['a', 'b']};
-
- module.exports = function calc(gd, trace) {
- var ternary = gd._fullLayout[trace.subplot];
- var displaySum = ternary.sum;
- var normSum = trace.sum || displaySum;
- var arrays = {a: trace.a, b: trace.b, c: trace.c};
-
- var i, j, dataArray, newArray, fillArray1, fillArray2;
-
- // fill in one missing component
- for(i = 0; i < dataArrays.length; i++) {
- dataArray = dataArrays[i];
- if(arrays[dataArray]) continue;
-
- fillArray1 = arrays[arraysToFill[dataArray][0]];
- fillArray2 = arrays[arraysToFill[dataArray][1]];
- newArray = new Array(fillArray1.length);
- for(j = 0; j < fillArray1.length; j++) {
- newArray[j] = normSum - fillArray1[j] - fillArray2[j];
- }
- arrays[dataArray] = newArray;
- }
-
- // make the calcdata array
- var serieslen = trace._length;
- var cd = new Array(serieslen);
- var a, b, c, norm, x, y;
- for(i = 0; i < serieslen; i++) {
- a = arrays.a[i];
- b = arrays.b[i];
- c = arrays.c[i];
- if(isNumeric(a) && isNumeric(b) && isNumeric(c)) {
- a = +a;
- b = +b;
- c = +c;
- norm = displaySum / (a + b + c);
- if(norm !== 1) {
- a *= norm;
- b *= norm;
- c *= norm;
- }
- // map a, b, c onto x and y where the full scale of y
- // is [0, sum], and x is [-sum, sum]
- // TODO: this makes `a` always the top, `b` the bottom left,
- // and `c` the bottom right. Do we want options to rearrange
- // these?
- y = a;
- x = c - b;
- cd[i] = {x: x, y: y, a: a, b: b, c: c};
- }
- else cd[i] = {x: false, y: false};
- }
-
- calcMarkerSize(trace, serieslen);
- calcColorscale(gd, trace);
- arraysToCalcdata(cd, trace);
- calcSelection(cd, trace);
-
- return cd;
- };
-
- },{"../scatter/arrays_to_calcdata":366,"../scatter/calc":368,"../scatter/calc_selection":369,"../scatter/colorscale_calc":370,"fast-isnumeric":18}],396:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- var constants = _dereq_('../scatter/constants');
- var subTypes = _dereq_('../scatter/subtypes');
- var handleMarkerDefaults = _dereq_('../scatter/marker_defaults');
- var handleLineDefaults = _dereq_('../scatter/line_defaults');
- var handleLineShapeDefaults = _dereq_('../scatter/line_shape_defaults');
- var handleTextDefaults = _dereq_('../scatter/text_defaults');
- var handleFillColorDefaults = _dereq_('../scatter/fillcolor_defaults');
-
- var attributes = _dereq_('./attributes');
-
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
-
- var a = coerce('a');
- var b = coerce('b');
- var c = coerce('c');
- var len;
-
- // allow any one array to be missing, len is the minimum length of those
- // present. Note that after coerce data_array's are either Arrays (which
- // are truthy even if empty) or undefined. As in scatter, an empty array
- // is different from undefined, because it can signify that this data is
- // not known yet but expected in the future
- if(a) {
- len = a.length;
- if(b) {
- len = Math.min(len, b.length);
- if(c) len = Math.min(len, c.length);
- }
- else if(c) len = Math.min(len, c.length);
- else len = 0;
- }
- else if(b && c) {
- len = Math.min(b.length, c.length);
- }
-
- if(!len) {
- traceOut.visible = false;
- return;
- }
-
- traceOut._length = len;
-
- coerce('sum');
-
- coerce('text');
- coerce('hovertext');
- if(traceOut.hoveron !== 'fills') coerce('hovertemplate');
-
- var defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines';
- coerce('mode', defaultMode);
-
- if(subTypes.hasLines(traceOut)) {
- handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
- handleLineShapeDefaults(traceIn, traceOut, coerce);
- coerce('connectgaps');
- }
-
- if(subTypes.hasMarkers(traceOut)) {
- handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true});
- }
-
- if(subTypes.hasText(traceOut)) {
- handleTextDefaults(traceIn, traceOut, layout, coerce);
- }
-
- var dfltHoverOn = [];
-
- if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) {
- coerce('cliponaxis');
- coerce('marker.maxdisplayed');
- dfltHoverOn.push('points');
- }
-
- coerce('fill');
- if(traceOut.fill !== 'none') {
- handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce);
- if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce);
- }
-
- if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') {
- dfltHoverOn.push('fills');
- }
- coerce('hoveron', dfltHoverOn.join('+') || 'points');
-
- Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
- };
-
- },{"../../lib":168,"../scatter/constants":371,"../scatter/fillcolor_defaults":376,"../scatter/line_defaults":380,"../scatter/line_shape_defaults":382,"../scatter/marker_defaults":386,"../scatter/subtypes":391,"../scatter/text_defaults":392,"./attributes":394}],397:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = function eventData(out, pt, trace, cd, pointNumber) {
- if(pt.xa) out.xaxis = pt.xa;
- if(pt.ya) out.yaxis = pt.ya;
-
- if(cd[pointNumber]) {
- var cdi = cd[pointNumber];
-
- // N.B. These are the normalized coordinates.
- out.a = cdi.a;
- out.b = cdi.b;
- out.c = cdi.c;
- } else {
- // for fill-hover only
- out.a = pt.a;
- out.b = pt.b;
- out.c = pt.c;
- }
-
- return out;
- };
-
- },{}],398:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var scatterHover = _dereq_('../scatter/hover');
- var Axes = _dereq_('../../plots/cartesian/axes');
-
-
- module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
- var scatterPointData = scatterHover(pointData, xval, yval, hovermode);
- if(!scatterPointData || scatterPointData[0].index === false) return;
-
- var newPointData = scatterPointData[0];
-
- // if hovering on a fill, we don't show any point data so the label is
- // unchanged from what scatter gives us - except that it needs to
- // be constrained to the trianglular plot area, not just the rectangular
- // area defined by the synthetic x and y axes
- // TODO: in some cases the vertical middle of the shape is not within
- // the triangular viewport at all, so the label can become disconnected
- // from the shape entirely. But calculating what portion of the shape
- // is actually visible, as constrained by the diagonal axis lines, is not
- // so easy and anyway we lost the information we would have needed to do
- // this inside scatterHover.
- if(newPointData.index === undefined) {
- var yFracUp = 1 - (newPointData.y0 / pointData.ya._length);
- var xLen = pointData.xa._length;
- var xMin = xLen * yFracUp / 2;
- var xMax = xLen - xMin;
- newPointData.x0 = Math.max(Math.min(newPointData.x0, xMax), xMin);
- newPointData.x1 = Math.max(Math.min(newPointData.x1, xMax), xMin);
- return scatterPointData;
- }
-
- var cdi = newPointData.cd[newPointData.index];
-
- newPointData.a = cdi.a;
- newPointData.b = cdi.b;
- newPointData.c = cdi.c;
-
- newPointData.xLabelVal = undefined;
- newPointData.yLabelVal = undefined;
- // TODO: nice formatting, and label by axis title, for a, b, and c?
-
- var trace = newPointData.trace;
- var ternary = newPointData.subplot;
- var hoverinfo = cdi.hi || trace.hoverinfo;
- var text = [];
- function textPart(ax, val) {
- text.push(ax._hovertitle + ': ' + Axes.tickText(ax, val, 'hover').text);
- }
- if(!trace.hovertemplate) {
- var parts = hoverinfo.split('+');
- if(parts.indexOf('all') !== -1) parts = ['a', 'b', 'c'];
- if(parts.indexOf('a') !== -1) textPart(ternary.aaxis, cdi.a);
- if(parts.indexOf('b') !== -1) textPart(ternary.baxis, cdi.b);
- if(parts.indexOf('c') !== -1) textPart(ternary.caxis, cdi.c);
- }
- newPointData.extraText = text.join('<br>');
- newPointData.hovertemplate = trace.hovertemplate;
- return scatterPointData;
- };
-
- },{"../../plots/cartesian/axes":212,"../scatter/hover":378}],399:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var ScatterTernary = {};
-
- ScatterTernary.attributes = _dereq_('./attributes');
- ScatterTernary.supplyDefaults = _dereq_('./defaults');
- ScatterTernary.colorbar = _dereq_('../scatter/marker_colorbar');
- ScatterTernary.calc = _dereq_('./calc');
- ScatterTernary.plot = _dereq_('./plot');
- ScatterTernary.style = _dereq_('../scatter/style').style;
- ScatterTernary.styleOnSelect = _dereq_('../scatter/style').styleOnSelect;
- ScatterTernary.hoverPoints = _dereq_('./hover');
- ScatterTernary.selectPoints = _dereq_('../scatter/select');
- ScatterTernary.eventData = _dereq_('./event_data');
-
- ScatterTernary.moduleType = 'trace';
- ScatterTernary.name = 'scatterternary';
- ScatterTernary.basePlotModule = _dereq_('../../plots/ternary');
- ScatterTernary.categories = ['ternary', 'symbols', 'showLegend', 'scatter-like'];
- ScatterTernary.meta = {
-
-
- };
-
- module.exports = ScatterTernary;
-
- },{"../../plots/ternary":253,"../scatter/marker_colorbar":385,"../scatter/select":388,"../scatter/style":390,"./attributes":394,"./calc":395,"./defaults":396,"./event_data":397,"./hover":398,"./plot":400}],400:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
- 'use strict';
-
- var scatterPlot = _dereq_('../scatter/plot');
-
- module.exports = function plot(gd, ternary, moduleCalcData) {
- var plotContainer = ternary.plotContainer;
-
- // remove all nodes inside the scatter layer
- plotContainer.select('.scatterlayer').selectAll('*').remove();
-
- // mimic cartesian plotinfo
- var plotinfo = {
- xaxis: ternary.xaxis,
- yaxis: ternary.yaxis,
- plot: plotContainer,
- layerClipId: ternary._hasClipOnAxisFalse ? ternary.clipIdRelative : null
- };
-
- var scatterLayer = ternary.layers.frontplot.select('g.scatterlayer');
-
- scatterPlot(gd, plotinfo, moduleCalcData, scatterLayer);
- };
-
- },{"../scatter/plot":387}],401:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var boxAttrs = _dereq_('../box/attributes');
- var extendFlat = _dereq_('../../lib/extend').extendFlat;
-
- module.exports = {
- y: boxAttrs.y,
- x: boxAttrs.x,
- x0: boxAttrs.x0,
- y0: boxAttrs.y0,
- name: boxAttrs.name,
- orientation: extendFlat({}, boxAttrs.orientation, {
-
- }),
-
- bandwidth: {
- valType: 'number',
- min: 0,
-
- editType: 'calc',
-
- },
-
- scalegroup: {
- valType: 'string',
-
- dflt: '',
- editType: 'calc',
-
- },
- scalemode: {
- valType: 'enumerated',
- values: ['width', 'count'],
- dflt: 'width',
-
- editType: 'calc',
-
- },
-
- spanmode: {
- valType: 'enumerated',
- values: ['soft', 'hard', 'manual'],
- dflt: 'soft',
-
- editType: 'calc',
-
- },
- span: {
- valType: 'info_array',
- items: [
- {valType: 'any', editType: 'calc'},
- {valType: 'any', editType: 'calc'}
- ],
-
- editType: 'calc',
-
- },
-
- line: {
- color: {
- valType: 'color',
-
- editType: 'style',
-
- },
- width: {
- valType: 'number',
-
- min: 0,
- dflt: 2,
- editType: 'style',
-
- },
- editType: 'plot'
- },
- fillcolor: boxAttrs.fillcolor,
-
- points: extendFlat({}, boxAttrs.boxpoints, {
-
- }),
- jitter: extendFlat({}, boxAttrs.jitter, {
-
- }),
- pointpos: extendFlat({}, boxAttrs.pointpos, {
-
- }),
-
- width: extendFlat({}, boxAttrs.width, {
-
- }),
-
- marker: boxAttrs.marker,
- text: boxAttrs.text,
-
- box: {
- visible: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'plot',
-
- },
- width: {
- valType: 'number',
- min: 0,
- max: 1,
- dflt: 0.25,
-
- editType: 'plot',
-
- },
- fillcolor: {
- valType: 'color',
-
- editType: 'style',
-
- },
- line: {
- color: {
- valType: 'color',
-
- editType: 'style',
-
- },
- width: {
- valType: 'number',
- min: 0,
-
- editType: 'style',
-
- },
- editType: 'style'
- },
- editType: 'plot'
- },
-
- meanline: {
- visible: {
- valType: 'boolean',
- dflt: false,
-
- editType: 'plot',
-
- },
- color: {
- valType: 'color',
-
- editType: 'style',
-
- },
- width: {
- valType: 'number',
- min: 0,
-
- editType: 'style',
-
- },
- editType: 'plot'
- },
-
- side: {
- valType: 'enumerated',
- values: ['both', 'positive', 'negative'],
- dflt: 'both',
-
- editType: 'calc',
-
- },
-
- selected: boxAttrs.selected,
- unselected: boxAttrs.unselected,
-
- hoveron: {
- valType: 'flaglist',
- flags: ['violins', 'points', 'kde'],
- dflt: 'violins+points+kde',
- extras: ['all'],
-
- editType: 'style',
-
- }
- };
-
- },{"../../lib/extend":162,"../box/attributes":282}],402:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var boxCalc = _dereq_('../box/calc');
- var helpers = _dereq_('./helpers');
- var BADNUM = _dereq_('../../constants/numerical').BADNUM;
-
- module.exports = function calc(gd, trace) {
- var cd = boxCalc(gd, trace);
-
- if(cd[0].t.empty) return cd;
-
- var fullLayout = gd._fullLayout;
- var valAxis = Axes.getFromId(
- gd,
- trace[trace.orientation === 'h' ? 'xaxis' : 'yaxis']
- );
-
- var spanMin = Infinity;
- var spanMax = -Infinity;
- var maxKDE = 0;
- var maxCount = 0;
-
- for(var i = 0; i < cd.length; i++) {
- var cdi = cd[i];
- var vals = cdi.pts.map(helpers.extractVal);
-
- var bandwidth = cdi.bandwidth = calcBandwidth(trace, cdi, vals);
- var span = cdi.span = calcSpan(trace, cdi, valAxis, bandwidth);
-
- // step that well covers the bandwidth and is multiple of span distance
- var dist = span[1] - span[0];
- var n = Math.ceil(dist / (bandwidth / 3));
- var step = dist / n;
-
- if(!isFinite(step) || !isFinite(n)) {
- Lib.error('Something went wrong with computing the violin span');
- cd[0].t.empty = true;
- return cd;
- }
-
- var kde = helpers.makeKDE(cdi, trace, vals);
- cdi.density = new Array(n);
-
- for(var k = 0, t = span[0]; t < (span[1] + step / 2); k++, t += step) {
- var v = kde(t);
- cdi.density[k] = {v: v, t: t};
- maxKDE = Math.max(maxKDE, v);
- }
-
- maxCount = Math.max(maxCount, vals.length);
- spanMin = Math.min(spanMin, span[0]);
- spanMax = Math.max(spanMax, span[1]);
- }
-
- var extremes = Axes.findExtremes(valAxis, [spanMin, spanMax], {padded: true});
- trace._extremes[valAxis._id] = extremes;
-
- if(trace.width) {
- cd[0].t.maxKDE = maxKDE;
- } else {
- var violinScaleGroupStats = fullLayout._violinScaleGroupStats;
- var scaleGroup = trace.scalegroup;
- var groupStats = violinScaleGroupStats[scaleGroup];
-
- if(groupStats) {
- groupStats.maxKDE = Math.max(groupStats.maxKDE, maxKDE);
- groupStats.maxCount = Math.max(groupStats.maxCount, maxCount);
- } else {
- violinScaleGroupStats[scaleGroup] = {
- maxKDE: maxKDE,
- maxCount: maxCount
- };
- }
- }
-
- cd[0].t.labels.kde = Lib._(gd, 'kde:');
-
- return cd;
- };
-
- // Default to Silveman's rule of thumb
- // - https://stats.stackexchange.com/a/6671
- // - https://en.wikipedia.org/wiki/Kernel_density_estimation#A_rule-of-thumb_bandwidth_estimator
- // - https://github.com/statsmodels/statsmodels/blob/master/statsmodels/nonparametric/bandwidths.py
- function silvermanRule(len, ssd, iqr) {
- var a = Math.min(ssd, iqr / 1.349);
- return 1.059 * a * Math.pow(len, -0.2);
- }
-
- function calcBandwidth(trace, cdi, vals) {
- var span = cdi.max - cdi.min;
-
- // plot single-value violin with bandwidth of 1
- if(!span) return 1;
-
- // Limit how small the bandwidth can be.
- //
- // Silverman's rule of thumb can be "very" small
- // when IQR does a poor job at describing the spread
- // of the distribution.
- // We also want to limit custom bandwidths
- // to not blow up kde computations.
-
- if(trace.bandwidth) {
- return Math.max(trace.bandwidth, span / 1e4);
- } else {
- var len = vals.length;
- var ssd = Lib.stdev(vals, len - 1, cdi.mean);
- return Math.max(
- silvermanRule(len, ssd, cdi.q3 - cdi.q1),
- span / 100
- );
- }
- }
-
- function calcSpan(trace, cdi, valAxis, bandwidth) {
- var spanmode = trace.spanmode;
- var spanIn = trace.span || [];
- var spanTight = [cdi.min, cdi.max];
- var spanLoose = [cdi.min - 2 * bandwidth, cdi.max + 2 * bandwidth];
- var spanOut;
-
- function calcSpanItem(index) {
- var s = spanIn[index];
- var sc = valAxis.type === 'multicategory' ?
- valAxis.r2c(s) :
- valAxis.d2c(s, 0, trace[cdi.valLetter + 'calendar']);
- return sc === BADNUM ? spanLoose[index] : sc;
- }
-
- if(spanmode === 'soft') {
- spanOut = spanLoose;
- } else if(spanmode === 'hard') {
- spanOut = spanTight;
- } else {
- spanOut = [calcSpanItem(0), calcSpanItem(1)];
- }
-
- // to reuse the equal-range-item block
- var dummyAx = {
- type: 'linear',
- range: spanOut
- };
- Axes.setConvert(dummyAx);
- dummyAx.cleanRange();
-
- return spanOut;
- }
-
- },{"../../constants/numerical":149,"../../lib":168,"../../plots/cartesian/axes":212,"../box/calc":283,"./helpers":405}],403:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var setPositionOffset = _dereq_('../box/cross_trace_calc').setPositionOffset;
- var orientations = ['v', 'h'];
-
- module.exports = function crossTraceCalc(gd, plotinfo) {
- var calcdata = gd.calcdata;
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- for(var i = 0; i < orientations.length; i++) {
- var orientation = orientations[i];
- var posAxis = orientation === 'h' ? ya : xa;
- var violinList = [];
-
- for(var j = 0; j < calcdata.length; j++) {
- var cd = calcdata[j];
- var t = cd[0].t;
- var trace = cd[0].trace;
-
- if(trace.visible === true && trace.type === 'violin' &&
- !t.empty &&
- trace.orientation === orientation &&
- trace.xaxis === xa._id &&
- trace.yaxis === ya._id
- ) {
- violinList.push(j);
- }
- }
-
- setPositionOffset('violin', gd, violinList, posAxis);
- }
- };
-
- },{"../box/cross_trace_calc":284}],404:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Color = _dereq_('../../components/color');
-
- var boxDefaults = _dereq_('../box/defaults');
- var attributes = _dereq_('./attributes');
-
- module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
- function coerce(attr, dflt) {
- return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
- }
- function coerce2(attr, dflt) {
- return Lib.coerce2(traceIn, traceOut, attributes, attr, dflt);
- }
-
- boxDefaults.handleSampleDefaults(traceIn, traceOut, coerce, layout);
- if(traceOut.visible === false) return;
-
- coerce('bandwidth');
- coerce('side');
-
- var width = coerce('width');
- if(!width) {
- coerce('scalegroup', traceOut.name);
- coerce('scalemode');
- }
-
- var span = coerce('span');
- var spanmodeDflt;
- if(Array.isArray(span)) spanmodeDflt = 'manual';
- coerce('spanmode', spanmodeDflt);
-
- var lineColor = coerce('line.color', (traceIn.marker || {}).color || defaultColor);
- var lineWidth = coerce('line.width');
- var fillColor = coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5));
-
- boxDefaults.handlePointsDefaults(traceIn, traceOut, coerce, {prefix: ''});
-
- var boxWidth = coerce2('box.width');
- var boxFillColor = coerce2('box.fillcolor', fillColor);
- var boxLineColor = coerce2('box.line.color', lineColor);
- var boxLineWidth = coerce2('box.line.width', lineWidth);
- var boxVisible = coerce('box.visible', Boolean(boxWidth || boxFillColor || boxLineColor || boxLineWidth));
- if(!boxVisible) traceOut.box = {visible: false};
-
- var meanLineColor = coerce2('meanline.color', lineColor);
- var meanLineWidth = coerce2('meanline.width', lineWidth);
- var meanLineVisible = coerce('meanline.visible', Boolean(meanLineColor || meanLineWidth));
- if(!meanLineVisible) traceOut.meanline = {visible: false};
- };
-
- },{"../../components/color":51,"../../lib":168,"../box/defaults":285,"./attributes":401}],405:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
-
- // Maybe add kernels more down the road,
- // but note that the default `spanmode: 'soft'` bounds might have
- // to become kernel-dependent
- var kernels = {
- gaussian: function(v) {
- return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * v * v);
- }
- };
-
- exports.makeKDE = function(calcItem, trace, vals) {
- var len = vals.length;
- var kernel = kernels.gaussian;
- var bandwidth = calcItem.bandwidth;
- var factor = 1 / (len * bandwidth);
-
- // don't use Lib.aggNums to skip isNumeric checks
- return function(x) {
- var sum = 0;
- for(var i = 0; i < len; i++) {
- sum += kernel((x - vals[i]) / bandwidth);
- }
- return factor * sum;
- };
- };
-
- exports.getPositionOnKdePath = function(calcItem, trace, valuePx) {
- var posLetter, valLetter;
-
- if(trace.orientation === 'h') {
- posLetter = 'y';
- valLetter = 'x';
- } else {
- posLetter = 'x';
- valLetter = 'y';
- }
-
- var pointOnPath = Lib.findPointOnPath(
- calcItem.path,
- valuePx,
- valLetter,
- {pathLength: calcItem.pathLength}
- );
-
- var posCenterPx = calcItem.posCenterPx;
- var posOnPath0 = pointOnPath[posLetter];
- var posOnPath1 = trace.side === 'both' ?
- 2 * posCenterPx - posOnPath0 :
- posCenterPx;
-
- return [posOnPath0, posOnPath1];
- };
-
- exports.getKdeValue = function(calcItem, trace, valueDist) {
- var vals = calcItem.pts.map(exports.extractVal);
- var kde = exports.makeKDE(calcItem, trace, vals);
- return kde(valueDist) / calcItem.posDensityScale;
- };
-
- exports.extractVal = function(o) { return o.v; };
-
- },{"../../lib":168}],406:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var Axes = _dereq_('../../plots/cartesian/axes');
- var boxHoverPoints = _dereq_('../box/hover');
- var helpers = _dereq_('./helpers');
-
- module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer) {
- var cd = pointData.cd;
- var trace = cd[0].trace;
- var hoveron = trace.hoveron;
- var hasHoveronViolins = hoveron.indexOf('violins') !== -1;
- var hasHoveronKDE = hoveron.indexOf('kde') !== -1;
- var closeData = [];
- var closePtData;
- var violinLineAttrs;
-
- if(hasHoveronViolins || hasHoveronKDE) {
- var closeBoxData = boxHoverPoints.hoverOnBoxes(pointData, xval, yval, hovermode);
-
- if(hasHoveronViolins) {
- closeData = closeData.concat(closeBoxData);
- }
-
- if(hasHoveronKDE && closeBoxData.length > 0) {
- var xa = pointData.xa;
- var ya = pointData.ya;
- var pLetter, vLetter, pAxis, vAxis, vVal;
-
- if(trace.orientation === 'h') {
- vVal = xval;
- pLetter = 'y';
- pAxis = ya;
- vLetter = 'x';
- vAxis = xa;
- } else {
- vVal = yval;
- pLetter = 'x';
- pAxis = xa;
- vLetter = 'y';
- vAxis = ya;
- }
-
- var di = cd[pointData.index];
-
- if(vVal >= di.span[0] && vVal <= di.span[1]) {
- var kdePointData = Lib.extendFlat({}, pointData);
- var vValPx = vAxis.c2p(vVal, true);
- var kdeVal = helpers.getKdeValue(di, trace, vVal);
- var pOnPath = helpers.getPositionOnKdePath(di, trace, vValPx);
- var paOffset = pAxis._offset;
- var paLength = pAxis._length;
-
- kdePointData[pLetter + '0'] = pOnPath[0];
- kdePointData[pLetter + '1'] = pOnPath[1];
- kdePointData[vLetter + '0'] = kdePointData[vLetter + '1'] = vValPx;
- kdePointData[vLetter + 'Label'] = vLetter + ': ' + Axes.hoverLabelText(vAxis, vVal) + ', ' + cd[0].t.labels.kde + ' ' + kdeVal.toFixed(3);
-
- // move the spike to the KDE point
- kdePointData.spikeDistance = closeBoxData[0].spikeDistance;
- var spikePosAttr = pLetter + 'Spike';
- kdePointData[spikePosAttr] = closeBoxData[0][spikePosAttr];
- closeBoxData[0].spikeDistance = undefined;
- closeBoxData[0][spikePosAttr] = undefined;
-
- closeData.push(kdePointData);
-
- violinLineAttrs = {stroke: pointData.color};
- violinLineAttrs[pLetter + '1'] = Lib.constrain(paOffset + pOnPath[0], paOffset, paOffset + paLength);
- violinLineAttrs[pLetter + '2'] = Lib.constrain(paOffset + pOnPath[1], paOffset, paOffset + paLength);
- violinLineAttrs[vLetter + '1'] = violinLineAttrs[vLetter + '2'] = vAxis._offset + vValPx;
- }
- }
- }
-
- if(hoveron.indexOf('points') !== -1) {
- closePtData = boxHoverPoints.hoverOnPoints(pointData, xval, yval);
- }
-
- // update violin line (if any)
- var violinLine = hoverLayer.selectAll('.violinline-' + trace.uid)
- .data(violinLineAttrs ? [0] : []);
- violinLine.enter().append('line')
- .classed('violinline-' + trace.uid, true)
- .attr('stroke-width', 1.5);
- violinLine.exit().remove();
- violinLine.attr(violinLineAttrs);
-
- // same combine logic as box hoverPoints
- if(hovermode === 'closest') {
- if(closePtData) return [closePtData];
- return closeData;
- }
- if(closePtData) {
- closeData.push(closePtData);
- return closeData;
- }
- return closeData;
- };
-
- },{"../../lib":168,"../../plots/cartesian/axes":212,"../box/hover":287,"./helpers":405}],407:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- module.exports = {
- attributes: _dereq_('./attributes'),
- layoutAttributes: _dereq_('./layout_attributes'),
- supplyDefaults: _dereq_('./defaults'),
- supplyLayoutDefaults: _dereq_('./layout_defaults'),
- calc: _dereq_('./calc'),
- crossTraceCalc: _dereq_('./cross_trace_calc'),
- plot: _dereq_('./plot'),
- style: _dereq_('./style'),
- styleOnSelect: _dereq_('../scatter/style').styleOnSelect,
- hoverPoints: _dereq_('./hover'),
- selectPoints: _dereq_('../box/select'),
-
- moduleType: 'trace',
- name: 'violin',
- basePlotModule: _dereq_('../../plots/cartesian'),
- categories: ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'violinLayout', 'zoomScale'],
- meta: {
-
- }
- };
-
- },{"../../plots/cartesian":224,"../box/select":292,"../scatter/style":390,"./attributes":401,"./calc":402,"./cross_trace_calc":403,"./defaults":404,"./hover":406,"./layout_attributes":408,"./layout_defaults":409,"./plot":410,"./style":411}],408:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var boxLayoutAttrs = _dereq_('../box/layout_attributes');
- var extendFlat = _dereq_('../../lib').extendFlat;
-
- module.exports = {
- violinmode: extendFlat({}, boxLayoutAttrs.boxmode, {
-
- }),
- violingap: extendFlat({}, boxLayoutAttrs.boxgap, {
-
- }),
- violingroupgap: extendFlat({}, boxLayoutAttrs.boxgroupgap, {
-
- })
- };
-
- },{"../../lib":168,"../box/layout_attributes":289}],409:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var Lib = _dereq_('../../lib');
- var layoutAttributes = _dereq_('./layout_attributes');
- var boxLayoutDefaults = _dereq_('../box/layout_defaults');
-
- module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
- function coerce(attr, dflt) {
- return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
- }
- boxLayoutDefaults._supply(layoutIn, layoutOut, fullData, coerce, 'violin');
- };
-
- },{"../../lib":168,"../box/layout_defaults":290,"./layout_attributes":408}],410:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var Lib = _dereq_('../../lib');
- var Drawing = _dereq_('../../components/drawing');
-
- var boxPlot = _dereq_('../box/plot');
- var linePoints = _dereq_('../scatter/line_points');
- var helpers = _dereq_('./helpers');
-
- module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) {
- var fullLayout = gd._fullLayout;
- var xa = plotinfo.xaxis;
- var ya = plotinfo.yaxis;
-
- function makePath(pts) {
- var segments = linePoints(pts, {
- xaxis: xa,
- yaxis: ya,
- connectGaps: true,
- baseTolerance: 0.75,
- shape: 'spline',
- simplify: true
- });
- return Drawing.smoothopen(segments[0], 1);
- }
-
- Lib.makeTraceGroups(violinLayer, cdViolins, 'trace violins').each(function(cd) {
- var plotGroup = d3.select(this);
- var cd0 = cd[0];
- var t = cd0.t;
- var trace = cd0.trace;
- if(!plotinfo.isRangePlot) cd0.node3 = plotGroup;
-
- if(trace.visible !== true || t.empty) {
- plotGroup.remove();
- return;
- }
-
- var bPos = t.bPos;
- var bdPos = t.bdPos;
- var valAxis = plotinfo[t.valLetter + 'axis'];
- var posAxis = plotinfo[t.posLetter + 'axis'];
- var hasBothSides = trace.side === 'both';
- var hasPositiveSide = hasBothSides || trace.side === 'positive';
- var hasNegativeSide = hasBothSides || trace.side === 'negative';
-
- var violins = plotGroup.selectAll('path.violin').data(Lib.identity);
-
- violins.enter().append('path')
- .style('vector-effect', 'non-scaling-stroke')
- .attr('class', 'violin');
-
- violins.exit().remove();
-
- violins.each(function(d) {
- var pathSel = d3.select(this);
- var density = d.density;
- var len = density.length;
- var posCenter = d.pos + bPos;
- var posCenterPx = posAxis.c2p(posCenter);
-
- var scale;
- if(trace.width) {
- scale = t.maxKDE / bdPos;
- } else {
- var groupStats = fullLayout._violinScaleGroupStats[trace.scalegroup];
- scale = trace.scalemode === 'count' ?
- (groupStats.maxKDE / bdPos) * (groupStats.maxCount / d.pts.length) :
- groupStats.maxKDE / bdPos;
- }
-
- var pathPos, pathNeg, path;
- var i, k, pts, pt;
-
- if(hasPositiveSide) {
- pts = new Array(len);
- for(i = 0; i < len; i++) {
- pt = pts[i] = {};
- pt[t.posLetter] = posCenter + (density[i].v / scale);
- pt[t.valLetter] = density[i].t;
- }
- pathPos = makePath(pts);
- }
-
- if(hasNegativeSide) {
- pts = new Array(len);
- for(k = 0, i = len - 1; k < len; k++, i--) {
- pt = pts[k] = {};
- pt[t.posLetter] = posCenter - (density[i].v / scale);
- pt[t.valLetter] = density[i].t;
- }
- pathNeg = makePath(pts);
- }
-
- if(hasBothSides) {
- path = pathPos + 'L' + pathNeg.substr(1) + 'Z';
- }
- else {
- var startPt = [posCenterPx, valAxis.c2p(density[0].t)];
- var endPt = [posCenterPx, valAxis.c2p(density[len - 1].t)];
-
- if(trace.orientation === 'h') {
- startPt.reverse();
- endPt.reverse();
- }
-
- if(hasPositiveSide) {
- path = 'M' + startPt + 'L' + pathPos.substr(1) + 'L' + endPt;
- } else {
- path = 'M' + endPt + 'L' + pathNeg.substr(1) + 'L' + startPt;
- }
- }
- pathSel.attr('d', path);
-
- // save a few things used in getPositionOnKdePath, getKdeValue
- // on hover and for meanline draw block below
- d.posCenterPx = posCenterPx;
- d.posDensityScale = scale * bdPos;
- d.path = pathSel.node();
- d.pathLength = d.path.getTotalLength() / (hasBothSides ? 2 : 1);
- });
-
- var boxAttrs = trace.box;
- var boxWidth = boxAttrs.width;
- var boxLineWidth = (boxAttrs.line || {}).width;
- var bdPosScaled;
- var bPosPxOffset;
-
- if(hasBothSides) {
- bdPosScaled = bdPos * boxWidth;
- bPosPxOffset = 0;
- } else if(hasPositiveSide) {
- bdPosScaled = [0, bdPos * boxWidth / 2];
- bPosPxOffset = -boxLineWidth;
- } else {
- bdPosScaled = [bdPos * boxWidth / 2, 0];
- bPosPxOffset = boxLineWidth;
- }
-
- // inner box
- boxPlot.plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, {
- bPos: bPos,
- bdPos: bdPosScaled,
- bPosPxOffset: bPosPxOffset
- });
-
- // meanline insider box
- boxPlot.plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, {
- bPos: bPos,
- bdPos: bdPosScaled,
- bPosPxOffset: bPosPxOffset
- });
-
- var fn;
- if(!trace.box.visible && trace.meanline.visible) {
- fn = Lib.identity;
- }
-
- // N.B. use different class name than boxPlot.plotBoxMean,
- // to avoid selectAll conflict
- var meanPaths = plotGroup.selectAll('path.meanline').data(fn || []);
- meanPaths.enter().append('path')
- .attr('class', 'meanline')
- .style('fill', 'none')
- .style('vector-effect', 'non-scaling-stroke');
- meanPaths.exit().remove();
- meanPaths.each(function(d) {
- var v = valAxis.c2p(d.mean, true);
- var p = helpers.getPositionOnKdePath(d, trace, v);
-
- d3.select(this).attr('d',
- trace.orientation === 'h' ?
- 'M' + v + ',' + p[0] + 'V' + p[1] :
- 'M' + p[0] + ',' + v + 'H' + p[1]
- );
- });
-
- boxPlot.plotPoints(plotGroup, {x: xa, y: ya}, trace, t);
- });
- };
-
- },{"../../components/drawing":72,"../../lib":168,"../box/plot":291,"../scatter/line_points":381,"./helpers":405,"d3":16}],411:[function(_dereq_,module,exports){
- /**
- * Copyright 2012-2019, Plotly, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
- 'use strict';
-
- var d3 = _dereq_('d3');
- var Color = _dereq_('../../components/color');
- var stylePoints = _dereq_('../scatter/style').stylePoints;
-
- module.exports = function style(gd, cd) {
- var s = cd ? cd[0].node3 : d3.select(gd).selectAll('g.trace.violins');
-
- s.style('opacity', function(d) { return d[0].trace.opacity; });
-
- s.each(function(d) {
- var trace = d[0].trace;
- var sel = d3.select(this);
- var box = trace.box || {};
- var boxLine = box.line || {};
- var meanline = trace.meanline || {};
- var meanLineWidth = meanline.width;
-
- sel.selectAll('path.violin')
- .style('stroke-width', trace.line.width + 'px')
- .call(Color.stroke, trace.line.color)
- .call(Color.fill, trace.fillcolor);
-
- sel.selectAll('path.box')
- .style('stroke-width', boxLine.width + 'px')
- .call(Color.stroke, boxLine.color)
- .call(Color.fill, box.fillcolor);
-
- var meanLineStyle = {
- 'stroke-width': meanLineWidth + 'px',
- 'stroke-dasharray': (2 * meanLineWidth) + 'px,' + meanLineWidth + 'px'
- };
-
- sel.selectAll('path.mean')
- .style(meanLineStyle)
- .call(Color.stroke, meanline.color);
-
- sel.selectAll('path.meanline')
- .style(meanLineStyle)
- .call(Color.stroke, meanline.color);
-
- stylePoints(sel, trace, gd);
- });
- };
-
- },{"../../components/color":51,"../scatter/style":390,"d3":16}]},{},[11])(11)
- });
|