gantt.src.js 572 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257
  1. /**
  2. * @license Highcharts Gantt JS v9.1.0 (2021-05-04)
  3. *
  4. * Gantt series
  5. *
  6. * (c) 2016-2021 Lars A. V. Cabrera
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/modules/gantt', ['highcharts'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'Series/XRange/XRangePoint.js', [_modules['Core/Series/Point.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (Point, SeriesRegistry, U) {
  32. /* *
  33. *
  34. * X-range series module
  35. *
  36. * (c) 2010-2021 Torstein Honsi, Lars A. V. Cabrera
  37. *
  38. * License: www.highcharts.com/license
  39. *
  40. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41. *
  42. * */
  43. var __extends = (this && this.__extends) || (function () {
  44. var extendStatics = function (d,
  45. b) {
  46. extendStatics = Object.setPrototypeOf ||
  47. ({ __proto__: [] } instanceof Array && function (d,
  48. b) { d.__proto__ = b; }) ||
  49. function (d,
  50. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  51. return extendStatics(d, b);
  52. };
  53. return function (d, b) {
  54. extendStatics(d, b);
  55. function __() { this.constructor = d; }
  56. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  57. };
  58. })();
  59. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  60. var extend = U.extend;
  61. /* *
  62. *
  63. * Class
  64. *
  65. * */
  66. var XRangePoint = /** @class */ (function (_super) {
  67. __extends(XRangePoint, _super);
  68. function XRangePoint() {
  69. var _this = _super !== null && _super.apply(this,
  70. arguments) || this;
  71. /* *
  72. *
  73. * Properties
  74. *
  75. * */
  76. _this.options = void 0;
  77. _this.series = void 0;
  78. return _this;
  79. /* eslint-enable valid-jsdoc */
  80. }
  81. /* *
  82. *
  83. * Static properties
  84. *
  85. * */
  86. /**
  87. * Return color of a point based on its category.
  88. *
  89. * @private
  90. * @function getColorByCategory
  91. *
  92. * @param {object} series
  93. * The series which the point belongs to.
  94. *
  95. * @param {object} point
  96. * The point to calculate its color for.
  97. *
  98. * @return {object}
  99. * Returns an object containing the properties color and colorIndex.
  100. */
  101. XRangePoint.getColorByCategory = function (series, point) {
  102. var colors = series.options.colors || series.chart.options.colors,
  103. colorCount = colors ?
  104. colors.length :
  105. series.chart.options.chart.colorCount,
  106. colorIndex = point.y % colorCount,
  107. color = colors && colors[colorIndex];
  108. return {
  109. colorIndex: colorIndex,
  110. color: color
  111. };
  112. };
  113. /* *
  114. *
  115. * Functions
  116. *
  117. * */
  118. /**
  119. * The ending X value of the range point.
  120. * @name Highcharts.Point#x2
  121. * @type {number|undefined}
  122. * @requires modules/xrange
  123. */
  124. /**
  125. * Extend applyOptions so that `colorByPoint` for x-range means that one
  126. * color is applied per Y axis category.
  127. *
  128. * @private
  129. * @function Highcharts.Point#applyOptions
  130. *
  131. * @return {Highcharts.Series}
  132. */
  133. /* eslint-disable valid-jsdoc */
  134. /**
  135. * @private
  136. */
  137. XRangePoint.prototype.resolveColor = function () {
  138. var series = this.series,
  139. colorByPoint;
  140. if (series.options.colorByPoint && !this.options.color) {
  141. colorByPoint = XRangePoint.getColorByCategory(series, this);
  142. if (!series.chart.styledMode) {
  143. this.color = colorByPoint.color;
  144. }
  145. if (!this.options.colorIndex) {
  146. this.colorIndex = colorByPoint.colorIndex;
  147. }
  148. }
  149. else if (!this.color) {
  150. this.color = series.color;
  151. }
  152. };
  153. /**
  154. * Extend init to have y default to 0.
  155. *
  156. * @private
  157. * @function Highcharts.Point#init
  158. *
  159. * @return {Highcharts.Point}
  160. */
  161. XRangePoint.prototype.init = function () {
  162. Point.prototype.init.apply(this, arguments);
  163. if (!this.y) {
  164. this.y = 0;
  165. }
  166. return this;
  167. };
  168. /**
  169. * @private
  170. * @function Highcharts.Point#setState
  171. */
  172. XRangePoint.prototype.setState = function () {
  173. Point.prototype.setState.apply(this, arguments);
  174. this.series.drawPoint(this, this.series.getAnimationVerb());
  175. };
  176. /**
  177. * @private
  178. * @function Highcharts.Point#getLabelConfig
  179. *
  180. * @return {Highcharts.PointLabelObject}
  181. */
  182. // Add x2 and yCategory to the available properties for tooltip formats
  183. XRangePoint.prototype.getLabelConfig = function () {
  184. var point = this,
  185. cfg = Point.prototype.getLabelConfig.call(point),
  186. yCats = point.series.yAxis.categories;
  187. cfg.x2 = point.x2;
  188. cfg.yCategory = point.yCategory = yCats && yCats[point.y];
  189. return cfg;
  190. };
  191. /**
  192. * @private
  193. * @function Highcharts.Point#isValid
  194. *
  195. * @return {boolean}
  196. */
  197. XRangePoint.prototype.isValid = function () {
  198. return typeof this.x === 'number' &&
  199. typeof this.x2 === 'number';
  200. };
  201. return XRangePoint;
  202. }(ColumnSeries.prototype.pointClass));
  203. extend(XRangePoint.prototype, {
  204. tooltipDateKeys: ['x', 'x2']
  205. });
  206. /* *
  207. *
  208. * Default Export
  209. *
  210. * */
  211. return XRangePoint;
  212. });
  213. _registerModule(_modules, 'Series/XRange/XRangeComposition.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Utilities.js']], function (Axis, U) {
  214. /* *
  215. *
  216. * X-range series module
  217. *
  218. * (c) 2010-2021 Torstein Honsi, Lars A. V. Cabrera
  219. *
  220. * License: www.highcharts.com/license
  221. *
  222. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  223. *
  224. * */
  225. /* *
  226. *
  227. * Imports
  228. *
  229. * */
  230. /* *
  231. *
  232. * Imports
  233. *
  234. * */
  235. var addEvent = U.addEvent,
  236. pick = U.pick;
  237. /**
  238. * Max x2 should be considered in xAxis extremes
  239. */
  240. addEvent(Axis, 'afterGetSeriesExtremes', function () {
  241. var axis = this, // eslint-disable-line no-invalid-this
  242. axisSeries = axis.series,
  243. dataMax,
  244. modMax;
  245. if (axis.isXAxis) {
  246. dataMax = pick(axis.dataMax, -Number.MAX_VALUE);
  247. axisSeries.forEach(function (series) {
  248. if (series.x2Data) {
  249. series.x2Data
  250. .forEach(function (val) {
  251. if (val > dataMax) {
  252. dataMax = val;
  253. modMax = true;
  254. }
  255. });
  256. }
  257. });
  258. if (modMax) {
  259. axis.dataMax = dataMax;
  260. }
  261. }
  262. });
  263. });
  264. _registerModule(_modules, 'Series/XRange/XRangeSeries.js', [_modules['Core/Globals.js'], _modules['Core/Color/Color.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js'], _modules['Series/XRange/XRangePoint.js']], function (H, Color, SeriesRegistry, U, XRangePoint) {
  265. /* *
  266. *
  267. * X-range series module
  268. *
  269. * (c) 2010-2021 Torstein Honsi, Lars A. V. Cabrera
  270. *
  271. * License: www.highcharts.com/license
  272. *
  273. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  274. *
  275. * */
  276. var __extends = (this && this.__extends) || (function () {
  277. var extendStatics = function (d,
  278. b) {
  279. extendStatics = Object.setPrototypeOf ||
  280. ({ __proto__: [] } instanceof Array && function (d,
  281. b) { d.__proto__ = b; }) ||
  282. function (d,
  283. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  284. return extendStatics(d, b);
  285. };
  286. return function (d, b) {
  287. extendStatics(d, b);
  288. function __() { this.constructor = d; }
  289. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  290. };
  291. })();
  292. var color = Color.parse;
  293. var Series = SeriesRegistry.series,
  294. ColumnSeries = SeriesRegistry.seriesTypes.column;
  295. var columnProto = ColumnSeries.prototype;
  296. var clamp = U.clamp,
  297. correctFloat = U.correctFloat,
  298. defined = U.defined,
  299. extend = U.extend,
  300. find = U.find,
  301. isNumber = U.isNumber,
  302. isObject = U.isObject,
  303. merge = U.merge,
  304. pick = U.pick;
  305. /* *
  306. * @interface Highcharts.PointOptionsObject in parts/Point.ts
  307. */ /**
  308. * The ending X value of the range point.
  309. * @name Highcharts.PointOptionsObject#x2
  310. * @type {number|undefined}
  311. * @requires modules/xrange
  312. */
  313. /**
  314. * @private
  315. * @class
  316. * @name Highcharts.seriesTypes.xrange
  317. *
  318. * @augments Highcharts.Series
  319. */
  320. var XRangeSeries = /** @class */ (function (_super) {
  321. __extends(XRangeSeries, _super);
  322. function XRangeSeries() {
  323. var _this = _super !== null && _super.apply(this,
  324. arguments) || this;
  325. /* *
  326. *
  327. * Properties
  328. *
  329. * */
  330. _this.data = void 0;
  331. _this.options = void 0;
  332. _this.points = void 0;
  333. return _this;
  334. /*
  335. // Override to remove stroke from points. For partial fill.
  336. pointAttribs: function () {
  337. let series = this,
  338. retVal = columnType.prototype.pointAttribs
  339. .apply(series, arguments);
  340. //retVal['stroke-width'] = 0;
  341. return retVal;
  342. }
  343. //*/
  344. /* eslint-enable valid-jsdoc */
  345. }
  346. /* *
  347. *
  348. * Functions
  349. *
  350. * */
  351. /* eslint-disable valid-jsdoc */
  352. /**
  353. * @private
  354. * @function Highcarts.seriesTypes.xrange#init
  355. * @return {void}
  356. */
  357. XRangeSeries.prototype.init = function () {
  358. ColumnSeries.prototype.init.apply(this, arguments);
  359. this.options.stacking = void 0; // #13161
  360. };
  361. /**
  362. * Borrow the column series metrics, but with swapped axes. This gives
  363. * free access to features like groupPadding, grouping, pointWidth etc.
  364. *
  365. * @private
  366. * @function Highcharts.Series#getColumnMetrics
  367. *
  368. * @return {Highcharts.ColumnMetricsObject}
  369. */
  370. XRangeSeries.prototype.getColumnMetrics = function () {
  371. var metrics,
  372. chart = this.chart;
  373. /**
  374. * @private
  375. */
  376. function swapAxes() {
  377. chart.series.forEach(function (s) {
  378. var xAxis = s.xAxis;
  379. s.xAxis = s.yAxis;
  380. s.yAxis = xAxis;
  381. });
  382. }
  383. swapAxes();
  384. metrics = columnProto.getColumnMetrics.call(this);
  385. swapAxes();
  386. return metrics;
  387. };
  388. /**
  389. * Override cropData to show a point where x or x2 is outside visible
  390. * range, but one of them is inside.
  391. *
  392. * @private
  393. * @function Highcharts.Series#cropData
  394. *
  395. * @param {Array<number>} xData
  396. *
  397. * @param {Array<number>} yData
  398. *
  399. * @param {number} min
  400. *
  401. * @param {number} max
  402. *
  403. * @param {number} [cropShoulder]
  404. *
  405. * @return {*}
  406. */
  407. XRangeSeries.prototype.cropData = function (xData, yData, min, max) {
  408. // Replace xData with x2Data to find the appropriate cropStart
  409. var cropData = Series.prototype.cropData,
  410. crop = cropData.call(this,
  411. this.x2Data,
  412. yData,
  413. min,
  414. max);
  415. // Re-insert the cropped xData
  416. crop.xData = xData.slice(crop.start, crop.end);
  417. return crop;
  418. };
  419. /**
  420. * Finds the index of an existing point that matches the given point
  421. * options.
  422. *
  423. * @private
  424. * @function Highcharts.Series#findPointIndex
  425. * @param {object} options The options of the point.
  426. * @returns {number|undefined} Returns index of a matching point,
  427. * returns undefined if no match is found.
  428. */
  429. XRangeSeries.prototype.findPointIndex = function (options) {
  430. var _a = this,
  431. cropped = _a.cropped,
  432. cropStart = _a.cropStart,
  433. points = _a.points;
  434. var id = options.id;
  435. var pointIndex;
  436. if (id) {
  437. var point = find(points,
  438. function (point) {
  439. return point.id === id;
  440. });
  441. pointIndex = point ? point.index : void 0;
  442. }
  443. if (typeof pointIndex === 'undefined') {
  444. var point = find(points,
  445. function (point) {
  446. return (point.x === options.x &&
  447. point.x2 === options.x2 &&
  448. !point.touched);
  449. });
  450. pointIndex = point ? point.index : void 0;
  451. }
  452. // Reduce pointIndex if data is cropped
  453. if (cropped &&
  454. isNumber(pointIndex) &&
  455. isNumber(cropStart) &&
  456. pointIndex >= cropStart) {
  457. pointIndex -= cropStart;
  458. }
  459. return pointIndex;
  460. };
  461. /**
  462. * @private
  463. * @function Highcharts.Series#translatePoint
  464. *
  465. * @param {Highcharts.Point} point
  466. */
  467. XRangeSeries.prototype.translatePoint = function (point) {
  468. var series = this,
  469. xAxis = series.xAxis,
  470. yAxis = series.yAxis,
  471. metrics = series.columnMetrics,
  472. options = series.options,
  473. minPointLength = options.minPointLength || 0,
  474. oldColWidth = (point.shapeArgs && point.shapeArgs.width || 0) / 2,
  475. seriesXOffset = series.pointXOffset = metrics.offset,
  476. plotX = point.plotX,
  477. posX = pick(point.x2,
  478. point.x + (point.len || 0)),
  479. plotX2 = xAxis.translate(posX, 0, 0, 0, 1),
  480. length = Math.abs(plotX2 - plotX),
  481. widthDifference,
  482. partialFill,
  483. inverted = this.chart.inverted,
  484. borderWidth = pick(options.borderWidth, 1),
  485. crisper = borderWidth % 2 / 2,
  486. yOffset = metrics.offset,
  487. pointHeight = Math.round(metrics.width),
  488. dlLeft,
  489. dlRight,
  490. dlWidth,
  491. clipRectWidth,
  492. tooltipYOffset;
  493. if (minPointLength) {
  494. widthDifference = minPointLength - length;
  495. if (widthDifference < 0) {
  496. widthDifference = 0;
  497. }
  498. plotX -= widthDifference / 2;
  499. plotX2 += widthDifference / 2;
  500. }
  501. plotX = Math.max(plotX, -10);
  502. plotX2 = clamp(plotX2, -10, xAxis.len + 10);
  503. // Handle individual pointWidth
  504. if (defined(point.options.pointWidth)) {
  505. yOffset -= ((Math.ceil(point.options.pointWidth) - pointHeight) / 2);
  506. pointHeight = Math.ceil(point.options.pointWidth);
  507. }
  508. // Apply pointPlacement to the Y axis
  509. if (options.pointPlacement &&
  510. isNumber(point.plotY) &&
  511. yAxis.categories) {
  512. point.plotY = yAxis.translate(point.y, 0, 1, 0, 1, options.pointPlacement);
  513. }
  514. var shapeArgs = {
  515. x: Math.floor(Math.min(plotX,
  516. plotX2)) + crisper,
  517. y: Math.floor(point.plotY + yOffset) + crisper,
  518. width: Math.round(Math.abs(plotX2 - plotX)),
  519. height: pointHeight,
  520. r: series.options.borderRadius
  521. };
  522. point.shapeArgs = shapeArgs;
  523. // Move tooltip to default position
  524. if (!inverted) {
  525. point.tooltipPos[0] -= oldColWidth +
  526. seriesXOffset -
  527. shapeArgs.width / 2;
  528. }
  529. else {
  530. point.tooltipPos[1] += seriesXOffset +
  531. oldColWidth;
  532. }
  533. // Align data labels inside the shape and inside the plot area
  534. dlLeft = shapeArgs.x;
  535. dlRight = dlLeft + shapeArgs.width;
  536. if (dlLeft < 0 || dlRight > xAxis.len) {
  537. dlLeft = clamp(dlLeft, 0, xAxis.len);
  538. dlRight = clamp(dlRight, 0, xAxis.len);
  539. dlWidth = dlRight - dlLeft;
  540. point.dlBox = merge(shapeArgs, {
  541. x: dlLeft,
  542. width: dlRight - dlLeft,
  543. centerX: dlWidth ? dlWidth / 2 : null
  544. });
  545. }
  546. else {
  547. point.dlBox = null;
  548. }
  549. // Tooltip position
  550. var tooltipPos = point.tooltipPos;
  551. var xIndex = !inverted ? 0 : 1;
  552. var yIndex = !inverted ? 1 : 0;
  553. tooltipYOffset = series.columnMetrics ?
  554. series.columnMetrics.offset : -metrics.width / 2;
  555. // Centering tooltip position (#14147)
  556. if (!inverted) {
  557. tooltipPos[xIndex] += (xAxis.reversed ? -1 : 0) * shapeArgs.width;
  558. }
  559. else {
  560. tooltipPos[xIndex] += shapeArgs.width / 2;
  561. }
  562. tooltipPos[yIndex] = clamp(tooltipPos[yIndex] + ((inverted ? -1 : 1) * tooltipYOffset), 0, yAxis.len - 1);
  563. // Add a partShapeArgs to the point, based on the shapeArgs property
  564. partialFill = point.partialFill;
  565. if (partialFill) {
  566. // Get the partial fill amount
  567. if (isObject(partialFill)) {
  568. partialFill = partialFill.amount;
  569. }
  570. // If it was not a number, assume 0
  571. if (!isNumber(partialFill)) {
  572. partialFill = 0;
  573. }
  574. point.partShapeArgs = merge(shapeArgs, {
  575. r: series.options.borderRadius
  576. });
  577. clipRectWidth = Math.max(Math.round(length * partialFill + point.plotX -
  578. plotX), 0);
  579. point.clipRectArgs = {
  580. x: xAxis.reversed ? // #10717
  581. shapeArgs.x + length - clipRectWidth :
  582. shapeArgs.x,
  583. y: shapeArgs.y,
  584. width: clipRectWidth,
  585. height: shapeArgs.height
  586. };
  587. }
  588. };
  589. /**
  590. * @private
  591. * @function Highcharts.Series#translate
  592. */
  593. XRangeSeries.prototype.translate = function () {
  594. columnProto.translate.apply(this, arguments);
  595. this.points.forEach(function (point) {
  596. this.translatePoint(point);
  597. }, this);
  598. };
  599. /**
  600. * Draws a single point in the series. Needed for partial fill.
  601. *
  602. * This override turns point.graphic into a group containing the
  603. * original graphic and an overlay displaying the partial fill.
  604. *
  605. * @private
  606. * @function Highcharts.Series#drawPoint
  607. *
  608. * @param {Highcharts.Point} point
  609. * An instance of Point in the series.
  610. *
  611. * @param {"animate"|"attr"} verb
  612. * 'animate' (animates changes) or 'attr' (sets options)
  613. */
  614. XRangeSeries.prototype.drawPoint = function (point, verb) {
  615. var series = this,
  616. seriesOpts = series.options,
  617. renderer = series.chart.renderer,
  618. graphic = point.graphic,
  619. type = point.shapeType,
  620. shapeArgs = point.shapeArgs,
  621. partShapeArgs = point.partShapeArgs,
  622. clipRectArgs = point.clipRectArgs,
  623. pfOptions = point.partialFill,
  624. cutOff = seriesOpts.stacking && !seriesOpts.borderRadius,
  625. pointState = point.state,
  626. stateOpts = (seriesOpts.states[pointState || 'normal'] ||
  627. {}),
  628. pointStateVerb = typeof pointState === 'undefined' ?
  629. 'attr' : verb,
  630. pointAttr = series.pointAttribs(point,
  631. pointState),
  632. animation = pick(series.chart.options.chart.animation,
  633. stateOpts.animation),
  634. fill;
  635. if (!point.isNull && point.visible !== false) {
  636. // Original graphic
  637. if (graphic) { // update
  638. graphic.rect[verb](shapeArgs);
  639. }
  640. else {
  641. point.graphic = graphic = renderer.g('point')
  642. .addClass(point.getClassName())
  643. .add(point.group || series.group);
  644. graphic.rect = renderer[type](merge(shapeArgs))
  645. .addClass(point.getClassName())
  646. .addClass('highcharts-partfill-original')
  647. .add(graphic);
  648. }
  649. // Partial fill graphic
  650. if (partShapeArgs) {
  651. if (graphic.partRect) {
  652. graphic.partRect[verb](merge(partShapeArgs));
  653. graphic.partialClipRect[verb](merge(clipRectArgs));
  654. }
  655. else {
  656. graphic.partialClipRect = renderer.clipRect(clipRectArgs.x, clipRectArgs.y, clipRectArgs.width, clipRectArgs.height);
  657. graphic.partRect =
  658. renderer[type](partShapeArgs)
  659. .addClass('highcharts-partfill-overlay')
  660. .add(graphic)
  661. .clip(graphic.partialClipRect);
  662. }
  663. }
  664. // Presentational
  665. if (!series.chart.styledMode) {
  666. graphic
  667. .rect[verb](pointAttr, animation)
  668. .shadow(seriesOpts.shadow, null, cutOff);
  669. if (partShapeArgs) {
  670. // Ensure pfOptions is an object
  671. if (!isObject(pfOptions)) {
  672. pfOptions = {};
  673. }
  674. if (isObject(seriesOpts.partialFill)) {
  675. pfOptions = merge(seriesOpts.partialFill, pfOptions);
  676. }
  677. fill = (pfOptions.fill ||
  678. color(pointAttr.fill).brighten(-0.3).get() ||
  679. color(point.color || series.color)
  680. .brighten(-0.3).get());
  681. pointAttr.fill = fill;
  682. graphic
  683. .partRect[pointStateVerb](pointAttr, animation)
  684. .shadow(seriesOpts.shadow, null, cutOff);
  685. }
  686. }
  687. }
  688. else if (graphic) {
  689. point.graphic = graphic.destroy(); // #1269
  690. }
  691. };
  692. /**
  693. * @private
  694. * @function Highcharts.Series#drawPoints
  695. */
  696. XRangeSeries.prototype.drawPoints = function () {
  697. var series = this,
  698. verb = series.getAnimationVerb();
  699. // Draw the columns
  700. series.points.forEach(function (point) {
  701. series.drawPoint(point, verb);
  702. });
  703. };
  704. /**
  705. * Returns "animate", or "attr" if the number of points is above the
  706. * animation limit.
  707. *
  708. * @private
  709. * @function Highcharts.Series#getAnimationVerb
  710. *
  711. * @return {string}
  712. */
  713. XRangeSeries.prototype.getAnimationVerb = function () {
  714. return (this.chart.pointCount < (this.options.animationLimit || 250) ?
  715. 'animate' :
  716. 'attr');
  717. };
  718. /**
  719. * @private
  720. * @function Highcharts.XRangeSeries#isPointInside
  721. */
  722. XRangeSeries.prototype.isPointInside = function (point) {
  723. var shapeArgs = point.shapeArgs,
  724. plotX = point.plotX,
  725. plotY = point.plotY;
  726. if (!shapeArgs) {
  727. return _super.prototype.isPointInside.apply(this, arguments);
  728. }
  729. var isInside = typeof plotX !== 'undefined' &&
  730. typeof plotY !== 'undefined' &&
  731. plotY >= 0 &&
  732. plotY <= this.yAxis.len &&
  733. (shapeArgs.x || 0) + (shapeArgs.width || 0) >= 0 &&
  734. plotX <= this.xAxis.len;
  735. return isInside;
  736. };
  737. /* *
  738. *
  739. * Static properties
  740. *
  741. * */
  742. /**
  743. * The X-range series displays ranges on the X axis, typically time
  744. * intervals with a start and end date.
  745. *
  746. * @sample {highcharts} highcharts/demo/x-range/
  747. * X-range
  748. * @sample {highcharts} highcharts/css/x-range/
  749. * Styled mode X-range
  750. * @sample {highcharts} highcharts/chart/inverted-xrange/
  751. * Inverted X-range
  752. *
  753. * @extends plotOptions.column
  754. * @since 6.0.0
  755. * @product highcharts highstock gantt
  756. * @excluding boostThreshold, crisp, cropThreshold, depth, edgeColor,
  757. * edgeWidth, findNearestPointBy, getExtremesFromAll,
  758. * negativeColor, pointInterval, pointIntervalUnit,
  759. * pointPlacement, pointRange, pointStart, softThreshold,
  760. * stacking, threshold, data, dataSorting, boostBlending
  761. * @requires modules/xrange
  762. * @optionparent plotOptions.xrange
  763. */
  764. XRangeSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  765. /**
  766. * A partial fill for each point, typically used to visualize how much
  767. * of a task is performed. The partial fill object can be set either on
  768. * series or point level.
  769. *
  770. * @sample {highcharts} highcharts/demo/x-range
  771. * X-range with partial fill
  772. *
  773. * @product highcharts highstock gantt
  774. * @apioption plotOptions.xrange.partialFill
  775. */
  776. /**
  777. * The fill color to be used for partial fills. Defaults to a darker
  778. * shade of the point color.
  779. *
  780. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  781. * @product highcharts highstock gantt
  782. * @apioption plotOptions.xrange.partialFill.fill
  783. */
  784. /**
  785. * A partial fill for each point, typically used to visualize how much
  786. * of a task is performed. See [completed](series.gantt.data.completed).
  787. *
  788. * @sample gantt/demo/progress-indicator
  789. * Gantt with progress indicator
  790. *
  791. * @product gantt
  792. * @apioption plotOptions.gantt.partialFill
  793. */
  794. /**
  795. * In an X-range series, this option makes all points of the same Y-axis
  796. * category the same color.
  797. */
  798. colorByPoint: true,
  799. dataLabels: {
  800. formatter: function () {
  801. var point = this.point,
  802. amount = point.partialFill;
  803. if (isObject(amount)) {
  804. amount = amount.amount;
  805. }
  806. if (isNumber(amount) && amount > 0) {
  807. return correctFloat(amount * 100) + '%';
  808. }
  809. },
  810. inside: true,
  811. verticalAlign: 'middle'
  812. },
  813. tooltip: {
  814. headerFormat: '<span style="font-size: 10px">{point.x} - {point.x2}</span><br/>',
  815. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.yCategory}</b><br/>'
  816. },
  817. borderRadius: 3,
  818. pointRange: 0
  819. });
  820. return XRangeSeries;
  821. }(ColumnSeries));
  822. extend(XRangeSeries.prototype, {
  823. type: 'xrange',
  824. parallelArrays: ['x', 'x2', 'y'],
  825. requireSorting: false,
  826. animate: Series.prototype.animate,
  827. cropShoulder: 1,
  828. getExtremesFromAll: true,
  829. autoIncrement: H.noop,
  830. buildKDTree: H.noop,
  831. pointClass: XRangePoint
  832. });
  833. SeriesRegistry.registerSeriesType('xrange', XRangeSeries);
  834. /* *
  835. *
  836. * Default Export
  837. *
  838. * */
  839. /* *
  840. *
  841. * API Options
  842. *
  843. * */
  844. /**
  845. * An `xrange` series. If the [type](#series.xrange.type) option is not
  846. * specified, it is inherited from [chart.type](#chart.type).
  847. *
  848. * @extends series,plotOptions.xrange
  849. * @excluding boostThreshold, crisp, cropThreshold, depth, edgeColor, edgeWidth,
  850. * findNearestPointBy, getExtremesFromAll, negativeColor,
  851. * pointInterval, pointIntervalUnit, pointPlacement, pointRange,
  852. * pointStart, softThreshold, stacking, threshold, dataSorting,
  853. * boostBlending
  854. * @product highcharts highstock gantt
  855. * @requires modules/xrange
  856. * @apioption series.xrange
  857. */
  858. /**
  859. * An array of data points for the series. For the `xrange` series type,
  860. * points can be given in the following ways:
  861. *
  862. * 1. An array of objects with named values. The objects are point configuration
  863. * objects as seen below.
  864. * ```js
  865. * data: [{
  866. * x: Date.UTC(2017, 0, 1),
  867. * x2: Date.UTC(2017, 0, 3),
  868. * name: "Test",
  869. * y: 0,
  870. * color: "#00FF00"
  871. * }, {
  872. * x: Date.UTC(2017, 0, 4),
  873. * x2: Date.UTC(2017, 0, 5),
  874. * name: "Deploy",
  875. * y: 1,
  876. * color: "#FF0000"
  877. * }]
  878. * ```
  879. *
  880. * @sample {highcharts} highcharts/series/data-array-of-objects/
  881. * Config objects
  882. *
  883. * @declare Highcharts.XrangePointOptionsObject
  884. * @type {Array<*>}
  885. * @extends series.line.data
  886. * @product highcharts highstock gantt
  887. * @apioption series.xrange.data
  888. */
  889. /**
  890. * The starting X value of the range point.
  891. *
  892. * @sample {highcharts} highcharts/demo/x-range
  893. * X-range
  894. *
  895. * @type {number}
  896. * @product highcharts highstock gantt
  897. * @apioption series.xrange.data.x
  898. */
  899. /**
  900. * The ending X value of the range point.
  901. *
  902. * @sample {highcharts} highcharts/demo/x-range
  903. * X-range
  904. *
  905. * @type {number}
  906. * @product highcharts highstock gantt
  907. * @apioption series.xrange.data.x2
  908. */
  909. /**
  910. * The Y value of the range point.
  911. *
  912. * @sample {highcharts} highcharts/demo/x-range
  913. * X-range
  914. *
  915. * @type {number}
  916. * @product highcharts highstock gantt
  917. * @apioption series.xrange.data.y
  918. */
  919. /**
  920. * A partial fill for each point, typically used to visualize how much of
  921. * a task is performed. The partial fill object can be set either on series
  922. * or point level.
  923. *
  924. * @sample {highcharts} highcharts/demo/x-range
  925. * X-range with partial fill
  926. *
  927. * @declare Highcharts.XrangePointPartialFillOptionsObject
  928. * @product highcharts highstock gantt
  929. * @apioption series.xrange.data.partialFill
  930. */
  931. /**
  932. * The amount of the X-range point to be filled. Values can be 0-1 and are
  933. * converted to percentages in the default data label formatter.
  934. *
  935. * @type {number}
  936. * @product highcharts highstock gantt
  937. * @apioption series.xrange.data.partialFill.amount
  938. */
  939. /**
  940. * The fill color to be used for partial fills. Defaults to a darker shade
  941. * of the point color.
  942. *
  943. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  944. * @product highcharts highstock gantt
  945. * @apioption series.xrange.data.partialFill.fill
  946. */
  947. ''; // adds doclets above to transpiled file
  948. return XRangeSeries;
  949. });
  950. _registerModule(_modules, 'Series/Gantt/GanttPoint.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, U) {
  951. /* *
  952. *
  953. * (c) 2016-2021 Highsoft AS
  954. *
  955. * Author: Lars A. V. Cabrera
  956. *
  957. * License: www.highcharts.com/license
  958. *
  959. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  960. *
  961. * */
  962. var __extends = (this && this.__extends) || (function () {
  963. var extendStatics = function (d,
  964. b) {
  965. extendStatics = Object.setPrototypeOf ||
  966. ({ __proto__: [] } instanceof Array && function (d,
  967. b) { d.__proto__ = b; }) ||
  968. function (d,
  969. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  970. return extendStatics(d, b);
  971. };
  972. return function (d, b) {
  973. extendStatics(d, b);
  974. function __() { this.constructor = d; }
  975. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  976. };
  977. })();
  978. var XRangePoint = SeriesRegistry.seriesTypes.xrange.prototype.pointClass;
  979. var pick = U.pick;
  980. /* *
  981. *
  982. * Class
  983. *
  984. * */
  985. var GanttPoint = /** @class */ (function (_super) {
  986. __extends(GanttPoint, _super);
  987. function GanttPoint() {
  988. /* *
  989. *
  990. * Static Functions
  991. *
  992. * */
  993. var _this = _super !== null && _super.apply(this,
  994. arguments) || this;
  995. _this.options = void 0;
  996. _this.series = void 0;
  997. return _this;
  998. /* eslint-enable valid-jsdoc */
  999. }
  1000. /* eslint-disable valid-jsdoc */
  1001. /**
  1002. * @private
  1003. */
  1004. GanttPoint.setGanttPointAliases = function (options) {
  1005. /**
  1006. * Add a value to options if the value exists.
  1007. * @private
  1008. */
  1009. function addIfExists(prop, val) {
  1010. if (typeof val !== 'undefined') {
  1011. options[prop] = val;
  1012. }
  1013. }
  1014. addIfExists('x', pick(options.start, options.x));
  1015. addIfExists('x2', pick(options.end, options.x2));
  1016. addIfExists('partialFill', pick(options.completed, options.partialFill));
  1017. };
  1018. /* *
  1019. *
  1020. * Functions
  1021. *
  1022. * */
  1023. /* eslint-disable valid-jsdoc */
  1024. /**
  1025. * Applies the options containing the x and y data and possible some
  1026. * extra properties. This is called on point init or from point.update.
  1027. *
  1028. * @private
  1029. * @function Highcharts.Point#applyOptions
  1030. *
  1031. * @param {object} options
  1032. * The point options
  1033. *
  1034. * @param {number} x
  1035. * The x value
  1036. *
  1037. * @return {Highcharts.Point}
  1038. * The Point instance
  1039. */
  1040. GanttPoint.prototype.applyOptions = function (options, x) {
  1041. var point = this,
  1042. ganttPoint;
  1043. ganttPoint = _super.prototype.applyOptions.call(point, options, x);
  1044. GanttPoint.setGanttPointAliases(ganttPoint);
  1045. return ganttPoint;
  1046. };
  1047. GanttPoint.prototype.isValid = function () {
  1048. return ((typeof this.start === 'number' ||
  1049. typeof this.x === 'number') &&
  1050. (typeof this.end === 'number' ||
  1051. typeof this.x2 === 'number' ||
  1052. this.milestone));
  1053. };
  1054. return GanttPoint;
  1055. }(XRangePoint));
  1056. /* *
  1057. *
  1058. * Default Export
  1059. *
  1060. * */
  1061. return GanttPoint;
  1062. });
  1063. _registerModule(_modules, 'Gantt/Tree.js', [_modules['Core/Utilities.js']], function (U) {
  1064. /* *
  1065. *
  1066. * (c) 2016-2021 Highsoft AS
  1067. *
  1068. * Authors: Jon Arild Nygard
  1069. *
  1070. * License: www.highcharts.com/license
  1071. *
  1072. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1073. *
  1074. * */
  1075. /* eslint no-console: 0 */
  1076. var extend = U.extend,
  1077. isNumber = U.isNumber,
  1078. pick = U.pick;
  1079. /**
  1080. * Creates an object map from parent id to childrens index.
  1081. *
  1082. * @private
  1083. * @function Highcharts.Tree#getListOfParents
  1084. *
  1085. * @param {Array<*>} data
  1086. * List of points set in options. `Array.parent` is parent id of point.
  1087. *
  1088. * @param {Array<string>} ids
  1089. * List of all point ids.
  1090. *
  1091. * @return {Highcharts.Dictionary<Array<*>>}
  1092. * Map from parent id to children index in data
  1093. */
  1094. var getListOfParents = function (data,
  1095. ids) {
  1096. var listOfParents = data.reduce(function (prev,
  1097. curr) {
  1098. var parent = pick(curr.parent, '');
  1099. if (typeof prev[parent] === 'undefined') {
  1100. prev[parent] = [];
  1101. }
  1102. prev[parent].push(curr);
  1103. return prev;
  1104. }, {}), parents = Object.keys(listOfParents);
  1105. // If parent does not exist, hoist parent to root of tree.
  1106. parents.forEach(function (parent, list) {
  1107. var children = listOfParents[parent];
  1108. if ((parent !== '') && (ids.indexOf(parent) === -1)) {
  1109. children.forEach(function (child) {
  1110. list[''].push(child);
  1111. });
  1112. delete list[parent];
  1113. }
  1114. });
  1115. return listOfParents;
  1116. };
  1117. var getNode = function (id,
  1118. parent,
  1119. level,
  1120. data,
  1121. mapOfIdToChildren,
  1122. options) {
  1123. var descendants = 0,
  1124. height = 0,
  1125. after = options && options.after,
  1126. before = options && options.before,
  1127. node = {
  1128. data: data,
  1129. depth: level - 1,
  1130. id: id,
  1131. level: level,
  1132. parent: parent
  1133. },
  1134. start,
  1135. end,
  1136. children;
  1137. // Allow custom logic before the children has been created.
  1138. if (typeof before === 'function') {
  1139. before(node, options);
  1140. }
  1141. // Call getNode recursively on the children. Calulate the height of the
  1142. // node, and the number of descendants.
  1143. children = ((mapOfIdToChildren[id] || [])).map(function (child) {
  1144. var node = getNode(child.id,
  1145. id, (level + 1),
  1146. child,
  1147. mapOfIdToChildren,
  1148. options),
  1149. childStart = child.start,
  1150. childEnd = (child.milestone === true ?
  1151. childStart :
  1152. child.end);
  1153. // Start should be the lowest child.start.
  1154. start = ((!isNumber(start) || childStart < start) ?
  1155. childStart :
  1156. start);
  1157. // End should be the largest child.end.
  1158. // If child is milestone, then use start as end.
  1159. end = ((!isNumber(end) || childEnd > end) ?
  1160. childEnd :
  1161. end);
  1162. descendants = descendants + 1 + node.descendants;
  1163. height = Math.max(node.height + 1, height);
  1164. return node;
  1165. });
  1166. // Calculate start and end for point if it is not already explicitly set.
  1167. if (data) {
  1168. data.start = pick(data.start, start);
  1169. data.end = pick(data.end, end);
  1170. }
  1171. extend(node, {
  1172. children: children,
  1173. descendants: descendants,
  1174. height: height
  1175. });
  1176. // Allow custom logic after the children has been created.
  1177. if (typeof after === 'function') {
  1178. after(node, options);
  1179. }
  1180. return node;
  1181. };
  1182. var getTree = function (data,
  1183. options) {
  1184. var ids = data.map(function (d) {
  1185. return d.id;
  1186. }), mapOfIdToChildren = getListOfParents(data, ids);
  1187. return getNode('', null, 1, null, mapOfIdToChildren, options);
  1188. };
  1189. var Tree = {
  1190. getListOfParents: getListOfParents,
  1191. getNode: getNode,
  1192. getTree: getTree
  1193. };
  1194. return Tree;
  1195. });
  1196. _registerModule(_modules, 'Core/Axis/TreeGridTick.js', [_modules['Core/Color/Palette.js'], _modules['Core/Utilities.js']], function (palette, U) {
  1197. /* *
  1198. *
  1199. * (c) 2016 Highsoft AS
  1200. * Authors: Jon Arild Nygard
  1201. *
  1202. * License: www.highcharts.com/license
  1203. *
  1204. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1205. *
  1206. * */
  1207. var addEvent = U.addEvent,
  1208. defined = U.defined,
  1209. isObject = U.isObject,
  1210. isNumber = U.isNumber,
  1211. pick = U.pick,
  1212. wrap = U.wrap;
  1213. /**
  1214. * @private
  1215. */
  1216. var TreeGridTick;
  1217. (function (TreeGridTick) {
  1218. /* *
  1219. *
  1220. * Interfaces
  1221. *
  1222. * */
  1223. /* *
  1224. *
  1225. * Variables
  1226. *
  1227. * */
  1228. var applied = false;
  1229. /* *
  1230. *
  1231. * Functions
  1232. *
  1233. * */
  1234. /**
  1235. * @private
  1236. */
  1237. function compose(TickClass) {
  1238. if (!applied) {
  1239. addEvent(TickClass, 'init', onInit);
  1240. wrap(TickClass.prototype, 'getLabelPosition', wrapGetLabelPosition);
  1241. wrap(TickClass.prototype, 'renderLabel', wrapRenderLabel);
  1242. // backwards compatibility
  1243. TickClass.prototype.collapse = function (redraw) {
  1244. this.treeGrid.collapse(redraw);
  1245. };
  1246. TickClass.prototype.expand = function (redraw) {
  1247. this.treeGrid.expand(redraw);
  1248. };
  1249. TickClass.prototype.toggleCollapse = function (redraw) {
  1250. this.treeGrid.toggleCollapse(redraw);
  1251. };
  1252. applied = true;
  1253. }
  1254. }
  1255. TreeGridTick.compose = compose;
  1256. /**
  1257. * @private
  1258. */
  1259. function onInit() {
  1260. var tick = this;
  1261. if (!tick.treeGrid) {
  1262. tick.treeGrid = new Additions(tick);
  1263. }
  1264. }
  1265. /**
  1266. * @private
  1267. */
  1268. function onTickHover(label) {
  1269. label.addClass('highcharts-treegrid-node-active');
  1270. if (!label.renderer.styledMode) {
  1271. label.css({
  1272. textDecoration: 'underline'
  1273. });
  1274. }
  1275. }
  1276. /**
  1277. * @private
  1278. */
  1279. function onTickHoverExit(label, options) {
  1280. var css = isObject(options.style) ? options.style : {};
  1281. label.removeClass('highcharts-treegrid-node-active');
  1282. if (!label.renderer.styledMode) {
  1283. label.css({ textDecoration: css.textDecoration });
  1284. }
  1285. }
  1286. /**
  1287. * @private
  1288. */
  1289. function renderLabelIcon(tick, params) {
  1290. var treeGrid = tick.treeGrid,
  1291. isNew = !treeGrid.labelIcon,
  1292. renderer = params.renderer,
  1293. labelBox = params.xy,
  1294. options = params.options,
  1295. width = options.width || 0,
  1296. height = options.height || 0,
  1297. iconCenter = {
  1298. x: labelBox.x - (width / 2) - (options.padding || 0),
  1299. y: labelBox.y - (height / 2)
  1300. },
  1301. rotation = params.collapsed ? 90 : 180,
  1302. shouldRender = params.show && isNumber(iconCenter.y);
  1303. var icon = treeGrid.labelIcon;
  1304. if (!icon) {
  1305. treeGrid.labelIcon = icon = renderer
  1306. .path(renderer.symbols[options.type](options.x || 0, options.y || 0, width, height))
  1307. .addClass('highcharts-label-icon')
  1308. .add(params.group);
  1309. }
  1310. // Set the new position, and show or hide
  1311. icon.attr({ y: shouldRender ? 0 : -9999 }); // #14904, #1338
  1312. // Presentational attributes
  1313. if (!renderer.styledMode) {
  1314. icon
  1315. .attr({
  1316. cursor: 'pointer',
  1317. 'fill': pick(params.color, palette.neutralColor60),
  1318. 'stroke-width': 1,
  1319. stroke: options.lineColor,
  1320. strokeWidth: options.lineWidth || 0
  1321. });
  1322. }
  1323. // Update the icon positions
  1324. icon[isNew ? 'attr' : 'animate']({
  1325. translateX: iconCenter.x,
  1326. translateY: iconCenter.y,
  1327. rotation: rotation
  1328. });
  1329. }
  1330. /**
  1331. * @private
  1332. */
  1333. function wrapGetLabelPosition(proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
  1334. var tick = this,
  1335. lbOptions = pick(tick.options && tick.options.labels,
  1336. labelOptions),
  1337. pos = tick.pos,
  1338. axis = tick.axis,
  1339. options = axis.options,
  1340. isTreeGrid = options.type === 'treegrid',
  1341. result = proceed.apply(tick,
  1342. [x,
  1343. y,
  1344. label,
  1345. horiz,
  1346. lbOptions,
  1347. tickmarkOffset,
  1348. index,
  1349. step]);
  1350. var symbolOptions,
  1351. indentation,
  1352. mapOfPosToGridNode,
  1353. node,
  1354. level;
  1355. if (isTreeGrid) {
  1356. symbolOptions = (lbOptions && isObject(lbOptions.symbol, true) ?
  1357. lbOptions.symbol :
  1358. {});
  1359. indentation = (lbOptions && isNumber(lbOptions.indentation) ?
  1360. lbOptions.indentation :
  1361. 0);
  1362. mapOfPosToGridNode = axis.treeGrid.mapOfPosToGridNode;
  1363. node = mapOfPosToGridNode && mapOfPosToGridNode[pos];
  1364. level = (node && node.depth) || 1;
  1365. result.x += (
  1366. // Add space for symbols
  1367. ((symbolOptions.width || 0) +
  1368. ((symbolOptions.padding || 0) * 2)) +
  1369. // Apply indentation
  1370. ((level - 1) * indentation));
  1371. }
  1372. return result;
  1373. }
  1374. /**
  1375. * @private
  1376. */
  1377. function wrapRenderLabel(proceed) {
  1378. var tick = this, pos = tick.pos, axis = tick.axis, label = tick.label, mapOfPosToGridNode = axis.treeGrid.mapOfPosToGridNode, options = axis.options, labelOptions = pick(tick.options && tick.options.labels, options && options.labels), symbolOptions = (labelOptions && isObject(labelOptions.symbol, true) ?
  1379. labelOptions.symbol :
  1380. {}), node = mapOfPosToGridNode && mapOfPosToGridNode[pos], level = node && node.depth, isTreeGrid = options.type === 'treegrid', shouldRender = axis.tickPositions.indexOf(pos) > -1, prefixClassName = 'highcharts-treegrid-node-', styledMode = axis.chart.styledMode;
  1381. var collapsed,
  1382. addClassName,
  1383. removeClassName;
  1384. if (isTreeGrid && node) {
  1385. // Add class name for hierarchical styling.
  1386. if (label &&
  1387. label.element) {
  1388. label.addClass(prefixClassName + 'level-' + level);
  1389. }
  1390. }
  1391. proceed.apply(tick, Array.prototype.slice.call(arguments, 1));
  1392. if (isTreeGrid &&
  1393. label &&
  1394. label.element &&
  1395. node &&
  1396. node.descendants &&
  1397. node.descendants > 0) {
  1398. collapsed = axis.treeGrid.isCollapsed(node);
  1399. renderLabelIcon(tick, {
  1400. color: !styledMode && label.styles && label.styles.color || '',
  1401. collapsed: collapsed,
  1402. group: label.parentGroup,
  1403. options: symbolOptions,
  1404. renderer: label.renderer,
  1405. show: shouldRender,
  1406. xy: label.xy
  1407. });
  1408. // Add class name for the node.
  1409. addClassName = prefixClassName +
  1410. (collapsed ? 'collapsed' : 'expanded');
  1411. removeClassName = prefixClassName +
  1412. (collapsed ? 'expanded' : 'collapsed');
  1413. label
  1414. .addClass(addClassName)
  1415. .removeClass(removeClassName);
  1416. if (!styledMode) {
  1417. label.css({
  1418. cursor: 'pointer'
  1419. });
  1420. }
  1421. // Add events to both label text and icon
  1422. [label, tick.treeGrid.labelIcon].forEach(function (object) {
  1423. if (object && !object.attachedTreeGridEvents) {
  1424. // On hover
  1425. addEvent(object.element, 'mouseover', function () {
  1426. onTickHover(label);
  1427. });
  1428. // On hover out
  1429. addEvent(object.element, 'mouseout', function () {
  1430. onTickHoverExit(label, labelOptions);
  1431. });
  1432. addEvent(object.element, 'click', function () {
  1433. tick.treeGrid.toggleCollapse();
  1434. });
  1435. object.attachedTreeGridEvents = true;
  1436. }
  1437. });
  1438. }
  1439. }
  1440. /* *
  1441. *
  1442. * Classes
  1443. *
  1444. * */
  1445. /**
  1446. * @private
  1447. * @class
  1448. */
  1449. var Additions = /** @class */ (function () {
  1450. /* *
  1451. *
  1452. * Constructors
  1453. *
  1454. * */
  1455. /**
  1456. * @private
  1457. */
  1458. function Additions(tick) {
  1459. this.tick = tick;
  1460. }
  1461. /* *
  1462. *
  1463. * Functions
  1464. *
  1465. * */
  1466. /**
  1467. * Collapse the grid cell. Used when axis is of type treegrid.
  1468. *
  1469. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  1470. *
  1471. * @private
  1472. * @function Highcharts.Tick#collapse
  1473. *
  1474. * @param {boolean} [redraw=true]
  1475. * Whether to redraw the chart or wait for an explicit call to
  1476. * {@link Highcharts.Chart#redraw}
  1477. */
  1478. Additions.prototype.collapse = function (redraw) {
  1479. var tick = this.tick,
  1480. axis = tick.axis,
  1481. brokenAxis = axis.brokenAxis;
  1482. if (brokenAxis &&
  1483. axis.treeGrid.mapOfPosToGridNode) {
  1484. var pos = tick.pos,
  1485. node = axis.treeGrid.mapOfPosToGridNode[pos],
  1486. breaks = axis.treeGrid.collapse(node);
  1487. brokenAxis.setBreaks(breaks, pick(redraw, true));
  1488. }
  1489. };
  1490. /**
  1491. * Expand the grid cell. Used when axis is of type treegrid.
  1492. *
  1493. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  1494. *
  1495. * @private
  1496. * @function Highcharts.Tick#expand
  1497. *
  1498. * @param {boolean} [redraw=true]
  1499. * Whether to redraw the chart or wait for an explicit call to
  1500. * {@link Highcharts.Chart#redraw}
  1501. */
  1502. Additions.prototype.expand = function (redraw) {
  1503. var tick = this.tick,
  1504. axis = tick.axis,
  1505. brokenAxis = axis.brokenAxis;
  1506. if (brokenAxis &&
  1507. axis.treeGrid.mapOfPosToGridNode) {
  1508. var pos = tick.pos,
  1509. node = axis.treeGrid.mapOfPosToGridNode[pos],
  1510. breaks = axis.treeGrid.expand(node);
  1511. brokenAxis.setBreaks(breaks, pick(redraw, true));
  1512. }
  1513. };
  1514. /**
  1515. * Toggle the collapse/expand state of the grid cell. Used when axis is
  1516. * of type treegrid.
  1517. *
  1518. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  1519. *
  1520. * @private
  1521. * @function Highcharts.Tick#toggleCollapse
  1522. *
  1523. * @param {boolean} [redraw=true]
  1524. * Whether to redraw the chart or wait for an explicit call to
  1525. * {@link Highcharts.Chart#redraw}
  1526. */
  1527. Additions.prototype.toggleCollapse = function (redraw) {
  1528. var tick = this.tick,
  1529. axis = tick.axis,
  1530. brokenAxis = axis.brokenAxis;
  1531. if (brokenAxis &&
  1532. axis.treeGrid.mapOfPosToGridNode) {
  1533. var pos = tick.pos,
  1534. node = axis.treeGrid.mapOfPosToGridNode[pos],
  1535. breaks = axis.treeGrid.toggleCollapse(node);
  1536. brokenAxis.setBreaks(breaks, pick(redraw, true));
  1537. }
  1538. };
  1539. return Additions;
  1540. }());
  1541. TreeGridTick.Additions = Additions;
  1542. })(TreeGridTick || (TreeGridTick = {}));
  1543. return TreeGridTick;
  1544. });
  1545. _registerModule(_modules, 'Mixins/TreeSeries.js', [_modules['Core/Color/Color.js'], _modules['Core/Utilities.js']], function (Color, U) {
  1546. /* *
  1547. *
  1548. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1549. *
  1550. * */
  1551. var extend = U.extend,
  1552. isArray = U.isArray,
  1553. isNumber = U.isNumber,
  1554. isObject = U.isObject,
  1555. merge = U.merge,
  1556. pick = U.pick;
  1557. var isBoolean = function (x) {
  1558. return typeof x === 'boolean';
  1559. }, isFn = function (x) {
  1560. return typeof x === 'function';
  1561. };
  1562. /* eslint-disable valid-jsdoc */
  1563. /**
  1564. * @todo Combine buildTree and buildNode with setTreeValues
  1565. * @todo Remove logic from Treemap and make it utilize this mixin.
  1566. * @private
  1567. */
  1568. var setTreeValues = function setTreeValues(tree,
  1569. options) {
  1570. var before = options.before,
  1571. idRoot = options.idRoot,
  1572. mapIdToNode = options.mapIdToNode,
  1573. nodeRoot = mapIdToNode[idRoot],
  1574. levelIsConstant = (isBoolean(options.levelIsConstant) ?
  1575. options.levelIsConstant :
  1576. true),
  1577. points = options.points,
  1578. point = points[tree.i],
  1579. optionsPoint = point && point.options || {},
  1580. childrenTotal = 0,
  1581. children = [],
  1582. value;
  1583. tree.levelDynamic = tree.level - (levelIsConstant ? 0 : nodeRoot.level);
  1584. tree.name = pick(point && point.name, '');
  1585. tree.visible = (idRoot === tree.id ||
  1586. (isBoolean(options.visible) ? options.visible : false));
  1587. if (isFn(before)) {
  1588. tree = before(tree, options);
  1589. }
  1590. // First give the children some values
  1591. tree.children.forEach(function (child, i) {
  1592. var newOptions = extend({},
  1593. options);
  1594. extend(newOptions, {
  1595. index: i,
  1596. siblings: tree.children.length,
  1597. visible: tree.visible
  1598. });
  1599. child = setTreeValues(child, newOptions);
  1600. children.push(child);
  1601. if (child.visible) {
  1602. childrenTotal += child.val;
  1603. }
  1604. });
  1605. tree.visible = childrenTotal > 0 || tree.visible;
  1606. // Set the values
  1607. value = pick(optionsPoint.value, childrenTotal);
  1608. tree.children = children;
  1609. tree.childrenTotal = childrenTotal;
  1610. tree.isLeaf = tree.visible && !childrenTotal;
  1611. tree.val = value;
  1612. return tree;
  1613. };
  1614. /**
  1615. * @private
  1616. */
  1617. var getColor = function getColor(node,
  1618. options) {
  1619. var index = options.index,
  1620. mapOptionsToLevel = options.mapOptionsToLevel,
  1621. parentColor = options.parentColor,
  1622. parentColorIndex = options.parentColorIndex,
  1623. series = options.series,
  1624. colors = options.colors,
  1625. siblings = options.siblings,
  1626. points = series.points,
  1627. getColorByPoint,
  1628. chartOptionsChart = series.chart.options.chart,
  1629. point,
  1630. level,
  1631. colorByPoint,
  1632. colorIndexByPoint,
  1633. color,
  1634. colorIndex;
  1635. /**
  1636. * @private
  1637. */
  1638. function variation(color) {
  1639. var colorVariation = level && level.colorVariation;
  1640. if (colorVariation) {
  1641. if (colorVariation.key === 'brightness') {
  1642. return Color.parse(color).brighten(colorVariation.to * (index / siblings)).get();
  1643. }
  1644. }
  1645. return color;
  1646. }
  1647. if (node) {
  1648. point = points[node.i];
  1649. level = mapOptionsToLevel[node.level] || {};
  1650. getColorByPoint = point && level.colorByPoint;
  1651. if (getColorByPoint) {
  1652. colorIndexByPoint = point.index % (colors ?
  1653. colors.length :
  1654. chartOptionsChart.colorCount);
  1655. colorByPoint = colors && colors[colorIndexByPoint];
  1656. }
  1657. // Select either point color, level color or inherited color.
  1658. if (!series.chart.styledMode) {
  1659. color = pick(point && point.options.color, level && level.color, colorByPoint, parentColor && variation(parentColor), series.color);
  1660. }
  1661. colorIndex = pick(point && point.options.colorIndex, level && level.colorIndex, colorIndexByPoint, parentColorIndex, options.colorIndex);
  1662. }
  1663. return {
  1664. color: color,
  1665. colorIndex: colorIndex
  1666. };
  1667. };
  1668. /**
  1669. * Creates a map from level number to its given options.
  1670. *
  1671. * @private
  1672. * @function getLevelOptions
  1673. * @param {object} params
  1674. * Object containing parameters.
  1675. * - `defaults` Object containing default options. The default options
  1676. * are merged with the userOptions to get the final options for a
  1677. * specific level.
  1678. * - `from` The lowest level number.
  1679. * - `levels` User options from series.levels.
  1680. * - `to` The highest level number.
  1681. * @return {Highcharts.Dictionary<object>|null}
  1682. * Returns a map from level number to its given options.
  1683. */
  1684. var getLevelOptions = function getLevelOptions(params) {
  1685. var result = null,
  1686. defaults,
  1687. converted,
  1688. i,
  1689. from,
  1690. to,
  1691. levels;
  1692. if (isObject(params)) {
  1693. result = {};
  1694. from = isNumber(params.from) ? params.from : 1;
  1695. levels = params.levels;
  1696. converted = {};
  1697. defaults = isObject(params.defaults) ? params.defaults : {};
  1698. if (isArray(levels)) {
  1699. converted = levels.reduce(function (obj, item) {
  1700. var level,
  1701. levelIsConstant,
  1702. options;
  1703. if (isObject(item) && isNumber(item.level)) {
  1704. options = merge({}, item);
  1705. levelIsConstant = (isBoolean(options.levelIsConstant) ?
  1706. options.levelIsConstant :
  1707. defaults.levelIsConstant);
  1708. // Delete redundant properties.
  1709. delete options.levelIsConstant;
  1710. delete options.level;
  1711. // Calculate which level these options apply to.
  1712. level = item.level + (levelIsConstant ? 0 : from - 1);
  1713. if (isObject(obj[level])) {
  1714. extend(obj[level], options);
  1715. }
  1716. else {
  1717. obj[level] = options;
  1718. }
  1719. }
  1720. return obj;
  1721. }, {});
  1722. }
  1723. to = isNumber(params.to) ? params.to : 1;
  1724. for (i = 0; i <= to; i++) {
  1725. result[i] = merge({}, defaults, isObject(converted[i]) ? converted[i] : {});
  1726. }
  1727. }
  1728. return result;
  1729. };
  1730. /**
  1731. * Update the rootId property on the series. Also makes sure that it is
  1732. * accessible to exporting.
  1733. *
  1734. * @private
  1735. * @function updateRootId
  1736. *
  1737. * @param {object} series
  1738. * The series to operate on.
  1739. *
  1740. * @return {string}
  1741. * Returns the resulting rootId after update.
  1742. */
  1743. var updateRootId = function (series) {
  1744. var rootId,
  1745. options;
  1746. if (isObject(series)) {
  1747. // Get the series options.
  1748. options = isObject(series.options) ? series.options : {};
  1749. // Calculate the rootId.
  1750. rootId = pick(series.rootNode, options.rootId, '');
  1751. // Set rootId on series.userOptions to pick it up in exporting.
  1752. if (isObject(series.userOptions)) {
  1753. series.userOptions.rootId = rootId;
  1754. }
  1755. // Set rootId on series to pick it up on next update.
  1756. series.rootNode = rootId;
  1757. }
  1758. return rootId;
  1759. };
  1760. var result = {
  1761. getColor: getColor,
  1762. getLevelOptions: getLevelOptions,
  1763. setTreeValues: setTreeValues,
  1764. updateRootId: updateRootId
  1765. };
  1766. return result;
  1767. });
  1768. _registerModule(_modules, 'Core/Axis/GridAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Axis/Tick.js'], _modules['Core/Utilities.js']], function (Axis, H, Tick, U) {
  1769. /* *
  1770. *
  1771. * (c) 2016 Highsoft AS
  1772. * Authors: Lars A. V. Cabrera
  1773. *
  1774. * License: www.highcharts.com/license
  1775. *
  1776. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1777. *
  1778. * */
  1779. var addEvent = U.addEvent,
  1780. defined = U.defined,
  1781. erase = U.erase,
  1782. find = U.find,
  1783. isArray = U.isArray,
  1784. isNumber = U.isNumber,
  1785. merge = U.merge,
  1786. pick = U.pick,
  1787. timeUnits = U.timeUnits,
  1788. wrap = U.wrap;
  1789. var argsToArray = function (args) {
  1790. return Array.prototype.slice.call(args, 1);
  1791. }, isObject = function (x) {
  1792. // Always use strict mode
  1793. return U.isObject(x, true);
  1794. }, Chart = H.Chart;
  1795. var applyGridOptions = function applyGridOptions(axis) {
  1796. var options = axis.options;
  1797. // Center-align by default
  1798. /*
  1799. if (!options.labels) {
  1800. options.labels = {};
  1801. }
  1802. */
  1803. options.labels.align = pick(options.labels.align, 'center');
  1804. // @todo: Check against tickLabelPlacement between/on etc
  1805. /* Prevents adding the last tick label if the axis is not a category
  1806. axis.
  1807. Since numeric labels are normally placed at starts and ends of a
  1808. range of value, and this module makes the label point at the value,
  1809. an "extra" label would appear. */
  1810. if (!axis.categories) {
  1811. options.showLastLabel = false;
  1812. }
  1813. // Prevents rotation of labels when squished, as rotating them would not
  1814. // help.
  1815. axis.labelRotation = 0;
  1816. options.labels.rotation = 0;
  1817. };
  1818. /**
  1819. * @productdesc {gantt}
  1820. * For grid axes (like in Gantt charts),
  1821. * it is possible to declare as a list to provide different
  1822. * formats depending on available space.
  1823. *
  1824. * Defaults to:
  1825. * ```js
  1826. * {
  1827. * hour: { list: ['%H:%M', '%H'] },
  1828. * day: { list: ['%A, %e. %B', '%a, %e. %b', '%E'] },
  1829. * week: { list: ['Week %W', 'W%W'] },
  1830. * month: { list: ['%B', '%b', '%o'] }
  1831. * }
  1832. * ```
  1833. *
  1834. * @sample {gantt} gantt/grid-axis/date-time-label-formats
  1835. * Gantt chart with custom axis date format.
  1836. *
  1837. * @apioption xAxis.dateTimeLabelFormats
  1838. */
  1839. /**
  1840. * Set grid options for the axis labels. Requires Highcharts Gantt.
  1841. *
  1842. * @since 6.2.0
  1843. * @product gantt
  1844. * @apioption xAxis.grid
  1845. */
  1846. /**
  1847. * Enable grid on the axis labels. Defaults to true for Gantt charts.
  1848. *
  1849. * @type {boolean}
  1850. * @default true
  1851. * @since 6.2.0
  1852. * @product gantt
  1853. * @apioption xAxis.grid.enabled
  1854. */
  1855. /**
  1856. * Set specific options for each column (or row for horizontal axes) in the
  1857. * grid. Each extra column/row is its own axis, and the axis options can be set
  1858. * here.
  1859. *
  1860. * @sample gantt/demo/left-axis-table
  1861. * Left axis as a table
  1862. *
  1863. * @type {Array<Highcharts.XAxisOptions>}
  1864. * @apioption xAxis.grid.columns
  1865. */
  1866. /**
  1867. * Set border color for the label grid lines.
  1868. *
  1869. * @type {Highcharts.ColorString}
  1870. * @apioption xAxis.grid.borderColor
  1871. */
  1872. /**
  1873. * Set border width of the label grid lines.
  1874. *
  1875. * @type {number}
  1876. * @default 1
  1877. * @apioption xAxis.grid.borderWidth
  1878. */
  1879. /**
  1880. * Set cell height for grid axis labels. By default this is calculated from font
  1881. * size. This option only applies to horizontal axes.
  1882. *
  1883. * @sample gantt/grid-axis/cellheight
  1884. * Gant chart with custom cell height
  1885. * @type {number}
  1886. * @apioption xAxis.grid.cellHeight
  1887. */
  1888. ''; // detach doclets above
  1889. /**
  1890. * Get the largest label width and height.
  1891. *
  1892. * @private
  1893. * @function Highcharts.Axis#getMaxLabelDimensions
  1894. *
  1895. * @param {Highcharts.Dictionary<Highcharts.Tick>} ticks
  1896. * All the ticks on one axis.
  1897. *
  1898. * @param {Array<number|string>} tickPositions
  1899. * All the tick positions on one axis.
  1900. *
  1901. * @return {Highcharts.SizeObject}
  1902. * Object containing the properties height and width.
  1903. *
  1904. * @todo Move this to the generic axis implementation, as it is used there.
  1905. */
  1906. Axis.prototype.getMaxLabelDimensions = function (ticks, tickPositions) {
  1907. var dimensions = {
  1908. width: 0,
  1909. height: 0
  1910. };
  1911. tickPositions.forEach(function (pos) {
  1912. var tick = ticks[pos],
  1913. labelHeight = 0,
  1914. labelWidth = 0,
  1915. label;
  1916. if (isObject(tick)) {
  1917. label = isObject(tick.label) ? tick.label : {};
  1918. // Find width and height of label
  1919. labelHeight = label.getBBox ? label.getBBox().height : 0;
  1920. if (label.textStr && !isNumber(label.textPxLength)) {
  1921. label.textPxLength = label.getBBox().width;
  1922. }
  1923. labelWidth = isNumber(label.textPxLength) ?
  1924. // Math.round ensures crisp lines
  1925. Math.round(label.textPxLength) :
  1926. 0;
  1927. if (label.textStr) {
  1928. // Set the tickWidth same as the label width after ellipsis
  1929. // applied #10281
  1930. labelWidth = Math.round(label.getBBox().width);
  1931. }
  1932. // Update the result if width and/or height are larger
  1933. dimensions.height = Math.max(labelHeight, dimensions.height);
  1934. dimensions.width = Math.max(labelWidth, dimensions.width);
  1935. }
  1936. });
  1937. return dimensions;
  1938. };
  1939. // Adds week date format
  1940. H.dateFormats.W = function (timestamp) {
  1941. var d = new this.Date(timestamp);
  1942. var firstDay = (this.get('Day',
  1943. d) + 6) % 7;
  1944. var thursday = new this.Date(d.valueOf());
  1945. this.set('Date', thursday, this.get('Date', d) - firstDay + 3);
  1946. var firstThursday = new this.Date(this.get('FullYear',
  1947. thursday), 0, 1);
  1948. if (this.get('Day', firstThursday) !== 4) {
  1949. this.set('Month', d, 0);
  1950. this.set('Date', d, 1 + (11 - this.get('Day', firstThursday)) % 7);
  1951. }
  1952. return (1 +
  1953. Math.floor((thursday.valueOf() - firstThursday.valueOf()) / 604800000)).toString();
  1954. };
  1955. // First letter of the day of the week, e.g. 'M' for 'Monday'.
  1956. H.dateFormats.E = function (timestamp) {
  1957. return this.dateFormat('%a', timestamp, true).charAt(0);
  1958. };
  1959. /* eslint-disable no-invalid-this */
  1960. addEvent(Chart, 'afterSetChartSize', function () {
  1961. this.axes.forEach(function (axis) {
  1962. (axis.grid && axis.grid.columns || []).forEach(function (column) {
  1963. column.setAxisSize();
  1964. column.setAxisTranslation();
  1965. });
  1966. });
  1967. });
  1968. // Center tick labels in cells.
  1969. addEvent(Tick, 'afterGetLabelPosition', function (e) {
  1970. var tick = this,
  1971. label = tick.label,
  1972. axis = tick.axis,
  1973. reversed = axis.reversed,
  1974. chart = axis.chart,
  1975. options = axis.options,
  1976. gridOptions = options.grid || {},
  1977. labelOpts = axis.options.labels,
  1978. align = labelOpts.align,
  1979. // verticalAlign is currently not supported for axis.labels.
  1980. verticalAlign = 'middle', // labelOpts.verticalAlign,
  1981. side = GridAxis.Side[axis.side],
  1982. tickmarkOffset = e.tickmarkOffset,
  1983. tickPositions = axis.tickPositions,
  1984. tickPos = tick.pos - tickmarkOffset,
  1985. nextTickPos = (isNumber(tickPositions[e.index + 1]) ?
  1986. tickPositions[e.index + 1] - tickmarkOffset :
  1987. axis.max + tickmarkOffset),
  1988. tickSize = axis.tickSize('tick'),
  1989. tickWidth = tickSize ? tickSize[0] : 0,
  1990. crispCorr = tickSize ? tickSize[1] / 2 : 0,
  1991. labelHeight,
  1992. lblMetrics,
  1993. lines,
  1994. bottom,
  1995. top,
  1996. left,
  1997. right;
  1998. // Only center tick labels in grid axes
  1999. if (gridOptions.enabled === true) {
  2000. // Calculate top and bottom positions of the cell.
  2001. if (side === 'top') {
  2002. bottom = axis.top + axis.offset;
  2003. top = bottom - tickWidth;
  2004. }
  2005. else if (side === 'bottom') {
  2006. top = chart.chartHeight - axis.bottom + axis.offset;
  2007. bottom = top + tickWidth;
  2008. }
  2009. else {
  2010. bottom = axis.top + axis.len - axis.translate(reversed ? nextTickPos : tickPos);
  2011. top = axis.top + axis.len - axis.translate(reversed ? tickPos : nextTickPos);
  2012. }
  2013. // Calculate left and right positions of the cell.
  2014. if (side === 'right') {
  2015. left = chart.chartWidth - axis.right + axis.offset;
  2016. right = left + tickWidth;
  2017. }
  2018. else if (side === 'left') {
  2019. right = axis.left + axis.offset;
  2020. left = right - tickWidth;
  2021. }
  2022. else {
  2023. left = Math.round(axis.left + axis.translate(reversed ? nextTickPos : tickPos)) - crispCorr;
  2024. right = Math.round(axis.left + axis.translate(reversed ? tickPos : nextTickPos)) - crispCorr;
  2025. }
  2026. tick.slotWidth = right - left;
  2027. // Calculate the positioning of the label based on
  2028. // alignment.
  2029. e.pos.x = (align === 'left' ?
  2030. left :
  2031. align === 'right' ?
  2032. right :
  2033. left + ((right - left) / 2) // default to center
  2034. );
  2035. e.pos.y = (verticalAlign === 'top' ?
  2036. top :
  2037. verticalAlign === 'bottom' ?
  2038. bottom :
  2039. top + ((bottom - top) / 2) // default to middle
  2040. );
  2041. lblMetrics = chart.renderer.fontMetrics(labelOpts.style.fontSize, label.element);
  2042. labelHeight = label.getBBox().height;
  2043. // Adjustment to y position to align the label correctly.
  2044. // Would be better to have a setter or similar for this.
  2045. if (!labelOpts.useHTML) {
  2046. lines = Math.round(labelHeight / lblMetrics.h);
  2047. e.pos.y += (
  2048. // Center the label
  2049. // TODO: why does this actually center the label?
  2050. ((lblMetrics.b - (lblMetrics.h - lblMetrics.f)) / 2) +
  2051. // Adjust for height of additional lines.
  2052. -(((lines - 1) * lblMetrics.h) / 2));
  2053. }
  2054. else {
  2055. e.pos.y += (
  2056. // Readjust yCorr in htmlUpdateTransform
  2057. lblMetrics.b +
  2058. // Adjust for height of html label
  2059. -(labelHeight / 2));
  2060. }
  2061. e.pos.x += (axis.horiz && labelOpts.x || 0);
  2062. }
  2063. });
  2064. addEvent(Tick, 'labelFormat', function (ctx) {
  2065. var axis = ctx.axis,
  2066. value = ctx.value;
  2067. if (axis.options.grid &&
  2068. axis.options.grid.enabled) {
  2069. var tickPos = axis.tickPositions;
  2070. var series = (axis.linkedParent || axis).series[0];
  2071. var isFirst = value === tickPos[0];
  2072. var isLast = value === tickPos[tickPos.length - 1];
  2073. var point = series && find(series.options.data,
  2074. function (p) {
  2075. return p[axis.isXAxis ? 'x' : 'y'] === value;
  2076. });
  2077. var pointCopy = void 0;
  2078. if (point && series.is('gantt')) {
  2079. // For the Gantt set point aliases to the pointCopy
  2080. // to do not change the original point
  2081. pointCopy = merge(point);
  2082. H.seriesTypes.gantt.prototype.pointClass
  2083. .setGanttPointAliases(pointCopy);
  2084. }
  2085. // Make additional properties available for the
  2086. // formatter
  2087. ctx.isFirst = isFirst;
  2088. ctx.isLast = isLast;
  2089. ctx.point = pointCopy;
  2090. }
  2091. });
  2092. /* eslint-enable no-invalid-this */
  2093. /**
  2094. * Additions for grid axes.
  2095. * @private
  2096. * @class
  2097. */
  2098. var GridAxisAdditions = /** @class */ (function () {
  2099. /* *
  2100. *
  2101. * Constructors
  2102. *
  2103. * */
  2104. function GridAxisAdditions(axis) {
  2105. this.axis = axis;
  2106. }
  2107. /* *
  2108. *
  2109. * Functions
  2110. *
  2111. * */
  2112. /**
  2113. * Checks if an axis is the outer axis in its dimension. Since
  2114. * axes are placed outwards in order, the axis with the highest
  2115. * index is the outermost axis.
  2116. *
  2117. * Example: If there are multiple x-axes at the top of the chart,
  2118. * this function returns true if the axis supplied is the last
  2119. * of the x-axes.
  2120. *
  2121. * @private
  2122. *
  2123. * @return {boolean}
  2124. * True if the axis is the outermost axis in its dimension; false if
  2125. * not.
  2126. */
  2127. GridAxisAdditions.prototype.isOuterAxis = function () {
  2128. var axis = this.axis;
  2129. var chart = axis.chart;
  2130. var columnIndex = axis.grid.columnIndex;
  2131. var columns = (axis.linkedParent && axis.linkedParent.grid.columns ||
  2132. axis.grid.columns);
  2133. var parentAxis = columnIndex ? axis.linkedParent : axis;
  2134. var thisIndex = -1,
  2135. lastIndex = 0;
  2136. chart[axis.coll].forEach(function (otherAxis, index) {
  2137. if (otherAxis.side === axis.side && !otherAxis.options.isInternal) {
  2138. lastIndex = index;
  2139. if (otherAxis === parentAxis) {
  2140. // Get the index of the axis in question
  2141. thisIndex = index;
  2142. }
  2143. }
  2144. });
  2145. return (lastIndex === thisIndex &&
  2146. (isNumber(columnIndex) ? columns.length === columnIndex : true));
  2147. };
  2148. /**
  2149. * Add extra border based on the provided path.
  2150. * *
  2151. * @private
  2152. *
  2153. * @param {SVGPath} path
  2154. * The path of the border.
  2155. *
  2156. * @return {Highcharts.SVGElement}
  2157. */
  2158. GridAxisAdditions.prototype.renderBorder = function (path) {
  2159. var axis = this.axis,
  2160. renderer = axis.chart.renderer,
  2161. options = axis.options,
  2162. extraBorderLine = renderer.path(path)
  2163. .addClass('highcharts-axis-line')
  2164. .add(axis.axisBorder);
  2165. if (!renderer.styledMode) {
  2166. extraBorderLine.attr({
  2167. stroke: options.lineColor,
  2168. 'stroke-width': options.lineWidth,
  2169. zIndex: 7
  2170. });
  2171. }
  2172. return extraBorderLine;
  2173. };
  2174. return GridAxisAdditions;
  2175. }());
  2176. /**
  2177. * Axis with grid support.
  2178. * @private
  2179. * @class
  2180. */
  2181. var GridAxis = /** @class */ (function () {
  2182. function GridAxis() {
  2183. }
  2184. /* *
  2185. *
  2186. * Static Functions
  2187. *
  2188. * */
  2189. /* eslint-disable valid-jsdoc */
  2190. /**
  2191. * Extends axis class with grid support.
  2192. * @private
  2193. */
  2194. GridAxis.compose = function (AxisClass) {
  2195. Axis.keepProps.push('grid');
  2196. wrap(AxisClass.prototype, 'unsquish', GridAxis.wrapUnsquish);
  2197. // Add event handlers
  2198. addEvent(AxisClass, 'init', GridAxis.onInit);
  2199. addEvent(AxisClass, 'afterGetOffset', GridAxis.onAfterGetOffset);
  2200. addEvent(AxisClass, 'afterGetTitlePosition', GridAxis.onAfterGetTitlePosition);
  2201. addEvent(AxisClass, 'afterInit', GridAxis.onAfterInit);
  2202. addEvent(AxisClass, 'afterRender', GridAxis.onAfterRender);
  2203. addEvent(AxisClass, 'afterSetAxisTranslation', GridAxis.onAfterSetAxisTranslation);
  2204. addEvent(AxisClass, 'afterSetOptions', GridAxis.onAfterSetOptions);
  2205. addEvent(AxisClass, 'afterSetOptions', GridAxis.onAfterSetOptions2);
  2206. addEvent(AxisClass, 'afterSetScale', GridAxis.onAfterSetScale);
  2207. addEvent(AxisClass, 'afterTickSize', GridAxis.onAfterTickSize);
  2208. addEvent(AxisClass, 'trimTicks', GridAxis.onTrimTicks);
  2209. addEvent(AxisClass, 'destroy', GridAxis.onDestroy);
  2210. };
  2211. /**
  2212. * Handle columns and getOffset.
  2213. * @private
  2214. */
  2215. GridAxis.onAfterGetOffset = function () {
  2216. var grid = this.grid;
  2217. (grid && grid.columns || []).forEach(function (column) {
  2218. column.getOffset();
  2219. });
  2220. };
  2221. /**
  2222. * @private
  2223. */
  2224. GridAxis.onAfterGetTitlePosition = function (e) {
  2225. var axis = this;
  2226. var options = axis.options;
  2227. var gridOptions = options.grid || {};
  2228. if (gridOptions.enabled === true) {
  2229. // compute anchor points for each of the title align options
  2230. var axisTitle = axis.axisTitle,
  2231. axisHeight = axis.height,
  2232. horiz = axis.horiz,
  2233. axisLeft = axis.left,
  2234. offset = axis.offset,
  2235. opposite = axis.opposite,
  2236. options_1 = axis.options,
  2237. axisTop = axis.top,
  2238. axisWidth = axis.width;
  2239. var tickSize = axis.tickSize();
  2240. var titleWidth = axisTitle && axisTitle.getBBox().width;
  2241. var xOption = options_1.title.x;
  2242. var yOption = options_1.title.y;
  2243. var titleMargin = pick(options_1.title.margin,
  2244. horiz ? 5 : 10);
  2245. var titleFontSize = axis.chart.renderer.fontMetrics(options_1.title.style.fontSize,
  2246. axisTitle).f;
  2247. var crispCorr = tickSize ? tickSize[0] / 2 : 0;
  2248. // TODO account for alignment
  2249. // the position in the perpendicular direction of the axis
  2250. var offAxis = ((horiz ? axisTop + axisHeight : axisLeft) +
  2251. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  2252. (opposite ? -1 : 1) * // so does opposite axes
  2253. crispCorr +
  2254. (axis.side === GridAxis.Side.bottom ? titleFontSize : 0));
  2255. e.titlePosition.x = horiz ?
  2256. axisLeft - (titleWidth || 0) / 2 - titleMargin + xOption :
  2257. offAxis + (opposite ? axisWidth : 0) + offset + xOption;
  2258. e.titlePosition.y = horiz ?
  2259. (offAxis -
  2260. (opposite ? axisHeight : 0) +
  2261. (opposite ? titleFontSize : -titleFontSize) / 2 +
  2262. offset +
  2263. yOption) :
  2264. axisTop - titleMargin + yOption;
  2265. }
  2266. };
  2267. /**
  2268. * @private
  2269. */
  2270. GridAxis.onAfterInit = function () {
  2271. var axis = this;
  2272. var chart = axis.chart,
  2273. _a = axis.options.grid,
  2274. gridOptions = _a === void 0 ? {} : _a,
  2275. userOptions = axis.userOptions;
  2276. if (gridOptions.enabled) {
  2277. applyGridOptions(axis);
  2278. }
  2279. if (gridOptions.columns) {
  2280. var columns = axis.grid.columns = [],
  2281. columnIndex = axis.grid.columnIndex = 0;
  2282. // Handle columns, each column is a grid axis
  2283. while (++columnIndex < gridOptions.columns.length) {
  2284. var columnOptions = merge(userOptions,
  2285. gridOptions.columns[gridOptions.columns.length - columnIndex - 1], {
  2286. linkedTo: 0,
  2287. // Force to behave like category axis
  2288. type: 'category',
  2289. // Disable by default the scrollbar on the grid axis
  2290. scrollbar: {
  2291. enabled: false
  2292. }
  2293. });
  2294. delete columnOptions.grid.columns; // Prevent recursion
  2295. var column = new Axis(axis.chart,
  2296. columnOptions);
  2297. column.grid.isColumn = true;
  2298. column.grid.columnIndex = columnIndex;
  2299. // Remove column axis from chart axes array, and place it
  2300. // in the columns array.
  2301. erase(chart.axes, column);
  2302. erase(chart[axis.coll], column);
  2303. columns.push(column);
  2304. }
  2305. }
  2306. };
  2307. /**
  2308. * Draw an extra line on the far side of the outermost axis,
  2309. * creating floor/roof/wall of a grid. And some padding.
  2310. * ```
  2311. * Make this:
  2312. * (axis.min) __________________________ (axis.max)
  2313. * | | | | |
  2314. * Into this:
  2315. * (axis.min) __________________________ (axis.max)
  2316. * ___|____|____|____|____|__
  2317. * ```
  2318. * @private
  2319. */
  2320. GridAxis.onAfterRender = function () {
  2321. var axis = this,
  2322. grid = axis.grid,
  2323. options = axis.options,
  2324. gridOptions = options.grid || {};
  2325. if (gridOptions.enabled === true) {
  2326. // @todo acutual label padding (top, bottom, left, right)
  2327. axis.maxLabelDimensions = axis.getMaxLabelDimensions(axis.ticks, axis.tickPositions);
  2328. // Remove right wall before rendering if updating
  2329. if (axis.rightWall) {
  2330. axis.rightWall.destroy();
  2331. }
  2332. /*
  2333. Draw an extra axis line on outer axes
  2334. >
  2335. Make this: |______|______|______|___
  2336. > _________________________
  2337. Into this: |______|______|______|__|
  2338. */
  2339. if (axis.grid && axis.grid.isOuterAxis() && axis.axisLine) {
  2340. var lineWidth = options.lineWidth;
  2341. if (lineWidth) {
  2342. var linePath = axis.getLinePath(lineWidth),
  2343. startPoint = linePath[0],
  2344. endPoint = linePath[1],
  2345. // Negate distance if top or left axis
  2346. // Subtract 1px to draw the line at the end of the tick
  2347. tickLength = (axis.tickSize('tick') || [1])[0],
  2348. distance = (tickLength - 1) * ((axis.side === GridAxis.Side.top ||
  2349. axis.side === GridAxis.Side.left) ? -1 : 1);
  2350. // If axis is horizontal, reposition line path vertically
  2351. if (startPoint[0] === 'M' && endPoint[0] === 'L') {
  2352. if (axis.horiz) {
  2353. startPoint[2] += distance;
  2354. endPoint[2] += distance;
  2355. }
  2356. else {
  2357. startPoint[1] += distance;
  2358. endPoint[1] += distance;
  2359. }
  2360. }
  2361. // If it doesn't exist, add an upper and lower border
  2362. // for the vertical grid axis.
  2363. if (!axis.horiz && axis.chart.marginRight) {
  2364. var upperBorderStartPoint = startPoint, upperBorderEndPoint = ['L', axis.left, startPoint[2]], upperBorderPath = [upperBorderStartPoint, upperBorderEndPoint], lowerBorderEndPoint = ['L', axis.chart.chartWidth - axis.chart.marginRight, axis.toPixels(axis.max + axis.tickmarkOffset)], lowerBorderStartPoint = ['M', endPoint[1], axis.toPixels(axis.max + axis.tickmarkOffset)], lowerBorderPath = [lowerBorderStartPoint, lowerBorderEndPoint];
  2365. if (!axis.grid.upperBorder && axis.min % 1 !== 0) {
  2366. axis.grid.upperBorder = axis.grid.renderBorder(upperBorderPath);
  2367. }
  2368. if (axis.grid.upperBorder) {
  2369. axis.grid.upperBorder.animate({
  2370. d: upperBorderPath
  2371. });
  2372. }
  2373. if (!axis.grid.lowerBorder && axis.max % 1 !== 0) {
  2374. axis.grid.lowerBorder = axis.grid.renderBorder(lowerBorderPath);
  2375. }
  2376. if (axis.grid.lowerBorder) {
  2377. axis.grid.lowerBorder.animate({
  2378. d: lowerBorderPath
  2379. });
  2380. }
  2381. }
  2382. // Render an extra line parallel to the existing axes,
  2383. // to close the grid.
  2384. if (!axis.grid.axisLineExtra) {
  2385. axis.grid.axisLineExtra = axis.grid.renderBorder(linePath);
  2386. }
  2387. else {
  2388. axis.grid.axisLineExtra.animate({
  2389. d: linePath
  2390. });
  2391. }
  2392. // show or hide the line depending on
  2393. // options.showEmpty
  2394. axis.axisLine[axis.showAxis ? 'show' : 'hide'](true);
  2395. }
  2396. }
  2397. (grid && grid.columns || []).forEach(function (column) {
  2398. column.render();
  2399. });
  2400. // Manipulate the tick mark visibility
  2401. // based on the axis.max- allows smooth scrolling.
  2402. if (!axis.horiz &&
  2403. axis.chart.hasRendered &&
  2404. (axis.scrollbar ||
  2405. (axis.linkedParent && axis.linkedParent.scrollbar))) {
  2406. var max = axis.max,
  2407. min = axis.min,
  2408. tickmarkOffset = axis.tickmarkOffset,
  2409. lastTick = axis.tickPositions[axis.tickPositions.length - 1],
  2410. firstTick = axis.tickPositions[0];
  2411. // Hide/show firts tick label.
  2412. if (min - firstTick > tickmarkOffset) {
  2413. axis.ticks[firstTick].label.hide();
  2414. }
  2415. else {
  2416. axis.ticks[firstTick].label.show();
  2417. }
  2418. // Hide/show last tick mark/label.
  2419. if (lastTick - max > tickmarkOffset) {
  2420. axis.ticks[lastTick].label.hide();
  2421. }
  2422. else {
  2423. axis.ticks[lastTick].label.show();
  2424. }
  2425. if (lastTick - max < tickmarkOffset && lastTick - max > 0 && axis.ticks[lastTick].isLast) {
  2426. axis.ticks[lastTick].mark.hide();
  2427. }
  2428. else if (axis.ticks[lastTick - 1]) {
  2429. axis.ticks[lastTick - 1].mark.show();
  2430. }
  2431. }
  2432. }
  2433. };
  2434. /**
  2435. * @private
  2436. */
  2437. GridAxis.onAfterSetAxisTranslation = function () {
  2438. var axis = this;
  2439. var tickInfo = axis.tickPositions && axis.tickPositions.info;
  2440. var options = axis.options;
  2441. var gridOptions = options.grid || {};
  2442. var userLabels = axis.userOptions.labels || {};
  2443. // Fire this only for the Gantt type chart, #14868.
  2444. if (gridOptions.enabled) {
  2445. if (axis.horiz) {
  2446. axis.series.forEach(function (series) {
  2447. series.options.pointRange = 0;
  2448. });
  2449. // Lower level time ticks, like hours or minutes, represent
  2450. // points in time and not ranges. These should be aligned
  2451. // left in the grid cell by default. The same applies to
  2452. // years of higher order.
  2453. if (tickInfo &&
  2454. options.dateTimeLabelFormats &&
  2455. options.labels &&
  2456. !defined(userLabels.align) &&
  2457. (options.dateTimeLabelFormats[tickInfo.unitName].range === false ||
  2458. tickInfo.count > 1 // years
  2459. )) {
  2460. options.labels.align = 'left';
  2461. if (!defined(userLabels.x)) {
  2462. options.labels.x = 3;
  2463. }
  2464. }
  2465. }
  2466. else {
  2467. // Don't trim ticks which not in min/max range but
  2468. // they are still in the min/max plus tickInterval.
  2469. if (this.options.type !== 'treegrid' &&
  2470. axis.grid &&
  2471. axis.grid.columns) {
  2472. this.minPointOffset = this.tickInterval;
  2473. }
  2474. }
  2475. }
  2476. };
  2477. /**
  2478. * Creates a left and right wall on horizontal axes:
  2479. * - Places leftmost tick at the start of the axis, to create a left
  2480. * wall
  2481. * - Ensures that the rightmost tick is at the end of the axis, to
  2482. * create a right wall.
  2483. * @private
  2484. */
  2485. GridAxis.onAfterSetOptions = function (e) {
  2486. var options = this.options,
  2487. userOptions = e.userOptions,
  2488. gridAxisOptions,
  2489. gridOptions = ((options && isObject(options.grid)) ? options.grid : {});
  2490. if (gridOptions.enabled === true) {
  2491. // Merge the user options into default grid axis options so
  2492. // that when a user option is set, it takes presedence.
  2493. gridAxisOptions = merge(true, {
  2494. className: ('highcharts-grid-axis ' + (userOptions.className || '')),
  2495. dateTimeLabelFormats: {
  2496. hour: {
  2497. list: ['%H:%M', '%H']
  2498. },
  2499. day: {
  2500. list: ['%A, %e. %B', '%a, %e. %b', '%E']
  2501. },
  2502. week: {
  2503. list: ['Week %W', 'W%W']
  2504. },
  2505. month: {
  2506. list: ['%B', '%b', '%o']
  2507. }
  2508. },
  2509. grid: {
  2510. borderWidth: 1
  2511. },
  2512. labels: {
  2513. padding: 2,
  2514. style: {
  2515. fontSize: '13px'
  2516. }
  2517. },
  2518. margin: 0,
  2519. title: {
  2520. text: null,
  2521. reserveSpace: false,
  2522. rotation: 0
  2523. },
  2524. // In a grid axis, only allow one unit of certain types,
  2525. // for example we shouln't have one grid cell spanning
  2526. // two days.
  2527. units: [[
  2528. 'millisecond',
  2529. [1, 10, 100]
  2530. ], [
  2531. 'second',
  2532. [1, 10]
  2533. ], [
  2534. 'minute',
  2535. [1, 5, 15]
  2536. ], [
  2537. 'hour',
  2538. [1, 6]
  2539. ], [
  2540. 'day',
  2541. [1]
  2542. ], [
  2543. 'week',
  2544. [1]
  2545. ], [
  2546. 'month',
  2547. [1]
  2548. ], [
  2549. 'year',
  2550. null
  2551. ]]
  2552. }, userOptions);
  2553. // X-axis specific options
  2554. if (this.coll === 'xAxis') {
  2555. // For linked axes, tickPixelInterval is used only if
  2556. // the tickPositioner below doesn't run or returns
  2557. // undefined (like multiple years)
  2558. if (defined(userOptions.linkedTo) &&
  2559. !defined(userOptions.tickPixelInterval)) {
  2560. gridAxisOptions.tickPixelInterval = 350;
  2561. }
  2562. // For the secondary grid axis, use the primary axis'
  2563. // tick intervals and return ticks one level higher.
  2564. if (
  2565. // Check for tick pixel interval in options
  2566. !defined(userOptions.tickPixelInterval) &&
  2567. // Only for linked axes
  2568. defined(userOptions.linkedTo) &&
  2569. !defined(userOptions.tickPositioner) &&
  2570. !defined(userOptions.tickInterval)) {
  2571. gridAxisOptions.tickPositioner = function (min, max) {
  2572. var parentInfo = (this.linkedParent &&
  2573. this.linkedParent.tickPositions &&
  2574. this.linkedParent.tickPositions.info);
  2575. if (parentInfo) {
  2576. var unitIdx = void 0,
  2577. count = void 0,
  2578. unitName = void 0,
  2579. i = void 0,
  2580. units = gridAxisOptions.units,
  2581. unitRange = void 0;
  2582. for (i = 0; i < units.length; i++) {
  2583. if (units[i][0] ===
  2584. parentInfo.unitName) {
  2585. unitIdx = i;
  2586. break;
  2587. }
  2588. }
  2589. // Get the first allowed count on the next
  2590. // unit.
  2591. if (units[unitIdx + 1]) {
  2592. unitName = units[unitIdx + 1][0];
  2593. count =
  2594. (units[unitIdx + 1][1] || [1])[0];
  2595. // In case the base X axis shows years, make
  2596. // the secondary axis show ten times the
  2597. // years (#11427)
  2598. }
  2599. else if (parentInfo.unitName === 'year') {
  2600. unitName = 'year';
  2601. count = parentInfo.count * 10;
  2602. }
  2603. unitRange = timeUnits[unitName];
  2604. this.tickInterval = unitRange * count;
  2605. return this.getTimeTicks({
  2606. unitRange: unitRange,
  2607. count: count,
  2608. unitName: unitName
  2609. }, min, max, this.options.startOfWeek);
  2610. }
  2611. };
  2612. }
  2613. }
  2614. // Now merge the combined options into the axis options
  2615. merge(true, this.options, gridAxisOptions);
  2616. if (this.horiz) {
  2617. /* _________________________
  2618. Make this: ___|_____|_____|_____|__|
  2619. ^ ^
  2620. _________________________
  2621. Into this: |_____|_____|_____|_____|
  2622. ^ ^ */
  2623. options.minPadding = pick(userOptions.minPadding, 0);
  2624. options.maxPadding = pick(userOptions.maxPadding, 0);
  2625. }
  2626. // If borderWidth is set, then use its value for tick and
  2627. // line width.
  2628. if (isNumber(options.grid.borderWidth)) {
  2629. options.tickWidth = options.lineWidth =
  2630. gridOptions.borderWidth;
  2631. }
  2632. }
  2633. };
  2634. /**
  2635. * @private
  2636. */
  2637. GridAxis.onAfterSetOptions2 = function (e) {
  2638. var axis = this;
  2639. var userOptions = e.userOptions;
  2640. var gridOptions = userOptions && userOptions.grid || {};
  2641. var columns = gridOptions.columns;
  2642. // Add column options to the parent axis. Children has their column
  2643. // options set on init in onGridAxisAfterInit.
  2644. if (gridOptions.enabled && columns) {
  2645. merge(true, axis.options, columns[columns.length - 1]);
  2646. }
  2647. };
  2648. /**
  2649. * Handle columns and setScale.
  2650. * @private
  2651. */
  2652. GridAxis.onAfterSetScale = function () {
  2653. var axis = this;
  2654. (axis.grid.columns || []).forEach(function (column) {
  2655. column.setScale();
  2656. });
  2657. };
  2658. /**
  2659. * Draw vertical axis ticks extra long to create cell floors and roofs.
  2660. * Overrides the tickLength for vertical axes.
  2661. * @private
  2662. */
  2663. GridAxis.onAfterTickSize = function (e) {
  2664. var defaultLeftAxisOptions = Axis.defaultLeftAxisOptions;
  2665. var _a = this,
  2666. horiz = _a.horiz,
  2667. maxLabelDimensions = _a.maxLabelDimensions,
  2668. _b = _a.options.grid,
  2669. gridOptions = _b === void 0 ? {} : _b;
  2670. if (gridOptions.enabled && maxLabelDimensions) {
  2671. var labelPadding = (Math.abs(defaultLeftAxisOptions.labels.x) * 2);
  2672. var distance = horiz ?
  2673. gridOptions.cellHeight || labelPadding + maxLabelDimensions.height :
  2674. labelPadding + maxLabelDimensions.width;
  2675. if (isArray(e.tickSize)) {
  2676. e.tickSize[0] = distance;
  2677. }
  2678. else {
  2679. e.tickSize = [distance, 0];
  2680. }
  2681. }
  2682. };
  2683. /**
  2684. * @private
  2685. */
  2686. GridAxis.onDestroy = function (e) {
  2687. var grid = this.grid;
  2688. (grid.columns || []).forEach(function (column) {
  2689. column.destroy(e.keepEvents);
  2690. });
  2691. grid.columns = void 0;
  2692. };
  2693. /**
  2694. * Wraps axis init to draw cell walls on vertical axes.
  2695. * @private
  2696. */
  2697. GridAxis.onInit = function (e) {
  2698. var axis = this;
  2699. var userOptions = e.userOptions || {};
  2700. var gridOptions = userOptions.grid || {};
  2701. if (gridOptions.enabled && defined(gridOptions.borderColor)) {
  2702. userOptions.tickColor = userOptions.lineColor = gridOptions.borderColor;
  2703. }
  2704. if (!axis.grid) {
  2705. axis.grid = new GridAxisAdditions(axis);
  2706. }
  2707. };
  2708. /**
  2709. * Makes tick labels which are usually ignored in a linked axis
  2710. * displayed if they are within range of linkedParent.min.
  2711. * ```
  2712. * _____________________________
  2713. * | | | | |
  2714. * Make this: | | 2 | 3 | 4 |
  2715. * |___|_______|_______|_______|
  2716. * ^
  2717. * _____________________________
  2718. * | | | | |
  2719. * Into this: | 1 | 2 | 3 | 4 |
  2720. * |___|_______|_______|_______|
  2721. * ^
  2722. * ```
  2723. * @private
  2724. * @todo Does this function do what the drawing says? Seems to affect
  2725. * ticks and not the labels directly?
  2726. */
  2727. GridAxis.onTrimTicks = function () {
  2728. var axis = this;
  2729. var options = axis.options;
  2730. var gridOptions = options.grid || {};
  2731. var categoryAxis = axis.categories;
  2732. var tickPositions = axis.tickPositions;
  2733. var firstPos = tickPositions[0];
  2734. var lastPos = tickPositions[tickPositions.length - 1];
  2735. var linkedMin = axis.linkedParent && axis.linkedParent.min;
  2736. var linkedMax = axis.linkedParent && axis.linkedParent.max;
  2737. var min = linkedMin || axis.min;
  2738. var max = linkedMax || axis.max;
  2739. var tickInterval = axis.tickInterval;
  2740. var endMoreThanMin = (firstPos < min &&
  2741. firstPos + tickInterval > min);
  2742. var startLessThanMax = (lastPos > max &&
  2743. lastPos - tickInterval < max);
  2744. if (gridOptions.enabled === true &&
  2745. !categoryAxis &&
  2746. (axis.horiz || axis.isLinked)) {
  2747. if (endMoreThanMin && !options.startOnTick) {
  2748. tickPositions[0] = min;
  2749. }
  2750. if (startLessThanMax && !options.endOnTick) {
  2751. tickPositions[tickPositions.length - 1] = max;
  2752. }
  2753. }
  2754. };
  2755. /**
  2756. * Avoid altering tickInterval when reserving space.
  2757. * @private
  2758. */
  2759. GridAxis.wrapUnsquish = function (proceed) {
  2760. var axis = this;
  2761. var _a = axis.options.grid,
  2762. gridOptions = _a === void 0 ? {} : _a;
  2763. if (gridOptions.enabled === true && axis.categories) {
  2764. return axis.tickInterval;
  2765. }
  2766. return proceed.apply(axis, argsToArray(arguments));
  2767. };
  2768. return GridAxis;
  2769. }());
  2770. (function (GridAxis) {
  2771. /**
  2772. * Enum for which side the axis is on. Maps to axis.side.
  2773. * @private
  2774. */
  2775. var Side;
  2776. (function (Side) {
  2777. Side[Side["top"] = 0] = "top";
  2778. Side[Side["right"] = 1] = "right";
  2779. Side[Side["bottom"] = 2] = "bottom";
  2780. Side[Side["left"] = 3] = "left";
  2781. })(Side = GridAxis.Side || (GridAxis.Side = {}));
  2782. })(GridAxis || (GridAxis = {}));
  2783. GridAxis.compose(Axis);
  2784. return GridAxis;
  2785. });
  2786. _registerModule(_modules, 'Core/Axis/BrokenAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Series/Series.js'], _modules['Extensions/Stacking.js'], _modules['Core/Utilities.js']], function (Axis, Series, StackItem, U) {
  2787. /* *
  2788. *
  2789. * (c) 2009-2021 Torstein Honsi
  2790. *
  2791. * License: www.highcharts.com/license
  2792. *
  2793. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  2794. *
  2795. * */
  2796. var addEvent = U.addEvent,
  2797. find = U.find,
  2798. fireEvent = U.fireEvent,
  2799. isArray = U.isArray,
  2800. isNumber = U.isNumber,
  2801. pick = U.pick;
  2802. /* eslint-disable valid-jsdoc */
  2803. /**
  2804. * Provides support for broken axes.
  2805. * @private
  2806. * @class
  2807. */
  2808. var BrokenAxisAdditions = /** @class */ (function () {
  2809. /* *
  2810. *
  2811. * Constructors
  2812. *
  2813. * */
  2814. function BrokenAxisAdditions(axis) {
  2815. this.hasBreaks = false;
  2816. this.axis = axis;
  2817. }
  2818. /* *
  2819. *
  2820. * Static Functions
  2821. *
  2822. * */
  2823. /**
  2824. * @private
  2825. */
  2826. BrokenAxisAdditions.isInBreak = function (brk, val) {
  2827. var ret,
  2828. repeat = brk.repeat || Infinity,
  2829. from = brk.from,
  2830. length = brk.to - brk.from,
  2831. test = (val >= from ?
  2832. (val - from) % repeat :
  2833. repeat - ((from - val) % repeat));
  2834. if (!brk.inclusive) {
  2835. ret = test < length && test !== 0;
  2836. }
  2837. else {
  2838. ret = test <= length;
  2839. }
  2840. return ret;
  2841. };
  2842. /**
  2843. * @private
  2844. */
  2845. BrokenAxisAdditions.lin2Val = function (val) {
  2846. var axis = this;
  2847. var brokenAxis = axis.brokenAxis;
  2848. var breakArray = brokenAxis && brokenAxis.breakArray;
  2849. if (!breakArray || !isNumber(val)) {
  2850. return val;
  2851. }
  2852. var nval = val,
  2853. brk,
  2854. i;
  2855. for (i = 0; i < breakArray.length; i++) {
  2856. brk = breakArray[i];
  2857. if (brk.from >= nval) {
  2858. break;
  2859. }
  2860. else if (brk.to < nval) {
  2861. nval += brk.len;
  2862. }
  2863. else if (BrokenAxisAdditions.isInBreak(brk, nval)) {
  2864. nval += brk.len;
  2865. }
  2866. }
  2867. return nval;
  2868. };
  2869. /**
  2870. * @private
  2871. */
  2872. BrokenAxisAdditions.val2Lin = function (val) {
  2873. var axis = this;
  2874. var brokenAxis = axis.brokenAxis;
  2875. var breakArray = brokenAxis && brokenAxis.breakArray;
  2876. if (!breakArray || !isNumber(val)) {
  2877. return val;
  2878. }
  2879. var nval = val,
  2880. brk,
  2881. i;
  2882. for (i = 0; i < breakArray.length; i++) {
  2883. brk = breakArray[i];
  2884. if (brk.to <= val) {
  2885. nval -= brk.len;
  2886. }
  2887. else if (brk.from >= val) {
  2888. break;
  2889. }
  2890. else if (BrokenAxisAdditions.isInBreak(brk, val)) {
  2891. nval -= (val - brk.from);
  2892. break;
  2893. }
  2894. }
  2895. return nval;
  2896. };
  2897. /* *
  2898. *
  2899. * Functions
  2900. *
  2901. * */
  2902. /**
  2903. * Returns the first break found where the x is larger then break.from and
  2904. * smaller then break.to.
  2905. *
  2906. * @param {number} x
  2907. * The number which should be within a break.
  2908. *
  2909. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  2910. * The array of breaks to search within.
  2911. *
  2912. * @return {Highcharts.XAxisBreaksOptions|undefined}
  2913. * Returns the first break found that matches, returns false if no break is
  2914. * found.
  2915. */
  2916. BrokenAxisAdditions.prototype.findBreakAt = function (x, breaks) {
  2917. return find(breaks, function (b) {
  2918. return b.from < x && x < b.to;
  2919. });
  2920. };
  2921. /**
  2922. * @private
  2923. */
  2924. BrokenAxisAdditions.prototype.isInAnyBreak = function (val, testKeep) {
  2925. var brokenAxis = this;
  2926. var axis = brokenAxis.axis;
  2927. var breaks = axis.options.breaks || [],
  2928. i = breaks.length,
  2929. inbrk,
  2930. keep,
  2931. ret;
  2932. if (i && isNumber(val)) {
  2933. while (i--) {
  2934. if (BrokenAxisAdditions.isInBreak(breaks[i], val)) {
  2935. inbrk = true;
  2936. if (!keep) {
  2937. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  2938. }
  2939. }
  2940. }
  2941. if (inbrk && testKeep) {
  2942. ret = inbrk && !keep;
  2943. }
  2944. else {
  2945. ret = inbrk;
  2946. }
  2947. }
  2948. return ret;
  2949. };
  2950. /**
  2951. * Dynamically set or unset breaks in an axis. This function in lighter than
  2952. * usin Axis.update, and it also preserves animation.
  2953. *
  2954. * @private
  2955. * @function Highcharts.Axis#setBreaks
  2956. *
  2957. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  2958. * The breaks to add. When `undefined` it removes existing breaks.
  2959. *
  2960. * @param {boolean} [redraw=true]
  2961. * Whether to redraw the chart immediately.
  2962. *
  2963. * @return {void}
  2964. */
  2965. BrokenAxisAdditions.prototype.setBreaks = function (breaks, redraw) {
  2966. var brokenAxis = this;
  2967. var axis = brokenAxis.axis;
  2968. var hasBreaks = (isArray(breaks) && !!breaks.length);
  2969. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  2970. brokenAxis.hasBreaks = hasBreaks;
  2971. axis.options.breaks = axis.userOptions.breaks = breaks;
  2972. axis.forceRedraw = true; // Force recalculation in setScale
  2973. // Recalculate series related to the axis.
  2974. axis.series.forEach(function (series) {
  2975. series.isDirty = true;
  2976. });
  2977. if (!hasBreaks && axis.val2lin === BrokenAxisAdditions.val2Lin) {
  2978. // Revert to prototype functions
  2979. delete axis.val2lin;
  2980. delete axis.lin2val;
  2981. }
  2982. if (hasBreaks) {
  2983. axis.userOptions.ordinal = false;
  2984. axis.lin2val = BrokenAxisAdditions.lin2Val;
  2985. axis.val2lin = BrokenAxisAdditions.val2Lin;
  2986. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  2987. // If trying to set extremes inside a break, extend min to
  2988. // after, and max to before the break ( #3857 )
  2989. if (brokenAxis.hasBreaks) {
  2990. var axisBreak = void 0,
  2991. breaks_1 = this.options.breaks;
  2992. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks_1))) {
  2993. newMin = axisBreak.to;
  2994. }
  2995. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks_1))) {
  2996. newMax = axisBreak.from;
  2997. }
  2998. // If both min and max is within the same break.
  2999. if (newMax < newMin) {
  3000. newMax = newMin;
  3001. }
  3002. }
  3003. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  3004. };
  3005. axis.setAxisTranslation = function () {
  3006. Axis.prototype.setAxisTranslation.call(this);
  3007. brokenAxis.unitLength = void 0;
  3008. if (brokenAxis.hasBreaks) {
  3009. var breaks_2 = axis.options.breaks || [],
  3010. // Temporary one:
  3011. breakArrayT_1 = [],
  3012. breakArray_1 = [],
  3013. length_1 = 0,
  3014. inBrk_1,
  3015. repeat_1,
  3016. min_1 = axis.userMin || axis.min,
  3017. max_1 = axis.userMax || axis.max,
  3018. pointRangePadding = pick(axis.pointRangePadding, 0),
  3019. start_1,
  3020. i_1;
  3021. // Min & max check (#4247)
  3022. breaks_2.forEach(function (brk) {
  3023. repeat_1 = brk.repeat || Infinity;
  3024. if (isNumber(min_1) && isNumber(max_1)) {
  3025. if (BrokenAxisAdditions.isInBreak(brk, min_1)) {
  3026. min_1 += (brk.to % repeat_1) - (min_1 % repeat_1);
  3027. }
  3028. if (BrokenAxisAdditions.isInBreak(brk, max_1)) {
  3029. max_1 -= (max_1 % repeat_1) - (brk.from % repeat_1);
  3030. }
  3031. }
  3032. });
  3033. // Construct an array holding all breaks in the axis
  3034. breaks_2.forEach(function (brk) {
  3035. start_1 = brk.from;
  3036. repeat_1 = brk.repeat || Infinity;
  3037. if (isNumber(min_1) && isNumber(max_1)) {
  3038. while (start_1 - repeat_1 > min_1) {
  3039. start_1 -= repeat_1;
  3040. }
  3041. while (start_1 < min_1) {
  3042. start_1 += repeat_1;
  3043. }
  3044. for (i_1 = start_1; i_1 < max_1; i_1 += repeat_1) {
  3045. breakArrayT_1.push({
  3046. value: i_1,
  3047. move: 'in'
  3048. });
  3049. breakArrayT_1.push({
  3050. value: i_1 + brk.to - brk.from,
  3051. move: 'out',
  3052. size: brk.breakSize
  3053. });
  3054. }
  3055. }
  3056. });
  3057. breakArrayT_1.sort(function (a, b) {
  3058. return ((a.value === b.value) ?
  3059. ((a.move === 'in' ? 0 : 1) -
  3060. (b.move === 'in' ? 0 : 1)) :
  3061. a.value - b.value);
  3062. });
  3063. // Simplify the breaks
  3064. inBrk_1 = 0;
  3065. start_1 = min_1;
  3066. breakArrayT_1.forEach(function (brk) {
  3067. inBrk_1 += (brk.move === 'in' ? 1 : -1);
  3068. if (inBrk_1 === 1 && brk.move === 'in') {
  3069. start_1 = brk.value;
  3070. }
  3071. if (inBrk_1 === 0 && isNumber(start_1)) {
  3072. breakArray_1.push({
  3073. from: start_1,
  3074. to: brk.value,
  3075. len: brk.value - start_1 - (brk.size || 0)
  3076. });
  3077. length_1 += brk.value - start_1 - (brk.size || 0);
  3078. }
  3079. });
  3080. brokenAxis.breakArray = breakArray_1;
  3081. // Used with staticScale, and below the actual axis length,
  3082. // when breaks are substracted.
  3083. if (isNumber(min_1) && isNumber(max_1) && isNumber(axis.min)) {
  3084. brokenAxis.unitLength = max_1 - min_1 - length_1 +
  3085. pointRangePadding;
  3086. fireEvent(axis, 'afterBreaks');
  3087. if (axis.staticScale) {
  3088. axis.transA = axis.staticScale;
  3089. }
  3090. else if (brokenAxis.unitLength) {
  3091. axis.transA *=
  3092. (max_1 - axis.min + pointRangePadding) /
  3093. brokenAxis.unitLength;
  3094. }
  3095. if (pointRangePadding) {
  3096. axis.minPixelPadding =
  3097. axis.transA * (axis.minPointOffset || 0);
  3098. }
  3099. axis.min = min_1;
  3100. axis.max = max_1;
  3101. }
  3102. }
  3103. };
  3104. }
  3105. if (pick(redraw, true)) {
  3106. axis.chart.redraw();
  3107. }
  3108. };
  3109. return BrokenAxisAdditions;
  3110. }());
  3111. /**
  3112. * Axis with support of broken data rows.
  3113. * @private
  3114. * @class
  3115. */
  3116. var BrokenAxis = /** @class */ (function () {
  3117. function BrokenAxis() {
  3118. }
  3119. /**
  3120. * Adds support for broken axes.
  3121. * @private
  3122. */
  3123. BrokenAxis.compose = function (AxisClass, SeriesClass) {
  3124. AxisClass.keepProps.push('brokenAxis');
  3125. var seriesProto = Series.prototype;
  3126. /**
  3127. * @private
  3128. */
  3129. seriesProto.drawBreaks = function (axis, keys) {
  3130. var series = this,
  3131. points = series.points,
  3132. breaks,
  3133. threshold,
  3134. eventName,
  3135. y;
  3136. if (axis && // #5950
  3137. axis.brokenAxis &&
  3138. axis.brokenAxis.hasBreaks) {
  3139. var brokenAxis_1 = axis.brokenAxis;
  3140. keys.forEach(function (key) {
  3141. breaks = brokenAxis_1 && brokenAxis_1.breakArray || [];
  3142. threshold = axis.isXAxis ?
  3143. axis.min :
  3144. pick(series.options.threshold, axis.min);
  3145. points.forEach(function (point) {
  3146. y = pick(point['stack' + key.toUpperCase()], point[key]);
  3147. breaks.forEach(function (brk) {
  3148. if (isNumber(threshold) && isNumber(y)) {
  3149. eventName = false;
  3150. if ((threshold < brk.from && y > brk.to) ||
  3151. (threshold > brk.from && y < brk.from)) {
  3152. eventName = 'pointBreak';
  3153. }
  3154. else if ((threshold < brk.from && y > brk.from && y < brk.to) ||
  3155. (threshold > brk.from && y > brk.to && y < brk.from)) {
  3156. eventName = 'pointInBreak';
  3157. }
  3158. if (eventName) {
  3159. fireEvent(axis, eventName, { point: point, brk: brk });
  3160. }
  3161. }
  3162. });
  3163. });
  3164. });
  3165. }
  3166. };
  3167. /**
  3168. * Extend getGraphPath by identifying gaps in the data so that we can
  3169. * draw a gap in the line or area. This was moved from ordinal axis
  3170. * module to broken axis module as of #5045.
  3171. *
  3172. * @private
  3173. * @function Highcharts.Series#gappedPath
  3174. *
  3175. * @return {Highcharts.SVGPathArray}
  3176. * Gapped path
  3177. */
  3178. seriesProto.gappedPath = function () {
  3179. var currentDataGrouping = this.currentDataGrouping,
  3180. groupingSize = currentDataGrouping && currentDataGrouping.gapSize,
  3181. gapSize = this.options.gapSize,
  3182. points = this.points.slice(),
  3183. i = points.length - 1,
  3184. yAxis = this.yAxis,
  3185. stack;
  3186. /**
  3187. * Defines when to display a gap in the graph, together with the
  3188. * [gapUnit](plotOptions.series.gapUnit) option.
  3189. *
  3190. * In case when `dataGrouping` is enabled, points can be grouped
  3191. * into a larger time span. This can make the grouped points to have
  3192. * a greater distance than the absolute value of `gapSize` property,
  3193. * which will result in disappearing graph completely. To prevent
  3194. * this situation the mentioned distance between grouped points is
  3195. * used instead of previously defined `gapSize`.
  3196. *
  3197. * In practice, this option is most often used to visualize gaps in
  3198. * time series. In a stock chart, intraday data is available for
  3199. * daytime hours, while gaps will appear in nights and weekends.
  3200. *
  3201. * @see [gapUnit](plotOptions.series.gapUnit)
  3202. * @see [xAxis.breaks](#xAxis.breaks)
  3203. *
  3204. * @sample {highstock} stock/plotoptions/series-gapsize/
  3205. * Setting the gap size to 2 introduces gaps for weekends
  3206. * in daily datasets.
  3207. *
  3208. * @type {number}
  3209. * @default 0
  3210. * @product highstock
  3211. * @requires modules/broken-axis
  3212. * @apioption plotOptions.series.gapSize
  3213. */
  3214. /**
  3215. * Together with [gapSize](plotOptions.series.gapSize), this option
  3216. * defines where to draw gaps in the graph.
  3217. *
  3218. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  3219. * means that if the distance between two points is greater than
  3220. * 5 times that of the two closest points, the graph will be broken.
  3221. *
  3222. * When the `gapUnit` is `"value"`, the gap is based on absolute
  3223. * axis values, which on a datetime axis is milliseconds. This also
  3224. * applies to the navigator series that inherits gap options from
  3225. * the base series.
  3226. *
  3227. * @see [gapSize](plotOptions.series.gapSize)
  3228. *
  3229. * @type {string}
  3230. * @default relative
  3231. * @since 5.0.13
  3232. * @product highstock
  3233. * @validvalue ["relative", "value"]
  3234. * @requires modules/broken-axis
  3235. * @apioption plotOptions.series.gapUnit
  3236. */
  3237. if (gapSize && i > 0) { // #5008
  3238. // Gap unit is relative
  3239. if (this.options.gapUnit !== 'value') {
  3240. gapSize *= this.basePointRange;
  3241. }
  3242. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  3243. if (groupingSize &&
  3244. groupingSize > gapSize &&
  3245. // Except when DG is forced (e.g. from other series)
  3246. // and has lower granularity than actual points (#11351)
  3247. groupingSize >= this.basePointRange) {
  3248. gapSize = groupingSize;
  3249. }
  3250. // extension for ordinal breaks
  3251. var current = void 0,
  3252. next = void 0;
  3253. while (i--) {
  3254. // Reassign next if it is not visible
  3255. if (!(next && next.visible !== false)) {
  3256. next = points[i + 1];
  3257. }
  3258. current = points[i];
  3259. // Skip iteration if one of the points is not visible
  3260. if (next.visible === false || current.visible === false) {
  3261. continue;
  3262. }
  3263. if (next.x - current.x > gapSize) {
  3264. var xRange = (current.x + next.x) / 2;
  3265. points.splice(// insert after this one
  3266. i + 1, 0, {
  3267. isNull: true,
  3268. x: xRange
  3269. });
  3270. // For stacked chart generate empty stack items, #6546
  3271. if (yAxis.stacking && this.options.stacking) {
  3272. stack = yAxis.stacking.stacks[this.stackKey][xRange] =
  3273. new StackItem(yAxis, yAxis.options
  3274. .stackLabels, false, xRange, this.stack);
  3275. stack.total = 0;
  3276. }
  3277. }
  3278. // Assign current to next for the upcoming iteration
  3279. next = current;
  3280. }
  3281. }
  3282. // Call base method
  3283. return this.getGraphPath(points);
  3284. };
  3285. /* eslint-disable no-invalid-this */
  3286. addEvent(AxisClass, 'init', function () {
  3287. var axis = this;
  3288. if (!axis.brokenAxis) {
  3289. axis.brokenAxis = new BrokenAxisAdditions(axis);
  3290. }
  3291. });
  3292. addEvent(AxisClass, 'afterInit', function () {
  3293. if (typeof this.brokenAxis !== 'undefined') {
  3294. this.brokenAxis.setBreaks(this.options.breaks, false);
  3295. }
  3296. });
  3297. addEvent(AxisClass, 'afterSetTickPositions', function () {
  3298. var axis = this;
  3299. var brokenAxis = axis.brokenAxis;
  3300. if (brokenAxis &&
  3301. brokenAxis.hasBreaks) {
  3302. var tickPositions = this.tickPositions,
  3303. info = this.tickPositions.info,
  3304. newPositions = [],
  3305. i = void 0;
  3306. for (i = 0; i < tickPositions.length; i++) {
  3307. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  3308. newPositions.push(tickPositions[i]);
  3309. }
  3310. }
  3311. this.tickPositions = newPositions;
  3312. this.tickPositions.info = info;
  3313. }
  3314. });
  3315. // Force Axis to be not-ordinal when breaks are defined
  3316. addEvent(AxisClass, 'afterSetOptions', function () {
  3317. if (this.brokenAxis && this.brokenAxis.hasBreaks) {
  3318. this.options.ordinal = false;
  3319. }
  3320. });
  3321. addEvent(SeriesClass, 'afterGeneratePoints', function () {
  3322. var _a = this,
  3323. isDirty = _a.isDirty,
  3324. connectNulls = _a.options.connectNulls,
  3325. points = _a.points,
  3326. xAxis = _a.xAxis,
  3327. yAxis = _a.yAxis;
  3328. // Set, or reset visibility of the points. Axis.setBreaks marks the
  3329. // series as isDirty
  3330. if (isDirty) {
  3331. var i = points.length;
  3332. while (i--) {
  3333. var point = points[i];
  3334. // Respect nulls inside the break (#4275)
  3335. var nullGap = point.y === null && connectNulls === false;
  3336. var isPointInBreak = (!nullGap && ((xAxis &&
  3337. xAxis.brokenAxis &&
  3338. xAxis.brokenAxis.isInAnyBreak(point.x,
  3339. true)) || (yAxis &&
  3340. yAxis.brokenAxis &&
  3341. yAxis.brokenAxis.isInAnyBreak(point.y,
  3342. true))));
  3343. // Set point.visible if in any break.
  3344. // If not in break, reset visible to original value.
  3345. point.visible = isPointInBreak ?
  3346. false :
  3347. point.options.visible !== false;
  3348. }
  3349. }
  3350. });
  3351. addEvent(SeriesClass, 'afterRender', function drawPointsWrapped() {
  3352. this.drawBreaks(this.xAxis, ['x']);
  3353. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  3354. });
  3355. };
  3356. return BrokenAxis;
  3357. }());
  3358. BrokenAxis.compose(Axis, Series); // @todo remove automatism
  3359. return BrokenAxis;
  3360. });
  3361. _registerModule(_modules, 'Core/Axis/TreeGridAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Axis/Tick.js'], _modules['Gantt/Tree.js'], _modules['Core/Axis/TreeGridTick.js'], _modules['Mixins/TreeSeries.js'], _modules['Core/Utilities.js']], function (Axis, Tick, Tree, TreeGridTick, mixinTreeSeries, U) {
  3362. /* *
  3363. *
  3364. * (c) 2016 Highsoft AS
  3365. * Authors: Jon Arild Nygard
  3366. *
  3367. * License: www.highcharts.com/license
  3368. *
  3369. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3370. *
  3371. * */
  3372. var getLevelOptions = mixinTreeSeries.getLevelOptions;
  3373. var addEvent = U.addEvent,
  3374. find = U.find,
  3375. fireEvent = U.fireEvent,
  3376. isArray = U.isArray,
  3377. isNumber = U.isNumber,
  3378. isObject = U.isObject,
  3379. isString = U.isString,
  3380. merge = U.merge,
  3381. pick = U.pick,
  3382. wrap = U.wrap;
  3383. /**
  3384. * @private
  3385. */
  3386. var TreeGridAxis;
  3387. (function (TreeGridAxis) {
  3388. /* *
  3389. *
  3390. * Interfaces
  3391. *
  3392. * */
  3393. /* *
  3394. *
  3395. * Variables
  3396. *
  3397. * */
  3398. var applied = false;
  3399. /* *
  3400. *
  3401. * Functions
  3402. *
  3403. * */
  3404. /**
  3405. * @private
  3406. */
  3407. function compose(AxisClass) {
  3408. if (!applied) {
  3409. wrap(AxisClass.prototype, 'generateTick', wrapGenerateTick);
  3410. wrap(AxisClass.prototype, 'getMaxLabelDimensions', wrapGetMaxLabelDimensions);
  3411. wrap(AxisClass.prototype, 'init', wrapInit);
  3412. wrap(AxisClass.prototype, 'setTickInterval', wrapSetTickInterval);
  3413. TreeGridTick.compose(Tick);
  3414. applied = true;
  3415. }
  3416. }
  3417. TreeGridAxis.compose = compose;
  3418. /**
  3419. * @private
  3420. */
  3421. function getBreakFromNode(node, max) {
  3422. var from = node.collapseStart || 0,
  3423. to = node.collapseEnd || 0;
  3424. // In broken-axis, the axis.max is minimized until it is not within a
  3425. // break. Therefore, if break.to is larger than axis.max, the axis.to
  3426. // should not add the 0.5 axis.tickMarkOffset, to avoid adding a break
  3427. // larger than axis.max.
  3428. // TODO consider simplifying broken-axis and this might solve itself
  3429. if (to >= max) {
  3430. from -= 0.5;
  3431. }
  3432. return {
  3433. from: from,
  3434. to: to,
  3435. showPoints: false
  3436. };
  3437. }
  3438. /**
  3439. * Creates a tree structure of the data, and the treegrid. Calculates
  3440. * categories, and y-values of points based on the tree.
  3441. *
  3442. * @private
  3443. * @function getTreeGridFromData
  3444. *
  3445. * @param {Array<Highcharts.GanttPointOptions>} data
  3446. * All the data points to display in the axis.
  3447. *
  3448. * @param {boolean} uniqueNames
  3449. * Wether or not the data node with the same name should share grid cell. If
  3450. * true they do share cell. False by default.
  3451. *
  3452. * @param {number} numberOfSeries
  3453. *
  3454. * @return {object}
  3455. * Returns an object containing categories, mapOfIdToNode,
  3456. * mapOfPosToGridNode, and tree.
  3457. *
  3458. * @todo There should be only one point per line.
  3459. * @todo It should be optional to have one category per point, or merge
  3460. * cells
  3461. * @todo Add unit-tests.
  3462. */
  3463. function getTreeGridFromData(data, uniqueNames, numberOfSeries) {
  3464. var categories = [],
  3465. collapsedNodes = [],
  3466. mapOfIdToNode = {},
  3467. mapOfPosToGridNode = {},
  3468. posIterator = -1,
  3469. uniqueNamesEnabled = typeof uniqueNames === 'boolean' ? uniqueNames : false,
  3470. tree;
  3471. // Build the tree from the series data.
  3472. var treeParams = {
  3473. // After the children has been created.
  3474. after: function (node) {
  3475. var gridNode = mapOfPosToGridNode[node.pos],
  3476. height = 0,
  3477. descendants = 0;
  3478. gridNode.children.forEach(function (child) {
  3479. descendants += (child.descendants || 0) + 1;
  3480. height = Math.max((child.height || 0) + 1, height);
  3481. });
  3482. gridNode.descendants = descendants;
  3483. gridNode.height = height;
  3484. if (gridNode.collapsed) {
  3485. collapsedNodes.push(gridNode);
  3486. }
  3487. },
  3488. // Before the children has been created.
  3489. before: function (node) {
  3490. var data = isObject(node.data,
  3491. true) ? node.data : {},
  3492. name = isString(data.name) ? data.name : '',
  3493. parentNode = mapOfIdToNode[node.parent],
  3494. parentGridNode = (isObject(parentNode,
  3495. true) ?
  3496. mapOfPosToGridNode[parentNode.pos] :
  3497. null),
  3498. hasSameName = function (x) {
  3499. return x.name === name;
  3500. }, gridNode, pos;
  3501. // If not unique names, look for sibling node with the same name
  3502. if (uniqueNamesEnabled &&
  3503. isObject(parentGridNode, true) &&
  3504. !!(gridNode = find(parentGridNode.children, hasSameName))) {
  3505. // If there is a gridNode with the same name, reuse position
  3506. pos = gridNode.pos;
  3507. // Add data node to list of nodes in the grid node.
  3508. gridNode.nodes.push(node);
  3509. }
  3510. else {
  3511. // If it is a new grid node, increment position.
  3512. pos = posIterator++;
  3513. }
  3514. // Add new grid node to map.
  3515. if (!mapOfPosToGridNode[pos]) {
  3516. mapOfPosToGridNode[pos] = gridNode = {
  3517. depth: parentGridNode ? parentGridNode.depth + 1 : 0,
  3518. name: name,
  3519. id: data.id,
  3520. nodes: [node],
  3521. children: [],
  3522. pos: pos
  3523. };
  3524. // If not root, then add name to categories.
  3525. if (pos !== -1) {
  3526. categories.push(name);
  3527. }
  3528. // Add name to list of children.
  3529. if (isObject(parentGridNode, true)) {
  3530. parentGridNode.children.push(gridNode);
  3531. }
  3532. }
  3533. // Add data node to map
  3534. if (isString(node.id)) {
  3535. mapOfIdToNode[node.id] = node;
  3536. }
  3537. // If one of the points are collapsed, then start the grid node
  3538. // in collapsed state.
  3539. if (gridNode &&
  3540. data.collapsed === true) {
  3541. gridNode.collapsed = true;
  3542. }
  3543. // Assign pos to data node
  3544. node.pos = pos;
  3545. }
  3546. };
  3547. var updateYValuesAndTickPos = function (map,
  3548. numberOfSeries) {
  3549. var setValues = function (gridNode,
  3550. start,
  3551. result) {
  3552. var nodes = gridNode.nodes,
  3553. end = start + (start === -1 ? 0 : numberOfSeries - 1),
  3554. diff = (end - start) / 2,
  3555. padding = 0.5,
  3556. pos = start + diff;
  3557. nodes.forEach(function (node) {
  3558. var data = node.data;
  3559. if (isObject(data, true)) {
  3560. // Update point
  3561. data.y = start + (data.seriesIndex || 0);
  3562. // Remove the property once used
  3563. delete data.seriesIndex;
  3564. }
  3565. node.pos = pos;
  3566. });
  3567. result[pos] = gridNode;
  3568. gridNode.pos = pos;
  3569. gridNode.tickmarkOffset = diff + padding;
  3570. gridNode.collapseStart = end + padding;
  3571. gridNode.children.forEach(function (child) {
  3572. setValues(child, end + 1, result);
  3573. end = (child.collapseEnd || 0) - padding;
  3574. });
  3575. // Set collapseEnd to the end of the last child node.
  3576. gridNode.collapseEnd = end + padding;
  3577. return result;
  3578. };
  3579. return setValues(map['-1'], -1, {});
  3580. };
  3581. // Create tree from data
  3582. tree = Tree.getTree(data, treeParams);
  3583. // Update y values of data, and set calculate tick positions.
  3584. mapOfPosToGridNode = updateYValuesAndTickPos(mapOfPosToGridNode, numberOfSeries);
  3585. // Return the resulting data.
  3586. return {
  3587. categories: categories,
  3588. mapOfIdToNode: mapOfIdToNode,
  3589. mapOfPosToGridNode: mapOfPosToGridNode,
  3590. collapsedNodes: collapsedNodes,
  3591. tree: tree
  3592. };
  3593. }
  3594. /**
  3595. * Builds the tree of categories and calculates its positions.
  3596. * @private
  3597. * @param {object} e Event object
  3598. * @param {object} e.target The chart instance which the event was fired on.
  3599. * @param {object[]} e.target.axes The axes of the chart.
  3600. */
  3601. function onBeforeRender(e) {
  3602. var chart = e.target,
  3603. axes = chart.axes;
  3604. axes.filter(function (axis) {
  3605. return axis.options.type === 'treegrid';
  3606. }).forEach(function (axis) {
  3607. var options = axis.options || {},
  3608. labelOptions = options.labels,
  3609. uniqueNames = options.uniqueNames,
  3610. numberOfSeries = 0,
  3611. isDirty,
  3612. data,
  3613. treeGrid,
  3614. max = options.max;
  3615. // Check whether any of series is rendering for the first time,
  3616. // visibility has changed, or its data is dirty,
  3617. // and only then update. #10570, #10580
  3618. // Also check if mapOfPosToGridNode exists. #10887
  3619. isDirty = (!axis.treeGrid.mapOfPosToGridNode ||
  3620. axis.series.some(function (series) {
  3621. return !series.hasRendered ||
  3622. series.isDirtyData ||
  3623. series.isDirty;
  3624. }));
  3625. if (isDirty) {
  3626. // Concatenate data from all series assigned to this axis.
  3627. data = axis.series.reduce(function (arr, s) {
  3628. if (s.visible) {
  3629. // Push all data to array
  3630. (s.options.data || []).forEach(function (data) {
  3631. // For using keys - rebuild the data structure
  3632. if (s.options.keys && s.options.keys.length) {
  3633. data = s.pointClass.prototype.optionsToObject.call({ series: s }, data);
  3634. s.pointClass.setGanttPointAliases(data);
  3635. }
  3636. if (isObject(data, true)) {
  3637. // Set series index on data. Removed again
  3638. // after use.
  3639. data.seriesIndex = numberOfSeries;
  3640. arr.push(data);
  3641. }
  3642. });
  3643. // Increment series index
  3644. if (uniqueNames === true) {
  3645. numberOfSeries++;
  3646. }
  3647. }
  3648. return arr;
  3649. }, []);
  3650. // If max is higher than set data - add a
  3651. // dummy data to render categories #10779
  3652. if (max && data.length < max) {
  3653. for (var i = data.length; i <= max; i++) {
  3654. data.push({
  3655. // Use the zero-width character
  3656. // to avoid conflict with uniqueNames
  3657. name: i + '\u200B'
  3658. });
  3659. }
  3660. }
  3661. // setScale is fired after all the series is initialized,
  3662. // which is an ideal time to update the axis.categories.
  3663. treeGrid = getTreeGridFromData(data, uniqueNames || false, (uniqueNames === true) ? numberOfSeries : 1);
  3664. // Assign values to the axis.
  3665. axis.categories = treeGrid.categories;
  3666. axis.treeGrid.mapOfPosToGridNode = treeGrid.mapOfPosToGridNode;
  3667. axis.hasNames = true;
  3668. axis.treeGrid.tree = treeGrid.tree;
  3669. // Update yData now that we have calculated the y values
  3670. axis.series.forEach(function (series) {
  3671. var axisData = (series.options.data || []).map(function (d) {
  3672. if (isArray(d) && series.options.keys && series.options.keys.length) {
  3673. // Get the axisData from the data array used to
  3674. // build the treeGrid where has been modified
  3675. data.forEach(function (point) {
  3676. if (d.indexOf(point.x) >= 0 && d.indexOf(point.x2) >= 0) {
  3677. d = point;
  3678. }
  3679. });
  3680. }
  3681. return isObject(d, true) ? merge(d) : d;
  3682. });
  3683. // Avoid destroying points when series is not visible
  3684. if (series.visible) {
  3685. series.setData(axisData, false);
  3686. }
  3687. });
  3688. // Calculate the label options for each level in the tree.
  3689. axis.treeGrid.mapOptionsToLevel =
  3690. getLevelOptions({
  3691. defaults: labelOptions,
  3692. from: 1,
  3693. levels: labelOptions && labelOptions.levels,
  3694. to: axis.treeGrid.tree && axis.treeGrid.tree.height
  3695. });
  3696. // Setting initial collapsed nodes
  3697. if (e.type === 'beforeRender') {
  3698. axis.treeGrid.collapsedNodes = treeGrid.collapsedNodes;
  3699. }
  3700. }
  3701. });
  3702. }
  3703. /**
  3704. * Generates a tick for initial positioning.
  3705. *
  3706. * @private
  3707. * @function Highcharts.GridAxis#generateTick
  3708. *
  3709. * @param {Function} proceed
  3710. * The original generateTick function.
  3711. *
  3712. * @param {number} pos
  3713. * The tick position in axis values.
  3714. */
  3715. function wrapGenerateTick(proceed, pos) {
  3716. var axis = this,
  3717. mapOptionsToLevel = axis.treeGrid.mapOptionsToLevel || {},
  3718. isTreeGrid = axis.options.type === 'treegrid',
  3719. ticks = axis.ticks;
  3720. var tick = ticks[pos],
  3721. levelOptions,
  3722. options,
  3723. gridNode;
  3724. if (isTreeGrid &&
  3725. axis.treeGrid.mapOfPosToGridNode) {
  3726. gridNode = axis.treeGrid.mapOfPosToGridNode[pos];
  3727. levelOptions = mapOptionsToLevel[gridNode.depth];
  3728. if (levelOptions) {
  3729. options = {
  3730. labels: levelOptions
  3731. };
  3732. }
  3733. if (!tick) {
  3734. ticks[pos] = tick =
  3735. new Tick(axis, pos, void 0, void 0, {
  3736. category: gridNode.name,
  3737. tickmarkOffset: gridNode.tickmarkOffset,
  3738. options: options
  3739. });
  3740. }
  3741. else {
  3742. // update labels depending on tick interval
  3743. tick.parameters.category = gridNode.name;
  3744. tick.options = options;
  3745. tick.addLabel();
  3746. }
  3747. }
  3748. else {
  3749. proceed.apply(axis, Array.prototype.slice.call(arguments, 1));
  3750. }
  3751. }
  3752. /**
  3753. * Override to add indentation to axis.maxLabelDimensions.
  3754. *
  3755. * @private
  3756. * @function Highcharts.GridAxis#getMaxLabelDimensions
  3757. *
  3758. * @param {Function} proceed
  3759. * The original function
  3760. */
  3761. function wrapGetMaxLabelDimensions(proceed) {
  3762. var axis = this,
  3763. options = axis.options,
  3764. retVal = proceed.apply(axis,
  3765. Array.prototype.slice.call(arguments, 1)),
  3766. isTreeGrid = options.type === 'treegrid';
  3767. var treeDepth;
  3768. if (isTreeGrid && axis.treeGrid.mapOfPosToGridNode) {
  3769. treeDepth = axis.treeGrid.mapOfPosToGridNode[-1].height || 0;
  3770. retVal.width += options.labels.indentation * (treeDepth - 1);
  3771. }
  3772. return retVal;
  3773. }
  3774. /**
  3775. * @private
  3776. */
  3777. function wrapInit(proceed, chart, userOptions) {
  3778. var axis = this,
  3779. isTreeGrid = userOptions.type === 'treegrid';
  3780. if (!axis.treeGrid) {
  3781. axis.treeGrid = new Additions(axis);
  3782. }
  3783. // Set default and forced options for TreeGrid
  3784. if (isTreeGrid) {
  3785. // Add event for updating the categories of a treegrid.
  3786. // NOTE Preferably these events should be set on the axis.
  3787. addEvent(chart, 'beforeRender', onBeforeRender);
  3788. addEvent(chart, 'beforeRedraw', onBeforeRender);
  3789. // Add new collapsed nodes on addseries
  3790. addEvent(chart, 'addSeries', function (e) {
  3791. if (e.options.data) {
  3792. var treeGrid = getTreeGridFromData(e.options.data,
  3793. userOptions.uniqueNames || false, 1);
  3794. axis.treeGrid.collapsedNodes = (axis.treeGrid.collapsedNodes || []).concat(treeGrid.collapsedNodes);
  3795. }
  3796. });
  3797. // Collapse all nodes in axis.treegrid.collapsednodes
  3798. // where collapsed equals true.
  3799. addEvent(axis, 'foundExtremes', function () {
  3800. if (axis.treeGrid.collapsedNodes) {
  3801. axis.treeGrid.collapsedNodes.forEach(function (node) {
  3802. var breaks = axis.treeGrid.collapse(node);
  3803. if (axis.brokenAxis) {
  3804. axis.brokenAxis.setBreaks(breaks, false);
  3805. // remove the node from the axis collapsedNodes
  3806. if (axis.treeGrid.collapsedNodes) {
  3807. axis.treeGrid.collapsedNodes = axis.treeGrid.collapsedNodes.filter(function (n) {
  3808. return node.collapseStart !== n.collapseStart ||
  3809. node.collapseEnd !== n.collapseEnd;
  3810. });
  3811. }
  3812. }
  3813. });
  3814. }
  3815. });
  3816. // If staticScale is not defined on the yAxis
  3817. // and chart height is set, set axis.isDirty
  3818. // to ensure collapsing works (#12012)
  3819. addEvent(axis, 'afterBreaks', function () {
  3820. if (axis.coll === 'yAxis' &&
  3821. !axis.staticScale &&
  3822. axis.chart.options.chart.height) {
  3823. axis.isDirty = true;
  3824. }
  3825. });
  3826. userOptions = merge({
  3827. // Default options
  3828. grid: {
  3829. enabled: true
  3830. },
  3831. // TODO: add support for align in treegrid.
  3832. labels: {
  3833. align: 'left',
  3834. /**
  3835. * Set options on specific levels in a tree grid axis. Takes
  3836. * precedence over labels options.
  3837. *
  3838. * @sample {gantt} gantt/treegrid-axis/labels-levels
  3839. * Levels on TreeGrid Labels
  3840. *
  3841. * @type {Array<*>}
  3842. * @product gantt
  3843. * @apioption yAxis.labels.levels
  3844. *
  3845. * @private
  3846. */
  3847. levels: [{
  3848. /**
  3849. * Specify the level which the options within this object
  3850. * applies to.
  3851. *
  3852. * @type {number}
  3853. * @product gantt
  3854. * @apioption yAxis.labels.levels.level
  3855. *
  3856. * @private
  3857. */
  3858. level: void 0
  3859. }, {
  3860. level: 1,
  3861. /**
  3862. * @type {Highcharts.CSSObject}
  3863. * @product gantt
  3864. * @apioption yAxis.labels.levels.style
  3865. *
  3866. * @private
  3867. */
  3868. style: {
  3869. /** @ignore-option */
  3870. fontWeight: 'bold'
  3871. }
  3872. }],
  3873. /**
  3874. * The symbol for the collapse and expand icon in a
  3875. * treegrid.
  3876. *
  3877. * @product gantt
  3878. * @optionparent yAxis.labels.symbol
  3879. *
  3880. * @private
  3881. */
  3882. symbol: {
  3883. /**
  3884. * The symbol type. Points to a definition function in
  3885. * the `Highcharts.Renderer.symbols` collection.
  3886. *
  3887. * @type {Highcharts.SymbolKeyValue}
  3888. *
  3889. * @private
  3890. */
  3891. type: 'triangle',
  3892. x: -5,
  3893. y: -5,
  3894. height: 10,
  3895. width: 10,
  3896. padding: 5
  3897. }
  3898. },
  3899. uniqueNames: false
  3900. }, userOptions, {
  3901. // Forced options
  3902. reversed: true,
  3903. // grid.columns is not supported in treegrid
  3904. grid: {
  3905. columns: void 0
  3906. }
  3907. });
  3908. }
  3909. // Now apply the original function with the original arguments,
  3910. // which are sliced off this function's arguments
  3911. proceed.apply(axis, [chart, userOptions]);
  3912. if (isTreeGrid) {
  3913. axis.hasNames = true;
  3914. axis.options.showLastLabel = true;
  3915. }
  3916. }
  3917. /**
  3918. * Set the tick positions, tickInterval, axis min and max.
  3919. *
  3920. * @private
  3921. * @function Highcharts.GridAxis#setTickInterval
  3922. *
  3923. * @param {Function} proceed
  3924. * The original setTickInterval function.
  3925. */
  3926. function wrapSetTickInterval(proceed) {
  3927. var axis = this,
  3928. options = axis.options,
  3929. isTreeGrid = options.type === 'treegrid';
  3930. if (isTreeGrid) {
  3931. axis.min = pick(axis.userMin, options.min, axis.dataMin);
  3932. axis.max = pick(axis.userMax, options.max, axis.dataMax);
  3933. fireEvent(axis, 'foundExtremes');
  3934. // setAxisTranslation modifies the min and max according to
  3935. // axis breaks.
  3936. axis.setAxisTranslation();
  3937. axis.tickmarkOffset = 0.5;
  3938. axis.tickInterval = 1;
  3939. axis.tickPositions = axis.treeGrid.mapOfPosToGridNode ?
  3940. axis.treeGrid.getTickPositions() :
  3941. [];
  3942. }
  3943. else {
  3944. proceed.apply(axis, Array.prototype.slice.call(arguments, 1));
  3945. }
  3946. }
  3947. /* *
  3948. *
  3949. * Classes
  3950. *
  3951. * */
  3952. /**
  3953. * @private
  3954. * @class
  3955. */
  3956. var Additions = /** @class */ (function () {
  3957. /* *
  3958. *
  3959. * Constructors
  3960. *
  3961. * */
  3962. /**
  3963. * @private
  3964. */
  3965. function Additions(axis) {
  3966. this.axis = axis;
  3967. }
  3968. /* *
  3969. *
  3970. * Functions
  3971. *
  3972. * */
  3973. /**
  3974. * Set the collapse status.
  3975. *
  3976. * @private
  3977. *
  3978. * @param {Highcharts.Axis} axis
  3979. * The axis to check against.
  3980. *
  3981. * @param {Highcharts.GridNode} node
  3982. * The node to collapse.
  3983. */
  3984. Additions.prototype.setCollapsedStatus = function (node) {
  3985. var axis = this.axis,
  3986. chart = axis.chart;
  3987. axis.series.forEach(function (series) {
  3988. var data = series.options.data;
  3989. if (node.id && data) {
  3990. var point = chart.get(node.id),
  3991. dataPoint = data[series.data.indexOf(point)];
  3992. if (point && dataPoint) {
  3993. point.collapsed = node.collapsed;
  3994. dataPoint.collapsed = node.collapsed;
  3995. }
  3996. }
  3997. });
  3998. };
  3999. /**
  4000. * Calculates the new axis breaks to collapse a node.
  4001. *
  4002. * @private
  4003. *
  4004. * @param {Highcharts.Axis} axis
  4005. * The axis to check against.
  4006. *
  4007. * @param {Highcharts.GridNode} node
  4008. * The node to collapse.
  4009. *
  4010. * @param {number} pos
  4011. * The tick position to collapse.
  4012. *
  4013. * @return {Array<object>}
  4014. * Returns an array of the new breaks for the axis.
  4015. */
  4016. Additions.prototype.collapse = function (node) {
  4017. var axis = this.axis,
  4018. breaks = (axis.options.breaks || []),
  4019. obj = getBreakFromNode(node,
  4020. axis.max);
  4021. breaks.push(obj);
  4022. // Change the collapsed flag #13838
  4023. node.collapsed = true;
  4024. axis.treeGrid.setCollapsedStatus(node);
  4025. return breaks;
  4026. };
  4027. /**
  4028. * Calculates the new axis breaks to expand a node.
  4029. *
  4030. * @private
  4031. *
  4032. * @param {Highcharts.Axis} axis
  4033. * The axis to check against.
  4034. *
  4035. * @param {Highcharts.GridNode} node
  4036. * The node to expand.
  4037. *
  4038. * @param {number} pos
  4039. * The tick position to expand.
  4040. *
  4041. * @return {Array<object>}
  4042. * Returns an array of the new breaks for the axis.
  4043. */
  4044. Additions.prototype.expand = function (node) {
  4045. var axis = this.axis,
  4046. breaks = (axis.options.breaks || []),
  4047. obj = getBreakFromNode(node,
  4048. axis.max);
  4049. // Change the collapsed flag #13838
  4050. node.collapsed = false;
  4051. axis.treeGrid.setCollapsedStatus(node);
  4052. // Remove the break from the axis breaks array.
  4053. return breaks.reduce(function (arr, b) {
  4054. if (b.to !== obj.to || b.from !== obj.from) {
  4055. arr.push(b);
  4056. }
  4057. return arr;
  4058. }, []);
  4059. };
  4060. /**
  4061. * Creates a list of positions for the ticks on the axis. Filters out
  4062. * positions that are outside min and max, or is inside an axis break.
  4063. *
  4064. * @private
  4065. *
  4066. * @return {Array<number>}
  4067. * List of positions.
  4068. */
  4069. Additions.prototype.getTickPositions = function () {
  4070. var axis = this.axis,
  4071. roundedMin = Math.floor(axis.min / axis.tickInterval) * axis.tickInterval,
  4072. roundedMax = Math.ceil(axis.max / axis.tickInterval) * axis.tickInterval;
  4073. return Object.keys(axis.treeGrid.mapOfPosToGridNode || {}).reduce(function (arr, key) {
  4074. var pos = +key;
  4075. if (pos >= roundedMin &&
  4076. pos <= roundedMax &&
  4077. !(axis.brokenAxis && axis.brokenAxis.isInAnyBreak(pos))) {
  4078. arr.push(pos);
  4079. }
  4080. return arr;
  4081. }, []);
  4082. };
  4083. /**
  4084. * Check if a node is collapsed.
  4085. *
  4086. * @private
  4087. *
  4088. * @param {Highcharts.Axis} axis
  4089. * The axis to check against.
  4090. *
  4091. * @param {object} node
  4092. * The node to check if is collapsed.
  4093. *
  4094. * @param {number} pos
  4095. * The tick position to collapse.
  4096. *
  4097. * @return {boolean}
  4098. * Returns true if collapsed, false if expanded.
  4099. */
  4100. Additions.prototype.isCollapsed = function (node) {
  4101. var axis = this.axis,
  4102. breaks = (axis.options.breaks || []),
  4103. obj = getBreakFromNode(node,
  4104. axis.max);
  4105. return breaks.some(function (b) {
  4106. return b.from === obj.from && b.to === obj.to;
  4107. });
  4108. };
  4109. /**
  4110. * Calculates the new axis breaks after toggling the collapse/expand
  4111. * state of a node. If it is collapsed it will be expanded, and if it is
  4112. * exapended it will be collapsed.
  4113. *
  4114. * @private
  4115. *
  4116. * @param {Highcharts.Axis} axis
  4117. * The axis to check against.
  4118. *
  4119. * @param {Highcharts.GridNode} node
  4120. * The node to toggle.
  4121. *
  4122. * @return {Array<object>}
  4123. * Returns an array of the new breaks for the axis.
  4124. */
  4125. Additions.prototype.toggleCollapse = function (node) {
  4126. return (this.isCollapsed(node) ?
  4127. this.expand(node) :
  4128. this.collapse(node));
  4129. };
  4130. return Additions;
  4131. }());
  4132. TreeGridAxis.Additions = Additions;
  4133. })(TreeGridAxis || (TreeGridAxis = {}));
  4134. // Make utility functions available for testing.
  4135. Axis.prototype.utils = {
  4136. getNode: Tree.getNode
  4137. };
  4138. TreeGridAxis.compose(Axis);
  4139. return TreeGridAxis;
  4140. });
  4141. _registerModule(_modules, 'Extensions/CurrentDateIndication.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Color/Palette.js'], _modules['Core/Utilities.js'], _modules['Core/Axis/PlotLineOrBand.js']], function (Axis, palette, U, PlotLineOrBand) {
  4142. /* *
  4143. *
  4144. * (c) 2016-2021 Highsoft AS
  4145. *
  4146. * Author: Lars A. V. Cabrera
  4147. *
  4148. * License: www.highcharts.com/license
  4149. *
  4150. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4151. *
  4152. * */
  4153. var addEvent = U.addEvent,
  4154. merge = U.merge,
  4155. wrap = U.wrap;
  4156. /**
  4157. * Show an indicator on the axis for the current date and time. Can be a
  4158. * boolean or a configuration object similar to
  4159. * [xAxis.plotLines](#xAxis.plotLines).
  4160. *
  4161. * @sample gantt/current-date-indicator/demo
  4162. * Current date indicator enabled
  4163. * @sample gantt/current-date-indicator/object-config
  4164. * Current date indicator with custom options
  4165. *
  4166. * @declare Highcharts.CurrentDateIndicatorOptions
  4167. * @type {boolean|CurrentDateIndicatorOptions}
  4168. * @default true
  4169. * @extends xAxis.plotLines
  4170. * @excluding value
  4171. * @product gantt
  4172. * @apioption xAxis.currentDateIndicator
  4173. */
  4174. var defaultOptions = {
  4175. color: palette.highlightColor20,
  4176. width: 2,
  4177. /**
  4178. * @declare Highcharts.AxisCurrentDateIndicatorLabelOptions
  4179. */
  4180. label: {
  4181. /**
  4182. * Format of the label. This options is passed as the fist argument to
  4183. * [dateFormat](/class-reference/Highcharts#.dateFormat) function.
  4184. *
  4185. * @type {string}
  4186. * @default %a, %b %d %Y, %H:%M
  4187. * @product gantt
  4188. * @apioption xAxis.currentDateIndicator.label.format
  4189. */
  4190. format: '%a, %b %d %Y, %H:%M',
  4191. formatter: function (value, format) {
  4192. return this.axis.chart.time.dateFormat(format || '', value);
  4193. },
  4194. rotation: 0,
  4195. /**
  4196. * @type {Highcharts.CSSObject}
  4197. */
  4198. style: {
  4199. /** @internal */
  4200. fontSize: '10px'
  4201. }
  4202. }
  4203. };
  4204. /* eslint-disable no-invalid-this */
  4205. addEvent(Axis, 'afterSetOptions', function () {
  4206. var options = this.options,
  4207. cdiOptions = options.currentDateIndicator;
  4208. if (cdiOptions) {
  4209. var plotLineOptions = typeof cdiOptions === 'object' ?
  4210. merge(defaultOptions,
  4211. cdiOptions) :
  4212. merge(defaultOptions);
  4213. plotLineOptions.value = Date.now();
  4214. plotLineOptions.className = 'highcharts-current-date-indicator';
  4215. if (!options.plotLines) {
  4216. options.plotLines = [];
  4217. }
  4218. options.plotLines.push(plotLineOptions);
  4219. }
  4220. });
  4221. addEvent(PlotLineOrBand, 'render', function () {
  4222. // If the label already exists, update its text
  4223. if (this.label) {
  4224. this.label.attr({
  4225. text: this.getLabelText(this.options.label)
  4226. });
  4227. }
  4228. });
  4229. wrap(PlotLineOrBand.prototype, 'getLabelText', function (defaultMethod, defaultLabelOptions) {
  4230. var options = this.options;
  4231. if (options &&
  4232. options.className &&
  4233. options.className.indexOf('highcharts-current-date-indicator') !== -1 &&
  4234. options.label &&
  4235. typeof options.label.formatter === 'function') {
  4236. options.value = Date.now();
  4237. return options.label.formatter
  4238. .call(this, options.value, options.label.format);
  4239. }
  4240. return defaultMethod.call(this, defaultLabelOptions);
  4241. });
  4242. });
  4243. _registerModule(_modules, 'Extensions/StaticScale.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Utilities.js']], function (Axis, Chart, U) {
  4244. /* *
  4245. *
  4246. * (c) 2016-2021 Torstein Honsi, Lars Cabrera
  4247. *
  4248. * License: www.highcharts.com/license
  4249. *
  4250. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4251. *
  4252. * */
  4253. var addEvent = U.addEvent,
  4254. defined = U.defined,
  4255. isNumber = U.isNumber,
  4256. pick = U.pick;
  4257. /* eslint-disable no-invalid-this */
  4258. /**
  4259. * For vertical axes only. Setting the static scale ensures that each tick unit
  4260. * is translated into a fixed pixel height. For example, setting the static
  4261. * scale to 24 results in each Y axis category taking up 24 pixels, and the
  4262. * height of the chart adjusts. Adding or removing items will make the chart
  4263. * resize.
  4264. *
  4265. * @sample gantt/xrange-series/demo/
  4266. * X-range series with static scale
  4267. *
  4268. * @type {number}
  4269. * @default 50
  4270. * @since 6.2.0
  4271. * @product gantt
  4272. * @apioption yAxis.staticScale
  4273. */
  4274. addEvent(Axis, 'afterSetOptions', function () {
  4275. var chartOptions = this.chart.options.chart;
  4276. if (!this.horiz &&
  4277. isNumber(this.options.staticScale) &&
  4278. (!chartOptions.height ||
  4279. (chartOptions.scrollablePlotArea &&
  4280. chartOptions.scrollablePlotArea.minHeight))) {
  4281. this.staticScale = this.options.staticScale;
  4282. }
  4283. });
  4284. Chart.prototype.adjustHeight = function () {
  4285. if (this.redrawTrigger !== 'adjustHeight') {
  4286. (this.axes || []).forEach(function (axis) {
  4287. var chart = axis.chart,
  4288. animate = !!chart.initiatedScale &&
  4289. chart.options.animation,
  4290. staticScale = axis.options.staticScale,
  4291. height,
  4292. diff;
  4293. if (axis.staticScale && defined(axis.min)) {
  4294. height = pick(axis.brokenAxis && axis.brokenAxis.unitLength, axis.max + axis.tickInterval - axis.min) * staticScale;
  4295. // Minimum height is 1 x staticScale.
  4296. height = Math.max(height, staticScale);
  4297. diff = height - chart.plotHeight;
  4298. if (!chart.scrollablePixelsY && Math.abs(diff) >= 1) {
  4299. chart.plotHeight = height;
  4300. chart.redrawTrigger = 'adjustHeight';
  4301. chart.setSize(void 0, chart.chartHeight + diff, animate);
  4302. }
  4303. // Make sure clip rects have the right height before initial
  4304. // animation.
  4305. axis.series.forEach(function (series) {
  4306. var clipRect = series.sharedClipKey &&
  4307. chart.sharedClips[series.sharedClipKey];
  4308. if (clipRect) {
  4309. clipRect.attr(chart.inverted ? {
  4310. width: chart.plotHeight
  4311. } : {
  4312. height: chart.plotHeight
  4313. });
  4314. }
  4315. });
  4316. }
  4317. });
  4318. this.initiatedScale = true;
  4319. }
  4320. this.redrawTrigger = null;
  4321. };
  4322. addEvent(Chart, 'render', Chart.prototype.adjustHeight);
  4323. });
  4324. _registerModule(_modules, 'Extensions/ArrowSymbols.js', [_modules['Core/Renderer/SVG/SVGRenderer.js']], function (SVGRenderer) {
  4325. /* *
  4326. *
  4327. * (c) 2017 Highsoft AS
  4328. * Authors: Lars A. V. Cabrera
  4329. *
  4330. * License: www.highcharts.com/license
  4331. *
  4332. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4333. *
  4334. * */
  4335. /**
  4336. * Creates an arrow symbol. Like a triangle, except not filled.
  4337. * ```
  4338. * o
  4339. * o
  4340. * o
  4341. * o
  4342. * o
  4343. * o
  4344. * o
  4345. * ```
  4346. *
  4347. * @private
  4348. * @function
  4349. *
  4350. * @param {number} x
  4351. * x position of the arrow
  4352. *
  4353. * @param {number} y
  4354. * y position of the arrow
  4355. *
  4356. * @param {number} w
  4357. * width of the arrow
  4358. *
  4359. * @param {number} h
  4360. * height of the arrow
  4361. *
  4362. * @return {Highcharts.SVGPathArray}
  4363. * Path array
  4364. */
  4365. SVGRenderer.prototype.symbols.arrow = function (x, y, w, h) {
  4366. return [
  4367. ['M', x, y + h / 2],
  4368. ['L', x + w, y],
  4369. ['L', x, y + h / 2],
  4370. ['L', x + w, y + h]
  4371. ];
  4372. };
  4373. /**
  4374. * Creates a half-width arrow symbol. Like a triangle, except not filled.
  4375. * ```
  4376. * o
  4377. * o
  4378. * o
  4379. * o
  4380. * o
  4381. * ```
  4382. *
  4383. * @private
  4384. * @function
  4385. *
  4386. * @param {number} x
  4387. * x position of the arrow
  4388. *
  4389. * @param {number} y
  4390. * y position of the arrow
  4391. *
  4392. * @param {number} w
  4393. * width of the arrow
  4394. *
  4395. * @param {number} h
  4396. * height of the arrow
  4397. *
  4398. * @return {Highcharts.SVGPathArray}
  4399. * Path array
  4400. */
  4401. SVGRenderer.prototype.symbols['arrow-half'] = function (x, y, w, h) {
  4402. return SVGRenderer.prototype.symbols.arrow(x, y, w / 2, h);
  4403. };
  4404. /**
  4405. * Creates a left-oriented triangle.
  4406. * ```
  4407. * o
  4408. * ooooooo
  4409. * ooooooooooooo
  4410. * ooooooo
  4411. * o
  4412. * ```
  4413. *
  4414. * @private
  4415. * @function
  4416. *
  4417. * @param {number} x
  4418. * x position of the triangle
  4419. *
  4420. * @param {number} y
  4421. * y position of the triangle
  4422. *
  4423. * @param {number} w
  4424. * width of the triangle
  4425. *
  4426. * @param {number} h
  4427. * height of the triangle
  4428. *
  4429. * @return {Highcharts.SVGPathArray}
  4430. * Path array
  4431. */
  4432. SVGRenderer.prototype.symbols['triangle-left'] = function (x, y, w, h) {
  4433. return [
  4434. ['M', x + w, y],
  4435. ['L', x, y + h / 2],
  4436. ['L', x + w, y + h],
  4437. ['Z']
  4438. ];
  4439. };
  4440. /**
  4441. * Alias function for triangle-left.
  4442. *
  4443. * @private
  4444. * @function
  4445. *
  4446. * @param {number} x
  4447. * x position of the arrow
  4448. *
  4449. * @param {number} y
  4450. * y position of the arrow
  4451. *
  4452. * @param {number} w
  4453. * width of the arrow
  4454. *
  4455. * @param {number} h
  4456. * height of the arrow
  4457. *
  4458. * @return {Highcharts.SVGPathArray}
  4459. * Path array
  4460. */
  4461. SVGRenderer.prototype.symbols['arrow-filled'] = SVGRenderer.prototype.symbols['triangle-left'];
  4462. /**
  4463. * Creates a half-width, left-oriented triangle.
  4464. * ```
  4465. * o
  4466. * oooo
  4467. * ooooooo
  4468. * oooo
  4469. * o
  4470. * ```
  4471. *
  4472. * @private
  4473. * @function
  4474. *
  4475. * @param {number} x
  4476. * x position of the triangle
  4477. *
  4478. * @param {number} y
  4479. * y position of the triangle
  4480. *
  4481. * @param {number} w
  4482. * width of the triangle
  4483. *
  4484. * @param {number} h
  4485. * height of the triangle
  4486. *
  4487. * @return {Highcharts.SVGPathArray}
  4488. * Path array
  4489. */
  4490. SVGRenderer.prototype.symbols['triangle-left-half'] = function (x, y, w, h) {
  4491. return SVGRenderer.prototype.symbols['triangle-left'](x, y, w / 2, h);
  4492. };
  4493. /**
  4494. * Alias function for triangle-left-half.
  4495. *
  4496. * @private
  4497. * @function
  4498. *
  4499. * @param {number} x
  4500. * x position of the arrow
  4501. *
  4502. * @param {number} y
  4503. * y position of the arrow
  4504. *
  4505. * @param {number} w
  4506. * width of the arrow
  4507. *
  4508. * @param {number} h
  4509. * height of the arrow
  4510. *
  4511. * @return {Highcharts.SVGPathArray}
  4512. * Path array
  4513. */
  4514. SVGRenderer.prototype.symbols['arrow-filled-half'] = SVGRenderer.prototype.symbols['triangle-left-half'];
  4515. });
  4516. _registerModule(_modules, 'Gantt/Connection.js', [_modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (H, O, Point, U) {
  4517. /* *
  4518. *
  4519. * (c) 2016 Highsoft AS
  4520. * Authors: Øystein Moseng, Lars A. V. Cabrera
  4521. *
  4522. * License: www.highcharts.com/license
  4523. *
  4524. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4525. *
  4526. * */
  4527. /**
  4528. * The default pathfinder algorithm to use for a chart. It is possible to define
  4529. * your own algorithms by adding them to the
  4530. * `Highcharts.Pathfinder.prototype.algorithms`
  4531. * object before the chart has been created.
  4532. *
  4533. * The default algorithms are as follows:
  4534. *
  4535. * `straight`: Draws a straight line between the connecting
  4536. * points. Does not avoid other points when drawing.
  4537. *
  4538. * `simpleConnect`: Finds a path between the points using right angles
  4539. * only. Takes only starting/ending points into
  4540. * account, and will not avoid other points.
  4541. *
  4542. * `fastAvoid`: Finds a path between the points using right angles
  4543. * only. Will attempt to avoid other points, but its
  4544. * focus is performance over accuracy. Works well with
  4545. * less dense datasets.
  4546. *
  4547. * @typedef {"fastAvoid"|"simpleConnect"|"straight"|string} Highcharts.PathfinderTypeValue
  4548. */
  4549. ''; // detach doclets above
  4550. var defaultOptions = O.defaultOptions;
  4551. var addEvent = U.addEvent,
  4552. defined = U.defined,
  4553. error = U.error,
  4554. extend = U.extend,
  4555. merge = U.merge,
  4556. objectEach = U.objectEach,
  4557. pick = U.pick,
  4558. splat = U.splat;
  4559. var deg2rad = H.deg2rad,
  4560. max = Math.max,
  4561. min = Math.min;
  4562. /*
  4563. @todo:
  4564. - Document how to write your own algorithms
  4565. - Consider adding a Point.pathTo method that wraps creating a connection
  4566. and rendering it
  4567. */
  4568. // Set default Pathfinder options
  4569. extend(defaultOptions, {
  4570. /**
  4571. * The Pathfinder module allows you to define connections between any two
  4572. * points, represented as lines - optionally with markers for the start
  4573. * and/or end points. Multiple algorithms are available for calculating how
  4574. * the connecting lines are drawn.
  4575. *
  4576. * Connector functionality requires Highcharts Gantt to be loaded. In Gantt
  4577. * charts, the connectors are used to draw dependencies between tasks.
  4578. *
  4579. * @see [dependency](series.gantt.data.dependency)
  4580. *
  4581. * @sample gantt/pathfinder/demo
  4582. * Pathfinder connections
  4583. *
  4584. * @declare Highcharts.ConnectorsOptions
  4585. * @product gantt
  4586. * @optionparent connectors
  4587. */
  4588. connectors: {
  4589. /**
  4590. * Enable connectors for this chart. Requires Highcharts Gantt.
  4591. *
  4592. * @type {boolean}
  4593. * @default true
  4594. * @since 6.2.0
  4595. * @apioption connectors.enabled
  4596. */
  4597. /**
  4598. * Set the default dash style for this chart's connecting lines.
  4599. *
  4600. * @type {string}
  4601. * @default solid
  4602. * @since 6.2.0
  4603. * @apioption connectors.dashStyle
  4604. */
  4605. /**
  4606. * Set the default color for this chart's Pathfinder connecting lines.
  4607. * Defaults to the color of the point being connected.
  4608. *
  4609. * @type {Highcharts.ColorString}
  4610. * @since 6.2.0
  4611. * @apioption connectors.lineColor
  4612. */
  4613. /**
  4614. * Set the default pathfinder margin to use, in pixels. Some Pathfinder
  4615. * algorithms attempt to avoid obstacles, such as other points in the
  4616. * chart. These algorithms use this margin to determine how close lines
  4617. * can be to an obstacle. The default is to compute this automatically
  4618. * from the size of the obstacles in the chart.
  4619. *
  4620. * To draw connecting lines close to existing points, set this to a low
  4621. * number. For more space around existing points, set this number
  4622. * higher.
  4623. *
  4624. * @sample gantt/pathfinder/algorithm-margin
  4625. * Small algorithmMargin
  4626. *
  4627. * @type {number}
  4628. * @since 6.2.0
  4629. * @apioption connectors.algorithmMargin
  4630. */
  4631. /**
  4632. * Set the default pathfinder algorithm to use for this chart. It is
  4633. * possible to define your own algorithms by adding them to the
  4634. * Highcharts.Pathfinder.prototype.algorithms object before the chart
  4635. * has been created.
  4636. *
  4637. * The default algorithms are as follows:
  4638. *
  4639. * `straight`: Draws a straight line between the connecting
  4640. * points. Does not avoid other points when drawing.
  4641. *
  4642. * `simpleConnect`: Finds a path between the points using right angles
  4643. * only. Takes only starting/ending points into
  4644. * account, and will not avoid other points.
  4645. *
  4646. * `fastAvoid`: Finds a path between the points using right angles
  4647. * only. Will attempt to avoid other points, but its
  4648. * focus is performance over accuracy. Works well with
  4649. * less dense datasets.
  4650. *
  4651. * Default value: `straight` is used as default for most series types,
  4652. * while `simpleConnect` is used as default for Gantt series, to show
  4653. * dependencies between points.
  4654. *
  4655. * @sample gantt/pathfinder/demo
  4656. * Different types used
  4657. *
  4658. * @type {Highcharts.PathfinderTypeValue}
  4659. * @default undefined
  4660. * @since 6.2.0
  4661. */
  4662. type: 'straight',
  4663. /**
  4664. * Set the default pixel width for this chart's Pathfinder connecting
  4665. * lines.
  4666. *
  4667. * @since 6.2.0
  4668. */
  4669. lineWidth: 1,
  4670. /**
  4671. * Marker options for this chart's Pathfinder connectors. Note that
  4672. * this option is overridden by the `startMarker` and `endMarker`
  4673. * options.
  4674. *
  4675. * @declare Highcharts.ConnectorsMarkerOptions
  4676. * @since 6.2.0
  4677. */
  4678. marker: {
  4679. /**
  4680. * Set the radius of the connector markers. The default is
  4681. * automatically computed based on the algorithmMargin setting.
  4682. *
  4683. * Setting marker.width and marker.height will override this
  4684. * setting.
  4685. *
  4686. * @type {number}
  4687. * @since 6.2.0
  4688. * @apioption connectors.marker.radius
  4689. */
  4690. /**
  4691. * Set the width of the connector markers. If not supplied, this
  4692. * is inferred from the marker radius.
  4693. *
  4694. * @type {number}
  4695. * @since 6.2.0
  4696. * @apioption connectors.marker.width
  4697. */
  4698. /**
  4699. * Set the height of the connector markers. If not supplied, this
  4700. * is inferred from the marker radius.
  4701. *
  4702. * @type {number}
  4703. * @since 6.2.0
  4704. * @apioption connectors.marker.height
  4705. */
  4706. /**
  4707. * Set the color of the connector markers. By default this is the
  4708. * same as the connector color.
  4709. *
  4710. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  4711. * @since 6.2.0
  4712. * @apioption connectors.marker.color
  4713. */
  4714. /**
  4715. * Set the line/border color of the connector markers. By default
  4716. * this is the same as the marker color.
  4717. *
  4718. * @type {Highcharts.ColorString}
  4719. * @since 6.2.0
  4720. * @apioption connectors.marker.lineColor
  4721. */
  4722. /**
  4723. * Enable markers for the connectors.
  4724. */
  4725. enabled: false,
  4726. /**
  4727. * Horizontal alignment of the markers relative to the points.
  4728. *
  4729. * @type {Highcharts.AlignValue}
  4730. */
  4731. align: 'center',
  4732. /**
  4733. * Vertical alignment of the markers relative to the points.
  4734. *
  4735. * @type {Highcharts.VerticalAlignValue}
  4736. */
  4737. verticalAlign: 'middle',
  4738. /**
  4739. * Whether or not to draw the markers inside the points.
  4740. */
  4741. inside: false,
  4742. /**
  4743. * Set the line/border width of the pathfinder markers.
  4744. */
  4745. lineWidth: 1
  4746. },
  4747. /**
  4748. * Marker options specific to the start markers for this chart's
  4749. * Pathfinder connectors. Overrides the generic marker options.
  4750. *
  4751. * @declare Highcharts.ConnectorsStartMarkerOptions
  4752. * @extends connectors.marker
  4753. * @since 6.2.0
  4754. */
  4755. startMarker: {
  4756. /**
  4757. * Set the symbol of the connector start markers.
  4758. */
  4759. symbol: 'diamond'
  4760. },
  4761. /**
  4762. * Marker options specific to the end markers for this chart's
  4763. * Pathfinder connectors. Overrides the generic marker options.
  4764. *
  4765. * @declare Highcharts.ConnectorsEndMarkerOptions
  4766. * @extends connectors.marker
  4767. * @since 6.2.0
  4768. */
  4769. endMarker: {
  4770. /**
  4771. * Set the symbol of the connector end markers.
  4772. */
  4773. symbol: 'arrow-filled'
  4774. }
  4775. }
  4776. });
  4777. /**
  4778. * Override Pathfinder connector options for a series. Requires Highcharts Gantt
  4779. * to be loaded.
  4780. *
  4781. * @declare Highcharts.SeriesConnectorsOptionsObject
  4782. * @extends connectors
  4783. * @since 6.2.0
  4784. * @excluding enabled, algorithmMargin
  4785. * @product gantt
  4786. * @apioption plotOptions.series.connectors
  4787. */
  4788. /**
  4789. * Connect to a point. This option can be either a string, referring to the ID
  4790. * of another point, or an object, or an array of either. If the option is an
  4791. * array, each element defines a connection.
  4792. *
  4793. * @sample gantt/pathfinder/demo
  4794. * Different connection types
  4795. *
  4796. * @declare Highcharts.XrangePointConnectorsOptionsObject
  4797. * @type {string|Array<string|*>|*}
  4798. * @extends plotOptions.series.connectors
  4799. * @since 6.2.0
  4800. * @excluding enabled
  4801. * @product gantt
  4802. * @requires highcharts-gantt
  4803. * @apioption series.xrange.data.connect
  4804. */
  4805. /**
  4806. * The ID of the point to connect to.
  4807. *
  4808. * @type {string}
  4809. * @since 6.2.0
  4810. * @product gantt
  4811. * @apioption series.xrange.data.connect.to
  4812. */
  4813. /**
  4814. * Get point bounding box using plotX/plotY and shapeArgs. If using
  4815. * graphic.getBBox() directly, the bbox will be affected by animation.
  4816. *
  4817. * @private
  4818. * @function
  4819. *
  4820. * @param {Highcharts.Point} point
  4821. * The point to get BB of.
  4822. *
  4823. * @return {Highcharts.Dictionary<number>|null}
  4824. * Result xMax, xMin, yMax, yMin.
  4825. */
  4826. function getPointBB(point) {
  4827. var shapeArgs = point.shapeArgs,
  4828. bb;
  4829. // Prefer using shapeArgs (columns)
  4830. if (shapeArgs) {
  4831. return {
  4832. xMin: shapeArgs.x || 0,
  4833. xMax: (shapeArgs.x || 0) + (shapeArgs.width || 0),
  4834. yMin: shapeArgs.y || 0,
  4835. yMax: (shapeArgs.y || 0) + (shapeArgs.height || 0)
  4836. };
  4837. }
  4838. // Otherwise use plotX/plotY and bb
  4839. bb = point.graphic && point.graphic.getBBox();
  4840. return bb ? {
  4841. xMin: point.plotX - bb.width / 2,
  4842. xMax: point.plotX + bb.width / 2,
  4843. yMin: point.plotY - bb.height / 2,
  4844. yMax: point.plotY + bb.height / 2
  4845. } : null;
  4846. }
  4847. /**
  4848. * Calculate margin to place around obstacles for the pathfinder in pixels.
  4849. * Returns a minimum of 1 pixel margin.
  4850. *
  4851. * @private
  4852. * @function
  4853. *
  4854. * @param {Array<object>} obstacles
  4855. * Obstacles to calculate margin from.
  4856. *
  4857. * @return {number}
  4858. * The calculated margin in pixels. At least 1.
  4859. */
  4860. function calculateObstacleMargin(obstacles) {
  4861. var len = obstacles.length,
  4862. i = 0,
  4863. j,
  4864. obstacleDistance,
  4865. distances = [],
  4866. // Compute smallest distance between two rectangles
  4867. distance = function (a,
  4868. b,
  4869. bbMargin) {
  4870. // Count the distance even if we are slightly off
  4871. var margin = pick(bbMargin, 10),
  4872. yOverlap = a.yMax + margin > b.yMin - margin &&
  4873. a.yMin - margin < b.yMax + margin,
  4874. xOverlap = a.xMax + margin > b.xMin - margin &&
  4875. a.xMin - margin < b.xMax + margin,
  4876. xDistance = yOverlap ? (a.xMin > b.xMax ? a.xMin - b.xMax : b.xMin - a.xMax) : Infinity,
  4877. yDistance = xOverlap ? (a.yMin > b.yMax ? a.yMin - b.yMax : b.yMin - a.yMax) : Infinity;
  4878. // If the rectangles collide, try recomputing with smaller margin.
  4879. // If they collide anyway, discard the obstacle.
  4880. if (xOverlap && yOverlap) {
  4881. return (margin ?
  4882. distance(a, b, Math.floor(margin / 2)) :
  4883. Infinity);
  4884. }
  4885. return min(xDistance, yDistance);
  4886. };
  4887. // Go over all obstacles and compare them to the others.
  4888. for (; i < len; ++i) {
  4889. // Compare to all obstacles ahead. We will already have compared this
  4890. // obstacle to the ones before.
  4891. for (j = i + 1; j < len; ++j) {
  4892. obstacleDistance = distance(obstacles[i], obstacles[j]);
  4893. // TODO: Magic number 80
  4894. if (obstacleDistance < 80) { // Ignore large distances
  4895. distances.push(obstacleDistance);
  4896. }
  4897. }
  4898. }
  4899. // Ensure we always have at least one value, even in very spaceous charts
  4900. distances.push(80);
  4901. return max(Math.floor(distances.sort(function (a, b) {
  4902. return (a - b);
  4903. })[
  4904. // Discard first 10% of the relevant distances, and then grab
  4905. // the smallest one.
  4906. Math.floor(distances.length / 10)] / 2 - 1 // Divide the distance by 2 and subtract 1.
  4907. ), 1 // 1 is the minimum margin
  4908. );
  4909. }
  4910. /* eslint-disable no-invalid-this, valid-jsdoc */
  4911. /**
  4912. * The Connection class. Used internally to represent a connection between two
  4913. * points.
  4914. *
  4915. * @private
  4916. * @class
  4917. * @name Highcharts.Connection
  4918. *
  4919. * @param {Highcharts.Point} from
  4920. * Connection runs from this Point.
  4921. *
  4922. * @param {Highcharts.Point} to
  4923. * Connection runs to this Point.
  4924. *
  4925. * @param {Highcharts.ConnectorsOptions} [options]
  4926. * Connection options.
  4927. */
  4928. var Connection = /** @class */ (function () {
  4929. function Connection(from, to, options) {
  4930. /* *
  4931. *
  4932. * Properties
  4933. *
  4934. * */
  4935. this.chart = void 0;
  4936. this.fromPoint = void 0;
  4937. this.graphics = void 0;
  4938. this.pathfinder = void 0;
  4939. this.toPoint = void 0;
  4940. this.init(from, to, options);
  4941. }
  4942. /**
  4943. * Initialize the Connection object. Used as constructor only.
  4944. *
  4945. * @function Highcharts.Connection#init
  4946. *
  4947. * @param {Highcharts.Point} from
  4948. * Connection runs from this Point.
  4949. *
  4950. * @param {Highcharts.Point} to
  4951. * Connection runs to this Point.
  4952. *
  4953. * @param {Highcharts.ConnectorsOptions} [options]
  4954. * Connection options.
  4955. */
  4956. Connection.prototype.init = function (from, to, options) {
  4957. this.fromPoint = from;
  4958. this.toPoint = to;
  4959. this.options = options;
  4960. this.chart = from.series.chart;
  4961. this.pathfinder = this.chart.pathfinder;
  4962. };
  4963. /**
  4964. * Add (or update) this connection's path on chart. Stores reference to the
  4965. * created element on this.graphics.path.
  4966. *
  4967. * @function Highcharts.Connection#renderPath
  4968. *
  4969. * @param {Highcharts.SVGPathArray} path
  4970. * Path to render, in array format. E.g. ['M', 0, 0, 'L', 10, 10]
  4971. *
  4972. * @param {Highcharts.SVGAttributes} [attribs]
  4973. * SVG attributes for the path.
  4974. *
  4975. * @param {Partial<Highcharts.AnimationOptionsObject>} [animation]
  4976. * Animation options for the rendering.
  4977. */
  4978. Connection.prototype.renderPath = function (path, attribs, animation) {
  4979. var connection = this,
  4980. chart = this.chart,
  4981. styledMode = chart.styledMode,
  4982. pathfinder = chart.pathfinder,
  4983. animate = !chart.options.chart.forExport && animation !== false,
  4984. pathGraphic = connection.graphics && connection.graphics.path,
  4985. anim;
  4986. // Add the SVG element of the pathfinder group if it doesn't exist
  4987. if (!pathfinder.group) {
  4988. pathfinder.group = chart.renderer.g()
  4989. .addClass('highcharts-pathfinder-group')
  4990. .attr({ zIndex: -1 })
  4991. .add(chart.seriesGroup);
  4992. }
  4993. // Shift the group to compensate for plot area.
  4994. // Note: Do this always (even when redrawing a path) to avoid issues
  4995. // when updating chart in a way that changes plot metrics.
  4996. pathfinder.group.translate(chart.plotLeft, chart.plotTop);
  4997. // Create path if does not exist
  4998. if (!(pathGraphic && pathGraphic.renderer)) {
  4999. pathGraphic = chart.renderer.path()
  5000. .add(pathfinder.group);
  5001. if (!styledMode) {
  5002. pathGraphic.attr({
  5003. opacity: 0
  5004. });
  5005. }
  5006. }
  5007. // Set path attribs and animate to the new path
  5008. pathGraphic.attr(attribs);
  5009. anim = { d: path };
  5010. if (!styledMode) {
  5011. anim.opacity = 1;
  5012. }
  5013. pathGraphic[animate ? 'animate' : 'attr'](anim, animation);
  5014. // Store reference on connection
  5015. this.graphics = this.graphics || {};
  5016. this.graphics.path = pathGraphic;
  5017. };
  5018. /**
  5019. * Calculate and add marker graphics for connection to the chart. The
  5020. * created/updated elements are stored on this.graphics.start and
  5021. * this.graphics.end.
  5022. *
  5023. * @function Highcharts.Connection#addMarker
  5024. *
  5025. * @param {string} type
  5026. * Marker type, either 'start' or 'end'.
  5027. *
  5028. * @param {Highcharts.ConnectorsMarkerOptions} options
  5029. * All options for this marker. Not calculated or merged with other
  5030. * options.
  5031. *
  5032. * @param {Highcharts.SVGPathArray} path
  5033. * Connection path in array format. This is used to calculate the
  5034. * rotation angle of the markers.
  5035. */
  5036. Connection.prototype.addMarker = function (type, options, path) {
  5037. var connection = this,
  5038. chart = connection.fromPoint.series.chart,
  5039. pathfinder = chart.pathfinder,
  5040. renderer = chart.renderer,
  5041. point = (type === 'start' ?
  5042. connection.fromPoint :
  5043. connection.toPoint),
  5044. anchor = point.getPathfinderAnchorPoint(options),
  5045. markerVector,
  5046. radians,
  5047. rotation,
  5048. box,
  5049. width,
  5050. height,
  5051. pathVector,
  5052. segment;
  5053. if (!options.enabled) {
  5054. return;
  5055. }
  5056. // Last vector before start/end of path, used to get angle
  5057. if (type === 'start') {
  5058. segment = path[1];
  5059. }
  5060. else { // 'end'
  5061. segment = path[path.length - 2];
  5062. }
  5063. if (segment && segment[0] === 'M' || segment[0] === 'L') {
  5064. pathVector = {
  5065. x: segment[1],
  5066. y: segment[2]
  5067. };
  5068. // Get angle between pathVector and anchor point and use it to
  5069. // create marker position.
  5070. radians = point.getRadiansToVector(pathVector, anchor);
  5071. markerVector = point.getMarkerVector(radians, options.radius, anchor);
  5072. // Rotation of marker is calculated from angle between pathVector
  5073. // and markerVector.
  5074. // (Note:
  5075. // Used to recalculate radians between markerVector and pathVector,
  5076. // but this should be the same as between pathVector and anchor.)
  5077. rotation = -radians / deg2rad;
  5078. if (options.width && options.height) {
  5079. width = options.width;
  5080. height = options.height;
  5081. }
  5082. else {
  5083. width = height = options.radius * 2;
  5084. }
  5085. // Add graphics object if it does not exist
  5086. connection.graphics = connection.graphics || {};
  5087. box = {
  5088. x: markerVector.x - (width / 2),
  5089. y: markerVector.y - (height / 2),
  5090. width: width,
  5091. height: height,
  5092. rotation: rotation,
  5093. rotationOriginX: markerVector.x,
  5094. rotationOriginY: markerVector.y
  5095. };
  5096. if (!connection.graphics[type]) {
  5097. // Create new marker element
  5098. connection.graphics[type] = renderer
  5099. .symbol(options.symbol)
  5100. .addClass('highcharts-point-connecting-path-' + type + '-marker')
  5101. .attr(box)
  5102. .add(pathfinder.group);
  5103. if (!renderer.styledMode) {
  5104. connection.graphics[type].attr({
  5105. fill: options.color || connection.fromPoint.color,
  5106. stroke: options.lineColor,
  5107. 'stroke-width': options.lineWidth,
  5108. opacity: 0
  5109. })
  5110. .animate({
  5111. opacity: 1
  5112. }, point.series.options.animation);
  5113. }
  5114. }
  5115. else {
  5116. connection.graphics[type].animate(box);
  5117. }
  5118. }
  5119. };
  5120. /**
  5121. * Calculate and return connection path.
  5122. * Note: Recalculates chart obstacles on demand if they aren't calculated.
  5123. *
  5124. * @function Highcharts.Connection#getPath
  5125. *
  5126. * @param {Highcharts.ConnectorsOptions} options
  5127. * Connector options. Not calculated or merged with other options.
  5128. *
  5129. * @return {object|undefined}
  5130. * Calculated SVG path data in array format.
  5131. */
  5132. Connection.prototype.getPath = function (options) {
  5133. var pathfinder = this.pathfinder,
  5134. chart = this.chart,
  5135. algorithm = pathfinder.algorithms[options.type],
  5136. chartObstacles = pathfinder.chartObstacles;
  5137. if (typeof algorithm !== 'function') {
  5138. error('"' + options.type + '" is not a Pathfinder algorithm.');
  5139. return {
  5140. path: [],
  5141. obstacles: []
  5142. };
  5143. }
  5144. // This function calculates obstacles on demand if they don't exist
  5145. if (algorithm.requiresObstacles && !chartObstacles) {
  5146. chartObstacles =
  5147. pathfinder.chartObstacles =
  5148. pathfinder.getChartObstacles(options);
  5149. // If the algorithmMargin was computed, store the result in default
  5150. // options.
  5151. chart.options.connectors.algorithmMargin =
  5152. options.algorithmMargin;
  5153. // Cache some metrics too
  5154. pathfinder.chartObstacleMetrics =
  5155. pathfinder.getObstacleMetrics(chartObstacles);
  5156. }
  5157. // Get the SVG path
  5158. return algorithm(
  5159. // From
  5160. this.fromPoint.getPathfinderAnchorPoint(options.startMarker),
  5161. // To
  5162. this.toPoint.getPathfinderAnchorPoint(options.endMarker), merge({
  5163. chartObstacles: chartObstacles,
  5164. lineObstacles: pathfinder.lineObstacles || [],
  5165. obstacleMetrics: pathfinder.chartObstacleMetrics,
  5166. hardBounds: {
  5167. xMin: 0,
  5168. xMax: chart.plotWidth,
  5169. yMin: 0,
  5170. yMax: chart.plotHeight
  5171. },
  5172. obstacleOptions: {
  5173. margin: options.algorithmMargin
  5174. },
  5175. startDirectionX: pathfinder.getAlgorithmStartDirection(options.startMarker)
  5176. }, options));
  5177. };
  5178. /**
  5179. * (re)Calculate and (re)draw the connection.
  5180. *
  5181. * @function Highcharts.Connection#render
  5182. */
  5183. Connection.prototype.render = function () {
  5184. var connection = this,
  5185. fromPoint = connection.fromPoint,
  5186. series = fromPoint.series,
  5187. chart = series.chart,
  5188. pathfinder = chart.pathfinder,
  5189. pathResult,
  5190. path,
  5191. options = merge(chart.options.connectors,
  5192. series.options.connectors,
  5193. fromPoint.options.connectors,
  5194. connection.options),
  5195. attribs = {};
  5196. // Set path attribs
  5197. if (!chart.styledMode) {
  5198. attribs.stroke = options.lineColor || fromPoint.color;
  5199. attribs['stroke-width'] = options.lineWidth;
  5200. if (options.dashStyle) {
  5201. attribs.dashstyle = options.dashStyle;
  5202. }
  5203. }
  5204. attribs['class'] = // eslint-disable-line dot-notation
  5205. 'highcharts-point-connecting-path ' +
  5206. 'highcharts-color-' + fromPoint.colorIndex;
  5207. options = merge(attribs, options);
  5208. // Set common marker options
  5209. if (!defined(options.marker.radius)) {
  5210. options.marker.radius = min(max(Math.ceil((options.algorithmMargin || 8) / 2) - 1, 1), 5);
  5211. }
  5212. // Get the path
  5213. pathResult = connection.getPath(options);
  5214. path = pathResult.path;
  5215. // Always update obstacle storage with obstacles from this path.
  5216. // We don't know if future calls will need this for their algorithm.
  5217. if (pathResult.obstacles) {
  5218. pathfinder.lineObstacles =
  5219. pathfinder.lineObstacles || [];
  5220. pathfinder.lineObstacles =
  5221. pathfinder.lineObstacles.concat(pathResult.obstacles);
  5222. }
  5223. // Add the calculated path to the pathfinder group
  5224. connection.renderPath(path, attribs, series.options.animation);
  5225. // Render the markers
  5226. connection.addMarker('start', merge(options.marker, options.startMarker), path);
  5227. connection.addMarker('end', merge(options.marker, options.endMarker), path);
  5228. };
  5229. /**
  5230. * Destroy connection by destroying the added graphics elements.
  5231. *
  5232. * @function Highcharts.Connection#destroy
  5233. */
  5234. Connection.prototype.destroy = function () {
  5235. if (this.graphics) {
  5236. objectEach(this.graphics, function (val) {
  5237. val.destroy();
  5238. });
  5239. delete this.graphics;
  5240. }
  5241. };
  5242. return Connection;
  5243. }());
  5244. // Add to Highcharts namespace
  5245. H.Connection = Connection;
  5246. // Add pathfinding capabilities to Points
  5247. extend(Point.prototype, /** @lends Point.prototype */ {
  5248. /**
  5249. * Get coordinates of anchor point for pathfinder connection.
  5250. *
  5251. * @private
  5252. * @function Highcharts.Point#getPathfinderAnchorPoint
  5253. *
  5254. * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
  5255. * Connection options for position on point.
  5256. *
  5257. * @return {Highcharts.PositionObject}
  5258. * An object with x/y properties for the position. Coordinates are
  5259. * in plot values, not relative to point.
  5260. */
  5261. getPathfinderAnchorPoint: function (markerOptions) {
  5262. var bb = getPointBB(this),
  5263. x,
  5264. y;
  5265. switch (markerOptions.align) { // eslint-disable-line default-case
  5266. case 'right':
  5267. x = 'xMax';
  5268. break;
  5269. case 'left':
  5270. x = 'xMin';
  5271. }
  5272. switch (markerOptions.verticalAlign) { // eslint-disable-line default-case
  5273. case 'top':
  5274. y = 'yMin';
  5275. break;
  5276. case 'bottom':
  5277. y = 'yMax';
  5278. }
  5279. return {
  5280. x: x ? bb[x] : (bb.xMin + bb.xMax) / 2,
  5281. y: y ? bb[y] : (bb.yMin + bb.yMax) / 2
  5282. };
  5283. },
  5284. /**
  5285. * Utility to get the angle from one point to another.
  5286. *
  5287. * @private
  5288. * @function Highcharts.Point#getRadiansToVector
  5289. *
  5290. * @param {Highcharts.PositionObject} v1
  5291. * The first vector, as an object with x/y properties.
  5292. *
  5293. * @param {Highcharts.PositionObject} v2
  5294. * The second vector, as an object with x/y properties.
  5295. *
  5296. * @return {number}
  5297. * The angle in degrees
  5298. */
  5299. getRadiansToVector: function (v1, v2) {
  5300. var box;
  5301. if (!defined(v2)) {
  5302. box = getPointBB(this);
  5303. if (box) {
  5304. v2 = {
  5305. x: (box.xMin + box.xMax) / 2,
  5306. y: (box.yMin + box.yMax) / 2
  5307. };
  5308. }
  5309. }
  5310. return Math.atan2(v2.y - v1.y, v1.x - v2.x);
  5311. },
  5312. /**
  5313. * Utility to get the position of the marker, based on the path angle and
  5314. * the marker's radius.
  5315. *
  5316. * @private
  5317. * @function Highcharts.Point#getMarkerVector
  5318. *
  5319. * @param {number} radians
  5320. * The angle in radians from the point center to another vector.
  5321. *
  5322. * @param {number} markerRadius
  5323. * The radius of the marker, to calculate the additional distance to
  5324. * the center of the marker.
  5325. *
  5326. * @param {object} anchor
  5327. * The anchor point of the path and marker as an object with x/y
  5328. * properties.
  5329. *
  5330. * @return {object}
  5331. * The marker vector as an object with x/y properties.
  5332. */
  5333. getMarkerVector: function (radians, markerRadius, anchor) {
  5334. var twoPI = Math.PI * 2.0,
  5335. theta = radians,
  5336. bb = getPointBB(this),
  5337. rectWidth = bb.xMax - bb.xMin,
  5338. rectHeight = bb.yMax - bb.yMin,
  5339. rAtan = Math.atan2(rectHeight,
  5340. rectWidth),
  5341. tanTheta = 1,
  5342. leftOrRightRegion = false,
  5343. rectHalfWidth = rectWidth / 2.0,
  5344. rectHalfHeight = rectHeight / 2.0,
  5345. rectHorizontalCenter = bb.xMin + rectHalfWidth,
  5346. rectVerticalCenter = bb.yMin + rectHalfHeight,
  5347. edgePoint = {
  5348. x: rectHorizontalCenter,
  5349. y: rectVerticalCenter
  5350. },
  5351. xFactor = 1,
  5352. yFactor = 1;
  5353. while (theta < -Math.PI) {
  5354. theta += twoPI;
  5355. }
  5356. while (theta > Math.PI) {
  5357. theta -= twoPI;
  5358. }
  5359. tanTheta = Math.tan(theta);
  5360. if ((theta > -rAtan) && (theta <= rAtan)) {
  5361. // Right side
  5362. yFactor = -1;
  5363. leftOrRightRegion = true;
  5364. }
  5365. else if (theta > rAtan && theta <= (Math.PI - rAtan)) {
  5366. // Top side
  5367. yFactor = -1;
  5368. }
  5369. else if (theta > (Math.PI - rAtan) || theta <= -(Math.PI - rAtan)) {
  5370. // Left side
  5371. xFactor = -1;
  5372. leftOrRightRegion = true;
  5373. }
  5374. else {
  5375. // Bottom side
  5376. xFactor = -1;
  5377. }
  5378. // Correct the edgePoint according to the placement of the marker
  5379. if (leftOrRightRegion) {
  5380. edgePoint.x += xFactor * (rectHalfWidth);
  5381. edgePoint.y += yFactor * (rectHalfWidth) * tanTheta;
  5382. }
  5383. else {
  5384. edgePoint.x += xFactor * (rectHeight / (2.0 * tanTheta));
  5385. edgePoint.y += yFactor * (rectHalfHeight);
  5386. }
  5387. if (anchor.x !== rectHorizontalCenter) {
  5388. edgePoint.x = anchor.x;
  5389. }
  5390. if (anchor.y !== rectVerticalCenter) {
  5391. edgePoint.y = anchor.y;
  5392. }
  5393. return {
  5394. x: edgePoint.x + (markerRadius * Math.cos(theta)),
  5395. y: edgePoint.y - (markerRadius * Math.sin(theta))
  5396. };
  5397. }
  5398. });
  5399. /**
  5400. * Warn if using legacy options. Copy the options over. Note that this will
  5401. * still break if using the legacy options in chart.update, addSeries etc.
  5402. * @private
  5403. */
  5404. function warnLegacy(chart) {
  5405. if (chart.options.pathfinder ||
  5406. chart.series.reduce(function (acc, series) {
  5407. if (series.options) {
  5408. merge(true, (series.options.connectors = series.options.connectors ||
  5409. {}), series.options.pathfinder);
  5410. }
  5411. return acc || series.options && series.options.pathfinder;
  5412. }, false)) {
  5413. merge(true, (chart.options.connectors = chart.options.connectors || {}), chart.options.pathfinder);
  5414. error('WARNING: Pathfinder options have been renamed. ' +
  5415. 'Use "chart.connectors" or "series.connectors" instead.');
  5416. }
  5417. }
  5418. return Connection;
  5419. });
  5420. _registerModule(_modules, 'Gantt/PathfinderAlgorithms.js', [_modules['Core/Utilities.js']], function (U) {
  5421. /* *
  5422. *
  5423. * (c) 2016 Highsoft AS
  5424. * Author: Øystein Moseng
  5425. *
  5426. * License: www.highcharts.com/license
  5427. *
  5428. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5429. *
  5430. * */
  5431. var extend = U.extend,
  5432. pick = U.pick;
  5433. var min = Math.min,
  5434. max = Math.max,
  5435. abs = Math.abs;
  5436. /**
  5437. * Get index of last obstacle before xMin. Employs a type of binary search, and
  5438. * thus requires that obstacles are sorted by xMin value.
  5439. *
  5440. * @private
  5441. * @function findLastObstacleBefore
  5442. *
  5443. * @param {Array<object>} obstacles
  5444. * Array of obstacles to search in.
  5445. *
  5446. * @param {number} xMin
  5447. * The xMin threshold.
  5448. *
  5449. * @param {number} [startIx]
  5450. * Starting index to search from. Must be within array range.
  5451. *
  5452. * @return {number}
  5453. * The index of the last obstacle element before xMin.
  5454. */
  5455. function findLastObstacleBefore(obstacles, xMin, startIx) {
  5456. var left = startIx || 0, // left limit
  5457. right = obstacles.length - 1, // right limit
  5458. min = xMin - 0.0000001, // Make sure we include all obstacles at xMin
  5459. cursor,
  5460. cmp;
  5461. while (left <= right) {
  5462. cursor = (right + left) >> 1;
  5463. cmp = min - obstacles[cursor].xMin;
  5464. if (cmp > 0) {
  5465. left = cursor + 1;
  5466. }
  5467. else if (cmp < 0) {
  5468. right = cursor - 1;
  5469. }
  5470. else {
  5471. return cursor;
  5472. }
  5473. }
  5474. return left > 0 ? left - 1 : 0;
  5475. }
  5476. /**
  5477. * Test if a point lays within an obstacle.
  5478. *
  5479. * @private
  5480. * @function pointWithinObstacle
  5481. *
  5482. * @param {object} obstacle
  5483. * Obstacle to test.
  5484. *
  5485. * @param {Highcharts.Point} point
  5486. * Point with x/y props.
  5487. *
  5488. * @return {boolean}
  5489. * Whether point is within the obstacle or not.
  5490. */
  5491. function pointWithinObstacle(obstacle, point) {
  5492. return (point.x <= obstacle.xMax &&
  5493. point.x >= obstacle.xMin &&
  5494. point.y <= obstacle.yMax &&
  5495. point.y >= obstacle.yMin);
  5496. }
  5497. /**
  5498. * Find the index of an obstacle that wraps around a point.
  5499. * Returns -1 if not found.
  5500. *
  5501. * @private
  5502. * @function findObstacleFromPoint
  5503. *
  5504. * @param {Array<object>} obstacles
  5505. * Obstacles to test.
  5506. *
  5507. * @param {Highcharts.Point} point
  5508. * Point with x/y props.
  5509. *
  5510. * @return {number}
  5511. * Ix of the obstacle in the array, or -1 if not found.
  5512. */
  5513. function findObstacleFromPoint(obstacles, point) {
  5514. var i = findLastObstacleBefore(obstacles,
  5515. point.x + 1) + 1;
  5516. while (i--) {
  5517. if (obstacles[i].xMax >= point.x &&
  5518. // optimization using lazy evaluation
  5519. pointWithinObstacle(obstacles[i], point)) {
  5520. return i;
  5521. }
  5522. }
  5523. return -1;
  5524. }
  5525. /**
  5526. * Get SVG path array from array of line segments.
  5527. *
  5528. * @private
  5529. * @function pathFromSegments
  5530. *
  5531. * @param {Array<object>} segments
  5532. * The segments to build the path from.
  5533. *
  5534. * @return {Highcharts.SVGPathArray}
  5535. * SVG path array as accepted by the SVG Renderer.
  5536. */
  5537. function pathFromSegments(segments) {
  5538. var path = [];
  5539. if (segments.length) {
  5540. path.push(['M', segments[0].start.x, segments[0].start.y]);
  5541. for (var i = 0; i < segments.length; ++i) {
  5542. path.push(['L', segments[i].end.x, segments[i].end.y]);
  5543. }
  5544. }
  5545. return path;
  5546. }
  5547. /**
  5548. * Limits obstacle max/mins in all directions to bounds. Modifies input
  5549. * obstacle.
  5550. *
  5551. * @private
  5552. * @function limitObstacleToBounds
  5553. *
  5554. * @param {object} obstacle
  5555. * Obstacle to limit.
  5556. *
  5557. * @param {object} bounds
  5558. * Bounds to use as limit.
  5559. *
  5560. * @return {void}
  5561. */
  5562. function limitObstacleToBounds(obstacle, bounds) {
  5563. obstacle.yMin = max(obstacle.yMin, bounds.yMin);
  5564. obstacle.yMax = min(obstacle.yMax, bounds.yMax);
  5565. obstacle.xMin = max(obstacle.xMin, bounds.xMin);
  5566. obstacle.xMax = min(obstacle.xMax, bounds.xMax);
  5567. }
  5568. /**
  5569. * Get an SVG path from a starting coordinate to an ending coordinate.
  5570. * Draws a straight line.
  5571. *
  5572. * @function Highcharts.Pathfinder.algorithms.straight
  5573. *
  5574. * @param {Highcharts.PositionObject} start
  5575. * Starting coordinate, object with x/y props.
  5576. *
  5577. * @param {Highcharts.PositionObject} end
  5578. * Ending coordinate, object with x/y props.
  5579. *
  5580. * @return {object}
  5581. * An object with the SVG path in Array form as accepted by the SVG
  5582. * renderer, as well as an array of new obstacles making up this
  5583. * path.
  5584. */
  5585. function straight(start, end) {
  5586. return {
  5587. path: [
  5588. ['M', start.x, start.y],
  5589. ['L', end.x, end.y]
  5590. ],
  5591. obstacles: [{ start: start, end: end }]
  5592. };
  5593. }
  5594. /**
  5595. * Find a path from a starting coordinate to an ending coordinate, using
  5596. * right angles only, and taking only starting/ending obstacle into
  5597. * consideration.
  5598. *
  5599. * @function Highcharts.Pathfinder.algorithms.simpleConnect
  5600. *
  5601. * @param {Highcharts.PositionObject} start
  5602. * Starting coordinate, object with x/y props.
  5603. *
  5604. * @param {Highcharts.PositionObject} end
  5605. * Ending coordinate, object with x/y props.
  5606. *
  5607. * @param {object} options
  5608. * Options for the algorithm:
  5609. * - chartObstacles: Array of chart obstacles to avoid
  5610. * - startDirectionX: Optional. True if starting in the X direction.
  5611. * If not provided, the algorithm starts in the direction that is
  5612. * the furthest between start/end.
  5613. *
  5614. * @return {object}
  5615. * An object with the SVG path in Array form as accepted by the SVG
  5616. * renderer, as well as an array of new obstacles making up this
  5617. * path.
  5618. */
  5619. var simpleConnect = function (start,
  5620. end,
  5621. options) {
  5622. var segments = [],
  5623. endSegment,
  5624. dir = pick(options.startDirectionX,
  5625. abs(end.x - start.x) > abs(end.y - start.y)) ? 'x' : 'y',
  5626. chartObstacles = options.chartObstacles,
  5627. startObstacleIx = findObstacleFromPoint(chartObstacles,
  5628. start),
  5629. endObstacleIx = findObstacleFromPoint(chartObstacles,
  5630. end),
  5631. startObstacle,
  5632. endObstacle,
  5633. prevWaypoint,
  5634. waypoint,
  5635. waypoint2,
  5636. useMax,
  5637. endPoint;
  5638. // eslint-disable-next-line valid-jsdoc
  5639. /**
  5640. * Return a clone of a point with a property set from a target object,
  5641. * optionally with an offset
  5642. * @private
  5643. */
  5644. function copyFromPoint(from, fromKey, to, toKey, offset) {
  5645. var point = {
  5646. x: from.x,
  5647. y: from.y
  5648. };
  5649. point[fromKey] = to[toKey || fromKey] + (offset || 0);
  5650. return point;
  5651. }
  5652. // eslint-disable-next-line valid-jsdoc
  5653. /**
  5654. * Return waypoint outside obstacle.
  5655. * @private
  5656. */
  5657. function getMeOut(obstacle, point, direction) {
  5658. var useMax = abs(point[direction] - obstacle[direction + 'Min']) >
  5659. abs(point[direction] - obstacle[direction + 'Max']);
  5660. return copyFromPoint(point, direction, obstacle, direction + (useMax ? 'Max' : 'Min'), useMax ? 1 : -1);
  5661. }
  5662. // Pull out end point
  5663. if (endObstacleIx > -1) {
  5664. endObstacle = chartObstacles[endObstacleIx];
  5665. waypoint = getMeOut(endObstacle, end, dir);
  5666. endSegment = {
  5667. start: waypoint,
  5668. end: end
  5669. };
  5670. endPoint = waypoint;
  5671. }
  5672. else {
  5673. endPoint = end;
  5674. }
  5675. // If an obstacle envelops the start point, add a segment to get out,
  5676. // and around it.
  5677. if (startObstacleIx > -1) {
  5678. startObstacle = chartObstacles[startObstacleIx];
  5679. waypoint = getMeOut(startObstacle, start, dir);
  5680. segments.push({
  5681. start: start,
  5682. end: waypoint
  5683. });
  5684. // If we are going back again, switch direction to get around start
  5685. // obstacle.
  5686. if (
  5687. // Going towards max from start:
  5688. waypoint[dir] >= start[dir] ===
  5689. // Going towards min to end:
  5690. waypoint[dir] >= endPoint[dir]) {
  5691. dir = dir === 'y' ? 'x' : 'y';
  5692. useMax = start[dir] < end[dir];
  5693. segments.push({
  5694. start: waypoint,
  5695. end: copyFromPoint(waypoint, dir, startObstacle, dir + (useMax ? 'Max' : 'Min'), useMax ? 1 : -1)
  5696. });
  5697. // Switch direction again
  5698. dir = dir === 'y' ? 'x' : 'y';
  5699. }
  5700. }
  5701. // We are around the start obstacle. Go towards the end in one
  5702. // direction.
  5703. prevWaypoint = segments.length ?
  5704. segments[segments.length - 1].end :
  5705. start;
  5706. waypoint = copyFromPoint(prevWaypoint, dir, endPoint);
  5707. segments.push({
  5708. start: prevWaypoint,
  5709. end: waypoint
  5710. });
  5711. // Final run to end point in the other direction
  5712. dir = dir === 'y' ? 'x' : 'y';
  5713. waypoint2 = copyFromPoint(waypoint, dir, endPoint);
  5714. segments.push({
  5715. start: waypoint,
  5716. end: waypoint2
  5717. });
  5718. // Finally add the endSegment
  5719. segments.push(endSegment);
  5720. return {
  5721. path: pathFromSegments(segments),
  5722. obstacles: segments
  5723. };
  5724. };
  5725. simpleConnect.requiresObstacles = true;
  5726. /**
  5727. * Find a path from a starting coordinate to an ending coordinate, taking
  5728. * obstacles into consideration. Might not always find the optimal path,
  5729. * but is fast, and usually good enough.
  5730. *
  5731. * @function Highcharts.Pathfinder.algorithms.fastAvoid
  5732. *
  5733. * @param {Highcharts.PositionObject} start
  5734. * Starting coordinate, object with x/y props.
  5735. *
  5736. * @param {Highcharts.PositionObject} end
  5737. * Ending coordinate, object with x/y props.
  5738. *
  5739. * @param {object} options
  5740. * Options for the algorithm.
  5741. * - chartObstacles: Array of chart obstacles to avoid
  5742. * - lineObstacles: Array of line obstacles to jump over
  5743. * - obstacleMetrics: Object with metrics of chartObstacles cached
  5744. * - hardBounds: Hard boundaries to not cross
  5745. * - obstacleOptions: Options for the obstacles, including margin
  5746. * - startDirectionX: Optional. True if starting in the X direction.
  5747. * If not provided, the algorithm starts in the
  5748. * direction that is the furthest between
  5749. * start/end.
  5750. *
  5751. * @return {object}
  5752. * An object with the SVG path in Array form as accepted by the SVG
  5753. * renderer, as well as an array of new obstacles making up this
  5754. * path.
  5755. */
  5756. var fastAvoid = function (start,
  5757. end,
  5758. options) {
  5759. /*
  5760. Algorithm rules/description
  5761. - Find initial direction
  5762. - Determine soft/hard max for each direction.
  5763. - Move along initial direction until obstacle.
  5764. - Change direction.
  5765. - If hitting obstacle,
  5766. first try to change length of previous line
  5767. before changing direction again.
  5768. Soft min/max x = start/destination x +/- widest obstacle + margin
  5769. Soft min/max y = start/destination y +/- tallest obstacle + margin
  5770. @todo:
  5771. - Make retrospective,
  5772. try changing prev segment to reduce
  5773. corners
  5774. - Fix logic for breaking out of end-points - not always picking
  5775. the best direction currently
  5776. - When going around the end obstacle we should not always go the
  5777. shortest route,
  5778. rather pick the one closer to the end point
  5779. */
  5780. var dirIsX = pick(options.startDirectionX,
  5781. abs(end.x - start.x) > abs(end.y - start.y)),
  5782. dir = dirIsX ? 'x' : 'y',
  5783. segments,
  5784. useMax,
  5785. extractedEndPoint,
  5786. endSegments = [],
  5787. forceObstacleBreak = false, // Used in clearPathTo to keep track of
  5788. // when to force break through an obstacle.
  5789. // Boundaries to stay within. If beyond soft boundary, prefer to
  5790. // change direction ASAP. If at hard max, always change immediately.
  5791. metrics = options.obstacleMetrics,
  5792. softMinX = min(start.x,
  5793. end.x) - metrics.maxWidth - 10,
  5794. softMaxX = max(start.x,
  5795. end.x) + metrics.maxWidth + 10,
  5796. softMinY = min(start.y,
  5797. end.y) - metrics.maxHeight - 10,
  5798. softMaxY = max(start.y,
  5799. end.y) + metrics.maxHeight + 10,
  5800. // Obstacles
  5801. chartObstacles = options.chartObstacles,
  5802. startObstacleIx = findLastObstacleBefore(chartObstacles,
  5803. softMinX),
  5804. endObstacleIx = findLastObstacleBefore(chartObstacles,
  5805. softMaxX);
  5806. // eslint-disable-next-line valid-jsdoc
  5807. /**
  5808. * How far can you go between two points before hitting an obstacle?
  5809. * Does not work for diagonal lines (because it doesn't have to).
  5810. * @private
  5811. */
  5812. function pivotPoint(fromPoint, toPoint, directionIsX) {
  5813. var firstPoint,
  5814. lastPoint,
  5815. highestPoint,
  5816. lowestPoint,
  5817. i,
  5818. searchDirection = fromPoint.x < toPoint.x ? 1 : -1;
  5819. if (fromPoint.x < toPoint.x) {
  5820. firstPoint = fromPoint;
  5821. lastPoint = toPoint;
  5822. }
  5823. else {
  5824. firstPoint = toPoint;
  5825. lastPoint = fromPoint;
  5826. }
  5827. if (fromPoint.y < toPoint.y) {
  5828. lowestPoint = fromPoint;
  5829. highestPoint = toPoint;
  5830. }
  5831. else {
  5832. lowestPoint = toPoint;
  5833. highestPoint = fromPoint;
  5834. }
  5835. // Go through obstacle range in reverse if toPoint is before
  5836. // fromPoint in the X-dimension.
  5837. i = searchDirection < 0 ?
  5838. // Searching backwards, start at last obstacle before last point
  5839. min(findLastObstacleBefore(chartObstacles, lastPoint.x), chartObstacles.length - 1) :
  5840. // Forwards. Since we're not sorted by xMax, we have to look
  5841. // at all obstacles.
  5842. 0;
  5843. // Go through obstacles in this X range
  5844. while (chartObstacles[i] && (searchDirection > 0 && chartObstacles[i].xMin <= lastPoint.x ||
  5845. searchDirection < 0 && chartObstacles[i].xMax >= firstPoint.x)) {
  5846. // If this obstacle is between from and to points in a straight
  5847. // line, pivot at the intersection.
  5848. if (chartObstacles[i].xMin <= lastPoint.x &&
  5849. chartObstacles[i].xMax >= firstPoint.x &&
  5850. chartObstacles[i].yMin <= highestPoint.y &&
  5851. chartObstacles[i].yMax >= lowestPoint.y) {
  5852. if (directionIsX) {
  5853. return {
  5854. y: fromPoint.y,
  5855. x: fromPoint.x < toPoint.x ?
  5856. chartObstacles[i].xMin - 1 :
  5857. chartObstacles[i].xMax + 1,
  5858. obstacle: chartObstacles[i]
  5859. };
  5860. }
  5861. // else ...
  5862. return {
  5863. x: fromPoint.x,
  5864. y: fromPoint.y < toPoint.y ?
  5865. chartObstacles[i].yMin - 1 :
  5866. chartObstacles[i].yMax + 1,
  5867. obstacle: chartObstacles[i]
  5868. };
  5869. }
  5870. i += searchDirection;
  5871. }
  5872. return toPoint;
  5873. }
  5874. /**
  5875. * Decide in which direction to dodge or get out of an obstacle.
  5876. * Considers desired direction, which way is shortest, soft and hard
  5877. * bounds.
  5878. *
  5879. * (? Returns a string, either xMin, xMax, yMin or yMax.)
  5880. *
  5881. * @private
  5882. * @function
  5883. *
  5884. * @param {object} obstacle
  5885. * Obstacle to dodge/escape.
  5886. *
  5887. * @param {object} fromPoint
  5888. * Point with x/y props that's dodging/escaping.
  5889. *
  5890. * @param {object} toPoint
  5891. * Goal point.
  5892. *
  5893. * @param {boolean} dirIsX
  5894. * Dodge in X dimension.
  5895. *
  5896. * @param {object} bounds
  5897. * Hard and soft boundaries.
  5898. *
  5899. * @return {boolean}
  5900. * Use max or not.
  5901. */
  5902. function getDodgeDirection(obstacle, fromPoint, toPoint, dirIsX, bounds) {
  5903. var softBounds = bounds.soft, hardBounds = bounds.hard, dir = dirIsX ? 'x' : 'y', toPointMax = { x: fromPoint.x, y: fromPoint.y }, toPointMin = { x: fromPoint.x, y: fromPoint.y }, minPivot, maxPivot, maxOutOfSoftBounds = obstacle[dir + 'Max'] >=
  5904. softBounds[dir + 'Max'], minOutOfSoftBounds = obstacle[dir + 'Min'] <=
  5905. softBounds[dir + 'Min'], maxOutOfHardBounds = obstacle[dir + 'Max'] >=
  5906. hardBounds[dir + 'Max'], minOutOfHardBounds = obstacle[dir + 'Min'] <=
  5907. hardBounds[dir + 'Min'],
  5908. // Find out if we should prefer one direction over the other if
  5909. // we can choose freely
  5910. minDistance = abs(obstacle[dir + 'Min'] - fromPoint[dir]), maxDistance = abs(obstacle[dir + 'Max'] - fromPoint[dir]),
  5911. // If it's a small difference, pick the one leading towards dest
  5912. // point. Otherwise pick the shortest distance
  5913. useMax = abs(minDistance - maxDistance) < 10 ?
  5914. fromPoint[dir] < toPoint[dir] :
  5915. maxDistance < minDistance;
  5916. // Check if we hit any obstacles trying to go around in either
  5917. // direction.
  5918. toPointMin[dir] = obstacle[dir + 'Min'];
  5919. toPointMax[dir] = obstacle[dir + 'Max'];
  5920. minPivot = pivotPoint(fromPoint, toPointMin, dirIsX)[dir] !==
  5921. toPointMin[dir];
  5922. maxPivot = pivotPoint(fromPoint, toPointMax, dirIsX)[dir] !==
  5923. toPointMax[dir];
  5924. useMax = minPivot ?
  5925. (maxPivot ? useMax : true) :
  5926. (maxPivot ? false : useMax);
  5927. // useMax now contains our preferred choice, bounds not taken into
  5928. // account. If both or neither direction is out of bounds we want to
  5929. // use this.
  5930. // Deal with soft bounds
  5931. useMax = minOutOfSoftBounds ?
  5932. (maxOutOfSoftBounds ? useMax : true) : // Out on min
  5933. (maxOutOfSoftBounds ? false : useMax); // Not out on min
  5934. // Deal with hard bounds
  5935. useMax = minOutOfHardBounds ?
  5936. (maxOutOfHardBounds ? useMax : true) : // Out on min
  5937. (maxOutOfHardBounds ? false : useMax); // Not out on min
  5938. return useMax;
  5939. }
  5940. // eslint-disable-next-line valid-jsdoc
  5941. /**
  5942. * Find a clear path between point.
  5943. * @private
  5944. */
  5945. function clearPathTo(fromPoint, toPoint, dirIsX) {
  5946. // Don't waste time if we've hit goal
  5947. if (fromPoint.x === toPoint.x && fromPoint.y === toPoint.y) {
  5948. return [];
  5949. }
  5950. var dir = dirIsX ? 'x' : 'y',
  5951. pivot,
  5952. segments,
  5953. waypoint,
  5954. waypointUseMax,
  5955. envelopingObstacle,
  5956. secondEnvelopingObstacle,
  5957. envelopWaypoint,
  5958. obstacleMargin = options.obstacleOptions.margin,
  5959. bounds = {
  5960. soft: {
  5961. xMin: softMinX,
  5962. xMax: softMaxX,
  5963. yMin: softMinY,
  5964. yMax: softMaxY
  5965. },
  5966. hard: options.hardBounds
  5967. };
  5968. // If fromPoint is inside an obstacle we have a problem. Break out
  5969. // by just going to the outside of this obstacle. We prefer to go to
  5970. // the nearest edge in the chosen direction.
  5971. envelopingObstacle =
  5972. findObstacleFromPoint(chartObstacles, fromPoint);
  5973. if (envelopingObstacle > -1) {
  5974. envelopingObstacle = chartObstacles[envelopingObstacle];
  5975. waypointUseMax = getDodgeDirection(envelopingObstacle, fromPoint, toPoint, dirIsX, bounds);
  5976. // Cut obstacle to hard bounds to make sure we stay within
  5977. limitObstacleToBounds(envelopingObstacle, options.hardBounds);
  5978. envelopWaypoint = dirIsX ? {
  5979. y: fromPoint.y,
  5980. x: envelopingObstacle[waypointUseMax ? 'xMax' : 'xMin'] +
  5981. (waypointUseMax ? 1 : -1)
  5982. } : {
  5983. x: fromPoint.x,
  5984. y: envelopingObstacle[waypointUseMax ? 'yMax' : 'yMin'] +
  5985. (waypointUseMax ? 1 : -1)
  5986. };
  5987. // If we crashed into another obstacle doing this, we put the
  5988. // waypoint between them instead
  5989. secondEnvelopingObstacle = findObstacleFromPoint(chartObstacles, envelopWaypoint);
  5990. if (secondEnvelopingObstacle > -1) {
  5991. secondEnvelopingObstacle = chartObstacles[secondEnvelopingObstacle];
  5992. // Cut obstacle to hard bounds
  5993. limitObstacleToBounds(secondEnvelopingObstacle, options.hardBounds);
  5994. // Modify waypoint to lay between obstacles
  5995. envelopWaypoint[dir] = waypointUseMax ? max(envelopingObstacle[dir + 'Max'] - obstacleMargin + 1, (secondEnvelopingObstacle[dir + 'Min'] +
  5996. envelopingObstacle[dir + 'Max']) / 2) :
  5997. min((envelopingObstacle[dir + 'Min'] + obstacleMargin - 1), ((secondEnvelopingObstacle[dir + 'Max'] +
  5998. envelopingObstacle[dir + 'Min']) / 2));
  5999. // We are not going anywhere. If this happens for the first
  6000. // time, do nothing. Otherwise, try to go to the extreme of
  6001. // the obstacle pair in the current direction.
  6002. if (fromPoint.x === envelopWaypoint.x &&
  6003. fromPoint.y === envelopWaypoint.y) {
  6004. if (forceObstacleBreak) {
  6005. envelopWaypoint[dir] = waypointUseMax ?
  6006. max(envelopingObstacle[dir + 'Max'], secondEnvelopingObstacle[dir + 'Max']) + 1 :
  6007. min(envelopingObstacle[dir + 'Min'], secondEnvelopingObstacle[dir + 'Min']) - 1;
  6008. }
  6009. // Toggle on if off, and the opposite
  6010. forceObstacleBreak = !forceObstacleBreak;
  6011. }
  6012. else {
  6013. // This point is not identical to previous.
  6014. // Clear break trigger.
  6015. forceObstacleBreak = false;
  6016. }
  6017. }
  6018. segments = [{
  6019. start: fromPoint,
  6020. end: envelopWaypoint
  6021. }];
  6022. }
  6023. else { // If not enveloping, use standard pivot calculation
  6024. pivot = pivotPoint(fromPoint, {
  6025. x: dirIsX ? toPoint.x : fromPoint.x,
  6026. y: dirIsX ? fromPoint.y : toPoint.y
  6027. }, dirIsX);
  6028. segments = [{
  6029. start: fromPoint,
  6030. end: {
  6031. x: pivot.x,
  6032. y: pivot.y
  6033. }
  6034. }];
  6035. // Pivot before goal, use a waypoint to dodge obstacle
  6036. if (pivot[dirIsX ? 'x' : 'y'] !== toPoint[dirIsX ? 'x' : 'y']) {
  6037. // Find direction of waypoint
  6038. waypointUseMax = getDodgeDirection(pivot.obstacle, pivot, toPoint, !dirIsX, bounds);
  6039. // Cut waypoint to hard bounds
  6040. limitObstacleToBounds(pivot.obstacle, options.hardBounds);
  6041. waypoint = {
  6042. x: dirIsX ?
  6043. pivot.x :
  6044. pivot.obstacle[waypointUseMax ? 'xMax' : 'xMin'] +
  6045. (waypointUseMax ? 1 : -1),
  6046. y: dirIsX ?
  6047. pivot.obstacle[waypointUseMax ? 'yMax' : 'yMin'] +
  6048. (waypointUseMax ? 1 : -1) :
  6049. pivot.y
  6050. };
  6051. // We're changing direction here, store that to make sure we
  6052. // also change direction when adding the last segment array
  6053. // after handling waypoint.
  6054. dirIsX = !dirIsX;
  6055. segments = segments.concat(clearPathTo({
  6056. x: pivot.x,
  6057. y: pivot.y
  6058. }, waypoint, dirIsX));
  6059. }
  6060. }
  6061. // Get segments for the other direction too
  6062. // Recursion is our friend
  6063. segments = segments.concat(clearPathTo(segments[segments.length - 1].end, toPoint, !dirIsX));
  6064. return segments;
  6065. }
  6066. // eslint-disable-next-line valid-jsdoc
  6067. /**
  6068. * Extract point to outside of obstacle in whichever direction is
  6069. * closest. Returns new point outside obstacle.
  6070. * @private
  6071. */
  6072. function extractFromObstacle(obstacle, point, goalPoint) {
  6073. var dirIsX = min(obstacle.xMax - point.x,
  6074. point.x - obstacle.xMin) <
  6075. min(obstacle.yMax - point.y,
  6076. point.y - obstacle.yMin),
  6077. bounds = {
  6078. soft: options.hardBounds,
  6079. hard: options.hardBounds
  6080. },
  6081. useMax = getDodgeDirection(obstacle,
  6082. point,
  6083. goalPoint,
  6084. dirIsX,
  6085. bounds);
  6086. return dirIsX ? {
  6087. y: point.y,
  6088. x: obstacle[useMax ? 'xMax' : 'xMin'] + (useMax ? 1 : -1)
  6089. } : {
  6090. x: point.x,
  6091. y: obstacle[useMax ? 'yMax' : 'yMin'] + (useMax ? 1 : -1)
  6092. };
  6093. }
  6094. // Cut the obstacle array to soft bounds for optimization in large
  6095. // datasets.
  6096. chartObstacles =
  6097. chartObstacles.slice(startObstacleIx, endObstacleIx + 1);
  6098. // If an obstacle envelops the end point, move it out of there and add
  6099. // a little segment to where it was.
  6100. if ((endObstacleIx = findObstacleFromPoint(chartObstacles, end)) > -1) {
  6101. extractedEndPoint = extractFromObstacle(chartObstacles[endObstacleIx], end, start);
  6102. endSegments.push({
  6103. end: end,
  6104. start: extractedEndPoint
  6105. });
  6106. end = extractedEndPoint;
  6107. }
  6108. // If it's still inside one or more obstacles, get out of there by
  6109. // force-moving towards the start point.
  6110. while ((endObstacleIx = findObstacleFromPoint(chartObstacles, end)) > -1) {
  6111. useMax = end[dir] - start[dir] < 0;
  6112. extractedEndPoint = {
  6113. x: end.x,
  6114. y: end.y
  6115. };
  6116. extractedEndPoint[dir] = chartObstacles[endObstacleIx][useMax ? dir + 'Max' : dir + 'Min'] + (useMax ? 1 : -1);
  6117. endSegments.push({
  6118. end: end,
  6119. start: extractedEndPoint
  6120. });
  6121. end = extractedEndPoint;
  6122. }
  6123. // Find the path
  6124. segments = clearPathTo(start, end, dirIsX);
  6125. // Add the end-point segments
  6126. segments = segments.concat(endSegments.reverse());
  6127. return {
  6128. path: pathFromSegments(segments),
  6129. obstacles: segments
  6130. };
  6131. };
  6132. fastAvoid.requiresObstacles = true;
  6133. // Define the available pathfinding algorithms.
  6134. // Algorithms take up to 3 arguments: starting point, ending point, and an
  6135. // options object.
  6136. var algorithms = {
  6137. fastAvoid: fastAvoid,
  6138. straight: straight,
  6139. simpleConnect: simpleConnect
  6140. };
  6141. return algorithms;
  6142. });
  6143. _registerModule(_modules, 'Gantt/Pathfinder.js', [_modules['Gantt/Connection.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js'], _modules['Gantt/PathfinderAlgorithms.js']], function (Connection, Chart, H, O, Point, U, pathfinderAlgorithms) {
  6144. /* *
  6145. *
  6146. * (c) 2016 Highsoft AS
  6147. * Authors: Øystein Moseng, Lars A. V. Cabrera
  6148. *
  6149. * License: www.highcharts.com/license
  6150. *
  6151. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6152. *
  6153. * */
  6154. /**
  6155. * The default pathfinder algorithm to use for a chart. It is possible to define
  6156. * your own algorithms by adding them to the
  6157. * `Highcharts.Pathfinder.prototype.algorithms`
  6158. * object before the chart has been created.
  6159. *
  6160. * The default algorithms are as follows:
  6161. *
  6162. * `straight`: Draws a straight line between the connecting
  6163. * points. Does not avoid other points when drawing.
  6164. *
  6165. * `simpleConnect`: Finds a path between the points using right angles
  6166. * only. Takes only starting/ending points into
  6167. * account, and will not avoid other points.
  6168. *
  6169. * `fastAvoid`: Finds a path between the points using right angles
  6170. * only. Will attempt to avoid other points, but its
  6171. * focus is performance over accuracy. Works well with
  6172. * less dense datasets.
  6173. *
  6174. * @typedef {"fastAvoid"|"simpleConnect"|"straight"|string} Highcharts.PathfinderTypeValue
  6175. */
  6176. ''; // detach doclets above
  6177. var defaultOptions = O.defaultOptions;
  6178. var addEvent = U.addEvent,
  6179. defined = U.defined,
  6180. error = U.error,
  6181. extend = U.extend,
  6182. merge = U.merge,
  6183. objectEach = U.objectEach,
  6184. pick = U.pick,
  6185. splat = U.splat;
  6186. var deg2rad = H.deg2rad,
  6187. max = Math.max,
  6188. min = Math.min;
  6189. /*
  6190. @todo:
  6191. - Document how to write your own algorithms
  6192. - Consider adding a Point.pathTo method that wraps creating a connection
  6193. and rendering it
  6194. */
  6195. // Set default Pathfinder options
  6196. extend(defaultOptions, {
  6197. /**
  6198. * The Pathfinder module allows you to define connections between any two
  6199. * points, represented as lines - optionally with markers for the start
  6200. * and/or end points. Multiple algorithms are available for calculating how
  6201. * the connecting lines are drawn.
  6202. *
  6203. * Connector functionality requires Highcharts Gantt to be loaded. In Gantt
  6204. * charts, the connectors are used to draw dependencies between tasks.
  6205. *
  6206. * @see [dependency](series.gantt.data.dependency)
  6207. *
  6208. * @sample gantt/pathfinder/demo
  6209. * Pathfinder connections
  6210. *
  6211. * @declare Highcharts.ConnectorsOptions
  6212. * @product gantt
  6213. * @optionparent connectors
  6214. */
  6215. connectors: {
  6216. /**
  6217. * Enable connectors for this chart. Requires Highcharts Gantt.
  6218. *
  6219. * @type {boolean}
  6220. * @default true
  6221. * @since 6.2.0
  6222. * @apioption connectors.enabled
  6223. */
  6224. /**
  6225. * Set the default dash style for this chart's connecting lines.
  6226. *
  6227. * @type {string}
  6228. * @default solid
  6229. * @since 6.2.0
  6230. * @apioption connectors.dashStyle
  6231. */
  6232. /**
  6233. * Set the default color for this chart's Pathfinder connecting lines.
  6234. * Defaults to the color of the point being connected.
  6235. *
  6236. * @type {Highcharts.ColorString}
  6237. * @since 6.2.0
  6238. * @apioption connectors.lineColor
  6239. */
  6240. /**
  6241. * Set the default pathfinder margin to use, in pixels. Some Pathfinder
  6242. * algorithms attempt to avoid obstacles, such as other points in the
  6243. * chart. These algorithms use this margin to determine how close lines
  6244. * can be to an obstacle. The default is to compute this automatically
  6245. * from the size of the obstacles in the chart.
  6246. *
  6247. * To draw connecting lines close to existing points, set this to a low
  6248. * number. For more space around existing points, set this number
  6249. * higher.
  6250. *
  6251. * @sample gantt/pathfinder/algorithm-margin
  6252. * Small algorithmMargin
  6253. *
  6254. * @type {number}
  6255. * @since 6.2.0
  6256. * @apioption connectors.algorithmMargin
  6257. */
  6258. /**
  6259. * Set the default pathfinder algorithm to use for this chart. It is
  6260. * possible to define your own algorithms by adding them to the
  6261. * Highcharts.Pathfinder.prototype.algorithms object before the chart
  6262. * has been created.
  6263. *
  6264. * The default algorithms are as follows:
  6265. *
  6266. * `straight`: Draws a straight line between the connecting
  6267. * points. Does not avoid other points when drawing.
  6268. *
  6269. * `simpleConnect`: Finds a path between the points using right angles
  6270. * only. Takes only starting/ending points into
  6271. * account, and will not avoid other points.
  6272. *
  6273. * `fastAvoid`: Finds a path between the points using right angles
  6274. * only. Will attempt to avoid other points, but its
  6275. * focus is performance over accuracy. Works well with
  6276. * less dense datasets.
  6277. *
  6278. * Default value: `straight` is used as default for most series types,
  6279. * while `simpleConnect` is used as default for Gantt series, to show
  6280. * dependencies between points.
  6281. *
  6282. * @sample gantt/pathfinder/demo
  6283. * Different types used
  6284. *
  6285. * @type {Highcharts.PathfinderTypeValue}
  6286. * @default undefined
  6287. * @since 6.2.0
  6288. */
  6289. type: 'straight',
  6290. /**
  6291. * Set the default pixel width for this chart's Pathfinder connecting
  6292. * lines.
  6293. *
  6294. * @since 6.2.0
  6295. */
  6296. lineWidth: 1,
  6297. /**
  6298. * Marker options for this chart's Pathfinder connectors. Note that
  6299. * this option is overridden by the `startMarker` and `endMarker`
  6300. * options.
  6301. *
  6302. * @declare Highcharts.ConnectorsMarkerOptions
  6303. * @since 6.2.0
  6304. */
  6305. marker: {
  6306. /**
  6307. * Set the radius of the connector markers. The default is
  6308. * automatically computed based on the algorithmMargin setting.
  6309. *
  6310. * Setting marker.width and marker.height will override this
  6311. * setting.
  6312. *
  6313. * @type {number}
  6314. * @since 6.2.0
  6315. * @apioption connectors.marker.radius
  6316. */
  6317. /**
  6318. * Set the width of the connector markers. If not supplied, this
  6319. * is inferred from the marker radius.
  6320. *
  6321. * @type {number}
  6322. * @since 6.2.0
  6323. * @apioption connectors.marker.width
  6324. */
  6325. /**
  6326. * Set the height of the connector markers. If not supplied, this
  6327. * is inferred from the marker radius.
  6328. *
  6329. * @type {number}
  6330. * @since 6.2.0
  6331. * @apioption connectors.marker.height
  6332. */
  6333. /**
  6334. * Set the color of the connector markers. By default this is the
  6335. * same as the connector color.
  6336. *
  6337. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6338. * @since 6.2.0
  6339. * @apioption connectors.marker.color
  6340. */
  6341. /**
  6342. * Set the line/border color of the connector markers. By default
  6343. * this is the same as the marker color.
  6344. *
  6345. * @type {Highcharts.ColorString}
  6346. * @since 6.2.0
  6347. * @apioption connectors.marker.lineColor
  6348. */
  6349. /**
  6350. * Enable markers for the connectors.
  6351. */
  6352. enabled: false,
  6353. /**
  6354. * Horizontal alignment of the markers relative to the points.
  6355. *
  6356. * @type {Highcharts.AlignValue}
  6357. */
  6358. align: 'center',
  6359. /**
  6360. * Vertical alignment of the markers relative to the points.
  6361. *
  6362. * @type {Highcharts.VerticalAlignValue}
  6363. */
  6364. verticalAlign: 'middle',
  6365. /**
  6366. * Whether or not to draw the markers inside the points.
  6367. */
  6368. inside: false,
  6369. /**
  6370. * Set the line/border width of the pathfinder markers.
  6371. */
  6372. lineWidth: 1
  6373. },
  6374. /**
  6375. * Marker options specific to the start markers for this chart's
  6376. * Pathfinder connectors. Overrides the generic marker options.
  6377. *
  6378. * @declare Highcharts.ConnectorsStartMarkerOptions
  6379. * @extends connectors.marker
  6380. * @since 6.2.0
  6381. */
  6382. startMarker: {
  6383. /**
  6384. * Set the symbol of the connector start markers.
  6385. */
  6386. symbol: 'diamond'
  6387. },
  6388. /**
  6389. * Marker options specific to the end markers for this chart's
  6390. * Pathfinder connectors. Overrides the generic marker options.
  6391. *
  6392. * @declare Highcharts.ConnectorsEndMarkerOptions
  6393. * @extends connectors.marker
  6394. * @since 6.2.0
  6395. */
  6396. endMarker: {
  6397. /**
  6398. * Set the symbol of the connector end markers.
  6399. */
  6400. symbol: 'arrow-filled'
  6401. }
  6402. }
  6403. });
  6404. /**
  6405. * Override Pathfinder connector options for a series. Requires Highcharts Gantt
  6406. * to be loaded.
  6407. *
  6408. * @declare Highcharts.SeriesConnectorsOptionsObject
  6409. * @extends connectors
  6410. * @since 6.2.0
  6411. * @excluding enabled, algorithmMargin
  6412. * @product gantt
  6413. * @apioption plotOptions.series.connectors
  6414. */
  6415. /**
  6416. * Connect to a point. This option can be either a string, referring to the ID
  6417. * of another point, or an object, or an array of either. If the option is an
  6418. * array, each element defines a connection.
  6419. *
  6420. * @sample gantt/pathfinder/demo
  6421. * Different connection types
  6422. *
  6423. * @declare Highcharts.XrangePointConnectorsOptionsObject
  6424. * @type {string|Array<string|*>|*}
  6425. * @extends plotOptions.series.connectors
  6426. * @since 6.2.0
  6427. * @excluding enabled
  6428. * @product gantt
  6429. * @requires highcharts-gantt
  6430. * @apioption series.xrange.data.connect
  6431. */
  6432. /**
  6433. * The ID of the point to connect to.
  6434. *
  6435. * @type {string}
  6436. * @since 6.2.0
  6437. * @product gantt
  6438. * @apioption series.xrange.data.connect.to
  6439. */
  6440. /**
  6441. * Get point bounding box using plotX/plotY and shapeArgs. If using
  6442. * graphic.getBBox() directly, the bbox will be affected by animation.
  6443. *
  6444. * @private
  6445. * @function
  6446. *
  6447. * @param {Highcharts.Point} point
  6448. * The point to get BB of.
  6449. *
  6450. * @return {Highcharts.Dictionary<number>|null}
  6451. * Result xMax, xMin, yMax, yMin.
  6452. */
  6453. function getPointBB(point) {
  6454. var shapeArgs = point.shapeArgs,
  6455. bb;
  6456. // Prefer using shapeArgs (columns)
  6457. if (shapeArgs) {
  6458. return {
  6459. xMin: shapeArgs.x || 0,
  6460. xMax: (shapeArgs.x || 0) + (shapeArgs.width || 0),
  6461. yMin: shapeArgs.y || 0,
  6462. yMax: (shapeArgs.y || 0) + (shapeArgs.height || 0)
  6463. };
  6464. }
  6465. // Otherwise use plotX/plotY and bb
  6466. bb = point.graphic && point.graphic.getBBox();
  6467. return bb ? {
  6468. xMin: point.plotX - bb.width / 2,
  6469. xMax: point.plotX + bb.width / 2,
  6470. yMin: point.plotY - bb.height / 2,
  6471. yMax: point.plotY + bb.height / 2
  6472. } : null;
  6473. }
  6474. /**
  6475. * Calculate margin to place around obstacles for the pathfinder in pixels.
  6476. * Returns a minimum of 1 pixel margin.
  6477. *
  6478. * @private
  6479. * @function
  6480. *
  6481. * @param {Array<object>} obstacles
  6482. * Obstacles to calculate margin from.
  6483. *
  6484. * @return {number}
  6485. * The calculated margin in pixels. At least 1.
  6486. */
  6487. function calculateObstacleMargin(obstacles) {
  6488. var len = obstacles.length,
  6489. i = 0,
  6490. j,
  6491. obstacleDistance,
  6492. distances = [],
  6493. // Compute smallest distance between two rectangles
  6494. distance = function (a,
  6495. b,
  6496. bbMargin) {
  6497. // Count the distance even if we are slightly off
  6498. var margin = pick(bbMargin, 10),
  6499. yOverlap = a.yMax + margin > b.yMin - margin &&
  6500. a.yMin - margin < b.yMax + margin,
  6501. xOverlap = a.xMax + margin > b.xMin - margin &&
  6502. a.xMin - margin < b.xMax + margin,
  6503. xDistance = yOverlap ? (a.xMin > b.xMax ? a.xMin - b.xMax : b.xMin - a.xMax) : Infinity,
  6504. yDistance = xOverlap ? (a.yMin > b.yMax ? a.yMin - b.yMax : b.yMin - a.yMax) : Infinity;
  6505. // If the rectangles collide, try recomputing with smaller margin.
  6506. // If they collide anyway, discard the obstacle.
  6507. if (xOverlap && yOverlap) {
  6508. return (margin ?
  6509. distance(a, b, Math.floor(margin / 2)) :
  6510. Infinity);
  6511. }
  6512. return min(xDistance, yDistance);
  6513. };
  6514. // Go over all obstacles and compare them to the others.
  6515. for (; i < len; ++i) {
  6516. // Compare to all obstacles ahead. We will already have compared this
  6517. // obstacle to the ones before.
  6518. for (j = i + 1; j < len; ++j) {
  6519. obstacleDistance = distance(obstacles[i], obstacles[j]);
  6520. // TODO: Magic number 80
  6521. if (obstacleDistance < 80) { // Ignore large distances
  6522. distances.push(obstacleDistance);
  6523. }
  6524. }
  6525. }
  6526. // Ensure we always have at least one value, even in very spaceous charts
  6527. distances.push(80);
  6528. return max(Math.floor(distances.sort(function (a, b) {
  6529. return (a - b);
  6530. })[
  6531. // Discard first 10% of the relevant distances, and then grab
  6532. // the smallest one.
  6533. Math.floor(distances.length / 10)] / 2 - 1 // Divide the distance by 2 and subtract 1.
  6534. ), 1 // 1 is the minimum margin
  6535. );
  6536. }
  6537. /* eslint-disable no-invalid-this, valid-jsdoc */
  6538. /**
  6539. * The Pathfinder class.
  6540. *
  6541. * @private
  6542. * @class
  6543. * @name Highcharts.Pathfinder
  6544. *
  6545. * @param {Highcharts.Chart} chart
  6546. * The chart to operate on.
  6547. */
  6548. var Pathfinder = /** @class */ (function () {
  6549. function Pathfinder(chart) {
  6550. /* *
  6551. *
  6552. * Properties
  6553. *
  6554. * */
  6555. this.chart = void 0;
  6556. this.chartObstacles = void 0;
  6557. this.chartObstacleMetrics = void 0;
  6558. this.connections = void 0;
  6559. this.group = void 0;
  6560. this.lineObstacles = void 0;
  6561. this.init(chart);
  6562. }
  6563. /**
  6564. * @name Highcharts.Pathfinder#algorithms
  6565. * @type {Highcharts.Dictionary<Function>}
  6566. */
  6567. /**
  6568. * Initialize the Pathfinder object.
  6569. *
  6570. * @function Highcharts.Pathfinder#init
  6571. *
  6572. * @param {Highcharts.Chart} chart
  6573. * The chart context.
  6574. */
  6575. Pathfinder.prototype.init = function (chart) {
  6576. // Initialize pathfinder with chart context
  6577. this.chart = chart;
  6578. // Init connection reference list
  6579. this.connections = [];
  6580. // Recalculate paths/obstacles on chart redraw
  6581. addEvent(chart, 'redraw', function () {
  6582. this.pathfinder.update();
  6583. });
  6584. };
  6585. /**
  6586. * Update Pathfinder connections from scratch.
  6587. *
  6588. * @function Highcharts.Pathfinder#update
  6589. *
  6590. * @param {boolean} [deferRender]
  6591. * Whether or not to defer rendering of connections until
  6592. * series.afterAnimate event has fired. Used on first render.
  6593. */
  6594. Pathfinder.prototype.update = function (deferRender) {
  6595. var chart = this.chart,
  6596. pathfinder = this,
  6597. oldConnections = pathfinder.connections;
  6598. // Rebuild pathfinder connections from options
  6599. pathfinder.connections = [];
  6600. chart.series.forEach(function (series) {
  6601. if (series.visible && !series.options.isInternal) {
  6602. series.points.forEach(function (point) {
  6603. var ganttPointOptions = point.options;
  6604. // For Gantt series the connect could be
  6605. // defined as a dependency
  6606. if (ganttPointOptions && ganttPointOptions.dependency) {
  6607. ganttPointOptions.connect = ganttPointOptions.dependency;
  6608. }
  6609. var to,
  6610. connects = (point.options &&
  6611. point.options.connect &&
  6612. splat(point.options.connect));
  6613. if (point.visible && point.isInside !== false && connects) {
  6614. connects.forEach(function (connect) {
  6615. to = chart.get(typeof connect === 'string' ?
  6616. connect : connect.to);
  6617. if (to instanceof Point &&
  6618. to.series.visible &&
  6619. to.visible &&
  6620. to.isInside !== false) {
  6621. // Add new connection
  6622. pathfinder.connections.push(new Connection(point, // from
  6623. to, typeof connect === 'string' ?
  6624. {} :
  6625. connect));
  6626. }
  6627. });
  6628. }
  6629. });
  6630. }
  6631. });
  6632. // Clear connections that should not be updated, and move old info over
  6633. // to new connections.
  6634. for (var j = 0, k = void 0, found = void 0, lenOld = oldConnections.length, lenNew = pathfinder.connections.length; j < lenOld; ++j) {
  6635. found = false;
  6636. for (k = 0; k < lenNew; ++k) {
  6637. if (oldConnections[j].fromPoint ===
  6638. pathfinder.connections[k].fromPoint &&
  6639. oldConnections[j].toPoint ===
  6640. pathfinder.connections[k].toPoint) {
  6641. pathfinder.connections[k].graphics =
  6642. oldConnections[j].graphics;
  6643. found = true;
  6644. break;
  6645. }
  6646. }
  6647. if (!found) {
  6648. oldConnections[j].destroy();
  6649. }
  6650. }
  6651. // Clear obstacles to force recalculation. This must be done on every
  6652. // redraw in case positions have changed. Recalculation is handled in
  6653. // Connection.getPath on demand.
  6654. delete this.chartObstacles;
  6655. delete this.lineObstacles;
  6656. // Draw the pending connections
  6657. pathfinder.renderConnections(deferRender);
  6658. };
  6659. /**
  6660. * Draw the chart's connecting paths.
  6661. *
  6662. * @function Highcharts.Pathfinder#renderConnections
  6663. *
  6664. * @param {boolean} [deferRender]
  6665. * Whether or not to defer render until series animation is finished.
  6666. * Used on first render.
  6667. */
  6668. Pathfinder.prototype.renderConnections = function (deferRender) {
  6669. if (deferRender) {
  6670. // Render after series are done animating
  6671. this.chart.series.forEach(function (series) {
  6672. var render = function () {
  6673. // Find pathfinder connections belonging to this series
  6674. // that haven't rendered, and render them now.
  6675. var pathfinder = series.chart.pathfinder,
  6676. conns = pathfinder && pathfinder.connections || [];
  6677. conns.forEach(function (connection) {
  6678. if (connection.fromPoint &&
  6679. connection.fromPoint.series === series) {
  6680. connection.render();
  6681. }
  6682. });
  6683. if (series.pathfinderRemoveRenderEvent) {
  6684. series.pathfinderRemoveRenderEvent();
  6685. delete series.pathfinderRemoveRenderEvent;
  6686. }
  6687. };
  6688. if (series.options.animation === false) {
  6689. render();
  6690. }
  6691. else {
  6692. series.pathfinderRemoveRenderEvent = addEvent(series, 'afterAnimate', render);
  6693. }
  6694. });
  6695. }
  6696. else {
  6697. // Go through connections and render them
  6698. this.connections.forEach(function (connection) {
  6699. connection.render();
  6700. });
  6701. }
  6702. };
  6703. /**
  6704. * Get obstacles for the points in the chart. Does not include connecting
  6705. * lines from Pathfinder. Applies algorithmMargin to the obstacles.
  6706. *
  6707. * @function Highcharts.Pathfinder#getChartObstacles
  6708. *
  6709. * @param {object} options
  6710. * Options for the calculation. Currenlty only
  6711. * options.algorithmMargin.
  6712. *
  6713. * @return {Array<object>}
  6714. * An array of calculated obstacles. Each obstacle is defined as an
  6715. * object with xMin, xMax, yMin and yMax properties.
  6716. */
  6717. Pathfinder.prototype.getChartObstacles = function (options) {
  6718. var obstacles = [],
  6719. series = this.chart.series,
  6720. margin = pick(options.algorithmMargin, 0),
  6721. calculatedMargin;
  6722. for (var i = 0, sLen = series.length; i < sLen; ++i) {
  6723. if (series[i].visible && !series[i].options.isInternal) {
  6724. for (var j = 0, pLen = series[i].points.length, bb = void 0, point = void 0; j < pLen; ++j) {
  6725. point = series[i].points[j];
  6726. if (point.visible) {
  6727. bb = getPointBB(point);
  6728. if (bb) {
  6729. obstacles.push({
  6730. xMin: bb.xMin - margin,
  6731. xMax: bb.xMax + margin,
  6732. yMin: bb.yMin - margin,
  6733. yMax: bb.yMax + margin
  6734. });
  6735. }
  6736. }
  6737. }
  6738. }
  6739. }
  6740. // Sort obstacles by xMin for optimization
  6741. obstacles = obstacles.sort(function (a, b) {
  6742. return a.xMin - b.xMin;
  6743. });
  6744. // Add auto-calculated margin if the option is not defined
  6745. if (!defined(options.algorithmMargin)) {
  6746. calculatedMargin =
  6747. options.algorithmMargin =
  6748. calculateObstacleMargin(obstacles);
  6749. obstacles.forEach(function (obstacle) {
  6750. obstacle.xMin -= calculatedMargin;
  6751. obstacle.xMax += calculatedMargin;
  6752. obstacle.yMin -= calculatedMargin;
  6753. obstacle.yMax += calculatedMargin;
  6754. });
  6755. }
  6756. return obstacles;
  6757. };
  6758. /**
  6759. * Utility function to get metrics for obstacles:
  6760. * - Widest obstacle width
  6761. * - Tallest obstacle height
  6762. *
  6763. * @function Highcharts.Pathfinder#getObstacleMetrics
  6764. *
  6765. * @param {Array<object>} obstacles
  6766. * An array of obstacles to inspect.
  6767. *
  6768. * @return {object}
  6769. * The calculated metrics, as an object with maxHeight and maxWidth
  6770. * properties.
  6771. */
  6772. Pathfinder.prototype.getObstacleMetrics = function (obstacles) {
  6773. var maxWidth = 0,
  6774. maxHeight = 0,
  6775. width,
  6776. height,
  6777. i = obstacles.length;
  6778. while (i--) {
  6779. width = obstacles[i].xMax - obstacles[i].xMin;
  6780. height = obstacles[i].yMax - obstacles[i].yMin;
  6781. if (maxWidth < width) {
  6782. maxWidth = width;
  6783. }
  6784. if (maxHeight < height) {
  6785. maxHeight = height;
  6786. }
  6787. }
  6788. return {
  6789. maxHeight: maxHeight,
  6790. maxWidth: maxWidth
  6791. };
  6792. };
  6793. /**
  6794. * Utility to get which direction to start the pathfinding algorithm
  6795. * (X vs Y), calculated from a set of marker options.
  6796. *
  6797. * @function Highcharts.Pathfinder#getAlgorithmStartDirection
  6798. *
  6799. * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
  6800. * Marker options to calculate from.
  6801. *
  6802. * @return {boolean}
  6803. * Returns true for X, false for Y, and undefined for autocalculate.
  6804. */
  6805. Pathfinder.prototype.getAlgorithmStartDirection = function (markerOptions) {
  6806. var xCenter = markerOptions.align !== 'left' &&
  6807. markerOptions.align !== 'right', yCenter = markerOptions.verticalAlign !== 'top' &&
  6808. markerOptions.verticalAlign !== 'bottom', undef;
  6809. return xCenter ?
  6810. (yCenter ? undef : false) : // x is centered
  6811. (yCenter ? true : undef); // x is off-center
  6812. };
  6813. return Pathfinder;
  6814. }());
  6815. Pathfinder.prototype.algorithms = pathfinderAlgorithms;
  6816. // Add to Highcharts namespace
  6817. H.Pathfinder = Pathfinder;
  6818. // Add pathfinding capabilities to Points
  6819. extend(Point.prototype, /** @lends Point.prototype */ {
  6820. /**
  6821. * Get coordinates of anchor point for pathfinder connection.
  6822. *
  6823. * @private
  6824. * @function Highcharts.Point#getPathfinderAnchorPoint
  6825. *
  6826. * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
  6827. * Connection options for position on point.
  6828. *
  6829. * @return {Highcharts.PositionObject}
  6830. * An object with x/y properties for the position. Coordinates are
  6831. * in plot values, not relative to point.
  6832. */
  6833. getPathfinderAnchorPoint: function (markerOptions) {
  6834. var bb = getPointBB(this),
  6835. x,
  6836. y;
  6837. switch (markerOptions.align) { // eslint-disable-line default-case
  6838. case 'right':
  6839. x = 'xMax';
  6840. break;
  6841. case 'left':
  6842. x = 'xMin';
  6843. }
  6844. switch (markerOptions.verticalAlign) { // eslint-disable-line default-case
  6845. case 'top':
  6846. y = 'yMin';
  6847. break;
  6848. case 'bottom':
  6849. y = 'yMax';
  6850. }
  6851. return {
  6852. x: x ? bb[x] : (bb.xMin + bb.xMax) / 2,
  6853. y: y ? bb[y] : (bb.yMin + bb.yMax) / 2
  6854. };
  6855. },
  6856. /**
  6857. * Utility to get the angle from one point to another.
  6858. *
  6859. * @private
  6860. * @function Highcharts.Point#getRadiansToVector
  6861. *
  6862. * @param {Highcharts.PositionObject} v1
  6863. * The first vector, as an object with x/y properties.
  6864. *
  6865. * @param {Highcharts.PositionObject} v2
  6866. * The second vector, as an object with x/y properties.
  6867. *
  6868. * @return {number}
  6869. * The angle in degrees
  6870. */
  6871. getRadiansToVector: function (v1, v2) {
  6872. var box;
  6873. if (!defined(v2)) {
  6874. box = getPointBB(this);
  6875. if (box) {
  6876. v2 = {
  6877. x: (box.xMin + box.xMax) / 2,
  6878. y: (box.yMin + box.yMax) / 2
  6879. };
  6880. }
  6881. }
  6882. return Math.atan2(v2.y - v1.y, v1.x - v2.x);
  6883. },
  6884. /**
  6885. * Utility to get the position of the marker, based on the path angle and
  6886. * the marker's radius.
  6887. *
  6888. * @private
  6889. * @function Highcharts.Point#getMarkerVector
  6890. *
  6891. * @param {number} radians
  6892. * The angle in radians from the point center to another vector.
  6893. *
  6894. * @param {number} markerRadius
  6895. * The radius of the marker, to calculate the additional distance to
  6896. * the center of the marker.
  6897. *
  6898. * @param {object} anchor
  6899. * The anchor point of the path and marker as an object with x/y
  6900. * properties.
  6901. *
  6902. * @return {object}
  6903. * The marker vector as an object with x/y properties.
  6904. */
  6905. getMarkerVector: function (radians, markerRadius, anchor) {
  6906. var twoPI = Math.PI * 2.0,
  6907. theta = radians,
  6908. bb = getPointBB(this),
  6909. rectWidth = bb.xMax - bb.xMin,
  6910. rectHeight = bb.yMax - bb.yMin,
  6911. rAtan = Math.atan2(rectHeight,
  6912. rectWidth),
  6913. tanTheta = 1,
  6914. leftOrRightRegion = false,
  6915. rectHalfWidth = rectWidth / 2.0,
  6916. rectHalfHeight = rectHeight / 2.0,
  6917. rectHorizontalCenter = bb.xMin + rectHalfWidth,
  6918. rectVerticalCenter = bb.yMin + rectHalfHeight,
  6919. edgePoint = {
  6920. x: rectHorizontalCenter,
  6921. y: rectVerticalCenter
  6922. },
  6923. xFactor = 1,
  6924. yFactor = 1;
  6925. while (theta < -Math.PI) {
  6926. theta += twoPI;
  6927. }
  6928. while (theta > Math.PI) {
  6929. theta -= twoPI;
  6930. }
  6931. tanTheta = Math.tan(theta);
  6932. if ((theta > -rAtan) && (theta <= rAtan)) {
  6933. // Right side
  6934. yFactor = -1;
  6935. leftOrRightRegion = true;
  6936. }
  6937. else if (theta > rAtan && theta <= (Math.PI - rAtan)) {
  6938. // Top side
  6939. yFactor = -1;
  6940. }
  6941. else if (theta > (Math.PI - rAtan) || theta <= -(Math.PI - rAtan)) {
  6942. // Left side
  6943. xFactor = -1;
  6944. leftOrRightRegion = true;
  6945. }
  6946. else {
  6947. // Bottom side
  6948. xFactor = -1;
  6949. }
  6950. // Correct the edgePoint according to the placement of the marker
  6951. if (leftOrRightRegion) {
  6952. edgePoint.x += xFactor * (rectHalfWidth);
  6953. edgePoint.y += yFactor * (rectHalfWidth) * tanTheta;
  6954. }
  6955. else {
  6956. edgePoint.x += xFactor * (rectHeight / (2.0 * tanTheta));
  6957. edgePoint.y += yFactor * (rectHalfHeight);
  6958. }
  6959. if (anchor.x !== rectHorizontalCenter) {
  6960. edgePoint.x = anchor.x;
  6961. }
  6962. if (anchor.y !== rectVerticalCenter) {
  6963. edgePoint.y = anchor.y;
  6964. }
  6965. return {
  6966. x: edgePoint.x + (markerRadius * Math.cos(theta)),
  6967. y: edgePoint.y - (markerRadius * Math.sin(theta))
  6968. };
  6969. }
  6970. });
  6971. /**
  6972. * Warn if using legacy options. Copy the options over. Note that this will
  6973. * still break if using the legacy options in chart.update, addSeries etc.
  6974. * @private
  6975. */
  6976. function warnLegacy(chart) {
  6977. if (chart.options.pathfinder ||
  6978. chart.series.reduce(function (acc, series) {
  6979. if (series.options) {
  6980. merge(true, (series.options.connectors = series.options.connectors ||
  6981. {}), series.options.pathfinder);
  6982. }
  6983. return acc || series.options && series.options.pathfinder;
  6984. }, false)) {
  6985. merge(true, (chart.options.connectors = chart.options.connectors || {}), chart.options.pathfinder);
  6986. error('WARNING: Pathfinder options have been renamed. ' +
  6987. 'Use "chart.connectors" or "series.connectors" instead.');
  6988. }
  6989. }
  6990. // Initialize Pathfinder for charts
  6991. Chart.prototype.callbacks.push(function (chart) {
  6992. var options = chart.options;
  6993. if (options.connectors.enabled !== false) {
  6994. warnLegacy(chart);
  6995. this.pathfinder = new Pathfinder(this);
  6996. this.pathfinder.update(true); // First draw, defer render
  6997. }
  6998. });
  6999. return Pathfinder;
  7000. });
  7001. _registerModule(_modules, 'Series/Gantt/GanttSeries.js', [_modules['Series/Gantt/GanttPoint.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (GanttPoint, SeriesRegistry, U) {
  7002. /* *
  7003. *
  7004. * (c) 2016-2021 Highsoft AS
  7005. *
  7006. * Author: Lars A. V. Cabrera
  7007. *
  7008. * License: www.highcharts.com/license
  7009. *
  7010. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7011. *
  7012. * */
  7013. var __extends = (this && this.__extends) || (function () {
  7014. var extendStatics = function (d,
  7015. b) {
  7016. extendStatics = Object.setPrototypeOf ||
  7017. ({ __proto__: [] } instanceof Array && function (d,
  7018. b) { d.__proto__ = b; }) ||
  7019. function (d,
  7020. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  7021. return extendStatics(d, b);
  7022. };
  7023. return function (d, b) {
  7024. extendStatics(d, b);
  7025. function __() { this.constructor = d; }
  7026. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  7027. };
  7028. })();
  7029. var Series = SeriesRegistry.series,
  7030. XRangeSeries = SeriesRegistry.seriesTypes.xrange;
  7031. var extend = U.extend,
  7032. isNumber = U.isNumber,
  7033. merge = U.merge,
  7034. splat = U.splat;
  7035. /* *
  7036. *
  7037. * Class
  7038. *
  7039. * */
  7040. /**
  7041. * @private
  7042. * @class
  7043. * @name Highcharts.seriesTypes.gantt
  7044. *
  7045. * @augments Highcharts.Series
  7046. */
  7047. var GanttSeries = /** @class */ (function (_super) {
  7048. __extends(GanttSeries, _super);
  7049. function GanttSeries() {
  7050. var _this = _super !== null && _super.apply(this,
  7051. arguments) || this;
  7052. /* *
  7053. *
  7054. * Properties
  7055. *
  7056. * */
  7057. _this.data = void 0;
  7058. _this.options = void 0;
  7059. _this.points = void 0;
  7060. return _this;
  7061. /* eslint-enable valid-jsdoc */
  7062. }
  7063. /* *
  7064. *
  7065. * Functions
  7066. *
  7067. * */
  7068. /* eslint-disable valid-jsdoc */
  7069. /**
  7070. * Draws a single point in the series.
  7071. *
  7072. * This override draws the point as a diamond if point.options.milestone
  7073. * is true, and uses the original drawPoint() if it is false or not set.
  7074. *
  7075. * @requires highcharts-gantt
  7076. *
  7077. * @private
  7078. * @function Highcharts.seriesTypes.gantt#drawPoint
  7079. *
  7080. * @param {Highcharts.Point} point
  7081. * An instance of Point in the series
  7082. *
  7083. * @param {"animate"|"attr"} verb
  7084. * 'animate' (animates changes) or 'attr' (sets options)
  7085. */
  7086. GanttSeries.prototype.drawPoint = function (point, verb) {
  7087. var series = this,
  7088. seriesOpts = series.options,
  7089. renderer = series.chart.renderer,
  7090. shapeArgs = point.shapeArgs,
  7091. plotY = point.plotY,
  7092. graphic = point.graphic,
  7093. state = point.selected && 'select',
  7094. cutOff = seriesOpts.stacking && !seriesOpts.borderRadius,
  7095. diamondShape;
  7096. if (point.options.milestone) {
  7097. if (isNumber(plotY) && point.y !== null && point.visible !== false) {
  7098. diamondShape = renderer.symbols.diamond(shapeArgs.x || 0, shapeArgs.y || 0, shapeArgs.width || 0, shapeArgs.height || 0);
  7099. if (graphic) {
  7100. graphic[verb]({
  7101. d: diamondShape
  7102. });
  7103. }
  7104. else {
  7105. point.graphic = graphic = renderer.path(diamondShape)
  7106. .addClass(point.getClassName(), true)
  7107. .add(point.group || series.group);
  7108. }
  7109. // Presentational
  7110. if (!series.chart.styledMode) {
  7111. point.graphic
  7112. .attr(series.pointAttribs(point, state))
  7113. .shadow(seriesOpts.shadow, null, cutOff);
  7114. }
  7115. }
  7116. else if (graphic) {
  7117. point.graphic = graphic.destroy(); // #1269
  7118. }
  7119. }
  7120. else {
  7121. XRangeSeries.prototype.drawPoint.call(series, point, verb);
  7122. }
  7123. };
  7124. /**
  7125. * Handle milestones, as they have no x2.
  7126. * @private
  7127. */
  7128. GanttSeries.prototype.translatePoint = function (point) {
  7129. var series = this,
  7130. shapeArgs,
  7131. size;
  7132. XRangeSeries.prototype.translatePoint.call(series, point);
  7133. if (point.options.milestone) {
  7134. shapeArgs = point.shapeArgs;
  7135. size = shapeArgs.height || 0;
  7136. point.shapeArgs = {
  7137. x: (shapeArgs.x || 0) - (size / 2),
  7138. y: shapeArgs.y,
  7139. width: size,
  7140. height: size
  7141. };
  7142. }
  7143. };
  7144. /**
  7145. * A `gantt` series. If the [type](#series.gantt.type) option is not specified,
  7146. * it is inherited from [chart.type](#chart.type).
  7147. *
  7148. * @extends plotOptions.xrange
  7149. * @product gantt
  7150. * @requires highcharts-gantt
  7151. * @optionparent plotOptions.gantt
  7152. */
  7153. GanttSeries.defaultOptions = merge(XRangeSeries.defaultOptions, {
  7154. // options - default options merged with parent
  7155. grouping: false,
  7156. dataLabels: {
  7157. enabled: true
  7158. },
  7159. tooltip: {
  7160. headerFormat: '<span style="font-size: 10px">{series.name}</span><br/>',
  7161. pointFormat: null,
  7162. pointFormatter: function () {
  7163. var point = this,
  7164. series = point.series,
  7165. tooltip = series.chart.tooltip,
  7166. xAxis = series.xAxis,
  7167. formats = series.tooltipOptions.dateTimeLabelFormats,
  7168. startOfWeek = xAxis.options.startOfWeek,
  7169. ttOptions = series.tooltipOptions,
  7170. format = ttOptions.xDateFormat,
  7171. start,
  7172. end,
  7173. milestone = point.options.milestone,
  7174. retVal = '<b>' + (point.name || point.yCategory) + '</b>';
  7175. if (ttOptions.pointFormat) {
  7176. return point.tooltipFormatter(ttOptions.pointFormat);
  7177. }
  7178. if (!format) {
  7179. format = splat(tooltip.getDateFormat(xAxis.closestPointRange, point.start, startOfWeek, formats))[0];
  7180. }
  7181. start = series.chart.time.dateFormat(format, point.start);
  7182. end = series.chart.time.dateFormat(format, point.end);
  7183. retVal += '<br/>';
  7184. if (!milestone) {
  7185. retVal += 'Start: ' + start + '<br/>';
  7186. retVal += 'End: ' + end + '<br/>';
  7187. }
  7188. else {
  7189. retVal += start + '<br/>';
  7190. }
  7191. return retVal;
  7192. }
  7193. },
  7194. connectors: {
  7195. type: 'simpleConnect',
  7196. /**
  7197. * @declare Highcharts.ConnectorsAnimationOptionsObject
  7198. */
  7199. animation: {
  7200. reversed: true // Dependencies go from child to parent
  7201. },
  7202. startMarker: {
  7203. enabled: true,
  7204. symbol: 'arrow-filled',
  7205. radius: 4,
  7206. fill: '#fa0',
  7207. align: 'left'
  7208. },
  7209. endMarker: {
  7210. enabled: false,
  7211. align: 'right'
  7212. }
  7213. }
  7214. });
  7215. return GanttSeries;
  7216. }(XRangeSeries));
  7217. extend(GanttSeries.prototype, {
  7218. // Keyboard navigation, don't use nearest vertical mode
  7219. keyboardMoveVertical: false,
  7220. pointArrayMap: ['start', 'end', 'y'],
  7221. pointClass: GanttPoint,
  7222. setData: Series.prototype.setData
  7223. });
  7224. SeriesRegistry.registerSeriesType('gantt', GanttSeries);
  7225. /* *
  7226. *
  7227. * Default Export
  7228. *
  7229. * */
  7230. /* *
  7231. *
  7232. * API Options
  7233. *
  7234. * */
  7235. /**
  7236. * A `gantt` series.
  7237. *
  7238. * @extends series,plotOptions.gantt
  7239. * @excluding boostThreshold, connectors, dashStyle, findNearestPointBy,
  7240. * getExtremesFromAll, marker, negativeColor, pointInterval,
  7241. * pointIntervalUnit, pointPlacement, pointStart
  7242. * @product gantt
  7243. * @requires highcharts-gantt
  7244. * @apioption series.gantt
  7245. */
  7246. /**
  7247. * Data for a Gantt series.
  7248. *
  7249. * @declare Highcharts.GanttPointOptionsObject
  7250. * @type {Array<*>}
  7251. * @extends series.xrange.data
  7252. * @excluding className, connect, dataLabels, events,
  7253. * partialFill, selected, x, x2
  7254. * @product gantt
  7255. * @apioption series.gantt.data
  7256. */
  7257. /**
  7258. * Whether the grid node belonging to this point should start as collapsed. Used
  7259. * in axes of type treegrid.
  7260. *
  7261. * @sample {gantt} gantt/treegrid-axis/collapsed/
  7262. * Start as collapsed
  7263. *
  7264. * @type {boolean}
  7265. * @default false
  7266. * @product gantt
  7267. * @apioption series.gantt.data.collapsed
  7268. */
  7269. /**
  7270. * The start time of a task.
  7271. *
  7272. * @type {number}
  7273. * @product gantt
  7274. * @apioption series.gantt.data.start
  7275. */
  7276. /**
  7277. * The end time of a task.
  7278. *
  7279. * @type {number}
  7280. * @product gantt
  7281. * @apioption series.gantt.data.end
  7282. */
  7283. /**
  7284. * The Y value of a task.
  7285. *
  7286. * @type {number}
  7287. * @product gantt
  7288. * @apioption series.gantt.data.y
  7289. */
  7290. /**
  7291. * The name of a task. If a `treegrid` y-axis is used (default in Gantt charts),
  7292. * this will be picked up automatically, and used to calculate the y-value.
  7293. *
  7294. * @type {string}
  7295. * @product gantt
  7296. * @apioption series.gantt.data.name
  7297. */
  7298. /**
  7299. * Progress indicator, how much of the task completed. If it is a number, the
  7300. * `fill` will be applied automatically.
  7301. *
  7302. * @sample {gantt} gantt/demo/progress-indicator
  7303. * Progress indicator
  7304. *
  7305. * @type {number|*}
  7306. * @extends series.xrange.data.partialFill
  7307. * @product gantt
  7308. * @apioption series.gantt.data.completed
  7309. */
  7310. /**
  7311. * The amount of the progress indicator, ranging from 0 (not started) to 1
  7312. * (finished).
  7313. *
  7314. * @type {number}
  7315. * @default 0
  7316. * @apioption series.gantt.data.completed.amount
  7317. */
  7318. /**
  7319. * The fill of the progress indicator. Defaults to a darkened variety of the
  7320. * main color.
  7321. *
  7322. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7323. * @apioption series.gantt.data.completed.fill
  7324. */
  7325. /**
  7326. * The ID of the point (task) that this point depends on in Gantt charts.
  7327. * Aliases [connect](series.xrange.data.connect). Can also be an object,
  7328. * specifying further connecting [options](series.gantt.connectors) between the
  7329. * points. Multiple connections can be specified by providing an array.
  7330. *
  7331. * @sample gantt/demo/project-management
  7332. * Dependencies
  7333. * @sample gantt/pathfinder/demo
  7334. * Different connection types
  7335. *
  7336. * @type {string|Array<string|*>|*}
  7337. * @extends series.xrange.data.connect
  7338. * @since 6.2.0
  7339. * @product gantt
  7340. * @apioption series.gantt.data.dependency
  7341. */
  7342. /**
  7343. * Whether this point is a milestone. If so, only the `start` option is handled,
  7344. * while `end` is ignored.
  7345. *
  7346. * @sample gantt/gantt/milestones
  7347. * Milestones
  7348. *
  7349. * @type {boolean}
  7350. * @since 6.2.0
  7351. * @product gantt
  7352. * @apioption series.gantt.data.milestone
  7353. */
  7354. /**
  7355. * The ID of the parent point (task) of this point in Gantt charts.
  7356. *
  7357. * @sample gantt/demo/subtasks
  7358. * Gantt chart with subtasks
  7359. *
  7360. * @type {string}
  7361. * @since 6.2.0
  7362. * @product gantt
  7363. * @apioption series.gantt.data.parent
  7364. */
  7365. /**
  7366. * @excluding afterAnimate
  7367. * @apioption series.gantt.events
  7368. */
  7369. ''; // adds doclets above to the transpiled file
  7370. return GanttSeries;
  7371. });
  7372. _registerModule(_modules, 'Core/Chart/GanttChart.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Options.js'], _modules['Core/Utilities.js']], function (Chart, O, U) {
  7373. /* *
  7374. *
  7375. * (c) 2016-2021 Highsoft AS
  7376. *
  7377. * Author: Lars A. V. Cabrera
  7378. *
  7379. * License: www.highcharts.com/license
  7380. *
  7381. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7382. *
  7383. * */
  7384. var __extends = (this && this.__extends) || (function () {
  7385. var extendStatics = function (d,
  7386. b) {
  7387. extendStatics = Object.setPrototypeOf ||
  7388. ({ __proto__: [] } instanceof Array && function (d,
  7389. b) { d.__proto__ = b; }) ||
  7390. function (d,
  7391. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  7392. return extendStatics(d, b);
  7393. };
  7394. return function (d, b) {
  7395. extendStatics(d, b);
  7396. function __() { this.constructor = d; }
  7397. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  7398. };
  7399. })();
  7400. var getOptions = O.getOptions;
  7401. var isArray = U.isArray,
  7402. merge = U.merge,
  7403. splat = U.splat;
  7404. /**
  7405. * Gantt-optimized chart. Use {@link Highcharts.Chart|Chart} for common charts.
  7406. *
  7407. * @requires modules/gantt
  7408. *
  7409. * @class
  7410. * @name Highcharts.GanttChart
  7411. * @extends Highcharts.Chart
  7412. */
  7413. var GanttChart = /** @class */ (function (_super) {
  7414. __extends(GanttChart, _super);
  7415. function GanttChart() {
  7416. return _super !== null && _super.apply(this, arguments) || this;
  7417. }
  7418. /**
  7419. * Initializes the chart. The constructor's arguments are passed on
  7420. * directly.
  7421. *
  7422. * @function Highcharts.GanttChart#init
  7423. *
  7424. * @param {Highcharts.Options} userOptions
  7425. * Custom options.
  7426. *
  7427. * @param {Function} [callback]
  7428. * Function to run when the chart has loaded and and all external
  7429. * images are loaded.
  7430. *
  7431. * @return {void}
  7432. *
  7433. * @fires Highcharts.GanttChart#event:init
  7434. * @fires Highcharts.GanttChart#event:afterInit
  7435. */
  7436. GanttChart.prototype.init = function (userOptions, callback) {
  7437. var seriesOptions = userOptions.series,
  7438. defaultOptions = getOptions(),
  7439. defaultLinkedTo;
  7440. // If user hasn't defined axes as array, make it into an array and add a
  7441. // second axis by default.
  7442. if (!isArray(userOptions.xAxis)) {
  7443. userOptions.xAxis = [userOptions.xAxis || {}, {}];
  7444. }
  7445. // apply X axis options to both single and multi x axes
  7446. userOptions.xAxis = userOptions.xAxis.map(function (xAxisOptions, i) {
  7447. if (i === 1) { // Second xAxis
  7448. defaultLinkedTo = 0;
  7449. }
  7450. return merge(defaultOptions.xAxis, {
  7451. grid: {
  7452. enabled: true
  7453. },
  7454. opposite: true,
  7455. linkedTo: defaultLinkedTo
  7456. }, xAxisOptions, // user options
  7457. {
  7458. type: 'datetime'
  7459. });
  7460. });
  7461. // apply Y axis options to both single and multi y axes
  7462. userOptions.yAxis = (splat(userOptions.yAxis || {})).map(function (yAxisOptions) {
  7463. return merge(defaultOptions.yAxis, // #3802
  7464. {
  7465. grid: {
  7466. enabled: true
  7467. },
  7468. staticScale: 50,
  7469. reversed: true,
  7470. // Set default type treegrid, but only if 'categories' is
  7471. // undefined
  7472. type: yAxisOptions.categories ? yAxisOptions.type : 'treegrid'
  7473. }, yAxisOptions // user options
  7474. );
  7475. });
  7476. delete userOptions.series;
  7477. userOptions = merge(true, {
  7478. chart: {
  7479. type: 'gantt'
  7480. },
  7481. title: {
  7482. text: null
  7483. },
  7484. legend: {
  7485. enabled: false
  7486. },
  7487. navigator: {
  7488. series: { type: 'gantt' },
  7489. // Bars were clipped, #14060.
  7490. yAxis: {
  7491. type: 'category'
  7492. }
  7493. }
  7494. }, userOptions, // user's options
  7495. // forced options
  7496. {
  7497. isGantt: true
  7498. });
  7499. userOptions.series = seriesOptions;
  7500. _super.prototype.init.call(this, userOptions, callback);
  7501. };
  7502. return GanttChart;
  7503. }(Chart));
  7504. /* eslint-disable valid-jsdoc */
  7505. (function (GanttChart) {
  7506. /**
  7507. * The factory function for creating new gantt charts. Creates a new {@link
  7508. * Highcharts.GanttChart|GanttChart} object with different default options
  7509. * than the basic Chart.
  7510. *
  7511. * @example
  7512. * // Render a chart in to div#container
  7513. * let chart = Highcharts.ganttChart('container', {
  7514. * title: {
  7515. * text: 'My chart'
  7516. * },
  7517. * series: [{
  7518. * data: ...
  7519. * }]
  7520. * });
  7521. *
  7522. * @function Highcharts.ganttChart
  7523. *
  7524. * @param {string|Highcharts.HTMLDOMElement} renderTo
  7525. * The DOM element to render to, or its id.
  7526. *
  7527. * @param {Highcharts.Options} options
  7528. * The chart options structure.
  7529. *
  7530. * @param {Highcharts.ChartCallbackFunction} [callback]
  7531. * Function to run when the chart has loaded and and all external
  7532. * images are loaded. Defining a
  7533. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  7534. * handler is equivalent.
  7535. *
  7536. * @return {Highcharts.GanttChart}
  7537. * Returns the Chart object.
  7538. */
  7539. function ganttChart(a, b, c) {
  7540. return new GanttChart(a, b, c);
  7541. }
  7542. GanttChart.ganttChart = ganttChart;
  7543. })(GanttChart || (GanttChart = {}));
  7544. return GanttChart;
  7545. });
  7546. _registerModule(_modules, 'Core/Axis/ScrollbarAxis.js', [_modules['Core/Utilities.js']], function (U) {
  7547. /* *
  7548. *
  7549. * (c) 2010-2021 Torstein Honsi
  7550. *
  7551. * License: www.highcharts.com/license
  7552. *
  7553. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7554. *
  7555. * */
  7556. var addEvent = U.addEvent,
  7557. defined = U.defined,
  7558. pick = U.pick;
  7559. /* eslint-disable no-invalid-this, valid-jsdoc */
  7560. /**
  7561. * Creates scrollbars if enabled.
  7562. *
  7563. * @private
  7564. */
  7565. var ScrollbarAxis = /** @class */ (function () {
  7566. function ScrollbarAxis() {
  7567. }
  7568. /**
  7569. * Attaches to axis events to create scrollbars if enabled.
  7570. *
  7571. * @private
  7572. *
  7573. * @param AxisClass
  7574. * Axis class to extend.
  7575. *
  7576. * @param ScrollbarClass
  7577. * Scrollbar class to use.
  7578. */
  7579. ScrollbarAxis.compose = function (AxisClass, ScrollbarClass) {
  7580. var getExtremes = function (axis) {
  7581. var axisMin = pick(axis.options && axis.options.min, axis.min);
  7582. var axisMax = pick(axis.options && axis.options.max,
  7583. axis.max);
  7584. return {
  7585. axisMin: axisMin,
  7586. axisMax: axisMax,
  7587. scrollMin: defined(axis.dataMin) ?
  7588. Math.min(axisMin, axis.min, axis.dataMin, pick(axis.threshold, Infinity)) : axisMin,
  7589. scrollMax: defined(axis.dataMax) ?
  7590. Math.max(axisMax, axis.max, axis.dataMax, pick(axis.threshold, -Infinity)) : axisMax
  7591. };
  7592. };
  7593. // Wrap axis initialization and create scrollbar if enabled:
  7594. addEvent(AxisClass, 'afterInit', function () {
  7595. var axis = this;
  7596. if (axis.options &&
  7597. axis.options.scrollbar &&
  7598. axis.options.scrollbar.enabled) {
  7599. // Predefined options:
  7600. axis.options.scrollbar.vertical = !axis.horiz;
  7601. axis.options.startOnTick = axis.options.endOnTick = false;
  7602. axis.scrollbar = new ScrollbarClass(axis.chart.renderer, axis.options.scrollbar, axis.chart);
  7603. addEvent(axis.scrollbar, 'changed', function (e) {
  7604. var _a = getExtremes(axis),
  7605. axisMin = _a.axisMin,
  7606. axisMax = _a.axisMax,
  7607. unitedMin = _a.scrollMin,
  7608. unitedMax = _a.scrollMax,
  7609. range = unitedMax - unitedMin,
  7610. to,
  7611. from;
  7612. // #12834, scroll when show/hide series, wrong extremes
  7613. if (!defined(axisMin) || !defined(axisMax)) {
  7614. return;
  7615. }
  7616. if ((axis.horiz && !axis.reversed) ||
  7617. (!axis.horiz && axis.reversed)) {
  7618. to = unitedMin + range * this.to;
  7619. from = unitedMin + range * this.from;
  7620. }
  7621. else {
  7622. // y-values in browser are reversed, but this also
  7623. // applies for reversed horizontal axis:
  7624. to = unitedMin + range * (1 - this.from);
  7625. from = unitedMin + range * (1 - this.to);
  7626. }
  7627. if (this.shouldUpdateExtremes(e.DOMType)) {
  7628. axis.setExtremes(from, to, true, e.DOMType !== 'mousemove' && e.DOMType !== 'touchmove', e);
  7629. }
  7630. else {
  7631. // When live redraw is disabled, don't change extremes
  7632. // Only change the position of the scollbar thumb
  7633. this.setRange(this.from, this.to);
  7634. }
  7635. });
  7636. }
  7637. });
  7638. // Wrap rendering axis, and update scrollbar if one is created:
  7639. addEvent(AxisClass, 'afterRender', function () {
  7640. var axis = this,
  7641. _a = getExtremes(axis),
  7642. scrollMin = _a.scrollMin,
  7643. scrollMax = _a.scrollMax,
  7644. scrollbar = axis.scrollbar,
  7645. offset = axis.axisTitleMargin + (axis.titleOffset || 0),
  7646. scrollbarsOffsets = axis.chart.scrollbarsOffsets,
  7647. axisMargin = axis.options.margin || 0,
  7648. offsetsIndex,
  7649. from,
  7650. to;
  7651. if (scrollbar) {
  7652. if (axis.horiz) {
  7653. // Reserve space for labels/title
  7654. if (!axis.opposite) {
  7655. scrollbarsOffsets[1] += offset;
  7656. }
  7657. scrollbar.position(axis.left, axis.top + axis.height + 2 + scrollbarsOffsets[1] -
  7658. (axis.opposite ? axisMargin : 0), axis.width, axis.height);
  7659. // Next scrollbar should reserve space for margin (if set)
  7660. if (!axis.opposite) {
  7661. scrollbarsOffsets[1] += axisMargin;
  7662. }
  7663. offsetsIndex = 1;
  7664. }
  7665. else {
  7666. // Reserve space for labels/title
  7667. if (axis.opposite) {
  7668. scrollbarsOffsets[0] += offset;
  7669. }
  7670. scrollbar.position(axis.left + axis.width + 2 + scrollbarsOffsets[0] -
  7671. (axis.opposite ? 0 : axisMargin), axis.top, axis.width, axis.height);
  7672. // Next scrollbar should reserve space for margin (if set)
  7673. if (axis.opposite) {
  7674. scrollbarsOffsets[0] += axisMargin;
  7675. }
  7676. offsetsIndex = 0;
  7677. }
  7678. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  7679. scrollbar.options.margin;
  7680. if (isNaN(scrollMin) ||
  7681. isNaN(scrollMax) ||
  7682. !defined(axis.min) ||
  7683. !defined(axis.max) ||
  7684. axis.min === axis.max // #10733
  7685. ) {
  7686. // default action: when extremes are the same or there is
  7687. // not extremes on the axis, but scrollbar exists, make it
  7688. // full size
  7689. scrollbar.setRange(0, 1);
  7690. }
  7691. else {
  7692. from =
  7693. (axis.min - scrollMin) / (scrollMax - scrollMin);
  7694. to =
  7695. (axis.max - scrollMin) / (scrollMax - scrollMin);
  7696. if ((axis.horiz && !axis.reversed) ||
  7697. (!axis.horiz && axis.reversed)) {
  7698. scrollbar.setRange(from, to);
  7699. }
  7700. else {
  7701. // inverse vertical axis
  7702. scrollbar.setRange(1 - to, 1 - from);
  7703. }
  7704. }
  7705. }
  7706. });
  7707. // Make space for a scrollbar:
  7708. addEvent(AxisClass, 'afterGetOffset', function () {
  7709. var axis = this,
  7710. index = axis.horiz ? 2 : 1,
  7711. scrollbar = axis.scrollbar;
  7712. if (scrollbar) {
  7713. axis.chart.scrollbarsOffsets = [0, 0]; // reset scrollbars offsets
  7714. axis.chart.axisOffset[index] +=
  7715. scrollbar.size + scrollbar.options.margin;
  7716. }
  7717. });
  7718. };
  7719. return ScrollbarAxis;
  7720. }());
  7721. return ScrollbarAxis;
  7722. });
  7723. _registerModule(_modules, 'Core/Scrollbar.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Color/Palette.js'], _modules['Core/Axis/ScrollbarAxis.js'], _modules['Core/Utilities.js'], _modules['Core/Options.js']], function (Axis, H, palette, ScrollbarAxis, U, O) {
  7724. /* *
  7725. *
  7726. * (c) 2010-2021 Torstein Honsi
  7727. *
  7728. * License: www.highcharts.com/license
  7729. *
  7730. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7731. *
  7732. * */
  7733. var addEvent = U.addEvent,
  7734. correctFloat = U.correctFloat,
  7735. defined = U.defined,
  7736. destroyObjectProperties = U.destroyObjectProperties,
  7737. fireEvent = U.fireEvent,
  7738. merge = U.merge,
  7739. pick = U.pick,
  7740. removeEvent = U.removeEvent;
  7741. var defaultOptions = O.defaultOptions;
  7742. var isTouchDevice = H.isTouchDevice;
  7743. /**
  7744. * When we have vertical scrollbar, rifles and arrow in buttons should be
  7745. * rotated. The same method is used in Navigator's handles, to rotate them.
  7746. *
  7747. * @function Highcharts.swapXY
  7748. *
  7749. * @param {Highcharts.SVGPathArray} path
  7750. * Path to be rotated.
  7751. *
  7752. * @param {boolean} [vertical]
  7753. * If vertical scrollbar, swap x-y values.
  7754. *
  7755. * @return {Highcharts.SVGPathArray}
  7756. * Rotated path.
  7757. *
  7758. * @requires modules/stock
  7759. */
  7760. var swapXY = H.swapXY = function (path,
  7761. vertical) {
  7762. if (vertical) {
  7763. path.forEach(function (seg) {
  7764. var len = seg.length;
  7765. var temp;
  7766. for (var i = 0; i < len; i += 2) {
  7767. temp = seg[i + 1];
  7768. if (typeof temp === 'number') {
  7769. seg[i + 1] = seg[i + 2];
  7770. seg[i + 2] = temp;
  7771. }
  7772. }
  7773. });
  7774. }
  7775. return path;
  7776. };
  7777. /* eslint-disable no-invalid-this, valid-jsdoc */
  7778. /**
  7779. * A reusable scrollbar, internally used in Highcharts Stock's
  7780. * navigator and optionally on individual axes.
  7781. *
  7782. * @private
  7783. * @class
  7784. * @name Highcharts.Scrollbar
  7785. * @param {Highcharts.SVGRenderer} renderer
  7786. * @param {Highcharts.ScrollbarOptions} options
  7787. * @param {Highcharts.Chart} chart
  7788. */
  7789. var Scrollbar = /** @class */ (function () {
  7790. /* *
  7791. *
  7792. * Constructors
  7793. *
  7794. * */
  7795. function Scrollbar(renderer, options, chart) {
  7796. /* *
  7797. *
  7798. * Properties
  7799. *
  7800. * */
  7801. this._events = [];
  7802. this.chartX = 0;
  7803. this.chartY = 0;
  7804. this.from = 0;
  7805. this.group = void 0;
  7806. this.scrollbar = void 0;
  7807. this.scrollbarButtons = [];
  7808. this.scrollbarGroup = void 0;
  7809. this.scrollbarLeft = 0;
  7810. this.scrollbarRifles = void 0;
  7811. this.scrollbarStrokeWidth = 1;
  7812. this.scrollbarTop = 0;
  7813. this.size = 0;
  7814. this.to = 0;
  7815. this.track = void 0;
  7816. this.trackBorderWidth = 1;
  7817. this.userOptions = {};
  7818. this.x = 0;
  7819. this.y = 0;
  7820. this.chart = chart;
  7821. this.options = options;
  7822. this.renderer = chart.renderer;
  7823. this.init(renderer, options, chart);
  7824. }
  7825. /* *
  7826. *
  7827. * Functions
  7828. *
  7829. * */
  7830. /**
  7831. * Set up the mouse and touch events for the Scrollbar
  7832. *
  7833. * @private
  7834. * @function Highcharts.Scrollbar#addEvents
  7835. * @return {void}
  7836. */
  7837. Scrollbar.prototype.addEvents = function () {
  7838. var buttonsOrder = this.options.inverted ? [1, 0] : [0, 1],
  7839. buttons = this.scrollbarButtons,
  7840. bar = this.scrollbarGroup.element,
  7841. track = this.track.element,
  7842. mouseDownHandler = this.mouseDownHandler.bind(this),
  7843. mouseMoveHandler = this.mouseMoveHandler.bind(this),
  7844. mouseUpHandler = this.mouseUpHandler.bind(this),
  7845. _events;
  7846. // Mouse events
  7847. _events = [
  7848. [buttons[buttonsOrder[0]].element, 'click', this.buttonToMinClick.bind(this)],
  7849. [buttons[buttonsOrder[1]].element, 'click', this.buttonToMaxClick.bind(this)],
  7850. [track, 'click', this.trackClick.bind(this)],
  7851. [bar, 'mousedown', mouseDownHandler],
  7852. [bar.ownerDocument, 'mousemove', mouseMoveHandler],
  7853. [bar.ownerDocument, 'mouseup', mouseUpHandler]
  7854. ];
  7855. // Touch events
  7856. if (H.hasTouch) {
  7857. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  7858. }
  7859. // Add them all
  7860. _events.forEach(function (args) {
  7861. addEvent.apply(null, args);
  7862. });
  7863. this._events = _events;
  7864. };
  7865. Scrollbar.prototype.buttonToMaxClick = function (e) {
  7866. var scroller = this;
  7867. var range = (scroller.to - scroller.from) * pick(scroller.options.step, 0.2);
  7868. scroller.updatePosition(scroller.from + range, scroller.to + range);
  7869. fireEvent(scroller, 'changed', {
  7870. from: scroller.from,
  7871. to: scroller.to,
  7872. trigger: 'scrollbar',
  7873. DOMEvent: e
  7874. });
  7875. };
  7876. Scrollbar.prototype.buttonToMinClick = function (e) {
  7877. var scroller = this;
  7878. var range = correctFloat(scroller.to - scroller.from) *
  7879. pick(scroller.options.step, 0.2);
  7880. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  7881. fireEvent(scroller, 'changed', {
  7882. from: scroller.from,
  7883. to: scroller.to,
  7884. trigger: 'scrollbar',
  7885. DOMEvent: e
  7886. });
  7887. };
  7888. /**
  7889. * Get normalized (0-1) cursor position over the scrollbar
  7890. *
  7891. * @private
  7892. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  7893. *
  7894. * @param {*} normalizedEvent
  7895. * normalized event, with chartX and chartY values
  7896. *
  7897. * @return {Highcharts.Dictionary<number>}
  7898. * Local position {chartX, chartY}
  7899. */
  7900. Scrollbar.prototype.cursorToScrollbarPosition = function (normalizedEvent) {
  7901. var scroller = this,
  7902. options = scroller.options,
  7903. minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  7904. options.minWidth :
  7905. 0; // minWidth distorts translation
  7906. return {
  7907. chartX: (normalizedEvent.chartX - scroller.x -
  7908. scroller.xOffset) /
  7909. (scroller.barWidth - minWidthDifference),
  7910. chartY: (normalizedEvent.chartY - scroller.y -
  7911. scroller.yOffset) /
  7912. (scroller.barWidth - minWidthDifference)
  7913. };
  7914. };
  7915. /**
  7916. * Destroys allocated elements.
  7917. *
  7918. * @private
  7919. * @function Highcharts.Scrollbar#destroy
  7920. * @return {void}
  7921. */
  7922. Scrollbar.prototype.destroy = function () {
  7923. var scroller = this.chart.scroller;
  7924. // Disconnect events added in addEvents
  7925. this.removeEvents();
  7926. // Destroy properties
  7927. [
  7928. 'track',
  7929. 'scrollbarRifles',
  7930. 'scrollbar',
  7931. 'scrollbarGroup',
  7932. 'group'
  7933. ].forEach(function (prop) {
  7934. if (this[prop] && this[prop].destroy) {
  7935. this[prop] = this[prop].destroy();
  7936. }
  7937. }, this);
  7938. // #6421, chart may have more scrollbars
  7939. if (scroller && this === scroller.scrollbar) {
  7940. scroller.scrollbar = null;
  7941. // Destroy elements in collection
  7942. destroyObjectProperties(scroller.scrollbarButtons);
  7943. }
  7944. };
  7945. /**
  7946. * Draw the scrollbar buttons with arrows
  7947. *
  7948. * @private
  7949. * @function Highcharts.Scrollbar#drawScrollbarButton
  7950. * @param {number} index
  7951. * 0 is left, 1 is right
  7952. * @return {void}
  7953. */
  7954. Scrollbar.prototype.drawScrollbarButton = function (index) {
  7955. var scroller = this,
  7956. renderer = scroller.renderer,
  7957. scrollbarButtons = scroller.scrollbarButtons,
  7958. options = scroller.options,
  7959. size = scroller.size,
  7960. group,
  7961. tempElem;
  7962. group = renderer.g().add(scroller.group);
  7963. scrollbarButtons.push(group);
  7964. // Create a rectangle for the scrollbar button
  7965. tempElem = renderer.rect()
  7966. .addClass('highcharts-scrollbar-button')
  7967. .add(group);
  7968. // Presentational attributes
  7969. if (!this.chart.styledMode) {
  7970. tempElem.attr({
  7971. stroke: options.buttonBorderColor,
  7972. 'stroke-width': options.buttonBorderWidth,
  7973. fill: options.buttonBackgroundColor
  7974. });
  7975. }
  7976. // Place the rectangle based on the rendered stroke width
  7977. tempElem.attr(tempElem.crisp({
  7978. x: -0.5,
  7979. y: -0.5,
  7980. width: size + 1,
  7981. height: size + 1,
  7982. r: options.buttonBorderRadius
  7983. }, tempElem.strokeWidth()));
  7984. // Button arrow
  7985. tempElem = renderer
  7986. .path(swapXY([[
  7987. 'M',
  7988. size / 2 + (index ? -1 : 1),
  7989. size / 2 - 3
  7990. ], [
  7991. 'L',
  7992. size / 2 + (index ? -1 : 1),
  7993. size / 2 + 3
  7994. ], [
  7995. 'L',
  7996. size / 2 + (index ? 2 : -2),
  7997. size / 2
  7998. ]], options.vertical))
  7999. .addClass('highcharts-scrollbar-arrow')
  8000. .add(scrollbarButtons[index]);
  8001. if (!this.chart.styledMode) {
  8002. tempElem.attr({
  8003. fill: options.buttonArrowColor
  8004. });
  8005. }
  8006. };
  8007. /**
  8008. * @private
  8009. * @function Highcharts.Scrollbar#init
  8010. * @param {Highcharts.SVGRenderer} renderer
  8011. * @param {Highcharts.ScrollbarOptions} options
  8012. * @param {Highcharts.Chart} chart
  8013. */
  8014. Scrollbar.prototype.init = function (renderer, options, chart) {
  8015. this.scrollbarButtons = [];
  8016. this.renderer = renderer;
  8017. this.userOptions = options;
  8018. this.options = merge(Scrollbar.defaultOptions, options);
  8019. this.chart = chart;
  8020. // backward compatibility
  8021. this.size = pick(this.options.size, this.options.height);
  8022. // Init
  8023. if (options.enabled) {
  8024. this.render();
  8025. this.addEvents();
  8026. }
  8027. };
  8028. Scrollbar.prototype.mouseDownHandler = function (e) {
  8029. var scroller = this;
  8030. var normalizedEvent = scroller.chart.pointer.normalize(e),
  8031. mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  8032. scroller.chartX = mousePosition.chartX;
  8033. scroller.chartY = mousePosition.chartY;
  8034. scroller.initPositions = [scroller.from, scroller.to];
  8035. scroller.grabbedCenter = true;
  8036. };
  8037. /**
  8038. * Event handler for the mouse move event.
  8039. * @private
  8040. */
  8041. Scrollbar.prototype.mouseMoveHandler = function (e) {
  8042. var scroller = this;
  8043. var normalizedEvent = scroller.chart.pointer.normalize(e),
  8044. options = scroller.options,
  8045. direction = options.vertical ? 'chartY' : 'chartX',
  8046. initPositions = scroller.initPositions || [],
  8047. scrollPosition,
  8048. chartPosition,
  8049. change;
  8050. // In iOS, a mousemove event with e.pageX === 0 is fired when
  8051. // holding the finger down in the center of the scrollbar. This
  8052. // should be ignored.
  8053. if (scroller.grabbedCenter &&
  8054. // #4696, scrollbar failed on Android
  8055. (!e.touches || e.touches[0][direction] !== 0)) {
  8056. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  8057. scrollPosition = scroller[direction];
  8058. change = chartPosition - scrollPosition;
  8059. scroller.hasDragged = true;
  8060. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  8061. if (scroller.hasDragged) {
  8062. fireEvent(scroller, 'changed', {
  8063. from: scroller.from,
  8064. to: scroller.to,
  8065. trigger: 'scrollbar',
  8066. DOMType: e.type,
  8067. DOMEvent: e
  8068. });
  8069. }
  8070. }
  8071. };
  8072. /**
  8073. * Event handler for the mouse up event.
  8074. * @private
  8075. */
  8076. Scrollbar.prototype.mouseUpHandler = function (e) {
  8077. var scroller = this;
  8078. if (scroller.hasDragged) {
  8079. fireEvent(scroller, 'changed', {
  8080. from: scroller.from,
  8081. to: scroller.to,
  8082. trigger: 'scrollbar',
  8083. DOMType: e.type,
  8084. DOMEvent: e
  8085. });
  8086. }
  8087. scroller.grabbedCenter =
  8088. scroller.hasDragged =
  8089. scroller.chartX =
  8090. scroller.chartY = null;
  8091. };
  8092. /**
  8093. * Position the scrollbar, method called from a parent with defined
  8094. * dimensions.
  8095. *
  8096. * @private
  8097. * @function Highcharts.Scrollbar#position
  8098. * @param {number} x
  8099. * x-position on the chart
  8100. * @param {number} y
  8101. * y-position on the chart
  8102. * @param {number} width
  8103. * width of the scrollbar
  8104. * @param {number} height
  8105. * height of the scorllbar
  8106. * @return {void}
  8107. */
  8108. Scrollbar.prototype.position = function (x, y, width, height) {
  8109. var scroller = this,
  8110. options = scroller.options,
  8111. vertical = options.vertical,
  8112. xOffset = height,
  8113. yOffset = 0,
  8114. method = scroller.rendered ? 'animate' : 'attr';
  8115. scroller.x = x;
  8116. scroller.y = y + this.trackBorderWidth;
  8117. scroller.width = width; // width with buttons
  8118. scroller.height = height;
  8119. scroller.xOffset = xOffset;
  8120. scroller.yOffset = yOffset;
  8121. // If Scrollbar is a vertical type, swap options:
  8122. if (vertical) {
  8123. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  8124. scroller.xOffset = xOffset = 0;
  8125. scroller.barWidth = height - width * 2; // width without buttons
  8126. scroller.x = x = x + scroller.options.margin;
  8127. }
  8128. else {
  8129. scroller.height = scroller.xOffset = height = xOffset =
  8130. scroller.size;
  8131. scroller.barWidth = width - height * 2; // width without buttons
  8132. scroller.y = scroller.y + scroller.options.margin;
  8133. }
  8134. // Set general position for a group:
  8135. scroller.group[method]({
  8136. translateX: x,
  8137. translateY: scroller.y
  8138. });
  8139. // Resize background/track:
  8140. scroller.track[method]({
  8141. width: width,
  8142. height: height
  8143. });
  8144. // Move right/bottom button ot it's place:
  8145. scroller.scrollbarButtons[1][method]({
  8146. translateX: vertical ? 0 : width - xOffset,
  8147. translateY: vertical ? height - yOffset : 0
  8148. });
  8149. };
  8150. /**
  8151. * Removes the event handlers attached previously with addEvents.
  8152. *
  8153. * @private
  8154. * @function Highcharts.Scrollbar#removeEvents
  8155. * @return {void}
  8156. */
  8157. Scrollbar.prototype.removeEvents = function () {
  8158. this._events.forEach(function (args) {
  8159. removeEvent.apply(null, args);
  8160. });
  8161. this._events.length = 0;
  8162. };
  8163. /**
  8164. * Render scrollbar with all required items.
  8165. *
  8166. * @private
  8167. * @function Highcharts.Scrollbar#render
  8168. */
  8169. Scrollbar.prototype.render = function () {
  8170. var scroller = this,
  8171. renderer = scroller.renderer,
  8172. options = scroller.options,
  8173. size = scroller.size,
  8174. styledMode = this.chart.styledMode,
  8175. group;
  8176. // Draw the scrollbar group
  8177. scroller.group = group = renderer.g('scrollbar').attr({
  8178. zIndex: options.zIndex,
  8179. translateY: -99999
  8180. }).add();
  8181. // Draw the scrollbar track:
  8182. scroller.track = renderer.rect()
  8183. .addClass('highcharts-scrollbar-track')
  8184. .attr({
  8185. x: 0,
  8186. r: options.trackBorderRadius || 0,
  8187. height: size,
  8188. width: size
  8189. }).add(group);
  8190. if (!styledMode) {
  8191. scroller.track.attr({
  8192. fill: options.trackBackgroundColor,
  8193. stroke: options.trackBorderColor,
  8194. 'stroke-width': options.trackBorderWidth
  8195. });
  8196. }
  8197. this.trackBorderWidth = scroller.track.strokeWidth();
  8198. scroller.track.attr({
  8199. y: -this.trackBorderWidth % 2 / 2
  8200. });
  8201. // Draw the scrollbar itself
  8202. scroller.scrollbarGroup = renderer.g().add(group);
  8203. scroller.scrollbar = renderer.rect()
  8204. .addClass('highcharts-scrollbar-thumb')
  8205. .attr({
  8206. height: size,
  8207. width: size,
  8208. r: options.barBorderRadius || 0
  8209. }).add(scroller.scrollbarGroup);
  8210. scroller.scrollbarRifles = renderer
  8211. .path(swapXY([
  8212. ['M', -3, size / 4],
  8213. ['L', -3, 2 * size / 3],
  8214. ['M', 0, size / 4],
  8215. ['L', 0, 2 * size / 3],
  8216. ['M', 3, size / 4],
  8217. ['L', 3, 2 * size / 3]
  8218. ], options.vertical))
  8219. .addClass('highcharts-scrollbar-rifles')
  8220. .add(scroller.scrollbarGroup);
  8221. if (!styledMode) {
  8222. scroller.scrollbar.attr({
  8223. fill: options.barBackgroundColor,
  8224. stroke: options.barBorderColor,
  8225. 'stroke-width': options.barBorderWidth
  8226. });
  8227. scroller.scrollbarRifles.attr({
  8228. stroke: options.rifleColor,
  8229. 'stroke-width': 1
  8230. });
  8231. }
  8232. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  8233. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  8234. // Draw the buttons:
  8235. scroller.drawScrollbarButton(0);
  8236. scroller.drawScrollbarButton(1);
  8237. };
  8238. /**
  8239. * Set scrollbar size, with a given scale.
  8240. *
  8241. * @private
  8242. * @function Highcharts.Scrollbar#setRange
  8243. * @param {number} from
  8244. * scale (0-1) where bar should start
  8245. * @param {number} to
  8246. * scale (0-1) where bar should end
  8247. * @return {void}
  8248. */
  8249. Scrollbar.prototype.setRange = function (from, to) {
  8250. var scroller = this,
  8251. options = scroller.options,
  8252. vertical = options.vertical,
  8253. minWidth = options.minWidth,
  8254. fullWidth = scroller.barWidth,
  8255. fromPX,
  8256. toPX,
  8257. newPos,
  8258. newSize,
  8259. newRiflesPos,
  8260. method = (this.rendered &&
  8261. !this.hasDragged &&
  8262. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  8263. if (!defined(fullWidth)) {
  8264. return;
  8265. }
  8266. from = Math.max(from, 0);
  8267. fromPX = Math.ceil(fullWidth * from);
  8268. toPX = fullWidth * Math.min(to, 1);
  8269. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  8270. // We need to recalculate position, if minWidth is used
  8271. if (newSize < minWidth) {
  8272. fromPX = (fullWidth - minWidth + newSize) * from;
  8273. newSize = minWidth;
  8274. }
  8275. newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  8276. newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  8277. // Store current position:
  8278. scroller.from = from;
  8279. scroller.to = to;
  8280. if (!vertical) {
  8281. scroller.scrollbarGroup[method]({
  8282. translateX: newPos
  8283. });
  8284. scroller.scrollbar[method]({
  8285. width: newSize
  8286. });
  8287. scroller.scrollbarRifles[method]({
  8288. translateX: newRiflesPos
  8289. });
  8290. scroller.scrollbarLeft = newPos;
  8291. scroller.scrollbarTop = 0;
  8292. }
  8293. else {
  8294. scroller.scrollbarGroup[method]({
  8295. translateY: newPos
  8296. });
  8297. scroller.scrollbar[method]({
  8298. height: newSize
  8299. });
  8300. scroller.scrollbarRifles[method]({
  8301. translateY: newRiflesPos
  8302. });
  8303. scroller.scrollbarTop = newPos;
  8304. scroller.scrollbarLeft = 0;
  8305. }
  8306. if (newSize <= 12) {
  8307. scroller.scrollbarRifles.hide();
  8308. }
  8309. else {
  8310. scroller.scrollbarRifles.show(true);
  8311. }
  8312. // Show or hide the scrollbar based on the showFull setting
  8313. if (options.showFull === false) {
  8314. if (from <= 0 && to >= 1) {
  8315. scroller.group.hide();
  8316. }
  8317. else {
  8318. scroller.group.show();
  8319. }
  8320. }
  8321. scroller.rendered = true;
  8322. };
  8323. /**
  8324. * Checks if the extremes should be updated in response to a scrollbar
  8325. * change event.
  8326. *
  8327. * @private
  8328. * @function Highcharts.Scrollbar#shouldUpdateExtremes
  8329. * @param {string} eventType
  8330. * @return {boolean}
  8331. */
  8332. Scrollbar.prototype.shouldUpdateExtremes = function (eventType) {
  8333. return (pick(this.options.liveRedraw, H.svg && !H.isTouchDevice && !this.chart.isBoosting) ||
  8334. // Mouseup always should change extremes
  8335. eventType === 'mouseup' ||
  8336. eventType === 'touchend' ||
  8337. // Internal events
  8338. !defined(eventType));
  8339. };
  8340. Scrollbar.prototype.trackClick = function (e) {
  8341. var scroller = this;
  8342. var normalizedEvent = scroller.chart.pointer.normalize(e),
  8343. range = scroller.to - scroller.from,
  8344. top = scroller.y + scroller.scrollbarTop,
  8345. left = scroller.x + scroller.scrollbarLeft;
  8346. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  8347. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  8348. // On the top or on the left side of the track:
  8349. scroller.updatePosition(scroller.from + range, scroller.to + range);
  8350. }
  8351. else {
  8352. // On the bottom or the right side of the track:
  8353. scroller.updatePosition(scroller.from - range, scroller.to - range);
  8354. }
  8355. fireEvent(scroller, 'changed', {
  8356. from: scroller.from,
  8357. to: scroller.to,
  8358. trigger: 'scrollbar',
  8359. DOMEvent: e
  8360. });
  8361. };
  8362. /**
  8363. * Update the scrollbar with new options
  8364. *
  8365. * @private
  8366. * @function Highcharts.Scrollbar#update
  8367. * @param {Highcharts.ScrollbarOptions} options
  8368. * @return {void}
  8369. */
  8370. Scrollbar.prototype.update = function (options) {
  8371. this.destroy();
  8372. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  8373. };
  8374. /**
  8375. * Update position option in the Scrollbar, with normalized 0-1 scale
  8376. *
  8377. * @private
  8378. * @function Highcharts.Scrollbar#updatePosition
  8379. * @param {number} from
  8380. * @param {number} to
  8381. * @return {void}
  8382. */
  8383. Scrollbar.prototype.updatePosition = function (from, to) {
  8384. if (to > 1) {
  8385. from = correctFloat(1 - correctFloat(to - from));
  8386. to = 1;
  8387. }
  8388. if (from < 0) {
  8389. to = correctFloat(to - from);
  8390. from = 0;
  8391. }
  8392. this.from = from;
  8393. this.to = to;
  8394. };
  8395. /* *
  8396. *
  8397. * Static Properties
  8398. *
  8399. * */
  8400. /**
  8401. *
  8402. * The scrollbar is a means of panning over the X axis of a stock chart.
  8403. * Scrollbars can also be applied to other types of axes.
  8404. *
  8405. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  8406. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  8407. * is especially suitable for simpler cartesian charts on mobile.
  8408. *
  8409. * In styled mode, all the presentational options for the
  8410. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  8411. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  8412. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  8413. *
  8414. * @sample stock/yaxis/inverted-bar-scrollbar/
  8415. * A scrollbar on a simple bar chart
  8416. *
  8417. * @product highstock gantt
  8418. * @optionparent scrollbar
  8419. *
  8420. * @private
  8421. */
  8422. Scrollbar.defaultOptions = {
  8423. /**
  8424. * The height of the scrollbar. The height also applies to the width
  8425. * of the scroll arrows so that they are always squares. Defaults to
  8426. * 20 for touch devices and 14 for mouse devices.
  8427. *
  8428. * @sample stock/scrollbar/height/
  8429. * A 30px scrollbar
  8430. *
  8431. * @type {number}
  8432. * @default 20/14
  8433. */
  8434. height: isTouchDevice ? 20 : 14,
  8435. /**
  8436. * The border rounding radius of the bar.
  8437. *
  8438. * @sample stock/scrollbar/style/
  8439. * Scrollbar styling
  8440. */
  8441. barBorderRadius: 0,
  8442. /**
  8443. * The corner radius of the scrollbar buttons.
  8444. *
  8445. * @sample stock/scrollbar/style/
  8446. * Scrollbar styling
  8447. */
  8448. buttonBorderRadius: 0,
  8449. /**
  8450. * Enable or disable the scrollbar.
  8451. *
  8452. * @sample stock/scrollbar/enabled/
  8453. * Disable the scrollbar, only use navigator
  8454. *
  8455. * @type {boolean}
  8456. * @default true
  8457. * @apioption scrollbar.enabled
  8458. */
  8459. /**
  8460. * Whether to redraw the main chart as the scrollbar or the navigator
  8461. * zoomed window is moved. Defaults to `true` for modern browsers and
  8462. * `false` for legacy IE browsers as well as mobile devices.
  8463. *
  8464. * @sample stock/scrollbar/liveredraw
  8465. * Setting live redraw to false
  8466. *
  8467. * @type {boolean}
  8468. * @since 1.3
  8469. */
  8470. liveRedraw: void 0,
  8471. /**
  8472. * The margin between the scrollbar and its axis when the scrollbar is
  8473. * applied directly to an axis.
  8474. */
  8475. margin: 10,
  8476. /**
  8477. * The minimum width of the scrollbar.
  8478. *
  8479. * @since 1.2.5
  8480. */
  8481. minWidth: 6,
  8482. /**
  8483. * Whether to show or hide the scrollbar when the scrolled content is
  8484. * zoomed out to it full extent.
  8485. *
  8486. * @type {boolean}
  8487. * @default true
  8488. * @apioption scrollbar.showFull
  8489. */
  8490. step: 0.2,
  8491. /**
  8492. * The z index of the scrollbar group.
  8493. */
  8494. zIndex: 3,
  8495. /**
  8496. * The background color of the scrollbar itself.
  8497. *
  8498. * @sample stock/scrollbar/style/
  8499. * Scrollbar styling
  8500. *
  8501. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8502. */
  8503. barBackgroundColor: palette.neutralColor20,
  8504. /**
  8505. * The width of the bar's border.
  8506. *
  8507. * @sample stock/scrollbar/style/
  8508. * Scrollbar styling
  8509. */
  8510. barBorderWidth: 1,
  8511. /**
  8512. * The color of the scrollbar's border.
  8513. *
  8514. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8515. */
  8516. barBorderColor: palette.neutralColor20,
  8517. /**
  8518. * The color of the small arrow inside the scrollbar buttons.
  8519. *
  8520. * @sample stock/scrollbar/style/
  8521. * Scrollbar styling
  8522. *
  8523. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8524. */
  8525. buttonArrowColor: palette.neutralColor80,
  8526. /**
  8527. * The color of scrollbar buttons.
  8528. *
  8529. * @sample stock/scrollbar/style/
  8530. * Scrollbar styling
  8531. *
  8532. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8533. */
  8534. buttonBackgroundColor: palette.neutralColor10,
  8535. /**
  8536. * The color of the border of the scrollbar buttons.
  8537. *
  8538. * @sample stock/scrollbar/style/
  8539. * Scrollbar styling
  8540. *
  8541. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8542. */
  8543. buttonBorderColor: palette.neutralColor20,
  8544. /**
  8545. * The border width of the scrollbar buttons.
  8546. *
  8547. * @sample stock/scrollbar/style/
  8548. * Scrollbar styling
  8549. */
  8550. buttonBorderWidth: 1,
  8551. /**
  8552. * The color of the small rifles in the middle of the scrollbar.
  8553. *
  8554. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8555. */
  8556. rifleColor: palette.neutralColor80,
  8557. /**
  8558. * The color of the track background.
  8559. *
  8560. * @sample stock/scrollbar/style/
  8561. * Scrollbar styling
  8562. *
  8563. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8564. */
  8565. trackBackgroundColor: palette.neutralColor5,
  8566. /**
  8567. * The color of the border of the scrollbar track.
  8568. *
  8569. * @sample stock/scrollbar/style/
  8570. * Scrollbar styling
  8571. *
  8572. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8573. */
  8574. trackBorderColor: palette.neutralColor5,
  8575. /**
  8576. * The corner radius of the border of the scrollbar track.
  8577. *
  8578. * @sample stock/scrollbar/style/
  8579. * Scrollbar styling
  8580. *
  8581. * @type {number}
  8582. * @default 0
  8583. * @apioption scrollbar.trackBorderRadius
  8584. */
  8585. /**
  8586. * The width of the border of the scrollbar track.
  8587. *
  8588. * @sample stock/scrollbar/style/
  8589. * Scrollbar styling
  8590. */
  8591. trackBorderWidth: 1
  8592. };
  8593. return Scrollbar;
  8594. }());
  8595. if (!H.Scrollbar) {
  8596. defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);
  8597. H.Scrollbar = Scrollbar;
  8598. ScrollbarAxis.compose(Axis, Scrollbar);
  8599. }
  8600. return H.Scrollbar;
  8601. });
  8602. _registerModule(_modules, 'Extensions/RangeSelector.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Color/Palette.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (Axis, Chart, H, O, palette, SVGElement, U) {
  8603. /* *
  8604. *
  8605. * (c) 2010-2021 Torstein Honsi
  8606. *
  8607. * License: www.highcharts.com/license
  8608. *
  8609. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8610. *
  8611. * */
  8612. var defaultOptions = O.defaultOptions;
  8613. var addEvent = U.addEvent,
  8614. createElement = U.createElement,
  8615. css = U.css,
  8616. defined = U.defined,
  8617. destroyObjectProperties = U.destroyObjectProperties,
  8618. discardElement = U.discardElement,
  8619. extend = U.extend,
  8620. find = U.find,
  8621. fireEvent = U.fireEvent,
  8622. isNumber = U.isNumber,
  8623. merge = U.merge,
  8624. objectEach = U.objectEach,
  8625. pad = U.pad,
  8626. pick = U.pick,
  8627. pInt = U.pInt,
  8628. splat = U.splat;
  8629. /**
  8630. * Define the time span for the button
  8631. *
  8632. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  8633. */
  8634. /**
  8635. * Callback function to react on button clicks.
  8636. *
  8637. * @callback Highcharts.RangeSelectorClickCallbackFunction
  8638. *
  8639. * @param {global.Event} e
  8640. * Event arguments.
  8641. *
  8642. * @param {boolean|undefined}
  8643. * Return false to cancel the default button event.
  8644. */
  8645. /**
  8646. * Callback function to parse values entered in the input boxes and return a
  8647. * valid JavaScript time as milliseconds since 1970.
  8648. *
  8649. * @callback Highcharts.RangeSelectorParseCallbackFunction
  8650. *
  8651. * @param {string} value
  8652. * Input value to parse.
  8653. *
  8654. * @return {number}
  8655. * Parsed JavaScript time value.
  8656. */
  8657. /* ************************************************************************** *
  8658. * Start Range Selector code *
  8659. * ************************************************************************** */
  8660. extend(defaultOptions, {
  8661. /**
  8662. * The range selector is a tool for selecting ranges to display within
  8663. * the chart. It provides buttons to select preconfigured ranges in
  8664. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  8665. * boxes where min and max dates can be manually input.
  8666. *
  8667. * @product highstock gantt
  8668. * @optionparent rangeSelector
  8669. */
  8670. rangeSelector: {
  8671. /**
  8672. * Whether to enable all buttons from the start. By default buttons are
  8673. * only enabled if the corresponding time range exists on the X axis,
  8674. * but enabling all buttons allows for dynamically loading different
  8675. * time ranges.
  8676. *
  8677. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  8678. * All buttons enabled
  8679. *
  8680. * @since 2.0.3
  8681. */
  8682. allButtonsEnabled: false,
  8683. /**
  8684. * An array of configuration objects for the buttons.
  8685. *
  8686. * Defaults to:
  8687. * ```js
  8688. * buttons: [{
  8689. * type: 'month',
  8690. * count: 1,
  8691. * text: '1m',
  8692. * title: 'View 1 month'
  8693. * }, {
  8694. * type: 'month',
  8695. * count: 3,
  8696. * text: '3m',
  8697. * title: 'View 3 months'
  8698. * }, {
  8699. * type: 'month',
  8700. * count: 6,
  8701. * text: '6m',
  8702. * title: 'View 6 months'
  8703. * }, {
  8704. * type: 'ytd',
  8705. * text: 'YTD',
  8706. * title: 'View year to date'
  8707. * }, {
  8708. * type: 'year',
  8709. * count: 1,
  8710. * text: '1y',
  8711. * title: 'View 1 year'
  8712. * }, {
  8713. * type: 'all',
  8714. * text: 'All',
  8715. * title: 'View all'
  8716. * }]
  8717. * ```
  8718. *
  8719. * @sample {highstock} stock/rangeselector/datagrouping/
  8720. * Data grouping by buttons
  8721. *
  8722. * @type {Array<*>}
  8723. */
  8724. buttons: void 0,
  8725. /**
  8726. * How many units of the defined type the button should span. If `type`
  8727. * is "month" and `count` is 3, the button spans three months.
  8728. *
  8729. * @type {number}
  8730. * @default 1
  8731. * @apioption rangeSelector.buttons.count
  8732. */
  8733. /**
  8734. * Fires when clicking on the rangeSelector button. One parameter,
  8735. * event, is passed to the function, containing common event
  8736. * information.
  8737. *
  8738. * ```js
  8739. * click: function(e) {
  8740. * console.log(this);
  8741. * }
  8742. * ```
  8743. *
  8744. * Return false to stop default button's click action.
  8745. *
  8746. * @sample {highstock} stock/rangeselector/button-click/
  8747. * Click event on the button
  8748. *
  8749. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  8750. * @apioption rangeSelector.buttons.events.click
  8751. */
  8752. /**
  8753. * Additional range (in milliseconds) added to the end of the calculated
  8754. * time span.
  8755. *
  8756. * @sample {highstock} stock/rangeselector/min-max-offsets/
  8757. * Button offsets
  8758. *
  8759. * @type {number}
  8760. * @default 0
  8761. * @since 6.0.0
  8762. * @apioption rangeSelector.buttons.offsetMax
  8763. */
  8764. /**
  8765. * Additional range (in milliseconds) added to the start of the
  8766. * calculated time span.
  8767. *
  8768. * @sample {highstock} stock/rangeselector/min-max-offsets/
  8769. * Button offsets
  8770. *
  8771. * @type {number}
  8772. * @default 0
  8773. * @since 6.0.0
  8774. * @apioption rangeSelector.buttons.offsetMin
  8775. */
  8776. /**
  8777. * When buttons apply dataGrouping on a series, by default zooming
  8778. * in/out will deselect buttons and unset dataGrouping. Enable this
  8779. * option to keep buttons selected when extremes change.
  8780. *
  8781. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  8782. * Different preserveDataGrouping settings
  8783. *
  8784. * @type {boolean}
  8785. * @default false
  8786. * @since 6.1.2
  8787. * @apioption rangeSelector.buttons.preserveDataGrouping
  8788. */
  8789. /**
  8790. * A custom data grouping object for each button.
  8791. *
  8792. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  8793. *
  8794. * @sample {highstock} stock/rangeselector/datagrouping/
  8795. * Data grouping by range selector buttons
  8796. *
  8797. * @type {*}
  8798. * @extends plotOptions.series.dataGrouping
  8799. * @apioption rangeSelector.buttons.dataGrouping
  8800. */
  8801. /**
  8802. * The text for the button itself.
  8803. *
  8804. * @type {string}
  8805. * @apioption rangeSelector.buttons.text
  8806. */
  8807. /**
  8808. * Explanation for the button, shown as a tooltip on hover, and used by
  8809. * assistive technology.
  8810. *
  8811. * @type {string}
  8812. * @apioption rangeSelector.buttons.title
  8813. */
  8814. /**
  8815. * Defined the time span for the button. Can be one of `millisecond`,
  8816. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  8817. * and `all`.
  8818. *
  8819. * @type {Highcharts.RangeSelectorButtonTypeValue}
  8820. * @apioption rangeSelector.buttons.type
  8821. */
  8822. /**
  8823. * The space in pixels between the buttons in the range selector.
  8824. */
  8825. buttonSpacing: 5,
  8826. /**
  8827. * Whether to collapse the range selector buttons into a dropdown when
  8828. * there is not enough room to show everything in a single row, instead
  8829. * of dividing the range selector into multiple rows.
  8830. * Can be one of the following:
  8831. * - `always`: Always collapse
  8832. * - `responsive`: Only collapse when there is not enough room
  8833. * - `never`: Never collapse
  8834. *
  8835. * @sample {highstock} stock/rangeselector/dropdown/
  8836. * Dropdown option
  8837. *
  8838. * @validvalue ["always", "responsive", "never"]
  8839. * @since 9.0.0
  8840. */
  8841. dropdown: 'responsive',
  8842. /**
  8843. * Enable or disable the range selector. Default to `true` for stock
  8844. * charts, using the `stockChart` factory.
  8845. *
  8846. * @sample {highstock} stock/rangeselector/enabled/
  8847. * Disable the range selector
  8848. *
  8849. * @type {boolean|undefined}
  8850. * @default {highstock} true
  8851. */
  8852. enabled: void 0,
  8853. /**
  8854. * The vertical alignment of the rangeselector box. Allowed properties
  8855. * are `top`, `middle`, `bottom`.
  8856. *
  8857. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  8858. * Middle
  8859. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  8860. * Bottom
  8861. *
  8862. * @type {Highcharts.VerticalAlignValue}
  8863. * @since 6.0.0
  8864. */
  8865. verticalAlign: 'top',
  8866. /**
  8867. * A collection of attributes for the buttons. The object takes SVG
  8868. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  8869. * a collection of CSS properties for the text.
  8870. *
  8871. * The object can also be extended with states, so you can set
  8872. * presentational options for `hover`, `select` or `disabled` button
  8873. * states.
  8874. *
  8875. * CSS styles for the text label.
  8876. *
  8877. * In styled mode, the buttons are styled by the
  8878. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  8879. * different states.
  8880. *
  8881. * @sample {highstock} stock/rangeselector/styling/
  8882. * Styling the buttons and inputs
  8883. *
  8884. * @type {Highcharts.SVGAttributes}
  8885. */
  8886. buttonTheme: {
  8887. /** @ignore */
  8888. width: 28,
  8889. /** @ignore */
  8890. height: 18,
  8891. /** @ignore */
  8892. padding: 2,
  8893. /** @ignore */
  8894. zIndex: 7 // #484, #852
  8895. },
  8896. /**
  8897. * When the rangeselector is floating, the plot area does not reserve
  8898. * space for it. This opens for positioning anywhere on the chart.
  8899. *
  8900. * @sample {highstock} stock/rangeselector/floating/
  8901. * Placing the range selector between the plot area and the
  8902. * navigator
  8903. *
  8904. * @since 6.0.0
  8905. */
  8906. floating: false,
  8907. /**
  8908. * The x offset of the range selector relative to its horizontal
  8909. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  8910. *
  8911. * @since 6.0.0
  8912. */
  8913. x: 0,
  8914. /**
  8915. * The y offset of the range selector relative to its horizontal
  8916. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  8917. *
  8918. * @since 6.0.0
  8919. */
  8920. y: 0,
  8921. /**
  8922. * Deprecated. The height of the range selector. Currently it is
  8923. * calculated dynamically.
  8924. *
  8925. * @deprecated
  8926. * @type {number|undefined}
  8927. * @since 2.1.9
  8928. */
  8929. height: void 0,
  8930. /**
  8931. * The border color of the date input boxes.
  8932. *
  8933. * @sample {highstock} stock/rangeselector/styling/
  8934. * Styling the buttons and inputs
  8935. *
  8936. * @type {Highcharts.ColorString}
  8937. * @since 1.3.7
  8938. */
  8939. inputBoxBorderColor: 'none',
  8940. /**
  8941. * The pixel height of the date input boxes.
  8942. *
  8943. * @sample {highstock} stock/rangeselector/styling/
  8944. * Styling the buttons and inputs
  8945. *
  8946. * @since 1.3.7
  8947. */
  8948. inputBoxHeight: 17,
  8949. /**
  8950. * The pixel width of the date input boxes. When `undefined`, the width
  8951. * is fitted to the rendered content.
  8952. *
  8953. * @sample {highstock} stock/rangeselector/styling/
  8954. * Styling the buttons and inputs
  8955. *
  8956. * @type {number|undefined}
  8957. * @since 1.3.7
  8958. */
  8959. inputBoxWidth: void 0,
  8960. /**
  8961. * The date format in the input boxes when not selected for editing.
  8962. * Defaults to `%b %e, %Y`.
  8963. *
  8964. * This is used to determine which type of input to show,
  8965. * `datetime-local`, `date` or `time` and falling back to `text` when
  8966. * the browser does not support the input type or the format contains
  8967. * milliseconds.
  8968. *
  8969. * @sample {highstock} stock/rangeselector/input-type/
  8970. * Input types
  8971. * @sample {highstock} stock/rangeselector/input-format/
  8972. * Milliseconds in the range selector
  8973. *
  8974. */
  8975. inputDateFormat: '%b %e, %Y',
  8976. /**
  8977. * A custom callback function to parse values entered in the input boxes
  8978. * and return a valid JavaScript time as milliseconds since 1970.
  8979. * The first argument passed is a value to parse,
  8980. * second is a boolean indicating use of the UTC time.
  8981. *
  8982. * This will only get called for inputs of type `text`. Since v8.2.3,
  8983. * the input type is dynamically determined based on the granularity
  8984. * of the `inputDateFormat` and the browser support.
  8985. *
  8986. * @sample {highstock} stock/rangeselector/input-format/
  8987. * Milliseconds in the range selector
  8988. *
  8989. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  8990. * @since 1.3.3
  8991. */
  8992. inputDateParser: void 0,
  8993. /**
  8994. * The date format in the input boxes when they are selected for
  8995. * editing. This must be a format that is recognized by JavaScript
  8996. * Date.parse.
  8997. *
  8998. * This will only be used for inputs of type `text`. Since v8.2.3,
  8999. * the input type is dynamically determined based on the granularity
  9000. * of the `inputDateFormat` and the browser support.
  9001. *
  9002. * @sample {highstock} stock/rangeselector/input-format/
  9003. * Milliseconds in the range selector
  9004. *
  9005. */
  9006. inputEditDateFormat: '%Y-%m-%d',
  9007. /**
  9008. * Enable or disable the date input boxes.
  9009. */
  9010. inputEnabled: true,
  9011. /**
  9012. * Positioning for the input boxes. Allowed properties are `align`,
  9013. * `x` and `y`.
  9014. *
  9015. * @since 1.2.4
  9016. */
  9017. inputPosition: {
  9018. /**
  9019. * The alignment of the input box. Allowed properties are `left`,
  9020. * `center`, `right`.
  9021. *
  9022. * @sample {highstock} stock/rangeselector/input-button-position/
  9023. * Alignment
  9024. *
  9025. * @type {Highcharts.AlignValue}
  9026. * @since 6.0.0
  9027. */
  9028. align: 'right',
  9029. /**
  9030. * X offset of the input row.
  9031. */
  9032. x: 0,
  9033. /**
  9034. * Y offset of the input row.
  9035. */
  9036. y: 0
  9037. },
  9038. /**
  9039. * The space in pixels between the labels and the date input boxes in
  9040. * the range selector.
  9041. *
  9042. * @since 9.0.0
  9043. */
  9044. inputSpacing: 5,
  9045. /**
  9046. * The index of the button to appear pre-selected.
  9047. *
  9048. * @type {number}
  9049. */
  9050. selected: void 0,
  9051. /**
  9052. * Positioning for the button row.
  9053. *
  9054. * @since 1.2.4
  9055. */
  9056. buttonPosition: {
  9057. /**
  9058. * The alignment of the input box. Allowed properties are `left`,
  9059. * `center`, `right`.
  9060. *
  9061. * @sample {highstock} stock/rangeselector/input-button-position/
  9062. * Alignment
  9063. *
  9064. * @type {Highcharts.AlignValue}
  9065. * @since 6.0.0
  9066. */
  9067. align: 'left',
  9068. /**
  9069. * X offset of the button row.
  9070. */
  9071. x: 0,
  9072. /**
  9073. * Y offset of the button row.
  9074. */
  9075. y: 0
  9076. },
  9077. /**
  9078. * CSS for the HTML inputs in the range selector.
  9079. *
  9080. * In styled mode, the inputs are styled by the
  9081. * `.highcharts-range-input text` rule in SVG mode, and
  9082. * `input.highcharts-range-selector` when active.
  9083. *
  9084. * @sample {highstock} stock/rangeselector/styling/
  9085. * Styling the buttons and inputs
  9086. *
  9087. * @type {Highcharts.CSSObject}
  9088. * @apioption rangeSelector.inputStyle
  9089. */
  9090. inputStyle: {
  9091. /** @ignore */
  9092. color: palette.highlightColor80,
  9093. /** @ignore */
  9094. cursor: 'pointer'
  9095. },
  9096. /**
  9097. * CSS styles for the labels - the Zoom, From and To texts.
  9098. *
  9099. * In styled mode, the labels are styled by the
  9100. * `.highcharts-range-label` class.
  9101. *
  9102. * @sample {highstock} stock/rangeselector/styling/
  9103. * Styling the buttons and inputs
  9104. *
  9105. * @type {Highcharts.CSSObject}
  9106. */
  9107. labelStyle: {
  9108. /** @ignore */
  9109. color: palette.neutralColor60
  9110. }
  9111. }
  9112. });
  9113. extend(defaultOptions.lang,
  9114. /**
  9115. * Language object. The language object is global and it can't be set
  9116. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  9117. * set it before any chart is initialized.
  9118. *
  9119. * ```js
  9120. * Highcharts.setOptions({
  9121. * lang: {
  9122. * months: [
  9123. * 'Janvier', 'Février', 'Mars', 'Avril',
  9124. * 'Mai', 'Juin', 'Juillet', 'Août',
  9125. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  9126. * ],
  9127. * weekdays: [
  9128. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  9129. * 'Jeudi', 'Vendredi', 'Samedi'
  9130. * ]
  9131. * }
  9132. * });
  9133. * ```
  9134. *
  9135. * @optionparent lang
  9136. */
  9137. {
  9138. /**
  9139. * The text for the label for the range selector buttons.
  9140. *
  9141. * @product highstock gantt
  9142. */
  9143. rangeSelectorZoom: 'Zoom',
  9144. /**
  9145. * The text for the label for the "from" input box in the range
  9146. * selector. Since v9.0, this string is empty as the label is not
  9147. * rendered by default.
  9148. *
  9149. * @product highstock gantt
  9150. */
  9151. rangeSelectorFrom: '',
  9152. /**
  9153. * The text for the label for the "to" input box in the range selector.
  9154. *
  9155. * @product highstock gantt
  9156. */
  9157. rangeSelectorTo: '→'
  9158. });
  9159. /* eslint-disable no-invalid-this, valid-jsdoc */
  9160. /**
  9161. * The range selector.
  9162. *
  9163. * @private
  9164. * @class
  9165. * @name Highcharts.RangeSelector
  9166. * @param {Highcharts.Chart} chart
  9167. */
  9168. var RangeSelector = /** @class */ (function () {
  9169. function RangeSelector(chart) {
  9170. /* *
  9171. *
  9172. * Properties
  9173. *
  9174. * */
  9175. this.buttons = void 0;
  9176. this.buttonOptions = RangeSelector.prototype.defaultButtons;
  9177. this.initialButtonGroupWidth = 0;
  9178. this.options = void 0;
  9179. this.chart = chart;
  9180. // Run RangeSelector
  9181. this.init(chart);
  9182. }
  9183. /**
  9184. * The method to run when one of the buttons in the range selectors is
  9185. * clicked
  9186. *
  9187. * @private
  9188. * @function Highcharts.RangeSelector#clickButton
  9189. * @param {number} i
  9190. * The index of the button
  9191. * @param {boolean} [redraw]
  9192. * @return {void}
  9193. */
  9194. RangeSelector.prototype.clickButton = function (i, redraw) {
  9195. var rangeSelector = this,
  9196. chart = rangeSelector.chart,
  9197. rangeOptions = rangeSelector.buttonOptions[i],
  9198. baseAxis = chart.xAxis[0],
  9199. unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {},
  9200. dataMin = unionExtremes.dataMin,
  9201. dataMax = unionExtremes.dataMax,
  9202. newMin,
  9203. newMax = baseAxis && Math.round(Math.min(baseAxis.max,
  9204. pick(dataMax,
  9205. baseAxis.max))), // #1568
  9206. type = rangeOptions.type,
  9207. baseXAxisOptions,
  9208. range = rangeOptions._range,
  9209. rangeMin,
  9210. minSetting,
  9211. rangeSetting,
  9212. ctx,
  9213. ytdExtremes,
  9214. dataGrouping = rangeOptions.dataGrouping;
  9215. // chart has no data, base series is removed
  9216. if (dataMin === null || dataMax === null) {
  9217. return;
  9218. }
  9219. // Set the fixed range before range is altered
  9220. chart.fixedRange = range;
  9221. rangeSelector.setSelected(i);
  9222. // Apply dataGrouping associated to button
  9223. if (dataGrouping) {
  9224. this.forcedDataGrouping = true;
  9225. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  9226. this.frozenStates = rangeOptions.preserveDataGrouping;
  9227. }
  9228. // Apply range
  9229. if (type === 'month' || type === 'year') {
  9230. if (!baseAxis) {
  9231. // This is set to the user options and picked up later when the
  9232. // axis is instantiated so that we know the min and max.
  9233. range = rangeOptions;
  9234. }
  9235. else {
  9236. ctx = {
  9237. range: rangeOptions,
  9238. max: newMax,
  9239. chart: chart,
  9240. dataMin: dataMin,
  9241. dataMax: dataMax
  9242. };
  9243. newMin = baseAxis.minFromRange.call(ctx);
  9244. if (isNumber(ctx.newMax)) {
  9245. newMax = ctx.newMax;
  9246. }
  9247. }
  9248. // Fixed times like minutes, hours, days
  9249. }
  9250. else if (range) {
  9251. newMin = Math.max(newMax - range, dataMin);
  9252. newMax = Math.min(newMin + range, dataMax);
  9253. }
  9254. else if (type === 'ytd') {
  9255. // On user clicks on the buttons, or a delayed action running from
  9256. // the beforeRender event (below), the baseAxis is defined.
  9257. if (baseAxis) {
  9258. // When "ytd" is the pre-selected button for the initial view,
  9259. // its calculation is delayed and rerun in the beforeRender
  9260. // event (below). When the series are initialized, but before
  9261. // the chart is rendered, we have access to the xData array
  9262. // (#942).
  9263. if (typeof dataMax === 'undefined') {
  9264. dataMin = Number.MAX_VALUE;
  9265. dataMax = Number.MIN_VALUE;
  9266. chart.series.forEach(function (series) {
  9267. // reassign it to the last item
  9268. var xData = series.xData;
  9269. dataMin = Math.min(xData[0], dataMin);
  9270. dataMax = Math.max(xData[xData.length - 1], dataMax);
  9271. });
  9272. redraw = false;
  9273. }
  9274. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  9275. newMin = rangeMin = ytdExtremes.min;
  9276. newMax = ytdExtremes.max;
  9277. // "ytd" is pre-selected. We don't yet have access to processed
  9278. // point and extremes data (things like pointStart and pointInterval
  9279. // are missing), so we delay the process (#942)
  9280. }
  9281. else {
  9282. rangeSelector.deferredYTDClick = i;
  9283. return;
  9284. }
  9285. }
  9286. else if (type === 'all' && baseAxis) {
  9287. newMin = dataMin;
  9288. newMax = dataMax;
  9289. }
  9290. if (defined(newMin)) {
  9291. newMin += rangeOptions._offsetMin;
  9292. }
  9293. if (defined(newMax)) {
  9294. newMax += rangeOptions._offsetMax;
  9295. }
  9296. if (this.dropdown) {
  9297. this.dropdown.selectedIndex = i + 1;
  9298. }
  9299. // Update the chart
  9300. if (!baseAxis) {
  9301. // Axis not yet instanciated. Temporarily set min and range
  9302. // options and remove them on chart load (#4317).
  9303. baseXAxisOptions = splat(chart.options.xAxis)[0];
  9304. rangeSetting = baseXAxisOptions.range;
  9305. baseXAxisOptions.range = range;
  9306. minSetting = baseXAxisOptions.min;
  9307. baseXAxisOptions.min = rangeMin;
  9308. addEvent(chart, 'load', function resetMinAndRange() {
  9309. baseXAxisOptions.range = rangeSetting;
  9310. baseXAxisOptions.min = minSetting;
  9311. });
  9312. }
  9313. else {
  9314. // Existing axis object. Set extremes after render time.
  9315. baseAxis.setExtremes(newMin, newMax, pick(redraw, true), void 0, // auto animation
  9316. {
  9317. trigger: 'rangeSelectorButton',
  9318. rangeSelectorButton: rangeOptions
  9319. });
  9320. }
  9321. fireEvent(this, 'afterBtnClick');
  9322. };
  9323. /**
  9324. * Set the selected option. This method only sets the internal flag, it
  9325. * doesn't update the buttons or the actual zoomed range.
  9326. *
  9327. * @private
  9328. * @function Highcharts.RangeSelector#setSelected
  9329. * @param {number} [selected]
  9330. * @return {void}
  9331. */
  9332. RangeSelector.prototype.setSelected = function (selected) {
  9333. this.selected = this.options.selected = selected;
  9334. };
  9335. /**
  9336. * Initialize the range selector
  9337. *
  9338. * @private
  9339. * @function Highcharts.RangeSelector#init
  9340. * @param {Highcharts.Chart} chart
  9341. * @return {void}
  9342. */
  9343. RangeSelector.prototype.init = function (chart) {
  9344. var rangeSelector = this,
  9345. options = chart.options.rangeSelector,
  9346. buttonOptions = options.buttons || rangeSelector.defaultButtons.slice(),
  9347. selectedOption = options.selected,
  9348. blurInputs = function () {
  9349. var minInput = rangeSelector.minInput,
  9350. maxInput = rangeSelector.maxInput;
  9351. // #3274 in some case blur is not defined
  9352. if (minInput && minInput.blur) {
  9353. fireEvent(minInput, 'blur');
  9354. }
  9355. if (maxInput && maxInput.blur) {
  9356. fireEvent(maxInput, 'blur');
  9357. }
  9358. };
  9359. rangeSelector.chart = chart;
  9360. rangeSelector.options = options;
  9361. rangeSelector.buttons = [];
  9362. rangeSelector.buttonOptions = buttonOptions;
  9363. this.eventsToUnbind = [];
  9364. this.eventsToUnbind.push(addEvent(chart.container, 'mousedown', blurInputs));
  9365. this.eventsToUnbind.push(addEvent(chart, 'resize', blurInputs));
  9366. // Extend the buttonOptions with actual range
  9367. buttonOptions.forEach(rangeSelector.computeButtonRange);
  9368. // zoomed range based on a pre-selected button index
  9369. if (typeof selectedOption !== 'undefined' &&
  9370. buttonOptions[selectedOption]) {
  9371. this.clickButton(selectedOption, false);
  9372. }
  9373. this.eventsToUnbind.push(addEvent(chart, 'load', function () {
  9374. // If a data grouping is applied to the current button, release it
  9375. // when extremes change
  9376. if (chart.xAxis && chart.xAxis[0]) {
  9377. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  9378. if (this.max - this.min !==
  9379. chart.fixedRange &&
  9380. e.trigger !== 'rangeSelectorButton' &&
  9381. e.trigger !== 'updatedData' &&
  9382. rangeSelector.forcedDataGrouping &&
  9383. !rangeSelector.frozenStates) {
  9384. this.setDataGrouping(false, false);
  9385. }
  9386. });
  9387. }
  9388. }));
  9389. };
  9390. /**
  9391. * Dynamically update the range selector buttons after a new range has been
  9392. * set
  9393. *
  9394. * @private
  9395. * @function Highcharts.RangeSelector#updateButtonStates
  9396. * @return {void}
  9397. */
  9398. RangeSelector.prototype.updateButtonStates = function () {
  9399. var rangeSelector = this,
  9400. chart = this.chart,
  9401. dropdown = this.dropdown,
  9402. baseAxis = chart.xAxis[0],
  9403. actualRange = Math.round(baseAxis.max - baseAxis.min),
  9404. hasNoData = !baseAxis.hasVisibleSeries,
  9405. day = 24 * 36e5, // A single day in milliseconds
  9406. unionExtremes = (chart.scroller &&
  9407. chart.scroller.getUnionExtremes()) || baseAxis,
  9408. dataMin = unionExtremes.dataMin,
  9409. dataMax = unionExtremes.dataMax,
  9410. ytdExtremes = rangeSelector.getYTDExtremes(dataMax,
  9411. dataMin,
  9412. chart.time.useUTC),
  9413. ytdMin = ytdExtremes.min,
  9414. ytdMax = ytdExtremes.max,
  9415. selected = rangeSelector.selected,
  9416. selectedExists = isNumber(selected),
  9417. allButtonsEnabled = rangeSelector.options.allButtonsEnabled,
  9418. buttons = rangeSelector.buttons;
  9419. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  9420. var range = rangeOptions._range,
  9421. type = rangeOptions.type,
  9422. count = rangeOptions.count || 1,
  9423. button = buttons[i],
  9424. state = 0,
  9425. disable,
  9426. select,
  9427. offsetRange = rangeOptions._offsetMax -
  9428. rangeOptions._offsetMin,
  9429. isSelected = i === selected,
  9430. // Disable buttons where the range exceeds what is allowed in
  9431. // the current view
  9432. isTooGreatRange = range >
  9433. dataMax - dataMin,
  9434. // Disable buttons where the range is smaller than the minimum
  9435. // range
  9436. isTooSmallRange = range < baseAxis.minRange,
  9437. // Do not select the YTD button if not explicitly told so
  9438. isYTDButNotSelected = false,
  9439. // Disable the All button if we're already showing all
  9440. isAllButAlreadyShowingAll = false,
  9441. isSameRange = range === actualRange;
  9442. // Months and years have a variable range so we check the extremes
  9443. if ((type === 'month' || type === 'year') &&
  9444. (actualRange + 36e5 >=
  9445. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  9446. (actualRange - 36e5 <=
  9447. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  9448. isSameRange = true;
  9449. }
  9450. else if (type === 'ytd') {
  9451. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  9452. isYTDButNotSelected = !isSelected;
  9453. }
  9454. else if (type === 'all') {
  9455. isSameRange = (baseAxis.max - baseAxis.min >=
  9456. dataMax - dataMin);
  9457. isAllButAlreadyShowingAll = (!isSelected &&
  9458. selectedExists &&
  9459. isSameRange);
  9460. }
  9461. // The new zoom area happens to match the range for a button - mark
  9462. // it selected. This happens when scrolling across an ordinal gap.
  9463. // It can be seen in the intraday demos when selecting 1h and scroll
  9464. // across the night gap.
  9465. disable = (!allButtonsEnabled &&
  9466. (isTooGreatRange ||
  9467. isTooSmallRange ||
  9468. isAllButAlreadyShowingAll ||
  9469. hasNoData));
  9470. select = ((isSelected && isSameRange) ||
  9471. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  9472. (isSelected && rangeSelector.frozenStates));
  9473. if (disable) {
  9474. state = 3;
  9475. }
  9476. else if (select) {
  9477. selectedExists = true; // Only one button can be selected
  9478. state = 2;
  9479. }
  9480. // If state has changed, update the button
  9481. if (button.state !== state) {
  9482. button.setState(state);
  9483. if (dropdown) {
  9484. dropdown.options[i + 1].disabled = disable;
  9485. if (state === 2) {
  9486. dropdown.selectedIndex = i + 1;
  9487. }
  9488. }
  9489. // Reset (#9209)
  9490. if (state === 0 && selected === i) {
  9491. rangeSelector.setSelected();
  9492. }
  9493. }
  9494. });
  9495. };
  9496. /**
  9497. * Compute and cache the range for an individual button
  9498. *
  9499. * @private
  9500. * @function Highcharts.RangeSelector#computeButtonRange
  9501. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  9502. * @return {void}
  9503. */
  9504. RangeSelector.prototype.computeButtonRange = function (rangeOptions) {
  9505. var type = rangeOptions.type,
  9506. count = rangeOptions.count || 1,
  9507. // these time intervals have a fixed number of milliseconds, as
  9508. // opposed to month, ytd and year
  9509. fixedTimes = {
  9510. millisecond: 1,
  9511. second: 1000,
  9512. minute: 60 * 1000,
  9513. hour: 3600 * 1000,
  9514. day: 24 * 3600 * 1000,
  9515. week: 7 * 24 * 3600 * 1000
  9516. };
  9517. // Store the range on the button object
  9518. if (fixedTimes[type]) {
  9519. rangeOptions._range = fixedTimes[type] * count;
  9520. }
  9521. else if (type === 'month' || type === 'year') {
  9522. rangeOptions._range = {
  9523. month: 30,
  9524. year: 365
  9525. }[type] * 24 * 36e5 * count;
  9526. }
  9527. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  9528. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  9529. rangeOptions._range +=
  9530. rangeOptions._offsetMax - rangeOptions._offsetMin;
  9531. };
  9532. /**
  9533. * Get the unix timestamp of a HTML input for the dates
  9534. *
  9535. * @private
  9536. * @function Highcharts.RangeSelector#getInputValue
  9537. * @param {string} name
  9538. * @return {number}
  9539. */
  9540. RangeSelector.prototype.getInputValue = function (name) {
  9541. var input = name === 'min' ? this.minInput : this.maxInput;
  9542. var options = this.chart.options.rangeSelector;
  9543. var time = this.chart.time;
  9544. if (input) {
  9545. return ((input.type === 'text' && options.inputDateParser) ||
  9546. this.defaultInputDateParser)(input.value, time.useUTC, time);
  9547. }
  9548. return 0;
  9549. };
  9550. /**
  9551. * Set the internal and displayed value of a HTML input for the dates
  9552. *
  9553. * @private
  9554. * @function Highcharts.RangeSelector#setInputValue
  9555. * @param {string} name
  9556. * @param {number} [inputTime]
  9557. * @return {void}
  9558. */
  9559. RangeSelector.prototype.setInputValue = function (name, inputTime) {
  9560. var options = this.options, time = this.chart.time, input = name === 'min' ? this.minInput : this.maxInput, dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  9561. if (input) {
  9562. var hcTimeAttr = input.getAttribute('data-hc-time');
  9563. var updatedTime = defined(hcTimeAttr) ? Number(hcTimeAttr) : void 0;
  9564. if (defined(inputTime)) {
  9565. var previousTime = updatedTime;
  9566. if (defined(previousTime)) {
  9567. input.setAttribute('data-hc-time-previous', previousTime);
  9568. }
  9569. input.setAttribute('data-hc-time', inputTime);
  9570. updatedTime = inputTime;
  9571. }
  9572. input.value = time.dateFormat(this.inputTypeFormats[input.type] || options.inputEditDateFormat, updatedTime);
  9573. if (dateBox) {
  9574. dateBox.attr({
  9575. text: time.dateFormat(options.inputDateFormat, updatedTime)
  9576. });
  9577. }
  9578. }
  9579. };
  9580. /**
  9581. * Set the min and max value of a HTML input for the dates
  9582. *
  9583. * @private
  9584. * @function Highcharts.RangeSelector#setInputExtremes
  9585. * @param {string} name
  9586. * @param {number} min
  9587. * @param {number} max
  9588. * @return {void}
  9589. */
  9590. RangeSelector.prototype.setInputExtremes = function (name, min, max) {
  9591. var input = name === 'min' ? this.minInput : this.maxInput;
  9592. if (input) {
  9593. var format = this.inputTypeFormats[input.type];
  9594. var time = this.chart.time;
  9595. if (format) {
  9596. var newMin = time.dateFormat(format,
  9597. min);
  9598. if (input.min !== newMin) {
  9599. input.min = newMin;
  9600. }
  9601. var newMax = time.dateFormat(format,
  9602. max);
  9603. if (input.max !== newMax) {
  9604. input.max = newMax;
  9605. }
  9606. }
  9607. }
  9608. };
  9609. /**
  9610. * @private
  9611. * @function Highcharts.RangeSelector#showInput
  9612. * @param {string} name
  9613. * @return {void}
  9614. */
  9615. RangeSelector.prototype.showInput = function (name) {
  9616. var dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  9617. var input = name === 'min' ? this.minInput : this.maxInput;
  9618. if (input && dateBox && this.inputGroup) {
  9619. var isTextInput = input.type === 'text';
  9620. var _a = this.inputGroup,
  9621. translateX = _a.translateX,
  9622. translateY = _a.translateY;
  9623. var inputBoxWidth = this.options.inputBoxWidth;
  9624. css(input, {
  9625. width: isTextInput ? ((dateBox.width + (inputBoxWidth ? -2 : 20)) + 'px') : 'auto',
  9626. height: isTextInput ? ((dateBox.height - 2) + 'px') : 'auto',
  9627. border: '2px solid silver'
  9628. });
  9629. if (isTextInput && inputBoxWidth) {
  9630. css(input, {
  9631. left: (translateX + dateBox.x) + 'px',
  9632. top: translateY + 'px'
  9633. });
  9634. // Inputs of types date, time or datetime-local should be centered
  9635. // on top of the dateBox
  9636. }
  9637. else {
  9638. css(input, {
  9639. left: Math.min(Math.round(dateBox.x +
  9640. translateX -
  9641. (input.offsetWidth - dateBox.width) / 2), this.chart.chartWidth - input.offsetWidth) + 'px',
  9642. top: (translateY - 1 - (input.offsetHeight - dateBox.height) / 2) + 'px'
  9643. });
  9644. }
  9645. }
  9646. };
  9647. /**
  9648. * @private
  9649. * @function Highcharts.RangeSelector#hideInput
  9650. * @param {string} name
  9651. * @return {void}
  9652. */
  9653. RangeSelector.prototype.hideInput = function (name) {
  9654. var input = name === 'min' ? this.minInput : this.maxInput;
  9655. if (input) {
  9656. css(input, {
  9657. top: '-9999em',
  9658. border: 0,
  9659. width: '1px',
  9660. height: '1px'
  9661. });
  9662. }
  9663. };
  9664. /**
  9665. * @private
  9666. * @function Highcharts.RangeSelector#defaultInputDateParser
  9667. */
  9668. RangeSelector.prototype.defaultInputDateParser = function (inputDate, useUTC, time) {
  9669. var hasTimezone = function (str) {
  9670. return str.length > 6 &&
  9671. (str.lastIndexOf('-') === str.length - 6 ||
  9672. str.lastIndexOf('+') === str.length - 6);
  9673. };
  9674. var input = inputDate.split('/').join('-').split(' ').join('T');
  9675. if (input.indexOf('T') === -1) {
  9676. input += 'T00:00';
  9677. }
  9678. if (useUTC) {
  9679. input += 'Z';
  9680. }
  9681. else if (H.isSafari && !hasTimezone(input)) {
  9682. var offset = new Date(input).getTimezoneOffset() / 60;
  9683. input += offset <= 0 ? "+" + pad(-offset) + ":00" : "-" + pad(offset) + ":00";
  9684. }
  9685. var date = Date.parse(input);
  9686. // If the value isn't parsed directly to a value by the
  9687. // browser's Date.parse method, like YYYY-MM-DD in IE8, try
  9688. // parsing it a different way
  9689. if (!isNumber(date)) {
  9690. var parts = inputDate.split('-');
  9691. date = Date.UTC(pInt(parts[0]), pInt(parts[1]) - 1, pInt(parts[2]));
  9692. }
  9693. if (time && useUTC && isNumber(date)) {
  9694. date += time.getTimezoneOffset(date);
  9695. }
  9696. return date;
  9697. };
  9698. /**
  9699. * Draw either the 'from' or the 'to' HTML input box of the range selector
  9700. *
  9701. * @private
  9702. * @function Highcharts.RangeSelector#drawInput
  9703. * @param {string} name
  9704. * @return {RangeSelectorInputElements}
  9705. */
  9706. RangeSelector.prototype.drawInput = function (name) {
  9707. var _a = this,
  9708. chart = _a.chart,
  9709. div = _a.div,
  9710. inputGroup = _a.inputGroup;
  9711. var rangeSelector = this,
  9712. chartStyle = chart.renderer.style || {},
  9713. renderer = chart.renderer,
  9714. options = chart.options.rangeSelector,
  9715. lang = defaultOptions.lang,
  9716. isMin = name === 'min';
  9717. /**
  9718. * @private
  9719. */
  9720. function updateExtremes() {
  9721. var value = rangeSelector.getInputValue(name),
  9722. chartAxis = chart.xAxis[0],
  9723. dataAxis = chart.scroller && chart.scroller.xAxis ?
  9724. chart.scroller.xAxis :
  9725. chartAxis,
  9726. dataMin = dataAxis.dataMin,
  9727. dataMax = dataAxis.dataMax;
  9728. var maxInput = rangeSelector.maxInput,
  9729. minInput = rangeSelector.minInput;
  9730. if (value !== Number(input.getAttribute('data-hc-time-previous')) &&
  9731. isNumber(value)) {
  9732. input.setAttribute('data-hc-time-previous', value);
  9733. // Validate the extremes. If it goes beyound the data min or
  9734. // max, use the actual data extreme (#2438).
  9735. if (isMin && maxInput && isNumber(dataMin)) {
  9736. if (value > Number(maxInput.getAttribute('data-hc-time'))) {
  9737. value = void 0;
  9738. }
  9739. else if (value < dataMin) {
  9740. value = dataMin;
  9741. }
  9742. }
  9743. else if (minInput && isNumber(dataMax)) {
  9744. if (value < Number(minInput.getAttribute('data-hc-time'))) {
  9745. value = void 0;
  9746. }
  9747. else if (value > dataMax) {
  9748. value = dataMax;
  9749. }
  9750. }
  9751. // Set the extremes
  9752. if (typeof value !== 'undefined') { // @todo typof undefined
  9753. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  9754. }
  9755. }
  9756. }
  9757. // Create the text label
  9758. var text = lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'];
  9759. var label = renderer
  9760. .label(text, 0)
  9761. .addClass('highcharts-range-label')
  9762. .attr({
  9763. padding: text ? 2 : 0
  9764. })
  9765. .add(inputGroup);
  9766. // Create an SVG label that shows updated date ranges and and records
  9767. // click events that bring in the HTML input.
  9768. var dateBox = renderer
  9769. .label('', 0)
  9770. .addClass('highcharts-range-input')
  9771. .attr({
  9772. padding: 2,
  9773. width: options.inputBoxWidth,
  9774. height: options.inputBoxHeight,
  9775. 'text-align': 'center'
  9776. })
  9777. .on('click',
  9778. function () {
  9779. // If it is already focused, the onfocus event doesn't fire
  9780. // (#3713)
  9781. rangeSelector.showInput(name);
  9782. rangeSelector[name + 'Input'].focus();
  9783. });
  9784. if (!chart.styledMode) {
  9785. dateBox.attr({
  9786. stroke: options.inputBoxBorderColor,
  9787. 'stroke-width': 1
  9788. });
  9789. }
  9790. dateBox.add(inputGroup);
  9791. // Create the HTML input element. This is rendered as 1x1 pixel then set
  9792. // to the right size when focused.
  9793. var input = createElement('input', {
  9794. name: name,
  9795. className: 'highcharts-range-selector'
  9796. },
  9797. void 0,
  9798. div);
  9799. // #14788: Setting input.type to an unsupported type throws in IE, so
  9800. // we need to use setAttribute instead
  9801. input.setAttribute('type', preferredInputType(options.inputDateFormat || '%b %e, %Y'));
  9802. if (!chart.styledMode) {
  9803. // Styles
  9804. label.css(merge(chartStyle, options.labelStyle));
  9805. dateBox.css(merge({
  9806. color: palette.neutralColor80
  9807. }, chartStyle, options.inputStyle));
  9808. css(input, extend({
  9809. position: 'absolute',
  9810. border: 0,
  9811. boxShadow: '0 0 15px rgba(0,0,0,0.3)',
  9812. width: '1px',
  9813. height: '1px',
  9814. padding: 0,
  9815. textAlign: 'center',
  9816. fontSize: chartStyle.fontSize,
  9817. fontFamily: chartStyle.fontFamily,
  9818. top: '-9999em' // #4798
  9819. }, options.inputStyle));
  9820. }
  9821. // Blow up the input box
  9822. input.onfocus = function () {
  9823. rangeSelector.showInput(name);
  9824. };
  9825. // Hide away the input box
  9826. input.onblur = function () {
  9827. // update extermes only when inputs are active
  9828. if (input === H.doc.activeElement) { // Only when focused
  9829. // Update also when no `change` event is triggered, like when
  9830. // clicking inside the SVG (#4710)
  9831. updateExtremes();
  9832. }
  9833. // #10404 - move hide and blur outside focus
  9834. rangeSelector.hideInput(name);
  9835. rangeSelector.setInputValue(name);
  9836. input.blur(); // #4606
  9837. };
  9838. var keyDown = false;
  9839. // handle changes in the input boxes
  9840. input.onchange = function () {
  9841. // Update extremes and blur input when clicking date input calendar
  9842. if (!keyDown) {
  9843. updateExtremes();
  9844. rangeSelector.hideInput(name);
  9845. input.blur();
  9846. }
  9847. };
  9848. input.onkeypress = function (event) {
  9849. // IE does not fire onchange on enter
  9850. if (event.keyCode === 13) {
  9851. updateExtremes();
  9852. }
  9853. };
  9854. input.onkeydown = function (event) {
  9855. keyDown = true;
  9856. // Arrow keys
  9857. if (event.keyCode === 38 || event.keyCode === 40) {
  9858. updateExtremes();
  9859. }
  9860. };
  9861. input.onkeyup = function () {
  9862. keyDown = false;
  9863. };
  9864. return { dateBox: dateBox, input: input, label: label };
  9865. };
  9866. /**
  9867. * Get the position of the range selector buttons and inputs. This can be
  9868. * overridden from outside for custom positioning.
  9869. *
  9870. * @private
  9871. * @function Highcharts.RangeSelector#getPosition
  9872. *
  9873. * @return {Highcharts.Dictionary<number>}
  9874. */
  9875. RangeSelector.prototype.getPosition = function () {
  9876. var chart = this.chart,
  9877. options = chart.options.rangeSelector,
  9878. top = options.verticalAlign === 'top' ?
  9879. chart.plotTop - chart.axisOffset[0] :
  9880. 0; // set offset only for varticalAlign top
  9881. return {
  9882. buttonTop: top + options.buttonPosition.y,
  9883. inputTop: top + options.inputPosition.y - 10
  9884. };
  9885. };
  9886. /**
  9887. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  9888. * the current timestamp. Will choose dataMin if its value is higher than
  9889. * the timestamp for the start of current year.
  9890. *
  9891. * @private
  9892. * @function Highcharts.RangeSelector#getYTDExtremes
  9893. *
  9894. * @param {number} dataMax
  9895. *
  9896. * @param {number} dataMin
  9897. *
  9898. * @return {*}
  9899. * Returns min and max for the YTD
  9900. */
  9901. RangeSelector.prototype.getYTDExtremes = function (dataMax, dataMin, useUTC) {
  9902. var time = this.chart.time,
  9903. min,
  9904. now = new time.Date(dataMax),
  9905. year = time.get('FullYear',
  9906. now),
  9907. startOfYear = useUTC ?
  9908. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  9909. +new time.Date(year, 0, 1);
  9910. min = Math.max(dataMin, startOfYear);
  9911. var ts = now.getTime();
  9912. return {
  9913. max: Math.min(dataMax || ts, ts),
  9914. min: min
  9915. };
  9916. };
  9917. /**
  9918. * Render the range selector including the buttons and the inputs. The first
  9919. * time render is called, the elements are created and positioned. On
  9920. * subsequent calls, they are moved and updated.
  9921. *
  9922. * @private
  9923. * @function Highcharts.RangeSelector#render
  9924. * @param {number} [min]
  9925. * X axis minimum
  9926. * @param {number} [max]
  9927. * X axis maximum
  9928. * @return {void}
  9929. */
  9930. RangeSelector.prototype.render = function (min, max) {
  9931. var chart = this.chart,
  9932. renderer = chart.renderer,
  9933. container = chart.container,
  9934. chartOptions = chart.options,
  9935. options = chartOptions.rangeSelector,
  9936. // Place inputs above the container
  9937. inputsZIndex = pick(chartOptions.chart.style &&
  9938. chartOptions.chart.style.zIndex, 0) + 1,
  9939. inputEnabled = options.inputEnabled,
  9940. rendered = this.rendered;
  9941. if (options.enabled === false) {
  9942. return;
  9943. }
  9944. // create the elements
  9945. if (!rendered) {
  9946. this.group = renderer.g('range-selector-group')
  9947. .attr({
  9948. zIndex: 7
  9949. })
  9950. .add();
  9951. this.div = createElement('div', void 0, {
  9952. position: 'relative',
  9953. height: 0,
  9954. zIndex: inputsZIndex
  9955. });
  9956. if (this.buttonOptions.length) {
  9957. this.renderButtons();
  9958. }
  9959. // First create a wrapper outside the container in order to make
  9960. // the inputs work and make export correct
  9961. if (container.parentNode) {
  9962. container.parentNode.insertBefore(this.div, container);
  9963. }
  9964. if (inputEnabled) {
  9965. // Create the group to keep the inputs
  9966. this.inputGroup = renderer.g('input-group').add(this.group);
  9967. var minElems = this.drawInput('min');
  9968. this.minDateBox = minElems.dateBox;
  9969. this.minLabel = minElems.label;
  9970. this.minInput = minElems.input;
  9971. var maxElems = this.drawInput('max');
  9972. this.maxDateBox = maxElems.dateBox;
  9973. this.maxLabel = maxElems.label;
  9974. this.maxInput = maxElems.input;
  9975. }
  9976. }
  9977. if (inputEnabled) {
  9978. // Set or reset the input values
  9979. this.setInputValue('min', min);
  9980. this.setInputValue('max', max);
  9981. var unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || chart.xAxis[0] || {};
  9982. if (defined(unionExtremes.dataMin) && defined(unionExtremes.dataMax)) {
  9983. var minRange = chart.xAxis[0].minRange || 0;
  9984. this.setInputExtremes('min', unionExtremes.dataMin, Math.min(unionExtremes.dataMax, this.getInputValue('max')) - minRange);
  9985. this.setInputExtremes('max', Math.max(unionExtremes.dataMin, this.getInputValue('min')) + minRange, unionExtremes.dataMax);
  9986. }
  9987. // Reflow
  9988. if (this.inputGroup) {
  9989. var x_1 = 0;
  9990. [
  9991. this.minLabel,
  9992. this.minDateBox,
  9993. this.maxLabel,
  9994. this.maxDateBox
  9995. ].forEach(function (label) {
  9996. if (label) {
  9997. var width = label.getBBox().width;
  9998. if (width) {
  9999. label.attr({ x: x_1 });
  10000. x_1 += width + options.inputSpacing;
  10001. }
  10002. }
  10003. });
  10004. }
  10005. }
  10006. this.alignElements();
  10007. this.rendered = true;
  10008. };
  10009. /**
  10010. * Render the range buttons. This only runs the first time, later the
  10011. * positioning is laid out in alignElements.
  10012. *
  10013. * @private
  10014. * @function Highcharts.RangeSelector#renderButtons
  10015. * @return {void}
  10016. */
  10017. RangeSelector.prototype.renderButtons = function () {
  10018. var _this = this;
  10019. var _a = this,
  10020. buttons = _a.buttons,
  10021. chart = _a.chart,
  10022. options = _a.options;
  10023. var lang = defaultOptions.lang;
  10024. var renderer = chart.renderer;
  10025. var buttonTheme = merge(options.buttonTheme);
  10026. var states = buttonTheme && buttonTheme.states;
  10027. // Prevent the button from resetting the width when the button state
  10028. // changes since we need more control over the width when collapsing
  10029. // the buttons
  10030. var width = buttonTheme.width || 28;
  10031. delete buttonTheme.width;
  10032. delete buttonTheme.states;
  10033. this.buttonGroup = renderer.g('range-selector-buttons').add(this.group);
  10034. var dropdown = this.dropdown = createElement('select',
  10035. void 0, {
  10036. position: 'absolute',
  10037. width: '1px',
  10038. height: '1px',
  10039. padding: 0,
  10040. border: 0,
  10041. top: '-9999em',
  10042. cursor: 'pointer',
  10043. opacity: 0.0001
  10044. },
  10045. this.div);
  10046. // Prevent page zoom on iPhone
  10047. addEvent(dropdown, 'touchstart', function () {
  10048. dropdown.style.fontSize = '16px';
  10049. });
  10050. // Forward events from select to button
  10051. [
  10052. [H.isMS ? 'mouseover' : 'mouseenter'],
  10053. [H.isMS ? 'mouseout' : 'mouseleave'],
  10054. ['change', 'click']
  10055. ].forEach(function (_a) {
  10056. var from = _a[0],
  10057. to = _a[1];
  10058. addEvent(dropdown, from, function () {
  10059. var button = buttons[_this.currentButtonIndex()];
  10060. if (button) {
  10061. fireEvent(button.element, to || from);
  10062. }
  10063. });
  10064. });
  10065. this.zoomText = renderer
  10066. .text(lang.rangeSelectorZoom, 0, 15)
  10067. .add(this.buttonGroup);
  10068. if (!this.chart.styledMode) {
  10069. this.zoomText.css(options.labelStyle);
  10070. buttonTheme['stroke-width'] = pick(buttonTheme['stroke-width'], 0);
  10071. }
  10072. createElement('option', {
  10073. textContent: this.zoomText.textStr,
  10074. disabled: true
  10075. }, void 0, dropdown);
  10076. this.buttonOptions.forEach(function (rangeOptions, i) {
  10077. createElement('option', {
  10078. textContent: rangeOptions.title || rangeOptions.text
  10079. }, void 0, dropdown);
  10080. buttons[i] = renderer
  10081. .button(rangeOptions.text, 0, 0, function (e) {
  10082. // extract events from button object and call
  10083. var buttonEvents = (rangeOptions.events &&
  10084. rangeOptions.events.click),
  10085. callDefaultEvent;
  10086. if (buttonEvents) {
  10087. callDefaultEvent =
  10088. buttonEvents.call(rangeOptions, e);
  10089. }
  10090. if (callDefaultEvent !== false) {
  10091. _this.clickButton(i);
  10092. }
  10093. _this.isActive = true;
  10094. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  10095. .attr({
  10096. 'text-align': 'center',
  10097. width: width
  10098. })
  10099. .add(_this.buttonGroup);
  10100. if (rangeOptions.title) {
  10101. buttons[i].attr('title', rangeOptions.title);
  10102. }
  10103. });
  10104. };
  10105. /**
  10106. * Align the elements horizontally and vertically.
  10107. *
  10108. * @private
  10109. * @function Highcharts.RangeSelector#alignElements
  10110. * @return {void}
  10111. */
  10112. RangeSelector.prototype.alignElements = function () {
  10113. var _this = this;
  10114. var _a = this,
  10115. buttonGroup = _a.buttonGroup,
  10116. buttons = _a.buttons,
  10117. chart = _a.chart,
  10118. group = _a.group,
  10119. inputGroup = _a.inputGroup,
  10120. options = _a.options,
  10121. zoomText = _a.zoomText;
  10122. var chartOptions = chart.options;
  10123. var navButtonOptions = (chartOptions.exporting &&
  10124. chartOptions.exporting.enabled !== false &&
  10125. chartOptions.navigation &&
  10126. chartOptions.navigation.buttonOptions);
  10127. var buttonPosition = options.buttonPosition,
  10128. inputPosition = options.inputPosition,
  10129. verticalAlign = options.verticalAlign;
  10130. // Get the X offset required to avoid overlapping with the exporting
  10131. // button. This is is used both by the buttonGroup and the inputGroup.
  10132. var getXOffsetForExportButton = function (group,
  10133. position) {
  10134. if (navButtonOptions &&
  10135. _this.titleCollision(chart) &&
  10136. verticalAlign === 'top' &&
  10137. position.align === 'right' && ((position.y -
  10138. group.getBBox().height - 12) <
  10139. ((navButtonOptions.y || 0) +
  10140. (navButtonOptions.height || 0) +
  10141. chart.spacing[0]))) {
  10142. return -40;
  10143. }
  10144. return 0;
  10145. };
  10146. var plotLeft = chart.plotLeft;
  10147. if (group && buttonPosition && inputPosition) {
  10148. var translateX = buttonPosition.x - chart.spacing[3];
  10149. if (buttonGroup) {
  10150. this.positionButtons();
  10151. if (!this.initialButtonGroupWidth) {
  10152. var width_1 = 0;
  10153. if (zoomText) {
  10154. width_1 += zoomText.getBBox().width + 5;
  10155. }
  10156. buttons.forEach(function (button, i) {
  10157. width_1 += button.width;
  10158. if (i !== buttons.length - 1) {
  10159. width_1 += options.buttonSpacing;
  10160. }
  10161. });
  10162. this.initialButtonGroupWidth = width_1;
  10163. }
  10164. plotLeft -= chart.spacing[3];
  10165. this.updateButtonStates();
  10166. // Detect collision between button group and exporting
  10167. var xOffsetForExportButton_1 = getXOffsetForExportButton(buttonGroup,
  10168. buttonPosition);
  10169. this.alignButtonGroup(xOffsetForExportButton_1);
  10170. // Skip animation
  10171. group.placed = buttonGroup.placed = chart.hasLoaded;
  10172. }
  10173. var xOffsetForExportButton = 0;
  10174. if (inputGroup) {
  10175. // Detect collision between the input group and exporting button
  10176. xOffsetForExportButton = getXOffsetForExportButton(inputGroup, inputPosition);
  10177. if (inputPosition.align === 'left') {
  10178. translateX = plotLeft;
  10179. }
  10180. else if (inputPosition.align === 'right') {
  10181. translateX = -Math.max(chart.axisOffset[1], -xOffsetForExportButton);
  10182. }
  10183. // Update the alignment to the updated spacing box
  10184. inputGroup.align({
  10185. y: inputPosition.y,
  10186. width: inputGroup.getBBox().width,
  10187. align: inputPosition.align,
  10188. // fix wrong getBBox() value on right align
  10189. x: inputPosition.x + translateX - 2
  10190. }, true, chart.spacingBox);
  10191. // Skip animation
  10192. inputGroup.placed = chart.hasLoaded;
  10193. }
  10194. this.handleCollision(xOffsetForExportButton);
  10195. // Vertical align
  10196. group.align({
  10197. verticalAlign: verticalAlign
  10198. }, true, chart.spacingBox);
  10199. var alignTranslateY = group.alignAttr.translateY;
  10200. // Set position
  10201. var groupHeight = group.getBBox().height + 20; // # 20 padding
  10202. var translateY = 0;
  10203. // Calculate bottom position
  10204. if (verticalAlign === 'bottom') {
  10205. var legendOptions = chart.legend && chart.legend.options;
  10206. var legendHeight = (legendOptions &&
  10207. legendOptions.verticalAlign === 'bottom' &&
  10208. legendOptions.enabled &&
  10209. !legendOptions.floating ?
  10210. (chart.legend.legendHeight +
  10211. pick(legendOptions.margin, 10)) :
  10212. 0);
  10213. groupHeight = groupHeight + legendHeight - 20;
  10214. translateY = (alignTranslateY -
  10215. groupHeight -
  10216. (options.floating ? 0 : options.y) -
  10217. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  10218. 10 // 10 spacing
  10219. );
  10220. }
  10221. if (verticalAlign === 'top') {
  10222. if (options.floating) {
  10223. translateY = 0;
  10224. }
  10225. if (chart.titleOffset && chart.titleOffset[0]) {
  10226. translateY = chart.titleOffset[0];
  10227. }
  10228. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  10229. }
  10230. else if (verticalAlign === 'middle') {
  10231. if (inputPosition.y === buttonPosition.y) {
  10232. translateY = alignTranslateY;
  10233. }
  10234. else if (inputPosition.y || buttonPosition.y) {
  10235. if (inputPosition.y < 0 ||
  10236. buttonPosition.y < 0) {
  10237. translateY -= Math.min(inputPosition.y, buttonPosition.y);
  10238. }
  10239. else {
  10240. translateY = alignTranslateY - groupHeight;
  10241. }
  10242. }
  10243. }
  10244. group.translate(options.x, options.y + Math.floor(translateY));
  10245. // Translate HTML inputs
  10246. var _b = this,
  10247. minInput = _b.minInput,
  10248. maxInput = _b.maxInput,
  10249. dropdown = _b.dropdown;
  10250. if (options.inputEnabled && minInput && maxInput) {
  10251. minInput.style.marginTop = group.translateY + 'px';
  10252. maxInput.style.marginTop = group.translateY + 'px';
  10253. }
  10254. if (dropdown) {
  10255. dropdown.style.marginTop = group.translateY + 'px';
  10256. }
  10257. }
  10258. };
  10259. /**
  10260. * Align the button group horizontally and vertically.
  10261. *
  10262. * @private
  10263. * @function Highcharts.RangeSelector#alignButtonGroup
  10264. * @param {number} xOffsetForExportButton
  10265. * @param {number} [width]
  10266. * @return {void}
  10267. */
  10268. RangeSelector.prototype.alignButtonGroup = function (xOffsetForExportButton, width) {
  10269. var _a = this,
  10270. chart = _a.chart,
  10271. options = _a.options,
  10272. buttonGroup = _a.buttonGroup,
  10273. buttons = _a.buttons;
  10274. var buttonPosition = options.buttonPosition;
  10275. var plotLeft = chart.plotLeft - chart.spacing[3];
  10276. var translateX = buttonPosition.x - chart.spacing[3];
  10277. if (buttonPosition.align === 'right') {
  10278. translateX += xOffsetForExportButton - plotLeft; // #13014
  10279. }
  10280. else if (buttonPosition.align === 'center') {
  10281. translateX -= plotLeft / 2;
  10282. }
  10283. if (buttonGroup) {
  10284. // Align button group
  10285. buttonGroup.align({
  10286. y: buttonPosition.y,
  10287. width: pick(width, this.initialButtonGroupWidth),
  10288. align: buttonPosition.align,
  10289. x: translateX
  10290. }, true, chart.spacingBox);
  10291. }
  10292. };
  10293. /**
  10294. * @private
  10295. * @function Highcharts.RangeSelector#positionButtons
  10296. * @return {void}
  10297. */
  10298. RangeSelector.prototype.positionButtons = function () {
  10299. var _a = this,
  10300. buttons = _a.buttons,
  10301. chart = _a.chart,
  10302. options = _a.options,
  10303. zoomText = _a.zoomText;
  10304. var verb = chart.hasLoaded ? 'animate' : 'attr';
  10305. var buttonPosition = options.buttonPosition;
  10306. var plotLeft = chart.plotLeft;
  10307. var buttonLeft = plotLeft;
  10308. if (zoomText && zoomText.visibility !== 'hidden') {
  10309. // #8769, allow dynamically updating margins
  10310. zoomText[verb]({
  10311. x: pick(plotLeft + buttonPosition.x, plotLeft)
  10312. });
  10313. // Button start position
  10314. buttonLeft += buttonPosition.x +
  10315. zoomText.getBBox().width + 5;
  10316. }
  10317. this.buttonOptions.forEach(function (rangeOptions, i) {
  10318. if (buttons[i].visibility !== 'hidden') {
  10319. buttons[i][verb]({ x: buttonLeft });
  10320. // increase button position for the next button
  10321. buttonLeft += buttons[i].width + options.buttonSpacing;
  10322. }
  10323. else {
  10324. buttons[i][verb]({ x: plotLeft });
  10325. }
  10326. });
  10327. };
  10328. /**
  10329. * Handle collision between the button group and the input group
  10330. *
  10331. * @private
  10332. * @function Highcharts.RangeSelector#handleCollision
  10333. *
  10334. * @param {number} xOffsetForExportButton
  10335. * The X offset of the group required to make room for the
  10336. * exporting button
  10337. * @return {void}
  10338. */
  10339. RangeSelector.prototype.handleCollision = function (xOffsetForExportButton) {
  10340. var _this = this;
  10341. var _a = this,
  10342. chart = _a.chart,
  10343. buttonGroup = _a.buttonGroup,
  10344. inputGroup = _a.inputGroup;
  10345. var _b = this.options,
  10346. buttonPosition = _b.buttonPosition,
  10347. dropdown = _b.dropdown,
  10348. inputPosition = _b.inputPosition;
  10349. var maxButtonWidth = function () {
  10350. var buttonWidth = 0;
  10351. _this.buttons.forEach(function (button) {
  10352. var bBox = button.getBBox();
  10353. if (bBox.width > buttonWidth) {
  10354. buttonWidth = bBox.width;
  10355. }
  10356. });
  10357. return buttonWidth;
  10358. };
  10359. var groupsOverlap = function (buttonGroupWidth) {
  10360. if (inputGroup && buttonGroup) {
  10361. var inputGroupX = (inputGroup.alignAttr.translateX +
  10362. inputGroup.alignOptions.x -
  10363. xOffsetForExportButton +
  10364. // getBBox for detecing left margin
  10365. inputGroup.getBBox().x +
  10366. // 2px padding to not overlap input and label
  10367. 2);
  10368. var inputGroupWidth = inputGroup.alignOptions.width;
  10369. var buttonGroupX = buttonGroup.alignAttr.translateX +
  10370. buttonGroup.getBBox().x;
  10371. return (buttonGroupX + buttonGroupWidth > inputGroupX) &&
  10372. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  10373. (buttonPosition.y <
  10374. (inputPosition.y +
  10375. inputGroup.getBBox().height));
  10376. }
  10377. return false;
  10378. };
  10379. var moveInputsDown = function () {
  10380. if (inputGroup && buttonGroup) {
  10381. inputGroup.attr({
  10382. translateX: inputGroup.alignAttr.translateX + (chart.axisOffset[1] >= -xOffsetForExportButton ?
  10383. 0 :
  10384. -xOffsetForExportButton),
  10385. translateY: inputGroup.alignAttr.translateY +
  10386. buttonGroup.getBBox().height + 10
  10387. });
  10388. }
  10389. };
  10390. if (buttonGroup) {
  10391. if (dropdown === 'always') {
  10392. this.collapseButtons(xOffsetForExportButton);
  10393. if (groupsOverlap(maxButtonWidth())) {
  10394. // Move the inputs down if there is still a collision
  10395. // after collapsing the buttons
  10396. moveInputsDown();
  10397. }
  10398. return;
  10399. }
  10400. if (dropdown === 'never') {
  10401. this.expandButtons();
  10402. }
  10403. }
  10404. // Detect collision
  10405. if (inputGroup && buttonGroup) {
  10406. if ((inputPosition.align === buttonPosition.align) ||
  10407. // 20 is minimal spacing between elements
  10408. groupsOverlap(this.initialButtonGroupWidth + 20)) {
  10409. if (dropdown === 'responsive') {
  10410. this.collapseButtons(xOffsetForExportButton);
  10411. if (groupsOverlap(maxButtonWidth())) {
  10412. moveInputsDown();
  10413. }
  10414. }
  10415. else {
  10416. moveInputsDown();
  10417. }
  10418. }
  10419. else if (dropdown === 'responsive') {
  10420. this.expandButtons();
  10421. }
  10422. }
  10423. else if (buttonGroup && dropdown === 'responsive') {
  10424. if (this.initialButtonGroupWidth > chart.plotWidth) {
  10425. this.collapseButtons(xOffsetForExportButton);
  10426. }
  10427. else {
  10428. this.expandButtons();
  10429. }
  10430. }
  10431. };
  10432. /**
  10433. * Collapse the buttons and put the select element on top.
  10434. *
  10435. * @private
  10436. * @function Highcharts.RangeSelector#collapseButtons
  10437. * @param {number} xOffsetForExportButton
  10438. * @return {void}
  10439. */
  10440. RangeSelector.prototype.collapseButtons = function (xOffsetForExportButton) {
  10441. var _a = this,
  10442. buttons = _a.buttons,
  10443. buttonOptions = _a.buttonOptions,
  10444. dropdown = _a.dropdown,
  10445. options = _a.options,
  10446. zoomText = _a.zoomText;
  10447. var getAttribs = function (text) { return ({
  10448. text: text ? text + " \u25BE" : '▾',
  10449. width: 'auto',
  10450. paddingLeft: 8,
  10451. paddingRight: 8
  10452. }); };
  10453. if (zoomText) {
  10454. zoomText.hide();
  10455. }
  10456. var hasActiveButton = false;
  10457. buttonOptions.forEach(function (rangeOptions, i) {
  10458. var button = buttons[i];
  10459. if (button.state !== 2) {
  10460. button.hide();
  10461. }
  10462. else {
  10463. button.show();
  10464. button.attr(getAttribs(rangeOptions.text));
  10465. hasActiveButton = true;
  10466. }
  10467. });
  10468. if (!hasActiveButton) {
  10469. if (dropdown) {
  10470. dropdown.selectedIndex = 0;
  10471. }
  10472. buttons[0].show();
  10473. buttons[0].attr(getAttribs(this.zoomText && this.zoomText.textStr));
  10474. }
  10475. var align = options.buttonPosition.align;
  10476. this.positionButtons();
  10477. if (align === 'right' || align === 'center') {
  10478. this.alignButtonGroup(xOffsetForExportButton, buttons[this.currentButtonIndex()].getBBox().width);
  10479. }
  10480. this.showDropdown();
  10481. };
  10482. /**
  10483. * Show all the buttons and hide the select element.
  10484. *
  10485. * @private
  10486. * @function Highcharts.RangeSelector#expandButtons
  10487. * @return {void}
  10488. */
  10489. RangeSelector.prototype.expandButtons = function () {
  10490. var _a = this,
  10491. buttons = _a.buttons,
  10492. buttonOptions = _a.buttonOptions,
  10493. options = _a.options,
  10494. zoomText = _a.zoomText;
  10495. this.hideDropdown();
  10496. if (zoomText) {
  10497. zoomText.show();
  10498. }
  10499. buttonOptions.forEach(function (rangeOptions, i) {
  10500. var button = buttons[i];
  10501. button.show();
  10502. button.attr({
  10503. text: rangeOptions.text,
  10504. width: options.buttonTheme.width || 28,
  10505. paddingLeft: 'unset',
  10506. paddingRight: 'unset'
  10507. });
  10508. if (button.state < 2) {
  10509. button.setState(0);
  10510. }
  10511. });
  10512. this.positionButtons();
  10513. };
  10514. /**
  10515. * Get the index of the visible button when the buttons are collapsed.
  10516. *
  10517. * @private
  10518. * @function Highcharts.RangeSelector#currentButtonIndex
  10519. * @return {number}
  10520. */
  10521. RangeSelector.prototype.currentButtonIndex = function () {
  10522. var dropdown = this.dropdown;
  10523. if (dropdown && dropdown.selectedIndex > 0) {
  10524. return dropdown.selectedIndex - 1;
  10525. }
  10526. return 0;
  10527. };
  10528. /**
  10529. * Position the select element on top of the button.
  10530. *
  10531. * @private
  10532. * @function Highcharts.RangeSelector#showDropdown
  10533. * @return {void}
  10534. */
  10535. RangeSelector.prototype.showDropdown = function () {
  10536. var _a = this,
  10537. buttonGroup = _a.buttonGroup,
  10538. buttons = _a.buttons,
  10539. chart = _a.chart,
  10540. dropdown = _a.dropdown;
  10541. if (buttonGroup && dropdown) {
  10542. var translateX = buttonGroup.translateX,
  10543. translateY = buttonGroup.translateY;
  10544. var bBox = buttons[this.currentButtonIndex()].getBBox();
  10545. css(dropdown, {
  10546. left: (chart.plotLeft + translateX) + 'px',
  10547. top: (translateY + 0.5) + 'px',
  10548. width: bBox.width + 'px',
  10549. height: bBox.height + 'px'
  10550. });
  10551. this.hasVisibleDropdown = true;
  10552. }
  10553. };
  10554. /**
  10555. * @private
  10556. * @function Highcharts.RangeSelector#hideDropdown
  10557. * @return {void}
  10558. */
  10559. RangeSelector.prototype.hideDropdown = function () {
  10560. var dropdown = this.dropdown;
  10561. if (dropdown) {
  10562. css(dropdown, {
  10563. top: '-9999em',
  10564. width: '1px',
  10565. height: '1px'
  10566. });
  10567. this.hasVisibleDropdown = false;
  10568. }
  10569. };
  10570. /**
  10571. * Extracts height of range selector
  10572. *
  10573. * @private
  10574. * @function Highcharts.RangeSelector#getHeight
  10575. * @return {number}
  10576. * Returns rangeSelector height
  10577. */
  10578. RangeSelector.prototype.getHeight = function () {
  10579. var rangeSelector = this,
  10580. options = rangeSelector.options,
  10581. rangeSelectorGroup = rangeSelector.group,
  10582. inputPosition = options.inputPosition,
  10583. buttonPosition = options.buttonPosition,
  10584. yPosition = options.y,
  10585. buttonPositionY = buttonPosition.y,
  10586. inputPositionY = inputPosition.y,
  10587. rangeSelectorHeight = 0,
  10588. minPosition;
  10589. if (options.height) {
  10590. return options.height;
  10591. }
  10592. // Align the elements before we read the height in case we're switching
  10593. // between wrapped and non-wrapped layout
  10594. this.alignElements();
  10595. rangeSelectorHeight = rangeSelectorGroup ?
  10596. // 13px to keep back compatibility
  10597. (rangeSelectorGroup.getBBox(true).height) + 13 +
  10598. yPosition :
  10599. 0;
  10600. minPosition = Math.min(inputPositionY, buttonPositionY);
  10601. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  10602. (inputPositionY > 0 && buttonPositionY > 0)) {
  10603. rangeSelectorHeight += Math.abs(minPosition);
  10604. }
  10605. return rangeSelectorHeight;
  10606. };
  10607. /**
  10608. * Detect collision with title or subtitle
  10609. *
  10610. * @private
  10611. * @function Highcharts.RangeSelector#titleCollision
  10612. *
  10613. * @param {Highcharts.Chart} chart
  10614. *
  10615. * @return {boolean}
  10616. * Returns collision status
  10617. */
  10618. RangeSelector.prototype.titleCollision = function (chart) {
  10619. return !(chart.options.title.text ||
  10620. chart.options.subtitle.text);
  10621. };
  10622. /**
  10623. * Update the range selector with new options
  10624. *
  10625. * @private
  10626. * @function Highcharts.RangeSelector#update
  10627. * @param {Highcharts.RangeSelectorOptions} options
  10628. * @return {void}
  10629. */
  10630. RangeSelector.prototype.update = function (options) {
  10631. var chart = this.chart;
  10632. merge(true, chart.options.rangeSelector, options);
  10633. this.destroy();
  10634. this.init(chart);
  10635. this.render();
  10636. };
  10637. /**
  10638. * Destroys allocated elements.
  10639. *
  10640. * @private
  10641. * @function Highcharts.RangeSelector#destroy
  10642. */
  10643. RangeSelector.prototype.destroy = function () {
  10644. var rSelector = this,
  10645. minInput = rSelector.minInput,
  10646. maxInput = rSelector.maxInput;
  10647. if (rSelector.eventsToUnbind) {
  10648. rSelector.eventsToUnbind.forEach(function (unbind) { return unbind(); });
  10649. rSelector.eventsToUnbind = void 0;
  10650. }
  10651. // Destroy elements in collections
  10652. destroyObjectProperties(rSelector.buttons);
  10653. // Clear input element events
  10654. if (minInput) {
  10655. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  10656. }
  10657. if (maxInput) {
  10658. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  10659. }
  10660. // Destroy HTML and SVG elements
  10661. objectEach(rSelector, function (val, key) {
  10662. if (val && key !== 'chart') {
  10663. if (val instanceof SVGElement) {
  10664. // SVGElement
  10665. val.destroy();
  10666. }
  10667. else if (val instanceof window.HTMLElement) {
  10668. // HTML element
  10669. discardElement(val);
  10670. }
  10671. }
  10672. if (val !== RangeSelector.prototype[key]) {
  10673. rSelector[key] = null;
  10674. }
  10675. }, this);
  10676. };
  10677. return RangeSelector;
  10678. }());
  10679. /**
  10680. * The default buttons for pre-selecting time frames
  10681. */
  10682. RangeSelector.prototype.defaultButtons = [{
  10683. type: 'month',
  10684. count: 1,
  10685. text: '1m',
  10686. title: 'View 1 month'
  10687. }, {
  10688. type: 'month',
  10689. count: 3,
  10690. text: '3m',
  10691. title: 'View 3 months'
  10692. }, {
  10693. type: 'month',
  10694. count: 6,
  10695. text: '6m',
  10696. title: 'View 6 months'
  10697. }, {
  10698. type: 'ytd',
  10699. text: 'YTD',
  10700. title: 'View year to date'
  10701. }, {
  10702. type: 'year',
  10703. count: 1,
  10704. text: '1y',
  10705. title: 'View 1 year'
  10706. }, {
  10707. type: 'all',
  10708. text: 'All',
  10709. title: 'View all'
  10710. }];
  10711. /**
  10712. * The date formats to use when setting min, max and value on date inputs
  10713. */
  10714. RangeSelector.prototype.inputTypeFormats = {
  10715. 'datetime-local': '%Y-%m-%dT%H:%M:%S',
  10716. 'date': '%Y-%m-%d',
  10717. 'time': '%H:%M:%S'
  10718. };
  10719. /**
  10720. * Get the preferred input type based on a date format string.
  10721. *
  10722. * @private
  10723. * @function preferredInputType
  10724. * @param {string} format
  10725. * @return {string}
  10726. */
  10727. function preferredInputType(format) {
  10728. var ms = format.indexOf('%L') !== -1;
  10729. if (ms) {
  10730. return 'text';
  10731. }
  10732. var date = ['a', 'A', 'd', 'e', 'w', 'b', 'B', 'm', 'o', 'y', 'Y'].some(function (char) {
  10733. return format.indexOf('%' + char) !== -1;
  10734. });
  10735. var time = ['H', 'k', 'I', 'l', 'M', 'S'].some(function (char) {
  10736. return format.indexOf('%' + char) !== -1;
  10737. });
  10738. if (date && time) {
  10739. return 'datetime-local';
  10740. }
  10741. if (date) {
  10742. return 'date';
  10743. }
  10744. if (time) {
  10745. return 'time';
  10746. }
  10747. return 'text';
  10748. }
  10749. /**
  10750. * Get the axis min value based on the range option and the current max. For
  10751. * stock charts this is extended via the {@link RangeSelector} so that if the
  10752. * selected range is a multiple of months or years, it is compensated for
  10753. * various month lengths.
  10754. *
  10755. * @private
  10756. * @function Highcharts.Axis#minFromRange
  10757. * @return {number|undefined}
  10758. * The new minimum value.
  10759. */
  10760. Axis.prototype.minFromRange = function () {
  10761. var rangeOptions = this.range,
  10762. type = rangeOptions.type,
  10763. min,
  10764. max = this.max,
  10765. dataMin,
  10766. range,
  10767. time = this.chart.time,
  10768. // Get the true range from a start date
  10769. getTrueRange = function (base,
  10770. count) {
  10771. var timeName = type === 'year' ? 'FullYear' : 'Month';
  10772. var date = new time.Date(base);
  10773. var basePeriod = time.get(timeName,
  10774. date);
  10775. time.set(timeName, date, basePeriod + count);
  10776. if (basePeriod === time.get(timeName, date)) {
  10777. time.set('Date', date, 0); // #6537
  10778. }
  10779. return date.getTime() - base;
  10780. };
  10781. if (isNumber(rangeOptions)) {
  10782. min = max - rangeOptions;
  10783. range = rangeOptions;
  10784. }
  10785. else {
  10786. min = max + getTrueRange(max, -rangeOptions.count);
  10787. // Let the fixedRange reflect initial settings (#5930)
  10788. if (this.chart) {
  10789. this.chart.fixedRange = max - min;
  10790. }
  10791. }
  10792. dataMin = pick(this.dataMin, Number.MIN_VALUE);
  10793. if (!isNumber(min)) {
  10794. min = dataMin;
  10795. }
  10796. if (min <= dataMin) {
  10797. min = dataMin;
  10798. if (typeof range === 'undefined') { // #4501
  10799. range = getTrueRange(min, rangeOptions.count);
  10800. }
  10801. this.newMax = Math.min(min + range, this.dataMax);
  10802. }
  10803. if (!isNumber(max)) {
  10804. min = void 0;
  10805. }
  10806. return min;
  10807. };
  10808. if (!H.RangeSelector) {
  10809. var chartDestroyEvents_1 = [];
  10810. var initRangeSelector_1 = function (chart) {
  10811. var extremes,
  10812. rangeSelector = chart.rangeSelector,
  10813. legend,
  10814. alignTo,
  10815. verticalAlign;
  10816. /**
  10817. * @private
  10818. */
  10819. function render() {
  10820. if (rangeSelector) {
  10821. extremes = chart.xAxis[0].getExtremes();
  10822. legend = chart.legend;
  10823. verticalAlign = (rangeSelector &&
  10824. rangeSelector.options.verticalAlign);
  10825. if (isNumber(extremes.min)) {
  10826. rangeSelector.render(extremes.min, extremes.max);
  10827. }
  10828. // Re-align the legend so that it's below the rangeselector
  10829. if (legend.display &&
  10830. verticalAlign === 'top' &&
  10831. verticalAlign === legend.options.verticalAlign) {
  10832. // Create a new alignment box for the legend.
  10833. alignTo = merge(chart.spacingBox);
  10834. if (legend.options.layout === 'vertical') {
  10835. alignTo.y = chart.plotTop;
  10836. }
  10837. else {
  10838. alignTo.y += rangeSelector.getHeight();
  10839. }
  10840. legend.group.placed = false; // Don't animate the alignment.
  10841. legend.align(alignTo);
  10842. }
  10843. }
  10844. }
  10845. if (rangeSelector) {
  10846. var events = find(chartDestroyEvents_1,
  10847. function (e) { return e[0] === chart; });
  10848. if (!events) {
  10849. chartDestroyEvents_1.push([chart, [
  10850. // redraw the scroller on setExtremes
  10851. addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  10852. if (rangeSelector) {
  10853. rangeSelector.render(e.min, e.max);
  10854. }
  10855. }),
  10856. // redraw the scroller chart resize
  10857. addEvent(chart, 'redraw', render)
  10858. ]]);
  10859. }
  10860. // do it now
  10861. render();
  10862. }
  10863. };
  10864. // Initialize rangeselector for stock charts
  10865. addEvent(Chart, 'afterGetContainer', function () {
  10866. if (this.options.rangeSelector &&
  10867. this.options.rangeSelector.enabled) {
  10868. this.rangeSelector = new RangeSelector(this);
  10869. }
  10870. });
  10871. addEvent(Chart, 'beforeRender', function () {
  10872. var chart = this,
  10873. axes = chart.axes,
  10874. rangeSelector = chart.rangeSelector,
  10875. verticalAlign;
  10876. if (rangeSelector) {
  10877. if (isNumber(rangeSelector.deferredYTDClick)) {
  10878. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  10879. delete rangeSelector.deferredYTDClick;
  10880. }
  10881. axes.forEach(function (axis) {
  10882. axis.updateNames();
  10883. axis.setScale();
  10884. });
  10885. chart.getAxisMargins();
  10886. rangeSelector.render();
  10887. verticalAlign = rangeSelector.options.verticalAlign;
  10888. if (!rangeSelector.options.floating) {
  10889. if (verticalAlign === 'bottom') {
  10890. this.extraBottomMargin = true;
  10891. }
  10892. else if (verticalAlign !== 'middle') {
  10893. this.extraTopMargin = true;
  10894. }
  10895. }
  10896. }
  10897. });
  10898. addEvent(Chart, 'update', function (e) {
  10899. var chart = this,
  10900. options = e.options,
  10901. optionsRangeSelector = options.rangeSelector,
  10902. rangeSelector = chart.rangeSelector,
  10903. verticalAlign,
  10904. extraBottomMarginWas = this.extraBottomMargin,
  10905. extraTopMarginWas = this.extraTopMargin;
  10906. if (optionsRangeSelector &&
  10907. optionsRangeSelector.enabled &&
  10908. !defined(rangeSelector) &&
  10909. this.options.rangeSelector) {
  10910. this.options.rangeSelector.enabled = true;
  10911. this.rangeSelector = rangeSelector = new RangeSelector(this);
  10912. }
  10913. this.extraBottomMargin = false;
  10914. this.extraTopMargin = false;
  10915. if (rangeSelector) {
  10916. initRangeSelector_1(this);
  10917. verticalAlign = (optionsRangeSelector &&
  10918. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  10919. if (!rangeSelector.options.floating) {
  10920. if (verticalAlign === 'bottom') {
  10921. this.extraBottomMargin = true;
  10922. }
  10923. else if (verticalAlign !== 'middle') {
  10924. this.extraTopMargin = true;
  10925. }
  10926. }
  10927. if (this.extraBottomMargin !== extraBottomMarginWas ||
  10928. this.extraTopMargin !== extraTopMarginWas) {
  10929. this.isDirtyBox = true;
  10930. }
  10931. }
  10932. });
  10933. addEvent(Chart, 'render', function () {
  10934. var chart = this,
  10935. rangeSelector = chart.rangeSelector,
  10936. verticalAlign;
  10937. if (rangeSelector && !rangeSelector.options.floating) {
  10938. rangeSelector.render();
  10939. verticalAlign = rangeSelector.options.verticalAlign;
  10940. if (verticalAlign === 'bottom') {
  10941. this.extraBottomMargin = true;
  10942. }
  10943. else if (verticalAlign !== 'middle') {
  10944. this.extraTopMargin = true;
  10945. }
  10946. }
  10947. });
  10948. addEvent(Chart, 'getMargins', function () {
  10949. var rangeSelector = this.rangeSelector,
  10950. rangeSelectorHeight;
  10951. if (rangeSelector) {
  10952. rangeSelectorHeight = rangeSelector.getHeight();
  10953. if (this.extraTopMargin) {
  10954. this.plotTop += rangeSelectorHeight;
  10955. }
  10956. if (this.extraBottomMargin) {
  10957. this.marginBottom += rangeSelectorHeight;
  10958. }
  10959. }
  10960. });
  10961. Chart.prototype.callbacks.push(initRangeSelector_1);
  10962. // Remove resize/afterSetExtremes at chart destroy
  10963. addEvent(Chart, 'destroy', function destroyEvents() {
  10964. for (var i = 0; i < chartDestroyEvents_1.length; i++) {
  10965. var events = chartDestroyEvents_1[i];
  10966. if (events[0] === this) {
  10967. events[1].forEach(function (unbind) { return unbind(); });
  10968. chartDestroyEvents_1.splice(i, 1);
  10969. return;
  10970. }
  10971. }
  10972. });
  10973. H.RangeSelector = RangeSelector;
  10974. }
  10975. return H.RangeSelector;
  10976. });
  10977. _registerModule(_modules, 'Core/Axis/NavigatorAxis.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  10978. /* *
  10979. *
  10980. * (c) 2010-2021 Torstein Honsi
  10981. *
  10982. * License: www.highcharts.com/license
  10983. *
  10984. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10985. *
  10986. * */
  10987. var isTouchDevice = H.isTouchDevice;
  10988. var addEvent = U.addEvent,
  10989. correctFloat = U.correctFloat,
  10990. defined = U.defined,
  10991. isNumber = U.isNumber,
  10992. pick = U.pick;
  10993. /* eslint-disable valid-jsdoc */
  10994. /**
  10995. * @private
  10996. * @class
  10997. */
  10998. var NavigatorAxisAdditions = /** @class */ (function () {
  10999. /* *
  11000. *
  11001. * Constructors
  11002. *
  11003. * */
  11004. function NavigatorAxisAdditions(axis) {
  11005. this.axis = axis;
  11006. }
  11007. /* *
  11008. *
  11009. * Functions
  11010. *
  11011. * */
  11012. /**
  11013. * @private
  11014. */
  11015. NavigatorAxisAdditions.prototype.destroy = function () {
  11016. this.axis = void 0;
  11017. };
  11018. /**
  11019. * Add logic to normalize the zoomed range in order to preserve the pressed
  11020. * state of range selector buttons
  11021. *
  11022. * @private
  11023. * @function Highcharts.Axis#toFixedRange
  11024. * @param {number} [pxMin]
  11025. * @param {number} [pxMax]
  11026. * @param {number} [fixedMin]
  11027. * @param {number} [fixedMax]
  11028. * @return {*}
  11029. */
  11030. NavigatorAxisAdditions.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) {
  11031. var navigator = this;
  11032. var axis = navigator.axis;
  11033. var chart = axis.chart;
  11034. var fixedRange = chart && chart.fixedRange,
  11035. halfPointRange = (axis.pointRange || 0) / 2,
  11036. newMin = pick(fixedMin,
  11037. axis.translate(pxMin,
  11038. true, !axis.horiz)),
  11039. newMax = pick(fixedMax,
  11040. axis.translate(pxMax,
  11041. true, !axis.horiz)),
  11042. changeRatio = fixedRange && (newMax - newMin) / fixedRange;
  11043. // Add/remove half point range to/from the extremes (#1172)
  11044. if (!defined(fixedMin)) {
  11045. newMin = correctFloat(newMin + halfPointRange);
  11046. }
  11047. if (!defined(fixedMax)) {
  11048. newMax = correctFloat(newMax - halfPointRange);
  11049. }
  11050. // If the difference between the fixed range and the actual requested
  11051. // range is too great, the user is dragging across an ordinal gap, and
  11052. // we need to release the range selector button.
  11053. if (changeRatio > 0.7 && changeRatio < 1.3) {
  11054. if (fixedMax) {
  11055. newMin = newMax - fixedRange;
  11056. }
  11057. else {
  11058. newMax = newMin + fixedRange;
  11059. }
  11060. }
  11061. if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
  11062. newMin = newMax = void 0;
  11063. }
  11064. return {
  11065. min: newMin,
  11066. max: newMax
  11067. };
  11068. };
  11069. return NavigatorAxisAdditions;
  11070. }());
  11071. /**
  11072. * @private
  11073. * @class
  11074. */
  11075. var NavigatorAxis = /** @class */ (function () {
  11076. function NavigatorAxis() {
  11077. }
  11078. /* *
  11079. *
  11080. * Static Functions
  11081. *
  11082. * */
  11083. /**
  11084. * @private
  11085. */
  11086. NavigatorAxis.compose = function (AxisClass) {
  11087. AxisClass.keepProps.push('navigatorAxis');
  11088. /* eslint-disable no-invalid-this */
  11089. addEvent(AxisClass, 'init', function () {
  11090. var axis = this;
  11091. if (!axis.navigatorAxis) {
  11092. axis.navigatorAxis = new NavigatorAxisAdditions(axis);
  11093. }
  11094. });
  11095. // For Stock charts, override selection zooming with some special
  11096. // features because X axis zooming is already allowed by the Navigator
  11097. // and Range selector.
  11098. addEvent(AxisClass, 'zoom', function (e) {
  11099. var axis = this;
  11100. var chart = axis.chart;
  11101. var chartOptions = chart.options;
  11102. var navigator = chartOptions.navigator;
  11103. var navigatorAxis = axis.navigatorAxis;
  11104. var pinchType = chartOptions.chart.pinchType;
  11105. var rangeSelector = chartOptions.rangeSelector;
  11106. var zoomType = chartOptions.chart.zoomType;
  11107. var previousZoom;
  11108. if (axis.isXAxis && ((navigator && navigator.enabled) ||
  11109. (rangeSelector && rangeSelector.enabled))) {
  11110. // For y only zooming, ignore the X axis completely
  11111. if (zoomType === 'y') {
  11112. e.zoomed = false;
  11113. // For xy zooming, record the state of the zoom before zoom
  11114. // selection, then when the reset button is pressed, revert to
  11115. // this state. This should apply only if the chart is
  11116. // initialized with a range (#6612), otherwise zoom all the way
  11117. // out.
  11118. }
  11119. else if (((!isTouchDevice && zoomType === 'xy') ||
  11120. (isTouchDevice && pinchType === 'xy')) &&
  11121. axis.options.range) {
  11122. previousZoom = navigatorAxis.previousZoom;
  11123. if (defined(e.newMin)) {
  11124. navigatorAxis.previousZoom = [axis.min, axis.max];
  11125. }
  11126. else if (previousZoom) {
  11127. e.newMin = previousZoom[0];
  11128. e.newMax = previousZoom[1];
  11129. navigatorAxis.previousZoom = void 0;
  11130. }
  11131. }
  11132. }
  11133. if (typeof e.zoomed !== 'undefined') {
  11134. e.preventDefault();
  11135. }
  11136. });
  11137. /* eslint-enable no-invalid-this */
  11138. };
  11139. /* *
  11140. *
  11141. * Static Properties
  11142. *
  11143. * */
  11144. /**
  11145. * @private
  11146. */
  11147. NavigatorAxis.AdditionsClass = NavigatorAxisAdditions;
  11148. return NavigatorAxis;
  11149. }());
  11150. return NavigatorAxis;
  11151. });
  11152. _registerModule(_modules, 'Core/Navigator.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Axis/NavigatorAxis.js'], _modules['Core/Options.js'], _modules['Core/Color/Palette.js'], _modules['Core/Scrollbar.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (Axis, Chart, Color, H, NavigatorAxis, O, palette, Scrollbar, Series, SeriesRegistry, U) {
  11153. /* *
  11154. *
  11155. * (c) 2010-2021 Torstein Honsi
  11156. *
  11157. * License: www.highcharts.com/license
  11158. *
  11159. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  11160. *
  11161. * */
  11162. var color = Color.parse;
  11163. var hasTouch = H.hasTouch,
  11164. isTouchDevice = H.isTouchDevice;
  11165. var defaultOptions = O.defaultOptions;
  11166. var seriesTypes = SeriesRegistry.seriesTypes;
  11167. var addEvent = U.addEvent,
  11168. clamp = U.clamp,
  11169. correctFloat = U.correctFloat,
  11170. defined = U.defined,
  11171. destroyObjectProperties = U.destroyObjectProperties,
  11172. erase = U.erase,
  11173. extend = U.extend,
  11174. find = U.find,
  11175. isArray = U.isArray,
  11176. isNumber = U.isNumber,
  11177. merge = U.merge,
  11178. pick = U.pick,
  11179. removeEvent = U.removeEvent,
  11180. splat = U.splat;
  11181. var defaultSeriesType,
  11182. // Finding the min or max of a set of variables where we don't know if they
  11183. // are defined, is a pattern that is repeated several places in Highcharts.
  11184. // Consider making this a global utility method.
  11185. numExt = function (extreme) {
  11186. var args = [];
  11187. for (var _i = 1; _i < arguments.length; _i++) {
  11188. args[_i - 1] = arguments[_i];
  11189. }
  11190. var numbers = [].filter.call(args,
  11191. isNumber);
  11192. if (numbers.length) {
  11193. return Math[extreme].apply(0, numbers);
  11194. }
  11195. };
  11196. defaultSeriesType = typeof seriesTypes.areaspline === 'undefined' ?
  11197. 'line' :
  11198. 'areaspline';
  11199. extend(defaultOptions, {
  11200. /**
  11201. * Maximum range which can be set using the navigator's handles.
  11202. * Opposite of [xAxis.minRange](#xAxis.minRange).
  11203. *
  11204. * @sample {highstock} stock/navigator/maxrange/
  11205. * Defined max and min range
  11206. *
  11207. * @type {number}
  11208. * @since 6.0.0
  11209. * @product highstock gantt
  11210. * @apioption xAxis.maxRange
  11211. */
  11212. /**
  11213. * The navigator is a small series below the main series, displaying
  11214. * a view of the entire data set. It provides tools to zoom in and
  11215. * out on parts of the data as well as panning across the dataset.
  11216. *
  11217. * @product highstock gantt
  11218. * @optionparent navigator
  11219. */
  11220. navigator: {
  11221. /**
  11222. * Whether the navigator and scrollbar should adapt to updated data
  11223. * in the base X axis. When loading data async, as in the demo below,
  11224. * this should be `false`. Otherwise new data will trigger navigator
  11225. * redraw, which will cause unwanted looping. In the demo below, the
  11226. * data in the navigator is set only once. On navigating, only the main
  11227. * chart content is updated.
  11228. *
  11229. * @sample {highstock} stock/demo/lazy-loading/
  11230. * Set to false with async data loading
  11231. *
  11232. * @type {boolean}
  11233. * @default true
  11234. * @apioption navigator.adaptToUpdatedData
  11235. */
  11236. /**
  11237. * An integer identifying the index to use for the base series, or a
  11238. * string representing the id of the series.
  11239. *
  11240. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  11241. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  11242. *
  11243. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  11244. *
  11245. * @deprecated
  11246. * @type {number|string}
  11247. * @default 0
  11248. * @apioption navigator.baseSeries
  11249. */
  11250. /**
  11251. * Enable or disable the navigator.
  11252. *
  11253. * @sample {highstock} stock/navigator/enabled/
  11254. * Disable the navigator
  11255. *
  11256. * @type {boolean}
  11257. * @default true
  11258. * @apioption navigator.enabled
  11259. */
  11260. /**
  11261. * When the chart is inverted, whether to draw the navigator on the
  11262. * opposite side.
  11263. *
  11264. * @type {boolean}
  11265. * @default false
  11266. * @since 5.0.8
  11267. * @apioption navigator.opposite
  11268. */
  11269. /**
  11270. * The height of the navigator.
  11271. *
  11272. * @sample {highstock} stock/navigator/height/
  11273. * A higher navigator
  11274. */
  11275. height: 40,
  11276. /**
  11277. * The distance from the nearest element, the X axis or X axis labels.
  11278. *
  11279. * @sample {highstock} stock/navigator/margin/
  11280. * A margin of 2 draws the navigator closer to the X axis labels
  11281. */
  11282. margin: 25,
  11283. /**
  11284. * Whether the mask should be inside the range marking the zoomed
  11285. * range, or outside. In Highcharts Stock 1.x it was always `false`.
  11286. *
  11287. * @sample {highstock} stock/navigator/maskinside-false/
  11288. * False, mask outside
  11289. *
  11290. * @since 2.0
  11291. */
  11292. maskInside: true,
  11293. /**
  11294. * Options for the handles for dragging the zoomed area.
  11295. *
  11296. * @sample {highstock} stock/navigator/handles/
  11297. * Colored handles
  11298. */
  11299. handles: {
  11300. /**
  11301. * Width for handles.
  11302. *
  11303. * @sample {highstock} stock/navigator/styled-handles/
  11304. * Styled handles
  11305. *
  11306. * @since 6.0.0
  11307. */
  11308. width: 7,
  11309. /**
  11310. * Height for handles.
  11311. *
  11312. * @sample {highstock} stock/navigator/styled-handles/
  11313. * Styled handles
  11314. *
  11315. * @since 6.0.0
  11316. */
  11317. height: 15,
  11318. /**
  11319. * Array to define shapes of handles. 0-index for left, 1-index for
  11320. * right.
  11321. *
  11322. * Additionally, the URL to a graphic can be given on this form:
  11323. * `url(graphic.png)`. Note that for the image to be applied to
  11324. * exported charts, its URL needs to be accessible by the export
  11325. * server.
  11326. *
  11327. * Custom callbacks for symbol path generation can also be added to
  11328. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  11329. * used by its method name, as shown in the demo.
  11330. *
  11331. * @sample {highstock} stock/navigator/styled-handles/
  11332. * Styled handles
  11333. *
  11334. * @type {Array<string>}
  11335. * @default ["navigator-handle", "navigator-handle"]
  11336. * @since 6.0.0
  11337. */
  11338. symbols: ['navigator-handle', 'navigator-handle'],
  11339. /**
  11340. * Allows to enable/disable handles.
  11341. *
  11342. * @since 6.0.0
  11343. */
  11344. enabled: true,
  11345. /**
  11346. * The width for the handle border and the stripes inside.
  11347. *
  11348. * @sample {highstock} stock/navigator/styled-handles/
  11349. * Styled handles
  11350. *
  11351. * @since 6.0.0
  11352. * @apioption navigator.handles.lineWidth
  11353. */
  11354. lineWidth: 1,
  11355. /**
  11356. * The fill for the handle.
  11357. *
  11358. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11359. */
  11360. backgroundColor: palette.neutralColor5,
  11361. /**
  11362. * The stroke for the handle border and the stripes inside.
  11363. *
  11364. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11365. */
  11366. borderColor: palette.neutralColor40
  11367. },
  11368. /**
  11369. * The color of the mask covering the areas of the navigator series
  11370. * that are currently not visible in the main series. The default
  11371. * color is bluish with an opacity of 0.3 to see the series below.
  11372. *
  11373. * @see In styled mode, the mask is styled with the
  11374. * `.highcharts-navigator-mask` and
  11375. * `.highcharts-navigator-mask-inside` classes.
  11376. *
  11377. * @sample {highstock} stock/navigator/maskfill/
  11378. * Blue, semi transparent mask
  11379. *
  11380. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11381. * @default rgba(102,133,194,0.3)
  11382. */
  11383. maskFill: color(palette.highlightColor60).setOpacity(0.3).get(),
  11384. /**
  11385. * The color of the line marking the currently zoomed area in the
  11386. * navigator.
  11387. *
  11388. * @sample {highstock} stock/navigator/outline/
  11389. * 2px blue outline
  11390. *
  11391. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11392. * @default #cccccc
  11393. */
  11394. outlineColor: palette.neutralColor20,
  11395. /**
  11396. * The width of the line marking the currently zoomed area in the
  11397. * navigator.
  11398. *
  11399. * @see In styled mode, the outline stroke width is set with the
  11400. * `.highcharts-navigator-outline` class.
  11401. *
  11402. * @sample {highstock} stock/navigator/outline/
  11403. * 2px blue outline
  11404. *
  11405. * @type {number}
  11406. */
  11407. outlineWidth: 1,
  11408. /**
  11409. * Options for the navigator series. Available options are the same
  11410. * as any series, documented at [plotOptions](#plotOptions.series)
  11411. * and [series](#series).
  11412. *
  11413. * Unless data is explicitly defined on navigator.series, the data
  11414. * is borrowed from the first series in the chart.
  11415. *
  11416. * Default series options for the navigator series are:
  11417. * ```js
  11418. * series: {
  11419. * type: 'areaspline',
  11420. * fillOpacity: 0.05,
  11421. * dataGrouping: {
  11422. * smoothed: true
  11423. * },
  11424. * lineWidth: 1,
  11425. * marker: {
  11426. * enabled: false
  11427. * }
  11428. * }
  11429. * ```
  11430. *
  11431. * @see In styled mode, the navigator series is styled with the
  11432. * `.highcharts-navigator-series` class.
  11433. *
  11434. * @sample {highstock} stock/navigator/series-data/
  11435. * Using a separate data set for the navigator
  11436. * @sample {highstock} stock/navigator/series/
  11437. * A green navigator series
  11438. *
  11439. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  11440. */
  11441. series: {
  11442. /**
  11443. * The type of the navigator series.
  11444. *
  11445. * Heads up:
  11446. * In column-type navigator, zooming is limited to at least one
  11447. * point with its `pointRange`.
  11448. *
  11449. * @sample {highstock} stock/navigator/column/
  11450. * Column type navigator
  11451. *
  11452. * @type {string}
  11453. * @default {highstock} `areaspline` if defined, otherwise `line`
  11454. * @default {gantt} gantt
  11455. */
  11456. type: defaultSeriesType,
  11457. /**
  11458. * The fill opacity of the navigator series.
  11459. */
  11460. fillOpacity: 0.05,
  11461. /**
  11462. * The pixel line width of the navigator series.
  11463. */
  11464. lineWidth: 1,
  11465. /**
  11466. * @ignore-option
  11467. */
  11468. compare: null,
  11469. /**
  11470. * Unless data is explicitly defined, the data is borrowed from the
  11471. * first series in the chart.
  11472. *
  11473. * @type {Array<number|Array<number|string|null>|object|null>}
  11474. * @product highstock
  11475. * @apioption navigator.series.data
  11476. */
  11477. /**
  11478. * Data grouping options for the navigator series.
  11479. *
  11480. * @extends plotOptions.series.dataGrouping
  11481. */
  11482. dataGrouping: {
  11483. approximation: 'average',
  11484. enabled: true,
  11485. groupPixelWidth: 2,
  11486. // Replace smoothed property by anchors, #12455.
  11487. firstAnchor: 'firstPoint',
  11488. anchor: 'middle',
  11489. lastAnchor: 'lastPoint',
  11490. // Day and week differs from plotOptions.series.dataGrouping
  11491. units: [
  11492. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  11493. ['second', [1, 2, 5, 10, 15, 30]],
  11494. ['minute', [1, 2, 5, 10, 15, 30]],
  11495. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  11496. ['day', [1, 2, 3, 4]],
  11497. ['week', [1, 2, 3]],
  11498. ['month', [1, 3, 6]],
  11499. ['year', null]
  11500. ]
  11501. },
  11502. /**
  11503. * Data label options for the navigator series. Data labels are
  11504. * disabled by default on the navigator series.
  11505. *
  11506. * @extends plotOptions.series.dataLabels
  11507. */
  11508. dataLabels: {
  11509. enabled: false,
  11510. zIndex: 2 // #1839
  11511. },
  11512. id: 'highcharts-navigator-series',
  11513. className: 'highcharts-navigator-series',
  11514. /**
  11515. * Sets the fill color of the navigator series.
  11516. *
  11517. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11518. * @apioption navigator.series.color
  11519. */
  11520. /**
  11521. * Line color for the navigator series. Allows setting the color
  11522. * while disallowing the default candlestick setting.
  11523. *
  11524. * @type {Highcharts.ColorString|null}
  11525. */
  11526. lineColor: null,
  11527. marker: {
  11528. enabled: false
  11529. },
  11530. /**
  11531. * Since Highcharts Stock v8, default value is the same as default
  11532. * `pointRange` defined for a specific type (e.g. `null` for
  11533. * column type).
  11534. *
  11535. * In Highcharts Stock version < 8, defaults to 0.
  11536. *
  11537. * @extends plotOptions.series.pointRange
  11538. * @type {number|null}
  11539. * @apioption navigator.series.pointRange
  11540. */
  11541. /**
  11542. * The threshold option. Setting it to 0 will make the default
  11543. * navigator area series draw its area from the 0 value and up.
  11544. *
  11545. * @type {number|null}
  11546. */
  11547. threshold: null
  11548. },
  11549. /**
  11550. * Options for the navigator X axis. Default series options for the
  11551. * navigator xAxis are:
  11552. * ```js
  11553. * xAxis: {
  11554. * tickWidth: 0,
  11555. * lineWidth: 0,
  11556. * gridLineWidth: 1,
  11557. * tickPixelInterval: 200,
  11558. * labels: {
  11559. * align: 'left',
  11560. * style: {
  11561. * color: '#888'
  11562. * },
  11563. * x: 3,
  11564. * y: -4
  11565. * }
  11566. * }
  11567. * ```
  11568. *
  11569. * @extends xAxis
  11570. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  11571. * showEmpty, maxRange
  11572. */
  11573. xAxis: {
  11574. /**
  11575. * Additional range on the right side of the xAxis. Works similar to
  11576. * xAxis.maxPadding, but value is set in milliseconds.
  11577. * Can be set for both, main xAxis and navigator's xAxis.
  11578. *
  11579. * @since 6.0.0
  11580. */
  11581. overscroll: 0,
  11582. className: 'highcharts-navigator-xaxis',
  11583. tickLength: 0,
  11584. lineWidth: 0,
  11585. gridLineColor: palette.neutralColor10,
  11586. gridLineWidth: 1,
  11587. tickPixelInterval: 200,
  11588. labels: {
  11589. align: 'left',
  11590. /**
  11591. * @type {Highcharts.CSSObject}
  11592. */
  11593. style: {
  11594. /** @ignore */
  11595. color: palette.neutralColor40
  11596. },
  11597. x: 3,
  11598. y: -4
  11599. },
  11600. crosshair: false
  11601. },
  11602. /**
  11603. * Options for the navigator Y axis. Default series options for the
  11604. * navigator yAxis are:
  11605. * ```js
  11606. * yAxis: {
  11607. * gridLineWidth: 0,
  11608. * startOnTick: false,
  11609. * endOnTick: false,
  11610. * minPadding: 0.1,
  11611. * maxPadding: 0.1,
  11612. * labels: {
  11613. * enabled: false
  11614. * },
  11615. * title: {
  11616. * text: null
  11617. * },
  11618. * tickWidth: 0
  11619. * }
  11620. * ```
  11621. *
  11622. * @extends yAxis
  11623. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  11624. * showEmpty, scrollbar, top, units, maxRange, minLength,
  11625. * maxLength, resize
  11626. */
  11627. yAxis: {
  11628. className: 'highcharts-navigator-yaxis',
  11629. gridLineWidth: 0,
  11630. startOnTick: false,
  11631. endOnTick: false,
  11632. minPadding: 0.1,
  11633. maxPadding: 0.1,
  11634. labels: {
  11635. enabled: false
  11636. },
  11637. crosshair: false,
  11638. title: {
  11639. text: null
  11640. },
  11641. tickLength: 0,
  11642. tickWidth: 0
  11643. }
  11644. }
  11645. });
  11646. /* eslint-disable no-invalid-this, valid-jsdoc */
  11647. /**
  11648. * Draw one of the handles on the side of the zoomed range in the navigator
  11649. *
  11650. * @private
  11651. * @function Highcharts.Renderer#symbols.navigator-handle
  11652. * @param {number} x
  11653. * @param {number} y
  11654. * @param {number} w
  11655. * @param {number} h
  11656. * @param {Highcharts.NavigatorHandlesOptions} options
  11657. * @return {Highcharts.SVGPathArray}
  11658. * Path to be used in a handle
  11659. */
  11660. H.Renderer.prototype.symbols['navigator-handle'] = function (x, y, w, h, options) {
  11661. var halfWidth = (options && options.width || 0) / 2,
  11662. markerPosition = Math.round(halfWidth / 3) + 0.5,
  11663. height = options && options.height || 0;
  11664. return [
  11665. ['M', -halfWidth - 1, 0.5],
  11666. ['L', halfWidth, 0.5],
  11667. ['L', halfWidth, height + 0.5],
  11668. ['L', -halfWidth - 1, height + 0.5],
  11669. ['L', -halfWidth - 1, 0.5],
  11670. ['M', -markerPosition, 4],
  11671. ['L', -markerPosition, height - 3],
  11672. ['M', markerPosition - 1, 4],
  11673. ['L', markerPosition - 1, height - 3]
  11674. ];
  11675. };
  11676. /**
  11677. * The Navigator class
  11678. *
  11679. * @private
  11680. * @class
  11681. * @name Highcharts.Navigator
  11682. *
  11683. * @param {Highcharts.Chart} chart
  11684. * Chart object
  11685. */
  11686. var Navigator = /** @class */ (function () {
  11687. function Navigator(chart) {
  11688. this.baseSeries = void 0;
  11689. this.chart = void 0;
  11690. this.handles = void 0;
  11691. this.height = void 0;
  11692. this.left = void 0;
  11693. this.navigatorEnabled = void 0;
  11694. this.navigatorGroup = void 0;
  11695. this.navigatorOptions = void 0;
  11696. this.navigatorSeries = void 0;
  11697. this.navigatorSize = void 0;
  11698. this.opposite = void 0;
  11699. this.outline = void 0;
  11700. this.outlineHeight = void 0;
  11701. this.range = void 0;
  11702. this.rendered = void 0;
  11703. this.shades = void 0;
  11704. this.size = void 0;
  11705. this.top = void 0;
  11706. this.xAxis = void 0;
  11707. this.yAxis = void 0;
  11708. this.zoomedMax = void 0;
  11709. this.zoomedMin = void 0;
  11710. this.init(chart);
  11711. }
  11712. /**
  11713. * Draw one of the handles on the side of the zoomed range in the navigator
  11714. *
  11715. * @private
  11716. * @function Highcharts.Navigator#drawHandle
  11717. *
  11718. * @param {number} x
  11719. * The x center for the handle
  11720. *
  11721. * @param {number} index
  11722. * 0 for left and 1 for right
  11723. *
  11724. * @param {boolean|undefined} inverted
  11725. * flag for chart.inverted
  11726. *
  11727. * @param {string} verb
  11728. * use 'animate' or 'attr'
  11729. */
  11730. Navigator.prototype.drawHandle = function (x, index, inverted, verb) {
  11731. var navigator = this,
  11732. height = navigator.navigatorOptions.handles.height;
  11733. // Place it
  11734. navigator.handles[index][verb](inverted ? {
  11735. translateX: Math.round(navigator.left + navigator.height / 2),
  11736. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  11737. } : {
  11738. translateX: Math.round(navigator.left + parseInt(x, 10)),
  11739. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  11740. });
  11741. };
  11742. /**
  11743. * Render outline around the zoomed range
  11744. *
  11745. * @private
  11746. * @function Highcharts.Navigator#drawOutline
  11747. *
  11748. * @param {number} zoomedMin
  11749. * in pixels position where zoomed range starts
  11750. *
  11751. * @param {number} zoomedMax
  11752. * in pixels position where zoomed range ends
  11753. *
  11754. * @param {boolean|undefined} inverted
  11755. * flag if chart is inverted
  11756. *
  11757. * @param {string} verb
  11758. * use 'animate' or 'attr'
  11759. */
  11760. Navigator.prototype.drawOutline = function (zoomedMin, zoomedMax, inverted, verb) {
  11761. var navigator = this,
  11762. maskInside = navigator.navigatorOptions.maskInside,
  11763. outlineWidth = navigator.outline.strokeWidth(),
  11764. halfOutline = outlineWidth / 2,
  11765. outlineCorrection = (outlineWidth % 2) / 2, // #5800
  11766. outlineHeight = navigator.outlineHeight,
  11767. scrollbarHeight = navigator.scrollbarHeight || 0,
  11768. navigatorSize = navigator.size,
  11769. left = navigator.left - scrollbarHeight,
  11770. navigatorTop = navigator.top,
  11771. verticalMin,
  11772. path;
  11773. if (inverted) {
  11774. left -= halfOutline;
  11775. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  11776. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  11777. path = [
  11778. ['M', left + outlineHeight, navigatorTop - scrollbarHeight - outlineCorrection],
  11779. ['L', left + outlineHeight, verticalMin],
  11780. ['L', left, verticalMin],
  11781. ['L', left, zoomedMax],
  11782. ['L', left + outlineHeight, zoomedMax],
  11783. ['L', left + outlineHeight, navigatorTop + navigatorSize + scrollbarHeight]
  11784. ];
  11785. if (maskInside) {
  11786. path.push(['M', left + outlineHeight, verticalMin - halfOutline], // upper left of zoomed range
  11787. ['L', left + outlineHeight, zoomedMax + halfOutline] // upper right of z.r.
  11788. );
  11789. }
  11790. }
  11791. else {
  11792. zoomedMin += left + scrollbarHeight - outlineCorrection;
  11793. zoomedMax += left + scrollbarHeight - outlineCorrection;
  11794. navigatorTop += halfOutline;
  11795. path = [
  11796. ['M', left, navigatorTop],
  11797. ['L', zoomedMin, navigatorTop],
  11798. ['L', zoomedMin, navigatorTop + outlineHeight],
  11799. ['L', zoomedMax, navigatorTop + outlineHeight],
  11800. ['L', zoomedMax, navigatorTop],
  11801. ['L', left + navigatorSize + scrollbarHeight * 2, navigatorTop] // right
  11802. ];
  11803. if (maskInside) {
  11804. path.push(['M', zoomedMin - halfOutline, navigatorTop], // upper left of zoomed range
  11805. ['L', zoomedMax + halfOutline, navigatorTop] // upper right of z.r.
  11806. );
  11807. }
  11808. }
  11809. navigator.outline[verb]({
  11810. d: path
  11811. });
  11812. };
  11813. /**
  11814. * Render outline around the zoomed range
  11815. *
  11816. * @private
  11817. * @function Highcharts.Navigator#drawMasks
  11818. *
  11819. * @param {number} zoomedMin
  11820. * in pixels position where zoomed range starts
  11821. *
  11822. * @param {number} zoomedMax
  11823. * in pixels position where zoomed range ends
  11824. *
  11825. * @param {boolean|undefined} inverted
  11826. * flag if chart is inverted
  11827. *
  11828. * @param {string} verb
  11829. * use 'animate' or 'attr'
  11830. */
  11831. Navigator.prototype.drawMasks = function (zoomedMin, zoomedMax, inverted, verb) {
  11832. var navigator = this,
  11833. left = navigator.left,
  11834. top = navigator.top,
  11835. navigatorHeight = navigator.height,
  11836. height,
  11837. width,
  11838. x,
  11839. y;
  11840. // Determine rectangle position & size
  11841. // According to (non)inverted position:
  11842. if (inverted) {
  11843. x = [left, left, left];
  11844. y = [top, top + zoomedMin, top + zoomedMax];
  11845. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  11846. height = [
  11847. zoomedMin,
  11848. zoomedMax - zoomedMin,
  11849. navigator.size - zoomedMax
  11850. ];
  11851. }
  11852. else {
  11853. x = [left, left + zoomedMin, left + zoomedMax];
  11854. y = [top, top, top];
  11855. width = [
  11856. zoomedMin,
  11857. zoomedMax - zoomedMin,
  11858. navigator.size - zoomedMax
  11859. ];
  11860. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  11861. }
  11862. navigator.shades.forEach(function (shade, i) {
  11863. shade[verb]({
  11864. x: x[i],
  11865. y: y[i],
  11866. width: width[i],
  11867. height: height[i]
  11868. });
  11869. });
  11870. };
  11871. /**
  11872. * Generate DOM elements for a navigator:
  11873. *
  11874. * - main navigator group
  11875. *
  11876. * - all shades
  11877. *
  11878. * - outline
  11879. *
  11880. * - handles
  11881. *
  11882. * @private
  11883. * @function Highcharts.Navigator#renderElements
  11884. */
  11885. Navigator.prototype.renderElements = function () {
  11886. var navigator = this,
  11887. navigatorOptions = navigator.navigatorOptions,
  11888. maskInside = navigatorOptions.maskInside,
  11889. chart = navigator.chart,
  11890. inverted = chart.inverted,
  11891. renderer = chart.renderer,
  11892. navigatorGroup,
  11893. mouseCursor = {
  11894. cursor: inverted ? 'ns-resize' : 'ew-resize'
  11895. };
  11896. // Create the main navigator group
  11897. navigator.navigatorGroup = navigatorGroup = renderer.g('navigator')
  11898. .attr({
  11899. zIndex: 8,
  11900. visibility: 'hidden'
  11901. })
  11902. .add();
  11903. // Create masks, each mask will get events and fill:
  11904. [
  11905. !maskInside,
  11906. maskInside,
  11907. !maskInside
  11908. ].forEach(function (hasMask, index) {
  11909. navigator.shades[index] = renderer.rect()
  11910. .addClass('highcharts-navigator-mask' +
  11911. (index === 1 ? '-inside' : '-outside'))
  11912. .add(navigatorGroup);
  11913. if (!chart.styledMode) {
  11914. navigator.shades[index]
  11915. .attr({
  11916. fill: hasMask ?
  11917. navigatorOptions.maskFill :
  11918. 'rgba(0,0,0,0)'
  11919. })
  11920. .css((index === 1) && mouseCursor);
  11921. }
  11922. });
  11923. // Create the outline:
  11924. navigator.outline = renderer.path()
  11925. .addClass('highcharts-navigator-outline')
  11926. .add(navigatorGroup);
  11927. if (!chart.styledMode) {
  11928. navigator.outline.attr({
  11929. 'stroke-width': navigatorOptions.outlineWidth,
  11930. stroke: navigatorOptions.outlineColor
  11931. });
  11932. }
  11933. // Create the handlers:
  11934. if (navigatorOptions.handles.enabled) {
  11935. [0, 1].forEach(function (index) {
  11936. navigatorOptions.handles.inverted = chart.inverted;
  11937. navigator.handles[index] = renderer.symbol(navigatorOptions.handles.symbols[index], -navigatorOptions.handles.width / 2 - 1, 0, navigatorOptions.handles.width, navigatorOptions.handles.height, navigatorOptions.handles);
  11938. // zIndex = 6 for right handle, 7 for left.
  11939. // Can't be 10, because of the tooltip in inverted chart #2908
  11940. navigator.handles[index].attr({ zIndex: 7 - index })
  11941. .addClass('highcharts-navigator-handle ' +
  11942. 'highcharts-navigator-handle-' +
  11943. ['left', 'right'][index]).add(navigatorGroup);
  11944. if (!chart.styledMode) {
  11945. var handlesOptions = navigatorOptions.handles;
  11946. navigator.handles[index]
  11947. .attr({
  11948. fill: handlesOptions.backgroundColor,
  11949. stroke: handlesOptions.borderColor,
  11950. 'stroke-width': handlesOptions.lineWidth
  11951. })
  11952. .css(mouseCursor);
  11953. }
  11954. });
  11955. }
  11956. };
  11957. /**
  11958. * Update navigator
  11959. *
  11960. * @private
  11961. * @function Highcharts.Navigator#update
  11962. *
  11963. * @param {Highcharts.NavigatorOptions} options
  11964. * Options to merge in when updating navigator
  11965. */
  11966. Navigator.prototype.update = function (options) {
  11967. // Remove references to old navigator series in base series
  11968. (this.series || []).forEach(function (series) {
  11969. if (series.baseSeries) {
  11970. delete series.baseSeries.navigatorSeries;
  11971. }
  11972. });
  11973. // Destroy and rebuild navigator
  11974. this.destroy();
  11975. var chartOptions = this.chart.options;
  11976. merge(true, chartOptions.navigator, this.options, options);
  11977. this.init(this.chart);
  11978. };
  11979. /**
  11980. * Render the navigator
  11981. *
  11982. * @private
  11983. * @function Highcharts.Navigator#render
  11984. * @param {number} min
  11985. * X axis value minimum
  11986. * @param {number} max
  11987. * X axis value maximum
  11988. * @param {number} [pxMin]
  11989. * Pixel value minimum
  11990. * @param {number} [pxMax]
  11991. * Pixel value maximum
  11992. * @return {void}
  11993. */
  11994. Navigator.prototype.render = function (min, max, pxMin, pxMax) {
  11995. var navigator = this,
  11996. chart = navigator.chart,
  11997. navigatorWidth,
  11998. scrollbarLeft,
  11999. scrollbarTop,
  12000. scrollbarHeight = navigator.scrollbarHeight,
  12001. navigatorSize,
  12002. xAxis = navigator.xAxis,
  12003. pointRange = xAxis.pointRange || 0,
  12004. scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis,
  12005. navigatorEnabled = navigator.navigatorEnabled,
  12006. zoomedMin,
  12007. zoomedMax,
  12008. rendered = navigator.rendered,
  12009. inverted = chart.inverted,
  12010. verb,
  12011. newMin,
  12012. newMax,
  12013. currentRange,
  12014. minRange = chart.xAxis[0].minRange,
  12015. maxRange = chart.xAxis[0].options.maxRange;
  12016. // Don't redraw while moving the handles (#4703).
  12017. if (this.hasDragged && !defined(pxMin)) {
  12018. return;
  12019. }
  12020. min = correctFloat(min - pointRange / 2);
  12021. max = correctFloat(max + pointRange / 2);
  12022. // Don't render the navigator until we have data (#486, #4202, #5172).
  12023. if (!isNumber(min) || !isNumber(max)) {
  12024. // However, if navigator was already rendered, we may need to resize
  12025. // it. For example hidden series, but visible navigator (#6022).
  12026. if (rendered) {
  12027. pxMin = 0;
  12028. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  12029. }
  12030. else {
  12031. return;
  12032. }
  12033. }
  12034. navigator.left = pick(xAxis.left,
  12035. // in case of scrollbar only, without navigator
  12036. chart.plotLeft + scrollbarHeight +
  12037. (inverted ? chart.plotWidth : 0));
  12038. navigator.size = zoomedMax = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  12039. 2 * scrollbarHeight);
  12040. if (inverted) {
  12041. navigatorWidth = scrollbarHeight;
  12042. }
  12043. else {
  12044. navigatorWidth = navigatorSize + 2 * scrollbarHeight;
  12045. }
  12046. // Get the pixel position of the handles
  12047. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  12048. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  12049. // Verify (#1851, #2238)
  12050. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  12051. pxMin = 0;
  12052. pxMax = navigatorWidth;
  12053. }
  12054. // Are we below the minRange? (#2618, #6191)
  12055. newMin = xAxis.toValue(pxMin, true);
  12056. newMax = xAxis.toValue(pxMax, true);
  12057. currentRange = Math.abs(correctFloat(newMax - newMin));
  12058. if (currentRange < minRange) {
  12059. if (this.grabbedLeft) {
  12060. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  12061. }
  12062. else if (this.grabbedRight) {
  12063. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  12064. }
  12065. }
  12066. else if (defined(maxRange) &&
  12067. correctFloat(currentRange - pointRange) > maxRange) {
  12068. if (this.grabbedLeft) {
  12069. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  12070. }
  12071. else if (this.grabbedRight) {
  12072. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  12073. }
  12074. }
  12075. // Handles are allowed to cross, but never exceed the plot area
  12076. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  12077. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  12078. navigator.zoomedMax - navigator.fixedWidth :
  12079. Math.min(pxMin, pxMax), 0, zoomedMax);
  12080. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  12081. zoomedMax = Math.round(navigator.zoomedMax);
  12082. zoomedMin = Math.round(navigator.zoomedMin);
  12083. if (navigatorEnabled) {
  12084. navigator.navigatorGroup.attr({
  12085. visibility: 'visible'
  12086. });
  12087. // Place elements
  12088. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  12089. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  12090. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  12091. if (navigator.navigatorOptions.handles.enabled) {
  12092. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  12093. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  12094. }
  12095. }
  12096. if (navigator.scrollbar) {
  12097. if (inverted) {
  12098. scrollbarTop = navigator.top - scrollbarHeight;
  12099. scrollbarLeft = navigator.left - scrollbarHeight +
  12100. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  12101. // Multiple axes has offsets:
  12102. (scrollbarXAxis.titleOffset || 0) +
  12103. // Self margin from the axis.title
  12104. scrollbarXAxis.axisTitleMargin);
  12105. scrollbarHeight = navigatorSize + 2 * scrollbarHeight;
  12106. }
  12107. else {
  12108. scrollbarTop = navigator.top + (navigatorEnabled ?
  12109. navigator.height :
  12110. -scrollbarHeight);
  12111. scrollbarLeft = navigator.left - scrollbarHeight;
  12112. }
  12113. // Reposition scrollbar
  12114. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  12115. // Keep scale 0-1
  12116. navigator.scrollbar.setRange(
  12117. // Use real value, not rounded because range can be very small
  12118. // (#1716)
  12119. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  12120. }
  12121. navigator.rendered = true;
  12122. };
  12123. /**
  12124. * Set up the mouse and touch events for the navigator
  12125. *
  12126. * @private
  12127. * @function Highcharts.Navigator#addMouseEvents
  12128. */
  12129. Navigator.prototype.addMouseEvents = function () {
  12130. var navigator = this,
  12131. chart = navigator.chart,
  12132. container = chart.container,
  12133. eventsToUnbind = [],
  12134. mouseMoveHandler,
  12135. mouseUpHandler;
  12136. /**
  12137. * Create mouse events' handlers.
  12138. * Make them as separate functions to enable wrapping them:
  12139. */
  12140. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  12141. navigator.onMouseMove(e);
  12142. };
  12143. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  12144. navigator.onMouseUp(e);
  12145. };
  12146. // Add shades and handles mousedown events
  12147. eventsToUnbind = navigator.getPartsEvents('mousedown');
  12148. // Add mouse move and mouseup events. These are bind to doc/container,
  12149. // because Navigator.grabbedSomething flags are stored in mousedown
  12150. // events
  12151. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  12152. // Touch events
  12153. if (hasTouch) {
  12154. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  12155. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  12156. }
  12157. navigator.eventsToUnbind = eventsToUnbind;
  12158. // Data events
  12159. if (navigator.series && navigator.series[0]) {
  12160. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  12161. chart.navigator.modifyNavigatorAxisExtremes();
  12162. }));
  12163. }
  12164. };
  12165. /**
  12166. * Generate events for handles and masks
  12167. *
  12168. * @private
  12169. * @function Highcharts.Navigator#getPartsEvents
  12170. *
  12171. * @param {string} eventName
  12172. * Event name handler, 'mousedown' or 'touchstart'
  12173. *
  12174. * @return {Array<Function>}
  12175. * An array of functions to remove navigator functions from the
  12176. * events again.
  12177. */
  12178. Navigator.prototype.getPartsEvents = function (eventName) {
  12179. var navigator = this,
  12180. events = [];
  12181. ['shades', 'handles'].forEach(function (name) {
  12182. navigator[name].forEach(function (navigatorItem, index) {
  12183. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  12184. navigator[name + 'Mousedown'](e, index);
  12185. }));
  12186. });
  12187. });
  12188. return events;
  12189. };
  12190. /**
  12191. * Mousedown on a shaded mask, either:
  12192. *
  12193. * - will be stored for future drag&drop
  12194. *
  12195. * - will directly shift to a new range
  12196. *
  12197. * @private
  12198. * @function Highcharts.Navigator#shadesMousedown
  12199. *
  12200. * @param {Highcharts.PointerEventObject} e
  12201. * Mouse event
  12202. *
  12203. * @param {number} index
  12204. * Index of a mask in Navigator.shades array
  12205. */
  12206. Navigator.prototype.shadesMousedown = function (e, index) {
  12207. e = this.chart.pointer.normalize(e);
  12208. var navigator = this,
  12209. chart = navigator.chart,
  12210. xAxis = navigator.xAxis,
  12211. zoomedMin = navigator.zoomedMin,
  12212. navigatorPosition = navigator.left,
  12213. navigatorSize = navigator.size,
  12214. range = navigator.range,
  12215. chartX = e.chartX,
  12216. fixedMax,
  12217. fixedMin,
  12218. ext,
  12219. left;
  12220. // For inverted chart, swap some options:
  12221. if (chart.inverted) {
  12222. chartX = e.chartY;
  12223. navigatorPosition = navigator.top;
  12224. }
  12225. if (index === 1) {
  12226. // Store information for drag&drop
  12227. navigator.grabbedCenter = chartX;
  12228. navigator.fixedWidth = range;
  12229. navigator.dragOffset = chartX - zoomedMin;
  12230. }
  12231. else {
  12232. // Shift the range by clicking on shaded areas
  12233. left = chartX - navigatorPosition - range / 2;
  12234. if (index === 0) {
  12235. left = Math.max(0, left);
  12236. }
  12237. else if (index === 2 && left + range >= navigatorSize) {
  12238. left = navigatorSize - range;
  12239. if (navigator.reversedExtremes) {
  12240. // #7713
  12241. left -= range;
  12242. fixedMin = navigator.getUnionExtremes().dataMin;
  12243. }
  12244. else {
  12245. // #2293, #3543
  12246. fixedMax = navigator.getUnionExtremes().dataMax;
  12247. }
  12248. }
  12249. if (left !== zoomedMin) { // it has actually moved
  12250. navigator.fixedWidth = range; // #1370
  12251. ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  12252. if (defined(ext.min)) { // #7411
  12253. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  12254. { trigger: 'navigator' });
  12255. }
  12256. }
  12257. }
  12258. };
  12259. /**
  12260. * Mousedown on a handle mask.
  12261. * Will store necessary information for drag&drop.
  12262. *
  12263. * @private
  12264. * @function Highcharts.Navigator#handlesMousedown
  12265. * @param {Highcharts.PointerEventObject} e
  12266. * Mouse event
  12267. * @param {number} index
  12268. * Index of a handle in Navigator.handles array
  12269. * @return {void}
  12270. */
  12271. Navigator.prototype.handlesMousedown = function (e, index) {
  12272. e = this.chart.pointer.normalize(e);
  12273. var navigator = this,
  12274. chart = navigator.chart,
  12275. baseXAxis = chart.xAxis[0],
  12276. // For reversed axes, min and max are changed,
  12277. // so the other extreme should be stored
  12278. reverse = navigator.reversedExtremes;
  12279. if (index === 0) {
  12280. // Grab the left handle
  12281. navigator.grabbedLeft = true;
  12282. navigator.otherHandlePos = navigator.zoomedMax;
  12283. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  12284. }
  12285. else {
  12286. // Grab the right handle
  12287. navigator.grabbedRight = true;
  12288. navigator.otherHandlePos = navigator.zoomedMin;
  12289. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  12290. }
  12291. chart.fixedRange = null;
  12292. };
  12293. /**
  12294. * Mouse move event based on x/y mouse position.
  12295. *
  12296. * @private
  12297. * @function Highcharts.Navigator#onMouseMove
  12298. *
  12299. * @param {Highcharts.PointerEventObject} e
  12300. * Mouse event
  12301. */
  12302. Navigator.prototype.onMouseMove = function (e) {
  12303. var navigator = this,
  12304. chart = navigator.chart,
  12305. left = navigator.left,
  12306. navigatorSize = navigator.navigatorSize,
  12307. range = navigator.range,
  12308. dragOffset = navigator.dragOffset,
  12309. inverted = chart.inverted,
  12310. chartX;
  12311. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  12312. // the finger down in the center of the scrollbar. This should be
  12313. // ignored.
  12314. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  12315. e = chart.pointer.normalize(e);
  12316. chartX = e.chartX;
  12317. // Swap some options for inverted chart
  12318. if (inverted) {
  12319. left = navigator.top;
  12320. chartX = e.chartY;
  12321. }
  12322. // Drag left handle or top handle
  12323. if (navigator.grabbedLeft) {
  12324. navigator.hasDragged = true;
  12325. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  12326. // Drag right handle or bottom handle
  12327. }
  12328. else if (navigator.grabbedRight) {
  12329. navigator.hasDragged = true;
  12330. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  12331. // Drag scrollbar or open area in navigator
  12332. }
  12333. else if (navigator.grabbedCenter) {
  12334. navigator.hasDragged = true;
  12335. if (chartX < dragOffset) { // outside left
  12336. chartX = dragOffset;
  12337. // outside right
  12338. }
  12339. else if (chartX >
  12340. navigatorSize + dragOffset - range) {
  12341. chartX = navigatorSize + dragOffset - range;
  12342. }
  12343. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  12344. }
  12345. if (navigator.hasDragged &&
  12346. navigator.scrollbar &&
  12347. pick(navigator.scrollbar.options.liveRedraw,
  12348. // By default, don't run live redraw on VML, on touch
  12349. // devices or if the chart is in boost.
  12350. H.svg && !isTouchDevice && !this.chart.isBoosting)) {
  12351. e.DOMType = e.type; // DOMType is for IE8
  12352. setTimeout(function () {
  12353. navigator.onMouseUp(e);
  12354. }, 0);
  12355. }
  12356. }
  12357. };
  12358. /**
  12359. * Mouse up event based on x/y mouse position.
  12360. *
  12361. * @private
  12362. * @function Highcharts.Navigator#onMouseUp
  12363. * @param {Highcharts.PointerEventObject} e
  12364. * Mouse event
  12365. * @return {void}
  12366. */
  12367. Navigator.prototype.onMouseUp = function (e) {
  12368. var navigator = this,
  12369. chart = navigator.chart,
  12370. xAxis = navigator.xAxis,
  12371. scrollbar = navigator.scrollbar,
  12372. DOMEvent = e.DOMEvent || e,
  12373. inverted = chart.inverted,
  12374. verb = navigator.rendered && !navigator.hasDragged ?
  12375. 'animate' : 'attr',
  12376. zoomedMax,
  12377. zoomedMin,
  12378. unionExtremes,
  12379. fixedMin,
  12380. fixedMax,
  12381. ext;
  12382. if (
  12383. // MouseUp is called for both, navigator and scrollbar (that order),
  12384. // which causes calling afterSetExtremes twice. Prevent first call
  12385. // by checking if scrollbar is going to set new extremes (#6334)
  12386. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  12387. e.trigger === 'scrollbar') {
  12388. unionExtremes = navigator.getUnionExtremes();
  12389. // When dragging one handle, make sure the other one doesn't change
  12390. if (navigator.zoomedMin === navigator.otherHandlePos) {
  12391. fixedMin = navigator.fixedExtreme;
  12392. }
  12393. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  12394. fixedMax = navigator.fixedExtreme;
  12395. }
  12396. // Snap to right edge (#4076)
  12397. if (navigator.zoomedMax === navigator.size) {
  12398. fixedMax = navigator.reversedExtremes ?
  12399. unionExtremes.dataMin :
  12400. unionExtremes.dataMax;
  12401. }
  12402. // Snap to left edge (#7576)
  12403. if (navigator.zoomedMin === 0) {
  12404. fixedMin = navigator.reversedExtremes ?
  12405. unionExtremes.dataMax :
  12406. unionExtremes.dataMin;
  12407. }
  12408. ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  12409. if (defined(ext.min)) {
  12410. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  12411. // Run animation when clicking buttons, scrollbar track etc,
  12412. // but not when dragging handles or scrollbar
  12413. navigator.hasDragged ? false : null, {
  12414. trigger: 'navigator',
  12415. triggerOp: 'navigator-drag',
  12416. DOMEvent: DOMEvent // #1838
  12417. });
  12418. }
  12419. }
  12420. if (e.DOMType !== 'mousemove' &&
  12421. e.DOMType !== 'touchmove') {
  12422. navigator.grabbedLeft = navigator.grabbedRight =
  12423. navigator.grabbedCenter = navigator.fixedWidth =
  12424. navigator.fixedExtreme = navigator.otherHandlePos =
  12425. navigator.hasDragged = navigator.dragOffset = null;
  12426. }
  12427. // Update position of navigator shades, outline and handles (#12573)
  12428. if (navigator.navigatorEnabled &&
  12429. isNumber(navigator.zoomedMin) &&
  12430. isNumber(navigator.zoomedMax)) {
  12431. zoomedMin = Math.round(navigator.zoomedMin);
  12432. zoomedMax = Math.round(navigator.zoomedMax);
  12433. if (navigator.shades) {
  12434. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  12435. }
  12436. if (navigator.outline) {
  12437. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  12438. }
  12439. if (navigator.navigatorOptions.handles.enabled &&
  12440. Object.keys(navigator.handles).length ===
  12441. navigator.handles.length) {
  12442. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  12443. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  12444. }
  12445. }
  12446. };
  12447. /**
  12448. * Removes the event handlers attached previously with addEvents.
  12449. *
  12450. * @private
  12451. * @function Highcharts.Navigator#removeEvents
  12452. * @return {void}
  12453. */
  12454. Navigator.prototype.removeEvents = function () {
  12455. if (this.eventsToUnbind) {
  12456. this.eventsToUnbind.forEach(function (unbind) {
  12457. unbind();
  12458. });
  12459. this.eventsToUnbind = void 0;
  12460. }
  12461. this.removeBaseSeriesEvents();
  12462. };
  12463. /**
  12464. * Remove data events.
  12465. *
  12466. * @private
  12467. * @function Highcharts.Navigator#removeBaseSeriesEvents
  12468. * @return {void}
  12469. */
  12470. Navigator.prototype.removeBaseSeriesEvents = function () {
  12471. var baseSeries = this.baseSeries || [];
  12472. if (this.navigatorEnabled && baseSeries[0]) {
  12473. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  12474. baseSeries.forEach(function (series) {
  12475. removeEvent(series, 'updatedData', this.updatedDataHandler);
  12476. }, this);
  12477. }
  12478. // We only listen for extremes-events on the first baseSeries
  12479. if (baseSeries[0].xAxis) {
  12480. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  12481. }
  12482. }
  12483. };
  12484. /**
  12485. * Initialize the Navigator object
  12486. *
  12487. * @private
  12488. * @function Highcharts.Navigator#init
  12489. *
  12490. * @param {Highcharts.Chart} chart
  12491. */
  12492. Navigator.prototype.init = function (chart) {
  12493. var chartOptions = chart.options,
  12494. navigatorOptions = chartOptions.navigator,
  12495. navigatorEnabled = navigatorOptions.enabled,
  12496. scrollbarOptions = chartOptions.scrollbar,
  12497. scrollbarEnabled = scrollbarOptions.enabled,
  12498. height = navigatorEnabled ? navigatorOptions.height : 0,
  12499. scrollbarHeight = scrollbarEnabled ?
  12500. scrollbarOptions.height :
  12501. 0;
  12502. this.handles = [];
  12503. this.shades = [];
  12504. this.chart = chart;
  12505. this.setBaseSeries();
  12506. this.height = height;
  12507. this.scrollbarHeight = scrollbarHeight;
  12508. this.scrollbarEnabled = scrollbarEnabled;
  12509. this.navigatorEnabled = navigatorEnabled;
  12510. this.navigatorOptions = navigatorOptions;
  12511. this.scrollbarOptions = scrollbarOptions;
  12512. this.outlineHeight = height + scrollbarHeight;
  12513. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  12514. var navigator = this,
  12515. baseSeries = navigator.baseSeries,
  12516. xAxisIndex = chart.xAxis.length,
  12517. yAxisIndex = chart.yAxis.length,
  12518. baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  12519. chart.xAxis[0] || { options: {} };
  12520. chart.isDirtyBox = true;
  12521. if (navigator.navigatorEnabled) {
  12522. // an x axis is required for scrollbar also
  12523. navigator.xAxis = new Axis(chart, merge({
  12524. // inherit base xAxis' break and ordinal options
  12525. breaks: baseXaxis.options.breaks,
  12526. ordinal: baseXaxis.options.ordinal
  12527. }, navigatorOptions.xAxis, {
  12528. id: 'navigator-x-axis',
  12529. yAxis: 'navigator-y-axis',
  12530. isX: true,
  12531. type: 'datetime',
  12532. index: xAxisIndex,
  12533. isInternal: true,
  12534. offset: 0,
  12535. keepOrdinalPadding: true,
  12536. startOnTick: false,
  12537. endOnTick: false,
  12538. minPadding: 0,
  12539. maxPadding: 0,
  12540. zoomEnabled: false
  12541. }, chart.inverted ? {
  12542. offsets: [scrollbarHeight, 0, -scrollbarHeight, 0],
  12543. width: height
  12544. } : {
  12545. offsets: [0, -scrollbarHeight, 0, scrollbarHeight],
  12546. height: height
  12547. }));
  12548. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  12549. id: 'navigator-y-axis',
  12550. alignTicks: false,
  12551. offset: 0,
  12552. index: yAxisIndex,
  12553. isInternal: true,
  12554. reversed: pick((navigatorOptions.yAxis && navigatorOptions.yAxis.reversed), (chart.yAxis[0] && chart.yAxis[0].reversed), false),
  12555. zoomEnabled: false
  12556. }, chart.inverted ? {
  12557. width: height
  12558. } : {
  12559. height: height
  12560. }));
  12561. // If we have a base series, initialize the navigator series
  12562. if (baseSeries || navigatorOptions.series.data) {
  12563. navigator.updateNavigatorSeries(false);
  12564. // If not, set up an event to listen for added series
  12565. }
  12566. else if (chart.series.length === 0) {
  12567. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  12568. // We've got one, now add it as base
  12569. if (chart.series.length > 0 && !navigator.series) {
  12570. navigator.setBaseSeries();
  12571. navigator.unbindRedraw(); // reset
  12572. }
  12573. });
  12574. }
  12575. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  12576. // Render items, so we can bind events to them:
  12577. navigator.renderElements();
  12578. // Add mouse events
  12579. navigator.addMouseEvents();
  12580. // in case of scrollbar only, fake an x axis to get translation
  12581. }
  12582. else {
  12583. navigator.xAxis = {
  12584. chart: chart,
  12585. navigatorAxis: {
  12586. fake: true
  12587. },
  12588. translate: function (value, reverse) {
  12589. var axis = chart.xAxis[0], ext = axis.getExtremes(), scrollTrackWidth = axis.len - 2 * scrollbarHeight, min = numExt('min', axis.options.min, ext.dataMin), valueRange = numExt('max', axis.options.max, ext.dataMax) - min;
  12590. return reverse ?
  12591. // from pixel to value
  12592. (value * valueRange / scrollTrackWidth) + min :
  12593. // from value to pixel
  12594. scrollTrackWidth * (value - min) / valueRange;
  12595. },
  12596. toPixels: function (value) {
  12597. return this.translate(value);
  12598. },
  12599. toValue: function (value) {
  12600. return this.translate(value, true);
  12601. }
  12602. };
  12603. navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
  12604. navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxis.AdditionsClass.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
  12605. }
  12606. // Initialize the scrollbar
  12607. if (chart.options.scrollbar.enabled) {
  12608. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, merge(chart.options.scrollbar, {
  12609. margin: navigator.navigatorEnabled ? 0 : 10,
  12610. vertical: chart.inverted
  12611. }), chart);
  12612. addEvent(navigator.scrollbar, 'changed', function (e) {
  12613. var range = navigator.size,
  12614. to = range * this.to,
  12615. from = range * this.from;
  12616. navigator.hasDragged = navigator.scrollbar.hasDragged;
  12617. navigator.render(0, 0, from, to);
  12618. if (this.shouldUpdateExtremes(e.DOMType)) {
  12619. setTimeout(function () {
  12620. navigator.onMouseUp(e);
  12621. });
  12622. }
  12623. });
  12624. }
  12625. // Add data events
  12626. navigator.addBaseSeriesEvents();
  12627. // Add redraw events
  12628. navigator.addChartEvents();
  12629. };
  12630. /**
  12631. * Get the union data extremes of the chart - the outer data extremes of the
  12632. * base X axis and the navigator axis.
  12633. *
  12634. * @private
  12635. * @function Highcharts.Navigator#getUnionExtremes
  12636. * @param {boolean} [returnFalseOnNoBaseSeries]
  12637. * as the param says.
  12638. * @return {Highcharts.Dictionary<(number|undefined)>|undefined}
  12639. */
  12640. Navigator.prototype.getUnionExtremes = function (returnFalseOnNoBaseSeries) {
  12641. var baseAxis = this.chart.xAxis[0],
  12642. navAxis = this.xAxis,
  12643. navAxisOptions = navAxis.options,
  12644. baseAxisOptions = baseAxis.options,
  12645. ret;
  12646. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  12647. ret = {
  12648. dataMin: pick(// #4053
  12649. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  12650. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  12651. };
  12652. }
  12653. return ret;
  12654. };
  12655. /**
  12656. * Set the base series and update the navigator series from this. With a bit
  12657. * of modification we should be able to make this an API method to be called
  12658. * from the outside
  12659. *
  12660. * @private
  12661. * @function Highcharts.Navigator#setBaseSeries
  12662. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  12663. * Additional series options for a navigator
  12664. * @param {boolean} [redraw]
  12665. * Whether to redraw after update.
  12666. * @return {void}
  12667. */
  12668. Navigator.prototype.setBaseSeries = function (baseSeriesOptions, redraw) {
  12669. var chart = this.chart,
  12670. baseSeries = this.baseSeries = [];
  12671. baseSeriesOptions = (baseSeriesOptions ||
  12672. chart.options && chart.options.navigator.baseSeries ||
  12673. (chart.series.length ?
  12674. // Find the first non-navigator series (#8430)
  12675. find(chart.series, function (s) {
  12676. return !s.options.isInternal;
  12677. }).index :
  12678. 0));
  12679. // Iterate through series and add the ones that should be shown in
  12680. // navigator.
  12681. (chart.series || []).forEach(function (series, i) {
  12682. if (
  12683. // Don't include existing nav series
  12684. !series.options.isInternal &&
  12685. (series.options.showInNavigator ||
  12686. (i === baseSeriesOptions ||
  12687. series.options.id === baseSeriesOptions) &&
  12688. series.options.showInNavigator !== false)) {
  12689. baseSeries.push(series);
  12690. }
  12691. });
  12692. // When run after render, this.xAxis already exists
  12693. if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
  12694. this.updateNavigatorSeries(true, redraw);
  12695. }
  12696. };
  12697. /**
  12698. * Update series in the navigator from baseSeries, adding new if does not
  12699. * exist.
  12700. *
  12701. * @private
  12702. * @function Highcharts.Navigator.updateNavigatorSeries
  12703. * @param {boolean} addEvents
  12704. * @param {boolean} [redraw]
  12705. * @return {void}
  12706. */
  12707. Navigator.prototype.updateNavigatorSeries = function (addEvents, redraw) {
  12708. var navigator = this,
  12709. chart = navigator.chart,
  12710. baseSeries = navigator.baseSeries,
  12711. baseOptions,
  12712. mergedNavSeriesOptions,
  12713. chartNavigatorSeriesOptions = navigator.navigatorOptions.series,
  12714. baseNavigatorOptions,
  12715. navSeriesMixin = {
  12716. enableMouseTracking: false,
  12717. index: null,
  12718. linkedTo: null,
  12719. group: 'nav',
  12720. padXAxis: false,
  12721. xAxis: 'navigator-x-axis',
  12722. yAxis: 'navigator-y-axis',
  12723. showInLegend: false,
  12724. stacking: void 0,
  12725. isInternal: true,
  12726. states: {
  12727. inactive: {
  12728. opacity: 1
  12729. }
  12730. }
  12731. },
  12732. // Remove navigator series that are no longer in the baseSeries
  12733. navigatorSeries = navigator.series =
  12734. (navigator.series || []).filter(function (navSeries) {
  12735. var base = navSeries.baseSeries;
  12736. if (baseSeries.indexOf(base) < 0) { // Not in array
  12737. // If there is still a base series connected to this
  12738. // series, remove event handler and reference.
  12739. if (base) {
  12740. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  12741. delete base.navigatorSeries;
  12742. }
  12743. // Kill the nav series. It may already have been
  12744. // destroyed (#8715).
  12745. if (navSeries.chart) {
  12746. navSeries.destroy();
  12747. }
  12748. return false;
  12749. }
  12750. return true;
  12751. });
  12752. // Go through each base series and merge the options to create new
  12753. // series
  12754. if (baseSeries && baseSeries.length) {
  12755. baseSeries.forEach(function eachBaseSeries(base) {
  12756. var linkedNavSeries = base.navigatorSeries,
  12757. userNavOptions = extend(
  12758. // Grab color and visibility from base as default
  12759. {
  12760. color: base.color,
  12761. visible: base.visible
  12762. }, !isArray(chartNavigatorSeriesOptions) ?
  12763. chartNavigatorSeriesOptions :
  12764. defaultOptions.navigator.series);
  12765. // Don't update if the series exists in nav and we have disabled
  12766. // adaptToUpdatedData.
  12767. if (linkedNavSeries &&
  12768. navigator.navigatorOptions.adaptToUpdatedData === false) {
  12769. return;
  12770. }
  12771. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  12772. baseOptions = base.options || {};
  12773. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  12774. // The dataLabels options are not merged correctly
  12775. // if the settings are an array, #13847.
  12776. userNavOptions.dataLabels = splat(userNavOptions.dataLabels);
  12777. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  12778. // Once nav series type is resolved, pick correct pointRange
  12779. mergedNavSeriesOptions.pointRange = pick(
  12780. // Stricte set pointRange in options
  12781. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  12782. // Fallback to default values, e.g. `null` for column
  12783. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  12784. // Merge data separately. Do a slice to avoid mutating the
  12785. // navigator options from base series (#4923).
  12786. var navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  12787. navigator.hasNavigatorData =
  12788. navigator.hasNavigatorData || !!navigatorSeriesData;
  12789. mergedNavSeriesOptions.data =
  12790. navigatorSeriesData ||
  12791. baseOptions.data && baseOptions.data.slice(0);
  12792. // Update or add the series
  12793. if (linkedNavSeries && linkedNavSeries.options) {
  12794. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  12795. }
  12796. else {
  12797. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  12798. base.navigatorSeries.baseSeries = base; // Store ref
  12799. navigatorSeries.push(base.navigatorSeries);
  12800. }
  12801. });
  12802. }
  12803. // If user has defined data (and no base series) or explicitly defined
  12804. // navigator.series as an array, we create these series on top of any
  12805. // base series.
  12806. if (chartNavigatorSeriesOptions.data &&
  12807. !(baseSeries && baseSeries.length) ||
  12808. isArray(chartNavigatorSeriesOptions)) {
  12809. navigator.hasNavigatorData = false;
  12810. // Allow navigator.series to be an array
  12811. chartNavigatorSeriesOptions =
  12812. splat(chartNavigatorSeriesOptions);
  12813. chartNavigatorSeriesOptions.forEach(function (userSeriesOptions, i) {
  12814. navSeriesMixin.name =
  12815. 'Navigator ' + (navigatorSeries.length + 1);
  12816. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  12817. // Since we don't have a base series to pull color from,
  12818. // try to fake it by using color from series with same
  12819. // index. Otherwise pull from the colors array. We need
  12820. // an explicit color as otherwise updates will increment
  12821. // color counter and we'll get a new color for each
  12822. // update of the nav series.
  12823. color: chart.series[i] &&
  12824. !chart.series[i].options.isInternal &&
  12825. chart.series[i].color ||
  12826. chart.options.colors[i] ||
  12827. chart.options.colors[0]
  12828. }, navSeriesMixin, userSeriesOptions);
  12829. mergedNavSeriesOptions.data = userSeriesOptions.data;
  12830. if (mergedNavSeriesOptions.data) {
  12831. navigator.hasNavigatorData = true;
  12832. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  12833. }
  12834. });
  12835. }
  12836. if (addEvents) {
  12837. this.addBaseSeriesEvents();
  12838. }
  12839. };
  12840. /**
  12841. * Add data events.
  12842. * For example when main series is updated we need to recalculate extremes
  12843. *
  12844. * @private
  12845. * @function Highcharts.Navigator#addBaseSeriesEvent
  12846. * @return {void}
  12847. */
  12848. Navigator.prototype.addBaseSeriesEvents = function () {
  12849. var navigator = this,
  12850. baseSeries = navigator.baseSeries || [];
  12851. // Bind modified extremes event to first base's xAxis only.
  12852. // In event of > 1 base-xAxes, the navigator will ignore those.
  12853. // Adding this multiple times to the same axis is no problem, as
  12854. // duplicates should be discarded by the browser.
  12855. if (baseSeries[0] && baseSeries[0].xAxis) {
  12856. baseSeries[0].eventsToUnbind.push(addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes));
  12857. }
  12858. baseSeries.forEach(function (base) {
  12859. // Link base series show/hide to navigator series visibility
  12860. base.eventsToUnbind.push(addEvent(base, 'show', function () {
  12861. if (this.navigatorSeries) {
  12862. this.navigatorSeries.setVisible(true, false);
  12863. }
  12864. }));
  12865. base.eventsToUnbind.push(addEvent(base, 'hide', function () {
  12866. if (this.navigatorSeries) {
  12867. this.navigatorSeries.setVisible(false, false);
  12868. }
  12869. }));
  12870. // Respond to updated data in the base series, unless explicitily
  12871. // not adapting to data changes.
  12872. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  12873. if (base.xAxis) {
  12874. base.eventsToUnbind.push(addEvent(base, 'updatedData', this.updatedDataHandler));
  12875. }
  12876. }
  12877. // Handle series removal
  12878. base.eventsToUnbind.push(addEvent(base, 'remove', function () {
  12879. if (this.navigatorSeries) {
  12880. erase(navigator.series, this.navigatorSeries);
  12881. if (defined(this.navigatorSeries.options)) {
  12882. this.navigatorSeries.remove(false);
  12883. }
  12884. delete this.navigatorSeries;
  12885. }
  12886. }));
  12887. }, this);
  12888. };
  12889. /**
  12890. * Get minimum from all base series connected to the navigator
  12891. * @private
  12892. * @param {number} currentSeriesMin
  12893. * Minium from the current series
  12894. * @return {number} Minimum from all series
  12895. */
  12896. Navigator.prototype.getBaseSeriesMin = function (currentSeriesMin) {
  12897. return this.baseSeries.reduce(function (min, series) {
  12898. // (#10193)
  12899. return Math.min(min, series.xData ? series.xData[0] : min);
  12900. }, currentSeriesMin);
  12901. };
  12902. /**
  12903. * Set the navigator x axis extremes to reflect the total. The navigator
  12904. * extremes should always be the extremes of the union of all series in the
  12905. * chart as well as the navigator series.
  12906. *
  12907. * @private
  12908. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  12909. */
  12910. Navigator.prototype.modifyNavigatorAxisExtremes = function () {
  12911. var xAxis = this.xAxis,
  12912. unionExtremes;
  12913. if (typeof xAxis.getExtremes !== 'undefined') {
  12914. unionExtremes = this.getUnionExtremes(true);
  12915. if (unionExtremes &&
  12916. (unionExtremes.dataMin !== xAxis.min ||
  12917. unionExtremes.dataMax !== xAxis.max)) {
  12918. xAxis.min = unionExtremes.dataMin;
  12919. xAxis.max = unionExtremes.dataMax;
  12920. }
  12921. }
  12922. };
  12923. /**
  12924. * Hook to modify the base axis extremes with information from the Navigator
  12925. *
  12926. * @private
  12927. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  12928. */
  12929. Navigator.prototype.modifyBaseAxisExtremes = function () {
  12930. var baseXAxis = this,
  12931. navigator = baseXAxis.chart.navigator,
  12932. baseExtremes = baseXAxis.getExtremes(),
  12933. baseMin = baseExtremes.min,
  12934. baseMax = baseExtremes.max,
  12935. baseDataMin = baseExtremes.dataMin,
  12936. baseDataMax = baseExtremes.dataMax,
  12937. range = baseMax - baseMin,
  12938. stickToMin = navigator.stickToMin,
  12939. stickToMax = navigator.stickToMax,
  12940. overscroll = pick(baseXAxis.options.overscroll, 0),
  12941. newMax,
  12942. newMin,
  12943. navigatorSeries = navigator.series && navigator.series[0],
  12944. hasSetExtremes = !!baseXAxis.setExtremes,
  12945. // When the extremes have been set by range selector button, don't
  12946. // stick to min or max. The range selector buttons will handle the
  12947. // extremes. (#5489)
  12948. unmutable = baseXAxis.eventArgs &&
  12949. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  12950. if (!unmutable) {
  12951. // If the zoomed range is already at the min, move it to the right
  12952. // as new data comes in
  12953. if (stickToMin) {
  12954. newMin = baseDataMin;
  12955. newMax = newMin + range;
  12956. }
  12957. // If the zoomed range is already at the max, move it to the right
  12958. // as new data comes in
  12959. if (stickToMax) {
  12960. newMax = baseDataMax + overscroll;
  12961. // If stickToMin is true, the new min value is set above
  12962. if (!stickToMin) {
  12963. newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)
  12964. newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  12965. navigatorSeries.xData[0] :
  12966. -Number.MAX_VALUE));
  12967. }
  12968. }
  12969. // Update the extremes
  12970. if (hasSetExtremes && (stickToMin || stickToMax)) {
  12971. if (isNumber(newMin)) {
  12972. baseXAxis.min = baseXAxis.userMin = newMin;
  12973. baseXAxis.max = baseXAxis.userMax = newMax;
  12974. }
  12975. }
  12976. }
  12977. // Reset
  12978. navigator.stickToMin =
  12979. navigator.stickToMax = null;
  12980. };
  12981. /**
  12982. * Handler for updated data on the base series. When data is modified, the
  12983. * navigator series must reflect it. This is called from the Chart.redraw
  12984. * function before axis and series extremes are computed.
  12985. *
  12986. * @private
  12987. * @function Highcharts.Navigator#updateDataHandler
  12988. */
  12989. Navigator.prototype.updatedDataHandler = function () {
  12990. var navigator = this.chart.navigator,
  12991. baseSeries = this,
  12992. navigatorSeries = this.navigatorSeries,
  12993. xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]);
  12994. // If the scrollbar is scrolled all the way to the right, keep right as
  12995. // new data comes in.
  12996. navigator.stickToMax = navigator.reversedExtremes ?
  12997. Math.round(navigator.zoomedMin) === 0 :
  12998. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  12999. // Detect whether the zoomed area should stick to the minimum or
  13000. // maximum. If the current axis minimum falls outside the new updated
  13001. // dataset, we must adjust.
  13002. navigator.stickToMin = isNumber(baseSeries.xAxis.min) &&
  13003. (baseSeries.xAxis.min <= xDataMin) &&
  13004. (!this.chart.fixedRange || !navigator.stickToMax);
  13005. // Set the navigator series data to the new data of the base series
  13006. if (navigatorSeries && !navigator.hasNavigatorData) {
  13007. navigatorSeries.options.pointStart = baseSeries.xData[0];
  13008. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  13009. }
  13010. };
  13011. /**
  13012. * Add chart events, like redrawing navigator, when chart requires that.
  13013. *
  13014. * @private
  13015. * @function Highcharts.Navigator#addChartEvents
  13016. * @return {void}
  13017. */
  13018. Navigator.prototype.addChartEvents = function () {
  13019. if (!this.eventsToUnbind) {
  13020. this.eventsToUnbind = [];
  13021. }
  13022. this.eventsToUnbind.push(
  13023. // Move the scrollbar after redraw, like after data updata even if
  13024. // axes don't redraw
  13025. addEvent(this.chart, 'redraw', function () {
  13026. var navigator = this.navigator,
  13027. xAxis = navigator && (navigator.baseSeries &&
  13028. navigator.baseSeries[0] &&
  13029. navigator.baseSeries[0].xAxis ||
  13030. this.xAxis[0]); // #5709, #13114
  13031. if (xAxis) {
  13032. navigator.render(xAxis.min,
  13033. xAxis.max);
  13034. }
  13035. }),
  13036. // Make room for the navigator, can be placed around the chart:
  13037. addEvent(this.chart, 'getMargins', function () {
  13038. var chart = this,
  13039. navigator = chart.navigator,
  13040. marginName = navigator.opposite ?
  13041. 'plotTop' : 'marginBottom';
  13042. if (chart.inverted) {
  13043. marginName = navigator.opposite ?
  13044. 'marginRight' : 'plotLeft';
  13045. }
  13046. chart[marginName] =
  13047. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  13048. navigator.outlineHeight :
  13049. 0) + navigator.navigatorOptions.margin;
  13050. }));
  13051. };
  13052. /**
  13053. * Destroys allocated elements.
  13054. *
  13055. * @private
  13056. * @function Highcharts.Navigator#destroy
  13057. */
  13058. Navigator.prototype.destroy = function () {
  13059. // Disconnect events added in addEvents
  13060. this.removeEvents();
  13061. if (this.xAxis) {
  13062. erase(this.chart.xAxis, this.xAxis);
  13063. erase(this.chart.axes, this.xAxis);
  13064. }
  13065. if (this.yAxis) {
  13066. erase(this.chart.yAxis, this.yAxis);
  13067. erase(this.chart.axes, this.yAxis);
  13068. }
  13069. // Destroy series
  13070. (this.series || []).forEach(function (s) {
  13071. if (s.destroy) {
  13072. s.destroy();
  13073. }
  13074. });
  13075. // Destroy properties
  13076. [
  13077. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  13078. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  13079. 'rendered'
  13080. ].forEach(function (prop) {
  13081. if (this[prop] && this[prop].destroy) {
  13082. this[prop].destroy();
  13083. }
  13084. this[prop] = null;
  13085. }, this);
  13086. // Destroy elements in collection
  13087. [this.handles].forEach(function (coll) {
  13088. destroyObjectProperties(coll);
  13089. }, this);
  13090. };
  13091. return Navigator;
  13092. }());
  13093. // End of prototype
  13094. if (!H.Navigator) {
  13095. H.Navigator = Navigator;
  13096. NavigatorAxis.compose(Axis);
  13097. // For Stock charts. For x only zooming, do not to create the zoom button
  13098. // because X axis zooming is already allowed by the Navigator and Range
  13099. // selector. (#9285)
  13100. addEvent(Chart, 'beforeShowResetZoom', function () {
  13101. var chartOptions = this.options,
  13102. navigator = chartOptions.navigator,
  13103. rangeSelector = chartOptions.rangeSelector;
  13104. if (((navigator && navigator.enabled) ||
  13105. (rangeSelector && rangeSelector.enabled)) &&
  13106. ((!isTouchDevice && chartOptions.chart.zoomType === 'x') ||
  13107. (isTouchDevice && chartOptions.chart.pinchType === 'x'))) {
  13108. return false;
  13109. }
  13110. });
  13111. // Initialize navigator for stock charts
  13112. addEvent(Chart, 'beforeRender', function () {
  13113. var options = this.options;
  13114. if (options.navigator.enabled ||
  13115. options.scrollbar.enabled) {
  13116. this.scroller = this.navigator = new Navigator(this);
  13117. }
  13118. });
  13119. // For stock charts, extend the Chart.setChartSize method so that we can set
  13120. // the final top position of the navigator once the height of the chart,
  13121. // including the legend, is determined. #367. We can't use Chart.getMargins,
  13122. // because labels offsets are not calculated yet.
  13123. addEvent(Chart, 'afterSetChartSize', function () {
  13124. var legend = this.legend,
  13125. navigator = this.navigator,
  13126. scrollbarHeight,
  13127. legendOptions,
  13128. xAxis,
  13129. yAxis;
  13130. if (navigator) {
  13131. legendOptions = legend && legend.options;
  13132. xAxis = navigator.xAxis;
  13133. yAxis = navigator.yAxis;
  13134. scrollbarHeight = navigator.scrollbarHeight;
  13135. // Compute the top position
  13136. if (this.inverted) {
  13137. navigator.left = navigator.opposite ?
  13138. this.chartWidth - scrollbarHeight -
  13139. navigator.height :
  13140. this.spacing[3] + scrollbarHeight;
  13141. navigator.top = this.plotTop + scrollbarHeight;
  13142. }
  13143. else {
  13144. navigator.left = this.plotLeft + scrollbarHeight;
  13145. navigator.top = navigator.navigatorOptions.top ||
  13146. this.chartHeight -
  13147. navigator.height -
  13148. scrollbarHeight -
  13149. this.spacing[2] -
  13150. (this.rangeSelector && this.extraBottomMargin ?
  13151. this.rangeSelector.getHeight() :
  13152. 0) -
  13153. ((legendOptions &&
  13154. legendOptions.verticalAlign === 'bottom' &&
  13155. legendOptions.layout !== 'proximate' && // #13392
  13156. legendOptions.enabled &&
  13157. !legendOptions.floating) ?
  13158. legend.legendHeight +
  13159. pick(legendOptions.margin, 10) :
  13160. 0) -
  13161. (this.titleOffset ? this.titleOffset[2] : 0);
  13162. }
  13163. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  13164. if (this.inverted) {
  13165. xAxis.options.left = yAxis.options.left = navigator.left;
  13166. }
  13167. else {
  13168. xAxis.options.top = yAxis.options.top = navigator.top;
  13169. }
  13170. xAxis.setAxisSize();
  13171. yAxis.setAxisSize();
  13172. }
  13173. }
  13174. });
  13175. // Merge options, if no scrolling exists yet
  13176. addEvent(Chart, 'update', function (e) {
  13177. var navigatorOptions = (e.options.navigator || {}),
  13178. scrollbarOptions = (e.options.scrollbar || {});
  13179. if (!this.navigator && !this.scroller &&
  13180. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  13181. merge(true, this.options.navigator, navigatorOptions);
  13182. merge(true, this.options.scrollbar, scrollbarOptions);
  13183. delete e.options.navigator;
  13184. delete e.options.scrollbar;
  13185. }
  13186. });
  13187. // Initialize navigator, if no scrolling exists yet
  13188. addEvent(Chart, 'afterUpdate', function (event) {
  13189. if (!this.navigator && !this.scroller &&
  13190. (this.options.navigator.enabled ||
  13191. this.options.scrollbar.enabled)) {
  13192. this.scroller = this.navigator = new Navigator(this);
  13193. if (pick(event.redraw, true)) {
  13194. this.redraw(event.animation); // #7067
  13195. }
  13196. }
  13197. });
  13198. // Handle adding new series
  13199. addEvent(Chart, 'afterAddSeries', function () {
  13200. if (this.navigator) {
  13201. // Recompute which series should be shown in navigator, and add them
  13202. this.navigator.setBaseSeries(null, false);
  13203. }
  13204. });
  13205. // Handle updating series
  13206. addEvent(Series, 'afterUpdate', function () {
  13207. if (this.chart.navigator && !this.options.isInternal) {
  13208. this.chart.navigator.setBaseSeries(null, false);
  13209. }
  13210. });
  13211. Chart.prototype.callbacks.push(function (chart) {
  13212. var extremes,
  13213. navigator = chart.navigator;
  13214. // Initialize the navigator
  13215. if (navigator && chart.xAxis[0]) {
  13216. extremes = chart.xAxis[0].getExtremes();
  13217. navigator.render(extremes.min, extremes.max);
  13218. }
  13219. });
  13220. }
  13221. H.Navigator = Navigator;
  13222. return H.Navigator;
  13223. });
  13224. _registerModule(_modules, 'masters/modules/gantt.src.js', [_modules['Core/Globals.js'], _modules['Core/Chart/GanttChart.js']], function (Highcharts, GanttChart) {
  13225. Highcharts.GanttChart = GanttChart;
  13226. Highcharts.ganttChart = GanttChart.ganttChart;
  13227. });
  13228. }));